mirror of
https://github.com/urbit/shrub.git
synced 2024-12-20 01:01:37 +03:00
Merge pull request #6194 from urbit/next/416
next: zuse 416 (terminal edition)
This commit is contained in:
commit
23dcad2394
32
.github/actions/glob/entrypoint.sh
vendored
32
.github/actions/glob/entrypoint.sh
vendored
@ -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
|
||||
|
@ -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"]
|
@ -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 }}
|
20
.github/actions/ota/entrypoint.sh
vendored
Executable file
20
.github/actions/ota/entrypoint.sh
vendored
Executable file
@ -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
|
27
.github/workflows/chromatic.yml
vendored
27
.github/workflows/chromatic.yml
vendored
@ -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
|
20
.github/workflows/glob.yml
vendored
20
.github/workflows/glob.yml
vendored
@ -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 }}
|
||||
|
27
.github/workflows/merge-master.yml
vendored
27
.github/workflows/merge-master.yml
vendored
@ -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 }}
|
17
.github/workflows/merge-release.yml
vendored
17
.github/workflows/merge-release.yml
vendored
@ -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 }}
|
||||
|
19
.github/workflows/ota.yml
vendored
Normal file
19
.github/workflows/ota.yml
vendored
Normal file
@ -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'
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c4247c64a7d9fc0c0f1d2f017c21dd3464ddfe56529c7d6eef0e64554bd453e8
|
||||
size 7611162
|
||||
oid sha256:bd487cdb8294fdef6878f623bceb893553b36b2a616d22d30017b430361586fb
|
||||
size 3889185
|
||||
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5123a1ac30b83ec026587574df1ce13a73e72d06588ff68b5c41c09e1bebb5b7
|
||||
size 949962
|
||||
oid sha256:26ff86808886beb831e4a135f478e42ce83ef4a09ad24808b3fe97248ce7a6b7
|
||||
size 1136643
|
||||
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:40bc203b8a2d2ebad81723da6fc946ee32d2f8a204884f50f9710177ae257d08
|
||||
size 5712264
|
||||
oid sha256:f4d585474b6df173dc1c172f343192ac88bd3ced1a9c7cf6877402722ac139de
|
||||
size 5748201
|
||||
|
70
doc/spec/nock/10.txt
Normal file
70
doc/spec/nock/10.txt
Normal file
@ -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]
|
74
doc/spec/nock/11.txt
Normal file
74
doc/spec/nock/11.txt
Normal file
@ -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)
|
75
doc/spec/nock/12.txt
Normal file
75
doc/spec/nock/12.txt
Normal file
@ -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)
|
71
doc/spec/nock/13.txt
Normal file
71
doc/spec/nock/13.txt
Normal file
@ -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)
|
42
doc/spec/nock/6.txt
Normal file
42
doc/spec/nock/6.txt
Normal file
@ -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
|
42
doc/spec/nock/7.txt
Normal file
42
doc/spec/nock/7.txt
Normal file
@ -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
|
45
doc/spec/nock/8.txt
Normal file
45
doc/spec/nock/8.txt
Normal file
@ -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
|
||||
|
||||
|
43
doc/spec/nock/9.txt
Normal file
43
doc/spec/nock/9.txt
Normal file
@ -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
|
@ -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)
|
||||
==
|
||||
|
File diff suppressed because one or more lines are too long
@ -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
|
||||
|
@ -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
|
||||
--
|
||||
|
@ -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
|
||||
|
@ -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]))
|
||||
|
@ -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)
|
||||
|
@ -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 ~]
|
||||
==
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
1
pkg/arvo/lib/dill.hoon
Symbolic link
1
pkg/arvo/lib/dill.hoon
Symbolic link
@ -0,0 +1 @@
|
||||
../../base-dev/lib/dill.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 "[{<src.hid>}, driving {<our.hid>}]")
|
||||
::
|
||||
++ 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))
|
||||
--
|
||||
--
|
||||
|
@ -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
|
||||
|
@ -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))]
|
||||
|
@ -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)
|
||||
==
|
||||
--
|
||||
|
@ -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
|
||||
--
|
||||
--
|
||||
|
46
pkg/arvo/mar/herm/task.hoon
Normal file
46
pkg/arvo/mar/herm/task.hoon
Normal file
@ -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
|
||||
--
|
||||
--
|
9
pkg/arvo/sur/herm.hoon
Normal file
9
pkg/arvo/sur/herm.hoon
Normal file
@ -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)
|
||||
==
|
||||
--
|
@ -1 +1 @@
|
||||
[%zuse 417]
|
||||
[%zuse 416]
|
||||
|
@ -415,7 +415,7 @@
|
||||
==
|
||||
--
|
||||
::
|
||||
:: |de: axal engine
|
||||
:: |of: axal engine
|
||||
::
|
||||
++ of
|
||||
=| fat=(axal)
|
||||
|
@ -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)
|
||||
|
@ -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 {<why=why lot=lot now=now syd=syd>}"
|
||||
~
|
||||
:: /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 {<sndr^rcvr^bone=bone^vane.plea^path.plea>}"
|
||||
"plea {<sndr rcvr bone=bone vane.plea path.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 {<sndr^rcvr^bone=bone^vane.plea^path.plea>}"
|
||||
"cork plea {<sndr rcvr bone=bone vane.plea path.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 {<our^ship>}"
|
||||
~> %slog.0^leaf/"ames: breach unknown {<our ship>}"
|
||||
event-core
|
||||
:: if an alien breached, this doesn't affect us
|
||||
::
|
||||
?: ?=([~ %alien *] ship-state)
|
||||
~> %slog.0^leaf/"ames: breach alien {<our^ship>}"
|
||||
~> %slog.0^leaf/"ames: breach alien {<our ship>}"
|
||||
event-core
|
||||
~> %slog.0^leaf/"ames: breach peer {<our^ship>}"
|
||||
~> %slog.0^leaf/"ames: breach peer {<our ship>}"
|
||||
:: 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 {<her.channel i.boz>}"
|
||||
$(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 {<her.channel i.boz>}"
|
||||
=/ =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 {<dat>}")
|
||||
=. 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 {<nums^show:gauge>}")
|
||||
|.("dead {<nums show:gauge>}")
|
||||
(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: {<fragment=fragment-num show:gauge>}"))
|
||||
:: .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 {<message-num=message-num^show:gauge>}"))
|
||||
%- (trace snd.veb |.("done {<message-num=message-num show:gauge>}"))
|
||||
(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 {<resend=resend in-recovery=in-recovery show>}")
|
||||
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 {<seq=seq^last-acked=last-acked.state>}")
|
||||
|.("future %hear {<seq=seq last-acked=last-acked.state>}")
|
||||
message-sink
|
||||
::
|
||||
=/ is-last-fragment=? =(+(fragment-num) num-fragments)
|
||||
@ -3676,7 +3701,7 @@
|
||||
:: single packet ack
|
||||
::
|
||||
%- %+ trace rcv.veb
|
||||
|.("send dupe ack {<seq=seq^fragment-num=fragment-num>}")
|
||||
|.("send dupe ack {<seq=seq fragment-num>}")
|
||||
(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 {<data>}"
|
||||
@ -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 {<data>}"
|
||||
(give %send seq %& fragment-num)
|
||||
@ -3737,7 +3762,7 @@
|
||||
"hear last dupe {<data>}"
|
||||
message-sink
|
||||
%- %+ trace rcv.veb
|
||||
|.("send dupe ack {<her.channel^seq=seq^fragment-num=fragment-num>}")
|
||||
|.("send dupe ack {<her.channel seq=seq fragment-num>}")
|
||||
(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 {<data>}"
|
||||
(give %send seq %& fragment-num)
|
||||
:: enqueue all completed messages starting at +(last-heard.state)
|
||||
|
@ -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)" ~)
|
||||
|
@ -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 "{<p.p.sih>}")]~)
|
||||
==
|
||||
::
|
||||
[%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 "<awaiting {(trip app)}, this may take a minute>")
|
||||
=/ zon=axon [app input=[~ ~] width=80 cursor=(lent see) lin+see]
|
||||
=/ say (tuba "<awaiting {(trip app)}, this may take a minute>")
|
||||
=/ 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 ..^$]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ~& "{<who>}: {line}" ""
|
||||
%nel ~& "{<who>}: {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
|
||||
~
|
||||
|
@ -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
|
||||
|
94
pkg/base-dev/lib/dill.hoon
Normal file
94
pkg/base-dev/lib/dill.hoon
Normal file
@ -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)
|
||||
--
|
||||
--
|
||||
--
|
@ -3,7 +3,8 @@
|
||||
::
|
||||
=/ debug |
|
||||
|%
|
||||
+* option [item]
|
||||
++ option
|
||||
|$ [item]
|
||||
[term=cord detail=item]
|
||||
::
|
||||
:: Like +rose except also produces line number
|
||||
|
@ -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))
|
||||
==
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
--
|
||||
|
@ -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
|
||||
::
|
||||
|
@ -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)
|
||||
--
|
||||
--
|
||||
|
@ -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 ~])))
|
||||
|
@ -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
|
||||
|
@ -1 +1,2 @@
|
||||
[%zuse 417]
|
||||
[%zuse 416]
|
||||
|
@ -1 +1 @@
|
||||
[%zuse 417]
|
||||
[%zuse 416]
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
@ -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 ()
|
||||
|
@ -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
|
||||
|
@ -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 ()]
|
||||
|
@ -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
|
||||
|
@ -158,6 +158,7 @@ a {
|
||||
@media all and (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
}
|
||||
.bg-black-d {
|
||||
background-color: black;
|
||||
|
23
pkg/interface/webterm/.eslintrc.js
Normal file
23
pkg/interface/webterm/.eslintrc.js
Normal file
@ -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 <rootdir>/tsconfig.json to eslint
|
||||
},
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"plugins": ["import", "react-hooks"]
|
||||
}
|
1
pkg/interface/webterm/.nvmrc
Normal file
1
pkg/interface/webterm/.nvmrc
Normal file
@ -0,0 +1 @@
|
||||
16.14.0
|
94
pkg/interface/webterm/App.tsx
Normal file
94
pkg/interface/webterm/App.tsx
Normal file
@ -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 (
|
||||
<>
|
||||
<ThemeProvider theme={dark ? _dark : _light}>
|
||||
<div className="header">
|
||||
<Tabs />
|
||||
<InfoButton />
|
||||
</div>
|
||||
<div className="buffer-container">
|
||||
{names.map((name) => {
|
||||
return <Buffer key={name} name={name} selected={name === selected} dark={dark} />;
|
||||
})}
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
}
|
350
pkg/interface/webterm/Buffer.tsx
Normal file
350
pkg/interface/webterm/Buffer.tsx
Normal file
@ -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<HTMLDivElement | null>(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 ?
|
||||
<p>Loading...</p>
|
||||
:
|
||||
<Box
|
||||
width='100%'
|
||||
height='100%'
|
||||
bg='white'
|
||||
fontFamily='mono'
|
||||
overflow='hidden'
|
||||
className="terminal-container"
|
||||
style={selected ? { zIndex: 999 } : {}}
|
||||
>
|
||||
<Col
|
||||
width='100%'
|
||||
height='100%'
|
||||
minHeight='0'
|
||||
px={['0', '2']}
|
||||
pb={['0', '2']}
|
||||
ref={containerRef}
|
||||
>
|
||||
</Col>
|
||||
</Box>
|
||||
);
|
||||
}
|
24
pkg/interface/webterm/InfoButton.tsx
Normal file
24
pkg/interface/webterm/InfoButton.tsx
Normal file
@ -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 (
|
||||
<>
|
||||
<button className="info-btn" onClick={onInfoClick}>
|
||||
<Icon
|
||||
icon="Info"
|
||||
size="18px"
|
||||
/>
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
55
pkg/interface/webterm/Spinner.tsx
Normal file
55
pkg/interface/webterm/Spinner.tsx
Normal file
@ -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<ReturnType<typeof setInterval> | 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 <span> {CHARS[index]}</span>;
|
||||
};
|
||||
|
||||
export const DelayedSpinner = () => {
|
||||
const [showSpinner, setShowSpinner] = useState(false);
|
||||
const [delayTimer, setDelayTimer] = useState<ReturnType<typeof setTimeout> | undefined>();
|
||||
const isMounted = useIsMounted();
|
||||
|
||||
useEffect(() => {
|
||||
setDelayTimer(
|
||||
setTimeout(() => {
|
||||
if (isMounted()) {
|
||||
setShowSpinner(true);
|
||||
}
|
||||
}, DELAY_MS)
|
||||
);
|
||||
|
||||
return () => {
|
||||
if (delayTimer) {
|
||||
clearTimeout(delayTimer);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return showSpinner ? <Spinner /> : null;
|
||||
};
|
55
pkg/interface/webterm/Tab.tsx
Normal file
55
pkg/interface/webterm/Tab.tsx
Normal file
@ -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 (
|
||||
<div className={'tab ' + (isSelected ? 'selected' : '')} onClick={onClick}>
|
||||
<a className='session-name'>
|
||||
{session?.hasBell ? '🔔 ' : ''}
|
||||
{name === DEFAULT_SESSION ? 'default' : name}
|
||||
{session && session.pending > 0 ? <Spinner /> : null}
|
||||
{' '}
|
||||
</a>
|
||||
{name === DEFAULT_SESSION ? null : <a className="delete-session" onClick={onDelete}>x</a>}
|
||||
</div>
|
||||
);
|
||||
};
|
26
pkg/interface/webterm/Tabs.tsx
Normal file
26
pkg/interface/webterm/Tabs.tsx
Normal file
@ -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 (
|
||||
<div className="tabs">
|
||||
{names.map((n, i) => {
|
||||
return (
|
||||
<Tab session={sessions[n]} name={n} key={i} />
|
||||
);
|
||||
})}
|
||||
<button className="tab" onClick={addSession}>
|
||||
<Icon
|
||||
icon="Plus"
|
||||
size="18px"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -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<HTMLDivElement>(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 (
|
||||
<>
|
||||
<ThemeProvider theme={dark ? _dark : _light}>
|
||||
<Reset />
|
||||
<Box
|
||||
width='100%'
|
||||
height='100%'
|
||||
bg='white'
|
||||
fontFamily='mono'
|
||||
overflow='hidden'
|
||||
>
|
||||
<Col
|
||||
width='100%'
|
||||
height='100%'
|
||||
minHeight='0'
|
||||
px={['0','2']}
|
||||
pb={['0','2']}
|
||||
ref={container}
|
||||
>
|
||||
</Col>
|
||||
</Box>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
}
|
28
pkg/interface/webterm/constants.ts
Normal file
28
pkg/interface/webterm/constants.ts
Normal file
@ -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-]*$)/;
|
@ -23,10 +23,113 @@
|
||||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body, #root {
|
||||
height: 100vh;
|
||||
height: 99vh; /* prevent scrollbar on outer frame */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.buffer-container {
|
||||
height: calc(100% - 40px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.terminal-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
div.header {
|
||||
display: grid;
|
||||
grid-template-areas: "tabs info";
|
||||
grid-template-columns: auto min-content;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
|
||||
div.info {
|
||||
grid-area: info;
|
||||
}
|
||||
|
||||
div.tabs {
|
||||
grid-area: tabs;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
padding: 5px 5px 0 5px;
|
||||
border-bottom: 1px solid black;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
div.tabs > * {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
border: solid 1px black;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.tab, button.tab {
|
||||
margin-bottom: -1px; /** To overlay the selected tab on the tabs container bottom border */
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
div.tabs > div.selected {
|
||||
border-bottom: white solid 1px;
|
||||
}
|
||||
|
||||
div.tabs > div.selected > a.session-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.tabs > a.delete-session {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
button.info-btn {
|
||||
border: none;
|
||||
border-bottom: solid black 1px;
|
||||
background: transparent;
|
||||
padding: 10px;
|
||||
line-height: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: rgb(26,26,26);
|
||||
}
|
||||
|
||||
div.tabs {
|
||||
background-color: rgb(26, 26, 26);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
border-bottom-color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
div.tab {
|
||||
background-color: rgb(26, 26, 26);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
border-color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
button.tab {
|
||||
background-color: rgb(42, 42, 42);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
border-color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
div.tabs > div.selected {
|
||||
border-bottom: rgb(26,26,26) solid 1px;
|
||||
}
|
||||
|
||||
button.info-btn {
|
||||
border-bottom: solid rgba(255, 255, 255, 0.9) 1px;
|
||||
background: rgb(26, 26, 26);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
129
pkg/interface/webterm/lib/blit.ts
Normal file
129
pkg/interface/webterm/lib/blit.ts
Normal file
@ -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 });
|
||||
}
|
||||
};
|
39
pkg/interface/webterm/lib/retry.ts
Normal file
39
pkg/interface/webterm/lib/retry.ts
Normal file
@ -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);
|
||||
}
|
60
pkg/interface/webterm/lib/stye.ts
Normal file
60
pkg/interface/webterm/lib/stye.ts
Normal file
@ -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';
|
||||
};
|
23
pkg/interface/webterm/lib/theme.ts
Normal file
23
pkg/interface/webterm/lib/theme.ts
Normal file
@ -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
|
||||
};
|
||||
};
|
66
pkg/interface/webterm/lib/useAddSession.ts
Normal file
66
pkg/interface/webterm/lib/useAddSession.ts
Normal file
@ -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;
|
||||
};
|
@ -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);
|
44
pkg/interface/webterm/lib/useDetectOS.ts
Normal file
44
pkg/interface/webterm/lib/useDetectOS.ts
Normal file
@ -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
|
||||
};
|
||||
};
|
17
pkg/interface/webterm/lib/useIsMounted.ts
Normal file
17
pkg/interface/webterm/lib/useIsMounted.ts
Normal file
@ -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;
|
716
pkg/interface/webterm/package-lock.json
generated
716
pkg/interface/webterm/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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<TermState>((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));
|
||||
}
|
||||
|
26
pkg/interface/webterm/tsconfig.json
Normal file
26
pkg/interface/webterm/tsconfig.json
Normal file
@ -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"
|
||||
]
|
||||
}
|
@ -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
|
||||
|
1
pkg/landscape/lib/dill.hoon
Symbolic link
1
pkg/landscape/lib/dill.hoon
Symbolic link
@ -0,0 +1 @@
|
||||
../../base-dev/lib/dill.hoon
|
@ -1 +1 @@
|
||||
[%zuse 417]
|
||||
[%zuse 416]
|
||||
|
@ -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).
|
||||
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).
|
||||
|
@ -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<SessionTask> => ({
|
||||
@ -13,9 +14,7 @@ export const pokeBelt = (
|
||||
): Poke<SessionTask> => 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`
|
||||
});
|
||||
|
@ -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<string> }
|
||||
//
|
||||
| { 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
|
||||
|
@ -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;
|
||||
|
||||
|
2
pkg/urbit/configure
vendored
2
pkg/urbit/configure
vendored
@ -211,7 +211,7 @@ for citem in $compat; do
|
||||
done
|
||||
|
||||
cat >config.mk <<EOF
|
||||
CFLAGS := $CFLAGS -funsigned-char -ffast-math -fcommon -std=gnu99
|
||||
CFLAGS := $CFLAGS -funsigned-char -ffast-math -fcommon -std=gnu99 -Wno-format-zero-length
|
||||
LDFLAGS := $LDFLAGS
|
||||
CC := ${CC-cc}
|
||||
compat := $compat
|
||||
|
@ -572,7 +572,7 @@ _setup_cert_store()
|
||||
{
|
||||
BIO* cbio = BIO_new_mem_buf(include_ca_bundle_crt, include_ca_bundle_crt_len);
|
||||
if ( !cbio || !(_cert_store = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL)) ) {
|
||||
u3l_log("boot: failed to decode embedded CA certificates\r\n");
|
||||
u3l_log("boot: failed to decode embedded CA certificates");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -1267,7 +1267,7 @@ _cw_eval(c3_i argc, c3_c* argv[])
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1858,7 +1858,7 @@ _cw_vere(c3_i argc, c3_c* argv[])
|
||||
// initialize curl
|
||||
//
|
||||
if ( 0 != curl_global_init(CURL_GLOBAL_DEFAULT) ) {
|
||||
u3l_log("boot: curl initialization failed\r\n");
|
||||
u3l_log("boot: curl initialization failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -1888,11 +1888,11 @@ _cw_vere(c3_i argc, c3_c* argv[])
|
||||
|
||||
|
||||
if ( u3_king_vere(pac_c, ver_c, arc_c, dir_c, 0) ) {
|
||||
u3l_log("vere: download failed\r\n");
|
||||
u3l_log("vere: download failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
u3l_log("vere: download succeeded\r\n");
|
||||
u3l_log("vere: download succeeded");
|
||||
}
|
||||
|
||||
/* _cw_vile(): generatoe/print keyfile
|
||||
@ -2138,7 +2138,7 @@ main(c3_i argc,
|
||||
sigemptyset(&set);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -2230,7 +2230,7 @@ main(c3_i argc,
|
||||
{
|
||||
SECURITY_ATTRIBUTES sa = {sizeof(sa), NULL, TRUE};
|
||||
if ( NULL == (u3_Host.cev_u = CreateEvent(&sa, FALSE, FALSE, NULL)) ) {
|
||||
u3l_log("boot: failed to create Ctrl-C event: %d\r\n", GetLastError());
|
||||
u3l_log("boot: failed to create Ctrl-C event: %d", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -2251,7 +2251,7 @@ main(c3_i argc,
|
||||
// initialize curl
|
||||
//
|
||||
if ( 0 != curl_global_init(CURL_GLOBAL_DEFAULT) ) {
|
||||
u3l_log("boot: curl initialization failed\r\n");
|
||||
u3l_log("boot: curl initialization failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user