diff --git a/script/deploy b/script/deploy index 1e648336d4..b375329c91 100755 --- a/script/deploy +++ b/script/deploy @@ -5,13 +5,33 @@ # - Log in to the DigitalOcean docker registry # doctl registry login # -# - Set the default K8s context to production +# - Target the `zed-1` kubernetes cluster # doctl kubernetes cluster kubeconfig save zed-1 -set -e +set -eu -IMAGE_ID=registry.digitalocean.com/zed/zed-server +if [[ $# < 1 ]]; then + echo "Usage: $0 [production|staging|...]" + exit 1 +fi -docker build . --tag $IMAGE_ID -docker push $IMAGE_ID -kubectl rollout restart deployment zed +export ZED_KUBE_NAMESPACE=$1 +ENV_FILE="server/k8s/environments/${ZED_KUBE_NAMESPACE}.sh" +if [[ ! -f $ENV_FILE ]]; then + echo "Invalid environment name '${ZED_KUBE_NAMESPACE}'" + exit 1 +fi + +if [[ $ZED_KUBE_NAMESPACE == "production" && -n $(git status --short) ]]; then + echo "Cannot deploy uncommited changes to production" + exit 1 +fi + +git_sha=$(git rev-parse HEAD) +export ZED_IMAGE_ID="registry.digitalocean.com/zed/zed-server:${ZED_KUBE_NAMESPACE}-${git_sha}" +export $(cat $ENV_FILE) + +docker build . --tag "$ZED_IMAGE_ID" +docker push "$ZED_IMAGE_ID" + +envsubst < server/k8s/manifest.template.yml | kubectl apply -f - diff --git a/script/deploy-migration b/script/deploy-migration index 251e7a4518..2ecc7d373b 100755 --- a/script/deploy-migration +++ b/script/deploy-migration @@ -1,11 +1,42 @@ #!/bin/bash -set -e +# Prerequisites: +# +# - Log in to the DigitalOcean docker registry +# doctl registry login +# +# - Target the `zed-1` kubernetes cluster +# doctl kubernetes cluster kubeconfig save zed-1 -IMAGE_ID=registry.digitalocean.com/zed/zed-migrator +set -eu + +if [[ $# < 1 ]]; then + echo "Usage: $0 [production|staging|...]" + exit 1 +fi + +export ZED_KUBE_NAMESPACE=$1 +ENV_FILE="server/k8s/environments/${ZED_KUBE_NAMESPACE}.sh" +if [[ ! -f $ENV_FILE ]]; then + echo "Invalid environment name '${ZED_KUBE_NAMESPACE}'" + exit 1 +fi + +if [[ -n $(git status --short) ]]; then + echo "Cannot deploy with uncommited changes" + exit 1 +fi + +git_sha=$(git rev-parse HEAD) +export ZED_IMAGE_ID=registry.digitalocean.com/zed/zed-migrator:${ZED_KUBE_NAMESPACE}-${git_sha} +export ZED_MIGRATE_JOB_NAME=zed-migrate-${git_sha} docker build . \ --file ./Dockerfile.migrator \ - --tag $IMAGE_ID -docker push $IMAGE_ID -kubectl apply -f ./server/migrate.yml + --tag $ZED_IMAGE_ID +docker push $ZED_IMAGE_ID + +envsubst < server/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 diff --git a/script/kube-shell b/script/kube-shell new file mode 100755 index 0000000000..9181dc959c --- /dev/null +++ b/script/kube-shell @@ -0,0 +1,11 @@ +#!/bin/bash + +if [[ $# < 1 ]]; then + echo "Usage: $0 [production|staging|...]" + exit 1 +fi + +export ZED_KUBE_NAMESPACE=$1 + +pod=$(kubectl --namespace=${ZED_KUBE_NAMESPACE} get pods --selector=app=zed --output=jsonpath='{.items[*].metadata.name}') +exec kubectl --namespace $ZED_KUBE_NAMESPACE exec --tty --stdin $pod -- /bin/bash \ No newline at end of file diff --git a/script/tailwind.config.js b/script/tailwind.config.js index fc8127ef6e..1208df7327 100644 --- a/script/tailwind.config.js +++ b/script/tailwind.config.js @@ -1,44 +1,46 @@ +const colors = require('tailwindcss/colors') + module.exports = { + corePlugins: { + float: false, + tableLayout: false, + sepia: false, + saturate: false, + }, + theme: { fontFamily: { display: [ - "Visby CF", "ui-sans-serif", "system-ui", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto", - "Helvetica Neue", "Arial", "Noto Sans", "sans-serif", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", + "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: [ - "Open Sans", "ui-sans-serif", "system-ui", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto", - "Helvetica Neue", "Arial", "Noto Sans", "sans-serif", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", - "Noto Color Emoji" + "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" ], }, - extend: { - typography: (theme) => ({ - DEFAULT: { - css: { - h1: { - fontFamily: theme("fontFamily.display").join(", ") - }, - h2: { - fontFamily: theme("fontFamily.display").join(", ") - }, - h3: { - fontFamily: theme("fontFamily.display").join(", ") - }, - h4: { - fontFamily: theme("fontFamily.display").join(", ") - } - } - } - }) - } + colors: { + transparent: 'transparent', + current: 'currentColor', + black: colors.black, + white: colors.white, + gray: colors.trueGray, + }, + extend: {}, }, + variants: { }, - plugins: [ - require('@tailwindcss/typography'), - ], + darkMode: false, purge: [ - "../server/templates/**/*.hbs" + "../server/templates/**/*.hbs", + "../server/templates/*.hbs" ] } \ No newline at end of file diff --git a/server/favicon.ico b/server/favicon.ico new file mode 100644 index 0000000000..93bce50392 Binary files /dev/null and b/server/favicon.ico differ diff --git a/server/k8s/environments/production.sh b/server/k8s/environments/production.sh new file mode 100644 index 0000000000..58c2ba81c6 --- /dev/null +++ b/server/k8s/environments/production.sh @@ -0,0 +1 @@ +ZED_LOAD_BALANCER_CERT_ID=6f857971-20fb-4c68-a7d6-35fef9e5ec4c diff --git a/server/k8s/environments/staging.sh b/server/k8s/environments/staging.sh new file mode 100644 index 0000000000..f068a07b4d --- /dev/null +++ b/server/k8s/environments/staging.sh @@ -0,0 +1 @@ +ZED_LOAD_BALANCER_CERT_ID=b0d48941-4895-4d75-a966-fe5a571b1fff diff --git a/server/manifest.yml b/server/k8s/manifest.template.yml similarity index 78% rename from server/manifest.yml rename to server/k8s/manifest.template.yml index 9f7f4260c4..ff9929e98a 100644 --- a/server/manifest.yml +++ b/server/k8s/manifest.template.yml @@ -1,11 +1,17 @@ --- +apiVersion: v1 +kind: Namespace +metadata: + name: ${ZED_KUBE_NAMESPACE} +--- kind: Service apiVersion: v1 metadata: + namespace: ${ZED_KUBE_NAMESPACE} name: zed annotations: service.beta.kubernetes.io/do-loadbalancer-tls-ports: "443" - service.beta.kubernetes.io/do-loadbalancer-certificate-id: "606e2db9-2b58-4ae7-b12c-a0c7d56af49b" + service.beta.kubernetes.io/do-loadbalancer-certificate-id: "${ZED_LOAD_BALANCER_CERT_ID}" spec: type: LoadBalancer selector: @@ -19,6 +25,7 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: + namespace: ${ZED_KUBE_NAMESPACE} name: zed spec: replicas: 1 @@ -32,7 +39,7 @@ spec: spec: containers: - name: zed - image: registry.digitalocean.com/zed/zed-server + image: "${ZED_IMAGE_ID}" ports: - containerPort: 8080 protocol: TCP @@ -69,3 +76,8 @@ spec: secretKeyRef: name: github key: privateKey + securityContext: + capabilities: + # FIXME - Switch to the more restrictive `PERFMON` capability. + # This capability isn't yet available in a stable version of Debian. + add: ["SYS_ADMIN"] diff --git a/server/k8s/migrate.template.yml b/server/k8s/migrate.template.yml new file mode 100644 index 0000000000..9b1dc14d7e --- /dev/null +++ b/server/k8s/migrate.template.yml @@ -0,0 +1,18 @@ +apiVersion: batch/v1 +kind: Job +metadata: + namespace: ${ZED_KUBE_NAMESPACE} + name: ${ZED_MIGRATE_JOB_NAME} +spec: + template: + spec: + restartPolicy: Never + containers: + - name: migrator + image: ${ZED_IMAGE_ID} + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: database + key: url diff --git a/server/migrate.yml b/server/migrate.yml deleted file mode 100644 index 58badca60d..0000000000 --- a/server/migrate.yml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: migrate -spec: - template: - spec: - restartPolicy: Never - containers: - - name: migrator - image: registry.digitalocean.com/zed/zed-migrator - env: - - name: DATABASE_URL - valueFrom: - secretKeyRef: - name: database - key: url diff --git a/server/migrations/20210920192001_add_interests_to_signups.sql b/server/migrations/20210920192001_add_interests_to_signups.sql new file mode 100644 index 0000000000..2457abfc75 --- /dev/null +++ b/server/migrations/20210920192001_add_interests_to_signups.sql @@ -0,0 +1,4 @@ +ALTER TABLE "signups" + ADD "wants_releases" BOOLEAN, + ADD "wants_updates" BOOLEAN, + ADD "wants_community" BOOLEAN; \ No newline at end of file diff --git a/server/src/assets.rs b/server/src/assets.rs index 4e79af8c60..ac0b261bd7 100644 --- a/server/src/assets.rs +++ b/server/src/assets.rs @@ -1,18 +1,16 @@ -use crate::{AppState, Request}; use anyhow::anyhow; use rust_embed::RustEmbed; -use std::sync::Arc; use tide::{http::mime, Server}; #[derive(RustEmbed)] #[folder = "static"] struct Static; -pub fn add_routes(app: &mut Server>) { - app.at("/static/*path").get(get_static_asset); +pub fn add_routes(app: &mut Server<()>) { + app.at("/*path").get(get_static_asset); } -async fn get_static_asset(request: Request) -> tide::Result { +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))?; diff --git a/server/src/community.rs b/server/src/community.rs new file mode 100644 index 0000000000..306cd30de5 --- /dev/null +++ b/server/src/community.rs @@ -0,0 +1,15 @@ +use crate::{AppState, Request, RequestExt}; +use std::sync::Arc; +use tide::http::mime; + +pub fn add_routes(community: &mut tide::Server>) { + 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()) +} diff --git a/server/src/db.rs b/server/src/db.rs index bd9fca85e7..ebc861be03 100644 --- a/server/src/db.rs +++ b/server/src/db.rs @@ -47,17 +47,30 @@ impl Db { github_login: &str, email_address: &str, about: &str, + wants_releases: bool, + wants_updates: bool, + wants_community: bool, ) -> Result { test_support!(self, { let query = " - INSERT INTO signups (github_login, email_address, about) - VALUES ($1, $2, $3) + INSERT INTO signups ( + github_login, + email_address, + about, + wants_releases, + wants_updates, + wants_community + ) + VALUES ($1, $2, $3, $4, $5, $6) RETURNING id "; 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) @@ -475,6 +488,9 @@ pub struct Signup { pub github_login: String, pub email_address: String, pub about: String, + pub wants_releases: Option, + pub wants_updates: Option, + pub wants_community: Option, } id_type!(ChannelId); diff --git a/server/src/home.rs b/server/src/home.rs index 25adde3a0f..69bee449a8 100644 --- a/server/src/home.rs +++ b/server/src/home.rs @@ -1,8 +1,5 @@ -use crate::{ - auth::RequestExt as _, github::Release, AppState, LayoutData, Request, RequestExt as _, -}; -use comrak::ComrakOptions; -use serde::{Deserialize, Serialize}; +use crate::{AppState, Request, RequestExt as _}; +use serde::Deserialize; use std::sync::Arc; use tide::{http::mime, log, Server}; @@ -13,42 +10,7 @@ pub fn add_routes(app: &mut Server>) { } async fn get_home(mut request: Request) -> tide::Result { - #[derive(Serialize)] - struct HomeData { - #[serde(flatten)] - layout: Arc, - releases: Option>, - } - - let mut data = HomeData { - 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(), - ); - } - } - + let data = request.layout_data().await?; Ok(tide::Response::builder(200) .body(request.state().render_template("home.hbs", &data)?) .content_type(mime::HTML) @@ -61,6 +23,12 @@ async fn post_signup(mut request: Request) -> tide::Result { 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?; @@ -75,7 +43,14 @@ async fn post_signup(mut request: Request) -> tide::Result { // Save signup in the database request .db() - .create_signup(&form.github_login, &form.email_address, &form.about) + .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?; diff --git a/server/src/main.rs b/server/src/main.rs index ba54f05f1c..49b9a76cf9 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,12 +1,14 @@ mod admin; mod assets; mod auth; +mod community; mod db; mod env; mod errors; mod expiring; mod github; mod home; +mod releases; mod rpc; mod team; @@ -173,13 +175,20 @@ pub async fn run_server( web.with(errors::Middleware); home::add_routes(&mut web); team::add_routes(&mut web); + releases::add_routes(&mut web); + community::add_routes(&mut web); admin::add_routes(&mut web); auth::add_routes(&mut web); - assets::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?; diff --git a/server/src/releases.rs b/server/src/releases.rs new file mode 100644 index 0000000000..f1f3410577 --- /dev/null +++ b/server/src/releases.rs @@ -0,0 +1,55 @@ +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>) { + releases.at("/releases").get(get_releases); +} + +async fn get_releases(mut request: Request) -> tide::Result { + + #[derive(Serialize)] + struct ReleasesData { + #[serde(flatten)] + layout: Arc, + releases: Option>, + } + + 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()) +} \ No newline at end of file diff --git a/server/static/browserconfig.xml b/server/static/browserconfig.xml new file mode 100644 index 0000000000..15a8331058 --- /dev/null +++ b/server/static/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #000000 + + + diff --git a/server/static/images/android-chrome-192x192.png b/server/static/images/android-chrome-192x192.png new file mode 100644 index 0000000000..2342d82c0b Binary files /dev/null and b/server/static/images/android-chrome-192x192.png differ diff --git a/server/static/images/android-chrome-512x512.png b/server/static/images/android-chrome-512x512.png new file mode 100644 index 0000000000..d25b64f77a Binary files /dev/null and b/server/static/images/android-chrome-512x512.png differ diff --git a/server/static/images/apple-touch-icon.png b/server/static/images/apple-touch-icon.png new file mode 100644 index 0000000000..f9ba99f40f Binary files /dev/null and b/server/static/images/apple-touch-icon.png differ diff --git a/server/static/images/favicon-16x16.png b/server/static/images/favicon-16x16.png new file mode 100644 index 0000000000..486c293a82 Binary files /dev/null and b/server/static/images/favicon-16x16.png differ diff --git a/server/static/images/favicon-32x32.png b/server/static/images/favicon-32x32.png new file mode 100644 index 0000000000..a34711800b Binary files /dev/null and b/server/static/images/favicon-32x32.png differ diff --git a/server/static/images/favicon.png b/server/static/images/favicon.png index 074140e062..821698c28f 100644 Binary files a/server/static/images/favicon.png and b/server/static/images/favicon.png differ diff --git a/server/static/images/favicon.svg b/server/static/images/favicon.svg new file mode 100644 index 0000000000..fdd3fe4610 --- /dev/null +++ b/server/static/images/favicon.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/server/static/images/mstile-144x144.png b/server/static/images/mstile-144x144.png new file mode 100644 index 0000000000..e70abaf8d2 Binary files /dev/null and b/server/static/images/mstile-144x144.png differ diff --git a/server/static/images/mstile-150x150.png b/server/static/images/mstile-150x150.png new file mode 100644 index 0000000000..545758300a Binary files /dev/null and b/server/static/images/mstile-150x150.png differ diff --git a/server/static/images/mstile-310x150.png b/server/static/images/mstile-310x150.png new file mode 100644 index 0000000000..73072f380d Binary files /dev/null and b/server/static/images/mstile-310x150.png differ diff --git a/server/static/images/mstile-310x310.png b/server/static/images/mstile-310x310.png new file mode 100644 index 0000000000..2f8fc1c3a1 Binary files /dev/null and b/server/static/images/mstile-310x310.png differ diff --git a/server/static/images/mstile-70x70.png b/server/static/images/mstile-70x70.png new file mode 100644 index 0000000000..c28ce9405c Binary files /dev/null and b/server/static/images/mstile-70x70.png differ diff --git a/server/static/images/safari-pinned-tab.svg b/server/static/images/safari-pinned-tab.svg new file mode 100644 index 0000000000..981cf3f1cb --- /dev/null +++ b/server/static/images/safari-pinned-tab.svg @@ -0,0 +1,19 @@ + + + + + + + diff --git a/server/static/images/zed-og-image.png b/server/static/images/zed-og-image.png new file mode 100644 index 0000000000..9410e705d8 Binary files /dev/null and b/server/static/images/zed-og-image.png differ diff --git a/server/static/images/zed-twitter-image.png b/server/static/images/zed-twitter-image.png new file mode 100644 index 0000000000..607d9bf9c6 Binary files /dev/null and b/server/static/images/zed-twitter-image.png differ diff --git a/server/static/prism.js b/server/static/prism.js new file mode 100644 index 0000000000..b08aaf6c6a --- /dev/null +++ b/server/static/prism.js @@ -0,0 +1,12 @@ +/* PrismJS 1.25.0 +https://prismjs.com/download.html#themes=prism-twilight&languages=markup+css+clike+javascript+bash+c+cpp+rust+scss */ +var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,n=0,e={},M={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof W?new W(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=l.reach);y+=m.value.length,m=m.next){var b=m.value;if(t.length>n.length)return;if(!(b instanceof W)){var k,x=1;if(h){if(!(k=z(v,y,n,f)))break;var w=k.index,A=k.index+k[0].length,P=y;for(P+=m.value.length;P<=w;)m=m.next,P+=m.value.length;if(P-=m.value.length,y=P,m.value instanceof W)continue;for(var E=m;E!==t.tail&&(Pl.reach&&(l.reach=N);var j=m.prev;O&&(j=I(t,j,O),y+=O.length),q(t,j,x);var C=new W(o,g?M.tokenize(S,g):S,d,S);if(m=I(t,j,C),L&&I(t,m,L),1l.reach&&(l.reach=_.reach)}}}}}}(e,a,n,a.head,0),function(e){var n=[],t=e.head.next;for(;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=M.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=M.hooks.all[e];if(t&&t.length)for(var r,a=0;r=t[a++];)r(n)}},Token:W};function W(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function z(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function i(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function I(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function q(e,n,t){for(var r=n.next,a=0;a"+a.content+""},!u.document)return u.addEventListener&&(M.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),t=n.language,r=n.code,a=n.immediateClose;u.postMessage(M.highlight(r,M.languages[t],t)),a&&u.close()},!1)),M;var t=M.util.currentScript();function r(){M.manual||M.highlightAll()}if(t&&(M.filename=t.src,t.hasAttribute("data-manual")&&(M.manual=!0)),!M.manual){var a=document.readyState;"loading"===a||"interactive"===a&&t&&t.defer?document.addEventListener("DOMContentLoaded",r):window.requestAnimationFrame?window.requestAnimationFrame(r):window.setTimeout(r,16)}return M}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); +Prism.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var t={"included-cdata":{pattern://i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:))*\\]\\]>|(?!)".replace(/__/g,function(){return a}),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml; +!function(s){var e=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+e.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+e.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+e.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:e,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism); +Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}; +Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript; +!function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},a={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:a},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:a},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:a.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:a.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|aptitude|apt-cache|apt-get|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:if|then|else|elif|fi|for|while|in|case|esac|function|select|do|done|until)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|break|cd|continue|eval|exec|exit|export|getopts|hash|pwd|readonly|return|shift|test|times|trap|umask|unset|alias|bind|builtin|caller|command|declare|echo|enable|help|let|local|logout|mapfile|printf|read|readarray|source|type|typeset|ulimit|unalias|set|shopt)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:true|false)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var s=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],i=a.variable[1].inside,o=0;o>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},Prism.languages.c.string],comment:Prism.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:Prism.languages.c}}},constant:/\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/}),delete Prism.languages.c.boolean; +!function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char8_t|char16_t|char32_t|class|compl|concept|const|consteval|constexpr|constinit|const_cast|continue|co_await|co_return|co_yield|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n="\\b(?!)\\w+(?:\\s*\\.\\s*\\w+)*\\b".replace(//g,function(){return t.source});e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp("(\\b(?:class|concept|enum|struct|typename)\\s+)(?!)\\w+".replace(//g,function(){return t.source})),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:true|false)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp('(\\b(?:module|import)\\s+)(?:"(?:\\\\(?:\r\n|[^])|[^"\\\\\r\n])*"|<[^<>\r\n]*>|'+"(?:\\s*:\\s*)?|:\\s*".replace(//g,function(){return n})+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(Prism); +!function(e){for(var a="/\\*(?:[^*/]|\\*(?!/)|/(?!\\*)|)*\\*/",t=0;t<2;t++)a=a.replace(//g,function(){return a});a=a.replace(//g,function(){return"[^\\s\\S]"}),e.languages.rust={comment:[{pattern:RegExp("(^|[^\\\\])"+a),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/,greedy:!0,alias:"string"},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|Self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:[ui](?:8|16|32|64|128|size)|f(?:32|64)|bool|char|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\b\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:[iu](?:8|16|32|64|size)?|f32|f64))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<>?=?|[@?]/},e.languages.rust["closure-params"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string}(Prism); +Prism.languages.scss=Prism.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/m,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),Prism.languages.insertBefore("scss","atrule",{keyword:[/@(?:if|else(?: if)?|forward|for|each|while|import|use|extend|debug|warn|mixin|include|function|return|content)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),Prism.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),Prism.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|with|show|hide)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:true|false)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|or|not)(?=\s)/,lookbehind:!0}}),Prism.languages.scss.atrule.inside.rest=Prism.languages.scss; diff --git a/server/static/prose.css b/server/static/prose.css new file mode 100644 index 0000000000..03bffb1196 --- /dev/null +++ b/server/static/prose.css @@ -0,0 +1,253 @@ +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 */ \ No newline at end of file diff --git a/server/static/prose.css.map b/server/static/prose.css.map new file mode 100644 index 0000000000..0125373da8 --- /dev/null +++ b/server/static/prose.css.map @@ -0,0 +1,9 @@ +{ + "version": 3, + "mappings": "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", + "sources": [ + "prose.scss" + ], + "names": [], + "file": "prose.css" +} \ No newline at end of file diff --git a/server/static/prose.scss b/server/static/prose.scss new file mode 100644 index 0000000000..e034fdd7a2 --- /dev/null +++ b/server/static/prose.scss @@ -0,0 +1,260 @@ +// Style prose by hand +// add .prose to any
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 */ +// } diff --git a/server/static/site.webmanifest b/server/static/site.webmanifest new file mode 100644 index 0000000000..f69ceec6eb --- /dev/null +++ b/server/static/site.webmanifest @@ -0,0 +1,18 @@ +{ + "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" +} diff --git a/server/styles.css b/server/styles.css index ab10493e32..33af3f1033 100644 --- a/server/styles.css +++ b/server/styles.css @@ -1,108 +1,136 @@ /* This file is compiled to /assets/styles/tailwind.css via script/tailwind */ -@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap'); - -@font-face { - font-family: 'Visby CF'; - src: - url('/static/fonts/VisbyCF-Thin.woff2') format('woff2'), - url('/static/fonts/VisbyCF-Thin.woff') format('woff'); - font-weight: 100; - font-style: normal; -} - - -@font-face { - font-family: 'Visby CF'; - src: - url('/static/fonts/VisbyCF-Light.woff2') format('woff2'), - url('/static/fonts/VisbyCF-Light.woff') format('woff'); - font-weight: 300; - font-style: normal; -} - -@font-face { - font-family: 'Visby CF'; - src: - url('/static/fonts/VisbyCF-Regular.woff2') format('woff2'), - url('/static/fonts/VisbyCF-Regular.woff') format('woff'); - font-weight: 400; - font-style: normal; -} - -@font-face { - font-family: 'Visby CF'; - src: - url('/static/fonts/VisbyCF-Medium.woff2') format('woff2'), - url('/static/fonts/VisbyCF-Medium.woff') format('woff'); - font-weight: 500; - font-style: normal; -} - -@font-face { - font-family: 'Visby CF'; - src: - url('/static/fonts/VisbyCF-DemiBold.woff2') format('woff2'), - url('/static/fonts/VisbyCF-DemiBold.woff') format('woff'); - font-weight: 600; - font-style: normal; -} - -@font-face { - font-family: 'Visby CF'; - src: - url('/static/fonts/VisbyCF-Bold.woff2') format('woff2'), - url('/static/fonts/VisbyCF-Bold.woff') format('woff'); - font-weight: 700; - font-style: normal; -} - -@font-face { - font-family: 'Visby CF'; - src: - url('/static/fonts/VisbyCF-ExtraBold.woff2') format('woff2'), - url('/static/fonts/VisbyCF-ExtraBold.woff') format('woff'); - font-weight: 800; - font-style: normal; -} - -@font-face { - font-family: 'Visby CF'; - src: - url('/static/fonts/VisbyCF-Heavy.woff2') format('woff2'), - url('/static/fonts/VisbyCF-Heavy.woff') format('woff'); - font-weight: 900; - font-style: normal; -} +@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 utilities { - @responsive { - .bg-dotgrid-sm { - background: - linear-gradient(90deg, theme('colors.gray.50') 38px, transparent 1%) center, - linear-gradient(theme('colors.gray.50') 38px, transparent 1%) center, - theme('colors.gray.600'); - background-size: 40px 40px; - } +@layer base { + html { + font-size: 110%; + text-rendering: geometricPrecision; + } - .bg-dotgrid-md { - background: - linear-gradient(90deg, theme('colors.gray.50') 58px, transparent 1%) center, - linear-gradient(theme('colors.gray.50') 58px, transparent 1%) center, - theme('colors.gray.600'); - background-size: 60px 60px; - } - - .bg-dotgrid-lg { - background: - linear-gradient(90deg, theme('colors.gray.50') 88px, transparent 1%) center, - linear-gradient(theme('colors.gray.50') 88px, transparent 1%) center, - theme('colors.gray.600'); - background-size: 90px 90px; - } + @media screen and (min-width: 1024px) { + html { + font-size: 125%; } -} \ No newline at end of file + } + + 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); + } +} diff --git a/server/templates/admin.hbs b/server/templates/admin.hbs index 4dbce023bb..d992e92c7b 100644 --- a/server/templates/admin.hbs +++ b/server/templates/admin.hbs @@ -20,62 +20,80 @@ }); -
-
-

Users

- - - - - - - - - - - - +
+
+

Admin

+ +
+ +

Users

+ +
+
+

Github Username

+

Admin

+

+
+
+ +
+

+

+

+
+
+
+ +
+
+

Github Username

+

Admin

+

+
+ {{#each users}} +
+
+
+

{{github_login}}

+

+

+
+ +
+ {{/each}} +
- {{#each users}} -
- - - - - - - {{/each}} -
GitHub LoginAdmin
- - - - - -
- {{github_login}} - - - - -
+

Signups

-

Signups

- - {{#each signups}} - - - - - - - - - {{/each}} -
{{github_login}}{{email_address}}{{about}} - -
+
+ +
+

Email

+

Github

+

Releases

+

Updates

+

Community

+

Remove

+
+ {{#each signups}} +
+
+
+

{{email_address}}

+

{{github_login}}

+

{{#if wants_releases}}[✓]{{else}}[ ]{{/if}}

+

{{#if wants_updates}}[✓]{{else}}[ ]{{/if}}

+

{{#if wants_community}}[✓]{{else}}[ ]{{/if}}

+

+
+
+

{{about}}

+
+ {{/each}}
{{/layout}} \ No newline at end of file diff --git a/server/templates/community.hbs b/server/templates/community.hbs new file mode 100644 index 0000000000..dfae551cdb --- /dev/null +++ b/server/templates/community.hbs @@ -0,0 +1,107 @@ +{{#> layout }} + +
+

We’re building a community of passionate developers & advocates.

+ +

If you would like to get involved early, sign up below and we will let you know when our developer community opens!

+ +
+
    +
  • + + + + + + + + + + + + + +
    + +

    + We'll put you on the list to play with an early beta and let you know when Zed launches. +

    +
    +
  • +
  • + + + + + + + + + + + + + + +
    + +

    + We'll send you occasional updates about our progress and share what we're learning along the way. +

    +
    +
  • +
  • + + + + + + + + + + + + + + + +
    + +

    + We'll add you to the waitlist to receive early builds and ask you to help us by testing them out. +

    +
    +
  • +
+
+ + + +
+ + + + + +

+ We're not gonna spam you—You can expect an email every 1 to 3 months from us if you sign up for updates, and occasional emails about beta releases. +

+
+
+ +{{/layout}} diff --git a/server/templates/error.hbs b/server/templates/error.hbs index 6013b2de80..4d0a99d27b 100644 --- a/server/templates/error.hbs +++ b/server/templates/error.hbs @@ -1,7 +1,15 @@ {{#> layout }} -
-
- Sorry, we encountered a {{status}} error: {{reason}}. -
+ +
+
+

Sorry!

+

+ Looks like we encountered a {{status}} error: {{reason}} +

+

+ Try refreshing or going home. +

+
+ {{/layout}} \ No newline at end of file diff --git a/server/templates/home.hbs b/server/templates/home.hbs index 83dc711209..da2fe8f976 100644 --- a/server/templates/home.hbs +++ b/server/templates/home.hbs @@ -1,69 +1,104 @@ {{#> layout }} -{{#if releases}} -
-
- {{#each releases}} -
-
-
- VERSION {{name}} -
- - DOWNLOAD - -
-
- {{{body}}} -
-
- {{/each}} -
+
+
+

Introducing Zed—A lightning-fast, collaborative code editor written in Rust.

+

+ We think there’s a better way to write code, and it starts with the following assumptions: +

+ +

Mission-critical tools should be hyper-responsive.

+ +

+ When you move the cursor or type a character, you should see pixels on the next refresh of your display—every time. Even sub-perceptual pauses add up over the course of a day to create unnecessary stress. +

+ +

+ Joyful coding starts with an editor that stays out of your way. +

+ +

Real-time collaboration produces better software.

+ +

+ 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. +

+ +

+ 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. +

+ +

Conversations about software should happen close to the code.

+ +

+ 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. +

+ +

+ 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. +

+ +

Our goal is to make you as efficient as possible.

+ +

+ 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. +

+ +

+ 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. +

+ +

———

+ +

Under the hood

+ +
+

Building in Rust

+

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.

+ +

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.

+ +

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.

+ +

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.

+
+ +
+

A new GPU-powered UI framework

+

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.

+ +

We call it GPUI.

+ +

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?

+ +

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.

+ +

It’s liberating to control every pixel, and it’s a rush to push those pixels at lightning speed.

+
+ +
+

Conflict-free replicated data types

+ +

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.

+ +

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.

+ +

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.

+ +

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.

+
+ +
+

Tree-sitter

+ +

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.

+ +

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.

+
+ +

———

+ +

Excited about what we are building? Sign up for updates to follow along in our development process.

+
-{{else}} - -
- -
- -
-
-

- We’re the team behind GitHub’s Atom text editor, and we’re building something new: -

- -

- Zed is a fully-native desktop code editor focused on high performance, - clean design, and seamless collaboration. -

- -

- We’re in early development, but we’d like to build a small community of developers who care deeply about - their tools and are willing to give us feedback. We'll be sharing alpha builds with community members and - telling our story along the way. -

- -

- If you’re interested in joining us, please let us know. -

-
- -
- - - - -
-
- -{{/if}} {{/layout}} \ No newline at end of file diff --git a/server/templates/partials/layout.hbs b/server/templates/partials/layout.hbs index 1e4a3561bd..3a39eeda3c 100644 --- a/server/templates/partials/layout.hbs +++ b/server/templates/partials/layout.hbs @@ -1,62 +1,193 @@ - + + + + + + + + + - Zed Industries + + + + + + + + + + + + + + + + + Zed - - -
-
- - ZEDINDUSTRIES - -
- - Team - + +
+ + +
-
+ - {{> @partial-block}} +
+ {{> @partial-block}} +
+ + + + \ No newline at end of file diff --git a/server/templates/releases.hbs b/server/templates/releases.hbs new file mode 100644 index 0000000000..ab02c16822 --- /dev/null +++ b/server/templates/releases.hbs @@ -0,0 +1,35 @@ +{{#> layout }} + +
+ {{#if current_user}} +

Releases

+

Zed is currently only available on macOS.

+

We are frequently shipping new versions, check back regularly to get the most recent version.

+ + {{#if releases}} + + {{#each releases}} + + {{/each}} + {{/if}} + + {{else}} + +

Hold it!

+

You can't access this without logging in.

+

Try joining our community to get access to updates & releases.

+ {{/if}} +
+ +{{/layout}} diff --git a/server/templates/signup.hbs b/server/templates/signup.hbs index 738cf2e6e6..5a032f457b 100644 --- a/server/templates/signup.hbs +++ b/server/templates/signup.hbs @@ -1,19 +1,18 @@ {{#> layout }} -
-
-
- THANKS -
-
-

- Thanks a ton for your interest! We'll add you to our list and let you know when we have something ready - for you to try out. -

-

- Back to / -

-
-
+
+

Thanks for signing up!

+

+ We'll add you to our list and let you know when we have something ready for you to try out. +

+ +

+ Thanks for your interest! +

+ + + Back to Home +
-{{/layout}} \ No newline at end of file + +{{/layout}} diff --git a/server/templates/team.hbs b/server/templates/team.hbs index e02f284c0c..82ac66ade3 100644 --- a/server/templates/team.hbs +++ b/server/templates/team.hbs @@ -1,62 +1,160 @@ {{#> layout }} -
-
-
- -
- - NATHAN SOBO - -
- Nathan joined GitHub in late 2011 to build the Atom text editor, and - he led the Atom team until 2018. He also co-led development of Teletype for Atom, pioneering one of the first production - uses of conflict-free replicated data types for collaborative text editing. 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. +
+

+ We think there’s a better way to write code—and we've been working for more than a decade to bring it into existence. +

+

+ 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. +

+

+ 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. +

+

+ 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. +

+

+ 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! +

+ +

The Team

+
+
+
+

Nathan Sobo

+
+

Nathan joined GitHub in late 2011 to build the Atom text editor, and he led the Atom team until 2018.

+ +

He also co-led development of Teletype for Atom, pioneering one of the first production uses of conflict-free replicated data types for collaborative text editing.

+ +

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.

+ + +
+

———

+
+
+ +
+
+

Antonio Scandurra

+
+

Antonio joined the Atom team in 2014 while still in university after his outstanding open source contributions caught the attention of the team.

+ +

He later joined Nathan in architecting Teletype for Atom and researching the foundations of what has turned into Zed.

+ +

For the last two years, he’s become an expert in distributed systems and conflict-free replicated data types through the development of a real-time, distributed, conflict-free database implemented in Rust for Ditto.

+ +
-
-
- -
- - ANTONIO SCANDURRA - -
- Antonio joined the Atom team in 2014 while still in university after his outstanding open source - contributions caught the attention of the team. He later joined Nathan in architecting Teletype for - Atom and researching the foundations of what has turned into Zed. For the last two years, - he’s - become an expert in distributed systems and conflict-free replicated data types through the - development of a real-time, distributed, conflict-free database implemented in Rust for Ditto. +

———

+
+ +
+
+

Max Brunsfeld

+
+

Max joined the Atom team in 2013 after working at Pivotal Labs.

+ +

While driving Atom towards its 1.0 launch during the day, Max spent nights and weekends building Tree-sitter, a blazing-fast and expressive incremental parsing framework that currently powers all code analysis at GitHub.

+ +

Before leaving to start Zed, Max helped GitHub's semantic analysis team integrate Tree-sitter to support syntax highlighting and code navigation on github.com.

+ +
- -
- -
- - MAX BRUNSFELD - -
- Max joined the Atom team in 2013 after working at Pivotal Labs. While driving Atom towards its 1.0 - launch during the day, Max spent nights and weekends building Tree-sitter, a blazing-fast and - expressive incremental parsing framework that currently powers all code analysis at GitHub. Before - leaving to start Zed, Max helped GitHub's semantic analysis team integrate Tree-sitter to support - syntax highlighting and code navigation on github.com. +

———

+
+ +
+
+

Nate Butler

+
+

Nate joined the Facebook team in 2015 as a product designer on News Feed, after spending the previous few years working on sites like Format.com and Lookbook.com.

+ +

He spent three years shipping creative tools and projects like Facebook Avatars. He later moved on to working on dev tools and design systems, focused on developer efficiency.

+ +

Before leaving to join Zed, Nate spent his last year at Facebook developing a future vision for Stories.

+ +
- +

———

+
+ +
+
+

Want to join us?

+
+

We hope to bring a few more staff-level engineers in the near future. Come join us in building the next generation of software.

+ +

Drop us a line.

+
+
+
+ + + - {{/layout}}