diff --git a/.github/actions/glob/entrypoint.sh b/.github/actions/glob/entrypoint.sh deleted file mode 100755 index 7428213a5b..0000000000 --- a/.github/actions/glob/entrypoint.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -cd "$GITHUB_WORKSPACE" || exit - -echo "$2" | base64 -d > service-account -echo "$3" | base64 -d > id_ssh -echo "$4" | base64 -d > id_ssh.pub - -chmod 600 service-account -chmod 600 id_ssh -chmod 600 id_ssh.pub - -janeway release glob-all --dev --no-pill \ - --credentials service-account \ - --ssh-key id_ssh \ - --ci \ - | bash - -SHORTHASH=$(git rev-parse --short HEAD) - -janeway release prepare-ota arvo-glob-"$SHORTHASH" "$1" \ - --credentials service-account \ - --ssh-key id_ssh \ - --ci \ - | bash - -janeway release perform-ota "$1" \ - --credentials service-account \ - --ssh-key id_ssh \ - --ci \ - | bash - diff --git a/.github/actions/glob/Dockerfile b/.github/actions/ota/Dockerfile similarity index 72% rename from .github/actions/glob/Dockerfile rename to .github/actions/ota/Dockerfile index e372532acd..bee1889179 100644 --- a/.github/actions/glob/Dockerfile +++ b/.github/actions/ota/Dockerfile @@ -1,4 +1,4 @@ -FROM tloncorp/janeway:v0.15.4 +FROM tloncorp/janeway:v0.17.0 COPY entrypoint.sh /entrypoint.sh EXPOSE 22/tcp ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/actions/glob/action.yml b/.github/actions/ota/action.yml similarity index 52% rename from .github/actions/glob/action.yml rename to .github/actions/ota/action.yml index 4617e3d494..f9b0d027e3 100644 --- a/.github/actions/glob/action.yml +++ b/.github/actions/ota/action.yml @@ -1,18 +1,21 @@ -name: 'glob' -description: 'Create a glob and deploy it to a moon' +name: 'ota' +description: 'perform an OTA update of arvo on a remote ship' inputs: ship: - description: "Ship to deploy to" + description: "target ship" required: true credentials: description: "base64-encoded GCP Service Account credentials" required: true ssh-sec-key: - description: "A base64-encoded SSH secret key for the container to use" + description: "base64-encoded SSH secret key for the container to use" required: true ssh-pub-key: - description: "The corresponding base64-encoded SSH public key" + description: "base64-encoded corresponding SSH public key" required: true + ref: + description: "git ref of arvo source to check out" + required: false runs: using: 'docker' @@ -22,4 +25,4 @@ runs: - ${{ inputs.credentials }} - ${{ inputs.ssh-sec-key }} - ${{ inputs.ssh-pub-key }} - + - ${{ inputs.ref }} diff --git a/.github/actions/ota/entrypoint.sh b/.github/actions/ota/entrypoint.sh new file mode 100755 index 0000000000..5839a49f4f --- /dev/null +++ b/.github/actions/ota/entrypoint.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +echo "$2" | base64 -d > /service-account +echo "$3" | base64 -d > /id_ssh +echo "$4" | base64 -d > /id_ssh.pub + +chmod 600 /service-account +chmod 600 /id_ssh +chmod 600 /id_ssh.pub + +janeway \ + --ci \ + --verbose \ + --credentials /service-account \ + --ssh-key /id_ssh \ + release ota \ + arvo \ + "$1" \ + ${5:+"--ref"} ${5:+"$5"} \ + | bash diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml deleted file mode 100644 index 1d8711f7f4..0000000000 --- a/.github/workflows/chromatic.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Chromatic Deployment - -on: - pull_request: - paths: - - 'pkg/interface/**' - push: - paths: - - 'pkg/interface/**' - branches: - - 'release/next-userspace' - -jobs: - chromatic-deployment: - runs-on: ubuntu-latest - name: "Deploy Chromatic" - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - run: npm i && npm run bootstrap - - name: Publish to Chromatic - uses: chromaui/action@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} - workingDir: pkg/interface diff --git a/.github/workflows/glob.yml b/.github/workflows/glob.yml deleted file mode 100644 index be5d77115c..0000000000 --- a/.github/workflows/glob.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: glob -on: - push: - branches: - - 'release/next-userspace' -jobs: - glob: - runs-on: ubuntu-latest - name: "Create and deploy a glob to ~hanruc-nalfus-nidsut-tomdun" - steps: - - uses: actions/checkout@v2 - with: - lfs: true - - uses: ./.github/actions/glob - with: - ship: 'hanruc-nalfus-nidsut-tomdun' - credentials: ${{ secrets.JANEWAY_SERVICE_KEY }} - ssh-sec-key: ${{ secrets.JANEWAY_SSH_SEC_KEY }} - ssh-pub-key: ${{ secrets.JANEWAY_SSH_PUB_KEY }} - diff --git a/.github/workflows/merge-master.yml b/.github/workflows/merge-master.yml deleted file mode 100644 index 48d0abf9fb..0000000000 --- a/.github/workflows/merge-master.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: merge -on: - push: - branches: - - 'master' -jobs: - merge-to-next-js: - runs-on: ubuntu-latest - name: "Merge master to release/next-userspace" - steps: - - uses: actions/checkout@v2 - - uses: devmasx/merge-branch@v1.3.1 - with: - type: now - target_branch: release/next-userspace - github_token: ${{ secrets.JANEWAY_BOT_TOKEN }} - - merge-to-group-timer: - runs-on: ubuntu-latest - name: "Merge master to ops/group-timer" - steps: - - uses: actions/checkout@v2 - - uses: devmasx/merge-branch@v1.3.1 - with: - type: now - target_branch: ops/group-timer - github_token: ${{ secrets.JANEWAY_BOT_TOKEN }} diff --git a/.github/workflows/merge-release.yml b/.github/workflows/merge-release.yml deleted file mode 100644 index eb7df325ab..0000000000 --- a/.github/workflows/merge-release.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: ops-merge -on: - push: - branches: - - 'release/*' -jobs: - merge-release-to-ops: - runs-on: ubuntu-latest - name: "Merge to ops-tlon" - steps: - - uses: actions/checkout@v2 - - uses: devmasx/merge-branch@v1.3.1 - with: - type: now - target_branch: ops-tlon - github_token: ${{ secrets.JANEWAY_BOT_TOKEN }} - diff --git a/.github/workflows/ota.yml b/.github/workflows/ota.yml new file mode 100644 index 0000000000..d4742e4784 --- /dev/null +++ b/.github/workflows/ota.yml @@ -0,0 +1,19 @@ +name: ota +on: + workflow_dispatch: + push: + branches: + - 'next/arvo' +jobs: + deploy: + runs-on: ubuntu-latest + name: "make an OTA update to arvo on ~binnec-dozzod-marzod" + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/ota + with: + ship: 'canary' + credentials: ${{ secrets.JANEWAY_SERVICE_KEY }} + ssh-sec-key: ${{ secrets.JANEWAY_SSH_SEC_KEY }} + ssh-pub-key: ${{ secrets.JANEWAY_SSH_PUB_KEY }} + ref: 'next/arvo' diff --git a/bin/brass.pill b/bin/brass.pill index ad7da6640a..a6baa096b0 100644 --- a/bin/brass.pill +++ b/bin/brass.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4247c64a7d9fc0c0f1d2f017c21dd3464ddfe56529c7d6eef0e64554bd453e8 -size 7611162 +oid sha256:bd487cdb8294fdef6878f623bceb893553b36b2a616d22d30017b430361586fb +size 3889185 diff --git a/bin/ivory.pill b/bin/ivory.pill index c782fb7250..3a6b069222 100644 --- a/bin/ivory.pill +++ b/bin/ivory.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5123a1ac30b83ec026587574df1ce13a73e72d06588ff68b5c41c09e1bebb5b7 -size 949962 +oid sha256:26ff86808886beb831e4a135f478e42ce83ef4a09ad24808b3fe97248ce7a6b7 +size 1136643 diff --git a/bin/solid.pill b/bin/solid.pill index c8d27030b2..b4afef2968 100644 --- a/bin/solid.pill +++ b/bin/solid.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40bc203b8a2d2ebad81723da6fc946ee32d2f8a204884f50f9710177ae257d08 -size 5712264 +oid sha256:f4d585474b6df173dc1c172f343192ac88bd3ced1a9c7cf6877402722ac139de +size 5748201 diff --git a/doc/spec/nock/10.txt b/doc/spec/nock/10.txt new file mode 100644 index 0000000000..27d1e8c730 --- /dev/null +++ b/doc/spec/nock/10.txt @@ -0,0 +1,70 @@ +Author: Mencius Moldbug [moldbug@gmail.com] +Date: 9/15/2008 +Version: 10K + +1. Introduction + + This file defines one function, "nock." + + nock is in the public domain. + +2. Data + + A "noun" is either an "atom" or a "cell." An "atom" is an unsigned + integer of any size. A "cell" is an ordered pair of any two nouns, + the "head" and "tail." + +3. Semantics + + nock maps one noun to another. It doesn't always terminate. + +4. Pseudocode + + nock is defined in a pattern-matching pseudocode, below. + + Brackets enclose cells. [a b c] is [a [b c]]. + +5. Definition + +5.1 Transformations + + *[a [b c] d] => [*[a b c] *[a d]] + *[a 0 b] => /[b a] + *[a 1 b] => [b] + *[a 2 b c d] => *[a 3 [0 1] 3 [1 c d] [1 0] 3 [1 2 3] [1 0] 5 5 b] + *[a 3 b] => **[a b] + *[a 4 b] => &*[a b] + *[a 5 b] => ^*[a b] + *[a 6 b] => =*[a b] + *[a] => *[a] + +5.2 Operators + +5.2.1 Goto [*] + + *[a] -> nock[a] + +5.2.2 Deep [&] + + &[a b] -> 0 + &[a] -> 1 + +5.2.4 Bump [^] + + ^[a b] -> ^[a b] + ^[a] -> (a + 1) + +5.2.5 Like [=] + + =[a a] -> 0 + =[a b] -> 1 + =[a] -> =[a] + +5.2.6 Snip [/] + + /[1 a] -> a + /[2 a b] -> a + /[3 a b] -> b + /[(a + a) b] -> /[2 /[a b]] + /[(a + a + 1) b] -> /[3 /[a b]] + /[a] -> /[a] diff --git a/doc/spec/nock/11.txt b/doc/spec/nock/11.txt new file mode 100644 index 0000000000..da9dafb91b --- /dev/null +++ b/doc/spec/nock/11.txt @@ -0,0 +1,74 @@ +Author: Mencius Moldbug (moldbug@gmail.com) +Date: 5/25/2008 +Version: 11K + +1. Introduction + + This file defines one function, "nock." + + nock is in the public domain. + +2. Data + + A "noun" is either an "atom" or a "cell." An "atom" is an unsigned + integer of any size. A "cell" is an ordered pair of any two nouns, + the "head" and "tail." + +3. Semantics + + nock maps one noun to another. It doesn't always terminate. + +4. Pseudocode + + nock is defined in a pattern-matching pseudocode, below. + + Parentheses enclose cells. (a b c) is (a (b c)). + +5. Definition + +5.1 Transformations + + *(a (b c) d) => (*(a b c) *(a d)) + *(a 0 b) => /(b a) + *(a 1 b) => (b) + *(a 2 b c d) => *(a 3 (0 1) 3 (1 c d) (1 0) 3 (1 2 3) (1 0) 5 5 b) + *(a 3 b) => **(a b) + *(a 4 b) => &*(a b) + *(a 5 b) => ^*(a b) + *(a 6 b) => =*(a b) + + *(a 7 b c) => *(a 3 (((1 0) b) c) 1 0 3) + *(a 8 b c) => *(a c) + + *(a) => *(a) + +5.2 Operators + +5.2.1 Goto (*) + + *(a) -> nock(a) + +5.2.2 Deep (&) + + &(a b) -> 0 + &(a) -> 1 + +5.2.4 Bump (^) + + ^(a b) -> ^(a b) + ^(a) -> a + 1 + +5.2.5 Same (=) + + =(a a) -> 0 + =(a b) -> 1 + =(a) -> =(a) + +5.2.6 Snip (/) + + /(1 a) -> a + /(2 a b) -> a + /(3 a b) -> b + /((a + a) b) -> /(2 /(a b)) + /((a + a + 1) b) -> /(3 /(a b)) + /(a) -> /(a) diff --git a/doc/spec/nock/12.txt b/doc/spec/nock/12.txt new file mode 100644 index 0000000000..d3762e8557 --- /dev/null +++ b/doc/spec/nock/12.txt @@ -0,0 +1,75 @@ +Author: Curtis Yarvin (curtis.yarvin@gmail.com) +Date: 3/28/2008 +Version: 0.12 + +1. Introduction + + This file defines one function, "nock." + + nock is in the public domain. + +2. Data + + A "noun" is either an "atom" or a "cell." An "atom" is an unsigned + integer of any size. A "cell" is an ordered pair of any two nouns, + the "head" and "tail." + +3. Semantics + + nock maps one noun to another. It doesn't always terminate. + +4. Pseudocode + + nock is defined in a pattern-matching pseudocode, below. + + Parentheses enclose cells. (a b c) is (a (b c)). + +5. Definition + +5.1 Transformations + + *(a (b c) d) => (*(a b c) *(a d)) + *(a 0 b) => /(b a) + *(a 1 b) => (b) + *(a 2 b c) => *(*(a b) c) + *(a 3 b) => **(a b) + *(a 4 b) => &*(a b) + *(a 5 b) => ^*(a b) + *(a 6 b) => =*(a b) + + *(a 7 b c d) => *(a 3 (0 1) 3 (1 c d) (1 0) 3 (1 2 3) (1 0) 5 5 b) + *(a 8 b c) => *(a 2 (((1 0) b) c) 0 3) + *(a 9 b c) => *(a c) + + *(a) => *(a) + +5.2 Operators + +5.2.1 Goto (*) + + *(a) -> nock(a) + +5.2.2 Deep (&) + + &(a b) -> 0 + &(a) -> 1 + +5.2.4 Bump (^) + + ^(a b) -> ^(a b) + ^(a) -> a + 1 + +5.2.5 Same (=) + + =(a a) -> 0 + =(a b) -> 1 + =(a) -> =(a) + +5.2.6 Snip (/) + + /(1 a) -> a + /(2 a b) -> a + /(3 a b) -> b + /((a + a) b) -> /(2 /(a b)) + /((a + a + 1) b) -> /(3 /(a b)) + /(a) -> /(a) diff --git a/doc/spec/nock/13.txt b/doc/spec/nock/13.txt new file mode 100644 index 0000000000..2295e5ead5 --- /dev/null +++ b/doc/spec/nock/13.txt @@ -0,0 +1,71 @@ +Author: Curtis Yarvin (curtis.yarvin@gmail.com) +Date: 3/8/2008 +Version: 0.13 + +1. Manifest + + This file defines one Turing-complete function, "nock." + + nock is in the public domain. So far as I know, it is + neither patentable nor patented. Use it at your own risk. + +2. Data + + Both the domain and range of nock are "nouns." + + A "noun" is either an "atom" or a "cell." An "atom" is an unsigned + integer of any size. A "cell" is an ordered pair of any two nouns, + the "head" and "tail." + +3. Pseudocode + + nock is defined in a pattern-matching pseudocode. + + Match precedence is top-down. Operators are prefix. Parens + denote cells, and group right: (a b c) is (a (b c)). + +4. Definition + +4.1 Transformations + + *(a 0 b c) => *(*(a b) c) + *(a 0 b) => /(b a) + *(a 1 b) => (b) + *(a 2 b) => **(a b) + *(a 3 b) => &*(a b) + *(a 4 b) => ^*(a b) + *(a 5 b) => =*(a b) + *(a 6 b c d) => *(a 2 (0 1) 2 (1 c d) (1 0) 2 (1 2 3) (1 0) 4 4 b) + *(a b c) => (*(a b) *(a c)) + *(a) => *(a) + +4.2 Operators + +4.2.1 Goto (*) + + *(a) -> nock(a) + +4.2.2 Deep (&) + + &(a b) -> 0 + &(a) -> 1 + +4.2.3 Bump (^) + + ^(a b) -> ^(a b) + ^(a) -> a + 1 + +4.2.4 Same (=) + + =(a a) -> 0 + =(a b) -> 1 + =(a) -> =(a) + +4.2.5 Snip (/) + + /(1 a) -> a + /(2 a b) -> a + /(3 a b) -> b + /((a + a) b) -> /(2 /(a b)) + /((a + a + 1) b) -> /(3 /(a b)) + /(a) -> /(a) diff --git a/doc/spec/nock/6.txt b/doc/spec/nock/6.txt new file mode 100644 index 0000000000..1dbd599583 --- /dev/null +++ b/doc/spec/nock/6.txt @@ -0,0 +1,42 @@ +1 Structures + + A noun is an atom or a cell. An atom is any natural number. + A cell is an ordered pair of nouns. + +2 Reductions + + nock(a) *a + [a b c] [a [b c]] + + ?[a b] 0 + ?a 1 + +a 1 + a + =[a a] 0 + =[a b] 1 + + /[1 a] a + /[2 a b] a + /[3 a b] b + /[(a + a) b] /[2 /[a b]] + /[(a + a + 1) b] /[3 /[a b]] + + *[a [b c] d] [*[a b c] *[a d]] + + *[a 0 b] /[b a] + *[a 1 b] b + *[a 2 b c] *[*[a b] *[a c]] + *[a 3 b] ?*[a b] + *[a 4 b] +*[a b] + *[a 5 b] =*[a b] + + *[a 6 b c d] *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b] + *[a 7 b c] *[a 2 b 1 c] + *[a 8 b c] *[a 7 [[0 1] b] c] + *[a 9 b c] *[a 7 c 0 b] + *[a 10 b c] *[a c] + *[a 10 [b c] d] *[a 8 c 7 [0 2] d] + + +[a b] +[a b] + =a =a + /a /a + *a *a diff --git a/doc/spec/nock/7.txt b/doc/spec/nock/7.txt new file mode 100644 index 0000000000..bd3e295083 --- /dev/null +++ b/doc/spec/nock/7.txt @@ -0,0 +1,42 @@ +1 Structures + + A noun is an atom or a cell. An atom is any natural number. + A cell is any ordered pair of nouns. + +2 Pseudocode + + [a b c] [a [b c]] + nock(a) *a + + ?[a b] 0 + ?a 1 + ^a 1 + a + =[a a] 0 + =[a b] 1 + + /[1 a] a + /[2 a b] a + /[3 a b] b + /[(a + a) b] /[2 /[a b]] + /[(a + a + 1) b] /[3 /[a b]] + + *[a [b c] d] [*[a b c] *[a d]] + + *[a 0 b] /[b a] + *[a 1 b] b + *[a 2 b c] *[*[a b] *[a c]] + *[a 3 b] ?*[a b] + *[a 4 b] ^*[a b] + *[a 5 b] =*[a b] + + *[a 6 b c d] *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b] + *[a 7 b c] *[a 2 b 1 c] + *[a 8 b c] *[a 7 [[7 [0 1] b] 0 1] c] + *[a 9 b c] *[a 7 c 0 b] + *[a 10 b c] *[a c] + *[a 10 [b c] d] *[a 8 c 7 [0 3] d] + + ^[a b] ^[a b] + =a =a + /a /a + *a *a diff --git a/doc/spec/nock/8.txt b/doc/spec/nock/8.txt new file mode 100644 index 0000000000..2981d6c5f7 --- /dev/null +++ b/doc/spec/nock/8.txt @@ -0,0 +1,45 @@ +1 Structures + + A noun is an atom or a cell. An atom is any unsigned integer. + A cell is an ordered pair of nouns. + +2 Pseudocode + + [a b c] is [a [b c]]; *a is nock(a). Reductions match top-down. + +3 Reductions + + ?[a b] 0 + ?a 1 + ^a (a + 1) + =[a a] 0 + =[a b] 1 + + /[1 a] a + /[2 a b] a + /[3 a b] b + /[(a + a) b] /[2 /[a b]] + /[(a + a + 1) b] /[3 /[a b]] + + *[a [b c] d] [*[a b c] *[a d]] + *[a 0 b] /[b a] + *[a 1 b] b + *[a 2 b c] *[*[a b] *[a c]] + *[a 3 b] ?*[a b] + *[a 4 b] ^*[a b] + *[a 5 b] =*[a b] + + *[a 6 b c d] *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b] + *[a 7 b c] *[a 2 b 1 c] + *[a 8 b c] *[a 7 [7 b [0 1]] c] + *[a 9 b c] *[a 8 b 2 [[7 [0 3] d] [0 5]] 0 5] + *[a 10 b c] *[a 8 b 8 [7 [0 3] c] 0 2] + *[a 11 b c] *[a 8 b 7 [0 3] c] + *[a 12 b c] *[a [1 0] 1 c] + + ^[a b] ^[a b] + =a =a + /a /a + *a *a + + diff --git a/doc/spec/nock/9.txt b/doc/spec/nock/9.txt new file mode 100644 index 0000000000..b6aabad768 --- /dev/null +++ b/doc/spec/nock/9.txt @@ -0,0 +1,43 @@ +1 Context + + This spec defines one function, Nock. + +2 Structures + + A noun is an atom or a cell. An atom is any unsigned integer. + A cell is an ordered pair of any two nouns. + +3 Pseudocode + + Brackets enclose cells. [a b c] is [a [b c]]. + + *a is Nock(a). Reductions match top-down. + +4 Reductions + + ?[a b] => 0 + ?a => 1 + + ^[a b] => ^[a b] + ^a => (a + 1) + + =[a a] => 0 + =[a b] => 1 + =a => =a + + /[1 a] => a + /[2 a b] => a + /[3 a b] => b + /[(a + a) b] => /[2 /[a b]] + /[(a + a + 1) b] => /[3 /[a b]] + /a => /a + + *[a 0 b] => /[b a] + *[a 1 b] => b + *[a 2 b c d] => *[a 3 [0 1] 3 [1 c d] [1 0] 3 [1 2 3] [1 0] 5 5 b] + *[a 3 b] => **[a b] + *[a 4 b] => ?*[a b] + *[a 5 b] => ^*[a b] + *[a 6 b] => =*[a b] + *[a [b c] d] => [*[a b c] *[a d]] + *a => *a diff --git a/pkg/arvo/app/dbug.hoon b/pkg/arvo/app/dbug.hoon index fbec14e292..90c17c22df 100644 --- a/pkg/arvo/app/dbug.hoon +++ b/pkg/arvo/app/dbug.hoon @@ -716,7 +716,7 @@ 'rtt'^(numb (div rtt ~s1)) 'rttvar'^(numb (div rttvar ~s1)) 'ssthresh'^(numb ssthresh) - 'num-live'^(numb num-live) + 'num-live'^(numb ~(wyt by live)) 'cwnd'^(numb cwnd) 'counter'^(numb counter) == diff --git a/pkg/arvo/app/debug/css/index.css b/pkg/arvo/app/debug/css/index.css index 2be3ef0814..1121ba3f85 100644 --- a/pkg/arvo/app/debug/css/index.css +++ b/pkg/arvo/app/debug/css/index.css @@ -1 +1 @@ -/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}[hidden],template{display:none}.aspect-ratio{height:0;position:relative}.aspect-ratio--16x9{padding-bottom:56.25%}.aspect-ratio--9x16{padding-bottom:177.77%}.aspect-ratio--4x3{padding-bottom:75%}.aspect-ratio--3x4{padding-bottom:133.33%}.aspect-ratio--6x4{padding-bottom:66.6%}.aspect-ratio--4x6{padding-bottom:150%}.aspect-ratio--8x5{padding-bottom:62.5%}.aspect-ratio--5x8{padding-bottom:160%}.aspect-ratio--7x5{padding-bottom:71.42%}.aspect-ratio--5x7{padding-bottom:140%}.aspect-ratio--1x1{padding-bottom:100%}.aspect-ratio--object{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover{background-size:cover!important}.contain{background-size:contain!important}.bg-center{background-position:50%}.bg-center,.bg-top{background-repeat:no-repeat}.bg-top{background-position:top}.bg-right{background-position:100%}.bg-bottom,.bg-right{background-repeat:no-repeat}.bg-bottom{background-position:bottom}.bg-left{background-repeat:no-repeat;background-position:0}.ba{border-style:solid;border-width:1px}.bt{border-top-style:solid;border-top-width:1px}.br{border-right-style:solid;border-right-width:1px}.bb{border-bottom-style:solid;border-bottom-width:1px}.bl{border-left-style:solid;border-left-width:1px}.bn{border-style:none;border-width:0}.b--black{border-color:#000}.b--white{border-color:#fff}.b--gray0{border-color:#333}.b--gray1{border-color:#4d4d4d}.b--gray2{border-color:#7f7f7f}.b--gray3{border-color:#b1b2b3}.b--gray4{border-color:#e6e6e6}.b--gray5{border-color:#f9f9f9}.b--blue0{border-color:#ecf6ff}.b--blue1{border-color:#b0c7ff}.b--blue2{border-color:#4330fc}.b--blue3{border-color:#190d7b}.b--red0{border-color:#f9d6ce}.b--red1{border-color:#ffa073}.b--red2{border-color:#ee5432}.b--red3{border-color:#c10d30}.b--green0{border-color:#bdebcc}.b--green1{border-color:#2ed196}.b--green2{border-color:#2aa779}.b--green3{border-color:#286e55}.b--yellow0{border-color:#ffefc5}.b--yellow1{border-color:#ffd972}.b--yellow2{border-color:#fcc440}.b--yellow3{border-color:#ee892b}.b--transparent{border-color:transparent}.br0{border-radius:0}.br1{border-radius:.125rem}.br2{border-radius:.25rem}.br3{border-radius:.5rem}.br4{border-radius:1rem}.br-100{border-radius:100%}.br-pill{border-radius:9999px}.br--bottom{border-top-left-radius:0;border-top-right-radius:0}.br--top{border-bottom-right-radius:0}.br--right,.br--top{border-bottom-left-radius:0}.br--right{border-top-left-radius:0}.br--left{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted{border-style:dotted}.b--dashed{border-style:dashed}.b--solid{border-style:solid}.b--none{border-style:none}.bw0{border-width:0}.bw1{border-width:.125rem}.bw2{border-width:.25rem}.bw3{border-width:.5rem}.bw4{border-width:1rem}.bw5{border-width:2rem}.bt-0{border-top-width:0}.br-0{border-right-width:0}.bb-0{border-bottom-width:0}.bl-0{border-left-width:0}.shadow-1{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.border-box,a,article,aside,blockquote,body,code,dd,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,html,input[type=email],input[type=number],input[type=password],input[type=tel],input[type=text],input[type=url],legend,li,main,nav,ol,p,pre,section,table,td,textarea,th,tr,ul{box-sizing:border-box}.pre{overflow-x:auto;overflow-y:hidden;overflow:scroll}.pa0{padding:0}.ma0,.na0{margin:0}.pl0{padding-left:0}.ml0,.nl0{margin-left:0}.pr0{padding-right:0}.mr0,.nr0{margin-right:0}.pt0{padding-top:0}.mt0,.nt0{margin-top:0}.pb0{padding-bottom:0}.mb0,.nb0{margin-bottom:0}.pv0{padding-top:0;padding-bottom:0}.mv0,.nv0{margin-top:0;margin-bottom:0}.ph0{padding-left:0;padding-right:0}.mh0,.nh0{margin-left:0;margin-right:0}.pa1{padding:.25rem}.ma1{margin:.25rem}.na1{margin:-.25rem}.pl1{padding-left:.25rem}.ml1{margin-left:.25rem}.nl1{margin-left:-.25rem}.pr1{padding-right:.25rem}.mr1{margin-right:.25rem}.nr1{margin-right:-.25rem}.pt1{padding-top:.25rem}.mt1{margin-top:.25rem}.nt1{margin-top:-.25rem}.pb1{padding-bottom:.25rem}.mb1{margin-bottom:.25rem}.nb1{margin-bottom:-.25rem}.pv1{padding-top:.25rem;padding-bottom:.25rem}.mv1{margin-top:.25rem;margin-bottom:.25rem}.nv1{margin-top:-.25rem;margin-bottom:-.25rem}.ph1{padding-left:.25rem;padding-right:.25rem}.mh1{margin-left:.25rem;margin-right:.25rem}.nh1{margin-left:-.25rem;margin-right:-.25rem}.pa2{padding:.5rem}.ma2{margin:.5rem}.na2{margin:-.5rem}.pl2{padding-left:.5rem}.ml2{margin-left:.5rem}.nl2{margin-left:-.5rem}.pr2{padding-right:.5rem}.mr2{margin-right:.5rem}.nr2{margin-right:-.5rem}.pt2{padding-top:.5rem}.mt2{margin-top:.5rem}.nt2{margin-top:-.5rem}.pb2{padding-bottom:.5rem}.mb2{margin-bottom:.5rem}.nb2{margin-bottom:-.5rem}.pv2{padding-top:.5rem;padding-bottom:.5rem}.mv2{margin-top:.5rem;margin-bottom:.5rem}.nv2{margin-top:-.5rem;margin-bottom:-.5rem}.ph2{padding-left:.5rem;padding-right:.5rem}.mh2{margin-left:.5rem;margin-right:.5rem}.nh2{margin-left:-.5rem;margin-right:-.5rem}.pa3{padding:.75rem}.ma3{margin:.75rem}.na3{margin:-.75rem}.pl3{padding-left:.75rem}.ml3{margin-left:.75rem}.nl3{margin-left:-.75rem}.pr3{padding-right:.75rem}.mr3{margin-right:.75rem}.nr3{margin-right:-.75rem}.pt3{padding-top:.75rem}.mt3{margin-top:.75rem}.nt3{margin-top:-.75rem}.pb3{padding-bottom:.75rem}.mb3{margin-bottom:.75rem}.nb3{margin-bottom:-.75rem}.pv3{padding-top:.75rem;padding-bottom:.75rem}.mv3{margin-top:.75rem;margin-bottom:.75rem}.nv3{margin-top:-.75rem;margin-bottom:-.75rem}.ph3{padding-left:.75rem;padding-right:.75rem}.mh3{margin-left:.75rem;margin-right:.75rem}.nh3{margin-left:-.75rem;margin-right:-.75rem}.pa4{padding:1rem}.ma4{margin:1rem}.na4{margin:-1rem}.pl4{padding-left:1rem}.ml4{margin-left:1rem}.nl4{margin-left:-1rem}.pr4{padding-right:1rem}.mr4{margin-right:1rem}.nr4{margin-right:-1rem}.pt4{padding-top:1rem}.mt4{margin-top:1rem}.nt4{margin-top:-1rem}.pb4{padding-bottom:1rem}.mb4{margin-bottom:1rem}.nb4{margin-bottom:-1rem}.pv4{padding-top:1rem;padding-bottom:1rem}.mv4{margin-top:1rem;margin-bottom:1rem}.nv4{margin-top:-1rem;margin-bottom:-1rem}.ph4{padding-left:1rem;padding-right:1rem}.mh4{margin-left:1rem;margin-right:1rem}.nh4{margin-left:-1rem;margin-right:-1rem}.pa5{padding:1.25rem}.ma5{margin:1.25rem}.na5{margin:-1.25rem}.pl5{padding-left:1.25rem}.ml5{margin-left:1.25rem}.nl5{margin-left:-1.25rem}.pr5{padding-right:1.25rem}.mr5{margin-right:1.25rem}.nr5{margin-right:-1.25rem}.pt5{padding-top:1.25rem}.mt5{margin-top:1.25rem}.nt5{margin-top:-1.25rem}.pb5{padding-bottom:1.25rem}.mb5{margin-bottom:1.25rem}.nb5{margin-bottom:-1.25rem}.pv5{padding-top:1.25rem;padding-bottom:1.25rem}.mv5{margin-top:1.25rem;margin-bottom:1.25rem}.nv5{margin-top:-1.25rem;margin-bottom:-1.25rem}.ph5{padding-left:1.25rem;padding-right:1.25rem}.mh5{margin-left:1.25rem;margin-right:1.25rem}.nh5{margin-left:-1.25rem;margin-right:-1.25rem}.pa6{padding:1.5rem}.ma6{margin:1.5rem}.na6{margin:-1.5rem}.pl6{padding-left:1.5rem}.ml6{margin-left:1.5rem}.nl6{margin-left:-1.5rem}.pr6{padding-right:1.5rem}.mr6{margin-right:1.5rem}.nr6{margin-right:-1.5rem}.pt6{padding-top:1.5rem}.mt6{margin-top:1.5rem}.nt6{margin-top:-1.5rem}.pb6{padding-bottom:1.5rem}.mb6{margin-bottom:1.5rem}.nb6{margin-bottom:-1.5rem}.pv6{padding-top:1.5rem;padding-bottom:1.5rem}.mv6{margin-top:1.5rem;margin-bottom:1.5rem}.nv6{margin-top:-1.5rem;margin-bottom:-1.5rem}.ph6{padding-left:1.5rem;padding-right:1.5rem}.mh6{margin-left:1.5rem;margin-right:1.5rem}.nh6{margin-left:-1.5rem;margin-right:-1.5rem}.pa7{padding:2rem}.ma7{margin:2rem}.na7{margin:-2rem}.pl7{padding-left:2rem}.ml7{margin-left:2rem}.nl7{margin-left:-2rem}.pr7{padding-right:2rem}.mr7{margin-right:2rem}.nr7{margin-right:-2rem}.pt7{padding-top:2rem}.mt7{margin-top:2rem}.nt7{margin-top:-2rem}.pb7{padding-bottom:2rem}.mb7{margin-bottom:2rem}.nb7{margin-bottom:-2rem}.pv7{padding-top:2rem;padding-bottom:2rem}.mv7{margin-top:2rem;margin-bottom:2rem}.nv7{margin-top:-2rem;margin-bottom:-2rem}.ph7{padding-left:2rem;padding-right:2rem}.mh7{margin-left:2rem;margin-right:2rem}.nh7{margin-left:-2rem;margin-right:-2rem}.pa8{padding:3rem}.ma8{margin:3rem}.na8{margin:-3rem}.pl8{padding-left:3rem}.ml8{margin-left:3rem}.nl8{margin-left:-3rem}.pr8{padding-right:3rem}.mr8{margin-right:3rem}.nr8{margin-right:-3rem}.pt8{padding-top:3rem}.mt8{margin-top:3rem}.nt8{margin-top:-3rem}.pb8{padding-bottom:3rem}.mb8{margin-bottom:3rem}.nb8{margin-bottom:-3rem}.pv8{padding-top:3rem;padding-bottom:3rem}.mv8{margin-top:3rem;margin-bottom:3rem}.nv8{margin-top:-3rem;margin-bottom:-3rem}.ph8{padding-left:3rem;padding-right:3rem}.mh8{margin-left:3rem;margin-right:3rem}.nh8{margin-left:-3rem;margin-right:-3rem}.pa9{padding:4rem}.ma9{margin:4rem}.na9{margin:-4rem}.pl9{padding-left:4rem}.ml9{margin-left:4rem}.nl9{margin-left:-4rem}.pr9{padding-right:4rem}.mr9{margin-right:4rem}.nr9{margin-right:-4rem}.pt9{padding-top:4rem}.mt9{margin-top:4rem}.nt9{margin-top:-4rem}.pb9{padding-bottom:4rem}.mb9{margin-bottom:4rem}.nb9{margin-bottom:-4rem}.pv9{padding-top:4rem;padding-bottom:4rem}.mv9{margin-top:4rem;margin-bottom:4rem}.nv9{margin-top:-4rem;margin-bottom:-4rem}.ph9{padding-left:4rem;padding-right:4rem}.mh9{margin-left:4rem;margin-right:4rem}.nh9{margin-left:-4rem;margin-right:-4rem}.pa10{padding:6rem}.ma10{margin:6rem}.na10{margin:-6rem}.pl10{padding-left:6rem}.ml10{margin-left:6rem}.nl10{margin-left:-6rem}.pr10{padding-right:6rem}.mr10{margin-right:6rem}.nr10{margin-right:-6rem}.pt10{padding-top:6rem}.mt10{margin-top:6rem}.nt10{margin-top:-6rem}.pb10{padding-bottom:6rem}.mb10{margin-bottom:6rem}.nb10{margin-bottom:-6rem}.pv10{padding-top:6rem;padding-bottom:6rem}.mv10{margin-top:6rem;margin-bottom:6rem}.nv10{margin-top:-6rem;margin-bottom:-6rem}.ph10{padding-left:6rem;padding-right:6rem}.mh10{margin-left:6rem;margin-right:6rem}.nh10{margin-left:-6rem;margin-right:-6rem}.pa11{padding:10rem}.ma11{margin:10rem}.na11{margin:-10rem}.pl11{padding-left:10rem}.ml11{margin-left:10rem}.nl11{margin-left:-10rem}.pr11{padding-right:10rem}.mr11{margin-right:10rem}.nr11{margin-right:-10rem}.pt11{padding-top:10rem}.mt11{margin-top:10rem}.nt11{margin-top:-10rem}.pb11{padding-bottom:10rem}.mb11{margin-bottom:10rem}.nb11{margin-bottom:-10rem}.pv11{padding-top:10rem;padding-bottom:10rem}.mv11{margin-top:10rem;margin-bottom:10rem}.nv11{margin-top:-10rem;margin-bottom:-10rem}.ph11{padding-left:10rem;padding-right:10rem}.mh11{margin-left:10rem;margin-right:10rem}.nh11{margin-left:-10rem;margin-right:-10rem}.pa12{padding:18rem}.ma12{margin:18rem}.na12{margin:-18rem}.pl12{padding-left:18rem}.ml12{margin-left:18rem}.nl12{margin-left:-18rem}.pr12{padding-right:18rem}.mr12{margin-right:18rem}.nr12{margin-right:-18rem}.pt12{padding-top:18rem}.mt12{margin-top:18rem}.nt12{margin-top:-18rem}.pb12{padding-bottom:18rem}.mb12{margin-bottom:18rem}.nb12{margin-bottom:-18rem}.pv12{padding-top:18rem;padding-bottom:18rem}.mv12{margin-top:18rem;margin-bottom:18rem}.nv12{margin-top:-18rem;margin-bottom:-18rem}.ph12{padding-left:18rem;padding-right:18rem}.mh12{margin-left:18rem;margin-right:18rem}.nh12{margin-left:-18rem;margin-right:-18rem}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}.left-0{left:0}.top-1{top:1rem}.right-1{right:1rem}.bottom-1{bottom:1rem}.left-1{left:1rem}.top-2{top:2rem}.right-2{right:2rem}.bottom-2{bottom:2rem}.left-2{left:2rem}.top--1{top:-1rem}.right--1{right:-1rem}.bottom--1{bottom:-1rem}.left--1{left:-1rem}.top--2{top:-2rem}.right--2{right:-2rem}.bottom--2{bottom:-2rem}.left--2{left:-2rem}.absolute--fill{top:0;right:0;bottom:0;left:0}.cf:after,.cf:before{content:" ";display:table}.cf:after{clear:both}.cf{*zoom:1}.cl{clear:left}.cr{clear:right}.cb{clear:both}.cn{clear:none}.dn{display:none}.di{display:inline}.db{display:block}.dib{display:inline-block}.dit{display:inline-table}.dt{display:table}.dtc{display:table-cell}.dt-row{display:table-row}.dt-row-group{display:table-row-group}.dt-column{display:table-column}.dt-column-group{display:table-column-group}.dt--fixed{table-layout:fixed;width:100%}.flex{display:flex}.inline-flex{display:inline-flex}.flex-auto{flex:1 1 auto;min-width:0;min-height:0}.flex-none{flex:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.flex-wrap-reverse{flex-wrap:wrap-reverse}.flex-column-reverse{flex-direction:column-reverse}.flex-row-reverse{flex-direction:row-reverse}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.self-start{align-self:flex-start}.self-end{align-self:flex-end}.self-center{align-self:center}.self-baseline{align-self:baseline}.self-stretch{align-self:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.content-start{align-content:flex-start}.content-end{align-content:flex-end}.content-center{align-content:center}.content-between{align-content:space-between}.content-around{align-content:space-around}.content-stretch{align-content:stretch}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-last{order:99999}.flex-grow-0{flex-grow:0}.flex-grow-1{flex-grow:1}.flex-shrink-0{flex-shrink:0}.flex-shrink-1{flex-shrink:1}.fl{float:left}.fl,.fr{_display:inline}.fr{float:right}.fn{float:none}.sans-serif{font-family:-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica neue,helvetica,ubuntu,roboto,noto,segoe ui,arial,sans-serif}.serif{font-family:georgia,times,serif}.system-sans-serif{font-family:sans-serif}.system-serif{font-family:serif}.code,code{font-family:Consolas,monaco,monospace}.courier{font-family:Courier Next,courier,monospace}.helvetica{font-family:helvetica neue,helvetica,sans-serif}.avenir{font-family:avenir next,avenir,sans-serif}.athelas{font-family:athelas,georgia,serif}.georgia{font-family:georgia,serif}.times{font-family:times,serif}.bodoni{font-family:Bodoni MT,serif}.calisto{font-family:Calisto MT,serif}.garamond{font-family:garamond,serif}.baskerville{font-family:baskerville,serif}.i{font-style:italic}.fs-normal{font-style:normal}.normal{font-weight:400}.b{font-weight:700}.fw1{font-weight:100}.fw2{font-weight:200}.fw3{font-weight:300}.fw4{font-weight:400}.fw5{font-weight:500}.fw6{font-weight:600}.fw7{font-weight:700}.fw8{font-weight:800}.fw9{font-weight:900}.input-reset{-webkit-appearance:none;-moz-appearance:none}.button-reset::-moz-focus-inner,.input-reset::-moz-focus-inner{border:0;padding:0}.debug *{outline:1px solid gold}.debug-white *{outline:1px solid #fff}.debug-black *{outline:1px solid #000}.debug-grid{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAFElEQVR4AWPAC97/9x0eCsAEPgwAVLshdpENIxcAAAAASUVORK5CYII=) repeat 0 0}.debug-grid-16{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMklEQVR4AWOgCLz/b0epAa6UGuBOqQHOQHLUgFEDnAbcBZ4UGwDOkiCnkIhdgNgNxAYAiYlD+8sEuo8AAAAASUVORK5CYII=) repeat 0 0}.debug-grid-8-solid{background:#fff url(data:image/gif;base64,R0lGODdhCAAIAPEAAADw/wDx/////wAAACwAAAAACAAIAAACDZQvgaeb/lxbAIKA8y0AOw==) repeat 0 0}.debug-grid-16-solid{background:#fff url(data:image/gif;base64,R0lGODdhEAAQAPEAAADw/wDx/xXy/////ywAAAAAEAAQAAACIZyPKckYDQFsb6ZqD85jZ2+BkwiRFKehhqQCQgDHcgwEBQA7) repeat 0 0}.link{text-decoration:none}.link,.link:active,.link:focus,.link:hover,.link:link,.link:visited{transition:color .15s ease-in}.link:focus{outline:1px dotted currentColor}.list{list-style-type:none}.h1{height:1rem}.h2{height:2rem}.h3{height:4rem}.h4{height:8rem}.h5{height:16rem}.h-25{height:25%}.h-50{height:50%}.h-75{height:75%}.h-100{height:100%}.min-h-100{min-height:100%}.vh-25{height:25vh}.vh-50{height:50vh}.vh-75{height:75vh}.vh-100{height:100vh}.min-vh-100{min-height:100vh}.h-auto{height:auto}.h-inherit{height:inherit}.black{color:#000}.white{color:#fff}.gray0{color:#333}.gray1{color:#4d4d4d}.gray2{color:#7f7f7f}.gray3{color:#b1b2b3}.gray4{color:#e6e6e6}.gray5{color:#f9f9f9}.blue0{color:#ecf6ff}.blue1{color:#b0c7ff}.blue2{color:#4330fc}.blue3{color:#190d7b}.red0{color:#f9d6ce}.red1{color:#ffa073}.red2{color:#ee5432}.red3{color:#c10d30}.green0{color:#bdebcc}.green1{color:#2ed196}.green2{color:#2aa779}.green3{color:#286e55}.yellow0{color:#ffefc5}.yellow1{color:#ffd972}.yellow2{color:#fcc440}.yellow3{color:#ee892b}.bg-black{background-color:#000}.bg-white{background-color:#fff}.bg-gray0{background-color:#333}.bg-gray1{background-color:#4d4d4d}.bg-gray2{background-color:#7f7f7f}.bg-gray3{background-color:#b1b2b3}.bg-gray4{background-color:#e6e6e6}.bg-gray5{background-color:#f9f9f9}.bg-blue0{background-color:#ecf6ff}.bg-blue1{background-color:#b0c7ff}.bg-blue2{background-color:#4330fc}.bg-blue3{background-color:#190d7b}.bg-red0{background-color:#f9d6ce}.bg-red1{background-color:#ffa073}.bg-red2{background-color:#ee5432}.bg-red3{background-color:#c10d30}.bg-green0{background-color:#bdebcc}.bg-green1{background-color:#2ed196}.bg-green2{background-color:#2aa779}.bg-green3{background-color:#286e55}.bg-yellow0{background-color:#ffefc5}.bg-yellow1{background-color:#ffd972}.bg-yellow2{background-color:#fcc440}.bg-yellow3{background-color:#ee892b}.bg-transparent{background-color:transparent}.hover-black:focus,.hover-black:hover{color:#000}.hover-white:focus,.hover-white:hover{color:#fff}.hover-gray0:focus,.hover-gray0:hover{color:#333}.hover-gray1:focus,.hover-gray1:hover{color:#4d4d4d}.hover-gray2:focus,.hover-gray2:hover{color:#7f7f7f}.hover-gray3:focus,.hover-gray3:hover{color:#b1b2b3}.hover-gray4:focus,.hover-gray4:hover{color:#e6e6e6}.hover-gray5:focus,.hover-gray5:hover{color:#f9f9f9}.hover-blue0:focus,.hover-blue0:hover{color:#ecf6ff}.hover-blue1:focus,.hover-blue1:hover{color:#b0c7ff}.hover-blue2:focus,.hover-blue2:hover{color:#4330fc}.hover-blue3:focus,.hover-blue3:hover{color:#190d7b}.hover-red0:focus,.hover-red0:hover{color:#f9d6ce}.hover-red1:focus,.hover-red1:hover{color:#ffa073}.hover-red2:focus,.hover-red2:hover{color:#ee5432}.hover-red3:focus,.hover-red3:hover{color:#c10d30}.hover-green0:focus,.hover-green0:hover{color:#bdebcc}.hover-green1:focus,.hover-green1:hover{color:#2ed196}.hover-green2:focus,.hover-green2:hover{color:#2aa779}.hover-green3:focus,.hover-green3:hover{color:#286e55}.hover-yellow0:focus,.hover-yellow0:hover{color:#ffefc5}.hover-yellow1:focus,.hover-yellow1:hover{color:#ffd972}.hover-yellow2:focus,.hover-yellow2:hover{color:#fcc440}.hover-yellow3:focus,.hover-yellow3:hover{color:#ee892b}.hover-bg-black:focus,.hover-bg-black:hover{background-color:#000}.hover-bg-white:focus,.hover-bg-white:hover{background-color:#fff}.hover-bg-gray0:focus,.hover-bg-gray0:hover{background-color:#333}.hover-bg-gray1:focus,.hover-bg-gray1:hover{background-color:#4d4d4d}.hover-bg-gray2:focus,.hover-bg-gray2:hover{background-color:#7f7f7f}.hover-bg-gray3:focus,.hover-bg-gray3:hover{background-color:#b1b2b3}.hover-bg-gray4:focus,.hover-bg-gray4:hover{background-color:#e6e6e6}.hover-bg-gray5:focus,.hover-bg-gray5:hover{background-color:#f9f9f9}.hover-bg-blue0:focus,.hover-bg-blue0:hover{background-color:#ecf6ff}.hover-bg-blue1:focus,.hover-bg-blue1:hover{background-color:#b0c7ff}.hover-bg-blue2:focus,.hover-bg-blue2:hover{background-color:#4330fc}.hover-bg-blue3:focus,.hover-bg-blue3:hover{background-color:#190d7b}.hover-bg-red0:focus,.hover-bg-red0:hover{background-color:#f9d6ce}.hover-bg-red1:focus,.hover-bg-red1:hover{background-color:#ffa073}.hover-bg-red2:focus,.hover-bg-red2:hover{background-color:#ee5432}.hover-bg-red3:focus,.hover-bg-red3:hover{background-color:#c10d30}.hover-bg-green0:focus,.hover-bg-green0:hover{background-color:#bdebcc}.hover-bg-green1:focus,.hover-bg-green1:hover{background-color:#2ed196}.hover-bg-green2:focus,.hover-bg-green2:hover{background-color:#2aa779}.hover-bg-green3:focus,.hover-bg-green3:hover{background-color:#286e55}.hover-bg-yellow0:focus,.hover-bg-yellow0:hover{background-color:#ffefc5}.hover-bg-yellow1:focus,.hover-bg-yellow1:hover{background-color:#ffd972}.hover-bg-yellow2:focus,.hover-bg-yellow2:hover{background-color:#fcc440}.hover-bg-yellow3:focus,.hover-bg-yellow3:hover{background-color:#ee892b}.hover-bg-transparent:focus,.hover-bg-transparent:hover{background-color:transparent}img{max-width:100%}.tracked{letter-spacing:.1em}.tracked-tight{letter-spacing:-.05em}.tracked-mega{letter-spacing:.25em}.lh-solid{line-height:1.333333}.lh-title{line-height:1.5}.lh-copy{line-height:1.666666}.mw1{max-width:1rem}.mw2{max-width:2rem}.mw3{max-width:4rem}.mw4{max-width:8rem}.mw5{max-width:16rem}.mw6{max-width:32rem}.mw7{max-width:48rem}.mw8{max-width:64rem}.mw9{max-width:96rem}.mw-none{max-width:none}.mw-100{max-width:100%}.nested-copy-line-height ol,.nested-copy-line-height p,.nested-copy-line-height ul{line-height:1.5}.nested-headline-line-height h1,.nested-headline-line-height h2,.nested-headline-line-height h3,.nested-headline-line-height h4,.nested-headline-line-height h5,.nested-headline-line-height h6{line-height:1.25}.nested-list-reset ol,.nested-list-reset ul{padding-left:0;margin-left:0;list-style-type:none}.nested-copy-indent p+p{text-indent:1em;margin-top:0;margin-bottom:0}.nested-copy-separator p+p{margin-top:1.5em}.nested-img img{width:100%;max-width:100%;display:block}.nested-links a{color:#357edd;transition:color .15s ease-in}.nested-links a:focus,.nested-links a:hover{color:#96ccff;transition:color .15s ease-in}.dim{opacity:1}.dim,.dim:focus,.dim:hover{transition:opacity .15s ease-in}.dim:focus,.dim:hover{opacity:.5}.dim:active{opacity:.8;transition:opacity .15s ease-out}.glow,.glow:focus,.glow:hover{transition:opacity .15s ease-in}.glow:focus,.glow:hover{opacity:1}.hide-child .child{opacity:0;transition:opacity .15s ease-in}.hide-child:active .child,.hide-child:focus .child,.hide-child:hover .child{opacity:1;transition:opacity .15s ease-in}.underline-hover:focus,.underline-hover:hover{text-decoration:underline}.grow{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);transform:translateZ(0);transition:-webkit-transform .25s ease-out;transition:transform .25s ease-out;transition:transform .25s ease-out,-webkit-transform .25s ease-out}.grow:focus,.grow:hover{-webkit-transform:scale(1.05);transform:scale(1.05)}.grow:active{-webkit-transform:scale(.9);transform:scale(.9)}.grow-large{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);transform:translateZ(0);transition:-webkit-transform .25s ease-in-out;transition:transform .25s ease-in-out;transition:transform .25s ease-in-out,-webkit-transform .25s ease-in-out}.grow-large:focus,.grow-large:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.grow-large:active{-webkit-transform:scale(.95);transform:scale(.95)}.pointer:hover,.shadow-hover{cursor:pointer}.shadow-hover{position:relative;transition:all .5s cubic-bezier(.165,.84,.44,1)}.shadow-hover:after{content:"";box-shadow:0 0 16px 2px rgba(0,0,0,.2);border-radius:inherit;opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:-1;transition:opacity .5s cubic-bezier(.165,.84,.44,1)}.shadow-hover:focus:after,.shadow-hover:hover:after{opacity:1}.bg-animate,.bg-animate:focus,.bg-animate:hover{transition:background-color .15s ease-in-out}.o-100{opacity:1}.o-90{opacity:.9}.o-80{opacity:.8}.o-70{opacity:.7}.o-60{opacity:.6}.o-50{opacity:.5}.o-40{opacity:.4}.o-30{opacity:.3}.o-20{opacity:.2}.o-10{opacity:.1}.o-05{opacity:.05}.o-025{opacity:.025}.o-0{opacity:0}.rotate-45{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.outline{outline:1px solid}.outline-transparent{outline:1px solid transparent}.outline-0{outline:0}.overflow-visible{overflow:visible}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.overflow-auto{overflow:auto}.overflow-x-visible{overflow-x:visible}.overflow-x-hidden{overflow-x:hidden}.overflow-x-scroll{overflow-x:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-visible{overflow-y:visible}.overflow-y-hidden{overflow-y:hidden}.overflow-y-scroll{overflow-y:scroll}.overflow-y-auto{overflow-y:auto}.static{position:static}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.collapse{border-collapse:collapse;border-spacing:0}.striped--light-silver:nth-child(odd){background-color:#aaa}.striped--moon-gray:nth-child(odd){background-color:#ccc}.striped--light-gray:nth-child(odd){background-color:#eee}.striped--near-white:nth-child(odd){background-color:#f4f4f4}.stripe-light:nth-child(odd){background-color:hsla(0,0%,100%,.1)}.stripe-dark:nth-child(odd){background-color:rgba(0,0,0,.1)}.strike{text-decoration:line-through}.underline{text-decoration:underline}.no-underline{text-decoration:none}.tl{text-align:left}.tr{text-align:right}.tc{text-align:center}.tj{text-align:justify}.ttc{text-transform:capitalize}.ttl{text-transform:lowercase}.ttu{text-transform:uppercase}.ttn{text-transform:none}.v-base{vertical-align:baseline}.v-mid{vertical-align:middle}.v-top{vertical-align:top}.v-btm{vertical-align:bottom}.f1{font-size:4.5rem}.f2{font-size:4rem}.f3{font-size:3rem}.f4{font-size:2rem}.f5{font-size:1.5rem}.f6{font-size:1.125rem}.f7{font-size:1rem}.f8{font-size:.875rem}.f9{font-size:.75rem}.measure{max-width:30em}.measure-wide{max-width:34em}.measure-narrow{max-width:20em}.small-caps{font-variant:small-caps}.indent{text-indent:1em;margin-top:0;margin-bottom:0}.truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overflow-container{overflow-y:scroll}.center{margin-left:auto}.center,.mr-auto{margin-right:auto}.ml-auto{margin-left:auto}.clip{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal{white-space:normal}.nowrap{white-space:nowrap}.pre{white-space:pre}.w1{width:1rem}.w2{width:2rem}.w3{width:4rem}.w4{width:8rem}.w5{width:16rem}.w-10{width:10%}.w-20{width:20%}.w-25{width:25%}.w-30{width:30%}.w-33{width:33%}.w-34{width:34%}.w-40{width:40%}.w-50{width:50%}.w-60{width:60%}.w-70{width:70%}.w-75{width:75%}.w-80{width:80%}.w-90{width:90%}.w-100{width:100%}.w-third{width:33.33333%}.w-two-thirds{width:66.66667%}.w-auto{width:auto}.z-0{z-index:0}.z-1{z-index:1}.z-2{z-index:2}.z-3{z-index:3}.z-4{z-index:4}.z-5{z-index:5}.z-999{z-index:999}.z-9999{z-index:9999}.z-max{z-index:2147483647}.z-inherit{z-index:inherit}.z-initial{z-index:auto}.z-unset{z-index:unset}@media screen and (min-width:34.375em) and (max-width:46.875em){.aspect-ratio-m{height:0;position:relative}.aspect-ratio--16x9-m{padding-bottom:56.25%}.aspect-ratio--9x16-m{padding-bottom:177.77%}.aspect-ratio--4x3-m{padding-bottom:75%}.aspect-ratio--3x4-m{padding-bottom:133.33%}.aspect-ratio--6x4-m{padding-bottom:66.6%}.aspect-ratio--4x6-m{padding-bottom:150%}.aspect-ratio--8x5-m{padding-bottom:62.5%}.aspect-ratio--5x8-m{padding-bottom:160%}.aspect-ratio--7x5-m{padding-bottom:71.42%}.aspect-ratio--5x7-m{padding-bottom:140%}.aspect-ratio--1x1-m{padding-bottom:100%}.aspect-ratio--object-m{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover-m{background-size:cover!important}.contain-m{background-size:contain!important}.bg-center-m{background-position:50%}.bg-center-m,.bg-top-m{background-repeat:no-repeat}.bg-top-m{background-position:top}.bg-right-m{background-position:100%}.bg-bottom-m,.bg-right-m{background-repeat:no-repeat}.bg-bottom-m{background-position:bottom}.bg-left-m{background-repeat:no-repeat;background-position:0}.ba-m{border-style:solid;border-width:1px}.bt-m{border-top-style:solid;border-top-width:1px}.br-m{border-right-style:solid;border-right-width:1px}.bb-m{border-bottom-style:solid;border-bottom-width:1px}.bl-m{border-left-style:solid;border-left-width:1px}.bn-m{border-style:none;border-width:0}.br0-m{border-radius:0}.br1-m{border-radius:.125rem}.br2-m{border-radius:.25rem}.br3-m{border-radius:.5rem}.br4-m{border-radius:1rem}.br-100-m{border-radius:100%}.br-pill-m{border-radius:9999px}.br--bottom-m{border-top-left-radius:0;border-top-right-radius:0}.br--top-m{border-bottom-right-radius:0}.br--right-m,.br--top-m{border-bottom-left-radius:0}.br--right-m{border-top-left-radius:0}.br--left-m{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted-m{border-style:dotted}.b--dashed-m{border-style:dashed}.b--solid-m{border-style:solid}.b--none-m{border-style:none}.bw0-m{border-width:0}.bw1-m{border-width:.125rem}.bw2-m{border-width:.25rem}.bw3-m{border-width:.5rem}.bw4-m{border-width:1rem}.bw5-m{border-width:2rem}.bt-0-m{border-top-width:0}.br-0-m{border-right-width:0}.bb-0-m{border-bottom-width:0}.bl-0-m{border-left-width:0}.shadow-1-m{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2-m{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3-m{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4-m{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5-m{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.pa0-m{padding:0}.ma0-m,.na0-m{margin:0}.pl0-m{padding-left:0}.ml0-m,.nl0-m{margin-left:0}.pr0-m{padding-right:0}.mr0-m,.nr0-m{margin-right:0}.pt0-m{padding-top:0}.mt0-m,.nt0-m{margin-top:0}.pb0-m{padding-bottom:0}.mb0-m,.nb0-m{margin-bottom:0}.pv0-m{padding-top:0;padding-bottom:0}.mv0-m,.nv0-m{margin-top:0;margin-bottom:0}.ph0-m{padding-left:0;padding-right:0}.mh0-m,.nh0-m{margin-left:0;margin-right:0}.pa1-m{padding:.25rem}.ma1-m{margin:.25rem}.na1-m{margin:-.25rem}.pl1-m{padding-left:.25rem}.ml1-m{margin-left:.25rem}.nl1-m{margin-left:-.25rem}.pr1-m{padding-right:.25rem}.mr1-m{margin-right:.25rem}.nr1-m{margin-right:-.25rem}.pt1-m{padding-top:.25rem}.mt1-m{margin-top:.25rem}.nt1-m{margin-top:-.25rem}.pb1-m{padding-bottom:.25rem}.mb1-m{margin-bottom:.25rem}.nb1-m{margin-bottom:-.25rem}.pv1-m{padding-top:.25rem;padding-bottom:.25rem}.mv1-m{margin-top:.25rem;margin-bottom:.25rem}.nv1-m{margin-top:-.25rem;margin-bottom:-.25rem}.ph1-m{padding-left:.25rem;padding-right:.25rem}.mh1-m{margin-left:.25rem;margin-right:.25rem}.nh1-m{margin-left:-.25rem;margin-right:-.25rem}.pa2-m{padding:.5rem}.ma2-m{margin:.5rem}.na2-m{margin:-.5rem}.pl2-m{padding-left:.5rem}.ml2-m{margin-left:.5rem}.nl2-m{margin-left:-.5rem}.pr2-m{padding-right:.5rem}.mr2-m{margin-right:.5rem}.nr2-m{margin-right:-.5rem}.pt2-m{padding-top:.5rem}.mt2-m{margin-top:.5rem}.nt2-m{margin-top:-.5rem}.pb2-m{padding-bottom:.5rem}.mb2-m{margin-bottom:.5rem}.nb2-m{margin-bottom:-.5rem}.pv2-m{padding-top:.5rem;padding-bottom:.5rem}.mv2-m{margin-top:.5rem;margin-bottom:.5rem}.nv2-m{margin-top:-.5rem;margin-bottom:-.5rem}.ph2-m{padding-left:.5rem;padding-right:.5rem}.mh2-m{margin-left:.5rem;margin-right:.5rem}.nh2-m{margin-left:-.5rem;margin-right:-.5rem}.pa3-m{padding:.75rem}.ma3-m{margin:.75rem}.na3-m{margin:-.75rem}.pl3-m{padding-left:.75rem}.ml3-m{margin-left:.75rem}.nl3-m{margin-left:-.75rem}.pr3-m{padding-right:.75rem}.mr3-m{margin-right:.75rem}.nr3-m{margin-right:-.75rem}.pt3-m{padding-top:.75rem}.mt3-m{margin-top:.75rem}.nt3-m{margin-top:-.75rem}.pb3-m{padding-bottom:.75rem}.mb3-m{margin-bottom:.75rem}.nb3-m{margin-bottom:-.75rem}.pv3-m{padding-top:.75rem;padding-bottom:.75rem}.mv3-m{margin-top:.75rem;margin-bottom:.75rem}.nv3-m{margin-top:-.75rem;margin-bottom:-.75rem}.ph3-m{padding-left:.75rem;padding-right:.75rem}.mh3-m{margin-left:.75rem;margin-right:.75rem}.nh3-m{margin-left:-.75rem;margin-right:-.75rem}.pa4-m{padding:1rem}.ma4-m{margin:1rem}.na4-m{margin:-1rem}.pl4-m{padding-left:1rem}.ml4-m{margin-left:1rem}.nl4-m{margin-left:-1rem}.pr4-m{padding-right:1rem}.mr4-m{margin-right:1rem}.nr4-m{margin-right:-1rem}.pt4-m{padding-top:1rem}.mt4-m{margin-top:1rem}.nt4-m{margin-top:-1rem}.pb4-m{padding-bottom:1rem}.mb4-m{margin-bottom:1rem}.nb4-m{margin-bottom:-1rem}.pv4-m{padding-top:1rem;padding-bottom:1rem}.mv4-m{margin-top:1rem;margin-bottom:1rem}.nv4-m{margin-top:-1rem;margin-bottom:-1rem}.ph4-m{padding-left:1rem;padding-right:1rem}.mh4-m{margin-left:1rem;margin-right:1rem}.nh4-m{margin-left:-1rem;margin-right:-1rem}.pa5-m{padding:1.25rem}.ma5-m{margin:1.25rem}.na5-m{margin:-1.25rem}.pl5-m{padding-left:1.25rem}.ml5-m{margin-left:1.25rem}.nl5-m{margin-left:-1.25rem}.pr5-m{padding-right:1.25rem}.mr5-m{margin-right:1.25rem}.nr5-m{margin-right:-1.25rem}.pt5-m{padding-top:1.25rem}.mt5-m{margin-top:1.25rem}.nt5-m{margin-top:-1.25rem}.pb5-m{padding-bottom:1.25rem}.mb5-m{margin-bottom:1.25rem}.nb5-m{margin-bottom:-1.25rem}.pv5-m{padding-top:1.25rem;padding-bottom:1.25rem}.mv5-m{margin-top:1.25rem;margin-bottom:1.25rem}.nv5-m{margin-top:-1.25rem;margin-bottom:-1.25rem}.ph5-m{padding-left:1.25rem;padding-right:1.25rem}.mh5-m{margin-left:1.25rem;margin-right:1.25rem}.nh5-m{margin-left:-1.25rem;margin-right:-1.25rem}.pa6-m{padding:1.5rem}.ma6-m{margin:1.5rem}.na6-m{margin:-1.5rem}.pl6-m{padding-left:1.5rem}.ml6-m{margin-left:1.5rem}.nl6-m{margin-left:-1.5rem}.pr6-m{padding-right:1.5rem}.mr6-m{margin-right:1.5rem}.nr6-m{margin-right:-1.5rem}.pt6-m{padding-top:1.5rem}.mt6-m{margin-top:1.5rem}.nt6-m{margin-top:-1.5rem}.pb6-m{padding-bottom:1.5rem}.mb6-m{margin-bottom:1.5rem}.nb6-m{margin-bottom:-1.5rem}.pv6-m{padding-top:1.5rem;padding-bottom:1.5rem}.mv6-m{margin-top:1.5rem;margin-bottom:1.5rem}.nv6-m{margin-top:-1.5rem;margin-bottom:-1.5rem}.ph6-m{padding-left:1.5rem;padding-right:1.5rem}.mh6-m{margin-left:1.5rem;margin-right:1.5rem}.nh6-m{margin-left:-1.5rem;margin-right:-1.5rem}.pa7-m{padding:2rem}.ma7-m{margin:2rem}.na7-m{margin:-2rem}.pl7-m{padding-left:2rem}.ml7-m{margin-left:2rem}.nl7-m{margin-left:-2rem}.pr7-m{padding-right:2rem}.mr7-m{margin-right:2rem}.nr7-m{margin-right:-2rem}.pt7-m{padding-top:2rem}.mt7-m{margin-top:2rem}.nt7-m{margin-top:-2rem}.pb7-m{padding-bottom:2rem}.mb7-m{margin-bottom:2rem}.nb7-m{margin-bottom:-2rem}.pv7-m{padding-top:2rem;padding-bottom:2rem}.mv7-m{margin-top:2rem;margin-bottom:2rem}.nv7-m{margin-top:-2rem;margin-bottom:-2rem}.ph7-m{padding-left:2rem;padding-right:2rem}.mh7-m{margin-left:2rem;margin-right:2rem}.nh7-m{margin-left:-2rem;margin-right:-2rem}.pa8-m{padding:3rem}.ma8-m{margin:3rem}.na8-m{margin:-3rem}.pl8-m{padding-left:3rem}.ml8-m{margin-left:3rem}.nl8-m{margin-left:-3rem}.pr8-m{padding-right:3rem}.mr8-m{margin-right:3rem}.nr8-m{margin-right:-3rem}.pt8-m{padding-top:3rem}.mt8-m{margin-top:3rem}.nt8-m{margin-top:-3rem}.pb8-m{padding-bottom:3rem}.mb8-m{margin-bottom:3rem}.nb8-m{margin-bottom:-3rem}.pv8-m{padding-top:3rem;padding-bottom:3rem}.mv8-m{margin-top:3rem;margin-bottom:3rem}.nv8-m{margin-top:-3rem;margin-bottom:-3rem}.ph8-m{padding-left:3rem;padding-right:3rem}.mh8-m{margin-left:3rem;margin-right:3rem}.nh8-m{margin-left:-3rem;margin-right:-3rem}.pa9-m{padding:4rem}.ma9-m{margin:4rem}.na9-m{margin:-4rem}.pl9-m{padding-left:4rem}.ml9-m{margin-left:4rem}.nl9-m{margin-left:-4rem}.pr9-m{padding-right:4rem}.mr9-m{margin-right:4rem}.nr9-m{margin-right:-4rem}.pt9-m{padding-top:4rem}.mt9-m{margin-top:4rem}.nt9-m{margin-top:-4rem}.pb9-m{padding-bottom:4rem}.mb9-m{margin-bottom:4rem}.nb9-m{margin-bottom:-4rem}.pv9-m{padding-top:4rem;padding-bottom:4rem}.mv9-m{margin-top:4rem;margin-bottom:4rem}.nv9-m{margin-top:-4rem;margin-bottom:-4rem}.ph9-m{padding-left:4rem;padding-right:4rem}.mh9-m{margin-left:4rem;margin-right:4rem}.nh9-m{margin-left:-4rem;margin-right:-4rem}.pa10-m{padding:6rem}.ma10-m{margin:6rem}.na10-m{margin:-6rem}.pl10-m{padding-left:6rem}.ml10-m{margin-left:6rem}.nl10-m{margin-left:-6rem}.pr10-m{padding-right:6rem}.mr10-m{margin-right:6rem}.nr10-m{margin-right:-6rem}.pt10-m{padding-top:6rem}.mt10-m{margin-top:6rem}.nt10-m{margin-top:-6rem}.pb10-m{padding-bottom:6rem}.mb10-m{margin-bottom:6rem}.nb10-m{margin-bottom:-6rem}.pv10-m{padding-top:6rem;padding-bottom:6rem}.mv10-m{margin-top:6rem;margin-bottom:6rem}.nv10-m{margin-top:-6rem;margin-bottom:-6rem}.ph10-m{padding-left:6rem;padding-right:6rem}.mh10-m{margin-left:6rem;margin-right:6rem}.nh10-m{margin-left:-6rem;margin-right:-6rem}.pa11-m{padding:10rem}.ma11-m{margin:10rem}.na11-m{margin:-10rem}.pl11-m{padding-left:10rem}.ml11-m{margin-left:10rem}.nl11-m{margin-left:-10rem}.pr11-m{padding-right:10rem}.mr11-m{margin-right:10rem}.nr11-m{margin-right:-10rem}.pt11-m{padding-top:10rem}.mt11-m{margin-top:10rem}.nt11-m{margin-top:-10rem}.pb11-m{padding-bottom:10rem}.mb11-m{margin-bottom:10rem}.nb11-m{margin-bottom:-10rem}.pv11-m{padding-top:10rem;padding-bottom:10rem}.mv11-m{margin-top:10rem;margin-bottom:10rem}.nv11-m{margin-top:-10rem;margin-bottom:-10rem}.ph11-m{padding-left:10rem;padding-right:10rem}.mh11-m{margin-left:10rem;margin-right:10rem}.nh11-m{margin-left:-10rem;margin-right:-10rem}.pa12-m{padding:18rem}.ma12-m{margin:18rem}.na12-m{margin:-18rem}.pl12-m{padding-left:18rem}.ml12-m{margin-left:18rem}.nl12-m{margin-left:-18rem}.pr12-m{padding-right:18rem}.mr12-m{margin-right:18rem}.nr12-m{margin-right:-18rem}.pt12-m{padding-top:18rem}.mt12-m{margin-top:18rem}.nt12-m{margin-top:-18rem}.pb12-m{padding-bottom:18rem}.mb12-m{margin-bottom:18rem}.nb12-m{margin-bottom:-18rem}.pv12-m{padding-top:18rem;padding-bottom:18rem}.mv12-m{margin-top:18rem;margin-bottom:18rem}.nv12-m{margin-top:-18rem;margin-bottom:-18rem}.ph12-m{padding-left:18rem;padding-right:18rem}.mh12-m{margin-left:18rem;margin-right:18rem}.nh12-m{margin-left:-18rem;margin-right:-18rem}.top-0-m{top:0}.right-0-m{right:0}.bottom-0-m{bottom:0}.left-0-m{left:0}.top-1-m{top:1rem}.right-1-m{right:1rem}.bottom-1-m{bottom:1rem}.left-1-m{left:1rem}.top-2-m{top:2rem}.right-2-m{right:2rem}.bottom-2-m{bottom:2rem}.left-2-m{left:2rem}.top--1-m{top:-1rem}.right--1-m{right:-1rem}.bottom--1-m{bottom:-1rem}.left--1-m{left:-1rem}.top--2-m{top:-2rem}.right--2-m{right:-2rem}.bottom--2-m{bottom:-2rem}.left--2-m{left:-2rem}.absolute--fill-m{top:0;right:0;bottom:0;left:0}.cf-m:after,.cf-m:before{content:" ";display:table}.cf-m:after{clear:both}.cf-m{*zoom:1}.cl-m{clear:left}.cr-m{clear:right}.cb-m{clear:both}.cn-m{clear:none}.dn-m{display:none}.di-m{display:inline}.db-m{display:block}.dib-m{display:inline-block}.dit-m{display:inline-table}.dt-m{display:table}.dtc-m{display:table-cell}.dt-row-m{display:table-row}.dt-row-group-m{display:table-row-group}.dt-column-m{display:table-column}.dt-column-group-m{display:table-column-group}.dt--fixed-m{table-layout:fixed;width:100%}.flex-m{display:flex}.inline-flex-m{display:inline-flex}.flex-auto-m{flex:1 1 auto;min-width:0;min-height:0}.flex-none-m{flex:none}.flex-column-m{flex-direction:column}.flex-row-m{flex-direction:row}.flex-wrap-m{flex-wrap:wrap}.flex-nowrap-m{flex-wrap:nowrap}.flex-wrap-reverse-m{flex-wrap:wrap-reverse}.flex-column-reverse-m{flex-direction:column-reverse}.flex-row-reverse-m{flex-direction:row-reverse}.items-start-m{align-items:flex-start}.items-end-m{align-items:flex-end}.items-center-m{align-items:center}.items-baseline-m{align-items:baseline}.items-stretch-m{align-items:stretch}.self-start-m{align-self:flex-start}.self-end-m{align-self:flex-end}.self-center-m{align-self:center}.self-baseline-m{align-self:baseline}.self-stretch-m{align-self:stretch}.justify-start-m{justify-content:flex-start}.justify-end-m{justify-content:flex-end}.justify-center-m{justify-content:center}.justify-between-m{justify-content:space-between}.justify-around-m{justify-content:space-around}.content-start-m{align-content:flex-start}.content-end-m{align-content:flex-end}.content-center-m{align-content:center}.content-between-m{align-content:space-between}.content-around-m{align-content:space-around}.content-stretch-m{align-content:stretch}.order-0-m{order:0}.order-1-m{order:1}.order-2-m{order:2}.order-3-m{order:3}.order-4-m{order:4}.order-5-m{order:5}.order-6-m{order:6}.order-7-m{order:7}.order-8-m{order:8}.order-last-m{order:99999}.flex-grow-0-m{flex-grow:0}.flex-grow-1-m{flex-grow:1}.flex-shrink-0-m{flex-shrink:0}.flex-shrink-1-m{flex-shrink:1}.fl-m{float:left}.fl-m,.fr-m{_display:inline}.fr-m{float:right}.fn-m{float:none}.i-m{font-style:italic}.fs-normal-m{font-style:normal}.normal-m{font-weight:400}.b-m{font-weight:700}.fw1-m{font-weight:100}.fw2-m{font-weight:200}.fw3-m{font-weight:300}.fw4-m{font-weight:400}.fw5-m{font-weight:500}.fw6-m{font-weight:600}.fw7-m{font-weight:700}.fw8-m{font-weight:800}.fw9-m{font-weight:900}.h1-m{height:1rem}.h2-m{height:2rem}.h3-m{height:4rem}.h4-m{height:8rem}.h5-m{height:16rem}.h-25-m{height:25%}.h-50-m{height:50%}.h-75-m{height:75%}.h-100-m{height:100%}.min-h-100-m{min-height:100%}.vh-25-m{height:25vh}.vh-50-m{height:50vh}.vh-75-m{height:75vh}.vh-100-m{height:100vh}.min-vh-100-m{min-height:100vh}.h-auto-m{height:auto}.h-inherit-m{height:inherit}.tracked-m{letter-spacing:.1em}.tracked-tight-m{letter-spacing:-.05em}.tracked-mega-m{letter-spacing:.25em}.lh-solid-m{line-height:1.333333}.lh-title-m{line-height:1.5}.lh-copy-m{line-height:1.666666}.mw1-m{max-width:1rem}.mw2-m{max-width:2rem}.mw3-m{max-width:4rem}.mw4-m{max-width:8rem}.mw5-m{max-width:16rem}.mw6-m{max-width:32rem}.mw7-m{max-width:48rem}.mw8-m{max-width:64rem}.mw9-m{max-width:96rem}.mw-none-m{max-width:none}.mw-100-m{max-width:100%}.o-100-m{opacity:1}.o-90-m{opacity:.9}.o-80-m{opacity:.8}.o-70-m{opacity:.7}.o-60-m{opacity:.6}.o-50-m{opacity:.5}.o-40-m{opacity:.4}.o-30-m{opacity:.3}.o-20-m{opacity:.2}.o-10-m{opacity:.1}.o-05-m{opacity:.05}.o-025-m{opacity:.025}.o-0-m{opacity:0}.rotate-45-m{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90-m{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135-m{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180-m{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225-m{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270-m{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315-m{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.outline-m{outline:1px solid}.outline-transparent-m{outline:1px solid transparent}.outline-0-m{outline:0}.overflow-visible-m{overflow:visible}.overflow-hidden-m{overflow:hidden}.overflow-scroll-m{overflow:scroll}.overflow-auto-m{overflow:auto}.overflow-x-visible-m{overflow-x:visible}.overflow-x-hidden-m{overflow-x:hidden}.overflow-x-scroll-m{overflow-x:scroll}.overflow-x-auto-m{overflow-x:auto}.overflow-y-visible-m{overflow-y:visible}.overflow-y-hidden-m{overflow-y:hidden}.overflow-y-scroll-m{overflow-y:scroll}.overflow-y-auto-m{overflow-y:auto}.static-m{position:static}.relative-m{position:relative}.absolute-m{position:absolute}.fixed-m{position:fixed}.strike-m{text-decoration:line-through}.underline-m{text-decoration:underline}.no-underline-m{text-decoration:none}.tl-m{text-align:left}.tr-m{text-align:right}.tc-m{text-align:center}.tj-m{text-align:justify}.ttc-m{text-transform:capitalize}.ttl-m{text-transform:lowercase}.ttu-m{text-transform:uppercase}.ttn-m{text-transform:none}.f1-m{font-size:4.5rem}.f2-m{font-size:4rem}.f3-m{font-size:3rem}.f4-m{font-size:2rem}.f5-m{font-size:1.5rem}.f6-m{font-size:1.125rem}.f7-m{font-size:1rem}.f8-m{font-size:.875rem}.f9-m{font-size:.75rem}.measure-m{max-width:30em}.measure-wide-m{max-width:34em}.measure-narrow-m{max-width:20em}.small-caps-m{font-variant:small-caps}.indent-m{text-indent:1em;margin-top:0;margin-bottom:0}.truncate-m{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.clip-m{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal-m{white-space:normal}.nowrap-m{white-space:nowrap}.pre-m{white-space:pre}.w1-m{width:1rem}.w2-m{width:2rem}.w3-m{width:4rem}.w4-m{width:8rem}.w5-m{width:16rem}.w-10-m{width:10%}.w-20-m{width:20%}.w-25-m{width:25%}.w-30-m{width:30%}.w-33-m{width:33%}.w-34-m{width:34%}.w-40-m{width:40%}.w-50-m{width:50%}.w-60-m{width:60%}.w-70-m{width:70%}.w-75-m{width:75%}.w-80-m{width:80%}.w-90-m{width:90%}.w-100-m{width:100%}.w-third-m{width:33.33333%}.w-two-thirds-m{width:66.66667%}.w-auto-m{width:auto}}@media screen and (min-width:46.875em) and (max-width:60em){.aspect-ratio-l{height:0;position:relative}.aspect-ratio--16x9-l{padding-bottom:56.25%}.aspect-ratio--9x16-l{padding-bottom:177.77%}.aspect-ratio--4x3-l{padding-bottom:75%}.aspect-ratio--3x4-l{padding-bottom:133.33%}.aspect-ratio--6x4-l{padding-bottom:66.6%}.aspect-ratio--4x6-l{padding-bottom:150%}.aspect-ratio--8x5-l{padding-bottom:62.5%}.aspect-ratio--5x8-l{padding-bottom:160%}.aspect-ratio--7x5-l{padding-bottom:71.42%}.aspect-ratio--5x7-l{padding-bottom:140%}.aspect-ratio--1x1-l{padding-bottom:100%}.aspect-ratio--object-l{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover-l{background-size:cover!important}.contain-l{background-size:contain!important}.bg-center-l{background-position:50%}.bg-center-l,.bg-top-l{background-repeat:no-repeat}.bg-top-l{background-position:top}.bg-right-l{background-position:100%}.bg-bottom-l,.bg-right-l{background-repeat:no-repeat}.bg-bottom-l{background-position:bottom}.bg-left-l{background-repeat:no-repeat;background-position:0}.ba-l{border-style:solid;border-width:1px}.bt-l{border-top-style:solid;border-top-width:1px}.br-l{border-right-style:solid;border-right-width:1px}.bb-l{border-bottom-style:solid;border-bottom-width:1px}.bl-l{border-left-style:solid;border-left-width:1px}.bn-l{border-style:none;border-width:0}.br0-l{border-radius:0}.br1-l{border-radius:.125rem}.br2-l{border-radius:.25rem}.br3-l{border-radius:.5rem}.br4-l{border-radius:1rem}.br-100-l{border-radius:100%}.br-pill-l{border-radius:9999px}.br--bottom-l{border-top-left-radius:0;border-top-right-radius:0}.br--top-l{border-bottom-right-radius:0}.br--right-l,.br--top-l{border-bottom-left-radius:0}.br--right-l{border-top-left-radius:0}.br--left-l{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted-l{border-style:dotted}.b--dashed-l{border-style:dashed}.b--solid-l{border-style:solid}.b--none-l{border-style:none}.bw0-l{border-width:0}.bw1-l{border-width:.125rem}.bw2-l{border-width:.25rem}.bw3-l{border-width:.5rem}.bw4-l{border-width:1rem}.bw5-l{border-width:2rem}.bt-0-l{border-top-width:0}.br-0-l{border-right-width:0}.bb-0-l{border-bottom-width:0}.bl-0-l{border-left-width:0}.shadow-1-l{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2-l{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3-l{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4-l{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5-l{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.pa0-l{padding:0}.ma0-l,.na0-l{margin:0}.pl0-l{padding-left:0}.ml0-l,.nl0-l{margin-left:0}.pr0-l{padding-right:0}.mr0-l,.nr0-l{margin-right:0}.pt0-l{padding-top:0}.mt0-l,.nt0-l{margin-top:0}.pb0-l{padding-bottom:0}.mb0-l,.nb0-l{margin-bottom:0}.pv0-l{padding-top:0;padding-bottom:0}.mv0-l,.nv0-l{margin-top:0;margin-bottom:0}.ph0-l{padding-left:0;padding-right:0}.mh0-l,.nh0-l{margin-left:0;margin-right:0}.pa1-l{padding:.25rem}.ma1-l{margin:.25rem}.na1-l{margin:-.25rem}.pl1-l{padding-left:.25rem}.ml1-l{margin-left:.25rem}.nl1-l{margin-left:-.25rem}.pr1-l{padding-right:.25rem}.mr1-l{margin-right:.25rem}.nr1-l{margin-right:-.25rem}.pt1-l{padding-top:.25rem}.mt1-l{margin-top:.25rem}.nt1-l{margin-top:-.25rem}.pb1-l{padding-bottom:.25rem}.mb1-l{margin-bottom:.25rem}.nb1-l{margin-bottom:-.25rem}.pv1-l{padding-top:.25rem;padding-bottom:.25rem}.mv1-l{margin-top:.25rem;margin-bottom:.25rem}.nv1-l{margin-top:-.25rem;margin-bottom:-.25rem}.ph1-l{padding-left:.25rem;padding-right:.25rem}.mh1-l{margin-left:.25rem;margin-right:.25rem}.nh1-l{margin-left:-.25rem;margin-right:-.25rem}.pa2-l{padding:.5rem}.ma2-l{margin:.5rem}.na2-l{margin:-.5rem}.pl2-l{padding-left:.5rem}.ml2-l{margin-left:.5rem}.nl2-l{margin-left:-.5rem}.pr2-l{padding-right:.5rem}.mr2-l{margin-right:.5rem}.nr2-l{margin-right:-.5rem}.pt2-l{padding-top:.5rem}.mt2-l{margin-top:.5rem}.nt2-l{margin-top:-.5rem}.pb2-l{padding-bottom:.5rem}.mb2-l{margin-bottom:.5rem}.nb2-l{margin-bottom:-.5rem}.pv2-l{padding-top:.5rem;padding-bottom:.5rem}.mv2-l{margin-top:.5rem;margin-bottom:.5rem}.nv2-l{margin-top:-.5rem;margin-bottom:-.5rem}.ph2-l{padding-left:.5rem;padding-right:.5rem}.mh2-l{margin-left:.5rem;margin-right:.5rem}.nh2-l{margin-left:-.5rem;margin-right:-.5rem}.pa3-l{padding:.75rem}.ma3-l{margin:.75rem}.na3-l{margin:-.75rem}.pl3-l{padding-left:.75rem}.ml3-l{margin-left:.75rem}.nl3-l{margin-left:-.75rem}.pr3-l{padding-right:.75rem}.mr3-l{margin-right:.75rem}.nr3-l{margin-right:-.75rem}.pt3-l{padding-top:.75rem}.mt3-l{margin-top:.75rem}.nt3-l{margin-top:-.75rem}.pb3-l{padding-bottom:.75rem}.mb3-l{margin-bottom:.75rem}.nb3-l{margin-bottom:-.75rem}.pv3-l{padding-top:.75rem;padding-bottom:.75rem}.mv3-l{margin-top:.75rem;margin-bottom:.75rem}.nv3-l{margin-top:-.75rem;margin-bottom:-.75rem}.ph3-l{padding-left:.75rem;padding-right:.75rem}.mh3-l{margin-left:.75rem;margin-right:.75rem}.nh3-l{margin-left:-.75rem;margin-right:-.75rem}.pa4-l{padding:1rem}.ma4-l{margin:1rem}.na4-l{margin:-1rem}.pl4-l{padding-left:1rem}.ml4-l{margin-left:1rem}.nl4-l{margin-left:-1rem}.pr4-l{padding-right:1rem}.mr4-l{margin-right:1rem}.nr4-l{margin-right:-1rem}.pt4-l{padding-top:1rem}.mt4-l{margin-top:1rem}.nt4-l{margin-top:-1rem}.pb4-l{padding-bottom:1rem}.mb4-l{margin-bottom:1rem}.nb4-l{margin-bottom:-1rem}.pv4-l{padding-top:1rem;padding-bottom:1rem}.mv4-l{margin-top:1rem;margin-bottom:1rem}.nv4-l{margin-top:-1rem;margin-bottom:-1rem}.ph4-l{padding-left:1rem;padding-right:1rem}.mh4-l{margin-left:1rem;margin-right:1rem}.nh4-l{margin-left:-1rem;margin-right:-1rem}.pa5-l{padding:1.25rem}.ma5-l{margin:1.25rem}.na5-l{margin:-1.25rem}.pl5-l{padding-left:1.25rem}.ml5-l{margin-left:1.25rem}.nl5-l{margin-left:-1.25rem}.pr5-l{padding-right:1.25rem}.mr5-l{margin-right:1.25rem}.nr5-l{margin-right:-1.25rem}.pt5-l{padding-top:1.25rem}.mt5-l{margin-top:1.25rem}.nt5-l{margin-top:-1.25rem}.pb5-l{padding-bottom:1.25rem}.mb5-l{margin-bottom:1.25rem}.nb5-l{margin-bottom:-1.25rem}.pv5-l{padding-top:1.25rem;padding-bottom:1.25rem}.mv5-l{margin-top:1.25rem;margin-bottom:1.25rem}.nv5-l{margin-top:-1.25rem;margin-bottom:-1.25rem}.ph5-l{padding-left:1.25rem;padding-right:1.25rem}.mh5-l{margin-left:1.25rem;margin-right:1.25rem}.nh5-l{margin-left:-1.25rem;margin-right:-1.25rem}.pa6-l{padding:1.5rem}.ma6-l{margin:1.5rem}.na6-l{margin:-1.5rem}.pl6-l{padding-left:1.5rem}.ml6-l{margin-left:1.5rem}.nl6-l{margin-left:-1.5rem}.pr6-l{padding-right:1.5rem}.mr6-l{margin-right:1.5rem}.nr6-l{margin-right:-1.5rem}.pt6-l{padding-top:1.5rem}.mt6-l{margin-top:1.5rem}.nt6-l{margin-top:-1.5rem}.pb6-l{padding-bottom:1.5rem}.mb6-l{margin-bottom:1.5rem}.nb6-l{margin-bottom:-1.5rem}.pv6-l{padding-top:1.5rem;padding-bottom:1.5rem}.mv6-l{margin-top:1.5rem;margin-bottom:1.5rem}.nv6-l{margin-top:-1.5rem;margin-bottom:-1.5rem}.ph6-l{padding-left:1.5rem;padding-right:1.5rem}.mh6-l{margin-left:1.5rem;margin-right:1.5rem}.nh6-l{margin-left:-1.5rem;margin-right:-1.5rem}.pa7-l{padding:2rem}.ma7-l{margin:2rem}.na7-l{margin:-2rem}.pl7-l{padding-left:2rem}.ml7-l{margin-left:2rem}.nl7-l{margin-left:-2rem}.pr7-l{padding-right:2rem}.mr7-l{margin-right:2rem}.nr7-l{margin-right:-2rem}.pt7-l{padding-top:2rem}.mt7-l{margin-top:2rem}.nt7-l{margin-top:-2rem}.pb7-l{padding-bottom:2rem}.mb7-l{margin-bottom:2rem}.nb7-l{margin-bottom:-2rem}.pv7-l{padding-top:2rem;padding-bottom:2rem}.mv7-l{margin-top:2rem;margin-bottom:2rem}.nv7-l{margin-top:-2rem;margin-bottom:-2rem}.ph7-l{padding-left:2rem;padding-right:2rem}.mh7-l{margin-left:2rem;margin-right:2rem}.nh7-l{margin-left:-2rem;margin-right:-2rem}.pa8-l{padding:3rem}.ma8-l{margin:3rem}.na8-l{margin:-3rem}.pl8-l{padding-left:3rem}.ml8-l{margin-left:3rem}.nl8-l{margin-left:-3rem}.pr8-l{padding-right:3rem}.mr8-l{margin-right:3rem}.nr8-l{margin-right:-3rem}.pt8-l{padding-top:3rem}.mt8-l{margin-top:3rem}.nt8-l{margin-top:-3rem}.pb8-l{padding-bottom:3rem}.mb8-l{margin-bottom:3rem}.nb8-l{margin-bottom:-3rem}.pv8-l{padding-top:3rem;padding-bottom:3rem}.mv8-l{margin-top:3rem;margin-bottom:3rem}.nv8-l{margin-top:-3rem;margin-bottom:-3rem}.ph8-l{padding-left:3rem;padding-right:3rem}.mh8-l{margin-left:3rem;margin-right:3rem}.nh8-l{margin-left:-3rem;margin-right:-3rem}.pa9-l{padding:4rem}.ma9-l{margin:4rem}.na9-l{margin:-4rem}.pl9-l{padding-left:4rem}.ml9-l{margin-left:4rem}.nl9-l{margin-left:-4rem}.pr9-l{padding-right:4rem}.mr9-l{margin-right:4rem}.nr9-l{margin-right:-4rem}.pt9-l{padding-top:4rem}.mt9-l{margin-top:4rem}.nt9-l{margin-top:-4rem}.pb9-l{padding-bottom:4rem}.mb9-l{margin-bottom:4rem}.nb9-l{margin-bottom:-4rem}.pv9-l{padding-top:4rem;padding-bottom:4rem}.mv9-l{margin-top:4rem;margin-bottom:4rem}.nv9-l{margin-top:-4rem;margin-bottom:-4rem}.ph9-l{padding-left:4rem;padding-right:4rem}.mh9-l{margin-left:4rem;margin-right:4rem}.nh9-l{margin-left:-4rem;margin-right:-4rem}.pa10-l{padding:6rem}.ma10-l{margin:6rem}.na10-l{margin:-6rem}.pl10-l{padding-left:6rem}.ml10-l{margin-left:6rem}.nl10-l{margin-left:-6rem}.pr10-l{padding-right:6rem}.mr10-l{margin-right:6rem}.nr10-l{margin-right:-6rem}.pt10-l{padding-top:6rem}.mt10-l{margin-top:6rem}.nt10-l{margin-top:-6rem}.pb10-l{padding-bottom:6rem}.mb10-l{margin-bottom:6rem}.nb10-l{margin-bottom:-6rem}.pv10-l{padding-top:6rem;padding-bottom:6rem}.mv10-l{margin-top:6rem;margin-bottom:6rem}.nv10-l{margin-top:-6rem;margin-bottom:-6rem}.ph10-l{padding-left:6rem;padding-right:6rem}.mh10-l{margin-left:6rem;margin-right:6rem}.nh10-l{margin-left:-6rem;margin-right:-6rem}.pa11-l{padding:10rem}.ma11-l{margin:10rem}.na11-l{margin:-10rem}.pl11-l{padding-left:10rem}.ml11-l{margin-left:10rem}.nl11-l{margin-left:-10rem}.pr11-l{padding-right:10rem}.mr11-l{margin-right:10rem}.nr11-l{margin-right:-10rem}.pt11-l{padding-top:10rem}.mt11-l{margin-top:10rem}.nt11-l{margin-top:-10rem}.pb11-l{padding-bottom:10rem}.mb11-l{margin-bottom:10rem}.nb11-l{margin-bottom:-10rem}.pv11-l{padding-top:10rem;padding-bottom:10rem}.mv11-l{margin-top:10rem;margin-bottom:10rem}.nv11-l{margin-top:-10rem;margin-bottom:-10rem}.ph11-l{padding-left:10rem;padding-right:10rem}.mh11-l{margin-left:10rem;margin-right:10rem}.nh11-l{margin-left:-10rem;margin-right:-10rem}.pa12-l{padding:18rem}.ma12-l{margin:18rem}.na12-l{margin:-18rem}.pl12-l{padding-left:18rem}.ml12-l{margin-left:18rem}.nl12-l{margin-left:-18rem}.pr12-l{padding-right:18rem}.mr12-l{margin-right:18rem}.nr12-l{margin-right:-18rem}.pt12-l{padding-top:18rem}.mt12-l{margin-top:18rem}.nt12-l{margin-top:-18rem}.pb12-l{padding-bottom:18rem}.mb12-l{margin-bottom:18rem}.nb12-l{margin-bottom:-18rem}.pv12-l{padding-top:18rem;padding-bottom:18rem}.mv12-l{margin-top:18rem;margin-bottom:18rem}.nv12-l{margin-top:-18rem;margin-bottom:-18rem}.ph12-l{padding-left:18rem;padding-right:18rem}.mh12-l{margin-left:18rem;margin-right:18rem}.nh12-l{margin-left:-18rem;margin-right:-18rem}.top-0-l{top:0}.right-0-l{right:0}.bottom-0-l{bottom:0}.left-0-l{left:0}.top-1-l{top:1rem}.right-1-l{right:1rem}.bottom-1-l{bottom:1rem}.left-1-l{left:1rem}.top-2-l{top:2rem}.right-2-l{right:2rem}.bottom-2-l{bottom:2rem}.left-2-l{left:2rem}.top--1-l{top:-1rem}.right--1-l{right:-1rem}.bottom--1-l{bottom:-1rem}.left--1-l{left:-1rem}.top--2-l{top:-2rem}.right--2-l{right:-2rem}.bottom--2-l{bottom:-2rem}.left--2-l{left:-2rem}.absolute--fill-l{top:0;right:0;bottom:0;left:0}.cf-l:after,.cf-l:before{content:" ";display:table}.cf-l:after{clear:both}.cf-l{*zoom:1}.cl-l{clear:left}.cr-l{clear:right}.cb-l{clear:both}.cn-l{clear:none}.dn-l{display:none}.di-l{display:inline}.db-l{display:block}.dib-l{display:inline-block}.dit-l{display:inline-table}.dt-l{display:table}.dtc-l{display:table-cell}.dt-row-l{display:table-row}.dt-row-group-l{display:table-row-group}.dt-column-l{display:table-column}.dt-column-group-l{display:table-column-group}.dt--fixed-l{table-layout:fixed;width:100%}.flex-l{display:flex}.inline-flex-l{display:inline-flex}.flex-auto-l{flex:1 1 auto;min-width:0;min-height:0}.flex-none-l{flex:none}.flex-column-l{flex-direction:column}.flex-row-l{flex-direction:row}.flex-wrap-l{flex-wrap:wrap}.flex-nowrap-l{flex-wrap:nowrap}.flex-wrap-reverse-l{flex-wrap:wrap-reverse}.flex-column-reverse-l{flex-direction:column-reverse}.flex-row-reverse-l{flex-direction:row-reverse}.items-start-l{align-items:flex-start}.items-end-l{align-items:flex-end}.items-center-l{align-items:center}.items-baseline-l{align-items:baseline}.items-stretch-l{align-items:stretch}.self-start-l{align-self:flex-start}.self-end-l{align-self:flex-end}.self-center-l{align-self:center}.self-baseline-l{align-self:baseline}.self-stretch-l{align-self:stretch}.justify-start-l{justify-content:flex-start}.justify-end-l{justify-content:flex-end}.justify-center-l{justify-content:center}.justify-between-l{justify-content:space-between}.justify-around-l{justify-content:space-around}.content-start-l{align-content:flex-start}.content-end-l{align-content:flex-end}.content-center-l{align-content:center}.content-between-l{align-content:space-between}.content-around-l{align-content:space-around}.content-stretch-l{align-content:stretch}.order-0-l{order:0}.order-1-l{order:1}.order-2-l{order:2}.order-3-l{order:3}.order-4-l{order:4}.order-5-l{order:5}.order-6-l{order:6}.order-7-l{order:7}.order-8-l{order:8}.order-last-l{order:99999}.flex-grow-0-l{flex-grow:0}.flex-grow-1-l{flex-grow:1}.flex-shrink-0-l{flex-shrink:0}.flex-shrink-1-l{flex-shrink:1}.fl-l{float:left}.fl-l,.fr-l{_display:inline}.fr-l{float:right}.fn-l{float:none}.i-l{font-style:italic}.fs-normal-l{font-style:normal}.normal-l{font-weight:400}.b-l{font-weight:700}.fw1-l{font-weight:100}.fw2-l{font-weight:200}.fw3-l{font-weight:300}.fw4-l{font-weight:400}.fw5-l{font-weight:500}.fw6-l{font-weight:600}.fw7-l{font-weight:700}.fw8-l{font-weight:800}.fw9-l{font-weight:900}.h1-l{height:1rem}.h2-l{height:2rem}.h3-l{height:4rem}.h4-l{height:8rem}.h5-l{height:16rem}.h-25-l{height:25%}.h-50-l{height:50%}.h-75-l{height:75%}.h-100-l{height:100%}.min-h-100-l{min-height:100%}.vh-25-l{height:25vh}.vh-50-l{height:50vh}.vh-75-l{height:75vh}.vh-100-l{height:100vh}.min-vh-100-l{min-height:100vh}.h-auto-l{height:auto}.h-inherit-l{height:inherit}.tracked-l{letter-spacing:.1em}.tracked-tight-l{letter-spacing:-.05em}.tracked-mega-l{letter-spacing:.25em}.lh-solid-l{line-height:1.333333}.lh-title-l{line-height:1.5}.lh-copy-l{line-height:1.666666}.mw1-l{max-width:1rem}.mw2-l{max-width:2rem}.mw3-l{max-width:4rem}.mw4-l{max-width:8rem}.mw5-l{max-width:16rem}.mw6-l{max-width:32rem}.mw7-l{max-width:48rem}.mw8-l{max-width:64rem}.mw9-l{max-width:96rem}.mw-none-l{max-width:none}.mw-100-l{max-width:100%}.o-100-l{opacity:1}.o-90-l{opacity:.9}.o-80-l{opacity:.8}.o-70-l{opacity:.7}.o-60-l{opacity:.6}.o-50-l{opacity:.5}.o-40-l{opacity:.4}.o-30-l{opacity:.3}.o-20-l{opacity:.2}.o-10-l{opacity:.1}.o-05-l{opacity:.05}.o-025-l{opacity:.025}.o-0-l{opacity:0}.rotate-45-l{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90-l{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135-l{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180-l{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225-l{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270-l{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315-l{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.outline-l{outline:1px solid}.outline-transparent-l{outline:1px solid transparent}.outline-0-l{outline:0}.overflow-visible-l{overflow:visible}.overflow-hidden-l{overflow:hidden}.overflow-scroll-l{overflow:scroll}.overflow-auto-l{overflow:auto}.overflow-x-visible-l{overflow-x:visible}.overflow-x-hidden-l{overflow-x:hidden}.overflow-x-scroll-l{overflow-x:scroll}.overflow-x-auto-l{overflow-x:auto}.overflow-y-visible-l{overflow-y:visible}.overflow-y-hidden-l{overflow-y:hidden}.overflow-y-scroll-l{overflow-y:scroll}.overflow-y-auto-l{overflow-y:auto}.static-l{position:static}.relative-l{position:relative}.absolute-l{position:absolute}.fixed-l{position:fixed}.strike-l{text-decoration:line-through}.underline-l{text-decoration:underline}.no-underline-l{text-decoration:none}.tl-l{text-align:left}.tr-l{text-align:right}.tc-l{text-align:center}.tj-l{text-align:justify}.ttc-l{text-transform:capitalize}.ttl-l{text-transform:lowercase}.ttu-l{text-transform:uppercase}.ttn-l{text-transform:none}.f1-l{font-size:4.5rem}.f2-l{font-size:4rem}.f3-l{font-size:3rem}.f4-l{font-size:2rem}.f5-l{font-size:1.5rem}.f6-l{font-size:1.125rem}.f7-l{font-size:1rem}.f8-l{font-size:.875rem}.f9-l{font-size:.75rem}.measure-l{max-width:30em}.measure-wide-l{max-width:34em}.measure-narrow-l{max-width:20em}.small-caps-l{font-variant:small-caps}.indent-l{text-indent:1em;margin-top:0;margin-bottom:0}.truncate-l{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.clip-l{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal-l{white-space:normal}.nowrap-l{white-space:nowrap}.pre-l{white-space:pre}.w1-l{width:1rem}.w2-l{width:2rem}.w3-l{width:4rem}.w4-l{width:8rem}.w5-l{width:16rem}.w-10-l{width:10%}.w-20-l{width:20%}.w-25-l{width:25%}.w-30-l{width:30%}.w-33-l{width:33%}.w-34-l{width:34%}.w-40-l{width:40%}.w-50-l{width:50%}.w-60-l{width:60%}.w-70-l{width:70%}.w-75-l{width:75%}.w-80-l{width:80%}.w-90-l{width:90%}.w-100-l{width:100%}.w-third-l{width:33.33333%}.w-two-thirds-l{width:66.66667%}.w-auto-l{width:auto}}@media screen and (min-width:60em){.aspect-ratio-xl{height:0;position:relative}.aspect-ratio--16x9-xl{padding-bottom:56.25%}.aspect-ratio--9x16-xl{padding-bottom:177.77%}.aspect-ratio--4x3-xl{padding-bottom:75%}.aspect-ratio--3x4-xl{padding-bottom:133.33%}.aspect-ratio--6x4-xl{padding-bottom:66.6%}.aspect-ratio--4x6-xl{padding-bottom:150%}.aspect-ratio--8x5-xl{padding-bottom:62.5%}.aspect-ratio--5x8-xl{padding-bottom:160%}.aspect-ratio--7x5-xl{padding-bottom:71.42%}.aspect-ratio--5x7-xl{padding-bottom:140%}.aspect-ratio--1x1-xl{padding-bottom:100%}.aspect-ratio--object-xl{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover-xl{background-size:cover!important}.contain-xl{background-size:contain!important}.bg-center-xl{background-position:50%}.bg-center-xl,.bg-top-xl{background-repeat:no-repeat}.bg-top-xl{background-position:top}.bg-right-xl{background-position:100%}.bg-bottom-xl,.bg-right-xl{background-repeat:no-repeat}.bg-bottom-xl{background-position:bottom}.bg-left-xl{background-repeat:no-repeat;background-position:0}.ba-xl{border-style:solid;border-width:1px}.bt-xl{border-top-style:solid;border-top-width:1px}.br-xl{border-right-style:solid;border-right-width:1px}.bb-xl{border-bottom-style:solid;border-bottom-width:1px}.bl-xl{border-left-style:solid;border-left-width:1px}.bn-xl{border-style:none;border-width:0}.br0-xl{border-radius:0}.br1-xl{border-radius:.125rem}.br2-xl{border-radius:.25rem}.br3-xl{border-radius:.5rem}.br4-xl{border-radius:1rem}.br-100-xl{border-radius:100%}.br-pill-xl{border-radius:9999px}.br--bottom-xl{border-top-left-radius:0;border-top-right-radius:0}.br--top-xl{border-bottom-right-radius:0}.br--right-xl,.br--top-xl{border-bottom-left-radius:0}.br--right-xl{border-top-left-radius:0}.br--left-xl{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted-xl{border-style:dotted}.b--dashed-xl{border-style:dashed}.b--solid-xl{border-style:solid}.b--none-xl{border-style:none}.bw0-xl{border-width:0}.bw1-xl{border-width:.125rem}.bw2-xl{border-width:.25rem}.bw3-xl{border-width:.5rem}.bw4-xl{border-width:1rem}.bw5-xl{border-width:2rem}.bt-0-xl{border-top-width:0}.br-0-xl{border-right-width:0}.bb-0-xl{border-bottom-width:0}.bl-0-xl{border-left-width:0}.shadow-1-xl{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2-xl{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3-xl{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4-xl{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5-xl{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.pa0-xl{padding:0}.ma0-xl,.na0-xl{margin:0}.pl0-xl{padding-left:0}.ml0-xl,.nl0-xl{margin-left:0}.pr0-xl{padding-right:0}.mr0-xl,.nr0-xl{margin-right:0}.pt0-xl{padding-top:0}.mt0-xl,.nt0-xl{margin-top:0}.pb0-xl{padding-bottom:0}.mb0-xl,.nb0-xl{margin-bottom:0}.pv0-xl{padding-top:0;padding-bottom:0}.mv0-xl,.nv0-xl{margin-top:0;margin-bottom:0}.ph0-xl{padding-left:0;padding-right:0}.mh0-xl,.nh0-xl{margin-left:0;margin-right:0}.pa1-xl{padding:.25rem}.ma1-xl{margin:.25rem}.na1-xl{margin:-.25rem}.pl1-xl{padding-left:.25rem}.ml1-xl{margin-left:.25rem}.nl1-xl{margin-left:-.25rem}.pr1-xl{padding-right:.25rem}.mr1-xl{margin-right:.25rem}.nr1-xl{margin-right:-.25rem}.pt1-xl{padding-top:.25rem}.mt1-xl{margin-top:.25rem}.nt1-xl{margin-top:-.25rem}.pb1-xl{padding-bottom:.25rem}.mb1-xl{margin-bottom:.25rem}.nb1-xl{margin-bottom:-.25rem}.pv1-xl{padding-top:.25rem;padding-bottom:.25rem}.mv1-xl{margin-top:.25rem;margin-bottom:.25rem}.nv1-xl{margin-top:-.25rem;margin-bottom:-.25rem}.ph1-xl{padding-left:.25rem;padding-right:.25rem}.mh1-xl{margin-left:.25rem;margin-right:.25rem}.nh1-xl{margin-left:-.25rem;margin-right:-.25rem}.pa2-xl{padding:.5rem}.ma2-xl{margin:.5rem}.na2-xl{margin:-.5rem}.pl2-xl{padding-left:.5rem}.ml2-xl{margin-left:.5rem}.nl2-xl{margin-left:-.5rem}.pr2-xl{padding-right:.5rem}.mr2-xl{margin-right:.5rem}.nr2-xl{margin-right:-.5rem}.pt2-xl{padding-top:.5rem}.mt2-xl{margin-top:.5rem}.nt2-xl{margin-top:-.5rem}.pb2-xl{padding-bottom:.5rem}.mb2-xl{margin-bottom:.5rem}.nb2-xl{margin-bottom:-.5rem}.pv2-xl{padding-top:.5rem;padding-bottom:.5rem}.mv2-xl{margin-top:.5rem;margin-bottom:.5rem}.nv2-xl{margin-top:-.5rem;margin-bottom:-.5rem}.ph2-xl{padding-left:.5rem;padding-right:.5rem}.mh2-xl{margin-left:.5rem;margin-right:.5rem}.nh2-xl{margin-left:-.5rem;margin-right:-.5rem}.pa3-xl{padding:.75rem}.ma3-xl{margin:.75rem}.na3-xl{margin:-.75rem}.pl3-xl{padding-left:.75rem}.ml3-xl{margin-left:.75rem}.nl3-xl{margin-left:-.75rem}.pr3-xl{padding-right:.75rem}.mr3-xl{margin-right:.75rem}.nr3-xl{margin-right:-.75rem}.pt3-xl{padding-top:.75rem}.mt3-xl{margin-top:.75rem}.nt3-xl{margin-top:-.75rem}.pb3-xl{padding-bottom:.75rem}.mb3-xl{margin-bottom:.75rem}.nb3-xl{margin-bottom:-.75rem}.pv3-xl{padding-top:.75rem;padding-bottom:.75rem}.mv3-xl{margin-top:.75rem;margin-bottom:.75rem}.nv3-xl{margin-top:-.75rem;margin-bottom:-.75rem}.ph3-xl{padding-left:.75rem;padding-right:.75rem}.mh3-xl{margin-left:.75rem;margin-right:.75rem}.nh3-xl{margin-left:-.75rem;margin-right:-.75rem}.pa4-xl{padding:1rem}.ma4-xl{margin:1rem}.na4-xl{margin:-1rem}.pl4-xl{padding-left:1rem}.ml4-xl{margin-left:1rem}.nl4-xl{margin-left:-1rem}.pr4-xl{padding-right:1rem}.mr4-xl{margin-right:1rem}.nr4-xl{margin-right:-1rem}.pt4-xl{padding-top:1rem}.mt4-xl{margin-top:1rem}.nt4-xl{margin-top:-1rem}.pb4-xl{padding-bottom:1rem}.mb4-xl{margin-bottom:1rem}.nb4-xl{margin-bottom:-1rem}.pv4-xl{padding-top:1rem;padding-bottom:1rem}.mv4-xl{margin-top:1rem;margin-bottom:1rem}.nv4-xl{margin-top:-1rem;margin-bottom:-1rem}.ph4-xl{padding-left:1rem;padding-right:1rem}.mh4-xl{margin-left:1rem;margin-right:1rem}.nh4-xl{margin-left:-1rem;margin-right:-1rem}.pa5-xl{padding:1.25rem}.ma5-xl{margin:1.25rem}.na5-xl{margin:-1.25rem}.pl5-xl{padding-left:1.25rem}.ml5-xl{margin-left:1.25rem}.nl5-xl{margin-left:-1.25rem}.pr5-xl{padding-right:1.25rem}.mr5-xl{margin-right:1.25rem}.nr5-xl{margin-right:-1.25rem}.pt5-xl{padding-top:1.25rem}.mt5-xl{margin-top:1.25rem}.nt5-xl{margin-top:-1.25rem}.pb5-xl{padding-bottom:1.25rem}.mb5-xl{margin-bottom:1.25rem}.nb5-xl{margin-bottom:-1.25rem}.pv5-xl{padding-top:1.25rem;padding-bottom:1.25rem}.mv5-xl{margin-top:1.25rem;margin-bottom:1.25rem}.nv5-xl{margin-top:-1.25rem;margin-bottom:-1.25rem}.ph5-xl{padding-left:1.25rem;padding-right:1.25rem}.mh5-xl{margin-left:1.25rem;margin-right:1.25rem}.nh5-xl{margin-left:-1.25rem;margin-right:-1.25rem}.pa6-xl{padding:1.5rem}.ma6-xl{margin:1.5rem}.na6-xl{margin:-1.5rem}.pl6-xl{padding-left:1.5rem}.ml6-xl{margin-left:1.5rem}.nl6-xl{margin-left:-1.5rem}.pr6-xl{padding-right:1.5rem}.mr6-xl{margin-right:1.5rem}.nr6-xl{margin-right:-1.5rem}.pt6-xl{padding-top:1.5rem}.mt6-xl{margin-top:1.5rem}.nt6-xl{margin-top:-1.5rem}.pb6-xl{padding-bottom:1.5rem}.mb6-xl{margin-bottom:1.5rem}.nb6-xl{margin-bottom:-1.5rem}.pv6-xl{padding-top:1.5rem;padding-bottom:1.5rem}.mv6-xl{margin-top:1.5rem;margin-bottom:1.5rem}.nv6-xl{margin-top:-1.5rem;margin-bottom:-1.5rem}.ph6-xl{padding-left:1.5rem;padding-right:1.5rem}.mh6-xl{margin-left:1.5rem;margin-right:1.5rem}.nh6-xl{margin-left:-1.5rem;margin-right:-1.5rem}.pa7-xl{padding:2rem}.ma7-xl{margin:2rem}.na7-xl{margin:-2rem}.pl7-xl{padding-left:2rem}.ml7-xl{margin-left:2rem}.nl7-xl{margin-left:-2rem}.pr7-xl{padding-right:2rem}.mr7-xl{margin-right:2rem}.nr7-xl{margin-right:-2rem}.pt7-xl{padding-top:2rem}.mt7-xl{margin-top:2rem}.nt7-xl{margin-top:-2rem}.pb7-xl{padding-bottom:2rem}.mb7-xl{margin-bottom:2rem}.nb7-xl{margin-bottom:-2rem}.pv7-xl{padding-top:2rem;padding-bottom:2rem}.mv7-xl{margin-top:2rem;margin-bottom:2rem}.nv7-xl{margin-top:-2rem;margin-bottom:-2rem}.ph7-xl{padding-left:2rem;padding-right:2rem}.mh7-xl{margin-left:2rem;margin-right:2rem}.nh7-xl{margin-left:-2rem;margin-right:-2rem}.pa8-xl{padding:3rem}.ma8-xl{margin:3rem}.na8-xl{margin:-3rem}.pl8-xl{padding-left:3rem}.ml8-xl{margin-left:3rem}.nl8-xl{margin-left:-3rem}.pr8-xl{padding-right:3rem}.mr8-xl{margin-right:3rem}.nr8-xl{margin-right:-3rem}.pt8-xl{padding-top:3rem}.mt8-xl{margin-top:3rem}.nt8-xl{margin-top:-3rem}.pb8-xl{padding-bottom:3rem}.mb8-xl{margin-bottom:3rem}.nb8-xl{margin-bottom:-3rem}.pv8-xl{padding-top:3rem;padding-bottom:3rem}.mv8-xl{margin-top:3rem;margin-bottom:3rem}.nv8-xl{margin-top:-3rem;margin-bottom:-3rem}.ph8-xl{padding-left:3rem;padding-right:3rem}.mh8-xl{margin-left:3rem;margin-right:3rem}.nh8-xl{margin-left:-3rem;margin-right:-3rem}.pa9-xl{padding:4rem}.ma9-xl{margin:4rem}.na9-xl{margin:-4rem}.pl9-xl{padding-left:4rem}.ml9-xl{margin-left:4rem}.nl9-xl{margin-left:-4rem}.pr9-xl{padding-right:4rem}.mr9-xl{margin-right:4rem}.nr9-xl{margin-right:-4rem}.pt9-xl{padding-top:4rem}.mt9-xl{margin-top:4rem}.nt9-xl{margin-top:-4rem}.pb9-xl{padding-bottom:4rem}.mb9-xl{margin-bottom:4rem}.nb9-xl{margin-bottom:-4rem}.pv9-xl{padding-top:4rem;padding-bottom:4rem}.mv9-xl{margin-top:4rem;margin-bottom:4rem}.nv9-xl{margin-top:-4rem;margin-bottom:-4rem}.ph9-xl{padding-left:4rem;padding-right:4rem}.mh9-xl{margin-left:4rem;margin-right:4rem}.nh9-xl{margin-left:-4rem;margin-right:-4rem}.pa10-xl{padding:6rem}.ma10-xl{margin:6rem}.na10-xl{margin:-6rem}.pl10-xl{padding-left:6rem}.ml10-xl{margin-left:6rem}.nl10-xl{margin-left:-6rem}.pr10-xl{padding-right:6rem}.mr10-xl{margin-right:6rem}.nr10-xl{margin-right:-6rem}.pt10-xl{padding-top:6rem}.mt10-xl{margin-top:6rem}.nt10-xl{margin-top:-6rem}.pb10-xl{padding-bottom:6rem}.mb10-xl{margin-bottom:6rem}.nb10-xl{margin-bottom:-6rem}.pv10-xl{padding-top:6rem;padding-bottom:6rem}.mv10-xl{margin-top:6rem;margin-bottom:6rem}.nv10-xl{margin-top:-6rem;margin-bottom:-6rem}.ph10-xl{padding-left:6rem;padding-right:6rem}.mh10-xl{margin-left:6rem;margin-right:6rem}.nh10-xl{margin-left:-6rem;margin-right:-6rem}.pa11-xl{padding:10rem}.ma11-xl{margin:10rem}.na11-xl{margin:-10rem}.pl11-xl{padding-left:10rem}.ml11-xl{margin-left:10rem}.nl11-xl{margin-left:-10rem}.pr11-xl{padding-right:10rem}.mr11-xl{margin-right:10rem}.nr11-xl{margin-right:-10rem}.pt11-xl{padding-top:10rem}.mt11-xl{margin-top:10rem}.nt11-xl{margin-top:-10rem}.pb11-xl{padding-bottom:10rem}.mb11-xl{margin-bottom:10rem}.nb11-xl{margin-bottom:-10rem}.pv11-xl{padding-top:10rem;padding-bottom:10rem}.mv11-xl{margin-top:10rem;margin-bottom:10rem}.nv11-xl{margin-top:-10rem;margin-bottom:-10rem}.ph11-xl{padding-left:10rem;padding-right:10rem}.mh11-xl{margin-left:10rem;margin-right:10rem}.nh11-xl{margin-left:-10rem;margin-right:-10rem}.pa12-xl{padding:18rem}.ma12-xl{margin:18rem}.na12-xl{margin:-18rem}.pl12-xl{padding-left:18rem}.ml12-xl{margin-left:18rem}.nl12-xl{margin-left:-18rem}.pr12-xl{padding-right:18rem}.mr12-xl{margin-right:18rem}.nr12-xl{margin-right:-18rem}.pt12-xl{padding-top:18rem}.mt12-xl{margin-top:18rem}.nt12-xl{margin-top:-18rem}.pb12-xl{padding-bottom:18rem}.mb12-xl{margin-bottom:18rem}.nb12-xl{margin-bottom:-18rem}.pv12-xl{padding-top:18rem;padding-bottom:18rem}.mv12-xl{margin-top:18rem;margin-bottom:18rem}.nv12-xl{margin-top:-18rem;margin-bottom:-18rem}.ph12-xl{padding-left:18rem;padding-right:18rem}.mh12-xl{margin-left:18rem;margin-right:18rem}.nh12-xl{margin-left:-18rem;margin-right:-18rem}.top-0-xl{top:0}.right-0-xl{right:0}.bottom-0-xl{bottom:0}.left-0-xl{left:0}.top-1-xl{top:1rem}.right-1-xl{right:1rem}.bottom-1-xl{bottom:1rem}.left-1-xl{left:1rem}.top-2-xl{top:2rem}.right-2-xl{right:2rem}.bottom-2-xl{bottom:2rem}.left-2-xl{left:2rem}.top--1-xl{top:-1rem}.right--1-xl{right:-1rem}.bottom--1-xl{bottom:-1rem}.left--1-xl{left:-1rem}.top--2-xl{top:-2rem}.right--2-xl{right:-2rem}.bottom--2-xl{bottom:-2rem}.left--2-xl{left:-2rem}.absolute--fill-xl{top:0;right:0;bottom:0;left:0}.cf-xl:after,.cf-xl:before{content:" ";display:table}.cf-xl:after{clear:both}.cf-xl{*zoom:1}.cl-xl{clear:left}.cr-xl{clear:right}.cb-xl{clear:both}.cn-xl{clear:none}.dn-xl{display:none}.di-xl{display:inline}.db-xl{display:block}.dib-xl{display:inline-block}.dit-xl{display:inline-table}.dt-xl{display:table}.dtc-xl{display:table-cell}.dt-row-xl{display:table-row}.dt-row-group-xl{display:table-row-group}.dt-column-xl{display:table-column}.dt-column-group-xl{display:table-column-group}.dt--fixed-xl{table-layout:fixed;width:100%}.flex-xl{display:flex}.inline-flex-xl{display:inline-flex}.flex-auto-xl{flex:1 1 auto;min-width:0;min-height:0}.flex-none-xl{flex:none}.flex-column-xl{flex-direction:column}.flex-row-xl{flex-direction:row}.flex-wrap-xl{flex-wrap:wrap}.flex-nowrap-xl{flex-wrap:nowrap}.flex-wrap-reverse-xl{flex-wrap:wrap-reverse}.flex-column-reverse-xl{flex-direction:column-reverse}.flex-row-reverse-xl{flex-direction:row-reverse}.items-start-xl{align-items:flex-start}.items-end-xl{align-items:flex-end}.items-center-xl{align-items:center}.items-baseline-xl{align-items:baseline}.items-stretch-xl{align-items:stretch}.self-start-xl{align-self:flex-start}.self-end-xl{align-self:flex-end}.self-center-xl{align-self:center}.self-baseline-xl{align-self:baseline}.self-stretch-xl{align-self:stretch}.justify-start-xl{justify-content:flex-start}.justify-end-xl{justify-content:flex-end}.justify-center-xl{justify-content:center}.justify-between-xl{justify-content:space-between}.justify-around-xl{justify-content:space-around}.content-start-xl{align-content:flex-start}.content-end-xl{align-content:flex-end}.content-center-xl{align-content:center}.content-between-xl{align-content:space-between}.content-around-xl{align-content:space-around}.content-stretch-xl{align-content:stretch}.order-0-xl{order:0}.order-1-xl{order:1}.order-2-xl{order:2}.order-3-xl{order:3}.order-4-xl{order:4}.order-5-xl{order:5}.order-6-xl{order:6}.order-7-xl{order:7}.order-8-xl{order:8}.order-last-xl{order:99999}.flex-grow-0-xl{flex-grow:0}.flex-grow-1-xl{flex-grow:1}.flex-shrink-0-xl{flex-shrink:0}.flex-shrink-1-xl{flex-shrink:1}.fl-xl{float:left}.fl-xl,.fr-xl{_display:inline}.fr-xl{float:right}.fn-xl{float:none}.i-xl{font-style:italic}.fs-normal-xl{font-style:normal}.normal-xl{font-weight:400}.b-xl{font-weight:700}.fw1-xl{font-weight:100}.fw2-xl{font-weight:200}.fw3-xl{font-weight:300}.fw4-xl{font-weight:400}.fw5-xl{font-weight:500}.fw6-xl{font-weight:600}.fw7-xl{font-weight:700}.fw8-xl{font-weight:800}.fw9-xl{font-weight:900}.h1-xl{height:1rem}.h2-xl{height:2rem}.h3-xl{height:4rem}.h4-xl{height:8rem}.h5-xl{height:16rem}.h-25-xl{height:25%}.h-50-xl{height:50%}.h-75-xl{height:75%}.h-100-xl{height:100%}.min-h-100-xl{min-height:100%}.vh-25-xl{height:25vh}.vh-50-xl{height:50vh}.vh-75-xl{height:75vh}.vh-100-xl{height:100vh}.min-vh-100-xl{min-height:100vh}.h-auto-xl{height:auto}.h-inherit-xl{height:inherit}.tracked-xl{letter-spacing:.1em}.tracked-tight-xl{letter-spacing:-.05em}.tracked-mega-xl{letter-spacing:.25em}.lh-solid-xl{line-height:1.333333}.lh-title-xl{line-height:1.5}.lh-copy-xl{line-height:1.666666}.mw1-xl{max-width:1rem}.mw2-xl{max-width:2rem}.mw3-xl{max-width:4rem}.mw4-xl{max-width:8rem}.mw5-xl{max-width:16rem}.mw6-xl{max-width:32rem}.mw7-xl{max-width:48rem}.mw8-xl{max-width:64rem}.mw9-xl{max-width:96rem}.mw-none-xl{max-width:none}.mw-100-xl{max-width:100%}.o-100-xl{opacity:1}.o-90-xl{opacity:.9}.o-80-xl{opacity:.8}.o-70-xl{opacity:.7}.o-60-xl{opacity:.6}.o-50-xl{opacity:.5}.o-40-xl{opacity:.4}.o-30-xl{opacity:.3}.o-20-xl{opacity:.2}.o-10-xl{opacity:.1}.o-05-xl{opacity:.05}.o-025-xl{opacity:.025}.o-0-xl{opacity:0}.rotate-45-xl{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90-xl{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135-xl{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180-xl{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225-xl{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270-xl{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315-xl{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.outline-xl{outline:1px solid}.outline-transparent-xl{outline:1px solid transparent}.outline-0-xl{outline:0}.overflow-visible-xl{overflow:visible}.overflow-hidden-xl{overflow:hidden}.overflow-scroll-xl{overflow:scroll}.overflow-auto-xl{overflow:auto}.overflow-x-visible-xl{overflow-x:visible}.overflow-x-hidden-xl{overflow-x:hidden}.overflow-x-scroll-xl{overflow-x:scroll}.overflow-x-auto-xl{overflow-x:auto}.overflow-y-visible-xl{overflow-y:visible}.overflow-y-hidden-xl{overflow-y:hidden}.overflow-y-scroll-xl{overflow-y:scroll}.overflow-y-auto-xl{overflow-y:auto}.static-xl{position:static}.relative-xl{position:relative}.absolute-xl{position:absolute}.fixed-xl{position:fixed}.strike-xl{text-decoration:line-through}.underline-xl{text-decoration:underline}.no-underline-xl{text-decoration:none}.tl-xl{text-align:left}.tr-xl{text-align:right}.tc-xl{text-align:center}.tj-xl{text-align:justify}.ttc-xl{text-transform:capitalize}.ttl-xl{text-transform:lowercase}.ttu-xl{text-transform:uppercase}.ttn-xl{text-transform:none}.f1-xl{font-size:4.5rem}.f2-xl{font-size:4rem}.f3-xl{font-size:3rem}.f4-xl{font-size:2rem}.f5-xl{font-size:1.5rem}.f6-xl{font-size:1.125rem}.f7-xl{font-size:1rem}.f8-xl{font-size:.875rem}.f9-xl{font-size:.75rem}.measure-xl{max-width:30em}.measure-wide-xl{max-width:34em}.measure-narrow-xl{max-width:20em}.small-caps-xl{font-variant:small-caps}.indent-xl{text-indent:1em;margin-top:0;margin-bottom:0}.truncate-xl{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.clip-xl{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal-xl{white-space:normal}.nowrap-xl{white-space:nowrap}.pre-xl{white-space:pre}.w1-xl{width:1rem}.w2-xl{width:2rem}.w3-xl{width:4rem}.w4-xl{width:8rem}.w5-xl{width:16rem}.w-10-xl{width:10%}.w-20-xl{width:20%}.w-25-xl{width:25%}.w-30-xl{width:30%}.w-33-xl{width:33%}.w-34-xl{width:34%}.w-40-xl{width:40%}.w-50-xl{width:50%}.w-60-xl{width:60%}.w-70-xl{width:70%}.w-75-xl{width:75%}.w-80-xl{width:80%}.w-90-xl{width:90%}.w-100-xl{width:100%}.w-third-xl{width:33.33333%}.w-two-thirds-xl{width:66.66667%}.w-auto-xl{width:auto}}@font-face{font-family:Inter;font-style:normal;font-weight:400;src:url(https://media.urbit.org/fonts/Inter-Regular.woff2) format("woff2")}@font-face{font-family:Inter;font-style:italic;font-weight:400;src:url(https://media.urbit.org/fonts/Inter-Italic.woff2) format("woff2")}@font-face{font-family:Inter;font-style:normal;font-weight:700;src:url(https://media.urbit.org/fonts/Inter-Bold.woff2) format("woff2")}@font-face{font-family:Inter;font-style:italic;font-weight:700;src:url(https://media.urbit.org/fonts/Inter-BoldItalic.woff2) format("woff2")}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-extralight.woff);font-weight:200}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-light.woff);font-weight:300}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-regular.woff);font-weight:400}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-medium.woff);font-weight:500}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-semibold.woff);font-weight:600}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-bold.woff);font-weight:700}body,html{height:100%;width:100%;-webkit-font-smoothing:antialiased;overflow:hidden}a,button,h1,h2,h3,h4,h5,h6,input,p,textarea{margin-block-end:unset;margin-block-start:unset;-webkit-margin-before:unset;-webkit-margin-after:unset;font-family:Inter,sans-serif;padding:0}button,summary{cursor:pointer}h2{font-weight:400}a{color:#000;text-decoration:none}.inter{font-family:Inter,sans-serif}.clamp-3{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-webkit-line-clamp:2;-webkit-box-orient:vertical}.clamp-message{max-width:calc(100% - 36px - 1.5rem)}.clamp-attachment{overflow:scroll;max-height:10em;max-width:100%}.lh-16{line-height:16px}.mono{font-family:Source Code Pro,monospace}.list-ship{line-height:2.2}.bg-welcome-green{background-color:#ecf6f2}.c-default{cursor:default}.m0a{margin:0 auto}.mix-blend-diff{mix-blend-mode:difference}.focus-b--black:focus{border-color:#000}.embed-container{position:relative;height:0;overflow:hidden;padding-bottom:56.25%}.embed-container embed,.embed-container iframe,.embed-container object{position:absolute;top:0;left:0;width:100%;height:100%}.spin-active{animation:spin 2s infinite}@keyframes spin{0%{transform:rotate(0deg)}25%{transform:rotate(90deg)}50%{transform:rotate(180deg)}75%{transform:rotate(270deg)}to{transform:rotate(1turn)}}.toggle:after{content:"";height:12px;width:12px;background:#fff;position:absolute;top:2px;left:2px;border-radius:100%}.toggle.checked:after{left:14px}@media (max-width:34.375em){.dn-s{display:none}.flex-basis-100-s,.flex-basis-full-s{flex-basis:100%}.h-100-m-40-s{height:calc(100% - 40px)}.black-s{color:#000}}@media (min-width:34.375em){.db-ns{display:block}.flex-basis-30-ns{flex-basis:30vw}.h-100-m-40-ns{height:calc(100% - 40px)}}@media (prefers-color-scheme:dark){body{background-color:#333}.bg-black-d{background-color:#000}.white-d{color:#fff}.gray1-d{color:#4d4d4d}.gray2-d{color:#7f7f7f}.gray3-d{color:#b1b2b3}.gray4-d{color:#e6e6e6}.bg-gray0-d{background-color:#333}.bg-gray1-d{background-color:#4d4d4d}.b--gray0-d{border-color:#333}.b--gray1-d{border-color:#4d4d4d}.b--gray2-d{border-color:#7f7f7f}.b--white-d{border-color:#fff}.bb-d{border-bottom-width:1px;border-bottom-style:solid}.invert-d{filter:invert(1)}.o-60-d{opacity:.6}.focus-b--white-d:focus{border-color:#fff}a{color:#fff}.hover-bg-gray1-d:hover{background-color:#4d4d4d}} \ No newline at end of file +/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}[hidden],template{display:none}.aspect-ratio{height:0;position:relative}.aspect-ratio--16x9{padding-bottom:56.25%}.aspect-ratio--9x16{padding-bottom:177.77%}.aspect-ratio--4x3{padding-bottom:75%}.aspect-ratio--3x4{padding-bottom:133.33%}.aspect-ratio--6x4{padding-bottom:66.6%}.aspect-ratio--4x6{padding-bottom:150%}.aspect-ratio--8x5{padding-bottom:62.5%}.aspect-ratio--5x8{padding-bottom:160%}.aspect-ratio--7x5{padding-bottom:71.42%}.aspect-ratio--5x7{padding-bottom:140%}.aspect-ratio--1x1{padding-bottom:100%}.aspect-ratio--object{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover{background-size:cover!important}.contain{background-size:contain!important}.bg-center{background-position:50%}.bg-center,.bg-top{background-repeat:no-repeat}.bg-top{background-position:top}.bg-right{background-position:100%}.bg-bottom,.bg-right{background-repeat:no-repeat}.bg-bottom{background-position:bottom}.bg-left{background-repeat:no-repeat;background-position:0}.ba{border-style:solid;border-width:1px}.bt{border-top-style:solid;border-top-width:1px}.br{border-right-style:solid;border-right-width:1px}.bb{border-bottom-style:solid;border-bottom-width:1px}.bl{border-left-style:solid;border-left-width:1px}.bn{border-style:none;border-width:0}.b--black{border-color:#000}.b--white{border-color:#fff}.b--gray0{border-color:#333}.b--gray1{border-color:#4d4d4d}.b--gray2{border-color:#7f7f7f}.b--gray3{border-color:#b1b2b3}.b--gray4{border-color:#e6e6e6}.b--gray5{border-color:#f9f9f9}.b--blue0{border-color:#ecf6ff}.b--blue1{border-color:#b0c7ff}.b--blue2{border-color:#4330fc}.b--blue3{border-color:#190d7b}.b--red0{border-color:#f9d6ce}.b--red1{border-color:#ffa073}.b--red2{border-color:#ee5432}.b--red3{border-color:#c10d30}.b--green0{border-color:#bdebcc}.b--green1{border-color:#2ed196}.b--green2{border-color:#2aa779}.b--green3{border-color:#286e55}.b--yellow0{border-color:#ffefc5}.b--yellow1{border-color:#ffd972}.b--yellow2{border-color:#fcc440}.b--yellow3{border-color:#ee892b}.b--transparent{border-color:transparent}.br0{border-radius:0}.br1{border-radius:.125rem}.br2{border-radius:.25rem}.br3{border-radius:.5rem}.br4{border-radius:1rem}.br-100{border-radius:100%}.br-pill{border-radius:9999px}.br--bottom{border-top-left-radius:0;border-top-right-radius:0}.br--top{border-bottom-right-radius:0}.br--right,.br--top{border-bottom-left-radius:0}.br--right{border-top-left-radius:0}.br--left{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted{border-style:dotted}.b--dashed{border-style:dashed}.b--solid{border-style:solid}.b--none{border-style:none}.bw0{border-width:0}.bw1{border-width:.125rem}.bw2{border-width:.25rem}.bw3{border-width:.5rem}.bw4{border-width:1rem}.bw5{border-width:2rem}.bt-0{border-top-width:0}.br-0{border-right-width:0}.bb-0{border-bottom-width:0}.bl-0{border-left-width:0}.shadow-1{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.border-box,a,article,aside,blockquote,body,code,dd,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,html,input[type=email],input[type=number],input[type=password],input[type=tel],input[type=text],input[type=url],legend,li,main,nav,ol,p,pre,section,table,td,textarea,th,tr,ul{box-sizing:border-box}.pre{overflow-x:auto;overflow-y:hidden;overflow:scroll}.pa0{padding:0}.ma0,.na0{margin:0}.pl0{padding-left:0}.ml0,.nl0{margin-left:0}.pr0{padding-right:0}.mr0,.nr0{margin-right:0}.pt0{padding-top:0}.mt0,.nt0{margin-top:0}.pb0{padding-bottom:0}.mb0,.nb0{margin-bottom:0}.pv0{padding-top:0;padding-bottom:0}.mv0,.nv0{margin-top:0;margin-bottom:0}.ph0{padding-left:0;padding-right:0}.mh0,.nh0{margin-left:0;margin-right:0}.pa1{padding:.25rem}.ma1{margin:.25rem}.na1{margin:-.25rem}.pl1{padding-left:.25rem}.ml1{margin-left:.25rem}.nl1{margin-left:-.25rem}.pr1{padding-right:.25rem}.mr1{margin-right:.25rem}.nr1{margin-right:-.25rem}.pt1{padding-top:.25rem}.mt1{margin-top:.25rem}.nt1{margin-top:-.25rem}.pb1{padding-bottom:.25rem}.mb1{margin-bottom:.25rem}.nb1{margin-bottom:-.25rem}.pv1{padding-top:.25rem;padding-bottom:.25rem}.mv1{margin-top:.25rem;margin-bottom:.25rem}.nv1{margin-top:-.25rem;margin-bottom:-.25rem}.ph1{padding-left:.25rem;padding-right:.25rem}.mh1{margin-left:.25rem;margin-right:.25rem}.nh1{margin-left:-.25rem;margin-right:-.25rem}.pa2{padding:.5rem}.ma2{margin:.5rem}.na2{margin:-.5rem}.pl2{padding-left:.5rem}.ml2{margin-left:.5rem}.nl2{margin-left:-.5rem}.pr2{padding-right:.5rem}.mr2{margin-right:.5rem}.nr2{margin-right:-.5rem}.pt2{padding-top:.5rem}.mt2{margin-top:.5rem}.nt2{margin-top:-.5rem}.pb2{padding-bottom:.5rem}.mb2{margin-bottom:.5rem}.nb2{margin-bottom:-.5rem}.pv2{padding-top:.5rem;padding-bottom:.5rem}.mv2{margin-top:.5rem;margin-bottom:.5rem}.nv2{margin-top:-.5rem;margin-bottom:-.5rem}.ph2{padding-left:.5rem;padding-right:.5rem}.mh2{margin-left:.5rem;margin-right:.5rem}.nh2{margin-left:-.5rem;margin-right:-.5rem}.pa3{padding:.75rem}.ma3{margin:.75rem}.na3{margin:-.75rem}.pl3{padding-left:.75rem}.ml3{margin-left:.75rem}.nl3{margin-left:-.75rem}.pr3{padding-right:.75rem}.mr3{margin-right:.75rem}.nr3{margin-right:-.75rem}.pt3{padding-top:.75rem}.mt3{margin-top:.75rem}.nt3{margin-top:-.75rem}.pb3{padding-bottom:.75rem}.mb3{margin-bottom:.75rem}.nb3{margin-bottom:-.75rem}.pv3{padding-top:.75rem;padding-bottom:.75rem}.mv3{margin-top:.75rem;margin-bottom:.75rem}.nv3{margin-top:-.75rem;margin-bottom:-.75rem}.ph3{padding-left:.75rem;padding-right:.75rem}.mh3{margin-left:.75rem;margin-right:.75rem}.nh3{margin-left:-.75rem;margin-right:-.75rem}.pa4{padding:1rem}.ma4{margin:1rem}.na4{margin:-1rem}.pl4{padding-left:1rem}.ml4{margin-left:1rem}.nl4{margin-left:-1rem}.pr4{padding-right:1rem}.mr4{margin-right:1rem}.nr4{margin-right:-1rem}.pt4{padding-top:1rem}.mt4{margin-top:1rem}.nt4{margin-top:-1rem}.pb4{padding-bottom:1rem}.mb4{margin-bottom:1rem}.nb4{margin-bottom:-1rem}.pv4{padding-top:1rem;padding-bottom:1rem}.mv4{margin-top:1rem;margin-bottom:1rem}.nv4{margin-top:-1rem;margin-bottom:-1rem}.ph4{padding-left:1rem;padding-right:1rem}.mh4{margin-left:1rem;margin-right:1rem}.nh4{margin-left:-1rem;margin-right:-1rem}.pa5{padding:1.25rem}.ma5{margin:1.25rem}.na5{margin:-1.25rem}.pl5{padding-left:1.25rem}.ml5{margin-left:1.25rem}.nl5{margin-left:-1.25rem}.pr5{padding-right:1.25rem}.mr5{margin-right:1.25rem}.nr5{margin-right:-1.25rem}.pt5{padding-top:1.25rem}.mt5{margin-top:1.25rem}.nt5{margin-top:-1.25rem}.pb5{padding-bottom:1.25rem}.mb5{margin-bottom:1.25rem}.nb5{margin-bottom:-1.25rem}.pv5{padding-top:1.25rem;padding-bottom:1.25rem}.mv5{margin-top:1.25rem;margin-bottom:1.25rem}.nv5{margin-top:-1.25rem;margin-bottom:-1.25rem}.ph5{padding-left:1.25rem;padding-right:1.25rem}.mh5{margin-left:1.25rem;margin-right:1.25rem}.nh5{margin-left:-1.25rem;margin-right:-1.25rem}.pa6{padding:1.5rem}.ma6{margin:1.5rem}.na6{margin:-1.5rem}.pl6{padding-left:1.5rem}.ml6{margin-left:1.5rem}.nl6{margin-left:-1.5rem}.pr6{padding-right:1.5rem}.mr6{margin-right:1.5rem}.nr6{margin-right:-1.5rem}.pt6{padding-top:1.5rem}.mt6{margin-top:1.5rem}.nt6{margin-top:-1.5rem}.pb6{padding-bottom:1.5rem}.mb6{margin-bottom:1.5rem}.nb6{margin-bottom:-1.5rem}.pv6{padding-top:1.5rem;padding-bottom:1.5rem}.mv6{margin-top:1.5rem;margin-bottom:1.5rem}.nv6{margin-top:-1.5rem;margin-bottom:-1.5rem}.ph6{padding-left:1.5rem;padding-right:1.5rem}.mh6{margin-left:1.5rem;margin-right:1.5rem}.nh6{margin-left:-1.5rem;margin-right:-1.5rem}.pa7{padding:2rem}.ma7{margin:2rem}.na7{margin:-2rem}.pl7{padding-left:2rem}.ml7{margin-left:2rem}.nl7{margin-left:-2rem}.pr7{padding-right:2rem}.mr7{margin-right:2rem}.nr7{margin-right:-2rem}.pt7{padding-top:2rem}.mt7{margin-top:2rem}.nt7{margin-top:-2rem}.pb7{padding-bottom:2rem}.mb7{margin-bottom:2rem}.nb7{margin-bottom:-2rem}.pv7{padding-top:2rem;padding-bottom:2rem}.mv7{margin-top:2rem;margin-bottom:2rem}.nv7{margin-top:-2rem;margin-bottom:-2rem}.ph7{padding-left:2rem;padding-right:2rem}.mh7{margin-left:2rem;margin-right:2rem}.nh7{margin-left:-2rem;margin-right:-2rem}.pa8{padding:3rem}.ma8{margin:3rem}.na8{margin:-3rem}.pl8{padding-left:3rem}.ml8{margin-left:3rem}.nl8{margin-left:-3rem}.pr8{padding-right:3rem}.mr8{margin-right:3rem}.nr8{margin-right:-3rem}.pt8{padding-top:3rem}.mt8{margin-top:3rem}.nt8{margin-top:-3rem}.pb8{padding-bottom:3rem}.mb8{margin-bottom:3rem}.nb8{margin-bottom:-3rem}.pv8{padding-top:3rem;padding-bottom:3rem}.mv8{margin-top:3rem;margin-bottom:3rem}.nv8{margin-top:-3rem;margin-bottom:-3rem}.ph8{padding-left:3rem;padding-right:3rem}.mh8{margin-left:3rem;margin-right:3rem}.nh8{margin-left:-3rem;margin-right:-3rem}.pa9{padding:4rem}.ma9{margin:4rem}.na9{margin:-4rem}.pl9{padding-left:4rem}.ml9{margin-left:4rem}.nl9{margin-left:-4rem}.pr9{padding-right:4rem}.mr9{margin-right:4rem}.nr9{margin-right:-4rem}.pt9{padding-top:4rem}.mt9{margin-top:4rem}.nt9{margin-top:-4rem}.pb9{padding-bottom:4rem}.mb9{margin-bottom:4rem}.nb9{margin-bottom:-4rem}.pv9{padding-top:4rem;padding-bottom:4rem}.mv9{margin-top:4rem;margin-bottom:4rem}.nv9{margin-top:-4rem;margin-bottom:-4rem}.ph9{padding-left:4rem;padding-right:4rem}.mh9{margin-left:4rem;margin-right:4rem}.nh9{margin-left:-4rem;margin-right:-4rem}.pa10{padding:6rem}.ma10{margin:6rem}.na10{margin:-6rem}.pl10{padding-left:6rem}.ml10{margin-left:6rem}.nl10{margin-left:-6rem}.pr10{padding-right:6rem}.mr10{margin-right:6rem}.nr10{margin-right:-6rem}.pt10{padding-top:6rem}.mt10{margin-top:6rem}.nt10{margin-top:-6rem}.pb10{padding-bottom:6rem}.mb10{margin-bottom:6rem}.nb10{margin-bottom:-6rem}.pv10{padding-top:6rem;padding-bottom:6rem}.mv10{margin-top:6rem;margin-bottom:6rem}.nv10{margin-top:-6rem;margin-bottom:-6rem}.ph10{padding-left:6rem;padding-right:6rem}.mh10{margin-left:6rem;margin-right:6rem}.nh10{margin-left:-6rem;margin-right:-6rem}.pa11{padding:10rem}.ma11{margin:10rem}.na11{margin:-10rem}.pl11{padding-left:10rem}.ml11{margin-left:10rem}.nl11{margin-left:-10rem}.pr11{padding-right:10rem}.mr11{margin-right:10rem}.nr11{margin-right:-10rem}.pt11{padding-top:10rem}.mt11{margin-top:10rem}.nt11{margin-top:-10rem}.pb11{padding-bottom:10rem}.mb11{margin-bottom:10rem}.nb11{margin-bottom:-10rem}.pv11{padding-top:10rem;padding-bottom:10rem}.mv11{margin-top:10rem;margin-bottom:10rem}.nv11{margin-top:-10rem;margin-bottom:-10rem}.ph11{padding-left:10rem;padding-right:10rem}.mh11{margin-left:10rem;margin-right:10rem}.nh11{margin-left:-10rem;margin-right:-10rem}.pa12{padding:18rem}.ma12{margin:18rem}.na12{margin:-18rem}.pl12{padding-left:18rem}.ml12{margin-left:18rem}.nl12{margin-left:-18rem}.pr12{padding-right:18rem}.mr12{margin-right:18rem}.nr12{margin-right:-18rem}.pt12{padding-top:18rem}.mt12{margin-top:18rem}.nt12{margin-top:-18rem}.pb12{padding-bottom:18rem}.mb12{margin-bottom:18rem}.nb12{margin-bottom:-18rem}.pv12{padding-top:18rem;padding-bottom:18rem}.mv12{margin-top:18rem;margin-bottom:18rem}.nv12{margin-top:-18rem;margin-bottom:-18rem}.ph12{padding-left:18rem;padding-right:18rem}.mh12{margin-left:18rem;margin-right:18rem}.nh12{margin-left:-18rem;margin-right:-18rem}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}.left-0{left:0}.top-1{top:1rem}.right-1{right:1rem}.bottom-1{bottom:1rem}.left-1{left:1rem}.top-2{top:2rem}.right-2{right:2rem}.bottom-2{bottom:2rem}.left-2{left:2rem}.top--1{top:-1rem}.right--1{right:-1rem}.bottom--1{bottom:-1rem}.left--1{left:-1rem}.top--2{top:-2rem}.right--2{right:-2rem}.bottom--2{bottom:-2rem}.left--2{left:-2rem}.absolute--fill{top:0;right:0;bottom:0;left:0}.cf:after,.cf:before{content:" ";display:table}.cf:after{clear:both}.cf{*zoom:1}.cl{clear:left}.cr{clear:right}.cb{clear:both}.cn{clear:none}.dn{display:none}.di{display:inline}.db{display:block}.dib{display:inline-block}.dit{display:inline-table}.dt{display:table}.dtc{display:table-cell}.dt-row{display:table-row}.dt-row-group{display:table-row-group}.dt-column{display:table-column}.dt-column-group{display:table-column-group}.dt--fixed{table-layout:fixed;width:100%}.flex{display:flex}.inline-flex{display:inline-flex}.flex-auto{flex:1 1 auto;min-width:0;min-height:0}.flex-none{flex:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.flex-wrap-reverse{flex-wrap:wrap-reverse}.flex-column-reverse{flex-direction:column-reverse}.flex-row-reverse{flex-direction:row-reverse}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.self-start{align-self:flex-start}.self-end{align-self:flex-end}.self-center{align-self:center}.self-baseline{align-self:baseline}.self-stretch{align-self:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.content-start{align-content:flex-start}.content-end{align-content:flex-end}.content-center{align-content:center}.content-between{align-content:space-between}.content-around{align-content:space-around}.content-stretch{align-content:stretch}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-last{order:99999}.flex-grow-0{flex-grow:0}.flex-grow-1{flex-grow:1}.flex-shrink-0{flex-shrink:0}.flex-shrink-1{flex-shrink:1}.fl{float:left}.fl,.fr{_display:inline}.fr{float:right}.fn{float:none}.sans-serif{font-family:-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica neue,helvetica,ubuntu,roboto,noto,segoe ui,arial,sans-serif}.serif{font-family:georgia,times,serif}.system-sans-serif{font-family:sans-serif}.system-serif{font-family:serif}.code,code{font-family:Consolas,monaco,monospace}.courier{font-family:Courier Next,courier,monospace}.helvetica{font-family:helvetica neue,helvetica,sans-serif}.avenir{font-family:avenir next,avenir,sans-serif}.athelas{font-family:athelas,georgia,serif}.georgia{font-family:georgia,serif}.times{font-family:times,serif}.bodoni{font-family:Bodoni MT,serif}.calisto{font-family:Calisto MT,serif}.garamond{font-family:garamond,serif}.baskerville{font-family:baskerville,serif}.i{font-style:italic}.fs-normal{font-style:normal}.normal{font-weight:400}.b{font-weight:700}.fw1{font-weight:100}.fw2{font-weight:200}.fw3{font-weight:300}.fw4{font-weight:400}.fw5{font-weight:500}.fw6{font-weight:600}.fw7{font-weight:700}.fw8{font-weight:800}.fw9{font-weight:900}.input-reset{-webkit-appearance:none;-moz-appearance:none}.button-reset::-moz-focus-inner,.input-reset::-moz-focus-inner{border:0;padding:0}.debug *{outline:1px solid gold}.debug-white *{outline:1px solid #fff}.debug-black *{outline:1px solid #000}.debug-grid{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAFElEQVR4AWPAC97/9x0eCsAEPgwAVLshdpENIxcAAAAASUVORK5CYII=) repeat 0 0}.debug-grid-16{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMklEQVR4AWOgCLz/b0epAa6UGuBOqQHOQHLUgFEDnAbcBZ4UGwDOkiCnkIhdgNgNxAYAiYlD+8sEuo8AAAAASUVORK5CYII=) repeat 0 0}.debug-grid-8-solid{background:#fff url(data:image/gif;base64,R0lGODdhCAAIAPEAAADw/wDx/////wAAACwAAAAACAAIAAACDZQvgaeb/lxbAIKA8y0AOw==) repeat 0 0}.debug-grid-16-solid{background:#fff url(data:image/gif;base64,R0lGODdhEAAQAPEAAADw/wDx/xXy/////ywAAAAAEAAQAAACIZyPKckYDQFsb6ZqD85jZ2+BkwiRFKehhqQCQgDHcgwEBQA7) repeat 0 0}.link{text-decoration:none}.link,.link:active,.link:focus,.link:hover,.link:link,.link:visited{transition:color .15s ease-in}.link:focus{outline:1px dotted currentColor}.list{list-style-type:none}.h1{height:1rem}.h2{height:2rem}.h3{height:4rem}.h4{height:8rem}.h5{height:16rem}.h-25{height:25%}.h-50{height:50%}.h-75{height:75%}.h-100{height:100%}.min-h-100{min-height:100%}.vh-25{height:25vh}.vh-50{height:50vh}.vh-75{height:75vh}.vh-100{height:100vh}.min-vh-100{min-height:100vh}.h-auto{height:auto}.h-inherit{height:inherit}.black{color:#000}.white{color:#fff}.gray0{color:#333}.gray1{color:#4d4d4d}.gray2{color:#7f7f7f}.gray3{color:#b1b2b3}.gray4{color:#e6e6e6}.gray5{color:#f9f9f9}.blue0{color:#ecf6ff}.blue1{color:#b0c7ff}.blue2{color:#4330fc}.blue3{color:#190d7b}.red0{color:#f9d6ce}.red1{color:#ffa073}.red2{color:#ee5432}.red3{color:#c10d30}.green0{color:#bdebcc}.green1{color:#2ed196}.green2{color:#2aa779}.green3{color:#286e55}.yellow0{color:#ffefc5}.yellow1{color:#ffd972}.yellow2{color:#fcc440}.yellow3{color:#ee892b}.bg-black{background-color:#000}.bg-white{background-color:#fff}.bg-gray0{background-color:#333}.bg-gray1{background-color:#4d4d4d}.bg-gray2{background-color:#7f7f7f}.bg-gray3{background-color:#b1b2b3}.bg-gray4{background-color:#e6e6e6}.bg-gray5{background-color:#f9f9f9}.bg-blue0{background-color:#ecf6ff}.bg-blue1{background-color:#b0c7ff}.bg-blue2{background-color:#4330fc}.bg-blue3{background-color:#190d7b}.bg-red0{background-color:#f9d6ce}.bg-red1{background-color:#ffa073}.bg-red2{background-color:#ee5432}.bg-red3{background-color:#c10d30}.bg-green0{background-color:#bdebcc}.bg-green1{background-color:#2ed196}.bg-green2{background-color:#2aa779}.bg-green3{background-color:#286e55}.bg-yellow0{background-color:#ffefc5}.bg-yellow1{background-color:#ffd972}.bg-yellow2{background-color:#fcc440}.bg-yellow3{background-color:#ee892b}.bg-transparent{background-color:transparent}.hover-black:focus,.hover-black:hover{color:#000}.hover-white:focus,.hover-white:hover{color:#fff}.hover-gray0:focus,.hover-gray0:hover{color:#333}.hover-gray1:focus,.hover-gray1:hover{color:#4d4d4d}.hover-gray2:focus,.hover-gray2:hover{color:#7f7f7f}.hover-gray3:focus,.hover-gray3:hover{color:#b1b2b3}.hover-gray4:focus,.hover-gray4:hover{color:#e6e6e6}.hover-gray5:focus,.hover-gray5:hover{color:#f9f9f9}.hover-blue0:focus,.hover-blue0:hover{color:#ecf6ff}.hover-blue1:focus,.hover-blue1:hover{color:#b0c7ff}.hover-blue2:focus,.hover-blue2:hover{color:#4330fc}.hover-blue3:focus,.hover-blue3:hover{color:#190d7b}.hover-red0:focus,.hover-red0:hover{color:#f9d6ce}.hover-red1:focus,.hover-red1:hover{color:#ffa073}.hover-red2:focus,.hover-red2:hover{color:#ee5432}.hover-red3:focus,.hover-red3:hover{color:#c10d30}.hover-green0:focus,.hover-green0:hover{color:#bdebcc}.hover-green1:focus,.hover-green1:hover{color:#2ed196}.hover-green2:focus,.hover-green2:hover{color:#2aa779}.hover-green3:focus,.hover-green3:hover{color:#286e55}.hover-yellow0:focus,.hover-yellow0:hover{color:#ffefc5}.hover-yellow1:focus,.hover-yellow1:hover{color:#ffd972}.hover-yellow2:focus,.hover-yellow2:hover{color:#fcc440}.hover-yellow3:focus,.hover-yellow3:hover{color:#ee892b}.hover-bg-black:focus,.hover-bg-black:hover{background-color:#000}.hover-bg-white:focus,.hover-bg-white:hover{background-color:#fff}.hover-bg-gray0:focus,.hover-bg-gray0:hover{background-color:#333}.hover-bg-gray1:focus,.hover-bg-gray1:hover{background-color:#4d4d4d}.hover-bg-gray2:focus,.hover-bg-gray2:hover{background-color:#7f7f7f}.hover-bg-gray3:focus,.hover-bg-gray3:hover{background-color:#b1b2b3}.hover-bg-gray4:focus,.hover-bg-gray4:hover{background-color:#e6e6e6}.hover-bg-gray5:focus,.hover-bg-gray5:hover{background-color:#f9f9f9}.hover-bg-blue0:focus,.hover-bg-blue0:hover{background-color:#ecf6ff}.hover-bg-blue1:focus,.hover-bg-blue1:hover{background-color:#b0c7ff}.hover-bg-blue2:focus,.hover-bg-blue2:hover{background-color:#4330fc}.hover-bg-blue3:focus,.hover-bg-blue3:hover{background-color:#190d7b}.hover-bg-red0:focus,.hover-bg-red0:hover{background-color:#f9d6ce}.hover-bg-red1:focus,.hover-bg-red1:hover{background-color:#ffa073}.hover-bg-red2:focus,.hover-bg-red2:hover{background-color:#ee5432}.hover-bg-red3:focus,.hover-bg-red3:hover{background-color:#c10d30}.hover-bg-green0:focus,.hover-bg-green0:hover{background-color:#bdebcc}.hover-bg-green1:focus,.hover-bg-green1:hover{background-color:#2ed196}.hover-bg-green2:focus,.hover-bg-green2:hover{background-color:#2aa779}.hover-bg-green3:focus,.hover-bg-green3:hover{background-color:#286e55}.hover-bg-yellow0:focus,.hover-bg-yellow0:hover{background-color:#ffefc5}.hover-bg-yellow1:focus,.hover-bg-yellow1:hover{background-color:#ffd972}.hover-bg-yellow2:focus,.hover-bg-yellow2:hover{background-color:#fcc440}.hover-bg-yellow3:focus,.hover-bg-yellow3:hover{background-color:#ee892b}.hover-bg-transparent:focus,.hover-bg-transparent:hover{background-color:transparent}img{max-width:100%}.tracked{letter-spacing:.1em}.tracked-tight{letter-spacing:-.05em}.tracked-mega{letter-spacing:.25em}.lh-solid{line-height:1.333333}.lh-title{line-height:1.5}.lh-copy{line-height:1.666666}.mw1{max-width:1rem}.mw2{max-width:2rem}.mw3{max-width:4rem}.mw4{max-width:8rem}.mw5{max-width:16rem}.mw6{max-width:32rem}.mw7{max-width:48rem}.mw8{max-width:64rem}.mw9{max-width:96rem}.mw-none{max-width:none}.mw-100{max-width:100%}.nested-copy-line-height ol,.nested-copy-line-height p,.nested-copy-line-height ul{line-height:1.5}.nested-headline-line-height h1,.nested-headline-line-height h2,.nested-headline-line-height h3,.nested-headline-line-height h4,.nested-headline-line-height h5,.nested-headline-line-height h6{line-height:1.25}.nested-list-reset ol,.nested-list-reset ul{padding-left:0;margin-left:0;list-style-type:none}.nested-copy-indent p+p{text-indent:1em;margin-top:0;margin-bottom:0}.nested-copy-separator p+p{margin-top:1.5em}.nested-img img{width:100%;max-width:100%;display:block}.nested-links a{color:#357edd;transition:color .15s ease-in}.nested-links a:focus,.nested-links a:hover{color:#96ccff;transition:color .15s ease-in}.dim{opacity:1}.dim,.dim:focus,.dim:hover{transition:opacity .15s ease-in}.dim:focus,.dim:hover{opacity:.5}.dim:active{opacity:.8;transition:opacity .15s ease-out}.glow,.glow:focus,.glow:hover{transition:opacity .15s ease-in}.glow:focus,.glow:hover{opacity:1}.hide-child .child{opacity:0;transition:opacity .15s ease-in}.hide-child:active .child,.hide-child:focus .child,.hide-child:hover .child{opacity:1;transition:opacity .15s ease-in}.underline-hover:focus,.underline-hover:hover{text-decoration:underline}.grow{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);transform:translateZ(0);transition:-webkit-transform .25s ease-out;transition:transform .25s ease-out;transition:transform .25s ease-out,-webkit-transform .25s ease-out}.grow:focus,.grow:hover{-webkit-transform:scale(1.05);transform:scale(1.05)}.grow:active{-webkit-transform:scale(.9);transform:scale(.9)}.grow-large{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);transform:translateZ(0);transition:-webkit-transform .25s ease-in-out;transition:transform .25s ease-in-out;transition:transform .25s ease-in-out,-webkit-transform .25s ease-in-out}.grow-large:focus,.grow-large:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.grow-large:active{-webkit-transform:scale(.95);transform:scale(.95)}.pointer:hover,.shadow-hover{cursor:pointer}.shadow-hover{position:relative;transition:all .5s cubic-bezier(.165,.84,.44,1)}.shadow-hover:after{content:"";box-shadow:0 0 16px 2px rgba(0,0,0,.2);border-radius:inherit;opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:-1;transition:opacity .5s cubic-bezier(.165,.84,.44,1)}.shadow-hover:focus:after,.shadow-hover:hover:after{opacity:1}.bg-animate,.bg-animate:focus,.bg-animate:hover{transition:background-color .15s ease-in-out}.o-100{opacity:1}.o-90{opacity:.9}.o-80{opacity:.8}.o-70{opacity:.7}.o-60{opacity:.6}.o-50{opacity:.5}.o-40{opacity:.4}.o-30{opacity:.3}.o-20{opacity:.2}.o-10{opacity:.1}.o-05{opacity:.05}.o-025{opacity:.025}.o-0{opacity:0}.rotate-45{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.outline{outline:1px solid}.outline-transparent{outline:1px solid transparent}.outline-0{outline:0}.overflow-visible{overflow:visible}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.overflow-auto{overflow:auto}.overflow-x-visible{overflow-x:visible}.overflow-x-hidden{overflow-x:hidden}.overflow-x-scroll{overflow-x:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-visible{overflow-y:visible}.overflow-y-hidden{overflow-y:hidden}.overflow-y-scroll{overflow-y:scroll}.overflow-y-auto{overflow-y:auto}.static{position:static}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.collapse{border-collapse:collapse;border-spacing:0}.striped--light-silver:nth-child(odd){background-color:#aaa}.striped--moon-gray:nth-child(odd){background-color:#ccc}.striped--light-gray:nth-child(odd){background-color:#eee}.striped--near-white:nth-child(odd){background-color:#f4f4f4}.stripe-light:nth-child(odd){background-color:hsla(0,0%,100%,.1)}.stripe-dark:nth-child(odd){background-color:rgba(0,0,0,.1)}.strike{text-decoration:line-through}.underline{text-decoration:underline}.no-underline{text-decoration:none}.tl{text-align:left}.tr{text-align:right}.tc{text-align:center}.tj{text-align:justify}.ttc{text-transform:capitalize}.ttl{text-transform:lowercase}.ttu{text-transform:uppercase}.ttn{text-transform:none}.v-base{vertical-align:baseline}.v-mid{vertical-align:middle}.v-top{vertical-align:top}.v-btm{vertical-align:bottom}.f1{font-size:4.5rem}.f2{font-size:4rem}.f3{font-size:3rem}.f4{font-size:2rem}.f5{font-size:1.5rem}.f6{font-size:1.125rem}.f7{font-size:1rem}.f8{font-size:.875rem}.f9{font-size:.75rem}.measure{max-width:30em}.measure-wide{max-width:34em}.measure-narrow{max-width:20em}.small-caps{font-variant:small-caps}.indent{text-indent:1em;margin-top:0;margin-bottom:0}.truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overflow-container{overflow-y:scroll}.center{margin-left:auto}.center,.mr-auto{margin-right:auto}.ml-auto{margin-left:auto}.clip{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal{white-space:normal}.nowrap{white-space:nowrap}.pre{white-space:pre}.w1{width:1rem}.w2{width:2rem}.w3{width:4rem}.w4{width:8rem}.w5{width:16rem}.w-10{width:10%}.w-20{width:20%}.w-25{width:25%}.w-30{width:30%}.w-33{width:33%}.w-34{width:34%}.w-40{width:40%}.w-50{width:50%}.w-60{width:60%}.w-70{width:70%}.w-75{width:75%}.w-80{width:80%}.w-90{width:90%}.w-100{width:100%}.w-third{width:33.33333%}.w-two-thirds{width:66.66667%}.w-auto{width:auto}.z-0{z-index:0}.z-1{z-index:1}.z-2{z-index:2}.z-3{z-index:3}.z-4{z-index:4}.z-5{z-index:5}.z-999{z-index:999}.z-9999{z-index:9999}.z-max{z-index:2147483647}.z-inherit{z-index:inherit}.z-initial{z-index:auto}.z-unset{z-index:unset}@media screen and (min-width:34.375em) and (max-width:46.875em){.aspect-ratio-m{height:0;position:relative}.aspect-ratio--16x9-m{padding-bottom:56.25%}.aspect-ratio--9x16-m{padding-bottom:177.77%}.aspect-ratio--4x3-m{padding-bottom:75%}.aspect-ratio--3x4-m{padding-bottom:133.33%}.aspect-ratio--6x4-m{padding-bottom:66.6%}.aspect-ratio--4x6-m{padding-bottom:150%}.aspect-ratio--8x5-m{padding-bottom:62.5%}.aspect-ratio--5x8-m{padding-bottom:160%}.aspect-ratio--7x5-m{padding-bottom:71.42%}.aspect-ratio--5x7-m{padding-bottom:140%}.aspect-ratio--1x1-m{padding-bottom:100%}.aspect-ratio--object-m{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover-m{background-size:cover!important}.contain-m{background-size:contain!important}.bg-center-m{background-position:50%}.bg-center-m,.bg-top-m{background-repeat:no-repeat}.bg-top-m{background-position:top}.bg-right-m{background-position:100%}.bg-bottom-m,.bg-right-m{background-repeat:no-repeat}.bg-bottom-m{background-position:bottom}.bg-left-m{background-repeat:no-repeat;background-position:0}.ba-m{border-style:solid;border-width:1px}.bt-m{border-top-style:solid;border-top-width:1px}.br-m{border-right-style:solid;border-right-width:1px}.bb-m{border-bottom-style:solid;border-bottom-width:1px}.bl-m{border-left-style:solid;border-left-width:1px}.bn-m{border-style:none;border-width:0}.br0-m{border-radius:0}.br1-m{border-radius:.125rem}.br2-m{border-radius:.25rem}.br3-m{border-radius:.5rem}.br4-m{border-radius:1rem}.br-100-m{border-radius:100%}.br-pill-m{border-radius:9999px}.br--bottom-m{border-top-left-radius:0;border-top-right-radius:0}.br--top-m{border-bottom-right-radius:0}.br--right-m,.br--top-m{border-bottom-left-radius:0}.br--right-m{border-top-left-radius:0}.br--left-m{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted-m{border-style:dotted}.b--dashed-m{border-style:dashed}.b--solid-m{border-style:solid}.b--none-m{border-style:none}.bw0-m{border-width:0}.bw1-m{border-width:.125rem}.bw2-m{border-width:.25rem}.bw3-m{border-width:.5rem}.bw4-m{border-width:1rem}.bw5-m{border-width:2rem}.bt-0-m{border-top-width:0}.br-0-m{border-right-width:0}.bb-0-m{border-bottom-width:0}.bl-0-m{border-left-width:0}.shadow-1-m{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2-m{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3-m{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4-m{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5-m{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.pa0-m{padding:0}.ma0-m,.na0-m{margin:0}.pl0-m{padding-left:0}.ml0-m,.nl0-m{margin-left:0}.pr0-m{padding-right:0}.mr0-m,.nr0-m{margin-right:0}.pt0-m{padding-top:0}.mt0-m,.nt0-m{margin-top:0}.pb0-m{padding-bottom:0}.mb0-m,.nb0-m{margin-bottom:0}.pv0-m{padding-top:0;padding-bottom:0}.mv0-m,.nv0-m{margin-top:0;margin-bottom:0}.ph0-m{padding-left:0;padding-right:0}.mh0-m,.nh0-m{margin-left:0;margin-right:0}.pa1-m{padding:.25rem}.ma1-m{margin:.25rem}.na1-m{margin:-.25rem}.pl1-m{padding-left:.25rem}.ml1-m{margin-left:.25rem}.nl1-m{margin-left:-.25rem}.pr1-m{padding-right:.25rem}.mr1-m{margin-right:.25rem}.nr1-m{margin-right:-.25rem}.pt1-m{padding-top:.25rem}.mt1-m{margin-top:.25rem}.nt1-m{margin-top:-.25rem}.pb1-m{padding-bottom:.25rem}.mb1-m{margin-bottom:.25rem}.nb1-m{margin-bottom:-.25rem}.pv1-m{padding-top:.25rem;padding-bottom:.25rem}.mv1-m{margin-top:.25rem;margin-bottom:.25rem}.nv1-m{margin-top:-.25rem;margin-bottom:-.25rem}.ph1-m{padding-left:.25rem;padding-right:.25rem}.mh1-m{margin-left:.25rem;margin-right:.25rem}.nh1-m{margin-left:-.25rem;margin-right:-.25rem}.pa2-m{padding:.5rem}.ma2-m{margin:.5rem}.na2-m{margin:-.5rem}.pl2-m{padding-left:.5rem}.ml2-m{margin-left:.5rem}.nl2-m{margin-left:-.5rem}.pr2-m{padding-right:.5rem}.mr2-m{margin-right:.5rem}.nr2-m{margin-right:-.5rem}.pt2-m{padding-top:.5rem}.mt2-m{margin-top:.5rem}.nt2-m{margin-top:-.5rem}.pb2-m{padding-bottom:.5rem}.mb2-m{margin-bottom:.5rem}.nb2-m{margin-bottom:-.5rem}.pv2-m{padding-top:.5rem;padding-bottom:.5rem}.mv2-m{margin-top:.5rem;margin-bottom:.5rem}.nv2-m{margin-top:-.5rem;margin-bottom:-.5rem}.ph2-m{padding-left:.5rem;padding-right:.5rem}.mh2-m{margin-left:.5rem;margin-right:.5rem}.nh2-m{margin-left:-.5rem;margin-right:-.5rem}.pa3-m{padding:.75rem}.ma3-m{margin:.75rem}.na3-m{margin:-.75rem}.pl3-m{padding-left:.75rem}.ml3-m{margin-left:.75rem}.nl3-m{margin-left:-.75rem}.pr3-m{padding-right:.75rem}.mr3-m{margin-right:.75rem}.nr3-m{margin-right:-.75rem}.pt3-m{padding-top:.75rem}.mt3-m{margin-top:.75rem}.nt3-m{margin-top:-.75rem}.pb3-m{padding-bottom:.75rem}.mb3-m{margin-bottom:.75rem}.nb3-m{margin-bottom:-.75rem}.pv3-m{padding-top:.75rem;padding-bottom:.75rem}.mv3-m{margin-top:.75rem;margin-bottom:.75rem}.nv3-m{margin-top:-.75rem;margin-bottom:-.75rem}.ph3-m{padding-left:.75rem;padding-right:.75rem}.mh3-m{margin-left:.75rem;margin-right:.75rem}.nh3-m{margin-left:-.75rem;margin-right:-.75rem}.pa4-m{padding:1rem}.ma4-m{margin:1rem}.na4-m{margin:-1rem}.pl4-m{padding-left:1rem}.ml4-m{margin-left:1rem}.nl4-m{margin-left:-1rem}.pr4-m{padding-right:1rem}.mr4-m{margin-right:1rem}.nr4-m{margin-right:-1rem}.pt4-m{padding-top:1rem}.mt4-m{margin-top:1rem}.nt4-m{margin-top:-1rem}.pb4-m{padding-bottom:1rem}.mb4-m{margin-bottom:1rem}.nb4-m{margin-bottom:-1rem}.pv4-m{padding-top:1rem;padding-bottom:1rem}.mv4-m{margin-top:1rem;margin-bottom:1rem}.nv4-m{margin-top:-1rem;margin-bottom:-1rem}.ph4-m{padding-left:1rem;padding-right:1rem}.mh4-m{margin-left:1rem;margin-right:1rem}.nh4-m{margin-left:-1rem;margin-right:-1rem}.pa5-m{padding:1.25rem}.ma5-m{margin:1.25rem}.na5-m{margin:-1.25rem}.pl5-m{padding-left:1.25rem}.ml5-m{margin-left:1.25rem}.nl5-m{margin-left:-1.25rem}.pr5-m{padding-right:1.25rem}.mr5-m{margin-right:1.25rem}.nr5-m{margin-right:-1.25rem}.pt5-m{padding-top:1.25rem}.mt5-m{margin-top:1.25rem}.nt5-m{margin-top:-1.25rem}.pb5-m{padding-bottom:1.25rem}.mb5-m{margin-bottom:1.25rem}.nb5-m{margin-bottom:-1.25rem}.pv5-m{padding-top:1.25rem;padding-bottom:1.25rem}.mv5-m{margin-top:1.25rem;margin-bottom:1.25rem}.nv5-m{margin-top:-1.25rem;margin-bottom:-1.25rem}.ph5-m{padding-left:1.25rem;padding-right:1.25rem}.mh5-m{margin-left:1.25rem;margin-right:1.25rem}.nh5-m{margin-left:-1.25rem;margin-right:-1.25rem}.pa6-m{padding:1.5rem}.ma6-m{margin:1.5rem}.na6-m{margin:-1.5rem}.pl6-m{padding-left:1.5rem}.ml6-m{margin-left:1.5rem}.nl6-m{margin-left:-1.5rem}.pr6-m{padding-right:1.5rem}.mr6-m{margin-right:1.5rem}.nr6-m{margin-right:-1.5rem}.pt6-m{padding-top:1.5rem}.mt6-m{margin-top:1.5rem}.nt6-m{margin-top:-1.5rem}.pb6-m{padding-bottom:1.5rem}.mb6-m{margin-bottom:1.5rem}.nb6-m{margin-bottom:-1.5rem}.pv6-m{padding-top:1.5rem;padding-bottom:1.5rem}.mv6-m{margin-top:1.5rem;margin-bottom:1.5rem}.nv6-m{margin-top:-1.5rem;margin-bottom:-1.5rem}.ph6-m{padding-left:1.5rem;padding-right:1.5rem}.mh6-m{margin-left:1.5rem;margin-right:1.5rem}.nh6-m{margin-left:-1.5rem;margin-right:-1.5rem}.pa7-m{padding:2rem}.ma7-m{margin:2rem}.na7-m{margin:-2rem}.pl7-m{padding-left:2rem}.ml7-m{margin-left:2rem}.nl7-m{margin-left:-2rem}.pr7-m{padding-right:2rem}.mr7-m{margin-right:2rem}.nr7-m{margin-right:-2rem}.pt7-m{padding-top:2rem}.mt7-m{margin-top:2rem}.nt7-m{margin-top:-2rem}.pb7-m{padding-bottom:2rem}.mb7-m{margin-bottom:2rem}.nb7-m{margin-bottom:-2rem}.pv7-m{padding-top:2rem;padding-bottom:2rem}.mv7-m{margin-top:2rem;margin-bottom:2rem}.nv7-m{margin-top:-2rem;margin-bottom:-2rem}.ph7-m{padding-left:2rem;padding-right:2rem}.mh7-m{margin-left:2rem;margin-right:2rem}.nh7-m{margin-left:-2rem;margin-right:-2rem}.pa8-m{padding:3rem}.ma8-m{margin:3rem}.na8-m{margin:-3rem}.pl8-m{padding-left:3rem}.ml8-m{margin-left:3rem}.nl8-m{margin-left:-3rem}.pr8-m{padding-right:3rem}.mr8-m{margin-right:3rem}.nr8-m{margin-right:-3rem}.pt8-m{padding-top:3rem}.mt8-m{margin-top:3rem}.nt8-m{margin-top:-3rem}.pb8-m{padding-bottom:3rem}.mb8-m{margin-bottom:3rem}.nb8-m{margin-bottom:-3rem}.pv8-m{padding-top:3rem;padding-bottom:3rem}.mv8-m{margin-top:3rem;margin-bottom:3rem}.nv8-m{margin-top:-3rem;margin-bottom:-3rem}.ph8-m{padding-left:3rem;padding-right:3rem}.mh8-m{margin-left:3rem;margin-right:3rem}.nh8-m{margin-left:-3rem;margin-right:-3rem}.pa9-m{padding:4rem}.ma9-m{margin:4rem}.na9-m{margin:-4rem}.pl9-m{padding-left:4rem}.ml9-m{margin-left:4rem}.nl9-m{margin-left:-4rem}.pr9-m{padding-right:4rem}.mr9-m{margin-right:4rem}.nr9-m{margin-right:-4rem}.pt9-m{padding-top:4rem}.mt9-m{margin-top:4rem}.nt9-m{margin-top:-4rem}.pb9-m{padding-bottom:4rem}.mb9-m{margin-bottom:4rem}.nb9-m{margin-bottom:-4rem}.pv9-m{padding-top:4rem;padding-bottom:4rem}.mv9-m{margin-top:4rem;margin-bottom:4rem}.nv9-m{margin-top:-4rem;margin-bottom:-4rem}.ph9-m{padding-left:4rem;padding-right:4rem}.mh9-m{margin-left:4rem;margin-right:4rem}.nh9-m{margin-left:-4rem;margin-right:-4rem}.pa10-m{padding:6rem}.ma10-m{margin:6rem}.na10-m{margin:-6rem}.pl10-m{padding-left:6rem}.ml10-m{margin-left:6rem}.nl10-m{margin-left:-6rem}.pr10-m{padding-right:6rem}.mr10-m{margin-right:6rem}.nr10-m{margin-right:-6rem}.pt10-m{padding-top:6rem}.mt10-m{margin-top:6rem}.nt10-m{margin-top:-6rem}.pb10-m{padding-bottom:6rem}.mb10-m{margin-bottom:6rem}.nb10-m{margin-bottom:-6rem}.pv10-m{padding-top:6rem;padding-bottom:6rem}.mv10-m{margin-top:6rem;margin-bottom:6rem}.nv10-m{margin-top:-6rem;margin-bottom:-6rem}.ph10-m{padding-left:6rem;padding-right:6rem}.mh10-m{margin-left:6rem;margin-right:6rem}.nh10-m{margin-left:-6rem;margin-right:-6rem}.pa11-m{padding:10rem}.ma11-m{margin:10rem}.na11-m{margin:-10rem}.pl11-m{padding-left:10rem}.ml11-m{margin-left:10rem}.nl11-m{margin-left:-10rem}.pr11-m{padding-right:10rem}.mr11-m{margin-right:10rem}.nr11-m{margin-right:-10rem}.pt11-m{padding-top:10rem}.mt11-m{margin-top:10rem}.nt11-m{margin-top:-10rem}.pb11-m{padding-bottom:10rem}.mb11-m{margin-bottom:10rem}.nb11-m{margin-bottom:-10rem}.pv11-m{padding-top:10rem;padding-bottom:10rem}.mv11-m{margin-top:10rem;margin-bottom:10rem}.nv11-m{margin-top:-10rem;margin-bottom:-10rem}.ph11-m{padding-left:10rem;padding-right:10rem}.mh11-m{margin-left:10rem;margin-right:10rem}.nh11-m{margin-left:-10rem;margin-right:-10rem}.pa12-m{padding:18rem}.ma12-m{margin:18rem}.na12-m{margin:-18rem}.pl12-m{padding-left:18rem}.ml12-m{margin-left:18rem}.nl12-m{margin-left:-18rem}.pr12-m{padding-right:18rem}.mr12-m{margin-right:18rem}.nr12-m{margin-right:-18rem}.pt12-m{padding-top:18rem}.mt12-m{margin-top:18rem}.nt12-m{margin-top:-18rem}.pb12-m{padding-bottom:18rem}.mb12-m{margin-bottom:18rem}.nb12-m{margin-bottom:-18rem}.pv12-m{padding-top:18rem;padding-bottom:18rem}.mv12-m{margin-top:18rem;margin-bottom:18rem}.nv12-m{margin-top:-18rem;margin-bottom:-18rem}.ph12-m{padding-left:18rem;padding-right:18rem}.mh12-m{margin-left:18rem;margin-right:18rem}.nh12-m{margin-left:-18rem;margin-right:-18rem}.top-0-m{top:0}.right-0-m{right:0}.bottom-0-m{bottom:0}.left-0-m{left:0}.top-1-m{top:1rem}.right-1-m{right:1rem}.bottom-1-m{bottom:1rem}.left-1-m{left:1rem}.top-2-m{top:2rem}.right-2-m{right:2rem}.bottom-2-m{bottom:2rem}.left-2-m{left:2rem}.top--1-m{top:-1rem}.right--1-m{right:-1rem}.bottom--1-m{bottom:-1rem}.left--1-m{left:-1rem}.top--2-m{top:-2rem}.right--2-m{right:-2rem}.bottom--2-m{bottom:-2rem}.left--2-m{left:-2rem}.absolute--fill-m{top:0;right:0;bottom:0;left:0}.cf-m:after,.cf-m:before{content:" ";display:table}.cf-m:after{clear:both}.cf-m{*zoom:1}.cl-m{clear:left}.cr-m{clear:right}.cb-m{clear:both}.cn-m{clear:none}.dn-m{display:none}.di-m{display:inline}.db-m{display:block}.dib-m{display:inline-block}.dit-m{display:inline-table}.dt-m{display:table}.dtc-m{display:table-cell}.dt-row-m{display:table-row}.dt-row-group-m{display:table-row-group}.dt-column-m{display:table-column}.dt-column-group-m{display:table-column-group}.dt--fixed-m{table-layout:fixed;width:100%}.flex-m{display:flex}.inline-flex-m{display:inline-flex}.flex-auto-m{flex:1 1 auto;min-width:0;min-height:0}.flex-none-m{flex:none}.flex-column-m{flex-direction:column}.flex-row-m{flex-direction:row}.flex-wrap-m{flex-wrap:wrap}.flex-nowrap-m{flex-wrap:nowrap}.flex-wrap-reverse-m{flex-wrap:wrap-reverse}.flex-column-reverse-m{flex-direction:column-reverse}.flex-row-reverse-m{flex-direction:row-reverse}.items-start-m{align-items:flex-start}.items-end-m{align-items:flex-end}.items-center-m{align-items:center}.items-baseline-m{align-items:baseline}.items-stretch-m{align-items:stretch}.self-start-m{align-self:flex-start}.self-end-m{align-self:flex-end}.self-center-m{align-self:center}.self-baseline-m{align-self:baseline}.self-stretch-m{align-self:stretch}.justify-start-m{justify-content:flex-start}.justify-end-m{justify-content:flex-end}.justify-center-m{justify-content:center}.justify-between-m{justify-content:space-between}.justify-around-m{justify-content:space-around}.content-start-m{align-content:flex-start}.content-end-m{align-content:flex-end}.content-center-m{align-content:center}.content-between-m{align-content:space-between}.content-around-m{align-content:space-around}.content-stretch-m{align-content:stretch}.order-0-m{order:0}.order-1-m{order:1}.order-2-m{order:2}.order-3-m{order:3}.order-4-m{order:4}.order-5-m{order:5}.order-6-m{order:6}.order-7-m{order:7}.order-8-m{order:8}.order-last-m{order:99999}.flex-grow-0-m{flex-grow:0}.flex-grow-1-m{flex-grow:1}.flex-shrink-0-m{flex-shrink:0}.flex-shrink-1-m{flex-shrink:1}.fl-m{float:left}.fl-m,.fr-m{_display:inline}.fr-m{float:right}.fn-m{float:none}.i-m{font-style:italic}.fs-normal-m{font-style:normal}.normal-m{font-weight:400}.b-m{font-weight:700}.fw1-m{font-weight:100}.fw2-m{font-weight:200}.fw3-m{font-weight:300}.fw4-m{font-weight:400}.fw5-m{font-weight:500}.fw6-m{font-weight:600}.fw7-m{font-weight:700}.fw8-m{font-weight:800}.fw9-m{font-weight:900}.h1-m{height:1rem}.h2-m{height:2rem}.h3-m{height:4rem}.h4-m{height:8rem}.h5-m{height:16rem}.h-25-m{height:25%}.h-50-m{height:50%}.h-75-m{height:75%}.h-100-m{height:100%}.min-h-100-m{min-height:100%}.vh-25-m{height:25vh}.vh-50-m{height:50vh}.vh-75-m{height:75vh}.vh-100-m{height:100vh}.min-vh-100-m{min-height:100vh}.h-auto-m{height:auto}.h-inherit-m{height:inherit}.tracked-m{letter-spacing:.1em}.tracked-tight-m{letter-spacing:-.05em}.tracked-mega-m{letter-spacing:.25em}.lh-solid-m{line-height:1.333333}.lh-title-m{line-height:1.5}.lh-copy-m{line-height:1.666666}.mw1-m{max-width:1rem}.mw2-m{max-width:2rem}.mw3-m{max-width:4rem}.mw4-m{max-width:8rem}.mw5-m{max-width:16rem}.mw6-m{max-width:32rem}.mw7-m{max-width:48rem}.mw8-m{max-width:64rem}.mw9-m{max-width:96rem}.mw-none-m{max-width:none}.mw-100-m{max-width:100%}.o-100-m{opacity:1}.o-90-m{opacity:.9}.o-80-m{opacity:.8}.o-70-m{opacity:.7}.o-60-m{opacity:.6}.o-50-m{opacity:.5}.o-40-m{opacity:.4}.o-30-m{opacity:.3}.o-20-m{opacity:.2}.o-10-m{opacity:.1}.o-05-m{opacity:.05}.o-025-m{opacity:.025}.o-0-m{opacity:0}.rotate-45-m{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90-m{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135-m{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180-m{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225-m{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270-m{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315-m{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.outline-m{outline:1px solid}.outline-transparent-m{outline:1px solid transparent}.outline-0-m{outline:0}.overflow-visible-m{overflow:visible}.overflow-hidden-m{overflow:hidden}.overflow-scroll-m{overflow:scroll}.overflow-auto-m{overflow:auto}.overflow-x-visible-m{overflow-x:visible}.overflow-x-hidden-m{overflow-x:hidden}.overflow-x-scroll-m{overflow-x:scroll}.overflow-x-auto-m{overflow-x:auto}.overflow-y-visible-m{overflow-y:visible}.overflow-y-hidden-m{overflow-y:hidden}.overflow-y-scroll-m{overflow-y:scroll}.overflow-y-auto-m{overflow-y:auto}.static-m{position:static}.relative-m{position:relative}.absolute-m{position:absolute}.fixed-m{position:fixed}.strike-m{text-decoration:line-through}.underline-m{text-decoration:underline}.no-underline-m{text-decoration:none}.tl-m{text-align:left}.tr-m{text-align:right}.tc-m{text-align:center}.tj-m{text-align:justify}.ttc-m{text-transform:capitalize}.ttl-m{text-transform:lowercase}.ttu-m{text-transform:uppercase}.ttn-m{text-transform:none}.f1-m{font-size:4.5rem}.f2-m{font-size:4rem}.f3-m{font-size:3rem}.f4-m{font-size:2rem}.f5-m{font-size:1.5rem}.f6-m{font-size:1.125rem}.f7-m{font-size:1rem}.f8-m{font-size:.875rem}.f9-m{font-size:.75rem}.measure-m{max-width:30em}.measure-wide-m{max-width:34em}.measure-narrow-m{max-width:20em}.small-caps-m{font-variant:small-caps}.indent-m{text-indent:1em;margin-top:0;margin-bottom:0}.truncate-m{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.clip-m{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal-m{white-space:normal}.nowrap-m{white-space:nowrap}.pre-m{white-space:pre}.w1-m{width:1rem}.w2-m{width:2rem}.w3-m{width:4rem}.w4-m{width:8rem}.w5-m{width:16rem}.w-10-m{width:10%}.w-20-m{width:20%}.w-25-m{width:25%}.w-30-m{width:30%}.w-33-m{width:33%}.w-34-m{width:34%}.w-40-m{width:40%}.w-50-m{width:50%}.w-60-m{width:60%}.w-70-m{width:70%}.w-75-m{width:75%}.w-80-m{width:80%}.w-90-m{width:90%}.w-100-m{width:100%}.w-third-m{width:33.33333%}.w-two-thirds-m{width:66.66667%}.w-auto-m{width:auto}}@media screen and (min-width:46.875em) and (max-width:60em){.aspect-ratio-l{height:0;position:relative}.aspect-ratio--16x9-l{padding-bottom:56.25%}.aspect-ratio--9x16-l{padding-bottom:177.77%}.aspect-ratio--4x3-l{padding-bottom:75%}.aspect-ratio--3x4-l{padding-bottom:133.33%}.aspect-ratio--6x4-l{padding-bottom:66.6%}.aspect-ratio--4x6-l{padding-bottom:150%}.aspect-ratio--8x5-l{padding-bottom:62.5%}.aspect-ratio--5x8-l{padding-bottom:160%}.aspect-ratio--7x5-l{padding-bottom:71.42%}.aspect-ratio--5x7-l{padding-bottom:140%}.aspect-ratio--1x1-l{padding-bottom:100%}.aspect-ratio--object-l{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover-l{background-size:cover!important}.contain-l{background-size:contain!important}.bg-center-l{background-position:50%}.bg-center-l,.bg-top-l{background-repeat:no-repeat}.bg-top-l{background-position:top}.bg-right-l{background-position:100%}.bg-bottom-l,.bg-right-l{background-repeat:no-repeat}.bg-bottom-l{background-position:bottom}.bg-left-l{background-repeat:no-repeat;background-position:0}.ba-l{border-style:solid;border-width:1px}.bt-l{border-top-style:solid;border-top-width:1px}.br-l{border-right-style:solid;border-right-width:1px}.bb-l{border-bottom-style:solid;border-bottom-width:1px}.bl-l{border-left-style:solid;border-left-width:1px}.bn-l{border-style:none;border-width:0}.br0-l{border-radius:0}.br1-l{border-radius:.125rem}.br2-l{border-radius:.25rem}.br3-l{border-radius:.5rem}.br4-l{border-radius:1rem}.br-100-l{border-radius:100%}.br-pill-l{border-radius:9999px}.br--bottom-l{border-top-left-radius:0;border-top-right-radius:0}.br--top-l{border-bottom-right-radius:0}.br--right-l,.br--top-l{border-bottom-left-radius:0}.br--right-l{border-top-left-radius:0}.br--left-l{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted-l{border-style:dotted}.b--dashed-l{border-style:dashed}.b--solid-l{border-style:solid}.b--none-l{border-style:none}.bw0-l{border-width:0}.bw1-l{border-width:.125rem}.bw2-l{border-width:.25rem}.bw3-l{border-width:.5rem}.bw4-l{border-width:1rem}.bw5-l{border-width:2rem}.bt-0-l{border-top-width:0}.br-0-l{border-right-width:0}.bb-0-l{border-bottom-width:0}.bl-0-l{border-left-width:0}.shadow-1-l{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2-l{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3-l{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4-l{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5-l{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.pa0-l{padding:0}.ma0-l,.na0-l{margin:0}.pl0-l{padding-left:0}.ml0-l,.nl0-l{margin-left:0}.pr0-l{padding-right:0}.mr0-l,.nr0-l{margin-right:0}.pt0-l{padding-top:0}.mt0-l,.nt0-l{margin-top:0}.pb0-l{padding-bottom:0}.mb0-l,.nb0-l{margin-bottom:0}.pv0-l{padding-top:0;padding-bottom:0}.mv0-l,.nv0-l{margin-top:0;margin-bottom:0}.ph0-l{padding-left:0;padding-right:0}.mh0-l,.nh0-l{margin-left:0;margin-right:0}.pa1-l{padding:.25rem}.ma1-l{margin:.25rem}.na1-l{margin:-.25rem}.pl1-l{padding-left:.25rem}.ml1-l{margin-left:.25rem}.nl1-l{margin-left:-.25rem}.pr1-l{padding-right:.25rem}.mr1-l{margin-right:.25rem}.nr1-l{margin-right:-.25rem}.pt1-l{padding-top:.25rem}.mt1-l{margin-top:.25rem}.nt1-l{margin-top:-.25rem}.pb1-l{padding-bottom:.25rem}.mb1-l{margin-bottom:.25rem}.nb1-l{margin-bottom:-.25rem}.pv1-l{padding-top:.25rem;padding-bottom:.25rem}.mv1-l{margin-top:.25rem;margin-bottom:.25rem}.nv1-l{margin-top:-.25rem;margin-bottom:-.25rem}.ph1-l{padding-left:.25rem;padding-right:.25rem}.mh1-l{margin-left:.25rem;margin-right:.25rem}.nh1-l{margin-left:-.25rem;margin-right:-.25rem}.pa2-l{padding:.5rem}.ma2-l{margin:.5rem}.na2-l{margin:-.5rem}.pl2-l{padding-left:.5rem}.ml2-l{margin-left:.5rem}.nl2-l{margin-left:-.5rem}.pr2-l{padding-right:.5rem}.mr2-l{margin-right:.5rem}.nr2-l{margin-right:-.5rem}.pt2-l{padding-top:.5rem}.mt2-l{margin-top:.5rem}.nt2-l{margin-top:-.5rem}.pb2-l{padding-bottom:.5rem}.mb2-l{margin-bottom:.5rem}.nb2-l{margin-bottom:-.5rem}.pv2-l{padding-top:.5rem;padding-bottom:.5rem}.mv2-l{margin-top:.5rem;margin-bottom:.5rem}.nv2-l{margin-top:-.5rem;margin-bottom:-.5rem}.ph2-l{padding-left:.5rem;padding-right:.5rem}.mh2-l{margin-left:.5rem;margin-right:.5rem}.nh2-l{margin-left:-.5rem;margin-right:-.5rem}.pa3-l{padding:.75rem}.ma3-l{margin:.75rem}.na3-l{margin:-.75rem}.pl3-l{padding-left:.75rem}.ml3-l{margin-left:.75rem}.nl3-l{margin-left:-.75rem}.pr3-l{padding-right:.75rem}.mr3-l{margin-right:.75rem}.nr3-l{margin-right:-.75rem}.pt3-l{padding-top:.75rem}.mt3-l{margin-top:.75rem}.nt3-l{margin-top:-.75rem}.pb3-l{padding-bottom:.75rem}.mb3-l{margin-bottom:.75rem}.nb3-l{margin-bottom:-.75rem}.pv3-l{padding-top:.75rem;padding-bottom:.75rem}.mv3-l{margin-top:.75rem;margin-bottom:.75rem}.nv3-l{margin-top:-.75rem;margin-bottom:-.75rem}.ph3-l{padding-left:.75rem;padding-right:.75rem}.mh3-l{margin-left:.75rem;margin-right:.75rem}.nh3-l{margin-left:-.75rem;margin-right:-.75rem}.pa4-l{padding:1rem}.ma4-l{margin:1rem}.na4-l{margin:-1rem}.pl4-l{padding-left:1rem}.ml4-l{margin-left:1rem}.nl4-l{margin-left:-1rem}.pr4-l{padding-right:1rem}.mr4-l{margin-right:1rem}.nr4-l{margin-right:-1rem}.pt4-l{padding-top:1rem}.mt4-l{margin-top:1rem}.nt4-l{margin-top:-1rem}.pb4-l{padding-bottom:1rem}.mb4-l{margin-bottom:1rem}.nb4-l{margin-bottom:-1rem}.pv4-l{padding-top:1rem;padding-bottom:1rem}.mv4-l{margin-top:1rem;margin-bottom:1rem}.nv4-l{margin-top:-1rem;margin-bottom:-1rem}.ph4-l{padding-left:1rem;padding-right:1rem}.mh4-l{margin-left:1rem;margin-right:1rem}.nh4-l{margin-left:-1rem;margin-right:-1rem}.pa5-l{padding:1.25rem}.ma5-l{margin:1.25rem}.na5-l{margin:-1.25rem}.pl5-l{padding-left:1.25rem}.ml5-l{margin-left:1.25rem}.nl5-l{margin-left:-1.25rem}.pr5-l{padding-right:1.25rem}.mr5-l{margin-right:1.25rem}.nr5-l{margin-right:-1.25rem}.pt5-l{padding-top:1.25rem}.mt5-l{margin-top:1.25rem}.nt5-l{margin-top:-1.25rem}.pb5-l{padding-bottom:1.25rem}.mb5-l{margin-bottom:1.25rem}.nb5-l{margin-bottom:-1.25rem}.pv5-l{padding-top:1.25rem;padding-bottom:1.25rem}.mv5-l{margin-top:1.25rem;margin-bottom:1.25rem}.nv5-l{margin-top:-1.25rem;margin-bottom:-1.25rem}.ph5-l{padding-left:1.25rem;padding-right:1.25rem}.mh5-l{margin-left:1.25rem;margin-right:1.25rem}.nh5-l{margin-left:-1.25rem;margin-right:-1.25rem}.pa6-l{padding:1.5rem}.ma6-l{margin:1.5rem}.na6-l{margin:-1.5rem}.pl6-l{padding-left:1.5rem}.ml6-l{margin-left:1.5rem}.nl6-l{margin-left:-1.5rem}.pr6-l{padding-right:1.5rem}.mr6-l{margin-right:1.5rem}.nr6-l{margin-right:-1.5rem}.pt6-l{padding-top:1.5rem}.mt6-l{margin-top:1.5rem}.nt6-l{margin-top:-1.5rem}.pb6-l{padding-bottom:1.5rem}.mb6-l{margin-bottom:1.5rem}.nb6-l{margin-bottom:-1.5rem}.pv6-l{padding-top:1.5rem;padding-bottom:1.5rem}.mv6-l{margin-top:1.5rem;margin-bottom:1.5rem}.nv6-l{margin-top:-1.5rem;margin-bottom:-1.5rem}.ph6-l{padding-left:1.5rem;padding-right:1.5rem}.mh6-l{margin-left:1.5rem;margin-right:1.5rem}.nh6-l{margin-left:-1.5rem;margin-right:-1.5rem}.pa7-l{padding:2rem}.ma7-l{margin:2rem}.na7-l{margin:-2rem}.pl7-l{padding-left:2rem}.ml7-l{margin-left:2rem}.nl7-l{margin-left:-2rem}.pr7-l{padding-right:2rem}.mr7-l{margin-right:2rem}.nr7-l{margin-right:-2rem}.pt7-l{padding-top:2rem}.mt7-l{margin-top:2rem}.nt7-l{margin-top:-2rem}.pb7-l{padding-bottom:2rem}.mb7-l{margin-bottom:2rem}.nb7-l{margin-bottom:-2rem}.pv7-l{padding-top:2rem;padding-bottom:2rem}.mv7-l{margin-top:2rem;margin-bottom:2rem}.nv7-l{margin-top:-2rem;margin-bottom:-2rem}.ph7-l{padding-left:2rem;padding-right:2rem}.mh7-l{margin-left:2rem;margin-right:2rem}.nh7-l{margin-left:-2rem;margin-right:-2rem}.pa8-l{padding:3rem}.ma8-l{margin:3rem}.na8-l{margin:-3rem}.pl8-l{padding-left:3rem}.ml8-l{margin-left:3rem}.nl8-l{margin-left:-3rem}.pr8-l{padding-right:3rem}.mr8-l{margin-right:3rem}.nr8-l{margin-right:-3rem}.pt8-l{padding-top:3rem}.mt8-l{margin-top:3rem}.nt8-l{margin-top:-3rem}.pb8-l{padding-bottom:3rem}.mb8-l{margin-bottom:3rem}.nb8-l{margin-bottom:-3rem}.pv8-l{padding-top:3rem;padding-bottom:3rem}.mv8-l{margin-top:3rem;margin-bottom:3rem}.nv8-l{margin-top:-3rem;margin-bottom:-3rem}.ph8-l{padding-left:3rem;padding-right:3rem}.mh8-l{margin-left:3rem;margin-right:3rem}.nh8-l{margin-left:-3rem;margin-right:-3rem}.pa9-l{padding:4rem}.ma9-l{margin:4rem}.na9-l{margin:-4rem}.pl9-l{padding-left:4rem}.ml9-l{margin-left:4rem}.nl9-l{margin-left:-4rem}.pr9-l{padding-right:4rem}.mr9-l{margin-right:4rem}.nr9-l{margin-right:-4rem}.pt9-l{padding-top:4rem}.mt9-l{margin-top:4rem}.nt9-l{margin-top:-4rem}.pb9-l{padding-bottom:4rem}.mb9-l{margin-bottom:4rem}.nb9-l{margin-bottom:-4rem}.pv9-l{padding-top:4rem;padding-bottom:4rem}.mv9-l{margin-top:4rem;margin-bottom:4rem}.nv9-l{margin-top:-4rem;margin-bottom:-4rem}.ph9-l{padding-left:4rem;padding-right:4rem}.mh9-l{margin-left:4rem;margin-right:4rem}.nh9-l{margin-left:-4rem;margin-right:-4rem}.pa10-l{padding:6rem}.ma10-l{margin:6rem}.na10-l{margin:-6rem}.pl10-l{padding-left:6rem}.ml10-l{margin-left:6rem}.nl10-l{margin-left:-6rem}.pr10-l{padding-right:6rem}.mr10-l{margin-right:6rem}.nr10-l{margin-right:-6rem}.pt10-l{padding-top:6rem}.mt10-l{margin-top:6rem}.nt10-l{margin-top:-6rem}.pb10-l{padding-bottom:6rem}.mb10-l{margin-bottom:6rem}.nb10-l{margin-bottom:-6rem}.pv10-l{padding-top:6rem;padding-bottom:6rem}.mv10-l{margin-top:6rem;margin-bottom:6rem}.nv10-l{margin-top:-6rem;margin-bottom:-6rem}.ph10-l{padding-left:6rem;padding-right:6rem}.mh10-l{margin-left:6rem;margin-right:6rem}.nh10-l{margin-left:-6rem;margin-right:-6rem}.pa11-l{padding:10rem}.ma11-l{margin:10rem}.na11-l{margin:-10rem}.pl11-l{padding-left:10rem}.ml11-l{margin-left:10rem}.nl11-l{margin-left:-10rem}.pr11-l{padding-right:10rem}.mr11-l{margin-right:10rem}.nr11-l{margin-right:-10rem}.pt11-l{padding-top:10rem}.mt11-l{margin-top:10rem}.nt11-l{margin-top:-10rem}.pb11-l{padding-bottom:10rem}.mb11-l{margin-bottom:10rem}.nb11-l{margin-bottom:-10rem}.pv11-l{padding-top:10rem;padding-bottom:10rem}.mv11-l{margin-top:10rem;margin-bottom:10rem}.nv11-l{margin-top:-10rem;margin-bottom:-10rem}.ph11-l{padding-left:10rem;padding-right:10rem}.mh11-l{margin-left:10rem;margin-right:10rem}.nh11-l{margin-left:-10rem;margin-right:-10rem}.pa12-l{padding:18rem}.ma12-l{margin:18rem}.na12-l{margin:-18rem}.pl12-l{padding-left:18rem}.ml12-l{margin-left:18rem}.nl12-l{margin-left:-18rem}.pr12-l{padding-right:18rem}.mr12-l{margin-right:18rem}.nr12-l{margin-right:-18rem}.pt12-l{padding-top:18rem}.mt12-l{margin-top:18rem}.nt12-l{margin-top:-18rem}.pb12-l{padding-bottom:18rem}.mb12-l{margin-bottom:18rem}.nb12-l{margin-bottom:-18rem}.pv12-l{padding-top:18rem;padding-bottom:18rem}.mv12-l{margin-top:18rem;margin-bottom:18rem}.nv12-l{margin-top:-18rem;margin-bottom:-18rem}.ph12-l{padding-left:18rem;padding-right:18rem}.mh12-l{margin-left:18rem;margin-right:18rem}.nh12-l{margin-left:-18rem;margin-right:-18rem}.top-0-l{top:0}.right-0-l{right:0}.bottom-0-l{bottom:0}.left-0-l{left:0}.top-1-l{top:1rem}.right-1-l{right:1rem}.bottom-1-l{bottom:1rem}.left-1-l{left:1rem}.top-2-l{top:2rem}.right-2-l{right:2rem}.bottom-2-l{bottom:2rem}.left-2-l{left:2rem}.top--1-l{top:-1rem}.right--1-l{right:-1rem}.bottom--1-l{bottom:-1rem}.left--1-l{left:-1rem}.top--2-l{top:-2rem}.right--2-l{right:-2rem}.bottom--2-l{bottom:-2rem}.left--2-l{left:-2rem}.absolute--fill-l{top:0;right:0;bottom:0;left:0}.cf-l:after,.cf-l:before{content:" ";display:table}.cf-l:after{clear:both}.cf-l{*zoom:1}.cl-l{clear:left}.cr-l{clear:right}.cb-l{clear:both}.cn-l{clear:none}.dn-l{display:none}.di-l{display:inline}.db-l{display:block}.dib-l{display:inline-block}.dit-l{display:inline-table}.dt-l{display:table}.dtc-l{display:table-cell}.dt-row-l{display:table-row}.dt-row-group-l{display:table-row-group}.dt-column-l{display:table-column}.dt-column-group-l{display:table-column-group}.dt--fixed-l{table-layout:fixed;width:100%}.flex-l{display:flex}.inline-flex-l{display:inline-flex}.flex-auto-l{flex:1 1 auto;min-width:0;min-height:0}.flex-none-l{flex:none}.flex-column-l{flex-direction:column}.flex-row-l{flex-direction:row}.flex-wrap-l{flex-wrap:wrap}.flex-nowrap-l{flex-wrap:nowrap}.flex-wrap-reverse-l{flex-wrap:wrap-reverse}.flex-column-reverse-l{flex-direction:column-reverse}.flex-row-reverse-l{flex-direction:row-reverse}.items-start-l{align-items:flex-start}.items-end-l{align-items:flex-end}.items-center-l{align-items:center}.items-baseline-l{align-items:baseline}.items-stretch-l{align-items:stretch}.self-start-l{align-self:flex-start}.self-end-l{align-self:flex-end}.self-center-l{align-self:center}.self-baseline-l{align-self:baseline}.self-stretch-l{align-self:stretch}.justify-start-l{justify-content:flex-start}.justify-end-l{justify-content:flex-end}.justify-center-l{justify-content:center}.justify-between-l{justify-content:space-between}.justify-around-l{justify-content:space-around}.content-start-l{align-content:flex-start}.content-end-l{align-content:flex-end}.content-center-l{align-content:center}.content-between-l{align-content:space-between}.content-around-l{align-content:space-around}.content-stretch-l{align-content:stretch}.order-0-l{order:0}.order-1-l{order:1}.order-2-l{order:2}.order-3-l{order:3}.order-4-l{order:4}.order-5-l{order:5}.order-6-l{order:6}.order-7-l{order:7}.order-8-l{order:8}.order-last-l{order:99999}.flex-grow-0-l{flex-grow:0}.flex-grow-1-l{flex-grow:1}.flex-shrink-0-l{flex-shrink:0}.flex-shrink-1-l{flex-shrink:1}.fl-l{float:left}.fl-l,.fr-l{_display:inline}.fr-l{float:right}.fn-l{float:none}.i-l{font-style:italic}.fs-normal-l{font-style:normal}.normal-l{font-weight:400}.b-l{font-weight:700}.fw1-l{font-weight:100}.fw2-l{font-weight:200}.fw3-l{font-weight:300}.fw4-l{font-weight:400}.fw5-l{font-weight:500}.fw6-l{font-weight:600}.fw7-l{font-weight:700}.fw8-l{font-weight:800}.fw9-l{font-weight:900}.h1-l{height:1rem}.h2-l{height:2rem}.h3-l{height:4rem}.h4-l{height:8rem}.h5-l{height:16rem}.h-25-l{height:25%}.h-50-l{height:50%}.h-75-l{height:75%}.h-100-l{height:100%}.min-h-100-l{min-height:100%}.vh-25-l{height:25vh}.vh-50-l{height:50vh}.vh-75-l{height:75vh}.vh-100-l{height:100vh}.min-vh-100-l{min-height:100vh}.h-auto-l{height:auto}.h-inherit-l{height:inherit}.tracked-l{letter-spacing:.1em}.tracked-tight-l{letter-spacing:-.05em}.tracked-mega-l{letter-spacing:.25em}.lh-solid-l{line-height:1.333333}.lh-title-l{line-height:1.5}.lh-copy-l{line-height:1.666666}.mw1-l{max-width:1rem}.mw2-l{max-width:2rem}.mw3-l{max-width:4rem}.mw4-l{max-width:8rem}.mw5-l{max-width:16rem}.mw6-l{max-width:32rem}.mw7-l{max-width:48rem}.mw8-l{max-width:64rem}.mw9-l{max-width:96rem}.mw-none-l{max-width:none}.mw-100-l{max-width:100%}.o-100-l{opacity:1}.o-90-l{opacity:.9}.o-80-l{opacity:.8}.o-70-l{opacity:.7}.o-60-l{opacity:.6}.o-50-l{opacity:.5}.o-40-l{opacity:.4}.o-30-l{opacity:.3}.o-20-l{opacity:.2}.o-10-l{opacity:.1}.o-05-l{opacity:.05}.o-025-l{opacity:.025}.o-0-l{opacity:0}.rotate-45-l{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90-l{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135-l{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180-l{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225-l{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270-l{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315-l{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.outline-l{outline:1px solid}.outline-transparent-l{outline:1px solid transparent}.outline-0-l{outline:0}.overflow-visible-l{overflow:visible}.overflow-hidden-l{overflow:hidden}.overflow-scroll-l{overflow:scroll}.overflow-auto-l{overflow:auto}.overflow-x-visible-l{overflow-x:visible}.overflow-x-hidden-l{overflow-x:hidden}.overflow-x-scroll-l{overflow-x:scroll}.overflow-x-auto-l{overflow-x:auto}.overflow-y-visible-l{overflow-y:visible}.overflow-y-hidden-l{overflow-y:hidden}.overflow-y-scroll-l{overflow-y:scroll}.overflow-y-auto-l{overflow-y:auto}.static-l{position:static}.relative-l{position:relative}.absolute-l{position:absolute}.fixed-l{position:fixed}.strike-l{text-decoration:line-through}.underline-l{text-decoration:underline}.no-underline-l{text-decoration:none}.tl-l{text-align:left}.tr-l{text-align:right}.tc-l{text-align:center}.tj-l{text-align:justify}.ttc-l{text-transform:capitalize}.ttl-l{text-transform:lowercase}.ttu-l{text-transform:uppercase}.ttn-l{text-transform:none}.f1-l{font-size:4.5rem}.f2-l{font-size:4rem}.f3-l{font-size:3rem}.f4-l{font-size:2rem}.f5-l{font-size:1.5rem}.f6-l{font-size:1.125rem}.f7-l{font-size:1rem}.f8-l{font-size:.875rem}.f9-l{font-size:.75rem}.measure-l{max-width:30em}.measure-wide-l{max-width:34em}.measure-narrow-l{max-width:20em}.small-caps-l{font-variant:small-caps}.indent-l{text-indent:1em;margin-top:0;margin-bottom:0}.truncate-l{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.clip-l{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal-l{white-space:normal}.nowrap-l{white-space:nowrap}.pre-l{white-space:pre}.w1-l{width:1rem}.w2-l{width:2rem}.w3-l{width:4rem}.w4-l{width:8rem}.w5-l{width:16rem}.w-10-l{width:10%}.w-20-l{width:20%}.w-25-l{width:25%}.w-30-l{width:30%}.w-33-l{width:33%}.w-34-l{width:34%}.w-40-l{width:40%}.w-50-l{width:50%}.w-60-l{width:60%}.w-70-l{width:70%}.w-75-l{width:75%}.w-80-l{width:80%}.w-90-l{width:90%}.w-100-l{width:100%}.w-third-l{width:33.33333%}.w-two-thirds-l{width:66.66667%}.w-auto-l{width:auto}}@media screen and (min-width:60em){.aspect-ratio-xl{height:0;position:relative}.aspect-ratio--16x9-xl{padding-bottom:56.25%}.aspect-ratio--9x16-xl{padding-bottom:177.77%}.aspect-ratio--4x3-xl{padding-bottom:75%}.aspect-ratio--3x4-xl{padding-bottom:133.33%}.aspect-ratio--6x4-xl{padding-bottom:66.6%}.aspect-ratio--4x6-xl{padding-bottom:150%}.aspect-ratio--8x5-xl{padding-bottom:62.5%}.aspect-ratio--5x8-xl{padding-bottom:160%}.aspect-ratio--7x5-xl{padding-bottom:71.42%}.aspect-ratio--5x7-xl{padding-bottom:140%}.aspect-ratio--1x1-xl{padding-bottom:100%}.aspect-ratio--object-xl{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover-xl{background-size:cover!important}.contain-xl{background-size:contain!important}.bg-center-xl{background-position:50%}.bg-center-xl,.bg-top-xl{background-repeat:no-repeat}.bg-top-xl{background-position:top}.bg-right-xl{background-position:100%}.bg-bottom-xl,.bg-right-xl{background-repeat:no-repeat}.bg-bottom-xl{background-position:bottom}.bg-left-xl{background-repeat:no-repeat;background-position:0}.ba-xl{border-style:solid;border-width:1px}.bt-xl{border-top-style:solid;border-top-width:1px}.br-xl{border-right-style:solid;border-right-width:1px}.bb-xl{border-bottom-style:solid;border-bottom-width:1px}.bl-xl{border-left-style:solid;border-left-width:1px}.bn-xl{border-style:none;border-width:0}.br0-xl{border-radius:0}.br1-xl{border-radius:.125rem}.br2-xl{border-radius:.25rem}.br3-xl{border-radius:.5rem}.br4-xl{border-radius:1rem}.br-100-xl{border-radius:100%}.br-pill-xl{border-radius:9999px}.br--bottom-xl{border-top-left-radius:0;border-top-right-radius:0}.br--top-xl{border-bottom-right-radius:0}.br--right-xl,.br--top-xl{border-bottom-left-radius:0}.br--right-xl{border-top-left-radius:0}.br--left-xl{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted-xl{border-style:dotted}.b--dashed-xl{border-style:dashed}.b--solid-xl{border-style:solid}.b--none-xl{border-style:none}.bw0-xl{border-width:0}.bw1-xl{border-width:.125rem}.bw2-xl{border-width:.25rem}.bw3-xl{border-width:.5rem}.bw4-xl{border-width:1rem}.bw5-xl{border-width:2rem}.bt-0-xl{border-top-width:0}.br-0-xl{border-right-width:0}.bb-0-xl{border-bottom-width:0}.bl-0-xl{border-left-width:0}.shadow-1-xl{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2-xl{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3-xl{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4-xl{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5-xl{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.pa0-xl{padding:0}.ma0-xl,.na0-xl{margin:0}.pl0-xl{padding-left:0}.ml0-xl,.nl0-xl{margin-left:0}.pr0-xl{padding-right:0}.mr0-xl,.nr0-xl{margin-right:0}.pt0-xl{padding-top:0}.mt0-xl,.nt0-xl{margin-top:0}.pb0-xl{padding-bottom:0}.mb0-xl,.nb0-xl{margin-bottom:0}.pv0-xl{padding-top:0;padding-bottom:0}.mv0-xl,.nv0-xl{margin-top:0;margin-bottom:0}.ph0-xl{padding-left:0;padding-right:0}.mh0-xl,.nh0-xl{margin-left:0;margin-right:0}.pa1-xl{padding:.25rem}.ma1-xl{margin:.25rem}.na1-xl{margin:-.25rem}.pl1-xl{padding-left:.25rem}.ml1-xl{margin-left:.25rem}.nl1-xl{margin-left:-.25rem}.pr1-xl{padding-right:.25rem}.mr1-xl{margin-right:.25rem}.nr1-xl{margin-right:-.25rem}.pt1-xl{padding-top:.25rem}.mt1-xl{margin-top:.25rem}.nt1-xl{margin-top:-.25rem}.pb1-xl{padding-bottom:.25rem}.mb1-xl{margin-bottom:.25rem}.nb1-xl{margin-bottom:-.25rem}.pv1-xl{padding-top:.25rem;padding-bottom:.25rem}.mv1-xl{margin-top:.25rem;margin-bottom:.25rem}.nv1-xl{margin-top:-.25rem;margin-bottom:-.25rem}.ph1-xl{padding-left:.25rem;padding-right:.25rem}.mh1-xl{margin-left:.25rem;margin-right:.25rem}.nh1-xl{margin-left:-.25rem;margin-right:-.25rem}.pa2-xl{padding:.5rem}.ma2-xl{margin:.5rem}.na2-xl{margin:-.5rem}.pl2-xl{padding-left:.5rem}.ml2-xl{margin-left:.5rem}.nl2-xl{margin-left:-.5rem}.pr2-xl{padding-right:.5rem}.mr2-xl{margin-right:.5rem}.nr2-xl{margin-right:-.5rem}.pt2-xl{padding-top:.5rem}.mt2-xl{margin-top:.5rem}.nt2-xl{margin-top:-.5rem}.pb2-xl{padding-bottom:.5rem}.mb2-xl{margin-bottom:.5rem}.nb2-xl{margin-bottom:-.5rem}.pv2-xl{padding-top:.5rem;padding-bottom:.5rem}.mv2-xl{margin-top:.5rem;margin-bottom:.5rem}.nv2-xl{margin-top:-.5rem;margin-bottom:-.5rem}.ph2-xl{padding-left:.5rem;padding-right:.5rem}.mh2-xl{margin-left:.5rem;margin-right:.5rem}.nh2-xl{margin-left:-.5rem;margin-right:-.5rem}.pa3-xl{padding:.75rem}.ma3-xl{margin:.75rem}.na3-xl{margin:-.75rem}.pl3-xl{padding-left:.75rem}.ml3-xl{margin-left:.75rem}.nl3-xl{margin-left:-.75rem}.pr3-xl{padding-right:.75rem}.mr3-xl{margin-right:.75rem}.nr3-xl{margin-right:-.75rem}.pt3-xl{padding-top:.75rem}.mt3-xl{margin-top:.75rem}.nt3-xl{margin-top:-.75rem}.pb3-xl{padding-bottom:.75rem}.mb3-xl{margin-bottom:.75rem}.nb3-xl{margin-bottom:-.75rem}.pv3-xl{padding-top:.75rem;padding-bottom:.75rem}.mv3-xl{margin-top:.75rem;margin-bottom:.75rem}.nv3-xl{margin-top:-.75rem;margin-bottom:-.75rem}.ph3-xl{padding-left:.75rem;padding-right:.75rem}.mh3-xl{margin-left:.75rem;margin-right:.75rem}.nh3-xl{margin-left:-.75rem;margin-right:-.75rem}.pa4-xl{padding:1rem}.ma4-xl{margin:1rem}.na4-xl{margin:-1rem}.pl4-xl{padding-left:1rem}.ml4-xl{margin-left:1rem}.nl4-xl{margin-left:-1rem}.pr4-xl{padding-right:1rem}.mr4-xl{margin-right:1rem}.nr4-xl{margin-right:-1rem}.pt4-xl{padding-top:1rem}.mt4-xl{margin-top:1rem}.nt4-xl{margin-top:-1rem}.pb4-xl{padding-bottom:1rem}.mb4-xl{margin-bottom:1rem}.nb4-xl{margin-bottom:-1rem}.pv4-xl{padding-top:1rem;padding-bottom:1rem}.mv4-xl{margin-top:1rem;margin-bottom:1rem}.nv4-xl{margin-top:-1rem;margin-bottom:-1rem}.ph4-xl{padding-left:1rem;padding-right:1rem}.mh4-xl{margin-left:1rem;margin-right:1rem}.nh4-xl{margin-left:-1rem;margin-right:-1rem}.pa5-xl{padding:1.25rem}.ma5-xl{margin:1.25rem}.na5-xl{margin:-1.25rem}.pl5-xl{padding-left:1.25rem}.ml5-xl{margin-left:1.25rem}.nl5-xl{margin-left:-1.25rem}.pr5-xl{padding-right:1.25rem}.mr5-xl{margin-right:1.25rem}.nr5-xl{margin-right:-1.25rem}.pt5-xl{padding-top:1.25rem}.mt5-xl{margin-top:1.25rem}.nt5-xl{margin-top:-1.25rem}.pb5-xl{padding-bottom:1.25rem}.mb5-xl{margin-bottom:1.25rem}.nb5-xl{margin-bottom:-1.25rem}.pv5-xl{padding-top:1.25rem;padding-bottom:1.25rem}.mv5-xl{margin-top:1.25rem;margin-bottom:1.25rem}.nv5-xl{margin-top:-1.25rem;margin-bottom:-1.25rem}.ph5-xl{padding-left:1.25rem;padding-right:1.25rem}.mh5-xl{margin-left:1.25rem;margin-right:1.25rem}.nh5-xl{margin-left:-1.25rem;margin-right:-1.25rem}.pa6-xl{padding:1.5rem}.ma6-xl{margin:1.5rem}.na6-xl{margin:-1.5rem}.pl6-xl{padding-left:1.5rem}.ml6-xl{margin-left:1.5rem}.nl6-xl{margin-left:-1.5rem}.pr6-xl{padding-right:1.5rem}.mr6-xl{margin-right:1.5rem}.nr6-xl{margin-right:-1.5rem}.pt6-xl{padding-top:1.5rem}.mt6-xl{margin-top:1.5rem}.nt6-xl{margin-top:-1.5rem}.pb6-xl{padding-bottom:1.5rem}.mb6-xl{margin-bottom:1.5rem}.nb6-xl{margin-bottom:-1.5rem}.pv6-xl{padding-top:1.5rem;padding-bottom:1.5rem}.mv6-xl{margin-top:1.5rem;margin-bottom:1.5rem}.nv6-xl{margin-top:-1.5rem;margin-bottom:-1.5rem}.ph6-xl{padding-left:1.5rem;padding-right:1.5rem}.mh6-xl{margin-left:1.5rem;margin-right:1.5rem}.nh6-xl{margin-left:-1.5rem;margin-right:-1.5rem}.pa7-xl{padding:2rem}.ma7-xl{margin:2rem}.na7-xl{margin:-2rem}.pl7-xl{padding-left:2rem}.ml7-xl{margin-left:2rem}.nl7-xl{margin-left:-2rem}.pr7-xl{padding-right:2rem}.mr7-xl{margin-right:2rem}.nr7-xl{margin-right:-2rem}.pt7-xl{padding-top:2rem}.mt7-xl{margin-top:2rem}.nt7-xl{margin-top:-2rem}.pb7-xl{padding-bottom:2rem}.mb7-xl{margin-bottom:2rem}.nb7-xl{margin-bottom:-2rem}.pv7-xl{padding-top:2rem;padding-bottom:2rem}.mv7-xl{margin-top:2rem;margin-bottom:2rem}.nv7-xl{margin-top:-2rem;margin-bottom:-2rem}.ph7-xl{padding-left:2rem;padding-right:2rem}.mh7-xl{margin-left:2rem;margin-right:2rem}.nh7-xl{margin-left:-2rem;margin-right:-2rem}.pa8-xl{padding:3rem}.ma8-xl{margin:3rem}.na8-xl{margin:-3rem}.pl8-xl{padding-left:3rem}.ml8-xl{margin-left:3rem}.nl8-xl{margin-left:-3rem}.pr8-xl{padding-right:3rem}.mr8-xl{margin-right:3rem}.nr8-xl{margin-right:-3rem}.pt8-xl{padding-top:3rem}.mt8-xl{margin-top:3rem}.nt8-xl{margin-top:-3rem}.pb8-xl{padding-bottom:3rem}.mb8-xl{margin-bottom:3rem}.nb8-xl{margin-bottom:-3rem}.pv8-xl{padding-top:3rem;padding-bottom:3rem}.mv8-xl{margin-top:3rem;margin-bottom:3rem}.nv8-xl{margin-top:-3rem;margin-bottom:-3rem}.ph8-xl{padding-left:3rem;padding-right:3rem}.mh8-xl{margin-left:3rem;margin-right:3rem}.nh8-xl{margin-left:-3rem;margin-right:-3rem}.pa9-xl{padding:4rem}.ma9-xl{margin:4rem}.na9-xl{margin:-4rem}.pl9-xl{padding-left:4rem}.ml9-xl{margin-left:4rem}.nl9-xl{margin-left:-4rem}.pr9-xl{padding-right:4rem}.mr9-xl{margin-right:4rem}.nr9-xl{margin-right:-4rem}.pt9-xl{padding-top:4rem}.mt9-xl{margin-top:4rem}.nt9-xl{margin-top:-4rem}.pb9-xl{padding-bottom:4rem}.mb9-xl{margin-bottom:4rem}.nb9-xl{margin-bottom:-4rem}.pv9-xl{padding-top:4rem;padding-bottom:4rem}.mv9-xl{margin-top:4rem;margin-bottom:4rem}.nv9-xl{margin-top:-4rem;margin-bottom:-4rem}.ph9-xl{padding-left:4rem;padding-right:4rem}.mh9-xl{margin-left:4rem;margin-right:4rem}.nh9-xl{margin-left:-4rem;margin-right:-4rem}.pa10-xl{padding:6rem}.ma10-xl{margin:6rem}.na10-xl{margin:-6rem}.pl10-xl{padding-left:6rem}.ml10-xl{margin-left:6rem}.nl10-xl{margin-left:-6rem}.pr10-xl{padding-right:6rem}.mr10-xl{margin-right:6rem}.nr10-xl{margin-right:-6rem}.pt10-xl{padding-top:6rem}.mt10-xl{margin-top:6rem}.nt10-xl{margin-top:-6rem}.pb10-xl{padding-bottom:6rem}.mb10-xl{margin-bottom:6rem}.nb10-xl{margin-bottom:-6rem}.pv10-xl{padding-top:6rem;padding-bottom:6rem}.mv10-xl{margin-top:6rem;margin-bottom:6rem}.nv10-xl{margin-top:-6rem;margin-bottom:-6rem}.ph10-xl{padding-left:6rem;padding-right:6rem}.mh10-xl{margin-left:6rem;margin-right:6rem}.nh10-xl{margin-left:-6rem;margin-right:-6rem}.pa11-xl{padding:10rem}.ma11-xl{margin:10rem}.na11-xl{margin:-10rem}.pl11-xl{padding-left:10rem}.ml11-xl{margin-left:10rem}.nl11-xl{margin-left:-10rem}.pr11-xl{padding-right:10rem}.mr11-xl{margin-right:10rem}.nr11-xl{margin-right:-10rem}.pt11-xl{padding-top:10rem}.mt11-xl{margin-top:10rem}.nt11-xl{margin-top:-10rem}.pb11-xl{padding-bottom:10rem}.mb11-xl{margin-bottom:10rem}.nb11-xl{margin-bottom:-10rem}.pv11-xl{padding-top:10rem;padding-bottom:10rem}.mv11-xl{margin-top:10rem;margin-bottom:10rem}.nv11-xl{margin-top:-10rem;margin-bottom:-10rem}.ph11-xl{padding-left:10rem;padding-right:10rem}.mh11-xl{margin-left:10rem;margin-right:10rem}.nh11-xl{margin-left:-10rem;margin-right:-10rem}.pa12-xl{padding:18rem}.ma12-xl{margin:18rem}.na12-xl{margin:-18rem}.pl12-xl{padding-left:18rem}.ml12-xl{margin-left:18rem}.nl12-xl{margin-left:-18rem}.pr12-xl{padding-right:18rem}.mr12-xl{margin-right:18rem}.nr12-xl{margin-right:-18rem}.pt12-xl{padding-top:18rem}.mt12-xl{margin-top:18rem}.nt12-xl{margin-top:-18rem}.pb12-xl{padding-bottom:18rem}.mb12-xl{margin-bottom:18rem}.nb12-xl{margin-bottom:-18rem}.pv12-xl{padding-top:18rem;padding-bottom:18rem}.mv12-xl{margin-top:18rem;margin-bottom:18rem}.nv12-xl{margin-top:-18rem;margin-bottom:-18rem}.ph12-xl{padding-left:18rem;padding-right:18rem}.mh12-xl{margin-left:18rem;margin-right:18rem}.nh12-xl{margin-left:-18rem;margin-right:-18rem}.top-0-xl{top:0}.right-0-xl{right:0}.bottom-0-xl{bottom:0}.left-0-xl{left:0}.top-1-xl{top:1rem}.right-1-xl{right:1rem}.bottom-1-xl{bottom:1rem}.left-1-xl{left:1rem}.top-2-xl{top:2rem}.right-2-xl{right:2rem}.bottom-2-xl{bottom:2rem}.left-2-xl{left:2rem}.top--1-xl{top:-1rem}.right--1-xl{right:-1rem}.bottom--1-xl{bottom:-1rem}.left--1-xl{left:-1rem}.top--2-xl{top:-2rem}.right--2-xl{right:-2rem}.bottom--2-xl{bottom:-2rem}.left--2-xl{left:-2rem}.absolute--fill-xl{top:0;right:0;bottom:0;left:0}.cf-xl:after,.cf-xl:before{content:" ";display:table}.cf-xl:after{clear:both}.cf-xl{*zoom:1}.cl-xl{clear:left}.cr-xl{clear:right}.cb-xl{clear:both}.cn-xl{clear:none}.dn-xl{display:none}.di-xl{display:inline}.db-xl{display:block}.dib-xl{display:inline-block}.dit-xl{display:inline-table}.dt-xl{display:table}.dtc-xl{display:table-cell}.dt-row-xl{display:table-row}.dt-row-group-xl{display:table-row-group}.dt-column-xl{display:table-column}.dt-column-group-xl{display:table-column-group}.dt--fixed-xl{table-layout:fixed;width:100%}.flex-xl{display:flex}.inline-flex-xl{display:inline-flex}.flex-auto-xl{flex:1 1 auto;min-width:0;min-height:0}.flex-none-xl{flex:none}.flex-column-xl{flex-direction:column}.flex-row-xl{flex-direction:row}.flex-wrap-xl{flex-wrap:wrap}.flex-nowrap-xl{flex-wrap:nowrap}.flex-wrap-reverse-xl{flex-wrap:wrap-reverse}.flex-column-reverse-xl{flex-direction:column-reverse}.flex-row-reverse-xl{flex-direction:row-reverse}.items-start-xl{align-items:flex-start}.items-end-xl{align-items:flex-end}.items-center-xl{align-items:center}.items-baseline-xl{align-items:baseline}.items-stretch-xl{align-items:stretch}.self-start-xl{align-self:flex-start}.self-end-xl{align-self:flex-end}.self-center-xl{align-self:center}.self-baseline-xl{align-self:baseline}.self-stretch-xl{align-self:stretch}.justify-start-xl{justify-content:flex-start}.justify-end-xl{justify-content:flex-end}.justify-center-xl{justify-content:center}.justify-between-xl{justify-content:space-between}.justify-around-xl{justify-content:space-around}.content-start-xl{align-content:flex-start}.content-end-xl{align-content:flex-end}.content-center-xl{align-content:center}.content-between-xl{align-content:space-between}.content-around-xl{align-content:space-around}.content-stretch-xl{align-content:stretch}.order-0-xl{order:0}.order-1-xl{order:1}.order-2-xl{order:2}.order-3-xl{order:3}.order-4-xl{order:4}.order-5-xl{order:5}.order-6-xl{order:6}.order-7-xl{order:7}.order-8-xl{order:8}.order-last-xl{order:99999}.flex-grow-0-xl{flex-grow:0}.flex-grow-1-xl{flex-grow:1}.flex-shrink-0-xl{flex-shrink:0}.flex-shrink-1-xl{flex-shrink:1}.fl-xl{float:left}.fl-xl,.fr-xl{_display:inline}.fr-xl{float:right}.fn-xl{float:none}.i-xl{font-style:italic}.fs-normal-xl{font-style:normal}.normal-xl{font-weight:400}.b-xl{font-weight:700}.fw1-xl{font-weight:100}.fw2-xl{font-weight:200}.fw3-xl{font-weight:300}.fw4-xl{font-weight:400}.fw5-xl{font-weight:500}.fw6-xl{font-weight:600}.fw7-xl{font-weight:700}.fw8-xl{font-weight:800}.fw9-xl{font-weight:900}.h1-xl{height:1rem}.h2-xl{height:2rem}.h3-xl{height:4rem}.h4-xl{height:8rem}.h5-xl{height:16rem}.h-25-xl{height:25%}.h-50-xl{height:50%}.h-75-xl{height:75%}.h-100-xl{height:100%}.min-h-100-xl{min-height:100%}.vh-25-xl{height:25vh}.vh-50-xl{height:50vh}.vh-75-xl{height:75vh}.vh-100-xl{height:100vh}.min-vh-100-xl{min-height:100vh}.h-auto-xl{height:auto}.h-inherit-xl{height:inherit}.tracked-xl{letter-spacing:.1em}.tracked-tight-xl{letter-spacing:-.05em}.tracked-mega-xl{letter-spacing:.25em}.lh-solid-xl{line-height:1.333333}.lh-title-xl{line-height:1.5}.lh-copy-xl{line-height:1.666666}.mw1-xl{max-width:1rem}.mw2-xl{max-width:2rem}.mw3-xl{max-width:4rem}.mw4-xl{max-width:8rem}.mw5-xl{max-width:16rem}.mw6-xl{max-width:32rem}.mw7-xl{max-width:48rem}.mw8-xl{max-width:64rem}.mw9-xl{max-width:96rem}.mw-none-xl{max-width:none}.mw-100-xl{max-width:100%}.o-100-xl{opacity:1}.o-90-xl{opacity:.9}.o-80-xl{opacity:.8}.o-70-xl{opacity:.7}.o-60-xl{opacity:.6}.o-50-xl{opacity:.5}.o-40-xl{opacity:.4}.o-30-xl{opacity:.3}.o-20-xl{opacity:.2}.o-10-xl{opacity:.1}.o-05-xl{opacity:.05}.o-025-xl{opacity:.025}.o-0-xl{opacity:0}.rotate-45-xl{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90-xl{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135-xl{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180-xl{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225-xl{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270-xl{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315-xl{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.outline-xl{outline:1px solid}.outline-transparent-xl{outline:1px solid transparent}.outline-0-xl{outline:0}.overflow-visible-xl{overflow:visible}.overflow-hidden-xl{overflow:hidden}.overflow-scroll-xl{overflow:scroll}.overflow-auto-xl{overflow:auto}.overflow-x-visible-xl{overflow-x:visible}.overflow-x-hidden-xl{overflow-x:hidden}.overflow-x-scroll-xl{overflow-x:scroll}.overflow-x-auto-xl{overflow-x:auto}.overflow-y-visible-xl{overflow-y:visible}.overflow-y-hidden-xl{overflow-y:hidden}.overflow-y-scroll-xl{overflow-y:scroll}.overflow-y-auto-xl{overflow-y:auto}.static-xl{position:static}.relative-xl{position:relative}.absolute-xl{position:absolute}.fixed-xl{position:fixed}.strike-xl{text-decoration:line-through}.underline-xl{text-decoration:underline}.no-underline-xl{text-decoration:none}.tl-xl{text-align:left}.tr-xl{text-align:right}.tc-xl{text-align:center}.tj-xl{text-align:justify}.ttc-xl{text-transform:capitalize}.ttl-xl{text-transform:lowercase}.ttu-xl{text-transform:uppercase}.ttn-xl{text-transform:none}.f1-xl{font-size:4.5rem}.f2-xl{font-size:4rem}.f3-xl{font-size:3rem}.f4-xl{font-size:2rem}.f5-xl{font-size:1.5rem}.f6-xl{font-size:1.125rem}.f7-xl{font-size:1rem}.f8-xl{font-size:.875rem}.f9-xl{font-size:.75rem}.measure-xl{max-width:30em}.measure-wide-xl{max-width:34em}.measure-narrow-xl{max-width:20em}.small-caps-xl{font-variant:small-caps}.indent-xl{text-indent:1em;margin-top:0;margin-bottom:0}.truncate-xl{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.clip-xl{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal-xl{white-space:normal}.nowrap-xl{white-space:nowrap}.pre-xl{white-space:pre}.w1-xl{width:1rem}.w2-xl{width:2rem}.w3-xl{width:4rem}.w4-xl{width:8rem}.w5-xl{width:16rem}.w-10-xl{width:10%}.w-20-xl{width:20%}.w-25-xl{width:25%}.w-30-xl{width:30%}.w-33-xl{width:33%}.w-34-xl{width:34%}.w-40-xl{width:40%}.w-50-xl{width:50%}.w-60-xl{width:60%}.w-70-xl{width:70%}.w-75-xl{width:75%}.w-80-xl{width:80%}.w-90-xl{width:90%}.w-100-xl{width:100%}.w-third-xl{width:33.33333%}.w-two-thirds-xl{width:66.66667%}.w-auto-xl{width:auto}}@font-face{font-family:Inter;font-style:normal;font-weight:400;src:url(https://media.urbit.org/fonts/Inter-Regular.woff2) format("woff2")}@font-face{font-family:Inter;font-style:italic;font-weight:400;src:url(https://media.urbit.org/fonts/Inter-Italic.woff2) format("woff2")}@font-face{font-family:Inter;font-style:normal;font-weight:700;src:url(https://media.urbit.org/fonts/Inter-Bold.woff2) format("woff2")}@font-face{font-family:Inter;font-style:italic;font-weight:700;src:url(https://media.urbit.org/fonts/Inter-BoldItalic.woff2) format("woff2")}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-extralight.woff);font-weight:200}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-light.woff);font-weight:300}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-regular.woff);font-weight:400}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-medium.woff);font-weight:500}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-semibold.woff);font-weight:600}@font-face{font-family:Source Code Pro;src:url(https://storage.googleapis.com/media.urbit.org/fonts/scp-bold.woff);font-weight:700}body,html{height:100%;width:100%;-webkit-font-smoothing:antialiased;overflow:hidden}a,button,h1,h2,h3,h4,h5,h6,input,p,textarea{margin-block-end:unset;margin-block-start:unset;-webkit-margin-before:unset;-webkit-margin-after:unset;font-family:Inter,sans-serif;padding:0}button,summary{cursor:pointer}h2{font-weight:400}a{color:#000;text-decoration:none}.inter{font-family:Inter,sans-serif}.clamp-3{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-webkit-line-clamp:2;-webkit-box-orient:vertical}.clamp-message{max-width:calc(100% - 36px - 1.5rem)}.clamp-attachment{overflow:scroll;max-height:10em;max-width:100%}.lh-16{line-height:16px}.mono{font-family:Source Code Pro,monospace}.list-ship{line-height:2.2}.bg-welcome-green{background-color:#ecf6f2}.c-default{cursor:default}.m0a{margin:0 auto}.mix-blend-diff{mix-blend-mode:difference}.focus-b--black:focus{border-color:#000}.embed-container{position:relative;height:0;overflow:hidden;padding-bottom:56.25%}.embed-container embed,.embed-container iframe,.embed-container object{position:absolute;top:0;left:0;width:100%;height:100%}.spin-active{animation:spin 2s infinite}@keyframes spin{0%{transform:rotate(0deg)}25%{transform:rotate(90deg)}50%{transform:rotate(180deg)}75%{transform:rotate(270deg)}to{transform:rotate(1turn)}}.toggle:after{content:"";height:12px;width:12px;background:#fff;position:absolute;top:2px;left:2px;border-radius:100%}.toggle.checked:after{left:14px}@media (max-width:34.375em){.dn-s{display:none}.flex-basis-100-s,.flex-basis-full-s{flex-basis:100%}.h-100-m-40-s{height:calc(100% - 40px)}.black-s{color:#000}}@media (min-width:34.375em){.db-ns{display:block}.flex-basis-30-ns{flex-basis:30vw}.h-100-m-40-ns{height:calc(100% - 40px)}}@media (prefers-color-scheme:dark){body{background-color:#333;color:#fff}.bg-black-d{background-color:#000}.white-d{color:#fff}.gray1-d{color:#4d4d4d}.gray2-d{color:#7f7f7f}.gray3-d{color:#b1b2b3}.gray4-d{color:#e6e6e6}.bg-gray0-d{background-color:#333}.bg-gray1-d{background-color:#4d4d4d}.b--gray0-d{border-color:#333}.b--gray1-d{border-color:#4d4d4d}.b--gray2-d{border-color:#7f7f7f}.b--white-d{border-color:#fff}.bb-d{border-bottom-width:1px;border-bottom-style:solid}.invert-d{filter:invert(1)}.o-60-d{opacity:.6}.focus-b--white-d:focus{border-color:#fff}a{color:#fff}.hover-bg-gray1-d:hover{background-color:#4d4d4d}} \ No newline at end of file diff --git a/pkg/arvo/app/dojo.hoon b/pkg/arvo/app/dojo.hoon index b24c681dac..75473796c6 100644 --- a/pkg/arvo/app/dojo.hoon +++ b/pkg/arvo/app/dojo.hoon @@ -10,9 +10,9 @@ :::: :: :::: :: :: :: => |% :: external structures - +$ id @tasession :: session id + +$ id sole-id :: session id +$ house :: all state - $: %8 + $: %9 egg=@u :: command count hoc=(map id session) :: conversations acl=(set ship) :: remote access whitelist @@ -820,12 +820,23 @@ =/ poz=vase (dy-sore p.cig) =/ kev=vase =/ kuv=(unit vase) (slew 7 som) - ?: =(~ q.cig) - (fall kuv !>(~)) =/ soz=(list [var=term vax=vase]) %~ tap by %- ~(run by q.cig) |=(val=(unit dojo-source) ?~(val !>([~ ~]) (dy-vase p.u.val))) + :: if the generator takes a named argument "drum-session", + :: then if a value isn't already supplied, we set it to the session + :: that this dojo instance is being run in. + :: (dojo is, indeed, quite coupled with drum.) + :: + =? soz + ?& ?=(^ kuv) + (slab %both %drum-session p.u.kuv) + !(~(has by q.cig) %drum-session) + == + [[%drum-session !>(ses.id)] soz] ::TODO does the who matter? + ?: =(~ soz) + (fall kuv !>(~)) ~| keyword-arg-failure+~(key by q.cig) %+ slap (with-faces kuv+(need kuv) rep+(with-faces soz) ~) @@ -1018,13 +1029,14 @@ |= =card:agent:gall ^+ +> =? card ?=(%pass -.card) - card(p [id p.card]) + ^- card:agent:gall + card(p [(scot %p who.id) ses.id p.card]) %_(+> moz [card moz]) :: ++ he-diff :: emit update |= fec=sole-effect ^+ +> - (he-card %give %fact ~[/sole/[id]] %sole-effect !>(fec)) + (he-card %give %fact ~[(id-to-path:sole id)] %sole-effect !>(fec)) :: ++ he-stop :: abort work ^+ . @@ -1532,21 +1544,47 @@ :: ++ on-load |= ole=vase + ^- (quip card:agent:gall _..on-init) |^ =+ old=!<(house-any ole) =? old ?=(%5 -.old) + ^- house-any + ^- house-6 (house-5-to-6 old) =? old ?=(?(%6 %7) -.old) (house-6-7-to-8 +.old) - ?> ?=(%8 -.old) - `..on-init(state old) + =^ caz old + ?. ?=(%8 -.old) [~ old] + (house-8-to-9 old) + ?> ?=(%9 -.old) + [caz ..on-init(state old)] :: - +$ house-any $%(house house-7 house-6 house-5) + +$ house-any $%(house house-8 house-7 house-6 house-5) + :: + +$ id-8 @tasession + +$ house-8 + $: %8 + egg=@u + hoc=(map id-8 session) + acl=(set ship) + == + ++ house-8-to-9 + |= old=house-8 + ^- (quip card:agent:gall house) + :- %+ turn ~(tap in ~(key by hoc.old)) + |= id=@ta + ^- card:agent:gall + [%give %kick ~[/sole/[id]] ~] + =- [%9 egg.old - acl.old] + %- ~(gas by *(map sole-id session)) + %+ murn ~(tap by hoc.old) + |= [id=@ta s=session] + (bind (upgrade-id:sole id) (late s)) :: +$ house-7 [%7 house-6-7] +$ house-6 [%6 house-6-7] +$ house-6-7 $: egg=@u :: command count - hoc=(map id session-6) :: conversations + hoc=(map id-8 session-6) :: conversations acl=(set ship) :: remote access whitelist == :: +$ session-6 :: per conversation @@ -1573,9 +1611,10 @@ old(poy ~, -.dir [our.hid %base ud+0]) :: +$ house-5 - [%5 egg=@u hoc=(map id session)] + [%5 egg=@u hoc=(map id-8 session-6)] ++ house-5-to-6 |= old=house-5 + ^- house-6 [%6 egg.old hoc.old *(set ship)] -- :: @@ -1591,7 +1630,8 @@ he-abet:(~(he-type he hid id.act ~ (~(got by hoc) id.act)) act) :: %lens-command - =+ !<([=id =command:lens] vase) + =+ !<([ses=@ta =command:lens] vase) + =/ =id [our.hid ses] he-abet:(~(he-lens he hid id ~ (~(got by hoc) id)) command) :: %allow-remote-login @@ -1629,8 +1669,7 @@ ?> ?| (team:title our.hid src.hid) (~(has in acl) src.hid) == - ?> ?=([%sole @ ~] path) - =/ id i.t.path + =/ =id (need (path-to-id:sole path)) =? hoc (~(has by hoc) id) ~& [%dojo-peer-replaced id] (~(del by hoc) id) @@ -1642,7 +1681,7 @@ ++ on-leave |= =path ?> ?=([%sole *] path) - =. hoc (~(del by hoc) t.path) + =. hoc (~(del by hoc) (need (path-to-id:sole path))) [~ ..on-init] :: ++ on-peek @@ -1651,13 +1690,15 @@ :: ++ on-agent |= [=wire =sign:agent:gall] - ?> ?=([@ @ *] wire) - =/ =session (~(got by hoc) i.wire) - =/ he-full ~(. he hid i.wire ~ session) + ^- (quip card:agent:gall _..on-init) + ?> ?=([@ @ @ *] wire) + =/ =id [(slav %p i.wire) i.t.wire] + =/ =session (~(got by hoc) id) + =/ he-full ~(. he hid id ~ session) =^ moves state =< he-abet ^+ he - ?+ i.t.wire ~|([%dojo-bad-on-agent wire -.sign] !!) + ?+ i.t.t.wire ~|([%dojo-bad-on-agent wire -.sign] !!) %poke (he-unto:he-full t.wire sign) %wool (he-wool:he-full t.wire sign) == @@ -1665,14 +1706,16 @@ :: ++ on-arvo |= [=wire =sign-arvo] - ?> ?=([@ *] wire) - =/ =session (~(got by hoc) i.wire) - =/ he-full ~(. he hid i.wire ~ session) + ^- (quip card:agent:gall _..on-init) + ?> ?=([@ @ *] wire) + =/ =id [(slav %p i.wire) i.t.wire] + =/ =session (~(got by hoc) id) + =/ he-full ~(. he hid id ~ session) =^ moves state =< he-abet ?+ +<.sign-arvo ~|([%dojo-bad-take +<.sign-arvo] !!) - %writ (he-writ:he-full t.wire +>.sign-arvo) - %http-response (he-http-response:he-full t.wire +>.sign-arvo) + %writ (he-writ:he-full t.t.wire +>.sign-arvo) + %http-response (he-http-response:he-full t.t.wire +>.sign-arvo) == [moves ..on-init] :: if dojo fails unexpectedly, kill whatever each session is working on diff --git a/pkg/arvo/app/herm.hoon b/pkg/arvo/app/herm.hoon index d19c927785..aefe6c8df7 100644 --- a/pkg/arvo/app/herm.hoon +++ b/pkg/arvo/app/herm.hoon @@ -1,8 +1,13 @@ :: herm: stand-in for term.c with http interface :: +/- herm /+ default-agent, dbug, verb +:: keep relevant mark conversions in cache for performance +:: /$ blit-to-json %blit %json /$ json-to-blit %json %blit +/$ json-to-task %json %herm-task +:: =, jael |% +$ state-0 [%0 ~] @@ -13,15 +18,18 @@ %+ verb | %- agent:dbug ^- agent:gall +=> |% + ++ pass-session + |= [ses=@tas tas=session-task:dill] + [%pass /dill/[ses] %arvo %d %shot ses tas] + -- |_ =bowl:gall +* this . def ~(. (default-agent this %|) bowl) :: ++ on-init ^- (quip card:agent:gall _this) - :: set up dill session subscription - :: - [[%pass [%view %$ ~] %arvo %d %view ~]~ this] + [~ this] :: ++ on-save !>([%0 ~]) ++ on-load @@ -32,47 +40,68 @@ ++ on-watch |= =path ^- (quip card:agent:gall _this) - ?> =(our src):bowl - ?> ?=([%session @ %view ~] path) :_ this - :: scry prompt and cursor position out of dill for initial response + ~| path + ?> ?=([%session @ %view ~] path) + =* ses i.t.path + :: subscribe to the requested session :: - =/ base=^path - /dx/(scot %p our.bowl)//(scot %da now.bowl)/sessions - :~ [%give %fact ~ %blit !>(.^(blit:dill (weld base //line)))] - [%give %fact ~ %blit !>(`blit:dill`hop+.^(@ud (weld base //cursor)))] - == + ::NOTE multiple views do not result in multiple subscriptions + :: because they go over the same wire/duct + :: + [(pass-session ses %view ~)]~ :: ++ on-arvo |= [=wire =sign-arvo] ^- (quip card:agent:gall _this) + ~| wire ?+ wire (on-arvo:def wire sign-arvo) [%tube *] [~ this] :: we no longer care about these :: :: pass on dill blits for the session :: - [%view %$ ~] + [%dill @ ~] + =* ses i.t.wire ?. ?=([%dill %blit *] sign-arvo) ~| [%unexpected-sign [- +<]:sign-arvo] !! :_ this %+ turn p.sign-arvo |= =blit:dill - [%give %fact [%session %$ %view ~]~ %blit !>(blit)] + [%give %fact [%session ses %view ~]~ %blit !>(blit)] + :: + :: clean up old-style subscriptions + :: + [%view @ ~] + =* ses i.t.wire + :_ this + [%pass wire %arvo %d %shot ses %flee ~]~ == :: ++ on-poke |= [=mark =vase] ^- (quip card:agent:gall _this) - ?> =(our src):bowl - ?. ?=(%belt mark) - ~| [%unexpected-mark mark] - !! :_ this - [%pass [%belt %$ ~] %arvo %d %belt !<(belt:dill vase)]~ + :_ ~ + ?+ mark ~|([%unexpected-mark mark] !!) + %belt (pass-session %$ %belt !<(belt:dill vase)) + %herm-task (pass-session !<(task:herm vase)) + == +:: +++ on-peek + |= =path + ^- (unit (unit cage)) + ?+ path ~ + [%x %sessions ~] + :+ ~ ~ + :- %json + !> ^- json + =- a+(turn ~(tap in -) (lead %s)) + .^((set @tas) %dy /(scot %p our.bowl)//(scot %da now.bowl)/sessions) + == :: ++ on-leave on-leave:def -++ on-peek on-peek:def +:: ++ on-agent on-agent:def ++ on-fail on-fail:def -- diff --git a/pkg/arvo/app/hood.hoon b/pkg/arvo/app/hood.hoon index 4866ce4399..41a1eb5d8c 100644 --- a/pkg/arvo/app/hood.hoon +++ b/pkg/arvo/app/hood.hoon @@ -2,8 +2,8 @@ /+ drum=hood-drum, helm=hood-helm, kiln=hood-kiln |% +$ state - $~ [%24 *state:drum *state:helm *state:kiln] - $>(%24 any-state) + $~ [%25 *state:drum *state:helm *state:kiln] + $>(%25 any-state) :: +$ any-state $% [ver=?(%1 %2 %3 %4 %5 %6) lac=(map @tas fin-any-state)] @@ -25,6 +25,7 @@ [%22 drum=state-4:drum helm=state-1:helm kiln=state-9:kiln] [%23 drum=state-4:drum helm=state-2:helm kiln=state-9:kiln] [%24 drum=state-4:drum helm=state-2:helm kiln=state-10:kiln] + [%25 drum=state-5:drum helm=state-2:helm kiln=state-10:kiln] == +$ any-state-tuple $: drum=any-state:drum @@ -92,8 +93,7 @@ :: ?+ mark (on-poke:def mark vase) %atom poke-helm(mark %helm-atom) - %dill-belt poke-drum(mark %drum-dill-belt) - %dill-blit poke-drum(mark %drum-dill-blit) + %dill-poke poke-drum %hood-sync poke-kiln(mark %kiln-sync) %write-sec-atom poke-helm(mark %helm-write-sec-atom) == @@ -108,6 +108,7 @@ ?+ path (on-watch:def +<) [%drum *] =^(c drum.state (peer:drum-core t.path) [c this]) [%kiln *] =^(c kiln.state (peer:kiln-core t.path) [c this]) + [%dill *] =^(c drum.state (peer:drum-core +<) [c this]) == :: ++ on-agent diff --git a/pkg/arvo/app/lens.hoon b/pkg/arvo/app/lens.hoon index 74734f42ee..3969a65d47 100644 --- a/pkg/arvo/app/lens.hoon +++ b/pkg/arvo/app/lens.hoon @@ -83,7 +83,8 @@ :: ?+ -.source.com :_ this(job.state (some [eyre-id com])) - [%pass /sole %agent [our.bowl %dojo] %watch /sole/[eyre-id]]~ + =/ =path /sole/(scot %p our.bowl)/[eyre-id] + [%pass /sole %agent [our.bowl %dojo] %watch path]~ :: %export :_ this(job.state (some [eyre-id com])) diff --git a/pkg/arvo/app/shoe.hoon b/pkg/arvo/app/shoe.hoon index 4b0538ad34..0e827f57c4 100644 --- a/pkg/arvo/app/shoe.hoon +++ b/pkg/arvo/app/shoe.hoon @@ -43,13 +43,13 @@ ++ on-fail on-fail:def :: ++ command-parser - |= sole-id=@ta + |= =sole-id:shoe ^+ |~(nail *(like [? command])) %+ stag & (perk %demo %row %table ~) :: ++ tab-list - |= sole-id=@ta + |= =sole-id:shoe ^- (list [@t tank]) :~ ['demo' leaf+"run example command"] ['row' leaf+"print a row"] @@ -57,7 +57,7 @@ == :: ++ on-command - |= [sole-id=@ta =command] + |= [=sole-id:shoe =command] ^- (quip card _this) =; [to=(list _sole-id) fec=shoe-effect:shoe] [[%shoe to fec]~ this] @@ -87,7 +87,7 @@ == :: ++ can-connect - |= sole-id=@ta + |= =sole-id:shoe ^- ? ?| =(~zod src.bowl) (team:title [our src]:bowl) diff --git a/pkg/arvo/gen/aqua/dojo.hoon b/pkg/arvo/gen/aqua/dojo.hoon index 03d63f5058..d9ef58a1da 100644 --- a/pkg/arvo/gen/aqua/dojo.hoon +++ b/pkg/arvo/gen/aqua/dojo.hoon @@ -5,8 +5,8 @@ :- %aqua-events %+ turn ^- (list unix-event) - :~ [/d/term/1 %belt %ctl `@c`%e] - [/d/term/1 %belt %ctl `@c`%u] + :~ [/d/term/1 %belt %mod %ctl `@c`%e] + [/d/term/1 %belt %mod %ctl `@c`%u] [/d/term/1 %belt %txt ((list @c) command)] [/d/term/1 %belt %ret ~] == diff --git a/pkg/arvo/gen/hood/link.hoon b/pkg/arvo/gen/hood/link.hoon index 5fe183279b..5e14126a1f 100644 --- a/pkg/arvo/gen/hood/link.hoon +++ b/pkg/arvo/gen/hood/link.hoon @@ -8,9 +8,11 @@ :: :- %say |= $: [now=@da eny=@uvJ byk=beak] - [arg=$?([dap=term ~] [who=ship dap=term ~]) ~] + arg=$?([dap=term ~] [who=ship dap=term ~]) + drum-session=@ta == :- %drum-link +:- drum-session ?~ +.arg [p.byk dap.arg] [who.arg dap.arg] diff --git a/pkg/arvo/gen/hood/unlink.hoon b/pkg/arvo/gen/hood/unlink.hoon index d7bc509b00..fc6e1fb012 100644 --- a/pkg/arvo/gen/hood/unlink.hoon +++ b/pkg/arvo/gen/hood/unlink.hoon @@ -8,9 +8,11 @@ :: :- %say |= $: [now=@da eny=@uvJ byk=beak] - [arg=$?([dap=term ~] [who=ship dap=term ~]) ~] + arg=$?([dap=term ~] [who=ship dap=term ~]) + drum-session=@ta == :- %drum-unlink +:- drum-session ?~ +.arg [p.byk dap.arg] [who.arg dap.arg] diff --git a/pkg/arvo/lib/dill.hoon b/pkg/arvo/lib/dill.hoon new file mode 120000 index 0000000000..f9c2362f07 --- /dev/null +++ b/pkg/arvo/lib/dill.hoon @@ -0,0 +1 @@ +../../base-dev/lib/dill.hoon \ No newline at end of file diff --git a/pkg/arvo/lib/hood/drum.hoon b/pkg/arvo/lib/hood/drum.hoon index 649e48e254..68778475d3 100644 --- a/pkg/arvo/lib/hood/drum.hoon +++ b/pkg/arvo/lib/hood/drum.hoon @@ -1,34 +1,66 @@ /- *sole /+ sole |% -+$ state state-4 ++$ state state-5 +$ any-state $~ *state - $% state-4 + $% state-5 + state-4 state-3 state-2 == ++$ state-5 [%5 pith-5] +$ state-4 [%4 pith-4] +$ state-3 [%3 pith-3] +$ state-2 [%2 pith-2] :: ++$ pith-5 + $: bin=(map @ source) :: terminals + == +:: +$ pith-4 $: eel=(set gill:gall) :: connect to - bin=(map bone source) :: terminals + bin=(map bone source-4) :: terminals == :: :: ++$ source-4 + $: edg=_80 + off=@ud + kil=kill + inx=@ud + fug=(map gill:gall (unit target-4)) + mir=(pair @ud stub) + == +:: ++$ target-4 + $: $= blt + %+ pair + (unit dill-belt-4) + (unit dill-belt-4) + ris=(unit search) + hit=history + pom=sole-prompt + inp=sole-command + == +:: ++$ dill-belt-4 + $% [%ctl p=@c] + [%met p=@c] + dill-belt:dill + == +:: ++ pith-3 :: $: eel=(set gill:gall) :: connect to ray=(map dude:gall desk) :: fur=(map dude:gall (unit *)) :: servers - bin=(map bone source) :: terminals + bin=(map bone source-4) :: terminals == :: :: :: ++ pith-2 :: $: eel=(set gill:gall) :: connect to ray=(set well:gall) :: fur=(map dude:gall (unit *)) :: servers - bin=(map bone source) :: terminals + bin=(map bone source-4) :: terminals == :: :: :: ++ kill :: kill ring @@ -42,6 +74,7 @@ off=@ud :: window offset kil=kill :: kill buffer inx=@ud :: ring index + eel=(set gill:gall) :: connect to fug=(map gill:gall (unit target)) :: connections mir=(pair @ud stub) :: mirrored terminal == :: @@ -65,52 +98,74 @@ pom=sole-prompt :: static prompt inp=sole-command :: input state == :: +:: -- :: :: :: :::: :: :: :: :: :: |% ++ en-gill :: gill to wire - |= gyl=gill:gall + |= [ses=@tas gyl=gill:gall] ^- wire - [%drum %phat (scot %p p.gyl) q.gyl ~] + [%drum %phat (scot %p p.gyl) q.gyl ?:(=(%$ ses) ~ [ses ~])] :: ++ de-gill :: gill from wire - |= way=wire ^- gill:gall - ~| way - ?>(?=([@ @ *] way) [(slav %p i.way) i.t.way]) + |= way=wire + ^- [@tas gill:gall] + ~| wire=way + ?> ?=([@ @ ?(~ [@ ~])] way) + :- ?~(t.t.way %$ i.t.t.way) + [(slav %p i.way) i.t.way] -- -:: TODO: remove .ost :: |= [hid=bowl:gall state] =* sat +<+ -=/ ost 0 -=+ (~(gut by bin) ost *source) +=/ ses=@tas %$ +=+ (~(gut by bin) ses *source) =* dev - =| moz=(list card:agent:gall) -=| biz=(list dill-blit:dill) +=| biz=(list blit:dill) ::TODO should be per-session |% ++ this . +++ klr klr:format +$ state ^state :: proxy +$ any-state ^any-state :: proxy -++ on-init (poke-link our.hid %dojo) +++ on-init (poke-link %$ our.hid %dojo) +:: +++ prep + |= s=@tas + =. ses s + =. dev (~(gut by bin) ses *source) + this +:: +++ open + %+ cork de-gill + |= [s=@tas g=gill:gall] + [g (prep s)] +:: ++ diff-sole-effect-phat :: app event |= [way=wire fec=sole-effect] - =< se-abet =< se-view - =+ gyl=(de-gill way) + =< se-abet + =^ gyl this (open way) ?: (se-aint gyl) +>.$ (se-diff gyl fec) :: ++ peer :: |= pax=path + =? this ?=([%dill @ ~] pax) + (prep i.t.pax) ~| [%drum-unauthorized our+our.hid src+src.hid] :: ourself ?> (team:title our.hid src.hid) :: or our own moon - =< se-abet =< se-view + =< se-abet (se-text "[{}, driving {}]") :: +++ poke-dill + |= [ses=@tas bet=dill-belt:dill] + (poke-dill-belt:(prep ses) bet) +:: ++ poke-dill-belt :: terminal event |= bet=dill-belt:dill - =< se-abet =< se-view + =< se-abet (se-belt bet) :: ++ poke-dill-blit :: terminal output @@ -118,29 +173,29 @@ se-abet:(se-blit-sys bit) :: ++ poke-link :: connect app - |= gyl=gill:gall - =< se-abet =< se-view - (se-link gyl) + |= [ses=@tas gyl=gill:gall] + =< se-abet + (se-link:(prep ses) gyl) :: ++ poke-unlink :: disconnect app - |= gyl=gill:gall - =< se-abet =< se-view - (se-drop:(se-pull gyl) & gyl) + |= [ses=@ta gyl=gill:gall] + =< se-abet + (se-drop:(se-pull:(prep ses) gyl) & gyl) :: ++ poke-exit :: shutdown |= ~ se-abet:(se-blit-sys `dill-blit:dill`[%qit ~]) :: ++ poke-put :: write file - |= [pax=path txt=@] + |= [pax=path arg=$@(@ [@tas @])] + =^ txt +> ?@(arg [arg +>] [+.arg (prep -.arg)]) se-abet:(se-blit-sys [%sav pax txt]) :: :: ++ poke |= [=mark =vase] ?> =(our src):hid ?+ mark ~|([%poke-drum-bad-mark mark] !!) - %drum-dill-belt =;(f (f !<(_+<.f vase)) poke-dill-belt) - %drum-dill-blit =;(f (f !<(_+<.f vase)) poke-dill-blit) + %dill-poke =;(f (f !<(_+<.f vase)) poke-dill) %drum-exit =;(f (f !<(_+<.f vase)) poke-exit) %drum-link =;(f (f !<(_+<.f vase)) poke-link) %drum-put =;(f (f !<(_+<.f vase)) poke-put) @@ -149,19 +204,40 @@ :: ++ on-load |= [hood-version=@ud old=any-state] - =< se-abet =< se-view + =< se-abet =? old ?=(%2 -.old) [%4 [eel bin]:old] =? old ?=(%3 -.old) [%4 [eel bin]:old] + =? old ?=(%4 -.old) + |^ 5+(~(run by bin.old) source-4-to-5) + ++ source-4-to-5 + |= source-4 + ^- source + =; fug [edg off kil inx eel.old fug mir] + (~(run by fug) |=(t=(unit target-4) (bind t target-4-to-5))) + :: + ++ target-4-to-5 + |= t=target-4 + ^- target + :_ +.t + :- (bind p.blt.t belt-4-to-5) + (bind q.blt.t belt-4-to-5) + :: + ++ belt-4-to-5 + |= b=dill-belt-4 + ^- dill-belt:dill + ?. ?=(?(%ctl %met) -.b) b + [%mod -.b p.b] + -- :: - ?> ?=(%4 -.old) + ?> ?=(%5 -.old) =. sat old - =. dev (~(gut by bin) ost *source) + =. dev (~(gut by bin) ses *source) this :: ++ reap-phat :: ack connect |= [way=wire saw=(unit tang)] - =< se-abet =< se-view - =+ gyl=(de-gill way) + =< se-abet + =^ gyl this (open way) ?~ saw (se-join gyl) :: Don't print stack trace because we probably just crashed to @@ -171,9 +247,9 @@ :: ++ take-coup-phat :: ack poke |= [way=wire saw=(unit tang)] - =< se-abet =< se-view + =< se-abet ?~ saw +> - =+ gyl=(de-gill way) + =^ gyl this (open way) ?: (se-aint gyl) +>.$ %- se-dump:(se-drop:(se-pull gyl) & gyl) :_ u.saw @@ -196,8 +272,8 @@ :: ++ quit-phat :: |= way=wire - =< se-abet =< se-view - =+ gyl=(de-gill way) + =< se-abet + =^ gyl this (open way) ~& [%drum-quit src.hid gyl] (se-drop %| gyl) :: :: :: @@ -205,13 +281,18 @@ :: :: :: ++ se-abet :: resolve ^- (quip card:agent:gall state) - =. . se-subze:se-adze - :_ sat(bin (~(put by bin) ost dev)) + =. . se-view:se-subze:se-adze + :_ sat(bin (~(put by bin) ses dev)) ^- (list card:agent:gall) ?~ biz (flop moz) :_ (flop moz) - =/ =dill-blit:dill ?~(t.biz i.biz [%mor (flop biz)]) - [%give %fact ~[/drum] %dill-blit !>(dill-blit)] + =/ =blit:dill ?~(t.biz i.biz [%mor (flop biz)]) + ::TODO remove /drum after dill cleans up + ::TODO but once we remove it, the empty trailing segment of + :: /dill/[ses] would prevent outsiders from subscribing + :: to the default session... + =/ to=(list path) [/dill/[ses] ?~(ses ~[/drum] ~)] + [%give %fact to %dill-blit !>(blit)] :: ++ se-adze :: update connections ^+ . @@ -230,14 +311,14 @@ (se-peer gil) :: ++ se-subze :: downdate connections - =< .(dev (~(got by bin) ost)) - =. bin (~(put by bin) ost dev) + =< .(dev (~(got by bin) ses)) + =. bin (~(put by bin) ses dev) ^+ . %- ~(rep by bin) =< .(con +>) - |: $:,[[ost=bone dev=source] con=_.] ^+ con - =+ xeno=se-subze-local:%_(con ost ost, dev dev) - xeno(ost ost.con, dev dev.con, bin (~(put by bin) ost dev.xeno)) + |: $:,[[ses=@tas dev=source] con=_.] ^+ con + =+ xeno=se-subze-local:%_(con ses ses, dev dev) + xeno(ses ses.con, dev dev.con, bin (~(put by bin.xeno) ses dev.xeno)) :: ++ se-subze-local ^+ . @@ -252,7 +333,7 @@ ++ se-aint :: ignore result |= gyl=gill:gall ^- ? - ?. (~(has by bin) ost) & + ?. (~(has by bin) ses) & =+ gyr=(~(get by fug) gyl) |(?=(~ gyr) ?=(~ u.gyr)) :: @@ -290,7 +371,7 @@ [%cru *] (se-dump:(se-text (trip p.bet)) q.bet) [%hey *] +>(mir [0 ~]) :: refresh [%rez *] +>(edg (dec p.bet)) :: resize window - [%yow *] ~&([%no-yow -.bet] +>) + [%yow *] (se-link p.bet) == =+ gul=se-agon ?: |(?=(~ gul) (se-aint u.gul)) @@ -341,6 +422,21 @@ leaf+(weld (scag (sub edg 3) tape) "...") leaf+tape :: +++ se-blin :: print and newline + |= $= lin + $~ [%put ~] + $>(?(%put %klr) dill-blit:dill) + ^+ +> + :: newline means we need to redraw the prompt, + :: so update the prompt mirror accordingly. + :: + =. mir [0 ~] + ::TODO doing hops and wyps conditionally based on the mirror state seems + :: better, but doesn't cover edge cases. results in dojo's ">=" being + :: rendered alongside the prompt in scrollback, for example. + :: figure out a way to make that work! + (se-blit %mor [%hop 0] [%wyp ~] lin [%nel ~] ~) +:: ++ se-dump :: print tanks |= tac=(list tank) ^+ +> @@ -351,7 +447,7 @@ ?. ((sane %t) (crip i.wol)) :: XX upstream validation ~& bad-text+<`*`i.wol> $(wol t.wol) - $(wol t.wol, +>.^$ (se-blit %out (tuba i.wol))) + $(wol t.wol, +>.^$ (se-blin %put (tuba i.wol))) :: ++ se-join :: confirm connection |= gyl=gill:gall @@ -379,20 +475,21 @@ +>(eel (~(put in eel) gyl)) :: ++ se-blit :: give output - |= bil=dill-blit:dill + |= bil=blit:dill +>(biz [bil biz]) :: ++ se-blit-sys :: output to system |= bil=dill-blit:dill ^+ +> - (se-emit %give %fact ~[/drum] %dill-blit !>(bil)) + ::TODO remove /drum after dill cleans up + (se-emit %give %fact ~[/drum /dill/[ses]] %dill-blit !>(bil)) :: ++ se-show :: show buffer, raw |= lin=(pair @ud stub) ^+ +> ?: =(mir lin) +> - =. +> ?:(=(p.mir p.lin) +> (se-blit %hop p.lin)) - =. +> ?:(=(q.mir q.lin) +> (se-blit %pom q.lin)) - +>(mir lin) + %- se-blit(mir lin) + ?: =(q.mir q.lin) [%hop p.lin] + mor+[[%hop 0] [%wyp ~] [%klr q.lin] [%hop p.lin] ~] :: ++ se-just :: adjusted buffer |= [pom=stub lin=(pair @ud (list @c))] @@ -430,22 +527,22 @@ ?. ((sane %t) (crip txt)) :: XX upstream validation ~& bad-text+<`*`txt> +> - (se-blit %out (tuba txt)) + (se-blin %put (tuba txt)) :: ++ se-poke :: send a poke |= [gyl=gill:gall par=cage] - (se-emit %pass (en-gill gyl) %agent gyl %poke par) + (se-emit %pass (en-gill ses gyl) %agent gyl %poke par) :: ++ se-peer :: send a peer |= gyl=gill:gall ~> %slog.0^leaf/"drum: link {<[p q]:gyl>}" - =/ =path /sole/(cat 3 'drum_' (scot %p our.hid)) + =/ =path (id-to-path:sole our.hid ses) %- se-emit(fug (~(put by fug) gyl ~)) - [%pass (en-gill gyl) %agent gyl %watch path] + [%pass (en-gill ses gyl) %agent gyl %watch path] :: ++ se-pull :: cancel subscription |= gyl=gill:gall - (se-emit %pass (en-gill gyl) %agent gyl %leave ~) + (se-emit %pass (en-gill ses gyl) %agent gyl %leave ~) :: ++ se-tame :: switch connection |= gyl=gill:gall @@ -470,7 +567,7 @@ ^+ +> (ta-poke %sole-action !>(act)) :: - ++ ta-id (cat 3 'drum_' (scot %p our.hid)) :: per-ship duct id + ++ ta-id [our.hid ses] :: per-ship-session id :: ++ ta-aro :: hear arrow |= key=?(%d %l %r %u) @@ -499,13 +596,19 @@ ?< ?=([?(%cru %hey %rez %yow) *] bet) :: target-specific =. blt [q.blt `bet] :: remember belt ?- bet + @ (ta-txt bet ~) [%aro *] (ta-aro p.bet) [%bac *] ta-bac - [%ctl *] (ta-ctl p.bet) [%del *] ta-del - [%met *] (ta-met p.bet) + [%hit *] (ta-hit +.bet) [%ret *] ta-ret [%txt *] (ta-txt p.bet) + :: + [%mod *] + ?+ mod.bet $(bet key.bet) + %ctl (ta-ctl key.bet) + %met (ta-met key.bet) + == == :: ++ ta-det :: send edit @@ -529,7 +632,7 @@ (ta-hom %del (dec pos.inp)) :: ++ ta-ctl :: hear control - |= key=@ud + |= key=bolt:dill ^+ +> =. ris ?.(?=(?(%g %r) key) ~ ris) ?+ key ta-bel @@ -539,7 +642,7 @@ %d ?^ buf.say.inp ta-del ?: =([our.hid %dojo] gyl) - +>(..ta (se-blit qit+~)) :: quit pier + +>(..ta (se-blit-sys %qit ~)) :: quit pier +>(..ta (se-klin gyl)) :: unlink app %e +>(pos.inp (lent buf.say.inp)) %f (ta-aro %r) @@ -550,7 +653,7 @@ ?: =(pos.inp len) ta-bel (ta-kil %r [pos.inp (sub len pos.inp)]) - %l +>(..ta (se-blit %clr ~)) + %l +>(..ta (se-blit(q.mir ~) %clr ~)) %n (ta-aro %d) %p (ta-aro %u) %r ?~ ris @@ -583,6 +686,14 @@ ta-bel (ta-hom %del pos.inp) :: + ++ ta-hit :: hear click + |= [x=@ud y=@ud] + ^+ +> + =/ pol=@ud + (lent-char:klr (make:klr cad.pom)) + =? x (lth x pol) pol + +>.$(pos.inp (min (sub x pol) (lent buf.say.inp))) + :: ++ ta-erl :: hear local error |= pos=@ud ta-bel(pos.inp (min pos (lent buf.say.inp))) @@ -594,14 +705,13 @@ ++ ta-fec :: apply effect |= fec=sole-effect ^+ +> - ?- fec + ?+ fec +>(..ta (se-blit fec)) [%bel *] ta-bel [%blk *] +> [%bye *] +>(..ta (se-klin gyl)) - [%clr *] +>(..ta (se-blit fec)) [%det *] (ta-got +.fec) [%err *] (ta-err p.fec) - [%klr *] +>(..ta (se-blit %klr (make:klr p.fec))) + [%klr *] +>(..ta (se-blin %klr (make:klr p.fec))) [%mor *] |- ^+ +>.^$ ?~ p.fec +>.^$ $(p.fec t.p.fec, +>.^$ ^$(fec i.p.fec)) @@ -609,10 +719,8 @@ [%pro *] (ta-pro +.fec) [%tab *] +>(..ta (se-tab p.fec)) [%tan *] +>(..ta (se-dump p.fec)) - [%sag *] +>(..ta (se-blit fec)) - [%sav *] +>(..ta (se-blit fec)) [%txt *] +>(..ta (se-text p.fec)) - [%url *] +>(..ta (se-blit fec)) + [%url *] +>(..ta (se-text:(se-blit fec) (trip p.fec))) == :: ++ ta-dog :: change cursor @@ -664,8 +772,8 @@ kil ?. ?& ?=(^ old.kil) ?=(^ p.blt) - ?| ?=([%ctl ?(%k %u %w)] u.p.blt) - ?=([%met ?(%d %bac)] u.p.blt) + ?| ?=([%mod %ctl ?(%k %u %w)] u.p.blt) + ?=([%mod %met ?(%d [%bac ~])] u.p.blt) == == %= kil :: prepend num +(num.kil) @@ -682,17 +790,18 @@ == :: ++ ta-met :: meta key - |= key=@ud + |= key=bolt:dill ^+ +> =. ris ~ ?+ key ta-bel - %dot ?. &(?=(^ old.hit) ?=(^ i.old.hit)) :: last "arg" from hist + %'.' ?. &(?=(^ old.hit) ?=(^ i.old.hit)) :: last "arg" from hist ta-bel =+ old=`(list @c)`i.old.hit =+ sop=(ta-jump(buf.say.inp old) %l %ace (lent old)) (ta-hom (cat:edit pos.inp (slag sop old))) :: - %bac ?: =(0 pos.inp) :: kill left-word + [%bac ~] + ?: =(0 pos.inp) :: kill left-word ta-bel =+ sop=(ta-pos %l %edg pos.inp) (ta-kil %l [(sub pos.inp sop) sop]) @@ -748,8 +857,8 @@ :: %y ?. ?& ?=(^ old.kil) :: rotate & yank ?=(^ p.blt) - ?| ?=([%ctl %y] u.p.blt) - ?=([%met %y] u.p.blt) + ?| ?=([%mod %ctl %y] u.p.blt) + ?=([%mod %met %y] u.p.blt) == == ta-bel =+ las=(lent ta-yan) @@ -927,82 +1036,4 @@ ?: |(?=(~ a) (alnm i.a)) i $(i +(i), a t.a) -- -:: -++ klr :: styx/stub engine - =, dill - |% - ++ make :: stub from styx - |= a=styx ^- stub - =| b=stye - %+ reel - |- ^- stub - %- zing %+ turn a - |= a=$@(@t (pair styl styx)) - ?@ a [b (tuba (trip a))]~ - ^$(a q.a, b (styd p.a b)) - :: - |= [a=(pair stye (list @c)) b=stub] - ?~ b [a ~] - ?. =(p.a p.i.b) [a b] - [[p.a (weld q.a q.i.b)] t.b] - :: - ++ styd :: stye from styl - |= [a=styl b=stye] ^+ b :: with inheritance - :+ ?~ p.a p.b - ?~ u.p.a ~ - (~(put in p.b) u.p.a) - (fall p.q.a p.q.b) - (fall q.q.a q.q.b) - :: - ++ lent-char - |= a=stub ^- @ - (roll (lnts-char a) add) - :: - ++ lnts-char :: stub pair tail lengths - |= a=stub ^- (list @) - %+ turn a - |= a=(pair stye (list @c)) - (lent q.a) - :: - ++ brek :: index + incl-len of - |= [a=@ b=(list @)] :: stub pair w= idx a - =| [c=@ i=@] - |- ^- (unit (pair @ @)) - ?~ b ~ - =. c (add c i.b) - ?: (gte c a) - `[i c] - $(i +(i), b t.b) - :: - ++ slag :: slag stub, keep stye - |= [a=@ b=stub] - ^- stub - =+ c=(lnts-char b) - =+ i=(brek a c) - ?~ i b - =+ r=(^slag +(p.u.i) b) - ?: =(a q.u.i) - r - =+ n=(snag p.u.i b) - :_ r :- p.n - (^slag (sub (snag p.u.i c) (sub q.u.i a)) q.n) - :: - ++ scag :: scag stub, keep stye - |= [a=@ b=stub] - ^- stub - =+ c=(lnts-char b) - =+ i=(brek a c) - ?~ i b - ?: =(a q.u.i) - (^scag +(p.u.i) b) - %+ welp - (^scag p.u.i b) - =+ n=(snag p.u.i b) - :_ ~ :- p.n - (^scag (sub (snag p.u.i c) (sub q.u.i a)) q.n) - :: - ++ swag :: swag stub, keep stye - |= [[a=@ b=@] c=stub] - (scag b (slag a c)) - -- -- diff --git a/pkg/arvo/lib/hood/kiln.hoon b/pkg/arvo/lib/hood/kiln.hoon index d8fac70f78..66a2d9acef 100644 --- a/pkg/arvo/lib/hood/kiln.hoon +++ b/pkg/arvo/lib/hood/kiln.hoon @@ -504,7 +504,7 @@ [%x %kiln %pikes ~] =+ .^(=rock:tire %cx /(scot %p our)//(scot %da now)/tire) :^ ~ ~ %kiln-pikes - !> ^- pikes + !> ^- pikes %- ~(rut by rock) |= [=desk =zest wic=(set weft)] ^- pike diff --git a/pkg/arvo/mar/dill/belt.hoon b/pkg/arvo/mar/dill/belt.hoon index 1677b6ef1e..5d50dcd98d 100644 --- a/pkg/arvo/mar/dill/belt.hoon +++ b/pkg/arvo/mar/dill/belt.hoon @@ -45,8 +45,8 @@ =+ cha=(tuba (trip q.kev)) ?> ?=([@ ~] cha) :: of a single character ?+ mod !! :: modified by one buckykey - [%ctrl ~ ~] [%ctl i.cha] - [%alt ~ ~] [%met i.cha] + [%ctrl ~ ~] [%mod %ctl i.cha] + [%alt ~ ~] [%mod %met i.cha] == ?@ q.kev [%txt (tuba (trip q.kev))] diff --git a/pkg/arvo/mar/dill/blit.hoon b/pkg/arvo/mar/dill/blit.hoon index 77e5cecf47..3ba85e3dcf 100644 --- a/pkg/arvo/mar/dill/blit.hoon +++ b/pkg/arvo/mar/dill/blit.hoon @@ -19,8 +19,10 @@ ^- ^json ?+ -.dib ~|(unsupported-blit+-.dib !!) %mor [%a (turn p.dib |=(a=dill-blit:dill json(dib a)))] - %hop (frond %hop (numb p.dib)) - ?(%pro %out) (frond -.dib (tape (tufa p.dib))) + %hop %+ frond %hop + ?@ p.dib (numb p.dib) + (pairs 'x'^(numb x.p.dib) 'y'^(numb y.p.dib) ~) + %put (frond -.dib (tape (tufa p.dib))) ?(%bel %clr) (frond %act %s -.dib) == -- diff --git a/pkg/arvo/mar/drum-put.hoon b/pkg/arvo/mar/drum-put.hoon index e6094158ed..8fa3773c01 100644 --- a/pkg/arvo/mar/drum-put.hoon +++ b/pkg/arvo/mar/drum-put.hoon @@ -1,8 +1,7 @@ +:: %drum-put: download into host system :: -:::: /hoon/do-claim/womb/mar - :: /? 310 -|_ [path @] +|_ [path $@(@ [@ta @])] :: ++ grad %noun ++ grow @@ -11,6 +10,6 @@ -- ++ grab :: convert from |% - +$ noun [path @] :: clam from %noun + +$ noun [path $@(@ [@ta @])] :: clam from %noun -- -- diff --git a/pkg/arvo/mar/herm/task.hoon b/pkg/arvo/mar/herm/task.hoon new file mode 100644 index 0000000000..93757418ec --- /dev/null +++ b/pkg/arvo/mar/herm/task.hoon @@ -0,0 +1,46 @@ +:: task: herm task for passthrough to dill +:: +/- herm +/+ dill +:: +|_ =task:herm +++ grad %noun +:: +grab: convert from +:: +++ grab + |% + ++ noun task:herm + :: + ++ json + |= jon=^json + ^+ task + ~| jon + ?> ?=([%o *] jon) + =+ ses=(~(got by p.jon) 'session') + ?> ?=([%s *] ses) + :- ?: =('' p.ses) %$ + (slav %tas p.ses) + =. p.jon (~(del by p.jon) 'session') + %. jon + =, dejs:format + |^ task + ++ task + %- of + :~ belt+belt:dejs:^dill + blew+(ot 'w'^ni 'h'^ni ~) + hail+ul + open+(ot 'term'^(se %tas) 'apps'^(ar gill) ~) + shut+ul + == + :: + ++ gill + (ot 'who'^(se %p) 'app'^(se %tas) ~) + -- + -- +:: +grow: convert to +:: +++ grow + |% + ++ noun task + -- +-- diff --git a/pkg/arvo/sur/herm.hoon b/pkg/arvo/sur/herm.hoon new file mode 100644 index 0000000000..eab8cbb1ba --- /dev/null +++ b/pkg/arvo/sur/herm.hoon @@ -0,0 +1,9 @@ +:: herm: stand-in for term.c with http interface +:: +|% ++$ task + $~ [%$ %hail ~] + $: session=@tas + task=$>(?(%open %shut %belt %blew %hail) session-task:dill) + == +-- diff --git a/pkg/arvo/sys.kelvin b/pkg/arvo/sys.kelvin index b7bcb9ecd8..0cb1220e5f 100644 --- a/pkg/arvo/sys.kelvin +++ b/pkg/arvo/sys.kelvin @@ -1 +1 @@ -[%zuse 417] +[%zuse 416] diff --git a/pkg/arvo/sys/arvo.hoon b/pkg/arvo/sys/arvo.hoon index 7dbbb0a239..c308777ebc 100644 --- a/pkg/arvo/sys/arvo.hoon +++ b/pkg/arvo/sys/arvo.hoon @@ -415,7 +415,7 @@ == -- :: -:: |de: axal engine +:: |of: axal engine :: ++ of =| fat=(axal) diff --git a/pkg/arvo/sys/lull.hoon b/pkg/arvo/sys/lull.hoon index 0936cdd587..084fc8b050 100644 --- a/pkg/arvo/sys/lull.hoon +++ b/pkg/arvo/sys/lull.hoon @@ -3,7 +3,7 @@ !: => ..part |% -++ lull %328 +++ lull %327 :: :: :: :::: :: :: (1) models :: :: :: @@ -752,7 +752,6 @@ [%hill p=(list @tas)] :: mount points [%done error=(unit error:ames)] :: ames message (n)ack [%mere p=(each (set path) (pair term tang))] :: merge result - [%note p=@tD q=tank] :: debug message [%ogre p=@tas] :: delete mount point [%rule red=dict wit=dict] :: node r+w permissions [%tire p=(each rock:tire wave:tire)] :: app state @@ -1160,9 +1159,7 @@ ++ dill ^? |% +$ gift :: out result <-$ - $% [%bbye ~] :: reset prompt - [%blit p=(list blit)] :: terminal output - [%burl p=@t] :: activate url + $% [%blit p=(list blit)] :: terminal output [%logo ~] :: logout [%meld ~] :: unify memory [%pack ~] :: compact memory @@ -1170,29 +1167,32 @@ == :: +$ task :: in request ->$ $~ [%vega ~] :: - $% [%belt p=belt] :: terminal input - [%blew p=blew] :: terminal config - [%boot lit=? p=*] :: weird %dill boot + $% [%boot lit=? p=*] :: weird %dill boot [%crop p=@ud] :: trim kernel state [%crud p=@tas q=(list tank)] :: print error - [%flee session=~] :: unwatch session [%flog p=flog] :: wrapped error - [%flow p=@tas q=(list gill:gall)] :: terminal config - [%hail ~] :: terminal refresh [%heft ~] :: memory report - [%hook ~] :: this term hung up - [%harm ~] :: all terms hung up $>(%init vane-task) :: after gall ready [%meld ~] :: unify memory - [%noop ~] :: no operation [%pack ~] :: compact memory - [%talk p=tank] :: - [%text p=tape] :: - [%view session=~] :: watch session blits + [%shot ses=@tas task=session-task] :: task for session + [%talk p=(list tank)] :: print tanks + [%text p=tape] :: print tape $>(%trim vane-task) :: trim state $>(%vega vane-task) :: report upgrade [%verb ~] :: verbose mode [%knob tag=term level=?(%hush %soft %loud)] :: error verbosity + session-task :: for default session + == :: + :: :: + +$ session-task :: session request + $% [%belt p=belt] :: terminal input + [%blew p=blew] :: terminal config + [%flee ~] :: unwatch session + [%hail ~] :: terminal refresh + [%open p=dude:gall q=(list gill:gall)] :: setup session + [%shut ~] :: close session + [%view ~] :: watch session blits == :: :: :::: :: (1d2) @@ -1200,59 +1200,41 @@ +$ blew [p=@ud q=@ud] :: columns rows +$ belt :: client input $? bolt :: simple input - $% [%mod mod=?(%ctl %met %hyp) key=bolt] :: w/ modifier + [%mod mod=?(%ctl %met %hyp) key=bolt] :: w/ modifier [%txt p=(list @c)] :: utf32 text ::TODO consider moving %hey, %rez, %yow here :: - ::TMP forward backwards-compatibility :: - :: :: - [%ctl p=@c] :: - [%met p=@c] :: - == == :: + == :: +$ bolt :: simple input $@ @c :: simple keystroke $% [%aro p=?(%d %l %r %u)] :: arrow key [%bac ~] :: true backspace [%del ~] :: true delete - [%hit r=@ud c=@ud] :: mouse click + [%hit x=@ud y=@ud] :: mouse click [%ret ~] :: return == :: - +$ blit :: old blit + +$ blit :: client output $% [%bel ~] :: make a noise [%clr ~] :: clear the screen - [%hop p=@ud] :: set cursor position - [%klr p=stub] :: set styled line - [%lin p=(list @c)] :: set current line - [%mor ~] :: newline + [%hop p=$@(@ud [x=@ud y=@ud])] :: set cursor col/pos + [%klr p=stub] :: put styled + [%mor p=(list blit)] :: multiple blits + [%nel ~] :: newline + [%put p=(list @c)] :: put text at cursor [%sag p=path q=*] :: save to jamfile [%sav p=path q=@] :: save to file [%url p=@t] :: activate url + [%wyp ~] :: wipe cursor line == :: - +$ dill-belt :: new belt - $% [%aro p=?(%d %l %r %u)] :: arrow key - [%bac ~] :: true backspace + +$ dill-belt :: arvo input + $% belt :: client input [%cru p=@tas q=(list tank)] :: echo error - [%ctl p=@] :: control-key - [%del ~] :: true delete [%hey ~] :: refresh - [%met p=@] :: meta-key - [%ret ~] :: return [%rez p=@ud q=@ud] :: resize, cols, rows - [%txt p=(list @c)] :: utf32 text [%yow p=gill:gall] :: connect to app == :: - +$ dill-blit :: new blit - $% [%bel ~] :: make a noise - [%clr ~] :: clear the screen - [%hop p=@ud] :: set cursor position - [%klr p=stub] :: styled text - [%mor p=(list dill-blit)] :: multiple blits - [%pom p=stub] :: styled prompt - [%pro p=(list @c)] :: show as cursor+line + +$ dill-blit :: arvo output + $% blit :: client output [%qit ~] :: close console - [%out p=(list @c)] :: send output line - [%sag p=path q=*] :: save to jamfile - [%sav p=path q=@] :: save to file - [%url p=@t] :: activate url == :: +$ flog :: sent to %dill $% [%crop p=@ud] :: trim kernel state @@ -1263,6 +1245,11 @@ [%text p=tape] :: [%verb ~] :: verbose mode == :: + :: :: + +$ poke :: dill to userspace + $: ses=@tas :: target session + dill-belt :: input + == :: -- ::dill :: :::: :::: ++eyre :: (1e) http-server @@ -1763,7 +1750,6 @@ +$ gift :: outgoing result $% [%boon payload=*] :: ames response [%done error=(unit error:ames)] :: ames message (n)ack - [%onto p=(each suss tang)] :: about agent [%unto p=unto] :: == :: +$ task :: incoming request @@ -2531,9 +2517,6 @@ :: %ames: hear packet :: $>(%hear task:ames) - :: %dill: hangup - :: - $>(%hook task:dill) :: %clay: external edit :: $>(%into task:clay) @@ -2554,6 +2537,9 @@ :: %eyre: starts handling an backdoor http request :: $>(%request-local task:eyre) + :: %dill: close session + :: + $>(%shut task:dill) :: %behn: wakeup :: $>(%wake task:behn) diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon index f6f1f9a587..75ba7ba8db 100644 --- a/pkg/arvo/sys/vane/ames.hoon +++ b/pkg/arvo/sys/vane/ames.hoon @@ -733,6 +733,7 @@ $% [%private-keys ~] [%public-keys ships=(set ship)] [%turf ~] + [%ruin ships=(set ship)] == == $: @tas $% [%plea =ship =plea] @@ -1247,7 +1248,7 @@ =(%$ syd) == ?. for.veb.bug.ames-state ~ - ~> %slog.0^leaf/"ames: scry-fail {<[why=why lot=lot now=now syd=syd]>}" + ~> %slog.0^leaf/"ames: scry-fail {}" ~ :: /ax/protocol/version @ :: /ax/peers (map ship ?(%alien %known)) @@ -1774,28 +1775,30 @@ |. ^- tape =/ sndr [our our-life.channel] =/ rcvr [ship her-life.channel] - "plea {}" + "plea {}" abet:(on-memo:(make-peer-core peer-state channel) bone plea %plea) :: +on-cork: handle request to kill a flow :: ++ on-cork |= =ship ^+ event-core + =/ =plea [%$ /flow [%cork ~]] =/ ship-state (~(get by peers.ames-state) ship) - :: - ?> ?=([~ %known *] ship-state) + ?. ?=([~ %known *] ship-state) + %+ enqueue-alien-todo ship + |= todos=alien-agenda + todos(messages [[duct plea] messages.todos]) =/ =peer-state +.u.ship-state =/ =channel [[our ship] now channel-state -.peer-state] :: =^ =bone ossuary.peer-state (bind-duct ossuary.peer-state duct) - =/ =plea [%$ /flow [%cork ~]] :: =. closing.peer-state (~(put in closing.peer-state) bone) %- %^ trace msg.veb ship |. ^- tape =/ sndr [our our-life.channel] =/ rcvr [ship her-life.channel] - "cork plea {}" + "cork plea {}" abet:(on-memo:(make-peer-core peer-state channel) bone plea %plea) :: +on-take-wake: receive wakeup or error notification from behn :: @@ -1923,14 +1926,14 @@ :: we shouldn't be hearing about ships we don't care about :: ?~ ship-state - ~> %slog.0^leaf/"ames: breach unknown {}" + ~> %slog.0^leaf/"ames: breach unknown {}" event-core :: if an alien breached, this doesn't affect us :: ?: ?=([~ %alien *] ship-state) - ~> %slog.0^leaf/"ames: breach alien {}" + ~> %slog.0^leaf/"ames: breach alien {}" event-core - ~> %slog.0^leaf/"ames: breach peer {}" + ~> %slog.0^leaf/"ames: breach peer {}" :: a peer breached; drop messaging state :: =/ =peer-state +.u.ship-state @@ -2063,6 +2066,8 @@ =. event-core %+ reel messages.todos |= [[=^duct =plea] core=_event-core] + ?: ?=(%$ -.plea) + (on-cork:core(duct duct) ship) (on-plea:core(duct duct) ship plea) :: apply outgoing packet blobs :: @@ -2141,10 +2146,27 @@ :: (emit unix-duct.ames-state %give %turf turfs) :: +on-vega: handle kernel reload - :: +on-trim: handle request to free memory :: ++ on-vega event-core - ++ on-trim event-core + :: +on-trim: handle request to free memory + :: + :: %ruin comets not seen for six months + :: + ++ on-trim + ^+ event-core + =; rui=(set @p) + (emit duct %pass /ruin %j %ruin rui) + =- (silt (turn - head)) + %+ skim + ~(tap by peers.ames-state) + |= [=ship s=ship-state] + ?. &(?=(%known -.s) =(%pawn (clan:title ship))) %.n + ?& (gth (sub now ~d180) last-contact.qos.s) + :: + %- ~(any by snd.s) + |= m=message-pump-state + !=(~ unsent-fragments.m) + == :: +enqueue-alien-todo: helper to enqueue a pending request :: :: Also requests key and life from Jael on first request. @@ -2590,12 +2612,12 @@ =(~ unsent-fragments.pum) =(~ live.packet-pump-state.pum) == - ~> %slog.0^leaf/"ames: bad pump state {<[her.channel i.boz]>}" + ~> %slog.0^leaf/"ames: bad pump state {}" $(boz t.boz) :: no outstanding messages, so send a new %cork :: :: TODO use +trace - ~> %slog.0^leaf/"ames: recork {<[her.channel i.boz]>}" + ~> %slog.0^leaf/"ames: recork {}" =/ =plea [%$ /flow [%cork ~]] (on-memo i.boz plea %plea) :: +got-duct: look up $duct by .bone, asserting already bound @@ -2768,11 +2790,18 @@ :: (emit [/ames]~ %pass wire %b %rest next-wake) =/ nax-bone=^bone (mix 0b10 bone) + =? peer-core (~(has by snd.peer-state) nax-bone) + %. peer-core + %+ trace odd.veb + =/ dat [her.channel bone=nax-bone message-num=message-num -.task] + |.("remove naxplanation flow {}") =. peer-state =, peer-state %_ peer-state + :: preemptively delete nax flows (e.g. nacks for initial %watches) + :: + snd (~(del by (~(del by snd) bone)) nax-bone) rcv (~(del by rcv) bone) - snd (~(del by snd) bone) corked (~(put in corked) bone) closing (~(del in closing) bone) krocs (~(del in krocs) bone) @@ -3196,9 +3225,14 @@ ++ packet-queue %- (ordered-map live-packet-key live-packet-val) lte-packets + :: +live-packets: number of sent packets awaiting ack + :: + ++ live-packets + ^- @ud + ~(wyt by live.state) :: +gauge: inflate a |pump-gauge to track congestion control :: - ++ gauge (make-pump-gauge now.channel metrics.state [her bug]:channel) + ++ gauge (make-pump-gauge metrics.state live-packets [now her bug]:channel) :: +work: handle $packet-pump-task request :: ++ work @@ -3253,7 +3287,7 @@ =? packet-pump ?=(^ static-fragment) %- %+ trace snd.veb =/ nums [message-num fragment-num]:u.static-fragment.res - |.("dead {}") + |.("dead {}") (give %send u.static-fragment.res) packet-pump :: @@ -3312,7 +3346,6 @@ :: update .live and .metrics :: =. live.state (gas:packet-queue live.state send-list) - =. metrics.state (on-sent:gauge (lent send-list)) :: TMI :: => .(sent `(list static-fragment)`sent) @@ -3374,7 +3407,7 @@ =(0 (mod counter.metrics.state 20)) == same - (trace snd.veb |.("send: {<[fragment=fragment-num show:gauge]>}")) + (trace snd.veb |.("send: {}")) :: .resends is backward, so fold backward and emit :: =. packet-pump @@ -3398,7 +3431,7 @@ == ^- [new-val=(unit live-packet-val) stop=? _acc] :: - =/ gauge (make-pump-gauge now.channel metrics.acc [her bug]:channel) + =/ gauge (make-pump-gauge metrics.acc live-packets [now her bug]:channel) :: is this the acked packet? :: ?: =(key [message-num fragment-num]) @@ -3433,7 +3466,7 @@ =- =. metrics.state metrics.- =. live.state live.- :: - %- (trace snd.veb |.("done {}")) + %- (trace snd.veb |.("done {}")) (fast-resend-after-ack message-num `fragment-num`0) :: ^+ [metrics=metrics.state live=live.state] @@ -3445,7 +3478,7 @@ == ^- [new-val=(unit live-packet-val) stop=? pump-metrics] :: - =/ gauge (make-pump-gauge now.channel metrics [her bug]:channel) + =/ gauge (make-pump-gauge metrics live-packets [now her bug]:channel) :: if we get an out-of-order ack for a message, skip until it :: ?: (lth message-num.key message-num) @@ -3493,9 +3526,10 @@ :: +make-pump-gauge: construct |pump-gauge congestion control core :: ++ make-pump-gauge - |= [now=@da pump-metrics =ship =bug] + |= [pump-metrics live-packets=@ud now=@da =ship =bug] + :: TODO rename live-packets num-live =* veb veb.bug - =* metrics +<+< + =* metrics +<- |% ++ trace |= [verb=? print=(trap tape)] @@ -3515,15 +3549,7 @@ :: ++ num-slots ^- @ud - (sub-safe cwnd num-live) - :: +on-sent: adjust metrics based on sending .num-sent fresh packets - :: - ++ on-sent - |= num-sent=@ud - ^- pump-metrics - :: - =. num-live (add num-live num-sent) - metrics + (sub-safe cwnd live-packets) :: +on-ack: adjust metrics based on a packet getting acknowledged :: ++ on-ack @@ -3531,7 +3557,6 @@ ^- pump-metrics :: =. counter +(counter) - =. num-live (dec num-live) :: if below congestion threshold, add 1; else, add avg. 1 / cwnd :: =. cwnd @@ -3571,7 +3596,7 @@ :: =? cwnd !in-recovery (max 2 (div cwnd 2)) %- %+ trace snd.veb - |.("skip {<[resend=resend in-recovery=in-recovery show]>}") + |.("skip {}") metrics :: +on-timeout: (re)enter slow-start mode on packet loss :: @@ -3597,12 +3622,12 @@ (lth cwnd ssthresh) :: +in-recovery: %.y iff we're recovering from a skipped packet :: - :: We finish recovering when .num-live finally dips back down to + :: We finish recovering when .live-packets finally dips back down to :: .cwnd. :: ++ in-recovery ^- ? - (gth num-live cwnd) + (gth live-packets cwnd) :: +sub-safe: subtract with underflow protection :: ++ sub-safe @@ -3619,7 +3644,7 @@ rttvar=(div rttvar ms) ssthresh=ssthresh cwnd=cwnd - num-live=num-live + num-live=live-packets counter=counter == -- @@ -3665,7 +3690,7 @@ :: ?: (gte seq (add 10 last-acked.state)) %- %+ trace odd.veb - |.("future %hear {}") + |.("future %hear {}") message-sink :: =/ is-last-fragment=? =(+(fragment-num) num-fragments) @@ -3676,7 +3701,7 @@ :: single packet ack :: %- %+ trace rcv.veb - |.("send dupe ack {}") + |.("send dupe ack {}") (give %send seq %& fragment-num) :: whole message (n)ack :: @@ -3696,8 +3721,8 @@ %- %+ trace rcv.veb |. ^- tape =/ data - :* her.channel seq=seq bone=bone - fragment-num=fragment-num num-fragments=num-fragments + :* her.channel seq=seq bone=bone.shut-packet + fragment-num num-fragments la=last-acked.state lh=last-heard.state == "hear last in-progress {}" @@ -3706,8 +3731,8 @@ :: %- %+ trace rcv.veb |. =/ data - :* seq=seq fragment-num=fragment-num - num-fragments=num-fragments closing=closing + :* seq=seq fragment-num + num-fragments closing=closing == "send ack-1 {}" (give %send seq %& fragment-num) @@ -3737,7 +3762,7 @@ "hear last dupe {}" message-sink %- %+ trace rcv.veb - |.("send dupe ack {}") + |.("send dupe ack {}") (give %send seq %& fragment-num) :: new fragment; store in state and check if message is done :: @@ -3754,7 +3779,7 @@ =? message-sink !is-last-fragment %- %+ trace rcv.veb |. =/ data - [seq=seq fragment-num=fragment-num num-fragments=num-fragments] + [seq=seq fragment-num num-fragments] "send ack-2 {}" (give %send seq %& fragment-num) :: enqueue all completed messages starting at +(last-heard.state) diff --git a/pkg/arvo/sys/vane/clay.hoon b/pkg/arvo/sys/vane/clay.hoon index ff2ccaf891..add88db470 100644 --- a/pkg/arvo/sys/vane/clay.hoon +++ b/pkg/arvo/sys/vane/clay.hoon @@ -432,7 +432,10 @@ task :: == :: $: %d :: to %dill - $>(%flog task:dill) :: + $> $? %flog :: + %text :: + == :: + task:dill :: == :: $: %g :: to %gall $> $? %deal @@ -460,14 +463,12 @@ == == :: $: %clay :: $> $? %mere :: - %note :: %writ :: == :: gift :: == :: $: %gall - $> $? %onto - %unto + $> $? %unto == gift:gall == @@ -2209,7 +2210,8 @@ |= [prefix=@tD paths=(set path)] %+ turn ~(tap in paths) |= =path - [u.hun %give %note prefix (path-to-tank path)] + ^- move + [u.hun %pass /note %d %text prefix ' ' ~(ram re (path-to-tank path))] :: ++ path-to-tank |= =path @@ -5774,8 +5776,6 @@ ?^ dud ~|(%clay-take-dud (mean tang.u.dud)) ?: ?=([%lu %load *] tea) - ?: ?=(%onto +<.hin) - [~ ..^$] ?> ?=(%unto +<.hin) ?> ?=(%poke-ack -.p.hin) ?~ p.p.hin @@ -5950,7 +5950,6 @@ q.p.p.+.hin [~ ..^$] :: - %note [[hen %give +.hin]~ ..^$] %wake :: TODO: handle behn errors :: @@ -5973,7 +5972,6 @@ :: %boon !! %lost !! - %onto !! %unto !! %writ %- (slog leaf+"clay: strange writ (expected on upgrade to Fusion)" ~) diff --git a/pkg/arvo/sys/vane/dill.hoon b/pkg/arvo/sys/vane/dill.hoon index ab9f77af63..f58a73bd03 100644 --- a/pkg/arvo/sys/vane/dill.hoon +++ b/pkg/arvo/sys/vane/dill.hoon @@ -8,27 +8,26 @@ -- :: => |% :: console protocol +$ axle :: - $: %4 ::TODO replace ducts with session ids :: + $: %6 :: hey=(unit duct) :: default duct - dug=(map duct axon) :: conversations - eye=(jug duct duct) :: outside listeners + dug=(map @tas axon) :: conversations + eye=(jug @tas duct) :: outside listeners lit=? :: boot in lite mode $= veb :: vane verbosities $~ (~(put by *(map @tas log-level)) %hole %soft) :: quiet packet crashes (map @tas log-level) :: + egg=_| :: see +take, removeme == :: -+$ axon :: dill per duct ++$ axon :: dill session $: ram=term :: console program tem=(unit (list dill-belt)) :: pending, reverse wid=_80 :: terminal width - pos=@ud :: cursor position - see=$%([%lin (list @c)] [%klr stub]) :: current line == :: +$ log-level ?(%hush %soft %loud) :: none, line, full -- => :: |% :: protocol outward +$ mess :: - $% [%dill-belt p=(hypo dill-belt)] :: + $% [%dill-poke p=(hypo poke)] :: == :: +$ move [p=duct q=(wind note gift)] :: local move +$ note :: out request $-> @@ -69,7 +68,6 @@ == == :: $: %clay :: $> $? %mere :: - %note :: %writ :: == :: gift:clay :: @@ -78,10 +76,7 @@ $>(%blit gift:dill) :: == :: $: %gall :: - $> $? %onto :: - %unto :: - == :: - gift:gall :: + $>(%unto gift:gall) :: == == :: :::::::: :: dill tiles -- @@ -91,39 +86,27 @@ |% ++ as :: per cause =| moz=(list move) - |_ [hen=duct axon] + |_ [hen=duct ses=@tas axon] ++ abet :: resolve ^- [(list move) axle] - [(flop moz) all(dug (~(put by dug.all) hen +<+))] + [(flop moz) all(dug (~(put by dug.all) ses +<+>))] :: ++ call :: receive input |= kyz=task ^+ +> ?+ -.kyz ~& [%strange-kiss -.kyz] +> - %flow +> - %harm +> - %hail (send %hey ~) - %text (from %out (tuba p.kyz)) - %crud :: (send `dill-belt`[%cru p.kyz q.kyz]) - (crud p.kyz q.kyz) - %blew (send %rez p.p.kyz q.p.kyz) - %heft (pass /whey %$ whey/~) - %meld (dump kyz) - %pack (dump kyz) - %crop (dump trim+p.kyz) - %verb (pass /verb %$ kyz) - %noop +> - %belt - %- send - ::TMP forwards compatibility with next-dill - :: - ?@ p.kyz [%txt p.kyz ~] - ?: ?=(%hit -.p.kyz) [%txt ~] - ?. ?=(%mod -.p.kyz) p.kyz - =/ =@c - ?@ key.p.kyz key.p.kyz - ?:(?=(?(%bac %del %ret) -.key.p.kyz) `@`-.key.p.kyz ~-) - ?:(?=(%met mod.p.kyz) [%met c] [%ctl c]) + %hail (send %hey ~) + %belt (send `dill-belt`p.kyz) + %talk (talk p.kyz) + %text (fore (tuba p.kyz) ~) + %crud :: (send `dill-belt`[%cru p.kyz q.kyz]) + (crud p.kyz q.kyz) + %blew (send(wid p.p.kyz) %rez p.p.kyz q.p.kyz) + %heft (pass /whey %$ whey/~) + %meld (dump kyz) + %pack (dump kyz) + %crop (dump trim+p.kyz) + %verb (pass /verb %$ kyz) == :: ++ crud @@ -133,30 +116,30 @@ =/ lev=log-level (~(gut by veb.all) err %loud) :: apply log level for this error tag :: - =/ =wall - ?- lev - %hush ~ - %soft ~["crud: %{(trip err)} event failed"] - %loud :- "crud: %{(trip err)} event failed" - %- zing - %+ turn (flop tac) - |=(a=tank (~(win re a) [0 wid])) - == - |- ^+ +>.^$ - ?~ wall +>.^$ - $(wall t.wall, +>.^$ (from %out (tuba i.wall))) + ?- lev + %hush +>.$ + %soft (fore (tuba "crud: %{(trip err)} event failed") ~) + %loud (talk leaf+"crud: %{(trip err)} event failed" (flop tac)) + == + :: + ++ talk + |= tac=(list tank) + %- fore + %- zing + %+ turn tac + |= a=tank + (turn (~(win re a) [0 wid]) tuba) :: ++ dump :: pass down to hey |= git=gift ?> ?=(^ hey.all) +>(moz [[u.hey.all %give git] moz]) :: - ++ done :: return gift + ++ done :: gift to viewers |= git=gift =- +>.$(moz (weld - moz)) %+ turn - :- hen - ~(tap in (~(get ju eye.all) hen)) + ~(tap in (~(get ju eye.all) ses)) |=(=duct [duct %give git]) :: ++ deal :: pass to %gall @@ -167,44 +150,37 @@ |= [=wire =note] +>(moz :_(moz [hen %pass wire note])) :: + ++ fore :: send dill output + ::NOTE there are still implicit assumptions + :: about the underlying console app's + :: semantics here. specifically, trailing + :: newlines are important to not getting + :: overwritten by the drum prompt, and a + :: bottom-of-screen cursor position gives + :: nicest results. a more agnostic solution + :: will need to replace this arm, someday. + :: perhaps +send this to .ram instead? + :: + |= liz=(list (list @c)) + ~? !=(%$ ses) [%d %foreing-in-session ses] + ^+ +> + =. +> + =| biz=(list blit) + |- ^+ +>.^$ + ?~ liz (done %blit [%hop 0] [%wyp ~] biz) + $(liz t.liz, biz (welp biz [%put i.liz] [%nel ~] ~)) + :: since dill is acting on its own accord, + :: we %hey the term app so it may clean up. + :: + (send %hey ~) + :: ++ from :: receive blit |= bit=dill-blit ^+ +> - ?: ?=(%mor -.bit) - |- ^+ +>.^$ - ?~ p.bit +>.^$ - $(p.bit t.p.bit, +>.^$ ^$(bit i.p.bit)) - ?: ?=(%out -.bit) - %+ done %blit - :~ [%lin p.bit] - [%mor ~] - see - [%hop pos] - == - ?: ?=(%klr -.bit) - %+ done %blit - :~ [%klr p.bit] - [%mor ~] - see - [%hop pos] - == - ?: ?=(%pro -.bit) - =. see [%lin p.bit] - (done %blit [see [%hop pos] ~]) - ?: ?=(%pom -.bit) - ::NOTE treat "styled prompt" without style as plain prompt, - :: to allow rendering by older runtimes - ::TODO remove me once v0.10.9+ has high/guaranteed adoption - :: - ?: (levy p.bit (cork head |*(s=stye =(*stye s)))) - $(bit [%pro (zing (turn p.bit tail))]) - =. see [%klr p.bit] - (done %blit [see [%hop pos] ~]) - ?: ?=(%hop -.bit) - (done(pos p.bit) %blit [bit ~]) ?: ?=(%qit -.bit) (dump %logo ~) - (done %blit [bit ~]) + ::TODO so why is this a (list blit) again? + (done %blit bit ~) :: ++ sponsor ^- ship @@ -221,7 +197,7 @@ =. tem ~ =. ..mere (pass /zest %c %zest %base %live) =. ..mere (show-desk %kids) - =. ..mere drum-watch + =. ..mere (open ~) |- ^+ ..mere ?~ myt ..mere $(myt t.myt, ..mere (send i.myt)) @@ -231,15 +207,27 @@ =. tem `(turn gyl |=(a=gill [%yow a])) (pass / [%c %warp our %base `[%sing %y [%ud 1] /]]) :: + ++ open + |= gyl=(list gill) + ::TODO should allow handlers from non-base desks + ::TODO maybe ensure :ram is running? + =. +> peer + %+ roll gyl + |= [g=gill _..open] + (send [%yow g]) + :: ++ send :: send action |= bet=dill-belt ^+ +> ?^ tem +>(tem `[bet u.tem]) - (deal / [%poke [%dill-belt -:!>(bet) bet]]) + (deal /send/[ses] [%poke [%dill-poke !>([ses bet])]]) :: - ++ drum-watch - (deal / [%watch /drum]) + ++ peer + (deal /peer/[ses] %watch /dill/[ses]) + :: + ++ pull + (deal /peer/[ses] %leave ~) :: ++ show-desk :: permit reads on desk |= des=desk @@ -249,21 +237,11 @@ |= [tea=wire sih=sign] ^+ +> ?- sih - [%gall %onto *] - :: NOTE effects during initial boot sequence are ignored, - :: so :hood compilation errors will not print; slog if desired - :: - :: ~& [%take-gall-onto +>.sih] - ?- -.+>.sih - %| (crud %onto p.p.+>.sih) - %& (done %blit [%lin (tuba "{}")]~) - == - :: [%gall %unto *] :: ~& [%take-gall-unto +>.sih] ?- -.+>.sih %raw-fact !! - %kick drum-watch + %kick peer %poke-ack ?~(p.p.+>.sih +>.$ (crud %coup u.p.p.+>.sih)) %watch-ack ?~ p.p.+>.sih @@ -275,9 +253,6 @@ +>.$ (from ;;(dill-blit q.q.cage.p.+>.sih)) == - :: - [%clay %note *] - (from %out (tuba p.sih ' ' ~(ram re q.sih))) :: [?(%behn %clay) %writ *] init @@ -292,12 +267,20 @@ == -- :: - ++ ax :: make ++as - |= hen=duct + ++ ax :: make ++as from name + |= [hen=duct ses=@tas] ^- (unit _as) - =/ nux (~(get by dug.all) hen) + =/ nux (~(get by dug.all) ses) ?~ nux ~ - (some ~(. as hen u.nux)) + (some ~(. as hen ses u.nux)) + :: + ++ aw :: make ++as from wire + |= [hen=duct wir=wire] + ^- (unit _as) + %+ ax hen + ?+ wir %$ + [?(%peer %send) @ *] i.t.wir + == -- |% :: poke+peek pattern ++ call :: handle request @@ -308,7 +291,10 @@ ^+ [*(list move) ..^$] ~| wrapped-task =/ task=task ((harden task) wrapped-task) + :: unwrap session tasks, default to session %$ :: + =^ ses=@tas task + ?:(?=(%shot -.task) +.task [%$ task]) :: error notifications "downcast" to %crud :: =? task ?=(^ dud) @@ -338,10 +324,11 @@ :: =* duc (need hey.all) =/ app %hood - =/ see (tuba "") - =/ zon=axon [app input=[~ ~] width=80 cursor=(lent see) lin+see] + =/ say (tuba "") + =/ zon=axon [app input=[~ ~] width=80] :: - =^ moz all abet:(~(into as duc zon) ~) + =^ moz all abet:(~(into as duc %$ zon) ~) + =. eye.all (~(put ju eye.all) %$ duc) [moz ..^$] :: %flog tasks are unwrapped and sent back to us on our default duct :: @@ -362,35 +349,60 @@ ?: ?=(%knob -.task) =. veb.all (~(put by veb.all) tag.task level.task) [~ ..^$] + :: %open opens a new dill session + :: + ?: ?=(%open -.task) + ?: (~(has by dug.all) ses) + ::TODO should we allow, and just send the %yow blits? + ~| [%cannot-open-existing ses] + !! + =/ zon=axon [p.task ~ width=80] + =^ moz all abet:(~(open as hen ses zon) q.task) + =. eye.all (~(put ju eye.all) ses hen) + [moz ..^$] + :: %shut closes an existing dill session + :: + ?: ?=(%shut -.task) + ?: =(%$ ses) + ~| %cannot-shut-default-session + !! + =/ nus + ~| [%no-session ses] + (need (ax hen ses)) + ::NOTE we do deletion from state outside of the core, + :: because +abet would re-insert. + ::TODO send a %bye blit? xx + =^ moz all abet:pull:nus + =. dug.all (~(del by dug.all) ses) + =. eye.all (~(del by eye.all) ses) + [moz ..^$] + :: %view opens a subscription to the target session, on the current duct :: ?: ?=(%view -.task) - :: crash on viewing non-existent session + =/ nus + :: crash on viewing non-existent session + :: + ~| [%no-session ses] + (need (ax hen ses)) + :: register the viewer and send a %hey so they get the full screen :: - ~| [%no-session session.task] - ?> =(~ session.task) - =/ session (need hey.all) - =/ =axon (~(got by dug.all) session) - :: register the viewer and send them the prompt line - :: - :- [hen %give %blit [see.axon]~]~ - ..^$(eye.all (~(put ju eye.all) session hen)) + =^ moz all + abet:(send:nus %hey ~) + :- moz + ..^$(eye.all (~(put ju eye.all) ses hen)) + :: %flee closes a subscription to the target session, from the current duct :: ?: ?=(%flee -.task) :- ~ - ~| [%no-session session.task] - ?> =(~ session.task) - =/ session (need hey.all) - ..^$(eye.all (~(del ju eye.all) session hen)) + ..^$(eye.all (~(del ju eye.all) ses hen)) :: - =/ nus (ax hen) - =? nus &(?=(~ nus) ?=(^ hey.all)) - ::TODO allow specifying target session in task - (ax u.hey.all) + =/ nus + (ax hen ses) ?~ nus - :: :hen is an unrecognized duct + :: session :ses does not exist :: could be before %boot (or %boot failed) :: - ~& [%dill-call-no-flow hen -.task] + ~& [%dill-call-no-session ses hen -.task] =/ tan ?:(?=(%crud -.task) q.task ~) [((slog (flop tan)) ~) ..^$] :: @@ -398,8 +410,79 @@ [moz ..^$] :: ++ load :: import old state - |= old=axle - ..^$(all old) + =< |= old=any-axle + ?- -.old + %6 ..^$(all old) + %5 $(old (axle-5-to-6 old)) + %4 $(old (axle-4-to-5 old)) + == + |% + +$ any-axle $%(axle axle-5 axle-4) + :: + +$ axle-5 + $: %5 + hey=(unit duct) :: default duct + dug=(map @tas axon) :: conversations + eye=(jug @tas duct) :: outside listeners + lit=? :: boot in lite mode + veb=(map @tas log-level) + == + :: + ++ axle-5-to-6 + |= a=axle-5 + ^- axle + :: [%6 hey `(map @tas axon)`dug eye lit veb |] + a(- %6, veb [veb.a &]) + :: + +$ axle-4 + $: %4 + hey=(unit duct) + dug=(map duct axon-4) + eye=(jug duct duct) + lit=? + veb=(map @tas log-level) + == + :: + +$ axon-4 + $: ram=term + tem=(unit (list dill-belt-4)) + wid=_80 + pos=$@(@ud [@ud @ud]) + see=$%([%lin (list @c)] [%klr stub]) + == + :: + +$ dill-belt-4 + $% [%ctl p=@c] + [%met p=@c] + dill-belt + == + :: + ++ axle-4-to-5 + |= axle-4 + ^- axle-5 + :- %5 + =- [hey nug nay lit veb] + %+ roll ~(tap by dug) + |= [[=duct =axon-4] nug=(map @tas axon) nay=(jug @tas duct)] + =/ ses=@tas + ~| [%unexpected-duct duct] + ?>(=([//term/1]~ duct) %$) + :- (~(put by nug) ses (axon-4-to-5 axon-4)) + %+ ~(put by nay) ses + (~(put in (~(get ju eye) duct)) duct) + :: + ++ axon-4-to-5 + |= axon-4 + ^- axon + =; tem [ram tem wid] + ?~ tem ~ + %- some + %+ turn u.tem + |= b=dill-belt-4 + ^- dill-belt + ?. ?=(?(%ctl %met) -.b) b + [%mod -.b p.b] + -- :: ++ scry ^- roon @@ -428,19 +511,12 @@ =(%$ syd) == ~ - :: /dx/sessions//line blit current line (prompt) of default session - :: /dx/sessions//cursor @ud current cursor position of default session - ::TODO support asking for specific sessions once session ids are real + :: /dy/sessions (set @tas) all existing sessions + :: /du/sessions/[ses] ? does session ses exist? :: - ?. ?=(%x ren) ~ - ?+ tyl ~ - [%sessions %$ *] - ?~ hey.all [~ ~] - ?~ session=(~(get by dug.all) u.hey.all) [~ ~] - ?+ t.t.tyl ~ - [%line ~] ``blit+!>(`blit`see.u.session) - [%cursor ~] ``atom+!>(pos.u.session) - == + ?+ [ren tyl] ~ + [%y %sessions ~] ``noun+!>(~(key by dug.all)) + [%u %sessions @ ~] ``noun+!>((~(has by dug.all) (snag 1 tyl))) == :: ++ stay all @@ -451,12 +527,27 @@ ?^ dud ~|(%dill-take-dud (mean tang.u.dud)) :: - =/ nus (ax hen) + =; [moz=(list move) lax=_..^$] + =? moz egg.all.lax + :: dill pre-release (version %5) in some cases ended up in a state + :: where it had both an old-style and new-style subscription open + :: for the default session. here, we obliterate both and establish + :: only the new-style subscription. + :: + =/ hey (need hey.all.lax) + :* [hey %pass / %g %deal [our our] %hood %leave ~] + [hey %pass [%peer %$ ~] %g %deal [our our] %hood %leave ~] + [hey %pass [%peer %$ ~] %g %deal [our our] %hood %watch [%dill %$ ~]] + moz + == + =. egg.all.lax | + [moz lax] + :: + =/ nus (aw hen tea) ?~ nus - :: :hen is an unrecognized duct - :: could be before %boot (or %boot failed) + :: :tea points to an unrecognized session :: - ~& [%dill-take-no-flow hen -.hin +<.hin] + ~& [%dill-take-no-session tea -.hin +<.hin] [~ ..^$] =^ moz all abet:(take:u.nus tea hin) [moz ..^$] diff --git a/pkg/arvo/sys/vane/gall.hoon b/pkg/arvo/sys/vane/gall.hoon index 842d9d3563..8ce10c6552 100644 --- a/pkg/arvo/sys/vane/gall.hoon +++ b/pkg/arvo/sys/vane/gall.hoon @@ -198,11 +198,21 @@ :: +mo-abet: finalize, reversing moves :: +mo-pass: prepend a standard %pass to the current list of moves :: +mo-give: prepend a standard %give to the current list of moves + :: +mo-talk: build task to print config report or failure trace :: ++ mo-core . ++ mo-abed |=(hun=duct mo-core(hen hun)) ++ mo-abet [(flop moves) gall-payload] ++ mo-give |=(g=gift mo-core(moves [[hen give+g] moves])) + ++ mo-talk + |= rup=(each suss tang) + ^- [wire note-arvo] + :+ /sys/say %d + ^- task:dill + ?- -.rup + %& [%text "gall: {(t q)}ed %{(t p)}":[t=trip p.rup]] + %| [%talk leaf+"gall: failed" (flop p.rup)] + == ++ mo-pass |=(p=[wire note-arvo] mo-core(moves [[hen pass+p] moves])) ++ mo-slip |=(p=note-arvo mo-core(moves [[hen slip+p] moves])) ++ mo-past @@ -303,12 +313,12 @@ =/ ap-core +.wag ?^ maybe-tang =. mo-core old - (mo-give %onto %.n u.maybe-tang) + (mo-pass (mo-talk %.n u.maybe-tang)) :: =. mo-core ap-abet:ap-core =. mo-core (mo-clear-queue dap) =/ =suss [dap %boot now] - (mo-give %onto [%.y suss]) + (mo-pass (mo-talk %.y suss)) :: +mo-send-foreign-request: handle local request to .ship :: ++ mo-send-foreign-request @@ -523,6 +533,11 @@ ?~ error=error.sign-arvo ~ `[[%leaf (trip tag.u.error)] tang.u.error] + :: send a %cork if we get a nack upon initial subscription + :: + =? mo-core + &(?=(^ err) |(?=(%watch-as remote-request) ?=(%watch remote-request))) + (mo-pass [%sys wire] %a %cork ship) :: ?- remote-request %watch-as (mo-give %unto %watch-ack err) @@ -899,8 +914,8 @@ :: =/ running (~(put by yokes.state) agent-name yoke) =/ moves - =/ giver |=(report=(each suss tang) [hen %give %onto report]) - =/ from-suss (turn agent-config giver) + =/ talker |=(report=(each suss tang) [hen %pass (mo-talk report)]) + =/ from-suss (turn agent-config talker) :(weld agent-moves from-suss moves) :: %_ mo-core diff --git a/pkg/arvo/sys/vane/jael.hoon b/pkg/arvo/sys/vane/jael.hoon index 69596a5c3b..2f1335564a 100644 --- a/pkg/arvo/sys/vane/jael.hoon +++ b/pkg/arvo/sys/vane/jael.hoon @@ -105,11 +105,8 @@ $>(%wake gift:behn) :: == :: $: %gall :: - $> $? %onto :: - %unto :: - == :: - gift:gall :: - == + $>(%unto gift:gall) :: + == :: == :: -- :: :: :::: @@ -462,12 +459,12 @@ :: [%vega ~] :: %vega - +>.$:: + +>.$ + :: :: in response to memory pressure :: [%trim p=@ud] :: %trim - ::TODO consider %ruin-ing long-offline comets +>.$ :: :: watch private keys @@ -507,7 +504,7 @@ %ruin ::NOTE we blast this out to _all_ known ducts, because the common :: use case for this is comets, about who nobody cares. - =/ dus ~(key by yen.zim.pki) + =/ dus (~(uni in nel.zim.pki) ~(key by yen.zim.pki)) =/ sus ~(. su hen now pki etn) =/ sis ~(tap in ships.tac) |- @@ -550,10 +547,6 @@ =/ ships (~(get ju ship-sources-reverse.etn) source-id) %- curd =< abet (sources:~(feel su hen now pki etn) ships source) - :: - [%gall %onto *] - ~& [%jael-onto tea hin] - +>.$ :: [%gall %unto *] ?- +>-.hin @@ -736,12 +729,10 @@ ++ subscribers-on-ship |= =ship ^- (set duct) - =/ specific-subs (~(get ju ney.zim) ship) - =/ general-subs=(set duct) - ?: ?=(?(%czar %king %duke) (clan:title ship)) - nel.zim - ~ - (~(uni in specific-subs) general-subs) + :: union of general and ship-specific subs + :: + %- ~(uni in nel.zim) + (~(get ju ney.zim) ship) :: ++ feed |_ :: hen: subscription source @@ -997,7 +988,7 @@ =| lex=state-2 |= $: :: now: current time :: eny: unique entropy - :: ski: namespace resolver + :: rof: namespace resolver :: now=@da eny=@uvJ diff --git a/pkg/arvo/sys/zuse.hoon b/pkg/arvo/sys/zuse.hoon index 7591a40878..0b516d7ba4 100644 --- a/pkg/arvo/sys/zuse.hoon +++ b/pkg/arvo/sys/zuse.hoon @@ -4,7 +4,7 @@ => ..lull ~% %zuse ..part ~ |% -++ zuse %417 +++ zuse %416 :: :: :: :::: :: :: (2) engines :: :: :: @@ -3843,6 +3843,102 @@ ~ (some (~(run by lum) need)) -- ::dejs-soft + :: + ++ klr :: styx/stub engine + =, dill + |% + ++ make :: stub from styx + |= a=styx ^- stub + =| b=stye + %+ reel + |- ^- stub + %- zing %+ turn a + |= a=$@(@t (pair styl styx)) + ?@ a [b (tuba (trip a))]~ + ^$(a q.a, b (styd p.a b)) + :: + |= [a=(pair stye (list @c)) b=stub] + ?~ b [a ~] + ?. =(p.a p.i.b) [a b] + [[p.a (weld q.a q.i.b)] t.b] + :: + ++ styd :: stye from styl + |= [a=styl b=stye] ^+ b :: with inheritance + :+ ?~ p.a p.b + ?~ u.p.a ~ + (~(put in p.b) u.p.a) + (fall p.q.a p.q.b) + (fall q.q.a q.q.b) + :: + ++ lent-char + |= a=stub ^- @ + (roll (lnts-char a) add) + :: + ++ lnts-char :: stub text lengths + |= a=stub ^- (list @) + %+ turn a + |= a=(pair stye (list @c)) + (lent q.a) + :: + ++ brek :: index + incl-len of + |= [a=@ b=(list @)] :: stub pair w/ idx a + =| [c=@ i=@] + |- ^- (unit (pair @ @)) + ?~ b ~ + =. c (add c i.b) + ?: (gte c a) + `[i c] + $(i +(i), b t.b) + :: + ++ pact :: condense stub + |= a=stub + ^- stub + ?~ a ~ + ?~ t.a a + ?. =(p.i.a p.i.t.a) [i.a $(a t.a)] + =. q.i.t.a (weld q.i.a q.i.t.a) + $(a t.a) + :: + ++ slag :: slag stub + |= [a=@ b=stub] + ^- stub + ?: =(0 a) b + ?~ b ~ + =+ c=(lent q.i.b) + ?: =(c a) t.b + ?: (gth c a) + [[p.i.b (^slag a q.i.b)] t.b] + $(a (sub a c), b t.b) + :: + ++ scag :: scag stub + |= [a=@ b=stub] + ^- stub + ?: =(0 a) ~ + ?~ b ~ + =+ c=(lent q.i.b) + ?: (gth c a) + [p.i.b (^scag a q.i.b)]~ + :- i.b + $(a (sub a c), b t.b) + :: + ++ swag :: swag stub + |= [[a=@ b=@] c=stub] + (scag b (slag a c)) + :: + ++ wail :: overlay stub + |= [a=stub b=@ c=stub d=@c] + ^- stub + ;: weld + (scag b a) + :: + =+ e=(lent-char a) + ?: (lte b e) ~ + [*stye (reap (sub b e) d)]~ + :: + c + (slag (add b (lent-char c)) a) + == + -- :: klr -- :: |cloy: clay helpers :: @@ -4928,7 +5024,7 @@ =< q.q %- need %- need (rof ~ %j `beam`[[our %sein %da now] /(scot %p who)]) -- - :: middle core: for userspace use, with .^ + :: middle core: stateless queries for default numeric sponsorship :: => |% :: :: ++clan:title @@ -4968,7 +5064,7 @@ %pawn (end 4 who) == -- - :: surface core: stateless queries for default numeric sponsorship + :: surface core: for userspace use, with .^ :: |% :: :: ++cite:title @@ -5003,13 +5099,25 @@ %j /(scot %p our)/sein/(scot %da now)/(scot %p who) == + :: +team was created with two meanings: + :: A. her / her moon + :: B. whoever should be able to control her ship + :: + :: these two things aren't obviously equal anymore, + :: and it's more important for +team to satisfy B than A, + :: so now +team just means "her". + :: + :: (ships can definitely be trusted to control themselves) :: :: ++team:title - ++ team :: our / our moon - |= [our=ship who=ship] + ++ team :: her + |= [her=ship who=ship] ^- ? - ?| =(our who) - &(?=(%earl (clan who)) =(our (^sein who))) - == + =(her who) + :: :: ++moon:title + ++ moon :: her moon + |= [her=ship who=ship] + ^- ? + &(=(%earl (clan who)) =(her (^sein who))) -- ::title :: :: :::: ++milly :: (2k) milliseconds diff --git a/pkg/arvo/ted/aqua/dill.hoon b/pkg/arvo/ted/aqua/dill.hoon index e001a65dc1..e7cb190174 100644 --- a/pkg/arvo/ted/aqua/dill.hoon +++ b/pkg/arvo/ted/aqua/dill.hoon @@ -17,15 +17,16 @@ %+ roll blits |= [b=blit:dill line=tape] ?- -.b - %lin (tape p.b) + %put (tape p.b) %klr (tape (zing (turn p.b tail))) - %mor ~& "{}: {line}" "" + %nel ~& "{}: {line}" "" %hop line %bel line %clr "" %sag ~& [%save-jamfile-to p.b] line %sav ~& [%save-file-to p.b] line %url ~& [%activate-url p.b] line + %wyp "" == ~? !=(~ last-line) last-line ~ diff --git a/pkg/base-dev/lib/bitcoin-utils.hoon b/pkg/base-dev/lib/bitcoin-utils.hoon index 648fb781bd..a23354c580 100644 --- a/pkg/base-dev/lib/bitcoin-utils.hoon +++ b/pkg/base-dev/lib/bitcoin-utils.hoon @@ -143,6 +143,7 @@ |= a=@ ^- hexb =/ l=@ (met 3 a) + ?: =(l 0) 1^a ?: =(l 1) 1^a ?: =(l 2) (cat:byt ~[1^0xfd (flip:byt 2^a)]) ?: (lte l 4) (cat:byt ~[1^0xfe (flip:byt 4^a)]) @@ -162,7 +163,7 @@ %0xfe 2 %0xff 3 == - :_ (drop:byt (add 1 len) h) + :_ (drop:byt (add 1 (bex len)) h) %- flip:byt (take:byt (bex len) (drop:byt 1 h)) :: +dea: atom instead of hexb for parsed CompactSize diff --git a/pkg/base-dev/lib/dill.hoon b/pkg/base-dev/lib/dill.hoon new file mode 100644 index 0000000000..41608b2635 --- /dev/null +++ b/pkg/base-dev/lib/dill.hoon @@ -0,0 +1,94 @@ +:: dill: utilities for dill's data structures +:: +=, dill +|% +++ enjs + |% + ++ blit + |= =blit:dill + ^- json + =, enjs:format + %+ frond -.blit + ?- -.blit + %bel b+& + %clr b+& + %hop ?@ p.blit (numb p.blit) + (pairs 'x'^(numb x.p.blit) 'y'^(numb y.p.blit) ~) + %put a+(turn p.blit |=(c=@c s+(tuft c))) + %nel b+& + %url s+p.blit + %wyp b+& + %mor a+(turn p.blit ^blit) + :: + %sag + %- pairs + :~ 'path'^(path p.blit) + 'file'^s+(en:base64:mimes:html (as-octs:mimes:html (jam q.blit))) + == + :: + %sav + %- pairs + :~ 'path'^(path p.blit) + 'file'^s+(en:base64:mimes:html (as-octs:mimes:html q.blit)) + == + :: + %klr + :- %a + %+ turn p.blit + |= [=stye text=(list @c)] + %- pairs + :~ 'text'^a+(turn text |=(c=@c s+(tuft c))) + :: + :- 'stye' + %- pairs + |^ :~ 'back'^(color p.q.stye) + 'fore'^(color q.q.stye) + 'deco'^a+(turn ~(tap in p.stye) |=(d=deco ?~(d ~ s+d))) + == + ++ color + |= =tint + ?@ tint ?~(tint ~ s+tint) + =, tint + (pairs r+(numb r) g+(numb g) b+(numb b) ~) + -- + == + == + -- +:: +++ dejs + |% + ++ belt + |= jon=json + ^- belt:dill + ?: ?=([%s *] jon) + (taft p.jon) + =, dejs:format + %. jon + %- of + |^ :* mod+(ot 'mod'^mod 'key'^bot ~) + txt+(ar (cu taft so)) + bol + == + :: + ++ bol + :~ aro+(su (perk %d %l %r %u ~)) + bac+ul + del+ul + hit+(ot 'x'^ni 'y'^ni ~) + ret+ul + == + :: + ++ bot + |= j=json + ^- bolt:dill + ?+ j !! + [%s *] (taft p.j) + [%o *] ((of bol) j) + == + :: + ++ mod + |= j=json + ((su (perk %ctl %met %hyp ~)) j) + -- + -- +-- \ No newline at end of file diff --git a/pkg/base-dev/lib/language-server/complete.hoon b/pkg/base-dev/lib/language-server/complete.hoon index 56b3ca498e..95ec07af7f 100644 --- a/pkg/base-dev/lib/language-server/complete.hoon +++ b/pkg/base-dev/lib/language-server/complete.hoon @@ -3,7 +3,8 @@ :: =/ debug | |% -+* option [item] +++ option + |$ [item] [term=cord detail=item] :: :: Like +rose except also produces line number diff --git a/pkg/base-dev/lib/ph/util.hoon b/pkg/base-dev/lib/ph/util.hoon index eee08a2ac0..f4b53ceb67 100644 --- a/pkg/base-dev/lib/ph/util.hoon +++ b/pkg/base-dev/lib/ph/util.hoon @@ -28,8 +28,8 @@ %+ send-events-to who ^- (list unix-event) :~ - [/d/term/1 %belt %ctl `@c`%e] - [/d/term/1 %belt %ctl `@c`%u] + [/d/term/1 %belt %mod %ctl `@c`%e] + [/d/term/1 %belt %mod %ctl `@c`%u] [/d/term/1 %belt %txt ((list @c) what)] [/d/term/1 %belt %ret ~] == @@ -40,7 +40,7 @@ |= [who=ship what=term] ^- (list ph-event) %+ send-events-to who - :~ [/d/term/1 %belt %ctl (,@c what)] + :~ [/d/term/1 %belt %mod %ctl (,@c what)] == :: :: Inject a file into a ship @@ -67,7 +67,7 @@ :: %+ lien p.q.uf |= =blit:dill - ?. ?=(%lin -.blit) + ?. ?=(%put -.blit) | !=(~ (find what p.blit)) == diff --git a/pkg/base-dev/lib/shoe.hoon b/pkg/base-dev/lib/shoe.hoon index cd4ab18db5..856a4d7822 100644 --- a/pkg/base-dev/lib/shoe.hoon +++ b/pkg/base-dev/lib/shoe.hoon @@ -13,15 +13,15 @@ /- *sole /+ sole, auto=language-server-complete |% -+$ state-0 - $: %0 - soles=(map @ta sole-share) ++$ state-1 + $: %1 + soles=(map sole-id sole-share) == :: $card: standard gall cards plus shoe effects :: +$ card $% card:agent:gall - [%shoe sole-ids=(list @ta) effect=shoe-effect] :: ~ sends to all soles + [%shoe sole-ids=(list sole-id) effect=shoe-effect] :: ~ sends to all == :: $shoe-effect: easier sole-effects :: @@ -47,30 +47,30 @@ :: if the head of the result is true, instantly run the command :: ++ command-parser - |~ sole-id=@ta + |~ =sole-id |~(nail *(like [? command-type])) :: +tab-list: autocomplete options for the session (to match +command-parser) :: ++ tab-list - |~ sole-id=@ta + |~ =sole-id :: (list [@t tank]) *(list (option:auto tank)) :: +on-command: called when a valid command is run :: ++ on-command - |~ [sole-id=@ta command=command-type] + |~ [=sole-id command=command-type] *(quip card _^|(..on-init)) :: ++ can-connect - |~ sole-id=@ta + |~ =sole-id *? :: ++ on-connect - |~ sole-id=@ta + |~ =sole-id *(quip card _^|(..on-init)) :: ++ on-disconnect - |~ sole-id=@ta + |~ =sole-id *(quip card _^|(..on-init)) :: ::NOTE standard gall agent arms below, though they may produce %shoe cards @@ -119,27 +119,27 @@ |* [shoe=* command-type=mold] |_ =bowl:gall ++ command-parser - |= sole-id=@ta + |= =sole-id (easy *[? command-type]) :: ++ tab-list - |= sole-id=@ta + |= =sole-id ~ :: ++ on-command - |= [sole-id=@ta command=command-type] + |= [=sole-id command=command-type] [~ shoe] :: ++ can-connect - |= sole-id=@ta + |= =sole-id (team:title [our src]:bowl) :: ++ on-connect - |= sole-id=@ta + |= =sole-id [~ shoe] :: ++ on-disconnect - |= sole-id=@ta + |= =sole-id [~ shoe] -- :: +agent: creates wrapper core that handles sole events and calls shoe arms @@ -147,7 +147,7 @@ ++ agent |* command-type=mold |= =(shoe command-type) - =| state-0 + =| state-1 =* state - ^- agent:gall => @@ -164,8 +164,7 @@ %+ turn ?^ sole-ids.card sole-ids.card ~(tap in ~(key by soles)) - |= sole-id=@ta - /sole/[sole-id] + id-to-path:sole :: %table =; fez=(list sole-effect) @@ -202,9 +201,36 @@ ?. ?=([%shoe-app ^] q.old-state) =^ cards shoe (on-load:og old-state) [(deal cards) this] - =^ old-inner state +:!<([%shoe-app vase state-0] old-state) - =^ cards shoe (on-load:og old-inner) - [(deal cards) this] + |^ =| old-outer=state-any + =^ old-inner old-outer + +:!<([%shoe-app vase state-any] old-state) + :: ~! q.old-state + :: ?+ +>.q.old-state !! + :: [%0 *] +:!<([%shoe-app vase state-0] old-state) + :: [%1 *] +:!<([%shoe-app vase state-1] old-state) + :: == + =^ caz shoe (on-load:og old-inner) + =^ cuz old-outer + ?. ?=(%0 -.old-outer) [~ old-outer] + (state-0-to-1 old-outer) + ?> ?=(%1 -.old-outer) + [(weld cuz (deal caz)) this(state old-outer)] + :: + +$ state-any $%(state-1 state-0) + +$ state-0 [%0 soles=(map @ta sole-share)] + ++ state-0-to-1 + |= old=state-0 + ^- (quip card:agent:gall state-1) + :- %+ turn ~(tap in ~(key by soles.old)) + |= id=@ta + ^- card:agent:gall + [%give %kick ~[/sole/[id]] ~] + :- %1 + %- ~(gas by *(map sole-id sole-share)) + %+ murn ~(tap by soles.old) + |= [id=@ta s=sole-share] + (bind (upgrade-id:sole id) (late s)) + -- :: ++ on-poke |= [=mark =vase] @@ -326,19 +352,18 @@ ++ on-watch |= =path ^- (quip card:agent:gall agent:gall) - ?. ?=([%sole @ ~] path) + ?~ sole-id=(path-to-id:sole path) =^ cards shoe (on-watch:og path) [(deal cards) this] - =* sole-id i.t.path - ?> (can-connect:og sole-id) - =. soles (~(put by soles) sole-id *sole-share) + ?> (can-connect:og u.sole-id) + =. soles (~(put by soles) u.sole-id *sole-share) =^ cards shoe - (on-connect:og sole-id) + (on-connect:og u.sole-id) :_ this %- deal :_ cards - [%shoe [sole-id]~ %sole %pro & dap.bowl "> "] + [%shoe [u.sole-id]~ %sole %pro & dap.bowl "> "] :: ++ on-leave |= =path diff --git a/pkg/base-dev/lib/sole.hoon b/pkg/base-dev/lib/sole.hoon index 66684668a6..ad6b749627 100644 --- a/pkg/base-dev/lib/sole.hoon +++ b/pkg/base-dev/lib/sole.hoon @@ -136,4 +136,28 @@ =+ dat=(transmute [%mor leg] [%ins pos `@c`0]) ?> ?=(%ins -.dat) p.dat +:: +:: +++ path-to-id + |= =path + ^- (unit sole-id) + ?. ?=([%sole @ ?(~ [@ ~])] path) ~ + ?~ who=(slaw %p i.t.path) ~ + `[u.who ?~(t.t.path %$ i.t.t.path)] +:: +++ id-to-path + |= sole-id + ^- path + ::TODO this whole "no empty path ending" business feels icky. + :: do we want default session to be ~.~ ? + :: concern here is that outsiders cannot subscribe to the default + :: session, because /sole/~zod/ isn't a valid path... + [%sole (scot %p who) ?~(ses ~ /[ses])] +:: +++ upgrade-id + |= old=@ta + ^- (unit sole-id) + %+ rush old + %+ cook (late %$) + ;~(pfix (jest 'drum_~') fed:ag) -- diff --git a/pkg/base-dev/mar/belt.hoon b/pkg/base-dev/mar/belt.hoon index 1c5a6e1ea6..acbf284c94 100644 --- a/pkg/base-dev/mar/belt.hoon +++ b/pkg/base-dev/mar/belt.hoon @@ -1,5 +1,7 @@ :: belt: runtime belt structure :: +/+ dill +:: |_ =belt:dill ++ grad %noun :: +grab: convert from @@ -7,18 +9,7 @@ ++ grab |% ++ noun belt:dill - ++ json - ^- $-(^json belt:dill) - =, dejs:format - %- of - :~ aro+(su (perk %d %l %r %u ~)) - bac+ul - ctl+(cu taft so) - del+ul - met+(cu taft so) - ret+ul - txt+(ar (cu taft so)) - == + ++ json belt:dejs:dill -- :: +grow: convert to :: diff --git a/pkg/base-dev/mar/blit.hoon b/pkg/base-dev/mar/blit.hoon index 4c23c5705e..4b852aa606 100644 --- a/pkg/base-dev/mar/blit.hoon +++ b/pkg/base-dev/mar/blit.hoon @@ -1,5 +1,7 @@ :: blit: runtime blit structure :: +/+ dill +:: |_ =blit:dill ++ grad %noun :: +grab: convert from @@ -13,49 +15,6 @@ ++ grow |% ++ noun blit - ++ json - ^- ^json - =, enjs:format - %+ frond -.blit - ?- -.blit - %bel b+& - %clr b+& - %hop (numb p.blit) - %lin a+(turn p.blit |=(c=@c s+(tuft c))) - %mor b+& - %url s+p.blit - :: - %sag - %- pairs - :~ 'path'^(path p.blit) - 'file'^s+(en:base64:mimes:html (as-octs:mimes:html (jam q.blit))) - == - :: - %sav - %- pairs - :~ 'path'^(path p.blit) - 'file'^s+(en:base64:mimes:html (as-octs:mimes:html q.blit)) - == - :: - %klr - :- %a - %+ turn p.blit - |= [=stye text=(list @c)] - %- pairs - :~ 'text'^a+(turn text |=(c=@c s+(tuft c))) - :: - :- 'stye' - %- pairs - |^ :~ 'back'^(color p.q.stye) - 'fore'^(color q.q.stye) - 'deco'^a+(turn ~(tap in p.stye) |=(d=deco ?~(d ~ s+d))) - == - ++ color - |= =tint - ?@ tint ?~(tint ~ s+tint) - s+(crip ((x-co:co 6) (rep 3 ~[b g r]:tint))) - -- - == - == + ++ json (blit:enjs:dill blit) -- -- diff --git a/pkg/base-dev/mar/sole/action.hoon b/pkg/base-dev/mar/sole/action.hoon index 149ef5ef16..de2835d721 100644 --- a/pkg/base-dev/mar/sole/action.hoon +++ b/pkg/base-dev/mar/sole/action.hoon @@ -20,7 +20,9 @@ |= jon=^json ^- sole-action %- need %. jon => [dejs-soft:format ..sole-action] - |^ (ot id+so dat+(fo %ret (of det+change tab+ni ~)) ~) + |^ (ot id+id dat+(fo %ret (of det+change tab+ni ~)) ~) + ++ id + (ot who+(su ;~(pfix sig fed:ag)) ses+so ~) ++ fo |* [a=term b=fist] |=(c=json ?.(=([%s a] c) (b c) (some [a ~]))) diff --git a/pkg/base-dev/sur/sole.hoon b/pkg/base-dev/sur/sole.hoon index e942bccb87..f8141f701e 100644 --- a/pkg/base-dev/sur/sole.hoon +++ b/pkg/base-dev/sur/sole.hoon @@ -3,8 +3,9 @@ :: ^? |% ++$ sole-id [who=@p ses=@ta] +$ sole-action :: sole to app - $: id=@ta :: duct id + $: id=sole-id :: session id $= dat $% :: [%abo ~] :: reset interaction [%det sole-change] :: command line edit diff --git a/pkg/bitcoin/sys.kelvin b/pkg/bitcoin/sys.kelvin index b7bcb9ecd8..47c282b538 100644 --- a/pkg/bitcoin/sys.kelvin +++ b/pkg/bitcoin/sys.kelvin @@ -1 +1,2 @@ [%zuse 417] +[%zuse 416] diff --git a/pkg/garden/sys.kelvin b/pkg/garden/sys.kelvin index b7bcb9ecd8..0cb1220e5f 100644 --- a/pkg/garden/sys.kelvin +++ b/pkg/garden/sys.kelvin @@ -1 +1 @@ -[%zuse 417] +[%zuse 416] diff --git a/pkg/hs/urbit-king/lib/Urbit/Arvo/Effect.hs b/pkg/hs/urbit-king/lib/Urbit/Arvo/Effect.hs index 5a57829124..6037e02988 100644 --- a/pkg/hs/urbit-king/lib/Urbit/Arvo/Effect.hs +++ b/pkg/hs/urbit-king/lib/Urbit/Arvo/Effect.hs @@ -119,15 +119,25 @@ deriveNoun ''BehnEf data Blit = Bel () | Clr () - | Hop Word64 + | Hop HopTarget | Klr Stub - | Lin [Char] - | Mor () + | Put [Char] + | Nel () | Sag Path Noun | Sav Path Atom | Url Cord + | Wyp () + --TMP backwards compatibility + | Lin [Char] + | Mor () deriving (Eq, Ord) +--NOTE bottom-left-0-based coordinates +data HopTarget + = Col Word64 + | Roc Word64 Word64 -- row, col + deriving (Eq, Ord, Show) + data Deco = DecoBl | DecoBr @@ -205,18 +215,33 @@ instance FromNoun Tint where "w" -> pure TintW t -> fail ("invalid: " <> unpack t) +instance FromNoun HopTarget where + parseNoun = \case + A c -> pure $ Col (fromIntegral c) + C (A r) (A c) -> pure $ Roc (fromIntegral r) (fromIntegral c) + n -> fail ("invalid hop target: " <> show n) + +instance ToNoun HopTarget where + toNoun = \case + Col c -> A (fromIntegral c) + Roc r c -> C (A (fromIntegral r)) (A (fromIntegral c)) + -- Manual instance to not save the noun/atom in Sag/Sav, because these can be -- megabytes and makes king hang. instance Show Blit where show (Bel ()) = "Bel ()" show (Clr ()) = "Clr ()" - show (Hop x) = "Hop " ++ (show x) + show (Hop t) = "Hop " ++ (show t) show (Klr s) = "Klr " ++ (show s) - show (Lin c) = "Lin " ++ (show c) - show (Mor ()) = "Mor ()" + show (Put c) = "Put " ++ (show c) + show (Nel ()) = "Nel ()" show (Sag path _) = "Sag " ++ (show path) show (Sav path _) = "Sav " ++ (show path) show (Url c) = "Url " ++ (show c) + show (Wyp ()) = "Wyp ()" + -- + show (Lin c) = "Lin " ++ (show c) + show (Mor ()) = "Mor ()" {-| %blip -- TODO diff --git a/pkg/hs/urbit-king/lib/Urbit/Arvo/Event.hs b/pkg/hs/urbit-king/lib/Urbit/Arvo/Event.hs index d2f316a53d..50ef1ac1f0 100644 --- a/pkg/hs/urbit-king/lib/Urbit/Arvo/Event.hs +++ b/pkg/hs/urbit-king/lib/Urbit/Arvo/Event.hs @@ -20,6 +20,7 @@ import Urbit.Arvo.Common (ReOrg(..), reorgThroughNoun) import qualified Crypto.Sign.Ed25519 as Ed import qualified Data.ByteString as BS +import qualified Data.Char as C import qualified Data.ByteString.Char8 as C import qualified Network.HTTP.Types.Method as H @@ -318,19 +319,52 @@ data LegacyBootEvent | Dawn Dawn deriving (Eq, Show) -data ArrowKey = D | L | R | U +data Bolt + = Key Char + | Aro ArrowKey + | Bac () + | Del () + | Hit Word64 Word64 + | Ret () deriving (Eq, Ord, Show) data Belt - = Aro ArrowKey - | Bac () - | Ctl Cord - | Del () - | Met Cord - | Ret () + = Bol Bolt + | Mod Modifier Bolt | Txt Tour deriving (Eq, Ord, Show) +data ArrowKey = D | L | R | U + deriving (Eq, Ord, Show) + +data Modifier = Ctl | Met | Hyp + deriving (Eq, Ord, Show) + +--NOTE required to get the above declarations into reify's type environment +-- see also ghc/ghc#9813 +$(pure []) + +instance FromNoun Bolt where + parseNoun = \case + A c -> pure $ Key $ C.chr $ fromIntegral c + C (A 7955819) _ -> fail "%key not valid bolt tag" + n -> $(deriveFromNounFunc ''Bolt) n + +instance FromNoun Belt where + parseNoun = \case + C (A 7106402) _ -> fail "%bol not valid belt tag" + n -> Bol <$> parseNoun n <|> $(deriveFromNounFunc ''Belt) n + +instance ToNoun Bolt where + toNoun = \case + Key c -> A $ fromIntegral $ C.ord c + n -> $(deriveToNounFunc ''Bolt) n + +instance ToNoun Belt where + toNoun = \case + Bol b -> toNoun b + n -> $(deriveToNounFunc ''Belt) n + data TermEv = TermEvBelt (UD, ()) Belt | TermEvBlew (UD, ()) Word Word @@ -341,7 +375,7 @@ data TermEv deriveNoun ''LegacyBootEvent deriveNoun ''ArrowKey -deriveNoun ''Belt +deriveNoun ''Modifier deriveNoun ''TermEv @@ -392,27 +426,23 @@ instance FromNoun Ev where -- Short Event Names ----------------------------------------------------------- {- - In the case of the user hitting enter, the cause is technically a - terminal event, but we don't display any name because the cause is - really the user. + In the case of user input, the cause is technically a terminal event, + but we don't display any name because the cause is really the user. -} getSpinnerNameForEvent :: Ev -> Maybe Text getSpinnerNameForEvent = \case EvBlip b -> case b of - BlipEvAmes _ -> Just "ames" - BlipEvArvo _ -> Just "arvo" - BlipEvBehn _ -> Just "behn" - BlipEvBoat _ -> Just "boat" - BlipEvHttpClient _ -> Just "iris" - BlipEvHttpServer _ -> Just "eyre" - BlipEvJael _ -> Just "jael" - BlipEvNewt _ -> Just "newt" - BlipEvSync _ -> Just "clay" - BlipEvTerm t | isRet t -> Nothing - BlipEvTerm t -> Just "term" - where - isRet (TermEvBelt _ (Ret ())) = True - isRet _ = False + BlipEvAmes _ -> Just "ames" + BlipEvArvo _ -> Just "arvo" + BlipEvBehn _ -> Just "behn" + BlipEvBoat _ -> Just "boat" + BlipEvHttpClient _ -> Just "iris" + BlipEvHttpServer _ -> Just "eyre" + BlipEvJael _ -> Just "jael" + BlipEvNewt _ -> Just "newt" + BlipEvSync _ -> Just "clay" + BlipEvTerm (TermEvBelt _ _) -> Nothing + BlipEvTerm t -> Just "term" summarizeEvent :: Ev -> Text summarizeEvent ev = diff --git a/pkg/hs/urbit-king/lib/Urbit/Vere/Pier.hs b/pkg/hs/urbit-king/lib/Urbit/Vere/Pier.hs index 5902675703..886da15c8d 100644 --- a/pkg/hs/urbit-king/lib/Urbit/Vere/Pier.hs +++ b/pkg/hs/urbit-king/lib/Urbit/Vere/Pier.hs @@ -333,7 +333,7 @@ pier (serf, log) vSlog startedSig injected = do io $ readTVarIO siteSlog >>= ($ s) logOther "serf" (display $ T.strip $ tankToText tank) - let err = atomically . Term.trace muxed . (<> "\r\n") + let err = atomically . Term.trace muxed (bootEvents, startDrivers) <- do env <- ask siz <- atomically $ Term.curDemuxSize demux diff --git a/pkg/hs/urbit-king/lib/Urbit/Vere/Term.hs b/pkg/hs/urbit-king/lib/Urbit/Vere/Term.hs index 0f86dd2539..ccd4c676df 100644 --- a/pkg/hs/urbit-king/lib/Urbit/Vere/Term.hs +++ b/pkg/hs/urbit-king/lib/Urbit/Vere/Term.hs @@ -45,8 +45,8 @@ import qualified Urbit.Vere.Term.Render as T -- | All stateful data in the printing to stdOutput. data LineState = LineState - { lsLine :: Text - , lsCurPos :: Int + { lsLine :: [(Stye, [Char])] + , lsCurPos :: CurPos , lsSpinTimer :: Maybe (Async ()) , lsSpinCause :: Maybe Text , lsSpinFirstRender :: Bool @@ -54,11 +54,19 @@ data LineState = LineState , lsPrevEndTime :: Wen } +data CurPos = CurPos + { row :: Int + , col :: Int + } + -- | A record used in reading data from stdInput. data ReadData = ReadData { rdBuf :: Ptr Word8 , rdEscape :: Bool , rdBracket :: Bool + , rdMouse :: Bool + , rdMouseBut :: Word8 + , rdMouseCol :: Word8 , rdUTF8 :: ByteString , rdUTF8width :: Int } @@ -165,7 +173,8 @@ leftBracket, rightBracket :: Text leftBracket = "«" rightBracket = "»" -_spin_cool_us, _spin_warm_us, _spin_rate_us, _spin_idle_us :: Integral i => i +_spin_fast_us, _spin_cool_us, _spin_warm_us, _spin_rate_us, _spin_idle_us :: Integral i => i +_spin_fast_us = 100000 _spin_cool_us = 500000 _spin_warm_us = 50000 _spin_rate_us = 250000 @@ -201,6 +210,9 @@ localClient doneSignal = fst <$> mkRAcquire start stop -- to the muxing client. putTMVar tsSizeChange ts) + -- start mouse reporting + putStr "\x1b[?9h" + pWriterThread <- asyncBound (writeTerminal tsWriteQueue spinnerMVar tsizeTVar) @@ -217,7 +229,7 @@ localClient doneSignal = fst <$> mkRAcquire start stop tsReadQueue <- newTQueueIO pReaderThread <- asyncBound - (readTerminal tsReadQueue tsWriteQueue (bell tsWriteQueue)) + (readTerminal tsReadQueue tsWriteQueue tsizeTVar (bell tsWriteQueue)) let client = Client { take = Just <$> asum [ readTQueue tsReadQueue <&> ClientTakeBelt, @@ -238,6 +250,9 @@ localClient doneSignal = fst <$> mkRAcquire start stop -- at shutdown, just leak the file descriptor. cancel pWriterThread + -- stop mouse reporting + putStr "\x1b[?9l" + -- inject one final newline, as we're usually on the prompt. putStr "\r\n" @@ -266,31 +281,50 @@ localClient doneSignal = fst <$> mkRAcquire start stop -- Writes data to the terminal. Both the terminal reading, normal logging, -- and effect handling can all emit bytes which go to the terminal. + --TODO blanks, traces and slogs should only be written into the default + -- terminal session. writeTerminal :: TQueue [Term.Ev] -> TMVar () -> TVar TermSize -> RIO e () writeTerminal q spinner termSizeVar = do currentTime <- io $ now - loop (LineState "" 0 Nothing Nothing True 0 currentTime) + loop + termSizeVar + (LineState [] (CurPos 0 0) Nothing Nothing True 0 currentTime) where writeBlank :: LineState -> RIO e LineState - writeBlank ls = putStr "\r\n" $> ls + writeBlank ls = do + TermSize _ height <- readTVarIO termSizeVar + --NOTE hijack creates a blank line + T.hijack (fromIntegral height) $ pure () + pure ls writeTrace :: LineState -> Text -> RIO e LineState writeTrace ls p = do - putStr "\r" - T.clearLine - putStr p - termRefreshLine ls + TermSize _ height <- readTVarIO termSizeVar + T.hijack (fromIntegral height) $ putStr p + pure ls writeSlog :: LineState -> (Atom, Tank) -> RIO e LineState writeSlog ls slog = do - putStr "\r" - T.clearLine - TermSize width _ <- atomically $ readTVar termSizeVar - -- TODO: Ignoring priority for now. Priority changes the color of, - -- and adds a prefix of '>' to, the output. - let lines = fmap unTape $ wash (WashCfg 0 width) $ tankTree $ snd slog - forM lines $ \line -> putStr (line <> "\r\n") - termRefreshLine ls + TermSize width height <- readTVarIO termSizeVar + T.hijack (fromIntegral height) do + let lines = fmap (pref . unTape) $ + wash (WashCfg 0 width) $ tankTree $ snd slog + T.putCsi 'm' styl + forM (intersperse "\n" lines) $ \line -> putStr line + T.putCsi 'm' [0] + pure ls + where + prio = fromIntegral $ fst slog + maxp = 3 + styl + | prio == 3 = [31] + | prio == 2 = [33] + | prio == 1 = [32] + | otherwise = [90] + pref + | prio > 0 && prio <= maxp = + ((replicate prio '>' ++ replicate (1 + maxp - prio) ' ') ++) + | otherwise = id {- Figure out how long to wait to show the spinner. When we @@ -305,7 +339,7 @@ localClient doneSignal = fst <$> mkRAcquire start stop current <- io $ now delay <- pure $ case mTxt of - Nothing -> 0 + Nothing -> _spin_fast_us Just _ -> if (gap current lsPrevEndTime ^. microSecs) < _spin_idle_us then _spin_warm_us @@ -326,34 +360,41 @@ localClient doneSignal = fst <$> mkRAcquire start stop maybe (pure ()) cancel lsSpinTimer -- We do a final flush of the spinner mvar to ensure we don't -- have a lingering signal which will redisplay the spinner after - -- we call termRefreshLine below. + -- we call termRestoreLine below. atomically $ tryTakeTMVar spinner -- If we ever actually ran the spinner display callback, we need -- to force a redisplay of the command prompt. - ls <- if not lsSpinFirstRender || True - then termRefreshLine ls - else pure ls + if not lsSpinFirstRender + then termRestoreLine ls termSizeVar + else pure () endTime <- io $ now pure $ ls { lsSpinTimer = Nothing, lsPrevEndTime = endTime } execEv :: LineState -> Term.Ev -> RIO e LineState execEv ls = \case - Term.Blits bs -> foldM writeBlit ls bs + Term.Blits bs -> foldM (writeBlit termSizeVar) ls bs Term.Trace p -> writeTrace ls (unCord p) Term.Slog s -> writeSlog ls s Term.Blank -> writeBlank ls Term.Spinr (Just txt) -> doSpin ls (unCord <$> txt) Term.Spinr Nothing -> unspin ls - -- TODO What does this do? - spin :: LineState -> RIO e LineState - spin ls@LineState{..} = do + spin :: TVar TermSize -> LineState -> RIO e LineState + spin ts ls@LineState{..} = do let spinner = (spinners !! lsSpinFrame) ++ case lsSpinCause of Nothing -> "" Just str -> leftBracket ++ str ++ rightBracket + --NOTE even after first render, because cursor might have moved... + if row lsCurPos > 0 + then do + TermSize _ h <- readTVarIO ts + T.cursorMove (fromIntegral h - 1) 0 + else + T.cursorRestore + putStr (spinner <> pack (ANSI.cursorBackwardCode (length spinner))) let newFrame = (lsSpinFrame + 1) `mod` length spinners @@ -362,28 +403,35 @@ localClient doneSignal = fst <$> mkRAcquire start stop , lsSpinFrame = newFrame } - loop :: LineState -> RIO e () - loop ls = do + loop :: TVar TermSize -> LineState -> RIO e () + loop ts ls = do join $ atomically $ asum - [ readTQueue q >>= pure . (foldM execEv ls >=> loop) - , takeTMVar spinner >> pure (spin ls >>= loop) + [ readTQueue q >>= pure . (foldM execEv ls >=> loop ts) + , takeTMVar spinner >> pure (spin ts ls >>= loop ts) ] -- Writes an individual blit to the screen - writeBlit :: LineState -> Blit -> RIO e LineState - writeBlit ls = \case + writeBlit :: TVar TermSize -> LineState -> Blit -> RIO e LineState + writeBlit ts ls = \case Bel () -> T.soundBell $> ls Clr () -> do T.clearScreen - termRefreshLine ls - Hop w -> termShowCursor ls (fromIntegral w) - Klr s -> do ls2 <- termShowClear ls - termShowStub ls2 s - Lin c -> do ls2 <- termShowClear ls - termShowLine ls2 (pack c) - Mor () -> termShowMore ls + T.cursorRestore + pure ls + Hop t -> case t of + Col c -> termShowCursor ls ts 0 (fromIntegral c) + Roc r c -> termShowCursor ls ts (fromIntegral r) (fromIntegral c) + Klr s -> termShowStub ls s + Put c -> termShowLine ls (pack c) + Nel () -> termShowNewline ls Sag path noun -> pure ls Sav path atom -> pure ls Url url -> pure ls + Wyp () -> termShowClear ls + -- + Lin c -> do termShowCursor ls ts 0 0 + termShowClear ls + termShowLine ls (pack c) + Mor () -> termShowNewline ls termRenderDeco :: Deco -> Char termRenderDeco = \case @@ -428,55 +476,88 @@ localClient doneSignal = fst <$> mkRAcquire start stop styled = mconcat [escape, styles, "m", tape, escape, "0m"] - -- Displays and sets styled text as the current line + bareStub :: [Char] -> [(Stye, [Char])] + bareStub c = [(Stye (setToHoonSet mempty) TintNull TintNull, c)] + + -- overwrite substring of base with put, starting at index + overwriteStub :: [(Stye, [Char])] -> Int -> [(Stye, [Char])] -> [(Stye, [Char])] + overwriteStub base index put = + scagStub index base + ++ ( let l = lentStub base in + if index <= l then [] + else bareStub $ take (index - l) [' ',' '..] + ) + ++ put + ++ slagStub (index + lentStub put) base + where + lentStub :: [(Stye, [Char])] -> Int + lentStub s = sum $ map (length . snd) s + + scagStub :: Int -> [(Stye, [Char])] -> [(Stye, [Char])] + scagStub 0 _ = [] + scagStub _ [] = [] + scagStub i ((y,c):s) = + (y, take i c) : scagStub (i - min i (length c)) s + + slagStub :: Int -> [(Stye, [Char])] -> [(Stye, [Char])] + slagStub 0 s = s + slagStub _ [] = [] + slagStub i ((y,c):s) + | i > l = slagStub (i - l) s + | otherwise = (y, drop i c) : s + where l = length c + + -- Displays styled text at the cursor termShowStub :: LineState -> Stub -> RIO e LineState - termShowStub ls (Stub s) = do - let visualLength = sum $ fmap (length . snd) s - let outText = pack $ mconcat $ fmap (uncurry termRenderStubSegment) s - putStr outText - pure ls { lsLine = outText, lsCurPos = visualLength } + termShowStub ls@LineState{lsCurPos, lsLine} (Stub s) = do + putStr $ pack $ mconcat $ fmap (uncurry termRenderStubSegment) s + T.cursorRestore + case row lsCurPos of + 0 -> pure ls { lsLine = overwriteStub lsLine (col lsCurPos) s } + _ -> pure ls -- Moves the cursor to the requested position - termShowCursor :: LineState -> Int -> RIO e LineState - termShowCursor ls@LineState{..} {-line pos)-} newPos = do - if newPos < lsCurPos then do - T.cursorLeft (lsCurPos - newPos) - pure ls { lsCurPos = newPos } - else if newPos > lsCurPos then do - T.cursorRight (newPos - lsCurPos) - pure ls { lsCurPos = newPos } - else - pure ls - - -- Moves the cursor left without any mutation of the LineState. Used only - -- in cursor spinning. - _termSpinnerMoveLeft :: Int -> RIO e () - _termSpinnerMoveLeft = T.cursorLeft + termShowCursor :: LineState -> TVar TermSize -> Int -> Int -> RIO e LineState + termShowCursor ls ts row col = do + TermSize _ h <- readTVarIO ts + T.cursorMove (max 0 (fromIntegral h - row - 1)) col + T.cursorSave + pure ls { lsCurPos = CurPos row col } -- Displays and sets the current line termShowLine :: LineState -> Text -> RIO e LineState - termShowLine ls newStr = do + termShowLine ls@LineState{lsCurPos, lsLine} newStr = do putStr newStr - pure ls { lsLine = newStr, lsCurPos = (length newStr) } + T.cursorRestore + case row lsCurPos of + 0 -> pure ls { lsLine = overwriteStub lsLine (col lsCurPos) (bareStub $ unpack newStr) } + _ -> pure ls termShowClear :: LineState -> RIO e LineState - termShowClear ls = do + termShowClear ls@LineState{lsCurPos} = do putStr "\r" T.clearLine - pure ls { lsLine = "", lsCurPos = 0 } + T.cursorRestore + case row lsCurPos of + 0 -> pure ls { lsLine = [] } + _ -> pure ls -- New Current Line - termShowMore :: LineState -> RIO e LineState - termShowMore ls = do + termShowNewline :: LineState -> RIO e LineState + termShowNewline ls@LineState{lsCurPos} = do putStr "\r\n" - pure ls { lsLine = "", lsCurPos = 0 } + case row lsCurPos of + 0 -> pure ls { lsLine = [], lsCurPos = lsCurPos { col = 0 } } + r -> pure ls { lsCurPos = CurPos (r-1) 0 } - -- Redraw the current LineState, maintaining the current curpos - termRefreshLine :: LineState -> RIO e LineState - termRefreshLine ls@LineState{lsCurPos,lsLine} = do - ls <- termShowClear ls - ls <- termShowLine ls lsLine - termShowCursor ls lsCurPos + -- Redraw the bottom LineState, maintaining the current curpos + termRestoreLine :: LineState -> TVar TermSize -> RIO e () + termRestoreLine ls@LineState{lsLine} ts = do + TermSize _ h <- readTVarIO ts + T.cursorMove (fromIntegral h - 1) 0 + T.clearLine + putStr $ pack $ mconcat $ fmap (uncurry termRenderStubSegment) lsLine + T.cursorRestore -- ring my bell bell :: TQueue [Term.Ev] -> RIO e () @@ -491,9 +572,14 @@ localClient doneSignal = fst <$> mkRAcquire start stop -- A better way to do this would be to get some sort of epoll on stdInput, -- since that's kinda closer to what libuv does? readTerminal :: forall e. HasLogFunc e - => TQueue Belt -> TQueue [Term.Ev] -> (RIO e ()) -> RIO e () - readTerminal rq wq bell = - rioAllocaBytes 1 $ \ buf -> loop (ReadData buf False False mempty 0) + => TQueue Belt + -> TQueue [Term.Ev] + -> TVar TermSize + -> RIO e () + -> RIO e () + readTerminal rq wq ts bell = + rioAllocaBytes 1 $ \ buf + -> loop (ReadData buf False False False 0 0 mempty 0) where loop :: ReadData -> RIO e () loop rd@ReadData{..} = do @@ -513,26 +599,41 @@ localClient doneSignal = fst <$> mkRAcquire start stop if rdEscape then if rdBracket then do case c of - 'A' -> sendBelt $ Aro U - 'B' -> sendBelt $ Aro D - 'C' -> sendBelt $ Aro R - 'D' -> sendBelt $ Aro L + 'A' -> sendBelt $ Bol $ Aro U + 'B' -> sendBelt $ Bol $ Aro D + 'C' -> sendBelt $ Bol $ Aro R + 'D' -> sendBelt $ Bol $ Aro L + 'M' -> pure () _ -> bell - loop rd { rdEscape = False, rdBracket = False} + rd <- case c of + 'M' -> pure rd { rdMouse = True } + _ -> pure rd + loop rd { rdEscape = False, rdBracket = False } else if isAsciiLower c then do - sendBelt $ Met $ Cord $ pack [c] - loop rd { rdEscape = False } - else if c == '.' then do - sendBelt $ Met $ Cord "dot" + sendBelt $ Mod Met $ Key c loop rd { rdEscape = False } else if w == 8 || w == 127 then do - sendBelt $ Met $ Cord "bac" + sendBelt $ Mod Met $ Bac () loop rd { rdEscape = False } else if c == '[' || c == '0' then do loop rd { rdBracket = True } else do bell loop rd { rdEscape = False } + else if rdMouse then + if rdMouseBut == 0 then do + loop rd { rdMouseBut = w - 31 } + else if rdMouseCol == 0 then do + loop rd { rdMouseCol = w - 32 } + else do + if rdMouseBut == 1 then do + let rdMouseRow = w - 32 + TermSize _ h <- readTVarIO ts + sendBelt $ Bol $ Hit + (fromIntegral h - fromIntegral rdMouseRow) + (fromIntegral rdMouseCol - 1) + else do pure () + loop rd { rdMouse = False, rdMouseBut = 0, rdMouseCol = 0 } else if rdUTF8width /= 0 then do -- continue reading into the utf8 accumulation buffer rd@ReadData{..} <- pure rd { rdUTF8 = snoc rdUTF8 w } @@ -543,30 +644,31 @@ localClient doneSignal = fst <$> mkRAcquire start stop error "empty utf8 accumulation buffer" Just (c, bytes) | bytes /= rdUTF8width -> error "utf8 character size mismatch?!" - Just (c, bytes) -> sendBelt $ Txt $ Tour $ [c] + Just (c, bytes) -> sendBelt $ Bol $ Key c loop rd { rdUTF8 = mempty, rdUTF8width = 0 } else if w >= 32 && w < 127 then do - sendBelt $ Txt $ Tour $ [c] + sendBelt $ Bol $ Key c loop rd else if w == 0 then do bell loop rd else if w == 8 || w == 127 then do - sendBelt $ Bac () + sendBelt $ Bol $ Bac () loop rd else if w == 13 then do - sendBelt $ Ret () + sendBelt $ Bol $ Ret () loop rd else if w == 3 then do -- ETX (^C) logInfo $ "Ctrl-c interrupt" atomically $ do - writeTQueue wq [Term.Trace "interrupt\r\n"] - writeTQueue rq $ Ctl $ Cord "c" + writeTQueue wq [Term.Trace "interrupt"] + writeTQueue rq $ Mod Ctl $ Key 'c' loop rd else if w <= 26 then do - case pack [BS.w2c (w + 97 - 1)] of - c -> do sendBelt $ Ctl $ Cord c + case BS.w2c (w + 97 - 1) of + 'd' -> atomically doneSignal + c -> do sendBelt $ Mod Ctl $ Key c loop rd else if w == 27 then do loop rd { rdEscape = True } @@ -643,7 +745,7 @@ term env (tsize, Client{..}) plan stat serfSIGINT = runTerm atomically take >>= \case Nothing -> pure () Just (ClientTakeBelt b) -> do - when (b == Ctl (Cord "c")) $ do + when (b == Mod Ctl (Key 'c')) $ do io serfSIGINT let beltEv = EvBlip $ BlipEvTerm $ TermEvBelt (UD 1, ()) $ b let beltFailed _ = pure () diff --git a/pkg/hs/urbit-king/lib/Urbit/Vere/Term/API.hs b/pkg/hs/urbit-king/lib/Urbit/Vere/Term/API.hs index 7e65d49e5e..d6b6fde609 100644 --- a/pkg/hs/urbit-king/lib/Urbit/Vere/Term/API.hs +++ b/pkg/hs/urbit-king/lib/Urbit/Vere/Term/API.hs @@ -22,7 +22,7 @@ import Urbit.TermSize Input Event for terminal driver: %blits -- list of blits from arvo. - %trace -- stderr line from runtime. + %trace -- stderr line from runtime (without trailing newline). %slog -- nock worker logging with priority %blank -- print a blank line %spinr -- Start or stop the spinner diff --git a/pkg/hs/urbit-king/lib/Urbit/Vere/Term/Logic.hs b/pkg/hs/urbit-king/lib/Urbit/Vere/Term/Logic.hs index 770742a684..748b3fa5a3 100644 --- a/pkg/hs/urbit-king/lib/Urbit/Vere/Term/Logic.hs +++ b/pkg/hs/urbit-king/lib/Urbit/Vere/Term/Logic.hs @@ -39,11 +39,11 @@ data Ev = EvLine Text | EvSlog (Atom, Tank) | EvSpin SpinnerState - | EvMove Word + | EvMove (Word, Word) | EvBell | EvDraw | EvEdit Text - | EvMore + | EvNewl deriving (Show) data Ef @@ -62,7 +62,7 @@ data History data St = St { sHistory :: !(Seq History) , sLine :: !Text - , sCurPos :: !Word + , sCurPos :: !(Word, Word) , sSpinner :: !SpinnerState } deriving (Show) @@ -70,10 +70,10 @@ data St = St -------------------------------------------------------------------------------- init :: St -init = St mempty "" 0 Nothing +init = St mempty "" (0, 0) Nothing {-| - When we process `EvMore`, we need to append a newline to the end of + When we process `EvNewl`, we need to append a newline to the end of the current line. During normal play, the ENTER key inserts the newline for us, so we need to recreate that newline when we rebuild the state for a new terminal connection. @@ -83,15 +83,17 @@ step st@St{..} = \case EvLine t -> st & recordText t EvSlog s -> st & recordSlog s EvSpin s -> st { sSpinner = s } - EvMove w -> st { sCurPos = min w (word $ length sLine) } - EvEdit t -> st { sLine = t, sCurPos = word (length t) } - EvMore -> st { sLine = "", sCurPos = 0 } & recordText (sLine <> "\n") + EvMove p -> st { sCurPos = p } EvBell -> st EvDraw -> st + EvEdit t | (0, _) <- sCurPos -> st { sLine = t } + | otherwise -> st + EvNewl | (0, _) <- sCurPos -> + st { sLine = "", sCurPos = (0, 0) } + & recordText (sLine <> "\n") + | otherwise -> + st { sCurPos = (fst sCurPos - 1, 0) } where - word :: Integral i => i -> Word - word = fromIntegral - recordText :: Text -> St -> St recordText !t st@St{..} = st { sHistory = trim (sHistory |> (HistoryText t)) @@ -111,8 +113,10 @@ drawState :: St -> [Ev] drawState St{..} = hist <> out <> cur <> spin where hist = drawHistory <$> toList sHistory - out = if null sLine then [] else [EvEdit sLine] - cur = if 0 == sCurPos then [] else [EvMove $ fromIntegral $ sCurPos] + out | null sLine = [] + | otherwise = [EvEdit sLine] + cur | (0, _) <- sCurPos = [] + | otherwise = [EvMove sCurPos] spin = maybe [] (singleton . EvSpin . Just) sSpinner drawHistory (HistoryText t) = EvLine t @@ -123,12 +127,13 @@ drawState St{..} = hist <> out <> cur <> spin fromBlit :: Arvo.Blit -> Maybe Ev fromBlit = \case - Arvo.Hop w -> Just $ EvMove $ fromIntegral w - Arvo.Bel () -> Just EvBell - Arvo.Clr () -> Just EvDraw - Arvo.Lin s -> Just $ EvEdit (pack s) - Arvo.Mor () -> Just EvMore - _ -> Nothing + Arvo.Hop (Arvo.Col c) -> Just $ EvMove (0, fromIntegral c) + Arvo.Hop (Arvo.Roc r c) -> Just $ EvMove (fromIntegral r, fromIntegral c) + Arvo.Bel () -> Just EvBell + Arvo.Clr () -> Just EvDraw + Arvo.Put s -> Just $ EvEdit (pack s) + Arvo.Nel () -> Just EvNewl + _ -> Nothing toCause :: Maybe Cord -> SpinnerCause toCause Nothing = User @@ -148,12 +153,12 @@ fromTermEv = \case toTermEv :: Ev -> Term.Ev toTermEv = \case - EvLine "" -> Term.Blank - EvLine t -> Term.Trace (Cord t) - EvSlog s -> Term.Slog s - EvSpin s -> Term.Spinr (fromCause <$> s) - EvMove w -> Term.Blits [Arvo.Hop $ fromIntegral w] - EvBell -> Term.Blits [Arvo.Bel ()] - EvDraw -> Term.Blits [Arvo.Clr ()] - EvEdit t -> Term.Blits [Arvo.Lin $ unpack t] - EvMore -> Term.Blits [Arvo.Mor ()] + EvLine "" -> Term.Blank + EvLine t -> Term.Trace (Cord t) + EvSlog s -> Term.Slog s + EvSpin s -> Term.Spinr (fromCause <$> s) + EvMove (r, c) -> Term.Blits [Arvo.Hop $ Arvo.Roc (fromIntegral r) (fromIntegral c)] + EvBell -> Term.Blits [Arvo.Bel ()] + EvDraw -> Term.Blits [Arvo.Clr ()] + EvEdit t -> Term.Blits [Arvo.Put $ unpack t] + EvNewl -> Term.Blits [Arvo.Nel ()] diff --git a/pkg/hs/urbit-king/lib/Urbit/Vere/Term/Render.hs b/pkg/hs/urbit-king/lib/Urbit/Vere/Term/Render.hs index f0982dfbe3..8290032594 100644 --- a/pkg/hs/urbit-king/lib/Urbit/Vere/Term/Render.hs +++ b/pkg/hs/urbit-king/lib/Urbit/Vere/Term/Render.hs @@ -4,9 +4,12 @@ module Urbit.Vere.Term.Render ( clearScreen , clearLine - , cursorRight - , cursorLeft , soundBell + , cursorMove + , cursorSave + , cursorRestore + , putCsi + , hijack ) where import Urbit.Prelude @@ -25,8 +28,27 @@ clearLine = liftIO $ ANSI.clearLine soundBell :: MonadIO m => m () soundBell = liftIO $ putStr "\a" -cursorLeft :: MonadIO m => Int -> m () -cursorLeft = liftIO . ANSI.cursorBackward +--NOTE top-left-0-based coordinates +cursorMove :: MonadIO m => Int -> Int -> m () +cursorMove r c = liftIO $ ANSI.setCursorPosition r c -cursorRight :: MonadIO m => Int -> m () -cursorRight = liftIO . ANSI.cursorForward +cursorSave :: MonadIO m => m () +cursorSave = liftIO ANSI.saveCursor + +cursorRestore :: MonadIO m => m () +cursorRestore = liftIO ANSI.restoreCursor + +putCsi :: MonadIO m => Char -> [Int] -> m () +putCsi c a = liftIO do + putStr "\x1b[" + putStr $ pack $ mconcat $ intersperse ";" (fmap show a) + putStr $ pack [c] + +hijack :: MonadIO m => Int -> IO () -> m () +hijack h d = liftIO do + putCsi 'r' [1, h-1] -- set scroll region to exclude bottom line + putCsi 'S' [1] -- scroll up one line + cursorMove (h-2) 0 -- move cursor to empty space + d + putCsi 'r' [] -- reset scroll region + cursorRestore -- restory cursor position diff --git a/pkg/interface/dbug/src/css/custom.css b/pkg/interface/dbug/src/css/custom.css index 112ad47eb8..00bcd4f4df 100644 --- a/pkg/interface/dbug/src/css/custom.css +++ b/pkg/interface/dbug/src/css/custom.css @@ -158,6 +158,7 @@ a { @media all and (prefers-color-scheme: dark) { body { background-color: #333; + color: white; } .bg-black-d { background-color: black; diff --git a/pkg/interface/webterm/.eslintrc.js b/pkg/interface/webterm/.eslintrc.js new file mode 100644 index 0000000000..8f36013edf --- /dev/null +++ b/pkg/interface/webterm/.eslintrc.js @@ -0,0 +1,23 @@ +module.exports = exports = { + "rules": { + "spaced-comment": 0, + }, + "extends": [ + "eslint:recommended", + "plugin:import/errors", + "plugin:react/recommended", + ], + "settings": { + "react": { + "version": "detect" + }, + "import/resolver": { + typescript: {} // this loads /tsconfig.json to eslint + }, + }, + "env": { + "browser": true, + "es6": true + }, + "plugins": ["import", "react-hooks"] +} diff --git a/pkg/interface/webterm/.nvmrc b/pkg/interface/webterm/.nvmrc new file mode 100644 index 0000000000..0b77208aee --- /dev/null +++ b/pkg/interface/webterm/.nvmrc @@ -0,0 +1 @@ +16.14.0 \ No newline at end of file diff --git a/pkg/interface/webterm/App.tsx b/pkg/interface/webterm/App.tsx new file mode 100644 index 0000000000..211066b74e --- /dev/null +++ b/pkg/interface/webterm/App.tsx @@ -0,0 +1,94 @@ +import React, { + useCallback, useEffect +} from 'react'; + +import useTermState from './state'; +import { useDark } from './lib/useDark'; +import api from './api'; + +import { _dark, _light } from '@tlon/indigo-react'; + +import 'xterm/css/xterm.css'; + +import { + scrySessions +} from '@urbit/api/term'; + +import { ThemeProvider } from 'styled-components'; +import { Tabs } from './Tabs'; +import Buffer from './Buffer'; +import { DEFAULT_SESSION } from './constants'; +import { showSlog } from './lib/blit'; +import { InfoButton } from './InfoButton'; + +const initSessions = async () => { + const response = await api.scry(scrySessions()); + + useTermState.getState().set((state) => { + state.names = response.sort(); + }); +}; + +export default function TermApp() { + const { names, selected } = useTermState(); + const dark = useDark(); + + const setupSlog = useCallback(() => { + console.log('slog: setting up...'); + let available = false; + const slog = new EventSource('/~_~/slog', { withCredentials: true }); + + slog.onopen = () => { + console.log('slog: opened stream'); + available = true; + }; + + slog.onmessage = (e) => { + const session = useTermState.getState().sessions[DEFAULT_SESSION]; + if (!session) { + console.log('slog: default session mia!', 'msg:', e.data); + console.log(Object.keys(useTermState.getState().sessions), session); + return; + } + showSlog(session.term, e.data); + }; + + slog.onerror = (e) => { + console.error('slog: eventsource error:', e); + if (available) { + window.setTimeout(() => { + if (slog.readyState !== EventSource.CLOSED) { + return; + } + console.log('slog: reconnecting...'); + setupSlog(); + }, 10000); + } + }; + + useTermState.getState().set((state) => { + state.slogstream = slog; + }); + }, []); + + useEffect(() => { + initSessions(); + setupSlog(); + }, []); + + return ( + <> + +
+ + +
+
+ {names.map((name) => { + return ; + })} +
+
+ + ); +} diff --git a/pkg/interface/webterm/Buffer.tsx b/pkg/interface/webterm/Buffer.tsx new file mode 100644 index 0000000000..1ee512f1a6 --- /dev/null +++ b/pkg/interface/webterm/Buffer.tsx @@ -0,0 +1,350 @@ +import { Terminal, ITerminalOptions } from 'xterm'; +import { FitAddon } from 'xterm-addon-fit'; +import { debounce } from 'lodash'; +import bel from './lib/bel'; +import api from './api'; + +import { + Belt, pokeTask, pokeBelt +} from '@urbit/api/term'; +import { Session } from './state'; +import { useCallback, useEffect, useRef } from 'react'; +import useTermState from './state'; +import React from 'react'; +import { Box, Col } from '@tlon/indigo-react'; +import { makeTheme } from './lib/theme'; +import { showBlit, csi, hasBell } from './lib/blit'; +import { DEFAULT_SESSION, RESIZE_DEBOUNCE_MS, RESIZE_THRESHOLD_PX } from './constants'; +import { retry } from './lib/retry'; + +const termConfig: ITerminalOptions = { + logLevel: 'warn', + // + convertEol: true, + // + rows: 24, + cols: 80, + scrollback: 10000, + // + fontFamily: '"Source Code Pro", "Roboto mono", "Courier New", monospace', + fontWeight: 400, + // NOTE theme colors configured dynamically + // + bellStyle: 'sound', + bellSound: bel, + // + // allows text selection by holding modifier (option, or shift) + macOptionClickForcesSelection: true, + // prevent insertion of simulated arrow keys on-altclick + altClickMovesCursor: false +}; + +const readInput = (term: Terminal, e: string): Belt[] => { + const belts: Belt[] = []; + let strap = ''; + + while (e.length > 0) { + let c = e.charCodeAt(0); + + // text input + // + if (c >= 32 && c !== 127) { + strap += e[0]; + e = e.slice(1); //TODO revisit wrt (list @c) & unicode characters + continue; + } else if ('' !== strap) { + belts.push({ txt: strap.split('') }); + strap = ''; + } + + // special keys/characters + // + if (0 === c) { + term.write('\x07'); // bel + } else if (8 === c || 127 === c) { + belts.push({ bac: null }); + } else if (13 === c) { + belts.push({ ret: null }); + } else if (c <= 26) { + const k = String.fromCharCode(96 + c); + //NOTE prevent remote shut-downs + if ('d' !== k) { + belts.push({ mod: { mod: 'ctl', key: k } }); + } + } + + // escape sequences + // + if (27 === c) { // ESC + e = e.slice(1); + c = e.charCodeAt(0); + if (91 === c || 79 === c) { // [ or O + e = e.slice(1); + c = e.charCodeAt(0); + /* eslint-disable max-statements-per-line */ + switch (c) { + case 65: belts.push({ aro: 'u' }); break; + case 66: belts.push({ aro: 'd' }); break; + case 67: belts.push({ aro: 'r' }); break; + case 68: belts.push({ aro: 'l' }); break; + // + case 77: { + const m = e.charCodeAt(1) - 31; + if (1 === m) { + const c = e.charCodeAt(2) - 32; + const r = e.charCodeAt(3) - 32; + belts.push({ hit: { y: r - 1, x: c - 1 } }); + } + e = e.slice(3); + break; + } + // + default: term.write('\x07'); break; // bel + } + } else if (c >= 97 && c <= 122) { // a <= c <= z + belts.push({ mod: { mod: 'met', key: e[0] } }); + } else if (c === 46) { // . + belts.push({ mod: { mod: 'met', key: '.' } }); + } else if (c === 8 || c === 127) { + belts.push({ mod: { mod: 'met', key: { bac: null } } }); + } else { + term.write('\x07'); break; // bel + } + } + + e = e.slice(1); + } + if ('' !== strap) { + if (1 === strap.length) { + belts.push(strap); + } else { + belts.push({ txt: strap.split('') }); + } + strap = ''; + } + return belts; +}; + +const onResize = async (name: string, session: Session) => { + if (session) { + session.fit.fit(); + useTermState.getState().set((state) => { + state.sessions[name].pending++; + }); + api.poke(pokeTask(name, { blew: { w: session.term.cols, h: session.term.rows } })).then(() => { + useTermState.getState().set((state) => { + state.sessions[name].pending--; + }); + }); + } +}; + +const onInput = (name: string, session: Session, e: string) => { + if (!session) { + return; + } + const term = session.term; + const belts = readInput(term, e); + belts.forEach((b) => { + useTermState.getState().set((state) => { + state.sessions[name].pending++; + }); + api.poke(pokeBelt(name, b)).then(() => { + useTermState.getState().set((state) => { + state.sessions[name].pending--; + }); + }); + }); +}; + +interface BufferProps { + name: string, + selected: boolean, + dark: boolean, +} + +export default function Buffer({ name, selected, dark }: BufferProps) { + const containerRef = useRef(null); + + const session: Session = useTermState(s => s.sessions[name]); + + const initSession = useCallback(async (name: string, dark: boolean) => { + console.log('setting up', name === DEFAULT_SESSION ? 'default' : name); + + // set up xterm terminal + // + const term = new Terminal(termConfig); + term.options.theme = makeTheme(dark); + const fit = new FitAddon(); + term.loadAddon(fit); + fit.fit(); + term.focus(); + + // start mouse reporting + // + term.write(csi('?9h')); + + const ses: Session = { + term, + fit, + hasBell: false, + pending: 0, + subscriptionId: null + }; + + // set up event handlers + // + term.attachCustomKeyEventHandler((e: KeyboardEvent) => { + //NOTE ctrl+shift keypresses never make it into term.onData somehow, + // so we handle them specially ourselves. + // we may be able to remove this once xterm.js fixes #3382 & co. + if (e.shiftKey + && e.ctrlKey + && e.type === 'keydown' + && e.key.length === 1 + ) { + api.poke(pokeBelt(name, { mod: { mod: 'ctl', key: e.key } })); + return false; + } + return true; + }); + term.onData(e => onInput(name, ses, e)); + term.onBinary(e => onInput(name, ses, e)); + + // open subscription + // + const initSubscription = async () => { + const subscriptionId = await api.subscribe({ + app: 'herm', path: '/session/' + name + '/view', + event: (e) => { + showBlit(ses.term, e); + //NOTE getting selected from state because selected prop is stale + if (hasBell(e) && (useTermState.getState().selected !== name)) { + useTermState.getState().set((state) => { + state.sessions[name].hasBell = true; + }); + } + //TODO should handle %bye on this higher level though, for deletion + }, + err: (e, id) => { + console.log(`subscription error, id ${id}:`, e); + }, + quit: async () => { // quit + console.error('quit, reconnecting...'); + try { + const newSubscriptionId = await retry(initSubscription, () => { + console.log('attempting to reconnect ...'); + }, 5); + useTermState.getState().set((state) => { + state.sessions[name].subscriptionId = newSubscriptionId; + }); + } catch (error) { + console.log('unable to reconnect', error); + } + } + }); + + return subscriptionId; + }; + + ses.subscriptionId = await initSubscription(); + + useTermState.getState().set((state) => { + state.sessions[name] = ses; + }); + }, []); + + const shouldResize = useCallback(() => { + if(!session) { + return false; + } + + const containerHeight = document.querySelector('.buffer-container')?.clientHeight || Infinity; + const terminalHeight = session.term.element?.clientHeight || 0; + + return (containerHeight - terminalHeight) >= RESIZE_THRESHOLD_PX; + }, [session]); + + const onSelect = useCallback(async () => { + if (session && selected && shouldResize()) { + session.fit.fit(); + await api.poke(pokeTask(name, { blew: { w: session.term.cols, h: session.term.rows } })); + session.term.focus(); + } + }, [session?.term, selected]); + + // Effects + // init session + useEffect(() => { + if(session) { + return; + } + + initSession(name, dark); + }, [name]); + + // attach to DOM when ref is available + useEffect(() => { + if(session && containerRef.current && !session.term.element) { + session.term.open(containerRef.current); + } + }, [session, containerRef]); + + // initialize resize listeners + // + useEffect(() => { + if(!session) { + return; + } + + // TODO: use ResizeObserver for improved performance? + const debouncedResize = debounce(() => onResize(name, session), RESIZE_DEBOUNCE_MS); + window.addEventListener('resize', debouncedResize); + + return () => { + window.removeEventListener('resize', debouncedResize); + }; + }, [session]); + + // on dark mode change, change terminals' theme + // + useEffect(() => { + const theme = makeTheme(dark); + if (session) { + session.term.options.theme = theme; + } + if (containerRef.current) { + containerRef.current.style.backgroundColor = theme.background || ''; + } + }, [session, dark]); + + // On select, resize, focus, and poke herm with updated cols and rows + useEffect(() => { + onSelect(); + }, [onSelect]); + + return ( + !session && !selected ? +

Loading...

+ : + + + + + ); +} diff --git a/pkg/interface/webterm/InfoButton.tsx b/pkg/interface/webterm/InfoButton.tsx new file mode 100644 index 0000000000..b0ba22953e --- /dev/null +++ b/pkg/interface/webterm/InfoButton.tsx @@ -0,0 +1,24 @@ +import React, { useCallback } from 'react'; +import { Icon } from '@tlon/indigo-react'; +import { useDetectOS } from './lib/useDetectOS'; + +export const InfoButton = () => { + const { isMacOS } = useDetectOS(); + + const onInfoClick = useCallback(() => { + const key = isMacOS ? 'alt' : 'shift'; + + alert(`To select text in the terminal, hold down the ${key} key.`); + }, [isMacOS]); + + return ( + <> + + + ); +}; diff --git a/pkg/interface/webterm/Spinner.tsx b/pkg/interface/webterm/Spinner.tsx new file mode 100644 index 0000000000..3dc9392a17 --- /dev/null +++ b/pkg/interface/webterm/Spinner.tsx @@ -0,0 +1,55 @@ +import useIsMounted from './lib/useIsMounted'; +import React from 'react'; +import { useEffect, useState } from 'react'; + +const DELAY_MS = 1000; +const FRAME_MS = 250; +const CHARS = '|/-\\'; + +const Spinner = () => { + const [index, setIndex] = useState(0); + const [intervalTimer, setIntervalTimer] = useState | undefined>(); + const isMounted = useIsMounted(); + + useEffect(() => { + setIntervalTimer( + setInterval(() => { + if (isMounted()) { + setIndex(idx => idx === CHARS.length - 1 ? 0 : idx + 1); + } + }, FRAME_MS) + ); + + return () => { + if (intervalTimer) { + clearInterval(intervalTimer); + } + }; + }, []); + + return  {CHARS[index]}; +}; + +export const DelayedSpinner = () => { + const [showSpinner, setShowSpinner] = useState(false); + const [delayTimer, setDelayTimer] = useState | undefined>(); + const isMounted = useIsMounted(); + + useEffect(() => { + setDelayTimer( + setTimeout(() => { + if (isMounted()) { + setShowSpinner(true); + } + }, DELAY_MS) + ); + + return () => { + if (delayTimer) { + clearTimeout(delayTimer); + } + }; + }, []); + + return showSpinner ? : null; +}; diff --git a/pkg/interface/webterm/Tab.tsx b/pkg/interface/webterm/Tab.tsx new file mode 100644 index 0000000000..85a08ef5e8 --- /dev/null +++ b/pkg/interface/webterm/Tab.tsx @@ -0,0 +1,55 @@ +import { DEFAULT_SESSION } from './constants'; +import React, { useCallback, useEffect } from 'react'; +import useTermState, { Session } from './state'; +import api from './api'; +import { pokeTask } from '@urbit/api/term'; +import { DelayedSpinner as Spinner } from './Spinner'; + +interface TabProps { + session: Session; + name: string; +} + +export const Tab = ( { session, name }: TabProps ) => { + const isSelected = useTermState().selected === name; + + const onClick = () => { + useTermState.getState().set((state) => { + state.selected = name; + state.sessions[name].hasBell = false; + }); + }; + + const onDelete = useCallback(async (e) => { + e.stopPropagation(); + + // clean up subscription + if(session && session.subscriptionId) { + await api.unsubscribe(session.subscriptionId); + } + + // DELETE + await api.poke(pokeTask(name, { shut: null })); + + // remove from zustand + useTermState.getState().set((state) => { + if (state.selected === name) { + state.selected = DEFAULT_SESSION; + } + state.names = state.names.filter(n => n !== name); + delete state.sessions[name]; + }); + }, [session]); + + return ( + + ); +}; diff --git a/pkg/interface/webterm/Tabs.tsx b/pkg/interface/webterm/Tabs.tsx new file mode 100644 index 0000000000..ed583635a3 --- /dev/null +++ b/pkg/interface/webterm/Tabs.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import useTermState from './state'; +import { Tab } from './Tab'; +import { useAddSession } from './lib/useAddSession'; +import { Icon } from '@tlon/indigo-react'; + +export const Tabs = () => { + const { sessions, names } = useTermState(); + const addSession = useAddSession(); + + return ( +
+ {names.map((n, i) => { + return ( + + ); + })} + +
+ ); +}; diff --git a/pkg/interface/webterm/app.tsx b/pkg/interface/webterm/app.tsx deleted file mode 100644 index c8de116fb3..0000000000 --- a/pkg/interface/webterm/app.tsx +++ /dev/null @@ -1,475 +0,0 @@ -/* eslint-disable max-lines */ -import React, { - useEffect, - useRef, - useCallback -} from 'react'; - -import useTermState from './state'; -import { useDark } from './join'; -import api from './api'; - -import { Terminal, ITerminalOptions, ITheme } from 'xterm'; -import { FitAddon } from 'xterm-addon-fit'; -import { saveAs } from 'file-saver'; - -import { Box, Col, Reset, _dark, _light } from '@tlon/indigo-react'; - -import 'xterm/css/xterm.css'; - -import { - Belt, Blit, Stye, Stub, Tint, Deco, - pokeTask, pokeBelt -} from '@urbit/api/term'; - -import bel from './lib/bel'; -import { ThemeProvider } from 'styled-components'; - -type TermAppProps = { - ship: string; -} - -const makeTheme = (dark: boolean): ITheme => { - let fg, bg: string; - if (dark) { - fg = 'white'; - bg = 'rgb(26,26,26)'; - } else { - fg = 'black'; - bg = 'white'; - } - // TODO indigo colors. - // we can't pluck these from ThemeContext because they have transparency. - // technically xterm supports transparency, but it degrades performance. - return { - foreground: fg, - background: bg, - brightBlack: '#7f7f7f', // NOTE slogs - cursor: fg, - cursorAccent: bg, - selection: fg - }; -}; - -const termConfig: ITerminalOptions = { - logLevel: 'warn', - // - convertEol: true, - // - rows: 24, - cols: 80, - scrollback: 10000, - // - fontFamily: '"Source Code Pro", "Roboto mono", "Courier New", monospace', - fontWeight: 400, - // NOTE theme colors configured dynamically - // - bellStyle: 'sound', - bellSound: bel, - // - // allows text selection by holding modifier (option, or shift) - macOptionClickForcesSelection: true, - // prevent insertion of simulated arrow keys on-altclick - altClickMovesCursor: false -}; - -const csi = (cmd: string, ...args: number[]) => { - return '\x1b[' + args.join(';') + cmd; -}; - -const tint = (t: Tint) => { - switch (t) { - case null: return '9'; - case 'k': return '0'; - case 'r': return '1'; - case 'g': return '2'; - case 'y': return '3'; - case 'b': return '4'; - case 'm': return '5'; - case 'c': return '6'; - case 'w': return '7'; - default: return `8;2;${t.r%256};${t.g%256};${t.b%256}`; - } -}; - -const stye = (s: Stye) => { - let out = ''; - - // text decorations - // - if (s.deco.length > 0) { - out += s.deco.reduce((decs: number[], deco: Deco) => { - /* eslint-disable max-statements-per-line */ - switch (deco) { - case null: decs.push(0); return decs; - case 'br': decs.push(1); return decs; - case 'un': decs.push(4); return decs; - case 'bl': decs.push(5); return decs; - default: console.log('weird deco', deco); return decs; - } - }, []).join(';'); - } - - // background color - // - if (s.back !== null) { - if (out !== '') { - out += ';'; - } - out += '4'; - out += tint(s.back); - } - - // foreground color - // - if (s.fore !== null) { - if (out !== '') { - out += ';'; - } - out += '3'; - out += tint(s.fore); - } - - if (out === '') { - return out; - } - return '\x1b[' + out + 'm'; -}; - -const showBlit = (term: Terminal, blit: Blit) => { - let out = ''; - - if ('bel' in blit) { - out += '\x07'; - } else if ('clr' in blit) { - term.clear(); - out += csi('u'); - } else if ('hop' in blit) { - if (typeof blit.hop === 'number') { - out += csi('H', term.rows, blit.hop + 1); - } else { - out += csi('H', term.rows - blit.hop.r, blit.hop.c + 1); - } - out += csi('s'); // save cursor position - } else if ('put' in blit) { - out += blit.put.join(''); - out += csi('u'); - } else if ('klr' in blit) { - //TODO remove for new backend - { - out += csi('H', term.rows, 1); - out += csi('K'); - } - out += blit.klr.reduce((lin: string, p: Stub) => { - lin += stye(p.stye); - lin += p.text.join(''); - lin += csi('m', 0); - return lin; - }, ''); - out += csi('u'); - } else if ('nel' in blit) { - out += '\n'; - } else if ('sag' in blit || 'sav' in blit) { - const sav = ('sag' in blit) ? blit.sag : blit.sav; - const name = sav.path.split('/').slice(-2).join('.'); - const buff = Buffer.from(sav.file, 'base64'); - const blob = new Blob([buff], { type: 'application/octet-stream' }); - saveAs(blob, name); - } else if ('url' in blit) { - window.open(blit.url); - } else if ('wyp' in blit) { - out += '\r' + csi('K'); - out += csi('u'); - // - //TODO remove for new backend - } else if ('lin' in blit) { - out += csi('H', term.rows, 1); - out += csi('K'); - out += blit.lin.join(''); - } else if ('mor' in blit) { - out += '\n'; - } else { - console.log('weird blit', blit); - } - - term.write(out); -}; - -// NOTE should generally only be passed the default terminal session -const showSlog = (term: Terminal, slog: string) => { - // set scroll region to exclude the bottom line, - // scroll up one line, - // move cursor to start of the newly created whitespace, - // set text to grey, - // print the slog, - // restore color, scroll region, and cursor. - // - term.write(csi('r', 1, term.rows - 1) - + csi('S', 1) - + csi('H', term.rows - 1, 1) - + csi('m', 90) - + slog - + csi('m', 0) - + csi('r') - + csi('u')); -}; - -const readInput = (term: Terminal, e: string): Belt[] => { - const belts: Belt[] = []; - let strap = ''; - - while (e.length > 0) { - let c = e.charCodeAt(0); - - // text input - // - if (c >= 32 && c !== 127) { - strap += e[0]; - e = e.slice(1); - continue; - } else if ('' !== strap) { - belts.push({ txt: strap.split('') }); - strap = ''; - } - - // special keys/characters - // - if (0 === c) { - term.write('\x07'); // bel - } else if (8 === c || 127 === c) { - belts.push({ bac: null }); - } else if (13 === c) { - belts.push({ ret: null }); - } else if (c <= 26) { - let k = String.fromCharCode(96 + c); - //NOTE prevent remote shut-downs - if ('d' !== k) { - belts.push({ ctl: k }); - //TODO for new backend - // belts.push({ mod: { mod: 'ctl', key: k } }); - } - } - - // escape sequences - // - if (27 === c) { // ESC - e = e.slice(1); - c = e.charCodeAt(0); - if (91 === c || 79 === c) { // [ or O - e = e.slice(1); - c = e.charCodeAt(0); - /* eslint-disable max-statements-per-line */ - switch (c) { - case 65: belts.push({ aro: 'u' }); break; - case 66: belts.push({ aro: 'd' }); break; - case 67: belts.push({ aro: 'r' }); break; - case 68: belts.push({ aro: 'l' }); break; - // - case 77: { - const m = e.charCodeAt(1) - 31; - if (1 === m) { - const c = e.charCodeAt(2) - 32; - const r = e.charCodeAt(3) - 32; - //TODO re-enable for new backend - // belts.push({ hit: { r: term.rows - r, c: c - 1 } }); - } - e = e.slice(3); - break; - } - // - default: term.write('\x07'); break; // bel - } - } else if (c >= 97 && c <= 122) { // a <= c <= z - belts.push({ mod: { mod: 'met', key: e[0] } }); - } else if (c === 46) { // . - belts.push({ mod: { mod: 'met', key: '.' } }); - } else if (c === 8 || c === 127) { - belts.push({ mod: { mod: 'met', key: { bac: null } } }); - } else { - term.write('\x07'); break; // bel - } - } - - e = e.slice(1); - } - if ('' !== strap) { - belts.push({ txt: strap.split('') }); - strap = ''; - } - return belts; -}; - -export default function TermApp(props: TermAppProps) { - const container = useRef(null); - // TODO allow switching of selected - const { sessions, selected, slogstream, set } = useTermState(); - const session = sessions[selected]; - const dark = useDark(); - - const setupSlog = useCallback(() => { - console.log('slog: setting up...'); - let available = false; - const slog = new EventSource('/~_~/slog', { withCredentials: true }); - - slog.onopen = (e) => { - console.log('slog: opened stream'); - available = true; - }; - - slog.onmessage = (e) => { - const session = useTermState.getState().sessions['']; - if (!session) { - console.log('default session mia!', 'slog:', slog); - return; - } - showSlog(session.term, e.data); - }; - - slog.onerror = (e) => { - console.error('slog: eventsource error:', e); - if (available) { - window.setTimeout(() => { - if (slog.readyState !== EventSource.CLOSED) { - return; - } - console.log('slog: reconnecting...'); - setupSlog(); - }, 10000); - } - }; - - set((state) => { - state.slogstream = slog; - }); - }, [sessions]); - - const onInput = useCallback((ses: string, e: string) => { - const term = useTermState.getState().sessions[ses].term; - const belts = readInput(term, e); - belts.map((b) => { // NOTE passing api.poke(pokeBelt makes `this` undefined! - //TODO pokeBelt(ses, b); - api.poke({ - app: 'herm', - mark: 'belt', - json: b - }); - }); - }, [sessions]); - - const onResize = useCallback(() => { - // TODO debounce, if it ever becomes a problem - session?.fit.fit(); - }, [session]); - - // on-init, open slogstream - // - useEffect(() => { - if (!slogstream) { - setupSlog(); - } - window.addEventListener('resize', onResize); - return () => { - // TODO clean up subs? - window.removeEventListener('resize', onResize); - }; - }, [onResize, setupSlog]); - - // on dark mode change, change terminals' theme - // - useEffect(() => { - const theme = makeTheme(dark); - for (const ses in sessions) { - sessions[ses].term.setOption('theme', theme); - } - if (container.current) { - container.current.style.backgroundColor = theme.background || ''; - } - }, [dark, sessions]); - - // on selected change, maybe setup the term, or put it into the container - // - useEffect(() => { - let ses = session; - // initialize terminal - // - if (!ses) { - // set up terminal - // - const term = new Terminal(termConfig); - term.setOption('theme', makeTheme(dark)); - const fit = new FitAddon(); - term.loadAddon(fit); - - // start mouse reporting - // - term.write(csi('?9h')); - - // set up event handlers - // - term.onData(e => onInput(selected, e)); - term.onBinary(e => onInput(selected, e)); - term.onResize((e) => { - //TODO re-enable once new backend lands - // api.poke(pokeTask(selected, { blew: { w: e.cols, h: e.rows } })); - }); - - ses = { term, fit }; - - // open subscription - // - api.subscribe({ app: 'herm', path: '/session/'+selected+'/view', - event: (e) => { - const ses = useTermState.getState().sessions[selected]; - if (!ses) { - console.log('on blit: no such session', selected, sessions, useTermState.getState().sessions); - return; - } - showBlit(ses.term, e); - }, - quit: () => { // quit - // TODO show user a message - } - }); - } - - if (container.current && !container.current.contains(ses.term.element || null)) { - ses.term.open(container.current); - ses.fit.fit(); - ses.term.focus(); - } - - set((state) => { - state.sessions[selected] = ses; - }); - - return () => { - // TODO unload term from container - // but term.dispose is too powerful? maybe just empty the container? - }; - }, [set, session, container]); - - return ( - <> - - - - - - - - - ); -} diff --git a/pkg/interface/webterm/constants.ts b/pkg/interface/webterm/constants.ts new file mode 100644 index 0000000000..d945b1b46c --- /dev/null +++ b/pkg/interface/webterm/constants.ts @@ -0,0 +1,28 @@ +export const DEFAULT_SESSION = ''; +export const DEFAULT_HANDLER = 'hood'; +export const RESIZE_DEBOUNCE_MS = 100; +export const RESIZE_THRESHOLD_PX = 15; + +/** + * Session ID validity: + * + * - must start with an alphabetical + * - can be composed of alphanumerics with hyphens + * - can be length 1 or longer + */ +export const SESSION_ID_REGEX = /(^[a-z]{1}[a-z\d-]*$)/; + +/** + * Open a session with a given agent using `[agent]![session_name]` + * + * For example: + * ``` + * book!my-session + * ``` + * + * This will create a new session in webterm for the `%book` agent. + * + * Note that the second capture group after the ! is composed of the session ID + * regex above. + */ +export const AGENT_SESSION_REGEX = /^([a-z]{1}[a-z\d-]*)!([a-z]{1}[a-z\d-]*$)/; diff --git a/pkg/interface/webterm/index.html b/pkg/interface/webterm/index.html index 5eaf05c35b..610ba697a6 100644 --- a/pkg/interface/webterm/index.html +++ b/pkg/interface/webterm/index.html @@ -23,10 +23,113 @@ diff --git a/pkg/interface/webterm/lib/blit.ts b/pkg/interface/webterm/lib/blit.ts new file mode 100644 index 0000000000..720941ff67 --- /dev/null +++ b/pkg/interface/webterm/lib/blit.ts @@ -0,0 +1,129 @@ +import { Terminal } from 'xterm'; +import { saveAs } from 'file-saver'; +import { Blit, Stub, Stye } from '@urbit/api/term'; +import { stye } from '../lib/stye'; + +export const csi = (cmd: string, ...args: number[]) => { + return '\x1b[' + args.join(';') + cmd; +}; + +export const showBlit = (term: Terminal, blit: Blit) => { + let out = ''; + + if ('mor' in blit) { + return blit.mor.map(b => showBlit(term, b)); + } else if ('bel' in blit) { + out += '\x07'; + } else if ('clr' in blit) { + term.clear(); + out += csi('u'); + } else if ('hop' in blit) { + if (typeof blit.hop === 'number') { + out += csi('H', term.rows, blit.hop + 1); + } else { + out += csi('H', blit.hop.y + 1, blit.hop.x + 1); + } + out += csi('s'); // save cursor position + } else if ('put' in blit) { + out += blit.put.join(''); + out += csi('u'); + } else if ('klr' in blit) { + out += blit.klr.reduce((lin: string, p: Stub) => { + lin += stye(p.stye); + lin += p.text.join(''); + lin += csi('m', 0); + return lin; + }, ''); + out += csi('u'); + } else if ('nel' in blit) { + out += '\n'; + } else if ('sag' in blit || 'sav' in blit) { + const sav = ('sag' in blit) ? blit.sag : blit.sav; + const name = sav.path.split('/').slice(-2).join('.'); + const buff = Buffer.from(sav.file, 'base64'); + const blob = new Blob([buff], { type: 'application/octet-stream' }); + saveAs(blob, name); + } else if ('url' in blit) { + window.open(blit.url); + } else if ('wyp' in blit) { + out += '\r' + csi('K'); + out += csi('u'); + // + } else { + console.log('weird blit', blit); + } + + term.write(out); +}; + +export const showSlog = (term: Terminal, slog: string) => { + // set scroll region to exclude the bottom line, + // scroll up one line, + // move cursor to start of the newly created whitespace, + // set text to grey, + // print the slog, + // restore color, scroll region, and cursor. + // + term.write(csi('r', 1, term.rows - 1) + + csi('S', 1) + + csi('H', term.rows - 1, 1) + + csi('m', 90) + + slog + + csi('m', 0) + + csi('r') + + csi('u')); +}; + +export const hasBell = (blit: Blit) => { + if ('bel' in blit) { + return true; + } else if ('mor' in blit) { + return blit.mor.some(hasBell); + } else { + return false; + } +}; + +// debug rendering +//NOTE doesn't behave nicely in the presence of eob %nel blits, +// because those aren't idempotent + +const blotStye: Stye = { deco: [], back: { r: 255, g: 0, b: 255 }, fore: 'k' }; +const blitToBlot = (blit: Blit): Blit => { + if ('mor' in blit) { + return { mor: blit.mor.map(blitToBlot) }; + } else if ('put' in blit) { + return { klr: [{ text: blit.put, stye: blotStye }] }; + } else if ('klr' in blit) { + return { klr: blit.klr.map((s: Stub) => { + return { text: s.text, stye: blotStye }; + }) }; + } else { + return blit; + } +}; + +const queue: {term: Terminal, blit: Blit}[] = []; +const renderFromQueue = () => { + const next = queue.shift(); + if (!next) { + return; + } + showBlit(next.term, next.blit); + if (0 === queue.length) { + return; + } + setTimeout(renderFromQueue, 200); +}; + +export const showBlitDebug = (term: Terminal, blit: Blit) => { + const blot = blitToBlot(blit); + if (0 === queue.length) { + showBlit(term, blot); + queue.push({ term, blit }); + setTimeout(renderFromQueue, 200); + } else { + queue.push({ term, blit: blot }); + queue.push({ term, blit }); + } +}; diff --git a/pkg/interface/webterm/lib/retry.ts b/pkg/interface/webterm/lib/retry.ts new file mode 100644 index 0000000000..dea78b0b2b --- /dev/null +++ b/pkg/interface/webterm/lib/retry.ts @@ -0,0 +1,39 @@ +/** + * Wait for the given milliseconds + * @param {number} milliseconds The given time to wait + * @returns {Promise} A fulfiled promise after the given time has passed + */ + function waitFor(milliseconds) { + return new Promise(resolve => setTimeout(resolve, milliseconds)); +} + +/** + * Execute a promise and retry with exponential backoff + * based on the maximum retry attempts it can perform + * @param {Promise} promise promise to be executed + * @param {function} onRetry callback executed on every retry + * @param {number} maxRetries The maximum number of retries to be attempted + * @returns {Promise} The result of the given promise passed in + */ +export function retry(promise, onRetry, maxRetries) { + async function retryWithBackoff(retries) { + try { + if (retries > 0) { + const timeToWait = 2 ** retries * 100; + console.log(`waiting for ${timeToWait}ms...`); + await waitFor(timeToWait); + } + return await promise(); + } catch (e) { + if (retries < maxRetries) { + onRetry(); + return retryWithBackoff(retries + 1); + } else { + console.warn('Max retries reached. Bubbling the error up'); + throw e; + } + } + } + + return retryWithBackoff(0); +} diff --git a/pkg/interface/webterm/lib/stye.ts b/pkg/interface/webterm/lib/stye.ts new file mode 100644 index 0000000000..bc2d99b5ea --- /dev/null +++ b/pkg/interface/webterm/lib/stye.ts @@ -0,0 +1,60 @@ +import { Deco, Stye, Tint } from '@urbit/api/term'; + +const tint = (t: Tint) => { + switch (t) { + case null: return '9'; + case 'k': return '0'; + case 'r': return '1'; + case 'g': return '2'; + case 'y': return '3'; + case 'b': return '4'; + case 'm': return '5'; + case 'c': return '6'; + case 'w': return '7'; + default: return `8;2;${t.r%256};${t.g%256};${t.b%256}`; + } +}; + +export const stye = (s: Stye) => { + let out = ''; + + // text decorations + // + if (s.deco.length > 0) { + out += s.deco.reduce((decs: number[], deco: Deco) => { + /* eslint-disable max-statements-per-line */ + switch (deco) { + case null: decs.push(0); return decs; + case 'br': decs.push(1); return decs; + case 'un': decs.push(4); return decs; + case 'bl': decs.push(5); return decs; + default: console.log('weird deco', deco); return decs; + } + }, []).join(';'); + } + + // background color + // + if (s.back !== null) { + if (out !== '') { + out += ';'; + } + out += '4'; + out += tint(s.back); + } + + // foreground color + // + if (s.fore !== null) { + if (out !== '') { + out += ';'; + } + out += '3'; + out += tint(s.fore); + } + + if (out === '') { + return out; + } + return '\x1b[' + out + 'm'; +}; diff --git a/pkg/interface/webterm/lib/theme.ts b/pkg/interface/webterm/lib/theme.ts new file mode 100644 index 0000000000..cf2beb152c --- /dev/null +++ b/pkg/interface/webterm/lib/theme.ts @@ -0,0 +1,23 @@ +import { ITheme } from 'xterm'; + +export const makeTheme = (dark: boolean): ITheme => { + let fg, bg: string; + if (dark) { + fg = 'white'; + bg = 'rgb(26,26,26)'; + } else { + fg = 'black'; + bg = 'white'; + } + // TODO indigo colors. + // we can't pluck these from ThemeContext because they have transparency. + // technically xterm supports transparency, but it degrades performance. + return { + foreground: fg, + background: bg, + brightBlack: '#7f7f7f', // NOTE slogs + cursor: fg, + cursorAccent: bg, + selection: fg + }; +}; diff --git a/pkg/interface/webterm/lib/useAddSession.ts b/pkg/interface/webterm/lib/useAddSession.ts new file mode 100644 index 0000000000..c5556c14b3 --- /dev/null +++ b/pkg/interface/webterm/lib/useAddSession.ts @@ -0,0 +1,66 @@ +import { + DEFAULT_HANDLER, + AGENT_SESSION_REGEX, + SESSION_ID_REGEX +} from '../constants'; +import useTermState from '../state'; +import api from '../api'; +import { pokeTask } from '@urbit/api/term'; +import { useCallback } from 'react'; + +export const useAddSession = () => { + const { names } = useTermState(); + + const addSession = useCallback(async () => { + let agent = DEFAULT_HANDLER; + let sessionName: string; + + const userInput = prompt('Please enter an alpha-numeric session name.'); + // user canceled or did not enter a value + if (null === userInput) { + return; + } + + // check for custom agent session syntax + if (AGENT_SESSION_REGEX.test(userInput)) { + const match = AGENT_SESSION_REGEX.exec(userInput); + if (!match) { + alert('Invalid format. Valid syntax: agent!session-name'); + return; + } + agent = match[1]; + sessionName = match[2]; + // else, use the default session creation regex + } else if (SESSION_ID_REGEX.test(userInput)) { + const match = SESSION_ID_REGEX.exec(userInput); + if (!match) { + alert('Invalid format. Valid syntax: session-name'); + return; + } + sessionName = match[1]; + } else { + alert('Invalid format. Valid syntax: session-name'); + return; + } + + // prevent duplicate sessions + if(names.includes(sessionName)) { + alert(`Session name must be unique ("${sessionName}" already in use)`); + return; + } + + try { + //TODO eventually, customizable app pre-linking? + await api.poke(pokeTask(sessionName, { open: { term: agent, apps: [{ who: '~' + (window as any).ship, app: 'dojo' }] } })); + useTermState.getState().set((state) => { + state.names = [sessionName, ...state.names].sort(); + state.selected = sessionName; + state.sessions[sessionName] = null; + }); + } catch (error) { + console.log('unable to create session:', error); + } + }, [names]); + + return addSession; +}; diff --git a/pkg/interface/webterm/join.ts b/pkg/interface/webterm/lib/useDark.ts similarity index 74% rename from pkg/interface/webterm/join.ts rename to pkg/interface/webterm/lib/useDark.ts index 957abbd3d1..ddd9c10aea 100644 --- a/pkg/interface/webterm/join.ts +++ b/pkg/interface/webterm/lib/useDark.ts @@ -1,6 +1,5 @@ import { useEffect, useState } from 'react'; -import { useTheme } from './settings'; -import useTermState from './state'; +import useTermState from '../state'; export function useDark() { const [osDark, setOsDark] = useState(false); @@ -11,12 +10,11 @@ export function useDark() { setOsDark(e.matches); }; setOsDark(themeWatcher.matches); - themeWatcher.addListener(update); + themeWatcher.addEventListener('change', update); return () => { - themeWatcher.removeListener(update); - } - + themeWatcher.removeEventListener('change', update); + }; }, []); const theme = useTermState(s => s.theme); diff --git a/pkg/interface/webterm/lib/useDetectOS.ts b/pkg/interface/webterm/lib/useDetectOS.ts new file mode 100644 index 0000000000..7b8f915f9f --- /dev/null +++ b/pkg/interface/webterm/lib/useDetectOS.ts @@ -0,0 +1,44 @@ +/* eslint-disable no-useless-escape */ +// Regex patterns inspired by: +// https://github.com/faisalman/ua-parser-js/blob/master/src/ua-parser.js +const LINUX = [ + /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, + /(mint)[\/\(\) ]?(\w*)/i, + /(mageia|vectorlinux)[; ]/i, + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, + /(hurd|linux) ?([\w\.]*)/i, + /(gnu) ?([\w\.]*)/i, + /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, + /(haiku) (\w+)/i, + /(sunos) ?([\w\.\d]*)/i, + /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, + /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, + /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, + /(unix) ?([\w\.]*)/i +]; + +const MAC_OS = [ + /(mac os x) ?([\w\. ]*)/i, + /(macintosh|mac_powerpc\b)(?!.+haiku)/i +]; + +const WINDOWS = [ + /microsoft (windows) (vista|xp)/i, + /(windows) nt 6\.2; (arm)/i, + /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i, + /(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i +]; + +export const useDetectOS = () => { + const userAgent = navigator.userAgent; + + const isLinux = LINUX.some(regex => regex.test(userAgent)); + const isMacOS = MAC_OS.some(regex => regex.test(userAgent)); + const isWindows = WINDOWS.some(regex => regex.test(userAgent)); + + return { + isLinux, + isMacOS, + isWindows + }; +}; diff --git a/pkg/interface/webterm/lib/useIsMounted.ts b/pkg/interface/webterm/lib/useIsMounted.ts new file mode 100644 index 0000000000..fdfaca3e0b --- /dev/null +++ b/pkg/interface/webterm/lib/useIsMounted.ts @@ -0,0 +1,17 @@ +import { useCallback, useEffect, useRef } from 'react'; + +function useIsMounted() { + const isMounted = useRef(false); + + useEffect(() => { + isMounted.current = true; + + return () => { + isMounted.current = false; + }; + }, []); + + return useCallback(() => isMounted.current, []); +} + +export default useIsMounted; diff --git a/pkg/interface/webterm/package-lock.json b/pkg/interface/webterm/package-lock.json index 2e42a0ff01..4e617d5f30 100644 --- a/pkg/interface/webterm/package-lock.json +++ b/pkg/interface/webterm/package-lock.json @@ -15,6 +15,7 @@ "@urbit/api": "^1.1.1", "@urbit/http-api": "^1.2.1", "file-saver": "^2.0.5", + "lodash": "^4.17.21", "react": "^16.14.0", "react-dom": "^16.14.0", "react-router-dom": "^5.2.0", @@ -40,7 +41,7 @@ "@types/styled-system": "^5.1.10", "@typescript-eslint/eslint-plugin": "^4.15.0", "@typescript-eslint/parser": "^4.24.0", - "@urbit/eslint-config": "^1.0.0", + "@urbit/eslint-config": "^1.0.3", "@welldone-software/why-did-you-render": "^6.1.0", "babel-eslint": "^10.1.0", "babel-jest": "^26.6.3", @@ -49,7 +50,9 @@ "clean-webpack-plugin": "^3.0.0", "cross-env": "^7.0.3", "eslint": "^7.26.0", - "eslint-plugin-react": "^7.22.0", + "eslint-import-resolver-typescript": "^2.5.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-react-hooks": "^4.3.0", "file-loader": "^6.2.0", "html-webpack-plugin": "^4.5.1", "husky": "^6.0.0", @@ -3006,6 +3009,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "node_modules/@types/lodash": { "version": "4.14.172", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.172.tgz", @@ -3867,16 +3876,16 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", + "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" + "is-string": "^1.0.7" }, "engines": { "node": ">= 0.4" @@ -3912,16 +3921,15 @@ "node": ">=0.10.0" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", - "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "node_modules/array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "function-bind": "^1.1.1" + "es-abstract": "^1.19.0" }, "engines": { "node": ">= 0.4" @@ -6464,9 +6472,9 @@ } }, "node_modules/es-abstract": { - "version": "1.18.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.6.tgz", - "integrity": "sha512-kAeIT4cku5eNLNuUKhlmtuk1/TRZvQoYccn6TO0cSVdf1kzB0T7+dYuVK9MWM7l+/53W2Q8M7N2c6MQvhXFcUQ==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", @@ -6480,7 +6488,9 @@ "is-callable": "^1.2.4", "is-negative-zero": "^2.0.1", "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", "is-string": "^1.0.7", + "is-weakref": "^1.0.1", "object-inspect": "^1.11.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", @@ -6623,34 +6633,171 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-plugin-react": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.1.tgz", - "integrity": "sha512-P4j9K1dHoFXxDNP05AtixcJEvIT6ht8FhYKsrkY0MPCPaUMYijhpWwNiRDZVtA8KFuZOkGSeft6QwH8KuVpJug==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", "dev": true, "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", - "doctrine": "^2.1.0", - "estraverse": "^5.2.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.4", - "object.fromentries": "^2.0.4", - "object.values": "^1.1.4", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.5" + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz", + "integrity": "sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.1", + "glob": "^7.1.7", + "is-glob": "^4.0.1", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.9.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7" + "eslint": "*", + "eslint-plugin-import": "*" } }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { + "node_modules/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", @@ -6662,26 +6809,22 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-react/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", + "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", "dev": true, "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, "node_modules/eslint-scope": { @@ -9250,9 +9393,9 @@ } }, "node_modules/is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -9355,9 +9498,9 @@ } }, "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==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -9487,6 +9630,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -9544,6 +9696,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -11786,19 +11950,6 @@ "json5": "lib/cli.js" } }, - "node_modules/jsx-ast-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", - "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.2", - "object.assign": "^4.1.2" - }, - "engines": { - "node": ">=4.0" - } - }, "node_modules/killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -12805,9 +12956,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -13375,38 +13526,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.entries": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", - "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", - "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.getownpropertydescriptors": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", @@ -13437,14 +13556,14 @@ } }, "node_modules/object.values": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", - "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "es-abstract": "^1.19.1" }, "engines": { "node": ">= 0.4" @@ -14107,13 +14226,13 @@ } }, "node_modules/prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "node_modules/proxy-addr": { @@ -15877,25 +15996,6 @@ "node": ">=8" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz", - "integrity": "sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -16501,6 +16601,27 @@ "node": ">=6" } }, + "node_modules/tsconfig-paths": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", + "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", @@ -20825,6 +20946,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/lodash": { "version": "4.14.172", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.172.tgz", @@ -21538,16 +21665,16 @@ "dev": true }, "array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", + "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" + "is-string": "^1.0.7" } }, "array-union": { @@ -21568,16 +21695,15 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, - "array.prototype.flatmap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", - "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "function-bind": "^1.1.1" + "es-abstract": "^1.19.0" } }, "asn1.js": { @@ -23681,9 +23807,9 @@ } }, "es-abstract": { - "version": "1.18.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.6.tgz", - "integrity": "sha512-kAeIT4cku5eNLNuUKhlmtuk1/TRZvQoYccn6TO0cSVdf1kzB0T7+dYuVK9MWM7l+/53W2Q8M7N2c6MQvhXFcUQ==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -23697,7 +23823,9 @@ "is-callable": "^1.2.4", "is-negative-zero": "^2.0.1", "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", "is-string": "^1.0.7", + "is-weakref": "^1.0.1", "object-inspect": "^1.11.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", @@ -24021,27 +24149,140 @@ } } }, - "eslint-plugin-react": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.1.tgz", - "integrity": "sha512-P4j9K1dHoFXxDNP05AtixcJEvIT6ht8FhYKsrkY0MPCPaUMYijhpWwNiRDZVtA8KFuZOkGSeft6QwH8KuVpJug==", + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", "dev": true, "requires": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", - "doctrine": "^2.1.0", - "estraverse": "^5.2.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.4", - "object.fromentries": "^2.0.4", - "object.values": "^1.1.4", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.5" + "debug": "^3.2.7", + "resolve": "^1.20.0" }, "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-import-resolver-typescript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz", + "integrity": "sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==", + "dev": true, + "requires": { + "debug": "^4.3.1", + "glob": "^7.1.7", + "is-glob": "^4.0.1", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.9.0" + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -24051,24 +24292,21 @@ "esutils": "^2.0.2" } }, - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true - }, - "resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } } } }, + "eslint-plugin-react-hooks": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", + "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", + "dev": true, + "requires": {} + }, "eslint-scope": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", @@ -25889,9 +26127,9 @@ } }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "requires": { "has": "^1.0.3" @@ -25960,9 +26198,9 @@ "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==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -26050,6 +26288,12 @@ "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", "dev": true }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -26086,6 +26330,15 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -27772,16 +28025,6 @@ "minimist": "^1.2.0" } }, - "jsx-ast-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", - "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", - "dev": true, - "requires": { - "array-includes": "^3.1.2", - "object.assign": "^4.1.2" - } - }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -28547,9 +28790,9 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -29031,29 +29274,6 @@ "object-keys": "^1.1.1" } }, - "object.entries": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", - "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" - } - }, - "object.fromentries": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", - "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" - } - }, "object.getownpropertydescriptors": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", @@ -29075,14 +29295,14 @@ } }, "object.values": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", - "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.2" + "es-abstract": "^1.19.1" } }, "obuf": { @@ -29614,13 +29834,13 @@ } }, "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "proxy-addr": { @@ -31082,22 +31302,6 @@ } } }, - "string.prototype.matchall": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz", - "integrity": "sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.2", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", - "side-channel": "^1.0.4" - } - }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -31573,6 +31777,26 @@ } } }, + "tsconfig-paths": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz", + "integrity": "sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, "tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", diff --git a/pkg/interface/webterm/package.json b/pkg/interface/webterm/package.json index e859b48253..0e58cc6080 100644 --- a/pkg/interface/webterm/package.json +++ b/pkg/interface/webterm/package.json @@ -11,6 +11,7 @@ "@urbit/api": "^1.1.1", "@urbit/http-api": "^1.2.1", "file-saver": "^2.0.5", + "lodash": "^4.17.21", "react": "^16.14.0", "react-dom": "^16.14.0", "react-router-dom": "^5.2.0", @@ -36,7 +37,7 @@ "@types/styled-system": "^5.1.10", "@typescript-eslint/eslint-plugin": "^4.15.0", "@typescript-eslint/parser": "^4.24.0", - "@urbit/eslint-config": "^1.0.0", + "@urbit/eslint-config": "^1.0.3", "@welldone-software/why-did-you-render": "^6.1.0", "babel-eslint": "^10.1.0", "babel-jest": "^26.6.3", @@ -45,7 +46,9 @@ "clean-webpack-plugin": "^3.0.0", "cross-env": "^7.0.3", "eslint": "^7.26.0", - "eslint-plugin-react": "^7.22.0", + "eslint-import-resolver-typescript": "^2.5.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-react-hooks": "^4.3.0", "file-loader": "^6.2.0", "html-webpack-plugin": "^4.5.1", "husky": "^6.0.0", @@ -58,7 +61,8 @@ "webpack-dev-server": "^3.11.2" }, "scripts": { - "lint": "eslint ./src/**/*.{ts,tsx}", + "lint": "eslint ./**/*.{ts,tsx}", + "lint-fix": "eslint --fix ./**/*.{ts,tsx}", "lint-file": "eslint", "tsc": "tsc", "tsc:watch": "tsc --watch", diff --git a/pkg/interface/webterm/state.ts b/pkg/interface/webterm/state.ts index 253e7854f8..d61d798cbe 100644 --- a/pkg/interface/webterm/state.ts +++ b/pkg/interface/webterm/state.ts @@ -3,21 +3,33 @@ import { FitAddon } from 'xterm-addon-fit'; import create from 'zustand'; import produce from 'immer'; -type Session = { term: Terminal, fit: FitAddon }; -type Sessions = { [id: string]: Session; } +export type Session = { + term: Terminal, + fit: FitAddon, + hasBell: boolean, + pending: number, + subscriptionId: number | null, +} | null; +export type Sessions = { [id: string]: Session; } export interface TermState { sessions: Sessions, + names: string[], selected: string, slogstream: null | EventSource, - theme: 'auto' | 'light' | 'dark' -}; + theme: 'auto' | 'light' | 'dark', + //TODO: figure out the type + set: any, +} +// eslint-disable-next-line no-unused-vars const useTermState = create((set, get) => ({ sessions: {} as Sessions, + names: [''], selected: '', // empty string is default session slogstream: null, theme: 'auto', + // eslint-disable-next-line no-unused-vars set: (f: (draft: TermState) => void) => { set(produce(f)); } diff --git a/pkg/interface/webterm/tsconfig.json b/pkg/interface/webterm/tsconfig.json new file mode 100644 index 0000000000..9a8642966d --- /dev/null +++ b/pkg/interface/webterm/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "noFallthroughCasesInSwitch": true, + "noUnusedParameters": false, + "noImplicitReturns": false, + "moduleResolution": "node", + "esModuleInterop": true, + "noUnusedLocals": false, + "noImplicitAny": false, + "noEmit": true, + "target": "ESNext", + "module": "ESNext", + "strict": false, + "strictNullChecks": true, + "jsx": "react", + "baseUrl": "." + }, + "include": [ + "**/*" + ], + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/pkg/landscape/app/chat-cli.hoon b/pkg/landscape/app/chat-cli.hoon index 4398cc7e81..a5aa5dee2c 100644 --- a/pkg/landscape/app/chat-cli.hoon +++ b/pkg/landscape/app/chat-cli.hoon @@ -16,15 +16,15 @@ +$ card card:shoe :: +$ versioned-state - $% state-3 + $% state-4 + state-3 state-2 state-1 state-0 == :: -+$ state-3 - $: %3 - ::TODO support multiple sessions ++$ state-4 + $: %4 sessions=(map sole-id session) :: sole sessions bound=(map resource glyph) :: bound resource glyphs binds=(jug glyph resource) :: resource glyph lookup @@ -33,7 +33,17 @@ timez=(pair ? @ud) :: timezone adjustment == :: -+$ sole-id @ta ++$ state-3 + $: %3 + sessions=(map @ta session) + bound=(map resource glyph) + binds=(jug glyph resource) + settings=(set term) + width=@ud + timez=(pair ? @ud) + == +:: ++$ sole-id sole-id:shoe +$ session $: viewing=(set resource) :: connected graphs history=(list uid:post) :: scrollback pointers @@ -115,7 +125,7 @@ == :: :: -- -=| state-3 +=| state-4 =* state - :: %- agent:dbug @@ -258,14 +268,14 @@ settings width timez == :: - =^ cards u.old + =^ cards-1 u.old ?. ?=(%2 -.u.old) [~ u.old] :- :~ [%pass /chat-store %agent [our-self %chat-store] %leave ~] [%pass /invites %agent [our.bowl %invite-store] %leave ~] == ^- state-3 :- %3 - :* %+ ~(put in *(map sole-id session)) + :* %+ ~(put in *(map @ta session)) (cat 3 'drum_' (scot %p our.bowl)) :* ~ ~ 0 :: @@ -290,14 +300,29 @@ timez.u.old == :: - ?> ?=(%3 -.u.old) + =^ cards-2 u.old + ?. ?=(%3 -.u.old) [~ u.old] + :- %+ turn ~(tap in ~(key by sessions.u.old)) + |= id=@ta + ^- card:agent:gall + [%give %kick ~[/sole/[id]] ~] + =- u.old(- %4, sessions -) + %- ~(gas by *(map sole-id session)) + %+ murn ~(tap by sessions.u.old) + |= [id=@ta s=session] + (bind (upgrade-id:sole:shoe id) (late s)) + :: + ?> ?=(%4 -.u.old) :_ u.old - %+ welp - cards - ?: %- ~(has by wex.bowl) - [/graph-store our-self %graph-store] - ~ - ~[connect] + ;: welp + cards-1 + cards-2 + :: + ?: %- ~(has by wex.bowl) + [/graph-store our-self %graph-store] + ~ + ~[connect] + == :: +connect: connect to the graph-store :: ++ connect diff --git a/pkg/landscape/lib/dill.hoon b/pkg/landscape/lib/dill.hoon new file mode 120000 index 0000000000..f9c2362f07 --- /dev/null +++ b/pkg/landscape/lib/dill.hoon @@ -0,0 +1 @@ +../../base-dev/lib/dill.hoon \ No newline at end of file diff --git a/pkg/landscape/sys.kelvin b/pkg/landscape/sys.kelvin index b7bcb9ecd8..0cb1220e5f 100644 --- a/pkg/landscape/sys.kelvin +++ b/pkg/landscape/sys.kelvin @@ -1 +1 @@ -[%zuse 417] +[%zuse 416] diff --git a/pkg/npm/api/README.md b/pkg/npm/api/README.md index c8381a52da..6de8a5f66a 100644 --- a/pkg/npm/api/README.md +++ b/pkg/npm/api/README.md @@ -24,7 +24,7 @@ With: import UrbitInterface from '@urbit/http-api'; import { settings } from '@urbit/api'; const api: UrbitInterface = useApi(); -api.poke(setings.putEntry(bucket, key, value)); +api.poke(settings.putEntry(bucket, key, value)); ``` You may import single functions @@ -38,4 +38,4 @@ import { settings } from '@urbit/api'; This package also provides types and utilities for working with Urbit's internal data structures, such as Nouns, Das, Tas, and so forth. -This package was originally developed as part of Tlon's Landscape client and therefore the best reference material exists [there](https://github.com/urbit/urbit/tree/master/pkg/interface/src). \ No newline at end of file +This package was originally developed as part of Tlon's Landscape client and therefore the best reference material exists [there](https://github.com/urbit/urbit/tree/master/pkg/interface/src). diff --git a/pkg/npm/api/term/lib.ts b/pkg/npm/api/term/lib.ts index d67f91477e..67e14175f5 100644 --- a/pkg/npm/api/term/lib.ts +++ b/pkg/npm/api/term/lib.ts @@ -1,4 +1,5 @@ -import { Poke } from '../lib' +import { Scry } from '../../http-api/src' +import { Poke } from '../../http-api/src/types'; import { Belt, Task, SessionTask } from './types'; export const pokeTask = (session: string, task: Task): Poke => ({ @@ -13,9 +14,7 @@ export const pokeBelt = ( ): Poke => pokeTask(session, { belt }); //NOTE scry will return string[] -// export const scrySessions = (): Scry => ({ -// app: 'herm', -// path: `/sessions` -// }); -//TODO remove stub once new backend lands -export const scrySessions = (): string[] => ['']; +export const scrySessions = (): Scry => ({ + app: 'herm', + path: `/sessions` +}); diff --git a/pkg/npm/api/term/types.ts b/pkg/npm/api/term/types.ts index 2920200aa0..2f352a7281 100644 --- a/pkg/npm/api/term/types.ts +++ b/pkg/npm/api/term/types.ts @@ -25,17 +25,15 @@ export type Stub = { export type Blit = | { bel: null } // make a noise | { clr: null } // clear the screen - | { hop: number | { r: number, c: number } } // set cursor col/pos + | { hop: number | { x: number, y: number } } // set cursor col/pos | { klr: Stub[] } // put styled - | { put: string[] } // put text at cursor + | { mor: Blit[] } // multiple blits | { nel: null } // newline + | { put: string[] } // put text at cursor | { sag: { path: string, file: string } } // save to jamfile | { sav: { path: string, file: string } } // save to file | { url: string } // activate url | { wyp: null } // wipe cursor line - // - | { lin: string[] } // legacy put - | { mor: true } // legacy nel // inputs // @@ -45,21 +43,19 @@ export type Bolt = | { aro: 'd' | 'l' | 'r' | 'u' } | { bac: null } | { del: null } - | { hit: { r: number, c: number } } + | { hit: { x: number, y: number } } | { ret: null } export type Belt = | Bolt | { mod: { mod: 'ctl' | 'met' | 'hyp', key: Bolt } } | { txt: Array } - // - | { ctl: string }; // legacy mod export type Task = | { belt: Belt } | { blew: { w: number, h: number } } - | { flow: { term: string, apps: Array<{ who: string, app: string }> } } | { hail: null } - | { hook: null } + | { open: { term: string, apps: Array<{ who: string, app: string }> } } + | { shut: null } export type SessionTask = { session: string } & Task diff --git a/pkg/urbit/compat/posix/ptty.c b/pkg/urbit/compat/posix/ptty.c index ff42abff94..7c1a17f009 100644 --- a/pkg/urbit/compat/posix/ptty.c +++ b/pkg/urbit/compat/posix/ptty.c @@ -167,6 +167,9 @@ u3_ptty_init(uv_loop_t* lup_u, const c3_c** err_c) // Construct raw termios configuration. // + // makes input available per-character, does not echo input, + // disables special input pre-processing, output post-processing. + // { pty_u->raw_u = pty_u->bak_u; diff --git a/pkg/urbit/configure b/pkg/urbit/configure index db2b1ec5ca..acea03a862 100755 --- a/pkg/urbit/configure +++ b/pkg/urbit/configure @@ -211,7 +211,7 @@ for citem in $compat; do done cat >config.mk <cax.har_p); @@ -31,7 +31,7 @@ u3qe_jam(u3_atom a) u3a_print_memory(stderr, "total", tot_w); u3a_print_memory(stderr, "memoization cache", mem_w); u3h_root* har_u = u3to(u3h_root, u3R->cax.har_p); - u3l_log("memoization entries: %d\r\n", har_u->use_w); + u3l_log("memoization entries: %d", har_u->use_w); u3a_print_memory(stderr, "unused free", u3a_open(u3R)); return tot_w; } diff --git a/pkg/urbit/jets/e/rub.c b/pkg/urbit/jets/e/rub.c index 641e53035b..48a1116b38 100644 --- a/pkg/urbit/jets/e/rub.c +++ b/pkg/urbit/jets/e/rub.c @@ -31,7 +31,7 @@ // Sanity check: crash if decoding more bits than available if ( c3y == u3qa_gth(x, m)) { - // u3l_log("[%%rub-hard %d %d %d]\r\n", a, x, m); + // u3l_log("[%%rub-hard %d %d %d]", a, x, m); return u3m_bail(c3__exit); } diff --git a/pkg/urbit/jets/e/secp.c b/pkg/urbit/jets/e/secp.c index 21569603c5..21c6f7daa1 100644 --- a/pkg/urbit/jets/e/secp.c +++ b/pkg/urbit/jets/e/secp.c @@ -16,7 +16,7 @@ u3je_secp_init() sec_u = malloc(urcrypt_secp_prealloc_size()); if ( 0 != urcrypt_secp_init(sec_u, ent_y) ) { - u3l_log("u3e_secp_init failed\r\n"); + u3l_log("u3e_secp_init failed"); abort(); } } diff --git a/pkg/urbit/jets/f/core.c b/pkg/urbit/jets/f/core.c index 753ea74de3..5d7ef8015c 100644 --- a/pkg/urbit/jets/f/core.c +++ b/pkg/urbit/jets/f/core.c @@ -22,7 +22,7 @@ (u3_nul == u3h(hr_con)) && (u3_nul == u3t(hr_con)) ) { - u3l_log("old core\r\n"); + u3l_log("old core"); abort(); } } diff --git a/pkg/urbit/jets/f/look.c b/pkg/urbit/jets/f/look.c index e54091b0b2..038f7a5228 100644 --- a/pkg/urbit/jets/f/look.c +++ b/pkg/urbit/jets/f/look.c @@ -20,7 +20,7 @@ u3r_trel(dab, &n_dab, &l_dab, &r_dab); if ( c3n == u3du(n_dab) ) { // return u3m_bail(c3__fail); - u3l_log("bad look\r\n"); + u3l_log("bad look"); return u3m_bail(c3__exit) ; } else { diff --git a/pkg/urbit/noun/allocate.c b/pkg/urbit/noun/allocate.c index f7ce679584..3eb32dac84 100644 --- a/pkg/urbit/noun/allocate.c +++ b/pkg/urbit/noun/allocate.c @@ -431,7 +431,7 @@ _ca_willoc(c3_w len_w, c3_w ald_w, c3_w alp_w) sel_w += 1; } - // u3l_log("walloc %d: *pfr_p %x\n", len_w, u3R->all.fre_p[sel_w]); + // u3l_log("walloc %d: *pfr_p %x", len_w, u3R->all.fre_p[sel_w]); while ( 1 ) { u3p(u3a_fbox) *pfr_p = &u3R->all.fre_p[sel_w]; @@ -566,7 +566,7 @@ u3a_walloc(c3_w len_w) u3a_botox(ptr_v) == (u3a_box*)(void *)0x200dfe3e4 ) { static int xuc_i; - u3l_log("xuc_i %d\r\n", xuc_i); + u3l_log("xuc_i %d", xuc_i); if ( 1 == xuc_i ) { u3a_box* box_u = u3a_botox(ptr_v); @@ -682,7 +682,7 @@ u3a_malloc(size_t len_i) if ( u3a_botox(out_w) == (u3a_box*)(void *)0x3bdd1c80) { static int xuc_i = 0; - u3l_log("xuc_i %d\r\n", xuc_i); + u3l_log("xuc_i %d", xuc_i); // if ( 1 == xuc_i ) { abort(); } xuc_i++; } @@ -885,7 +885,7 @@ u3a_free(void* tox_v) c3_w pad_w = tox_w[-1]; c3_w* org_w = tox_w - (pad_w + 1); - // u3l_log("free %p %p\r\n", org_w, tox_w); + // u3l_log("free %p %p", org_w, tox_w); u3a_wfree(org_w); } @@ -1013,7 +1013,7 @@ _ca_take_atom(u3a_atom* old_u) u3_noun new = u3a_to_pug(u3a_outa(new_u)); #ifdef VERBOSE_TAKE - u3l_log("%s: atom %p to %p\r\n", ( c3y == u3a_is_north(u3R) ) + u3l_log("%s: atom %p to %p", ( c3y == u3a_is_north(u3R) ) ? "north" : "south", old_u, @@ -1051,7 +1051,7 @@ _ca_take_cell(u3a_cell* old_u, u3_noun hed, u3_noun tel) u3_cell new = u3a_to_pom(u3a_outa(new_u)); #ifdef VERBOSE_TAKE - u3l_log("%s: cell %p to %p\r\n", ( c3y == u3a_is_north(u3R) ) + u3l_log("%s: cell %p to %p", ( c3y == u3a_is_north(u3R) ) ? "north" : "south", old_u, @@ -1110,7 +1110,7 @@ _ca_take_next_north(u3a_pile* pil_u, u3_noun veb) c3_assert( c3y == u3a_north_is_normal(u3R, nov) ); #ifdef VERBOSE_TAKE - u3l_log("north: %p is already %p\r\n", veb_u, u3a_to_ptr(nov)); + u3l_log("north: %p is already %p", veb_u, u3a_to_ptr(nov)); #endif _me_gain_use(nov); // bypass branches in u3k() @@ -1165,7 +1165,7 @@ _ca_take_next_south(u3a_pile* pil_u, u3_noun veb) c3_assert( c3y == u3a_south_is_normal(u3R, nov) ); #ifdef VERBOSE_TAKE - u3l_log("south: %p is already %p\r\n", veb_u, u3a_to_ptr(nov)); + u3l_log("south: %p is already %p", veb_u, u3a_to_ptr(nov)); #endif _me_gain_use(nov); // bypass branches in u3k() diff --git a/pkg/urbit/noun/events.c b/pkg/urbit/noun/events.c index ceec1bd411..d277213c05 100644 --- a/pkg/urbit/noun/events.c +++ b/pkg/urbit/noun/events.c @@ -142,7 +142,7 @@ u3e_check(c3_c* cap_c) } sum_w += mug_w; } - u3l_log("%s: sum %x (%x, %x)\r\n", cap_c, sum_w, nor_w, sou_w); + u3l_log("%s: sum %x (%x, %x)", cap_c, sum_w, nor_w, sou_w); } } @@ -488,7 +488,7 @@ _ce_patch_verify(u3_ce_patch* pat_u) } #if 0 else { - u3l_log("verify: patch %d/%d, %x\r\n", pag_w, i_w, mug_w); + u3l_log("verify: patch %d/%d, %x", pag_w, i_w, mug_w); } #endif } @@ -815,7 +815,7 @@ _ce_patch_apply(u3_ce_patch* pat_u) } } #if 0 - u3l_log("apply: %d, %x\n", pag_w, u3r_mug_words(mem_w, pag_wiz_i)); + u3l_log("apply: %d, %x", pag_w, u3r_mug_words(mem_w, pag_wiz_i)); #endif } } @@ -1169,13 +1169,13 @@ u3e_live(c3_o nuu_o, c3_c* dir_c) (u3_Loom + u3C.wor_i) - pag_wiz_i, -(ssize_t)pag_wiz_i); - u3l_log("boot: protected loom\r\n"); + u3l_log("boot: protected loom"); } /* If the images were empty, we are logically booting. */ if ( (0 == u3P.nor_u.pgs_w) && (0 == u3P.sou_u.pgs_w) ) { - u3l_log("live: logical boot\r\n"); + u3l_log("live: logical boot"); nuu_o = c3y; } else { diff --git a/pkg/urbit/noun/jets.c b/pkg/urbit/noun/jets.c index 357e94bd86..c60badf5db 100644 --- a/pkg/urbit/noun/jets.c +++ b/pkg/urbit/noun/jets.c @@ -75,7 +75,7 @@ _cj_hash(c3_c* has_c) { c3_w i_w, len_w = strlen(has_c); if ( 64 != len_w ) { - u3l_log("bash not 64 characters: %s\r\n", has_c); + u3l_log("bash not 64 characters: %s", has_c); c3_assert(0); } c3_assert( 64 == len_w ); @@ -236,18 +236,18 @@ _cj_axis(u3_noun fol) (0 != p_fol) || (!_(u3a_is_cat(q_fol))) ) { - u3l_log("axis: bad a\r\n"); + u3l_log("axis: bad a"); return 0; } return q_fol; } else { if ( 9 != p_fol ) - { u3l_log("axis: bad b\r\n"); return 0; } + { u3l_log("axis: bad b"); return 0; } if ( !_(u3a_is_cat(q_fol)) ) - { u3l_log("axis: bad c\r\n"); return 0; } + { u3l_log("axis: bad c"); return 0; } if ( !_(u3du(r_fol)) || (0 != u3h(r_fol)) || (1 != u3t(r_fol)) ) - { u3l_log("axis: bad d\r\n"); return 0; } + { u3l_log("axis: bad d"); return 0; } return q_fol; } @@ -278,7 +278,7 @@ _cj_warm_hump(c3_l jax_l, u3_noun huc) ((1 << 31) & (axe_l = (c3_w)axe_d)) || (axe_l < 2) ) { - u3l_log("jets: activate: bad fcs %s\r\n", jet_u->fcs_c); + u3l_log("jets: activate: bad fcs %s", jet_u->fcs_c); } } else { @@ -286,7 +286,7 @@ _cj_warm_hump(c3_l jax_l, u3_noun huc) u3_noun fol = u3kdb_get(u3k(huc), nam); if ( u3_none == fol ) { - u3l_log("jets: activate: bad fcs %s\r\n", jet_u->fcs_c); + u3l_log("jets: activate: bad fcs %s", jet_u->fcs_c); } else { axe_l = _cj_axis(fol); @@ -781,7 +781,7 @@ _cj_hot_mean(c3_l par_l, u3_noun nam) while ( (cop_u = &dev_u[i_l])->cos_c ) { if ( _(u3r_sing_c(cop_u->cos_c, nam)) ) { #if 0 - u3l_log("hot: bound jet %d/%s/%s/\r\n", + u3l_log("hot: bound jet %d/%s/%s/", cop_u->jax_l, cop_u->cos_c, par_u ? par_u->cos_c : "~"); @@ -893,7 +893,7 @@ _cj_kick_z(u3_noun cor, u3j_core* cop_u, u3j_harm* ham_u, u3_atom axe) ham_u->liv = c3y; if ( c3n == u3r_sing(ame, pro) ) { - u3l_log("test: %s %s: mismatch: good %x, bad %x\r\n", + u3l_log("test: %s %s: mismatch: good %x, bad %x", cop_u->cos_c, (!strcmp(".2", ham_u->fcs_c)) ? "$" : ham_u->fcs_c, u3r_mug(ame), @@ -905,7 +905,7 @@ _cj_kick_z(u3_noun cor, u3j_core* cop_u, u3j_harm* ham_u, u3_atom axe) else { #if 0 - u3l_log("test: %s %s\r\n", + u3l_log("test: %s %s", cop_u->cos_c, (!strcmp(".2", ham_u->fcs_c)) ? "$" : ham_u->fcs_c); #endif @@ -928,13 +928,13 @@ _cj_hook_in(u3_noun cor, u3_noun roc, tem, got, pat, nam, huc; if ( c3n == u3du(cor) ) { - u3l_log("_cj_hook_in failure: c3n == u3du(cor)\r\n"); + u3l_log("_cj_hook_in failure: c3n == u3du(cor)"); return u3m_bail(c3__fail); } loc = _cj_spot(cor, NULL); if ( u3_none == loc ) { - u3l_log("_cj_hook_in failure: u3_none == loc\r\n"); + u3l_log("_cj_hook_in failure: u3_none == loc"); return u3m_bail(c3__fail); } @@ -999,7 +999,7 @@ _cj_hook_in(u3_noun cor, else { u3_noun sat = u3t(pat); if ( c3y == u3h(sat) ) { - u3l_log("_cj_hook_in failure: c3y == u3h(sat)\r\n"); + u3l_log("_cj_hook_in failure: c3y == u3h(sat)"); return u3m_bail(c3__fail); } else { @@ -1687,7 +1687,7 @@ u3j_gate_prep(u3j_site* sit_u, u3_noun cor) pay = u3qc_cap(pax), pam = u3qc_mas(pax); if ( 3 != pay || 2 == pam || (3 != pam && 3 != u3qc_cap(pam)) ) { - u3l_log("u3j_gate_prep(): parent axis includes sample\r\n"); + u3l_log("u3j_gate_prep(): parent axis includes sample"); u3m_p("axis", pax); u3_weak act = _cj_find_warm(loc); c3_assert( u3_none != act ); @@ -1758,12 +1758,12 @@ _cj_minx(u3_noun cey, u3_noun cor) par = u3r_at(axe, cor); if ( u3_none == par || c3n == u3du(par) ) { - u3l_log("fund: %s is bogus\r\n", u3r_string(nam)); + u3l_log("fund: %s is bogus", u3r_string(nam)); return u3_none; } pel = _cj_spot(par, NULL); if ( u3_none == pel ) { - u3l_log("fund: in %s, parent %x not found at %d\r\n", + u3l_log("fund: in %s, parent %x not found at %d", u3r_string(nam), u3r_mug(u3h(par)), axe); @@ -1822,7 +1822,7 @@ _cj_mine(u3_noun cey, u3_noun cor, u3_noun bas) jax_l = _cj_hot_mean(par_l, nam); #if 0 u3m_p("new jet", bal); - u3l_log(" bat %x, jax %d\r\n", u3r_mug(bat), jax_l); + u3l_log(" bat %x, jax %d", u3r_mug(bat), jax_l); #endif if ( !(u3C.wag_w & u3o_hashless) ) { @@ -1840,7 +1840,7 @@ _cj_mine(u3_noun cey, u3_noun cor, u3_noun bas) for ( i_w = 32; i_w > 0; ) { u3l_log("%02x", dig_y[--i_w]); } - u3l_log("\r\n"); + u3l_log(""); } } @@ -1870,7 +1870,7 @@ _cj_mine(u3_noun cey, u3_noun cor, u3_noun bas) if ( c3n == hav_o ) { u3m_p("unregistered battery", bal); - u3l_log("hash: %x\r\n", bas); + u3l_log("hash: %x", bas); } u3z(bas); } @@ -2093,7 +2093,7 @@ _cj_ream(u3_noun all) act = u3nq(jax_l, hap, bal, _cj_jit(jax_l, bat)); #if 0 u3m_p("old jet", bal); - u3l_log(" bat %x, jax %d\r\n", u3r_mug(bat), jax_l); + u3l_log(" bat %x, jax %d", u3r_mug(bat), jax_l); #endif u3h_put(u3R->jed.war_p, loc, act); } @@ -2132,7 +2132,7 @@ _cj_ream(u3_noun all) act = u3nq(jax_l, hap, bal, _cj_jit(jax_l, bat)); #if 0 u3m_p("old jet", bal); - u3l_log(" bat %x, jax %d\r\n", u3r_mug(bat), jax_l); + u3l_log(" bat %x, jax %d", u3r_mug(bat), jax_l); #endif u3h_put(u3R->jed.war_p, loc, act); } diff --git a/pkg/urbit/noun/log.c b/pkg/urbit/noun/log.c index 866a7430e2..ddab9d52bf 100644 --- a/pkg/urbit/noun/log.c +++ b/pkg/urbit/noun/log.c @@ -22,6 +22,8 @@ u3l_log(const char* format, ...) // this process did not set a logging function, fallback to stderr // vfprintf(stderr, format, myargs); + fprintf(stderr, "\r\n"); + fflush(stderr); } va_end(myargs); @@ -31,7 +33,7 @@ u3_weak u3l_punt(const char* name, u3_weak pro) { if ( u3_none == pro ) { - u3l_log("%s-punt\r\n", name); + u3l_log("%s-punt", name); } return pro; } diff --git a/pkg/urbit/noun/manage.c b/pkg/urbit/noun/manage.c index aa33b03287..7052c4d405 100644 --- a/pkg/urbit/noun/manage.c +++ b/pkg/urbit/noun/manage.c @@ -318,7 +318,7 @@ _cm_signal_recover(c3_l sig_l, u3_noun arg) while ( rod_u->kid_p ) { #if 0 - u3l_log("collecting %d frames\r\n", + u3l_log("collecting %d frames", u3kb_lent((u3to(u3_road, rod_u->kid_p)->bug.tax)); #endif tax = u3kb_weld(_cm_stack_recover(u3to(u3_road, rod_u->kid_p)), tax); @@ -374,7 +374,7 @@ _cm_signal_deep(c3_w mil_w) itm_u.it_value.tv_usec = 1000 * (mil_w % 1000); if ( rsignal_setitimer(ITIMER_VIRTUAL, &itm_u, 0) ) { - u3l_log("loom: set timer failed %s\r\n", strerror(errno)); + u3l_log("loom: set timer failed %s", strerror(errno)); } else { rsignal_install_handler(SIGVTALRM, _cm_signal_handle_alrm); @@ -407,7 +407,7 @@ _cm_signal_done() timerclear(&itm_u.it_value); if ( rsignal_setitimer(ITIMER_VIRTUAL, &itm_u, 0) ) { - u3l_log("loom: clear timer failed %s\r\n", strerror(errno)); + u3l_log("loom: clear timer failed %s", strerror(errno)); } } @@ -439,7 +439,7 @@ u3m_file(c3_c* pas_c) c3_y* pad_y; if ( (fid_i < 0) || (fstat(fid_i, &buf_b) < 0) ) { - u3l_log("%s: %s\r\n", pas_c, strerror(errno)); + u3l_log("%s: %s", pas_c, strerror(errno)); return u3m_bail(c3__fail); } fln_w = buf_b.st_size; @@ -639,7 +639,7 @@ u3m_dump(void) fre_u = fre_u->nex_u; } } - u3l_log("dump: hat_w %x, fre_w %x, allocated %x\n", + u3l_log("dump: hat_w %x, fre_w %x, allocated %x", hat_w, fre_w, (hat_w - fre_w)); if ( 0 != (hat_w - fre_w) ) { @@ -651,14 +651,14 @@ u3m_dump(void) if ( 0 != box_u->use_w ) { #ifdef U3_MEMORY_DEBUG - // u3l_log("live %d words, code %x\n", box_u->siz_w, box_u->cod_w); + // u3l_log("live %d words, code %x", box_u->siz_w, box_u->cod_w); #endif mem_w += box_u->siz_w; } box_w += box_u->siz_w; } - u3l_log("second count: %x\n", mem_w); + u3l_log("second count: %x", mem_w); } } #endif @@ -1585,7 +1585,7 @@ u3m_p(const c3_c* cap_c, u3_noun som) { c3_c* pre_c = u3m_pretty(som); - u3l_log("%s: %s\r\n", cap_c, pre_c); + u3l_log("%s: %s", cap_c, pre_c); c3_free(pre_c); } @@ -1646,7 +1646,7 @@ _cm_limits(void) rlm.rlim_cur = c3_min(rlm.rlim_max, (65536 << 10)); if ( 0 != setrlimit(RLIMIT_STACK, &rlm) ) { - u3l_log("boot: stack size: %s\r\n", strerror(errno)); + u3l_log("boot: stack size: %s", strerror(errno)); exit(1); } } @@ -1665,7 +1665,7 @@ _cm_limits(void) // no exit, not a critical limit // if ( 0 != setrlimit(RLIMIT_NOFILE, &rlm) ) { - u3l_log("boot: open file limit: %s\r\n", strerror(errno)); + u3l_log("boot: open file limit: %s", strerror(errno)); } } @@ -1679,7 +1679,7 @@ _cm_limits(void) // no exit, not a critical limit // if ( 0 != setrlimit(RLIMIT_CORE, &rlm) ) { - u3l_log("boot: core limit: %s\r\n", strerror(errno)); + u3l_log("boot: core limit: %s", strerror(errno)); } } # endif @@ -1702,7 +1702,7 @@ _cm_signals(void) // access and stack overflow exceptions. It calls u3e_fault directly. # else if ( 0 != sigsegv_install_handler(u3e_fault) ) { - u3l_log("boot: sigsegv install failed\n"); + u3l_log("boot: sigsegv install failed"); exit(1); } # endif @@ -1718,7 +1718,7 @@ _cm_signals(void) sigaddset(&set, SIGPROF); if ( 0 != pthread_sigmask(SIG_BLOCK, &set, NULL) ) { - u3l_log("boot: thread mask SIGPROF: %s\r\n", strerror(errno)); + u3l_log("boot: thread mask SIGPROF: %s", strerror(errno)); exit(1); } } @@ -1772,7 +1772,7 @@ _cm_crypto() if ( 0 == CRYPTO_set_mem_functions(&_cm_malloc_ssl, &_cm_realloc_ssl, &_cm_free_ssl) ) { - u3l_log("%s\r\n", "openssl initialization failed"); + u3l_log("%s", "openssl initialization failed"); abort(); } @@ -1815,7 +1815,7 @@ u3m_init(size_t len_i) || (len_i < (1 << (u3a_page + 2))) || (len_i > u3a_bytes) ) { - u3l_log("loom: bad size: %zu\r\n", len_i); + u3l_log("loom: bad size: %zu", len_i); exit(1); } @@ -1835,18 +1835,18 @@ u3m_init(size_t len_i) (MAP_ANON | MAP_PRIVATE), -1, 0); - u3l_log("boot: mapping %zuMB failed\r\n", len_i >> 20); + u3l_log("boot: mapping %zuMB failed", len_i >> 20); u3l_log("see urbit.org/using/install/#about-swap-space" - " for adding swap space\r\n"); + " for adding swap space"); if ( -1 != (c3_ps)map_v ) { - u3l_log("if porting to a new platform, try U3_OS_LoomBase %p\r\n", + u3l_log("if porting to a new platform, try U3_OS_LoomBase %p", map_v); } exit(1); } u3C.wor_i = len_i >> 2; - u3l_log("loom: mapped %zuMB\r\n", len_i >> 20); + u3l_log("loom: mapped %zuMB", len_i >> 20); } } @@ -1894,7 +1894,7 @@ u3m_boot(c3_c* dir_c, size_t len_i) */ { c3_w len_w = u3j_boot(nuu_o); - u3l_log("boot: installed %d jets\r\n", len_w); + u3l_log("boot: installed %d jets", len_w); } /* Reactivate jets on old kernel. diff --git a/pkg/urbit/noun/nock.c b/pkg/urbit/noun/nock.c index dda8a4921a..518ff51616 100644 --- a/pkg/urbit/noun/nock.c +++ b/pkg/urbit/noun/nock.c @@ -44,7 +44,7 @@ _n_hint(u3_noun zep, low_i = 1; if ( 0 == (u3R->pro.nox_d % 65536ULL) ) { if ( c3__spot == zep ) { - u3l_log("spot %d/%d : %d/%d\r\n", + u3l_log("spot %d/%d : %d/%d", u3h(u3h(u3t(hod))), u3t(u3h(u3t(hod))), u3h(u3t(u3t(hod))), diff --git a/pkg/urbit/noun/trace.c b/pkg/urbit/noun/trace.c index 79d290930d..841d51ca7f 100644 --- a/pkg/urbit/noun/trace.c +++ b/pkg/urbit/noun/trace.c @@ -167,7 +167,7 @@ _t_samp_process(u3_road* rod_u) pef = t_pef; } - // u3l_log("sample: stack length %d\r\n", u3kb_lent(u3k(pal))); + // u3l_log("sample: stack length %d", u3kb_lent(u3k(pal))); return pal; } } @@ -540,7 +540,7 @@ u3t_boot(void) sigemptyset(&set); sigaddset(&set, SIGPROF); if ( 0 != pthread_sigmask(SIG_UNBLOCK, &set, NULL) ) { - u3l_log("trace: thread mask SIGPROF: %s\r\n", strerror(errno)); + u3l_log("trace: thread mask SIGPROF: %s", strerror(errno)); } } @@ -569,7 +569,7 @@ u3t_boff(void) sigemptyset(&set); sigaddset(&set, SIGPROF); if ( 0 != pthread_sigmask(SIG_BLOCK, &set, NULL) ) { - u3l_log("trace: thread mask SIGPROF: %s\r\n", strerror(errno)); + u3l_log("trace: thread mask SIGPROF: %s", strerror(errno)); } } diff --git a/pkg/urbit/noun/vortex.c b/pkg/urbit/noun/vortex.c index 016588ea86..7b6eb0da45 100644 --- a/pkg/urbit/noun/vortex.c +++ b/pkg/urbit/noun/vortex.c @@ -60,9 +60,9 @@ _cv_lite(u3_noun pil) eve = tal; } - u3l_log("lite: arvo formula %x\r\n", u3r_mug(pil)); + u3l_log("lite: arvo formula %x", u3r_mug(pil)); pro = u3v_life(u3k(eve)); - u3l_log("lite: core %x\r\n", u3r_mug(pro)); + u3l_log("lite: core %x", u3r_mug(pro)); u3z(pil); return pro; @@ -89,7 +89,7 @@ u3v_boot_lite(u3_noun pil) u3z(pro); } - u3l_log("lite: final state %x\r\n", u3r_mug(u3A->roc)); + u3l_log("lite: final state %x", u3r_mug(u3A->roc)); return c3y; } @@ -198,7 +198,7 @@ u3v_lily(u3_noun fot, u3_noun txt, c3_l* tid_l) (c3n == u3r_safe_word(q_uco, &wad_w)) || (wad_w & 0x80000000) ) { - u3l_log("strange lily %s\n", u3r_string(txt)); + u3l_log("strange lily %s", u3r_string(txt)); u3z(txt); u3z(uco); return c3n; } else { diff --git a/pkg/urbit/vere/auto.c b/pkg/urbit/vere/auto.c index c03cd93e28..e1d4de0c04 100644 --- a/pkg/urbit/vere/auto.c +++ b/pkg/urbit/vere/auto.c @@ -91,7 +91,7 @@ u3_auto_bail_slog(u3_ovum* egg_u, u3_noun lud) c3_w len_w = 1; while ( u3_nul != dul ) { - u3l_log("%s: bail %u\r\n", car_c, len_w++); + u3l_log("%s: bail %u", car_c, len_w++); u3_pier_punt_goof(car_c, u3k(u3h(dul))); dul = u3t(dul); @@ -240,7 +240,7 @@ _auto_kick_lost(u3_noun pax, u3_noun fav) c3_c* tag_c = u3r_string(u3h(fav)); c3_c* pax_c = u3r_string(tox); - u3l_log("kick: lost %%%s on %s\n", tag_c, pax_c); + u3l_log("kick: lost %%%s on %s", tag_c, pax_c); c3_free(pax_c); c3_free(tag_c); @@ -376,12 +376,12 @@ u3_auto_slog(u3_auto* car_u) { u3_auto* nex_u; - u3l_log(" drivers:\n"); + u3l_log(" drivers:"); while ( car_u ) { nex_u = car_u->nex_u; - u3l_log(" %.*s: live=%s, queue=%u\n", + u3l_log(" %.*s: live=%s, queue=%u", u3r_met(3, car_u->nam_m), (c3_c*)&car_u->nam_m, ( c3y == car_u->liv_o ) ? "&" : "|", diff --git a/pkg/urbit/vere/dawn.c b/pkg/urbit/vere/dawn.c index ca6eb31a76..195d5ccac4 100644 --- a/pkg/urbit/vere/dawn.c +++ b/pkg/urbit/vere/dawn.c @@ -71,7 +71,7 @@ _dawn_post_json(c3_c* url_c, uv_buf_t lod_u) uv_buf_t buf_u = uv_buf_init(c3_malloc(1), 0); if ( !(curl = curl_easy_init()) ) { - u3l_log("failed to initialize libcurl\n"); + u3l_log("failed to initialize libcurl"); exit(1); } @@ -95,12 +95,12 @@ _dawn_post_json(c3_c* url_c, uv_buf_t lod_u) // XX retry? if ( CURLE_OK != result ) { - u3l_log("failed to fetch %s: %s\n", + u3l_log("failed to fetch %s: %s", url_c, curl_easy_strerror(result)); exit(1); } if ( 300 <= cod_l ) { - u3l_log("error fetching %s: HTTP %ld\n", url_c, cod_l); + u3l_log("error fetching %s: HTTP %ld", url_c, cod_l); exit(1); } @@ -122,7 +122,7 @@ _dawn_get_jam(c3_c* url_c) uv_buf_t buf_u = uv_buf_init(c3_malloc(1), 0); if ( !(curl = curl_easy_init()) ) { - u3l_log("failed to initialize libcurl\n"); + u3l_log("failed to initialize libcurl"); exit(1); } @@ -138,12 +138,12 @@ _dawn_get_jam(c3_c* url_c) // XX retry? if ( CURLE_OK != result ) { - u3l_log("failed to fetch %s: %s\n", + u3l_log("failed to fetch %s: %s", url_c, curl_easy_strerror(result)); exit(1); } if ( 300 <= cod_l ) { - u3l_log("error fetching %s: HTTP %ld\n", url_c, cod_l); + u3l_log("error fetching %s: HTTP %ld", url_c, cod_l); exit(1); } @@ -207,7 +207,7 @@ _dawn_fail(u3_noun who, u3_noun rac, u3_noun sas) } } - u3l_log("boot: invalid keys for %s '%s'\r\n", rac_c, how_c); + u3l_log("boot: invalid keys for %s '%s'", rac_c, how_c); // XX deconstruct sas, print helpful error messages while ( u3_nul != sas ) { @@ -226,7 +226,7 @@ static u3_noun _dawn_need_unit(u3_noun nit, c3_c* msg_c) { if ( u3_nul == nit ) { - u3l_log("%s\r\n", msg_c); + u3l_log("%s", msg_c); exit(1); } else { @@ -248,11 +248,11 @@ _dawn_turf(c3_c* dns_c) u3_noun rul = u3dc("rush", u3k(dns), u3k(par)); if ( (u3_nul == rul) || (c3n == u3h(u3t(rul))) ) { - u3l_log("boot: invalid domain specified with -H %s\r\n", dns_c); + u3l_log("boot: invalid domain specified with -H %s", dns_c); exit(1); } else { - u3l_log("boot: overriding network domains with %s\r\n", dns_c); + u3l_log("boot: overriding network domains with %s", dns_c); u3_noun dom = u3t(u3t(rul)); tuf = u3nc(u3k(dom), u3_nul); } @@ -308,7 +308,7 @@ u3_dawn_vent(u3_noun ship, u3_noun feed) pot = u3v_wish("*point:azimuth"); } else { - u3l_log("boot: retrieving %s's public keys\r\n", + u3l_log("boot: retrieving %s's public keys", u3_Host.ops_u.who_c); { @@ -327,7 +327,7 @@ u3_dawn_vent(u3_noun ship, u3_noun feed) u3_noun liv = u3_nul; // u3_noun liv = _dawn_get_json(parent, /some/url) - u3l_log("boot: verifying keys\r\n"); + u3l_log("boot: verifying keys"); // (each seed (lest error=@tas)) // @@ -339,7 +339,7 @@ u3_dawn_vent(u3_noun ship, u3_noun feed) return u3_none; } - u3l_log("boot: getting sponsor\r\n"); + u3l_log("boot: getting sponsor"); pos = _dawn_sponsor(u3k(ship), u3k(rank), u3k(pot)); u3z(pot); u3z(liv); } @@ -348,7 +348,7 @@ u3_dawn_vent(u3_noun ship, u3_noun feed) // (map ship [=life =pass]): galaxy table // { - u3l_log("boot: retrieving galaxy table\r\n"); + u3l_log("boot: retrieving galaxy table"); u3_noun oct = u3v_wish("czar:give:dawn"); u3_noun raz = _dawn_eth_rpc(url_c, u3k(oct)); @@ -364,7 +364,7 @@ u3_dawn_vent(u3_noun ship, u3_noun feed) tuf = _dawn_turf(u3_Host.ops_u.dns_c); } else { - u3l_log("boot: retrieving network domains\r\n"); + u3l_log("boot: retrieving network domains"); u3_noun oct = u3v_wish("turf:give:dawn"); u3_noun fut = _dawn_eth_rpc(url_c, u3k(oct)); @@ -382,7 +382,7 @@ u3_dawn_vent(u3_noun ship, u3_noun feed) { u3_noun who = u3dc("scot", 'p', u3k(pos)); c3_c* who_c = u3r_string(who); - u3l_log("boot: retrieving keys for sponsor %s\r\n", who_c); + u3l_log("boot: retrieving keys for sponsor %s", who_c); u3z(who); c3_free(who_c); } @@ -436,8 +436,8 @@ _dawn_come(u3_noun stars) c3_rand(eny_w); eny = u3i_words(16, eny_w); - u3l_log("boot: mining a comet. May take up to an hour.\r\n"); - u3l_log("If you want to boot faster, get an Urbit identity.\r\n"); + u3l_log("boot: mining a comet. May take up to an hour."); + u3l_log("If you want to boot faster, get an Urbit identity."); seed = u3dc("come:dawn", u3k(stars), u3k(eny)); u3z(eny); @@ -447,7 +447,7 @@ _dawn_come(u3_noun stars) u3_noun who = u3dc("scot", 'p', u3k(u3h(seed))); c3_c* who_c = u3r_string(who); - u3l_log("boot: found comet %s\r\n", who_c); + u3l_log("boot: found comet %s", who_c); // enable to print and save comet private key for future reuse // @@ -456,7 +456,7 @@ _dawn_come(u3_noun stars) u3_noun key = u3dc("scot", c3__uw, u3qe_jam(seed)); c3_c* key_c = u3r_string(key); - u3l_log("boot: comet private key\n %s\n", key_c); + u3l_log("boot: comet private key\n %s", key_c); { c3_c pat_c[64]; diff --git a/pkg/urbit/vere/disk.c b/pkg/urbit/vere/disk.c index eb4a275339..817a424c1c 100644 --- a/pkg/urbit/vere/disk.c +++ b/pkg/urbit/vere/disk.c @@ -242,7 +242,7 @@ u3_disk_plan(u3_disk* log_u, u3_fact* tac_u) { c3_assert( (1ULL + log_u->sen_d) == tac_u->eve_d ); log_u->sen_d++; - + if ( !log_u->put_u.ent_u ) { c3_assert( !log_u->put_u.ext_u ); log_u->put_u.ent_u = log_u->put_u.ext_u = tac_u; @@ -610,7 +610,7 @@ u3_disk_acquire(c3_c* pax_c) if ( NULL != (loq_u = c3_fopen(paf_c, "r")) ) { if ( 1 != fscanf(loq_u, "%" SCNu32, &pid_w) ) { - u3l_log("lockfile %s is corrupt!\n", paf_c); + u3l_log("lockfile %s is corrupt!", paf_c); kill(getpid(), SIGTERM); sleep(1); c3_assert(0); } @@ -620,13 +620,13 @@ u3_disk_acquire(c3_c* pax_c) int ret = kill(pid_w, SIGTERM); if ( -1 == ret && errno == EPERM ) { - u3l_log("disk: permission denied when trying to kill process %d!\n", pid_w); + u3l_log("disk: permission denied when trying to kill process %d!", pid_w); kill(getpid(), SIGTERM); sleep(1); c3_assert(0); } if ( -1 != ret ) { - u3l_log("disk: stopping process %d, live in %s...\n", + u3l_log("disk: stopping process %d, live in %s...", pid_w, pax_c); for ( i_w = 0; i_w < 16; i_w++ ) { @@ -644,10 +644,10 @@ u3_disk_acquire(c3_c* pax_c) } } if ( 16 == i_w ) { - u3l_log("disk: process %d seems unkillable!\n", pid_w); + u3l_log("disk: process %d seems unkillable!", pid_w); c3_assert(0); } - u3l_log("disk: stopped old process %u\n", pid_w); + u3l_log("disk: stopped old process %u", pid_w); } } fclose(loq_u); @@ -655,7 +655,7 @@ u3_disk_acquire(c3_c* pax_c) } if ( NULL == (loq_u = c3_fopen(paf_c, "w")) ) { - u3l_log("disk: unable to open %s\n", paf_c); + u3l_log("disk: unable to open %s", paf_c); c3_assert(0); } @@ -703,7 +703,7 @@ u3_disk_exit(u3_disk* log_u) if ( (c3y == log_u->ted_o) && (0 > uv_cancel(&log_u->req_u)) ) { - // u3l_log("disk: unable to cleanup\r\n"); + // u3l_log("disk: unable to cleanup"); return; } @@ -781,7 +781,7 @@ u3_disk_info(u3_disk* log_u) void u3_disk_slog(u3_disk* log_u) { - u3l_log(" disk: live=%s, event=%" PRIu64 "\n", + u3l_log(" disk: live=%s, event=%" PRIu64, ( c3y == log_u->liv_o ) ? "&" : "|", log_u->dun_d); @@ -789,7 +789,7 @@ u3_disk_slog(u3_disk* log_u) u3_read* red_u = log_u->red_u; while ( red_u ) { - u3l_log(" read: %" PRIu64 "-%" PRIu64 "\n", + u3l_log(" read: %" PRIu64 "-%" PRIu64, red_u->eve_d, (red_u->eve_d + red_u->len_d) - 1); red_u = red_u->nex_u; @@ -798,12 +798,12 @@ u3_disk_slog(u3_disk* log_u) if ( log_u->put_u.ext_u ) { if ( log_u->put_u.ext_u != log_u->put_u.ent_u ) { - u3l_log(" save: %" PRIu64 "-%" PRIu64 "\n", + u3l_log(" save: %" PRIu64 "-%" PRIu64, log_u->put_u.ext_u->eve_d, log_u->put_u.ent_u->eve_d); } else { - u3l_log(" save: %" PRIu64 "\n", log_u->put_u.ext_u->eve_d); + u3l_log(" save: %" PRIu64, log_u->put_u.ext_u->eve_d); } } } diff --git a/pkg/urbit/vere/foil.c b/pkg/urbit/vere/foil.c index 1d844f5d41..186c8fdecd 100644 --- a/pkg/urbit/vere/foil.c +++ b/pkg/urbit/vere/foil.c @@ -36,10 +36,10 @@ static void _foil_fail(const c3_c* why_c, c3_i err_i) { if ( err_i ) { - u3l_log("%s: error: %s\r\n", why_c, uv_strerror(err_i)); + u3l_log("%s: error: %s", why_c, uv_strerror(err_i)); c3_assert(0); } else { - u3l_log("%s: file error\r\n", why_c); + u3l_log("%s: file error", why_c); } exit(1); } diff --git a/pkg/urbit/vere/io/ames.c b/pkg/urbit/vere/io/ames.c index bdc3201afc..913045b3ed 100644 --- a/pkg/urbit/vere/io/ames.c +++ b/pkg/urbit/vere/io/ames.c @@ -341,7 +341,7 @@ _ames_send_cb(uv_udp_send_t* req_u, c3_i sas_i) u3_ames* sam_u = pac_u->sam_u; if ( sas_i && (c3y == sam_u->fig_u.net_o) ) { - u3l_log("ames: send fail: %s\n", uv_strerror(sas_i)); + u3l_log("ames: send fail: %s", uv_strerror(sas_i)); sam_u->fig_u.net_o = c3n; } else { @@ -380,7 +380,7 @@ _ames_send(u3_pact* pac_u) if ( sas_i ) { if ( c3y == sam_u->fig_u.net_o ) { - u3l_log("ames: send fail: %s\n", uv_strerror(sas_i)); + u3l_log("ames: send fail: %s", uv_strerror(sas_i)); sam_u->fig_u.net_o = c3n; } @@ -517,7 +517,7 @@ _ames_czar_gone(u3_pact* pac_u, time_t now) u3_ames* sam_u = pac_u->sam_u; if ( c3y == sam_u->imp_o[pac_u->imp_y] ) { - u3l_log("ames: czar at %s: not found (b)\n", pac_u->dns_c); + u3l_log("ames: czar at %s: not found (b)", pac_u->dns_c); sam_u->imp_o[pac_u->imp_y] = c3n; } @@ -547,7 +547,7 @@ _ames_czar_here(u3_pact* pac_u, time_t now, struct sockaddr_in* add_u) u3_noun nam = u3dc("scot", c3__if, u3i_word(pip_w)); c3_c* nam_c = u3r_string(nam); - u3l_log("ames: czar %s: ip %s\n", pac_u->dns_c, nam_c); + u3l_log("ames: czar %s: ip %s", pac_u->dns_c, nam_c); c3_free(nam_c); u3z(nam); @@ -612,7 +612,7 @@ _ames_czar(u3_pact* pac_u) if ( !sam_u->dns_c ) { u3_noun nam = u3dc("scot", 'p', pac_u->imp_y); c3_c* nam_c = u3r_string(nam); - u3l_log("ames: no galaxy domain for %s, no-op\r\n", nam_c); + u3l_log("ames: no galaxy domain for %s, no-op", nam_c); c3_free(nam_c); u3z(nam); @@ -657,7 +657,7 @@ _ames_czar(u3_pact* pac_u) } if ( 255 <= sas_i ) { - u3l_log("ames: czar: galaxy domain %s truncated\n", sam_u->dns_c); + u3l_log("ames: czar: galaxy domain %s truncated", sam_u->dns_c); _ames_pact_free(pac_u); return; } @@ -670,7 +670,7 @@ _ames_czar(u3_pact* pac_u) _ames_czar_cb, pac_u->dns_c, 0, 0)) ) { - u3l_log("ames: %s\n", uv_strerror(sas_i)); + u3l_log("ames: %s", uv_strerror(sas_i)); _ames_czar_gone(pac_u, now); return; } @@ -685,7 +685,7 @@ static void _ames_ef_send(u3_ames* sam_u, u3_noun lan, u3_noun pac) { if ( c3n == sam_u->car_u.liv_o ) { - u3l_log("ames: not yet live, dropping outbound\r\n"); + u3l_log("ames: not yet live, dropping outbound"); u3z(lan); u3z(pac); return; } @@ -726,7 +726,7 @@ _ames_ef_send(u3_ames* sam_u, u3_noun lan, u3_noun pac) // else if ( 0 == lan_u.por_s ) { if ( u3C.wag_w & u3o_verbose ) { - u3l_log("ames: inscrutable lane\n"); + u3l_log("ames: inscrutable lane"); } _ames_pact_free(pac_u); } @@ -755,7 +755,7 @@ _ames_cap_queue(u3_ames* sam_u) sam_u->sat_u.dop_d++; if ( u3C.wag_w & u3o_verbose ) { - u3l_log("ames: packet dropped (%" PRIu64 " total)\n", sam_u->sat_u.dop_d); + u3l_log("ames: packet dropped (%" PRIu64 " total)", sam_u->sat_u.dop_d); } } @@ -765,7 +765,7 @@ _ames_cap_queue(u3_ames* sam_u) if ( (sam_u->sat_u.dop_d && (0 == (sam_u->sat_u.dop_d % 1000))) && !(u3C.wag_w & u3o_verbose) ) { - u3l_log("ames: packet dropped (%" PRIu64 " total)\n", sam_u->sat_u.dop_d); + u3l_log("ames: packet dropped (%" PRIu64 " total)", sam_u->sat_u.dop_d); } } @@ -784,7 +784,7 @@ _ames_hear_bail(u3_ovum* egg_u, u3_noun lud) || (0 == (sam_u->sat_u.bad_d % 100)) ) { u3l_log("ames: heard bad crypto (%" PRIu64 " total), " - "check azimuth state\r\n", + "check azimuth state", sam_u->sat_u.bad_d); } } @@ -804,7 +804,7 @@ _ames_hear_bail(u3_ovum* egg_u, u3_noun lud) u3_pier_punt_goof("hear", u3k(u3h(lud))); } - u3l_log("ames: packet failed (%" PRIu64 " total)\r\n", + u3l_log("ames: packet failed (%" PRIu64 " total)", sam_u->sat_u.fal_d); } } @@ -840,7 +840,7 @@ _ames_forward(u3_panc* pac_u, u3_noun las) sam_u->sat_u.fow_d++; if ( 0 == (sam_u->sat_u.fow_d % 1000000) ) { - u3l_log("ames: forwarded %" PRIu64 " total\n", sam_u->sat_u.fow_d); + u3l_log("ames: forwarded %" PRIu64 " total", sam_u->sat_u.fow_d); } if ( u3C.wag_w & u3o_verbose ) { @@ -851,7 +851,7 @@ _ames_forward(u3_panc* pac_u, u3_noun las) c3_y* pip_y = (c3_y*)&pac_u->ore_u.pip_w; //NOTE ip byte order assumes little-endian - u3l_log("ames: forwarding for %s to %s from %d.%d.%d.%d:%d\n", + u3l_log("ames: forwarding for %s to %s from %d.%d.%d.%d:%d", sen_c, rec_c, pip_y[3], pip_y[2], pip_y[1], pip_y[0], pac_u->ore_u.por_s); @@ -870,7 +870,7 @@ _ames_forward(u3_panc* pac_u, u3_noun las) // validate lane and skip self if galaxy // if ( c3n == u3r_cell(lan, &tag, &dat) ) { - u3l_log("ames: bogus lane\n"); + u3l_log("ames: bogus lane"); u3m_p("lan", lan); } else { @@ -885,7 +885,7 @@ _ames_forward(u3_panc* pac_u, u3_noun las) { sen_o = c3n; if ( u3C.wag_w & u3o_verbose ) { - u3l_log("ames: forward skipping self\n"); + u3l_log("ames: forward skipping self"); } } } @@ -918,7 +918,7 @@ _ames_lane_scry_cb(void* vod_p, u3_noun nun) // if ( u3_none == las ) { if ( 5 < ++sam_u->sat_u.saw_d ) { - u3l_log("ames: giving up scry\n"); + u3l_log("ames: giving up scry"); sam_u->fig_u.see_o = c3n; } _ames_put_packet(sam_u, _ames_serialize_packet(pac_u, c3n), pac_u->ore_u); @@ -982,7 +982,7 @@ _ames_try_forward(u3_ames* sam_u, if ( (u3_none == lac) && (1000 < sam_u->sat_u.foq_d) ) { sam_u->sat_u.fod_d++; if ( 0 == (sam_u->sat_u.fod_d % 10000) ) { - u3l_log("ames: dropped %" PRIu64 " forwards total\n", + u3l_log("ames: dropped %" PRIu64 " forwards total", sam_u->sat_u.fod_d); } @@ -1086,7 +1086,7 @@ _ames_hear(u3_ames* sam_u, { sam_u->sat_u.hed_d++; if ( 0 == (sam_u->sat_u.hed_d % 100000) ) { - u3l_log("ames: %" PRIu64 " dropped, failed to read header\n", + u3l_log("ames: %" PRIu64 " dropped, failed to read header", sam_u->sat_u.hed_d); } @@ -1103,7 +1103,7 @@ _ames_hear(u3_ames* sam_u, { sam_u->sat_u.vet_d++; if ( 0 == (sam_u->sat_u.vet_d % 100000) ) { - u3l_log("ames: %" PRIu64 " dropped for version mismatch\n", + u3l_log("ames: %" PRIu64 " dropped for version mismatch", sam_u->sat_u.vet_d); } @@ -1120,7 +1120,7 @@ _ames_hear(u3_ames* sam_u, if ( (c3n == _ames_sift_body(&hed_u, &bod_u, bod_w, bod_y)) ) { sam_u->sat_u.bod_d++; if ( 0 == (sam_u->sat_u.bod_d % 100) ) { - u3l_log("ames: %" PRIu64 " dropped, failed to read body\n", + u3l_log("ames: %" PRIu64 " dropped, failed to read body", sam_u->sat_u.bod_d); } @@ -1133,7 +1133,7 @@ _ames_hear(u3_ames* sam_u, if ( bod_u.mug_l != hed_u.mug_l ) { sam_u->sat_u.mut_d++; if ( 0 == (sam_u->sat_u.mut_d % 100000) ) { - u3l_log("ames: %" PRIu64 " dropped for invalid mug\n", + u3l_log("ames: %" PRIu64 " dropped for invalid mug", sam_u->sat_u.mut_d); } @@ -1181,7 +1181,7 @@ _ames_recv_cb(uv_udp_t* wax_u, { if ( 0 > nrd_i ) { if ( u3C.wag_w & u3o_verbose ) { - u3l_log("ames: recv: fail: %s\n", uv_strerror(nrd_i)); + u3l_log("ames: recv: fail: %s", uv_strerror(nrd_i)); } c3_free(buf_u->base); } @@ -1190,7 +1190,7 @@ _ames_recv_cb(uv_udp_t* wax_u, } else if ( flg_i & UV_UDP_PARTIAL ) { if ( u3C.wag_w & u3o_verbose ) { - u3l_log("ames: recv: fail: message truncated\n"); + u3l_log("ames: recv: fail: message truncated"); } c3_free(buf_u->base); } @@ -1226,8 +1226,8 @@ _ames_io_start(u3_ames* sam_u) por_s = zar_s; } else if ( por_s != zar_s ) { - u3l_log("ames: czar: overriding port %d with -p %d\n", zar_s, por_s); - u3l_log("ames: czar: WARNING: %d required for discoverability\n", zar_s); + u3l_log("ames: czar: overriding port %d with -p %d", zar_s, por_s); + u3l_log("ames: czar: WARNING: %d required for discoverability", zar_s); } } @@ -1246,12 +1246,12 @@ _ames_io_start(u3_ames* sam_u) if ( (ret_i = uv_udp_bind(&sam_u->wax_u, (const struct sockaddr*)&add_u, 0)) != 0 ) { - u3l_log("ames: bind: %s\n", uv_strerror(ret_i)); + u3l_log("ames: bind: %s", uv_strerror(ret_i)); if ( (c3__czar == rac) && (UV_EADDRINUSE == ret_i) ) { - u3l_log(" ...perhaps you've got two copies of vere running?\n"); + u3l_log(" ...perhaps you've got two copies of vere running?"); } // XX revise @@ -1266,10 +1266,10 @@ _ames_io_start(u3_ames* sam_u) } if ( c3y == u3_Host.ops_u.net ) { - u3l_log("ames: live on %d\n", sam_u->pir_u->por_s); + u3l_log("ames: live on %d", sam_u->pir_u->por_s); } else { - u3l_log("ames: live on %d (localhost only)\n", sam_u->pir_u->por_s); + u3l_log("ames: live on %d (localhost only)", sam_u->pir_u->por_s); } uv_udp_recv_start(&sam_u->wax_u, _ames_alloc, _ames_recv_cb); @@ -1300,7 +1300,7 @@ _ames_ef_turf(u3_ames* sam_u, u3_noun tuf) u3z(tuf); } else if ( (c3n == sam_u->pir_u->fak_o) && (0 == sam_u->dns_c) ) { - u3l_log("ames: turf: no domains\n"); + u3l_log("ames: turf: no domains"); } // XX is this ever necessary? @@ -1444,7 +1444,7 @@ _ames_io_kick(u3_auto* car_u, u3_noun wir, u3_noun cad) ret_o = c3n; } else { - u3l_log("kick: strange send\r\n"); + u3l_log("kick: strange send"); ret_o = _ames_kick_newt(sam_u, u3k(tag), u3k(dat)); } } break; @@ -1521,21 +1521,21 @@ _ames_io_slog(u3_auto* car_u) // TODO rewrite in terms of info_f // - u3l_log(" config:\n"); - u3l_log(" filtering: %s\n", FLAG(sam_u->fig_u.fit_o)); - u3l_log(" can send: %s\n", FLAG(sam_u->fig_u.net_o)); - u3l_log(" can scry: %s\n", FLAG(sam_u->fig_u.see_o)); - u3l_log(" counters:\n"); - u3l_log(" dropped: %" PRIu64 "\n", sam_u->sat_u.dop_d); - u3l_log(" forwards dropped: %" PRIu64 "\n", sam_u->sat_u.fod_d); - u3l_log(" forwards pending: %" PRIu64 "\n", sam_u->sat_u.foq_d); - u3l_log(" forwarded: %" PRIu64 "\n", sam_u->sat_u.fow_d); - u3l_log(" filtered (hed): %" PRIu64 "\n", sam_u->sat_u.hed_d); - u3l_log(" filtered (ver): %" PRIu64 "\n", sam_u->sat_u.vet_d); - u3l_log(" filtered (mug): %" PRIu64 "\n", sam_u->sat_u.mut_d); - u3l_log(" filtered (bod): %" PRIu64 "\n", sam_u->sat_u.bod_d); - u3l_log(" crashed: %" PRIu64 "\n", sam_u->sat_u.fal_d); - u3l_log(" cached lanes: %u\n", u3h_wyt(sam_u->lax_p)); + u3l_log(" config:"); + u3l_log(" filtering: %s", FLAG(sam_u->fig_u.fit_o)); + u3l_log(" can send: %s", FLAG(sam_u->fig_u.net_o)); + u3l_log(" can scry: %s", FLAG(sam_u->fig_u.see_o)); + u3l_log(" counters:"); + u3l_log(" dropped: %" PRIu64, sam_u->sat_u.dop_d); + u3l_log(" forwards dropped: %" PRIu64, sam_u->sat_u.fod_d); + u3l_log(" forwards pending: %" PRIu64, sam_u->sat_u.foq_d); + u3l_log(" forwarded: %" PRIu64, sam_u->sat_u.fow_d); + u3l_log(" filtered (hed): %" PRIu64, sam_u->sat_u.hed_d); + u3l_log(" filtered (ver): %" PRIu64, sam_u->sat_u.vet_d); + u3l_log(" filtered (mug): %" PRIu64, sam_u->sat_u.mut_d); + u3l_log(" filtered (bod): %" PRIu64, sam_u->sat_u.bod_d); + u3l_log(" crashed: %" PRIu64, sam_u->sat_u.fal_d); + u3l_log(" cached lanes: %u", u3h_wyt(sam_u->lax_p)); } /* u3_ames_io_init(): initialize ames I/O. diff --git a/pkg/urbit/vere/io/behn.c b/pkg/urbit/vere/io/behn.c index 45a4347738..7f42805a36 100644 --- a/pkg/urbit/vere/io/behn.c +++ b/pkg/urbit/vere/io/behn.c @@ -48,7 +48,7 @@ _behn_wake_bail(u3_ovum* egg_u, u3_noun lud) u3_auto_bail_slog(egg_u, lud); u3_ovum_free(egg_u); - u3l_log("behn: timer failed; queue blocked\n"); + u3l_log("behn: timer failed; queue blocked"); // XX review, add flag to continue? // @@ -149,7 +149,7 @@ _behn_born_bail(u3_ovum* egg_u, u3_noun lud) u3_auto_bail_slog(egg_u, lud); u3_ovum_free(egg_u); - u3l_log("behn: initialization failed\n"); + u3l_log("behn: initialization failed"); // XX review, add flag to continue? // diff --git a/pkg/urbit/vere/io/conn.c b/pkg/urbit/vere/io/conn.c index ab717dd1ea..86d9087ff9 100644 --- a/pkg/urbit/vere/io/conn.c +++ b/pkg/urbit/vere/io/conn.c @@ -333,7 +333,7 @@ _conn_moor_bail(void* ptr_v, ssize_t err_i, const c3_c* err_c) u3_shan* san_u = can_u->san_u; if ( err_i != UV_EOF ) { - u3l_log("conn: moor bail %zd %s\n", err_i, err_c); + u3l_log("conn: moor bail %zd %s", err_i, err_c); if ( _(can_u->liv_o) ) { _conn_send_noun(can_u, u3nq(0, c3__bail, u3i_word(err_i), u3i_string(err_c))); @@ -573,7 +573,7 @@ _conn_moor_poke(void* ptr_v, c3_d len_d, c3_y* byt_y) rud = u3dc("scot", c3__uv, u3k(rid)); tag_c = u3r_string(tag); rid_c = u3r_string(rud); - u3l_log("conn: %s %s\n", tag_c, rid_c); + u3l_log("conn: %s %s", tag_c, rid_c); c3_free(tag_c); c3_free(rid_c); switch (tag) { @@ -690,7 +690,7 @@ _conn_init_sock(u3_shan* san_u) c3_assert(!ret_i); ret_i = uv_listen((uv_stream_t*)&san_u->pyp_u, 0, _conn_sock_cb); c3_assert(!ret_i); - u3l_log("conn: listening on %s\n", pip_c); + u3l_log("conn: listening on %s", pip_c); #else // _WIN32 // the full socket path is limited to about 108 characters, @@ -702,46 +702,46 @@ _conn_init_sock(u3_shan* san_u) c3_i err_i; if ( NULL == getcwd(pax_c, sizeof(pax_c)) ) { - u3l_log("conn: getcwd: %s\n", uv_strerror(errno)); + u3l_log("conn: getcwd: %s", uv_strerror(errno)); u3_king_bail(); } if ( 0 != chdir(u3_Host.dir_c) ) { - u3l_log("conn: chdir: %s\n", uv_strerror(errno)); + u3l_log("conn: chdir: %s", uv_strerror(errno)); u3_king_bail(); } if ( 0 != unlink(URB_SOCK_PATH) && errno != ENOENT ) { - u3l_log("conn: unlink: %s\n", uv_strerror(errno)); + u3l_log("conn: unlink: %s", uv_strerror(errno)); goto _conn_sock_err_chdir; } if ( 0 != (err_i = uv_pipe_init(u3L, &san_u->pyp_u, 0)) ) { - u3l_log("conn: uv_pipe_init: %s\n", uv_strerror(err_i)); + u3l_log("conn: uv_pipe_init: %s", uv_strerror(err_i)); goto _conn_sock_err_chdir; } if ( 0 != (err_i = uv_pipe_bind(&san_u->pyp_u, URB_SOCK_PATH)) ) { - u3l_log("conn: uv_pipe_bind: %s\n", uv_strerror(err_i)); + u3l_log("conn: uv_pipe_bind: %s", uv_strerror(err_i)); goto _conn_sock_err_chdir; } if ( 0 != (err_i = uv_listen((uv_stream_t*)&san_u->pyp_u, 0, _conn_sock_cb)) ) { - u3l_log("conn: uv_listen: %s\n", uv_strerror(err_i)); + u3l_log("conn: uv_listen: %s", uv_strerror(err_i)); goto _conn_sock_err_unlink; } if ( 0 != chdir(pax_c) ) { - u3l_log("conn: chdir: %s\n", uv_strerror(errno)); + u3l_log("conn: chdir: %s", uv_strerror(errno)); goto _conn_sock_err_close; } - u3l_log("conn: listening on %s/%s\n", u3_Host.dir_c, URB_SOCK_PATH); + u3l_log("conn: listening on %s/%s", u3_Host.dir_c, URB_SOCK_PATH); return; _conn_sock_err_close: uv_close((uv_handle_t*)&san_u->pyp_u, _conn_close_cb); _conn_sock_err_unlink: if ( 0 != unlink(URB_SOCK_PATH) ) { - u3l_log("conn: unlink: %s\n", uv_strerror(errno)); + u3l_log("conn: unlink: %s", uv_strerror(errno)); } _conn_sock_err_chdir: if ( 0 != chdir(pax_c) ) { - u3l_log("conn: chdir: %s\n", uv_strerror(errno)); + u3l_log("conn: chdir: %s", uv_strerror(errno)); } u3_king_bail(); #endif // _WIN32 @@ -764,7 +764,7 @@ _conn_born_news(u3_ovum* egg_u, u3_ovum_news new_e) static void _conn_born_bail(u3_ovum* egg_u, u3_noun lud) { - u3l_log("conn: %%born failure; %%fyrd not supported\n"); + u3l_log("conn: %%born failure; %%fyrd not supported"); u3z(lud); u3_ovum_free(egg_u); } @@ -820,7 +820,7 @@ _conn_ef_handle(u3_conn* con_u, } } else { - u3l_log("conn: handle-no-coq %" PRIx32 " %" PRIu32 "\n", + u3l_log("conn: handle-no-coq %" PRIx32 " %" PRIu32, sev_l, coq_l); } u3z(rid); u3z(tag); u3z(dat); @@ -866,11 +866,11 @@ _conn_io_exit(u3_auto* car_u) if ( 0 != unlink(paf_c) ) { if ( ENOENT != errno ) { - u3l_log("conn: failed to unlink socket: %s\n", uv_strerror(errno)); + u3l_log("conn: failed to unlink socket: %s", uv_strerror(errno)); } } else { - // u3l_log("conn: unlinked %s\n", paf_c); + // u3l_log("conn: unlinked %s", paf_c); } c3_free(paf_c); diff --git a/pkg/urbit/vere/io/cttp.c b/pkg/urbit/vere/io/cttp.c index 1f19c3e414..76c1dd3966 100644 --- a/pkg/urbit/vere/io/cttp.c +++ b/pkg/urbit/vere/io/cttp.c @@ -558,7 +558,7 @@ _cttp_creq_new(u3_cttp* ctp_u, c3_l num_l, u3_noun hes) if ( c3n == u3r_du(unit_pul) ) { c3_c* url_c = u3r_string(url); - u3l_log("cttp: unable to parse url:\n %s\n", url_c); + u3l_log("cttp: unable to parse url:\n %s", url_c); c3_free(url_c); u3z(hes); return 0; @@ -735,7 +735,7 @@ _cttp_creq_fail(u3_creq* ceq_u, const c3_c* err_c) // XX anything other than a 504? c3_w cod_w = 504; - u3l_log("http: fail (%d, %d): %s\r\n", ceq_u->num_l, cod_w, err_c); + u3l_log("http: fail (%d, %d): %s", ceq_u->num_l, cod_w, err_c); // XX include err_c as response body? _cttp_http_client_receive(ceq_u, cod_w, u3_nul, u3_nul); @@ -1003,7 +1003,7 @@ _cttp_ef_http_client(u3_cttp* ctp_u, u3_noun tag, u3_noun dat) if ( (c3n == u3r_cell(dat, &num, &req)) || (c3n == u3r_safe_word(num, &num_l)) ) { - u3l_log("cttp: strange request\n"); + u3l_log("cttp: strange request"); ret_o = c3n; } else if ( (ceq_u = _cttp_creq_new(ctp_u, num_l, u3k(req))) ) { @@ -1018,7 +1018,7 @@ _cttp_ef_http_client(u3_cttp* ctp_u, u3_noun tag, u3_noun dat) c3_l num_l; if ( c3n == u3r_safe_word(dat, &num_l) ) { - u3l_log("cttp: strange cancel-request\n"); + u3l_log("cttp: strange cancel-request"); ret_o = c3n; } else if ( (ceq_u =_cttp_creq_find(ctp_u, num_l)) ) { @@ -1032,7 +1032,7 @@ _cttp_ef_http_client(u3_cttp* ctp_u, u3_noun tag, u3_noun dat) } } else { - u3l_log("cttp: strange effect (unknown type)\n"); + u3l_log("cttp: strange effect (unknown type)"); ret_o = c3n; } diff --git a/pkg/urbit/vere/io/fore.c b/pkg/urbit/vere/io/fore.c index 7354a8225d..2fdd022371 100644 --- a/pkg/urbit/vere/io/fore.c +++ b/pkg/urbit/vere/io/fore.c @@ -10,7 +10,7 @@ static void _fore_inject_bail(u3_ovum* egg_u, u3_noun lud) { u3_auto_bail_slog(egg_u, lud); - u3l_log("pier: injected event failed\n"); + u3l_log("pier: injected event failed"); u3_ovum_free(egg_u); } @@ -21,7 +21,7 @@ static void _fore_import_bail(u3_ovum* egg_u, u3_noun lud) { u3_auto_bail_slog(egg_u, lud); - u3l_log("pier: import failed\n"); + u3l_log("pier: import failed"); u3_ovum_free(egg_u); } @@ -37,20 +37,20 @@ _fore_inject(u3_auto* car_u, c3_c* pax_c) u3_noun riw, cad, tar, wir; if ( c3n == u3r_cell(ovo, &riw, &cad) ) { - u3l_log("pier: invalid ovum in -I\n"); + u3l_log("pier: invalid ovum in -I"); } else if ( (c3n == u3a_is_cell(cad)) || (c3n == u3a_is_atom(u3h(cad))) ) { - u3l_log("pier: invalid card in -I ovum\n"); + u3l_log("pier: invalid card in -I ovum"); } else if ( c3n == u3r_cell(riw, &tar, &wir) ) { - u3l_log("pier: invalid wire in -I ovum\n"); + u3l_log("pier: invalid wire in -I ovum"); } else if ( (c3n == u3a_is_atom(tar)) || (4 < u3r_met(3, tar)) ) { - u3l_log("pier: invalid target in -I wire\n"); + u3l_log("pier: invalid target in -I wire"); } else { { @@ -58,7 +58,7 @@ _fore_inject(u3_auto* car_u, c3_c* pax_c) u3_noun ser = u3do("spat", u3k(riw)); c3_c* wir_c = u3r_string(ser); - u3l_log("pier: injecting %%%s event on %s\n", tag_c, wir_c); + u3l_log("pier: injecting %%%s event on %s", tag_c, wir_c); c3_free(tag_c); c3_free(wir_c); diff --git a/pkg/urbit/vere/io/hind.c b/pkg/urbit/vere/io/hind.c index b0d207e838..d15534806e 100644 --- a/pkg/urbit/vere/io/hind.c +++ b/pkg/urbit/vere/io/hind.c @@ -30,7 +30,7 @@ _hind_io_kick(u3_auto* car_u, u3_noun wir, u3_noun cad) case c3__exit: { ret_o = c3y; - u3l_log("<<>>\n"); + u3l_log("<<>>"); u3_pier_exit(car_u->pir_u); } break; @@ -44,7 +44,7 @@ _hind_io_kick(u3_auto* car_u, u3_noun wir, u3_noun cad) case c3__vega: { ret_o = c3y; - u3l_log("<<>>\n"); + u3l_log("<<>>"); } break; // NB: startup explicitly handled in pier.c diff --git a/pkg/urbit/vere/io/http.c b/pkg/urbit/vere/io/http.c index fa7d778f2c..3f90df80dc 100644 --- a/pkg/urbit/vere/io/http.c +++ b/pkg/urbit/vere/io/http.c @@ -33,7 +33,7 @@ typedef struct _u3_h2o_serv { void* gen_u; // response generator struct _u3_hcon* hon_u; // connection backlink struct _u3_hreq* nex_u; // next in connection's list - struct _u3_hreq* pre_u; // next in connection's list + struct _u3_hreq* pre_u; // prev in connection's list } u3_hreq; /* u3_hcon: incoming http connection. @@ -48,7 +48,7 @@ typedef struct _u3_h2o_serv { struct _u3_http* htp_u; // server backlink struct _u3_hreq* req_u; // request list struct _u3_hcon* nex_u; // next in server's list - struct _u3_hcon* pre_u; // next in server's list + struct _u3_hcon* pre_u; // prev in server's list } u3_hcon; /* u3_http: http server. @@ -640,10 +640,10 @@ _http_start_respond(u3_hreq* req_u, u3_noun data, u3_noun complete) { - // u3l_log("start\n"); + // u3l_log("start"); if ( u3_rsat_plan != req_u->sat_e ) { - //u3l_log("duplicate response\n"); + //u3l_log("duplicate response"); return; } @@ -724,14 +724,14 @@ _http_continue_respond(u3_hreq* req_u, u3_noun data, u3_noun complete) { - // u3l_log("continue\n"); + // u3l_log("continue"); // XX add sequence numbers for %continue effects? // Arvo does not (currently) guarantee effect idempotence!! // response has not yet been started if ( u3_rsat_ripe != req_u->sat_e ) { - // u3l_log("duplicate response\n"); + // u3l_log("duplicate response"); return; } @@ -874,7 +874,7 @@ _http_seq_continue(void* vod_p, u3_noun nun) _http_start_respond(req_u, 403, u3_nul, u3_nul, c3y); } else if ( u3_none == aut ) { - u3l_log("http: authentication scry failed\n"); + u3l_log("http: authentication scry failed"); _http_start_respond(req_u, 500, u3_nul, u3_nul, c3y); } else { @@ -962,7 +962,7 @@ _http_rec_accept(h2o_handler_t* han_u, h2o_req_t* rec_u) if ( u3_none == req ) { if ( (u3C.wag_w & u3o_verbose) ) { - u3l_log("strange %.*s request\n", (c3_i)rec_u->method.len, + u3l_log("strange %.*s request", (c3_i)rec_u->method.len, rec_u->method.base); } c3_c* msg_c = "bad request"; @@ -1052,7 +1052,7 @@ _http_conn_free(uv_handle_t* han_t) noh_u = noh_u->nex_u; } - u3l_log("http conn free %d of %u server %d\n", hon_u->coq_l, len_w, htp_u->sev_l); + u3l_log("http conn free %d of %u server %d", hon_u->coq_l, len_w, htp_u->sev_l); } #endif @@ -1069,13 +1069,13 @@ _http_conn_free(uv_handle_t* han_t) noh_u = noh_u->nex_u; } - u3l_log("http conn free %u remaining\n", len_w); + u3l_log("http conn free %u remaining", len_w); } #endif if ( (0 == htp_u->hon_u) && (0 != h2o_u->ctx_u.shutdown_requested) ) { #if 0 - u3l_log("http conn free %d free server %d\n", hon_u->coq_l, htp_u->sev_l); + u3l_log("http conn free %d free server %d", hon_u->coq_l, htp_u->sev_l); #endif _http_serv_free(htp_u); } @@ -1099,7 +1099,7 @@ _http_conn_new(u3_http* htp_u) _http_conn_link(htp_u, hon_u); #if 0 - u3l_log("http conn neww %d server %d\n", hon_u->coq_l, htp_u->sev_l); + u3l_log("http conn neww %d server %d", hon_u->coq_l, htp_u->sev_l); #endif return hon_u; @@ -1149,7 +1149,7 @@ _http_serv_unlink(u3_http* htp_u) { // XX link elsewhere initially, relink on start? #if 0 - u3l_log("http serv unlink %d\n", htp_u->sev_l); + u3l_log("http serv unlink %d", htp_u->sev_l); #endif u3_http* pre_u = htp_u->htd_u->htp_u; @@ -1258,7 +1258,7 @@ http_serv_free_cb(uv_timer_t* tim_u) u3_http* htp_u = tim_u->data; #if 0 - u3l_log("http serv free cb %d\n", htp_u->sev_l); + u3l_log("http serv free cb %d", htp_u->sev_l); #endif _http_serv_really_free(htp_u); @@ -1272,7 +1272,7 @@ static void _http_serv_free(u3_http* htp_u) { #if 0 - u3l_log("http serv free %d\n", htp_u->sev_l); + u3l_log("http serv free %d", htp_u->sev_l); #endif c3_assert( 0 == htp_u->hon_u ); @@ -1342,7 +1342,7 @@ _http_serv_close(u3_http* htp_u) h2o_context_request_shutdown(&h2o_u->ctx_u); #if 0 - u3l_log("http serv close %d %p\n", htp_u->sev_l, &htp_u->wax_u); + u3l_log("http serv close %d %p", htp_u->sev_l, &htp_u->wax_u); #endif uv_close((uv_handle_t*)&htp_u->wax_u, _http_serv_close_cb); @@ -1384,7 +1384,7 @@ _http_serv_accept(u3_http* htp_u) if ( 0 != (sas_i = uv_accept((uv_stream_t*)&htp_u->wax_u, (uv_stream_t*)&hon_u->wax_u)) ) { if ( (u3C.wag_w & u3o_verbose) ) { - u3l_log("http: accept: %s\n", uv_strerror(sas_i)); + u3l_log("http: accept: %s", uv_strerror(sas_i)); } uv_close((uv_handle_t*)&hon_u->wax_u, _http_conn_free); @@ -1413,7 +1413,7 @@ _http_serv_listen_cb(uv_stream_t* str_u, c3_i sas_i) u3_http* htp_u = (u3_http*)str_u; if ( 0 != sas_i ) { - u3l_log("http: listen_cb: %s\n", uv_strerror(sas_i)); + u3l_log("http: listen_cb: %s", uv_strerror(sas_i)); } else { _http_serv_accept(htp_u); @@ -1574,7 +1574,7 @@ _http_serv_start(u3_http* htp_u) return; } - u3l_log("http: %s live on %s://localhost:%d\n", + u3l_log("http: %s live on %s://localhost:%d", (c3y == htp_u->lop) ? "loopback" : "web interface", (c3y == htp_u->sec) ? "https" : "http", htp_u->por_s); @@ -1632,9 +1632,10 @@ _http_init_tls(uv_buf_t key_u, uv_buf_t cer_u) BIO_free(bio_u); if( 0 == sas_i ) { - u3l_log("http: load private key failed:\n"); - ERR_print_errors_fp(u3_term_io_hija()); - u3_term_io_loja(1); + u3l_log("http: load private key failed:"); + FILE* fil_u = u3_term_io_hija(); + ERR_print_errors_fp(fil_u); + u3_term_io_loja(1, fil_u); SSL_CTX_free(tls_u); @@ -1650,9 +1651,10 @@ _http_init_tls(uv_buf_t key_u, uv_buf_t cer_u) X509_free(xer_u); if( 0 == sas_i ) { - u3l_log("http: load certificate failed:\n"); - ERR_print_errors_fp(u3_term_io_hija()); - u3_term_io_loja(1); + u3l_log("http: load certificate failed:"); + FILE* fil_u = u3_term_io_hija(); + ERR_print_errors_fp(fil_u); + u3_term_io_loja(1,fil_u); BIO_free(bio_u); SSL_CTX_free(tls_u); @@ -1735,19 +1737,19 @@ _http_search_req(u3_httd* htd_u, if ( !(htp_u = _http_serv_find(htd_u, sev_l)) ) { if ( bug_w ) { - u3l_log("http: server not found: %x\r\n", sev_l); + u3l_log("http: server not found: %x", sev_l); } return 0; } else if ( !(hon_u = _http_conn_find(htp_u, coq_l)) ) { if ( bug_w ) { - u3l_log("http: connection not found: %x/%d\r\n", sev_l, coq_l); + u3l_log("http: connection not found: %x/%d", sev_l, coq_l); } return 0; } else if ( !(req_u = _http_req_find(hon_u, seq_l)) ) { if ( bug_w ) { - u3l_log("http: request not found: %x/%d/%d\r\n", + u3l_log("http: request not found: %x/%d/%d", sev_l, coq_l, seq_l); } return 0; @@ -1853,7 +1855,7 @@ _http_serv_restart(u3_httd* htd_u) _http_serv_start_all(htd_u); } else { - u3l_log("http: restarting servers to apply configuration\n"); + u3l_log("http: restarting servers to apply configuration"); while ( 0 != htp_u ) { if ( c3y == htp_u->liv ) { @@ -1905,7 +1907,7 @@ u3_http_ef_form(u3_httd* htd_u, u3_noun fig) !( c3y == pro || c3n == pro ) || !( c3y == log || c3n == log ) || !( c3y == red || c3n == red ) ) { - u3l_log("http: form: invalid card\n"); + u3l_log("http: form: invalid card"); u3z(fig); return; } @@ -2000,14 +2002,14 @@ _http_ef_http_server(u3_httd* htd_u, _http_continue_respond(req_u, u3k(data), u3k(complete)); } else if (c3y == u3r_sing_c("cancel", u3h(response))) { - u3l_log("http: %%cancel not handled yet\n"); + u3l_log("http: %%cancel not handled yet"); } else { - u3l_log("http: strange response\n"); + u3l_log("http: strange response"); } } else { - u3l_log("http: strange response\n"); + u3l_log("http: strange response"); } } @@ -2254,7 +2256,7 @@ _http_io_slog(u3_auto* car_u) sec_y++; seq_u = seq_u->nex_u; } - u3l_log(" open slogstreams: %d\n", sec_y); + u3l_log(" open slogstreams: %d", sec_y); } /* u3_http_io_init(): initialize http I/O. diff --git a/pkg/urbit/vere/io/term.c b/pkg/urbit/vere/io/term.c index 9994fcfcb3..5970a56e1a 100644 --- a/pkg/urbit/vere/io/term.c +++ b/pkg/urbit/vere/io/term.c @@ -16,6 +16,7 @@ static u3_utty* _term_main(); static void _term_read_cb(uv_stream_t* tcp_u, ssize_t siz_i, const uv_buf_t* buf_u); +static void _term_it_send_stub(u3_utty* uty_u, u3_noun tub); /* u3_write_fd(): retry interrupts, continue partial writes, assert errors. */ @@ -85,39 +86,6 @@ _term_alloc(uv_handle_t* had_u, *buf = uv_buf_init(ptr_v, 123); } -// XX unused, but %hook is in %zuse. -// implement or remove -// -#if 0 -/* _term_close_cb(): free terminal. -*/ -static void -_term_close_cb(uv_handle_t* han_t) -{ - u3_utty* tty_u = (void*) han_t; - if ( u3_Host.uty_u == tty_u ) { - u3_Host.uty_u = tty_u->nex_u; - } - else { - u3_utty* uty_u; - for (uty_u = u3_Host.uty_u; uty_u; uty_u = uty_u->nex_u ) { - if ( uty_u->nex_u == tty_u ) { - uty_u->nex_u = tty_u->nex_u; - break; - } - } - } - - { - u3_noun tid = u3dc("scot", c3__ud, tty_u->tid_l); - u3_noun pax = u3nq(u3_blip, c3__term, tid, u3_nul); - u3_pier_plan(u3k(pax), u3nc(c3__hook, u3_nul)); - u3z(pax); - } - c3_free(tty_u); -} -#endif - /* u3_term_log_init(): initialize terminal for logging */ void @@ -149,51 +117,44 @@ u3_term_log_init(void) // and simply use constant sequences. // { - uty_u->ufo_u.out.clear_u = TERM_LIT_BUF("\033[H\033[2J"); - uty_u->ufo_u.out.el_u = TERM_LIT_BUF("\033[K"); - // uty_u->ufo_u.out.el1_u = TERM_LIT_BUF("\033[1K"); - uty_u->ufo_u.out.ed_u = TERM_LIT_BUF("\033[J"); - uty_u->ufo_u.out.bel_u = TERM_LIT_BUF("\x7"); - uty_u->ufo_u.out.cub1_u = TERM_LIT_BUF("\x8"); - uty_u->ufo_u.out.cuf1_u = TERM_LIT_BUF("\033[C"); - uty_u->ufo_u.out.cuu1_u = TERM_LIT_BUF("\033[A"); - uty_u->ufo_u.out.cud1_u = TERM_LIT_BUF("\xa"); - // uty_u->ufo_u.out.cub_u = TERM_LIT_BUF("\033[%p1%dD"); - // uty_u->ufo_u.out.cuf_u = TERM_LIT_BUF("\033[%p1%dC"); - } + uty_u->ufo_u.mon_u = TERM_LIT_BUF("\033[?9h"); + uty_u->ufo_u.mof_u = TERM_LIT_BUF("\033[?9l"); - // configure input escape sequences - // - // NB: terminfo reports the wrong sequence for arrow keys on xterms. - // disabled, currently unused - // { - // uty_u->ufo_u.inn.kcuu1_u = TERM_LIT_BUF("\033[A"); // terminfo reports "\033OA" - // uty_u->ufo_u.inn.kcud1_u = TERM_LIT_BUF("\033[B"); // terminfo reports "\033OB" - // uty_u->ufo_u.inn.kcuf1_u = TERM_LIT_BUF("\033[C"); // terminfo reports "\033OC" - // uty_u->ufo_u.inn.kcub1_u = TERM_LIT_BUF("\033[D"); // terminfo reports "\033OD" - // } + uty_u->ufo_u.reg_u = TERM_LIT_BUF("\033[r"); + + uty_u->ufo_u.suc_u = TERM_LIT_BUF("\033[s"); + uty_u->ufo_u.ruc_u = TERM_LIT_BUF("\033[u"); + uty_u->ufo_u.cub_u = TERM_LIT_BUF("\x8"); + + uty_u->ufo_u.clr_u = TERM_LIT_BUF("\033[H\033[2J"); + uty_u->ufo_u.cel_u = TERM_LIT_BUF("\033[K"); + + uty_u->ufo_u.bel_u = TERM_LIT_BUF("\x7"); + } // Initialize mirror and accumulator state. // { - uty_u->tat_u.mir.lin_y = 0; - uty_u->tat_u.mir.byt_w = 0; - uty_u->tat_u.mir.wor_w = 0; - uty_u->tat_u.mir.sap_w = 0; + uty_u->tat_u.mir.lin = u3_nul; + uty_u->tat_u.mir.rus_w = 0; uty_u->tat_u.mir.cus_w = 0; uty_u->tat_u.esc.ape = c3n; uty_u->tat_u.esc.bra = c3n; + uty_u->tat_u.esc.mou = c3n; + uty_u->tat_u.esc.ton_y = 0; + uty_u->tat_u.esc.col_y = 0; uty_u->tat_u.fut.len_w = 0; uty_u->tat_u.fut.wid_w = 0; + uty_u->tat_u.fut.imp = u3_nul; } // default size // { uty_u->tat_u.siz.col_l = 80; - uty_u->tat_u.siz.row_l = 24; + uty_u->tat_u.siz.row_l = 0; } // initialize spinner state @@ -273,7 +234,7 @@ _term_it_write_cb(uv_write_t* wri_u, c3_i sas_i) // write failure is logged, but otherwise ignored. // if ( 0 != sas_i ) { - u3l_log("term: write: %s\n", uv_strerror(sas_i)); + u3l_log("term: write: %s", uv_strerror(sas_i)); } c3_free(wri_u->data); @@ -325,7 +286,7 @@ _term_it_write(u3_utty* uty_u, // synchronous write failure is logged, but otherwise ignored // if ( ret_i < 0 ) { - u3l_log("term: write: %s\n", uv_strerror(ret_i)); + u3l_log("term: write: %s", uv_strerror(ret_i)); } c3_free(ptr_v); @@ -370,32 +331,62 @@ _term_it_send(u3_utty* uty_u, } } -/* _term_it_send_cord(): write a cord. +/* _term_it_send_csi(): send csi escape sequence */ static void -_term_it_send_cord(u3_utty* uty_u, - u3_atom txt) +_term_it_send_csi(u3_utty *uty_u, c3_c cmd_c, c3_w num_w, ...) { - c3_w len_w = u3r_met(3, txt); - c3_y* hun_y = c3_malloc(len_w); - u3r_bytes(0, len_w, hun_y, txt); + va_list ap; + va_start(ap, num_w); - _term_it_send(uty_u, len_w, hun_y); + // allocate for escape sequence (2), command char (1), + // argument digits (5 per arg) and separators (1 per arg, minus 1). + // freed via _term_it_write. + // + c3_c* pas_c = c3_malloc( 2 + num_w * 6 ); + c3_y len_y = 0; - u3z(txt); + pas_c[len_y++] = '\033'; + pas_c[len_y++] = '['; + + while ( num_w-- ) { + c3_w par_w = va_arg(ap, c3_w); + len_y += sprintf(pas_c+len_y, "%d", par_w); + + if ( num_w ) { + pas_c[len_y++] = ';'; + } + } + + pas_c[len_y++] = cmd_c; + + _term_it_send(uty_u, len_y, (c3_y*)pas_c); + + va_end(ap); } -/* _term_it_show_clear(): clear to the beginning of the current line. +/* _term_it_free_line(): wipe line stored by _term_it_save_stub */ static void -_term_it_show_clear(u3_utty* uty_u) +_term_it_free_line(u3_utty* uty_u) { - if ( uty_u->tat_u.siz.col_l ) { - _term_it_dump(uty_u, TERM_LIT("\r")); - _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.el_u); + u3z(uty_u->tat_u.mir.lin); + uty_u->tat_u.mir.lin = u3_nul; +} - uty_u->tat_u.mir.wor_w = 0; - uty_u->tat_u.mir.cus_w = 0; +/* _term_it_clear_line(): clear line of cursor +*/ +static void +_term_it_clear_line(u3_utty* uty_u) +{ + _term_it_dump(uty_u, TERM_LIT("\r")); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.cel_u); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.ruc_u); + + // if we're clearing the bottom line, clear our mirror of it too + // + if ( uty_u->tat_u.siz.row_l - 1 == uty_u->tat_u.mir.rus_w ) { + _term_it_free_line(uty_u); } } @@ -404,87 +395,35 @@ _term_it_show_clear(u3_utty* uty_u) static void _term_it_show_blank(u3_utty* uty_u) { - _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.clear_u); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.clr_u); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.ruc_u); } -/* _term_it_show_cursor(): set current line, transferring pointer. -*/ +/* _term_it_move_cursor(): move cursor to row & column + * + * row 0 is at the top, col 0 is to the left. + * if the given position exceeds the known window size, + * it is clipped to stay within the window. + */ static void -_term_it_show_cursor(u3_utty* uty_u, c3_w cur_w) +_term_it_move_cursor(u3_utty* uty_u, c3_w col_w, c3_w row_w) { - c3_w cus_w = uty_u->tat_u.mir.cus_w; - c3_w dif_w; + c3_l row_l = uty_u->tat_u.siz.row_l; + c3_l col_l = uty_u->tat_u.siz.col_l; + if ( row_w >= row_l ) { row_w = row_l - 1; } + if ( col_w >= col_l ) { col_w = col_l - 1; } - //NOTE assumes all styled text precedes the cursor. drum enforces this. - // - cur_w += uty_u->tat_u.mir.sap_w; + _term_it_send_csi(uty_u, 'H', 2, row_w + 1, col_w + 1); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.suc_u); - if ( cur_w < cus_w ) { - dif_w = cus_w - cur_w; - - while ( dif_w-- ) { - _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.cub1_u); - } - } - else if ( cur_w > cus_w ) { - dif_w = cur_w - cus_w; - - while ( dif_w-- ) { - _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.cuf1_u); - } - } - - uty_u->tat_u.mir.cus_w = cur_w; + uty_u->tat_u.mir.rus_w = row_w; + uty_u->tat_u.mir.cus_w = col_w; } -/* _term_it_show_line(): render current line. +/* _term_it_show_line(): print at cursor */ static void -_term_it_show_line(u3_utty* uty_u, c3_w wor_w, c3_w sap_w) -{ - u3_utat* tat_u = &uty_u->tat_u; - - // we have to reallocate the current line on write, - // or we have a data race if a) the write is async, - // and b) a new output line arrives before the write completes. - // - { - c3_w len_w = tat_u->mir.byt_w; - c3_y* hun_y = c3_malloc(len_w); - memcpy(hun_y, tat_u->mir.lin_y, len_w); - - _term_it_send(uty_u, len_w, hun_y); - } - - // XX refactor to avoid updating state - // - tat_u->mir.cus_w += wor_w; - tat_u->mir.wor_w = wor_w; - tat_u->mir.sap_w = sap_w; -} - -/* _term_it_refresh_line(): refresh current line. -*/ -static void -_term_it_refresh_line(u3_utty* uty_u) -{ - u3_utat* tat_u = &uty_u->tat_u; - c3_w wor_w = tat_u->mir.wor_w; - c3_w sap_w = tat_u->mir.sap_w; - c3_w cus_w = tat_u->mir.cus_w; - - _term_it_show_clear(uty_u); - _term_it_show_line(uty_u, wor_w, sap_w); - _term_it_show_cursor(uty_u, cus_w); -} - -/* _term_it_set_line(): set current line. -*/ -static void -_term_it_set_line(u3_utty* uty_u, - c3_w* lin_w, - c3_w wor_w, - c3_w sap_w) +_term_it_show_line(u3_utty* uty_u, c3_w* lin_w, c3_w wor_w) { u3_utat* tat_u = &uty_u->tat_u; c3_y* hun_y = (c3_y*)lin_w; @@ -522,28 +461,67 @@ _term_it_set_line(u3_utty* uty_u, } } - c3_free(tat_u->mir.lin_y); - tat_u->mir.lin_y = hun_y; - tat_u->mir.byt_w = byt_w; - tat_u->mir.wor_w = wor_w; - tat_u->mir.sap_w = sap_w; - - _term_it_show_line(uty_u, wor_w, sap_w); + //NOTE lin_w freed through hun_y by _send + _term_it_send(uty_u, byt_w, hun_y); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.ruc_u); } -/* _term_it_show_more(): new current line. +/* _term_it_restore_line(): re-render original line at bottom of screen */ static void -_term_it_show_more(u3_utty* uty_u) +_term_it_restore_line(u3_utty* uty_u) +{ + u3_utat* tat_u = &uty_u->tat_u; + + _term_it_send_csi(uty_u, 'H', 2, tat_u->siz.row_l, 1); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.cel_u); + _term_it_send_stub(uty_u, u3k(tat_u->mir.lin)); + //NOTE send_stub restores cursor position +} + +/* _term_it_save_stub(): store line if relevant to internal logic + */ +static void +_term_it_save_stub(u3_utty* uty_u, u3_noun tub) +{ + u3_utat* tat_u = &uty_u->tat_u; + u3_noun lin = tat_u->mir.lin; + + // keep track of changes to bottom-most line, to aid spinner drawing logic. + // -t mode doesn't need this logic, because it doesn't render the spinner. + // + if ( ( tat_u->siz.row_l - 1 == tat_u->mir.rus_w ) && + ( c3n == u3_Host.ops_u.tem ) ) { + lin = u3dq("wail:klr:format", lin, tat_u->mir.cus_w, u3k(tub), ' '); + lin = u3do("pact:klr:format", lin); + } + + tat_u->mir.lin = lin; + u3z(tub); +} + +/* _term_it_show_nel(): render newline, moving cursor down +*/ +static void +_term_it_show_nel(u3_utty* uty_u) { if ( c3y == u3_Host.ops_u.tem ) { _term_it_dump(uty_u, TERM_LIT("\n")); } else { _term_it_dump(uty_u, TERM_LIT("\r\n")); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.suc_u); } uty_u->tat_u.mir.cus_w = 0; + if ( uty_u->tat_u.mir.rus_w < uty_u->tat_u.siz.row_l - 1 ) { + uty_u->tat_u.mir.rus_w++; + } + else { + // newline at bottom of screen, so bottom line is now empty + // + _term_it_free_line(uty_u); + } } /* _term_it_path(): path for console file. @@ -641,39 +619,83 @@ _term_io_belt(u3_utty* uty_u, u3_noun blb) } } -/* _term_io_suck_char(): process a single character. +/* _term_io_spit(): input the buffer (if any), then input the belt (if any) */ static void +_term_io_spit(u3_utty* uty_u, u3_noun bet) { + u3_utat* tat_u = &uty_u->tat_u; + if (u3_nul != tat_u->fut.imp) { + _term_io_belt(uty_u, u3nc(c3__txt, u3kb_flop(tat_u->fut.imp))); + tat_u->fut.imp = u3_nul; + } + if (u3_none != bet) { + _term_io_belt(uty_u, bet); + } +} + +/* _term_io_suck_char(): process a single character. + * + * Note that this accumulates simple inputs in a buffer, and is not + * guaranteed to flush it fully. After a call (or sequence of calls) + * to this function, please call _term_io_spit(uty_u, u3_none) to + * flush any remainder. + */ +static void _term_io_suck_char(u3_utty* uty_u, c3_y cay_y) { u3_utat* tat_u = &uty_u->tat_u; + // escape sequences + // if ( c3y == tat_u->esc.ape ) { if ( c3y == tat_u->esc.bra ) { - switch ( cay_y ) { - default: { - _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.bel_u); - break; + // vt sequence + // + if ( cay_y == '~' ) { + switch ( tat_u->esc.seq_y ) { + default: { + _term_it_dump_buf(uty_u, &uty_u->ufo_u.bel_u); + break; + } + case '3': _term_io_spit(uty_u, u3nc(c3__del, u3_nul)); break; } - case 'A': _term_io_belt(uty_u, u3nc(c3__aro, 'u')); break; - case 'B': _term_io_belt(uty_u, u3nc(c3__aro, 'd')); break; - case 'C': _term_io_belt(uty_u, u3nc(c3__aro, 'r')); break; - case 'D': _term_io_belt(uty_u, u3nc(c3__aro, 'l')); break; + tat_u->esc.ape = tat_u->esc.bra = c3n; + tat_u->esc.seq_y = 0; + } + else if ( cay_y <= '9' ) { + tat_u->esc.seq_y = cay_y; + } + // xterm sequence + // + else { + switch ( cay_y ) { + default: { + _term_it_dump_buf(uty_u, &uty_u->ufo_u.bel_u); + break; + } + case 'A': _term_io_spit(uty_u, u3nc(c3__aro, 'u')); break; + case 'B': _term_io_spit(uty_u, u3nc(c3__aro, 'd')); break; + case 'C': _term_io_spit(uty_u, u3nc(c3__aro, 'r')); break; + case 'D': _term_io_spit(uty_u, u3nc(c3__aro, 'l')); break; + // + case 'M': tat_u->esc.mou = c3y; break; + } + tat_u->esc.ape = tat_u->esc.bra = c3n; } - tat_u->esc.ape = tat_u->esc.bra = c3n; } else { if ( (cay_y >= 'a') && (cay_y <= 'z') ) { tat_u->esc.ape = c3n; - _term_io_belt(uty_u, u3nc(c3__met, cay_y)); - } - else if ( '.' == cay_y ) { - tat_u->esc.ape = c3n; - _term_io_belt(uty_u, u3nc(c3__met, c3__dot)); + // XX for backwards compatibility, check kelvin version + // and fallback to [%met @c] + // + _term_io_spit(uty_u, u3nt(c3__mod, c3__met, cay_y)); } else if ( 8 == cay_y || 127 == cay_y ) { tat_u->esc.ape = c3n; - _term_io_belt(uty_u, u3nc(c3__met, c3__bac)); + // XX backwards compatibility [%met @c] + // + _term_io_spit(uty_u, u3nq(c3__mod, c3__met, c3__bac, u3_nul)); } else if ( ('[' == cay_y) || ('O' == cay_y) ) { tat_u->esc.bra = c3y; @@ -681,10 +703,31 @@ _term_io_suck_char(u3_utty* uty_u, c3_y cay_y) else { tat_u->esc.ape = c3n; - _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.bel_u); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.bel_u); } } } + // mouse input + // + else if ( c3y == tat_u->esc.mou ) { + if ( 0 == tat_u->esc.ton_y ) { + tat_u->esc.ton_y = cay_y - 31; + } + else if ( 0 == tat_u->esc.col_y ) { + tat_u->esc.col_y = cay_y - 32; + } + else { + c3_y row_y = cay_y - 32; + // only acknowledge button 1 presses within our known window + if ( 1 != tat_u->esc.ton_y && row_y <= tat_u->siz.row_l ) { + _term_io_spit(uty_u, u3nt(c3__hit, tat_u->esc.col_y - 1, row_y - 1)); + } + tat_u->esc.mou = c3n; + tat_u->esc.ton_y = tat_u->esc.col_y = 0; + } + } + // unicode inputs + // else if ( 0 != tat_u->fut.wid_w ) { tat_u->fut.syb_y[tat_u->fut.len_w++] = cay_y; @@ -697,29 +740,28 @@ _term_io_suck_char(u3_utty* uty_u, c3_y cay_y) wug = u3do("taft", huv); tat_u->fut.len_w = tat_u->fut.wid_w = 0; - _term_io_belt(uty_u, u3nt(c3__txt, wug, u3_nul)); + tat_u->fut.imp = u3nc(wug, tat_u->fut.imp); } } + // individual characters + // else { - if ( (cay_y >= 32) && (cay_y < 127) ) { - _term_io_belt(uty_u, u3nt(c3__txt, cay_y, u3_nul)); + if ( (cay_y >= 32) && (cay_y < 127) ) { // visual ascii + tat_u->fut.imp = u3nc(cay_y, tat_u->fut.imp); } - else if ( 0 == cay_y ) { - _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.bel_u); + else if ( 0 == cay_y ) { // null + _term_it_dump_buf(uty_u, &uty_u->ufo_u.bel_u); } - else if ( 8 == cay_y || 127 == cay_y ) { - _term_io_belt(uty_u, u3nc(c3__bac, u3_nul)); + else if ( 8 == cay_y || 127 == cay_y ) { // backspace & delete + _term_io_spit(uty_u, u3nc(c3__bac, u3_nul)); } - else if ( 13 == cay_y || 10 == cay_y ) { - _term_io_belt(uty_u, u3nc(c3__ret, u3_nul)); + else if ( 10 == cay_y || 13 == cay_y ) { // newline & carriage return + _term_io_spit(uty_u, u3nc(c3__ret, u3_nul)); } -#if 0 - else if ( 6 == cay_y ) { - _term_io_flow(uty_u); // XX hack - } -#endif else if ( cay_y <= 26 ) { - _term_io_belt(uty_u, u3nc(c3__ctl, ('a' + (cay_y - 1)))); + // XX backwards compatibility [%ctl @c] + // + _term_io_spit(uty_u, u3nt(c3__mod, c3__ctl, ('a' + (cay_y - 1)))); } else if ( 27 == cay_y ) { tat_u->esc.ape = c3y; @@ -762,14 +804,14 @@ _term_suck(u3_utty* uty_u, const c3_y* buf, ssize_t siz_i) // The process hangs if we do nothing (and ctrl-z // then corrupts the event log), so we force shutdown. // - u3l_log("term: hangup (EOF)\r\n"); + u3l_log("term: hangup (EOF)"); // XX revise // u3_pier_bail(u3_king_stub()); } else if ( siz_i < 0 ) { - u3l_log("term %d: read: %s\n", uty_u->tid_l, uv_strerror(siz_i)); + u3l_log("term %d: read: %s", uty_u->tid_l, uv_strerror(siz_i)); } else { c3_i i; @@ -777,6 +819,7 @@ _term_suck(u3_utty* uty_u, const c3_y* buf, ssize_t siz_i) for ( i=0; i < siz_i; i++ ) { _term_io_suck_char(uty_u, buf[i]); } + _term_io_spit(uty_u, u3_none); } } } @@ -855,14 +898,20 @@ _term_spin_step(u3_utty* uty_u) // NB: we simply bail out if anything goes wrong // { - uv_buf_t lef_u = uty_u->ufo_u.out.cub1_u; + uv_buf_t lef_u = uty_u->ufo_u.cub_u; c3_i fid_i = uty_u->fid_i; // One-time cursor backoff. // if ( c3n == tat_u->sun_u.diz_o ) { - c3_w i_w; + // if we know where the bottom line is, and the cursor is not on it, + // move it to the bottom left + // + if ( tat_u->siz.row_l && tat_u->mir.rus_w < tat_u->siz.row_l - 1 ) { + _term_it_send_csi(uty_u, 'H', 2, tat_u->siz.row_l, 1); + } + c3_w i_w; for ( i_w = bac_w; i_w < sol_w; i_w++ ) { if ( lef_u.len != write(fid_i, lef_u.base, lef_u.len) ) { return; @@ -898,6 +947,7 @@ _term_spin_timer_cb(uv_timer_t* tim_u) _term_spin_step(uty_u); } +#define _SPIN_FAST_US 100UL // spinner activation delay when expected #define _SPIN_COOL_US 500UL // spinner activation delay when cool #define _SPIN_WARM_US 50UL // spinner activation delay when warm #define _SPIN_RATE_US 250UL // spinner rate (ms/frame) @@ -922,7 +972,7 @@ u3_term_start_spinner(u3_atom say, c3_o del_o) { c3_d now_d = _term_msc_out_host(); c3_d end_d = tat_u->sun_u.end_d; - c3_d wen_d = (c3n == del_o) ? 0UL : + c3_d wen_d = (c3n == del_o) ? _SPIN_FAST_US : (now_d - end_d < _SPIN_IDLE_US) ? _SPIN_WARM_US : _SPIN_COOL_US; @@ -947,7 +997,7 @@ u3_term_stop_spinner(void) uv_timer_stop(&tat_u->sun_u.tim_u); if ( c3y == tat_u->sun_u.diz_o ) { - _term_it_refresh_line(uty_u); + _term_it_restore_line(uty_u); tat_u->sun_u.end_d = _term_msc_out_host(); tat_u->sun_u.diz_o = c3n; } @@ -1038,13 +1088,11 @@ u3_term_ef_ctlc(void) if ( uty_u->car_u ) { u3_noun wir = u3nt(c3__term, '1', u3_nul); - u3_noun cad = u3nt(c3__belt, c3__ctl, 'c'); + u3_noun cad = u3nq(c3__belt, c3__mod, c3__ctl, 'c'); c3_assert( 1 == uty_u->tid_l ); _term_ovum_plan(uty_u->car_u, wir, cad); } - - _term_it_refresh_line(uty_u); } /* _term_it_put_value(): put numeric color value on lin_w. @@ -1131,10 +1179,10 @@ _term_it_put_deco(c3_w* lin_w, } } -/* _term_it_show_stub(): send styled text to terminal as ansi escape sequences +/* _term_it_send_stub(): send styled text, without saving */ static void -_term_it_show_stub(u3_utty* uty_u, +_term_it_send_stub(u3_utty* uty_u, u3_noun tub) { c3_w tuc_w = u3qb_lent(tub); @@ -1153,17 +1201,16 @@ _term_it_show_stub(u3_utty* uty_u, // allocate enough memory for every display character, plus styles // - //NOTE we use max 31 characters per styl for escape codes: - // 3 for opening, 4 for decorations, 15 for colors, 4 for closing, - // and 5 as separators between decorations and colors. + //NOTE we use max 48 characters per styl for escape codes: + // 2 for opening, 7 for decorations, 2x16 for colors, 4 for closing, + // and 3 as separators between decorations and colors. // - c3_w* lin_w = c3_malloc( sizeof(c3_w) * (lec_w + (31 * tuc_w)) ); + c3_w* lin_w = c3_malloc( sizeof(c3_w) * (lec_w + (48 * tuc_w)) ); // write the contents to the buffer, // tracking total and escape characters written // c3_w i_w = 0; - c3_w sap_w = 0; { u3_noun nub = tub; while ( u3_nul != nub ) { @@ -1183,7 +1230,6 @@ _term_it_show_stub(u3_utty* uty_u, c3_o mor_o = c3n; lin_w[i_w++] = 27; lin_w[i_w++] = '['; - sap_w += 2; // text decorations // @@ -1193,10 +1239,8 @@ _term_it_show_stub(u3_utty* uty_u, while ( u3_nul != des ) { if ( c3y == mor_o ) { lin_w[i_w++] = ';'; - sap_w++; } _term_it_put_deco(&lin_w[i_w++], u3h(des)); - sap_w++; mor_o = c3y; des = u3t(des); } @@ -1208,12 +1252,10 @@ _term_it_show_stub(u3_utty* uty_u, if ( u3_nul != bag ) { if ( c3y == mor_o ) { lin_w[i_w++] = ';'; - sap_w++; } lin_w[i_w++] = '4'; c3_w put_w = _term_it_put_tint(&lin_w[i_w], bag); i_w += put_w; - sap_w += ++put_w; mor_o = c3y; } @@ -1222,17 +1264,14 @@ _term_it_show_stub(u3_utty* uty_u, if ( u3_nul != fog ) { if ( c3y == mor_o ) { lin_w[i_w++] = ';'; - sap_w++; } lin_w[i_w++] = '3'; c3_w put_w = _term_it_put_tint(&lin_w[i_w], fog); i_w += put_w; - sap_w += ++put_w; mor_o = c3y; } lin_w[i_w++] = 'm'; - sap_w++; } // write the text itself @@ -1248,18 +1287,27 @@ _term_it_show_stub(u3_utty* uty_u, lin_w[i_w++] = '['; lin_w[i_w++] = '0'; lin_w[i_w++] = 'm'; - sap_w += 4; } nub = u3t(nub); } } - _term_it_set_line(uty_u, lin_w, i_w, sap_w); + _term_it_show_line(uty_u, lin_w, i_w); u3z(tub); } +/* _term_it_send_stub(): send styled text to terminal as ansi escape sequences +*/ +static void +_term_it_show_stub(u3_utty* uty_u, + u3_noun tub) +{ + _term_it_send_stub(uty_u, u3k(tub)); + _term_it_save_stub(uty_u, tub); +} + /* _term_it_show_tour(): send utf32 to terminal. */ static void @@ -1277,9 +1325,14 @@ _term_it_show_tour(u3_utty* uty_u, } } - _term_it_set_line(uty_u, lin_w, len_w, 0); + _term_it_show_line(uty_u, lin_w, len_w); - u3z(lin); + { + u3_noun tub = u3i_list(u3nc(u3nt(u3_nul, u3_nul, u3_nul), lin), u3_none); + _term_it_save_stub(uty_u, tub); + } + + //NOTE lin transferred to tub above } /* _term_ef_blit(): send blit to terminal. @@ -1292,40 +1345,48 @@ _term_ef_blit(u3_utty* uty_u, default: break; case c3__bel: { - if ( c3n == u3_Host.ops_u.tem ) { - _term_it_dump_buf(uty_u, &uty_u->ufo_u.out.bel_u); - } + _term_it_dump_buf(uty_u, &uty_u->ufo_u.bel_u); } break; case c3__clr: { - if ( c3n == u3_Host.ops_u.tem ) { - _term_it_show_blank(uty_u); - _term_it_refresh_line(uty_u); - } + _term_it_show_blank(uty_u); } break; case c3__hop: { - if ( c3n == u3_Host.ops_u.tem ) { - _term_it_show_cursor(uty_u, u3t(blt)); + u3_noun pos = u3t(blt); + if ( c3y == u3r_ud(pos) ) { + _term_it_move_cursor(uty_u, pos, uty_u->tat_u.siz.row_l - 1); + } + else { + _term_it_move_cursor(uty_u, u3h(pos), u3t(pos)); } } break; case c3__klr: { - if ( c3n == u3_Host.ops_u.tem ) { - _term_it_show_clear(uty_u); - } _term_it_show_stub(uty_u, u3k(u3t(blt))); } break; - case c3__lin: { - if ( c3n == u3_Host.ops_u.tem ) { - _term_it_show_clear(uty_u); - } + case c3__lin: { //TMP backwards compatibility + _term_it_move_cursor(uty_u, 0, uty_u->tat_u.siz.row_l - 1); + _term_it_clear_line(uty_u); + } // + case c3__put: { _term_it_show_tour(uty_u, u3k(u3t(blt))); } break; case c3__mor: { - _term_it_show_more(uty_u); + if (u3_nul != u3t(blt)) { + u3_noun bis = u3t(blt); + while (u3_nul != bis) { + _term_ef_blit(uty_u, u3k(u3h(bis))); + bis = u3t(bis); + } + break; + } + //TMP fall through to nel for backwards compatibility + } + case c3__nel: { + _term_it_show_nel(uty_u); } break; case c3__sav: { @@ -1343,20 +1404,50 @@ _term_ef_blit(u3_utty* uty_u, } break; case c3__url: { - u3_noun txt = u3t(blt); + // platform-agnostically opening the default web browser from within a + // c program is an unsolved problem. + } break; - // XX check u3_Host.ops_u.tem ? - // XX this looks to be broken, - // multiple calls to _show_clear will discard the mirror state - // - if ( c3y == u3a_is_atom(txt) ) { - _term_it_show_clear(uty_u); + case c3__wyp: { + _term_it_clear_line(uty_u); + } break; + } - _term_it_send_cord(uty_u, u3k(txt)); + u3z(blt); +} - _term_it_show_more(uty_u); - _term_it_refresh_line(uty_u); - } +/* _term_ef_blit_lame(): simplified output handling for -t +*/ +static void +_term_ef_blit_lame(u3_utty* uty_u, + u3_noun blt) +{ + switch ( u3h(blt) ) { + default: break; + + case c3__klr: { + _term_it_show_stub(uty_u, u3k(u3t(blt))); + _term_it_show_nel(uty_u); + } break; + + case c3__lin: //TMP backwards compatibility + case c3__put: { + _term_it_show_tour(uty_u, u3k(u3t(blt))); + _term_it_show_nel(uty_u); + } break; + + case c3__sav: { + u3_noun pax, dat; + u3x_cell(u3t(blt), &pax, &dat); + + _term_it_save(u3k(pax), u3k(dat)); + } break; + + case c3__sag: { + u3_noun pax, dat; + u3x_cell(u3t(blt), &pax, &dat); + + _term_it_save(u3k(pax), u3qe_jam(dat)); } break; } @@ -1370,7 +1461,7 @@ u3_term_io_hija(void) { u3_utty* uty_u = _term_main(); - if ( uty_u ) { + if ( uty_u && uty_u->tat_u.siz.row_l ) { if ( uty_u->fid_i > 2 ) { // We *should* in fact, produce some kind of fake FILE* for // non-console terminals. If we use this interface enough... @@ -1382,11 +1473,14 @@ u3_term_io_hija(void) if ( c3y != uty_u->hij_f(uty_u) ) { c3_assert(!"hija-tcsetattr"); } - u3_write_fd(uty_u->fid_i, "\r", 1); - { - uv_buf_t* buf_u = &uty_u->ufo_u.out.el_u; - u3_write_fd(uty_u->fid_i, buf_u->base, buf_u->len); - } + + // set scroll region to exclude the prompt, + // scroll up one line to make space, + // and move the cursor onto that space. + // + _term_it_send_csi(uty_u, 'r', 2, 1, uty_u->tat_u.siz.row_l - 1); + _term_it_send_csi(uty_u, 'S', 1, 1); + _term_it_send_csi(uty_u, 'H', 2, uty_u->tat_u.siz.row_l - 1, 1); } } } @@ -1396,11 +1490,11 @@ u3_term_io_hija(void) /* u3_term_io_loja(): release console from fprintf. */ void -u3_term_io_loja(int x) +u3_term_io_loja(int x, FILE* f) { u3_utty* uty_u = _term_main(); - if ( uty_u ) { + if ( uty_u && uty_u->tat_u.siz.row_l ) { if ( uty_u->fid_i > 2 ) { // We *should* in fact, produce some kind of fake FILE* for // non-console terminals. If we use this interface enough... @@ -1409,15 +1503,25 @@ u3_term_io_loja(int x) } else { if ( c3y == u3_Host.ops_u.tem ) { - fflush(stdout); - } else { + fprintf(f, "\n"); + fflush(f); + } + else { if ( c3y != uty_u->loj_f(uty_u) ) { c3_assert(!"loja-tcsetattr"); } - _term_it_refresh_line(uty_u); + + // clear the scrolling region we set previously, + // and restore cursor to its original position. + // + _term_it_dump_buf(uty_u, &uty_u->ufo_u.reg_u); + _term_it_dump_buf(uty_u, &uty_u->ufo_u.ruc_u); } } } + else { + fprintf(f, "\r\n"); + } } /* u3_term_it_log(): writes a log message @@ -1426,7 +1530,9 @@ void u3_term_io_log(c3_c* line) { FILE* stream = u3_term_io_hija(); - u3_term_io_loja(fprintf(stream, "%s", line)); + int x = fprintf(stream, "%s", line); + fflush(stream); + u3_term_io_loja(x, stream); //TODO remove arg? unused... } /* u3_term_tape_to(): dump a tape to a file. @@ -1458,7 +1564,7 @@ u3_term_tape(u3_noun tep) u3_term_tape_to(fil_f, tep); - u3_term_io_loja(0); + u3_term_io_loja(0, fil_f); } /* u3_term_wall(): dump a wall to stdout. @@ -1477,7 +1583,7 @@ u3_term_wall(u3_noun wol) wal = u3t(wal); } - u3_term_io_loja(0); + u3_term_io_loja(0, fil_f); u3z(wol); } @@ -1490,12 +1596,16 @@ _term_io_talk(u3_auto* car_u) if ( c3n == u3_Host.ops_u.tem ) { u3_utty* uty_u = _term_main(); + // start mouse handling + // + _term_it_dump_buf(uty_u, &uty_u->ufo_u.mon_u); + uv_read_start((uv_stream_t*)&(uty_u->pin_u), _term_alloc, _term_read_cb); } - // XX groace hardcoded terminal number + //TODO reevaluate wrt dill sessions // u3_noun wir = u3nt(c3__term, '1', u3_nul); u3_noun cad; @@ -1507,10 +1617,6 @@ _term_io_talk(u3_auto* car_u) _term_ovum_plan(car_u, u3k(wir), cad); } - // NB, term.c used to also start :dojo - // - // u3nq(c3__flow, c3__seat, c3__dojo, u3_nul) - // refresh terminal state // { @@ -1563,7 +1669,7 @@ _term_io_kick(u3_auto* car_u, u3_noun wir, u3_noun cad) || (u3_nul != q_pud) || (c3n == _reck_orchid(c3__ud, u3k(p_pud), &tid_l)) ) { - u3l_log("term: bad tire\n"); + u3l_log("term: bad tire"); ret_o = c3n; } else { @@ -1572,26 +1678,25 @@ _term_io_kick(u3_auto* car_u, u3_noun wir, u3_noun cad) ret_o = c3n; } break; - // XX review, accepted and ignored - // - case c3__bbye: { - ret_o = c3y; - } break; - case c3__blit: { ret_o = c3y; { u3_utty* uty_u = _term_ef_get(tid_l); if ( 0 == uty_u ) { - // u3l_log("no terminal %d\n", tid_l); - // u3l_log("uty_u %p\n", u3_Host.uty_u); + // u3l_log("no terminal %d", tid_l); + // u3l_log("uty_u %p", u3_Host.uty_u); } else { u3_noun bis = dat; while ( c3y == u3du(bis) ) { - _term_ef_blit(uty_u, u3k(u3h(bis))); + if (c3n == u3_Host.ops_u.tem) { + _term_ef_blit(uty_u, u3k(u3h(bis))); + } + else { + _term_ef_blit_lame(uty_u, u3k(u3h(bis))); + } bis = u3t(bis); } } @@ -1652,6 +1757,14 @@ _term_io_exit(u3_auto* car_u) u3_utty* uty_u = _term_main(); if ( c3n == u3_Host.ops_u.tem ) { + // stop mouse handling + // + _term_it_dump_buf(uty_u, &uty_u->ufo_u.mof_u); + + // move cursor to the end + // + _term_it_move_cursor(uty_u, 0, uty_u->tat_u.siz.row_l - 1); + // NB, closed in u3_term_log_exit() // uv_read_stop((uv_stream_t*)&(uty_u->pin_u)); diff --git a/pkg/urbit/vere/io/unix.c b/pkg/urbit/vere/io/unix.c index f9e175b5d2..57df1f9999 100644 --- a/pkg/urbit/vere/io/unix.c +++ b/pkg/urbit/vere/io/unix.c @@ -293,7 +293,7 @@ _unix_mkdirp(c3_c* pax_c) while ( fas_c ) { *fas_c = 0; if ( 0 != mkdir(pax_c, 0777) && EEXIST != errno ) { - u3l_log("unix: mkdir %s: %s\n", pax_c, strerror(errno)); + u3l_log("unix: mkdir %s: %s", pax_c, strerror(errno)); u3m_bail(c3__fail); } *fas_c++ = '/'; @@ -318,7 +318,7 @@ u3_unix_save(c3_c* pax_c, u3_atom pad) c3_c* ful_c; if ( !u3_unix_cane(pax_c) ) { - u3l_log("%s: non-canonical path\n", pax_c); + u3l_log("%s: non-canonical path", pax_c); u3z(pad); u3m_bail(c3__fail); } if ( '/' == *pax_c) { @@ -333,7 +333,7 @@ u3_unix_save(c3_c* pax_c, u3_atom pad) _unix_mkdirp(ful_c); fid_i = c3_open(ful_c, O_WRONLY | O_CREAT | O_TRUNC, 0666); if ( fid_i < 0 ) { - u3l_log("%s: %s\n", ful_c, strerror(errno)); + u3l_log("%s: %s", ful_c, strerror(errno)); c3_free(ful_c); u3z(pad); u3m_bail(c3__fail); } @@ -347,7 +347,7 @@ u3_unix_save(c3_c* pax_c, u3_atom pad) c3_free(pad_y); if ( rit_w != fln_w ) { - u3l_log("%s: %s\n", ful_c, strerror(errno)); + u3l_log("%s: %s", ful_c, strerror(errno)); c3_free(ful_c); u3m_bail(c3__fail); } @@ -364,35 +364,35 @@ _unix_rm_r_cb(const c3_c* pax_c, { switch ( typeflag ) { default: - u3l_log("bad file type in rm_r: %s\r\n", pax_c); + u3l_log("bad file type in rm_r: %s", pax_c); break; case FTW_F: if ( 0 != c3_unlink(pax_c) && ENOENT != errno ) { - u3l_log("error unlinking (in rm_r) %s: %s\n", + u3l_log("error unlinking (in rm_r) %s: %s", pax_c, strerror(errno)); c3_assert(0); } break; case FTW_D: - u3l_log("shouldn't have gotten pure directory: %s\r\n", pax_c); + u3l_log("shouldn't have gotten pure directory: %s", pax_c); break; case FTW_DNR: - u3l_log("couldn't read directory: %s\r\n", pax_c); + u3l_log("couldn't read directory: %s", pax_c); break; case FTW_NS: - u3l_log("couldn't stat path: %s\r\n", pax_c); + u3l_log("couldn't stat path: %s", pax_c); break; case FTW_DP: if ( 0 != c3_rmdir(pax_c) && ENOENT != errno ) { - u3l_log("error rmdiring %s: %s\n", pax_c, strerror(errno)); + u3l_log("error rmdiring %s: %s", pax_c, strerror(errno)); c3_assert(0); } break; case FTW_SL: - u3l_log("got symbolic link: %s\r\n", pax_c); + u3l_log("got symbolic link: %s", pax_c); break; case FTW_SLN: - u3l_log("got nonexistent symbolic link: %s\r\n", pax_c); + u3l_log("got nonexistent symbolic link: %s", pax_c); break; } @@ -406,7 +406,7 @@ _unix_rm_r(c3_c* pax_c) { if ( 0 > nftw(pax_c, _unix_rm_r_cb, 100, FTW_DEPTH | FTW_PHYS ) && ENOENT != errno) { - u3l_log("rm_r error on %s: %s\r\n", pax_c, strerror(errno)); + u3l_log("rm_r error on %s: %s", pax_c, strerror(errno)); } } @@ -416,7 +416,7 @@ static void _unix_mkdir(c3_c* pax_c) { if ( 0 != c3_mkdir(pax_c, 0755) && EEXIST != errno) { - u3l_log("error mkdiring %s: %s\n", pax_c, strerror(errno)); + u3l_log("error mkdiring %s: %s", pax_c, strerror(errno)); c3_assert(0); } } @@ -433,7 +433,7 @@ _unix_write_file_hard(c3_c* pax_c, u3_noun mim) u3_noun dat = u3t(u3t(mim)); if ( fid_i < 0 ) { - u3l_log("error opening %s for writing: %s\r\n", + u3l_log("error opening %s for writing: %s", pax_c, strerror(errno)); u3z(mim); return 0; @@ -449,7 +449,7 @@ _unix_write_file_hard(c3_c* pax_c, u3_noun mim) rit_w = write(fid_i, dat_y, siz_w); if ( rit_w != siz_w ) { - u3l_log("error writing %s: %s\r\n", + u3l_log("error writing %s: %s", pax_c, strerror(errno)); mug_w = 0; } @@ -479,7 +479,7 @@ _unix_write_file_soft(u3_ufil* fil_u, u3_noun mim) goto _unix_write_file_soft_go; } else { - u3l_log("error opening file (soft) %s: %s\r\n", + u3l_log("error opening file (soft) %s: %s", fil_u->pax_c, strerror(errno)); u3z(mim); return; @@ -492,17 +492,17 @@ _unix_write_file_soft(u3_ufil* fil_u, u3_noun mim) red_ws = read(fid_i, old_y, len_ws); if ( close(fid_i) < 0 ) { - u3l_log("error closing file (soft) %s: %s\r\n", + u3l_log("error closing file (soft) %s: %s", fil_u->pax_c, strerror(errno)); } if ( len_ws != red_ws ) { if ( red_ws < 0 ) { - u3l_log("error reading file (soft) %s: %s\r\n", + u3l_log("error reading file (soft) %s: %s", fil_u->pax_c, strerror(errno)); } else { - u3l_log("wrong # of bytes read in file %s: %d %d\r\n", + u3l_log("wrong # of bytes read in file %s: %d %d", fil_u->pax_c, len_ws, red_ws); } c3_free(old_y); @@ -578,7 +578,7 @@ _unix_scan_mount_point(u3_unix* unx_u, u3_umon* mon_u) { DIR* rid_u = c3_opendir(mon_u->dir_u.pax_c); if ( !rid_u ) { - u3l_log("error opening pier directory: %s: %s\r\n", + u3l_log("error opening pier directory: %s: %s", mon_u->dir_u.pax_c, strerror(errno)); return; } @@ -591,7 +591,7 @@ _unix_scan_mount_point(u3_unix* unx_u, u3_umon* mon_u) c3_w err_w; if ( 0 != (err_w = u3_readdir_r(rid_u, &ent_u, &out_u)) ) { - u3l_log("erroring loading pier directory %s: %s\r\n", + u3l_log("erroring loading pier directory %s: %s", mon_u->dir_u.pax_c, strerror(errno)); c3_assert(0); @@ -611,7 +611,7 @@ _unix_scan_mount_point(u3_unix* unx_u, u3_umon* mon_u) struct stat buf_u; if ( 0 != stat(pax_c, &buf_u) ) { - u3l_log("can't stat pier directory %s: %s\r\n", + u3l_log("can't stat pier directory %s: %s", mon_u->dir_u.pax_c, strerror(errno)); c3_free(pax_c); continue; @@ -654,7 +654,7 @@ static void _unix_free_file(u3_ufil *fil_u) { if ( 0 != c3_unlink(fil_u->pax_c) && ENOENT != errno ) { - u3l_log("error unlinking %s: %s\n", fil_u->pax_c, strerror(errno)); + u3l_log("error unlinking %s: %s", fil_u->pax_c, strerror(errno)); c3_assert(0); } @@ -766,7 +766,7 @@ _unix_delete_mount_point(u3_unix* unx_u, u3_noun mon) mon_u = unx_u->mon_u; if ( !mon_u ) { - u3l_log("mount point already gone: %s\r\n", nam_c); + u3l_log("mount point already gone: %s", nam_c); goto _delete_mount_point_out; } if ( 0 == strcmp(nam_c, mon_u->nam_c) ) { @@ -782,7 +782,7 @@ _unix_delete_mount_point(u3_unix* unx_u, u3_noun mon) } if ( !mon_u->nex_u ) { - u3l_log("mount point already gone: %s\r\n", nam_c); + u3l_log("mount point already gone: %s", nam_c); goto _delete_mount_point_out; } @@ -901,7 +901,7 @@ _unix_update_file(u3_unix* unx_u, u3_ufil* fil_u) return u3nc(u3nc(_unix_string_to_path(unx_u, fil_u->pax_c), u3_nul), u3_nul); } else { - u3l_log("error opening file %s: %s\r\n", + u3l_log("error opening file %s: %s", fil_u->pax_c, strerror(errno)); return u3_nul; } @@ -913,17 +913,17 @@ _unix_update_file(u3_unix* unx_u, u3_ufil* fil_u) red_ws = read(fid_i, dat_y, len_ws); if ( close(fid_i) < 0 ) { - u3l_log("error closing file %s: %s\r\n", + u3l_log("error closing file %s: %s", fil_u->pax_c, strerror(errno)); } if ( len_ws != red_ws ) { if ( red_ws < 0 ) { - u3l_log("error reading file %s: %s\r\n", + u3l_log("error reading file %s: %s", fil_u->pax_c, strerror(errno)); } else { - u3l_log("wrong # of bytes read in file %s: %d %d\r\n", + u3l_log("wrong # of bytes read in file %s: %d %d", fil_u->pax_c, len_ws, red_ws); } c3_free(dat_y); @@ -992,7 +992,7 @@ _unix_update_dir(u3_unix* unx_u, u3_udir* dir_u) if ( (fid_i < 0) || (fstat(fid_i, &buf_u) < 0) ) { if ( ENOENT != errno ) { - u3l_log("_unix_update_dir: error opening file %s: %s\r\n", + u3l_log("_unix_update_dir: error opening file %s: %s", nod_u->pax_c, strerror(errno)); } @@ -1002,7 +1002,7 @@ _unix_update_dir(u3_unix* unx_u, u3_udir* dir_u) } else { if ( close(fid_i) < 0 ) { - u3l_log("_unix_update_dir: error closing file %s: %s\r\n", + u3l_log("_unix_update_dir: error closing file %s: %s", nod_u->pax_c, strerror(errno)); } @@ -1017,7 +1017,7 @@ _unix_update_dir(u3_unix* unx_u, u3_udir* dir_u) DIR* rid_u = c3_opendir(dir_u->pax_c); if ( !rid_u ) { - u3l_log("error opening directory %s: %s\r\n", + u3l_log("error opening directory %s: %s", dir_u->pax_c, strerror(errno)); c3_assert(0); } @@ -1029,7 +1029,7 @@ _unix_update_dir(u3_unix* unx_u, u3_udir* dir_u) if ( (err_w = u3_readdir_r(rid_u, &ent_u, &out_u)) != 0 ) { - u3l_log("error loading directory %s: %s\r\n", + u3l_log("error loading directory %s: %s", dir_u->pax_c, strerror(err_w)); c3_assert(0); } @@ -1045,7 +1045,7 @@ _unix_update_dir(u3_unix* unx_u, u3_udir* dir_u) struct stat buf_u; if ( 0 != stat(pax_c, &buf_u) ) { - u3l_log("can't stat %s: %s\r\n", pax_c, strerror(errno)); + u3l_log("can't stat %s: %s", pax_c, strerror(errno)); c3_free(pax_c); continue; } @@ -1055,13 +1055,13 @@ _unix_update_dir(u3_unix* unx_u, u3_udir* dir_u) if ( 0 == strcmp(pax_c, nod_u->pax_c) ) { if ( S_ISDIR(buf_u.st_mode) ) { if ( c3n == nod_u->dir ) { - u3l_log("not a directory: %s\r\n", nod_u->pax_c); + u3l_log("not a directory: %s", nod_u->pax_c); c3_assert(0); } } else { if ( c3y == nod_u->dir ) { - u3l_log("not a file: %s\r\n", nod_u->pax_c); + u3l_log("not a file: %s", nod_u->pax_c); c3_assert(0); } } @@ -1095,7 +1095,7 @@ _unix_update_dir(u3_unix* unx_u, u3_udir* dir_u) } if ( closedir(rid_u) < 0 ) { - u3l_log("error closing directory %s: %s\r\n", + u3l_log("error closing directory %s: %s", dir_u->pax_c, strerror(errno)); } @@ -1167,7 +1167,7 @@ _unix_initial_update_file(c3_c* pax_c, c3_c* bas_c) return u3_nul; } else { - u3l_log("error opening initial file %s: %s\r\n", + u3l_log("error opening initial file %s: %s", pax_c, strerror(errno)); return u3_nul; } @@ -1179,17 +1179,17 @@ _unix_initial_update_file(c3_c* pax_c, c3_c* bas_c) red_ws = read(fid_i, dat_y, len_ws); if ( close(fid_i) < 0 ) { - u3l_log("error closing initial file %s: %s\r\n", + u3l_log("error closing initial file %s: %s", pax_c, strerror(errno)); } if ( len_ws != red_ws ) { if ( red_ws < 0 ) { - u3l_log("error reading initial file %s: %s\r\n", + u3l_log("error reading initial file %s: %s", pax_c, strerror(errno)); } else { - u3l_log("wrong # of bytes read in initial file %s: %d %d\r\n", + u3l_log("wrong # of bytes read in initial file %s: %d %d", pax_c, len_ws, red_ws); } c3_free(dat_y); @@ -1217,7 +1217,7 @@ _unix_initial_update_dir(c3_c* pax_c, c3_c* bas_c) DIR* rid_u = c3_opendir(pax_c); if ( !rid_u ) { - u3l_log("error opening initial directory: %s: %s\r\n", + u3l_log("error opening initial directory: %s: %s", pax_c, strerror(errno)); return u3_nul; } @@ -1228,7 +1228,7 @@ _unix_initial_update_dir(c3_c* pax_c, c3_c* bas_c) c3_w err_w; if ( 0 != (err_w = u3_readdir_r(rid_u, &ent_u, &out_u)) ) { - u3l_log("error loading initial directory %s: %s\r\n", + u3l_log("error loading initial directory %s: %s", pax_c, strerror(errno)); c3_assert(0); @@ -1245,7 +1245,7 @@ _unix_initial_update_dir(c3_c* pax_c, c3_c* bas_c) struct stat buf_u; if ( 0 != stat(pox_c, &buf_u) ) { - u3l_log("initial can't stat %s: %s\r\n", + u3l_log("initial can't stat %s: %s", pox_c, strerror(errno)); c3_free(pox_c); continue; @@ -1263,7 +1263,7 @@ _unix_initial_update_dir(c3_c* pax_c, c3_c* bas_c) } if ( closedir(rid_u) < 0 ) { - u3l_log("error closing initial directory %s: %s\r\n", + u3l_log("error closing initial directory %s: %s", pax_c, strerror(errno)); } @@ -1354,16 +1354,16 @@ _unix_sync_change(u3_unix* unx_u, u3_udir* dir_u, u3_noun pax, u3_noun mim) if ( c3n == u3du(pax) ) { if ( u3_nul == pax ) { - u3l_log("can't sync out file as top-level, strange\r\n"); + u3l_log("can't sync out file as top-level, strange"); } else { - u3l_log("sync out: bad path\r\n"); + u3l_log("sync out: bad path"); } u3z(pax); u3z(mim); return; } else if ( c3n == u3du(u3t(pax)) ) { - u3l_log("can't sync out file as top-level, strangely\r\n"); + u3l_log("can't sync out file as top-level, strangely"); u3z(pax); u3z(mim); } else { @@ -1393,7 +1393,7 @@ _unix_sync_change(u3_unix* unx_u, u3_udir* dir_u, u3_noun pax, u3_noun mim) } if ( c3n == nod_u->dir ) { - u3l_log("weird, we got a file when we weren't expecting to\r\n"); + u3l_log("weird, we got a file when we weren't expecting to"); c3_assert(0); } diff --git a/pkg/urbit/vere/king.c b/pkg/urbit/vere/king.c index 44a8cd502a..64896a6975 100644 --- a/pkg/urbit/vere/king.c +++ b/pkg/urbit/vere/king.c @@ -246,7 +246,7 @@ _king_curl_bytes(c3_c* url_c, c3_w* len_w, c3_y** hun_y, c3_t veb_t) uv_buf_t buf_u = uv_buf_init(c3_malloc(1), 0); if ( !(cul_u = curl_easy_init()) ) { - u3l_log("failed to initialize libcurl\n"); + u3l_log("failed to initialize libcurl"); exit(1); } @@ -262,13 +262,13 @@ _king_curl_bytes(c3_c* url_c, c3_w* len_w, c3_y** hun_y, c3_t veb_t) // if ( CURLE_OK != res_i ) { if ( veb_t ) { - u3l_log("curl: failed %s: %s\n", url_c, curl_easy_strerror(res_i)); + u3l_log("curl: failed %s: %s", url_c, curl_easy_strerror(res_i)); } ret_i = -1; } if ( 300 <= cod_i ) { if ( veb_t ) { - u3l_log("curl: error %s: HTTP %ld\n", url_c, cod_i); + u3l_log("curl: error %s: HTTP %ld", url_c, cod_i); } ret_i = -2; } @@ -406,12 +406,12 @@ _get_cmd_output(c3_c *cmd_c, c3_c *out_c, c3_w len_c) { FILE *fp = popen(cmd_c, "r"); if ( NULL == fp ) { - u3l_log("'%s' failed\n", cmd_c); + u3l_log("'%s' failed", cmd_c); exit(1); } if ( NULL == fgets(out_c, len_c, fp) ) { - u3l_log("'%s' produced no output\n", cmd_c); + u3l_log("'%s' produced no output", cmd_c); exit(1); } @@ -444,7 +444,7 @@ _git_pill_url(c3_c *out_c, c3_c *arv_c) assert(NULL != arv_c); if ( 0 != system("which git >> /dev/null") ) { - u3l_log("boot: could not find git executable\r\n"); + u3l_log("boot: could not find git executable"); exit(1); } @@ -461,7 +461,7 @@ _boothack_pill(void) u3_noun pil; if ( 0 != u3_Host.ops_u.pil_c ) { - u3l_log("boot: loading pill %s\r\n", u3_Host.ops_u.pil_c); + u3l_log("boot: loading pill %s", u3_Host.ops_u.pil_c); pil = u3m_file(u3_Host.ops_u.pil_c); } else { @@ -477,12 +477,12 @@ _boothack_pill(void) strcpy(url_c, u3_Host.ops_u.url_c); } - u3l_log("boot: downloading pill %s\r\n", url_c); + u3l_log("boot: downloading pill %s", url_c); pil = _king_get_atom(url_c); } if ( 0 != u3_Host.ops_u.arv_c ) { - u3l_log("boot: preparing filesystem from %s\r\n", + u3l_log("boot: preparing filesystem from %s", u3_Host.ops_u.arv_c); arv = u3nc(u3_nul, u3_unix_initial_into_card(u3_Host.ops_u.arv_c)); } @@ -503,7 +503,7 @@ _boothack_key(u3_noun kef) if ( u3_nul == des ) { c3_c* kef_c = u3r_string(kef); - u3l_log("dawn: invalid private keys: %s\r\n", kef_c); + u3l_log("dawn: invalid private keys: %s", kef_c); c3_free(kef_c); exit(1); } @@ -512,7 +512,7 @@ _boothack_key(u3_noun kef) // u3_noun pro = u3m_soft(0, u3ke_cue, u3k(u3t(des))); if ( u3_blip != u3h(pro) ) { - u3l_log("dawn: unable to cue keyfile\r\n"); + u3l_log("dawn: unable to cue keyfile"); exit(1); } seed = u3k(u3t(pro)); @@ -535,7 +535,7 @@ _boothack_key(u3_noun kef) u3_noun whu = u3dc("slaw", 'p', u3k(woh)); if ( u3_nul == whu ) { - u3l_log("dawn: invalid ship specified with -w %s\r\n", + u3l_log("dawn: invalid ship specified with -w %s", u3_Host.ops_u.who_c); exit(1); } @@ -545,7 +545,7 @@ _boothack_key(u3_noun kef) { u3_noun how = u3dc("scot", 'p', u3k(ship)); c3_c* how_c = u3r_string(u3k(how)); - u3l_log("dawn: mismatch between -w %s and -K %s\r\n", + u3l_log("dawn: mismatch between -w %s and -K %s", u3_Host.ops_u.who_c, how_c); u3z(how); @@ -576,7 +576,7 @@ _boothack_doom(void) u3_noun whu = u3dc("slaw", 'p', u3k(fak)); if ( u3_nul == whu ) { - u3l_log("boot: malformed -F ship %s\r\n", u3_Host.ops_u.fak_c); + u3l_log("boot: malformed -F ship %s", u3_Host.ops_u.fak_c); u3_king_bail(); } @@ -610,7 +610,7 @@ _boothack_doom(void) kef = u3i_string(u3_Host.ops_u.gen_c); } else { - u3l_log("boot: must specify a key with -k or -G\r\n"); + u3l_log("boot: must specify a key with -k or -G"); exit(1); } @@ -705,7 +705,7 @@ _king_sign_cb(uv_signal_t* sil_u, c3_i num_i) { switch ( num_i ) { default: { - u3l_log("\r\nmysterious signal %d\r\n", num_i); + u3l_log("\r\nmysterious signal %d", num_i); break; } @@ -715,7 +715,7 @@ _king_sign_cb(uv_signal_t* sil_u, c3_i num_i) } case SIGINT: { - u3l_log("\r\ninterrupt\r\n"); + u3l_log("\r\ninterrupt"); u3_term_ef_ctlc(); #ifdef U3_OS_mingw @@ -818,7 +818,7 @@ _king_boot_ivory(void) if ( u3_Host.ops_u.lit_c ) { if ( c3n == u3u_mmap_read("lite", u3_Host.ops_u.lit_c, &len_d, &byt_y) ) { - u3l_log("lite: unable to load ivory pill at %s\n", + u3l_log("lite: unable to load ivory pill at %s", u3_Host.ops_u.lit_c); exit(1); } @@ -833,21 +833,21 @@ _king_boot_ivory(void) u3_weak pil; if ( u3_none == (pil = u3s_cue_xeno_with(sil_u, len_d, byt_y)) ) { - u3l_log("lite: unable to cue ivory pill\r\n"); + u3l_log("lite: unable to cue ivory pill"); exit(1); } u3s_cue_xeno_done(sil_u); if ( c3n == u3v_boot_lite(pil)) { - u3l_log("lite: boot failed\r\n"); + u3l_log("lite: boot failed"); exit(1); } } if ( u3_Host.ops_u.lit_c ) { if ( c3n == u3u_munmap(len_d, byt_y) ) { - u3l_log("lite: unable to unmap ivory pill at %s\n", + u3l_log("lite: unable to unmap ivory pill at %s", u3_Host.ops_u.lit_c); exit(1); } @@ -901,7 +901,7 @@ u3_king_commence() rlm.rlim_cur = 0; if ( 0 != setrlimit(RLIMIT_CORE, &rlm) ) { - u3l_log("king: unable to disable core dumps: %s\r\n", strerror(errno)); + u3l_log("king: unable to disable core dumps: %s", strerror(errno)); exit(1); } } @@ -974,7 +974,7 @@ _king_save_file(c3_c* url_c, FILE* fil_u) long cod_i; if ( !(cul_u = curl_easy_init()) ) { - u3l_log("failed to initialize libcurl\n"); + u3l_log("failed to initialize libcurl"); exit(1); } @@ -988,11 +988,11 @@ _king_save_file(c3_c* url_c, FILE* fil_u) // XX retry? // if ( CURLE_OK != res_i ) { - u3l_log("curl: failed %s: %s\n", url_c, curl_easy_strerror(res_i)); + u3l_log("curl: failed %s: %s", url_c, curl_easy_strerror(res_i)); ret_i = -1; } if ( 300 <= cod_i ) { - u3l_log("curl: error %s: HTTP %ld\n", url_c, cod_i); + u3l_log("curl: error %s: HTTP %ld", url_c, cod_i); ret_i = -2; } @@ -1058,14 +1058,14 @@ _king_init_pace(c3_c* pac_c) return 0; } else { - u3l_log("dock: init pace (%s): open %s\n", pac_c, strerror(errno)); + u3l_log("dock: init pace (%s): open %s", pac_c, strerror(errno)); c3_free(bin_c); return -1; } } if ( _king_write_raw(fid_i, (c3_y*)pac_c, strlen(pac_c)) ) { - u3l_log("dock: init pace (%s): write %s\n", pac_c, strerror(errno)); + u3l_log("dock: init pace (%s): write %s", pac_c, strerror(errno)); close(fid_i); c3_free(bin_c); return -1; @@ -1073,12 +1073,12 @@ _king_init_pace(c3_c* pac_c) // XX sync first? // else if ( close(fid_i) ) { - u3l_log("dock: init pace (%s): close %s\n", pac_c, strerror(errno)); + u3l_log("dock: init pace (%s): close %s", pac_c, strerror(errno)); c3_free(bin_c); return 1; } - u3l_log("dock: pace (%s): configured at %s/.bin/pace\r\n", + u3l_log("dock: pace (%s): configured at %s/.bin/pace", pac_c, u3_Host.dir_c); return 0; @@ -1138,12 +1138,12 @@ u3_king_vere(c3_c* pac_c, // pace || !(fil_u = fdopen(fid_i, "wb")) ) { if ( EEXIST == errno ) { - u3l_log("already installed\n"); + u3l_log("already installed"); c3_free(bin_c); return 0; } else { - u3l_log("unable to open %s: %s\r\n", bin_c, strerror(errno)); + u3l_log("unable to open %s: %s", bin_c, strerror(errno)); c3_free(bin_c); return -1; } @@ -1154,7 +1154,7 @@ u3_king_vere(c3_c* pac_c, // pace c3_assert( ret_i > 0 ); if ( (ret_i = _king_save_file(url_c, fil_u)) ) { - u3l_log("unable to save %s to %s: %d\r\n", url_c, bin_c, ret_i); + u3l_log("unable to save %s to %s: %d", url_c, bin_c, ret_i); c3_free(url_c); fclose(fil_u); unlink(bin_c); @@ -1187,7 +1187,7 @@ u3_king_vere(c3_c* pac_c, // pace } } - u3l_log("vere: saved to %s\n", bin_c); + u3l_log("vere: saved to %s", bin_c); c3_free(url_c); c3_free(bin_c); @@ -1210,13 +1210,13 @@ _king_do_upgrade(c3_c* pac_c, c3_c* ver_c) arc_c = u3_Host.arc_c; } else { - u3l_log("vere: --arch required\r\n"); + u3l_log("vere: --arch required"); return; } #endif if ( _king_make_pace(pac_c) ) { - u3l_log("vere: unable to make pace (%s) directory in pier\n", pac_c); + u3l_log("vere: unable to make pace (%s) directory in pier", pac_c); u3_king_bail(); exit(1); } @@ -1229,13 +1229,13 @@ _king_do_upgrade(c3_c* pac_c, c3_c* ver_c) // XX get link option // if ( u3_king_vere(pac_c, ver_c, arc_c, dir_c, 1) ) { - u3l_log("vere: upgrade failed\r\n"); + u3l_log("vere: upgrade failed"); u3_king_bail(); exit(1); } c3_free(dir_c); - u3l_log("vere: upgrade succeeded\r\n"); + u3l_log("vere: upgrade succeeded"); // XX print restart instructions } @@ -1471,7 +1471,7 @@ u3_king_dock(c3_c* pac_c) // XX get link option // if ( _king_copy_vere(pac_c, URBIT_VERSION, arc_c, 1) ) { - u3l_log("vere: binary copy failed\r\n"); + u3l_log("vere: binary copy failed"); u3_king_bail(); exit(1); } @@ -1479,7 +1479,7 @@ u3_king_dock(c3_c* pac_c) // NB: failure ignored // _king_init_pace(pac_c); - u3l_log("vere: binary copy succeeded\r\n"); + u3l_log("vere: binary copy succeeded"); // XX print restart instructions } } @@ -1509,10 +1509,10 @@ u3_king_done(void) if ( u3_Host.xit_i ) { if ( c3y == u3_Host.nex_o ) { - u3l_log("vere: upgrade failed\r\n"); + u3l_log("vere: upgrade failed"); } else if ( c3y == u3_Host.pep_o ) { - u3l_log("vere: prep for upgrade failed\r\n"); + u3l_log("vere: prep for upgrade failed"); } } else { @@ -1530,15 +1530,15 @@ u3_king_done(void) switch ( u3_king_next(pac_c, &ver_c) ) { case -2: { - u3l_log("vere: unable to check for next version\n"); + u3l_log("vere: unable to check for next version"); } break; case -1: { - u3l_log("vere: up to date\n"); + u3l_log("vere: up to date"); } break; case 0: { - u3l_log("vere: next (%%%s): %s\n", pac_c, ver_c); + u3l_log("vere: next (%%%s): %s", pac_c, ver_c); _king_do_upgrade(pac_c, ver_c); c3_free(ver_c); } break; @@ -1549,7 +1549,7 @@ u3_king_done(void) c3_free(pac_c); } else if ( c3y == u3_Host.pep_o ) { - u3l_log("vere: ready for upgrade\n"); + u3l_log("vere: ready for upgrade"); } // copy binary into pier on boot @@ -1652,7 +1652,7 @@ u3_king_grab(void* vod_p) } #else { - u3_term_io_loja(0); + u3_term_io_loja(0, fil_u); } #endif } diff --git a/pkg/urbit/vere/lord.c b/pkg/urbit/vere/lord.c index 2051908485..b7b356e571 100644 --- a/pkg/urbit/vere/lord.c +++ b/pkg/urbit/vere/lord.c @@ -267,13 +267,13 @@ _lord_plea_live(u3_lord* god_u, u3_noun dat) case u3_writ_meld: { // XX wire into cb // - u3l_log("pier: meld complete\n"); + u3l_log("pier: meld complete"); } break; case u3_writ_pack: { // XX wire into cb // - u3l_log("pier: pack complete\n"); + u3l_log("pier: pack complete"); } break; } @@ -1056,7 +1056,7 @@ _lord_on_serf_err_cb(uv_stream_t* pyp_u, uv_read_stop(pyp_u); if ( siz_i != UV_EOF ) { - u3l_log("lord: serf stderr: %s\r\n", uv_strerror(siz_i)); + u3l_log("lord: serf stderr: %s", uv_strerror(siz_i)); } } @@ -1098,10 +1098,10 @@ _lord_on_serf_bail(void* ptr_v, u3_lord* god_u = ptr_v; if ( UV_EOF == err_i ) { - u3l_log("pier: serf unexpectedly shut down\r\n"); + u3l_log("pier: serf unexpectedly shut down"); } else { - u3l_log("pier: serf error: %s\r\n", err_c); + u3l_log("pier: serf error: %s", err_c); } _lord_bail(god_u); @@ -1128,7 +1128,7 @@ u3_lord_info(u3_lord* god_u) void u3_lord_slog(u3_lord* god_u) { - u3l_log(" lord: live=%s, event=%" PRIu64 ", mug=%x, queue=%u\n", + u3l_log(" lord: live=%s, event=%" PRIu64 ", mug=%x, queue=%u", ( c3y == god_u->liv_o ) ? "&" : "|", god_u->eve_d, god_u->mug_l, @@ -1165,7 +1165,7 @@ u3_lord_init(c3_c* pax_c, c3_w wag_w, c3_d key_d[4], u3_lord_cb cb_u) c3_c lom_c[11]; c3_i err_i; - sprintf(key_c, "%" PRIx64 ":%" PRIx64 ":%" PRIx64 ":%" PRIx64 "", + sprintf(key_c, "%" PRIx64 ":%" PRIx64 ":%" PRIx64 ":%" PRIx64, god_u->key_d[0], god_u->key_d[1], god_u->key_d[2], diff --git a/pkg/urbit/vere/newt.c b/pkg/urbit/vere/newt.c index 02dd23b10a..583edf5e39 100644 --- a/pkg/urbit/vere/newt.c +++ b/pkg/urbit/vere/newt.c @@ -384,7 +384,7 @@ u3_newt_moat_slog(u3_moat* mot_u) } if ( len_w ) { - u3l_log(" newt: %u inbound ipc messages pending\n", len_w); + u3l_log(" newt: %u inbound ipc messages pending", len_w); } } diff --git a/pkg/urbit/vere/pier.c b/pkg/urbit/vere/pier.c index 9f1fd00e01..a0870ff60b 100644 --- a/pkg/urbit/vere/pier.c +++ b/pkg/urbit/vere/pier.c @@ -251,7 +251,7 @@ _pier_work(u3_work* wok_u) // XX this is when "boot" is actually complete // XX even better would be after neighboring with our sponsor // - u3l_log("pier (%" PRIu64 "): live\r\n", pir_u->god_u->eve_d); + u3l_log("pier (%" PRIu64 "): live", pir_u->god_u->eve_d); // XX move callbacking to king // @@ -286,7 +286,7 @@ _pier_on_lord_work_spin(void* ptr_v, u3_atom pin, c3_o del_o) u3_term_start_spinner(pin, del_o); } -/* _pier_on_lord_work_spin(): stop spinner +/* _pier_on_lord_work_spun(): stop spinner */ static void _pier_on_lord_work_spun(void* ptr_v) @@ -472,13 +472,13 @@ _pier_on_scry_done(void* ptr_v, u3_noun nun) u3_weak res = u3r_at(7, nun); if (u3_none == res) { - u3l_log("pier: scry failed\n"); + u3l_log("pier: scry failed"); } else { u3_weak out; c3_c *ext_c, *pac_c; - u3l_log("pier: scry succeeded\n"); + u3l_log("pier: scry succeeded"); if ( u3_Host.ops_u.puk_c ) { pac_c = u3_Host.ops_u.puk_c; @@ -504,7 +504,7 @@ _pier_on_scry_done(void* ptr_v, u3_noun nun) ext_c = "txt"; } else { - u3l_log("pier: cannot export cell as %s\n", u3_Host.ops_u.puf_c); + u3l_log("pier: cannot export cell as %s", u3_Host.ops_u.puf_c); out = u3_none; } u3z(puf); @@ -517,11 +517,11 @@ _pier_on_scry_done(void* ptr_v, u3_noun nun) snprintf(fil_c, 256, "%s.%s", pac_c + 1, ext_c); u3_unix_save(fil_c, out); - u3l_log("pier: scry result in %s/.urb/put/%s\n", u3_Host.dir_c, fil_c); + u3l_log("pier: scry result in %s/.urb/put/%s", u3_Host.dir_c, fil_c); } } - u3l_log("pier: exit\n"); + u3l_log("pier: exit"); u3_pier_exit(pir_u); u3z(nun); @@ -600,7 +600,7 @@ _pier_work_init(u3_pier* pir_u) } else { // run the requested scry, jam to disk, then exit // - u3l_log("pier: scry\n"); + u3l_log("pier: scry"); u3_pier_peek_last(pir_u, u3_nul, u3k(car), u3k(dek), u3k(pax), pir_u, _pier_on_scry_done); } @@ -649,7 +649,7 @@ _pier_wyrd_fail(u3_pier* pir_u, u3_ovum* egg_u, u3_noun lud) { // XX version negotiation failed, print upgrade message // - u3l_log("pier: version negotation failed\n\n"); + u3l_log("pier: version negotation failed"); // XX only print trace with -v ? // @@ -673,8 +673,8 @@ _pier_wyrd_fail(u3_pier* pir_u, u3_ovum* egg_u, u3_noun lud) // XX organizing version constants // #define VERE_NAME "vere" -#define VERE_ZUSE 417 -#define VERE_LULL 328 +#define VERE_ZUSE 416 +#define VERE_LULL 327 /* _pier_wyrd_aver(): check for %wend effect and version downgrade. RETAIN */ @@ -736,7 +736,7 @@ _pier_on_lord_wyrd_done(void* ptr_v, // XX messaging, cli argument to bypass // - u3l_log("pier: version negotiation failed; downgrade\n"); + u3l_log("pier: version negotiation failed; downgrade"); _pier_wyrd_fail(pir_u, egg_u, u3_nul); } else { @@ -829,7 +829,7 @@ _pier_wyrd_init(u3_pier* pir_u) pir_u->sat_e = u3_psat_wyrd; - u3l_log("vere: checking version compatibility\n"); + u3l_log("vere: checking version compatibility"); { u3_lord* god_u = pir_u->god_u; @@ -1018,11 +1018,11 @@ _pier_play(u3_play* pay_u) if ( god_u->eve_d == pay_u->eve_d ) { // XX should be play_cb // - u3l_log("---------------- playback complete ----------------\r\n"); + u3l_log("---------------- playback complete ----------------"); u3_term_stop_spinner(); if ( pay_u->eve_d < log_u->dun_d ) { - // u3l_log("pier: replay barrier reached, shutting down\r\n"); + // u3l_log("pier: replay barrier reached, shutting down"); // // XX graceful shutdown // // // u3_lord_save(pir_u->god_u); @@ -1031,7 +1031,7 @@ _pier_play(u3_play* pay_u) // XX temporary hack // - u3l_log("pier: replay barrier reached, cramming\r\n"); + u3l_log("pier: replay barrier reached, cramming"); u3_pier_cram(pir_u); } else if ( pay_u->eve_d == log_u->dun_d ) { @@ -1067,12 +1067,12 @@ _pier_on_lord_play_done(void* ptr_v, u3_info fon_u, c3_l mug_l) c3_assert( u3_psat_play == pir_u->sat_e ); - u3l_log("pier: (%" PRIu64 "): play: done\r\n", tac_u->eve_d); + u3l_log("pier: (%" PRIu64 "): play: done", tac_u->eve_d); // XX optional // if ( tac_u->mug_l && (tac_u->mug_l != mug_l) ) { - u3l_log("pier: (%" PRIu64 "): play: mug mismatch %x %x\r\n", + u3l_log("pier: (%" PRIu64 "): play: mug mismatch %x %x", tac_u->eve_d, tac_u->mug_l, mug_l); @@ -1121,7 +1121,7 @@ _pier_on_lord_play_bail(void* ptr_v, u3_info fon_u, // XX optional // if ( las_l && (las_l != mug_l) ) { - u3l_log("pier: (%" PRIu64 "): play bail: mug mismatch %x %x\r\n", + u3l_log("pier: (%" PRIu64 "): play bail: mug mismatch %x %x", (c3_d)(eve_d - 1ULL), las_l, mug_l); @@ -1132,7 +1132,7 @@ _pier_on_lord_play_bail(void* ptr_v, u3_info fon_u, // #if 0 { - u3l_log("pier: (%" PRIu64 "): play: retry\r\n", eve_d); + u3l_log("pier: (%" PRIu64 "): play: retry", eve_d); fon_u.ext_u = tac_u; @@ -1153,7 +1153,7 @@ _pier_on_lord_play_bail(void* ptr_v, u3_info fon_u, } #else { - u3l_log("pier: (%" PRIu64 "): play: bail\r\n", eve_d); + u3l_log("pier: (%" PRIu64 "): play: bail", eve_d); u3_pier_punt_goof("play", dud); { u3_noun wir, tag; @@ -1189,12 +1189,12 @@ _pier_play_init(u3_pier* pir_u, c3_d eve_d) pay_u->eve_d = eve_d; pay_u->sen_d = god_u->eve_d; - u3l_log("---------------- playback starting ----------------\r\n"); + u3l_log("---------------- playback starting ----------------"); if ( (1ULL + god_u->eve_d) == eve_d ) { - u3l_log("pier: replaying event %" PRIu64 "\r\n", eve_d); + u3l_log("pier: replaying event %" PRIu64, eve_d); } else { - u3l_log("pier: replaying events %" PRIu64 "-%" PRIu64 "\r\n", + u3l_log("pier: replaying events %" PRIu64 "-%" PRIu64, (c3_d)(1ULL + god_u->eve_d), eve_d); } @@ -1323,7 +1323,7 @@ _pier_on_lord_cram(void* ptr_v) // XX temporary hack // if ( u3_psat_play == pir_u->sat_e ) { - u3l_log("pier: cram complete, shutting down\r\n"); + u3l_log("pier: cram complete, shutting down"); u3_pier_bail(pir_u); exit(0); } @@ -1352,7 +1352,7 @@ _pier_on_lord_exit(void* ptr_v) pir_u->god_u = 0; if ( u3_psat_done != pir_u->sat_e ) { - u3l_log("pier: serf shutdown unexpected\r\n"); + u3l_log("pier: serf shutdown unexpected"); u3_pier_bail(pir_u); } // if we made it all the way here, it's our jab to wrap up @@ -1413,10 +1413,10 @@ _pier_on_lord_live(void* ptr_v) // if ( u3_Host.ops_u.til_c ) { if ( 1 == sscanf(u3_Host.ops_u.til_c, "%" PRIu64 "", &eve_d) ) { - u3l_log("pier: replay till %" PRIu64 "\r\n", eve_d); + u3l_log("pier: replay till %" PRIu64, eve_d); } else { - u3l_log("pier: ignoring invalid replay barrier '%s'\r\n", + u3l_log("pier: ignoring invalid replay barrier '%s'", u3_Host.ops_u.til_c); eve_d = log_u->dun_d; } @@ -1534,51 +1534,51 @@ u3_pier_slog(u3_pier* pir_u) { switch ( pir_u->sat_e ) { default: { - u3l_log("pier: unknown state: %u\r\n", pir_u->sat_e); + u3l_log("pier: unknown state: %u", pir_u->sat_e); } break; case u3_psat_init: { - u3l_log("pier: init\n"); + u3l_log("pier: init"); } break; case u3_psat_boot: { - u3l_log("pier: boot\n"); + u3l_log("pier: boot"); } break; case u3_psat_play: { - u3l_log("pier: play\n"); + u3l_log("pier: play"); { u3_play* pay_u = pir_u->pay_u; - u3l_log(" target: %" PRIu64 "\n", pay_u->eve_d); - u3l_log(" sent: %" PRIu64 "\n", pay_u->sen_d); - u3l_log(" read: %" PRIu64 "\n", pay_u->req_d); + u3l_log(" target: %" PRIu64, pay_u->eve_d); + u3l_log(" sent: %" PRIu64, pay_u->sen_d); + u3l_log(" read: %" PRIu64, pay_u->req_d); } } break; case u3_psat_work: { - u3l_log("pier: work\n"); + u3l_log("pier: work"); { u3_work* wok_u = pir_u->wok_u; - u3l_log(" effects: released=%" PRIu64 "\n", wok_u->fec_u.rel_d); + u3l_log(" effects: released=%" PRIu64, wok_u->fec_u.rel_d); if ( wok_u->fec_u.ext_u ) { if ( wok_u->fec_u.ext_u != wok_u->fec_u.ent_u ) { - u3l_log(" pending %" PRIu64 "-%" PRIu64 "\n", + u3l_log(" pending %" PRIu64 "-%" PRIu64, wok_u->fec_u.ext_u->eve_d, wok_u->fec_u.ent_u->eve_d); } else { - u3l_log(" pending %" PRIu64 "\n", wok_u->fec_u.ext_u->eve_d); + u3l_log(" pending %" PRIu64, wok_u->fec_u.ext_u->eve_d); } } if ( wok_u->wal_u ) { - u3l_log(" wall: %" PRIu64 "\n", wok_u->wal_u->eve_d); + u3l_log(" wall: %" PRIu64, wok_u->wal_u->eve_d); } if ( wok_u->car_u ) { @@ -1588,7 +1588,7 @@ u3_pier_slog(u3_pier* pir_u) } break; case u3_psat_done: { - u3l_log("pier: done\n"); + u3l_log("pier: done"); } break; } @@ -1704,7 +1704,7 @@ u3_pier_stay(c3_w wag_w, u3_noun pax) if ( c3y == u3_Host.ops_u.veb ) { FILE* fil_u = u3_term_io_hija(); u3_lmdb_stat(pir_u->log_u->mdb_u, fil_u); - u3_term_io_loja(1); + u3_term_io_loja(1, fil_u); } u3z(pax); @@ -2317,10 +2317,12 @@ _pier_dump_wall(FILE* fil_u, u3_noun wol) while ( u3_nul != wal ) { _pier_dump_tape(fil_u, u3k(u3h(wal))); - putc(13, fil_u); - putc(10, fil_u); - wal = u3t(wal); + + if ( u3_nul != wal ) { + putc(13, fil_u); + putc(10, fil_u); + } } u3z(wol); @@ -2346,6 +2348,7 @@ u3_pier_tank(c3_l tab_l, c3_w pri_w, u3_noun tac) case 3: fprintf(fil_u, "\033[31m>>> "); break; case 2: fprintf(fil_u, "\033[33m>> "); break; case 1: fprintf(fil_u, "\033[32m> "); break; + case 0: fprintf(fil_u, "\033[90m" ); break; } } else { @@ -2362,8 +2365,6 @@ u3_pier_tank(c3_l tab_l, c3_w pri_w, u3_noun tac) if ( 0 == u3A->roc ) { if ( c3__leaf == u3h(tac) ) { _pier_dump_tape(fil_u, u3k(u3t(tac))); - putc(13, fil_u); - putc(10, fil_u); } } // We are calling nock here, but hopefully need no protection. @@ -2380,7 +2381,7 @@ u3_pier_tank(c3_l tab_l, c3_w pri_w, u3_noun tac) fflush(fil_u); - u3_term_io_loja(0); + u3_term_io_loja(0, fil_u); u3z(blu); u3z(tac); } @@ -2410,12 +2411,12 @@ u3_pier_punt_goof(const c3_c* cap_c, u3_noun dud) u3x_cell(dud, &mot, &tan); - u3l_log("\n"); + u3l_log(""); u3_pier_punt(0, u3qb_flop(tan)); { c3_c* mot_c = u3r_string(mot); - u3l_log("%s: bail: %%%s\r\n", cap_c, mot_c); + u3l_log("%s: bail: %%%s", cap_c, mot_c); c3_free(mot_c); } @@ -2431,7 +2432,7 @@ u3_pier_punt_ovum(const c3_c* cap_c, u3_noun wir, u3_noun tag) u3_noun riw = u3do("spat", wir); c3_c* wir_c = u3r_string(riw); - u3l_log("%s: %%%s event on %s failed\r\n\n", cap_c, tag_c, wir_c); + u3l_log("%s: %%%s event on %s failed", cap_c, tag_c, wir_c); c3_free(tag_c); c3_free(wir_c); diff --git a/pkg/urbit/vere/save.c b/pkg/urbit/vere/save.c index b2fc5b3904..956d750cca 100644 --- a/pkg/urbit/vere/save.c +++ b/pkg/urbit/vere/save.c @@ -25,7 +25,7 @@ u3_save_ef_chld(u3_pier *pir_u) /* modified for cases with no pid_w */ - u3l_log("checkpoint: complete %d\n", sav_u->pid_w); + u3l_log("checkpoint: complete %d", sav_u->pid_w); pid_w = wait(&loc_i); if (0 != sav_u->pid_w) { c3_assert(pid_w == sav_u->pid_w); diff --git a/pkg/urbit/worker/serf.c b/pkg/urbit/worker/serf.c index 85420a122e..ec20a32ecc 100644 --- a/pkg/urbit/worker/serf.c +++ b/pkg/urbit/worker/serf.c @@ -243,7 +243,7 @@ _serf_grab(u3_noun sac) u3z(sac); - u3l_log("\n"); + u3l_log(""); } } @@ -318,7 +318,7 @@ u3_serf_post(u3_serf* sef_u) if ( c3y == sef_u->pac_o ) { u3a_print_memory(stderr, "serf: pack: gained", u3m_pack()); - u3l_log("\n"); + u3l_log(""); sef_u->pac_o = c3n; } } @@ -500,7 +500,7 @@ _serf_poke(u3_serf* sef_u, c3_c* cap_c, c3_w mil_w, u3_noun job) if ( (c3__belt != tag) && (c3__crud != tag) ) { - u3l_log("serf: %s (%" PRIu64 ") %s\r\n", cap_c, sef_u->sen_d, txt_c); + u3l_log("serf: %s (%" PRIu64 ") %s", cap_c, sef_u->sen_d, txt_c); } } #endif @@ -520,7 +520,7 @@ _serf_poke(u3_serf* sef_u, c3_c* cap_c, c3_w mil_w, u3_noun job) clr_w = ms_w > 1000 ? 1 : ms_w < 100 ? 2 : 3; // red, green, yellow if ( clr_w != 2 ) { - u3l_log("\x1b[3%dm%%%s (%" PRIu64 ") %4d.%02dms\x1b[0m\n", + u3l_log("\x1b[3%dm%%%s (%" PRIu64 ") %4d.%02dms\x1b[0m", clr_w, txt_c, sef_u->sen_d, ms_w, (int) (d0.tv_usec % 1000) / 10); } @@ -559,7 +559,7 @@ _serf_work(u3_serf* sef_u, c3_w mil_w, u3_noun job) // if ( u3_blip == u3h(gon) ) { u3_noun vir = _serf_sure(sef_u, pre_w, u3k(u3t(gon))); - + u3z(gon); u3z(job); return u3nc(c3__done, u3nt(u3i_chubs(1, &sef_u->dun_d), sef_u->mug_l, @@ -941,7 +941,7 @@ u3_serf_live(u3_serf* sef_u, u3_noun com, u3_noun* ret) return c3n; } - u3l_log("serf (%" PRIu64 "): saving rock\r\n", sef_u->dun_d); + u3l_log("serf (%" PRIu64 "): saving rock", sef_u->dun_d); if ( c3n == u3u_cram(sef_u->dir_c, eve_d) ) { fprintf(stderr, "serf (%" PRIu64 "): unable to jam state\r\n", eve_d); @@ -1085,7 +1085,7 @@ u3_serf_writ(u3_serf* sef_u, u3_noun wit, u3_noun* pel) static u3_noun _serf_ripe(u3_serf* sef_u) { - // u3l_log("serf: ripe %" PRIu64 "\r\n", sef_u->dun_d); + // u3l_log("serf: ripe %" PRIu64, sef_u->dun_d); sef_u->mug_l = ( 0 == sef_u->dun_d ) ? 0 diff --git a/pkg/webterm/desk.docket-0 b/pkg/webterm/desk.docket-0 index 549932de6e..531bd1d58c 100644 --- a/pkg/webterm/desk.docket-0 +++ b/pkg/webterm/desk.docket-0 @@ -1,9 +1,9 @@ :~ title+'Terminal' info+'A web interface to your Urbit\'s command line.' color+0x2e.4347 - glob-http+['https://bootstrap.urbit.org/glob-0v7.1hgb7.euged.6oj3e.cdhdg.rah02.glob' 0v7.1hgb7.euged.6oj3e.cdhdg.rah02] + glob-http+['https://bootstrap.urbit.org/glob-0v5.hvjci.n7c4h.1onl6.34g14.fut7c.glob' 0v5.hvjci.n7c4h.1onl6.34g14.fut7c] base+'webterm' - version+[1 0 1] + version+[1 1 0] website+'https://tlon.io' license+'MIT' == diff --git a/pkg/webterm/sys.kelvin b/pkg/webterm/sys.kelvin index b7bcb9ecd8..0cb1220e5f 100644 --- a/pkg/webterm/sys.kelvin +++ b/pkg/webterm/sys.kelvin @@ -1 +1 @@ -[%zuse 417] +[%zuse 416]