Merge pull request #865 from zed-industries/remove-web-frontend

Nathan Sobo created

Remove web frontend from collab server

Change summary

Cargo.lock                                                |  620 --
Dockerfile                                                |   10 
Dockerfile.migrator                                       |    2 
crates/collab/.env.template.toml                          |    9 
crates/collab/Cargo.toml                                  |   14 
crates/collab/README.md                                   |   16 
crates/collab/favicon.ico                                 |    0 
crates/collab/migrations/20220421165757_drop_signups.sql  |    1 
crates/collab/src/admin.rs                                |  117 
crates/collab/src/assets.rs                               |   29 
crates/collab/src/auth.rs                                 |  215 
crates/collab/src/careers.rs                              |   15 
crates/collab/src/community.rs                            |   15 
crates/collab/src/db.rs                                   |   98 
crates/collab/src/errors.rs                               |   44 
crates/collab/src/expiring.rs                             |   43 
crates/collab/src/github.rs                               |  281 -
crates/collab/src/home.rs                                 |   80 
crates/collab/src/main.rs                                 |  115 
crates/collab/src/releases.rs                             |   54 
crates/collab/src/rpc.rs                                  |    9 
crates/collab/src/team.rs                                 |   15 
crates/collab/static/browserconfig.xml                    |    9 
crates/collab/static/fonts/VisbyCF-Bold.eot               |    0 
crates/collab/static/fonts/VisbyCF-Bold.woff              |    0 
crates/collab/static/fonts/VisbyCF-Bold.woff2             |    0 
crates/collab/static/fonts/VisbyCF-BoldOblique.eot        |    0 
crates/collab/static/fonts/VisbyCF-BoldOblique.woff       |    0 
crates/collab/static/fonts/VisbyCF-BoldOblique.woff2      |    0 
crates/collab/static/fonts/VisbyCF-DemiBold.eot           |    0 
crates/collab/static/fonts/VisbyCF-DemiBold.woff          |    0 
crates/collab/static/fonts/VisbyCF-DemiBold.woff2         |    0 
crates/collab/static/fonts/VisbyCF-DemiBoldOblique.eot    |    0 
crates/collab/static/fonts/VisbyCF-DemiBoldOblique.woff   |    0 
crates/collab/static/fonts/VisbyCF-DemiBoldOblique.woff2  |    0 
crates/collab/static/fonts/VisbyCF-ExtraBold.eot          |    0 
crates/collab/static/fonts/VisbyCF-ExtraBold.woff         |    0 
crates/collab/static/fonts/VisbyCF-ExtraBold.woff2        |    0 
crates/collab/static/fonts/VisbyCF-ExtraBoldOblique.eot   |    0 
crates/collab/static/fonts/VisbyCF-ExtraBoldOblique.woff  |    0 
crates/collab/static/fonts/VisbyCF-ExtraBoldOblique.woff2 |    0 
crates/collab/static/fonts/VisbyCF-Heavy.eot              |    0 
crates/collab/static/fonts/VisbyCF-Heavy.woff             |    0 
crates/collab/static/fonts/VisbyCF-Heavy.woff2            |    0 
crates/collab/static/fonts/VisbyCF-HeavyOblique.eot       |    0 
crates/collab/static/fonts/VisbyCF-HeavyOblique.woff      |    0 
crates/collab/static/fonts/VisbyCF-HeavyOblique.woff2     |    0 
crates/collab/static/fonts/VisbyCF-Light.eot              |    0 
crates/collab/static/fonts/VisbyCF-Light.woff             |    0 
crates/collab/static/fonts/VisbyCF-Light.woff2            |    0 
crates/collab/static/fonts/VisbyCF-LightOblique.eot       |    0 
crates/collab/static/fonts/VisbyCF-LightOblique.woff      |    0 
crates/collab/static/fonts/VisbyCF-LightOblique.woff2     |    0 
crates/collab/static/fonts/VisbyCF-Medium.eot             |    0 
crates/collab/static/fonts/VisbyCF-Medium.woff            |    0 
crates/collab/static/fonts/VisbyCF-Medium.woff2           |    0 
crates/collab/static/fonts/VisbyCF-MediumOblique.eot      |    0 
crates/collab/static/fonts/VisbyCF-MediumOblique.woff     |    0 
crates/collab/static/fonts/VisbyCF-MediumOblique.woff2    |    0 
crates/collab/static/fonts/VisbyCF-Regular.eot            |    0 
crates/collab/static/fonts/VisbyCF-Regular.woff           |    0 
crates/collab/static/fonts/VisbyCF-Regular.woff2          |    0 
crates/collab/static/fonts/VisbyCF-RegularOblique.eot     |    0 
crates/collab/static/fonts/VisbyCF-RegularOblique.woff    |    0 
crates/collab/static/fonts/VisbyCF-RegularOblique.woff2   |    0 
crates/collab/static/fonts/VisbyCF-Thin.eot               |    0 
crates/collab/static/fonts/VisbyCF-Thin.woff              |    0 
crates/collab/static/fonts/VisbyCF-Thin.woff2             |    0 
crates/collab/static/fonts/VisbyCF-ThinOblique.eot        |    0 
crates/collab/static/fonts/VisbyCF-ThinOblique.woff       |    0 
crates/collab/static/fonts/VisbyCF-ThinOblique.woff2      |    0 
crates/collab/static/images/android-chrome-192x192.png    |    0 
crates/collab/static/images/android-chrome-512x512.png    |    0 
crates/collab/static/images/apple-touch-icon.png          |    0 
crates/collab/static/images/favicon-16x16.png             |    0 
crates/collab/static/images/favicon-32x32.png             |    0 
crates/collab/static/images/favicon.png                   |    0 
crates/collab/static/images/favicon.svg                   |   14 
crates/collab/static/images/mstile-144x144.png            |    0 
crates/collab/static/images/mstile-150x150.png            |    0 
crates/collab/static/images/mstile-310x150.png            |    0 
crates/collab/static/images/mstile-310x310.png            |    0 
crates/collab/static/images/mstile-70x70.png              |    0 
crates/collab/static/images/safari-pinned-tab.svg         |   19 
crates/collab/static/images/zed-og-image.png              |    0 
crates/collab/static/images/zed-twitter-image.png         |    0 
crates/collab/static/prism.js                             |    2 
crates/collab/static/prose.css                            |  253 
crates/collab/static/prose.css.map                        |    2 
crates/collab/static/prose.scss                           |  260 
crates/collab/static/site.webmanifest                     |   18 
crates/collab/static/svg/hero.svg                         |    4 
crates/collab/styles.css                                  |  136 
crates/collab/templates/admin.hbs                         |  105 
crates/collab/templates/careers.hbs                       |   20 
crates/collab/templates/community.hbs                     |  107 
crates/collab/templates/docs.hbs                          |   41 
crates/collab/templates/error.hbs                         |   15 
crates/collab/templates/home.hbs                          |  110 
crates/collab/templates/partials/layout.hbs               |  199 
crates/collab/templates/releases.hbs                      |   35 
crates/collab/templates/signup.hbs                        |   18 
crates/collab/templates/team.hbs                          |   34 
script/build-css                                          |   10 
script/deploy-migration                                   |    4 
script/package-lock.json                                  | 2452 ---------
script/package.json                                       |    6 
script/tailwind.config.js                                 |   46 
108 files changed, 41 insertions(+), 5,680 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -29,7 +29,7 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331"
 dependencies = [
- "generic-array 0.14.4",
+ "generic-array",
 ]
 
 [[package]]
@@ -64,7 +64,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
 dependencies = [
  "cipher 0.2.5",
- "opaque-debug 0.3.0",
+ "opaque-debug",
 ]
 
 [[package]]
@@ -74,24 +74,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
 dependencies = [
  "cipher 0.2.5",
- "opaque-debug 0.3.0",
-]
-
-[[package]]
-name = "ahash"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
-
-[[package]]
-name = "ahash"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "796540673305a66d127804eef19ad696f1f204b8c1025aaca4958c17eab32877"
-dependencies = [
- "getrandom 0.2.2",
- "once_cell",
- "version_check",
+ "opaque-debug",
 ]
 
 [[package]]
@@ -378,17 +361,6 @@ dependencies = [
  "syn",
 ]
 
-[[package]]
-name = "async-rustls"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f38092e8f467f47aadaff680903c7cbfeee7926b058d7f40af2dd4c878fbdee"
-dependencies = [
- "futures-lite",
- "rustls 0.18.1",
- "webpki",
-]
-
 [[package]]
 name = "async-rustls"
 version = "0.2.0"
@@ -396,7 +368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c86f33abd5a4f3e2d6d9251a9e0c6a7e52eb1113caf893dae8429bf4a53f378"
 dependencies = [
  "futures-lite",
- "rustls 0.19.1",
+ "rustls",
  "webpki",
 ]
 
@@ -421,17 +393,6 @@ dependencies = [
  "sha2 0.9.5",
 ]
 
-[[package]]
-name = "async-sqlx-session"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b66fb8c6ffbf26cdba6705c36f086b6f02f0b4778b6a348134302a2a00730bc"
-dependencies = [
- "async-session",
- "async-std",
- "sqlx 0.4.2",
-]
-
 [[package]]
 name = "async-sse"
 version = "4.1.0"
@@ -488,7 +449,7 @@ checksum = "2f23d769dbf1838d5df5156e7b1ad404f4c463d1ac2c6aeb6cd943630f8a8400"
 dependencies = [
  "futures-core",
  "futures-io",
- "rustls 0.19.1",
+ "rustls",
  "webpki",
  "webpki-roots",
 ]
@@ -694,25 +655,13 @@ version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
 
-[[package]]
-name = "block-buffer"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
-dependencies = [
- "block-padding",
- "byte-tools",
- "byteorder",
- "generic-array 0.12.4",
-]
-
 [[package]]
 name = "block-buffer"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
 dependencies = [
- "generic-array 0.14.4",
+ "generic-array",
 ]
 
 [[package]]
@@ -721,16 +670,7 @@ version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
 dependencies = [
- "generic-array 0.14.4",
-]
-
-[[package]]
-name = "block-padding"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
-dependencies = [
- "byte-tools",
+ "generic-array",
 ]
 
 [[package]]
@@ -814,12 +754,6 @@ dependencies = [
  "stable_deref_trait",
 ]
 
-[[package]]
-name = "byte-tools"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
-
 [[package]]
 name = "bytemuck"
 version = "1.5.1"
@@ -850,28 +784,6 @@ version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
 
-[[package]]
-name = "cargo-platform"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "cargo_metadata"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f"
-dependencies = [
- "cargo-platform",
- "semver 0.11.0",
- "semver-parser 0.10.2",
- "serde",
- "serde_json",
-]
-
 [[package]]
 name = "cc"
 version = "1.0.67"
@@ -943,7 +855,7 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
 dependencies = [
- "generic-array 0.14.4",
+ "generic-array",
 ]
 
 [[package]]
@@ -952,7 +864,7 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
 dependencies = [
- "generic-array 0.14.4",
+ "generic-array",
 ]
 
 [[package]]
@@ -1066,18 +978,6 @@ dependencies = [
  "cc",
 ]
 
-[[package]]
-name = "coarsetime"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2918e2ffa91a49dabbba4965fe38a37a1ba0b6953a29e32cc250a8d59cd42232"
-dependencies = [
- "libc",
- "once_cell",
- "wasi 0.10.0+wasi-snapshot-preview1",
- "wasm-bindgen",
-]
-
 [[package]]
 name = "cocoa"
 version = "0.24.0"
@@ -1113,44 +1013,34 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "async-io",
- "async-sqlx-session",
  "async-std",
  "async-trait",
  "async-tungstenite",
  "base64 0.13.0",
- "clap 3.1.8",
  "client",
  "collections",
- "comrak",
  "ctor",
  "editor",
- "either",
  "env_logger 0.8.3",
  "envy",
  "futures",
  "gpui",
- "handlebars",
- "http-auth-basic",
  "json_env_logger",
- "jwt-simple",
  "language",
  "lazy_static",
  "lipsum",
  "log",
  "lsp",
- "oauth2",
- "oauth2-surf",
  "parking_lot",
  "project",
  "rand 0.8.3",
  "rpc",
- "rust-embed",
  "scrypt",
  "serde",
  "serde_json",
  "settings",
- "sha-1 0.9.6",
- "sqlx 0.5.5",
+ "sha-1",
+ "sqlx",
  "surf",
  "theme",
  "tide",
@@ -1191,25 +1081,6 @@ dependencies = [
  "workspace",
 ]
 
-[[package]]
-name = "comrak"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b423acba50d5016684beaf643f9991e622633a4c858be6885653071c2da2b0c6"
-dependencies = [
- "clap 2.33.3",
- "entities",
- "lazy_static",
- "pest",
- "pest_derive",
- "regex",
- "shell-words",
- "twoway",
- "typed-arena",
- "unicode_categories",
- "xdg",
-]
-
 [[package]]
 name = "concurrent-queue"
 version = "1.2.2"
@@ -1219,12 +1090,6 @@ dependencies = [
  "cache-padded",
 ]
 
-[[package]]
-name = "const-oid"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44c32f031ea41b4291d695026c023b95d59db2d8a2c7640800ed56bc8f510f22"
-
 [[package]]
 name = "const_fn"
 version = "0.4.8"
@@ -1444,25 +1309,13 @@ dependencies = [
  "loom",
 ]
 
-[[package]]
-name = "crypto-bigint"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b32a398eb1ccfbe7e4f452bc749c44d38dd732e9a253f19da224c416f00ee7f4"
-dependencies = [
- "generic-array 0.14.4",
- "rand_core 0.6.2",
- "subtle",
- "zeroize",
-]
-
 [[package]]
 name = "crypto-common"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
 dependencies = [
- "generic-array 0.14.4",
+ "generic-array",
  "typenum",
 ]
 
@@ -1472,7 +1325,7 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
 dependencies = [
- "generic-array 0.14.4",
+ "generic-array",
  "subtle",
 ]
 
@@ -1482,7 +1335,7 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6"
 dependencies = [
- "generic-array 0.14.4",
+ "generic-array",
  "subtle",
 ]
 
@@ -1492,16 +1345,10 @@ version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e"
 dependencies = [
- "generic-array 0.14.4",
+ "generic-array",
  "subtle",
 ]
 
-[[package]]
-name = "ct-codecs"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df"
-
 [[package]]
 name = "ctor"
 version = "0.1.20"
@@ -1587,15 +1434,6 @@ dependencies = [
  "byteorder",
 ]
 
-[[package]]
-name = "der"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f215f706081a44cb702c71c39a52c05da637822e9c1645a50b7202689e982d"
-dependencies = [
- "const-oid",
-]
-
 [[package]]
 name = "dhat"
 version = "0.3.0"
@@ -1631,22 +1469,13 @@ dependencies = [
  "workspace",
 ]
 
-[[package]]
-name = "digest"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
-dependencies = [
- "generic-array 0.12.4",
-]
-
 [[package]]
 name = "digest"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
 dependencies = [
- "generic-array 0.14.4",
+ "generic-array",
 ]
 
 [[package]]
@@ -1745,27 +1574,6 @@ version = "3.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1dd4afd79212583ff429b913ad6605242ed7eec277e950b1438f300748f948f4"
 
-[[package]]
-name = "ecdsa"
-version = "0.12.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05cb0ed2d2ce37766ac86c05f66973ace8c51f7f1533bedce8fb79e2b54b3f14"
-dependencies = [
- "der",
- "elliptic-curve",
- "hmac 0.11.0",
- "signature",
-]
-
-[[package]]
-name = "ed25519-compact"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aaf396058cc7285b342f9a10ed7a377f088942396c46c4c9a7eb4f0782cb1171"
-dependencies = [
- "getrandom 0.2.2",
-]
-
 [[package]]
 name = "editor"
 version = "0.1.0"
@@ -1812,22 +1620,6 @@ version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 
-[[package]]
-name = "elliptic-curve"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83e5c176479da93a0983f0a6fdc3c1b8e7d5be0d7fe3fe05a99f15b96582b9a8"
-dependencies = [
- "crypto-bigint",
- "ff",
- "generic-array 0.14.4",
- "group",
- "pkcs8",
- "rand_core 0.6.2",
- "subtle",
- "zeroize",
-]
-
 [[package]]
 name = "encoding_rs"
 version = "0.8.28"
@@ -1837,12 +1629,6 @@ dependencies = [
  "cfg-if 1.0.0",
 ]
 
-[[package]]
-name = "entities"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
-
 [[package]]
 name = "env_logger"
 version = "0.7.1"
@@ -1918,12 +1704,6 @@ dependencies = [
  "pkg-config",
 ]
 
-[[package]]
-name = "fake-simd"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
-
 [[package]]
 name = "fastrand"
 version = "1.7.0"
@@ -1949,16 +1729,6 @@ dependencies = [
  "web-sys",
 ]
 
-[[package]]
-name = "ff"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63eec06c61e487eecf0f7e6e6372e596a81922c28d33e645d6983ca6493a1af0"
-dependencies = [
- "rand_core 0.6.2",
- "subtle",
-]
-
 [[package]]
 name = "file_finder"
 version = "0.1.0"
@@ -2281,15 +2051,6 @@ dependencies = [
  "winapi 0.3.9",
 ]
 
-[[package]]
-name = "generic-array"
-version = "0.12.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
-dependencies = [
- "typenum",
-]
-
 [[package]]
 name = "generic-array"
 version = "0.14.4"
@@ -2318,10 +2079,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
 dependencies = [
  "cfg-if 1.0.0",
- "js-sys",
  "libc",
  "wasi 0.10.0+wasi-snapshot-preview1",
- "wasm-bindgen",
 ]
 
 [[package]]
@@ -2330,7 +2089,7 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375"
 dependencies = [
- "opaque-debug 0.3.0",
+ "opaque-debug",
  "polyval",
 ]
 
@@ -2456,39 +2215,11 @@ dependencies = [
  "syn",
 ]
 
-[[package]]
-name = "group"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912"
-dependencies = [
- "ff",
- "rand_core 0.6.2",
- "subtle",
-]
-
-[[package]]
-name = "handlebars"
-version = "3.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3"
-dependencies = [
- "log",
- "pest",
- "pest_derive",
- "quick-error",
- "serde",
- "serde_json",
-]
-
 [[package]]
 name = "hashbrown"
 version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
-dependencies = [
- "ahash 0.4.7",
-]
 
 [[package]]
 name = "hashbrown"
@@ -2496,16 +2227,7 @@ version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
 dependencies = [
- "ahash 0.7.4",
-]
-
-[[package]]
-name = "hashlink"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8"
-dependencies = [
- "hashbrown 0.9.1",
+ "ahash",
 ]
 
 [[package]]
@@ -2587,24 +2309,6 @@ dependencies = [
  "digest 0.9.0",
 ]
 
-[[package]]
-name = "hmac-sha256"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcdc571e566521512579aab40bf807c5066e1765fb36857f16ed7595c13567c6"
-dependencies = [
- "digest 0.9.0",
-]
-
-[[package]]
-name = "hmac-sha512"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383"
-dependencies = [
- "digest 0.9.0",
-]
-
 [[package]]
 name = "http"
 version = "0.2.4"
@@ -2880,40 +2584,6 @@ dependencies = [
  "serde_json",
 ]
 
-[[package]]
-name = "jwt-simple"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b5630483dc02fd2274f6d1ec91209811f1a3bc442da4d76a31872d6a02cf9e9"
-dependencies = [
- "anyhow",
- "coarsetime",
- "ct-codecs",
- "ed25519-compact",
- "hmac-sha256",
- "hmac-sha512",
- "k256",
- "p256",
- "rand 0.8.3",
- "rsa",
- "serde",
- "serde_json",
- "thiserror",
- "zeroize",
-]
-
-[[package]]
-name = "k256"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3a26a4a8e8b0ab315c687767b543c923c9667a1f2bf42a42818d1453891c7c1"
-dependencies = [
- "cfg-if 1.0.0",
- "ecdsa",
- "elliptic-curve",
- "sha2 0.9.5",
-]
-
 [[package]]
 name = "kernel32-sys"
 version = "0.2.2"
@@ -3181,7 +2851,7 @@ checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
 dependencies = [
  "block-buffer 0.9.0",
  "digest 0.9.0",
- "opaque-debug 0.3.0",
+ "opaque-debug",
 ]
 
 [[package]]
@@ -3437,38 +3107,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "oauth2"
-version = "4.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80e47cfc4c0a1a519d9a025ebfbac3a2439d1b5cdf397d72dcb79b11d9920dab"
-dependencies = [
- "base64 0.13.0",
- "chrono",
- "getrandom 0.2.2",
- "http",
- "rand 0.8.3",
- "serde",
- "serde_json",
- "serde_path_to_error",
- "sha2 0.9.5",
- "thiserror",
- "url",
-]
-
-[[package]]
-name = "oauth2-surf"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8a041fdfcfb6aac56f08d021befc1493bde6e6699fef2364e74f0d9adedd27b"
-dependencies = [
- "anyhow",
- "http",
- "oauth2",
- "surf",
- "thiserror",
-]
-
 [[package]]
 name = "objc"
 version = "0.2.7"
@@ -3503,12 +3141,6 @@ version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
 
-[[package]]
-name = "opaque-debug"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
-
 [[package]]
 name = "opaque-debug"
 version = "0.3.0"
@@ -3569,17 +3201,6 @@ dependencies = [
  "workspace",
 ]
 
-[[package]]
-name = "p256"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d053368e1bae4c8a672953397bd1bd7183dde1c72b0b7612a15719173148d186"
-dependencies = [
- "ecdsa",
- "elliptic-curve",
- "sha2 0.9.5",
-]
-
 [[package]]
 name = "parking"
 version = "2.0.0"
@@ -3682,49 +3303,6 @@ version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
-[[package]]
-name = "pest"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
-dependencies = [
- "ucd-trie",
-]
-
-[[package]]
-name = "pest_derive"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
-dependencies = [
- "pest",
- "pest_generator",
-]
-
-[[package]]
-name = "pest_generator"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
-dependencies = [
- "pest",
- "pest_meta",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "pest_meta"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
-dependencies = [
- "maplit",
- "pest",
- "sha-1 0.8.2",
-]
-
 [[package]]
 name = "petgraph"
 version = "0.5.1"
@@ -3794,18 +3372,6 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
-[[package]]
-name = "pkcs8"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09d156817ae0125e8aa5067710b0db24f0984830614f99875a70aa5e3b74db69"
-dependencies = [
- "base64ct",
- "der",
- "spki",
- "zeroize",
-]
-
 [[package]]
 name = "pkg-config"
 version = "0.3.19"
@@ -3864,7 +3430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd"
 dependencies = [
  "cpuid-bool",
- "opaque-debug 0.3.0",
+ "opaque-debug",
  "universal-hash",
 ]
 
@@ -4061,12 +3627,6 @@ dependencies = [
  "prost",
 ]
 
-[[package]]
-name = "quick-error"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
-
 [[package]]
 name = "quote"
 version = "1.0.9"
@@ -4430,20 +3990,7 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
 dependencies = [
- "semver 0.9.0",
-]
-
-[[package]]
-name = "rustls"
-version = "0.18.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81"
-dependencies = [
- "base64 0.12.3",
- "log",
- "ring",
- "sct",
- "webpki",
+ "semver",
 ]
 
 [[package]]
@@ -4623,17 +4170,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
 dependencies = [
- "semver-parser 0.7.0",
-]
-
-[[package]]
-name = "semver"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
-dependencies = [
- "semver-parser 0.10.2",
- "serde",
+ "semver-parser",
 ]
 
 [[package]]
@@ -4642,15 +4179,6 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 
-[[package]]
-name = "semver-parser"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
-dependencies = [
- "pest",
-]
-
 [[package]]
 name = "serde"
 version = "1.0.125"
@@ -4785,18 +4313,6 @@ dependencies = [
  "util",
 ]
 
-[[package]]
-name = "sha-1"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
-dependencies = [
- "block-buffer 0.7.3",
- "digest 0.8.1",
- "fake-simd",
- "opaque-debug 0.2.3",
-]
-
 [[package]]
 name = "sha-1"
 version = "0.9.6"
@@ -4807,7 +4323,7 @@ dependencies = [
  "cfg-if 1.0.0",
  "cpufeatures 0.1.4",
  "digest 0.9.0",
- "opaque-debug 0.3.0",
+ "opaque-debug",
 ]
 
 [[package]]
@@ -4826,7 +4342,7 @@ dependencies = [
  "cfg-if 1.0.0",
  "cpufeatures 0.1.4",
  "digest 0.9.0",
- "opaque-debug 0.3.0",
+ "opaque-debug",
 ]
 
 [[package]]
@@ -4840,12 +4356,6 @@ dependencies = [
  "digest 0.10.3",
 ]
 
-[[package]]
-name = "shell-words"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074"
-
 [[package]]
 name = "shlex"
 version = "1.0.0"
@@ -4871,16 +4381,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "signature"
-version = "1.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19772be3c4dd2ceaacf03cb41d5885f2a02c4d8804884918e3a258480803335"
-dependencies = [
- "digest 0.9.0",
- "rand_core 0.6.2",
-]
-
 [[package]]
 name = "similar"
 version = "1.3.0"
@@ -5029,15 +4529,6 @@ dependencies = [
  "lock_api",
 ]
 
-[[package]]
-name = "spki"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "987637c5ae6b3121aba9d513f869bd2bff11c4cc086c22473befd6649c0bd521"
-dependencies = [
- "der",
-]
-
 [[package]]
 name = "sqlformat"
 version = "0.1.6"
@@ -5051,73 +4542,14 @@ dependencies = [
  "unicode_categories",
 ]
 
-[[package]]
-name = "sqlx"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1a98f9bf17b690f026b6fec565293a995b46dfbd6293debcb654dcffd2d1b34"
-dependencies = [
- "sqlx-core 0.4.2",
- "sqlx-macros 0.4.2",
-]
-
 [[package]]
 name = "sqlx"
 version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ba82f79b31f30acebf19905bcd8b978f46891b9d0723f578447361a8910b6584"
 dependencies = [
- "sqlx-core 0.5.5",
- "sqlx-macros 0.5.5",
-]
-
-[[package]]
-name = "sqlx-core"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36bb6a2ca3345a86493bc3b71eabc2c6c16a8bb1aa476cf5303bee27f67627d7"
-dependencies = [
- "ahash 0.6.3",
- "atoi",
- "base64 0.13.0",
- "bitflags",
- "byteorder",
- "bytes 0.5.6",
- "chrono",
- "crc",
- "crossbeam-channel 0.5.0",
- "crossbeam-queue",
- "crossbeam-utils 0.8.2",
- "either",
- "futures-channel",
- "futures-core",
- "futures-util",
- "hashlink 0.6.0",
- "hex",
- "hmac 0.10.1",
- "itoa 0.4.7",
- "libc",
- "log",
- "md-5",
- "memchr",
- "once_cell",
- "parking_lot",
- "percent-encoding",
- "rand 0.7.3",
- "rustls 0.18.1",
- "serde",
- "serde_json",
- "sha-1 0.9.6",
- "sha2 0.9.5",
- "smallvec",
- "sqlformat",
- "sqlx-rt 0.2.0",
- "stringprep",
- "thiserror",
- "url",
- "webpki",
- "webpki-roots",
- "whoami",
+ "sqlx-core",
+ "sqlx-macros",
 ]
 
 [[package]]

Dockerfile 🔗

@@ -2,18 +2,8 @@
 
 FROM rust:1.58-bullseye as builder
 WORKDIR app
-RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash -
-RUN apt-get install -y nodejs
 COPY . .
 
-# Install script dependencies
-RUN --mount=type=cache,target=./script/node_modules \
-    cd ./script && npm install --quiet
-
-# Build CSS
-RUN --mount=type=cache,target=./script/node_modules \
-    script/build-css --release
-
 # Compile collab server
 RUN --mount=type=cache,target=./script/node_modules \
     --mount=type=cache,target=/usr/local/cargo/registry \

Dockerfile.migrator 🔗

@@ -11,5 +11,5 @@ RUN apt-get update; \
     apt-get install -y --no-install-recommends libssl1.1
 WORKDIR app
 COPY --from=builder /app/bin/sqlx /app
-COPY ./collab/migrations /app/migrations
+COPY ./crates/collab/migrations /app/migrations
 ENTRYPOINT ["/app/sqlx", "migrate", "run"]

crates/collab/.env.template.toml 🔗

@@ -1,11 +1,2 @@
 DATABASE_URL = "postgres://postgres@localhost/zed"
-SESSION_SECRET = "6E1GS6IQNOLIBKWMEVWF1AFO4H78KNU8"
-
 HTTP_PORT = 8080
-
-# Available at https://github.com/organizations/zed-industries/settings/apps/zed-local-development
-GITHUB_APP_ID = 115633
-GITHUB_CLIENT_ID = "Iv1.768076c9becc75c4"
-GITHUB_CLIENT_SECRET = ""
-GITHUB_PRIVATE_KEY = """\
-"""

crates/collab/Cargo.toml 🔗

@@ -22,22 +22,13 @@ async-std = { version = "1.8.0", features = ["attributes"] }
 async-trait = "0.1.50"
 async-tungstenite = "0.16"
 base64 = "0.13"
-clap = "3.1"
-comrak = "0.10"
-either = "1.6"
 envy = "0.4.2"
 futures = "0.3"
-handlebars = "3.5"
-http-auth-basic = "0.1.3"
 json_env_logger = "0.1"
-jwt-simple = "0.10.0"
 lipsum = { version = "0.8", optional = true }
 log = { version = "0.4.16", features = ["kv_unstable_serde"] }
-oauth2 = { version = "4.0.0", default_features = false }
-oauth2-surf = "0.1.1"
 parking_lot = "0.11.1"
 rand = "0.8"
-rust-embed = { version = "6.3", features = ["include-exclude"] }
 scrypt = "0.7"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
@@ -48,11 +39,6 @@ tide-compress = "0.9.0"
 time = "0.2"
 toml = "0.5.8"
 
-[dependencies.async-sqlx-session]
-version = "0.3.0"
-features = ["pg", "rustls"]
-default-features = false
-
 [dependencies.sqlx]
 version = "0.5.2"
 features = ["runtime-async-std-rustls", "postgres", "time", "uuid"]

crates/collab/README.md 🔗

@@ -1,17 +1,5 @@
 # Zed Server
 
-This crate is what we run at https://zed.dev.
+This crate is what we run at https://collab.zed.dev.
 
-It contains our web presence as well as the backend logic for collaboration, to which we connect from the Zed client via a websocket.
-
-## Templates
-
-We use handlebars templates that are interpreted at runtime. When running in debug mode, you can change templates and see the latest content without restarting the server. This is enabled by the `rust-embed` crate, which we use to access the contents of the `/templates` folder at runtime. In debug mode it reads contents from the file system, but in release the templates will be embedded in the server binary.
-
-## Static assets
-
-We also use `rust-embed` to access the contents of the `/static` folder via the `/static/*` route. The app will pick up changes to the contents of this folder when running in debug mode.
-
-## CSS
-
-This site uses Tailwind CSS, which means our stylesheets don't need to change very frequently. We check `static/styles.css` into the repository, but it's actually compiled from `/styles.css` via `script/build-css`. This script runs the Tailwind compilation flow to regenerate `static/styles.css` via PostCSS.
+It contains our back-end logic for collaboration, to which we connect from the Zed client via a websocket after authenticating via https://zed.dev, which is a separate repo running on Vercel.

crates/collab/src/admin.rs 🔗

@@ -1,117 +0,0 @@
-use crate::{auth::RequestExt as _, db, AppState, LayoutData, Request, RequestExt as _};
-use async_trait::async_trait;
-use serde::{Deserialize, Serialize};
-use std::sync::Arc;
-use surf::http::mime;
-
-#[async_trait]
-pub trait RequestExt {
-    async fn require_admin(&self) -> tide::Result<()>;
-}
-
-#[async_trait]
-impl RequestExt for Request {
-    async fn require_admin(&self) -> tide::Result<()> {
-        let current_user = self
-            .current_user()
-            .await?
-            .ok_or_else(|| tide::Error::from_str(401, "not logged in"))?;
-
-        if current_user.is_admin {
-            Ok(())
-        } else {
-            Err(tide::Error::from_str(
-                403,
-                "authenticated user is not an admin",
-            ))
-        }
-    }
-}
-
-pub fn add_routes(app: &mut tide::Server<Arc<AppState>>) {
-    app.at("/admin").get(get_admin_page);
-    app.at("/admin/users").post(post_user);
-    app.at("/admin/users/:id").put(put_user);
-    app.at("/admin/users/:id/delete").post(delete_user);
-    app.at("/admin/signups/:id/delete").post(delete_signup);
-}
-
-#[derive(Serialize)]
-struct AdminData {
-    #[serde(flatten)]
-    layout: Arc<LayoutData>,
-    users: Vec<db::User>,
-    signups: Vec<db::Signup>,
-}
-
-async fn get_admin_page(mut request: Request) -> tide::Result {
-    request.require_admin().await?;
-
-    let data = AdminData {
-        layout: request.layout_data().await?,
-        users: request.db().get_all_users().await?,
-        signups: request.db().get_all_signups().await?,
-    };
-
-    Ok(tide::Response::builder(200)
-        .body(request.state().render_template("admin.hbs", &data)?)
-        .content_type(mime::HTML)
-        .build())
-}
-
-async fn post_user(mut request: Request) -> tide::Result {
-    request.require_admin().await?;
-
-    #[derive(Deserialize)]
-    struct Form {
-        github_login: String,
-        #[serde(default)]
-        admin: bool,
-    }
-
-    let form = request.body_form::<Form>().await?;
-    let github_login = form
-        .github_login
-        .strip_prefix("@")
-        .unwrap_or(&form.github_login);
-
-    if !github_login.is_empty() {
-        request.db().create_user(github_login, form.admin).await?;
-    }
-
-    Ok(tide::Redirect::new("/admin").into())
-}
-
-async fn put_user(mut request: Request) -> tide::Result {
-    request.require_admin().await?;
-
-    let user_id = request.param("id")?.parse()?;
-
-    #[derive(Deserialize)]
-    struct Body {
-        admin: bool,
-    }
-
-    let body: Body = request.body_json().await?;
-
-    request
-        .db()
-        .set_user_is_admin(db::UserId(user_id), body.admin)
-        .await?;
-
-    Ok(tide::Response::builder(200).build())
-}
-
-async fn delete_user(request: Request) -> tide::Result {
-    request.require_admin().await?;
-    let user_id = db::UserId(request.param("id")?.parse()?);
-    request.db().destroy_user(user_id).await?;
-    Ok(tide::Redirect::new("/admin").into())
-}
-
-async fn delete_signup(request: Request) -> tide::Result {
-    request.require_admin().await?;
-    let signup_id = db::SignupId(request.param("id")?.parse()?);
-    request.db().destroy_signup(signup_id).await?;
-    Ok(tide::Redirect::new("/admin").into())
-}

crates/collab/src/assets.rs 🔗

@@ -1,29 +0,0 @@
-use anyhow::anyhow;
-use rust_embed::RustEmbed;
-use tide::{http::mime, Server};
-
-#[derive(RustEmbed)]
-#[folder = "static"]
-struct Static;
-
-pub fn add_routes(app: &mut Server<()>) {
-    app.at("/*path").get(get_static_asset);
-}
-
-async fn get_static_asset(request: tide::Request<()>) -> tide::Result {
-    let path = request.param("path").unwrap();
-    let content = Static::get(path).ok_or_else(|| anyhow!("asset not found at {}", path))?;
-
-    let content_type = if path.starts_with("svg") {
-        mime::SVG
-    } else if path.starts_with("styles") {
-        mime::CSS
-    } else {
-        mime::BYTE_STREAM
-    };
-
-    Ok(tide::Response::builder(200)
-        .content_type(content_type)
-        .body(content.data.as_ref())
-        .build())
-}

crates/collab/src/auth.rs 🔗

@@ -2,36 +2,17 @@ use super::{
     db::{self, UserId},
     errors::TideResultExt,
 };
-use crate::{github, AppState, Request, RequestExt as _};
+use crate::Request;
 use anyhow::{anyhow, Context};
-use async_trait::async_trait;
-pub use oauth2::basic::BasicClient as Client;
-use oauth2::{
-    AuthUrl, AuthorizationCode, ClientId, CsrfToken, PkceCodeChallenge, RedirectUrl,
-    TokenResponse as _, TokenUrl,
-};
 use rand::thread_rng;
 use rpc::auth as zed_auth;
 use scrypt::{
     password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
     Scrypt,
 };
-use serde::{Deserialize, Serialize};
-use std::{borrow::Cow, convert::TryFrom, sync::Arc};
-use surf::{StatusCode, Url};
-use tide::{log, Error, Server};
-
-static CURRENT_GITHUB_USER: &'static str = "current_github_user";
-static GITHUB_AUTH_URL: &'static str = "https://github.com/login/oauth/authorize";
-static GITHUB_TOKEN_URL: &'static str = "https://github.com/login/oauth/access_token";
-
-#[derive(Serialize)]
-pub struct User {
-    pub github_login: String,
-    pub avatar_url: String,
-    pub is_insider: bool,
-    pub is_admin: bool,
-}
+use std::convert::TryFrom;
+use surf::StatusCode;
+use tide::Error;
 
 pub async fn process_auth_header(request: &Request) -> tide::Result<UserId> {
     let mut auth_header = request
@@ -77,194 +58,6 @@ pub async fn process_auth_header(request: &Request) -> tide::Result<UserId> {
     Ok(user_id)
 }
 
-#[async_trait]
-pub trait RequestExt {
-    async fn current_user(&self) -> tide::Result<Option<User>>;
-}
-
-#[async_trait]
-impl RequestExt for Request {
-    async fn current_user(&self) -> tide::Result<Option<User>> {
-        if let Some(details) = self.session().get::<github::User>(CURRENT_GITHUB_USER) {
-            let user = self.db().get_user_by_github_login(&details.login).await?;
-            Ok(Some(User {
-                github_login: details.login,
-                avatar_url: details.avatar_url,
-                is_insider: user.is_some(),
-                is_admin: user.map_or(false, |user| user.admin),
-            }))
-        } else {
-            Ok(None)
-        }
-    }
-}
-
-pub fn build_client(client_id: &str, client_secret: &str) -> Client {
-    Client::new(
-        ClientId::new(client_id.to_string()),
-        Some(oauth2::ClientSecret::new(client_secret.to_string())),
-        AuthUrl::new(GITHUB_AUTH_URL.into()).unwrap(),
-        Some(TokenUrl::new(GITHUB_TOKEN_URL.into()).unwrap()),
-    )
-}
-
-pub fn add_routes(app: &mut Server<Arc<AppState>>) {
-    app.at("/sign_in").get(get_sign_in);
-    app.at("/sign_out").post(post_sign_out);
-    app.at("/auth_callback").get(get_auth_callback);
-    app.at("/native_app_signin").get(get_sign_in);
-    app.at("/native_app_signin_succeeded")
-        .get(get_app_signin_success);
-}
-
-#[derive(Debug, Deserialize)]
-struct NativeAppSignInParams {
-    native_app_port: String,
-    native_app_public_key: String,
-    impersonate: Option<String>,
-}
-
-async fn get_sign_in(mut request: Request) -> tide::Result {
-    let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();
-
-    request
-        .session_mut()
-        .insert("pkce_verifier", pkce_verifier)?;
-
-    let mut redirect_url = Url::parse(&format!(
-        "{}://{}/auth_callback",
-        request
-            .header("X-Forwarded-Proto")
-            .and_then(|values| values.get(0))
-            .map(|value| value.as_str())
-            .unwrap_or("http"),
-        request.host().unwrap()
-    ))?;
-
-    let app_sign_in_params: Option<NativeAppSignInParams> = request.query().ok();
-    if let Some(query) = app_sign_in_params {
-        let mut redirect_query = redirect_url.query_pairs_mut();
-        redirect_query
-            .clear()
-            .append_pair("native_app_port", &query.native_app_port)
-            .append_pair("native_app_public_key", &query.native_app_public_key);
-
-        if let Some(impersonate) = &query.impersonate {
-            redirect_query.append_pair("impersonate", impersonate);
-        }
-    }
-
-    let (auth_url, csrf_token) = request
-        .state()
-        .auth_client
-        .authorize_url(CsrfToken::new_random)
-        .set_redirect_uri(Cow::Owned(RedirectUrl::from_url(redirect_url)))
-        .set_pkce_challenge(pkce_challenge)
-        .url();
-
-    request
-        .session_mut()
-        .insert("auth_csrf_token", csrf_token)?;
-
-    Ok(tide::Redirect::new(auth_url).into())
-}
-
-async fn get_app_signin_success(_: Request) -> tide::Result {
-    Ok(tide::Redirect::new("/").into())
-}
-
-async fn get_auth_callback(mut request: Request) -> tide::Result {
-    #[derive(Debug, Deserialize)]
-    struct Query {
-        code: String,
-        state: String,
-
-        #[serde(flatten)]
-        native_app_sign_in_params: Option<NativeAppSignInParams>,
-    }
-
-    let query: Query = request.query()?;
-
-    let pkce_verifier = request
-        .session()
-        .get("pkce_verifier")
-        .ok_or_else(|| anyhow!("could not retrieve pkce_verifier from session"))?;
-
-    let csrf_token = request
-        .session()
-        .get::<CsrfToken>("auth_csrf_token")
-        .ok_or_else(|| anyhow!("could not retrieve auth_csrf_token from session"))?;
-
-    if &query.state != csrf_token.secret() {
-        return Err(anyhow!("csrf token does not match").into());
-    }
-
-    let github_access_token = request
-        .state()
-        .auth_client
-        .exchange_code(AuthorizationCode::new(query.code))
-        .set_pkce_verifier(pkce_verifier)
-        .request_async(oauth2_surf::http_client)
-        .await
-        .context("failed to exchange oauth code")?
-        .access_token()
-        .secret()
-        .clone();
-
-    let user_details = request
-        .state()
-        .github_client
-        .user(github_access_token)
-        .details()
-        .await
-        .context("failed to fetch user")?;
-
-    let user = request
-        .db()
-        .get_user_by_github_login(&user_details.login)
-        .await?;
-
-    request
-        .session_mut()
-        .insert(CURRENT_GITHUB_USER, user_details.clone())?;
-
-    // When signing in from the native app, generate a new access token for the current user. Return
-    // a redirect so that the user's browser sends this access token to the locally-running app.
-    if let Some((user, app_sign_in_params)) = user.zip(query.native_app_sign_in_params) {
-        let mut user_id = user.id;
-        if let Some(impersonated_login) = app_sign_in_params.impersonate {
-            log::info!("attempting to impersonate user @{}", impersonated_login);
-            if let Some(user) = request.db().get_users_by_ids(vec![user_id]).await?.first() {
-                if user.admin {
-                    user_id = request.db().create_user(&impersonated_login, false).await?;
-                    log::info!("impersonating user {}", user_id.0);
-                } else {
-                    log::info!("refusing to impersonate user");
-                }
-            }
-        }
-
-        let access_token = create_access_token(request.db().as_ref(), user_id).await?;
-        let encrypted_access_token = encrypt_access_token(
-            &access_token,
-            app_sign_in_params.native_app_public_key.clone(),
-        )?;
-
-        return Ok(tide::Redirect::new(&format!(
-            "http://127.0.0.1:{}?user_id={}&access_token={}",
-            app_sign_in_params.native_app_port, user_id.0, encrypted_access_token,
-        ))
-        .into());
-    }
-
-    Ok(tide::Redirect::new("/").into())
-}
-
-async fn post_sign_out(mut request: Request) -> tide::Result {
-    request.session_mut().remove(CURRENT_GITHUB_USER);
-    Ok(tide::Redirect::new("/").into())
-}
-
 const MAX_ACCESS_TOKENS_TO_STORE: usize = 8;
 
 pub async fn create_access_token(db: &dyn db::Db, user_id: UserId) -> tide::Result<String> {

crates/collab/src/careers.rs 🔗

@@ -1,15 +0,0 @@
-use crate::{AppState, Request, RequestExt};
-use std::sync::Arc;
-use tide::http::mime;
-
-pub fn add_routes(app: &mut tide::Server<Arc<AppState>>) {
-    app.at("/careers").get(get_careers);
-}
-
-async fn get_careers(mut request: Request) -> tide::Result {
-    let data = request.layout_data().await?;
-    Ok(tide::Response::builder(200)
-        .body(request.state().render_template("careers.hbs", &data)?)
-        .content_type(mime::HTML)
-        .build())
-}

crates/collab/src/community.rs 🔗

@@ -1,15 +0,0 @@
-use crate::{AppState, Request, RequestExt};
-use std::sync::Arc;
-use tide::http::mime;
-
-pub fn add_routes(community: &mut tide::Server<Arc<AppState>>) {
-    community.at("/community").get(get_community);
-}
-
-async fn get_community(mut request: Request) -> tide::Result {
-    let data = request.layout_data().await?;
-    Ok(tide::Response::builder(200)
-        .body(request.state().render_template("community.hbs", &data)?)
-        .content_type(mime::HTML)
-        .build())
-}

crates/collab/src/db.rs 🔗

@@ -1,6 +1,5 @@
 use anyhow::Context;
 use anyhow::Result;
-pub use async_sqlx_session::PostgresSessionStore as SessionStore;
 use async_std::task::{block_on, yield_now};
 use async_trait::async_trait;
 use serde::Serialize;
@@ -24,17 +23,6 @@ macro_rules! test_support {
 
 #[async_trait]
 pub trait Db: Send + Sync {
-    async fn create_signup(
-        &self,
-        github_login: &str,
-        email_address: &str,
-        about: &str,
-        wants_releases: bool,
-        wants_updates: bool,
-        wants_community: bool,
-    ) -> Result<SignupId>;
-    async fn get_all_signups(&self) -> Result<Vec<Signup>>;
-    async fn destroy_signup(&self, id: SignupId) -> Result<()>;
     async fn create_user(&self, github_login: &str, admin: bool) -> Result<UserId>;
     async fn get_all_users(&self) -> Result<Vec<User>>;
     async fn get_user_by_id(&self, id: UserId) -> Result<Option<User>>;
@@ -108,60 +96,6 @@ impl PostgresDb {
 
 #[async_trait]
 impl Db for PostgresDb {
-    // signups
-    async fn create_signup(
-        &self,
-        github_login: &str,
-        email_address: &str,
-        about: &str,
-        wants_releases: bool,
-        wants_updates: bool,
-        wants_community: bool,
-    ) -> Result<SignupId> {
-        test_support!(self, {
-            let query = "
-                INSERT INTO signups (
-                    github_login,
-                    email_address,
-                    about,
-                    wants_releases,
-                    wants_updates,
-                    wants_community
-                )
-                VALUES ($1, $2, $3, $4, $5, $6)
-                RETURNING id
-            ";
-            Ok(sqlx::query_scalar(query)
-                .bind(github_login)
-                .bind(email_address)
-                .bind(about)
-                .bind(wants_releases)
-                .bind(wants_updates)
-                .bind(wants_community)
-                .fetch_one(&self.pool)
-                .await
-                .map(SignupId)?)
-        })
-    }
-
-    async fn get_all_signups(&self) -> Result<Vec<Signup>> {
-        test_support!(self, {
-            let query = "SELECT * FROM signups ORDER BY github_login ASC";
-            Ok(sqlx::query_as(query).fetch_all(&self.pool).await?)
-        })
-    }
-
-    async fn destroy_signup(&self, id: SignupId) -> Result<()> {
-        test_support!(self, {
-            let query = "DELETE FROM signups WHERE id = $1";
-            Ok(sqlx::query(query)
-                .bind(id.0)
-                .execute(&self.pool)
-                .await
-                .map(drop)?)
-        })
-    }
-
     // users
 
     async fn create_user(&self, github_login: &str, admin: bool) -> Result<UserId> {
@@ -578,18 +512,6 @@ pub struct Org {
     pub slug: String,
 }
 
-id_type!(SignupId);
-#[derive(Debug, FromRow, Serialize)]
-pub struct Signup {
-    pub id: SignupId,
-    pub github_login: String,
-    pub email_address: String,
-    pub about: String,
-    pub wants_releases: Option<bool>,
-    pub wants_updates: Option<bool>,
-    pub wants_community: Option<bool>,
-}
-
 id_type!(ChannelId);
 #[derive(Clone, Debug, FromRow, Serialize)]
 pub struct Channel {
@@ -854,26 +776,6 @@ pub mod tests {
 
     #[async_trait]
     impl Db for FakeDb {
-        async fn create_signup(
-            &self,
-            _github_login: &str,
-            _email_address: &str,
-            _about: &str,
-            _wants_releases: bool,
-            _wants_updates: bool,
-            _wants_community: bool,
-        ) -> Result<SignupId> {
-            unimplemented!()
-        }
-
-        async fn get_all_signups(&self) -> Result<Vec<Signup>> {
-            unimplemented!()
-        }
-
-        async fn destroy_signup(&self, _id: SignupId) -> Result<()> {
-            unimplemented!()
-        }
-
         async fn create_user(&self, github_login: &str, admin: bool) -> Result<UserId> {
             self.background.simulate_random_delay().await;
 

crates/collab/src/errors.rs 🔗

@@ -1,47 +1,3 @@
-use crate::{AppState, LayoutData, Request, RequestExt};
-use async_trait::async_trait;
-use serde::Serialize;
-use std::sync::Arc;
-use tide::http::mime;
-
-pub struct Middleware;
-
-#[async_trait]
-impl tide::Middleware<Arc<AppState>> for Middleware {
-    async fn handle(
-        &self,
-        mut request: Request,
-        next: tide::Next<'_, Arc<AppState>>,
-    ) -> tide::Result {
-        let app = request.state().clone();
-        let layout_data = request.layout_data().await?;
-
-        let mut response = next.run(request).await;
-
-        #[derive(Serialize)]
-        struct ErrorData {
-            #[serde(flatten)]
-            layout: Arc<LayoutData>,
-            status: u16,
-            reason: &'static str,
-        }
-
-        if !response.status().is_success() {
-            response.set_body(app.render_template(
-                "error.hbs",
-                &ErrorData {
-                    layout: layout_data,
-                    status: response.status().into(),
-                    reason: response.status().canonical_reason(),
-                },
-            )?);
-            response.set_content_type(mime::HTML);
-        }
-
-        Ok(response)
-    }
-}
-
 // Allow tide Results to accept context like other Results do when
 // using anyhow.
 pub trait TideResultExt {

crates/collab/src/expiring.rs 🔗

@@ -1,43 +0,0 @@
-use std::{future::Future, time::Instant};
-
-use async_std::sync::Mutex;
-
-#[derive(Default)]
-pub struct Expiring<T>(Mutex<Option<ExpiringState<T>>>);
-
-pub struct ExpiringState<T> {
-    value: T,
-    expires_at: Instant,
-}
-
-impl<T: Clone> Expiring<T> {
-    pub async fn get_or_refresh<F, G>(&self, f: F) -> tide::Result<T>
-    where
-        F: FnOnce() -> G,
-        G: Future<Output = tide::Result<(T, Instant)>>,
-    {
-        let mut state = self.0.lock().await;
-
-        if let Some(state) = state.as_mut() {
-            if Instant::now() >= state.expires_at {
-                let (value, expires_at) = f().await?;
-                state.value = value.clone();
-                state.expires_at = expires_at;
-                Ok(value)
-            } else {
-                Ok(state.value.clone())
-            }
-        } else {
-            let (value, expires_at) = f().await?;
-            *state = Some(ExpiringState {
-                value: value.clone(),
-                expires_at,
-            });
-            Ok(value)
-        }
-    }
-
-    pub async fn clear(&self) {
-        self.0.lock().await.take();
-    }
-}

crates/collab/src/github.rs 🔗

@@ -1,281 +0,0 @@
-use crate::expiring::Expiring;
-use anyhow::{anyhow, Context};
-use serde::{de::DeserializeOwned, Deserialize, Serialize};
-use std::{
-    future::Future,
-    sync::Arc,
-    time::{Duration, Instant},
-};
-use surf::{http::Method, RequestBuilder, Url};
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Release {
-    pub tag_name: String,
-    pub name: String,
-    pub body: String,
-    pub draft: bool,
-    pub assets: Vec<Asset>,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Asset {
-    pub name: String,
-    pub url: String,
-}
-
-pub struct AppClient {
-    id: usize,
-    private_key: String,
-    jwt_bearer_header: Expiring<String>,
-}
-
-#[derive(Deserialize)]
-struct Installation {
-    #[allow(unused)]
-    id: usize,
-}
-
-impl AppClient {
-    #[cfg(test)]
-    pub fn test() -> Arc<Self> {
-        Arc::new(Self {
-            id: Default::default(),
-            private_key: Default::default(),
-            jwt_bearer_header: Default::default(),
-        })
-    }
-
-    pub fn new(id: usize, private_key: String) -> Arc<Self> {
-        Arc::new(Self {
-            id,
-            private_key,
-            jwt_bearer_header: Default::default(),
-        })
-    }
-
-    pub async fn repo(self: &Arc<Self>, nwo: String) -> tide::Result<RepoClient> {
-        let installation: Installation = self
-            .request(
-                Method::Get,
-                &format!("/repos/{}/installation", &nwo),
-                |refresh| self.bearer_header(refresh),
-            )
-            .await?;
-
-        Ok(RepoClient {
-            app: self.clone(),
-            nwo,
-            installation_id: installation.id,
-            installation_token_header: Default::default(),
-        })
-    }
-
-    pub fn user(self: &Arc<Self>, access_token: String) -> UserClient {
-        UserClient {
-            app: self.clone(),
-            access_token,
-        }
-    }
-
-    async fn request<T, F, G>(
-        &self,
-        method: Method,
-        path: &str,
-        get_auth_header: F,
-    ) -> tide::Result<T>
-    where
-        T: DeserializeOwned,
-        F: Fn(bool) -> G,
-        G: Future<Output = tide::Result<String>>,
-    {
-        let mut retried = false;
-
-        loop {
-            let response = RequestBuilder::new(
-                method,
-                Url::parse(&format!("https://api.github.com{}", path))?,
-            )
-            .header("Accept", "application/vnd.github.v3+json")
-            .header("Authorization", get_auth_header(retried).await?)
-            .recv_json()
-            .await;
-
-            if let Err(error) = response.as_ref() {
-                if error.status() == 401 && !retried {
-                    retried = true;
-                    continue;
-                }
-            }
-
-            return response;
-        }
-    }
-
-    async fn bearer_header(&self, refresh: bool) -> tide::Result<String> {
-        if refresh {
-            self.jwt_bearer_header.clear().await;
-        }
-
-        self.jwt_bearer_header
-            .get_or_refresh(|| async {
-                use jwt_simple::{algorithms::RS256KeyPair, prelude::*};
-                use std::time;
-
-                let key_pair = RS256KeyPair::from_pem(&self.private_key)
-                    .with_context(|| format!("invalid private key {:?}", self.private_key))?;
-                let mut claims = Claims::create(Duration::from_mins(10));
-                claims.issued_at = Some(Clock::now_since_epoch() - Duration::from_mins(1));
-                claims.issuer = Some(self.id.to_string());
-                let token = key_pair.sign(claims).context("failed to sign claims")?;
-                let expires_at = time::Instant::now() + time::Duration::from_secs(9 * 60);
-
-                Ok((format!("Bearer {}", token), expires_at))
-            })
-            .await
-    }
-
-    async fn installation_token_header(
-        &self,
-        header: &Expiring<String>,
-        installation_id: usize,
-        refresh: bool,
-    ) -> tide::Result<String> {
-        if refresh {
-            header.clear().await;
-        }
-
-        header
-            .get_or_refresh(|| async {
-                #[derive(Debug, Deserialize)]
-                struct AccessToken {
-                    token: String,
-                }
-
-                let access_token: AccessToken = self
-                    .request(
-                        Method::Post,
-                        &format!("/app/installations/{}/access_tokens", installation_id),
-                        |refresh| self.bearer_header(refresh),
-                    )
-                    .await?;
-
-                let header = format!("Token {}", access_token.token);
-                let expires_at = Instant::now() + Duration::from_secs(60 * 30);
-
-                Ok((header, expires_at))
-            })
-            .await
-    }
-}
-
-pub struct RepoClient {
-    app: Arc<AppClient>,
-    nwo: String,
-    installation_id: usize,
-    installation_token_header: Expiring<String>,
-}
-
-impl RepoClient {
-    #[cfg(test)]
-    pub fn test(app_client: &Arc<AppClient>) -> Self {
-        Self {
-            app: app_client.clone(),
-            nwo: String::new(),
-            installation_id: 0,
-            installation_token_header: Default::default(),
-        }
-    }
-
-    pub async fn releases(&self) -> tide::Result<Vec<Release>> {
-        self.get(&format!("/repos/{}/releases?per_page=100", self.nwo))
-            .await
-    }
-
-    pub async fn release_asset(&self, tag: &str, name: &str) -> tide::Result<surf::Body> {
-        let release: Release = self
-            .get(&format!("/repos/{}/releases/tags/{}", self.nwo, tag))
-            .await?;
-
-        let asset = release
-            .assets
-            .iter()
-            .find(|asset| asset.name == name)
-            .ok_or_else(|| anyhow!("no asset found with name {}", name))?;
-
-        let request = surf::get(&asset.url)
-            .header("Accept", "application/octet-stream'")
-            .header(
-                "Authorization",
-                self.installation_token_header(false).await?,
-            );
-
-        let client = surf::client();
-        let mut response = client.send(request).await?;
-
-        // Avoid using `surf::middleware::Redirect` because that type forwards
-        // the original request headers to the redirect URI. In this case, the
-        // redirect will be to S3, which forbids us from supplying an
-        // `Authorization` header.
-        if response.status().is_redirection() {
-            if let Some(url) = response.header("location") {
-                let request = surf::get(url.as_str()).header("Accept", "application/octet-stream");
-                response = client.send(request).await?;
-            }
-        }
-
-        if !response.status().is_success() {
-            Err(anyhow!("failed to fetch release asset {} {}", tag, name))?;
-        }
-
-        Ok(response.take_body())
-    }
-
-    async fn get<T: DeserializeOwned>(&self, path: &str) -> tide::Result<T> {
-        self.request::<T>(Method::Get, path).await
-    }
-
-    async fn request<T: DeserializeOwned>(&self, method: Method, path: &str) -> tide::Result<T> {
-        Ok(self
-            .app
-            .request(method, path, |refresh| {
-                self.installation_token_header(refresh)
-            })
-            .await?)
-    }
-
-    async fn installation_token_header(&self, refresh: bool) -> tide::Result<String> {
-        self.app
-            .installation_token_header(
-                &self.installation_token_header,
-                self.installation_id,
-                refresh,
-            )
-            .await
-    }
-}
-
-pub struct UserClient {
-    app: Arc<AppClient>,
-    access_token: String,
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct User {
-    pub login: String,
-    pub avatar_url: String,
-}
-
-impl UserClient {
-    pub async fn details(&self) -> tide::Result<User> {
-        Ok(self
-            .app
-            .request(Method::Get, "/user", |_| async {
-                Ok(self.access_token_header())
-            })
-            .await?)
-    }
-
-    fn access_token_header(&self) -> String {
-        format!("Token {}", self.access_token)
-    }
-}

crates/collab/src/home.rs 🔗

@@ -1,80 +0,0 @@
-use crate::{AppState, Request, RequestExt as _};
-use log::as_serde;
-use serde::{Deserialize, Serialize};
-use std::sync::Arc;
-use tide::{http::mime, Server};
-
-pub fn add_routes(app: &mut Server<Arc<AppState>>) {
-    app.at("/").get(get_home);
-    app.at("/signups").post(post_signup);
-    app.at("/releases/:tag_name/:name").get(get_release_asset);
-}
-
-async fn get_home(mut request: Request) -> tide::Result {
-    let data = request.layout_data().await?;
-    Ok(tide::Response::builder(200)
-        .body(request.state().render_template("home.hbs", &data)?)
-        .content_type(mime::HTML)
-        .build())
-}
-
-async fn post_signup(mut request: Request) -> tide::Result {
-    #[derive(Debug, Deserialize, Serialize)]
-    struct Form {
-        github_login: String,
-        email_address: String,
-        about: String,
-        #[serde(default)]
-        wants_releases: bool,
-        #[serde(default)]
-        wants_updates: bool,
-        #[serde(default)]
-        wants_community: bool,
-    }
-
-    let mut form: Form = request.body_form().await?;
-    form.github_login = form
-        .github_login
-        .strip_prefix("@")
-        .map(str::to_string)
-        .unwrap_or(form.github_login);
-
-    log::info!(form = as_serde!(form); "signup submitted");
-
-    // Save signup in the database
-    request
-        .db()
-        .create_signup(
-            &form.github_login,
-            &form.email_address,
-            &form.about,
-            form.wants_releases,
-            form.wants_updates,
-            form.wants_community,
-        )
-        .await?;
-
-    let layout_data = request.layout_data().await?;
-    Ok(tide::Response::builder(200)
-        .body(
-            request
-                .state()
-                .render_template("signup.hbs", &layout_data)?,
-        )
-        .content_type(mime::HTML)
-        .build())
-}
-
-async fn get_release_asset(request: Request) -> tide::Result {
-    let body = request
-        .state()
-        .repo_client
-        .release_asset(request.param("tag_name")?, request.param("name")?)
-        .await?;
-
-    Ok(tide::Response::builder(200)
-        .header("Cache-Control", "no-transform")
-        .content_type(mime::BYTE_STREAM)
-        .body(body)
-        .build())
-}

crates/collab/src/main.rs 🔗

@@ -1,141 +1,56 @@
-mod admin;
 mod api;
-mod assets;
 mod auth;
-mod careers;
-mod community;
 mod db;
 mod env;
 mod errors;
-mod expiring;
-mod github;
-mod home;
-mod releases;
 mod rpc;
-mod team;
 
-use self::errors::TideResultExt as _;
 use ::rpc::Peer;
-use anyhow::Result;
 use async_std::net::TcpListener;
 use async_trait::async_trait;
-use auth::RequestExt as _;
 use db::{Db, PostgresDb};
-use handlebars::{Handlebars, TemplateRenderError};
-use parking_lot::RwLock;
-use rust_embed::RustEmbed;
-use serde::{Deserialize, Serialize};
+use serde::Deserialize;
 use std::sync::Arc;
-use surf::http::cookies::SameSite;
-use tide::sessions::SessionMiddleware;
 use tide_compress::CompressMiddleware;
 
 type Request = tide::Request<Arc<AppState>>;
 
-#[derive(RustEmbed)]
-#[folder = "templates"]
-struct Templates;
-
 #[derive(Default, Deserialize)]
 pub struct Config {
     pub http_port: u16,
     pub database_url: String,
-    pub session_secret: String,
-    pub github_app_id: usize,
-    pub github_client_id: String,
-    pub github_client_secret: String,
-    pub github_private_key: String,
     pub api_token: String,
 }
 
 pub struct AppState {
     db: Arc<dyn Db>,
-    handlebars: RwLock<Handlebars<'static>>,
-    auth_client: auth::Client,
-    github_client: Arc<github::AppClient>,
-    repo_client: github::RepoClient,
     config: Config,
 }
 
 impl AppState {
     async fn new(config: Config) -> tide::Result<Arc<Self>> {
         let db = PostgresDb::new(&config.database_url, 5).await?;
-        let github_client =
-            github::AppClient::new(config.github_app_id, config.github_private_key.clone());
-        let repo_client = github_client
-            .repo("zed-industries/zed".into())
-            .await
-            .context("failed to initialize github client")?;
 
         let this = Self {
             db: Arc::new(db),
-            handlebars: Default::default(),
-            auth_client: auth::build_client(&config.github_client_id, &config.github_client_secret),
-            github_client,
-            repo_client,
             config,
         };
-        this.register_partials();
         Ok(Arc::new(this))
     }
-
-    fn register_partials(&self) {
-        for path in Templates::iter() {
-            if let Some(partial_name) = path
-                .strip_prefix("partials/")
-                .and_then(|path| path.strip_suffix(".hbs"))
-            {
-                let partial = Templates::get(path.as_ref()).unwrap();
-                self.handlebars
-                    .write()
-                    .register_partial(partial_name, std::str::from_utf8(&partial.data).unwrap())
-                    .unwrap()
-            }
-        }
-    }
-
-    fn render_template(
-        &self,
-        path: &'static str,
-        data: &impl Serialize,
-    ) -> Result<String, TemplateRenderError> {
-        #[cfg(debug_assertions)]
-        self.register_partials();
-
-        self.handlebars.read().render_template(
-            std::str::from_utf8(&Templates::get(path).unwrap().data).unwrap(),
-            data,
-        )
-    }
 }
 
 #[async_trait]
 trait RequestExt {
-    async fn layout_data(&mut self) -> tide::Result<Arc<LayoutData>>;
     fn db(&self) -> &Arc<dyn Db>;
 }
 
 #[async_trait]
 impl RequestExt for Request {
-    async fn layout_data(&mut self) -> tide::Result<Arc<LayoutData>> {
-        if self.ext::<Arc<LayoutData>>().is_none() {
-            self.set_ext(Arc::new(LayoutData {
-                current_user: self.current_user().await?,
-            }));
-        }
-        Ok(self.ext::<Arc<LayoutData>>().unwrap().clone())
-    }
-
     fn db(&self) -> &Arc<dyn Db> {
         &self.state().db
     }
 }
 
-#[derive(Serialize)]
-struct LayoutData {
-    current_user: Option<auth::User>,
-}
-
 #[async_std::main]
 async fn main() -> tide::Result<()> {
     if std::env::var("LOG_JSON").is_ok() {
@@ -168,36 +83,14 @@ pub async fn run_server(
     rpc: Arc<Peer>,
     listener: TcpListener,
 ) -> tide::Result<()> {
+    let mut app = tide::with_state(state.clone());
+    rpc::add_routes(&mut app, &rpc);
+
     let mut web = tide::with_state(state.clone());
     web.with(CompressMiddleware::new());
-    web.with(
-        SessionMiddleware::new(
-            db::SessionStore::new_with_table_name(&state.config.database_url, "sessions")
-                .await
-                .unwrap(),
-            state.config.session_secret.as_bytes(),
-        )
-        .with_same_site_policy(SameSite::Lax), // Required obtain our session in /auth_callback
-    );
-    web.with(errors::Middleware);
     api::add_routes(&mut web);
-    home::add_routes(&mut web);
-    team::add_routes(&mut web);
-    careers::add_routes(&mut web);
-    releases::add_routes(&mut web);
-    community::add_routes(&mut web);
-    admin::add_routes(&mut web);
-    auth::add_routes(&mut web);
-
-    let mut assets = tide::new();
-    assets.with(CompressMiddleware::new());
-    assets::add_routes(&mut assets);
-
-    let mut app = tide::with_state(state.clone());
-    rpc::add_routes(&mut app, &rpc);
 
     app.at("/").nest(web);
-    app.at("/static").nest(assets);
 
     app.listen(listener).await?;
 

crates/collab/src/releases.rs 🔗

@@ -1,54 +0,0 @@
-use crate::{
-    auth::RequestExt as _, github::Release, AppState, LayoutData, Request, RequestExt as _,
-};
-use comrak::ComrakOptions;
-use serde::Serialize;
-use std::sync::Arc;
-use tide::http::mime;
-
-pub fn add_routes(releases: &mut tide::Server<Arc<AppState>>) {
-    releases.at("/releases").get(get_releases);
-}
-
-async fn get_releases(mut request: Request) -> tide::Result {
-    #[derive(Serialize)]
-    struct ReleasesData {
-        #[serde(flatten)]
-        layout: Arc<LayoutData>,
-        releases: Option<Vec<Release>>,
-    }
-
-    let mut data = ReleasesData {
-        layout: request.layout_data().await?,
-        releases: None,
-    };
-
-    if let Some(user) = request.current_user().await? {
-        if user.is_insider {
-            data.releases = Some(
-                request
-                    .state()
-                    .repo_client
-                    .releases()
-                    .await?
-                    .into_iter()
-                    .filter_map(|mut release| {
-                        if release.draft {
-                            None
-                        } else {
-                            let mut options = ComrakOptions::default();
-                            options.render.unsafe_ = true; // Allow raw HTML in the markup. We control these release notes anyway.
-                            release.body = comrak::markdown_to_html(&release.body, &options);
-                            Some(release)
-                        }
-                    })
-                    .collect(),
-            );
-        }
-    }
-
-    Ok(tide::Response::builder(200)
-        .body(request.state().render_template("releases.hbs", &data)?)
-        .content_type(mime::HTML)
-        .build())
-}

crates/collab/src/rpc.rs 🔗

@@ -1180,9 +1180,8 @@ fn header_contains_ignore_case<T>(
 mod tests {
     use super::*;
     use crate::{
-        auth,
         db::{tests::TestDb, UserId},
-        github, AppState, Config,
+        AppState, Config,
     };
     use ::rpc::Peer;
     use client::{
@@ -5729,15 +5728,9 @@ mod tests {
 
         async fn build_app_state(test_db: &TestDb) -> Arc<AppState> {
             let mut config = Config::default();
-            config.session_secret = "a".repeat(32);
             config.database_url = test_db.url.clone();
-            let github_client = github::AppClient::test();
             Arc::new(AppState {
                 db: test_db.db().clone(),
-                handlebars: Default::default(),
-                auth_client: auth::build_client("", ""),
-                repo_client: github::RepoClient::test(&github_client),
-                github_client,
                 config,
             })
         }

crates/collab/src/team.rs 🔗

@@ -1,15 +0,0 @@
-use crate::{AppState, Request, RequestExt};
-use std::sync::Arc;
-use tide::http::mime;
-
-pub fn add_routes(app: &mut tide::Server<Arc<AppState>>) {
-    app.at("/team").get(get_team);
-}
-
-async fn get_team(mut request: Request) -> tide::Result {
-    let data = request.layout_data().await?;
-    Ok(tide::Response::builder(200)
-        .body(request.state().render_template("team.hbs", &data)?)
-        .content_type(mime::HTML)
-        .build())
-}

crates/collab/static/browserconfig.xml 🔗

@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<browserconfig>
-    <msapplication>
-        <tile>
-            <square150x150logo src="/static/images/mstile-150x150.png"/>
-            <TileColor>#000000</TileColor>
-        </tile>
-    </msapplication>
-</browserconfig>

crates/collab/static/images/favicon.svg 🔗

@@ -1,14 +0,0 @@
-<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
-  <style>
-		path {
-        fill: #000000;
-      }
-      @media ( prefers-color-scheme: dark ) {
-        path {
-          fill: #FFFFFF;
-        }
-      }
-	</style>
-  <path fill-rule="evenodd" clip-rule="evenodd" d="M256 0C114.615 0 0 114.615 0 256C0 397.385 114.615 512 256 512C397.385 512 512 397.385 512 256C512 114.615 397.385 0 256 0ZM256 64C149.961 64 64 149.961 64 256C64 362.039 149.961 448 256 448C362.039 448 448 362.039 448 256C448 149.961 362.039 64 256 64Z"/>
-  <path fill-rule="evenodd" clip-rule="evenodd" d="M160 160L376 160L238 304H304L352 352H136L274 208H208L160 160Z"/>
-</svg>

crates/collab/static/images/safari-pinned-tab.svg 🔗

@@ -1,19 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
- "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
-<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
- width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
- preserveAspectRatio="xMidYMid meet">
-  <style>
-		path {
-        fill: #000000;
-      }
-      @media ( prefers-color-scheme: dark ) {
-        path {
-          fill: #FFFFFF;
-        }
-      }
-	</style>
-  <path fill-rule="evenodd" clip-rule="evenodd" d="M256 0C114.615 0 0 114.615 0 256C0 397.385 114.615 512 256 512C397.385 512 512 397.385 512 256C512 114.615 397.385 0 256 0ZM256 64C149.961 64 64 149.961 64 256C64 362.039 149.961 448 256 448C362.039 448 448 362.039 448 256C448 149.961 362.039 64 256 64Z"/>
-  <path fill-rule="evenodd" clip-rule="evenodd" d="M160 160L376 160L238 304H304L352 352H136L274 208H208L160 160Z"/>
-</svg>

crates/collab/static/prism.js 🔗

@@ -1,12 +0,0 @@
-/* PrismJS 1.25.0
-https://prismjs.com/download.html#themes=prism-twilight&languages=markup+css+clike+javascript+bash+c+cpp+rust+scss */

crates/collab/static/prose.css 🔗

@@ -1,253 +0,0 @@
-article.prose {
-  margin-bottom: 2.5rem;
-}
-
-article.prose,
-.type-prose {
-  font-family: "Spectral", "Constantia", "Lucida Bright", "Lucidabright", "Lucida Serif", "Lucida", "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", "Georgia", "serif", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", serif;
-  letter-spacing: -0.05rem;
-}
-
-article.prose h1,
-article.prose h2,
-article.prose h3,
-article.prose h4,
-.type-prose h1,
-.type-prose h2,
-.type-prose h3,
-.type-prose h4 {
-  margin: 3rem 0 1rem 0;
-}
-
-article.prose h1,
-.type-prose h1 {
-  font-size: 2.25rem;
-  line-height: 2.5rem;
-}
-
-article.prose h2,
-.type-prose h2 {
-  font-size: 1.875rem;
-  line-height: 2.25rem;
-}
-
-article.prose h3,
-.type-prose h3 {
-  font-size: 1.6rem;
-  line-height: 2rem;
-}
-
-article.prose h4,
-.type-prose h4 {
-  font-size: 1.4rem;
-  line-height: 1.75rem;
-}
-
-article.prose p,
-article.prose li,
-article.prose a,
-.type-prose p,
-.type-prose li,
-.type-prose a {
-  color: #eee;
-  font-size: 1.3rem;
-  line-height: 2.1rem;
-}
-
-article.prose a:not(img),
-.type-prose a:not(img) {
-  text-decoration: underline;
-  text-underline-offset: 4px;
-}
-
-article.prose strong,
-.type-prose strong {
-  font-weight: 600;
-}
-
-article.prose i,
-.type-prose i {
-  font-style: italic;
-}
-
-article.prose p:not(:last-of-type),
-.type-prose p:not(:last-of-type) {
-  margin-bottom: 1.5rem;
-}
-
-article.prose img,
-article.prose pre,
-.type-prose img,
-.type-prose pre {
-  margin: 1.5rem 0;
-}
-
-article.prose ul,
-.type-prose ul {
-  margin-left: 1.5rem;
-}
-
-article.prose ul li,
-.type-prose ul li {
-  list-style-type: disc;
-  list-style-position: outside;
-}
-
-article.prose ul li:not(:last-of-type),
-.type-prose ul li:not(:last-of-type) {
-  margin-bottom: 0.2rem;
-}
-
-article.prose code,
-.type-prose code {
-  font-family: "JetBrains Mono", "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", monospace;
-  font-size: 0.96rem;
-  letter-spacing: 0rem;
-}
-
-article.prose :not(pre) > code,
-.type-prose :not(pre) > code {
-  padding: 0.2rem 0.4rem;
-}
-
-article.prose pre,
-.type-prose pre {
-  padding: 0.8rem;
-}
-
-article.prose pre,
-article.prose :not(pre) > code,
-.type-prose pre,
-.type-prose :not(pre) > code {
-  border-radius: 4px;
-  background-color: rgba(255, 255, 255, 0.1);
-}
-
-/* Code Highlighting Styles
-/* Based on PrismJS 1.25.0
-https://prismjs.com/download.html#themes=prism-twilight&languages=markup+css+clike+javascript+bash+c+cpp+rust+scss */
-code[class*="language-"],
-pre[class*="language-"] {
-  color: #ddd;
-  text-align: left;
-  white-space: pre;
-  word-spacing: normal;
-  word-break: normal;
-  word-wrap: normal;
-  -moz-tab-size: 4;
-  -o-tab-size: 4;
-  tab-size: 4;
-  -webkit-hyphens: none;
-  -ms-hyphens: none;
-  hyphens: none;
-}
-
-/* Code blocks */
-pre[class*="language-"]::-moz-selection {
-  /* Firefox */
-  background: #3b57bc33;
-}
-
-pre[class*="language-"]::selection {
-  /* Safari */
-  background: #3b57bc33;
-}
-
-/* Text Selection colour */
-pre[class*="language-"]::-moz-selection,
-pre[class*="language-"] ::-moz-selection,
-code[class*="language-"]::-moz-selection,
-code[class*="language-"] ::-moz-selection {
-  text-shadow: none;
-  background: #3b57bc33;
-}
-
-pre[class*="language-"]::selection,
-pre[class*="language-"] ::selection,
-code[class*="language-"]::selection,
-code[class*="language-"] ::selection {
-  text-shadow: none;
-  background: #3b57bc33;
-}
-
-.token.comment,
-.token.prolog,
-.token.doctype,
-.token.cdata {
-  color: #9cdcfe;
-}
-
-.token.punctuation {
-  opacity: 0.7;
-}
-
-.token.namespace {
-  opacity: 0.7;
-}
-
-.token.tag,
-.token.boolean,
-.token.number,
-.token.deleted {
-  color: #b5cea8;
-}
-
-.token.keyword,
-.token.property,
-.token.selector,
-.token.constant,
-.token.symbol,
-.token.builtin {
-  color: #0086c0;
-  /* #F9EE98 */
-}
-
-.token.attr-name,
-.token.attr-value,
-.token.string,
-.token.char,
-.token.operator,
-.token.entity,
-.token.url,
-.language-css .token.string,
-.style .token.string,
-.token.variable,
-.token.inserted {
-  color: #4e94ce;
-}
-
-.token.atrule {
-  color: #4ec9b0;
-}
-
-.token.regex,
-.token.important {
-  color: #dcdcaa;
-}
-
-.token.important,
-.token.bold {
-  font-weight: bold;
-}
-
-.token.italic {
-  font-style: italic;
-}
-
-.token.entity {
-  cursor: help;
-}
-
-/* Markup */
-.language-markup .token.tag,
-.language-markup .token.attr-name,
-.language-markup .token.punctuation {
-  color: #4e94ce;
-}
-
-/* Make the tokens sit above the line highlight so the colours don't look faded. */
-.token {
-  position: relative;
-  z-index: 1;
-}
-/*# sourceMappingURL=prose.css.map */

crates/collab/static/prose.scss 🔗

@@ -1,260 +0,0 @@
-// Style prose by hand
-// add .prose to any <article> to activate prose styles
-// or .type-prose to any element
-
-article.prose {
-  margin-bottom: 2.5rem;
-}
-
-article.prose,
-.type-prose {
-  font-family: "Spectral", "Constantia", "Lucida Bright", "Lucidabright",
-    "Lucida Serif", "Lucida", "DejaVu Serif", "Bitstream Vera Serif",
-    "Liberation Serif", "Georgia", "serif", "Apple Color Emoji",
-    "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", serif;
-  letter-spacing: -0.05rem;
-
-  h1,
-  h2,
-  h3,
-  h4 {
-    margin: 3rem 0 1rem 0;
-  }
-
-  h1 {
-    font-size: 2.25rem;
-    line-height: 2.5rem;
-  }
-
-  h2 {
-    font-size: 1.875rem;
-    line-height: 2.25rem;
-  }
-
-  h3 {
-    font-size: 1.6rem;
-    line-height: 2rem;
-  }
-
-  h4 {
-    font-size: 1.4rem;
-    line-height: 1.75rem;
-  }
-
-  p,
-  li,
-  a {
-    color: #eee;
-    font-size: 1.3rem;
-    line-height: 2.1rem;
-  }
-
-  a:not(img) {
-    text-decoration: underline;
-    text-underline-offset: 4px;
-  }
-
-  strong {
-    font-weight: 600;
-  }
-
-  i {
-    font-style: italic;
-  }
-
-  p:not(:last-of-type) {
-    margin-bottom: 1.5rem;
-  }
-
-  img,
-  pre {
-    margin: 1.5rem 0;
-  }
-
-  ul {
-    margin-left: 1.5rem;
-  }
-
-  ul li {
-    list-style-type: disc;
-    list-style-position: outside;
-    &:not(:last-of-type) {
-      margin-bottom: 0.2rem;
-    }
-  }
-
-  code {
-    font-family: "JetBrains Mono", "Andale Mono WT", "Andale Mono",
-      "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono",
-      "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L",
-      "Courier New", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
-      "Noto Color Emoji", monospace;
-    font-size: 0.96rem;
-    letter-spacing: 0rem;
-  }
-
-  :not(pre) > code {
-    padding: 0.2rem 0.4rem;
-  }
-
-  pre {
-    padding: 0.8rem;
-  }
-
-  pre,
-  :not(pre) > code {
-    border-radius: 4px;
-    background-color: rgba(255, 255, 255, 0.1);
-  }
-}
-
-/* Code Highlighting Styles
-/* Based on PrismJS 1.25.0
-https://prismjs.com/download.html#themes=prism-twilight&languages=markup+css+clike+javascript+bash+c+cpp+rust+scss */
-
-code[class*="language-"],
-pre[class*="language-"] {
-  color: #ddd;
-  text-align: left;
-  white-space: pre;
-  word-spacing: normal;
-  word-break: normal;
-  word-wrap: normal;
-
-  -moz-tab-size: 4;
-  -o-tab-size: 4;
-  tab-size: 4;
-
-  -webkit-hyphens: none;
-  -moz-hyphens: none;
-  -ms-hyphens: none;
-  hyphens: none;
-}
-
-/* Code blocks */
-pre[class*="language-"] {
-  // Language specific code block styles
-}
-
-pre[class*="language-"]::-moz-selection {
-  /* Firefox */
-  background: #3b57bc33;
-}
-
-pre[class*="language-"]::selection {
-  /* Safari */
-  background: #3b57bc33;
-}
-
-/* Text Selection colour */
-pre[class*="language-"]::-moz-selection,
-pre[class*="language-"] ::-moz-selection,
-code[class*="language-"]::-moz-selection,
-code[class*="language-"] ::-moz-selection {
-  text-shadow: none;
-  background: #3b57bc33;
-}
-
-pre[class*="language-"]::selection,
-pre[class*="language-"] ::selection,
-code[class*="language-"]::selection,
-code[class*="language-"] ::selection {
-  text-shadow: none;
-  background: #3b57bc33;
-}
-
-.token.comment,
-.token.prolog,
-.token.doctype,
-.token.cdata {
-  color: #9cdcfe;
-}
-
-.token.punctuation {
-  opacity: 0.7;
-}
-
-.token.namespace {
-  opacity: 0.7;
-}
-
-.token.tag,
-.token.boolean,
-.token.number,
-.token.deleted {
-  color: #b5cea8;
-}
-
-.token.keyword,
-.token.property,
-.token.selector,
-.token.constant,
-.token.symbol,
-.token.builtin {
-  color: #0086c0; /* #F9EE98 */
-}
-
-.token.attr-name,
-.token.attr-value,
-.token.string,
-.token.char,
-.token.operator,
-.token.entity,
-.token.url,
-.language-css .token.string,
-.style .token.string,
-.token.variable,
-.token.inserted {
-  color: #4e94ce;
-}
-
-.token.atrule {
-  color: #4ec9b0;
-}
-
-.token.regex,
-.token.important {
-  color: #dcdcaa;
-}
-
-.token.important,
-.token.bold {
-  font-weight: bold;
-}
-.token.italic {
-  font-style: italic;
-}
-
-.token.entity {
-  cursor: help;
-}
-
-/* Markup */
-.language-markup .token.tag,
-.language-markup .token.attr-name,
-.language-markup .token.punctuation {
-  color: #4e94ce;
-}
-
-/* Make the tokens sit above the line highlight so the colours don't look faded. */
-.token {
-  position: relative;
-  z-index: 1;
-}
-
-// TODO: Style line highlights
-
-// .line-highlight.line-highlight {
-// 	background: hsla(0, 0%, 33%, 0.25); /* #545454 */
-// 	background: linear-gradient(to right, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */
-// 	border-bottom: 1px dashed hsl(0, 0%, 33%); /* #545454 */
-// 	border-top: 1px dashed hsl(0, 0%, 33%); /* #545454 */
-// 	margin-top: 0.75em; /* Same as .prism’s padding-top */
-// 	z-index: 0;
-// }
-
-// .line-highlight.line-highlight:before,
-// .line-highlight.line-highlight[data-end]:after {
-// 	background-color: hsl(215, 15%, 59%); /* #8794A6 */
-// 	color: hsl(24, 20%, 95%); /* #F5F2F0 */
-// }

crates/collab/static/site.webmanifest 🔗

@@ -1,18 +0,0 @@
-{
-    "name": "Zed – a lightning fast, collaborative code editor written natively in Rust",
-    "short_name": "Zed",
-    "icons": [
-        {
-            "src": "/static/android-chrome-192x192.png",
-            "sizes": "192x192",
-            "type": "image/png"
-        },
-        {
-            "src": "/static/android-chrome-512x512.png",
-            "sizes": "512x512",
-            "type": "image/png"
-        }
-    ],
-    "theme_color": "#000",
-    "background_color": "#000"
-}

crates/collab/static/svg/hero.svg 🔗

@@ -1,6 +0,0 @@
-<svg width="1147" height="310" viewBox="0 0 1147 310" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M4.34388 233H166.2V186.632H82.1039L169.368 28.52H7.79988V74.888H91.6079L4.34388 233ZM233.094 186.632V152.936H291.27V107.144H233.094V74.888H303.078V28.52H182.406V233H305.382V186.632H233.094ZM323.108 233H406.916C460.484 233 504.548 190.376 504.548 130.76C504.548 71.144 460.484 28.52 406.916 28.52H323.108V233ZM373.796 185.192V76.328H405.188C436.292 76.328 451.556 99.08 451.556 130.76C451.556 162.44 436.292 185.192 405.188 185.192H373.796Z" fill="black"/>
-<rect x="372" y="222" width="773" height="86" rx="2" fill="black" stroke="black" stroke-width="4"/>
-<rect x="351" y="205" width="773" height="85" rx="2" fill="#F9FAFB" stroke="black" stroke-width="4"/>

crates/collab/styles.css 🔗

@@ -1,136 +0,0 @@
-/* This file is compiled to /assets/styles/tailwind.css via script/tailwind */
-
-@import url("https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,200;0,300;1,200;1,300&display=swap");
-@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100;0,300;0,600;1,100;1,300;1,600&display=swap");
-
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-@layer base {
-  html {
-    font-size: 110%;
-    text-rendering: geometricPrecision;
-  }
-
-  @media screen and (min-width: 1024px) {
-    html {
-      font-size: 125%;
-    }
-  }
-
-  h1 {
-    @apply text-4xl;
-    @apply tracking-tighter;
-  }
-  h2 {
-    @apply text-3xl;
-    @apply tracking-tighter;
-  }
-  h3 {
-    @apply text-2xl;
-    @apply tracking-tighter;
-  }
-  h4 {
-    @apply text-xl;
-  }
-
-  .underline,
-  .hover\:underline:hover {
-    text-underline-offset: 6px;
-  }
-
-  @media screen and (min-width: 1024px) {
-    .text-columns-2 {
-        column-count: 2;
-        column-gap: 3rem;
-    }
-    
-}
-  .site-nav a:not(.active) .nav-active-arrow {
-    display: none;
-  }
-
-  .site-nav a.active .nav-active-arrow {
-    display: flex;
-  }
-
-  .site-mobile-nav a.active {
-    font-weight: bold;
-  }
-
-  .text-columns-2 {
-    -webkit-column-break-inside: avoid;
-    page-break-inside: avoid;
-    break-inside: avoid;
-  }
-
-  .text-columns-2 p {
-    break-inside: avoid;
-  }
-
-  .alternate-bg:nth-of-type(even) {
-    background-color: rgba(255,255,255,0.06);
-  }
-
-  /* div:not(.type-prose) code {
-    background-color: rgba(255,255,255,0.15);
-    padding: 0px 4px;
-    border-radius: 2px;
-    color: #eee;
-  } */
-  
-  /* This fixes scrollbar jump */
-  @media screen and (min-width: 960px) {
-    html {
-      margin-left: calc(100vw - 100%);
-      margin-right: 0;
-    }
-  }
-
-  :root {
-    --color: white;
-    --disabled: #959495;
-  }
-  
-  .checkbox {
-    color: var(--color);
-  }
-
-  .checkbox--disabled {
-    color: var(--disabled);
-  }
-  
-  .checkbox__control {
-    width: 30px;
-    height: 30px;
-  }
-
-  .checkbox__control svg {
-    transform: scale(0);
-    transform-origin: center center;
-  }
-  
-  .checkbox__brackets {
-    left: -1px;
-    top: -1px;
-  }
-  
-  .checkbox__input input {
-    opacity: 0;
-    width: 30px;
-    height: 30px;
-  }
-
-  .checkbox__input input:focus + .checkbox__control {
-    box-shadow: 0 0 0 2px #000, 0 0 0 4px #3b57bc99
-  }
-
-  .checkbox__input input:checked + .checkbox__control svg {
-    transform: scale(1);
-  }
-
-  .checkbox__input input:disabled + .checkbox__control {
-    color: var(--disabled);
-  }
-}

crates/collab/templates/admin.hbs 🔗

@@ -1,105 +0,0 @@
-{{#> layout }}
-<script>
-    window.addEventListener("DOMContentLoaded", function () {
-        let users = document.getElementById("users");
-        if (users) {
-            users.addEventListener("change", async function (event) {
-                const action = event.target.getAttribute("action");
-                if (action) {
-                    console.log(action, event.target.checked);
-                    const response = await fetch(action, {
-                        method: 'PUT',
-                        headers: {
-                            'Content-Type': 'application/json'
-                        },
-                        body: JSON.stringify({ admin: event.target.checked })
-                    });
-                }
-            });
-        }
-    });
-</script>
-
-<div class="h-screen text-white border-r border-white text-main font-extralight">
-    <div class="flex flex-row items-center justify-between p-5 border-b border-white pl-7 pr-7 admin-nav">
-        <h1 class="font-display font-extralight">Admin</h1>
-        <ul class="flex flex-row">
-            <li><a href="#userlist"
-                    class="mr-4 leading-relaxed no-underline lowercase text-main hover:underline">Users</a></li>
-            <li><a href="#signuplist"
-                    class="leading-relaxed no-underline lowercase text-main hover:underline">Signups</a></li>
-        </ul>
-    </div>
-
-    <h2 id="userlist" class="pt-10 mb-5 text-white pl-7 pr-7 font-display font-extralight">Users</h2>
-
-    <div class="flex flex-col w-full pb-5 font-mono text-xs" id="users">
-        <div class="flex flex-row pl-5 pr-10 font-bold">
-            <p class="w-1/3 p-2">Github Username</p>
-            <p class="w-32 p-2 text-center">Admin</p>
-            <p class="w-24 p-2"> </p>
-        </div>
-        <div class="flex flex-col pl-5 pr-10 text-gray-100">
-            <form action="/admin/users" method="post" class="m-0">
-                <div class="flex flex-row items-center">
-                    <p class="w-1/3 p-2"><input class="block w-full p-2 text-xs bg-transparent border border-white"
-                            type="text" name="github_login" required minlength="4" placeholder="@github_handle"></p>
-                    <p class="w-32 p-2 text-center"><input type="checkbox" id="admin" name="admin" value="true"></p>
-                    <p class="w-24 p-2"><button class="underline hover:no-underline">Add</button></p>
-                </div>
-            </form>
-        </div>
-    </div>
-
-    <div class="flex flex-col w-full pb-10 font-mono text-xs border-b border-white">
-        <div class="flex flex-row pl-5 pr-10 font-bold">
-            <p class="w-1/3 p-2">Github Username</p>
-            <p class="w-32 p-2 text-center">Admin</p>
-            <p class="w-24 p-2"> </p>
-        </div>
-        {{#each users}}
-        <div class="flex flex-col pl-5 pr-10 text-gray-100 alternate-bg">
-            <form action="/admin/users/{{id}}/delete" method="post" class="m-0">
-                <div class="flex flex-row items-center">
-                    <p class="w-1/3 p-2">{{github_login}}</p>
-                    <p class="w-32 p-2 text-center"><input action="/admin/users/{{id}}" type="checkbox" {{#if
-                            admin}}checked{{/if}}></p>
-                    <p class="w-24 p-2"><button class="underline hover:no-underline">Remove</button></p>
-                </div>
-            </form>
-        </div>
-        {{/each}}
-    </div>
-
-    <h2 class="pt-10 mb-5 text-white pl-7 pr-7 font-display font-extralight">Signups</h2>
-
-    <div class="flex flex-col w-full pb-10 font-mono text-xs border-b border-white">
-
-        <div class="flex flex-row justify-between pl-5 pr-10 font-bold">
-            <p class="w-1/5 p-2">Email</p>
-            <p class="w-1/5 p-2">Github</p>
-            <p class="w-24 p-2 text-center">Releases</p>
-            <p class="w-24 p-2 text-center">Updates</p>
-            <p class="w-24 p-2 text-center">Community</p>
-            <p class="w-24 p-2 text-right">Remove</p>
-        </div>
-        {{#each signups}}
-        <div class="flex flex-col pb-1 pl-5 pr-10 text-gray-100 alternate-bg">
-            <form action="/admin/signups/{{id}}/delete" method="post" class="m-0">
-                <div class="flex flex-row items-center justify-between">
-                    <p class="w-1/5 p-2 pb-1">{{email_address}}</p>
-                    <p class="w-1/5 p-2 pb-1">{{github_login}}</p>
-                    <p class="w-24 p-2 pb-1 text-center">{{#if wants_releases}}[✓]{{else}}[ ]{{/if}}</p>
-                    <p class="w-24 p-2 pb-1 text-center">{{#if wants_updates}}[✓]{{else}}[ ]{{/if}}</p>
-                    <p class="w-24 p-2 pb-1 text-center">{{#if wants_community}}[✓]{{else}}[ ]{{/if}}</p>
-                    <p class="w-24 p-2 pb-1 text-right"><button
-                            class="text-lg text-gray-500 hover:text-white">×</button></p>
-                </div>
-            </form>
-            <p class="max-w-full p-2 pt-0 overflow-hidden leading-normal text-gray-400 max-h-5 whitespace-nowrap overflow-ellipsis"
-                title="{{about}}">{{about}}</p>
-        </div>
-        {{/each}}
-    </div>
-</div>
-{{/layout}}

crates/collab/templates/careers.hbs 🔗

@@ -1,20 +0,0 @@
-{{#> layout }}
-
-<div class="max-w-screen-lg p-5 mx-auto font-extralight text-main lg:p-20">
-    <h1 class="mb-10 text-4xl text-white font-display font-extralight">
-        We think there's a better way to write code&mdash;and we've been working for more than a decade to bring it into existence.
-    </h1>
-    <p class="mt-5 leading-relaxed">Zed is looking for a Rust engineer to help us advance the art of code editing. We're currently three engineers and a designer developing a new editor with a focus on performance, clean design, and collaboration.</p> 
-    <p class="mt-5 leading-relaxed">We're working on exciting technologies, including a custom, GPU-accelerated UI framework, heavy use of parallelism and persistent data structures, conflict-free replicated data types, and an incremental parsing framework called Tree-sitter that was created by one of our founders.</p>
-    <p class="mt-5 leading-relaxed">Our codebase consists of about 60,000 lines of well-factored, thoroughly-tested Rust that compiles quickly. In addition to the Zed editor, we're also developing a server-side component to facilitate collaboration that tightly integrates with the client.</p>
-    <p class="mt-10 leading-relaxed">We'd love to hear from you if you're excited to build the world's best code editor and meet this description:</p>
-    <ul class="mt-10 mb-16 leading-relaxed">
-        <li class="mt-5 leading-relaxed">You're experienced in Rust. You've developed a substantial piece of software or meaningfully contributed to an open source project.</li>
-        <li class="mt-5 leading-relaxed">You'd be excited to to pair program with us multiple times a week to learn the codebase.</li>
-        <li class="mt-5 leading-relaxed">You know how to maintain code quality while being pragmatic and prioritizing the needs of the customer.</li>
-        <li class="mt-5 leading-relaxed">You know how to write performant software, including algorithmic analysis and profile-driven optimization.</li>
-        <li class="mt-5 leading-relaxed">Bonus: You have experience working on code editors, compilers, distributed systems, CRDTs, or real-time graphics.</li>
-    </ul>
-</div>
-
-{{/layout}}

crates/collab/templates/community.hbs 🔗

@@ -1,107 +0,0 @@
-{{#> layout }}
-
-<div class="max-w-screen-lg p-5 mx-auto font-extralight text-main lg:p-20">  
-    <h1 class="mb-10 font-display font-extralight">We’re building a community of passionate developers &amp; advocates.</h1>
-
-    <p class="mt-5 leading-relaxed">If you would like to get involved early, sign up below and we will let you know when our developer community opens!</p>
-
-    <form action="/signups" method="post" class="max-w-screen-md mt-10">
-        <ul>
-            <li class="flex flex-row mt-5 text-lg">
-                <span class="relative w-6 mt-2 mr-5 checkbox__input">
-                    <span class="absolute z-0 block checkbox__brackets">
-                        <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" focusable="false">
-                        <path d="M8 0H0V32H8V31H1V1H8V0Z" fill="white"/>
-                        <path d="M24 31H31V1H24V0H32V32H24V31Z" fill="white"/>
-                        </svg>
-                    </span>
-                    <input class="absolute z-10" type="checkbox" value=true name="wants_releases">
-                    <span class="flex items-center justify-center checkbox__control">
-                        <svg width="12" height="11" viewBox="0 0 12 11" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
-                            <path d="M1 6.38905L4.33333 9.72239L11 0.833496" fill='none' stroke='currentColor' stroke-width="1" stroke-linecap="round"/>
-                        </svg>
-                    </span>
-                </span>
-                    
-                <div class="flex-1 mt-1">
-                    <label for="wants_releases">
-                        <span class="radio__label">Let me know when there is a beta release I can try.</span>
-                    </label>
-                    <p class="text-sm leading-6 text-gray-300">
-                        We'll put you on the list to play with an early beta and let you know when Zed launches.
-                    </p>
-                </div>
-            </li>
-            <li class="flex flex-row mt-5 text-lg">
-                <span class="relative w-6 mt-2 mr-5 checkbox__input">
-                    <span class="absolute z-0 block checkbox__brackets">
-                        <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" focusable="false">
-                        <path d="M8 0H0V32H8V31H1V1H8V0Z" fill="white"/>
-                        <path d="M24 31H31V1H24V0H32V32H24V31Z" fill="white"/>
-                        </svg>
-                    </span>
-
-                    <input class="absolute z-10" type="checkbox" value=true name="wants_updates">
-                    <span class="flex items-center justify-center checkbox__control">
-                        <svg width="12" height="11" viewBox="0 0 12 11" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
-                            <path d="M1 6.38905L4.33333 9.72239L11 0.833496" fill='none' stroke='currentColor' stroke-width="1" stroke-linecap="round"/>
-                        </svg>
-                    </span>
-                </span>
-                    
-                <div class="flex-1 mt-1">
-                    <label for="wants_updates">
-                        <span class="radio__label">I’m interested in following Zed's development</span>
-                    </label>
-                    <p class="text-sm leading-6 text-gray-300">
-                        We'll send you occasional updates about our progress and share what we're learning along the way.
-                    </p>
-                </div>
-            </li>
-            <li class="flex flex-row mt-5 text-lg">
-                <span class="relative w-6 mt-2 mr-5 checkbox__input">
-                    <span class="absolute z-0 block checkbox__brackets">
-                        <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" focusable="false">
-                        <path d="M8 0H0V32H8V31H1V1H8V0Z" fill="white"/>
-                        <path d="M24 31H31V1H24V0H32V32H24V31Z" fill="white"/>
-                        </svg>
-                    </span>
-
-                    <input class="absolute z-10" type="checkbox" value=true name="wants_community">
-                    
-                    <span class="flex items-center justify-center checkbox__control">
-                        <svg width="12" height="11" viewBox="0 0 12 11" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
-                            <path d="M1 6.38905L4.33333 9.72239L11 0.833496" fill='none' stroke='currentColor' stroke-width="1" stroke-linecap="round"/>
-                        </svg>
-                    </span>
-                </span>
-                    
-                <div class="flex-1 mt-1">
-                    <label for="wants_community">
-                        <span class="radio__label">I want to test early releases & join the Zed community</span>
-                    </label>
-                    <p class="text-sm leading-6 text-gray-300">
-						We'll add you to the waitlist to receive early builds and ask you to help us by testing them out.
-                    </p>
-                </div>
-            </li>
-        </ul>
-        <div class="lg:flex lg:flex-row lg:mt-10">
-            <input class="block w-full p-5 mt-10 bg-transparent border border-white font-extralight lg:mt-0 lg:mr-5" type="text" id="form-email" name="email_address" required minlength="4" placeholder="Your email">
-
-            <input class="block w-full p-5 mt-5 bg-transparent border border-white font-extralight lg:mt-0" type="text" id="form-gh" name="github_login" placeholder="Github Username">
-        </div>
-        
-        <textarea class="block w-full h-40 p-5 mt-5 leading-relaxed bg-transparent border border-white font-extralight" type="text" name="about" placeholder="Tell us a bit about yourself and your interest in Zed. &#10;What does your dream code-editing experience look like? &#10;What do you love or hate about your current editor?"></textarea>
-
-        <button class="block mt-10 text-lg opacity-100 font-extralight hover:opacity-80">
-            Join the waitlist &rarr;
-        </button>
-
-        <p class="mt-10 text-sm leading-relaxed text-gray-500">
-            We're not gonna spam you&mdash;You can expect an email every 1 to 3 months from us if you sign up for updates, and occasional emails about beta releases.
-        </p>
-    </form>
-</div>
-
-{{/layout}}

crates/collab/templates/docs.hbs 🔗

@@ -1,41 +0,0 @@
-{{#> layout }}
-
-<div class="bg-white">
-    <div class="container mx-auto py-12 px-8 md:px-12">
-        <h1 class="text-4xl font-black font-display mb-8">Bypassing code signing restrictions</h1>
-        <div class="lg:flex lg:flex-row items-start">
-            <div class="prose xl:prose-xl lg:mr-12">
-                <p>
-                    We haven't yet applied to Apple for the required certificate to sign our application bundle, which
-                    means there's a small speed bump when you run our app.
-                </p>
-                <p>
-                    Instead of double-clicking the app, right click it and choose Open.
-                </p>
-                <p>
-                    You need to attempt open the app <b>twice</b>. On the second attempt, you should see the option
-                    to open the application anyway in the dialog.
-                </p>
-            </div>
-            <img class="float-1 lg:w-1/3 object-contain mt-8 lg:mt-0" alt="Screen Shot 2021-06-02 at 2 38 12 PM"
-                src="https://user-images.githubusercontent.com/1789/120550754-86514480-c3b2-11eb-8995-32f5eea79664.png">
-            <img class="float-1 lg:w-1/3 object-contain -ml-10 lg:ml-0 lg:-mt-10"
-                alt="Screen Shot 2021-06-02 at 2 38 19 PM"
-                src="https://user-images.githubusercontent.com/1789/120550759-88b39e80-c3b2-11eb-88e2-ddfc1b1c7a03.png">
-        </div>
-
-        <h1 class="text-4xl font-black font-display my-8">Key bindings</h1>
-        <div class="prose">
-            <dl>
-                <dt>
-                    <pre>cmd-shift-L</pre>
-                </dt>
-                <dd>
-                    Split selection into lines
-                </dd>
-            </dl>
-        </div>
-    </div>
-</div>
-
-{{/layout}}

crates/collab/templates/error.hbs 🔗

@@ -1,15 +0,0 @@
-{{#> layout }}
-
-<div class="max-w-screen-lg p-10 mx-auto text-main font-extralight lg:p-20">
-    <article class="max-w-xl">
-        <h1 class="mb-5 text-4xl text-white font-display font-extralight">Sorry!</h1>
-        <p class="mb-5 leading-relaxed">
-            Looks like we encountered a {{status}} error: {{reason}}
-        </p>
-        <p class="leading-relaxed">
-            Try refreshing or going <a class="underline hover:no-underline" href="/">home</a>.
-        </p>
-    </article>
-</div>
-
-{{/layout}}

crates/collab/templates/home.hbs 🔗

@@ -1,110 +0,0 @@
-{{#> layout }}
-
-<div class="max-w-screen-lg p-5 mx-auto text-gray-200 font-extralight text-main lg:p-20">
-    <article class="">
-        <h1 class="mb-10 text-4xl leading-tight text-white font-display font-extralight">Introducing Zed&mdash;A lightning-fast, collaborative code editor written in Rust.</h1>
-        <p class="mt-5 leading-relaxed">
-            We think there's a better way to write code, and it starts with the following assumptions:
-        </p>
-
-        <h3 class="mt-10 leading-tight text-white font-display font-extralight">Mission-critical tools should be hyper-responsive.</h3>
-
-        <p class="mt-3 leading-relaxed">
-            When you move the cursor or type a character, you should see pixels on the next refresh of your display&mdash;every time. Even sub-perceptual pauses add up over the course of a day to create unnecessary stress.
-        </p>
-
-        <p class="mt-5 leading-relaxed">
-            Joyful coding starts with an editor that stays out of your way.
-        </p>
-        
-        <h3 class="mt-10 leading-tight text-white font-display font-extralight">Real-time collaboration produces better software.</h3>
-
-        <p class="mt-3 leading-relaxed">
-            Pull requests have their place, but sometimes you just want to code together. It's more fun than code review, builds trust between colleagues, and it's one of the best ways to distribute knowledge through a team. But it has to be easy.
-        </p>
-            
-        <p class="mt-5 leading-relaxed">
-            If you want someone's perspective on a piece of code, it should be as easy as sending a message to pull them into your working copy and start coding.
-        </p>
-
-        <h3 class="mt-10 leading-tight text-white font-display font-extralight">Conversations about software should happen close to the code.</h3>
-
-        <p class="mt-3 leading-relaxed">
-            If you want to talk about code on GitHub you have to commit and push it first, and pasting code into a chat application sucks.
-        </p>
-
-        <p class="mt-5 leading-relaxed">
-           By treating keystrokes as the fundamental unit of change and integrating chat into the editor, we can make it easy to have a conversation about any line of code in any working copy–whether it was committed last year or just typed ten seconds ago.
-        </p>
-
-        <h3 class="mt-10 leading-tight text-white font-display font-extralight">Our goal is to make you as efficient as possible.</h3>
-
-        <p class="mt-3 leading-relaxed">
-            If you're living in a tool for hours every day, you want it to disappear. Every pixel must carry its weight, and the software you're creating should always be the focus. So we constantly ask ourselves how we can maximize signal and minimize noise.
-        </p>
-        
-        <p class="mt-5 leading-relaxed">
-            Do we need an icon? Do we need a gradient? We all want to achieve mastery. Our goal is to find the most efficient way to accomplish each coding task and make it accessible. We will never dumb things down.
-        </p>
-
-        <p class="mt-10 mb-10 leading-relaxed">&mdash;&mdash;&mdash;</p>
-
-        <p class="mt-5 leading-relaxed">
-            We're looking to add another engineer to the team. Want to help us build the future? <a href="/careers">Join us</a>.
-        </p>
-
-        <p class="mt-10 mb-10 leading-relaxed">&mdash;&mdash;&mdash;</p>
-
-        <h2 class="mt-16 mb-5 leading-tight text-white font-display font-extralight">Under the hood</h2>
-
-        <article class="leading-relaxed">
-            <h3 class="mt-10 leading-tight text-white font-display font-extralight">Building in Rust</h3>
-            <p class="mt-3">Rust offers expressivity and developer productivity rivaling languages that depend on a VM, while simultaneously offering the control required to fully utilize the underlying hardware.</p>
-                
-            <p class="mt-5">Rust's unique ownership model is a challenge at the beginning, but once you internalize it, you can write extremely efficient multi-core code without fear of invoking undefined behavior.</p>
-            
-            <p class="mt-5">It also makes it straightforward to call directly into the C-based APIs of the operating system. Rust makes it possible for a small team to build a complex product quickly, and Zed wouldn't have been possible without it.</p>
-            
-            <p class="mt-5">In the past, to write software with this performant you would need to use C++. Rust, for the first time, enables us to write software at that level as a very small team.</p>
-        </article>
-
-        <article class="leading-relaxed">
-            <h3 class="mt-10 mb-4 leading-tight text-white font-display font-extralight">A new GPU-powered UI framework</h3>
-            <p class="mt-3">We originally planned to use Electron as a convenient means of delivering a cross-platform GUI for Zed, while building the core of the application in Rust. But at every turn, we found that web technology was the bottleneck in achieving amazing performance. Finally, we decided to take full control and simply build a GPU-powered UI framework that met our needs.</p>
-                
-            <p class="mt-5"></p>We call it GPUI.</p>
-            
-            <p class="mt-5">We took a lot of inspiration from Mozilla's Webrender project. The key insight was that modern graphics hardware can render complex 3D graphics at high frame rates, so why not use it to render relatively simple 2D user interfaces with an immediate mode architecture? </p>
-            
-            <p class="mt-5">Rust's ownership model required us to rethink much of what we learned in other UI programming paradigms, but the result is a framework that's productive to use and remarkably easy to reason about.</p>
-                
-            <p class="mt-5">It's liberating to control every pixel, and it's a rush to push those pixels at lightning speed.</p>
-        </article>
-
-        <article class="leading-relaxed">
-            <h3 class="mt-10 mb-4 leading-tight text-white font-display font-extralight">Conflict-free replicated data types</h3>
-            
-            <p class="mt-3">Real-time collaborative editing presents the illusion that multiple people are editing the same buffer. In reality, each collaborator maintains their own personal replica of the buffer to which they can apply local edits immediately without network latency.</p>
-            
-            <p class="mt-5">After being applied locally, edits are transmitted to collaborators over the network, whose copies may have also changed in the meantime. This means that as participants edit together, their replicas continuously diverge and reconverge. Turns out this is a tricky problem.</p>
-                
-            <p class="mt-5">To solve it, we're using conflict-free replicated data types, which have emerged in the last decade as a general framework for achieving eventual consistency in a variety of circumstances.</p>
-                
-            <p class="mt-5">Making Zed's buffers CRDTs allows for collaborative editing, but it also helps us reason about concurrent change so that we can push work into background threads and keep the UI thread responsive.</p>
-        </article>
-
-        <article class="leading-relaxed">
-            <h3 class="mt-10 mb-4 leading-tight text-white font-display font-extralight">Tree-sitter</h3>
-            
-            <p class="mt-3">We plan to integrate with the Language Server Protocol to support advanced IDE features, but we also think it's important for a code editor to have a rich, native understanding of syntax.</p>
-                
-            <p class="mt-5">That's why we built Tree-sitter, a fast, general, incremental parsing library that can provide Zed with syntax trees for over 50 languages. Tree-sitter already powers production functionality on GitHub, and we'll use it to deliver syntactically-precise syntax highlighting, tree-based selection and cursor navigation, robust auto-indent support, symbolic navigation, and more.</p>
-        </article>
-
-        <p class="mt-10 mb-10 leading-relaxed">&mdash;&mdash;&mdash;</p>
-
-        <p class="mt-5 leading-relaxed">Excited about what we are building? <a class="underline hover:no-underline" href="/community">Sign up for updates</a> to follow along in our development process.</p>
-    </article>
-</div>
-
-{{/layout}}

crates/collab/templates/partials/layout.hbs 🔗

@@ -1,199 +0,0 @@
-<html>
-
-<head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=0.86, maximum-scale=5.0, minimum-scale=0.86">
-    
-    <link rel="mask-icon" href="/static/images/favicon.svg" color="#000000">
-    <link rel="alternate icon" type="image/png" href="/static/images/favicon.png">
-    <link rel="shortcut icon" href="/static/images/favicon.svg">
-    <link rel="apple-touch-icon" sizes="180x180" href="/static/images/apple-touch-icon.png">
-    <link rel="manifest" href="/static/site.webmanifest">
-
-    <link rel="stylesheet" href="/static/styles.css">
-    <link rel="stylesheet" href="/static/prose.css">
-
-    <link rel="mask-icon" href="/static/images/safari-pinned-tab.svg" color="#000000">
-    <meta name="msapplication-TileColor" content="#000000">
-    <meta name="msapplication-config" content="/static/browserconfig.xml">
-    <meta name="theme-color" content="#000">
-
-    <!-- Open Graph stuff -->
-    <meta name="twitter:card" content="summary" />
-    <meta property="twitter:image" content="/static/images/zed-twitter-image.png" />
-    <meta name="twitter:image:alt" content="An image of Zed's logo, sharp Z inside a circle, in white on a black background." />
-    <meta property="og:url" content="https://zed.dev/" />
-    <meta property="og:title" content="Introducing Zed" />
-    <meta property="og:description" content="A lightning-fast, collaborative code editor written in Rust." />
-    <meta property="og:image" content="/static/images/zed-og-image.png" />
-    <meta property="og:type" content="website" />
-
-    <title>Zed</title>
-
-</head>
-
-<body class="box-border font-light bg-black font-body text-main">
-    <main class="container flex flex-col mx-auto lg:bg-white lg:flex-none lg:grid lg:min-h-full lg:grid-cols-4 max-w-screen-2xl lg:gap-x-px text-gray-50">
-        <nav id="mobile-nav" class="p-5 pt-10 pb-5 bg-black font-extralight lg:hidden site-mobile-nav">
-            <div class="flex flex-row justify-between">
-                <a href="/" class="block hover:opacity-80">
-                    <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
-                    <path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0ZM16 4C9.37258 4 4 9.37258 4 16C4 22.6274 9.37258 28 16 28C22.6274 28 28 22.6274 28 16C28 9.37258 22.6274 4 16 4Z" fill="white"/>
-                    <path fill-rule="evenodd" clip-rule="evenodd" d="M10 10L23.5 10L14.875 19H19L22 22H8.5L17.125 13H13L10 10Z" fill="white"/>
-                    </svg>
-                </a>
-                <div class="flex flex-row">
-                    {{#if current_user}}
-
-                    <form id="sign_out" action="/sign_out" method="post" class="inline m-0 text-gray-500 font-extralight">
-                        <button class="text-lg leading-relaxed √ no-underline font-extralight hover:underline">Log Out</button>
-                    </form>
-
-                    {{else}}
-
-                    <!-- Otherwise let user log in -->
-                    <a href=" /sign_in"
-                        class="text-lg leading-relaxed text-gray-500">
-                        Log in
-                    </a>
-                    {{/if}}
-                </div>
-            </div>
-            
-            <div class="flex flex-row flex-wrap mt-10">
-                <a href="/" class="mr-2 text-lg leading-relaxed">
-                    Meet Zed <span class="pt-1 mr-1 text-lg text-gray-500">·</span>
-                </a>
-                <a href="/team" class="mr-2 text-lg leading-relaxed">
-                    The Team <span class="pt-1 mr-1 text-lg text-gray-500">·</span>
-                </a>
-                
-                <a href="/community" class="mr-2 text-lg leading-relaxed">
-                    Waitlist
-                </a>
-                {{#if current_user}}
-                <a href="/releases" class="mr-2 text-lg leading-relaxed">
-                    <span class="pt-1 mr-1 text-lg text-gray-500">·</span> Releases
-                </a>
-
-                {{#if current_user.is_admin }}
-                    <a href="/admin" class="inline-block mr-2 text-lg leading-relaxed">
-                        <span class="pt-1 mr-1 text-lg text-gray-500">·</span> Manage Users
-                    </a>
-                {{/if}}
-                {{/if}}
-            </div>
-
-            <!-- If user is logged in AND -->
-            <!-- If user is at least an insider -->
-            {{#if current_user}}
-
-            <!-- AND If user is an admin -->
-            
-
-            {{/if}}
-        </nav>
-
-        <nav id="nav" class="hidden p-10 pt-20 bg-black lg:flex lg:flex-col font-extralight site-nav">
-            <a href="/" class="block hover:opacity-80">
-                <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
-                <path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0ZM16 4C9.37258 4 4 9.37258 4 16C4 22.6274 9.37258 28 16 28C22.6274 28 28 22.6274 28 16C28 9.37258 22.6274 4 16 4Z" fill="white"/>
-                <path fill-rule="evenodd" clip-rule="evenodd" d="M10 10L23.5 10L14.875 19H19L22 22H8.5L17.125 13H13L10 10Z" fill="white"/>
-                </svg>
-            </a>
-            <div class="flex flex-col mt-10 font-mono">
-                <a href="/" class="relative mt-2 text-base no-underline lowercase hover:underline">
-                    <span class="absolute items-center justify-center hidden h-full align-middle nav-active-arrow -left-7">
-                        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.1594 12.1606C10.0309 12.0567 9.96803 11.8899 9.96803 11.7204C9.96803 11.5509 10.0321 11.3846 10.1603 11.2564L12.7596 8.65813H1.6562C1.29392 8.65813 1 8.36284 1 7.97732C1 7.63829 1.29392 7.34573 1.6562 7.34573H12.7596L10.1613 4.74747C9.90501 4.49114 9.90501 4.07596 10.1613 3.81949C10.4177 3.56303 10.8328 3.56317 11.0893 3.81949L14.8078 7.53794C15.0641 7.79427 15.0641 8.20945 14.8078 8.46592L11.0893 12.1844C10.832 12.4395 10.4164 12.4395 10.1594 12.1606Z" fill="white"/></svg>
-                    </span>
-                    Meet Zed
-                </a>
-                <a href="/team" class="relative mt-2 text-base no-underline lowercase hover:underline">
-                    <span class="absolute items-center justify-center hidden h-full align-middle nav-active-arrow -left-7">
-                        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.1594 12.1606C10.0309 12.0567 9.96803 11.8899 9.96803 11.7204C9.96803 11.5509 10.0321 11.3846 10.1603 11.2564L12.7596 8.65813H1.6562C1.29392 8.65813 1 8.36284 1 7.97732C1 7.63829 1.29392 7.34573 1.6562 7.34573H12.7596L10.1613 4.74747C9.90501 4.49114 9.90501 4.07596 10.1613 3.81949C10.4177 3.56303 10.8328 3.56317 11.0893 3.81949L14.8078 7.53794C15.0641 7.79427 15.0641 8.20945 14.8078 8.46592L11.0893 12.1844C10.832 12.4395 10.4164 12.4395 10.1594 12.1606Z" fill="white"/></svg>
-                    </span>
-                    The Team
-                </a>
-                <a href="/careers" class="relative mt-2 text-base no-underline lowercase hover:underline">
-                    <span class="absolute items-center justify-center hidden h-full align-middle nav-active-arrow -left-7">
-                        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.1594 12.1606C10.0309 12.0567 9.96803 11.8899 9.96803 11.7204C9.96803 11.5509 10.0321 11.3846 10.1603 11.2564L12.7596 8.65813H1.6562C1.29392 8.65813 1 8.36284 1 7.97732C1 7.63829 1.29392 7.34573 1.6562 7.34573H12.7596L10.1613 4.74747C9.90501 4.49114 9.90501 4.07596 10.1613 3.81949C10.4177 3.56303 10.8328 3.56317 11.0893 3.81949L14.8078 7.53794C15.0641 7.79427 15.0641 8.20945 14.8078 8.46592L11.0893 12.1844C10.832 12.4395 10.4164 12.4395 10.1594 12.1606Z" fill="white"/></svg>
-                    </span>
-                    Careers
-                </a>
-                <a href="/community" class="relative mt-2 text-base no-underline lowercase hover:underline">
-                    <span class="absolute items-center justify-center hidden h-full align-middle nav-active-arrow -left-7">
-                        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.1594 12.1606C10.0309 12.0567 9.96803 11.8899 9.96803 11.7204C9.96803 11.5509 10.0321 11.3846 10.1603 11.2564L12.7596 8.65813H1.6562C1.29392 8.65813 1 8.36284 1 7.97732C1 7.63829 1.29392 7.34573 1.6562 7.34573H12.7596L10.1613 4.74747C9.90501 4.49114 9.90501 4.07596 10.1613 3.81949C10.4177 3.56303 10.8328 3.56317 11.0893 3.81949L14.8078 7.53794C15.0641 7.79427 15.0641 8.20945 14.8078 8.46592L11.0893 12.1844C10.832 12.4395 10.4164 12.4395 10.1594 12.1606Z" fill="white"/></svg>
-                    </span>
-                    Waitlist
-                </a>
-            </div>
-            
-            <!-- If user is logged in AND -->
-            <!-- If user is at least an insider -->
-            {{#if current_user}}
-            <div class="flex flex-col mt-10 font-mono">
-                <p class="text-xs tracking-widest uppercase opacity-50">Insiders</p>
-                <a href="/releases" class="relative mt-2 text-base no-underline lowercase hover:underline">
-                    <span class="absolute items-center justify-center hidden h-full align-middle nav-active-arrow -left-7">
-                        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.1594 12.1606C10.0309 12.0567 9.96803 11.8899 9.96803 11.7204C9.96803 11.5509 10.0321 11.3846 10.1603 11.2564L12.7596 8.65813H1.6562C1.29392 8.65813 1 8.36284 1 7.97732C1 7.63829 1.29392 7.34573 1.6562 7.34573H12.7596L10.1613 4.74747C9.90501 4.49114 9.90501 4.07596 10.1613 3.81949C10.4177 3.56303 10.8328 3.56317 11.0893 3.81949L14.8078 7.53794C15.0641 7.79427 15.0641 8.20945 14.8078 8.46592L11.0893 12.1844C10.832 12.4395 10.4164 12.4395 10.1594 12.1606Z" fill="white"/></svg>
-                    </span>
-                    Releases
-                </a>
-            </div>
-
-            <!-- AND If user is an admin -->
-            {{#if current_user.is_admin }}
-                <div class="flex flex-col mt-10 font-mono">
-                    <p class="text-xs tracking-widest uppercase opacity-50">Admin</p>
-                    <a href="/admin" class="relative inline-block mt-2 text-base no-underline lowercase hover:underline">
-                        <span class="absolute items-center justify-center hidden h-full align-middle nav-active-arrow -left-7">
-                            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.1594 12.1606C10.0309 12.0567 9.96803 11.8899 9.96803 11.7204C9.96803 11.5509 10.0321 11.3846 10.1603 11.2564L12.7596 8.65813H1.6562C1.29392 8.65813 1 8.36284 1 7.97732C1 7.63829 1.29392 7.34573 1.6562 7.34573H12.7596L10.1613 4.74747C9.90501 4.49114 9.90501 4.07596 10.1613 3.81949C10.4177 3.56303 10.8328 3.56317 11.0893 3.81949L14.8078 7.53794C15.0641 7.79427 15.0641 8.20945 14.8078 8.46592L11.0893 12.1844C10.832 12.4395 10.4164 12.4395 10.1594 12.1606Z" fill="white"/></svg>
-                        </span>
-                        Manage
-                    </a>
-                </div>
-            {{/if}}
-       
-            <div class="mt-10">
-                <p class="text-base no-underline">@{{current_user.github_login}}</p>
-                <form id="sign_out" action="/sign_out" method="post">
-                    <button class="mt-2 text-xs no-underline lowercase opacity-50 font-extralight hover:opacity-100">Sign out</button>
-                </form>
-            </div>
-
-            {{else}}
-            <!-- Otherwise let user log in -->
-            <a href=" /sign_in"
-                class="mt-10 text-base text-gray-400 no-underline lowercase hover:underline">
-                Log in
-            </a>
-            {{/if}}
-        </nav>
-
-        <div class="col-span-3 pb-20 bg-black">
-            {{> @partial-block}}
-        </div>
-    </main>
-    <script src="/static/prism.js"></script>
-</body>
-
-<script>
-    const nav = document.getElementById('nav');
-    const links = nav.getElementsByTagName('a');
-    const currentPath = window.location.pathname;
-    for (const link of links) {
-        if (link.getAttribute('href') === currentPath) {
-            link.classList.add('active');
-        }
-    }
-    const mnav = document.getElementById('mobile-nav');
-    const mlinks = mnav.getElementsByTagName('a');
-    const mcurrentPath = window.location.pathname;
-    for (const mlink of mlinks) {
-        if (mlink.getAttribute('href') === mcurrentPath) {
-            mlink.classList.add('active');
-        }
-    }
-</script>
-
-</html>

crates/collab/templates/releases.hbs 🔗

@@ -1,35 +0,0 @@
-{{#> layout }}
-
-<div class="max-w-screen-lg p-5 mx-auto font-extralight text-main lg:p-20">
-    {{#if current_user}}
-    <h1 class="mb-10 font-display font-extralight">Releases</h1>
-    <p class="mt-5 leading-relaxed">Zed is currently only available on macOS.</p>
-    <p class="mt-5 mb-10 leading-relaxed">We are frequently shipping new versions, check back regularly to get the most recent version.</p>
-
-    {{#if releases}}
-    
-    {{#each releases}}
-    <article id="{{name}}" class="mb-20">
-        <h3 class="mb-2 font-display font-extralight">{{name}}</h3>
-        <div>
-          <a class="text-sm underline opacity-80 hover:opacity-100" href="/releases/{{tag_name}}/{{assets.0.name}}">Download</a>  · <a class="text-sm underline opacity-50 hover:opacity-100" href="https://github.com/zed-industries/zed/releases/tag/{{tag_name}}">Release Notes</a>
-        </p> 
-        <div class="mt-10 type-prose">
-            {{{body}}}
-        </div>
-        <div class="mt-5">
-            <p>&mdash;&mdash;&mdash;</p>
-        </div>
-    </article>
-    {{/each}}
-    {{/if}}
-
-    {{else}}
-
-    <h1 class="mb-10 font-display font-extralight">Hold it!</h1>
-    <p class="mt-5 leading-relaxed">You can't access this without <a href=" /sign_in" class="mt-5 leading-relaxed underline">logging in</a>.</p>
-    <p class="mt-5 leading-relaxed">Try <a href="/community" class="mt-5 leading-relaxed underline">joining our community</a> to get access to updates & releases.</p>
-    {{/if}}
-</div>
-
-{{/layout}}

crates/collab/templates/signup.hbs 🔗

@@ -1,18 +0,0 @@
-{{#> layout }}
-
-<div class="max-w-screen-lg p-10 mx-auto font-extralight text-main lg:p-20">  
-    <h1 class="mb-10 font-display font-extralight">Thanks for signing up!</h1>
-    <p class="mt-5 leading-relaxed">
-        We'll add you to our list and let you know when we have something ready for you to try out.
-    </p>
-
-    <p class="mt-5 leading-relaxed">
-        Thanks for your interest!
-    </p>
-
-    <a href="/" class="block mt-10 text-lg font-extralight underline opacity-100 hover:opacity-80">
-        Back to Home
-    </a>
-</div>
-
-{{/layout}}

crates/collab/templates/team.hbs 🔗

@@ -1,160 +0,0 @@
-{{#> layout }}
-
-<div class="max-w-screen-lg p-5 mx-auto font-extralight text-main lg:p-20">
-    <h1 class="mb-10 text-4xl text-white font-display font-extralight">
-        We think there’s a better way to write code&mdash;and we've been working for more than a decade to bring it into existence.
-    </h1>
-    <p class="mt-5 leading-relaxed">
-        Our first attempt was Atom, which we loved like a child but which ultimately fell short of our original vision. When we created Electron in 2012 to serve as Atom's runtime, there weren't a lot of great options for building cross-platform desktop apps. 
-    </p>
-    <p class="mt-5 leading-relaxed">
-        Had we tried to write Atom in C or C++, it never would have shipped, and we loved the idea of developers extending their editor with the familiar tools of JavaScript, HTML, and CSS.
-    </p>
-    <p class="mt-5 leading-relaxed">
-        In the end, however, we reached the conclusion that the editor we wanted to use couldn't be built in a single-threaded scripting language. It was time to start over. Now we're back from the wilderness, this time with the knowledge and tools we need to execute without compromise.
-    </p>
-    <p class="mt-5 leading-relaxed">
-        We're leveraging Rust, conflict-free replicated data types, and every core of your CPU and GPU to deliver an editor that will make coding more productive, fun, and collaborative. We look forward to sharing our best with you!
-    </p>
-    
-    <h1 class="mt-20 mb-5 text-4xl text-white font-display font-extralight">The Team</h1>
-    <div class="">
-        <article class="mb-10 bg-black">
-            <div class="content">
-                <h3 class="mb-2 font-display font-extralight">Nathan Sobo</h3>
-                <div class="">
-                    <p class="mt-3 mb-3 leading-relaxed">Nathan joined GitHub in late 2011 to build the <a href="https://atom.io" class="underline hover:no-underline">Atom text editor</a>, and he led the Atom team until 2018.</p>
-                
-                    <p class="mt-3 mb-3 leading-relaxed">He also co-led development of <a href="https://teletype.atom.io" class="underline hover:no-underline">Teletype for Atom</a>, pioneering one of the first production uses of conflict-free replicated data types for collaborative text editing.</p>
-                    
-                    <p class="mt-3 mb-3 leading-relaxed">He's been dreaming about building the world’s best text editor since he graduated from college, and is excited to finally have the knowledge, tools, and resources to achieve this vision.</p>
-
-                    <div class="flex flex-row mt-6 mb-3">
-                        <a class="block mr-4 opacity-60 hover:opacity-100" href="https://github.com/nathansobo">
-                            <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">

script/build-css 🔗

@@ -1,10 +0,0 @@
-#!/bin/bash
-
-set -e
-
-cd ./script
-[ -d node_modules ] || npm install
-if [[ $1 == --release ]]; then
-    export NODE_ENV=production # Purge unused styles in --release mode
-fi
-npx tailwindcss build ../crates/collab/styles.css --output ../crates/collab/static/styles.css

script/deploy-migration 🔗

@@ -16,7 +16,7 @@ if [[ $# < 1 ]]; then
 fi
 
 export ZED_KUBE_NAMESPACE=$1
-ENV_FILE="server/k8s/environments/${ZED_KUBE_NAMESPACE}.sh"
+ENV_FILE="crates/collab/k8s/environments/${ZED_KUBE_NAMESPACE}.sh"
 if [[ ! -f $ENV_FILE ]]; then
   echo "Invalid environment name '${ZED_KUBE_NAMESPACE}'"
   exit 1
@@ -36,7 +36,7 @@ docker build . \
   --tag $ZED_IMAGE_ID
 docker push $ZED_IMAGE_ID
 
-envsubst < server/k8s/migrate.template.yml | kubectl apply -f -
+envsubst < crates/collab/k8s/migrate.template.yml | kubectl apply -f -
 
 pod=$(kubectl --namespace=${ZED_KUBE_NAMESPACE} get pods --selector=job-name=${ZED_MIGRATE_JOB_NAME} --output=jsonpath='{.items[*].metadata.name}')
 echo "pod:" $pod

script/package-lock.json 🔗

@@ -1,2452 +0,0 @@
-{
-  "name": "script",
-  "lockfileVersion": 2,
-  "requires": true,
-  "packages": {
-    "": {
-      "devDependencies": {
-        "@tailwindcss/typography": "^0.4.0",
-        "tailwindcss-cli": "^0.1.2"
-      }
-    },
-    "node_modules/@fullhuman/postcss-purgecss": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.1.3.tgz",
-      "integrity": "sha512-kwOXw8fZ0Lt1QmeOOrd+o4Ibvp4UTEBFQbzvWldjlKv5n+G9sXfIPn1hh63IQIL8K8vbvv1oYMJiIUbuy9bGaA==",
-      "dev": true,
-      "dependencies": {
-        "purgecss": "^3.1.3"
-      }
-    },
-    "node_modules/@nodelib/fs.scandir": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
-      "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
-      "dev": true,
-      "dependencies": {
-        "@nodelib/fs.stat": "2.0.4",
-        "run-parallel": "^1.1.9"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.stat": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
-      "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==",
-      "dev": true,
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@nodelib/fs.walk": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
-      "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
-      "dev": true,
-      "dependencies": {
-        "@nodelib/fs.scandir": "2.1.4",
-        "fastq": "^1.6.0"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/@tailwindcss/typography": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.4.0.tgz",
-      "integrity": "sha512-3BfOYT5MYNEq81Ism3L2qu/HRP2Q5vWqZtZRQqQrthHuaTK9qpuPfbMT5WATjAM5J1OePKBaI5pLoX4S1JGNMQ==",
-      "dev": true,
-      "dependencies": {
-        "lodash.castarray": "^4.4.0",
-        "lodash.isplainobject": "^4.0.6",
-        "lodash.merge": "^4.6.2",
-        "lodash.uniq": "^4.5.0"
-      },
-      "peerDependencies": {
-        "tailwindcss": "2.0.0-alpha.24 || ^2.0.0"
-      }
-    },
-    "node_modules/acorn": {
-      "version": "7.4.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
-      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
-      "dev": true,
-      "bin": {
-        "acorn": "bin/acorn"
-      },
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/acorn-node": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
-      "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
-      "dev": true,
-      "dependencies": {
-        "acorn": "^7.0.0",
-        "acorn-walk": "^7.0.0",
-        "xtend": "^4.0.2"
-      }
-    },
-    "node_modules/acorn-walk": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
-      "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "dev": true,
-      "dependencies": {
-        "color-convert": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
-    "node_modules/anymatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
-      "dev": true,
-      "dependencies": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/at-least-node": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
-      "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 4.0.0"
-      }
-    },
-    "node_modules/autoprefixer": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.5.tgz",
-      "integrity": "sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==",
-      "dev": true,
-      "dependencies": {
-        "browserslist": "^4.16.3",
-        "caniuse-lite": "^1.0.30001196",
-        "colorette": "^1.2.2",
-        "fraction.js": "^4.0.13",
-        "normalize-range": "^0.1.2",
-        "postcss-value-parser": "^4.1.0"
-      },
-      "bin": {
-        "autoprefixer": "bin/autoprefixer"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      },
-      "peerDependencies": {
-        "postcss": "^8.1.0"
-      }
-    },
-    "node_modules/balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
-    },
-    "node_modules/binary-extensions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
-      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "dependencies": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "node_modules/braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
-      "dependencies": {
-        "fill-range": "^7.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/browserslist": {
-      "version": "4.16.6",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
-      "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
-      "dev": true,
-      "dependencies": {
-        "caniuse-lite": "^1.0.30001219",
-        "colorette": "^1.2.2",
-        "electron-to-chromium": "^1.3.723",
-        "escalade": "^3.1.1",
-        "node-releases": "^1.1.71"
-      },
-      "bin": {
-        "browserslist": "cli.js"
-      },
-      "engines": {
-        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/browserslist"
-      }
-    },
-    "node_modules/bytes": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
-      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/camelcase-css": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
-      "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
-      "dev": true,
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/caniuse-lite": {
-      "version": "1.0.30001228",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz",
-      "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==",
-      "dev": true,
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/browserslist"
-      }
-    },
-    "node_modules/chalk": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
-      "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/chalk?sponsor=1"
-      }
-    },
-    "node_modules/chokidar": {
-      "version": "3.5.1",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
-      "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
-      "dev": true,
-      "dependencies": {
-        "anymatch": "~3.1.1",
-        "braces": "~3.0.2",
-        "fsevents": "~2.3.1",
-        "glob-parent": "~5.1.0",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.5.0"
-      },
-      "engines": {
-        "node": ">= 8.10.0"
-      },
-      "optionalDependencies": {
-        "fsevents": "~2.3.1"
-      }
-    },
-    "node_modules/color": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz",
-      "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==",
-      "dev": true,
-      "dependencies": {
-        "color-convert": "^1.9.1",
-        "color-string": "^1.5.4"
-      }
-    },
-    "node_modules/color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "dev": true,
-      "dependencies": {
-        "color-name": "~1.1.4"
-      },
-      "engines": {
-        "node": ">=7.0.0"
-      }
-    },
-    "node_modules/color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true
-    },
-    "node_modules/color-string": {
-      "version": "1.5.5",
-      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz",
-      "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==",
-      "dev": true,
-      "dependencies": {
-        "color-name": "^1.0.0",
-        "simple-swizzle": "^0.2.2"
-      }
-    },
-    "node_modules/color/node_modules/color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/color/node_modules/color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-      "dev": true
-    },
-    "node_modules/colorette": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
-      "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
-      "dev": true
-    },
-    "node_modules/commander": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
-      "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
-      "dev": true,
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
-    },
-    "node_modules/css-unit-converter": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
-      "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==",
-      "dev": true
-    },
-    "node_modules/cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "dev": true,
-      "bin": {
-        "cssesc": "bin/cssesc"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/defined": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
-      "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
-      "dev": true
-    },
-    "node_modules/detective": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz",
-      "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==",
-      "dev": true,
-      "dependencies": {
-        "acorn-node": "^1.6.1",
-        "defined": "^1.0.0",
-        "minimist": "^1.1.1"
-      },
-      "bin": {
-        "detective": "bin/detective.js"
-      },
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/didyoumean": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz",
-      "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=",
-      "dev": true
-    },
-    "node_modules/dlv": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
-      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
-      "dev": true
-    },
-    "node_modules/electron-to-chromium": {
-      "version": "1.3.728",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.728.tgz",
-      "integrity": "sha512-SHv4ziXruBpb1Nz4aTuqEHBYi/9GNCJMYIJgDEXrp/2V01nFXMNFUTli5Z85f5ivSkioLilQatqBYFB44wNJrA==",
-      "dev": true
-    },
-    "node_modules/escalade": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
-      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      }
-    },
-    "node_modules/escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-      "dev": true,
-      "engines": {
-        "node": ">=0.8.0"
-      }
-    },
-    "node_modules/fast-glob": {
-      "version": "3.2.5",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
-      "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
-      "dev": true,
-      "dependencies": {
-        "@nodelib/fs.stat": "^2.0.2",
-        "@nodelib/fs.walk": "^1.2.3",
-        "glob-parent": "^5.1.0",
-        "merge2": "^1.3.0",
-        "micromatch": "^4.0.2",
-        "picomatch": "^2.2.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/fastq": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
-      "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
-      "dev": true,
-      "dependencies": {
-        "reusify": "^1.0.4"
-      }
-    },
-    "node_modules/fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
-      "dependencies": {
-        "to-regex-range": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/fraction.js": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.0.tgz",
-      "integrity": "sha512-o9lSKpK0TDqDwTL24Hxqi6I99s942l6TYkfl6WvGWgLOIFz/YonSGKfiSeMadoiNvTfqnfOa9mjb5SGVbBK9/w==",
-      "dev": true,
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/fs-extra": {
-      "version": "9.1.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
-      "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
-      "dev": true,
-      "dependencies": {
-        "at-least-node": "^1.0.0",
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^6.0.1",
-        "universalify": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
-    },
-    "node_modules/fsevents": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
-      "dev": true,
-      "hasInstallScript": true,
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
-      }
-    },
-    "node_modules/function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
-    },
-    "node_modules/glob": {
-      "version": "7.1.7",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
-      "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
-      "dev": true,
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      },
-      "engines": {
-        "node": "*"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/glob-base": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
-      "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
-      "dev": true,
-      "dependencies": {
-        "glob-parent": "^2.0.0",
-        "is-glob": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/glob-base/node_modules/glob-parent": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
-      "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
-      "dev": true,
-      "dependencies": {
-        "is-glob": "^2.0.0"
-      }
-    },
-    "node_modules/glob-base/node_modules/is-extglob": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
-      "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/glob-base/node_modules/is-glob": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
-      "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
-      "dev": true,
-      "dependencies": {
-        "is-extglob": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/glob-parent": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
-      "dependencies": {
-        "is-glob": "^4.0.1"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/graceful-fs": {
-      "version": "4.2.6",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
-      "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
-      "dev": true
-    },
-    "node_modules/has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
-      "dependencies": {
-        "function-bind": "^1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.4.0"
-      }
-    },
-    "node_modules/has-flag": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/html-tags": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz",
-      "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
-      "dependencies": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "node_modules/inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
-    },
-    "node_modules/is-arrayish": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
-      "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
-      "dev": true
-    },
-    "node_modules/is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
-      "dependencies": {
-        "binary-extensions": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/is-core-module": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
-      "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
-      "dev": true,
-      "dependencies": {
-        "has": "^1.0.3"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/is-dotfile": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
-      "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-glob": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
-      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
-      "dev": true,
-      "dependencies": {
-        "is-extglob": "^2.1.1"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.12.0"
-      }
-    },
-    "node_modules/jsonfile": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
-      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
-      "dev": true,
-      "dependencies": {
-        "graceful-fs": "^4.1.6",
-        "universalify": "^2.0.0"
-      },
-      "optionalDependencies": {
-        "graceful-fs": "^4.1.6"
-      }
-    },
-    "node_modules/lodash": {
-      "version": "4.17.21",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
-      "dev": true
-    },
-    "node_modules/lodash.castarray": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
-      "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=",
-      "dev": true
-    },
-    "node_modules/lodash.isplainobject": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
-      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
-      "dev": true
-    },
-    "node_modules/lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true
-    },
-    "node_modules/lodash.toarray": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
-      "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=",
-      "dev": true
-    },
-    "node_modules/lodash.topath": {
-      "version": "4.5.2",
-      "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz",
-      "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=",
-      "dev": true
-    },
-    "node_modules/lodash.uniq": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
-      "dev": true
-    },
-    "node_modules/merge2": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
-      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/micromatch": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
-      "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
-      "dev": true,
-      "dependencies": {
-        "braces": "^3.0.1",
-        "picomatch": "^2.2.3"
-      },
-      "engines": {
-        "node": ">=8.6"
-      }
-    },
-    "node_modules/minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
-      "dependencies": {
-        "brace-expansion": "^1.1.7"
-      },
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/minimist": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-      "dev": true
-    },
-    "node_modules/modern-normalize": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz",
-      "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==",
-      "dev": true,
-      "engines": {
-        "node": ">=6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/nanoid": {
-      "version": "3.1.23",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
-      "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==",
-      "dev": true,
-      "bin": {
-        "nanoid": "bin/nanoid.cjs"
-      },
-      "engines": {
-        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
-      }
-    },
-    "node_modules/node-emoji": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
-      "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
-      "dev": true,
-      "dependencies": {
-        "lodash.toarray": "^4.4.0"
-      }
-    },
-    "node_modules/node-releases": {
-      "version": "1.1.72",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz",
-      "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==",
-      "dev": true
-    },
-    "node_modules/normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/normalize-range": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
-      "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/object-hash": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz",
-      "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==",
-      "dev": true,
-      "engines": {
-        "node": ">= 6"
-      }
-    },
-    "node_modules/once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
-      "dependencies": {
-        "wrappy": "1"
-      }
-    },
-    "node_modules/parse-glob": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
-      "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
-      "dev": true,
-      "dependencies": {
-        "glob-base": "^0.3.0",
-        "is-dotfile": "^1.0.0",
-        "is-extglob": "^1.0.0",
-        "is-glob": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/parse-glob/node_modules/is-extglob": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
-      "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/parse-glob/node_modules/is-glob": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
-      "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
-      "dev": true,
-      "dependencies": {
-        "is-extglob": "^1.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/path-parse": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
-      "dev": true
-    },
-    "node_modules/picomatch": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
-      "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==",
-      "dev": true,
-      "engines": {
-        "node": ">=8.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/jonschlinkert"
-      }
-    },
-    "node_modules/postcss": {
-      "version": "8.2.15",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz",
-      "integrity": "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==",
-      "dev": true,
-      "dependencies": {
-        "colorette": "^1.2.2",
-        "nanoid": "^3.1.23",
-        "source-map": "^0.6.1"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      }
-    },
-    "node_modules/postcss-functions": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz",
-      "integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=",
-      "dev": true,
-      "dependencies": {
-        "glob": "^7.1.2",
-        "object-assign": "^4.1.1",
-        "postcss": "^6.0.9",
-        "postcss-value-parser": "^3.3.0"
-      }
-    },
-    "node_modules/postcss-functions/node_modules/ansi-styles": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
-      "dependencies": {
-        "color-convert": "^1.9.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-functions/node_modules/chalk": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
-      "dependencies": {
-        "ansi-styles": "^3.2.1",
-        "escape-string-regexp": "^1.0.5",
-        "supports-color": "^5.3.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-functions/node_modules/color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "dependencies": {
-        "color-name": "1.1.3"
-      }
-    },
-    "node_modules/postcss-functions/node_modules/color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-      "dev": true
-    },
-    "node_modules/postcss-functions/node_modules/has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-functions/node_modules/postcss": {
-      "version": "6.0.23",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
-      "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
-      "dev": true,
-      "dependencies": {
-        "chalk": "^2.4.1",
-        "source-map": "^0.6.1",
-        "supports-color": "^5.4.0"
-      },
-      "engines": {
-        "node": ">=4.0.0"
-      }
-    },
-    "node_modules/postcss-functions/node_modules/postcss-value-parser": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
-      "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
-      "dev": true
-    },
-    "node_modules/postcss-functions/node_modules/supports-color": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
-      "dependencies": {
-        "has-flag": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-js": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz",
-      "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==",
-      "dev": true,
-      "dependencies": {
-        "camelcase-css": "^2.0.1",
-        "postcss": "^8.1.6"
-      },
-      "engines": {
-        "node": ">=10.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      }
-    },
-    "node_modules/postcss-nested": {
-      "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.5.tgz",
-      "integrity": "sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew==",
-      "dev": true,
-      "dependencies": {
-        "postcss-selector-parser": "^6.0.4"
-      },
-      "engines": {
-        "node": ">=10.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/postcss/"
-      },
-      "peerDependencies": {
-        "postcss": "^8.1.13"
-      }
-    },
-    "node_modules/postcss-selector-parser": {
-      "version": "6.0.6",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz",
-      "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==",
-      "dev": true,
-      "dependencies": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/postcss-value-parser": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
-      "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
-      "dev": true
-    },
-    "node_modules/pretty-hrtime": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
-      "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/purgecss": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-3.1.3.tgz",
-      "integrity": "sha512-hRSLN9mguJ2lzlIQtW4qmPS2kh6oMnA9RxdIYK8sz18QYqd6ePp4GNDl18oWHA1f2v2NEQIh51CO8s/E3YGckQ==",
-      "dev": true,
-      "dependencies": {
-        "commander": "^6.0.0",
-        "glob": "^7.0.0",
-        "postcss": "^8.2.1",
-        "postcss-selector-parser": "^6.0.2"
-      },
-      "bin": {
-        "purgecss": "bin/purgecss.js"
-      }
-    },
-    "node_modules/queue-microtask": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
-      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ]
-    },
-    "node_modules/quick-lru": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
-      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/readdirp": {
-      "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
-      "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
-      "dev": true,
-      "dependencies": {
-        "picomatch": "^2.2.1"
-      },
-      "engines": {
-        "node": ">=8.10.0"
-      }
-    },
-    "node_modules/reduce-css-calc": {
-      "version": "2.1.8",
-      "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz",
-      "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==",
-      "dev": true,
-      "dependencies": {
-        "css-unit-converter": "^1.1.1",
-        "postcss-value-parser": "^3.3.0"
-      }
-    },
-    "node_modules/reduce-css-calc/node_modules/postcss-value-parser": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
-      "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
-      "dev": true
-    },
-    "node_modules/resolve": {
-      "version": "1.20.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
-      "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
-      "dev": true,
-      "dependencies": {
-        "is-core-module": "^2.2.0",
-        "path-parse": "^1.0.6"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/reusify": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true,
-      "engines": {
-        "iojs": ">=1.0.0",
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/run-parallel": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
-      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "dependencies": {
-        "queue-microtask": "^1.2.2"
-      }
-    },
-    "node_modules/simple-swizzle": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
-      "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
-      "dev": true,
-      "dependencies": {
-        "is-arrayish": "^0.3.1"
-      }
-    },
-    "node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/supports-color": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "dev": true,
-      "dependencies": {
-        "has-flag": "^4.0.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/tailwindcss": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.1.2.tgz",
-      "integrity": "sha512-T5t+wwd+/hsOyRw2HJuFuv0LTUm3MUdHm2DJ94GPVgzqwPPFa9XxX0KlwLWupUuiOUj6uiKURCzYPHFcuPch/w==",
-      "dev": true,
-      "dependencies": {
-        "@fullhuman/postcss-purgecss": "^3.1.3",
-        "bytes": "^3.0.0",
-        "chalk": "^4.1.0",
-        "chokidar": "^3.5.1",
-        "color": "^3.1.3",
-        "detective": "^5.2.0",
-        "didyoumean": "^1.2.1",
-        "dlv": "^1.1.3",
-        "fast-glob": "^3.2.5",
-        "fs-extra": "^9.1.0",
-        "html-tags": "^3.1.0",
-        "lodash": "^4.17.21",
-        "lodash.topath": "^4.5.2",
-        "modern-normalize": "^1.0.0",
-        "node-emoji": "^1.8.1",
-        "normalize-path": "^3.0.0",
-        "object-hash": "^2.1.1",
-        "parse-glob": "^3.0.4",
-        "postcss-functions": "^3",
-        "postcss-js": "^3.0.3",
-        "postcss-nested": "5.0.5",
-        "postcss-selector-parser": "^6.0.4",
-        "postcss-value-parser": "^4.1.0",
-        "pretty-hrtime": "^1.0.3",
-        "quick-lru": "^5.1.1",
-        "reduce-css-calc": "^2.1.8",
-        "resolve": "^1.20.0"
-      },
-      "bin": {
-        "tailwind": "lib/cli.js",
-        "tailwindcss": "lib/cli.js"
-      },
-      "engines": {
-        "node": ">=12.13.0"
-      },
-      "peerDependencies": {
-        "autoprefixer": "^10.0.2",
-        "postcss": "^8.0.9"
-      }
-    },
-    "node_modules/tailwindcss-cli": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/tailwindcss-cli/-/tailwindcss-cli-0.1.2.tgz",
-      "integrity": "sha512-17NuGSHKTr4twN1BFxuoTArMcBQH+7YL6x4PHFnmWsGNOX45O4Roc8EdMVhSSH2rQoSDoLvR4TmlfddMon3yKg==",
-      "dev": true,
-      "dependencies": {
-        "autoprefixer": "^10.0.2",
-        "postcss": "^8.1.8",
-        "tailwindcss": "^2.0.1"
-      },
-      "bin": {
-        "tailwindcss-cli": "index.js"
-      }
-    },
-    "node_modules/to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
-      "dependencies": {
-        "is-number": "^7.0.0"
-      },
-      "engines": {
-        "node": ">=8.0"
-      }
-    },
-    "node_modules/universalify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
-      "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
-      "dev": true,
-      "engines": {
-        "node": ">= 10.0.0"
-      }
-    },
-    "node_modules/util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-      "dev": true
-    },
-    "node_modules/wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
-    },
-    "node_modules/xtend": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
-      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.4"
-      }
-    }
-  },
-  "dependencies": {
-    "@fullhuman/postcss-purgecss": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.1.3.tgz",
-      "integrity": "sha512-kwOXw8fZ0Lt1QmeOOrd+o4Ibvp4UTEBFQbzvWldjlKv5n+G9sXfIPn1hh63IQIL8K8vbvv1oYMJiIUbuy9bGaA==",
-      "dev": true,
-      "requires": {
-        "purgecss": "^3.1.3"
-      }
-    },
-    "@nodelib/fs.scandir": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
-      "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
-      "dev": true,
-      "requires": {
-        "@nodelib/fs.stat": "2.0.4",
-        "run-parallel": "^1.1.9"
-      }
-    },
-    "@nodelib/fs.stat": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
-      "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==",
-      "dev": true
-    },
-    "@nodelib/fs.walk": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
-      "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
-      "dev": true,
-      "requires": {
-        "@nodelib/fs.scandir": "2.1.4",
-        "fastq": "^1.6.0"
-      }
-    },
-    "@tailwindcss/typography": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.4.0.tgz",
-      "integrity": "sha512-3BfOYT5MYNEq81Ism3L2qu/HRP2Q5vWqZtZRQqQrthHuaTK9qpuPfbMT5WATjAM5J1OePKBaI5pLoX4S1JGNMQ==",
-      "dev": true,
-      "requires": {
-        "lodash.castarray": "^4.4.0",
-        "lodash.isplainobject": "^4.0.6",
-        "lodash.merge": "^4.6.2",
-        "lodash.uniq": "^4.5.0"
-      }
-    },
-    "acorn": {
-      "version": "7.4.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
-      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
-      "dev": true
-    },
-    "acorn-node": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
-      "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
-      "dev": true,
-      "requires": {
-        "acorn": "^7.0.0",
-        "acorn-walk": "^7.0.0",
-        "xtend": "^4.0.2"
-      }
-    },
-    "acorn-walk": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
-      "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
-      "dev": true
-    },
-    "ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "dev": true,
-      "requires": {
-        "color-convert": "^2.0.1"
-      }
-    },
-    "anymatch": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
-      "dev": true,
-      "requires": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      }
-    },
-    "at-least-node": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
-      "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
-      "dev": true
-    },
-    "autoprefixer": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.5.tgz",
-      "integrity": "sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==",
-      "dev": true,
-      "requires": {
-        "browserslist": "^4.16.3",
-        "caniuse-lite": "^1.0.30001196",
-        "colorette": "^1.2.2",
-        "fraction.js": "^4.0.13",
-        "normalize-range": "^0.1.2",
-        "postcss-value-parser": "^4.1.0"
-      }
-    },
-    "balanced-match": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
-      "dev": true
-    },
-    "binary-extensions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
-      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
-      "dev": true
-    },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
-      "requires": {
-        "fill-range": "^7.0.1"
-      }
-    },
-    "browserslist": {
-      "version": "4.16.6",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
-      "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
-      "dev": true,
-      "requires": {
-        "caniuse-lite": "^1.0.30001219",
-        "colorette": "^1.2.2",
-        "electron-to-chromium": "^1.3.723",
-        "escalade": "^3.1.1",
-        "node-releases": "^1.1.71"
-      }
-    },
-    "bytes": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
-      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
-      "dev": true
-    },
-    "camelcase-css": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
-      "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
-      "dev": true
-    },
-    "caniuse-lite": {
-      "version": "1.0.30001228",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz",
-      "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==",
-      "dev": true
-    },
-    "chalk": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
-      "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      }
-    },
-    "chokidar": {
-      "version": "3.5.1",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
-      "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
-      "dev": true,
-      "requires": {
-        "anymatch": "~3.1.1",
-        "braces": "~3.0.2",
-        "fsevents": "~2.3.1",
-        "glob-parent": "~5.1.0",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.5.0"
-      }
-    },
-    "color": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz",
-      "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==",
-      "dev": true,
-      "requires": {
-        "color-convert": "^1.9.1",
-        "color-string": "^1.5.4"
-      },
-      "dependencies": {
-        "color-convert": {
-          "version": "1.9.3",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-          "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
-          "requires": {
-            "color-name": "1.1.3"
-          }
-        },
-        "color-name": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-          "dev": true
-        }
-      }
-    },
-    "color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "dev": true,
-      "requires": {
-        "color-name": "~1.1.4"
-      }
-    },
-    "color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true
-    },
-    "color-string": {
-      "version": "1.5.5",
-      "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz",
-      "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==",
-      "dev": true,
-      "requires": {
-        "color-name": "^1.0.0",
-        "simple-swizzle": "^0.2.2"
-      }
-    },
-    "colorette": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
-      "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
-      "dev": true
-    },
-    "commander": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
-      "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
-      "dev": true
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
-    },
-    "css-unit-converter": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
-      "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==",
-      "dev": true
-    },
-    "cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "dev": true
-    },
-    "defined": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
-      "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
-      "dev": true
-    },
-    "detective": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz",
-      "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==",
-      "dev": true,
-      "requires": {
-        "acorn-node": "^1.6.1",
-        "defined": "^1.0.0",
-        "minimist": "^1.1.1"
-      }
-    },
-    "didyoumean": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz",
-      "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=",
-      "dev": true
-    },
-    "dlv": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
-      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
-      "dev": true
-    },
-    "electron-to-chromium": {
-      "version": "1.3.728",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.728.tgz",
-      "integrity": "sha512-SHv4ziXruBpb1Nz4aTuqEHBYi/9GNCJMYIJgDEXrp/2V01nFXMNFUTli5Z85f5ivSkioLilQatqBYFB44wNJrA==",
-      "dev": true
-    },
-    "escalade": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
-      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
-      "dev": true
-    },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-      "dev": true
-    },
-    "fast-glob": {
-      "version": "3.2.5",
-      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
-      "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
-      "dev": true,
-      "requires": {
-        "@nodelib/fs.stat": "^2.0.2",
-        "@nodelib/fs.walk": "^1.2.3",
-        "glob-parent": "^5.1.0",
-        "merge2": "^1.3.0",
-        "micromatch": "^4.0.2",
-        "picomatch": "^2.2.1"
-      }
-    },
-    "fastq": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
-      "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
-      "dev": true,
-      "requires": {
-        "reusify": "^1.0.4"
-      }
-    },
-    "fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
-      "requires": {
-        "to-regex-range": "^5.0.1"
-      }
-    },
-    "fraction.js": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.0.tgz",
-      "integrity": "sha512-o9lSKpK0TDqDwTL24Hxqi6I99s942l6TYkfl6WvGWgLOIFz/YonSGKfiSeMadoiNvTfqnfOa9mjb5SGVbBK9/w==",
-      "dev": true
-    },
-    "fs-extra": {
-      "version": "9.1.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
-      "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
-      "dev": true,
-      "requires": {
-        "at-least-node": "^1.0.0",
-        "graceful-fs": "^4.2.0",
-        "jsonfile": "^6.0.1",
-        "universalify": "^2.0.0"
-      }
-    },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
-    },
-    "fsevents": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
-      "dev": true,
-      "optional": true
-    },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
-    },
-    "glob": {
-      "version": "7.1.7",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
-      "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
-      "dev": true,
-      "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      }
-    },
-    "glob-base": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
-      "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
-      "dev": true,
-      "requires": {
-        "glob-parent": "^2.0.0",
-        "is-glob": "^2.0.0"
-      },
-      "dependencies": {
-        "glob-parent": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
-          "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
-          "dev": true,
-          "requires": {
-            "is-glob": "^2.0.0"
-          }
-        },
-        "is-extglob": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
-          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
-          "dev": true
-        },
-        "is-glob": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
-          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^1.0.0"
-          }
-        }
-      }
-    },
-    "glob-parent": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
-      "requires": {
-        "is-glob": "^4.0.1"
-      }
-    },
-    "graceful-fs": {
-      "version": "4.2.6",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
-      "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
-      "dev": true
-    },
-    "has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
-      "requires": {
-        "function-bind": "^1.1.1"
-      }
-    },
-    "has-flag": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true
-    },
-    "html-tags": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz",
-      "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==",
-      "dev": true
-    },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
-    },
-    "is-arrayish": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
-      "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
-      "dev": true
-    },
-    "is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
-      "requires": {
-        "binary-extensions": "^2.0.0"
-      }
-    },
-    "is-core-module": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
-      "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
-      "dev": true,
-      "requires": {
-        "has": "^1.0.3"
-      }
-    },
-    "is-dotfile": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
-      "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
-      "dev": true
-    },
-    "is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true
-    },
-    "is-glob": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
-      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
-      "dev": true,
-      "requires": {
-        "is-extglob": "^2.1.1"
-      }
-    },
-    "is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true
-    },
-    "jsonfile": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
-      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.6",
-        "universalify": "^2.0.0"
-      }
-    },
-    "lodash": {
-      "version": "4.17.21",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
-      "dev": true
-    },
-    "lodash.castarray": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
-      "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=",
-      "dev": true
-    },
-    "lodash.isplainobject": {
-      "version": "4.0.6",
-      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
-      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
-      "dev": true
-    },
-    "lodash.merge": {
-      "version": "4.6.2",
-      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
-      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
-      "dev": true
-    },
-    "lodash.toarray": {
-      "version": "4.4.0",
-      "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
-      "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=",
-      "dev": true
-    },
-    "lodash.topath": {
-      "version": "4.5.2",
-      "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz",
-      "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=",
-      "dev": true
-    },
-    "lodash.uniq": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
-      "dev": true
-    },
-    "merge2": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
-      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true
-    },
-    "micromatch": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
-      "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
-      "dev": true,
-      "requires": {
-        "braces": "^3.0.1",
-        "picomatch": "^2.2.3"
-      }
-    },
-    "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
-      "requires": {
-        "brace-expansion": "^1.1.7"
-      }
-    },
-    "minimist": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-      "dev": true
-    },
-    "modern-normalize": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz",
-      "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==",
-      "dev": true
-    },
-    "nanoid": {
-      "version": "3.1.23",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
-      "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==",
-      "dev": true
-    },
-    "node-emoji": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
-      "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
-      "dev": true,
-      "requires": {
-        "lodash.toarray": "^4.4.0"
-      }
-    },
-    "node-releases": {
-      "version": "1.1.72",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz",
-      "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==",
-      "dev": true
-    },
-    "normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true
-    },
-    "normalize-range": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
-      "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
-      "dev": true
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
-      "dev": true
-    },
-    "object-hash": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.1.1.tgz",
-      "integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==",
-      "dev": true
-    },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "parse-glob": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
-      "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
-      "dev": true,
-      "requires": {
-        "glob-base": "^0.3.0",
-        "is-dotfile": "^1.0.0",
-        "is-extglob": "^1.0.0",
-        "is-glob": "^2.0.0"
-      },
-      "dependencies": {
-        "is-extglob": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
-          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
-          "dev": true
-        },
-        "is-glob": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
-          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^1.0.0"
-          }
-        }
-      }
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
-    },
-    "path-parse": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
-      "dev": true
-    },
-    "picomatch": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
-      "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==",
-      "dev": true
-    },
-    "postcss": {
-      "version": "8.2.15",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz",
-      "integrity": "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==",
-      "dev": true,
-      "requires": {
-        "colorette": "^1.2.2",
-        "nanoid": "^3.1.23",
-        "source-map": "^0.6.1"
-      }
-    },
-    "postcss-functions": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz",
-      "integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=",
-      "dev": true,
-      "requires": {
-        "glob": "^7.1.2",
-        "object-assign": "^4.1.1",
-        "postcss": "^6.0.9",
-        "postcss-value-parser": "^3.3.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "color-convert": {
-          "version": "1.9.3",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-          "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
-          "requires": {
-            "color-name": "1.1.3"
-          }
-        },
-        "color-name": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "6.0.23",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
-          "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
-          "dev": true,
-          "requires": {
-            "chalk": "^2.4.1",
-            "source-map": "^0.6.1",
-            "supports-color": "^5.4.0"
-          }
-        },
-        "postcss-value-parser": {
-          "version": "3.3.1",
-          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
-          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
-      }
-    },
-    "postcss-js": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz",
-      "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==",
-      "dev": true,
-      "requires": {
-        "camelcase-css": "^2.0.1",
-        "postcss": "^8.1.6"
-      }
-    },
-    "postcss-nested": {
-      "version": "5.0.5",
-      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.5.tgz",
-      "integrity": "sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew==",
-      "dev": true,
-      "requires": {
-        "postcss-selector-parser": "^6.0.4"
-      }
-    },
-    "postcss-selector-parser": {
-      "version": "6.0.6",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz",
-      "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==",
-      "dev": true,
-      "requires": {
-        "cssesc": "^3.0.0",
-        "util-deprecate": "^1.0.2"
-      }
-    },
-    "postcss-value-parser": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
-      "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
-      "dev": true
-    },
-    "pretty-hrtime": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
-      "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
-      "dev": true
-    },
-    "purgecss": {
-      "version": "3.1.3",
-      "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-3.1.3.tgz",
-      "integrity": "sha512-hRSLN9mguJ2lzlIQtW4qmPS2kh6oMnA9RxdIYK8sz18QYqd6ePp4GNDl18oWHA1f2v2NEQIh51CO8s/E3YGckQ==",
-      "dev": true,
-      "requires": {
-        "commander": "^6.0.0",
-        "glob": "^7.0.0",
-        "postcss": "^8.2.1",
-        "postcss-selector-parser": "^6.0.2"
-      }
-    },
-    "queue-microtask": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
-      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-      "dev": true
-    },
-    "quick-lru": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
-      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
-      "dev": true
-    },
-    "readdirp": {
-      "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
-      "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
-      "dev": true,
-      "requires": {
-        "picomatch": "^2.2.1"
-      }
-    },
-    "reduce-css-calc": {
-      "version": "2.1.8",
-      "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz",
-      "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==",
-      "dev": true,
-      "requires": {
-        "css-unit-converter": "^1.1.1",
-        "postcss-value-parser": "^3.3.0"
-      },
-      "dependencies": {
-        "postcss-value-parser": {
-          "version": "3.3.1",
-          "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
-          "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
-          "dev": true
-        }
-      }
-    },
-    "resolve": {
-      "version": "1.20.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
-      "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
-      "dev": true,
-      "requires": {
-        "is-core-module": "^2.2.0",
-        "path-parse": "^1.0.6"
-      }
-    },
-    "reusify": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true
-    },
-    "run-parallel": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
-      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
-      "requires": {
-        "queue-microtask": "^1.2.2"
-      }
-    },
-    "simple-swizzle": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
-      "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
-      "dev": true,
-      "requires": {
-        "is-arrayish": "^0.3.1"
-      }
-    },
-    "source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true
-    },
-    "supports-color": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "dev": true,
-      "requires": {
-        "has-flag": "^4.0.0"
-      }
-    },
-    "tailwindcss": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.1.2.tgz",
-      "integrity": "sha512-T5t+wwd+/hsOyRw2HJuFuv0LTUm3MUdHm2DJ94GPVgzqwPPFa9XxX0KlwLWupUuiOUj6uiKURCzYPHFcuPch/w==",
-      "dev": true,
-      "requires": {
-        "@fullhuman/postcss-purgecss": "^3.1.3",
-        "bytes": "^3.0.0",
-        "chalk": "^4.1.0",
-        "chokidar": "^3.5.1",
-        "color": "^3.1.3",
-        "detective": "^5.2.0",
-        "didyoumean": "^1.2.1",
-        "dlv": "^1.1.3",
-        "fast-glob": "^3.2.5",
-        "fs-extra": "^9.1.0",
-        "html-tags": "^3.1.0",
-        "lodash": "^4.17.21",
-        "lodash.topath": "^4.5.2",
-        "modern-normalize": "^1.0.0",
-        "node-emoji": "^1.8.1",
-        "normalize-path": "^3.0.0",
-        "object-hash": "^2.1.1",
-        "parse-glob": "^3.0.4",
-        "postcss-functions": "^3",
-        "postcss-js": "^3.0.3",
-        "postcss-nested": "5.0.5",
-        "postcss-selector-parser": "^6.0.4",
-        "postcss-value-parser": "^4.1.0",
-        "pretty-hrtime": "^1.0.3",
-        "quick-lru": "^5.1.1",
-        "reduce-css-calc": "^2.1.8",
-        "resolve": "^1.20.0"
-      }
-    },
-    "tailwindcss-cli": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/tailwindcss-cli/-/tailwindcss-cli-0.1.2.tgz",
-      "integrity": "sha512-17NuGSHKTr4twN1BFxuoTArMcBQH+7YL6x4PHFnmWsGNOX45O4Roc8EdMVhSSH2rQoSDoLvR4TmlfddMon3yKg==",
-      "dev": true,
-      "requires": {
-        "autoprefixer": "^10.0.2",
-        "postcss": "^8.1.8",
-        "tailwindcss": "^2.0.1"
-      }
-    },
-    "to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
-      "requires": {
-        "is-number": "^7.0.0"
-      }
-    },
-    "universalify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
-      "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
-      "dev": true
-    },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-      "dev": true
-    },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
-    },
-    "xtend": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
-      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
-      "dev": true
-    }
-  }
-}

script/package.json 🔗

@@ -1,6 +0,0 @@
-{
-  "devDependencies": {
-    "@tailwindcss/typography": "^0.4.0",
-    "tailwindcss-cli": "^0.1.2"
-  }
-}

script/tailwind.config.js 🔗

@@ -1,46 +0,0 @@
-const colors = require('tailwindcss/colors')
-
-module.exports = {
-    corePlugins: {
-        float: false,
-        tableLayout: false,
-        sepia: false,
-        saturate: false,
-    },
-
-    theme: {
-        fontFamily: {
-            display: [
-                "Spectral", "Constantia", "Lucida Bright", "Lucidabright", "Lucida Serif", "Lucida", "DejaVu Serif", "Bitstream Vera Serif",
-                "Liberation Serif", "Georgia", "serif", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
-                "Noto Color Emoji"
-            ],
-            body: [
-                "JetBrains Mono", "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono",
-                "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", "Apple Color Emoji", "Segoe UI Emoji",
-                "Segoe UI Symbol", "Noto Color Emoji"
-            ],
-            mono: [
-                "JetBrains Mono", "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono",
-                "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", "Courier New", "Apple Color Emoji", "Segoe UI Emoji",
-                "Segoe UI Symbol", "Noto Color Emoji"
-            ],
-        },
-        colors: {
-            transparent: 'transparent',
-            current: 'currentColor',
-            black: colors.black,
-            white: colors.white,
-            gray: colors.trueGray,
-        },
-        extend: {},
-    },
-
-    variants: {
-    },
-    darkMode: false,
-    purge: [
-        "../crates/collab/templates/**/*.hbs",
-        "../crates/collab/templates/*.hbs"
-    ]
-}