on main help sign in
.vscode/settings.json+6
since splitsnapshots
unifiedsplit
@@ -48,5 +48,11 @@
48 }
48 }
49 ]
49 ]
50 }
50 }
51+ ],
52+ "grammarly.selectors": [
53+ {
54+ "language": "markdown",
55+ "scheme": "file"
56+ }
51 ]
57 ]
52}
58}
.vscode/tasks.json+3−3
@@ -3,11 +3,11 @@
3 "tasks": [
3 "tasks": [
4 {
4 {
5 "type": "npm",
5 "type": "npm",
6− "script": "commit",
6+ "script": "install:desk",
7 "path": "ui",
7 "path": "ui",
8 "problemMatcher": [],
8 "problemMatcher": [],
9− "label": "npm: commit - ui",
9+ "label": "npm: install:desk - ui",
10− "detail": "dotenv -c local npm run _commit"
10+ "detail": "dotenv -c local npm run _install:desk"
11 }
11 }
12 ]
12 ]
13}
13}
README.md+2
@@ -35,8 +35,10 @@ Regardless of what you run to develop, Vite will hot-reload code changes as you
35 2. `|mount %astrolabe` to mount it.
35 2. `|mount %astrolabe` to mount it.
36 3. Set `ASTROLABE_DESK` in `.env.local` if the path to your desk is different.
36 3. Set `ASTROLABE_DESK` in `.env.local` if the path to your desk is different.
373. Create and upload `glob-http`, if applicable (see below)
373. Create and upload `glob-http`, if applicable (see below)
38+4. Bump the `version` in `desk.docket-0`.
383. Install the desk
393. Install the desk
39 1. From the `ui` folder: `npm run install:desk`
40 1. From the `ui` folder: `npm run install:desk`
41+ 2. (first time only, on dev ship): `|install our %astrolabe`
40 2. From the distribution ship's dojo: `:treaty|unpublish %astrolabe`
42 2. From the distribution ship's dojo: `:treaty|unpublish %astrolabe`
41 2. From middleman or distribution ship's dojo: `|commit %astrolabe`
43 2. From middleman or distribution ship's dojo: `|commit %astrolabe`
42 3. (if using a comet as a middleman) `|public %astrolabe`
44 3. (if using a comet as a middleman) `|public %astrolabe`
base-desk/lib/agentio.hoon+4−6
@@ -66,7 +66,7 @@
66 ::
66 ::
67 ++ rest
67 ++ rest
68 |= p=@da
68 |= p=@da
69− (arvo %b %wait p)
69+ (arvo %b %rest p)
70 ::
70 ::
71 ++ warp
71 ++ warp
72 |= [wer=ship =riff:clay]
72 |= [wer=ship =riff:clay]
@@ -85,16 +85,14 @@
85 ?:(?=(%sing genre) [genre mood] [genre mood])
85 ?:(?=(%sing genre) [genre mood] [genre mood])
86 (warp-our q.byk.bowl `rave)
86 (warp-our q.byk.bowl `rave)
87 ::
87 ::
88+ ++ tire
89+ (arvo %c %tire `~)
90+ ::
88 ++ connect
91 ++ connect
89 |= [=binding:eyre app=term]
92 |= [=binding:eyre app=term]
90 (arvo %e %connect binding app)
93 (arvo %e %connect binding app)
91 --
94 --
92::
95::
93−++ fact-curry
94− |* [=mark =mold]
95− |= [paths=(list path) fac=mold]
96− (fact mark^!>(fac) paths)
97−::
98++ fact-kick
96++ fact-kick
99 |= [=path =cage]
97 |= [=path =cage]
100 ^- (list card)
98 ^- (list card)
base-desk/lib/azimuth.hoon+9−9
@@ -114,29 +114,29 @@
114 ::
114 ::
115 :: Testnet contract addresses
115 :: Testnet contract addresses
116 ::
116 ::
117− ++ ropsten-contracts
117+ ++ goerli-contracts
118 |%
118 |%
119 ++ azimuth
119 ++ azimuth
120− 0x308a.b6a6.024c.f198.b57e.008d.0ac9.ad02.1988.6579
120+ 0xbb61.fa68.3e4b.9104.18e2.7b00.a143.8a93.6234.df52
121 ::
121 ::
122 ++ ecliptic
122 ++ ecliptic
123− 0x8b9f.86a2.8921.d9c7.05b3.113a.755f.b979.e1bd.1bce
123+ 0xe129.0a32.9014.5e63.e6a8.ec1e.f661.6906.856d.0c8f
124 ::
124 ::
125 ++ linear-star-release
125 ++ linear-star-release
126− 0x1f8e.dd03.1ee4.1474.0aed.b39b.84fb.8f2f.66ca.422f
126+ 0x0
127 ::
127 ::
128 ++ conditional-star-release
128 ++ conditional-star-release
129 0x0
129 0x0
130 ::
130 ::
131 ++ delegated-sending
131 ++ delegated-sending
132− 0x3e8c.a510.354b.c2fd.bbd6.1502.52d9.3105.c9c2.7bbe
132+ 0x0
133 ::
133 ::
134 ++ naive
134 ++ naive
135− 0xe7cf.4b83.06d3.11ba.ca15.585f.e3f0.7cd0.441c.21d1
135+ 0x56e3.7137.cdaf.c026.a732.e8e8.40cd.621e.d50b.d210
136 ::
136 ::
137− ++ launch 4.601.630
137+ ++ launch 7.834.742
138− ++ public launch
138+ ++ public 7.834.800
139− ++ chain-id 3
139+ ++ chain-id 5
140 --
140 --
141 ::
141 ::
142 :: Local contract addresses
142 :: Local contract addresses
base-desk/lib/azimuthio.hoon+3−3
@@ -43,8 +43,8 @@
43 url=url
43 url=url
44 header-list=['Content-Type'^'application/json' ~]
44 header-list=['Content-Type'^'application/json' ~]
45 ^= body
45 ^= body
46− %- some %- as-octt:mimes:html
46+ %- some %- as-octs:mimes:html
47− %- en-json:html
47+ %- en:json:html
48 (request-to-json:rpc:ethereum id req)
48 (request-to-json:rpc:ethereum id req)
49 ==
49 ==
50 ;< ~ bind:m (send-request:strandio request)
50 ;< ~ bind:m (send-request:strandio request)
@@ -62,7 +62,7 @@
62 ?~ full-file.client-response
62 ?~ full-file.client-response
63 (pure:m ~)
63 (pure:m ~)
64 =/ body=@t q.data.u.full-file.client-response
64 =/ body=@t q.data.u.full-file.client-response
65− =/ jon=(unit json) (de-json:html body)
65+ =/ jon=(unit json) (de:json:html body)
66 ?~ jon
66 ?~ jon
67 (pure:m ~)
67 (pure:m ~)
68 =, dejs-soft:format
68 =, dejs-soft:format
base-desk/lib/bitcoin-utils.hoon+2−1
@@ -143,6 +143,7 @@
143 |= a=@
143 |= a=@
144 ^- hexb
144 ^- hexb
145 =/ l=@ (met 3 a)
145 =/ l=@ (met 3 a)
146+ ?: =(l 0) 1^a
146 ?: =(l 1) 1^a
147 ?: =(l 1) 1^a
147 ?: =(l 2) (cat:byt ~[1^0xfd (flip:byt 2^a)])
148 ?: =(l 2) (cat:byt ~[1^0xfd (flip:byt 2^a)])
148 ?: (lte l 4) (cat:byt ~[1^0xfe (flip:byt 4^a)])
149 ?: (lte l 4) (cat:byt ~[1^0xfe (flip:byt 4^a)])
@@ -162,7 +163,7 @@
162 %0xfe 2
163 %0xfe 2
163 %0xff 3
164 %0xff 3
164 ==
165 ==
165− :_ (drop:byt (add 1 len) h)
166+ :_ (drop:byt (add 1 (bex len)) h)
166 %- flip:byt
167 %- flip:byt
167 (take:byt (bex len) (drop:byt 1 h))
168 (take:byt (bex len) (drop:byt 1 h))
168 :: +dea: atom instead of hexb for parsed CompactSize
169 :: +dea: atom instead of hexb for parsed CompactSize
base-desk/lib/dbug.hoon+9−3
@@ -4,7 +4,8 @@
4::
4::
5|%
5|%
6+$ poke
6+$ poke
7+ $% [%skip depth=@ud =poke]
7− $% [%bowl ~]
8+ [%bowl ~]
8 [%state grab=cord]
9 [%state grab=cord]
9 [%incoming =about]
10 [%incoming =about]
10 [%outgoing =about]
11 [%outgoing =about]
@@ -32,8 +33,13 @@
32 ?. ?=(%dbug mark)
33 ?. ?=(%dbug mark)
33 =^ cards agent (on-poke:ag mark vase)
34 =^ cards agent (on-poke:ag mark vase)
34 [cards this]
35 [cards this]
35− =/ dbug
36− !<(poke vase)
36+ =/ dbug !<(poke vase)
37+ =? dbug ?=([%skip %0 *] dbug) poke.dbug
38+ ?: ?=(%skip -.dbug)
39+ =^ cards agent
40+ %+ on-poke:ag %dbug
41+ !>(`poke`dbug(depth (dec depth.dbug)))
42+ [cards this]
37 =; =tang
43 =; =tang
38 ((%*(. slog pri 1) tang) [~ this])
44 ((%*(. slog pri 1) tang) [~ this])
39 ?- -.dbug
45 ?- -.dbug
base-desk/lib/dill.hoonnew file+94
@@ -0,0 +1,94 @@
1+:: dill: utilities for dill's data structures
2+::
3+=, dill
4+|%
5+++ enjs
6+ |%
7+ ++ blit
8+ |= =blit:dill
9+ ^- json
10+ =, enjs:format
11+ %+ frond -.blit
12+ ?- -.blit
13+ %bel b+&
14+ %clr b+&
15+ %hop ?@ p.blit (numb p.blit)
16+ (pairs 'x'^(numb x.p.blit) 'y'^(numb y.p.blit) ~)
17+ %put a+(turn p.blit |=(c=@c s+(tuft c)))
18+ %nel b+&
19+ %url s+p.blit
20+ %wyp b+&
21+ %mor a+(turn p.blit ^blit)
22+ ::
23+ %sag
24+ %- pairs
25+ :~ 'path'^(path p.blit)
26+ 'file'^s+(en:base64:mimes:html (as-octs:mimes:html (jam q.blit)))
27+ ==
28+ ::
29+ %sav
30+ %- pairs
31+ :~ 'path'^(path p.blit)
32+ 'file'^s+(en:base64:mimes:html (as-octs:mimes:html q.blit))
33+ ==
34+ ::
35+ %klr
36+ :- %a
37+ %+ turn p.blit
38+ |= [=stye text=(list @c)]
39+ %- pairs
40+ :~ 'text'^a+(turn text |=(c=@c s+(tuft c)))
41+ ::
42+ :- 'stye'
43+ %- pairs
44+ |^ :~ 'back'^(color p.q.stye)
45+ 'fore'^(color q.q.stye)
46+ 'deco'^a+(turn ~(tap in p.stye) |=(d=deco ?~(d ~ s+d)))
47+ ==
48+ ++ color
49+ |= =tint
50+ ?@ tint ?~(tint ~ s+tint)
51+ =, tint
52+ (pairs r+(numb r) g+(numb g) b+(numb b) ~)
53+ --
54+ ==
55+ ==
56+ --
57+::
58+++ dejs
59+ |%
60+ ++ belt
61+ |= jon=json
62+ ^- belt:dill
63+ ?: ?=([%s *] jon)
64+ (taft p.jon)
65+ =, dejs:format
66+ %. jon
67+ %- of
68+ |^ :* mod+(ot 'mod'^mod 'key'^bot ~)
69+ txt+(ar (cu taft so))
70+ bol
71+ ==
72+ ::
73+ ++ bol
74+ :~ aro+(su (perk %d %l %r %u ~))
75+ bac+ul
76+ del+ul
77+ hit+(ot 'x'^ni 'y'^ni ~)
78+ ret+ul
79+ ==
80+ ::
81+ ++ bot
82+ |= j=json
83+ ^- bolt:dill
84+ ?+ j !!
85+ [%s *] (taft p.j)
86+ [%o *] ((of bol) j)
87+ ==
88+ ::
89+ ++ mod
90+ |= j=json
91+ ((su (perk %ctl %met %hyp ~)) j)
92+ --
93+ --
94+--
base-desk/lib/ethereum.hoon+9−3
@@ -652,7 +652,7 @@
652 :^ url %post
652 :^ url %post
653 %- ~(gas in *math)
653 %- ~(gas in *math)
654 ~['Content-Type'^['application/json']~]
654 ~['Content-Type'^['application/json']~]
655− (some (as-octt (en-json:html jon)))
655+ (some (as-octs (en:json:html jon)))
656 :: +light-json-request: like json-request, but for %l
656 :: +light-json-request: like json-request, but for %l
657 ::
657 ::
658 :: TODO: Exorcising +purl from our system is a much longer term effort;
658 :: TODO: Exorcising +purl from our system is a much longer term effort;
@@ -665,7 +665,7 @@
665 :* %'POST'
665 :* %'POST'
666 (crip (en-purl:html url))
666 (crip (en-purl:html url))
667 ~[['content-type' 'application/json']]
667 ~[['content-type' 'application/json']]
668− (some (as-octt (en-json:html jon)))
668+ (some (as-octs (en:json:html jon)))
669 ==
669 ==
670 ::
670 ::
671 ++ batch-read-request
671 ++ batch-read-request
@@ -733,7 +733,7 @@
733 ::
733 ::
734 %eth-get-block-by-number
734 %eth-get-block-by-number
735 :- 'eth_getBlockByNumber'
735 :- 'eth_getBlockByNumber'
736− :~ (tape (num-to-hex bon.req))
736+ :~ (tape (num-to-hex-minimal bon.req))
737 b+txs.req
737 b+txs.req
738 ==
738 ==
739 ::
739 ::
@@ -942,6 +942,12 @@
942 %- render-hex-bytes
942 %- render-hex-bytes
943 (as-octs:mimes:html n)
943 (as-octs:mimes:html n)
944::
944::
945+++ num-to-hex-minimal
946+ |= n=@
947+ ^- tape
948+ %- prefix-hex
949+ ((x-co:co 1) n)
950+::
945++ address-to-hex
951++ address-to-hex
946 |= a=address
952 |= a=address
947 ^- tape
953 ^- tape
base-desk/lib/ethio.hoon+3−3
@@ -76,8 +76,8 @@
76 header-list=['Content-Type'^'application/json' ~]
76 header-list=['Content-Type'^'application/json' ~]
77 ::
77 ::
78 ^= body
78 ^= body
79− %- some %- as-octt:mimes:html
79+ %- some %- as-octs:mimes:html
80− %- en-json:html
80+ %- en:json:html
81 a+(turn reqs request-to-json:rpc:ethereum)
81 a+(turn reqs request-to-json:rpc:ethereum)
82 ==
82 ==
83 ;< ~ bind:m
83 ;< ~ bind:m
@@ -96,7 +96,7 @@
96 ?~ full-file.client-response
96 ?~ full-file.client-response
97 (pure:m ~)
97 (pure:m ~)
98 =/ body=@t q.data.u.full-file.client-response
98 =/ body=@t q.data.u.full-file.client-response
99− =/ jon=(unit json) (de-json:html body)
99+ =/ jon=(unit json) (de:json:html body)
100 ?~ jon
100 ?~ jon
101 (pure:m ~)
101 (pure:m ~)
102 =/ array=(unit (list response:rpc))
102 =/ array=(unit (list response:rpc))
base-desk/lib/jose.hoon+11−3
@@ -33,7 +33,6 @@
33 |^ |=([sor=$-(^ ?) val=json] (apex val sor ""))
33 |^ |=([sor=$-(^ ?) val=json] (apex val sor ""))
34 :: :: ++apex:en-json:html
34 :: :: ++apex:en-json:html
35 ++ apex
35 ++ apex
36− =, en-json:html
37 |= [val=json sor=$-(^ ?) rez=tape]
36 |= [val=json sor=$-(^ ?) rez=tape]
38 ^- tape
37 ^- tape
39 ?~ val (weld "null" rez)
38 ?~ val (weld "null" rez)
@@ -46,7 +45,7 @@
46 |-
45 |-
47 ?~ t.p.val ^$(val i.p.val)
46 ?~ t.p.val ^$(val i.p.val)
48 ^$(val i.p.val, rez [',' $(p.val t.p.val)])
47 ^$(val i.p.val, rez [',' $(p.val t.p.val)])
49− ::
48+ ::
50 %b (weld ?:(p.val "true" "false") rez)
49 %b (weld ?:(p.val "true" "false") rez)
51 %n (weld (trip p.val) rez)
50 %n (weld (trip p.val) rez)
52 %s
51 %s
@@ -60,7 +59,7 @@
60 ?: ?=([@ ~] hed)
59 ?: ?=([@ ~] hed)
61 [i.hed $(viz t.viz)]
60 [i.hed $(viz t.viz)]
62 (weld hed $(viz t.viz))
61 (weld hed $(viz t.viz))
63− ::
62+ ::
64 %o
63 %o
65 :- '{'
64 :- '{'
66 =. rez ['}' rez]
65 =. rez ['}' rez]
@@ -74,6 +73,15 @@
74 =. rez [',' $(viz t.viz)]
73 =. rez [',' $(viz t.viz)]
75 ^$(val [%s p.i.viz], rez [':' ^$(val q.i.viz)])
74 ^$(val [%s p.i.viz], rez [':' ^$(val q.i.viz)])
76 ==
75 ==
76+ ::
77+ ++ jesc
78+ =+ utf=|=(a=@ ['\\' 'u' ((x-co 4):co a)])
79+ |= a=@ ^- tape
80+ ?+ a ?:(&((gth a 0x1f) !=(a 0x7f)) [a ~] (utf a))
81+ %10 "\\n"
82+ %34 "\\\""
83+ %92 "\\\\"
84+ ==
77 --
85 --
78:: %/lib/jose
86:: %/lib/jose
79::
87::
base-desk/lib/keygen.hoon+1−1
@@ -94,7 +94,7 @@
94 |= seed=@
94 |= seed=@
95 ^- edkeys
95 ^- edkeys
96 =+ =< [pub=pub:ex sec=sec:ex]
96 =+ =< [pub=pub:ex sec=sec:ex]
97− (pit:nu:crub:crypto 256 seed)
97+ (pit:nu:cric:crypto 256 seed %b ~)
98 :- ^= auth
98 :- ^= auth
99 :- (rsh 3 (end [3 33] pub))
99 :- (rsh 3 (end [3 33] pub))
100 (rsh 3 (end [3 33] sec))
100 (rsh 3 (end [3 33] sec))
base-desk/lib/language-server/complete.hoon+4−3
@@ -3,7 +3,8 @@
3::
3::
4=/ debug |
4=/ debug |
5|%
5|%
6−+* option [item]
6+++ option
7+ |$ [item]
7 [term=cord detail=item]
8 [term=cord detail=item]
8::
9::
9:: Like +rose except also produces line number
10:: Like +rose except also produces line number
@@ -42,7 +43,7 @@
42 %+ turn ~(tap by q.r.q.ty)
43 %+ turn ~(tap by q.r.q.ty)
43 |= [term =tome]
44 |= [term =tome]
44 %+ turn
45 %+ turn
45− ~(tap by q.tome)
46+ ~(tap by tome)
46 |= [name=term =hoon]
47 |= [name=term =hoon]
47 ^- (pair term type)
48 ^- (pair term type)
48 ~| term=term
49 ~| term=term
@@ -226,7 +227,7 @@
226 =* outer-loop $
227 =* outer-loop $
227 ?~ tomes
228 ?~ tomes
228 ~
229 ~
229− =/ arms ~(tap by q.q.i.tomes)
230+ =/ arms ~(tap by q.i.tomes)
230 |- ^- (unit [term type])
231 |- ^- (unit [term type])
231 =* inner-loop $
232 =* inner-loop $
232 ?~ arms
233 ?~ arms
base-desk/lib/language-server/easy-print.hoon+3−3
@@ -421,11 +421,11 @@
421 ?:(?=([%plot *] q.yon) [q.hin p.q.yon] [q.hin q.yon ~])
421 ?:(?=([%plot *] q.yon) [q.hin p.q.yon] [q.hin q.yon ~])
422 ::
422 ::
423 [%core *]
423 [%core *]
424− ?: ?=([[%$ * [[%$ @ *] ~ ~]] ~ ~] q.r.q.sut)
424+ ?: ?=([[%$ [[%$ @ *] ~ ~]] ~ ~] q.r.q.sut)
425 =/ dad $(sut p.sut)
425 =/ dad $(sut p.sut)
426 :- p.dad
426 :- p.dad
427 ~! q.r.q.sut
427 ~! q.r.q.sut
428− [%gate q.n.q.q.n.q.r.q.sut sut(r.p.q %gold) q.dad]
428+ [%gate q.n.q.n.q.r.q.sut sut(r.p.q %gold) q.dad]
429 =+ yad=$(sut p.sut)
429 =+ yad=$(sut p.sut)
430 :- p.yad
430 :- p.yad
431 =+ ^= doy ^- [p=(list @ta) q=wine]
431 =+ ^= doy ^- [p=(list @ta) q=wine]
@@ -438,7 +438,7 @@
438 %^ cat 3
438 %^ cat 3
439 %~ rent co
439 %~ rent co
440 :+ %$ %ud
440 :+ %$ %ud
441− %- ~(rep by (~(run by q.r.q.sut) |=(tome ~(wyt by q.+<))))
441+ %- ~(rep by (~(run by q.r.q.sut) |=(tome ~(wyt by +<))))
442 |=([[@ a=@u] b=@u] (add a b))
442 |=([[@ a=@u] b=@u] (add a b))
443 %^ cat 3
443 %^ cat 3
444 ?-(r.p.q.sut %gold '.', %iron '|', %lead '?', %zinc '&')
444 ?-(r.p.q.sut %gold '.', %iron '|', %lead '?', %zinc '&')
base-desk/lib/language-server/rune-snippet.hoon+2−2
@@ -509,8 +509,8 @@
509 :- '!@'
509 :- '!@'
510 """
510 """
511 $\{1:wing}
511 $\{1:wing}
512− $\{2:if-exists}
512+ $\{2:if-not-exists}
513− $\{3:if-not-exists}
513+ $\{3:if-exists}
514 """
514 """
515 :- '!?'
515 :- '!?'
516 """
516 """
base-desk/lib/noun-diff.hoonnew file+213
@@ -0,0 +1,213 @@
1+=<
2+|%
3+++ diff
4+ |= [old=* new=*]
5+ ^- patch
6+ =/ del (extract-del (oracle old new) old)
7+ =/ ins (extract-ins (oracle old new) new)
8+ =/ allowed-holes (~(int in (find-del-holes del)) (find-ins-holes ins))
9+ =. del (filter-del-holes allowed-holes del)
10+ =/ ins (filter-ins-holes allowed-holes ins)
11+ =/ closed-patch (closure (gcp [del ins]))
12+ ?> =(& +.closed-patch)
13+ -.closed-patch
14+++ apply
15+ |= [patch=_id noun=*]
16+ ?- -.patch
17+ %diff
18+ =/ var-map (del del.patch noun)
19+ (ins ins.patch var-map)
20+ ::
21+ %cell
22+ ?> ?=(^ noun)
23+ [$(patch lhs.patch, noun -.noun) $(patch rhs.patch, noun +.noun)]
24+ ==
25+++ id
26+ ^- patch
27+ [%diff [%hole ~] [%hole ~]]
28++$ patch
29+ $% [%cell lhs=patch rhs=patch]
30+ [%diff ^diff]
31+ ==
32+--
33+::
34+|%
35++$ del-diff
36+ $% [%hole @]
37+ [%cell lhs=del-diff rhs=del-diff]
38+ [%ignore ~]
39+ ==
40++$ ins-diff
41+ $% [%hole @ original=*]
42+ [%cell lhs=ins-diff rhs=ins-diff]
43+ [%atom @]
44+ ==
45++$ final-ins-diff
46+ $% [%hole @]
47+ [%cell lhs=final-ins-diff rhs=final-ins-diff]
48+ [%atom @]
49+ ==
50++$ diff [del=del-diff ins=final-ins-diff]
51++$ patch
52+ $% [%cell lhs=patch rhs=patch]
53+ [%diff diff]
54+ ==
55+++ insify-noun
56+ |= noun=*
57+ ^- final-ins-diff
58+ ?- noun
59+ ^ [%cell $(noun -.noun) $(noun +.noun)]
60+ @ [%atom noun]
61+ ==
62+++ empty-set (silt `(list @)`~)
63+++ find-del-holes
64+ |= diff=del-diff
65+ ~+
66+ ^- (set @)
67+ ?- -.diff
68+ %hole (silt ~[+.diff])
69+ %cell (~(uni in $(diff lhs.diff)) $(diff rhs.diff))
70+ %ignore empty-set
71+ ==
72+++ find-final-ins-holes
73+ |= diff=final-ins-diff
74+ ~+
75+ ^- (set @)
76+ ?- -.diff
77+ %hole (silt ~[+.diff])
78+ %cell (~(uni in $(diff lhs.diff)) $(diff rhs.diff))
79+ %atom empty-set
80+ ==
81+++ find-ins-holes
82+ |= diff=ins-diff
83+ ~+
84+ ^- (set @)
85+ ?- -.diff
86+ %hole (silt ~[+<.diff])
87+ %cell (~(uni in $(diff lhs.diff)) $(diff rhs.diff))
88+ %atom empty-set
89+ ==
90+++ filter-del-holes
91+ |= [allowed-holes=(set @) diff=del-diff]
92+ ^- del-diff
93+ ?: ?=(%ignore -.diff) diff
94+ ?- -.diff
95+ %hole ?:((~(has in allowed-holes) +.diff) diff [%ignore ~])
96+ %cell [%cell $(diff +<.diff) $(diff +>.diff)]
97+ ==
98+++ filter-ins-holes
99+ |= [allowed-holes=(set @) diff=ins-diff]
100+ ^- final-ins-diff
101+ ?- -.diff
102+ %cell [%cell $(diff +<.diff) $(diff +>.diff)]
103+ %atom diff
104+ %hole
105+ ?: (~(has in allowed-holes) +<.diff) [%hole +<.diff]
106+ (insify-noun original:diff)
107+ ==
108+++ gcp
109+ |= diff=diff
110+ ^- patch
111+ ?- -.ins.diff
112+ %atom [%diff diff]
113+ %hole [%diff diff]
114+ %cell
115+ ?. ?=(%cell -.del.diff)
116+ [%diff diff]
117+ [%cell $(diff [+<.del.diff +<.ins.diff]) $(diff [+>.del.diff +>.ins.diff])]
118+ ==
119+++ closure
120+ |= =patch
121+ ^- [^patch ?]
122+ ?- -.patch
123+ %diff
124+ =/ del-holes (find-del-holes del:patch)
125+ =/ ins-holes (find-final-ins-holes ins:patch)
126+ =/ difference (~(dif in ins-holes) del-holes)
127+ [patch =(difference empty-set)]
128+ ::
129+ %cell
130+ =/ lhs $(patch lhs:patch)
131+ =/ rhs $(patch rhs:patch)
132+ ?: ?&(+.lhs +.rhs) [[%cell -.lhs -.rhs] &]
133+ $(patch (pull-diff [%cell -.lhs -.rhs]))
134+ ==
135+++ pull-diff
136+ |= =patch
137+ ^- [%diff del=del-diff ins=final-ins-diff]
138+ ?- -.patch
139+ %diff patch
140+ %cell
141+ =/ pulled-lhs $(patch lhs:patch)
142+ =/ pulled-rhs $(patch rhs:patch)
143+ :+ %diff
144+ [%cell del:pulled-lhs del:pulled-rhs]
145+ [%cell ins:pulled-lhs ins:pulled-rhs]
146+ ==
147+++ is-subtree
148+ |= [tree=* subtree=*]
149+ ~+
150+ ^- ?
151+ ?: =(tree subtree) &
152+ ?@ tree |
153+ ?| (is-subtree -.tree subtree)
154+ (is-subtree +.tree subtree)
155+ ==
156+++ oracle
157+ |= [a=* b=*]
158+ |= subtree=*
159+ ^- (unit @)
160+ ?: ?& (is-subtree a subtree)
161+ (is-subtree b subtree)
162+ ==
163+ `(mug subtree)
164+ ~
165+++ extract-del
166+ |= [oracle=$-(* (unit @)) subtree=*]
167+ ~+
168+ ^- del-diff
169+ =/ hash (oracle subtree)
170+ ?^ hash [%hole +.hash]
171+ ?@ subtree [%ignore ~]
172+ [%cell (extract-del oracle -.subtree) (extract-del oracle +.subtree)]
173+++ extract-ins
174+ |= [oracle=$-(* (unit @)) subtree=*]
175+ ~+
176+ ^- ins-diff
177+ =/ hash (oracle subtree)
178+ ?^ hash [%hole +.hash subtree]
179+ ?@ subtree [%atom subtree]
180+ [%cell (extract-ins oracle -.subtree) (extract-ins oracle +.subtree)]
181+++ ins
182+ |= [diff=final-ins-diff var-map=(map @ *)]
183+ ^- *
184+ ?- -.diff
185+ %atom +.diff
186+ %cell [$(diff +<.diff) $(diff +>.diff)]
187+ %hole (~(got by var-map) +.diff)
188+ ==
189+++ del
190+ |= [diff=del-diff noun=*]
191+ ^- (map @ *)
192+ |^ (go diff noun ((map @ *) ~))
193+ ++ go
194+ |= [diff=del-diff noun=* var-map=(map @ *)]
195+ ^- (map @ *)
196+ ?- -.diff
197+ %ignore
198+ var-map
199+ ::
200+ %hole
201+ =/ subtree (~(get by var-map) +.diff)
202+ ?~ subtree (~(put by var-map) +.diff noun)
203+ ?> =(+.subtree noun)
204+ var-map
205+ ::
206+ %cell
207+ ?> ?=(^ noun)
208+ =/ lhs-var-map $(diff +<.diff, noun -.noun)
209+ =/ rhs-var-map $(diff +>.diff, noun +.noun, var-map lhs-var-map)
210+ rhs-var-map
211+ ==
212+ --
213+--
base-desk/lib/ph/io.hoon+226−4
@@ -1,5 +1,5 @@
1/- *aquarium, spider
1/- *aquarium, spider
2−/+ libstrand=strand, *strandio, util=ph-util, aqua-azimuth
2+/+ libstrand=strand, *strandio, util=ph-util, aqua-azimuth, vere
3=, strand=strand:libstrand
3=, strand=strand:libstrand
4|%
4|%
5++ send-events
5++ send-events
@@ -88,7 +88,6 @@
88 ^- form:m
88 ^- form:m
89 (pure:m ~)
89 (pure:m ~)
90::
90::
91−::
92++ init
91++ init
93 =/ m (strand ,~)
92 =/ m (strand ,~)
94 ^- form:m
93 ^- form:m
@@ -125,6 +124,8 @@
125 1
124 1
126 +(+.old-rut)
125 +(+.old-rut)
127 ;< ~ bind:m (send-azimuth-action %breach who)
126 ;< ~ bind:m (send-azimuth-action %breach who)
127+ ;< ~ bind:m ?~ old-rut (sleep ~s20) :: XX don't wait if possible
128+ (wait-for-sunk her who) :: wait only if previously talked
128 |- ^- form:m
129 |- ^- form:m
129 =* loop $
130 =* loop $
130 ;< ~ bind:m (sleep ~s10)
131 ;< ~ bind:m (sleep ~s10)
@@ -142,7 +143,6 @@
142 ?> ?=(%earl (clan:title moon))
143 ?> ?=(%earl (clan:title moon))
143 ?: fake (init-ship moon &)
144 ?: fake (init-ship moon &)
144 =/ m (strand ,~)
145 =/ m (strand ,~)
145− ^- form:m
146 ;< ~ bind:m
146 ;< ~ bind:m
147 %+ dojo (^sein:title moon)
147 %+ dojo (^sein:title moon)
148 =/ =pass pub:ex:(get-keys:aqua-azimuth moon 1)
148 =/ =pass pub:ex:(get-keys:aqua-azimuth moon 1)
@@ -150,13 +150,46 @@
150 (init-ship moon |)
150 (init-ship moon |)
151::
151::
152++ init-ship
152++ init-ship
153+ =| core=?(%mesa %ames) :: XX make %mesa the default core
153 |= [=ship fake=?]
154 |= [=ship fake=?]
154 =/ m (strand ,~)
155 =/ m (strand ,~)
155 ^- form:m
156 ^- form:m
156 ~& > "starting {<ship>}"
157 ~& > "starting {<ship>}"
157− ;< ~ bind:m (send-events (init:util ship fake))
158+ ;< ~ bind:m (send-events (init:util ship fake ~ core))
158 (check-ship-booted ship)
159 (check-ship-booted ship)
159::
160::
161+++ init-comet
162+ =| core=?(%mesa %ames) :: XX make %mesa the default core
163+ |= comet=ship
164+ =/ m (strand ,~)
165+ ^- form:m
166+ :: hardcoded for:
167+ :: ~londeg-tirlys-somlyd-poltus--pintyn-tarbyl-bicnux-marbud
168+ ::
169+ =/ =feed:jael
170+ :* [%2 ~]
171+ who=comet
172+ ryf=0
173+ :_ ~
174+ :- lyf=1
175+ key=0wfm.lBEWM.08gfy.AxYjy.8-tBQ.uq-aa.LZt9c.CVQqd.XBJIs.
176+ CoG90.BNNGV.1ZmVi.ZbAhY.LuhwC.idNnU.lCVkt.Z4qug.7iY92
177+ ==
178+ ::
179+ ?> ?=(^ (veri:dawn:vere comet feed *point:azimuth-types ~))
180+ ~& > "mining comet under {<(^sein:title comet)>}"
181+ ;< ~ bind:m (send-events (init:util comet fake=%.n `feed core))
182+ (check-ship-booted comet)
183+::
184+:: Load network core protocol
185+::
186+++ load
187+ |= [who=ship ore=?(%mesa %ames)]
188+ =/ m (strand ,~)
189+ ^- form:m
190+ ;< ~ bind:m (send-events [%event who [/a/aqua/load %load ore]]~)
191+ (pure:m ~)
192+::
160++ check-ship-booted
193++ check-ship-booted
161 |= =ship
194 |= =ship
162 =/ m (strand ,~)
195 =/ m (strand ,~)
@@ -193,6 +226,129 @@
193 (pure:m ~)
226 (pure:m ~)
194 loop
227 loop
195::
228::
229+++ wait-for-flub
230+ |= [our=ship her=ship dap=term]
231+ =/ m (strand ,~)
232+ ^- form:m
233+ ;< =bowl:spider bind:m get-bowl
234+ |- ^- form:m
235+ =* loop $
236+ ;< [from=^ship =unix-effect] bind:m take-unix-effect
237+ ;< now=@da bind:m get-time
238+ ?. =(from our)
239+ :: our sends the $boon with the halted flow number, at this point
240+ :: gall has updated its state adding the app to gall's .halts map
241+ :: XX search deeper in the .unix-effect?
242+ ::
243+ loop
244+ =/ aqua-pax
245+ :- %i
246+ /(scot %p our)/gg/(scot %p our)//(scot %da now)/[%$]/noun
247+ =+ ;; flubs=(unit (jug ship term))
248+ (scry-aqua:util noun our.bowl now aqua-pax)
249+ ?~ flubs loop
250+ ?. (~(has ju u.flubs) her dap)
251+ loop
252+ (pure:m ~)
253+::
254+++ wait-for-spur
255+ |= [our=ship her=ship dap=term]
256+ =/ m (strand ,~)
257+ ^- form:m
258+ ;< =bowl:spider bind:m get-bowl
259+ |- ^- form:m
260+ =* loop $
261+ ;< [from=ship =unix-effect] bind:m take-unix-effect
262+ ;< now=@da bind:m get-time
263+ ?. =(from our)
264+ :: our ack to the %spur $boon is sent, at this point
265+ :: gall has updated its state adding the app to gall's .flubs map
266+ :: XX search deeper in the .unix-effect?
267+ ::
268+ loop
269+ =/ aqua-pax
270+ :- %i
271+ /(scot %p our)/gg/(scot %p our)//(scot %da now)/[%$]/noun
272+ =+ ;; flubs=(unit (jug ship term))
273+ (scry-aqua:util noun our.bowl now aqua-pax)
274+ ?~ flubs loop
275+ ?: (~(has ju u.flubs) her dap)
276+ loop
277+ (pure:m ~)
278+::
279+++ wait-for-has-halt
280+ |= [our=ship her=ship dap=term]
281+ =/ m (strand ,~)
282+ ^- form:m
283+ ;< =bowl:spider bind:m get-bowl
284+ |- ^- form:m
285+ =* loop $
286+ ;< [from=ship =unix-effect] bind:m take-unix-effect
287+ ;< now=@da bind:m get-time
288+ :: only %send or %push effects
289+ ::
290+ ?. ?=(?(%send %push) -.q.unix-effect)
291+ loop
292+ ~& >> from^unix-effect
293+ ?. =(from our)
294+ :: wait until our ack for the %flub $boon is sent, at this point
295+ :: gall has updated its state adding the app to gall's .flubs map
296+ :: XX search deeper in the .unix-effect?
297+ ::
298+ loop
299+ =/ aqua-pax
300+ :- %i
301+ /(scot %p our)/gh/(scot %p our)//(scot %da now)/[%$]/noun
302+ =+ ;; halts=(unit (jug app=term [ship =duct]))
303+ (scry-aqua:util noun our.bowl now aqua-pax)
304+ ?~ halts loop
305+ ?. (~(has by u.halts) dap) :: XX check .her as well
306+ loop
307+ (pure:m ~)
308+::
309+++ wait-for-del-halt
310+ |= [our=ship her=ship dap=term]
311+ =/ m (strand ,~)
312+ ^- form:m
313+ ;< =bowl:spider bind:m get-bowl
314+ |- ^- form:m
315+ =* loop $
316+ ;< [from=ship =unix-effect] bind:m take-unix-effect
317+ ;< now=@da bind:m get-time
318+ ?. =(from our)
319+ :: when the %spur $boon has been sent, gall has updated its state deleting
320+ :: the app from gall's .flubs map
321+ :: XX search deeper in the .unix-effect?
322+ ::
323+ loop
324+ =/ aqua-pax
325+ :- %i
326+ /(scot %p our)/gh/(scot %p our)//(scot %da now)/[%$]/noun
327+ =+ ;; halts=(unit (jug app=term [ship =duct]))
328+ (scry-aqua:util noun our.bowl now aqua-pax)
329+ ?~ halts loop
330+ ?: (~(has by u.halts) dap) :: XX check .her as well
331+ loop
332+ (pure:m ~)
333+::
334+++ wait-for-pac
335+ |= [our=ship to=ship]
336+ =/ m (strand ,~)
337+ ^- form:m
338+ ~& > "waiting for ack: {<[from=our to=to]>}"
339+ |- ^- form:m
340+ =* loop $
341+ ;< [from=ship =unix-effect] bind:m take-unix-effect
342+ ?. =(our from)
343+ loop
344+ :: only %send or %push effects
345+ ::
346+ ?. ?=(?(%send %push) -.q.unix-effect)
347+ loop
348+ :: XX check that this is an %ack?
349+ :: XX check that this is for .to?
350+ ::
351+ (pure:m ~)
196:: Send "|hi" from one ship to another
352:: Send "|hi" from one ship to another
197::
353::
198++ send-hi
354++ send-hi
@@ -204,6 +360,64 @@
204::
360::
205:: Send "|hi" and wait for "not responding" message
361:: Send "|hi" and wait for "not responding" message
206::
362::
363+++ wait-for-sunk
364+ |= [from=@p to=@p]
365+ =/ m (strand ,~)
366+ (wait-for-output from "{(scow %p to)} has sunk")
367+::
368+++ wait-for-fact
369+ |= [=ship =mark =wire gate=$-([mark noun] ?)]
370+ =/ m (strand ,noun)
371+ ^- form:m
372+ ~& > "waiting for fact: {<mark>}"
373+ |- ^- form:m
374+ =* loop $
375+ ;< [her=^ship =unix-effect] bind:m take-unix-effect
376+ ?. =(her ship)
377+ loop
378+ ?. ?& =(wire p.unix-effect)
379+ ?=([%unto %raw-fact *] q.unix-effect)
380+ =(mark mark.unto.q.unix-effect)
381+ (gate mark noun.unto.q.unix-effect)
382+ ==
383+ loop
384+ (pure:m noun.unto.q.unix-effect)
385+::
386+++ wait-for-cork
387+ |= [our=ship her=ship flow=(each bone:ames side:ames)]
388+ =/ m (strand ,~)
389+ ^- form:m
390+ ;< =bowl:spider bind:m get-bowl
391+ |- ^- form:m
392+ =* loop $
393+ ;< [from=ship =unix-effect] bind:m take-unix-effect
394+ ;< now=@da bind:m get-time
395+ :: only %send or %push effects
396+ ::
397+ ?. ?=(?(%send %push) -.q.unix-effect)
398+ loop
399+ :: XX check that this is an %ack?
400+ ::
401+ ?. =(from our)
402+ :: wait until the ack for the %cork $plea is sent, at this point
403+ :: the publisher has already corked the flow
404+ ::
405+ loop
406+ =/ aqua-pax
407+ %+ weld /i/(scot %p our)/ax/(scot %p our)//(scot %da now)
408+ ?: ?=(%& -.flow)
409+ /corked/(scot %p her)/(scot %ud +.flow)/noun
410+ =/ [=bone:ames =dire:ames] +.flow
411+ /corked/(scot %p her)/[dire]/(scot %ud bone)/noun
412+ =+ ;; corked=(unit ?) (scry-aqua:util noun our.bowl now aqua-pax)
413+ ?~ corked loop
414+ ?. u.corked :: XX check .her as well
415+ loop
416+ ~& >> flow-is-corked/flow
417+ (pure:m ~)
418+::
419+:: Send "|hi" and wait for "not responding" message
420+::
207++ send-hi-not-responding
421++ send-hi-not-responding
208 |= [from=@p to=@p]
422 |= [from=@p to=@p]
209 ~& > 'sending hi not responding'
423 ~& > 'sending hi not responding'
@@ -256,6 +470,14 @@
256 ;< ~ bind:m (send-events (insert-files:util her desk [pax warped] ~))
470 ;< ~ bind:m (send-events (insert-files:util her desk [pax warped] ~))
257 (pure:m warped)
471 (pure:m warped)
258::
472::
473+++ copy-file
474+ =/ m (strand ,~)
475+ |= [her=ship pax=path file=@t]
476+ ^- form:m
477+ ;< ~ bind:m
478+ (send-events (insert-files:util her %base [pax file] ~))
479+ (sleep ~s1)
480+::
259:: Check /sur/aquarium/hoon on the given has the given contents.
481:: Check /sur/aquarium/hoon on the given has the given contents.
260::
482::
261++ check-file-touched
483++ check-file-touched
base-desk/lib/ph/util.hoon+24−11
@@ -16,9 +16,11 @@
16:: Start a ship (low-level; prefer +raw-ship)
16:: Start a ship (low-level; prefer +raw-ship)
17::
17::
18++ init
18++ init
19− |= [who=ship fake=?]
19+ |= [who=ship ake=? eed=(unit feed:jael) ore=?(%mesa %ames)]
20 ^- (list aqua-event)
20 ^- (list aqua-event)
21− [%init-ship who fake]~
21+ :~ [%init-ship who ake eed]
22+ [%event who [/a/aqua/load %load ore]]
23+ ==
22::
24::
23:: Send dojo command
25:: Send dojo command
24::
26::
@@ -28,8 +30,8 @@
28 %+ send-events-to who
30 %+ send-events-to who
29 ^- (list unix-event)
31 ^- (list unix-event)
30 :~
32 :~
31− [/d/term/1 %belt %ctl `@c`%e]
33+ [/d/term/1 %belt %mod %ctl `@c`%e]
32− [/d/term/1 %belt %ctl `@c`%u]
34+ [/d/term/1 %belt %mod %ctl `@c`%u]
33 [/d/term/1 %belt %txt ((list @c) what)]
35 [/d/term/1 %belt %txt ((list @c) what)]
34 [/d/term/1 %belt %ret ~]
36 [/d/term/1 %belt %ret ~]
35 ==
37 ==
@@ -40,7 +42,7 @@
40 |= [who=ship what=term]
42 |= [who=ship what=term]
41 ^- (list ph-event)
43 ^- (list ph-event)
42 %+ send-events-to who
44 %+ send-events-to who
43− :~ [/d/term/1 %belt %ctl (,@c what)]
45+ :~ [/d/term/1 %belt %mod %ctl (,@c what)]
44 ==
46 ==
45::
47::
46:: Inject a file into a ship
48:: Inject a file into a ship
@@ -62,15 +64,26 @@
62::TODO should be rename -dill-output
64::TODO should be rename -dill-output
63++ is-dojo-output
65++ is-dojo-output
64 |= [who=ship her=ship uf=unix-effect what=tape]
66 |= [who=ship her=ship uf=unix-effect what=tape]
67+ |^
65 ?& =(who her)
68 ?& =(who her)
66 ?=(%blit -.q.uf)
69 ?=(%blit -.q.uf)
67− ::
68− %+ lien p.q.uf
70+ (lien p.q.uf handle-blit)
69− |= =blit:dill
70− ?. ?=(%lin -.blit)
71− |
72− !=(~ (find what p.blit))
73 ==
71 ==
72+ ::
73+ ++ handle-blit
74+ |= =blit:dill
75+ ^- ?
76+ ?: ?=(%mor -.blit)
77+ (lien p.blit handle-blit)
78+ ?+ -.blit |
79+ %put !=(~ (find what p.blit))
80+ ::
81+ %klr
82+ %+ lien p.blit
83+ |= [* q=(list @c)]
84+ !=(~ (find what q))
85+ ==
86+ --
74::
87::
75:: Test is successful if +is-dojo-output
88:: Test is successful if +is-dojo-output
76::
89::
base-desk/lib/pill.hoon+499−17
@@ -2,6 +2,7 @@
2::
2::
3/- dice
3/- dice
4^?
4^?
5+=<
5|%
6|%
6::
7::
7+$ pill
8+$ pill
@@ -11,6 +12,14 @@
11 boot-ova=(list)
12 boot-ova=(list)
12 kernel-ova=(list unix-event)
13 kernel-ova=(list unix-event)
13 userspace-ova=(list unix-event)
14 userspace-ova=(list unix-event)
15+ ==
16+ $: %cash
17+ $: nam=term
18+ boot-ova=(list)
19+ kernel-ova=(list unix-event)
20+ userspace-ova=(list unix-event)
21+ ==
22+ cache=(list)
14 == ==
23 == ==
15::
24::
16+$ unix-event
25+$ unix-event
@@ -32,25 +41,23 @@
32 [/sys/hoon hoon/hoon]
41 [/sys/hoon hoon/hoon]
33 [/sys/arvo hoon/arvo]
42 [/sys/arvo hoon/arvo]
34 ==
43 ==
44+:: +dirs: filter for userspace directories to include
45+::
46+++ dirs
47+ |= [bas=path exc=(list spur)]
48+ ^- (list spur)
49+ ?~ exc ~(tap in dir:.^(arch %cy bas))
50+ %+ skim
51+ ~(tap in dir:.^(arch %cy bas))
52+ |=(=spur =(~ (find ~[spur] exc)))
35:: +file-ovum: userspace filesystem load
53:: +file-ovum: userspace filesystem load
36::
54::
37:: bas: full path to / directory
55:: bas: full path to / directory
38::
56::
39++ file-ovum
57++ file-ovum
40− =/ directories=(list path)
41− :~ /app :: %gall applications
42− /gen :: :dojo generators
43− /lib :: libraries
44− /mar :: mark definitions
45− /sur :: structures
46− /sys :: system files
47− /ted :: :spider strands
48− /web :: %eyre web content
49− /desk :: desk manifest
50− ==
51− |= [des=desk bas=path]
58+ |= [des=desk bas=path exc=(list spur)]
52 ^- unix-event
59 ^- unix-event
53− %. directories
60+ %. (dirs bas exc)
54 |= :: sal: all spurs to load from
61 |= :: sal: all spurs to load from
55 ::
62 ::
56 sal=(list spur)
63 sal=(list spur)
@@ -64,7 +71,11 @@
64 %- ~(gas by *(map path (each page lobe:clay)))
71 %- ~(gas by *(map path (each page lobe:clay)))
65 (turn hav |=([=path =page] [path &+page]))
72 (turn hav |=([=path =page] [path &+page]))
66 [/c/sync [%park des &+yuki *rang:clay]]
73 [/c/sync [%park des &+yuki *rang:clay]]
74+ (file-pages bas sal)
75+::
76+++ file-pages
77+ |= [bas=path sal=(list spur)]
67− =| hav=(list [path page])
78+ =| hav=(list [path page:clay])
68 |- ^+ hav
79 |- ^+ hav
69 ?~ sal ~
80 ?~ sal ~
70 =. hav $(sal t.sal)
81 =. hav $(sal t.sal)
@@ -95,13 +106,43 @@
95::
106::
96:: +file-ovum2: electric boogaloo
107:: +file-ovum2: electric boogaloo
97::
108::
98−++ file-ovum2 |=(p=path `unix-event`[//arvo what/(user-files p)])
109+++ file-ovum2 |=(z=[path (list spur)] `unix-event`[//arvo what/(user-files z)])
110+::
111+++ get-wynn
112+ |= [sys=path new-wynn=?]
113+ ^- wynn
114+ ?. new-wynn
115+ :~ zuse+zuse
116+ lull+lull
117+ arvo+arvo
118+ hoon+hoon-version
119+ ==
120+ ~> %bout.[0 %get-wynn]
121+ =+ .^(zus=vase %ca (welp sys /zuse/hoon))
122+ =+ .^(lul=vase %ca (welp sys /lull/hoon))
123+ =+ .^(arv=vase %ca (welp sys /arvo/hoon))
124+ =+ .^(hun=vase %ca (welp sys /hoon/hoon))
125+ :~ [%zuse ;;(@ud q:(slap zus limb+%zuse))]
126+ [%lull ;;(@ud q:(slap lul limb+%lull))]
127+ [%arvo ;;(@ud q:(slap arv limb+%arvo))]
128+ [%hoon ;;(@ud q:(slap hun limb+%hoon-version))]
129+ ==
130+::
131+++ prep-ovum
132+ |= dez=(list path)
133+ ^- unix-event
134+ =- ~& clay-blobs+~(wyt by -)
135+ [/c/inflate [%prep -]]
136+ %+ roll dez
137+ |= [bas=path out=(map lobe:clay page:clay)]
138+ %- ~(uni by out)
139+ .^(_out %cs (snoc bas %bloc))
99::
140::
100:: +user-files: all userspace hoon files
141:: +user-files: all userspace hoon files
101::
142::
102++ user-files
143++ user-files
103− |= bas=path
144+ |= [bas=path exc=(list spur)]
104− %. directories:file-ovum
145+ %. (dirs bas exc)
105 |= sal=(list spur)
146 |= sal=(list spur)
106 ^- (list (pair path (cask)))
147 ^- (list (pair path (cask)))
107 ::
148 ::
@@ -135,4 +176,445 @@
135 |- ^+ hav
176 |- ^+ hav
136 ?~ all hav
177 ?~ all hav
137 $(all t.all, hav ^$(tyl [p.i.all tyl]))
178 $(all t.all, hav ^$(tyl [p.i.all tyl]))
179+::
180+::TODO include %prep task in solid and brass?
181+++ solid
182+ :: sys: root path to boot system, `/~me/[desk]/now/sys`
183+ :: dez: secondary desks and their root paths
184+ :: exc: list of desk folders to exclude
185+ ::
186+ |= [sys=path dez=(list [desk path]) dub=? now=@da prime=? exc=(list spur)]
187+ ^- pill
188+ =/ bas=path (scag 3 sys)
189+ =/ compiler-path (weld sys /hoon)
190+ =/ arvo-path (weld sys /arvo)
191+ ~& %solid-start
192+ =/ compiler-src .^(@t %cx (weld compiler-path /hoon))
193+ =/ arvo-src .^(@t %cx (weld arvo-path /hoon))
194+ =/ arvo-formula
195+ ~& %solid-loaded
196+ =/ compiler-hoon (rain compiler-path compiler-src)
197+ ?. dub
198+ :: compile arvo against hoon, with our current compiler
199+ ::
200+ =/ whole-hoon=hoon
201+ [%tsgr compiler-hoon [%tsgr [%$ 7] (rain arvo-path arvo-src)]]
202+ ~& %solid-parsed
203+ =/ whole-formula q:(~(mint ut %noun) %noun whole-hoon)
204+ ~& %solid-arvo
205+ whole-formula
206+ :: compile arvo against hoon, with a freshly compiled hoon (via +ride)
207+ ::
208+ ~& %solid-parsed
209+ =/ compiler-formula q:(~(mint ut %noun) %noun compiler-hoon)
210+ ~& %solid-compiled
211+ =/ whole-src
212+ (rap 3 ['=> ' compiler-src '=> +7 ' arvo-src ~])
213+ ~& %solid-double-loaded
214+ =/ whole-formula
215+ =< +
216+ .* [%noun whole-src]
217+ [%8 compiler-formula [%9 2 %10 [6 %0 3] [%0 2]]]
218+ ~& %solid-double-compiled
219+ whole-formula
220+ ::
221+ ~& [%solid-kernel `@ux`(mug arvo-formula)]
222+ ::
223+ :: installed: Arvo gate (formal interface) with %zuse and vanes installed
224+ ::
225+ =/ installed
226+ =< q
227+ %^ spin
228+ ^- (list ovum)
229+ :- (boot-ovum:pill compiler-src arvo-src)
230+ %+ turn
231+ (snoc (turn dez tail) bas)
232+ |= =path
233+ (file-ovum2 [path exc])
234+ .*(0 arvo-formula)
235+ |= [ovo=ovum ken=*]
236+ [~ (slum ken [now ovo])]
237+ ::
238+ :: kernel-formula
239+ ::
240+ :: We evaluate :arvo-formula (for jet registration),
241+ :: then ignore the result and produce .installed
242+ ::
243+ =/ kernel-formula
244+ [%7 arvo-formula %1 installed]
245+ ::
246+ :: boot-two: startup formula
247+ ::
248+ =/ boot-two
249+ => *[kernel-formula=^ tale=*]
250+ != [.*(0 kernel-formula) tale]
251+ ::
252+ :: boot-ova
253+ ::
254+ =/ boot-ova=(list)
255+ [aeon:eden:part boot-two kernel-formula ~]
256+ ::
257+ :: a pill is a 3-tuple of event-lists: [boot kernel userspace]
258+ ::
259+ :: Our kernel event-list is ~, as we've already installed them.
260+ :: Our userspace event-list is a list containing a full %clay
261+ :: filesystem sync event.
262+ ::
263+ :+ %pill %solid
264+ :+ boot-ova ~
265+ =. dez (snoc dez [%base bas])
266+ ;: weld
267+ %+ turn dez
268+ |= [dek=desk bas=path]
269+ (file-ovum [dek bas exc])
270+ ::
271+ ?. prime ~
272+ [(prep-ovum (turn dez tail))]~
273+ ::
274+ %+ turn dez
275+ |= [dek=desk *]
276+ ^- unix-event
277+ [/c/essential/[dek] %esse dek %.y]
278+ ==
279+::
280+++ brass
281+ :: sys: root path to boot system, `/~me/[desk]/now/sys`
282+ :: dez: secondary desks and their root paths
283+ :: exc: list of desk folders to exclude
284+ ::
285+ |= [sys=path dez=(list [desk path]) cache=? prime=? new-wynn=? exc=(list spur)]
286+ ^- pill
287+ =/ bas=path (scag 3 sys)
288+ :: compiler-source: hoon source file producing compiler, `sys/hoon`
289+ ::
290+ =+ compiler-source=.^(@t %cx (welp sys /hoon/hoon))
291+ ::
292+ :: compiler-twig: compiler as hoon expression
293+ ::
294+ ~& %brass-parsing
295+ =+ compiler-twig=(rain /sys/hoon/hoon compiler-source)
296+ ~& %brass-parsed
297+ ::
298+ :: compiler-formula: compiler as nock formula
299+ ::
300+ ~& %brass-compiling
301+ =+ compiler-formula=q:(~(mint ut %noun) %noun compiler-twig)
302+ ~& %brass-compiled
303+ ::
304+ :: arvo-source: hoon source file producing arvo kernel, `sys/arvo`
305+ ::
306+ =+ arvo-source=.^(@t %cx (welp sys /arvo/hoon))
307+ ::
308+ :: boot-ova: startup events
309+ ::
310+ =/ boot-ova=(list)
311+ :~ aeon:eden:part
312+ boot:eden:part
313+ compiler-formula
314+ compiler-source
315+ arvo-source
316+ ==
317+ ::
318+ =/ kernel-ova=(list unix-event)
319+ :~ (boot-ovum compiler-source arvo-source)
320+ (file-ovum2 [bas exc])
321+ ==
322+ ::
323+ =/ userspace-ova
324+ =. dez (snoc dez [%base bas])
325+ ;: weld
326+ %+ turn dez
327+ |= [dek=desk bas=path]
328+ (file-ovum [dek bas exc])
329+ ::
330+ ?. prime ~
331+ [(prep-ovum (turn dez tail))]~
332+ ::
333+ %+ turn dez
334+ |= [dek=desk *]
335+ ^- unix-event
336+ [/c/essential/[dek] %esse dek %.y]
337+ ==
338+ ::
339+ =/ =wynn (get-wynn sys new-wynn)
340+ ::
341+ =/ cache=(list)
342+ ?. cache ~
343+ %~ tap by
344+ =< +
345+ %- mice
346+ :_ [%2 [%0 3] %0 2]
347+ ;: weld
348+ boot-ova
349+ ^- (list)
350+ :~ [*@da //arvo %wack *@uvJ]
351+ [*@da //arvo %whom *@p]
352+ [*@da //arvo %wyrd [~.nonce /] wynn]
353+ ==
354+ `(list)`(turn kernel-ova (lead *@da))
355+ `(list)`[*@da [/d/term/1 %boot & %fake *@p]]^~
356+ `(list)`(turn userspace-ova (lead *@da))
357+ `(list)`[*@da [/c/init-zest %zeal (turn dez |=([d=desk *] [d %live]))]]^~
358+ ==
359+ ::
360+ ~> %slog.[0 leaf+"brass: cache entries: {<(lent cache)>}"]
361+ ::
362+ :- %cash
363+ :_ cache
364+ [%brass boot-ova kernel-ova userspace-ova]
365+::
366+++ ivory
367+ |= sys=path
368+ ^- pill
369+ =/ lib (snoc (scag 3 sys) %lib)
370+ |^ =/ ver
371+ =/ sub *(trap vase)
372+ =. sub (build-sys sub %hoon)
373+ =. sub (build-sys sub %arvo)
374+ =. sub (build-sys sub %lull)
375+ =. sub (build-sys sub %zuse)
376+ =. sub (build-lib sub & %ethereum)
377+ =. sub (build-lib sub & %azimuth)
378+ (build-lib sub | %vere)
379+ =/ nok !.
380+ => *[ver=(trap vase) ~]
381+ != q:$:ver
382+ ivory/[nok ver ~]
383+ ::
384+ ++ build-sys
385+ |= [sub=(trap vase) nam=term] ^- (trap vase)
386+ ~> %slog.[0 leaf+"ivory: building /sys/{(trip nam)}"]
387+ (swat sub (rain /sys/[nam]/hoon .^(@t cx+(welp sys /[nam]/hoon))))
388+ ::
389+ ++ build-lib
390+ |= [sub=(trap vase) imp=? nam=term] ^- (trap vase)
391+ ~> %slog.[0 leaf+"ivory: building /lib/{(trip nam)}"]
392+ =/ hun=hoon
393+ %+ mist /lib/[nam]/hoon
394+ .^(@t cx+(welp lib /[nam]/hoon))
395+ ?. imp (swat sub hun)
396+ (swel sub [%ktts nam hun])
397+ :: +mist: +rain but skipping past ford runes
398+ ::
399+ ++ mist
400+ |= [bon=path txt=@]
401+ ^- hoon
402+ =+ vas=vast
403+ ~| bon
404+ %+ scan (trip txt)
405+ %- full
406+ =; fud
407+ (ifix [;~(plug gay fud) gay] tall:vas(wer bon))
408+ %- star
409+ ;~ pose vul
410+ %+ ifix [fas (just `@`10)]
411+ (star ;~(less (just `@`10) next))
412+ ==
413+ :: +swel: +swat but with +slop
414+ ::
415+ ++ swel
416+ |= [tap=(trap vase) gen=hoon]
417+ ^- (trap vase)
418+ =/ gun (~(mint ut p:$:tap) %noun gen)
419+ => [tap=tap gun=gun]
420+ |. ~+
421+ =/ pro q:$:tap
422+ [[%cell p.gun p:$:tap] [.*(pro q.gun) pro]]
423+ --
424+::
425+++ events
426+ |%
427+ +$ prop [%prop meta tier (list ovum)]
428+ +$ meta term
429+ +$ tier ?(%fore %hind) :: before or after userspace
430+ ::
431+ ++ install
432+ |= [as=desk =beak pri=?]
433+ ^- prop
434+ :^ %prop (rap 3 %install '-' as ~) %hind
435+ ::TODO will exclude non-:directories files, such as /changelog/txt
436+ =- (murn - same)
437+ ^- (list (unit ovum))
438+ :~ `(file-ovum as (en-beam beak /) ~)
439+ ::
440+ ?. pri ~
441+ `(prep-ovum (en-beam beak /) ~)
442+ ::
443+ `[/d/install/[as] [%seat as]]
444+ ==
445+ --
446+--
447+::
448+~% %non ..part ~
449+:: +mice: %memo hint caching version of mink
450+::
451+|%
452+++ mice !.
453+ ~/ %mice
454+ |= [subject=* formula=*]
455+ =| trace=(list [@ta *])
456+ =| memo=(map)
457+ =; [ton=tone mem=_memo]
458+ ?. ?=(%0 -.ton)
459+ [ton ~]
460+ [ton mem]
461+ |^ ^- [tone _memo]
462+ ?+ formula [[%2 trace] memo]
463+ [^ *]
464+ =^ head memo $(formula -.formula)
465+ ?. ?=(%0 -.head) [head memo]
466+ =^ tail memo $(formula +.formula)
467+ :_ memo
468+ ?. ?=(%0 -.tail) tail
469+ [%0 product.head product.tail]
470+ ::
471+ [%0 axis=@]
472+ :_ memo
473+ =/ part (frag axis.formula subject)
474+ ?~ part [%2 trace]
475+ [%0 u.part]
476+ ::
477+ [%1 constant=*]
478+ [[%0 constant.formula] memo]
479+ ::
480+ [%2 subject=* formula=*]
481+ =^ subject memo $(formula subject.formula)
482+ ?. ?=(%0 -.subject) [subject memo]
483+ =^ formula memo $(formula formula.formula)
484+ ?. ?=(%0 -.formula) [formula memo]
485+ %= $
486+ subject product.subject
487+ formula product.formula
488+ ==
489+ ::
490+ [%3 argument=*]
491+ =^ argument memo $(formula argument.formula)
492+ :_ memo
493+ ?. ?=(%0 -.argument) argument
494+ [%0 .?(product.argument)]
495+ ::
496+ [%4 argument=*]
497+ =^ argument memo $(formula argument.formula)
498+ :_ memo
499+ ?. ?=(%0 -.argument) argument
500+ ?^ product.argument [%2 trace]
501+ [%0 .+(product.argument)]
502+ ::
503+ [%5 a=* b=*]
504+ =^ a memo $(formula a.formula)
505+ ?. ?=(%0 -.a) [a memo]
506+ =^ b memo $(formula b.formula)
507+ :_ memo
508+ ?. ?=(%0 -.b) b
509+ [%0 =(product.a product.b)]
510+ ::
511+ [%6 test=* yes=* no=*]
512+ =^ result memo $(formula test.formula)
513+ ?. ?=(%0 -.result) [result memo]
514+ ?+ product.result [[%2 trace] memo]
515+ %& $(formula yes.formula)
516+ %| $(formula no.formula)
517+ ==
518+ ::
519+ [%7 subject=* next=*]
520+ =^ subject memo $(formula subject.formula)
521+ ?. ?=(%0 -.subject) [subject memo]
522+ %= $
523+ subject product.subject
524+ formula next.formula
525+ ==
526+ ::
527+ [%8 head=* next=*]
528+ =^ head memo $(formula head.formula)
529+ ?. ?=(%0 -.head) [head memo]
530+ %= $
531+ subject [product.head subject]
532+ formula next.formula
533+ ==
534+ ::
535+ [%9 axis=@ core=*]
536+ =^ core memo
537+ $(formula core.formula)
538+ ?. ?=(%0 -.core) [core memo]
539+ =/ arm (frag axis.formula product.core)
540+ ?~ arm [[%2 trace] memo]
541+ %= $
542+ subject product.core
543+ formula u.arm
544+ ==
545+ ::
546+ [%10 [axis=@ value=*] target=*]
547+ ?: =(0 axis.formula) [[%2 trace] memo]
548+ =^ target memo $(formula target.formula)
549+ ?. ?=(%0 -.target) [target memo]
550+ =^ value memo $(formula value.formula)
551+ :_ memo
552+ ?. ?=(%0 -.value) value
553+ =/ mutant=(unit *)
554+ (edit axis.formula product.target product.value)
555+ ?~ mutant [%2 trace]
556+ [%0 u.mutant]
557+ ::
558+ [%11 tag=@ next=*]
559+ =^ next memo $(formula next.formula)
560+ :_ memo
561+ ?. ?=(%0 -.next) next
562+ :- %0
563+ .* subject
564+ [11 tag.formula 1 product.next]
565+ ::
566+ [%11 [tag=@ clue=*] next=*]
567+ =^ clue memo $(formula clue.formula)
568+ ?. ?=(%0 -.clue) [clue memo]
569+ ?: ?=(%memo tag.formula)
570+ ?^ hit=(~(get by memo) subject next.formula)
571+ [[%0 u.hit] memo]
572+ =^ ton memo $(formula next.formula)
573+ ?. ?=(%0 -.ton)
574+ [ton memo]
575+ ?: ?=(?(@ [%clay %ford *]) product.clue)
576+ [ton memo]
577+ [ton (~(put by memo) [subject next.formula] product.ton)]
578+ =^ next memo
579+ =? trace
580+ ?=(?(%hunk %hand %lose %mean %spot) tag.formula)
581+ [[tag.formula product.clue] trace]
582+ $(formula next.formula)
583+ :_ memo
584+ ?. ?=(%0 -.next) next
585+ :- %0
586+ .* subject
587+ [11 [tag.formula 1 product.clue] 1 product.next]
588+ ==
589+ ::
590+ ++ frag
591+ |= [axis=@ noun=*]
592+ ^- (unit)
593+ ?: =(0 axis) ~
594+ |- ^- (unit)
595+ ?: =(1 axis) `noun
596+ ?@ noun ~
597+ =/ pick (cap axis)
598+ %= $
599+ axis (mas axis)
600+ noun ?-(pick %2 -.noun, %3 +.noun)
601+ ==
602+ ::
603+ ++ edit
604+ |= [axis=@ target=* value=*]
605+ ^- (unit)
606+ ?: =(1 axis) `value
607+ ?@ target ~
608+ =/ pick (cap axis)
609+ =/ mutant
610+ %= $
611+ axis (mas axis)
612+ target ?-(pick %2 -.target, %3 +.target)
613+ ==
614+ ?~ mutant ~
615+ ?- pick
616+ %2 `[u.mutant +.target]
617+ %3 `[-.target u.mutant]
618+ ==
619+ --
138--
620--
base-desk/lib/ring.hoon+36−36
@@ -18,10 +18,10 @@
18 ::
18 ::
19 ++ generate-public-linkage
19 ++ generate-public-linkage
20 |= link-scope=@
20 |= link-scope=@
21− ^- [data=@ h=@udpoint]
21+ ^- [data=@ h=@uxpoint]
22 ::
22 ::
23 =/ data=@ (mod link-scope l:ed:crypto)
23 =/ data=@ (mod link-scope l:ed:crypto)
24− =/ h=@udpoint (scalarmult-base:ed:crypto data)
24+ =/ h=@uxpoint (scalarmult-base:ed:crypto data)
25 [data h]
25 [data h]
26 :: +generate-linkage: linkage information from scope and private key
26 :: +generate-linkage: linkage information from scope and private key
27 ::
27 ::
@@ -30,13 +30,13 @@
30 :: y: y = [x] * h
30 :: y: y = [x] * h
31 ++ generate-linkage
31 ++ generate-linkage
32 |= [link-scope=(unit @) my-private-key=@]
32 |= [link-scope=(unit @) my-private-key=@]
33− ^- (unit [data=@ h=@udpoint y=@udpoint])
33+ ^- (unit [data=@ h=@uxpoint y=@uxpoint])
34 ::
34 ::
35 ?~ link-scope
35 ?~ link-scope
36 ~
36 ~
37 ::
37 ::
38− =+ [data=@ h=@udpoint]=(generate-public-linkage u.link-scope)
38+ =+ [data=@ h=@uxpoint]=(generate-public-linkage u.link-scope)
39− =/ y=@udpoint (scalarmult:ed:crypto my-private-key h)
39+ =/ y=@uxpoint (scalarmult:ed:crypto my-private-key h)
40 [~ data h y]
40 [~ data h y]
41 :: +generate-challenge: generate challenge from a given message
41 :: +generate-challenge: generate challenge from a given message
42 ::
42 ::
@@ -47,13 +47,13 @@
47 ++ generate-challenge
47 ++ generate-challenge
48 |= $: :: common to both linked and unlinked
48 |= $: :: common to both linked and unlinked
49 message=@
49 message=@
50− g=@udpoint
50+ g=@uxpoint
51 :: high level universal state
51 :: high level universal state
52 ::
52 ::
53− link-state=(unit [data=@ h=@udpoint y=@udpoint])
53+ link-state=(unit [data=@ h=@uxpoint y=@uxpoint])
54 :: point to include in challenge when link-state isn't ~
54 :: point to include in challenge when link-state isn't ~
55 ::
55 ::
56− h=(unit @udpoint)
56+ h=(unit @uxpoint)
57 ==
57 ==
58 ^- @
58 ^- @
59 :: concatenate and reduce our message down to a 512-bit hash
59 :: concatenate and reduce our message down to a 512-bit hash
@@ -74,9 +74,9 @@
74 :: +generate-challenges: generates the full list of challenges
74 :: +generate-challenges: generates the full list of challenges
75 ::
75 ::
76 ++ generate-challenges
76 ++ generate-challenges
77− |= $: link-state=(unit [data=@ h=@udpoint y=@udpoint])
77+ |= $: link-state=(unit [data=@ h=@uxpoint y=@uxpoint])
78 message=@
78 message=@
79− public-keys=(list @udpoint)
79+ public-keys=(list @uxpoint)
80 ss=(list @)
80 ss=(list @)
81 ::
81 ::
82 prev-k=@u
82 prev-k=@u
@@ -86,14 +86,14 @@
86 ==
86 ==
87 ^- (list @)
87 ^- (list @)
88 ::
88 ::
89− =/ gs=@udpoint
89+ =/ gs=@uxpoint
90 %- add-scalarmult-scalarmult-base:ed:crypto :*
90 %- add-scalarmult-scalarmult-base:ed:crypto :*
91 prev-ch
91 prev-ch
92 (snag prev-k public-keys)
92 (snag prev-k public-keys)
93 prev-s
93 prev-s
94 ==
94 ==
95 ::
95 ::
96− =/ hs=(unit @udpoint)
96+ =/ hs=(unit @uxpoint)
97 ?~ link-state
97 ?~ link-state
98 ~
98 ~
99 ::
99 ::
@@ -125,8 +125,8 @@
125 :: used to generate the (unit point) consumed by +generate-challenge.
125 :: used to generate the (unit point) consumed by +generate-challenge.
126 ::
126 ::
127 ++ scalarmult-h
127 ++ scalarmult-h
128− |= [u=@ linkage=(unit [data=@ h=@udpoint y=@udpoint])]
128+ |= [u=@ linkage=(unit [data=@ h=@uxpoint y=@uxpoint])]
129− ^- (unit @udpoint)
129+ ^- (unit @uxpoint)
130 ?~ linkage
130 ?~ linkage
131 ~
131 ~
132 [~ (scalarmult:ed:crypto u h.u.linkage)]
132 [~ (scalarmult:ed:crypto u h.u.linkage)]
@@ -143,9 +143,9 @@
143 |= $: message=@
143 |= $: message=@
144 link-scope=(unit @)
144 link-scope=(unit @)
145 ::
145 ::
146− anonymity-set=(set @udpoint)
146+ anonymity-set=(set @uxpoint)
147− my-public-key=@udpoint
147+ my-public-key=@uxpoint
148− my-private-key=@udscalar
148+ my-private-key=@uxscalar
149 ::
149 ::
150 eny=@uvJ
150 eny=@uvJ
151 ==
151 ==
@@ -157,7 +157,7 @@
157 (need (find [my-public-key ~] anonymity-list))
157 (need (find [my-public-key ~] anonymity-list))
158 :: Generate linkage information if given
158 :: Generate linkage information if given
159 ::
159 ::
160− =/ linkage=(unit [data=@ h=@udpoint y=@udpoint])
160+ =/ linkage=(unit [data=@ h=@uxpoint y=@uxpoint])
161 (generate-linkage link-scope my-private-key)
161 (generate-linkage link-scope my-private-key)
162 :: initialize our random number generator from entropy
162 :: initialize our random number generator from entropy
163 ::
163 ::
@@ -237,7 +237,7 @@
237 |= $: message=@
237 |= $: message=@
238 link-scope=(unit @)
238 link-scope=(unit @)
239 ::
239 ::
240− anonymity-set=(set @udpoint)
240+ anonymity-set=(set @uxpoint)
241 signature=raw-ring-signature
241 signature=raw-ring-signature
242 ==
242 ==
243 ^- ?
243 ^- ?
@@ -259,14 +259,14 @@
259 =/ s2-to-end=(list @) t.t.s.signature
259 =/ s2-to-end=(list @) t.t.s.signature
260 :: anonymity-list: set of public keys listified in ring order
260 :: anonymity-list: set of public keys listified in ring order
261 ::
261 ::
262− =/ anonymity-list=(list @udpoint)
262+ =/ anonymity-list=(list @uxpoint)
263 ~(tap in anonymity-set)
263 ~(tap in anonymity-set)
264 :: participants: length of :anonymity-list
264 :: participants: length of :anonymity-list
265 ::
265 ::
266 =/ participants=@u
266 =/ participants=@u
267 (lent anonymity-list)
267 (lent anonymity-list)
268 ::
268 ::
269− =/ z0p=@udpoint
269+ =/ z0p=@uxpoint
270 %- add-scalarmult-scalarmult-base:ed:crypto :*
270 %- add-scalarmult-scalarmult-base:ed:crypto :*
271 ch0.signature
271 ch0.signature
272 (head anonymity-list)
272 (head anonymity-list)
@@ -275,14 +275,14 @@
275 :: generate the linkage using public data, and the y point from the
275 :: generate the linkage using public data, and the y point from the
276 :: signature
276 :: signature
277 ::
277 ::
278− =/ linkage=(unit [data=@ h=@udpoint y=@udpoint])
278+ =/ linkage=(unit [data=@ h=@uxpoint y=@uxpoint])
279 ?~ link-scope
279 ?~ link-scope
280 ~
280 ~
281− =+ [data=@ h=@udpoint]=(generate-public-linkage u.link-scope)
281+ =+ [data=@ h=@uxpoint]=(generate-public-linkage u.link-scope)
282 :- ~
282 :- ~
283 [data h (need y.signature)]
283 [data h (need y.signature)]
284 ::
284 ::
285− =/ z0pp=(unit @udpoint)
285+ =/ z0pp=(unit @uxpoint)
286 ?~ linkage
286 ?~ linkage
287 ~
287 ~
288 :- ~
288 :- ~
@@ -319,7 +319,7 @@
319 :: +seed-to-private-key-scalar: keyfile form to scalar we can multiply with
319 :: +seed-to-private-key-scalar: keyfile form to scalar we can multiply with
320 ::
320 ::
321 ++ seed-to-private-key-scalar
321 ++ seed-to-private-key-scalar
322− |= sk=@I ^- @udscalar
322+ |= sk=@I ^- @uxscalar
323 ?: (gth (met 3 sk) 32) !!
323 ?: (gth (met 3 sk) 32) !!
324 =+ h=(shal (rsh [0 3] b:ed:crypto) sk)
324 =+ h=(shal (rsh [0 3] b:ed:crypto) sk)
325 %+ add
325 %+ add
@@ -347,7 +347,7 @@
347 ::
347 ::
348 ++ ship-life-to-pubid
348 ++ ship-life-to-pubid
349 |= [our=@p now=@da ship=@p =life]
349 |= [our=@p now=@da ship=@p =life]
350− ^- @udpoint
350+ ^- @uxpoint
351 ::
351 ::
352 =/ d=[=^life =pass *]
352 =/ d=[=^life =pass *]
353 =/ scry-path=path
353 =/ scry-path=path
@@ -364,14 +364,14 @@
364 ::
364 ::
365 =/ x=[crypt=@ auth=@] (get-public-key-from-pass pass.d)
365 =/ x=[crypt=@ auth=@] (get-public-key-from-pass pass.d)
366 ::
366 ::
367− `@udpoint`auth.x
367+ `@uxpoint`auth.x
368 ::
368 ::
369 ++ build-signing-participants
369 ++ build-signing-participants
370 |= [our=@p now=@da invited=(list @p)]
370 |= [our=@p now=@da invited=(list @p)]
371− ^- [(set [@p life]) (set @udpoint)]
371+ ^- [(set [@p life]) (set @uxpoint)]
372 ::
372 ::
373 =| participants=(set [@p life])
373 =| participants=(set [@p life])
374− =| keys=(set @udpoint)
374+ =| keys=(set @uxpoint)
375 ::
375 ::
376 |-
376 |-
377 ?~ invited
377 ?~ invited
@@ -383,7 +383,7 @@
383 ?~ lyfe
383 ?~ lyfe
384 $(invited t.invited)
384 $(invited t.invited)
385 ::
385 ::
386− =/ pubkey=@udpoint (ship-life-to-pubid our now i.invited u.lyfe)
386+ =/ pubkey=@uxpoint (ship-life-to-pubid our now i.invited u.lyfe)
387 ::
387 ::
388 =. participants (~(put in participants) [i.invited u.lyfe])
388 =. participants (~(put in participants) [i.invited u.lyfe])
389 =. keys (~(put in keys) pubkey)
389 =. keys (~(put in keys) pubkey)
@@ -393,15 +393,15 @@
393 ::
393 ::
394 ++ build-verifying-participants
394 ++ build-verifying-participants
395 |= [our=@p now=@da invited=(list [ship=@p =life])]
395 |= [our=@p now=@da invited=(list [ship=@p =life])]
396− ^- (set @udpoint)
396+ ^- (set @uxpoint)
397 ::
397 ::
398− =| keys=(set @udpoint)
398+ =| keys=(set @uxpoint)
399 ::
399 ::
400 |-
400 |-
401 ?~ invited
401 ?~ invited
402 keys
402 keys
403 ::
403 ::
404− =/ pubkey=@udpoint
404+ =/ pubkey=@uxpoint
405 (ship-life-to-pubid our now ship.i.invited life.i.invited)
405 (ship-life-to-pubid our now ship.i.invited life.i.invited)
406 =. keys
406 =. keys
407 (~(put in keys) pubkey)
407 (~(put in keys) pubkey)
@@ -432,7 +432,7 @@
432 =/ link-hash=(unit @) (bind link-scope |=(a=* (shaz (jam a))))
432 =/ link-hash=(unit @) (bind link-scope |=(a=* (shaz (jam a))))
433 :: get everyone's public keys
433 :: get everyone's public keys
434 ::
434 ::
435− =/ p=[participants=(set [ship=@p =life]) keys=(set @udpoint)]
435+ =/ p=[participants=(set [ship=@p =life]) keys=(set @uxpoint)]
436 (build-signing-participants:detail our now ~(tap in anonymity-set))
436 (build-signing-participants:detail our now ~(tap in anonymity-set))
437 :: get our ships' current life
437 :: get our ships' current life
438 ::
438 ::
@@ -448,7 +448,7 @@
448 +:(get-private-key-from-ring:detail secret-ring)
448 +:(get-private-key-from-ring:detail secret-ring)
449 :: get our ships' public key
449 :: get our ships' public key
450 ::
450 ::
451− =/ public-key=@udpoint
451+ =/ public-key=@uxpoint
452 (ship-life-to-pubid:detail our now our our-life)
452 (ship-life-to-pubid:detail our now our our-life)
453 ::
453 ::
454 :- participants.p
454 :- participants.p
@@ -467,7 +467,7 @@
467 |= [our=@p now=@da message=* =ring-signature]
467 |= [our=@p now=@da message=* =ring-signature]
468 ^- ?
468 ^- ?
469 ::
469 ::
470− =/ keys=(set @udpoint)
470+ =/ keys=(set @uxpoint)
471 %^ build-verifying-participants:detail our now
471 %^ build-verifying-participants:detail our now
472 ~(tap in participants.ring-signature)
472 ~(tap in participants.ring-signature)
473 ::
473 ::
base-desk/lib/server.hoon+2−2
@@ -19,7 +19,7 @@
19++ json-to-octs
19++ json-to-octs
20 |= jon=json
20 |= jon=json
21 ^- octs
21 ^- octs
22− (as-octt:mimes:html (en-json:html jon))
22+ (as-octs:mimes:html (en:json:html jon))
23::
23::
24++ app
24++ app
25 |%
25 |%
@@ -114,7 +114,7 @@
114 :_ `octs
114 :_ `octs
115 [200 [['content-type' 'image/svg+xml'] ?:(cache [max-1-wk ~] ~)]]
115 [200 [['content-type' 'image/svg+xml'] ?:(cache [max-1-wk ~] ~)]]
116 ::
116 ::
117− ++ ico-response
117+ ++ ico-response
118 |= =octs
118 |= =octs
119 ^- simple-payload:http
119 ^- simple-payload:http
120 [[200 [['content-type' 'image/x-icon'] max-1-wk ~]] `octs]
120 [[200 [['content-type' 'image/x-icon'] max-1-wk ~]] `octs]
base-desk/lib/shoe.hoon+72−36
@@ -13,15 +13,15 @@
13/- *sole
13/- *sole
14/+ sole, auto=language-server-complete
14/+ sole, auto=language-server-complete
15|%
15|%
16−+$ state-0
16++$ state-2
17− $: %0
17+ $: %2
18− soles=(map @ta sole-share)
18+ soles=(map sole-id sole-share)
19 ==
19 ==
20:: $card: standard gall cards plus shoe effects
20:: $card: standard gall cards plus shoe effects
21::
21::
22+$ card
22+$ card
23 $% card:agent:gall
23 $% card:agent:gall
24− [%shoe sole-ids=(list @ta) effect=shoe-effect] :: ~ sends to all soles
24+ [%shoe sole-ids=(list sole-id) effect=shoe-effect] :: ~ sends to all
25 ==
25 ==
26:: $shoe-effect: easier sole-effects
26:: $shoe-effect: easier sole-effects
27::
27::
@@ -47,30 +47,30 @@
47 :: if the head of the result is true, instantly run the command
47 :: if the head of the result is true, instantly run the command
48 ::
48 ::
49 ++ command-parser
49 ++ command-parser
50− |~ sole-id=@ta
50+ |~ =sole-id
51 |~(nail *(like [? command-type]))
51 |~(nail *(like [? command-type]))
52 :: +tab-list: autocomplete options for the session (to match +command-parser)
52 :: +tab-list: autocomplete options for the session (to match +command-parser)
53 ::
53 ::
54 ++ tab-list
54 ++ tab-list
55− |~ sole-id=@ta
55+ |~ =sole-id
56 :: (list [@t tank])
56 :: (list [@t tank])
57 *(list (option:auto tank))
57 *(list (option:auto tank))
58 :: +on-command: called when a valid command is run
58 :: +on-command: called when a valid command is run
59 ::
59 ::
60 ++ on-command
60 ++ on-command
61− |~ [sole-id=@ta command=command-type]
61+ |~ [=sole-id command=command-type]
62 *(quip card _^|(..on-init))
62 *(quip card _^|(..on-init))
63 ::
63 ::
64 ++ can-connect
64 ++ can-connect
65− |~ sole-id=@ta
65+ |~ =sole-id
66 *?
66 *?
67 ::
67 ::
68 ++ on-connect
68 ++ on-connect
69− |~ sole-id=@ta
69+ |~ =sole-id
70 *(quip card _^|(..on-init))
70 *(quip card _^|(..on-init))
71 ::
71 ::
72 ++ on-disconnect
72 ++ on-disconnect
73− |~ sole-id=@ta
73+ |~ =sole-id
74 *(quip card _^|(..on-init))
74 *(quip card _^|(..on-init))
75 ::
75 ::
76 ::NOTE standard gall agent arms below, though they may produce %shoe cards
76 ::NOTE standard gall agent arms below, though they may produce %shoe cards
@@ -119,27 +119,27 @@
119 |* [shoe=* command-type=mold]
119 |* [shoe=* command-type=mold]
120 |_ =bowl:gall
120 |_ =bowl:gall
121 ++ command-parser
121 ++ command-parser
122− |= sole-id=@ta
122+ |= =sole-id
123 (easy *[? command-type])
123 (easy *[? command-type])
124 ::
124 ::
125 ++ tab-list
125 ++ tab-list
126− |= sole-id=@ta
126+ |= =sole-id
127 ~
127 ~
128 ::
128 ::
129 ++ on-command
129 ++ on-command
130− |= [sole-id=@ta command=command-type]
130+ |= [=sole-id command=command-type]
131 [~ shoe]
131 [~ shoe]
132 ::
132 ::
133 ++ can-connect
133 ++ can-connect
134− |= sole-id=@ta
134+ |= =sole-id
135 (team:title [our src]:bowl)
135 (team:title [our src]:bowl)
136 ::
136 ::
137 ++ on-connect
137 ++ on-connect
138− |= sole-id=@ta
138+ |= =sole-id
139 [~ shoe]
139 [~ shoe]
140 ::
140 ::
141 ++ on-disconnect
141 ++ on-disconnect
142− |= sole-id=@ta
142+ |= =sole-id
143 [~ shoe]
143 [~ shoe]
144 --
144 --
145:: +agent: creates wrapper core that handles sole events and calls shoe arms
145:: +agent: creates wrapper core that handles sole events and calls shoe arms
@@ -147,7 +147,7 @@
147++ agent
147++ agent
148 |* command-type=mold
148 |* command-type=mold
149 |= =(shoe command-type)
149 |= =(shoe command-type)
150− =| state-0
150+ =| state-2
151 =* state -
151 =* state -
152 ^- agent:gall
152 ^- agent:gall
153 =>
153 =>
@@ -164,8 +164,7 @@
164 %+ turn
164 %+ turn
165 ?^ sole-ids.card sole-ids.card
165 ?^ sole-ids.card sole-ids.card
166 ~(tap in ~(key by soles))
166 ~(tap in ~(key by soles))
167− |= sole-id=@ta
167+ id-to-path:sole
168− /sole/[sole-id]
169 ::
168 ::
170 %table
169 %table
171 =; fez=(list sole-effect)
170 =; fez=(list sole-effect)
@@ -189,7 +188,7 @@
189 =^ cards shoe on-init:og
188 =^ cards shoe on-init:og
190 [(deal cards) this]
189 [(deal cards) this]
191 ::
190 ::
192− ++ on-save !>([%shoe-app on-save:og state])
191+ ++ on-save :(slop !>(%shoe-app) on-save:og !>(state))
193 ::
192 ::
194 ++ on-load
193 ++ on-load
195 |= old-state=vase
194 |= old-state=vase
@@ -202,9 +201,45 @@
202 ?. ?=([%shoe-app ^] q.old-state)
201 ?. ?=([%shoe-app ^] q.old-state)
203 =^ cards shoe (on-load:og old-state)
202 =^ cards shoe (on-load:og old-state)
204 [(deal cards) this]
203 [(deal cards) this]
204+ |^ :: versions %0 and %1 stored the inner state double-vased.
205+ :: fixing this coincided with the 408 type-of-type migration.
206+ ::
207+ ?: ?=([%shoe-app * ?(%0 %1) *] q.old-state)
208+ =| old-outer=state-any
209+ =^ old-inner old-outer
205− =^ old-inner state +:!<([%shoe-app vase state-0] old-state)
210+ +:!<([%shoe-app vase:h136 state-any] old-state)
206− =^ cards shoe (on-load:og old-inner)
211+ =^ caz shoe (on-load:og (next-vase:h136 old-inner))
207− [(deal cards) this]
212+ =^ cuz old-outer
213+ ?. ?=(%0 -.old-outer) [~ old-outer]
214+ (state-0-to-1 old-outer)
215+ =? old-outer ?=(%1 -.old-outer) (state-1-to-2 old-outer)
216+ ?> ?=(%2 -.old-outer)
217+ [(weld cuz (deal caz)) this(state old-outer)]
218+ ::
219+ =+ !<(old=state-any (slot 7 old-state))
220+ ?> ?=(%2 -.old)
221+ =. state old
222+ ::
223+ =^ caz shoe (on-load:og (slot 6 old-state))
224+ [(deal caz) this]
225+ ::
226+ +$ state-any $%(state-2 state-1 state-0)
227+ +$ state-1 _%*(. *state-2 - %1)
228+ +$ state-0 [%0 soles=(map @ta sole-share)]
229+ ++ state-1-to-2 |=(old=state-1 old(- %2))
230+ ++ state-0-to-1
231+ |= old=state-0
232+ ^- (quip card:agent:gall state-1)
233+ :- %+ turn ~(tap in ~(key by soles.old))
234+ |= id=@ta
235+ ^- card:agent:gall
236+ [%give %kick ~[/sole/[id]] ~]
237+ :- %1
238+ %- ~(gas by *(map sole-id sole-share))
239+ %+ murn ~(tap by soles.old)
240+ |= [id=@ta s=sole-share]
241+ (bind (upgrade-id:sole id) (late s))
242+ --
208 ::
243 ::
209 ++ on-poke
244 ++ on-poke
210 |= [=mark =vase]
245 |= [=mark =vase]
@@ -326,19 +361,18 @@
326 ++ on-watch
361 ++ on-watch
327 |= =path
362 |= =path
328 ^- (quip card:agent:gall agent:gall)
363 ^- (quip card:agent:gall agent:gall)
329− ?. ?=([%sole @ ~] path)
364+ ?~ sole-id=(path-to-id:sole path)
330 =^ cards shoe
365 =^ cards shoe
331 (on-watch:og path)
366 (on-watch:og path)
332 [(deal cards) this]
367 [(deal cards) this]
333− =* sole-id i.t.path
334− ?> (can-connect:og sole-id)
368+ ?> (can-connect:og u.sole-id)
335− =. soles (~(put by soles) sole-id *sole-share)
369+ =. soles (~(put by soles) u.sole-id *sole-share)
336 =^ cards shoe
370 =^ cards shoe
337− (on-connect:og sole-id)
371+ (on-connect:og u.sole-id)
338 :_ this
372 :_ this
339 %- deal
373 %- deal
340 :_ cards
374 :_ cards
341− [%shoe [sole-id]~ %sole %pro & dap.bowl "> "]
375+ [%shoe [u.sole-id]~ %sole %pro & dap.bowl "> "]
342 ::
376 ::
343 ++ on-leave
377 ++ on-leave
344 |= =path
378 |= =path
@@ -388,23 +422,25 @@
388 =/ cobs=(list [wid=@ud (list tape)])
422 =/ cobs=(list [wid=@ud (list tape)])
389 (turn cows col-as-lines)
423 (turn cows col-as-lines)
390 =+ [lin=0 any=|]
424 =+ [lin=0 any=|]
425+ =+ len=(lent cobs)
391 =| fez=(list sole-effect)
426 =| fez=(list sole-effect)
392 |- ^+ fez
427 |- ^+ fez
393− =; out=tape
428+ =; [line=(list tape) end=@ud]
394 :: done when we're past the end of all columns
429 :: done when we're past the end of all columns
395 ::
430 ::
396− ?: (levy out (cury test ' '))
431+ ?: =(len end)
397 (flop fez)
432 (flop fez)
398 =; fec=sole-effect
433 =; fec=sole-effect
399 $(lin +(lin), fez [fec fez])
434 $(lin +(lin), fez [fec fez])
435+ =/ out=tape
436+ (zing (join " " line))
400 ?. bold txt+out
437 ?. bold txt+out
401 klr+[[`%br ~ ~]^[(crip out)]~]~
438 klr+[[`%br ~ ~]^[(crip out)]~]~
402− %+ roll cobs
439+ %^ spin cobs 0
403− |= [[wid=@ud lines=(list tape)] out=tape]
440+ |= [[wid=@ud lines=(list tape)] empty=@ud]
404− %+ weld out
405− %+ weld ?~(out "" " ")
406 =+ l=(swag [lin 1] lines)
441 =+ l=(swag [lin 1] lines)
442+ ?^ l [i.l empty]
407− ?^(l i.l (reap wid ' '))
443+ [(reap wid ' ') +(empty)]
408 ::
444 ::
409 ++ col-as-lines
445 ++ col-as-lines
410 |= [wid=@ud col=dime]
446 |= [wid=@ud col=dime]
base-desk/lib/sink.hoonnew file+26
@@ -0,0 +1,26 @@
1+/+ noun-diff
2+|%
3+++ clog
4+ |$ [stut]
5+ $%
6+ [%flush stut]
7+ [%drain patch:noun-diff]
8+ ==
9+++ sink
10+ |* pats=(list path)
11+ |* stat=*
12+ |@
13+ ++ sync
14+ |= [stat=_stat]
15+ ^- [card:agent:gall _..sync]
16+ =/ dif
17+ %+ diff:noun-diff ^stat stat
18+ :-
19+ [%give %fact pats %noun !>(^-((clog) [%drain dif]))]
20+ ..sync(stat stat)
21+ ++ paths pats
22+ ++ flush
23+ ^- card:agent:gall
24+ [%give %fact pats %noun !>(^-((clog) [%flush stat]))]
25+ --
26+--
base-desk/lib/sole.hoon+24
@@ -136,4 +136,28 @@
136 =+ dat=(transmute [%mor leg] [%ins pos `@c`0])
136 =+ dat=(transmute [%mor leg] [%ins pos `@c`0])
137 ?> ?=(%ins -.dat)
137 ?> ?=(%ins -.dat)
138 p.dat
138 p.dat
139+::
140+::
141+++ path-to-id
142+ |= =path
143+ ^- (unit sole-id)
144+ ?. ?=([%sole @ ?(~ [@ ~])] path) ~
145+ ?~ who=(slaw %p i.t.path) ~
146+ `[u.who ?~(t.t.path %$ i.t.t.path)]
147+::
148+++ id-to-path
149+ |= sole-id
150+ ^- path
151+ ::TODO this whole "no empty path ending" business feels icky.
152+ :: do we want default session to be ~.~ ?
153+ :: concern here is that outsiders cannot subscribe to the default
154+ :: session, because /sole/~zod/ isn't a valid path...
155+ [%sole (scot %p who) ?~(ses ~ /[ses])]
156+::
157+++ upgrade-id
158+ |= old=@ta
159+ ^- (unit sole-id)
160+ %+ rush old
161+ %+ cook (late %$)
162+ ;~(pfix (jest 'drum_~') fed:ag)
139--
163--
base-desk/lib/sss.hoonnew file+445
@@ -0,0 +1,445 @@
1+/- *sss
2+/+ *mip
3+::
4+|%
5+++ mk-subs :: Create sub-map.
6+ |* [=(lake) paths=mold]
7+ -:+6:(da lake paths)
8+::
9+++ mk-pubs :: Create pub-map.
10+ |* [=(lake) paths=mold]
11+ -:+6:(du lake paths)
12+::
13+++ mk-mar :: Create mar.
14+ |* =(lake)
15+ |_ =(response:poke lake *)
16+ ++ grow
17+ |%
18+ ++ noun response
19+ --
20+ ++ grab
21+ |%
22+ ++ noun (response:poke lake *)
23+ --
24+ ++ grad %noun
25+ --
26+++ fled :: Like +sped but head is a path.
27+ |= vax=vase
28+ ^- vase
29+ :_ q.vax
30+ %- ~(play ut p.vax)
31+ =- [%wtgr [%wtts - [%& 2]~] [%$ 1]]
32+ =/ pax ~| %path-none ;;(path -.q.vax)
33+ |- ^- spec
34+ ?~ pax [%base %null]
35+ [%bccl ~[[%leaf %ta -.pax] $(pax +.pax)]]
36+::
37+++ zoom |= =noun ~| %need-path $/sss/;;(path noun)
38+::
39+++ da :: Manage subscriptions.
40+ |* [=(lake) paths=mold]
41+ =>
42+ |%
43+ +$ from (on-rock:poke lake paths)
44+ +$ into (response:poke lake paths)
45+ +$ result (request:poke paths)
46+ +$ fail [paths ship dude]
47+ +$ flow [=aeon stale=_| fail=_| =rock:lake]
48+ +$ subs [%0 (map [ship dude paths] (unit flow))]
49+ --
50+ |= $: sub=subs
51+ =bowl:gall
52+ result-type=type
53+ on-rock-type=type
54+ fail-type=type
55+ ==
56+ => .(sub +.sub)
57+ |%
58+ ++ surf :: Subscribe to [ship dude path].
59+ |= which=[ship dude paths]
60+ ^- (quip card:agent:gall subs)
61+ ?+ flow=(~(get by sub) which) `0/sub
62+ ~ [~[(pine which)] 0/(~(put by sub) which ~)]
63+ [~ ~] [~[(pine which)] 0/sub]
64+ [~ ~ [* %& * *]] [~[(pine which)] 0/sub]
65+ ==
66+ ++ quit (corl (lead %0) ~(del by sub)) :: Unsub from [ship dude path].
67+ ++ read :: See current subscribed states.
68+ ^- (map [ship dude paths] [stale=? fail=? =rock:lake])
69+ %- malt %+ murn ~(tap by sub)
70+ |= [key=[ship dude paths] val=(unit flow)]
71+ ?~ val ~
72+ `[key +.u.val]
73+ :: :: Check poke-ack for errors.
74+ :: :: If an %sss-on-rock poke nacks,
75+ ++ chit :: that state is flagged as failed.
76+ |= [[aeon=term ship=term dude=term path=paths] =sign:agent:gall]
77+ ^- subs
78+ :- %0
79+ ?> ?=(%poke-ack -.sign)
80+ ?~ p.sign sub
81+ %+ ~(jab by sub) [(slav %p ship) dude path]
82+ |= (unit flow)
83+ =/ =flow (need +<)
84+ ?> =(aeon.flow (slav %ud aeon))
85+ `flow(fail &)
86+ :: :: Check poke-ack for errors.
87+ :: :: If a scry request nacks,
88+ ++ tell :: that state is flagged as stale.
89+ |= [[ship=term =dude aeon=term path=paths] =sign:agent:gall]
90+ ^- (quip card:agent:gall subs)
91+ ?> ?=(%poke-ack -.sign)
92+ ?~ p.sign `0/sub
93+ =/ current [ship=(slav %p ship) dude=dude path=path]
94+ ?+ flow=(~(get by sub) current) `0/sub
95+ [~ ~ *]
96+ =. stale.u.u.flow &
97+ :_ 0/(~(put by sub) current u.flow)
98+ ~[(on-rock-poke fake=& current u.u.flow ~)]
99+ ::
100+ [~ ~]
101+ :_ 0/(~(del by sub) current) :_ ~
102+ :* %pass (zoom surf-fail/aeon/ship/dude/path)
103+ %agent [our dap]:bowl
104+ %poke %sss-surf-fail fail-type ^- fail
105+ [path ship dude]:current
106+ ==
107+ ==
108+ ++ apply :: Handle response from publisher.
109+ |= res=(response:poke lake paths)
110+ ^- (quip card:agent:gall subs)
111+ =* current [src.bowl dude.res path.res]
112+ =/ old=flow (fall (~(got by sub) current) *flow)
113+ ?: ?=(%tomb what.res)
114+ =/ =flow old(stale &)
115+ :_ 0/(~(put by sub) current `flow) :_ ~
116+ (on-rock-poke fake=& current flow ~)
117+ ::
118+ =/ [wave=(unit wave:lake) new=(unit flow)]
119+ ?- what.res
120+ %rock ?: (lte aeon.res aeon.old) [~ ~]
121+ [~ `[aeon.res | | rock.res]]
122+ %wave ?: (lte aeon.res aeon.old) [~ ~]
123+ ?> =(aeon.res +(aeon.old))
124+ [`wave.res `[aeon.res | | (wash:lake rock.old wave.res)]]
125+ ==
126+ ?~ new `0/sub
127+ :_ 0/(~(put by sub) current new) :_ ~
128+ (on-rock-poke fake=& current u.new wave)
129+ ::
130+ ++ handle-fake-on-rock
131+ |= =(on-rock:poke lake paths)
132+ ^- (list card:agent:gall)
133+ ?~ flow=(~(get by sub) [src from path]:on-rock) ~
134+ ?~ u.flow ~
135+ ?. =([stale fail rock]:u.u.flow [stale fail rock]:on-rock) ~
136+ ~[(on-rock-poke fake=| [src from path]:on-rock u.u.flow wave.on-rock)]
137+ ::
138+ :: Non-public facing arms below
139+ ::
140+ ++ pine
141+ |= [who=ship which=dude where=paths]
142+ ^- card:agent:gall
143+ :* %pass (zoom scry-request/(scot %p who)^which^where)
144+ %agent [who which]
145+ %poke sss-to-pub/[result-type `result`[where dap.bowl]]
146+ ==
147+ ++ on-rock-poke
148+ |= [fake=? [=ship =dude path=paths] flow wave=(unit wave:lake)]
149+ ^- card:agent:gall
150+ :* %pass %+ zoom ?:(fake %fake %on-rock)
151+ (scot %ud aeon)^(scot %p ship)^dude^path
152+ %agent [our dap]:bowl
153+ %poke ?:(fake %sss-fake-on-rock %sss-on-rock)
154+ on-rock-type `from`[path ship dude stale fail rock wave]
155+ ==
156+ --
157+++ du :: Manage publications.
158+ |* [=(lake) paths=mold]
159+ =>
160+ |%
161+ +$ into (request:poke paths)
162+ +$ result (response:poke lake paths)
163+ +$ rule $~ [`5 5]
164+ [horizon=(unit @ud) frequency=@ud] :: Retention policy
165+ +$ tide
166+ $: rok=((mop aeon rock:lake) gte)
167+ wav=((mop aeon wave:lake) lte)
168+ rul=rule
169+ mem=(jug ship dude)
170+ ==
171+ +$ buoy
172+ $: tid=$~(*tide $@(aeon tide))
173+ alo=(unit (set ship))
174+ ==
175+ ++ pubs
176+ =< $>(%1 versioned)
177+ |%
178+ ++ update
179+ |= =versioned
180+ ^- pubs
181+ ?- -.versioned
182+ %1 versioned
183+ %0
184+ :- %1
185+ %- ~(run by +.versioned)
186+ |= =buoy-0:^versioned
187+ ^- buoy
188+ %= buoy-0
189+ tid
190+ ?@ tid.buoy-0 tid.buoy-0
191+ ^- tide
192+ %= tid.buoy-0
193+ rocks.rul
194+ ?: =(waves.rul.tid.buoy-0 0) ~
195+ `(mul [+(rocks) waves]:rul.tid.buoy-0)
196+ ::
197+ mem
198+ ^- (jug ship dude)
199+ %- ~(run by mem.tid.buoy-0)
200+ |= =(map dude @da)
201+ ^- (set dude)
202+ ~(key by map)
203+ ==
204+ ==
205+ ==
206+ ++ versioned
207+ =< $% [%0 (map paths buoy-0)]
208+ [%1 (map paths buoy)]
209+ ==
210+ |%
211+ +$ buoy-0
212+ $: tid=$~(*tide-0 $@(aeon tide-0))
213+ alo=(unit (set ship))
214+ ==
215+ +$ tide-0
216+ $: rok=((mop aeon rock:lake) gte)
217+ wav=((mop aeon wave:lake) lte)
218+ rul=[rocks=@ud waves=@ud]
219+ mem=(mip ship dude @da)
220+ ==
221+ --
222+ --
223+ --
224+ |= [pub=versioned:pubs =bowl:gall result-type=type]
225+ => .(pub +:(update:pubs pub))
226+ =* rok ((on aeon rock:lake) gte)
227+ =* wav ((on aeon wave:lake) lte)
228+ |%
229+ ++ rule :: Set new retention policy.
230+ |= [path=paths =^rule]
231+ ^- pubs
232+ :- %1
233+ %- fall :_ (~(put by pub) path %*(. *$<(aeon buoy) rul.tid rule))
234+ %- mole |.
235+ %+ ~(jab by pub) path
236+ |= =buoy
237+ ?@ tid.buoy buoy
238+ buoy(tid (form tid.buoy(rul rule)))
239+ ::
240+ ++ wipe :: Create new rock and wipe rest.
241+ |= path=paths
242+ ^- pubs
243+ :- %1
244+ %+ ~(jab by pub) path
245+ |= =buoy
246+ ?@ tid.buoy buoy
247+ %* . buoy(tid (form tid.buoy(rul [`0 1])))
248+ rul.tid rul.tid.buoy
249+ wav.tid ~
250+ ==
251+ ++ give :: Give a wave on a path.
252+ |= [path=paths =wave:lake]
253+ ^- (quip card:agent:gall pubs)
254+ ?~ ((soft ^path) path) ~| %need-path !!
255+ =/ buoy (~(gut by pub) path *buoy)
256+ =? buoy ?=(@ tid.buoy)
257+ %*(. buoy(tid *tide) rok.tid (put:rok ~ +(tid.buoy) *rock:lake))
258+ ?> ?=(^ tid.buoy)
259+ =* tide tid.buoy
260+ =/ next=aeon +((latest tide))
261+ :- %- zing
262+ %+ turn ~(tap by mem.tide)
263+ |= [=ship =(set dude)]
264+ %+ turn ~(tap in set)
265+ |= =dude
266+ (send wave/[next wave] ship dude path)
267+ :- %1
268+ %+ ~(put by pub) path
269+ =/ last=[=aeon =rock:lake] (fall (pry:rok rok.tide) *[key val]:rok)
270+ =. wav.tide (put:wav wav.tide next wave)
271+ =. mem.tide ~
272+ ?. =(next (add aeon.last frequency.rul.tide)) buoy
273+ buoy(tid (form tide))
274+ ::
275+ ++ fork :: Fork a pub into an empty path.
276+ |= [from=paths to=paths]
277+ ^- pubs
278+ :- %1
279+ ?< (~(has by pub) to)
280+ (~(put by pub) to (~(got by pub) from))
281+ ::
282+ ++ copy :: Fork a sub into an empty path.
283+ |= [sub=_(mk-subs lake *) from=[ship dude *] to=paths]
284+ ^- pubs
285+ :- %1
286+ ?< (~(has by pub) to)
287+ %+ ~(put by pub) to
288+ %* . *$<(aeon buoy)
289+ rok.tid (put:rok ~ [aeon rock]:(need (~(got by +:sub) from)))
290+ ==
291+ ::
292+ ++ perm :: Change permissions with gate.
293+ |= [where=(list paths) diff=$-((unit (set ship)) (unit (set ship)))]
294+ ^- (quip card:agent:gall pubs)
295+ %+ edit where
296+ |= =buoy
297+ =/ new=_alo.buoy (diff alo.buoy)
298+ ?@ tid.buoy buoy(alo new)
299+ %= buoy
300+ alo new
301+ mem.tid ?~ new mem.tid.buoy
302+ %. mem.tid.buoy
303+ ~(int by (malt (turn ~(tap in u.new) (late *(set @)))))
304+ ==
305+ ++ public (curr perm _~) :: Make list of paths public.
306+ ++ secret (curr perm _`~) :: Make list of paths secret.
307+ :: :: Block ships from paths.
308+ ++ block :: No-ops on public paths.
309+ |= [who=(list ship) whence=(list paths)]
310+ ^- (quip card:agent:gall pubs)
311+ %+ perm whence
312+ |= old=(unit (set ship))
313+ ?~ old ~ `(~(dif in u.old) (sy who))
314+ :: :: Allow ships to paths.
315+ ++ allow :: Any public paths will no-op.
316+ |= [who=(list ship) where=(list paths)]
317+ ^- (quip card:agent:gall pubs)
318+ %+ perm where
319+ |= old=(unit (set ship))
320+ ?~ old ~ `(~(gas in u.old) who)
321+ :: :: Kill a list of paths, i.e. tell
322+ ++ kill :: subs to not expect updates.
323+ (curr edit |=(=buoy buoy(tid (latest tid.buoy))))
324+ :: :: Reopen list of killed paths.
325+ ++ read :: See current published states.
326+ ^- (map paths [allowed=(unit (set ship)) =rock:lake])
327+ %- malt %+ murn ~(tap by pub)
328+ |= [path=paths =buoy]
329+ ^- (unit [paths (unit (set ship)) rock:lake])
330+ ?@ tide=tid.buoy ~
331+ :^ ~ path alo.buoy =< rock
332+ =/ snap=[=aeon =rock:lake] (fall (pry:rok rok.tide) *[key val]:rok)
333+ %+ roll (tap:wav (lot:wav wav.tide `aeon.snap ~))
334+ |= [[=aeon =wave:lake] =_snap]
335+ ?. =(aeon +(aeon.snap)) snap
336+ [aeon (wash:lake rock.snap wave)]
337+ ::
338+ ++ apply :: Handle request from subscriber.
339+ |= req=(request:poke paths)
340+ ^- (quip card:agent:gall pubs)
341+ =/ =buoy (~(gut by pub) path.req *buoy)
342+ ?< &(?=(^ alo.buoy) !(~(has in u.alo.buoy) src.bowl))
343+ ?@ tid.buoy
344+ :_ 1/pub :_ ~
345+ (send tomb/~ src.bowl dude.req path.req)
346+ :_ 1/pub :_ ~
347+ =/ last (fall (pry:rok rok.tid.buoy) *[=key =val]:rok)
348+ (send rock/last src.bowl dude.req path.req)
349+ ::
350+ ++ tell
351+ |= [[ship=term =dude aeon=term path=paths] =sign:agent:gall]
352+ ^- (quip card:agent:gall pubs)
353+ ?> ?=(%poke-ack -.sign)
354+ ?^ p.sign `1/pub
355+ =/ =buoy (~(gut by pub) path *buoy)
356+ ?< &(?=(^ alo.buoy) !(~(has in u.alo.buoy) src.bowl))
357+ ?@ tid.buoy
358+ :_ 1/pub :_ ~
359+ (send tomb/~ src.bowl dude path)
360+ ::
361+ => .(aeon +((slav %ud aeon)))
362+ ?^ dat=(get:wav wav.tid.buoy aeon)
363+ :_ 1/pub :_ ~
364+ (send wave/[aeon u.dat] src.bowl dude path)
365+ =/ last (fall (pry:rok rok.tid.buoy) [=key =val]:rok)
366+ ?: (lte aeon key.last)
367+ :_ 1/pub :_ ~
368+ (send rock/last src.bowl dude path)
369+ :- ~
370+ :- %1
371+ %+ ~(put by pub) path
372+ %= buoy
373+ mem.tid (~(put ju mem.tid.buoy) src.bowl dude)
374+ ==
375+ ::
376+ :: Non-public facing arms below
377+ ::
378+ ++ send
379+ |= [payload=_|2:*(response:poke lake paths) =ship =dude path=paths]
380+ ^- card:agent:gall
381+ =* mark (cat 3 %sss- name:lake)
382+ =/ callback=^path
383+ ?: ?=(%tomb what.payload) (zoom tomb-response/(scot %p ship)^dude^path)
384+ (zoom scry-response/(scot %p ship)^dude^(scot %ud aeon.payload)^path)
385+ :* %pass callback
386+ %agent [ship dude]
387+ %poke mark result-type ^- (response:poke lake paths)
388+ [path dap.bowl payload]
389+ ==
390+ ++ latest
391+ |= =$@(aeon tide)
392+ ^- aeon
393+ ?@ tide tide
394+ %+ max (fall (bind (pry:rok rok.tide) head) 0)
395+ (fall (bind (ram:wav wav.tide) head) 0)
396+ ::
397+ ++ edit
398+ |= [ps=(list paths) edit=$-(buoy buoy)]
399+ ^- (quip card:agent:gall pubs)
400+ %- ~(rep in (sy ps))
401+ |= [path=paths caz=(list card:agent:gall) %1 =_pub]
402+ ?~ old=(~(get by pub) path) [caz 1/pub]
403+ =/ new=buoy (edit u.old)
404+ :_ 1/(~(put by pub) path new)
405+ %- weld :_ caz
406+ ^- (list card:agent:gall)
407+ ?@ tid.u.old ~
408+ ?@ tid.new
409+ %- zing
410+ %+ turn ~(tap by mem.tid.u.old)
411+ |= [=ship =(set dude)]
412+ (turn ~(tap in set) |=(=dude (send tomb/~ ship dude path)))
413+ ?~ alo.new ~
414+ =/ new-alo=(jug ship dude)
415+ (malt (turn ~(tap in u.alo.new) (late *(set @))))
416+ %- zing
417+ %+ turn ~(tap by (~(dif by mem.tid.u.old) new-alo))
418+ |= [=ship =(set dude)]
419+ (turn ~(tap in set) |=(=dude (send tomb/~ ship dude path)))
420+ ::
421+ ++ form
422+ |= =tide
423+ ^+ tide
424+ =/ max-rock=[=aeon =rock:lake] (fall (pry:rok rok.tide) *[key val]:rok)
425+ =/ max-wave (fall (bind (ram:wav wav.tide) head) 0)
426+ =? rok.tide :: Create new rock.
427+ ?& !=(frequency.rul.tide 0)
428+ (gte max-wave (add aeon.max-rock frequency.rul.tide))
429+ ==
430+ %+ put:rok rok.tide
431+ %+ roll (tab:wav wav.tide `aeon.max-rock max-wave)
432+ |: [*[now=aeon =wave:lake] `[prev=aeon =rock:lake]`max-rock]
433+ ~| %aeon-awry
434+ ?> =(now +(prev))
435+ [now (wash:lake rock wave)]
436+ =. rok.tide
437+ ?~ horizon.rul.tide :: Only keep genesis and latest.
438+ (gas:rok ~ (murn ~[(ram:rok rok.tide) (pry:rok rok.tide)] same))
439+ %^ lot:rok rok.tide :: Delete beyond horizon.
440+ ~
441+ (mole |.((sub max-wave (max [u.horizon frequency]:rul.tide))))
442+ ~| %rock-zero
443+ tide(wav (lot:wav wav.tide (bind (ram:rok rok.tide) |=([r=@ *] (dec r))) ~))
444+ --
445+--
base-desk/lib/story.hoon+11−35
@@ -38,41 +38,17 @@
38::
38::
39:: Canonical textual representation
39:: Canonical textual representation
40::
40::
41−++ tako-to-text
41+++ chapter-to-text
42− |= [=tako:clay]
42+ |= [=tako:clay =proses]
43− ^- tape
43+ ^- wain
44− "commit: {<`@uv`tako>}\0a"
44+ :- (crip "commit: {<`@uv`tako>}")
45−::
45+ %- zing
46−++ proses-to-text
46+ %+ join `wain`~['|||']
47− |= [=proses]
48− ^- tape
49− =/ proses-list=(list prose) ~(tap in proses)
47+ %+ turn ~(tap in proses)
50− ?: ?=(~ proses-list) ""
51− ?: ?=([prose ~] proses-list)
52− (prose-to-text i.proses-list)
53− %- tail
54− %^ spin `(list prose)`t.proses-list
55− (prose-to-text i.proses-list)
56− |= [prz=prose state=tape]
48+ |= prose
57− ^- [prose tape]
49+ ^- wain
58− :- prz
50+ %- to-wain:format
59− ;: welp
51+ (rap 3 title '\0a\0a' body ~)
60− state
61− "|||"
62− "\0a"
63− (prose-to-text prz)
64− ==
65−::
66−++ prose-to-text
67− |= prz=prose
68− =/ [title=@t body=@t] prz
69− ^- tape
70− ;: welp
71− "{(trip title)}"
72− "\0a\0a"
73− "{(trip body)}"
74− "\0a"
75− ==
76::
52::
77:: Parsers
53:: Parsers
78::
54::
base-desk/lib/strand.hoon+1−188
@@ -1,188 +1 @@
1−|%
1+rand
2−+$ card card:agent:gall
3−+$ input
4− $% [%poke =cage]
5− [%sign =wire =sign-arvo]
6− [%agent =wire =sign:agent:gall]
7− [%watch =path]
8− ==
9−+$ strand-input [=bowl in=(unit input)]
10−+$ tid @tatid
11−+$ bowl
12− $: our=ship
13− src=ship
14− tid=tid
15− mom=(unit tid)
16− wex=boat:gall
17− sup=bitt:gall
18− eny=@uvJ
19− now=@da
20− byk=beak
21− ==
22−::
23−:: cards: cards to send immediately. These will go out even if a
24−:: later stage of the computation fails, so they shouldn't have
25−:: any semantic effect on the rest of the system.
26−:: Alternately, they may record an entry in contracts with
27−:: enough information to undo the effect if the computation
28−:: fails.
29−:: wait: don't move on, stay here. The next sign should come back
30−:: to this same callback.
31−:: skip: didn't expect this input; drop it down to be handled
32−:: elsewhere
33−:: cont: continue computation with new callback.
34−:: fail: abort computation; don't send effects
35−:: done: finish computation; send effects
36−::
37−++ strand-output-raw
38− |* a=mold
39− $~ [~ %done *a]
40− $: cards=(list card)
41− $= next
42− $% [%wait ~]
43− [%skip ~]
44− [%cont self=(strand-form-raw a)]
45− [%fail err=(pair term tang)]
46− [%done value=a]
47− ==
48− ==
49−::
50−++ strand-form-raw
51− |* a=mold
52− $-(strand-input (strand-output-raw a))
53−::
54−:: Abort strand computation with error message
55−::
56−++ strand-fail
57− |= err=(pair term tang)
58− |= strand-input
59− [~ %fail err]
60−::
61−:: Asynchronous transcaction monad.
62−::
63−:: Combo of four monads:
64−:: - Reader on input
65−:: - Writer on card
66−:: - Continuation
67−:: - Exception
68−::
69−++ strand
70− |* a=mold
71− |%
72− ++ output (strand-output-raw a)
73− ::
74− :: Type of an strand computation.
75− ::
76− ++ form (strand-form-raw a)
77− ::
78− :: Monadic pure. Identity computation for bind.
79− ::
80− ++ pure
81− |= arg=a
82− ^- form
83− |= strand-input
84− [~ %done arg]
85− ::
86− :: Monadic bind. Combines two computations, associatively.
87− ::
88− ++ bind
89− |* b=mold
90− |= [m-b=(strand-form-raw b) fun=$-(b form)]
91− ^- form
92− |= input=strand-input
93− =/ b-res=(strand-output-raw b)
94− (m-b input)
95− ^- output
96− :- cards.b-res
97− ?- -.next.b-res
98− %wait [%wait ~]
99− %skip [%skip ~]
100− %cont [%cont ..$(m-b self.next.b-res)]
101− %fail [%fail err.next.b-res]
102− %done [%cont (fun value.next.b-res)]
103− ==
104− ::
105− :: The strand monad must be evaluted in a particular way to maintain
106− :: its monadic character. +take:eval implements this.
107− ::
108− ++ eval
109− |%
110− :: Indelible state of a strand
111− ::
112− +$ eval-form
113− $: =form
114− ==
115− ::
116− :: Convert initial form to eval-form
117− ::
118− ++ from-form
119− |= =form
120− ^- eval-form
121− form
122− ::
123− :: The cases of results of +take
124− ::
125− +$ eval-result
126− $% [%next ~]
127− [%fail err=(pair term tang)]
128− [%done value=a]
129− ==
130− ::
131− ++ validate-mark
132− |= [in=* =mark =bowl]
133− ^- cage
134− =+ .^ =dais:clay %cb
135− /(scot %p our.bowl)/[q.byk.bowl]/(scot %da now.bowl)/[mark]
136− ==
137− =/ res (mule |.((vale.dais in)))
138− ?: ?=(%| -.res)
139− ~|(%spider-mark-fail (mean leaf+"spider: ames vale fail {<mark>}" p.res))
140− [mark p.res]
141− ::
142− :: Take a new sign and run the strand against it
143− ::
144− ++ take
145− :: cards: accumulate throughout recursion the cards to be
146− :: produced now
147− =| cards=(list card)
148− |= [=eval-form =strand-input]
149− ^- [[(list card) =eval-result] _eval-form]
150− =* take-loop $
151− =. in.strand-input
152− ?~ in.strand-input ~
153− =/ in u.in.strand-input
154− ?. ?=(%agent -.in) `in
155− ?. ?=(%fact -.sign.in) `in
156− ::
157− :- ~
158− :+ %agent wire.in
159− [%fact (validate-mark q.q.cage.sign.in p.cage.sign.in bowl.strand-input)]
160− :: run the strand callback
161− ::
162− =/ =output (form.eval-form strand-input)
163− :: add cards to cards
164− ::
165− =. cards
166− %+ welp
167− cards
168− :: XX add tag to wires?
169− cards.output
170− :: case-wise handle next steps
171− ::
172− ?- -.next.output
173− %wait [[cards %next ~] eval-form]
174− %skip [[cards %next ~] eval-form]
175− %fail [[cards %fail err.next.output] eval-form]
176− %done [[cards %done value.next.output] eval-form]
177− %cont
178− :: recurse to run continuation with initialization input
179− ::
180− %_ take-loop
181− form.eval-form self.next.output
182− strand-input [bowl.strand-input ~]
183− ==
184− ==
185− --
186− --
187−--
188−::
base-desk/lib/strandio.hoon+170−13
@@ -73,21 +73,23 @@
73 ^- form:m
73 ^- form:m
74 |= tin=strand-input:strand
74 |= tin=strand-input:strand
75 ?+ in.tin `[%skip ~]
75 ?+ in.tin `[%skip ~]
76+ ~
76− ~ `[%wait ~]
77+ `[%wait ~]
78+ ::
77 [~ %poke @ *]
79 [~ %poke @ *]
78 ?. =(mark p.cage.u.in.tin)
80 ?. =(mark p.cage.u.in.tin)
79 `[%skip ~]
81 `[%skip ~]
80 `[%done q.cage.u.in.tin]
82 `[%done q.cage.u.in.tin]
81 ==
83 ==
82::
84::
83−::
84−::
85++ take-sign-arvo
85++ take-sign-arvo
86 =/ m (strand ,[wire sign-arvo])
86 =/ m (strand ,[wire sign-arvo])
87 ^- form:m
87 ^- form:m
88 |= tin=strand-input:strand
88 |= tin=strand-input:strand
89 ?+ in.tin `[%skip ~]
89 ?+ in.tin `[%skip ~]
90+ ~
90− ~ `[%wait ~]
91+ `[%wait ~]
92+ ::
91 [~ %sign *]
93 [~ %sign *]
92 `[%done [wire sign-arvo]:u.in.tin]
94 `[%done [wire sign-arvo]:u.in.tin]
93 ==
95 ==
@@ -180,6 +182,53 @@
180 `[%done ~]
182 `[%done ~]
181 `[%fail %timer-error u.error.sign-arvo.u.in.tin]
183 `[%fail %timer-error u.error.sign-arvo.u.in.tin]
182 ==
184 ==
185+++ take-whey
186+ |= =wire
187+ =/ m (strand ,[spar:ames fragment-size=@ud num-fragments=@ud])
188+ ^- form:m
189+ |= tin=strand-input:strand
190+ ?+ in.tin `[%skip ~]
191+ ~ `[%wait ~]
192+ ::
193+ [~ %sign * %ames %sage sage=*]
194+ ?. =(wire wire.u.in.tin)
195+ `[%skip ~]
196+ =/ =sage:mess:ames sage.sign-arvo.u.in.tin
197+ :^ ~ %done spar=p.sage
198+ ?~ q.sage
199+ [boq=13 tot=0]
200+ =< [boq tot]
201+ ;;([%whey boq=@ud tot=@ud] q.sage)
202+ ==
203+::
204+++ take-sage
205+ |= =wire
206+ =/ m (strand ,sage:mess:ames)
207+ ^- form:m
208+ |= tin=strand-input:strand
209+ ?+ in.tin `[%skip ~]
210+ ~ `[%wait ~]
211+ ::
212+ [~ %sign * %ames %sage sage=*]
213+ ?. =(wire wire.u.in.tin)
214+ `[%skip ~]
215+ `[%done sage.sign-arvo.u.in.tin]
216+ ==
217+:: XX deprecate in 409k
218+::
219+++ take-near
220+ |= =wire
221+ =/ m (strand ,[spar:ames (unit (unit page))])
222+ ^- form:m
223+ |= tin=strand-input:strand
224+ ?+ in.tin `[%skip ~]
225+ ~ `[%wait ~]
226+ ::
227+ [~ %sign * %ames %near ^ *]
228+ ?. =(wire wire.u.in.tin)
229+ `[%skip ~]
230+ `[%done +>.sign-arvo.u.in.tin]
231+ ==
183::
232::
184++ take-poke-ack
233++ take-poke-ack
185 |= =wire
234 |= =wire
@@ -315,6 +364,78 @@
315 ;< ~ bind:m (send-wait until)
364 ;< ~ bind:m (send-wait until)
316 (take-wake `until)
365 (take-wake `until)
317::
366::
367+++ mass
368+ =/ m (strand ,(list quac:dill))
369+ ^- form:m
370+ =/ =card:agent:gall [%pass /mass %arvo %d %mass ~]
371+ ;< ~ bind:m (send-raw-card card)
372+ ;< quz=(list quac:dill) bind:m take-meme
373+ (pure:m quz)
374+::
375+++ take-meme
376+ =/ m (strand ,(list quac:dill))
377+ ^- form:m
378+ |= tin=strand-input:strand
379+ ?+ in.tin `[%skip ~]
380+ ~ `[%wait ~]
381+ [~ %sign [%mass ~] %dill %meme *]
382+ `[%done p.sign-arvo.u.in.tin]
383+ ==
384+::
385+++ keen
386+ |= [=wire =spar:ames sec=(unit [@ @])]
387+ =/ m (strand ,~)
388+ ^- form:m
389+ (send-raw-card %pass wire %arvo %a %keen sec spar)
390+::
391+++ chum
392+ |= [=wire =spar:ames]
393+ =/ m (strand ,~)
394+ ^- form:m
395+ (send-raw-card %pass wire %arvo %a %chum spar)
396+::
397+++ keen-shut
398+ |= [=wire =spar:ames]
399+ =/ m (strand ,~)
400+ ^- form:m
401+ (send-raw-card %pass wire %keen & spar)
402+::
403+++ yawn
404+ |= [=wire =spar:ames]
405+ =/ m (strand ,~)
406+ ^- form:m
407+ (send-raw-card %pass wire %arvo %a %yawn spar)
408+::
409+++ whey
410+ |= [=wire boq=@ud =spar:ames]
411+ =/ m (strand ,~)
412+ ^- form:m
413+ :: encrypted using %chum namespace
414+ ::
415+ ;< our=@p bind:m get-our
416+ =. path.spar
417+ [%a %x '1' %$ %whey (scot %ud boq) (scot %p our) path.spar]
418+ (chum wire spar)
419+::
420+++ meta
421+ |= [=wire =spar:ames]
422+ =/ m (strand ,~)
423+ ^- form:m
424+ ?+ path.spar !!
425+ $% [%ames bone=@ %cork ~]
426+ [%ames bone=@ %sent seq=@ ~]
427+ [%flow bone=@ =dire:ames %cork ~]
428+ [?(%ames %flow) bone=@ =dire:ames ?(%sent %loads %naxp %acked) seq=@ ~]
429+ ==
430+ :: encrypted using %chum namespace
431+ ::
432+ ;< our=@p bind:m get-our
433+ %- send-raw-card
434+ :* %pass wire %arvo %a %chum
435+ spar(path [%a %x '1' %$ %meta (scot %p our) path.spar])
436+ ==
437+ ==
438+::
318++ sleep
439++ sleep
319 |= for=@dr
440 |= for=@dr
320 =/ m (strand ,~)
441 =/ m (strand ,~)
@@ -433,7 +554,6 @@
433 %- pure:m
554 %- pure:m
434 ?~ full-file.client-response ''
555 ?~ full-file.client-response ''
435 q.data.u.full-file.client-response
556 q.data.u.full-file.client-response
436−
437::
557::
438++ fetch-cord
558++ fetch-cord
439 |= url=tape
559 |= url=tape
@@ -449,7 +569,7 @@
449 =/ m (strand ,json)
569 =/ m (strand ,json)
450 ^- form:m
570 ^- form:m
451 ;< =cord bind:m (fetch-cord url)
571 ;< =cord bind:m (fetch-cord url)
452− =/ json=(unit json) (de-json:html cord)
572+ =/ json=(unit json) (de:json:html cord)
453 ?~ json
573 ?~ json
454 (strand-fail %json-parse-error ~)
574 (strand-fail %json-parse-error ~)
455 (pure:m u.json)
575 (pure:m u.json)
@@ -474,6 +594,18 @@
474 (pure:m ~)
594 (pure:m ~)
475 ?> =(%vase p.r.u.riot)
595 ?> =(%vase p.r.u.riot)
476 (pure:m (some !<(vase q.r.u.riot)))
596 (pure:m (some !<(vase q.r.u.riot)))
597+::
598+++ build-file-hard
599+ |= [[=ship =desk =case] =spur]
600+ =* arg +<
601+ =/ m (strand ,vase)
602+ ^- form:m
603+ ;< =riot:clay
604+ bind:m
605+ (warp ship desk ~ %sing %a case spur)
606+ ?> ?=(^ riot)
607+ ?> ?=(%vase p.r.u.riot)
608+ (pure:m !<(vase q.r.u.riot))
477:: +build-mark: build a mark definition to a $dais
609:: +build-mark: build a mark definition to a $dais
478::
610::
479++ build-mark
611++ build-mark
@@ -537,7 +669,7 @@
537 (take-writ /warp)
669 (take-writ /warp)
538::
670::
539++ read-file
671++ read-file
540− |= [[=ship =desk =case:clay] =spur]
672+ |= [[=ship =desk =case] =spur]
541 =* arg +<
673 =* arg +<
542 =/ m (strand ,cage)
674 =/ m (strand ,cage)
543 ;< =riot:clay bind:m (warp ship desk ~ %sing %x case spur)
675 ;< =riot:clay bind:m (warp ship desk ~ %sing %x case spur)
@@ -546,13 +678,14 @@
546 (pure:m r.u.riot)
678 (pure:m r.u.riot)
547::
679::
548++ check-for-file
680++ check-for-file
549− |= [[=ship =desk =case:clay] =spur]
681+ |= [[=ship =desk =case] =spur]
550 =/ m (strand ,?)
682 =/ m (strand ,?)
551− ;< =riot:clay bind:m (warp ship desk ~ %sing %x case spur)
683+ ;< =riot:clay bind:m (warp ship desk ~ %sing %u case spur)
552− (pure:m ?=(^ riot))
684+ ?> ?=(^ riot)
685+ (pure:m !<(? q.r.u.riot))
553::
686::
554++ list-tree
687++ list-tree
555− |= [[=ship =desk =case:clay] =spur]
688+ |= [[=ship =desk =case] =spur]
556 =* arg +<
689 =* arg +<
557 =/ m (strand ,(list path))
690 =/ m (strand ,(list path))
558 ;< =riot:clay bind:m (warp ship desk ~ %sing %t case spur)
691 ;< =riot:clay bind:m (warp ship desk ~ %sing %t case spur)
@@ -560,6 +693,14 @@
560 (strand-fail %list-tree >arg< ~)
693 (strand-fail %list-tree >arg< ~)
561 (pure:m !<((list path) q.r.u.riot))
694 (pure:m !<((list path) q.r.u.riot))
562::
695::
696+++ list-desk
697+ |= [[=ship =desk =case] =spur]
698+ =* arg +<
699+ =/ m (strand arch)
700+ ;< =riot:clay bind:m (warp ship desk ~ %sing %y case spur)
701+ ?~ riot
702+ (strand-fail %list-desk >arg< ~)
703+ (pure:m !<(arch q.r.u.riot))
563:: Take Clay read result
704:: Take Clay read result
564::
705::
565++ take-writ
706++ take-writ
@@ -586,6 +727,23 @@
586 (poke [who %hood] %helm-hi !>(~))
727 (poke [who %hood] %helm-hi !>(~))
587 (pure:m ~)
728 (pure:m ~)
588::
729::
730+++ eval-hoon
731+ |= [gen=hoon bez=(list beam)]
732+ =/ m (strand ,vase)
733+ ^- form:m
734+ =/ sut=vase !>(..zuse)
735+ |-
736+ ?~ bez
737+ (pure:m (slap sut gen))
738+ ;< vax=vase bind:m (build-file-hard i.bez)
739+ $(bez t.bez, sut (slop vax sut))
740+::
741+++ send-thread
742+ |= [=bear:khan =shed:khan =wire]
743+ =/ m (strand ,~)
744+ ^- form:m
745+ (send-raw-card %pass wire %arvo %k %lard bear shed)
746+::
589:: Queue on skip, try next on fail %ignore
747:: Queue on skip, try next on fail %ignore
590::
748::
591++ main-loop
749++ main-loop
@@ -751,11 +909,10 @@
751 =/ poke-vase !>(`start-args:spider`[`tid.bowl `tid byk.bowl file args])
909 =/ poke-vase !>(`start-args:spider`[`tid.bowl `tid byk.bowl file args])
752 ;< ~ bind:m (watch-our /awaiting/[tid] %spider /thread-result/[tid])
910 ;< ~ bind:m (watch-our /awaiting/[tid] %spider /thread-result/[tid])
753 ;< ~ bind:m (poke-our %spider %spider-start poke-vase)
911 ;< ~ bind:m (poke-our %spider %spider-start poke-vase)
754− ;< ~ bind:m (sleep ~s0) :: wait for thread to start
755 ;< =cage bind:m (take-fact /awaiting/[tid])
912 ;< =cage bind:m (take-fact /awaiting/[tid])
756 ;< ~ bind:m (take-kick /awaiting/[tid])
913 ;< ~ bind:m (take-kick /awaiting/[tid])
757 ?+ p.cage ~|([%strange-thread-result p.cage file tid] !!)
914 ?+ p.cage ~|([%strange-thread-result p.cage file tid] !!)
758 %thread-done (pure:m %& q.cage)
915 %thread-done (pure:m %& q.cage)
759− %thread-fail (pure:m %| ;;([term tang] q.q.cage))
916+ %thread-fail (pure:m %| !<([term tang] q.cage))
760 ==
917 ==
761--
918--
base-desk/lib/test.hoon+54−1
@@ -38,7 +38,60 @@
38 =/ b (mule a)
38 =/ b (mule a)
39 ?- -.b
39 ?- -.b
40 %| ~
40 %| ~
41+ %& ['expected failure - succeeded' ~]
42+ ==
43+:: +expect-success: kicks a trap, expecting success; returns trace on failure
44+::
45+++ expect-success
46+ |= a=(trap)
47+ ^- tang
48+ =/ b (mule a)
49+ ?- -.b
50+ %& ~
51+ %| ['expected success - failed' p.b]
52+ ==
53+::
54+:: +expect-fail-message: kicks a trap, expecting crash, compares the resulting error message
55+++ expect-fail-message
56+ |= [msg=@t a=(trap)]
57+ ^- tang
58+ =/ b (mule a)
59+ ?- -.b
60+ %| |^
61+ =/ =tang (flatten +.b)
62+ ?: ?=(^ (find (trip msg) tang))
63+ ~
64+ ['expected error message - not found' ~]
65+ ++ flatten
66+ |= tang=(list tank)
67+ =| res=tape
68+ |- ^- tape
69+ ?~ tang res
70+ $(tang t.tang, res (weld ~(ram re i.tang) res))
71+ --
41− %& [leaf+"expected failure - succeeded" ~]
72+ %& ['expected failure - succeeded' ~]
73+ ==
74+:: $a-test-chain: a sequence of tests to be run
75+::
76+:: NB: arms shouldn't start with `test-` so that `-test % ~` runs
77+::
78++$ a-test-chain
79+ $_
80+ |?
81+ ?: =(0 0)
82+ [%& p=*tang]
83+ [%| p=[tang=*tang next=^?(..$)]]
84+:: +run-chain: run a sequence of tests, stopping at first failure
85+::
86+++ run-chain
87+ |= seq=a-test-chain
88+ ^- tang
89+ =/ res $:seq
90+ ?- -.res
91+ %& p.res
92+ %| ?. =(~ tang.p.res)
93+ tang.p.res
94+ $(seq next.p.res)
42 ==
95 ==
43:: +category: prepends a name to an error result; passes successes unchanged
96:: +category: prepends a name to an error result; passes successes unchanged
44::
97::
base-desk/lib/verb.hoon+88−11
@@ -1,6 +1,6 @@
1:: Print what your agent is doing.
1:: Print what your agent is doing.
2::
2::
3−/- verb
3+/- *verb
4::
4::
5|= [loud=? =agent:gall]
5|= [loud=? =agent:gall]
6=| bowl-print=_|
6=| bowl-print=_|
@@ -14,7 +14,10 @@
14 ^- (quip card:agent:gall agent:gall)
14 ^- (quip card:agent:gall agent:gall)
15 %- (print bowl |.("{<dap.bowl>}: on-init"))
15 %- (print bowl |.("{<dap.bowl>}: on-init"))
16 =^ cards agent on-init:ag
16 =^ cards agent on-init:ag
17− [[(emit-event %on-init ~) cards] this]
17+ :_ this
18+ :_ :_ cards
19+ (emit-event %on-init ~)
20+ (emit-event-plus bowl [%on-init ~] cards)
18::
21::
19++ on-save
22++ on-save
20 ^- vase
23 ^- vase
@@ -26,7 +29,10 @@
26 ^- (quip card:agent:gall agent:gall)
29 ^- (quip card:agent:gall agent:gall)
27 %- (print bowl |.("{<dap.bowl>}: on-load"))
30 %- (print bowl |.("{<dap.bowl>}: on-load"))
28 =^ cards agent (on-load:ag old-state)
31 =^ cards agent (on-load:ag old-state)
29− [[(emit-event %on-load ~) cards] this]
32+ :_ this
33+ :_ :_ cards
34+ (emit-event %on-load ~)
35+ (emit-event-plus bowl [%on-load ~] cards)
30::
36::
31++ on-poke
37++ on-poke
32 |= [=mark =vase]
38 |= [=mark =vase]
@@ -38,17 +44,23 @@
38 %bowl `this(bowl-print !bowl-print)
44 %bowl `this(bowl-print !bowl-print)
39 ==
45 ==
40 =^ cards agent (on-poke:ag mark vase)
46 =^ cards agent (on-poke:ag mark vase)
41− [[(emit-event %on-poke mark) cards] this]
47+ :_ this
48+ :_ :_ cards
49+ (emit-event %on-poke mark)
50+ (emit-event-plus bowl [%on-poke mark (mug q.vase)] cards)
42::
51::
43++ on-watch
52++ on-watch
44 |= =path
53 |= =path
45 ^- (quip card:agent:gall agent:gall)
54 ^- (quip card:agent:gall agent:gall)
46 %- (print bowl |.("{<dap.bowl>}: on-watch on path {<path>}"))
55 %- (print bowl |.("{<dap.bowl>}: on-watch on path {<path>}"))
47 =^ cards agent
56 =^ cards agent
48− ?: ?=([%verb %events ~] path)
57+ ?: ?=([%verb ?(%events %events-plus) ~] path)
49 [~ agent]
58 [~ agent]
50 (on-watch:ag path)
59 (on-watch:ag path)
51− [[(emit-event %on-watch path) cards] this]
60+ :_ this
61+ :_ :_ cards
62+ (emit-event %on-watch path)
63+ (emit-event-plus bowl [%on-watch path] cards)
52::
64::
53++ on-leave
65++ on-leave
54 |= =path
66 |= =path
@@ -57,7 +69,10 @@
57 ?: ?=([%verb %event ~] path)
69 ?: ?=([%verb %event ~] path)
58 [~ this]
70 [~ this]
59 =^ cards agent (on-leave:ag path)
71 =^ cards agent (on-leave:ag path)
60− [[(emit-event %on-leave path) cards] this]
72+ :_ this
73+ :_ :_ cards
74+ (emit-event %on-leave path)
75+ (emit-event-plus bowl [%on-leave path] cards)
61::
76::
62++ on-peek
77++ on-peek
63 |= =path
78 |= =path
@@ -70,7 +85,17 @@
70 ^- (quip card:agent:gall agent:gall)
85 ^- (quip card:agent:gall agent:gall)
71 %- (print bowl |.("{<dap.bowl>}: on-agent on wire {<wire>}, {<-.sign>}"))
86 %- (print bowl |.("{<dap.bowl>}: on-agent on wire {<wire>}, {<-.sign>}"))
72 =^ cards agent (on-agent:ag wire sign)
87 =^ cards agent (on-agent:ag wire sign)
73− [[(emit-event %on-agent wire -.sign) cards] this]
88+ :_ this
89+ :_ :_ cards
90+ (emit-event %on-agent wire -.sign)
91+ =; =^sign
92+ (emit-event-plus bowl [%on-agent wire sign] cards)
93+ ?- -.sign
94+ %poke-ack [%poke-ack ?=(~ p.sign)]
95+ %watch-ack [%watch-ack ?=(~ p.sign)]
96+ %kick [%kick ~]
97+ %fact [%fact p.cage.sign (mug q.q.cage.sign)]
98+ ==
74::
99::
75++ on-arvo
100++ on-arvo
76 |= [=wire =sign-arvo]
101 |= [=wire =sign-arvo]
@@ -78,14 +103,20 @@
78 %- %+ print bowl |.
103 %- %+ print bowl |.
79 "{<dap.bowl>}: on-arvo on wire {<wire>}, {<[- +<]:sign-arvo>}"
104 "{<dap.bowl>}: on-arvo on wire {<wire>}, {<[- +<]:sign-arvo>}"
80 =^ cards agent (on-arvo:ag wire sign-arvo)
105 =^ cards agent (on-arvo:ag wire sign-arvo)
81− [[(emit-event %on-arvo wire [- +<]:sign-arvo) cards] this]
106+ :_ this
107+ :_ :_ cards
108+ (emit-event %on-arvo wire [- +<]:sign-arvo)
109+ (emit-event-plus bowl [%on-arvo wire [- +<]:sign-arvo] cards)
82::
110::
83++ on-fail
111++ on-fail
84 |= [=term =tang]
112 |= [=term =tang]
85 ^- (quip card:agent:gall agent:gall)
113 ^- (quip card:agent:gall agent:gall)
86 %- (print bowl |.("{<dap.bowl>}: on-fail with term {<term>}"))
114 %- (print bowl |.("{<dap.bowl>}: on-fail with term {<term>}"))
87 =^ cards agent (on-fail:ag term tang)
115 =^ cards agent (on-fail:ag term tang)
88− [[(emit-event %on-fail term) cards] this]
116+ :_ this
117+ :_ :_ cards
118+ (emit-event %on-fail term)
119+ (emit-event-plus bowl [%on-fail term] cards)
89--
120--
90::
121::
91++ print
122++ print
@@ -99,7 +130,53 @@
99 same
130 same
100::
131::
101++ emit-event
132++ emit-event
102− |= =event:verb
133+ |= =event
103 ^- card:agent:gall
134 ^- card:agent:gall
104 [%give %fact ~[/verb/events] %verb-event !>(event)]
135 [%give %fact ~[/verb/events] %verb-event !>(event)]
136+::
137+++ emit-event-plus
138+ |= [=bowl:gall =cause cards=(list card:agent:gall)]
139+ ^- card:agent:gall
140+ =; event=event-plus
141+ [%give %fact ~[/verb/events-plus] %verb-event-plus !>(event)]
142+ =- [act.bowl now.bowl src.bowl sap.bowl cause -]
143+ %+ turn cards
144+ |= =card:agent:gall
145+ ^- effect
146+ ::TODO for %fact, %kick, could calculate how many ships affected
147+ ?- card
148+ [%pass * %agent * ?(%poke %poke-as) *]
149+ =, q.card
150+ =/ =cage ?-(-.task.q.card %poke cage.task, %poke-as [mark.task q.cage.task])
151+ [%poke p.card [ship name] p.cage `@`(mug q.q.cage)]
152+ ::
153+ [%pass * %agent * ?(%watch %watch-as) *]
154+ =, q.card
155+ =/ =path ?-(-.task.q.card %watch path.task, %watch-as path.task)
156+ [%watch p.card [ship name] path]
157+ ::
158+ [%pass * %agent * %leave *]
159+ =, q.card
160+ [%leave p.card [ship name]]
161+ ::
162+ [%give %fact *]
163+ =, p.card
164+ [%fact paths p.cage (mug q.q.cage)]
165+ ::
166+ [%give %kick *]
167+ [%kick paths.p.card]
168+ ::
169+ [%give ?(%poke-ack %watch-ack) *]
170+ ~| %explicit-ack
171+ !! :: shouldn't be given explicitly
172+ ::
173+ [%pass * %arvo *]
174+ [%arvo p.card -.q.card +<.q.card]
175+ ::
176+ [%pass *]
177+ [%arvo p.card %$ -.q.card]
178+ ::
179+ [%slip *]
180+ $(card [%pass //slip p.card])
181+ ==
105--
182--
base-desk/mar/belt.hoon+3−12
@@ -1,5 +1,7 @@
1:: belt: runtime belt structure
1:: belt: runtime belt structure
2::
2::
3+/+ dill
4+::
3|_ =belt:dill
5|_ =belt:dill
4++ grad %noun
6++ grad %noun
5:: +grab: convert from
7:: +grab: convert from
@@ -7,18 +9,7 @@
7++ grab
9++ grab
8 |%
10 |%
9 ++ noun belt:dill
11 ++ noun belt:dill
10− ++ json
12+ ++ json belt:dejs:dill
11− ^- $-(^json belt:dill)
12− =, dejs:format
13− %- of
14− :~ aro+(su (perk %d %l %r %u ~))
15− bac+ul
16− ctl+(cu taft so)
17− del+ul
18− met+(cu taft so)
19− ret+ul
20− txt+(ar (cu taft so))
21− ==
22 --
13 --
23:: +grow: convert to
14:: +grow: convert to
24::
15::
base-desk/mar/blit.hoon+3−44
@@ -1,5 +1,7 @@
1:: blit: runtime blit structure
1:: blit: runtime blit structure
2::
2::
3+/+ dill
4+::
3|_ =blit:dill
5|_ =blit:dill
4++ grad %noun
6++ grad %noun
5:: +grab: convert from
7:: +grab: convert from
@@ -13,49 +15,6 @@
13++ grow
15++ grow
14 |%
16 |%
15 ++ noun blit
17 ++ noun blit
16− ++ json
18+ ++ json (blit:enjs:dill blit)
17− ^- ^json
18− =, enjs:format
19− %+ frond -.blit
20− ?- -.blit
21− %bel b+&
22− %clr b+&
23− %hop (numb p.blit)
24− %lin a+(turn p.blit |=(c=@c s+(tuft c)))
25− %mor b+&
26− %url s+p.blit
27− ::
28− %sag
29− %- pairs
30− :~ 'path'^(path p.blit)
31− 'file'^s+(en:base64:mimes:html (as-octs:mimes:html (jam q.blit)))
32− ==
33− ::
34− %sav
35− %- pairs
36− :~ 'path'^(path p.blit)
37− 'file'^s+(en:base64:mimes:html (as-octs:mimes:html q.blit))
38− ==
39− ::
40− %klr
41− :- %a
42− %+ turn p.blit
43− |= [=stye text=(list @c)]
44− %- pairs
45− :~ 'text'^a+(turn text |=(c=@c s+(tuft c)))
46− ::
47− :- 'stye'
48− %- pairs
49− |^ :~ 'back'^(color p.q.stye)
50− 'fore'^(color q.q.stye)
51− 'deco'^a+(turn ~(tap in p.stye) |=(d=deco ?~(d ~ s+d)))
52− ==
53− ++ color
54− |= =tint
55− ?@ tint ?~(tint ~ s+tint)
56− s+(crip ((x-co:co 6) (rep 3 ~[b g r]:tint)))
57− --
58− ==
59− ==
60 --
19 --
61--
20--
base-desk/mar/css.hoon+9−5
@@ -6,11 +6,15 @@
6=, mimes:html
6=, mimes:html
7|_ mud=@t
7|_ mud=@t
8++ grow :: convert to
8++ grow :: convert to
9+ |%
9− |% ++ mime [/text/css (as-octs mud)] :: convert to %mime
10+ ++ mime [/text/css (as-octs mud)] :: convert to %mime
10− ++ elem ;style :: convert to %hymn
11+ ++ hymn :: convert to %hymn
12+ |^ html
13+ ++ style ;style
11− ;- (trip mud)
14+ ;- (trip mud)
12− ==
15+ ==
13− ++ hymn ;html:(head:"{elem}" body)
16+ ++ html ;html:(head:"{style}" body)
17+ --
14 --
18 --
15++ grab
19++ grab
16 |% :: convert from
20 |% :: convert from
base-desk/mar/hoon.hoon+285−31
@@ -1,41 +1,295 @@
1−:::: /hoon/hoon/mar
2− ::
3−/? 310
4−::
5−=, eyre
6|_ own=@t
1|_ own=@t
7::
2::
8++ grow :: convert to
3++ grow :: convert to
9 |%
4 |%
10 ++ mime `^mime`[/text/x-hoon (as-octs:mimes:html own)] :: convert to %mime
5 ++ mime `^mime`[/text/x-hoon (as-octs:mimes:html own)] :: convert to %mime
11− ++ elem :: convert to %html
12− ;div:pre(urb_codemirror "", mode "hoon"):"{(trip own)}"
13− :: =+ gen-id="src-{<`@ui`(mug own)>}"
14− :: ;div
15− :: ;textarea(id "{gen-id}"):"{(trip own)}"
16− :: ;script:"""
17− :: CodeMirror.fromTextArea(
18− :: window[{<gen-id>}],
19− :: \{lineNumbers:true, readOnly:true}
20− :: )
21− :: """
22− :: ==
23 ++ hymn
6 ++ hymn
24− :: ;html:(head:title:"Source" "+{elem}")
7+ ^- manx
25− ;html
8+ =/ to-wall
26− ;head
9+ |= =tape
27− ;title:"Source"
10+ ^- wall
28− ;script@"//cdnjs.cloudflare.com/ajax/libs/codemirror/4.3.0/codemirror.js";
11+ %+ roll (flop tape)
29− ;script@"/lib/syntax/hoon.js";
12+ |= [char=@tD =wall]
30− ;link(rel "stylesheet", href "//cdnjs.cloudflare.com/ajax/libs/".
13+ ?~ wall
31− "codemirror/4.3.0/codemirror.min.css");
14+ [[char ~] ~]
32− ;link/"/lib/syntax/codemirror.css"(rel "stylesheet");
15+ ?: =('\0a' char)
33− ==
16+ [~ wall]
34− ;body
17+ [[char i.wall] t.wall]
18+ ::
35− ;textarea#src:"{(trip own)}"
19+ =/ src=tape (trip own)
36− ;script:'CodeMirror.fromTextArea(src, {lineNumbers:true, readOnly:true})'
20+ =/ count=@ud (lent (to-wall src))
21+ =; [style=tape script=tape]
22+ ;html
23+ ;head
24+ ;title: source
25+ ;meta(charset "utf-8");
26+ ;meta(name "viewport", content "width=device-width, initial-scale=1");
27+ ;style: {style}
28+ ==
29+ ;body
30+ ;div.viewer
31+ :: ;div.viewer-bar
32+ :: ;span.viewer-name: {(scow %ud count)} lines
33+ :: ;button.copy-link(id "copy-link", hidden ""): copy link
34+ :: ==
35+ ;div.code
36+ ;pre.gutter(id "gutter");
37+ ;div.code-body
38+ ;div(id "hl");
39+ ;pre.code-pre(id "code"): {src}
40+ ==
41+ ==
42+ ==
43+ ;script: {script}
44+ ==
37 ==
45 ==
38− ==
46+ ::
47+ :-
48+ ^~ ^= style
49+ ^- tape
50+ %- trip
51+ '''
52+ :root {
53+ --bg: #fbfbf9;
54+ --panel: #ffffff;
55+ --gutter-bg: #f3f3ef;
56+ --line: #e4e4dd;
57+ --fg: #2b2b2b;
58+ --fg-dim: #9a9a90;
59+ --sel: #fff7d6;
60+ --sel-edge: #f1e4a8;
61+ --accent: #4a6da7;
62+ --lh: 20px;
63+ --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
64+ "Liberation Mono", "Courier New", monospace;
65+ }
66+ * { box-sizing: border-box; }
67+ html, body {
68+ margin: 0;
69+ height: 100%;
70+ background: var(--bg);
71+ color: var(--fg);
72+ font-family: var(--mono);
73+ -webkit-font-smoothing: antialiased;
74+ }
75+ .viewer {
76+ height: 100vh;
77+ display: flex;
78+ flex-direction: column;
79+ padding: 16px;
80+ gap: 12px;
81+ }
82+ .viewer-bar {
83+ flex: 0 0 auto;
84+ display: flex;
85+ align-items: center;
86+ gap: 12px;
87+ font-size: 13px;
88+ color: var(--fg-dim);
89+ }
90+ .viewer-name {
91+ flex: 1 1 auto;
92+ min-width: 0;
93+ overflow: hidden;
94+ text-overflow: ellipsis;
95+ white-space: nowrap;
96+ color: var(--fg);
97+ }
98+ .copy-link {
99+ flex: 0 0 auto;
100+ padding: 4px 12px;
101+ border: 1px solid var(--line);
102+ border-radius: 6px;
103+ background: var(--panel);
104+ color: var(--fg-dim);
105+ font: inherit;
106+ font-size: 12px;
107+ cursor: pointer;
108+ transition: color .12s, border-color .12s;
109+ }
110+ .copy-link:hover { color: var(--accent); border-color: var(--accent); }
111+ .copy-link[hidden] { display: none; }
112+ .code {
113+ flex: 1 1 auto;
114+ min-height: 0;
115+ display: flex;
116+ align-items: flex-start;
117+ overflow: auto;
118+ background: var(--panel);
119+ border: 1px solid var(--line);
120+ border-radius: 8px;
121+ }
122+ .code-body {
123+ position: relative;
124+ flex: 1 1 auto;
125+ min-width: 0;
126+ width: max-content;
127+ }
128+ #gutter, #code {
129+ margin: 0;
130+ padding: 14px 0;
131+ font-family: var(--mono);
132+ font-size: 13px;
133+ line-height: var(--lh);
134+ white-space: pre;
135+ tab-size: 2;
136+ }
137+ #gutter {
138+ position: sticky;
139+ left: 0;
140+ z-index: 2;
141+ flex: 0 0 auto;
142+ padding-left: 16px;
143+ padding-right: 12px;
144+ text-align: right;
145+ color: var(--fg-dim);
146+ background: var(--gutter-bg);
147+ border-right: 1px solid var(--line);
148+ user-select: none;
149+ -webkit-user-select: none;
150+ cursor: pointer;
151+ }
152+ #code {
153+ display: block;
154+ padding-left: 14px;
155+ padding-right: 18px;
156+ color: var(--fg);
157+ }
158+ #hl {
159+ position: absolute;
160+ left: 0;
161+ right: 0;
162+ z-index: 0; /* under the text */
163+ display: none;
164+ background: var(--sel);
165+ box-shadow: inset 2px 0 0 var(--sel-edge);
166+ pointer-events: none;
167+ }
168+ #code { position: relative; z-index: 1; } /* text above the bar */
169+ :focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
170+ '''
171+ ^~ ^= code
172+ ^- tape
173+ %- trip
174+ '''
175+ (function () {
176+ "use strict";
177+ const panel = document.querySelector(".code"); // the scroll container
178+ const code = document.getElementById("code");
179+ const gutter = document.getElementById("gutter");
180+ const hl = document.getElementById("hl");
181+ if (!panel || !code || !gutter || !hl) return;
182+ const text = code.textContent;
183+ let lineCount = text.length ? text.split("\n").length : 1;
184+ if (text.endsWith("\n")) lineCount -= 1;
185+ if (lineCount < 1) lineCount = 1;
186+ (function fillGutter() {
187+ let s = "";
188+ for (let i = 1; i <= lineCount; i++) s += i + "\n";
189+ gutter.textContent = s;
190+ })();
191+ let lineH = 0;
192+ let codeTop = 0; // code's content-box top padding, the y-origin of line 1
193+ function measure() {
194+ const cs = getComputedStyle(code);
195+ const lh = parseFloat(cs.lineHeight);
196+ lineH = isNaN(lh) ? parseFloat(cs.fontSize) * 1.55 : lh;
197+ codeTop = parseFloat(cs.paddingTop) || 0;
198+ positionHighlight(); // keep the bar correct after a re-measure
199+ }
200+ let anchor = null;
201+ let selLo = 0;
202+ let selHi = 0;
203+ function positionHighlight() {
204+ if (!selLo) { hl.style.display = "none"; return; }
205+ hl.style.display = "block";
206+ hl.style.top = (codeTop + (selLo - 1) * lineH) + "px";
207+ hl.style.height = ((selHi - selLo + 1) * lineH) + "px";
208+ }
209+ function applySel(lo, hi) {
210+ selLo = lo;
211+ selHi = hi;
212+ positionHighlight();
213+ }
214+ function deselect() {
215+ selLo = selHi = 0;
216+ anchor = null;
217+ positionHighlight();
218+ history.replaceState(null, "", location.pathname + location.search);
219+ }
220+ function parseHash(hash) {
221+ const m = /^#L(\d+)(?:-L(\d+))?$/.exec(hash || "");
222+ if (!m) return null;
223+ let lo = parseInt(m[1], 10);
224+ let hi = m[2] ? parseInt(m[2], 10) : lo;
225+ if (hi < lo) { const t = lo; lo = hi; hi = t; }
226+ lo = Math.max(1, Math.min(lo, lineCount));
227+ hi = Math.max(1, Math.min(hi, lineCount));
228+ return [lo, hi];
229+ }
230+ function setHash(lo, hi) {
231+ const frag = lo === hi ? "L" + lo : "L" + lo + "-L" + hi;
232+ history.replaceState(null, "", "#" + frag);
233+ }
234+ function scrollToLine(n) {
235+ const y = codeTop + (n - 1) * lineH;
236+ if (y < panel.scrollTop || y > panel.scrollTop + panel.clientHeight) {
237+ panel.scrollTop = Math.max(0, y - panel.clientHeight / 2);
238+ }
239+ }
240+ function selectFromHash(scroll) {
241+ const r = parseHash(location.hash);
242+ if (!r) return false;
243+ anchor = r[0];
244+ applySel(r[0], r[1]);
245+ if (scroll) scrollToLine(r[0]);
246+ return true;
247+ }
248+ function lineFromEvent(e) {
249+ // y within the code content: pointer relative to the code pre's rendered
250+ // top, which already accounts for scroll since getBoundingClientRect is
251+ // viewport-relative and the pre moves as the panel scrolls.
252+ const rect = code.getBoundingClientRect();
253+ const y = e.clientY - rect.top - codeTop;
254+ let n = Math.floor(y / lineH) + 1;
255+ if (n < 1) n = 1;
256+ if (n > lineCount) n = lineCount;
257+ return n;
258+ }
259+ function onClick(e) {
260+ const inGutter = e.target === gutter;
261+ if (!inGutter && !e.shiftKey) return; // normal text interaction in code
262+ const n = lineFromEvent(e);
263+ if (!e.shiftKey && selLo === n && selHi === n) {
264+ deselect();
265+ return;
266+ }
267+ if (e.shiftKey && anchor !== null) {
268+ applySel(Math.min(anchor, n), Math.max(anchor, n));
269+ setHash(selLo, selHi);
270+ } else {
271+ anchor = n;
272+ applySel(n, n);
273+ setHash(n, n);
274+ }
275+ }
276+ gutter.addEventListener("click", onClick);
277+ code.addEventListener("click", onClick);
278+ document.addEventListener("keydown", function (e) {
279+ if (e.key === "Escape") deselect();
280+ });
281+ window.addEventListener("hashchange", function () {
282+ if (!selectFromHash(true)) deselect();
283+ });
284+ let rz = null;
285+ window.addEventListener("resize", function () {
286+ clearTimeout(rz);
287+ rz = setTimeout(measure, 100);
288+ });
289+ measure();
290+ selectFromHash(true);
291+ })();
292+ '''
39 ++ txt
293 ++ txt
40 (to-wain:format own)
294 (to-wain:format own)
41 --
295 --
base-desk/mar/htm.hoon+2−11
@@ -2,14 +2,5 @@
2:::: /hoon/htm/mar
2:::: /hoon/htm/mar
3 ::
3 ::
4/? 310
4/? 310
5−|_ own=manx
5+/= htm /mar/html
6−::
6+htm
7−++ grad %noun
8−++ grow :: convert to
9− |%
10− ++ noun own
11− ++ hymn own
12− --
13−++ grab |% :: convert from
14− ++ noun manx :: clam from %noun
15−-- --
base-desk/mar/httr.hoon+1−2
@@ -5,12 +5,11 @@
5::
5::
6=, eyre
6=, eyre
7=, format
7=, format
8−=, html
9|_ hit=httr
8|_ hit=httr
10++ grad %noun
9++ grad %noun
11++ grow |% ++ wall (turn wain trip)
10++ grow |% ++ wall (turn wain trip)
12 ++ wain (to-wain cord)
11 ++ wain (to-wain cord)
13− ++ json (need (de-json cord))
12+ ++ json (need (de:json:html cord))
14 ++ cord q:octs
13 ++ cord q:octs
15 ++ noun hit
14 ++ noun hit
16 ++ octs
15 ++ octs
base-desk/mar/js.hoon+7−4
@@ -8,10 +8,13 @@
8++ grow
8++ grow
9 |%
9 |%
10 ++ mime [/application/javascript (as-octs:mimes:html (@t mud))]
10 ++ mime [/application/javascript (as-octs:mimes:html (@t mud))]
11+ ++ hymn :: convert to %hymn
12+ |^ html
11− ++ elem ;script
13+ ++ script ;script
12− ;- (trip (@t mud))
14+ ;- (trip (@t mud))
13− ==
15+ ==
14− ++ hymn ;html:(head:"+{elem}" body)
16+ ++ html ;html:(head:"{script}" body)
17+ --
15 --
18 --
16++ grab
19++ grab
17 |% :: convert from
20 |% :: convert from
base-desk/mar/json.hoon+4−4
@@ -8,17 +8,17 @@
8=, eyre
8=, eyre
9=, format
9=, format
10=, html
10=, html
11−|_ jon=json
11+|_ jon=^json
12::
12::
13++ grow :: convert to
13++ grow :: convert to
14 |%
14 |%
15 ++ mime [/application/json (as-octs:mimes -:txt)] :: convert to %mime
15 ++ mime [/application/json (as-octs:mimes -:txt)] :: convert to %mime
16− ++ txt [(crip (en-json jon))]~
16+ ++ txt [(en:json jon)]~
17 --
17 --
18++ grab
18++ grab
19 |% :: convert from
19 |% :: convert from
20− ++ mime |=([p=mite q=octs] (fall (rush (@t q.q) apex:de-json) *json))
20+ ++ mime |=([p=mite q=octs] (fall (de:json (@t q.q)) *^json))
21− ++ noun json :: clam from %noun
21+ ++ noun ^json :: clam from %noun
22 ++ numb numb:enjs
22 ++ numb numb:enjs
23 ++ time time:enjs
23 ++ time time:enjs
24 --
24 --
base-desk/mar/json/rpc/response.hoon+1−2
@@ -16,11 +16,10 @@
16 ^- response
16 ^- response
17 ~| hit
17 ~| hit
18 ?: ?=(%2 (div p.hit 100))
18 ?: ?=(%2 (div p.hit 100))
19− =, html
20 %- json
19 %- json
21 ?~ r.hit
20 ?~ r.hit
22 a+~
21 a+~
23− (need (de-json q:u.r.hit))
22+ (need (de:json:html q:u.r.hit))
24 fail+hit
23 fail+hit
25 ++ json :: from json
24 ++ json :: from json
26 =, dejs-soft:format
25 =, dejs-soft:format
base-desk/mar/kelvin.hoon+16−6
@@ -1,18 +1,28 @@
1−=/ weft ,[lal=@tas num=@ud] :: TODO remove after merge
1+|_ kal=waft:clay
2−|_ kel=weft
3++ grow
2++ grow
4 |%
3 |%
5 ++ mime `^mime`[/text/x-kelvin (as-octs:mimes:html hoon)]
4 ++ mime `^mime`[/text/x-kelvin (as-octs:mimes:html hoon)]
6− ++ noun kel
5+ ++ noun kal
7− ++ hoon (crip "{<[lal num]:kel>}\0a")
6+ ++ hoon
7+ %+ rap 3
8+ %+ turn
9+ %+ sort
10+ ~(tap in (waft-to-wefts:clay kal))
11+ |= [a=weft b=weft]
12+ ?: =(lal.a lal.b)
13+ (gte num.a num.b)
14+ (gte lal.a lal.b)
15+ |= =weft
16+ (rap 3 '[%' (scot %tas lal.weft) ' ' (scot %ud num.weft) ']\0a' ~)
17+ ::
8 ++ txt (to-wain:format hoon)
18 ++ txt (to-wain:format hoon)
9 --
19 --
10++ grab
20++ grab
11 |%
21 |%
12− ++ noun weft
22+ ++ noun waft:clay
13 ++ mime
23 ++ mime
14 |= [=mite len=@ud tex=@]
24 |= [=mite len=@ud tex=@]
15− !<(weft (slap !>(~) (ream tex)))
25+ (cord-to-waft:clay tex)
16 --
26 --
17++ grad %noun
27++ grad %noun
18--
28--
base-desk/mar/noun.hoon+3
@@ -8,6 +8,9 @@
8++ grab |%
8++ grab |%
9 ++ noun *
9 ++ noun *
10 --
10 --
11+++ grow |%
12+ ++ mime [/application/x-urb-jam (as-octs:mimes:html (jam non))]
13+ --
11++ grad
14++ grad
12 |%
15 |%
13 ++ form %noun
16 ++ form %noun
base-desk/mar/sole/action.hoon+3−1
@@ -20,7 +20,9 @@
20 |= jon=^json ^- sole-action
20 |= jon=^json ^- sole-action
21 %- need %. jon
21 %- need %. jon
22 => [dejs-soft:format ..sole-action]
22 => [dejs-soft:format ..sole-action]
23− |^ (ot id+so dat+(fo %ret (of det+change tab+ni ~)) ~)
23+ |^ (ot id+id dat+(fo %ret (of det+change tab+ni ~)) ~)
24+ ++ id
25+ (ot who+(su ;~(pfix sig fed:ag)) ses+so ~)
24 ++ fo
26 ++ fo
25 |* [a=term b=fist]
27 |* [a=term b=fist]
26 |=(c=json ?.(=([%s a] c) (b c) (some [a ~])))
28 |=(c=json ?.(=([%s a] c) (b c) (some [a ~])))
base-desk/mar/story.hoon+5−10
@@ -46,18 +46,13 @@
46 [/text/x-urb-story (as-octs:mimes:html (of-wain:format txt))]
46 [/text/x-urb-story (as-octs:mimes:html (of-wain:format txt))]
47 ++ txt
47 ++ txt
48 ^- wain
48 ^- wain
49− %- snoc :_ '' :: ensures terminating newline is present
49+ %- zing
50+ %+ join `wain`~['---']
50 %+ murn ~(tap by tale)
51 %+ murn ~(tap by tale)
51− |= [[=tako:clay =proses]]
52+ |= [=tako:clay =proses]
52− ^- (unit cord)
53+ ^- (unit wain)
53 ?~ proses ~
54 ?~ proses ~
54− %- some
55− %- crip
56− ;: welp
57− (tako-to-text tako)
55+ (some (chapter-to-text tako proses))
58− (proses-to-text proses)
59− "---"
60− ==
61 --
56 --
62++ grab
57++ grab
63 |% :: convert from
58 |% :: convert from
base-desk/mar/tang.hoon+5−5
@@ -16,15 +16,15 @@
16 %& a+p.result
16 %& a+p.result
17 %| a+[a+[%s '[[output rendering error]]']~]~
17 %| a+[a+[%s '[[output rendering error]]']~]~
18 ==
18 ==
19− ::
19+ ++ txt
20− ++ elem
20+ ^- wain
21− =- ;pre:code:"{(of-wall -)}"
22− ^- wall %- zing ^- (list wall)
23− (turn (flop tan) |=(a=tank (wash 0^160 a)))
21+ %+ turn (flop tan)
22+ |=(=tank (crip ~(ram re tank)))
24 --
23 --
25++ grab :: convert from
24++ grab :: convert from
26 |%
25 |%
27 ++ noun (list ^tank) :: clam from %noun
26 ++ noun (list ^tank) :: clam from %noun
28 ++ tank |=(a=^tank [a]~)
27 ++ tank |=(a=^tank [a]~)
28+ ++ txt |=(=wain (flop (turn wain tank)))
29 --
29 --
30--
30--
base-desk/mar/thread-fail.hoon+1−1
@@ -1,4 +1,4 @@
1−|_ err=*
1+|_ err=[term tang]
2++ grab
2++ grab
3 |%
3 |%
4 ++ noun (pair term tang)
4 ++ noun (pair term tang)
base-desk/mar/txt.hoon−1
@@ -18,7 +18,6 @@
18 => v=.
18 => v=.
19 |%
19 |%
20 ++ mime => v [/text/plain (as-octs (of-wain txt))]
20 ++ mime => v [/text/plain (as-octs (of-wain txt))]
21− ++ elem => v ;pre: {(trip (of-wain txt))}
22 --
21 --
23++ grad
22++ grad
24 |%
23 |%
base-desk/mar/udon.hoon−4
@@ -9,10 +9,6 @@
9 ++ mime [/text/x-unmark (as-octs:mimes:html mud)]
9 ++ mime [/text/x-unmark (as-octs:mimes:html mud)]
10 ++ txt
10 ++ txt
11 (to-wain:format mud)
11 (to-wain:format mud)
12− ++ elem
13− ^- manx
14− =, cram
15− elm:(static (ream mud))
16 ++ front :: XX performance, types
12 ++ front :: XX performance, types
17 ^- (map term knot)
13 ^- (map term knot)
18 %- ~(run by inf:(static:cram (ream mud)))
14 %- ~(run by inf:(static:cram (ream mud)))
base-desk/mar/umd.hoon−4
@@ -9,10 +9,6 @@
9 ++ mime [/text/x-unmark (as-octs:mimes:html mud)]
9 ++ mime [/text/x-unmark (as-octs:mimes:html mud)]
10 ++ txt
10 ++ txt
11 (to-wain:format mud)
11 (to-wain:format mud)
12− ++ elem
13− ^- manx
14− =, cram
15− elm:(static (ream mud))
16 ++ front :: XX performance, types
12 ++ front :: XX performance, types
17 ^- (map term knot)
13 ^- (map term knot)
18 %- ~(run by inf:(static:cram (ream mud)))
14 %- ~(run by inf:(static:cram (ream mud)))
base-desk/mar/urb.hoon+1−1
@@ -1,5 +1,5 @@
1::
1::
2−:::: /hoon/elem/urb/mar
2+:::: /hoon/urb/mar
3 ::
3 ::
4/? 310
4/? 310
5=, mimes:html
5=, mimes:html
base-desk/sources.txt+2−3
@@ -1,4 +1,3 @@
1urbit-src/pkg/base-dev
1urbit-src/pkg/base-dev
2−urbit-src/pkg/garden-dev
2+urbit-src/pkg/landscape
3−urbit-src/pkg/arvo/lib/naive
4−urbit-src/pkg/arvo/lib/tiny
3+urbit-src/pkg/arvo
base-desk/sur/aquarium.hoon+8−4
@@ -10,8 +10,6 @@
10:: Short names are simply the first letter of each word plus `s` if
10:: Short names are simply the first letter of each word plus `s` if
11:: it's a list.
11:: it's a list.
12::
12::
13−/+ pill
14−=, pill-lib=pill
15|%
13|%
16+$ az-log [topics=(lest @) data=@t]
14+$ az-log [topics=(lest @) data=@t]
17+$ az-state
15+$ az-state
@@ -34,13 +32,13 @@
34 [%verb p=(unit ?)]
32 [%verb p=(unit ?)]
35 task-arvo
33 task-arvo
36 ==
34 ==
37−+$ pill pill:pill-lib
38::
35::
39+$ aqua-event
36+$ aqua-event
40− $% [%init-ship who=ship fake=?]
37+ $% [%init-ship who=ship fake=? feed=(unit feed:jael)]
41 [%pause-events who=ship]
38 [%pause-events who=ship]
42 [%snap-ships lab=term hers=(list ship)]
39 [%snap-ships lab=term hers=(list ship)]
43 [%restore-snap lab=term]
40 [%restore-snap lab=term]
41+ [%read [from=[=ship life=@ubC] =path] for=[=lane:ames life=@ubC] num=@ud]
44 [%event who=ship ue=unix-event]
42 [%event who=ship ue=unix-event]
45 ==
43 ==
46::
44::
@@ -73,6 +71,9 @@
73 %+ pair wire
71 %+ pair wire
74 $% [%blit p=(list blit:dill)]
72 $% [%blit p=(list blit:dill)]
75 [%send p=lane:ames q=@]
73 [%send p=lane:ames q=@]
74+ [%push p=(list lane:pact:ames) q=@]
75+ [%saxo sponsors=(list ship)]
76+ [%nail =ship lanes=(list lane:ames)]
76 [%doze p=(unit @da)]
77 [%doze p=(unit @da)]
77 [%thus p=@ud q=(unit hiss:eyre)]
78 [%thus p=@ud q=(unit hiss:eyre)]
78 [%ergo p=@tas q=mode:clay]
79 [%ergo p=@tas q=mode:clay]
@@ -81,5 +82,8 @@
81 [%kill ~]
82 [%kill ~]
82 [%init ~]
83 [%init ~]
83 [%request id=@ud request=request:http]
84 [%request id=@ud request=request:http]
85+ [%turf p=(list turf)]
86+ [%unto =unto:gall] :: internal gifts to host ships
87+ :: (%facts are transformed to %raw-facts)
84 ==
88 ==
85--
89--
base-desk/sur/hood.hoon+281−185
@@ -1,147 +1,312 @@
1+/% kelvin %kelvin
1=, clay
2=, clay
2=* dude dude:gall
3=* dude dude:gall
3|%
4|%
4−:: $snap: kiln snapshot
5++$ pike
6+ $: sync=(unit [=ship =desk])
7+ hash=@uv
8+ =zest
9+ wic=(set weft)
10+ ==
5::
11::
6−+$ snap (map desk arak)
12++$ pikes (map desk pike)
7−:: $diff: subscription update
13+:: $jump: changes to update source change requests
8::
14::
9−+$ diff
15++$ jump
10− $% [%block =desk =arak =weft blockers=(set desk)]
16+ $% [%all all=(map dock dock)] :: pending requests
11− [%reset =desk =arak]
17+ [%add old=dock new=dock] :: new request
12− [%commit =desk =arak]
18+ [%yea old=dock new=dock] :: approved
13− [%merge-sunk =desk =arak =tang]
19+ [%nay old=dock new=dock] :: denied
14− [%merge-fail =desk =arak =tang]
15− [%suspend =desk =arak]
16− [%revive =desk =arak]
17 ==
20 ==
18−:: $arak: foreign vat tracker
21+:: $rung: reference to upstream commit
19::
22::
20−:: .rail: upstream tracking state, if any
23++$ rung [=aeon =weft]
21−:: .rein: configuration for agents
24+:: #sync-record: source and destination of a kiln sync
22::
25::
23−+$ arak
26++$ sync-record ::
24− $: rail=(unit rail)
27+ $: syd=desk :: local desk
25− =rein
28+ her=ship :: foreign ship
29+ sud=desk :: foreign desk
26 ==
30 ==
27−:: $rail: upstream tracking state
28::
31::
29−:: .publisher: Ship that originally published desk, if available
32++$ sync-state ::
30−:: .paused: is tracking paused? or live
33+ $: nun=@ta :: nonce
31−:: .ship: upstream ship (could be .our)
34+ kid=(unit desk) :: has kids desk too?
32−:: .desk: name of upstream desk
35+ let=@ud :: next revision
33−:: .aeon: next aeon to pull from upstream
36+ nit=(unit ?) :: automerge or default
34−:: .next: list of pending commits with future kelvins
37+ hav=(unit @ud) :: update available
38+ yea=? :: update approved
39+ ==
35::
40::
36−+$ rail
41++$ sync-update
37− $: publisher=(unit ship)
42+ $% [%new for=sync-record rev=@ud]
38− paused=?
43+ [%done for=sync-record rev=@ud]
39− =ship
44+ [%drop for=sync-record rev=@ud]
40− =desk
45+ [%pending pending=(set [for=sync-record rev=@ud])]
41− =aeon
42− next=(list rung)
43 ==
46 ==
44−:: $rung: reference to upstream commit
45::
47::
46−+$ rung [=aeon =weft]
48++$ sink (unit [her=@p sud=desk kid=(unit desk) let=@ud])
47−:: $rein: diff from desk manifest
49+:: +truncate-hash: get last 5 digits of hash and convert to tape
48::
50::
49−:: .liv: suspended? if suspended, no agents should run
51+++ truncate-hash
50−:: .add: agents not in manifest that should be running
52+ |= hash=@uv
51−:: .sub: agents in manifest that should not be running
53+ ^- tape
54+ (slag 2 <`@uv`(mod hash 0v1.00000)>)
55+:: +report-prep: get data required for reports
52::
56::
53−+$ rein
57+++ report-prep
54− $: liv=_&
58+ |= [our=@p now=@da]
55− add=(set dude)
59+ =/ ego (scot %p our)
56− sub=(set dude)
60+ =/ wen (scot %da now)
61+ :* .^(rock:tire %cx /[ego]//[wen]/tire)
62+ .^(cone %cx /[ego]//[wen]/domes)
63+ .^((map desk [ship desk]) %gx /[ego]/hood/[wen]/kiln/sources/noun)
64+ .^ (map [desk ship desk] sync-state) %gx
65+ /[ego]/hood/[wen]/kiln/syncs/noun
66+ ==
57 ==
67 ==
58−::
59−+$ vat [=desk hash=@uv =cass =arak]
60:: +report-vats: report on all desk installations
68:: +report-vats: report on all desk installations
61::
69::
62++ report-vats
70++ report-vats
63− |= [our=@p now=@da]
71+ |= [our=@p now=@da desks=(list desk) filt=@tas verb=?]
64 ^- tang
72 ^- tang
65− =+ .^ raz=(list vat)
73+ =/ ego (scot %p our)
66− %gx /(scot %p our)/hood/(scot %da now)/kiln/vats/noun
74+ =/ wen (scot %da now)
75+ =+ prep=[tyr cone sor zyn]=(report-prep our now)
76+ ?: =(%$ filt)
77+ %- zing
78+ %+ turn
79+ ?^ desks
80+ (flop desks)
81+ %+ sort ~(tap in ~(key by tyr.prep))
82+ |= [a=desk b=desk]
83+ ?: |(=(a %kids) =(b %base)) &
84+ ?: |(=(a %base) =(b %kids)) |
85+ (aor b a)
86+ |=(syd=@tas (report-vat prep our now syd verb))
87+ =/ deks=(list [=desk =zest wic=(set weft)])
88+ ?~ desks
89+ %+ sort ~(tap by tyr.prep)
90+ |= [[a=desk *] [b=desk *]]
91+ ?: |(=(a %kids) =(b %base)) &
92+ ?: |(=(a %base) =(b %kids)) |
93+ (aor b a)
94+ %+ murn (flop desks)
95+ |= des=desk
96+ ^- (unit [=desk =zest wic=(set weft)])
97+ ?~ got=(~(get by tyr.prep) des)
98+ ~
99+ `[des u.got]
100+ ?: =(filt %blocking)
101+ =/ base-weft=(unit weft)
102+ %- ~(rep in wic:(~(got by tyr.prep) %base))
103+ |= [=weft out=(unit weft)]
104+ ?~ out
105+ `weft
106+ ?: (lth num.weft num.u.out)
107+ out
108+ `weft
109+ ?~ base-weft ~['%base already up-to-date']
110+ =/ blockers=(list desk)
111+ %+ sort
112+ ^- (list desk)
113+ %+ murn deks
114+ |= [=desk =zest wic=(set weft)]
115+ ^- (unit ^desk)
116+ ?. =(%live zest)
117+ ~
118+ ?: (~(has in wic) u.base-weft)
119+ ~
120+ `desk
121+ aor
122+ ?~ blockers ~['No desks blocking upgrade']
123+ %- zing
124+ ^- (list tang)
125+ :- :~ %+ rap 3
126+ :~ 'These desks are blocking upgrade to [%zuse '
127+ (scot %ud num.u.base-weft)
128+ ']:'
129+ == ==
130+ %+ turn blockers
131+ |=(syd=desk (report-vat prep our now syd verb))
132+ ::
133+ %- zing
134+ %+ turn
135+ ?+ filt !!
136+ %exists
137+ %+ skip deks
138+ |= [syd=desk *]
139+ ?~ got=(~(get by cone.prep) our syd)
140+ &
141+ =(0 let.u.got)
142+ ::
143+ %running
144+ %+ skim deks
145+ |=([* [zest=@tas *]] =(zest %live))
146+ ::
147+ %suspended
148+ %+ skip deks
149+ |= [syd=@tas [zest=@tas *]]
150+ ?| =(syd %kids)
151+ =(zest %live)
152+ ?~ got=(~(get by cone.prep) our syd)
153+ &
154+ =(0 let.u.got)
67 ==
155 ==
68− :- (report-kids our now)
156+ ::
157+ %exists-not
158+ %+ skim deks
159+ |= [syd=desk *]
160+ ?~ got=(~(get by cone.prep) our syd)
161+ |
162+ =(0 let.u.got)
163+ ==
69− (turn raz |=(v=vat (report-vat our now v)))
164+ |=([syd=desk *] (report-vat prep our now syd verb))
70:: +report-vat: report on a single desk installation
165:: +report-vat: report on a single desk installation
71::
166::
72++ report-vat
167++ report-vat
168+ |= $: $: tyr=rock:tire =cone sor=(map desk (pair ship desk))
169+ zyn=(map [desk ship desk] sync-state)
170+ ==
73− |= [our=ship now=@da vat]
171+ our=ship now=@da syd=desk verb=?
74− ^- tank
172+ ==
75− ?: =(ud.cass 0)
173+ |^ ^- tang
174+ =/ ego (scot %p our)
175+ =/ wen (scot %da now)
176+ ?. ((sane %tas) syd)
177+ ~[(cat 3 'insane desk: %' syd)]
178+ ?. (~(has by cone) our syd)
76− leaf+"desk does not yet exist: {<desk>}"
179+ ~[(cat 3 'desk does not yet exist: %' syd)]
180+ =/ hash .^(@uv %cz /[ego]/[syd]/[wen])
181+ ?: =(%kids syd)
182+ ~[(cat 3 '%kids %cz hash: ' (scot %uv hash))]
77− =/ kel-path
183+ =/ kel-path /[ego]/[syd]/[wen]/sys/kelvin
78− /(scot %p our)/[desk]/(scot %da now)/sys/kelvin
79 ?. .^(? %cu kel-path)
184 ?. .^(? %cu kel-path)
80− leaf+"bad desk: {<desk>}"
185+ ~[(cat 3 'bad desk: %' syd)]
81− =+ .^(=weft %cx kel-path)
186+ =+ .^(=waft %cx kel-path)
82− :+ %rose ["" "{<desk>}" "::"]
83 ^- tang
187 ^- tang
84− =/ meb (mergebase-hashes our desk now arak)
188+ =/ =sink
85− =/ poz
189+ ?~ s=(~(get by sor) syd)
86− ?~ rail.arak "local"
190+ ~
87− ?:(paused.u.rail.arak "paused" "tracking")
191+ ?~ z=(~(get by zyn) syd u.s)
88− =/ sat ?:(liv.rein.arak "running" "suspended")
192+ ~
89− =/ pen
193+ `[p.u.s q.u.s [kid let]:u.z]
90− ?~ rail.arak "~"
194+ =/ meb=(list @uv)
91− <(turn next.u.rail.arak |=([@ lal=@tas num=@] [lal num]))>
195+ ?~ sink ~[hash]
92− :~ leaf/"/sys/kelvin: {<[lal num]:weft>}"
196+ %+ turn
93− leaf/"base hash: {?.(=(1 (lent meb)) <meb> <(head meb)>)}"
197+ .^ (list tako) %cs
94− leaf/"%cz hash: {<hash>}"
198+ /[ego]/[syd]/[wen]/base/(scot %p her.u.sink)/[sud.u.sink]
95− ::
199+ ==
96− leaf/"app status: {sat}"
200+ |=(=tako .^(@uv %cs /[ego]/[syd]/[wen]/hash/(scot %uv tako)))
97− leaf/"force on: {?:(=(~ add.rein.arak) "~" <add.rein.arak>)}"
201+ =/ dek (~(got by tyr) syd)
98− leaf/"force off: {?:(=(~ sub.rein.arak) "~" <sub.rein.arak>)}"
202+ =/ sat
99− ::
203+ ?- zest.dek
204+ %live 'running'
205+ %dead 'suspended'
206+ %held 'suspended until next update'
207+ ==
208+ =/ kul=cord (print-wefts (waft-to-wefts waft))
209+ =/ ese=cord
210+ ?:(.^(? %cx /[ego]//[wen]/esse/[syd]) 'yes' 'no')
211+ =/ pax=path /(scot %p our)/[syd]/(scot %da now)
212+ ?. verb
213+ :~ '::'
214+ =+ .^(exist=? cu+(weld pax /desk/bill))
215+ ?. exist ' /desk/bill: missing'
216+ =+ .^(dudes=(list dude:gall) cx+(weld pax /desk/bill))
217+ (cat 3 ' /desk/bill: ' (crip "{<dudes>}"))
218+ (cat 3 ' pending updates: ' (print-wefts wic.dek))
219+ (cat 3 ' source ship: ' ?~(sink '~' (scot %p her.u.sink)))
220+ (cat 3 ' app status: ' sat)
221+ (cat 3 ' essential desk: ' ese)
222+ (cat 3 ' %cz hash ends in: ' (print-shorthash hash))
223+ (cat 3 ' /sys/kelvin: ' (print-wefts (waft-to-wefts waft)))
224+ (cat 3 '%' syd)
225+ ==
226+ ::
227+ =/ [on=(list @tas) of=(list @tas)]
228+ =/ [on=(list @tas) of=(list @tas)]
229+ %- ~(rep by ren:(~(got by cone) our syd))
230+ |= [[=dude:gall is-on=?] on=(list @tas) of=(list @tas)]
231+ ?: is-on
232+ [[dude on] of]
233+ [on [dude of]]
234+ [(sort on aor) (sort of aor)]
235+ :~ '::'
236+ (cat 3 ' pending updates: ' (print-wefts wic.dek))
237+ %^ cat 3 ' kids desk: ' ?~ sink '~'
238+ ?~ kid.u.sink '~'
239+ (cat 3 '%' u.kid.u.sink)
240+ (cat 3 ' source aeon: ' ?~(sink '~' (scot %ud let.u.sink)))
241+ (cat 3 ' source desk: ' ?~(sink '~' (cat 3 '%' sud.u.sink)))
242+ (cat 3 ' source ship: ' ?~(sink '~' (scot %p her.u.sink)))
243+ (cat 3 ' updates: ' ?~(sink 'local' 'remote'))
100− leaf/"publishing ship: {?~(rail.arak <~> <publisher.u.rail.arak>)}"
244+ %^ cat 3 ' publishing ship: ' ?~ got=(get-publisher our syd now)
101− leaf/"updates: {poz}"
245+ '~'
102− leaf/"source ship: {?~(rail.arak <~> <ship.u.rail.arak>)}"
246+ (scot %p u.got)
103− leaf/"source desk: {?~(rail.arak <~> <desk.u.rail.arak>)}"
247+ ::
104− leaf/"source aeon: {?~(rail.arak <~> <aeon.u.rail.arak>)}"
248+ (cat 3 ' force off: ' (print-agents of))
105− leaf/"pending updates: {pen}"
249+ (cat 3 ' force on: ' (print-agents on))
250+ (cat 3 ' app status: ' sat)
251+ (cat 3 ' essential desk: ' ese)
252+ ::
253+ (cat 3 ' %cz hash: ' (scot %uv hash))
254+ (cat 3 ' base hash: ' (print-mergebases meb))
255+ (cat 3 ' /sys/kelvin: ' (print-wefts (waft-to-wefts waft)))
256+ (cat 3 '%' syd)
106 ==
257 ==
258+ ++ print-wefts
259+ |= wefts=(set weft)
260+ ^- @t
261+ ?: =(~ wefts)
262+ '~'
263+ %+ roll (sort ~(tap in wefts) aor)
264+ |= [=weft out=@t]
265+ ?: =('' out)
266+ (rap 3 '[%' lal.weft ' ' (scot %ud num.weft) ']' ~)
267+ (rap 3 out ' [%' lal.weft ' ' (scot %ud num.weft) ']' ~)
268+ ::
269+ ++ print-shorthash
270+ |= hash=@uv
271+ ^- @t
272+ (crip ((v-co:co 5) (end [0 25] hash)))
273+ ::
274+ ++ print-mergebases
275+ |= hashes=(list @uv)
276+ ^- @t
277+ ?~ hashes
278+ '~'
279+ ?~ t.hashes
280+ (scot %uv i.hashes)
281+ %+ roll `(list @uv)`hashes
282+ |= [hash=@uv out=@t]
283+ ?: =('' out)
284+ (print-shorthash hash)
285+ (rap 3 out ' ' (print-shorthash hash) ~)
286+ ::
287+ ++ print-agents
288+ |= agents=(list @tas)
289+ ^- @t
290+ ?~ agents
291+ '~'
292+ %+ roll `(list @tas)`agents
293+ |= [agent=@tas out=@tas]
294+ ?: =('' out)
295+ (cat 3 '%' agent)
296+ (rap 3 out ' %' agent ~)
297+ --
107:: +report-kids: non-vat cz hash report for kids desk
298:: +report-kids: non-vat cz hash report for kids desk
108::
299::
109++ report-kids
300++ report-kids
110 |= [our=ship now=@da]
301 |= [our=ship now=@da]
111 ^- tank
302 ^- tank
112− =/ dek %kids
303+ =/ syd %kids
113 =/ ego (scot %p our)
304 =/ ego (scot %p our)
114 =/ wen (scot %da now)
305 =/ wen (scot %da now)
115− ?. (~(has in .^((set desk) %cd /[ego]//[wen])) dek)
306+ ?. (~(has in .^((set desk) %cd /[ego]//[wen])) syd)
116− leaf/"no %kids desk"
307+ 'no %kids desk'
117− =+ .^(hash=@uv %cz /[ego]/[dek]/[wen])
308+ =+ .^(hash=@uv %cz /[ego]/[syd]/[wen])
118− leaf/"%kids %cz hash: {<hash>}"
309+ (cat 3 '%kids %cz hash: ' (scot %uv hash))
119−:: +read-kelvin-foreign: read /sys/kelvin from a foreign desk
120−::
121−++ read-kelvin-foreign
122− |= [=ship =desk =aeon]
123− ^- weft
124− ~| read-foreign-kelvin/+<
125− =/ her (scot %p ship)
126− =/ syd (scot %tas desk)
127− =/ yon (scot %ud aeon)
128− ::
129− =/ dom .^(dome cv/~[her syd yon])
130− =/ tak (scot %uv (~(got by hit.dom) let.dom))
131− =/ yak .^(yaki cs/~[her syd yon %yaki tak])
132− =/ lob (scot %uv (~(got by q.yak) /sys/kelvin))
133− =/ peg .^(page cs/~[her syd yon %blob lob])
134− ;;(weft q.peg)
135−:: +read-kelvin-local: read /sys/kelvin from a local desk
136−::
137−++ read-kelvin-local
138− |= [our=ship =desk now=@da]
139− ^- (unit weft)
140− ~| read-kelvin-local+desk
141− =/ pax (en-beam [our desk da+now] /sys/kelvin)
142− ?. .^(? cu/pax)
143− ~
144− [~ .^(weft cx/pax)]
145:: +read-bill-foreign: read /desk/bill from a foreign desk
310:: +read-bill-foreign: read /desk/bill from a foreign desk
146::
311::
147++ read-bill-foreign
312++ read-bill-foreign
@@ -152,7 +317,7 @@
152 =/ syd (scot %tas desk)
317 =/ syd (scot %tas desk)
153 =/ yon (scot %ud aeon)
318 =/ yon (scot %ud aeon)
154 ::
319 ::
155− =/ dom .^(dome cv/~[her syd yon])
320+ =/ dom .^(domo cv/~[her syd yon])
156 =/ tak ~| aeons=~(key by hit.dom)
321 =/ tak ~| aeons=~(key by hit.dom)
157 (scot %uv (~(got by hit.dom) aeon))
322 (scot %uv (~(got by hit.dom) aeon))
158 =/ yak .^(yaki cs/~[her syd yon %yaki tak])
323 =/ yak .^(yaki cs/~[her syd yon %yaki tak])
@@ -169,21 +334,6 @@
169 ?. .^(? cu/pax)
334 ?. .^(? cu/pax)
170 *(list dude)
335 *(list dude)
171 .^((list dude) cx/pax)
336 .^((list dude) cx/pax)
172−:: +adjust-dudes: which agents should be started and stopped
173−::
174−:: Will ask Gall to start agents that it's already running
175−:: but that should be ok, and might be safer in case other
176−:: unprocessed moves would have turned them off.
177−::
178−++ adjust-dudes
179− |= $: local=[our=ship =desk now=@da]
180− =rein
181− ==
182− ^- [jolt=(list dude) idle=(list dude)]
183− =/ all=(list dude) (read-bill local)
184− =/ want (get-apps-want local all rein)
185− =/ have (get-apps-live local)
186− [want (skip have ~(has in (sy want)))]
187::
337::
188++ get-remote-diff
338++ get-remote-diff
189 |= [our=ship here=desk now=@da her=ship there=desk when=aeon]
339 |= [our=ship here=desk now=@da her=ship there=desk when=aeon]
@@ -209,39 +359,19 @@
209 |= [our=ship =desk now=@da]
359 |= [our=ship =desk now=@da]
210 ^- (list [=dude live=?])
360 ^- (list [=dude live=?])
211 %~ tap in
361 %~ tap in
212− .^((set [=dude live=?]) ge+/(scot %p our)/[desk]/(scot %da now))
362+ .^((set [=dude live=?]) ge+/(scot %p our)/[desk]/(scot %da now)/$)
213−:: +get-apps-want: find which apps should be running on a desk
214−::
215−++ get-apps-want
216− |= [local=[our=ship =desk now=@da] duz=(list dude) =rein]
217− ^- (list dude)
218− ?. liv.rein ~
219− ?. |(=(`zuse+zuse (read-kelvin-local local)) =(%base desk.local)) ~
220− =. duz (skip duz ~(has in sub.rein))
221− =. duz (weld duz (skip ~(tap in add.rein) ~(has in (sy duz))))
222− duz
223::
363::
224++ mergebase-hashes
364++ mergebase-hashes
225− |= [our=@p =desk now=@da =arak]
365+ |= [our=@p syd=desk now=@da her=ship sud=desk]
226− ?~ rail.arak
227− ~
228− =/ her (scot %p ship.u.rail.arak)
366+ =/ her (scot %p her)
229 =/ ego (scot %p our)
367 =/ ego (scot %p our)
230 =/ wen (scot %da now)
368 =/ wen (scot %da now)
231− %+ turn .^((list tako) %cs ~[ego desk wen %base her desk.u.rail.arak])
369+ %+ turn .^((list tako) %cs /[ego]/[syd]/[wen]/base/[her]/[sud])
232− |=(=tako .^(@uv %cs ~[ego desk wen %hash (scot %uv tako)]))
370+ |=(=tako .^(@uv %cs /[ego]/[syd]/[wen]/hash/(scot %uv tako)))
233::
371::
234++ enjs
372++ enjs
235 =, enjs:format
373 =, enjs:format
236 |%
374 |%
237− ++ vats
238− |= v=(list ^vat)
239− ^- json
240− %- pairs
241− %+ turn v
242− |= va=^vat
243− [desk.va (vat va)]
244− ::
245 ++ tim
375 ++ tim
246 |= t=@
376 |= t=@
247 ^- json
377 ^- json
@@ -254,15 +384,6 @@
254 da+(tim da.c)
384 da+(tim da.c)
255 ==
385 ==
256 ::
386 ::
257− ++ vat
258− |= v=^vat
259− %- pairs
260− :~ desk+s+desk.v
261− hash+s+(scot %uv hash.v)
262− cass+(cass cass.v)
263− arak+(arak arak.v)
264− ==
265− ::
266 ++ weft
387 ++ weft
267 |= w=^weft
388 |= w=^weft
268 %- pairs
389 %- pairs
@@ -276,30 +397,5 @@
276 :~ aeon+(numb aeon.r)
397 :~ aeon+(numb aeon.r)
277 weft+(weft weft.r)
398 weft+(weft weft.r)
278 ==
399 ==
279− ::
280− ++ rein
281− |= r=^rein
282− %- pairs
283− :~ add+a+(turn ~(tap in add.r) (lead %s))
284− sub+a+(turn ~(tap in sub.r) (lead %s))
285− ==
286− ::
287− ++ arak
288− |= a=^arak
289− %- pairs
290− :~ rail+?~(rail.a ~ (rail u.rail.a))
291− rein+(rein rein.a)
292− ==
293− ::
294− ++ rail
295− |= r=^rail
296− %- pairs
297− :~ ship+s+(scot %p ship.r)
298− publisher+?~(publisher.r ~ s+(scot %p u.publisher.r))
299− desk+s+desk.r
300− paused+b+paused.r
301− aeon+(numb aeon.r)
302− next+a+(turn next.r rung)
303− ==
304 --
400 --
305--
401--
base-desk/sur/ring.hoon+1−1
@@ -15,7 +15,7 @@
15 :: resistance...but if your private keys are compromised, your
15 :: resistance...but if your private keys are compromised, your
16 :: adversary can determine which signatures you made.
16 :: adversary can determine which signatures you made.
17 ::
17 ::
18− y=(unit @udpoint)
18+ y=(unit @uxpoint)
19 ==
19 ==
20:: +ring-signature: higher level ring signature type
20:: +ring-signature: higher level ring signature type
21::
21::
base-desk/sur/sole.hoon+2−1
@@ -3,8 +3,9 @@
3 ::
3 ::
4^?
4^?
5|%
5|%
6++$ sole-id [who=@p ses=@ta]
6+$ sole-action :: sole to app
7+$ sole-action :: sole to app
7− $: id=@ta :: duct id
8+ $: id=sole-id :: session id
8 $= dat
9 $= dat
9 $% :: [%abo ~] :: reset interaction
10 $% :: [%abo ~] :: reset interaction
10 [%det sole-change] :: command line edit
11 [%det sole-change] :: command line edit
base-desk/sur/spider.hoon+7−1
@@ -1,7 +1,7 @@
1/+ libstrand=strand
1/+ libstrand=strand
2=, strand=strand:libstrand
2=, strand=strand:libstrand
3|%
3|%
4−+$ thread $-(vase _*form:(strand ,vase))
4++$ thread $-(vase shed:khan)
5+$ input [=tid =cage]
5+$ input [=tid =cage]
6+$ tid tid:strand
6+$ tid tid:strand
7+$ bowl bowl:strand
7+$ bowl bowl:strand
@@ -18,4 +18,10 @@
18 file=term
18 file=term
19 =vase
19 =vase
20 ==
20 ==
21++$ inline-args
22+ $: parent=(unit tid)
23+ use=(unit tid)
24+ =beak
25+ =shed:khan
26+ ==
21--
27--
base-desk/sur/sss.hoonnew file+39
@@ -0,0 +1,39 @@
1+|%
2+++ lake
3+ |$ [rock wave]
4+ $_ ^?
5+ |%
6+ ++ name *term
7+ +$ rock ^rock
8+ +$ wave ^wave
9+ ++ wash |~ [rock wave] *rock
10+ --
11++$ aeon @ud
12++$ dude dude:agent:gall
13+++ poke
14+ |%
15+ ++ request
16+ |* paths=mold
17+ $: path=paths
18+ =dude
19+ ==
20+ ++ response
21+ |* [=(lake) paths=mold]
22+ $: path=paths
23+ =dude
24+ $% [what=%tomb ~]
25+ [what=%rock =aeon =rock:lake]
26+ [what=%wave =aeon =wave:lake]
27+ == ==
28+ ++ on-rock
29+ |* [=(lake) paths=mold]
30+ $: path=paths
31+ src=ship
32+ from=dude
33+ stale=?
34+ fail=?
35+ =rock:lake
36+ wave=(unit wave:lake)
37+ ==
38+ --
39+--
base-desk/sur/verb.hoon+37−1
@@ -9,4 +9,40 @@
9 [%on-arvo =wire vane=term sign=term]
9 [%on-arvo =wire vane=term sign=term]
10 [%on-fail =term]
10 [%on-fail =term]
11 ==
11 ==
12+::
13++$ event-plus
14+ $: act=@ud
15+ now=@da
16+ src=@p
17+ sap=path
18+ =cause
19+ effects=(list effect)
20+ ==
21+::
22++$ cause
23+ $% [%on-init ~]
24+ [%on-load ~]
25+ [%on-poke =mark mug=@ux]
26+ [%on-watch =path]
27+ [%on-leave =path]
28+ [%on-agent =wire =sign]
29+ [%on-arvo =wire vane=term sign=term]
30+ [%on-fail =term]
31+ ==
32+::
33++$ sign
34+ $% [%poke-ack ack=?]
35+ [%watch-ack ack=?]
36+ [%kick ~]
37+ [%fact =mark mug=@ux]
38+ ==
39+::
40++$ effect
41+ $% [%poke =wire =gill:gall =mark mug=@ux]
42+ [%watch =wire =gill:gall =path]
43+ [%leave =wire =gill:gall]
44+ [%fact paths=(list path) =mark mug=@ux]
45+ [%kick paths=(list path)]
46+ [%arvo =wire vane=term task=term]
47+ ==
12−--
48+--
bin/make_glob.pynew file+208
@@ -0,0 +1,208 @@
1+#!/usr/bin/env python3
2+
3+import hashlib
4+import sys
5+from pathlib import Path
6+from urllib.parse import quote
7+
8+from noun import Cell, deep, intbytes, mug
9+
10+MIME_TYPES = {
11+ "css": "text/css",
12+ "gif": "image/gif",
13+ "html": "text/html",
14+ "ico": "image/x-icon",
15+ "jpeg": "image/jpeg",
16+ "jpg": "image/jpeg",
17+ "js": "application/javascript",
18+ "json": "application/json",
19+ "png": "image/png",
20+ "svg": "image/svg+xml",
21+ "ttf": "font/ttf",
22+ "webmanifest": "application/manifest+json",
23+ "woff": "font/woff",
24+ "woff2": "font/woff2",
25+}
26+BASE32 = "0123456789abcdefghijklmnopqrstuv"
27+
28+
29+class BitWriter:
30+ def __init__(self):
31+ self.output = bytearray()
32+ self.buffer = 0
33+ self.used = 0
34+ self.cursor = 0
35+
36+ def write(self, value, count):
37+ if count == 0:
38+ return
39+ self.buffer |= value << self.used
40+ self.used += count
41+ self.cursor += count
42+ byte_count = self.used // 8
43+ if byte_count:
44+ self.output.extend(self.buffer.to_bytes((self.used + 7) // 8, "little")[:byte_count])
45+ self.buffer >>= byte_count * 8
46+ self.used -= byte_count * 8
47+
48+ def finish(self):
49+ if self.used:
50+ self.output.append(self.buffer)
51+ self.buffer = 0
52+ self.used = 0
53+ return bytes(self.output)
54+
55+
56+def jam_bytes(value):
57+ writer = BitWriter()
58+ references = {}
59+
60+ def save(noun):
61+ references[noun] = writer.cursor
62+
63+ def mat(atom):
64+ if atom == 0:
65+ writer.write(1, 1)
66+ return
67+ atom_length = atom.bit_length()
68+ length_size = atom_length.bit_length()
69+ writer.write(1 << length_size, length_size + 1)
70+ writer.write(atom_length & ((1 << (length_size - 1)) - 1), length_size - 1)
71+ writer.write(atom, atom_length)
72+
73+ def back(reference):
74+ writer.write(3, 2)
75+ mat(reference)
76+
77+ def serialize(noun):
78+ duplicate = references.get(noun)
79+ if deep(noun):
80+ if duplicate is not None:
81+ back(duplicate)
82+ return
83+ save(noun)
84+ writer.write(1, 1)
85+ writer.write(0, 1)
86+ serialize(noun.head)
87+ serialize(noun.tail)
88+ return
89+ if duplicate is not None:
90+ atom_size = noun.bit_length()
91+ reference_size = duplicate.bit_length()
92+ if atom_size >= reference_size:
93+ back(duplicate)
94+ return
95+ save(noun)
96+ writer.write(0, 1)
97+ mat(noun)
98+
99+ serialize(value)
100+ return writer.finish()
101+
102+
103+def cord(value):
104+ return int.from_bytes(value.encode(), "little")
105+
106+
107+def path_noun(parts):
108+ result = 0
109+ for part in reversed(parts):
110+ result = Cell(cord(part), result)
111+ return result
112+
113+
114+def dor(a, b):
115+ if a == b:
116+ return True
117+ if deep(a):
118+ if not deep(b):
119+ return False
120+ if a.head == b.head:
121+ return dor(a.tail, b.tail)
122+ return dor(a.head, b.head)
123+ if deep(b):
124+ return True
125+ return a < b
126+
127+
128+def gor(a, b):
129+ a_mug = mug(a)
130+ b_mug = mug(b)
131+ return dor(a, b) if a_mug == b_mug else a_mug < b_mug
132+
133+
134+def map_put(tree, entry):
135+ if tree == 0:
136+ return Cell(entry, Cell(0, 0))
137+ node = tree.head
138+ if entry.head == node.head:
139+ return Cell(entry, tree.tail)
140+ left = tree.tail.head
141+ right = tree.tail.tail
142+ if gor(entry.head, node.head):
143+ return Cell(node, Cell(map_put(left, entry), right))
144+ return Cell(node, Cell(left, map_put(right, entry)))
145+
146+
147+def shas(salt, serialized):
148+ value_hash = int.from_bytes(hashlib.sha256(serialized).digest(), "little")
149+ length = max(32, len(intbytes(salt)))
150+ digest = hashlib.sha256((salt ^ value_hash).to_bytes(length, "little")).digest()
151+ return int.from_bytes(digest, "little")
152+
153+
154+def sham(serialized):
155+ hashed = shas(cord("sham"), serialized)
156+ mask = (1 << 128) - 1
157+ return (hashed & mask) ^ (hashed >> 128)
158+
159+
160+def scow_uv(value):
161+ if value == 0:
162+ return "0v0"
163+ digits = []
164+ while value:
165+ value, digit = divmod(value, 32)
166+ digits.append(BASE32[digit])
167+ digits = "".join(reversed(digits))
168+ first_length = (len(digits) - 1) % 5 + 1
169+ groups = [digits[:first_length]]
170+ groups.extend(digits[i:i + 5] for i in range(first_length, len(digits), 5))
171+ return "0v" + ".".join(groups)
172+
173+
174+def file_entry(root, file_path):
175+ relative = file_path.relative_to(root)
176+ parts = []
177+ for part in relative.parts:
178+ parts.extend(quote(segment, safe="-_~") for segment in part.split("."))
179+ extension = relative.suffix.removeprefix(".").lower()
180+ if extension not in MIME_TYPES:
181+ raise ValueError(f"no MIME type configured for {relative}")
182+ data = file_path.read_bytes()
183+ mime = Cell(path_noun(MIME_TYPES[extension].split("/")), Cell(len(data), int.from_bytes(data, "little")))
184+ return Cell(path_noun(parts), mime)
185+
186+
187+def make_glob(source, destination):
188+ root = Path(source)
189+ files = sorted((path for path in root.rglob("*") if path.is_file()), key=lambda path: path.as_posix())
190+ if not (root / "index.html").is_file():
191+ raise ValueError("glob source must contain index.html")
192+ glob = 0
193+ for file_path in files:
194+ glob = map_put(glob, file_entry(root, file_path))
195+ serialized = jam_bytes(glob)
196+ glob_hash = scow_uv(sham(serialized))
197+ output = Path(destination)
198+ output.mkdir(parents=True, exist_ok=True)
199+ filename = f"glob-{glob_hash}.glob"
200+ (output / filename).write_bytes(serialized)
201+ (output / "hash").write_text(glob_hash + "\n")
202+ print(filename)
203+
204+
205+if __name__ == "__main__":
206+ if len(sys.argv) != 3:
207+ raise SystemExit(f"usage: {sys.argv[0]} SOURCE_DIRECTORY OUTPUT_DIRECTORY")
208+ make_glob(sys.argv[1], sys.argv[2])
desk/app/astrolabe.hoon+40−48
@@ -1,35 +1,12 @@
1/- *astrolabe
1/- *astrolabe
2+/+ *astrolabe, *search, slib=state
2−/+ *astrolabe, *search, *mip, naive, default-agent, dbug, agentio
3+/+ *mip, naive, default-agent, dbug, agentio
3/$ udon-to-docu %udon %docu
4/$ udon-to-docu %udon %docu
4|%
5|%
5−+$ versioned-state
6− $% state-0
7− state-1
8− state-2
9− ==
10−+$ state-0
11− $: %0
12− spa=spawned
13− ==
14−+$ fropoints-1
15− $: fopoints=opoints :: forward ordered points
16− ropoints=opoints :: reversed ordered points, e.g. .~palnet-sampel
17− ==
18−+$ state-1
19− $: %1
20− fropoints-1
21− ==
22−+$ state-2
23− $: %2
24− spam=spawn-mip
25− fropoints-1
26− ==
27−::
28−+$ spawned (jug ship ship)
29+$ card card:agent:gall
6+$ card card:agent:gall
30--
7--
31%- agent:dbug
8%- agent:dbug
32−=| state-2
9+=| state-latest
33=* state -
10=* state -
34^- agent:gall
11^- agent:gall
35=<
12=<
@@ -41,18 +18,28 @@
41++ on-init
18++ on-init
42 ^- (quip card _this)
19 ^- (quip card _this)
43 :_ this
20 :_ this
21+ :~ ug-handler-card:hc
22+ groups-link-patch-card:hc
23+ talk-link-patch-card:hc
44− [az-events-card:hc]~
24+ az-events-card:hc
25+ ==
45::
26::
46++ on-save !>(state)
27++ on-save !>(state)
47++ on-load
28++ on-load
48 |= old-state=vase
29 |= old-state=vase
49 ^- (quip card _this)
30 ^- (quip card _this)
50 =/ old !<(versioned-state old-state)
31 =/ old !<(versioned-state old-state)
51− =^ cards-01 old
32+ =/ [cards-01=(list card) new=state-latest]
52− ?. ?=(?(%0 %1) -.old) `old
33+ ?. |(?=(?(%0 %1 %2 %3) -.old) (lth bump.old bump)) `old
53 (on-naive-state:hc get-nas:hc)
34 (on-naive-state:hc get-nas:hc)
54− ?> ?=(%2 -.old)
35+ ?> ?=(%4 -.new)
36+ =/ cards=(list card)
37+ :~ ug-handler-card:hc
38+ groups-link-patch-card:hc
39+ talk-link-patch-card:hc
40+ ==
41+ =. cards (weld cards-01 cards)
55− [cards-01 this(state old)]
42+ [cards this(state new)]
56++ on-poke on-poke:def
43++ on-poke on-poke:def
57++ on-watch on-watch:def
44++ on-watch on-watch:def
58++ on-leave on-leave:def
45++ on-leave on-leave:def
@@ -61,11 +48,14 @@
61 |= =path
48 |= =path
62 ^- (unit (unit cage))
49 ^- (unit (unit cage))
63 ?+ path (on-peek:def path)
50 ?+ path (on-peek:def path)
51+ [%x %nop ~]
52+ ``noun+!>(~)
64 [%x %point @ ~]
53 [%x %point @ ~]
65 ``astrolabe-point+!>((get-point:hc &3.path))
54 ``astrolabe-point+!>((get-point:hc &3.path))
66 [%x %point @ %spawned ?(~ [spawned-filter ~])]
55 [%x %point @ %spawned ?(~ [spawned-filter ~])]
67 =/ =ship `@p`(slav %p &3.path)
56 =/ =ship `@p`(slav %p &3.path)
68− =/ arg ?~(t.t.t.t.path %all i.t.t.t.t.path)
57+ =/ filter t.t.t.t.path
58+ =/ arg ?~(filter %all -.filter)
69 ``astrolabe-point-set+!>((get-spawned-points:hc ship arg))
59 ``astrolabe-point-set+!>((get-spawned-points:hc ship arg))
70 [%x %search %patp @ ?(~ [%narrow ~])]
60 [%x %search %patp @ ?(~ [%narrow ~])]
71 =/ =search-text (trip &4.path)
61 =/ =search-text (trip &4.path)
@@ -147,22 +137,7 @@
147 |= [child=ship nas=^state:naive]
137 |= [child=ship nas=^state:naive]
148 ^- _state
138 ^- _state
149 =/ =npoint (get-npoint-from-nas child nas)
139 =/ =npoint (get-npoint-from-nas child nas)
150− =. spam
140+ (put-in-spawn-mip:slib child npoint state)
151− =/ parent (^sein:title child)
152− ?: =(parent child) spam
153− ?: (is-npoint-locked npoint) spam
154− =/ pdata (pdata:smel (pmeta:smel child npoint) ~)
155− =* spo sponsor.net.npoint
156− =? spam &(has.spo !=(parent who.spo))
157− %^ ~(put bi-meta spam)
158− who.spo
159− child
160− pdata
161− %^ ~(put bi-meta spam)
162− parent
163− child
164− pdata
165− state
166::
141::
167++ on-naive-state
142++ on-naive-state
168 |= nas=^state:naive
143 |= nas=^state:naive
@@ -339,6 +314,23 @@
339 (pdata:smel (pmeta:smel gal npoint) ~)
314 (pdata:smel (pmeta:smel gal npoint) ~)
340 s+(crip :(weld "[" gal-data main-data "]"))
315 s+(crip :(weld "[" gal-data main-data "]"))
341::
316::
317+::
318+++ ug-handler-card
319+ =/ ug-vase !>([/astrolabe '/apps/astrolabe/#'])
320+ [%pass /monkey-ug %agent [our.bowl %monkey] %poke [%bind-ug ug-vase]]
321+++ groups-link-patch-card
322+ =/ catch [%cord '/apps/groups/desk.js']
323+ =/ hatch [%before "."]
324+ =/ patch [/apps/groups catch hatch groups-link-thatch]
325+ =/ patch-kit-vase !>([/groups/astrolabe/link patch])
326+ [%pass /monkey/patch/groups/link %agent [our.bowl %monkey] %poke [%patch patch-kit-vase]]
327+++ talk-link-patch-card
328+ =/ catch [%cord '/apps/talk/desk.js']
329+ =/ hatch [%before "."]
330+ =/ patch [/apps/talk catch hatch groups-link-thatch]
331+ =/ patch-kit-vase !>([/talk/astrolabe/link patch])
332+ [%pass /monkey/patch/talk/link %agent [our.bowl %monkey] %poke [%patch patch-kit-vase]]
333+::
342++ read-doc
334++ read-doc
343 |= =path
335 |= =path
344 =/ doc-path=^path :(weld /doc/usr path /udon)
336 =/ doc-path=^path :(weld /doc/usr path /udon)
desk/desk.docket-0+3−3
@@ -1,10 +1,10 @@
1:~ title+'Astrolabe'
1:~ title+'Astrolabe'
2− info+'Your guide to the Urbit constellations'
2+ info+'Your guide to the Urbit constellations. Search for ships, view their info, and explore the Star Chart.'
3 color+0x1f.243c
3 color+0x1f.243c
4 image+'https://raw.githubusercontent.com/johnhyde/astrolabe/main/ui/src/assets/astrolabe-tile.svg'
4 image+'https://raw.githubusercontent.com/johnhyde/astrolabe/main/ui/src/assets/astrolabe-tile.svg'
5− glob-http+['https://s3.us-east-2.amazonaws.com/warkgnall.com/urbit/astrolabe/globs/glob-0v1.pc6av.k7vu1.4nkef.lgob3.u6tae.glob' 0v1.pc6av.k7vu1.4nkef.lgob3.u6tae]
5+ glob-http+['https://s3.us-east-2.amazonaws.com/warkgnall.com/urbit/astrolabe/globs/glob-0v4.88rt1.guqn5.nnatp.lca68.ej9aj.glob' 0v4.88rt1.guqn5.nnatp.lca68.ej9aj]
6 base+'astrolabe'
6 base+'astrolabe'
7− version+[0 3 2]
7+ version+[1 0 7]
8 website+'https://github.com/johnhyde/astrolabe'
8 website+'https://github.com/johnhyde/astrolabe'
9 license+'MIT'
9 license+'MIT'
10==
10==
desk/desk.sealnew file+7
@@ -0,0 +1,7 @@
1+:- :~ [%watch & %azimuth /event]
2+ [%reads %gall `%x `%azimuth /]
3+ [%reads %clay `%u ~ /]
4+ [%reads %clay `%x ~ /]
5+ [%reads %ames `%x ~ /peers]
6+ ==
7+~
desk/doc.toc+2−1
@@ -1,4 +1,4 @@
1−/overview/udon About Astrolabe
1+/overview/udon Overview
2/usr User
2/usr User
3 /urbit-id/udon What is an Urbit ID?
3 /urbit-id/udon What is an Urbit ID?
4 /galaxy/udon Galaxies
4 /galaxy/udon Galaxies
@@ -12,3 +12,4 @@
12 /azimuth/udon What is Azimuth?
12 /azimuth/udon What is Azimuth?
13 /l2/udon Layer 1 and Layer 2
13 /l2/udon Layer 1 and Layer 2
14 /keys/udon Networking Keys
14 /keys/udon Networking Keys
15+ /astrolabe/udon About Astrolabe
desk/doc/overview.udon+2
@@ -5,6 +5,8 @@ Currently Astrolabe lets you input `@p`s, Azimuth point numbers, or sigils
5to look up ships on the network. Use the Star Chart to explore visually.
5to look up ships on the network. Use the Star Chart to explore visually.
6There's also Help Documentation if you want to learn more about how Urbit IDs work.
6There's also Help Documentation if you want to learn more about how Urbit IDs work.
7
7
8+[How to Use Astrolabe](/~/scry/docs/usr/astrolabe/astrolabe.html)
9+
8## Info
10## Info
9
11
10- Developer: ~midlev-mindyr
12- Developer: ~midlev-mindyr
desk/doc/usr/astrolabe.udon+20−9
@@ -9,6 +9,16 @@ spawned ships with partial or wildcard searches, e.g. `sampel-***net`.
9There's also Help Documentation if
9There's also Help Documentation if
10you want to learn more about how Urbit IDs work.
10you want to learn more about how Urbit IDs work.
11
11
12+## Keyboard Shortcuts
13+
14+- `/` - focus the search bar
15+- `escape` - leave text fields, close help pages, etc
16+- `?` - open the help docs
17+- `s` - switch to sigil search
18+- `p` - switch to patp search
19+- `backspace` or `x` - clear sigil input info
20+
21+
12## How to Use Search
22## How to Use Search
13
23
14When you type part of a `@p` into the search bar, all your contacts and peers will be
24When you type part of a `@p` into the search bar, all your contacts and peers will be
@@ -32,20 +42,21 @@ Clicking the symbol at the right of the search bar opens the Sigil Input Tool.
32Use the dropdown at the top to select Galaxy, Star, or Planet.
42Use the dropdown at the top to select Galaxy, Star, or Planet.
33Click on a square to start selecting sigil components.
43Click on a square to start selecting sigil components.
34
44
35−Select component catogeries on the right, and then tap the options on the sigil
45+Select component categories on the right, and then click the options on the sigil
36to add them to your search.
46to add them to your search.
37
47
38The ^ button goes up a level so you can select another category.
48The ^ button goes up a level so you can select another category.
39−The X button will clear sigil components based on what type you're inputting.
49+The X button will clear sigil components from the current symbol,
50+except the basic shape. If only the basic shape remains, the X button will clear that.
40At the top level, the X button will clear all inputs.
51At the top level, the X button will clear all inputs.
41
52
42−Your contacts will be searched for any search.
53+Your contacts will be searched for any input.
43When the search has become specific enough,
54When the search has become specific enough,
44all spawned ships with matching sigils will be shown.
55all spawned ships with matching sigils will be shown.
45If your input is unambiguous, the page for the matching ship will be shown,
56If your input is unambiguous, the page for the matching ship will be shown,
46whether the ship is spawned or not.
57whether the ship is spawned or not.
47
58
48−NOTE: Some sigil inputs are considered ambiguous even if they exactly match
59+NOTE: Some sigil inputs are considered ambiguous, even if they exactly match
49a particular sigil, because further details could match a different sigil.
60a particular sigil, because further details could match a different sigil.
50E.g. `~zod` is a circle, but a circle in the suffix position might be intended as a more general search.
61E.g. `~zod` is a circle, but a circle in the suffix position might be intended as a more general search.
51A future version of Astrolabe will allow you to view the ship
62A future version of Astrolabe will allow you to view the ship
@@ -55,7 +66,7 @@ Read more about Sigils [here](/~/scry/docs/usr/astrolabe/sigil.html).
55
66
56## The Star Chart
67## The Star Chart
57
68
58−Click and drag to move your view, use the mouse wheel to zoom in and out. Click on a dot
69+Click and drag to move your view; use the mouse wheel to zoom in and out. Click on a dot
59to see the full Ship View. Enter full `@p`s in the search bar to find a specific point.
70to see the full Ship View. Enter full `@p`s in the search bar to find a specific point.
60
71
61Please note that stars [locked in the Linear Release Contract](/~/scry/docs/usr/astrolabe/star.html)
72Please note that stars [locked in the Linear Release Contract](/~/scry/docs/usr/astrolabe/star.html)
@@ -63,16 +74,16 @@ are not shown in the Star Chart, as they are not yet a part of the network (thou
63been spawned).
74been spawned).
64
75
65The Sponsors/Parents toggle lets you switch between two different views of the network.
76The Sponsors/Parents toggle lets you switch between two different views of the network.
66−The default Sponsors view shows each planet under its sponsoring star, and each
77+The default Sponsors view shows each planet under its sponsoring star and each
67star under its sponsoring galaxy. Planets and Stars without sponsors are not
78star under its sponsoring galaxy. Planets and Stars without sponsors are not
68present in this view.
79present in this view.
69The Parent view simply shows each point under the point that spawned it. All spawned points
80The Parent view simply shows each point under the point that spawned it. All spawned points
70are shown in this view, with the exception of locked stars.
81are shown in this view, with the exception of locked stars.
71In both views, all galaxies are shown for completeness.
82In both views, all galaxies are shown for completeness.
72
83
73−## Upcoming Features
84+The All/Peers/Pals selector lets you filter which points are easily visible in the graph.
74−
85+If you select Peers, only those ships that your ship has talked to will be full-colored.
75−In addition to improving the search features over time, Astrolabe will one day feature ntegrations with Groups, `%pals`, `%gora`, and whatever else seems fun and/or useful.
86+If you select Pals, only ships you've added in `%pals` will be full-colored.
76
87
77## Info
88## Info
78
89
desk/doc/usr/sigil-input.udonnew file+27
@@ -0,0 +1,27 @@
1+;>
2+# How to Search by Sigil
3+
4+Use the dropdown at the top to select Galaxy, Star, or Planet.
5+Click on an empty square to start selecting sigil components.
6+
7+Select component categories on the right, and then click the options on the sigil
8+to add them to your search.
9+
10+The ^ button goes up a level so you can select another category.
11+The X button will clear sigil components from the current symbol,
12+except the basic shape. If only the basic shape remains, the X button will clear that.
13+At the top level, the X button will clear all inputs.
14+
15+Your contacts will be searched for any input.
16+When the search has become specific enough,
17+all spawned ships with matching sigils will be shown.
18+If your input is unambiguous, the page for the matching ship will be shown,
19+whether the ship is spawned or not.
20+
21+_NOTE:_ Some sigil inputs are considered ambiguous, even if they exactly match
22+a particular sigil, because further details could match a different sigil.
23+E.g. `~zod` is a circle, but a circle in the suffix position might be intended as a more general search.
24+A future version of Astrolabe will allow you to view the ship
25+whose sigil most closely matches your search, even if the search is ambiguous.
26+
27+_See Also:_ [*What is a Sigil?*](/~/scry/docs/usr/astrolabe/sigil.html)
desk/doc/usr/sigil.udon+4−3
@@ -2,11 +2,11 @@
2# What is a Sigil?
2# What is a Sigil?
3
3
4It's a default profile picture, based on your [`@p`](/~/scry/docs/usr/astrolabe/patpee.html).
4It's a default profile picture, based on your [`@p`](/~/scry/docs/usr/astrolabe/patpee.html).
5−Each 3-letter syllable corresponds to sigil shape, or symbol.
5+Each 3-letter syllable corresponds to a sigil shape, or "symbol".
6Since planet `@p`s have 4 syllables, a planet sigil is a 2x2 grid of 4 symbols.
6Since planet `@p`s have 4 syllables, a planet sigil is a 2x2 grid of 4 symbols.
7−Star sigils have 2 symbols, and galaxies just 1.
7+Star sigils have 2 symbols, and galaxies have just 1.
8
8
9−![planet sigils](/apps/astrolabe/src/assets/sigil-example.png)
9+![planet sigils](/apps/astrolabe/sigil-example.png)
10
10
11## Is there a pattern to the sigil shapes?
11## Is there a pattern to the sigil shapes?
12
12
@@ -19,3 +19,4 @@ with a focus on visual design rather than algorithmic purity.
19Tlon saw that Urbit IDs need a visual representation,
19Tlon saw that Urbit IDs need a visual representation,
20so they designed a system for generating default avatars.
20so they designed a system for generating default avatars.
21You can read about it in the blog post [_Creating Sigils_](https://urbit.org/blog/creating-sigils).
21You can read about it in the blog post [_Creating Sigils_](https://urbit.org/blog/creating-sigils).
22+
desk/lib/astrolabe.hoon+29−2
@@ -1,5 +1,5 @@
1/- sur=astrolabe
1/- sur=astrolabe
2−/+ azimuth, naive, mip
2+/+ azimuth, naive
3=< [sur .]
3=< [sur .]
4=, sur
4=, sur
5|%
5|%
@@ -25,7 +25,6 @@
25 ^- ?
25 ^- ?
26 (rev-f lte a b)
26 (rev-f lte a b)
27::
27::
28−+$ opoints (tree [@ @]) :: ordered points [ship ~]
29++ ors ((on @q @) rpor)
28++ ors ((on @q @) rpor)
30++ or-meta ((on @p tape) por:naive)
29++ or-meta ((on @p tape) por:naive)
31++ bi-meta
30++ bi-meta
@@ -204,4 +203,32 @@
204 ^- $-(json [%share ship])
203 ^- $-(json [%share ship])
205 (of share+(su ;~(pfix sig fed:ag)) ~)
204 (of share+(su ;~(pfix sig fed:ag)) ~)
206 --
205 --
206+::
207+::
208+++ groups-link-thatch
209+'''
210+const buttonText = 'View in Astrolabe';
211+setInterval(() => {
212+ const footer = document.querySelectorAll('section > div > footer')?.[0];
213+ if (!footer) return;
214+ if (footer.children[0].innerText === buttonText) return;
215+ const spans = document.querySelectorAll('section > div > div > div > span > span:first-child');
216+ spans.forEach((span) => {
217+ if (span.innerHTML !== '~') return;
218+ if (footer.children[0].innerText === buttonText) return;
219+ let cspan = span.parentElement;
220+ let patp = cspan.innerText;
221+ let b = document.createElement('a');
222+ b.innerHTML = buttonText;
223+ b.className = 'button';
224+ // b.href = 'web+urbitgraph://astrolabe/ship/' + patp;
225+ b.href = '/apps/astrolabe/#/ship/' + patp;
226+ b.target = '_blank';
227+ b.style.backgroundColor = "navy";
228+ b.style.marginLeft = "1rem";
229+ b.style.color = "white";
230+ footer.prepend(b);
231+ });
232+}, 100);
233+'''
207--
234--
desk/lib/state.hoonnew file+21
@@ -0,0 +1,21 @@
1+/- *astrolabe
2+/+ *astrolabe
3+|%
4+++ put-in-spawn-mip
5+ |= [child=ship =npoint s=state-latest]
6+ ^- _s
7+ =/ parent (^sein:title child)
8+ ?: =(parent child) s
9+ ?: (is-npoint-locked npoint) s
10+ =/ pdata (pdata:smel (pmeta:smel child npoint) ~)
11+ =. spam.s (~(put bi spam.s) parent child pdata)
12+ =* spo sponsor.net.npoint
13+ =/ prev (~(gut by prev-spo.s) child parent)
14+ =. prev-spo.s (~(del by prev-spo.s) child)
15+ =? spam.s !=(parent prev)
16+ (~(del bi spam.s) prev child)
17+ ?. has.spo s
18+ =. prev-spo.s (~(put by prev-spo.s) child who.spo)
19+ =. spam.s (~(put bi spam.s) who.spo child pdata)
20+ s
21+--
desk/mar/seal.hoonnew file+12
@@ -0,0 +1,12 @@
1+|_ dat=@
2+++ grow
3+ |%
4+ ++ mime [/text (as-octs:mimes:html dat)]
5+ --
6+++ grab
7+ |%
8+ ++ mime |=([p=mite q=octs] q.q)
9+ ++ noun @
10+ --
11+++ grad %mime
12+--
desk/sur/astrolabe.hoon+43−1
@@ -1,5 +1,48 @@
1/+ naive, *mip
1/+ naive, *mip
2|%
2|%
3++$ versioned-state
4+ $% state-0
5+ state-1
6+ state-2
7+ state-3
8+ state-4
9+ ==
10++$ state-latest state-4
11++$ state-0
12+ $: %0
13+ spa=spawned
14+ ==
15++$ state-1
16+ $: %1
17+ fropoints-1
18+ ==
19++$ state-2
20+ $: %2
21+ spam=spawn-mip
22+ fropoints-1
23+ ==
24++$ state-3
25+ $: %3
26+ prev-spo=(map ship ship) :: so we know who the old sponsor was when sponsor changes
27+ spam=spawn-mip
28+ fropoints-1
29+ ==
30++$ state-4
31+ $: %4
32+ bump=_1 :: bump to update state without changing the type
33+ prev-spo=(map ship ship) :: so we know who the old sponsor was when sponsor changes
34+ spam=spawn-mip
35+ fropoints-1
36+ ==
37+::
38++$ fropoints-1
39+ $: fopoints=opoints :: forward ordered points
40+ ropoints=opoints :: reversed ordered points, e.g. .~palnet-sampel
41+ ==
42++$ spawned (jug ship ship)
43++$ spawn-mip (mip ship ship tape)
44++$ opoints (tree [@ @]) :: ordered points [ship ~]
45+::
3+$ npoint point:naive
46+$ npoint point:naive
4++ unpoint (unit npoint)
47++ unpoint (unit npoint)
5+$ point
48+$ point
@@ -11,6 +54,5 @@
11 ==
54 ==
12+$ spawned-filter ?(%all %locked %unlocked)
55+$ spawned-filter ?(%all %locked %unlocked)
13::
56::
14−+$ spawn-mip (mip ship ship tape)
15::
57::
16--
58--
desk/sys.kelvin+1−1
@@ -1 +1 @@
1−[%zuse 418]
1+[%zuse 408]
desk/tests/lib/state.hoonnew file+59
@@ -0,0 +1,59 @@
1+/- *astrolabe
2+/+ *astrolabe, *state, *mip, *test
3+|%
4+++ test-put-in-spawn-mip
5+ =| state=state-latest
6+ =/ moved-to-bus=npoint
7+ =< .(who.sponsor.net ~bus)
8+ *npoint
9+ =/ moved-to-hec=npoint
10+ =< .(who.sponsor.net ~hec)
11+ *npoint
12+ ;: weld
13+ %+ expect-eq
14+ !> :-
15+ `~zod :: new prev-spo
16+ "marzod:s;" :: spawn-mip has something under ~zod
17+ !> =- :-
18+ (~(get by prev-spo) ~marzod)
19+ (~(gut bi spam) ~zod ~marzod "nothing!")
20+ (put-in-spawn-mip ~marzod *npoint state)
21+ ::
22+ %+ expect-eq
23+ !> :*
24+ `~hec :: new prev-spo
25+ "marzod:s:hec!zod;" :: under ~zod
26+ %.n :: nothing under ~bus
27+ "marzod:s:hec!zod;" :: under ~hec
28+ ==
29+ !>
30+ =. state (put-in-spawn-mip ~marzod *npoint state)
31+ =. state (put-in-spawn-mip ~marzod moved-to-bus state)
32+ =+ (put-in-spawn-mip ~marzod moved-to-hec state)
33+ :*
34+ (~(get by prev-spo) ~marzod)
35+ (~(gut bi spam) ~zod ~marzod "nothing!")
36+ (~(has by spam) ~bus)
37+ (~(gut bi spam) ~hec ~marzod "nothing!")
38+ ==
39+ ::
40+ %+ expect-eq
41+ !> :*
42+ `~zod :: new prev-spo
43+ "marzod:s;" :: under ~zod
44+ %.n :: nothing under ~bus
45+ %.n :: nothing under ~hec
46+ ==
47+ !>
48+ =. state (put-in-spawn-mip ~marzod *npoint state)
49+ =. state (put-in-spawn-mip ~marzod moved-to-bus state)
50+ =. state (put-in-spawn-mip ~marzod moved-to-hec state)
51+ =+ (put-in-spawn-mip ~marzod *npoint state)
52+ :*
53+ (~(get by prev-spo) ~marzod)
54+ (~(gut bi spam) ~zod ~marzod "nothing!")
55+ (~(has by spam) ~bus)
56+ (~(has by spam) ~hec)
57+ ==
58+ ==
59+--
flake.locknew file+27
@@ -0,0 +1,27 @@
1+{
2+ "nodes": {
3+ "nixpkgs": {
4+ "locked": {
5+ "lastModified": 1793341285,
6+ "narHash": "sha256-Y1CiZ4SoBUjLQyQM/pY+rS2dXD3CHLPstF0lmOc73D4=",
7+ "owner": "NixOS",
8+ "repo": "nixpkgs",
9+ "rev": "1c89083e451e891152365b799595cf7790a43325",
10+ "type": "github"
11+ },
12+ "original": {
13+ "owner": "NixOS",
14+ "ref": "nixos-unstable",
15+ "repo": "nixpkgs",
16+ "type": "github"
17+ }
18+ },
19+ "root": {
20+ "inputs": {
21+ "nixpkgs": "nixpkgs"
22+ }
23+ }
24+ },
25+ "root": "root",
26+ "version": 7
27+}
flake.nixnew file+73
@@ -0,0 +1,73 @@
1+{
2+ description = "Astrolabe Urbit desk";
3+
4+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
5+
6+ outputs = { self, nixpkgs }:
7+ let
8+ systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
9+ forAllSystems = nixpkgs.lib.genAttrs systems;
10+ in
11+ {
12+ packages = forAllSystems (system:
13+ let
14+ pkgs = import nixpkgs { inherit system; };
15+ nounPy = pkgs.fetchurl {
16+ name = "noun.py";
17+ url = "https://raw.githubusercontent.com/urbit/tools/f9cda82a7699cad0c1c0f8c031a59e93f028e548/pkg/pynoun/noun.py";
18+ hash = "sha256-vSKQ/UuSmGTANfW5c3I9lQcUFycvqmu5gqysFuP5SAA=";
19+ };
20+ ui = pkgs.buildNpmPackage {
21+ pname = "astrolabe-ui";
22+ version = "0.3.0";
23+ src = ./ui;
24+ nodejs = pkgs.nodejs_22;
25+ npmDepsHash = "sha256-jCkfgNgBmq0mKjmXBbPwdpzBR/E09OgS9XD6O9hRHfw=";
26+ makeCacheWritable = true;
27+ npmFlags = [ "--legacy-peer-deps" ];
28+ npmBuildScript = "build";
29+
30+ installPhase = ''
31+ runHook preInstall
32+ cp -r dist $out
33+ runHook postInstall
34+ '';
35+ };
36+ in
37+ rec {
38+ inherit ui;
39+ desk = pkgs.stdenvNoCC.mkDerivation {
40+ pname = "astrolabe-desk";
41+ version = "1.0.7";
42+ dontUnpack = true;
43+ nativeBuildInputs = [ pkgs.rsync ];
44+
45+ installPhase = ''
46+ runHook preInstall
47+ mkdir -p $out
48+ rsync -r --copy-links --exclude-from=${./config/ignore_files_on_install.txt} ${./base-desk}/ $out/
49+ rsync -r --copy-links --exclude-from=${./config/ignore_files_on_install.txt} ${./docs-dev-desk}/ $out/
50+ rsync -r --copy-links --exclude-from=${./config/ignore_files_on_install.txt} ${./desk}/ $out/
51+ runHook postInstall
52+ '';
53+ };
54+ glob = pkgs.stdenvNoCC.mkDerivation {
55+ pname = "astrolabe-glob";
56+ version = "0.3.0";
57+ dontUnpack = true;
58+ nativeBuildInputs = [
59+ (pkgs.python3.withPackages (ps: [ ps.mmh3 ps.bitstring ]))
60+ ];
61+
62+ installPhase = ''
63+ runHook preInstall
64+ mkdir -p $TMPDIR/pynoun
65+ ln -s ${nounPy} $TMPDIR/pynoun/noun.py
66+ PYTHONPATH=$TMPDIR/pynoun python ${./bin/make_glob.py} ${ui} $out
67+ runHook postInstall
68+ '';
69+ };
70+ default = desk;
71+ });
72+ };
73+}
ui/index.html+1
@@ -8,6 +8,7 @@
8 <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
8 <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
9 <link rel="manifest" href="/site.webmanifest">
9 <link rel="manifest" href="/site.webmanifest">
10 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
10 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
11+ <meta name="color-scheme" content="light only">
11 </head>
12 </head>
12 <body>
13 <body>
13 <div id="app" class="h-full"></div>
14 <div id="app" class="h-full"></div>
ui/package-lock.json+60−54
@@ -8,7 +8,6 @@
8 "name": "urbit-astrolabe",
8 "name": "urbit-astrolabe",
9 "version": "0.3.0",
9 "version": "0.3.0",
10 "dependencies": {
10 "dependencies": {
11− "@johnhyde/legil-js": "file:../../legil-js",
12 "@johnhyde/sigil-js": "github:johnhyde/sigil-js",
11 "@johnhyde/sigil-js": "github:johnhyde/sigil-js",
13 "@tlon/phonemes-js": "^1.0.0",
12 "@tlon/phonemes-js": "^1.0.0",
14 "@tlon/sigil-js": "^1.4.5",
13 "@tlon/sigil-js": "^1.4.5",
@@ -17,8 +16,9 @@
17 "bn.js": "^5.2.0",
16 "bn.js": "^5.2.0",
18 "graphology": "^0.24.1",
17 "graphology": "^0.24.1",
19 "lodash": "^4.17.21",
18 "lodash": "^4.17.21",
20− "sigma": "^2.3.1",
19+ "sigma-jh": "^2.4.2",
21 "svelte-copy-to-clipboard": "^0.2.5",
20 "svelte-copy-to-clipboard": "^0.2.5",
21+ "svelte-markdown": "^0.2.3",
22 "tippy.js": "^6.3.7",
22 "tippy.js": "^6.3.7",
23 "urbit-ob": "^5.0.1"
23 "urbit-ob": "^5.0.1"
24 },
24 },
@@ -42,27 +42,6 @@
42 "vite": "^2.7.2"
42 "vite": "^2.7.2"
43 }
43 }
44 },
44 },
45− "../../legil-js": {
46− "name": "@johnhyde/legil-js",
47− "version": "0.0.0",
48− "license": "MIT",
49− "dependencies": {
50− "invariant": "^2.2.4",
51− "svgson": "^4.0.0",
52− "transformation-matrix": "2.1.1"
53− },
54− "devDependencies": {
55− "@types/invariant": "^2.2.31",
56− "@types/react": "^17.0.20",
57− "husky": "^4.2.3",
58− "prettier": "^1.19.1",
59− "tsdx": "^0.14.1",
60− "typescript": "^3.8.3"
61− },
62− "peerDependencies": {
63− "react": "^16 || ^17"
64− }
65− },
66 "../../sigil-js": {
45 "../../sigil-js": {
67 "name": "@johnhyde/sigil-js",
46 "name": "@johnhyde/sigil-js",
68 "version": "1.4.6",
47 "version": "1.4.6",
@@ -193,13 +172,9 @@
193 "node": ">=6.9.0"
172 "node": ">=6.9.0"
194 }
173 }
195 },
174 },
196− "node_modules/@johnhyde/legil-js": {
197− "resolved": "../../legil-js",
198− "link": true
199− },
200 "node_modules/@johnhyde/sigil-js": {
175 "node_modules/@johnhyde/sigil-js": {
201 "version": "1.4.6",
176 "version": "1.4.6",
202− "resolved": "git+ssh://git@github.com/johnhyde/sigil-js.git#35f71ac00b54dd56d010b6fccfa48c6f75c48ee8",
177+ "resolved": "git+ssh://git@github.com/johnhyde/sigil-js.git#f89ce78c34b5811ea19ddea4a6964ea8360e7b4d",
203 "license": "MIT",
178 "license": "MIT",
204 "dependencies": {
179 "dependencies": {
205 "invariant": "^2.2.4",
180 "invariant": "^2.2.4",
@@ -323,6 +298,11 @@
323 "integrity": "sha512-aqkICXbM1oX5FfgZd2qSSAGdyo/NRxjWCamxoyi3T8iVQnzGge19HhDYzZ6NrVOW7bhcWNSq9XexWFtMzbB24A==",
298 "integrity": "sha512-aqkICXbM1oX5FfgZd2qSSAGdyo/NRxjWCamxoyi3T8iVQnzGge19HhDYzZ6NrVOW7bhcWNSq9XexWFtMzbB24A==",
324 "dev": true
299 "dev": true
325 },
300 },
301+ "node_modules/@types/marked": {
302+ "version": "4.0.7",
303+ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
304+ "integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw=="
305+ },
326 "node_modules/@types/node": {
306 "node_modules/@types/node": {
327 "version": "17.0.8",
307 "version": "17.0.8",
328 "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz",
308 "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz",
@@ -1776,6 +1756,17 @@
1776 "sourcemap-codec": "^1.4.4"
1756 "sourcemap-codec": "^1.4.4"
1777 }
1757 }
1778 },
1758 },
1759+ "node_modules/marked": {
1760+ "version": "4.2.3",
1761+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.3.tgz",
1762+ "integrity": "sha512-slWRdJkbTZ+PjkyJnE30Uid64eHwbwa1Q25INCAYfZlK4o6ylagBy/Le9eWntqJFoFT93ikUKMv47GZ4gTwHkw==",
1763+ "bin": {
1764+ "marked": "bin/marked.js"
1765+ },
1766+ "engines": {
1767+ "node": ">= 12"
1768+ }
1769+ },
1779 "node_modules/merge2": {
1770 "node_modules/merge2": {
1780 "version": "1.4.1",
1771 "version": "1.4.1",
1781 "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
1772 "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -2476,10 +2467,10 @@
2476 "node": ">=8"
2467 "node": ">=8"
2477 }
2468 }
2478 },
2469 },
2479− "node_modules/sigma": {
2470+ "node_modules/sigma-jh": {
2480− "version": "2.3.1",
2471+ "version": "2.4.2",
2481− "resolved": "https://registry.npmjs.org/sigma/-/sigma-2.3.1.tgz",
2472+ "resolved": "https://registry.npmjs.org/sigma-jh/-/sigma-jh-2.4.2.tgz",
2482− "integrity": "sha512-YfQsVZf78CeIUv8EYLqp/yvhMwwBXGrbpy+rD1N2X9IqtJ11+VfQRjWfVqHDhjM8oPfWPsaJMtpWFQ8pIflvCA==",
2473+ "integrity": "sha512-viB4M5Y79N5rAgs0z3M4Gff4HX53S+UMwblPDEH/Nkwwe7AcmcWwsEYsGv/xL/TNQzLIBBb0NaNAg5ZhnbEm8g==",
2483 "dependencies": {
2474 "dependencies": {
2484 "@yomguithereal/helpers": "^1.1.1",
2475 "@yomguithereal/helpers": "^1.1.1",
2485 "events": "^3.3.0",
2476 "events": "^3.3.0",
@@ -2609,7 +2600,6 @@
2609 "version": "3.48.0",
2600 "version": "3.48.0",
2610 "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.48.0.tgz",
2601 "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.48.0.tgz",
2611 "integrity": "sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ==",
2602 "integrity": "sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ==",
2612− "dev": true,
2613 "engines": {
2603 "engines": {
2614 "node": ">= 8"
2604 "node": ">= 8"
2615 }
2605 }
@@ -2651,6 +2641,18 @@
2651 "svelte": ">=3.19.0"
2641 "svelte": ">=3.19.0"
2652 }
2642 }
2653 },
2643 },
2644+ "node_modules/svelte-markdown": {
2645+ "version": "0.2.3",
2646+ "resolved": "https://registry.npmjs.org/svelte-markdown/-/svelte-markdown-0.2.3.tgz",
2647+ "integrity": "sha512-2h680NzTXnAD0CXhxe3GeHl6W+ayG4iKQRl+BIDRw+R0mUE0OiNxP1Vt8Rn+aWevB/LBiBIPCAwvL+0BkG057A==",
2648+ "dependencies": {
2649+ "@types/marked": "^4.0.1",
2650+ "marked": "^4.0.10"
2651+ },
2652+ "peerDependencies": {
2653+ "svelte": "^3.0.0"
2654+ }
2655+ },
2654 "node_modules/svelte-preprocess": {
2656 "node_modules/svelte-preprocess": {
2655 "version": "4.10.1",
2657 "version": "4.10.1",
2656 "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.1.tgz",
2658 "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.1.tgz",
@@ -3152,22 +3154,8 @@
3152 "regenerator-runtime": "^0.13.4"
3154 "regenerator-runtime": "^0.13.4"
3153 }
3155 }
3154 },
3156 },
3155− "@johnhyde/legil-js": {
3156− "version": "file:../../legil-js",
3157− "requires": {
3158− "@types/invariant": "^2.2.31",
3159− "@types/react": "^17.0.20",
3160− "husky": "^4.2.3",
3161− "invariant": "^2.2.4",
3162− "prettier": "^1.19.1",
3163− "svgson": "^4.0.0",
3164− "transformation-matrix": "2.1.1",
3165− "tsdx": "^0.14.1",
3166− "typescript": "^3.8.3"
3167− }
3168− },
3169 "@johnhyde/sigil-js": {
3157 "@johnhyde/sigil-js": {
3170− "version": "git+ssh://git@github.com/johnhyde/sigil-js.git#35f71ac00b54dd56d010b6fccfa48c6f75c48ee8",
3158+ "version": "git+ssh://git@github.com/johnhyde/sigil-js.git#f89ce78c34b5811ea19ddea4a6964ea8360e7b4d",
3171 "from": "@johnhyde/sigil-js@github:johnhyde/sigil-js",
3159 "from": "@johnhyde/sigil-js@github:johnhyde/sigil-js",
3172 "requires": {
3160 "requires": {
3173 "invariant": "^2.2.4",
3161 "invariant": "^2.2.4",
@@ -3256,6 +3244,11 @@
3256 "integrity": "sha512-aqkICXbM1oX5FfgZd2qSSAGdyo/NRxjWCamxoyi3T8iVQnzGge19HhDYzZ6NrVOW7bhcWNSq9XexWFtMzbB24A==",
3244 "integrity": "sha512-aqkICXbM1oX5FfgZd2qSSAGdyo/NRxjWCamxoyi3T8iVQnzGge19HhDYzZ6NrVOW7bhcWNSq9XexWFtMzbB24A==",
3257 "dev": true
3245 "dev": true
3258 },
3246 },
3247+ "@types/marked": {
3248+ "version": "4.0.7",
3249+ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
3250+ "integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw=="
3251+ },
3259 "@types/node": {
3252 "@types/node": {
3260 "version": "17.0.8",
3253 "version": "17.0.8",
3261 "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz",
3254 "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz",
@@ -4308,6 +4301,11 @@
4308 "sourcemap-codec": "^1.4.4"
4301 "sourcemap-codec": "^1.4.4"
4309 }
4302 }
4310 },
4303 },
4304+ "marked": {
4305+ "version": "4.2.3",
4306+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.3.tgz",
4307+ "integrity": "sha512-slWRdJkbTZ+PjkyJnE30Uid64eHwbwa1Q25INCAYfZlK4o6ylagBy/Le9eWntqJFoFT93ikUKMv47GZ4gTwHkw=="
4308+ },
4311 "merge2": {
4309 "merge2": {
4312 "version": "1.4.1",
4310 "version": "1.4.1",
4313 "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
4311 "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -4784,10 +4782,10 @@
4784 "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
4782 "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
4785 "dev": true
4783 "dev": true
4786 },
4784 },
4787− "sigma": {
4785+ "sigma-jh": {
4788− "version": "2.3.1",
4786+ "version": "2.4.2",
4789− "resolved": "https://registry.npmjs.org/sigma/-/sigma-2.3.1.tgz",
4787+ "resolved": "https://registry.npmjs.org/sigma-jh/-/sigma-jh-2.4.2.tgz",
4790− "integrity": "sha512-YfQsVZf78CeIUv8EYLqp/yvhMwwBXGrbpy+rD1N2X9IqtJ11+VfQRjWfVqHDhjM8oPfWPsaJMtpWFQ8pIflvCA==",
4788+ "integrity": "sha512-viB4M5Y79N5rAgs0z3M4Gff4HX53S+UMwblPDEH/Nkwwe7AcmcWwsEYsGv/xL/TNQzLIBBb0NaNAg5ZhnbEm8g==",
4791 "requires": {
4789 "requires": {
4792 "@yomguithereal/helpers": "^1.1.1",
4790 "@yomguithereal/helpers": "^1.1.1",
4793 "events": "^3.3.0",
4791 "events": "^3.3.0",
@@ -4883,8 +4881,7 @@
4883 "svelte": {
4881 "svelte": {
4884 "version": "3.48.0",
4882 "version": "3.48.0",
4885 "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.48.0.tgz",
4883 "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.48.0.tgz",
4886− "integrity": "sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ==",
4884+ "integrity": "sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ=="
4887− "dev": true
4888 },
4885 },
4889 "svelte-check": {
4886 "svelte-check": {
4890 "version": "2.2.11",
4887 "version": "2.2.11",
@@ -4915,6 +4912,15 @@
4915 "dev": true,
4912 "dev": true,
4916 "requires": {}
4913 "requires": {}
4917 },
4914 },
4915+ "svelte-markdown": {
4916+ "version": "0.2.3",
4917+ "resolved": "https://registry.npmjs.org/svelte-markdown/-/svelte-markdown-0.2.3.tgz",
4918+ "integrity": "sha512-2h680NzTXnAD0CXhxe3GeHl6W+ayG4iKQRl+BIDRw+R0mUE0OiNxP1Vt8Rn+aWevB/LBiBIPCAwvL+0BkG057A==",
4919+ "requires": {
4920+ "@types/marked": "^4.0.1",
4921+ "marked": "^4.0.10"
4922+ }
4923+ },
4918 "svelte-preprocess": {
4924 "svelte-preprocess": {
4919 "version": "4.10.1",
4925 "version": "4.10.1",
4920 "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.1.tgz",
4926 "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.10.1.tgz",
ui/package.json+2−2
@@ -43,7 +43,6 @@
43 "vite": "^2.7.2"
43 "vite": "^2.7.2"
44 },
44 },
45 "dependencies": {
45 "dependencies": {
46− "@johnhyde/legil-js": "file:../../legil-js",
47 "@johnhyde/sigil-js": "github:johnhyde/sigil-js",
46 "@johnhyde/sigil-js": "github:johnhyde/sigil-js",
48 "@tlon/phonemes-js": "^1.0.0",
47 "@tlon/phonemes-js": "^1.0.0",
49 "@tlon/sigil-js": "^1.4.5",
48 "@tlon/sigil-js": "^1.4.5",
@@ -52,8 +51,9 @@
52 "bn.js": "^5.2.0",
51 "bn.js": "^5.2.0",
53 "graphology": "^0.24.1",
52 "graphology": "^0.24.1",
54 "lodash": "^4.17.21",
53 "lodash": "^4.17.21",
55− "sigma": "^2.3.1",
54+ "sigma-jh": "^2.4.2",
56 "svelte-copy-to-clipboard": "^0.2.5",
55 "svelte-copy-to-clipboard": "^0.2.5",
56+ "svelte-markdown": "^0.2.3",
57 "tippy.js": "^6.3.7",
57 "tippy.js": "^6.3.7",
58 "urbit-ob": "^5.0.1"
58 "urbit-ob": "^5.0.1"
59 }
59 }
ui/src/assets/sigil-example.png → ui/public/sigil-example.pngrenamed

Binary file not shown.

ui/src/App.svelte+35−1
@@ -5,6 +5,8 @@
5 import Home from './routes/Home.svelte';
5 import Home from './routes/Home.svelte';
6 import HelpModal from '@/docs/HelpModal.svelte';
6 import HelpModal from '@/docs/HelpModal.svelte';
7 import "./styles/styles.scss";
7 import "./styles/styles.scss";
8+ import docs from 'stores/docs';
9+ import keys from 'stores/keys';
8
10
9 // const routes = {
11 // const routes = {
10 // '/help/:path?': HelpPage,
12 // '/help/:path?': HelpPage,
@@ -30,6 +32,36 @@
30 $: {
32 $: {
31 document.title = title
33 document.title = title
32 }
34 }
35+
36+ function inputEvent(event) {
37+ if (event.target.tagName == 'INPUT') return true;
38+ if (event.target.tagName == 'TEXTAREA') return true;
39+ return false;
40+ }
41+
42+ function handleKeyDown(event: KeyboardEvent) {
43+ keys.runCallbacks($keys.globalkeydown, event);
44+ if (!inputEvent(event)) {
45+ keys.runCallbacks($keys.keydown, event);
46+ }
47+ }
48+
49+ function handleKeyUp(event: KeyboardEvent) {
50+ keys.runCallbacks($keys.globalkeyup, event);
51+ if (!inputEvent(event)) {
52+ keys.runCallbacks($keys.keyup, event);
53+ }
54+ }
55+
56+ $:{
57+ $keys.keyup.get('?').set('app', () => docs.show(''));
58+ $keys.keyup.get('shift+?').set('app', () => docs.show(''));
59+ $keys.globalkeyup.get('escape').set('app', (event) => {
60+ if (inputEvent(event)) {
61+ event.target.blur();
62+ }
63+ });
64+ }
33</script>
65</script>
34
66
35<main class="h-full overflow-y-auto">
67<main class="h-full overflow-y-auto">
@@ -39,4 +71,6 @@
39
71
40<svelte:head>
72<svelte:head>
41 <title>{title}</title>
73 <title>{title}</title>
42−</svelte:head>
74+</svelte:head>
75+
76+<svelte:body on:keydown={handleKeyDown} on:keyup={handleKeyUp} />
ui/src/actions/tooltip.ts+2−2
@@ -23,11 +23,11 @@ function getParams(paramsOrContent:any = {}, overrides = {}) {
23
23
24export default function tooltip(node, paramsOrContent: any = {}) {
24export default function tooltip(node, paramsOrContent: any = {}) {
25 const onTrigger = () => {
25 const onTrigger = () => {
26− console.log('triggered');
26+ // console.log('triggered');
27 node.dispatchEvent(new CustomEvent("tooltipTriggered"));
27 node.dispatchEvent(new CustomEvent("tooltipTriggered"));
28 };
28 };
29 const onUntrigger = () => {
29 const onUntrigger = () => {
30− console.log('untriggered');
30+ // console.log('untriggered');
31 node.dispatchEvent(new CustomEvent("tooltipUntriggered"));
31 node.dispatchEvent(new CustomEvent("tooltipUntriggered"));
32 };
32 };
33 const onClickOutside = (_, e) => {
33 const onClickOutside = (_, e) => {
ui/src/components/CollapsibleContent.svelte+4−5
@@ -1,18 +1,17 @@
1<script lang="ts">
1<script lang="ts">
2− import { slide } from 'svelte/transition';
2+ import { slide } from 'svelte/transition';
3− import { quintOut } from 'svelte/easing';
4−
5 export let open: boolean = true;
3 export let open: boolean = true;
6</script>
4</script>
7
5
8<div class="mt-2">
6<div class="mt-2">
9 <button on:click={() => (open = !open)}>
7 <button on:click={() => (open = !open)}>
10− <span class="font-mono">{open ? '▼' : '▶'}</span>
8+ <span class="font-mono inline-block transition duration-150 {open ? 'rotate-90' : ''}">▶</span>
11 &nbsp;
9 &nbsp;
12 <slot name="title"></slot>
10 <slot name="title"></slot>
13 </button>
11 </button>
14 {#if open}
12 {#if open}
13+ <!-- turns out it's important not to put an await in the content -->
15− <div class="ml-[22px]" transition:slide="{{duration: 50 }}">
14+ <div class="ml-[22px]" transition:slide|local="{{duration: 150 }}">
16 <slot name="content"></slot>
15 <slot name="content"></slot>
17 </div>
16 </div>
18 {/if}
17 {/if}
ui/src/components/ExtensionWindow.sveltenew file+25
@@ -0,0 +1,25 @@
1+<script lang="ts">
2+ export let name: string;
3+ export let open = true;
4+ export let halfWidth = false;
5+ $: containerClasses = open ? ('w-full ' + (halfWidth ? 'md:w-1/2' : '')) : 'w-min justify-self-start';
6+ $: boxClasses = open ? 'border-gray-500' : 'border-gray-400 bg-gray-400';
7+ $: buttonClasses = open ? 'text-gray-600' : 'text-white';
8+</script>
9+
10+<div class="p-1 {containerClasses}">
11+ <div class="border rounded-md {boxClasses}" on:click={() => { if (!open) open = true; }}>
12+ <div class="flex flex-wrap">
13+ <button class="px-2 text-sm {buttonClasses}" on:click|stopPropagation={() => open = !open}>
14+ {name}
15+ </button>
16+ {#if open}
17+ <slot name="top-bar"/>
18+ {/if}
19+ </div>
20+ {#if open}
21+ <slot/>
22+ {/if}
23+ </div>
24+</div>
25+
ui/src/components/ShipListing.svelte+15−9
@@ -1,16 +1,20 @@
1<script lang="ts">
1<script lang="ts">
2
2
3− import Sigil from "./Sigil.svelte";
3+ import Sigil from "@/common/Sigil.svelte";
4+ import PalStatusIndicator from '@/pals/PalStatusIndicator.svelte';
4 import { push } from 'svelte-spa-router'
5 import { push } from 'svelte-spa-router'
5 import { normalizeId } from 'lib/id';
6 import { normalizeId } from 'lib/id';
6 import { linkToShip as generateLinkToShip } from 'lib/link';
7 import { linkToShip as generateLinkToShip } from 'lib/link';
8+ import { getPalStatus } from 'lib/pals';
9+ import store from 'stores/store';
7 export let ship: any;
10 export let ship: any;
8 export let linkToShip: boolean = false;
11 export let linkToShip: boolean = false;
9 export let differentiateContacts = false;
12 export let differentiateContacts = false;
10
13
11 $: patp = normalizeId(ship.patp);
14 $: patp = normalizeId(ship.patp);
15+ $: contact = $store.contacts[ship.patp];
12 $: borderClass = differentiateContacts ? (
16 $: borderClass = differentiateContacts ? (
13− 'border-l-8 ' + (Object.keys(ship).length > 1 ? 'border-gold' : 'border-gray-300')
17+ 'border-l-8 ' + (contact ? 'border-gold' : 'border-gray-300')
14 ) : '';
18 ) : '';
15
19
16 function handleClick() {
20 function handleClick() {
@@ -24,13 +28,15 @@
24on:click={handleClick}
28on:click={handleClick}
25on:click
29on:click
26>
30>
27− <div class="flex flex-wrap p-4 items-center">
31+ <div class="flex p-4 items-center gap-x-4">
28− <Sigil {patp} size={48} useNew />
32+ <Sigil {patp} size={48} useNew noShrink />
29− <!-- {#if ship.avatar}
30− <img src={ship.avatar} class="h-12 w-12 mr-4" />
31− {/if} -->
32− <p class="ml-4 break-words">
33+ <p class="break-words grow">
34+ <span class="align-middle mr-1">
35+ {patp}
36+ </span>
37+ <PalStatusIndicator status={getPalStatus($store.pals, patp)} class="inline" />
38+ <br/>
33− {patp}<br/>{ship.nickname ? `(${ship.nickname})` : ''}
39+ {contact?.nickname ? `(${contact.nickname})` : ''}
34 </p>
40 </p>
35 </div>
41 </div>
36</div>
42</div>
ui/src/components/ShipView.sveltedeleted−353
@@ -1,353 +0,0 @@
1−<script lang="ts">
2− import { clan, sein, patp2dec } from 'urbit-ob';
3−
4− import { cite, normalizeId } from 'lib/id';
5− import { getPoint, getSpawnedPoints } from 'lib/api';
6− import Sigil from "./Sigil.svelte";
7− import GoldBadge from './GoldBadge.svelte';
8− import TooltipAndDocLink from "./TooltipAndDocLink.svelte";
9− import ShipLink from './ShipLink.svelte';
10− import ChartLink from './ChartLink.svelte';
11− import EthAddressLink from './EthAddressLink.svelte';
12− import CollapsibleContent from './CollapsibleContent.svelte';
13− import ShipChain from './ShipChain.svelte';
14− import ShipListings from './ShipListings.svelte';
15− import LoadingSpinner from './LoadingSpinner.svelte';
16−
17− export let patp: string;
18−
19− let parentChain: any[];
20− let sponsorChain: any[];
21− let rawPointInfoPromise: any;
22− let rawPointInfo: any = null;
23− let pointInfo: any = {};
24− let rawSpawnedPointsPromise: any;
25− let rawSpawnedPoints: any = null;
26− let spawnedPoints: string[] = [];
27− const spawnStatusOptions = {
28− unspawned: {
29− text: 'unspawned',
30− tooltip: 'This point has not yet been spawned by its parent',
31− },
32− locked: {
33− text: 'locked',
34− tooltip: 'This point is currently locked in a Linear Release Contract',
35− },
36− spawned: {
37− text: 'spawned',
38− tooltip: `This point has been spawned`,
39− },
40− spawnedNoKeys: {
41− text: 'spawned (no keys set)',
42− tooltip: 'This point has been spawned, but no keys have been set',
43− },
44− }
45− const layerOptions = {
46− l1: {
47− text: 'on layer 1',
48− tooltip: 'This point is secured as an NFT on the Ethereum blockchain',
49− },
50− l2: {
51− text: 'on layer 2',
52− tooltip: `This point is secured by Urbit's "Layer 2" solution`,
53− },
54− spawn: {
55− text: 'spawns on layer 2',
56− tooltip: 'This point lives on layer 1, but it spawns new points on layer 2',
57− },
58− }
59−
60− $: shipClass = clan(patp);
61− $: azPoint = ['galaxy', 'star', 'planet'].includes(shipClass);
62− $: canSpawnPoints = ['galaxy', 'star'].includes(shipClass);
63− $: spawnableClassPlural = !canSpawnPoints ? '' : (shipClass === 'galaxy' ? 'Stars' : 'Planets');
64− $: {
65− let parents = [patp];
66− let galaxyFound = false;
67− let count = 10;
68− while (!galaxyFound && count > 0) {
69− count--;
70− if (count == 0) {
71− throw new Error("You've got an infinite loop in building the parent chain");
72− }
73− let firstItem = parents[0];
74− if (clan(firstItem) == 'galaxy') {
75− galaxyFound = true;
76− } else {
77− parents.unshift(sein(firstItem))
78− }
79− }
80− parents.pop();
81− parentChain = parents;
82− }
83− $: {
84− rawPointInfo = null;
85− if (azPoint) {
86− rawPointInfoPromise = getPoint(patp);
87− rawPointInfoPromise.then((info) => {
88− rawPointInfo = info;
89− }).catch((error) => {
90− rawPointInfo = { error };
91− });
92− }
93− }
94− $: {
95− rawSpawnedPoints = null;
96− if (canSpawnPoints) {
97− rawSpawnedPointsPromise = getSpawnedPoints(patp);
98− rawSpawnedPointsPromise.then((info) => {
99− rawSpawnedPoints = info;
100− }).catch((error) => {
101− rawSpawnedPoints = { error };
102− });
103− }
104− }
105− $: {
106− spawnedPoints = [];
107− if (rawSpawnedPoints?.points) {
108− spawnedPoints = rawSpawnedPoints?.points.map(patp => ({ patp: normalizeId(patp) }));
109− }
110− }
111− $: {
112− pointInfo = {};
113− let spawnStatus: any,
114− spawnedUnlocked: boolean = false,
115− layer: any,
116− life: number,
117− rift: number,
118− spawnedCount: number;
119− let keysSet = false;
120− if (!azPoint) {
121−
122− } else if (rawPointInfo) {
123− if (rawPointInfo.point) {
124− const { point } = rawPointInfo;
125− const {
126− 'probable-dominion': dominion,
127− npoint
128− } = point;
129− layer = layerOptions[dominion];
130− spawnedCount = point['spa-count'];
131− sponsorChain = point['sponsor-chain'].reverse();
132− if (npoint) {
133− life = npoint.net.keys.life;
134− rift = npoint.net.rift;
135− if (life > 0) {
136− keysSet = true;
137− }
138−
139− pointInfo.proxies = npoint.own;
140− if (pointInfo.proxies.owner.address === '0x86cd9cd0992f04231751e3761de45cecea5d1801') {
141− spawnStatus = spawnStatusOptions.locked;
142− } else if (keysSet) {
143− spawnStatus = spawnStatusOptions.spawned;
144− spawnedUnlocked = true;
145− } else {
146− spawnStatus = spawnStatusOptions.spawnedNoKeys;
147− spawnedUnlocked = true;
148− }
149− // const { sponsor } = npoint.net;
150− // if (sponsor.has) {
151− // pointInfo.sponsor = normalizeId(sponsor.who);
152− // }
153− } else {
154− // no info found
155− spawnStatus = spawnStatusOptions.unspawned;
156− life = 0;
157− rift = 0;
158− }
159− } else {
160− // no info found
161− spawnStatus = spawnStatusOptions.unspawned;
162− life = 0;
163− rift = 0;
164− }
165− } else {
166− // request pending
167− }
168− pointInfo = {
169− ...pointInfo,
170− layer,
171− spawnStatus,
172− spawnedUnlocked,
173− keysSet,
174− life,
175− rift,
176− spawnedCount,
177− };
178− }
179−</script>
180−
181−<div class="p-8 max-w-md w-full rounded-lg bg-white flex flex-col md:max-w-2xl">
182− <div class="flex flex-col items-center space-y-4 md:flex-row md:items-start md:space-x-8 md:space-y-0">
183− <div class="w-full max-w-[256px] md:min-w-[256px] flex-shrink-0">
184− <Sigil patp={patp} size={256} useNew />
185− </div>
186− <div class="grow w-full min-w-0">
187− <h2 class="text-2xl text-center">
188− <TooltipAndDocLink
189− text="The ship's @p (click to learn more)"
190− doc="patpee" class="inline"
191− >
192− {cite(patp)}
193− </TooltipAndDocLink>
194− </h2>
195− {#if (!azPoint)}
196− <h3 class="text-lg text-center text-gray-700">{patp}</h3>
197− {/if}
198− <h4 class="text-center text-gray-700 break-words">
199− <!-- <TooltipAndDocLink
200− text="The integer representation of this ID"
201− doc="patpee" class="inline"
202− > -->
203− {patp2dec(patp)}
204− <!-- </TooltipAndDocLink> -->
205− </h4>
206− <div class="flex flex-wrap justify-center gap-1.5 my-1.5">
207− <GoldBadge title={"it's a " + shipClass} doc={shipClass}>{shipClass}</GoldBadge>
208− {#if azPoint}
209− {#if pointInfo.spawnStatus}
210− <GoldBadge title={pointInfo.spawnStatus.tooltip}>
211− {pointInfo.spawnStatus.text}
212− </GoldBadge>
213− {/if}
214− {#if pointInfo.layer}
215− <GoldBadge title={pointInfo.layer.tooltip} doc="l2">{pointInfo.layer.text}</GoldBadge>
216− {/if}
217− {:else if (shipClass === 'moon')}
218− <span>of</span>
219− <ShipLink patp={parentChain.at(-1)}></ShipLink>
220− {:else if (shipClass === 'comet')}
221− <span>under</span>
222− <ShipLink patp={parentChain.at(-1)}></ShipLink>
223− {/if}
224− </div>
225− {#if pointInfo?.spawnedUnlocked}
226− <div class="w-full text-center my-3">
227− <ChartLink {patp}>View in Star Chart ↗</ChartLink>
228− </div>
229− {/if}
230− <div class="mt-4 w-full break-words">
231− {#if parentChain.length > 0}
232− <p>
233− {parentChain.length === 1 ? 'Parent:' : 'Parent Chain:'}
234− <ShipChain shipChain={parentChain}></ShipChain>
235− </p>
236− {/if}
237− {#if azPoint}
238− <CollapsibleContent>
239− <b slot="title">Azimuth Details</b>
240− <div slot="content">
241− {#await rawPointInfoPromise}
242− <LoadingSpinner spinnerClass="text-black" />
243− {:then}
244− {#if shipClass !== 'galaxy'}
245− <p>
246− {#if sponsorChain && sponsorChain.length > 0}
247− <p>
248− {sponsorChain.length === 1 ? 'Sponsor:' : 'Sponsor Chain:'}
249− <ShipChain shipChain={sponsorChain}></ShipChain>
250− </p>
251− {:else}
252− Unsponsored
253− {/if}
254− </p>
255− {/if}
256− <p>
257− <TooltipAndDocLink
258− text="Number of times this ship's keys have been set. Must be at least 1 to use the network."
259− doc="keys"
260− class="inline"
261− >
262− Key Revision:
263− {pointInfo.life}
264− </TooltipAndDocLink>
265− </p>
266− <p>
267− <TooltipAndDocLink
268− text="Number of times this ship has breached."
269− doc="keys"
270− class="inline"
271− >
272− Factory Resets:
273− {pointInfo.rift}
274− </TooltipAndDocLink>
275− </p>
276− {#if pointInfo.proxies}
277− <p>
278− <span>
279− Owner:
280− <EthAddressLink address={pointInfo.proxies.owner.address} />
281− </span>
282− </p>
283− <p>
284− <span>
285− Management Proxy:
286− <EthAddressLink address={pointInfo.proxies['management-proxy'].address}>
287− <span class="text-gray-700">not set</span>
288− </EthAddressLink>
289− </span>
290− </p>
291− <p>
292− <span>
293− Spawn Proxy:
294− <EthAddressLink address={pointInfo.proxies['spawn-proxy'].address}>
295− <span class="text-gray-700">not set</span>
296− </EthAddressLink>
297− </span>
298− </p>
299− <p>
300− <span>
301− Transfer Proxy:
302− <EthAddressLink address={pointInfo.proxies['transfer-proxy'].address}>
303− <span class="text-gray-700">not set</span>
304− </EthAddressLink>
305− </span>
306− </p>
307− {#if shipClass === 'galaxy'}
308− <p>
309− <span>
310− Voting Proxy:
311− <EthAddressLink address={pointInfo.proxies['voting-proxy'].address}>
312− [not set]
313− </EthAddressLink>
314− </span>
315− </p>
316− {/if}
317− {/if}
318−
319− <!-- {#if value && value.point}
320− <pre class="text-sm overflow-scroll">
321− Raw Info:{JSON.stringify(rawPointInfo, null, 2)}
322− </pre>
323− {/if} -->
324− {/await}
325− </div>
326− </CollapsibleContent>
327− {/if}
328− </div>
329− </div>
330− </div>
331− {#if canSpawnPoints}
332− <div class="mt-5 p-3 border-t">
333− {#await rawPointInfoPromise then}
334− <h3 class="text-lg">
335− {#if pointInfo.spawnedCount}
336− {pointInfo.spawnedCount} Spawned {spawnableClassPlural}:
337− {:else}
338− No Spawned {spawnableClassPlural}
339− {/if}
340− </h3>
341− {/await}
342− {#await rawSpawnedPointsPromise}
343− Loading {spawnableClassPlural}...
344− {:then}
345− <!-- TODO: pagination -->
346− <ShipListings
347− ships={spawnedPoints}
348− linkToShips
349− />
350− {/await}
351− </div>
352− {/if}
353−</div>
ui/src/components/Sigil.sveltedeleted−61
@@ -1,61 +0,0 @@
1−<script lang="ts">
2− import { clan } from 'urbit-ob';
3− import { sigil as sigil1, stringRenderer as sr1 } from '@tlon/sigil-js';
4− import { sigil as sigil2, stringRenderer as sr2 } from '@johnhyde/sigil-js';
5− import { sigilScalingFunction } from 'lib/sigil';
6− import moonPng from 'assets/moon.png';
7− import cometSvg from 'assets/comet.svg';
8−
9− export let patp: string;
10− export let size: number;
11− export let useNew: boolean = false;
12− export let fgColor: string = 'white';
13− export let bgColor: string = 'black';
14− export let altBgColor: string = undefined;
15− let svgString: any;
16− let imgSrc: any;
17−
18− $: shipClass = clan(patp);
19− $: displaySigil = ['galaxy', 'star', 'planet'].includes(shipClass);
20− $: {
21− if (displaySigil) {
22− let sigil = sigil1;
23− let sr = sr1;
24− let svgAST;
25− if (useNew) {
26− sigil = sigil2;
27− sr = sr2;
28− svgAST = sigil({
29− patp: patp,
30− size,
31− colors: [bgColor, fgColor],
32− autoScaleStrokes: true,
33− strokeScalingFunctionV2: sigilScalingFunction,
34− });
35− } else {
36− svgAST = sigil({
37− patp: patp,
38− size,
39− colors: [bgColor, fgColor],
40− });
41− }
42− svgAST.attributes.preserveAspectRatio = 'xMidYMin slice';
43− svgString = sr(svgAST);
44− if (altBgColor) {
45− svgString = svgString.replace(/rect fill="[^"]+"/, `rect fill="${altBgColor}"`);
46− }
47− } else if (shipClass === 'moon') {
48− imgSrc = moonPng;
49− } else {
50− imgSrc = cometSvg;
51− }
52− }
53−</script>
54−
55−<div class="grow" style:max-width="{size}px">
56− {#if displaySigil}
57− {@html svgString}
58− {:else}
59− <img src={imgSrc} alt={shipClass} class="mx-auto" />
60− {/if}
61−</div>
ui/src/components/StarChart.svelte+42−13
@@ -1,14 +1,16 @@
1<script lang="ts">
1<script lang="ts">
2− import LoadingSpinner from './LoadingSpinner.svelte';
2+ import LoadingSpinner from '@/common/LoadingSpinner.svelte';
3
3
4− import { push } from 'svelte-spa-router'
4+ import { push } from 'svelte-spa-router';
5+ import { onDestroy } from 'svelte';
5
6
6− import Sigma from 'sigma';
7+ import Sigma from 'sigma-jh';
7 import Graph from 'graphology';
8 import Graph from 'graphology';
8
9
9 import { clan } from 'urbit-ob';
10 import { clan } from 'urbit-ob';
10 import DocLink from './docs/DocLink.svelte';
11 import DocLink from './docs/DocLink.svelte';
11− import { cite, normalizeId } from 'lib/id';
12+ import store from 'stores/store';
13+ import keys from 'stores/keys';
12 import { parseChartData, populateGraph } from 'lib/graph';
14 import { parseChartData, populateGraph } from 'lib/graph';
13 import { getChartData } from 'lib/api';
15 import { getChartData } from 'lib/api';
14 import { linkToShip } from 'lib/link';
16 import { linkToShip } from 'lib/link';
@@ -23,6 +25,7 @@
23 let firstRender = true;
25 let firstRender = true;
24 let mouseDown = false;
26 let mouseDown = false;
25 let isDragging = false;
27 let isDragging = false;
28+ let stoppedDragging = Date.now();
26
29
27 function sigma(container: HTMLElement, params: any = {}) {
30 function sigma(container: HTMLElement, params: any = {}) {
28 try {
31 try {
@@ -32,13 +35,15 @@
32 allowInvalidContainer: true,
35 allowInvalidContainer: true,
33 // labelColor: { color: WHITE },
36 // labelColor: { color: WHITE },
34 labelFont: 'monospace',
37 labelFont: 'monospace',
35− labelRenderedSizeThreshold: 9,
38+ labelRenderedSizeThreshold: 2.2,
36 zIndex: true,
39 zIndex: true,
37 maxCameraRatio: 10,
40 maxCameraRatio: 10,
38 });
41 });
39
42
40 s.on('clickNode', ({ node }) => {
43 s.on('clickNode', ({ node }) => {
44+ if (!isDragging && 100 < Date.now() - stoppedDragging) {
41− push(linkToShip(node));
45+ push(linkToShip(node));
46+ }
42 });
47 });
43 s.on('afterRender', () => {
48 s.on('afterRender', () => {
44 if (firstRender) {
49 if (firstRender) {
@@ -58,6 +63,8 @@
58 mc.on('mouseup', () => {
63 mc.on('mouseup', () => {
59 if (!isDragging) {
64 if (!isDragging) {
60 clearHighlight();
65 clearHighlight();
66+ } else {
67+ stoppedDragging = Date.now();
61 }
68 }
62 mouseDown = false;
69 mouseDown = false;
63 isDragging = false;
70 isDragging = false;
@@ -88,8 +95,8 @@
88 rawChartData = { error };
95 rawChartData = { error };
89 });
96 });
90 }
97 }
91− $: parsedChartData = parseChartData(rawChartData);
98+ $: parsedChartData = parseChartData(rawChartData, filterSet);
92− $: [parsedChartData, bySponsor], populateGraph(g, parsedChartData, bySponsor, () => {
99+ $: [parsedChartData, childKey], populateGraph(g, parsedChartData, childKey, () => {
93 searchFunc();
100 searchFunc();
94 });
101 });
95
102
@@ -140,7 +147,22 @@
140 "w-full" :
147 "w-full" :
141 "border-r";
148 "border-r";
142 }
149 }
143− let bySponsor = true;
150+ let childKey: ('sponsees' | 'children') = 'sponsees';
151+ let filterSet: Set<string> = null;
152+ $: peersSet = new Set($store.peers);
153+ $: palsSet = new Set(Object.keys($store.pals.outgoing));
154+
155+ function closeChart() {
156+ push('/');
157+ }
158+
159+ $: {
160+ $keys.keyup.get('escape').set('starChart', closeChart);
161+ }
162+
163+ onDestroy(() => {
164+ $keys.keyup.get('escape').delete('starChart');
165+ });
144</script>
166</script>
145
167
146<div class="border border-gold bg-navy w-full grow flex flex-col relative">
168<div class="border border-gold bg-navy w-full grow flex flex-col relative">
@@ -182,13 +204,20 @@
182 on:click={() => push('/') }>
204 on:click={() => push('/') }>
183 X
205 X
184 </div>
206 </div>
185− <div class="flex flex-col absolute -bottom-2 inset-x-0 text-white z-10 2xs:-bottom-3.5">
207+ <div class="flex absolute -bottom-2 inset-x-0 text-white z-10 2xs:-bottom-3.5">
186 <!-- <div class="ml-8 px-4 py-1 flex gap-x-1 items-center text-white"> -->
208 <!-- <div class="ml-8 px-4 py-1 flex gap-x-1 items-center text-white"> -->
187 <!-- <span>Arrange by</span> -->
209 <!-- <span>Arrange by</span> -->
188− <InlineSelect bind:value={bySponsor}
210+ <InlineSelect bind:value={childKey}
211+ options={[
212+ ['Sponsors', 'sponsees'],
213+ ['Parents', 'children']
214+ ]}
215+ ></InlineSelect>
216+ <InlineSelect bind:value={filterSet}
189 options={[
217 options={[
190− ['Sponsors', true],
218+ ['All', null],
191− ['Parents', false]
219+ ['Peers', peersSet],
220+ ['Pals', palsSet],
192 ]}
221 ]}
193 ></InlineSelect>
222 ></InlineSelect>
194 <!-- </div> -->
223 <!-- </div> -->
ui/src/components/ChartLink.svelte → ui/src/components/common/ChartLink.svelterenamed

The content is the same.

ui/src/components/EthAddressLink.svelte → ui/src/components/common/EthAddressLink.svelterenamed

The content is the same.

ui/src/components/GoldBadge.svelte → ui/src/components/common/GoldBadge.svelterenamed

The content is the same.

ui/src/components/Legil.svelte → ui/src/components/common/Legil.svelterenamed

The content is the same.

ui/src/components/LoadingSpinner.svelte → ui/src/components/common/LoadingSpinner.svelterenamed

The content is the same.

ui/src/components/ShipLink.svelte → ui/src/components/common/ShipLink.svelterenamed

The content is the same.

ui/src/components/common/Sigil.sveltenew file+65
@@ -0,0 +1,65 @@
1+<script lang="ts">
2+ import { clan } from 'urbit-ob';
3+ // import { sigil as sigil1, stringRenderer as sr1 } from '@tlon/sigil-js';
4+ import { sigil as sigil2, stringRenderer as sr2 } from '@johnhyde/sigil-js';
5+ import { sigilScalingFunction, crowdedSigilScalingFunction } from 'lib/sigil';
6+ import moonPng from 'assets/moon.png';
7+ import cometSvg from 'assets/comet.svg';
8+
9+ export let patp: string;
10+ export let size: number;
11+ export let useNew: boolean = false;
12+ export let fgColor: string = 'white';
13+ export let bgColor: string = 'black';
14+ export let altBgColor: string = undefined;
15+ export let noShrink: boolean = false;
16+ let svgString: any;
17+ let imgSrc: any;
18+
19+ $: noShrinkClasses = noShrink ? 'shrink-0' : '';
20+ $: noShrinkWidth = noShrink ? size+'px' : 'initial';
21+
22+ $: shipClass = clan(patp);
23+ $: crowded = ['comet', 'moon'].includes(shipClass);
24+ $: {
25+ // if (displaySigil) {
26+ // let sigil = sigil1;
27+ // let sr = sr1;
28+ // let svgAST;
29+ // if (useNew) {
30+ let sigil = sigil2;
31+ let sr = sr2;
32+ let svgAST = sigil({
33+ patp: patp,
34+ size,
35+ colors: [bgColor, fgColor],
36+ autoScaleStrokes: true,
37+ strokeScalingFunctionV2: crowded ? crowdedSigilScalingFunction : sigilScalingFunction,
38+ });
39+ // } else {
40+ // svgAST = sigil({
41+ // patp: patp,
42+ // size,
43+ // colors: [bgColor, fgColor],
44+ // });
45+ // }
46+ svgAST.attributes.preserveAspectRatio = 'xMidYMin slice';
47+ svgString = sr(svgAST);
48+ if (altBgColor) {
49+ svgString = svgString.replace(/rect fill="[^"]+"/, `rect fill="${altBgColor}"`);
50+ }
51+ // } else if (shipClass === 'moon') {
52+ // imgSrc = moonPng;
53+ // } else {
54+ // imgSrc = cometSvg;
55+ // }
56+ }
57+</script>
58+
59+<div class="grow {noShrinkClasses}" style:max-width="{size}px" style:width={noShrinkWidth}>
60+ <!-- {#if displaySigil} -->
61+ {@html svgString}
62+ <!-- {:else}
63+ <img src={imgSrc} alt={shipClass} class="mx-auto" />
64+ {/if} -->
65+</div>
ui/src/components/Tooltip.svelte → ui/src/components/common/Tooltip.svelterenamed

The content is the same.

ui/src/components/TooltipAndDocLink.svelte → ui/src/components/common/TooltipAndDocLink.svelterenamed+1−1
@@ -1,6 +1,6 @@
1<script lang="ts">
1<script lang="ts">
2 import Tooltip from "./Tooltip.svelte";
2 import Tooltip from "./Tooltip.svelte";
3− import DocLink from './docs/DocLink.svelte'; // TODO try to add a link in the tooltip somehow or something
3+ import DocLink from '@/docs/DocLink.svelte'; // TODO try to add a link in the tooltip somehow or something
4 export let text: string = '';
4 export let text: string = '';
5 export let doc: string = '';
5 export let doc: string = '';
6 let className: string = '';
6 let className: string = '';
ui/src/components/docs/Doc.svelte+10−1
@@ -1,9 +1,10 @@
1<script lang="ts">
1<script lang="ts">
2− import { createEventDispatcher } from 'svelte';
2+ import { createEventDispatcher, onDestroy } from 'svelte';
3 import DocContent from './DocContent.svelte';
3 import DocContent from './DocContent.svelte';
4 import DocHome from './DocHome.svelte';
4 import DocHome from './DocHome.svelte';
5 import Hamburger from '@/buttons/Hamburger.svelte';
5 import Hamburger from '@/buttons/Hamburger.svelte';
6 import CloseButton from '@/buttons/CloseButton.svelte';
6 import CloseButton from '@/buttons/CloseButton.svelte';
7+ import keys from 'stores/keys';
7
8
8 export let path: string;
9 export let path: string;
9
10
@@ -17,6 +18,14 @@
17 function navigate(path) {
18 function navigate(path) {
18 dispatch('navigate', path);
19 dispatch('navigate', path);
19 }
20 }
21+
22+ $: {
23+ $keys.globalkeyup.get('escape').set('doc', close);
24+ }
25+
26+ onDestroy(() => {
27+ $keys.globalkeyup.get('escape').delete('doc');
28+ });
20</script>
29</script>
21
30
22<div class="rounded-lg bg-white">
31<div class="rounded-lg bg-white">
ui/src/components/docs/DocContent.svelte+1−1
@@ -5,7 +5,7 @@
5 import docLinks from "actions/docLinks";
5 import docLinks from "actions/docLinks";
6 import docs from "stores/docs";
6 import docs from "stores/docs";
7
7
8− import LoadingSpinner from "@/LoadingSpinner.svelte";
8+ import LoadingSpinner from "@/common/LoadingSpinner.svelte";
9
9
10 export let path:string;
10 export let path:string;
11
11
ui/src/components/extensions/AppIcon.sveltenew file+61
@@ -0,0 +1,61 @@
1+<script lang="ts">
2+ import CopyToClipboard from 'svelte-copy-to-clipboard';
3+ import { uxToHex } from 'lib/utils';
4+ import { getAppByPath } from 'lib/apps';
5+ import store from 'stores/store';
6+ import LoadingSpinner from '@/common/LoadingSpinner.svelte';
7+
8+ export let path: string;
9+
10+ $: desk = path.split('/')[1];
11+ $: appPromise = getAppByPath(path);
12+ $: appUrbitGraphLink = `web+urbitgraph://${path}`;
13+ $: appLink = `/apps/grid/perma?ext=${appUrbitGraphLink}`;
14+
15+ let overrideText = null;
16+
17+ const handleSuccessfullyCopied = (e) => {
18+ overrideText = 'Copied!';
19+ setTimeout(() => {
20+ overrideText = null;
21+ }, 1000);
22+ }
23+
24+ const handleFailedCopy = () => {
25+ alert('failed to copy :(');
26+ }
27+</script>
28+
29+<div class="w-20 text-sm text-center">
30+ <a class="block w-20 h-20 border border-gray-300 bg-gray-600 text-white rounded-lg overflow-hidden"
31+ href={appLink} target="_blank">
32+ {#await appPromise}
33+ <div class="flex h-full items-center">
34+ <LoadingSpinner />
35+ </div>
36+ {:then app}
37+ <div style:background-color={uxToHex(app.color)} class="h-full w-full text-sm">
38+ {#if app.image}
39+ <img src={app.image} alt="app icon"
40+ class="aspect-square object-cover w-full h-full"
41+ />
42+ {/if}
43+ </div>
44+ {:catch}
45+ {path}
46+ {/await}
47+ </a>
48+ <CopyToClipboard text={path} on:copy={handleSuccessfullyCopied} on:fail={handleFailedCopy} let:copy>
49+ <span on:click={copy}>
50+ {#if overrideText}
51+ {overrideText}
52+ {:else}
53+ {#await appPromise}
54+ {desk}
55+ {:then app}
56+ {app.title}
57+ {/await}
58+ {/if}
59+ </span>
60+ </CopyToClipboard>
61+</div>
ui/src/components/extensions/AppsShipViewExtension.sveltenew file+56
@@ -0,0 +1,56 @@
1+<script lang="ts">
2+
3+ import { sein } from 'urbit-ob';
4+ import uniq from 'lodash/uniq';
5+ import tooltip from 'actions/tooltip';
6+ import { normalizeId, filterIdsForChildMoons } from 'lib/id';
7+ import { filterObject } from 'lib/utils';
8+ import { addAllies, delAllies } from 'lib/apps';
9+ import store from 'stores/store';
10+ import ExtensionWindow from '@/ExtensionWindow.svelte';
11+ import AppIcon from './AppIcon.svelte';
12+
13+ export let patp: string;
14+ $: moons = filterIdsForChildMoons($store.peers, patp);
15+ $: possibleAllies = [patp, ...moons];
16+ $: knownApps = uniq(Object.values(filterObject($store.apps.allies, (ally) => {
17+ ally = normalizeId(ally);
18+ return patp === ally || sein(ally) === patp;
19+ })).flat());
20+ $: nonAllies = possibleAllies.filter((ally) => !$store.apps.allies[ally]);
21+ $: emptyAllies = possibleAllies.filter((ally) => {
22+ let apps = $store.apps.allies[ally];
23+ return apps && !apps.length;
24+ });
25+</script>
26+<ExtensionWindow name="Apps" halfWidth>
27+ <svelte:fragment slot="top-bar">
28+ <div class="grow m-2 flex justify-end align-center">
29+ {#if nonAllies.length > 0}
30+ <button class="bg-gray-200 ml-3 px-2 rounded"
31+ on:click={() => addAllies(nonAllies)}
32+ >
33+ Check for More
34+ </button>
35+ {/if}
36+ {#if emptyAllies.length > 0}
37+ <button class="bg-gray-200 ml-3 px-2 rounded"
38+ on:click={() => delAllies(emptyAllies)}
39+ >
40+ Stop Watching
41+ <span use:tooltip={emptyAllies.join(', ')}>
42+ {emptyAllies.length} Ship{emptyAllies.length > 1 ? 's': ''}
43+ </span>
44+ </button>
45+ {/if}
46+ </div>
47+ </svelte:fragment>
48+ {#if knownApps.length > 0}
49+ <div class="flex flex-wrap p-2 gap-2 justify-center">
50+ {#each knownApps as app}
51+ <AppIcon path={app} />
52+ {/each}
53+ </div>
54+ {/if}
55+</ExtensionWindow>
56+
ui/src/components/extensions/GroupsShipViewExtension.sveltenew file+15
@@ -0,0 +1,15 @@
1+<script lang="ts">
2+ import { uxToHex } from 'lib/utils';
3+ import { normalizeId } from 'lib/id';
4+ import store from 'stores/store';
5+ import ExtensionWindow from '@/ExtensionWindow.svelte';
6+
7+ export let patp: string;
8+</script>
9+
10+{#if patp}
11+ <ExtensionWindow name="Shared Groups">
12+ TODO: implement for Groups 2, don't bother with Groups 1
13+ </ExtensionWindow>
14+{/if}
15+
ui/src/components/extensions/ProfileShipViewExtension.sveltenew file+52
@@ -0,0 +1,52 @@
1+<script lang="ts">
2+ import SvelteMarkdown from 'svelte-markdown';
3+ import { uxToHex } from 'lib/utils';
4+ import { normalizeId } from 'lib/id';
5+ import store from 'stores/store';
6+ import Sigil from "@/common/Sigil.svelte";
7+ import ExtensionWindow from '@/ExtensionWindow.svelte';
8+
9+ export let patp: string;
10+ $: contact = $store.contacts[normalizeId(patp)];
11+ $: color = contact && uxToHex(contact.color);
12+ $: somethingToShow = contact && (contact.nickname || contact.cover || contact.avatar || contact.bio);
13+</script>
14+
15+{#if somethingToShow}
16+ <ExtensionWindow name="Groups Profile">
17+ <svelte:fragment slot="top-bar">
18+ {#if contact.nickname}
19+ <div class="grow m-1 flex justify-center align-center">
20+ {contact.nickname}
21+ </div>
22+ {/if}
23+ </svelte:fragment>
24+ <div class="flex flex-wrap py-2">
25+ {#if contact.cover}
26+ <img src={contact.cover} alt="profile cover image"
27+ class="m-1 -mb-16 w-full h-48 border border-black rounded-lg object-cover min-w-0"
28+ />
29+ {/if}
30+ <div class="flex flex-wrap md:flex-nowrap justify-center">
31+ <div class="mx-8 max-w-[128px] min-w-[128px] h-fit grow overflow-hidden rounded-lg">
32+ {#if contact.avatar}
33+ <img src={contact.avatar} alt="custom avatar"
34+ class="aspect-square object-cover max-w-[128px] min-w-0"
35+ />
36+ {:else}
37+ <Sigil patp={patp} size={128} bgColor={color} useNew />
38+ {/if}
39+ </div>
40+ {#if contact.bio}
41+ <div class="text-sm p-3 bg-white rounded-tl-lg rounded-br-lg whitespace-pre-line">
42+ <SvelteMarkdown source={contact.bio} />
43+ </div>
44+ {/if}
45+ </div>
46+ <!-- <p class="whitespace-pre-line">
47+ {contact.bio}
48+ </p> -->
49+ </div>
50+ </ExtensionWindow>
51+{/if}
52+
ui/src/components/pals/PalStatusIndicator.sveltenew file+191
@@ -0,0 +1,191 @@
1+<script lang="ts">
2+ import type { PalStatus } from 'lib/pals';
3+ import tooltip from 'actions/tooltip';
4+ export let status: PalStatus = 'unconnected';
5+ export let acked: boolean = true;
6+ let className: string = '';
7+ export { className as class };
8+
9+ let statusDescription = '', viewBox = '', width = '25';
10+ $: {
11+ switch (status) {
12+ case 'mutual':
13+ statusDescription = 'you are pals'
14+ viewBox = '12 12 100 100';
15+ width = '25';
16+ break;
17+ case 'outgoing':
18+ statusDescription = 'you added them in pals'
19+ viewBox = '0 12 68 100';
20+ width = '17';
21+ if (!acked) {
22+ statusDescription += ' (pending)'
23+ }
24+ break;
25+ case 'incoming':
26+ statusDescription = 'they added you in pals'
27+ viewBox = '24 12 76 100';
28+ width = '19';
29+ break;
30+ }
31+ }
32+</script>
33+{#if status !== 'unconnected'}
34+ <svg width={width} height="25" {viewBox} fill="none" xmlns="http://www.w3.org/2000/svg"
35+ use:tooltip={statusDescription}
36+ class={className}
37+ >
38+
39+ {#if status == 'mutual'}
40+ <path d="M108 50
41+ C108 61.9348 103.259 73.3807 94.8198 81.8198
42+ C86.3807 90.2589 74.9347 95 63 95
43+ C51.0653 95 39.6193 90.2589 31.1802 81.8198
44+ C22.7411 73.3807 18 61.9347 18 50" stroke="black" stroke-width="10"></path><path d="M87.5114 31.5994
45+ C87.5114 28.4354 84.9226 25.8466 81.7585 25.8466
46+ C78.5945 25.8466 76.0057 28.4354 76.0057 31.5994
47+ C76.0057 34.7635 78.5945 37.3523 81.7585 37.3523
48+ C84.9226 37.3523 87.5114 34.7635 87.5114 31.5994
49+ Z
50+ M51.0767 31.5994
51+ C51.0767 28.4354 48.4879 25.8466 45.3239 25.8466
52+ C42.1598 25.8466 39.571 28.4354 39.571 31.5994
53+ C39.571 34.7635 42.1598 37.3523 45.3239 37.3523
54+ C48.4879 37.3523 51.0767 34.7635 51.0767 31.5994
55+ Z" fill="black"></path>
56+ {:else if status == 'outgoing' }
57+ {#if acked}
58+ <path d="M50 95
59+ C44.0905 95 38.2389 93.836 32.7792 91.5746
60+ C27.3196 89.3131 22.3588 85.9984 18.1802 81.8198
61+ C14.0016 77.6412 10.6869 72.6804 8.42542 67.2208
62+ C6.16396 61.7611 5 55.9095 5 50" stroke="black" stroke-width="10"></path><path d="M30.5546 65.9099
63+ L28.7868 67.6777
64+ L32.3223 71.2132
65+ L34.0901 69.4454
66+ L30.5546 65.9099
67+ Z
68+ M67.6777 32.3223
69+ L39.7938 39.7938
70+ L60.2062 60.2062
71+ L67.6777 32.3223
72+ Z
73+ M34.0901 69.4454
74+ L53.5355 50
75+ L50 46.4645
76+ L30.5546 65.9099
77+ L34.0901 69.4454
78+ Z" fill="black"></path>
79+ {:else}
80+ <path d="M50 95
81+ C44.0905 95 38.2389 93.836 32.7792 91.5746
82+ C27.3196 89.3131 22.3588 85.9984 18.1802 81.8198
83+ C14.0016 77.6412 10.6869 72.6804 8.42542 67.2208
84+ C6.16396 61.7611 5 55.9095 5 50" stroke="black" stroke-width="10"></path><path d="M31.4384 66.7938
85+ L30.5546 67.6777
86+ L32.3223 69.4454
87+ L33.2062 68.5616
88+ L31.4384 66.7938
89+ Z
90+ M67.6777 32.3223
91+ L53.7357 36.0581
92+ L63.9419 46.2643
93+ L67.6777 32.3223
94+ Z
95+ M33.2062 68.5616
96+ L34.974 66.7938
97+ L33.2062 65.026
98+ L31.4384 66.7938
99+ L33.2062 68.5616
100+ Z
101+ M38.5095 63.2583
102+ L42.045 59.7227
103+ L40.2773 57.955
104+ L36.7417 61.4905
105+ L38.5095 63.2583
106+ Z
107+ M45.5806 56.1872
108+ L49.1161 52.6517
109+ L47.3483 50.8839
110+ L43.8128 54.4194
111+ L45.5806 56.1872
112+ Z
113+ M52.6516 49.1161
114+ L56.1872 45.5806
115+ L54.4194 43.8128
116+ L50.8839 47.3484
117+ L52.6516 49.1161
118+ Z
119+ M59.7227 42.0451
120+ L63.2583 38.5095
121+ L61.4905 36.7418
122+ L57.9549 40.2773
123+ L59.7227 42.0451
124+ Z
125+ M30.5546 65.9099
126+ L28.7868 67.6777
127+ L32.3223 71.2132
128+ L34.0901 69.4454
129+ L30.5546 65.9099
130+ Z
131+ M67.6777 32.3223
132+ L39.7938 39.7938
133+ L60.2062 60.2062
134+ L67.6777 32.3223
135+ Z
136+ M34.0901 69.4454
137+ L35.8579 67.6777
138+ L32.3223 64.1421
139+ L30.5546 65.9099
140+ L34.0901 69.4454
141+ Z
142+ M39.3934 64.1421
143+ L42.9289 60.6066
144+ L39.3934 57.0711
145+ L35.8579 60.6066
146+ L39.3934 64.1421
147+ Z
148+ M46.4645 57.0711
149+ L50 53.5355
150+ L46.4645 50
151+ L42.9289 53.5355
152+ L46.4645 57.0711
153+ Z
154+ M53.5355 50
155+ L57.0711 46.4645
156+ L53.5355 42.9289
157+ L50 46.4645
158+ L53.5355 50
159+ Z
160+ M60.6066 42.9289
161+ L64.1421 39.3934
162+ L60.6066 35.8579
163+ L57.0711 39.3934
164+ L60.6066 42.9289
165+ Z" fill="black"></path>
166+ {/if}
167+ {:else if status == 'incoming' }
168+ <path d="M95 50
169+ C95 55.9095 93.836 61.7611 91.5746 67.2208
170+ C89.3131 72.6804 85.9984 77.6412 81.8198 81.8198
171+ C77.6412 85.9984 72.6804 89.3131 67.2208 91.5746
172+ C61.7611 93.836 55.9095 95 50 95" stroke="black" stroke-width="10"></path><path d="M67.2383 68.7383
173+ L59.7669 40.8545
174+ L39.3545 61.2669
175+ L67.2383 68.7383
176+ Z
177+ M31.5294 29.4939
178+ L29.7617 27.7262
179+ L26.2261 31.2617
180+ L27.9939 33.0295
181+ L31.5294 29.4939
182+ Z
183+ M53.0962 51.0607
184+ L31.5294 29.4939
185+ L27.9939 33.0295
186+ L49.5607 54.5962
187+ L53.0962 51.0607
188+ Z" fill="black"></path>
189+ {/if}
190+ </svg>
191+{/if}
ui/src/components/pals/PalTagger.sveltenew file+40
@@ -0,0 +1,40 @@
1+<script lang="ts">
2+ import { getPal, tagPal, untagPal } from 'lib/pals';
3+ import store from 'stores/store';
4+
5+ export let patp: string;
6+
7+ $: pal = getPal($store.pals, patp);
8+
9+ let newTagText = '';
10+ $: newTags = newTagText.split(',').map(t => t.trim());
11+ $: sanitizedNewTags = newTags.map(t => t.toLocaleLowerCase().replaceAll(/[^-_.~a-z0-9]+/g, '_'));
12+ $: newTagsAreValid = newTags.every(tagIsValid);
13+ $: validationClasses = newTagsAreValid ? '' : 'border-red-500 bg-red-100'
14+
15+ function tagIsValid(tag: string): boolean {
16+ return /^[-_.~a-z0-9]*$/.test(tag);
17+ }
18+
19+ async function onTagSubmit({ key }) {
20+ if (newTagText && key == 'Enter') {
21+ await tagPal(patp, sanitizedNewTags);
22+ newTagText = '';
23+ }
24+ }
25+</script>
26+
27+<div class="flex flex-wrap gap-1 text-sm justify-center">
28+ {#each pal.tags as tag}
29+ <div class="flex bg-gray-200 rounded divide-gray-500 divide-x items-center">
30+ <span class="px-1">
31+ {tag}
32+ </span>
33+ <button class="px-1 text-gray-500" on:click={() => untagPal(patp, tag)}>
34+ X
35+ </button>
36+ </div>
37+ {/each}
38+</div>
39+<input type="text" class="px-1 text-sm border border-gray-400 rounded {validationClasses}"
40+ on:keydown={onTagSubmit} bind:value={newTagText} placeholder="add_tags, like_this" />
ui/src/components/pals/PalsShipViewExtension.sveltenew file+34
@@ -0,0 +1,34 @@
1+<script lang="ts">
2+ import { getPal, addPal, removePal } from 'lib/pals';
3+ import store from 'stores/store';
4+ import ExtensionWindow from '@/ExtensionWindow.svelte';
5+ import PalStatusIndicator from './PalStatusIndicator.svelte';
6+ import PalTagger from './PalTagger.svelte';
7+
8+ export let patp: string;
9+
10+ $: pal = getPal($store.pals, patp);
11+ $: palAdded = pal.status == 'outgoing' || pal.status == 'mutual';
12+ $: [buttonText, buttonFunc] = palAdded ? ['Remove Pal', removePal] : ['Add Pal', addPal];
13+ $: itsUs = patp.substring(1) === window.ship;
14+</script>
15+
16+{#if $store.palsInstalled && !itsUs}
17+ <ExtensionWindow name="%pals" halfWidth>
18+ <svelte:fragment slot="top-bar">
19+ <div class="grow ml-1 my-2 flex justify-center align-center">
20+ <PalStatusIndicator status={pal.status} acked={pal.ack} />
21+ <button class="bg-gray-200 ml-3 px-2 rounded" on:click={() => buttonFunc(patp)}>
22+ {buttonText}
23+ </button>
24+ </div>
25+ </svelte:fragment>
26+
27+ {#if palAdded}
28+ <div class="flex flex-wrap justify-center gap-3 my-3 conteents">
29+ <PalTagger {patp}></PalTagger>
30+ </div>
31+ {/if}
32+ </ExtensionWindow>
33+{/if}
34+
ui/src/components/search/SearchBar.svelte+38−11
@@ -3,6 +3,7 @@
3 import Symbol from './sigil/Symbol.svelte';
3 import Symbol from './sigil/Symbol.svelte';
4
4
5 import docs from 'stores/docs';
5 import docs from 'stores/docs';
6+ import keys from 'stores/keys';
6 import { SearchAnalysis } from 'lib/id';
7 import { SearchAnalysis } from 'lib/id';
7 import { SigilQuery } from 'lib/sigil';
8 import { SigilQuery } from 'lib/sigil';
8
9
@@ -12,8 +13,16 @@
12 export let sigilQuery: SigilQuery = new SigilQuery('planet');
13 export let sigilQuery: SigilQuery = new SigilQuery('planet');
13 sigilQuery.urlString = search;
14 sigilQuery.urlString = search;
14 export let searchMode: ('patp' | 'sigil') = 'patp';
15 export let searchMode: ('patp' | 'sigil') = 'patp';
16+ export let allowSigilSearch: boolean = true;
15− export let placeholderText = "Search by @p or #. '*' matches any character";
17+ let defaultPlaceholderText = "Search by @p or #. '*' matches any character";
18+ export let placeholderText = defaultPlaceholderText;
16
19
20+ $: {
21+ placeholderText = placeholderText ? placeholderText : defaultPlaceholderText;
22+ }
23+ $: {
24+ searchMode = allowSigilSearch ? searchMode : 'patp';
25+ }
17 $: showSigilInput = searchMode == 'sigil';
26 $: showSigilInput = searchMode == 'sigil';
18
27
19 let bouncySearch: string = search;
28 let bouncySearch: string = search;
@@ -70,8 +79,22 @@
70 }
79 }
71 }
80 }
72
81
82+ let searchInput;
83+ let searchFocused = false;
84+ function onFocus() { searchFocused = true; }
85+ function onBlur() { searchFocused = false; }
86+ function focus() { if (searchInput) searchInput.focus() }
87+ function blur() { if (searchInput) searchInput.blur() }
88+ $:{
89+ $keys.keyup.get('/').set('searchBar', focus);
90+ // generic binding in App.svelte takes care of blur
91+ $keys.keyup.get('p').set('searchBar', closeSigilInput);
92+ $keys.keyup.get('s').set('searchBar', openSigilInput);
93+ }
94+
73 function openSigilInput() {
95 function openSigilInput() {
96+ if (allowSigilSearch)
74− searchMode = 'sigil';
97+ searchMode = 'sigil';
75 }
98 }
76
99
77 function closeSigilInput() {
100 function closeSigilInput() {
@@ -82,7 +105,7 @@
82 }
105 }
83
106
84 function showSigilDoc() {
107 function showSigilDoc() {
85− docs.show('sigil');
108+ docs.show('sigil-input');
86 }
109 }
87
110
88 const buttonClasses = 'rounded-full bg-black text-white text-sm transition-opacity opacity-50 hover:opacity-80';
111 const buttonClasses = 'rounded-full bg-black text-white text-sm transition-opacity opacity-50 hover:opacity-80';
@@ -110,16 +133,20 @@
110 spellcheck="false"
133 spellcheck="false"
111 placeholder={placeholderText}
134 placeholder={placeholderText}
112 on:input={(e) => bouncySearch = e.target.value}
135 on:input={(e) => bouncySearch = e.target.value}
136+ on:focus={onFocus} on:blur={onBlur}
137+ bind:this={searchInput}
113 value={search}
138 value={search}
114 autofocus
139 autofocus
115 />
140 />
141+ {#if allowSigilSearch}
116− <button
142+ <button
117− on:click={openSigilInput}
143+ on:click={openSigilInput}
118− class="absolute top-1 right-1 w-8 h-8 {buttonClasses}"
144+ class="absolute top-1 right-1 w-8 h-8 {buttonClasses}"
119− >
145+ >
120− <Symbol components={'gci.lvf.lhf.lbhb.atrf4.al3.cbl2lf.cbl2mb'.split('.')} size={32}
146+ <Symbol components={'gci.lvf.lhf.lbhb.atrf4.al3.cbl2lf.cbl2mb'.split('.')} size={32}
121− altBgColor="transparent"
147+ altBgColor="transparent"
122− />
148+ />
123− </button>
149+ </button>
150+ {/if}
124 {/if}
151 {/if}
125</div>
152</div>
ui/src/components/search/sigil/ClanSelect.svelte+10−2
@@ -3,20 +3,28 @@
3
3
4 import { capitalize } from 'lib/utils';
4 import { capitalize } from 'lib/utils';
5 import { CLANS, Clan } from 'types/sigil';
5 import { CLANS, Clan } from 'types/sigil';
6+ import keys from 'stores/keys';
6
7
7 export let clan: Clan = 'planet';
8 export let clan: Clan = 'planet';
8
9
9 let dropdownOpen = false;
10 let dropdownOpen = false;
10
11
12+ function openDropdown() { dropdownOpen = true }
13+ function closeDropdown() { dropdownOpen = false }
14+
11 function selectClan(itemClan: typeof clan) {
15 function selectClan(itemClan: typeof clan) {
12 clan = itemClan;
16 clan = itemClan;
13 dropdownOpen = false;
17 dropdownOpen = false;
14 }
18 }
19+
20+ $: {
21+ $keys.keyup.get('escape').set('clanSelect', closeDropdown);
22+ }
15</script>
23</script>
16
24
17<div class="text-xl text-center bg-white">
25<div class="text-xl text-center bg-white">
18 <p
26 <p
19− on:click={() => dropdownOpen = true}
27+ on:click={openDropdown}
20 class=" py-2"
28 class=" py-2"
21 >
29 >
22 {capitalize(clan)}
30 {capitalize(clan)}
@@ -24,7 +32,7 @@
24 </p>
32 </p>
25 {#if dropdownOpen}
33 {#if dropdownOpen}
26 <div class="absolute z-20 top-1 w-full flex flex-col mx-auto divide-y bg-white rounded-2xl border-b">
34 <div class="absolute z-20 top-1 w-full flex flex-col mx-auto divide-y bg-white rounded-2xl border-b">
27− <h3 class="py-2" on:click={() => dropdownOpen = false}>
35+ <h3 class="py-2" on:click={closeDropdown}>
28 Select Sigil Type
36 Select Sigil Type
29 </h3>
37 </h3>
30 {#each CLANS as itemClan}
38 {#each CLANS as itemClan}
ui/src/components/search/sigil/SigilInput.svelte+13−1
@@ -6,8 +6,9 @@
6 import ToggleButton from '@/buttons/ToggleButton.svelte';
6 import ToggleButton from '@/buttons/ToggleButton.svelte';
7
7
8 import type { SigilQuery } from 'lib/sigil';
8 import type { SigilQuery } from 'lib/sigil';
9− import { searchSettings } from 'stores/searchStores';
10 import { toggleStoreKey } from 'lib/utils';
9 import { toggleStoreKey } from 'lib/utils';
10+ import { searchSettings } from 'stores/searchStores';
11+ import keys from 'stores/keys';
11
12
12 export let sigilQuery: SigilQuery;
13 export let sigilQuery: SigilQuery;
13 let focusedSymbolIndex: number = undefined;
14 let focusedSymbolIndex: number = undefined;
@@ -37,6 +38,17 @@
37 $: {
38 $: {
38 sigilQuery.allowFictional = $searchSettings.allowFictionalSigils;
39 sigilQuery.allowFictional = $searchSettings.allowFictionalSigils;
39 }
40 }
41+
42+ function keyboardClear() {
43+ if (!focusedSymbol) {
44+ clear();
45+ }
46+ }
47+
48+ $: {
49+ $keys.keyup.get('x').set('sigilInput', keyboardClear);
50+ $keys.keyup.get('backspace').set('sigilInput', keyboardClear);
51+ }
40</script>
52</script>
41
53
42<div>
54<div>
ui/src/components/search/sigil/SymbolInputModeSelector.svelte+96−57
@@ -5,6 +5,7 @@
5 import SymbolInputModeSelectorButton from './SymbolInputModeSelectorButton.svelte';
5 import SymbolInputModeSelectorButton from './SymbolInputModeSelectorButton.svelte';
6
6
7 import { searchSettings } from 'stores/searchStores';
7 import { searchSettings } from 'stores/searchStores';
8+ import keys from 'stores/keys';
8 import type { PartType, ModeDef, ModeDefs } from 'types/sigil';
9 import type { PartType, ModeDef, ModeDefs } from 'types/sigil';
9 import type { SymbolQuery } from 'lib/sigil';
10 import type { SymbolQuery } from 'lib/sigil';
10 import { MODE_DEFS } from 'types/sigil';
11 import { MODE_DEFS } from 'types/sigil';
@@ -14,96 +15,128 @@
14 export let inputComponents: string[] = [];
15 export let inputComponents: string[] = [];
15 export let unfocusSymbol: Function = () => {};
16 export let unfocusSymbol: Function = () => {};
16
17
17− let modes: string[] = [];
18+ let mode: string = null;
19+ let subMode: string = null;
18 let modeDef: ModeDef = undefined;
20 let modeDef: ModeDef = undefined;
21+ let unselectedSubModesExist: boolean = false;
19− let subModeDefs: ModeDefs = MODE_DEFS;
22+ let modeDefs: ModeDefs = MODE_DEFS;
23+ let plausibleModeDefs: { name: string, def: ModeDef }[] = [];
20
24
21 function showGeonSelector() {
25 function showGeonSelector() {
22− if (!symbolQuery.components.length && modes.length == 0) {
26+ if (!symbolQuery.components.length && !mode) {
23− modes = ['geon'];
27+ mode = 'geon';
28+ subMode = null;
24 }
29 }
25 }
30 }
26
31
27 $: symbolQuery.components, showGeonSelector();
32 $: symbolQuery.components, showGeonSelector();
28
33
29− function pushMode(mode: string) {
34+ function pushMode(newMode: string) {
30− if (modeDef?.modes) {
35+ const subModes = modeDef?.modes;
31− modes = [...modes, mode];
36+ if (subModes || subMode) {
37+ subMode = newMode;
32 } else {
38 } else {
33− modes = [...modes.slice(0, -1), mode];
39+ mode = newMode;
40+ subMode = null;
34 }
41 }
35 }
42 }
36
43
37 function popMode() {
44 function popMode() {
38− if (modes.length) {
45+ if (mode || subMode) {
39− if (modeDef?.modes) {
46+ mode = null;
40− modes = modes.slice(0, -1);
47+ subMode = null;
41− } else {
42− modes = modes.slice(0, -2);
43− }
44 } else {
48 } else {
45 unfocusSymbol();
49 unfocusSymbol();
46 }
50 }
47 }
51 }
48
52
49 $: {
53 $: {
54+ let modeResolved = false;
55+ let count = 0;
56+ while (!modeResolved && count < 5) {
50− modeDef = undefined;
57+ modeDef = undefined;
51− subModeDefs = MODE_DEFS;
58+ modeDefs = MODE_DEFS;
52− for (let mode of modes) {
59+ if (mode) {
53− if (modeDef && modeDef.modes) {
60+ modeDef = MODE_DEFS[mode];
54− subModeDefs = modeDef.modes;
61+ }
62+ unselectedSubModesExist = !!modeDef?.modes;
63+ if (unselectedSubModesExist) {
64+ modeDefs = modeDef.modes;
65+ if (subMode) {
66+ if (modeDefs[subMode]) {
67+ modeDef = modeDefs[subMode];
68+ } else {
69+ console.error(`Unknown subMode: ${subMode} of ${mode} in defs:`, modeDefs);
70+ subMode = null;
71+ }
72+ }
55 }
73 }
56− if (subModeDefs[mode]) {
74+ unselectedSubModesExist = !!modeDef?.modes;
57− modeDef = subModeDefs[mode];
75+ if (!isDefPlausible(modeDef) && mode !== 'geon') {
76+ if (subMode) {
77+ subMode = null;
78+ } else {
79+ mode = null;
80+ }
58 } else {
81 } else {
59− console.error(`Unknown mode: ${mode} of ${modes} in defs:`, subModeDefs);
82+ plausibleModeDefs = getPlausibleModeDefs(modeDefs);
83+ if (unselectedSubModesExist) {
84+ subMode = plausibleModeDefs[0].name;
85+ } else {
86+ modeResolved = true;
87+ }
88+ }
89+ count++;
90+ if (count >= 5 && !modeResolved) {
91+ console.error("sigil input mode resulotion failed");
60 }
92 }
61 }
93 }
62−
94+ inputComponents = [];
63− if (modeDef && modeDef.modes) {
64− subModeDefs = modeDef.modes;
65− }
66−
67− if (modeDef?.parts?.length && !modeDef.modes) {
95+ if (modeDef?.parts?.length) {
68 inputComponents = modeDef.parts;
96 inputComponents = modeDef.parts;
69 if (!$searchSettings.allowFictionalSigils) {
97 if (!$searchSettings.allowFictionalSigils) {
70 inputComponents = symbolQuery.filterPlausibleParts(inputComponents);
98 inputComponents = symbolQuery.filterPlausibleParts(inputComponents);
71 }
99 }
72− } else {
73− inputComponents = [];
74 }
100 }
75 }
101 }
76
102
77− $: plausibleModes = Object.entries(subModeDefs).map(([modeName, def]) => {
103+ function isDefPlausible(def: ModeDef): boolean {
104+ if (!def) return true;
78 const deepParts = getDeepParts(def);
105 const deepParts = getDeepParts(def);
79− const plausible = $searchSettings.allowFictionalSigils || symbolQuery.arePartsPlausible(deepParts);
106+ return $searchSettings.allowFictionalSigils || symbolQuery.arePartsPlausible(deepParts);
80− if (!plausible && modeName !== 'geon') return null;
107+ }
81− const parts = filterPartsByGeon(def.displayParts || def.parts, symbolQuery.geon);
82− return { parts, modeName };
83− }).filter(mode => mode !== null);
84
108
85− $: {
109+ function getPlausibleModeDefs(modeDefs: ModeDefs) {
86− const currentMode = modes.slice(-1)[0];
110+ return Object.entries(modeDefs).map(([modeName, def]) => {
87− if (currentMode != 'geon') {
111+ const plausible = isDefPlausible(def);
88− if (plausibleModes.length === 0) {
112+ if (!plausible && modeName !== 'geon') return null;
89− popMode();
113+ const parts = filterPartsByGeon(def.displayParts || def.parts, symbolQuery.geon);
90− } else if (plausibleModes.length >= 1) {
114+ return {
91− const leafMode = modeDef && !(modeDef.modes);
115+ name: modeName,
92− const plausibleModeNames = plausibleModes.map(({ modeName }) => modeName);
116+ def: {
93− if ((modes.length == 1 && modeDef.modes) || (leafMode && !plausibleModeNames.includes(currentMode))) {
117+ ...def,
94− pushMode(plausibleModes[0].modeName);
118+ parts,
95− }
119+ },
96− }
120+ };
97− }
121+ }).filter(mode => mode !== null);
98 }
122 }
99
123
100 function clear() {
124 function clear() {
101− if (modes.length) {
125+ // If a geon is present it should be preserved, unless it's the only component
102− symbolQuery = symbolQuery.clearPartType(modes[0] as PartType);
126+ // If there's only 1 component, it should be removed
103− } else {
127+ // Otherwise, at least one component might be a geon and should be preserved
128+ if (symbolQuery.components.length === 1) {
104 symbolQuery = symbolQuery.clear();
129 symbolQuery = symbolQuery.clear();
130+ } else {
131+ symbolQuery = symbolQuery.clearExceptPartType('geon');
105 }
132 }
106 }
133 }
134+
135+ $: {
136+ $keys.keyup.get('x').set('sigilMode', clear);
137+ $keys.keyup.get('backspace').set('sigilMode', clear);
138+ $keys.keyup.get('escape').set('sigilMode', popMode);
139+ }
107</script>
140</script>
108
141
109<style lang="scss">
142<style lang="scss">
@@ -113,17 +146,23 @@
113</style>
146</style>
114
147
115<div class="mx-auto flex justify-center xs:items-center xs:flex-col">
148<div class="mx-auto flex justify-center xs:items-center xs:flex-col">
116− <SymbolInputNavButtons onDone={popMode} onClear={clear} />
149+ <SymbolInputNavButtons onDone={popMode} onClear={clear} hideClearButton={symbolQuery.components.length == 0} />
117 <div class="max-w-[15rem] h-full grid grid-cols-custom gap-1 justify-center xs:max-w-[8rem]">
150 <div class="max-w-[15rem] h-full grid grid-cols-custom gap-1 justify-center xs:max-w-[8rem]">
118− {#if modes[0] === 'geon'}
151+ {#if mode === 'geon'}
119 <GeonSelector bind:symbolQuery {popMode} />
152 <GeonSelector bind:symbolQuery {popMode} />
120 {:else}
153 {:else}
121− {#each plausibleModes as { modeName, parts }}
154+ {#each plausibleModeDefs as { name, def: { parts } }}
122− <SymbolInputModeSelectorButton components={parts} focused={modes.slice(-1)[0] === modeName}
155+ <SymbolInputModeSelectorButton components={parts} focused={mode === name || subMode === name}
123− on:click={() => pushMode(modeName)}
156+ on:click={() => pushMode(name)}
124 />
157 />
125 {/each}
158 {/each}
126 {/if}
159 {/if}
127 </div>
160 </div>
161+ <!-- <p class="break-all w-24">
162+ mode: {mode}
163+ </p>
164+ <p class="break-all w-24">
165+ sub: {subMode}
166+ </p> -->
128</div>
167</div>
129
168
ui/src/components/search/sigil/SymbolInputNavButtons.svelte+8−3
@@ -4,6 +4,11 @@
4
4
5 export let onDone: () => void = null;
5 export let onDone: () => void = null;
6 export let onClear: () => void = null;
6 export let onClear: () => void = null;
7+ export let hideDoneButton = false;
8+ export let hideClearButton = false;
9+
10+ $: showDoneButton = (onDone && !hideDoneButton);
11+ $: showClearButton = (onClear && !hideClearButton);
7
12
8 let buttonClasses = 'cursor-pointer border border-gray-500 rounded-md aspect-square w-[48px]';
13 let buttonClasses = 'cursor-pointer border border-gray-500 rounded-md aspect-square w-[48px]';
9</script>
14</script>
@@ -14,11 +19,11 @@
14 }
19 }
15</style>
20</style>
16
21
17−<div class="grid gap-1 self-start justify-center mr-1 xs:mr-0 xs:mb-1 xs:self-stretch {(onDone && onClear) ? 'grid-cols-custom' : ''}">
22+<div class="grid gap-1 self-start justify-center mr-1 xs:mr-0 xs:mb-1 xs:self-stretch {(showDoneButton && showClearButton) ? 'grid-cols-custom' : ''}">
18− {#if onDone}
23+ {#if showDoneButton}
19 <img src={upArrow} alt="done" class={buttonClasses} on:click={onDone}/>
24 <img src={upArrow} alt="done" class={buttonClasses} on:click={onDone}/>
20 {/if}
25 {/if}
21− {#if onClear}
26+ {#if showClearButton}
22 <img src={xIcon} alt="clear" class={buttonClasses} on:click={onClear}/>
27 <img src={xIcon} alt="clear" class={buttonClasses} on:click={onClear}/>
23 {/if}
28 {/if}
24 </div>
29 </div>
ui/src/components/ship_view/AzimuthDetails.sveltenew file+90
@@ -0,0 +1,90 @@
1+<script lang="ts">
2+ import { clan } from 'urbit-ob';
3+ import TooltipAndDocLink from '@/common/TooltipAndDocLink.svelte';
4+ import EthAddressLink from '@/common/EthAddressLink.svelte';
5+ import ShipChain from './ShipChain.svelte';
6+
7+ export let patp: string;
8+ export let pointInfo: any = {};
9+
10+
11+ $: shipClass = clan(patp);
12+ $: azPoint = ['galaxy', 'star', 'planet'].includes(shipClass);
13+</script>
14+
15+{#if azPoint && pointInfo.life !== undefined}
16+ {#if shipClass !== 'galaxy'}
17+ <p>
18+ {#if pointInfo.sponsorChain && pointInfo.sponsorChain.length > 0}
19+ <p>
20+ {pointInfo.sponsorChain.length === 1 ? 'Sponsor:' : 'Sponsor Chain:'}
21+ <ShipChain shipChain={pointInfo.sponsorChain}></ShipChain>
22+ </p>
23+ {:else}
24+ Unsponsored
25+ {/if}
26+ </p>
27+ {/if}
28+ <p>
29+ <TooltipAndDocLink
30+ text="Number of times this ship's keys have been set. Must be at least 1 to use the network."
31+ doc="keys"
32+ class="inline"
33+ >
34+ Key Revision:
35+ {pointInfo.life}
36+ </TooltipAndDocLink>
37+ </p>
38+ <p>
39+ <TooltipAndDocLink
40+ text="Number of times this ship has breached."
41+ doc="keys"
42+ class="inline"
43+ >
44+ Factory Resets:
45+ {pointInfo.rift}
46+ </TooltipAndDocLink>
47+ </p>
48+ {#if pointInfo.proxies}
49+ <p>
50+ <span>
51+ Owner:
52+ <EthAddressLink address={pointInfo.proxies.owner.address} />
53+ </span>
54+ </p>
55+ <p>
56+ <span>
57+ Management Proxy:
58+ <EthAddressLink address={pointInfo.proxies['management-proxy'].address}>
59+ <span class="text-gray-700">not set</span>
60+ </EthAddressLink>
61+ </span>
62+ </p>
63+ <p>
64+ <span>
65+ Spawn Proxy:
66+ <EthAddressLink address={pointInfo.proxies['spawn-proxy'].address}>
67+ <span class="text-gray-700">not set</span>
68+ </EthAddressLink>
69+ </span>
70+ </p>
71+ <p>
72+ <span>
73+ Transfer Proxy:
74+ <EthAddressLink address={pointInfo.proxies['transfer-proxy'].address}>
75+ <span class="text-gray-700">not set</span>
76+ </EthAddressLink>
77+ </span>
78+ </p>
79+ {#if shipClass === 'galaxy'}
80+ <p>
81+ <span>
82+ Voting Proxy:
83+ <EthAddressLink address={pointInfo.proxies['voting-proxy'].address}>
84+ [not set]
85+ </EthAddressLink>
86+ </span>
87+ </p>
88+ {/if}
89+ {/if}
90+{/if}
ui/src/components/ShipChain.svelte → ui/src/components/ship_view/ShipChain.svelterenamed+1−1
@@ -1,5 +1,5 @@
1<script lang="ts">
1<script lang="ts">
2− import ShipLink from './ShipLink.svelte';
2+ import ShipLink from '@/common/ShipLink.svelte';
3
3
4 export let shipChain: string[];
4 export let shipChain: string[];
5</script>
5</script>
ui/src/components/ship_view/ShipChildrenTabs.sveltenew file+136
@@ -0,0 +1,136 @@
1+<script lang="ts">
2+ import { clan } from 'urbit-ob';
3+
4+ import { normalizeId, filterIdsForChildMoons } from 'lib/id';
5+ import { getSpawnedPoints, getUnlockedSpawnedPoints, getLockedSpawnedPoints } from 'lib/api';
6+ import store from 'stores/store';
7+ import ShipListings from '@/ShipListings.svelte';
8+
9+ export let patp: string;
10+ export let spawnedCount: number = undefined;
11+
12+ let rawSpawnedPointsPromise: any;
13+ let rawSpawnedPoints: any = null;
14+ let rawLockedPointsPromise: any;
15+ let rawLockedPoints: any = null;
16+ let spawnedPoints: string[] = [];
17+ let lockedPoints: string[] = [];
18+
19+ $: shipClass = clan(patp);
20+ $: azPoint = ['galaxy', 'star', 'planet'].includes(shipClass);
21+ $: canSpawnPoints = ['galaxy', 'star'].includes(shipClass);
22+ $: spawnableClassPlural = !canSpawnPoints ? '' : (shipClass === 'galaxy' ? 'Star' : 'Planet');
23+ $: {
24+ rawSpawnedPoints = null;
25+ rawLockedPoints = null;
26+ if (canSpawnPoints) {
27+ let galaxy = shipClass === 'galaxy';
28+ let getSpawnedPointsFunc = galaxy ? getUnlockedSpawnedPoints : getSpawnedPoints;
29+ if (galaxy) {
30+ rawLockedPointsPromise = getLockedSpawnedPoints(patp);
31+ rawLockedPointsPromise.then((info) => {
32+ rawLockedPoints = info;
33+ }).catch((error) => {
34+ rawLockedPoints = { error };
35+ });
36+ }
37+ rawSpawnedPointsPromise = getSpawnedPointsFunc(patp);
38+ rawSpawnedPointsPromise.then((info) => {
39+ rawSpawnedPoints = info;
40+ }).catch((error) => {
41+ rawSpawnedPoints = { error };
42+ });
43+ }
44+ }
45+ $: {
46+ spawnedPoints = [];
47+ if (rawSpawnedPoints?.points) {
48+ spawnedPoints = rawSpawnedPoints?.points.map(patpToShip);
49+ }
50+ }
51+ $: {
52+ lockedPoints = [];
53+ if (rawLockedPoints?.points) {
54+ lockedPoints = rawLockedPoints?.points.map(patpToShip);
55+ }
56+ }
57+ $: trueSpawnedCount = spawnedCount || spawnedPoints.length;
58+ $: spawnedTab = {
59+ name: 'Spawned',
60+ plural: 'Spawned ' + spawnableClassPlural + (trueSpawnedCount == 1 ? '' : 's'),
61+ promise: rawSpawnedPointsPromise,
62+ points: spawnedPoints,
63+ count: trueSpawnedCount,
64+ };
65+ $: lockedTab = {
66+ name: 'Locked',
67+ plural: 'Locked ' + spawnableClassPlural + (lockedPoints.length == 1 ? '' : 's'),
68+ promise: rawLockedPointsPromise,
69+ points: lockedPoints,
70+ count: lockedPoints.length,
71+ };
72+ $: moons = filterIdsForChildMoons($store.peers, patp).map(patpToShip);
73+ $: moonsTab = {
74+ name: 'Moons',
75+ plural: 'Known Moon' + (moons.length == 1 ? '' : 's'),
76+ promise: true,
77+ points: moons,
78+ count: moons.length,
79+ };
80+ let tabIndex = 0;
81+ let tabs = [];
82+ $: {
83+ tabs = [];
84+ if (spawnedCount !== undefined || spawnedPoints.length > 0) {
85+ tabs.push(spawnedTab);
86+ }
87+ if (lockedPoints.length > 0) {
88+ tabs.push(lockedTab);
89+ }
90+ if (azPoint) {
91+ tabs.push(moonsTab);
92+ }
93+ }
94+ $: tab = tabs[tabIndex];
95+
96+ function patpToShip(id: string) {
97+ return { patp: normalizeId(id) };
98+ }
99+</script>
100+
101+<div class="p-3">
102+ {#if tabs.length > 1}
103+ <div class="flex gap-x-1">
104+ {#each tabs as tab, i}
105+ <h3 on:click={() => tabIndex = i}
106+ class:bg-gray-200={tabIndex !== i}
107+ class="px-2 border border-gray-300 border-b-0 rounded-t-md cursor-pointer"
108+ >
109+ {tab.name}
110+ </h3>
111+ {/each}
112+ </div>
113+ {/if}
114+ {#if tab}
115+ <div>
116+ <h3 class="text-lg m-2">
117+ {#if tab.count}
118+ {tab.count} {tab.plural}:
119+ {:else}
120+ No {tab.plural}
121+ {/if}
122+ </h3>
123+ {#await tab.promise}
124+ Loading {tab.plural}...
125+ {:then}
126+ <!-- TODO: pagination -->
127+ <ShipListings
128+ ships={tab.points}
129+ linkToShips
130+ differentiateContacts
131+ />
132+ {/await}
133+ </div>
134+ {/if}
135+</div>
136+
ui/src/components/ship_view/ShipView.sveltenew file+130
@@ -0,0 +1,130 @@
1+<script lang="ts">
2+ import { clan, patp2dec } from 'urbit-ob';
3+
4+ import { cite, normalizeId } from 'lib/id';
5+ import { getPoint, getSpawnedPoints } from 'lib/api';
6+ import { generateParentChain, processPointInfo } from 'lib/ship';
7+ import Sigil from '@/common/Sigil.svelte';
8+ import GoldBadge from '@/common/GoldBadge.svelte';
9+ import TooltipAndDocLink from '@/common/TooltipAndDocLink.svelte';
10+ import ShipLink from '@/common/ShipLink.svelte';
11+ import ChartLink from '@/common/ChartLink.svelte';
12+ import EthAddressLink from '@/common/EthAddressLink.svelte';
13+ import LoadingSpinner from '@/common/LoadingSpinner.svelte';
14+ import CollapsibleContent from '@/CollapsibleContent.svelte';
15+ import ShipChain from './ShipChain.svelte';
16+ import AzimuthDetails from './AzimuthDetails.svelte';
17+ import ShipChildrenTabs from './ShipChildrenTabs.svelte';
18+ import ProfileShipViewExtension from '~/components/extensions/ProfileShipViewExtension.svelte';
19+ import PalsShipViewExtension from '@/pals/PalsShipViewExtension.svelte';
20+ import AppsShipViewExtension from '@/extensions/AppsShipViewExtension.svelte';
21+ // import GroupsShipViewExtension from '~/components/extensions/GroupsShipViewExtension.svelte';
22+
23+ export let patp: string;
24+
25+ let parentChain: any[];
26+ let rawPointInfoPromise: any;
27+ let rawPointInfo: any = null;
28+ let pointInfo: any = {};
29+
30+ $: shipClass = clan(patp);
31+ $: azPoint = ['galaxy', 'star', 'planet'].includes(shipClass);
32+ $: {
33+ parentChain = generateParentChain(patp);
34+ }
35+ $: {
36+ rawPointInfo = null;
37+ if (azPoint) {
38+ rawPointInfoPromise = getPoint(patp);
39+ rawPointInfoPromise.then((info) => {
40+ rawPointInfo = info;
41+ }).catch((error) => {
42+ rawPointInfo = { error };
43+ });
44+ }
45+ }
46+ $: {
47+ pointInfo = {};
48+ if (azPoint) {
49+ pointInfo = processPointInfo(rawPointInfo);
50+ }
51+ }
52+</script>
53+
54+<div class="p-8 max-w-md w-full rounded-lg bg-white flex flex-col md:max-w-2xl">
55+ <div class="flex flex-col items-center space-y-4 md:flex-row md:items-start md:space-x-8 md:space-y-0">
56+ <div class="w-full max-w-[256px] md:min-w-[256px] flex-shrink-0">
57+ <Sigil patp={patp} size={256} useNew />
58+ </div>
59+ <div class="grow w-full min-w-0">
60+ <h2 class="text-2xl text-center">
61+ <TooltipAndDocLink
62+ text="The ship's @p (click to learn more)"
63+ doc="patpee" class="inline"
64+ >
65+ {cite(patp)}
66+ </TooltipAndDocLink>
67+ </h2>
68+ {#if (!azPoint)}
69+ <h3 class="text-lg text-center text-gray-700">{patp}</h3>
70+ {/if}
71+ <h4 class="text-center text-gray-700 break-words">
72+ {patp2dec(patp)}
73+ </h4>
74+ <div class="flex flex-wrap justify-center gap-1.5 my-1.5">
75+ <GoldBadge title={"it's a " + shipClass} doc={shipClass}>{shipClass}</GoldBadge>
76+ {#if azPoint}
77+ {#if pointInfo.spawnStatus}
78+ <GoldBadge title={pointInfo.spawnStatus.tooltip}>
79+ {pointInfo.spawnStatus.text}
80+ </GoldBadge>
81+ {/if}
82+ {#if pointInfo.layer}
83+ <GoldBadge title={pointInfo.layer.tooltip} doc="l2">{pointInfo.layer.text}</GoldBadge>
84+ {/if}
85+ {:else if (shipClass === 'moon')}
86+ <span>of</span>
87+ <ShipLink patp={parentChain.at(-1)}></ShipLink>
88+ {:else if (shipClass === 'comet')}
89+ <span>under</span>
90+ <ShipLink patp={parentChain.at(-1)}></ShipLink>
91+ {/if}
92+ </div>
93+ {#if pointInfo?.spawnedUnlocked}
94+ <div class="w-full text-center my-3">
95+ <ChartLink {patp}>View in Star Chart ↗</ChartLink>
96+ </div>
97+ {/if}
98+ <div class="mt-4 w-full break-words">
99+ {#if parentChain.length > 0}
100+ <p>
101+ {parentChain.length === 1 ? 'Parent:' : 'Parent Chain:'}
102+ <ShipChain shipChain={parentChain}></ShipChain>
103+ </p>
104+ {/if}
105+ {#if azPoint}
106+ {#await rawPointInfoPromise}
107+ <LoadingSpinner spinnerClass="text-black" />
108+ {:then}
109+ <CollapsibleContent>
110+ <b slot="title">Azimuth Details</b>
111+ <div slot="content">
112+ <AzimuthDetails {patp} {pointInfo}></AzimuthDetails>
113+ </div>
114+ </CollapsibleContent>
115+ {/await}
116+ {/if}
117+ </div>
118+ </div>
119+ </div>
120+ <div class="flex flex-wrap shrink min-h-0 h-fit -m-1 mt-2 justify-evenly justify-items-center">
121+ <ProfileShipViewExtension {patp} />
122+ <PalsShipViewExtension {patp} />
123+ <AppsShipViewExtension {patp} />
124+ </div>
125+ {#if azPoint}
126+ <div class="mt-5 border-t">
127+ <ShipChildrenTabs {patp} />
128+ </div>
129+ {/if}
130+</div>
ui/src/lib/api.ts+11−1
@@ -10,6 +10,8 @@ const api: UrbitApi = new UrbitApi('', '', window.desk);
10api.ship = window.ship;
10api.ship = window.ship;
11// api.connect();
11// api.connect();
12
12
13+export { api };
14+
13export function scry<T = any>(path: string): Promise<T> {
15export function scry<T = any>(path: string): Promise<T> {
14 return api.scry<T>({ app: 'astrolabe', path });
16 return api.scry<T>({ app: 'astrolabe', path });
15}
17}
@@ -22,6 +24,14 @@ export function getSpawnedPoints(patp: string): Promise<any> {
22 return scry<any>(`/point/${patp}/spawned`);
24 return scry<any>(`/point/${patp}/spawned`);
23}
25}
24
26
27+export function getLockedSpawnedPoints(patp: string): Promise<any> {
28+ return scry<any>(`/point/${patp}/spawned/locked`);
29+}
30+
31+export function getUnlockedSpawnedPoints(patp: string): Promise<any> {
32+ return scry<any>(`/point/${patp}/spawned/unlocked`);
33+}
34+
25export function getPeers(): Promise<any> {
35export function getPeers(): Promise<any> {
26 return scry<any>(`/peers`);
36 return scry<any>(`/peers`);
27}
37}
@@ -86,7 +96,7 @@ export function subscribeToContacts(e: (data: any) => void): Promise<any> {
86 console.error(`Subscription to contact-store/all just get "err"`);
96 console.error(`Subscription to contact-store/all just get "err"`);
87 },
97 },
88 quit: () => {
98 quit: () => {
89− console.error(`Subscription to contact-store/all just get "quit"`)
99+ console.error(`Subscription to contact-store/all just get "quit"`);
90 }
100 }
91 })
101 })
92}
102}
ui/src/lib/apps.tsnew file+177
@@ -0,0 +1,177 @@
1+import _get from 'lodash/get';
2+import { compose } from 'lodash/fp';
3+import type { Apps } from 'types/store';
4+import store from 'stores/store';
5+import { sleep } from 'lib/utils';
6+import { api } from 'lib/api';
7+import { normalizeId } from 'lib/id';
8+
9+export function getProvidedApps(patp: string): Promise<any> {
10+ return api.scry<any>({ app: 'treaty', path: `/treaties/${patp}` });
11+}
12+
13+// Unused, use subscription
14+export function getKnownApps(): Promise<any> {
15+ return api.scry<any>({ app: 'treaty', path: `/allies` }).then(processAllies);
16+}
17+
18+export function getApp(ship: string, desk: string): Promise<any> {
19+ return getAppByPath(`${normalizeId(ship)}/${desk}`);
20+}
21+
22+// @param path - ship/desk e.g. ~mister-dister-dozzod-dozzod/webterm
23+export async function getAppByPath(path: string, retry: number = 2): Promise<any> {
24+ try {
25+ return await api.scry<any>({ app: 'treaty', path: `/treaty/${path}` });
26+ } catch (e) {
27+ if (retry > 0) {
28+ console.log(`Got error when fetching app: ${path}.\nTrying at most ${retry} more time(s)`);
29+ await sleep(750);
30+ return getAppByPath(path, retry - 1);
31+ } else {
32+ throw e;
33+ }
34+ }
35+}
36+
37+export function getInstalledApps(): Promise<any> {
38+ return api.scry<any>({ app: 'docket', path: `/charges` });
39+}
40+
41+export async function sendAllyUpdate(cmd: string, ship: string) {
42+ let result = await api.poke({
43+ app: 'treaty',
44+ mark: 'ally-update-0',
45+ json: { [cmd]: normalizeId(ship) },
46+ });
47+ return result;
48+}
49+
50+export function addAlly(patp: string) {
51+ return sendAllyUpdate('add', patp);
52+}
53+
54+export function delAlly(patp: string) {
55+ return sendAllyUpdate('del', patp);
56+}
57+
58+export async function addAllies(ships: string[]) {
59+ let results = await Promise.allSettled(ships.map((ship: string) => {
60+ return addAlly(normalizeId(ship));
61+ }));
62+ results = results.filter(r => r.status === 'fulfilled').map((r) => {
63+ return (r as PromiseFulfilledResult<any>).value;
64+ });
65+ return results;
66+}
67+
68+export async function delAllies(ships: string[], retry: number = 0) {
69+ let results = await Promise.allSettled(ships.map((ship: string) => {
70+ return delAlly(normalizeId(ship));
71+ }));
72+ results = results.filter(r => r.status === 'fulfilled').map((r) => {
73+ return (r as PromiseFulfilledResult<any>).value;
74+ });
75+ return results;
76+}
77+
78+export function processAllies(rawPals: any): Apps {
79+ let allies = rawPals.ini;
80+ let paths = Object.values(allies).flat() as string[];
81+ let set = new Set(paths);
82+ return {
83+ allies,
84+ set,
85+ };
86+}
87+
88+export type AllyUpdate = AllyUpdateIni | AllyUpdateAdd | AllyUpdateDel | AllyUpdateNew;
89+
90+export interface AllyUpdateIni {
91+ ini: {
92+ [ship: string]: string[];
93+ }
94+}
95+
96+export interface AllyUpdateNew {
97+ new: {
98+ ship: string;
99+ alliance: string[];
100+ }
101+}
102+
103+export interface AllyUpdateAdd {
104+ add: string;
105+}
106+
107+export interface AllyUpdateDel {
108+ del: string;
109+}
110+
111+export function handleAllyUpdateIni(json: AllyUpdate, apps: Apps): Apps {
112+ const data = _get(json, 'ini', false);
113+ if (data) {
114+ apps = processAllies(json)
115+ }
116+ return apps;
117+};
118+
119+export function handleAllyUpdateNew(json: AllyUpdate, apps: Apps): Apps {
120+ const data = _get(json, 'new', false);
121+ if (data) {
122+ apps = processAllies({
123+ ini: {
124+ ...apps.allies,
125+ [data.ship]: data.alliance,
126+ }
127+ });
128+ }
129+ return apps;
130+};
131+
132+export function handleAllyUpdateAdd(json: AllyUpdate, apps: Apps): Apps {
133+ const data = _get(json, 'add', false);
134+ if (data) {
135+ apps = processAllies({
136+ ini: {
137+ ...apps.allies,
138+ [data]: [],
139+ }
140+ });
141+ }
142+ return apps;
143+};
144+
145+export function handleAllyUpdateDel(json: AllyUpdate, apps: Apps): Apps {
146+ const data = _get(json, 'del', false);
147+ if (data && (data in apps.allies)) {
148+ delete apps.allies[data];
149+ apps = processAllies({
150+ ini: apps.allies,
151+ });
152+ }
153+ return apps;
154+};
155+
156+export function handleAllyUpdateEvent(updateApps) {
157+ return (allyUpdate: AllyUpdate) => {
158+ console.log(`received ally-update: ${Object.keys(allyUpdate)}`);
159+ const reducers = [handleAllyUpdateIni, handleAllyUpdateNew, handleAllyUpdateAdd, handleAllyUpdateDel];
160+ const reducer = compose(reducers.map(r => sta => r(allyUpdate, sta)));
161+ updateApps(reducer);
162+ };
163+}
164+
165+export function subscribeToApps(e: (data: any) => void): Promise<any> {
166+ return api.subscribe({
167+ app: 'treaty',
168+ path: '/allies',
169+ event: handleAllyUpdateEvent(e),
170+ err: () => {
171+ console.error(`Subscription to treaty/all just got "err"`);
172+ },
173+ quit: () => {
174+ console.error(`Subscription to treaty/all just got "quit"`);
175+ }
176+ })
177+}
ui/src/lib/colors.ts+3
@@ -6,9 +6,12 @@ export const GOLDS2 = '#caa93e';
6export const WHITE = '#FFF';
6export const WHITE = '#FFF';
7export const LGREY = '#BBB';
7export const LGREY = '#BBB';
8export const DGREY = '#777';
8export const DGREY = '#777';
9+export const DDGREY = '#555';
9export const BLUE = '#1792c7';
10export const BLUE = '#1792c7';
10export const GREEN = '#17c76a';
11export const GREEN = '#17c76a';
11export const YOU_ARE_HERE = '#802';
12export const YOU_ARE_HERE = '#802';
12export const YOU = '#b01';
13export const YOU = '#b01';
13export const YOUR_STAR = '#d71';
14export const YOUR_STAR = '#d71';
14export const YOUR_GAL = '#fb1';
15export const YOUR_GAL = '#fb1';
16+export const SUB_PICKED = LGREY;
17+export const NOT_PICKED = DDGREY;
ui/src/lib/graph.ts+248−185
@@ -6,123 +6,161 @@ const example = '[0[3216291:t;9219219;4608:s[156546;1612196;219621:~;321965149:4
6export interface ChartPoint {
6export interface ChartPoint {
7 patp: string,
7 patp: string,
8 dominion: ('l1' | 'l2' | 'spawn'),
8 dominion: ('l1' | 'l2' | 'spawn'),
9+ picked: boolean,
10+ pickedChildren?: boolean,
11+ pickedSponsees?: boolean,
9 sponsor?: string,
12 sponsor?: string,
10 parent?: string,
13 parent?: string,
11 children?: ChartPoint[],
14 children?: ChartPoint[],
12 sponsees?: ChartPoint[],
15 sponsees?: ChartPoint[],
13}
16}
14
17
15−function parseMetaString(metaString: string): ChartPoint {
18+interface ChartSubs {
16− let idString, subMetaString = '';
19+ children: ChartPoint[],
17− const colIndex = metaString.indexOf(':');
20+ sponsees: ChartPoint[],
18− if (colIndex === -1) {
21+}
19− idString = metaString;
22+
20− } else {
23+class ChartDataParser {
21− idString = metaString.slice(0, colIndex);
24+ filter: Set<string>;
22− subMetaString = metaString.slice(colIndex + 1);
25+
23− }
26+ constructor(filter: Set<string>) {
24− const meta: ChartPoint = {
27+ this.filter = filter;
25− patp: '~' + idString,
26− dominion: 'l1',
27 }
28 }
28− if (subMetaString) {
29+
29− const firstChar = subMetaString[0];
30+ parseMetaString(metaString: string): ChartPoint {
30− if (firstChar === 't') {
31+ let idString, subMetaString = '';
31− meta.dominion = 'l2';
32+ const colIndex = metaString.indexOf(':');
33+ if (colIndex === -1) {
34+ idString = metaString;
35+ } else {
36+ idString = metaString.slice(0, colIndex);
32− subMetaString = subMetaString.slice(1);
37+ subMetaString = metaString.slice(colIndex + 1);
33− } else if (firstChar === 's') {
38+ }
34− meta.dominion = 'spawn';
39+ const patp = '~' + idString;
35− subMetaString = subMetaString.slice(1);
40+ const meta: ChartPoint = {
41+ patp,
42+ picked: !this.filter ? true : (this.filter.has(patp)),
43+ dominion: 'l1',
36 }
44 }
37 if (subMetaString) {
45 if (subMetaString) {
46+ const firstChar = subMetaString[0];
47+ if (firstChar === 't') {
48+ meta.dominion = 'l2';
38− if (subMetaString[0] === '~') {
49+ subMetaString = subMetaString.slice(1);
50+ } else if (firstChar === 's') {
51+ meta.dominion = 'spawn';
52+ subMetaString = subMetaString.slice(1);
53+ }
54+ if (subMetaString) {
55+ if (subMetaString[0] === '~') {
39− meta.sponsor = null;
56+ meta.sponsor = null;
40− } else {
57+ } else {
41− let [sponsor, parent] = subMetaString.slice(1).split('!');
58+ let [sponsor, parent] = subMetaString.slice(1).split('!');
42− meta.sponsor = '~' + sponsor;
59+ meta.sponsor = '~' + sponsor;
43− meta.parent = '~' + parent;
60+ meta.parent = '~' + parent;
61+ }
44 }
62 }
45 }
63 }
64+ return meta;
46 }
65 }
47− return meta;
48−}
49
66
50−function extractMeta(data: string): [ChartPoint, string] {
67+ extractMeta(data: string): [ChartPoint, string] {
51− const index = data.search(/[[;]/);
68+ const index = data.search(/[[;]/);
52− if (index === -1) {
69+ if (index === -1) {
53− throw new Error(`No point meta present in ${data}`);
70+ throw new Error(`No point meta present in ${data}`);
71+ }
72+ const metaString = data.slice(0, index);
73+ data = data.slice(index);
74+ return [this.parseMetaString(metaString), data];
54 }
75 }
55− const metaString = data.slice(0, index);
56− data = data.slice(index);
57− return [parseMetaString(metaString), data];
58−}
59
76
60−function extractChildren(data: string, sup: string = undefined): [ChartPoint[], ChartPoint[], string] {
77+ extractChildren(data: string, sup: string = undefined): [ChartSubs, string] {
78+ if (data[0] === ';')
79+ return [{
80+ children: [],
81+ sponsees: [],
61− if (data[0] === ';') return [[], [], data.slice(1)];
82+ }, data.slice(1)];
62− if (data[0] !== '[') throw new Error('Invalid children in data: ' + data.slice(0, 20));
83+ if (data[0] !== '[') throw new Error('Invalid children in data: ' + data.slice(0, 20));
63
84
64− let endFound = false;
85+ let endFound = false;
65− let counter = 0;
86+ let counter = 0;
66− data = data.slice(1);
87+ data = data.slice(1);
67− const children = [];
88+ const children = [];
68− const sponsees = [];
89+ const sponsees = [];
69− while (!endFound && data.length && counter < 20000) {
90+ while (!endFound && data.length && counter < 20000) {
70− if (data[0] === ']') {
91+ if (data[0] === ']') {
71− endFound = true;
92+ endFound = true;
72− data = data.slice(1);
93+ data = data.slice(1);
73− } else {
94+ } else {
74− let sub;
95+ let sub;
75− [sub, data] = extractChartPoint(data);
96+ [sub, data] = this.extractChartPoint(data);
76− if (!sub.parent || sub.parent === sup)
97+ if (!sub.parent || sub.parent === sup)
77− children.push(sub);
98+ children.push(sub);
78− if (sub.sponsor === undefined || sub.sponsor === sup)
99+ if (sub.sponsor === undefined || sub.sponsor === sup)
79− sponsees.push(sub);
100+ sponsees.push(sub);
101+ }
102+ counter++;
80 }
103 }
81− counter++;
104+ return [{ children, sponsees }, data];
82 }
105 }
83− return [children, sponsees, data];
84−}
85
106
86−function extractChartPoint(data: string): [ChartPoint, string] {
107+ extractChartPoint(data: string): [ChartPoint, string] {
87− let point, children, sponsees;
108+ let point, subs;
88− [point, data] = extractMeta(data);
109+ [point, data] = this.extractMeta(data);
89− [children, sponsees, data] = extractChildren(data, point.patp);
110+ [subs, data] = this.extractChildren(data, point.patp);
90− point.children = children;
111+ point = {
91− point.sponsees = sponsees;
112+ ...point,
113+ ...subs,
114+ };
92− return [point, data];
115+ return [point, data];
93−}
116+ }
94
117
95−function linkChartData(points: ChartPoint[], store: any): ChartPoint[] {
118+ linkChartData(points: ChartPoint[], store: any): ChartPoint[] {
96− if (!points) return points;
119+ if (!points) return points;
97− return points.map(point => {
120+ return points.map(point => {
98− if (!store[point.patp]) return point;
121+ if (!store[point.patp]) return point;
99− if (!point.children?.length) {
122+ if (!point.children?.length) {
100− point.children = linkChartData(store[point.patp].children, store);
123+ point.children = this.linkChartData(store[point.patp].children, store);
124+ let picked = point.children.filter(child => child.picked || child.pickedChildren);
125+ point.pickedChildren = picked.length > 0;
101− }
126+ }
102− if (!point.sponsees?.length) {
127+ if (!point.sponsees?.length) {
103− point.sponsees = linkChartData(store[point.patp].sponsees, store);
128+ point.sponsees = this.linkChartData(store[point.patp].sponsees, store);
129+ let picked = point.sponsees.filter(child => child.picked || child.pickedSponsees);
130+ point.pickedSponsees = picked.length > 0;
104− }
131+ }
105− return point;
132+ return point;
106− });
133+ });
107−}
134+ }
108
135
109−function _parseChartData(str: string): ChartPoint[] {
136+ parse(str: string): ChartSubs {
110− if (!str) return [];
137+ if (!str) return { children: [], sponsees: [] };
111− let [baseChildren, _] = extractChildren(str);
138+ let [{ children: baseChildren }, _] = this.extractChildren(str, undefined);
112− let store = {};
139+ let store = {};
113− baseChildren.forEach(child => {
140+ baseChildren.forEach(child => {
114− store[child.patp] = {
141+ store[child.patp] = {
115− children: child.children,
142+ children: child.children,
116− sponsees: child.sponsees,
143+ sponsees: child.sponsees,
117− };
144+ };
118− });
145+ });
146+ let base: ChartPoint = {
147+ patp: '~base',
148+ dominion: 'l1',
149+ picked: true,
150+ }
119
151
120− return linkChartData(store['~base'].children, store);
152+ let points = this.linkChartData([base], store);
153+ return {
154+ children: points[0].children || [],
155+ sponsees: points[0].sponsees || [],
156+ }
157+ }
121}
158}
122
159
123−export function parseChartData(data: string): ChartPoint[] {
160+export function parseChartData(data: string, filter: Set<string>): ChartSubs {
124 let start = Date.now();
161 let start = Date.now();
125− let parsed = _parseChartData(data);
162+ let parser = new ChartDataParser(filter);
163+ let parsed = parser.parse(data);
126 // console.log(`finished parsing chart data to js object in ${Date.now() - start} ms`);
164 // console.log(`finished parsing chart data to js object in ${Date.now() - start} ms`);
127 return parsed;
165 return parsed;
128}
166}
@@ -188,16 +226,23 @@ export function arrangePointsInSpirals(
188 minDistanceBetween,
226 minDistanceBetween,
189 };
227 };
190}
228}
191−
229+type childKey = ('sponsees' | 'children');
192−export function populateGraph(g: Graph, chartData: ChartPoint[], bySponsor: boolean, callback?): Graph {
230+export function populateGraph(g: Graph, chartSubs: ChartSubs, subsKey: childKey, callback?): Graph {
231+ let pickedSubsKey = subsKey == 'children' ? 'pickedChildren' : 'pickedSponsees';
232+ function pickedColor(point: ChartPoint, normal: string) {
233+ return point.picked ? normal : (point[pickedSubsKey] ? colors.SUB_PICKED : colors.NOT_PICKED);
234+ }
235+ function pickedArmColor(point: ChartPoint, normal: string) {
236+ return point[pickedSubsKey] ? normal : colors.NOT_PICKED;
237+ }
238+ let points = chartSubs[subsKey];
193− if (!(chartData instanceof Array)) return;
239+ if (!(points instanceof Array)) return;
194 if (!g) {
240 if (!g) {
195 g = new Graph();
241 g = new Graph();
196 } else {
242 } else {
197 g.clear();
243 g.clear();
198 }
244 }
199
245
200− let subsKey = bySponsor ? 'sponsees' : 'children';
201 let sponsorEdges = [];
246 let sponsorEdges = [];
202
247
203 let offsetX = 0;
248 let offsetX = 0;
@@ -207,7 +252,7 @@ export function populateGraph(g: Graph, chartData: ChartPoint[], bySponsor: bool
207 let b = 9000000;
252 let b = 9000000;
208 let theta = 0;
253 let theta = 0;
209
254
210− chartData.forEach((galaxy, gi) => {
255+ points.forEach((galaxy, gi) => {
211 offsetX = 0 + (a + b * theta) * Math.cos(theta);
256 offsetX = 0 + (a + b * theta) * Math.cos(theta);
212 offsetY = 0 + (a + b * theta) * Math.sin(theta);
257 offsetY = 0 + (a + b * theta) * Math.sin(theta);
213
258
@@ -217,7 +262,7 @@ export function populateGraph(g: Graph, chartData: ChartPoint[], bySponsor: bool
217 g.addNode(gPatp, {
262 g.addNode(gPatp, {
218 label: gPatp,
263 label: gPatp,
219 size: 3,
264 size: 3,
220− color: itsYou ? colors.YOU : colors.WHITE,
265+ color: itsYou ? colors.YOU : pickedColor(galaxy, colors.WHITE),
221 x: offsetX,
266 x: offsetX,
222 y: offsetY,
267 y: offsetY,
223 zIndex: 10,
268 zIndex: 10,
@@ -242,6 +287,7 @@ export function populateGraph(g: Graph, chartData: ChartPoint[], bySponsor: bool
242 });
287 });
243 let lastStarArm = null;
288 let lastStarArm = null;
244 let lastStarPatp = null;
289 let lastStarPatp = null;
290+ let GSarmColor = pickedArmColor(galaxy, colors.LGREY)
245 stars.forEach((star, si) => {
291 stars.forEach((star, si) => {
246 const sPatp = star.patp;
292 const sPatp = star.patp;
247
293
@@ -252,101 +298,114 @@ export function populateGraph(g: Graph, chartData: ChartPoint[], bySponsor: bool
252 if (itsYou) {
298 if (itsYou) {
253 g.setNodeAttribute(gPatp, 'color', colors.YOUR_GAL);
299 g.setNodeAttribute(gPatp, 'color', colors.YOUR_GAL);
254 }
300 }
301+ try {
255− g.addNode(sPatp, {
302+ g.addNode(sPatp, {
256− label: sPatp,
303+ label: sPatp,
257− size: starSize,
304+ size: starSize,
258− color: itsYou ? colors.YOU : colors.GOLDT2,
305+ color: itsYou ? colors.YOU : pickedColor(star, colors.GOLDT2),
259− x: starX,
306+ x: starX,
260− y: starY,
307+ y: starY,
261− zIndex: 8,
308+ zIndex: 8,
262− });
263− if (lastStarArm == arm) {
264− g.addEdge(sPatp, lastStarPatp, {
265− color: colors.LGREY,
266− size: 0.1,
267− zIndex: 7,
268− });
269− } else {
270− g.addEdge(sPatp, gPatp, {
271− color: colors.LGREY,
272− size: 0.2,
273− zIndex: 9,
274 });
309 });
275− }
276− lastStarArm = arm;
310+ if (lastStarArm == arm) {
277− lastStarPatp = sPatp;
311+ g.addEdge(sPatp, lastStarPatp, {
278− if (star.sponsor) {
312+ color: GSarmColor,
279− sponsorEdges.push([sPatp, star.sponsor, {
280− color: colors.GREEN,
281− size: 0.2,
313+ size: 0.1,
282− zIndex: 11,
283− }]);
284− }
285−
286− const planets = star[subsKey] || [];
287− const numPlanets = planets.length;
288− // const maxPlanetDistance = starSize * 1.6 * 4000 * 64;
289− const maxPlanetDistance = minDistanceBetweenStars * 0.45;
290− const minPlanetDistance = maxPlanetDistance / 4;
291− const {
292− coords: planetCoords,
293− numArms: numPlanetArms,
294− minDistanceBetween: minDistanceBetweenPlanets,
295− } = arrangePointsInSpirals(numPlanets,
296− {
297− maxDistance: maxPlanetDistance,
298− minDistance: minPlanetDistance,
299− startX: starX,
300− startY: starY,
301− maxArms: 30,
302− makeSingleArmCircle: true,
303− });
304− let lastPlanetArm = null;
305− let lastPlanetPatp = null;
306− planets.forEach((planet, pi) => {
307− const pPatp = planet.patp;
308− const [planetX, planetY] = planetCoords[pi].coords;
309− const arm = planetCoords[pi].arm;
310− const planetSize = Math.sqrt(Math.min(minPlanetDistance, minDistanceBetweenPlanets)) / 1300;
311− const planetEdgeSize = planetSize / 3;
312− const itsYou = pPatp === '~' + window.ship;
313− if (itsYou) {
314− g.setNodeAttribute(sPatp, 'color', colors.YOUR_STAR);
315− g.setNodeAttribute(gPatp, 'color', colors.YOUR_GAL);
316− }
317− g.addNode(pPatp, {
318− label: pPatp,
319− size: planetSize,
320− color: itsYou ? colors.YOU : (planet.dominion == 'l2' ? colors.GREEN : colors.BLUE),
321− x: planetX,
322− y: planetY,
323− zIndex: 6,
324− });
325− if (lastPlanetArm == arm) {
326− g.addEdge(pPatp, lastPlanetPatp, {
327− color: colors.DGREY,
328− size: planetEdgeSize,
329− zIndex: 5,
314+ zIndex: 7,
330 });
315 });
331 } else {
316 } else {
332− if (numPlanetArms == 1) {
333− g.addEdge(pPatp, sPatp, {
317+ g.addEdge(sPatp, gPatp, {
334− color: colors.DGREY,
318+ color: GSarmColor,
335− size: planetEdgeSize,
319+ size: 0.2,
336− zIndex: 5,
320+ zIndex: 9,
337 });
321 });
338 }
322 }
339− }
323+ lastStarArm = arm;
324+ lastStarPatp = sPatp;
340− if (planet.sponsor) {
325+ if (star.sponsor) {
341− sponsorEdges.push([pPatp, planet.sponsor, {
326+ sponsorEdges.push([sPatp, star.sponsor, {
342 color: colors.GREEN,
327 color: colors.GREEN,
343− size: planetEdgeSize,
328+ size: 0.2,
344− zIndex: 11,
329+ zIndex: 11,
345 }]);
330 }]);
346 }
331 }
332+
333+ const planets = star[subsKey] || [];
334+ const numPlanets = planets.length;
335+ // const maxPlanetDistance = starSize * 1.6 * 4000 * 64;
336+ const maxPlanetDistance = minDistanceBetweenStars * 0.45;
337+ const minPlanetDistance = maxPlanetDistance / 4;
338+ const {
339+ coords: planetCoords,
340+ numArms: numPlanetArms,
341+ minDistanceBetween: minDistanceBetweenPlanets,
342+ } = arrangePointsInSpirals(numPlanets,
343+ {
344+ maxDistance: maxPlanetDistance,
345+ minDistance: minPlanetDistance,
346+ startX: starX,
347+ startY: starY,
348+ maxArms: 30,
349+ makeSingleArmCircle: true,
350+ });
347− lastPlanetArm = arm;
351+ let lastPlanetArm = null;
348− lastPlanetPatp = pPatp;
352+ let lastPlanetPatp = null;
349− });
353+ let SParmColor = pickedArmColor(star, colors.DGREY)
354+ planets.forEach((planet, pi) => {
355+ const pPatp = planet.patp;
356+ const [planetX, planetY] = planetCoords[pi].coords;
357+ const arm = planetCoords[pi].arm;
358+ const planetSize = Math.sqrt(Math.min(minPlanetDistance, minDistanceBetweenPlanets)) / 1300;
359+ const planetEdgeSize = planetSize / 3;
360+ const itsYou = pPatp === '~' + window.ship;
361+ if (itsYou) {
362+ g.setNodeAttribute(sPatp, 'color', colors.YOUR_STAR);
363+ g.setNodeAttribute(gPatp, 'color', colors.YOUR_GAL);
364+ }
365+ try {
366+ g.addNode(pPatp, {
367+ label: pPatp,
368+ size: planetSize,
369+ color: itsYou ? colors.YOU : pickedColor(planet, planet.dominion == 'l2' ? colors.GREEN : colors.BLUE),
370+ x: planetX,
371+ y: planetY,
372+ zIndex: 6,
373+ });
374+ if (lastPlanetArm == arm) {
375+ g.addEdge(pPatp, lastPlanetPatp, {
376+ color: SParmColor,
377+ size: planetEdgeSize,
378+ zIndex: 5,
379+ });
380+ } else {
381+ if (numPlanetArms == 1) {
382+ g.addEdge(pPatp, sPatp, {
383+ color: SParmColor,
384+ size: planetEdgeSize,
385+ zIndex: 5,
386+ });
387+ }
388+ }
389+ if (planet.sponsor) {
390+ sponsorEdges.push([pPatp, planet.sponsor, {
391+ color: colors.GREEN,
392+ size: planetEdgeSize,
393+ zIndex: 11,
394+ }]);
395+ }
396+ lastPlanetArm = arm;
397+ lastPlanetPatp = pPatp;
398+ } catch (e) {
399+ let msg = `Couldn't add ${pPatp} to the graph`;
400+ console.error(msg, e);
401+ alertMe(msg + '\n' + e.message);
402+ }
403+ });
404+ } catch (e) {
405+ let msg = `Couldn't add ${sPatp} to the graph`;
406+ console.error(msg, e);
407+ alertMe(msg + '\n' + e.message);
408+ }
350 });
409 });
351 });
410 });
352 sponsorEdges.forEach(([point, sponsor, attrs]) => {
411 sponsorEdges.forEach(([point, sponsor, attrs]) => {
@@ -357,3 +416,7 @@ export function populateGraph(g: Graph, chartData: ChartPoint[], bySponsor: bool
357 }
416 }
358 return g;
417 return g;
359}
418}
419+
420+function alertMe(msg: string) {
421+ alert('Encountered an error, please tell ~midlev-mindyr: ' + msg);
422+}
ui/src/lib/id.ts+19−1
@@ -1,6 +1,6 @@
1import BN from 'bn.js';
1import BN from 'bn.js';
2import type { Patp } from '@urbit/api'
2import type { Patp } from '@urbit/api'
3−import { hex2patp, patp2hex, patp as any2patp } from 'urbit-ob';
3+import { hex2patp, patp2hex, patp as any2patp, sein } from 'urbit-ob';
4import { isStringNaN, listStrings } from './utils';
4import { isStringNaN, listStrings } from './utils';
5
5
6// 340282366920938463463374607431768211455
6// 340282366920938463463374607431768211455
@@ -501,6 +501,21 @@ function analyzeSearch(search: string): SearchAnalysis {
501 return new SearchAnalysis(search);
501 return new SearchAnalysis(search);
502}
502}
503
503
504+function isMoonLength(patp: string): boolean {
505+ return patp.length > 14 && patp.length <= 28;
506+}
507+
508+function isCometLength(patp: string): boolean {
509+ return patp.length > 28;
510+}
511+
512+function filterIdsForChildMoons(ids: string[], parentId: string): string[] {
513+ return ids.filter((peerId) => {
514+ if (!isMoonLength(peerId)) return false;
515+ return sein(peerId) === normalizeId(parentId);
516+ })
517+}
518+
504export {
519export {
505 prefixes,
520 prefixes,
506 suffixes,
521 suffixes,
@@ -510,4 +525,7 @@ export {
510 cite,
525 cite,
511 convertSearchTextToRegex,
526 convertSearchTextToRegex,
512 analyzeSearch,
527 analyzeSearch,
528+ isMoonLength,
529+ isCometLength,
530+ filterIdsForChildMoons,
513};
531};
ui/src/lib/link.ts+3−3
@@ -1,16 +1,16 @@
1import { buildQuerystring } from './utils';
1import { buildQuerystring } from './utils';
2
2
3export function linkToShip(patp: string, queryParams = {}): string {
3export function linkToShip(patp: string, queryParams = {}): string {
4− return `/ship/${patp}` + buildQuerystring(queryParams);
4+ return `/ship/${encodeURIComponent(patp)}` + buildQuerystring(queryParams);
5}
5}
6
6
7export function linkToSearch(search: string, queryParams = {}): string {
7export function linkToSearch(search: string, queryParams = {}): string {
8− return `/search/${search}` + buildQuerystring(queryParams);
8+ return `/search/${encodeURIComponent(search)}` + buildQuerystring(queryParams);
9}
9}
10
10
11export function linkToChart(patp: string): string {
11export function linkToChart(patp: string): string {
12 if (patp) {
12 if (patp) {
13− return `/chart/${patp}`;
13+ return `/chart/${encodeURIComponent(patp)}`;
14 }
14 }
15 return `/chart`;
15 return `/chart`;
16}
16}
ui/src/lib/pals.tsnew file+91
@@ -0,0 +1,91 @@
1+import type { Patp } from '@urbit/api';
2+// import uniq from 'lodash/without';
3+// import without from 'lodash/without';
4+import type { Pals, PalList, PalNames, Pal, PalInfo, PalStatus } from 'types/store';
5+import store from 'stores/store';
6+import { api } from 'lib/api';
7+import { normalizeId, normalizeIdAndDesig } from 'lib/id';
8+
9+export function getPals() {
10+ return api.scry<any>({ app: 'pals', path: '/json' }).then(processPals);
11+}
12+
13+function palCommand(cmd: string, ship: string, tags: string[] = []) {
14+ return {
15+ [cmd]: {
16+ ship: normalizeIdAndDesig(ship),
17+ in: tags,
18+ },
19+ };
20+}
21+
22+export async function sendPalCommand(cmd: string, ship: string, tags: string[] = []) {
23+ let result = await api.poke({
24+ app: 'pals',
25+ mark: 'pals-command',
26+ json: palCommand(cmd, ship, tags),
27+ });
28+ store.refreshPals();
29+ return result;
30+}
31+
32+export function addPal(patp: string, tags: string[] = []) {
33+ return sendPalCommand('meet', patp, tags);
34+}
35+
36+export function removePal(patp: string) {
37+ return sendPalCommand('part', patp, []);
38+}
39+
40+export function tagPal(patp: string, tag: (string | string[])) {
41+ if (typeof tag == 'string') tag = [tag];
42+ return addPal(patp, tag);
43+}
44+
45+export function untagPal(patp: string, tag: (string | string[])) {
46+ if (typeof tag == 'string') tag = [tag];
47+ return sendPalCommand('part', patp, tag);
48+}
49+
50+export function processPals(rawPals: any): Pals {
51+ let incoming: PalNames = {};
52+ let outgoing: PalList = {};
53+ let mutuals: PalList = {};
54+
55+ Object.entries(rawPals.incoming).forEach(([patp, bool]) => {
56+ incoming[normalizeId(patp)] = bool as boolean;
57+ });
58+ (Object.entries(rawPals.outgoing) as [string, any][]).forEach(([patp, { lists, ack }]) => {
59+ patp = normalizeId(patp);
60+ outgoing[patp] = {
61+ tags: lists as string[],
62+ ack: ack as boolean,
63+ };
64+ if (incoming[patp]) {
65+ mutuals[patp] = outgoing[patp];
66+ }
67+ });
68+
69+ return {
70+ incoming,
71+ outgoing,
72+ mutuals,
73+ };
74+}
75+
76+export function getPalStatus(pals: Pals, patp: Patp): PalStatus {
77+ if (pals.mutuals[patp]) return 'mutual';
78+ if (pals.outgoing[patp]) return 'outgoing';
79+ if (pals.incoming[patp]) return 'incoming';
80+ return 'unconnected';
81+}
82+
83+export function getPal(pals: Pals, patp: Patp): PalInfo {
84+ return {
85+ patp,
86+ status: getPalStatus(pals, patp),
87+ tags: [],
88+ ack: null,
89+ ...pals.outgoing[patp],
90+ };
91+}
ui/src/lib/ship.tsnew file+114
@@ -0,0 +1,114 @@
1+import { clan, sein } from 'urbit-ob';
2+
3+export const spawnStatusOptions = {
4+ unspawned: {
5+ text: 'unspawned',
6+ tooltip: 'This point has not yet been spawned by its parent',
7+ },
8+ locked: {
9+ text: 'locked',
10+ tooltip: 'This point is currently locked in a Linear Release Contract',
11+ },
12+ spawned: {
13+ text: 'spawned',
14+ tooltip: `This point has been spawned`,
15+ },
16+ spawnedNoKeys: {
17+ text: 'spawned (no keys set)',
18+ tooltip: 'This point has been spawned, but no keys have been set',
19+ },
20+};
21+export const layerOptions = {
22+ l1: {
23+ text: 'on layer 1',
24+ tooltip: 'This point is secured as an NFT on the Ethereum blockchain',
25+ },
26+ l2: {
27+ text: 'on layer 2',
28+ tooltip: `This point is secured by Urbit's "Layer 2" solution`,
29+ },
30+ spawn: {
31+ text: 'spawns on layer 2',
32+ tooltip: 'This point lives on layer 1, but it spawns new points on layer 2',
33+ },
34+};
35+
36+export function generateParentChain(patp: string): any[] {
37+ let parents = [patp];
38+ let galaxyFound = false;
39+ let count = 10;
40+ while (!galaxyFound && count > 0) {
41+ count--;
42+ if (count == 0) {
43+ throw new Error("You've got an infinite loop in building the parent chain");
44+ }
45+ let firstItem = parents[0];
46+ if (clan(firstItem) == 'galaxy') {
47+ galaxyFound = true;
48+ } else {
49+ parents.unshift(sein(firstItem))
50+ }
51+ }
52+ parents.pop();
53+ return parents;
54+}
55+
56+export function processPointInfo(rawPointInfo: any): any {
57+ let pointInfo: any = {};
58+ let spawnStatus: any,
59+ spawnedUnlocked: boolean = false,
60+ layer: any,
61+ life: number,
62+ rift: number,
63+ spawnedCount: number,
64+ sponsorChain: any[];
65+ let keysSet = false;
66+ if (!rawPointInfo) {
67+ // request pending
68+ } else {
69+ // defaults in case no info found
70+ spawnStatus = spawnStatusOptions.unspawned;
71+ life = 0;
72+ rift = 0;
73+
74+ if (rawPointInfo.point) {
75+ const { point } = rawPointInfo;
76+ const {
77+ 'probable-dominion': dominion,
78+ npoint
79+ } = point;
80+ layer = layerOptions[dominion];
81+ spawnedCount = point['spa-count'];
82+ sponsorChain = point['sponsor-chain'].reverse();
83+
84+ if (npoint) {
85+ life = npoint.net.keys.life;
86+ rift = npoint.net.rift;
87+ if (life > 0) {
88+ keysSet = true;
89+ }
90+ pointInfo.proxies = npoint.own;
91+ if (pointInfo.proxies.owner.address === '0x86cd9cd0992f04231751e3761de45cecea5d1801') {
92+ spawnStatus = spawnStatusOptions.locked;
93+ } else if (keysSet) {
94+ spawnStatus = spawnStatusOptions.spawned;
95+ spawnedUnlocked = true;
96+ } else {
97+ spawnStatus = spawnStatusOptions.spawnedNoKeys;
98+ spawnedUnlocked = true;
99+ }
100+ }
101+ }
102+ }
103+ return {
104+ ...pointInfo,
105+ layer,
106+ spawnStatus,
107+ spawnedUnlocked,
108+ keysSet,
109+ life,
110+ rift,
111+ spawnedCount,
112+ sponsorChain,
113+ };
114+}
ui/src/lib/sigil.ts+10−1
@@ -11,6 +11,10 @@ export function sigilScalingFunction(size: number): number {
11 return 3.5 * Math.sqrt(51 / size);
11 return 3.5 * Math.sqrt(51 / size);
12}
12}
13
13
14+export function crowdedSigilScalingFunction(size: number): number {
15+ return 2.8 * Math.sqrt(51 / size);
16+}
17+
14export class SigilQuery {
18export class SigilQuery {
15 clan: Clan;
19 clan: Clan;
16 symbols: SymbolQuery[];
20 symbols: SymbolQuery[];
@@ -123,7 +127,7 @@ export class SigilQuery {
123 }
127 }
124 });
128 });
125 } catch (e) {
129 } catch (e) {
126− console.error("Couldn't load in sigil query text", e);
130+ console.log("Couldn't load in sigil query text:", e.message);
127 this.clan = 'planet';
131 this.clan = 'planet';
128 symbols = [];
132 symbols = [];
129 }
133 }
@@ -343,6 +347,11 @@ export class SymbolQuery {
343 return this;
347 return this;
344 }
348 }
345
349
350+ clearExceptPartType(partType: PartType) {
351+ this.components = this.components.filter(c => c[0] === partType[0]);
352+ return this;
353+ }
354+
346 clear() {
355 clear() {
347 this.components = [];
356 this.components = [];
348 return this;
357 return this;
ui/src/lib/utils.ts+14
@@ -45,3 +45,17 @@ export function toggleStoreKey(store, key) {
45export function capitalize(string: string): string {
45export function capitalize(string: string): string {
46 return string.charAt(0).toUpperCase() + string.slice(1);
46 return string.charAt(0).toUpperCase() + string.slice(1);
47}
47}
48+
49+export function uxToHex(ux: string) {
50+ let value = ux;
51+ if (ux.length > 2 && ux.substring(0, 2) === '0x') {
52+ value = ux.substring(2)
53+ }
54+
55+ value = value.replace('.', '').padStart(6, '0');
56+ return '#' + value;
57+}
58+
59+export function sleep(ms) {
60+ return new Promise(resolve => setTimeout(resolve, ms));
61+}
ui/src/routes/Home.svelte+41−25
@@ -7,11 +7,12 @@
7 import { linkToShip, linkToChart, linkToSearch } from 'lib/link';
7 import { linkToShip, linkToChart, linkToSearch } from 'lib/link';
8 import SearchBar from "@/search/SearchBar.svelte";
8 import SearchBar from "@/search/SearchBar.svelte";
9 import SearchResults from "@/search/SearchResults.svelte";
9 import SearchResults from "@/search/SearchResults.svelte";
10− import ShipView from '@/ShipView.svelte';
10+ import ShipView from '@/ship_view/ShipView.svelte';
11 import StarChart from '@/StarChart.svelte';
11 import StarChart from '@/StarChart.svelte';
12 import ValidationProblems from '@/ValidationProblems.svelte';
12 import ValidationProblems from '@/ValidationProblems.svelte';
13 import HomeScreenTiles from '@/HomeScreenTiles.svelte';
13 import HomeScreenTiles from '@/HomeScreenTiles.svelte';
14 export let params: any = {};
14 export let params: any = {};
15+
15 let analysis: SearchAnalysis = new SearchAnalysis();
16 let analysis: SearchAnalysis = new SearchAnalysis();
16 let sigilQuery: SigilQuery = new SigilQuery('planet');
17 let sigilQuery: SigilQuery = new SigilQuery('planet');
17 let navigating = false;
18 let navigating = false;
@@ -34,11 +35,42 @@
34 urlSearchMode = getUrlSearchMode(searchParams);
35 urlSearchMode = getUrlSearchMode(searchParams);
35 urlChangedSinceShipQuery = true;
36 urlChangedSinceShipQuery = true;
36 navigating = false;
37 navigating = false;
38+ // console.log('url changed');
37 }
39 }
38 }
40 }
39 $: {
41 $: {
40 urlChangedSinceShipQuery = (search || true) && searchMode && false;
42 urlChangedSinceShipQuery = (search || true) && searchMode && false;
43+ // console.log('search changed');
44+ }
45+ function autoNav() {
46+ // console.log('autonav');
47+ if ((search !== urlSearch || searchMode !== urlSearchMode)) {
48+ if (urlChangedSinceShipQuery) {
49+ search = urlSearch;
50+ searchMode = urlSearchMode;
51+ // console.log('url wins');
52+ } else {
53+ navigating = true;
54+ // console.log('search wins');
55+ // create a temp one because the real one doesn't always update in time!
56+ let tempAnalysis = new SearchAnalysis(search);
57+ if (params.mode === 'chart') {
58+ // console.log('to chart');
59+ navToChart(tempAnalysis.patpIsValid);
60+ } else {
61+ if (tempAnalysis.patpIsValid) {
62+ // console.log('to ship view');
63+ navToShipView(tempAnalysis.patp, true);
64+ } else {
65+ const fromShipMode = params.mode === 'ship';
66+ // console.log('to search');
67+ navToSearch(fromShipMode);
68+ }
69+ }
70+ }
71+ }
41 }
72 }
73+ $: urlSearch, urlSearchMode, search, searchMode, autoNav();
42 function nav(path, addToHistory = false) {
74 function nav(path, addToHistory = false) {
43 (addToHistory ? push : replace)(path);
75 (addToHistory ? push : replace)(path);
44 }
76 }
@@ -51,7 +83,7 @@
51 nav('/' + buildQuerystring({ mode: searchMode }), addToHistory);
83 nav('/' + buildQuerystring({ mode: searchMode }), addToHistory);
52 }
84 }
53 }
85 }
54− function navToShipView(addToHistory = false) {
86+ function navToShipView(patp, addToHistory = false) {
55 params.mode = 'ship';
87 params.mode = 'ship';
56 params.arg = patp;
88 params.arg = patp;
57 if (patp === search) {
89 if (patp === search) {
@@ -87,27 +119,6 @@
87 navHome(addToHistory);
119 navHome(addToHistory);
88 }
120 }
89 }
121 }
90− function autoNav() {
91− if ((search !== urlSearch || searchMode !== urlSearchMode)) {
92− if (urlChangedSinceShipQuery) {
93− search = urlSearch;
94− searchMode = urlSearchMode;
95− } else {
96− navigating = true;
97− if (params.mode === 'chart') {
98− navToChart(analysis.patpIsValid);
99− } else {
100− if (patp) {
101− navToShipView(true);
102− } else {
103− const fromShipMode = params.mode === 'ship';
104− navToSearch(fromShipMode);
105− }
106− }
107− }
108− }
109− }
110− $: urlSearch, urlSearchMode, search, searchMode, autoNav();
111 $: {
122 $: {
112 let properMode = params.mode;
123 let properMode = params.mode;
113 if (properMode === 'chart') {
124 if (properMode === 'chart') {
@@ -149,8 +160,13 @@
149</script>
160</script>
150
161
151<div class="p-4 space-y-4 min-h-full flex flex-col items-center 2xs:p-8 2xs:space-y-8">
162<div class="p-4 space-y-4 min-h-full flex flex-col items-center 2xs:p-8 2xs:space-y-8">
152− <SearchBar bind:analysis bind:sigilQuery bind:search bind:searchMode bind:patp {placeholderText} />
163+ <SearchBar bind:analysis bind:sigilQuery bind:search bind:searchMode bind:patp {placeholderText}
164+ allowSigilSearch={params.mode !== 'chart'}
165+ />
153 <!-- <div class="bg-white">
166 <!-- <div class="bg-white">
167+ <p>
168+ Mode: {params.mode}
169+ </p>
154 <p>
170 <p>
155 Search: {search}
171 Search: {search}
156 </p>
172 </p>
@@ -164,7 +180,7 @@
164 Patp: {patp}
180 Patp: {patp}
165 </p>
181 </p>
166 </div> -->
182 </div> -->
167− {#if params.mode == "chart"}
183+ {#if params.mode == 'chart'}
168 <StarChart patp={patp || null} />
184 <StarChart patp={patp || null} />
169 {:else if patp}
185 {:else if patp}
170 <ShipView {patp} />
186 <ShipView {patp} />
ui/src/routes/SigilPage.svelte+4−4
@@ -1,7 +1,7 @@
1<script lang="ts">
1<script lang="ts">
2 import { prefixes, suffixes } from 'lib/id';
2 import { prefixes, suffixes } from 'lib/id';
3− import Sigil from "@/Sigil.svelte";
3+ import Sigil from "@/common/Sigil.svelte";
4− import Legil from "@/Legil.svelte";
4+ // import Legil from "@/common/Legil.svelte";
5
5
6 let index: number = undefined;
6 let index: number = undefined;
7 let altBgColor = 'red';
7 let altBgColor = 'red';
@@ -38,7 +38,7 @@
38 <div class="flex">
38 <div class="flex">
39 <Sigil patp={phoneme} size={64} {altBgColor} />
39 <Sigil patp={phoneme} size={64} {altBgColor} />
40 <Sigil patp={phoneme} size={64} {altBgColor} useNew />
40 <Sigil patp={phoneme} size={64} {altBgColor} useNew />
41− <Legil patp={phoneme} size={64} {altBgColor} />
41+ <!-- <Legil patp={phoneme} size={64} {altBgColor} /> -->
42 </div>
42 </div>
43 </div>
43 </div>
44 {/each}
44 {/each}
@@ -55,7 +55,7 @@
55 <div class="flex w-full">
55 <div class="flex w-full">
56 <Sigil patp={phonemes[index]} size={400} {altBgColor} />
56 <Sigil patp={phonemes[index]} size={400} {altBgColor} />
57 <Sigil patp={phonemes[index]} size={400} {altBgColor} useNew />
57 <Sigil patp={phonemes[index]} size={400} {altBgColor} useNew />
58− <Legil patp={phonemes[index]} size={400} {altBgColor} />
58+ <!-- <Legil patp={phonemes[index]} size={400} {altBgColor} /> -->
59 </div>
59 </div>
60 {/if}
60 {/if}
61</div>
61</div>
ui/src/stores/keys.tsnew file+68
@@ -0,0 +1,68 @@
1+import { writable, get } from 'svelte/store';
2+
3+const docs = {};
4+
5+class MapWithDefault<K, V> extends Map<K, V> {
6+ default: Function;
7+
8+ get(key: K) {
9+ if (!this.has(key)) {
10+ this.set(key, this.default());
11+ }
12+ return super.get(key);
13+ }
14+
15+ constructor(defaultFunction: () => V) {
16+ super();
17+ this.default = defaultFunction;
18+ }
19+}
20+
21+type Bindings = MapWithDefault<string, MapWithDefault<string, Function>>;
22+interface KeysStore {
23+ keydown: Bindings;
24+ keyup: Bindings;
25+ globalkeydown: Bindings;
26+ globalkeyup: Bindings;
27+};
28+function newEmptyBindings(): Bindings {
29+ return new MapWithDefault(() => new MapWithDefault(() => Function));
30+}
31+const initStore: KeysStore = {
32+ keydown: newEmptyBindings(),
33+ keyup: newEmptyBindings(),
34+ globalkeydown: newEmptyBindings(),
35+ globalkeyup: newEmptyBindings(),
36+};
37+
38+const store = writable(initStore);
39+const { subscribe, update, set } = store;
40+
41+function runCallbacks(bindings: Bindings, event: KeyboardEvent) {
42+ let els = [];
43+ if (event.ctrlKey) els.push('ctrl');
44+ if (event.metaKey) els.push('cmd');
45+ if (event.altKey) els.push('alt');
46+ if (event.shiftKey) els.push('shift');
47+ els.push(event.key == ' ' ? 'Space' : event.key.toLowerCase());
48+ let combo = els.join('+');
49+ // console.log(event);
50+ let callbacks = bindings.get(combo);
51+ if (callbacks) {
52+ callbacks.forEach((callback) => {
53+ try {
54+ if (callback)
55+ callback(event);
56+ } catch (e) {
57+ console.error(e);
58+ }
59+ });
60+ }
61+}
62+
63+export default {
64+ subscribe,
65+ update,
66+ set,
67+ runCallbacks,
68+};
ui/src/stores/searchStores.ts+5−7
@@ -3,8 +3,9 @@ import { writable, derived } from 'svelte/store';
3import type { Contact, Rolodex } from '@urbit/api';
3import type { Contact, Rolodex } from '@urbit/api';
4import store from './store';
4import store from './store';
5import type { StoreState, SearchedContactsState } from 'types/store';
5import type { StoreState, SearchedContactsState } from 'types/store';
6+import { createEmptyContact } from 'types/store';
6import { SearchSettings } from 'types/store';
7import { SearchSettings } from 'types/store';
7−import { splitIdIntoSyls } from 'lib/id';
8+import { splitIdIntoSyls, isMoonLength, isCometLength } from 'lib/id';
8import { filterObject } from 'lib/utils';
9import { filterObject } from 'lib/utils';
9
10
10const searchSettings = writable(new SearchSettings());
11const searchSettings = writable(new SearchSettings());
@@ -27,10 +28,10 @@ function deriveSearchedContacts([$store, $searchSettings]): SearchedContactsStat
27 idMatches = idMatchesSyls;
28 idMatches = idMatchesSyls;
28 }
29 }
29 function isIdAllowed(id) {
30 function isIdAllowed(id) {
30− if (!$searchSettings.includeMoons && id.length > 14 && id.length <= 28) {
31+ if (!$searchSettings.includeMoons && isMoonLength(id)) {
31 return false;
32 return false;
32 }
33 }
33− if (!$searchSettings.includeComets && id.length > 28) {
34+ if (!$searchSettings.includeComets && isCometLength(id)) {
34 return false;
35 return false;
35 }
36 }
36 return true;
37 return true;
@@ -52,10 +53,7 @@ function deriveSearchedContacts([$store, $searchSettings]): SearchedContactsStat
52 const filteredContacts = filterObject($store.contacts, idMatches) as Rolodex;
53 const filteredContacts = filterObject($store.contacts, idMatches) as Rolodex;
53 $store.peers.forEach((id) => {
54 $store.peers.forEach((id) => {
54 if (idMatches(id) && !filteredContacts[id]) {
55 if (idMatches(id) && !filteredContacts[id]) {
55− filteredContacts[id] = {
56+ filteredContacts[id] = createEmptyContact();
56− nickname: null, bio: null, status: null, color: null, avatar: null,
57− cover: null, groups: [], 'last-updated': 0,
58− } as Contact
59 }
57 }
60 })
58 })
61 return filteredContacts;
59 return filteredContacts;
ui/src/stores/store.ts+35−4
@@ -2,13 +2,25 @@ import { writable } from 'svelte/store';
2
2
3import type { Rolodex } from '@urbit/api';
3import type { Rolodex } from '@urbit/api';
4import { subscribeToContacts, getPeers } from 'lib/api';
4import { subscribeToContacts, getPeers } from 'lib/api';
5−import type { StoreState } from 'types/store';
5+import type { StoreState, Apps } from 'types/store';
6−import { normalizeId } from 'lib/id';
6+import { normalizeId } from 'lib/id';
7−import { setStoreKey } from 'lib/utils';
7+import { setStoreKey } from 'lib/utils';
8+import { getPals } from 'lib/pals';
9+import { subscribeToApps } from 'lib/apps';
8
10
9const initStore: StoreState = {
11const initStore: StoreState = {
10 contacts: {},
12 contacts: {},
11 peers: [],
13 peers: [],
14+ pals: {
15+ incoming: {},
16+ outgoing: {},
17+ mutuals: {},
18+ },
19+ palsInstalled: false,
20+ apps: {
21+ allies: {},
22+ set: new Set(),
23+ },
12 patpQuery: null,
24 patpQuery: null,
13 sigilQuery: null,
25 sigilQuery: null,
14 searchMode: 'patp',
26 searchMode: 'patp',
@@ -48,4 +60,23 @@ getPeers().then(({ points }) => {
48 setStoreKey(store, 'peers', points.map(normalizeId));
60 setStoreKey(store, 'peers', points.map(normalizeId));
49});
61});
50
62
63+function refreshPals() {
64+ return getPals().then((pals) => {
65+ setStoreKey(store, 'pals', pals);
66+ setStoreKey(store, 'palsInstalled', true);
67+ });
68+}
69+refreshPals();
70+
71+function updateApps(callback: (apps) => Apps): void {
72+ update($store => {
73+ return {
74+ ...$store,
75+ apps: callback($store.apps),
76+ };
77+ });
78+}
79+
80+subscribeToApps(updateApps);
81+
51−export default { subscribe, set, searchPatp, searchSigil, reset };
82+export default { subscribe, set, searchPatp, searchSigil, refreshPals, reset };
ui/src/styles/tailwind-output.css+309−94
@@ -577,6 +577,9 @@ Ensure the default browser behavior of the `hidden` attribute.
577.m-2 {
577.m-2 {
578 margin: 0.5rem;
578 margin: 0.5rem;
579}
579}
580+.-m-1 {
581+ margin: -0.25rem;
582+}
580.m-8 {
583.m-8 {
581 margin: 2rem;
584 margin: 2rem;
582}
585}
@@ -593,10 +596,6 @@ Ensure the default browser behavior of the `hidden` attribute.
593 margin-left: auto;
596 margin-left: auto;
594 margin-right: auto;
597 margin-right: auto;
595}
598}
596−.my-1\.5 {
597− margin-top: 0.375rem;
598− margin-bottom: 0.375rem;
599−}
600.my-1 {
599.my-1 {
601 margin-top: 0.25rem;
600 margin-top: 0.25rem;
602 margin-bottom: 0.25rem;
601 margin-bottom: 0.25rem;
@@ -633,20 +632,35 @@ Ensure the default browser behavior of the `hidden` attribute.
633 margin-top: auto;
632 margin-top: auto;
634 margin-bottom: auto;
633 margin-bottom: auto;
635}
634}
635+.-mx-1 {
636+ margin-left: -0.25rem;
637+ margin-right: -0.25rem;
638+}
639+.mx-1 {
640+ margin-left: 0.25rem;
641+ margin-right: 0.25rem;
642+}
643+.my-1\.5 {
644+ margin-top: 0.375rem;
645+ margin-bottom: 0.375rem;
646+}
636.mt-2 {
647.mt-2 {
637 margin-top: 0.5rem;
648 margin-top: 0.5rem;
638}
649}
639.ml-\[22px\] {
650.ml-\[22px\] {
640 margin-left: 22px;
651 margin-left: 22px;
641}
652}
642−.ml-1 {
653+.mr-1 {
643− margin-left: 0.25rem;
654+ margin-right: 0.25rem;
655+}
656+.ml-8 {
657+ margin-left: 2rem;
644}
658}
645.mr-4 {
659.mr-4 {
646 margin-right: 1rem;
660 margin-right: 1rem;
647}
661}
648−.ml-4 {
662+.ml-1 {
649− margin-left: 1rem;
663+ margin-left: 0.25rem;
650}
664}
651.mt-4 {
665.mt-4 {
652 margin-top: 1rem;
666 margin-top: 1rem;
@@ -654,8 +668,8 @@ Ensure the default browser behavior of the `hidden` attribute.
654.mt-5 {
668.mt-5 {
655 margin-top: 1.25rem;
669 margin-top: 1.25rem;
656}
670}
657−.ml-8 {
671+.ml-4 {
658− margin-left: 2rem;
672+ margin-left: 1rem;
659}
673}
660.-mt-2 {
674.-mt-2 {
661 margin-top: -0.5rem;
675 margin-top: -0.5rem;
@@ -699,8 +713,23 @@ Ensure the default browser behavior of the `hidden` attribute.
699.mb-1 {
713.mb-1 {
700 margin-bottom: 0.25rem;
714 margin-bottom: 0.25rem;
701}
715}
702−.mr-1 {
716+.mr-2 {
717+ margin-right: 0.5rem;
718+}
719+.mr-0 {
703− margin-right: 0.25rem;
720+ margin-right: 0px;
721+}
722+.-mb-24 {
723+ margin-bottom: -6rem;
724+}
725+.-mb-20 {
726+ margin-bottom: -5rem;
727+}
728+.-mb-16 {
729+ margin-bottom: -4rem;
730+}
731+.mb-2 {
732+ margin-bottom: 0.5rem;
704}
733}
705.-mt-0\.5 {
734.-mt-0\.5 {
706 margin-top: -0.125rem;
735 margin-top: -0.125rem;
@@ -735,18 +764,23 @@ Ensure the default browser behavior of the `hidden` attribute.
735.h-full {
764.h-full {
736 height: 100%;
765 height: 100%;
737}
766}
738−.h-5 {
739− height: 1.25rem;
740−}
741−.h-12 {
742− height: 3rem;
743−}
744.h-3 {
767.h-3 {
745 height: 0.75rem;
768 height: 0.75rem;
746}
769}
747.h-\[100vh\] {
770.h-\[100vh\] {
748 height: 100vh;
771 height: 100vh;
749}
772}
773+.h-12 {
774+ height: 3rem;
775+}
776+.h-5 {
777+ height: 1.25rem;
778+}
779+.h-fit {
780+ height: -webkit-fit-content;
781+ height: -moz-fit-content;
782+ height: fit-content;
783+}
750.h-10 {
784.h-10 {
751 height: 2.5rem;
785 height: 2.5rem;
752}
786}
@@ -789,6 +823,20 @@ Ensure the default browser behavior of the `hidden` attribute.
789.h-auto {
823.h-auto {
790 height: auto;
824 height: auto;
791}
825}
826+.h-0 {
827+ height: 0px;
828+}
829+.h-min {
830+ height: -webkit-min-content;
831+ height: -moz-min-content;
832+ height: min-content;
833+}
834+.h-40 {
835+ height: 10rem;
836+}
837+.h-60 {
838+ height: 15rem;
839+}
792.min-h-\[20px\] {
840.min-h-\[20px\] {
793 min-height: 20px;
841 min-height: 20px;
794}
842}
@@ -804,11 +852,10 @@ Ensure the default browser behavior of the `hidden` attribute.
804.w-full {
852.w-full {
805 width: 100%;
853 width: 100%;
806}
854}
807−.w-5 {
855+.w-min {
808− width: 1.25rem;
856+ width: -webkit-min-content;
809−}
857+ width: -moz-min-content;
810−.w-12 {
811− width: 3rem;
858+ width: min-content;
812}
859}
813.w-3 {
860.w-3 {
814 width: 0.75rem;
861 width: 0.75rem;
@@ -819,6 +866,12 @@ Ensure the default browser behavior of the `hidden` attribute.
819.w-\[100vw\] {
866.w-\[100vw\] {
820 width: 100vw;
867 width: 100vw;
821}
868}
869+.w-12 {
870+ width: 3rem;
871+}
872+.w-5 {
873+ width: 1.25rem;
874+}
822.w-10 {
875.w-10 {
823 width: 2.5rem;
876 width: 2.5rem;
824}
877}
@@ -858,21 +911,31 @@ Ensure the default browser behavior of the `hidden` attribute.
858.w-40 {
911.w-40 {
859 width: 10rem;
912 width: 10rem;
860}
913}
914+.w-fit {
915+ width: -webkit-fit-content;
916+ width: -moz-fit-content;
917+ width: fit-content;
918+}
919+.w-max {
920+ width: -webkit-max-content;
921+ width: -moz-max-content;
922+ width: max-content;
923+}
861.w-\[48px\] {
924.w-\[48px\] {
862 width: 48px;
925 width: 48px;
863}
926}
864.min-w-0 {
927.min-w-0 {
865 min-width: 0px;
928 min-width: 0px;
866}
929}
930+.min-w-\[128px\] {
931+ min-width: 128px;
932+}
867.max-w-md {
933.max-w-md {
868 max-width: 28rem;
934 max-width: 28rem;
869}
935}
870.max-w-xs {
936.max-w-xs {
871 max-width: 20rem;
937 max-width: 20rem;
872}
938}
873−.max-w-\[256px\] {
874− max-width: 256px;
875−}
876.max-w-sm {
939.max-w-sm {
877 max-width: 24rem;
940 max-width: 24rem;
878}
941}
@@ -882,6 +945,18 @@ Ensure the default browser behavior of the `hidden` attribute.
882.max-w-2xl {
945.max-w-2xl {
883 max-width: 42rem;
946 max-width: 42rem;
884}
947}
948+.max-w-xl {
949+ max-width: 36rem;
950+}
951+.max-w-lg {
952+ max-width: 32rem;
953+}
954+.max-w-\[128px\] {
955+ max-width: 128px;
956+}
957+.max-w-\[256px\] {
958+ max-width: 256px;
959+}
885.max-w-\[10rem\] {
960.max-w-\[10rem\] {
886 max-width: 10rem;
961 max-width: 10rem;
887}
962}
@@ -900,6 +975,9 @@ Ensure the default browser behavior of the `hidden` attribute.
900.shrink {
975.shrink {
901 flex-shrink: 1;
976 flex-shrink: 1;
902}
977}
978+.shrink-0 {
979+ flex-shrink: 0;
980+}
903.flex-grow {
981.flex-grow {
904 flex-grow: 1;
982 flex-grow: 1;
905}
983}
@@ -954,6 +1032,10 @@ Ensure the default browser behavior of the `hidden` attribute.
954 --tw-translate-y: -25%;
1032 --tw-translate-y: -25%;
955 transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1033 transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
956}
1034}
1035+.rotate-90 {
1036+ --tw-rotate: 90deg;
1037+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1038+}
957.rotate-45 {
1039.rotate-45 {
958 --tw-rotate: 45deg;
1040 --tw-rotate: 45deg;
959 transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1041 transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
@@ -1004,12 +1086,19 @@ Ensure the default browser behavior of the `hidden` attribute.
1004.auto-cols-fr {
1086.auto-cols-fr {
1005 grid-auto-columns: minmax(0, 1fr);
1087 grid-auto-columns: minmax(0, 1fr);
1006}
1088}
1089+.auto-cols-max {
1090+ grid-auto-columns: -webkit-max-content;
1091+ grid-auto-columns: max-content;
1092+}
1007.grid-flow-row {
1093.grid-flow-row {
1008 grid-auto-flow: row;
1094 grid-auto-flow: row;
1009}
1095}
1010.grid-flow-col {
1096.grid-flow-col {
1011 grid-auto-flow: column;
1097 grid-auto-flow: column;
1012}
1098}
1099+.grid-flow-row-dense {
1100+ grid-auto-flow: row dense;
1101+}
1013.grid-flow-col-dense {
1102.grid-flow-col-dense {
1014 grid-auto-flow: column dense;
1103 grid-auto-flow: column dense;
1015}
1104}
@@ -1017,6 +1106,13 @@ Ensure the default browser behavior of the `hidden` attribute.
1017 grid-auto-rows: -webkit-min-content;
1106 grid-auto-rows: -webkit-min-content;
1018 grid-auto-rows: min-content;
1107 grid-auto-rows: min-content;
1019}
1108}
1109+.auto-rows-auto {
1110+ grid-auto-rows: auto;
1111+}
1112+.auto-rows-max {
1113+ grid-auto-rows: -webkit-max-content;
1114+ grid-auto-rows: max-content;
1115+}
1020.grid-cols-3 {
1116.grid-cols-3 {
1021 grid-template-columns: repeat(3, minmax(0, 1fr));
1117 grid-template-columns: repeat(3, minmax(0, 1fr));
1022}
1118}
@@ -1038,6 +1134,9 @@ Ensure the default browser behavior of the `hidden` attribute.
1038.grid-rows-none {
1134.grid-rows-none {
1039 grid-template-rows: none;
1135 grid-template-rows: none;
1040}
1136}
1137+.grid-rows-3 {
1138+ grid-template-rows: repeat(3, minmax(0, 1fr));
1139+}
1041.flex-col {
1140.flex-col {
1042 flex-direction: column;
1141 flex-direction: column;
1043}
1142}
@@ -1065,24 +1164,40 @@ Ensure the default browser behavior of the `hidden` attribute.
1065.justify-center {
1164.justify-center {
1066 justify-content: center;
1165 justify-content: center;
1067}
1166}
1167+.justify-between {
1168+ justify-content: space-between;
1169+}
1170+.justify-evenly {
1171+ justify-content: space-evenly;
1172+}
1068.justify-items-center {
1173.justify-items-center {
1069 justify-items: center;
1174 justify-items: center;
1070}
1175}
1176+.justify-items-stretch {
1177+ justify-items: stretch;
1178+}
1071.gap-5 {
1179.gap-5 {
1072 gap: 1.25rem;
1180 gap: 1.25rem;
1073}
1181}
1074−.gap-1\.5 {
1075− gap: 0.375rem;
1076−}
1077−.gap-1 {
1078− gap: 0.25rem;
1079−}
1080.gap-4 {
1182.gap-4 {
1081 gap: 1rem;
1183 gap: 1rem;
1082}
1184}
1083.gap-2 {
1185.gap-2 {
1084 gap: 0.5rem;
1186 gap: 0.5rem;
1085}
1187}
1188+.gap-1 {
1189+ gap: 0.25rem;
1190+}
1191+.gap-3 {
1192+ gap: 0.75rem;
1193+}
1194+.gap-1\.5 {
1195+ gap: 0.375rem;
1196+}
1197+.gap-x-4 {
1198+ -moz-column-gap: 1rem;
1199+ column-gap: 1rem;
1200+}
1086.gap-x-1 {
1201.gap-x-1 {
1087 -moz-column-gap: 0.25rem;
1202 -moz-column-gap: 0.25rem;
1088 column-gap: 0.25rem;
1203 column-gap: 0.25rem;
@@ -1095,10 +1210,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1095 -moz-column-gap: 0.75rem;
1210 -moz-column-gap: 0.75rem;
1096 column-gap: 0.75rem;
1211 column-gap: 0.75rem;
1097}
1212}
1098−.gap-x-4 {
1099− -moz-column-gap: 1rem;
1100− column-gap: 1rem;
1101−}
1102.space-y-4 > :not([hidden]) ~ :not([hidden]) {
1213.space-y-4 > :not([hidden]) ~ :not([hidden]) {
1103 --tw-space-y-reverse: 0;
1214 --tw-space-y-reverse: 0;
1104 margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
1215 margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
@@ -1138,6 +1249,38 @@ Ensure the default browser behavior of the `hidden` attribute.
1138 --tw-divide-opacity: 1;
1249 --tw-divide-opacity: 1;
1139 border-color: rgb(252 211 77 / var(--tw-divide-opacity));
1250 border-color: rgb(252 211 77 / var(--tw-divide-opacity));
1140}
1251}
1252+.divide-black > :not([hidden]) ~ :not([hidden]) {
1253+ --tw-divide-opacity: 1;
1254+ border-color: rgb(0 0 0 / var(--tw-divide-opacity));
1255+}
1256+.divide-gray-800 > :not([hidden]) ~ :not([hidden]) {
1257+ --tw-divide-opacity: 1;
1258+ border-color: rgb(31 41 55 / var(--tw-divide-opacity));
1259+}
1260+.divide-gray-600 > :not([hidden]) ~ :not([hidden]) {
1261+ --tw-divide-opacity: 1;
1262+ border-color: rgb(75 85 99 / var(--tw-divide-opacity));
1263+}
1264+.divide-red-600 > :not([hidden]) ~ :not([hidden]) {
1265+ --tw-divide-opacity: 1;
1266+ border-color: rgb(220 38 38 / var(--tw-divide-opacity));
1267+}
1268+.divide-gray-200 > :not([hidden]) ~ :not([hidden]) {
1269+ --tw-divide-opacity: 1;
1270+ border-color: rgb(229 231 235 / var(--tw-divide-opacity));
1271+}
1272+.divide-gray-400 > :not([hidden]) ~ :not([hidden]) {
1273+ --tw-divide-opacity: 1;
1274+ border-color: rgb(156 163 175 / var(--tw-divide-opacity));
1275+}
1276+.divide-gray-300 > :not([hidden]) ~ :not([hidden]) {
1277+ --tw-divide-opacity: 1;
1278+ border-color: rgb(209 213 219 / var(--tw-divide-opacity));
1279+}
1280+.divide-gray-500 > :not([hidden]) ~ :not([hidden]) {
1281+ --tw-divide-opacity: 1;
1282+ border-color: rgb(107 114 128 / var(--tw-divide-opacity));
1283+}
1141.place-self-center {
1284.place-self-center {
1142 place-self: center;
1285 place-self: center;
1143}
1286}
@@ -1165,15 +1308,19 @@ Ensure the default browser behavior of the `hidden` attribute.
1165.overflow-y-auto {
1308.overflow-y-auto {
1166 overflow-y: auto;
1309 overflow-y: auto;
1167}
1310}
1311+.whitespace-pre-line {
1312+ white-space: pre-line;
1313+}
1314+.break-normal {
1315+ overflow-wrap: normal;
1316+ word-break: normal;
1317+}
1168.break-words {
1318.break-words {
1169 overflow-wrap: break-word;
1319 overflow-wrap: break-word;
1170}
1320}
1171.break-all {
1321.break-all {
1172 word-break: break-all;
1322 word-break: break-all;
1173}
1323}
1174−.rounded {
1175− border-radius: 0.25rem;
1176−}
1177.rounded-md {
1324.rounded-md {
1178 border-radius: 0.375rem;
1325 border-radius: 0.375rem;
1179}
1326}
@@ -1183,6 +1330,9 @@ Ensure the default browser behavior of the `hidden` attribute.
1183.rounded-full {
1330.rounded-full {
1184 border-radius: 9999px;
1331 border-radius: 9999px;
1185}
1332}
1333+.rounded {
1334+ border-radius: 0.25rem;
1335+}
1186.rounded-xl {
1336.rounded-xl {
1187 border-radius: 0.75rem;
1337 border-radius: 0.75rem;
1188}
1338}
@@ -1203,12 +1353,22 @@ Ensure the default browser behavior of the `hidden` attribute.
1203 border-top-left-radius: 1rem;
1353 border-top-left-radius: 1rem;
1204 border-top-right-radius: 1rem;
1354 border-top-right-radius: 1rem;
1205}
1355}
1356+.rounded-t-md {
1357+ border-top-left-radius: 0.375rem;
1358+ border-top-right-radius: 0.375rem;
1359+}
1206.rounded-br-2xl {
1360.rounded-br-2xl {
1207 border-bottom-right-radius: 1rem;
1361 border-bottom-right-radius: 1rem;
1208}
1362}
1209.rounded-bl-none {
1363.rounded-bl-none {
1210 border-bottom-left-radius: 0px;
1364 border-bottom-left-radius: 0px;
1211}
1365}
1366+.rounded-tl-lg {
1367+ border-top-left-radius: 0.5rem;
1368+}
1369+.rounded-br-lg {
1370+ border-bottom-right-radius: 0.5rem;
1371+}
1212.border {
1372.border {
1213 border-width: 1px;
1373 border-width: 1px;
1214}
1374}
@@ -1230,9 +1390,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1230.border-l-8 {
1390.border-l-8 {
1231 border-left-width: 8px;
1391 border-left-width: 8px;
1232}
1392}
1233−.border-t {
1234− border-top-width: 1px;
1235−}
1236.border-r {
1393.border-r {
1237 border-right-width: 1px;
1394 border-right-width: 1px;
1238}
1395}
@@ -1242,6 +1399,23 @@ Ensure the default browser behavior of the `hidden` attribute.
1242.border-l {
1399.border-l {
1243 border-left-width: 1px;
1400 border-left-width: 1px;
1244}
1401}
1402+.border-t {
1403+ border-top-width: 1px;
1404+}
1405+.border-b-0 {
1406+ border-bottom-width: 0px;
1407+}
1408+.border-none {
1409+ border-style: none;
1410+}
1411+.border-gray-500 {
1412+ --tw-border-opacity: 1;
1413+ border-color: rgb(107 114 128 / var(--tw-border-opacity));
1414+}
1415+.border-gray-400 {
1416+ --tw-border-opacity: 1;
1417+ border-color: rgb(156 163 175 / var(--tw-border-opacity));
1418+}
1245.border-gold {
1419.border-gold {
1246 --tw-border-opacity: 1;
1420 --tw-border-opacity: 1;
1247 border-color: rgb(252 211 77 / var(--tw-border-opacity));
1421 border-color: rgb(252 211 77 / var(--tw-border-opacity));
@@ -1262,10 +1436,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1262 --tw-border-opacity: 1;
1436 --tw-border-opacity: 1;
1263 border-color: rgb(0 0 0 / var(--tw-border-opacity));
1437 border-color: rgb(0 0 0 / var(--tw-border-opacity));
1264}
1438}
1265−.border-gray-400 {
1266− --tw-border-opacity: 1;
1267− border-color: rgb(156 163 175 / var(--tw-border-opacity));
1268−}
1269.border-red-500 {
1439.border-red-500 {
1270 --tw-border-opacity: 1;
1440 --tw-border-opacity: 1;
1271 border-color: rgb(239 68 68 / var(--tw-border-opacity));
1441 border-color: rgb(239 68 68 / var(--tw-border-opacity));
@@ -1286,9 +1456,21 @@ Ensure the default browser behavior of the `hidden` attribute.
1286 --tw-border-opacity: 1;
1456 --tw-border-opacity: 1;
1287 border-color: rgb(31 41 55 / var(--tw-border-opacity));
1457 border-color: rgb(31 41 55 / var(--tw-border-opacity));
1288}
1458}
1289−.border-gray-500 {
1459+.border-gray-600 {
1290 --tw-border-opacity: 1;
1460 --tw-border-opacity: 1;
1291− border-color: rgb(107 114 128 / var(--tw-border-opacity));
1461+ border-color: rgb(75 85 99 / var(--tw-border-opacity));
1462+}
1463+.border-gray-200 {
1464+ --tw-border-opacity: 1;
1465+ border-color: rgb(229 231 235 / var(--tw-border-opacity));
1466+}
1467+.bg-gray-400 {
1468+ --tw-bg-opacity: 1;
1469+ background-color: rgb(156 163 175 / var(--tw-bg-opacity));
1470+}
1471+.bg-white {
1472+ --tw-bg-opacity: 1;
1473+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
1292}
1474}
1293.bg-navy {
1475.bg-navy {
1294 --tw-bg-opacity: 1;
1476 --tw-bg-opacity: 1;
@@ -1298,10 +1480,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1298 --tw-bg-opacity: 1;
1480 --tw-bg-opacity: 1;
1299 background-color: rgb(252 211 77 / var(--tw-bg-opacity));
1481 background-color: rgb(252 211 77 / var(--tw-bg-opacity));
1300}
1482}
1301−.bg-white {
1302− --tw-bg-opacity: 1;
1303− background-color: rgb(255 255 255 / var(--tw-bg-opacity));
1304−}
1305.bg-l1-blue {
1483.bg-l1-blue {
1306 --tw-bg-opacity: 1;
1484 --tw-bg-opacity: 1;
1307 background-color: rgb(23 146 199 / var(--tw-bg-opacity));
1485 background-color: rgb(23 146 199 / var(--tw-bg-opacity));
@@ -1322,6 +1500,10 @@ Ensure the default browser behavior of the `hidden` attribute.
1322 --tw-bg-opacity: 1;
1500 --tw-bg-opacity: 1;
1323 background-color: rgb(209 213 219 / var(--tw-bg-opacity));
1501 background-color: rgb(209 213 219 / var(--tw-bg-opacity));
1324}
1502}
1503+.bg-gray-200 {
1504+ --tw-bg-opacity: 1;
1505+ background-color: rgb(229 231 235 / var(--tw-bg-opacity));
1506+}
1325.bg-black {
1507.bg-black {
1326 --tw-bg-opacity: 1;
1508 --tw-bg-opacity: 1;
1327 background-color: rgb(0 0 0 / var(--tw-bg-opacity));
1509 background-color: rgb(0 0 0 / var(--tw-bg-opacity));
@@ -1334,10 +1516,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1334 --tw-bg-opacity: 1;
1516 --tw-bg-opacity: 1;
1335 background-color: rgb(253 224 71 / var(--tw-bg-opacity));
1517 background-color: rgb(253 224 71 / var(--tw-bg-opacity));
1336}
1518}
1337−.bg-gray-400 {
1338− --tw-bg-opacity: 1;
1339− background-color: rgb(156 163 175 / var(--tw-bg-opacity));
1340−}
1341.bg-orange-300 {
1519.bg-orange-300 {
1342 --tw-bg-opacity: 1;
1520 --tw-bg-opacity: 1;
1343 background-color: rgb(253 186 116 / var(--tw-bg-opacity));
1521 background-color: rgb(253 186 116 / var(--tw-bg-opacity));
@@ -1362,10 +1540,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1362 --tw-bg-opacity: 1;
1540 --tw-bg-opacity: 1;
1363 background-color: rgb(253 230 138 / var(--tw-bg-opacity));
1541 background-color: rgb(253 230 138 / var(--tw-bg-opacity));
1364}
1542}
1365−.bg-gray-200 {
1366− --tw-bg-opacity: 1;
1367− background-color: rgb(229 231 235 / var(--tw-bg-opacity));
1368−}
1369.bg-gold-t8 {
1543.bg-gold-t8 {
1370 --tw-bg-opacity: 1;
1544 --tw-bg-opacity: 1;
1371 background-color: rgb(254 246 219 / var(--tw-bg-opacity));
1545 background-color: rgb(254 246 219 / var(--tw-bg-opacity));
@@ -1414,9 +1588,28 @@ Ensure the default browser behavior of the `hidden` attribute.
1414 --tw-bg-opacity: 1;
1588 --tw-bg-opacity: 1;
1415 background-color: rgb(107 114 128 / var(--tw-bg-opacity));
1589 background-color: rgb(107 114 128 / var(--tw-bg-opacity));
1416}
1590}
1591+.bg-red-200 {
1592+ --tw-bg-opacity: 1;
1593+ background-color: rgb(254 202 202 / var(--tw-bg-opacity));
1594+}
1595+.bg-red-100 {
1596+ --tw-bg-opacity: 1;
1597+ background-color: rgb(254 226 226 / var(--tw-bg-opacity));
1598+}
1599+.bg-gray-600 {
1600+ --tw-bg-opacity: 1;
1601+ background-color: rgb(75 85 99 / var(--tw-bg-opacity));
1602+}
1417.fill-transparent {
1603.fill-transparent {
1418 fill: transparent;
1604 fill: transparent;
1419}
1605}
1606+.object-cover {
1607+ -o-object-fit: cover;
1608+ object-fit: cover;
1609+}
1610+.p-1 {
1611+ padding: 0.25rem;
1612+}
1420.p-5 {
1613.p-5 {
1421 padding: 1.25rem;
1614 padding: 1.25rem;
1422}
1615}
@@ -1435,9 +1628,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1435.p-6 {
1628.p-6 {
1436 padding: 1.5rem;
1629 padding: 1.5rem;
1437}
1630}
1438−.p-1 {
1439− padding: 0.25rem;
1440−}
1441.p-0 {
1631.p-0 {
1442 padding: 0px;
1632 padding: 0px;
1443}
1633}
@@ -1455,14 +1645,6 @@ Ensure the default browser behavior of the `hidden` attribute.
1455 padding-top: 0.25rem;
1645 padding-top: 0.25rem;
1456 padding-bottom: 0.25rem;
1646 padding-bottom: 0.25rem;
1457}
1647}
1458−.py-0\.5 {
1459− padding-top: 0.125rem;
1460− padding-bottom: 0.125rem;
1461−}
1462−.py-0 {
1463− padding-top: 0px;
1464− padding-bottom: 0px;
1465−}
1466.px-4 {
1648.px-4 {
1467 padding-left: 1rem;
1649 padding-left: 1rem;
1468 padding-right: 1rem;
1650 padding-right: 1rem;
@@ -1475,6 +1657,10 @@ Ensure the default browser behavior of the `hidden` attribute.
1475 padding-left: 0px;
1657 padding-left: 0px;
1476 padding-right: 0px;
1658 padding-right: 0px;
1477}
1659}
1660+.py-0 {
1661+ padding-top: 0px;
1662+ padding-bottom: 0px;
1663+}
1478.py-4 {
1664.py-4 {
1479 padding-top: 1rem;
1665 padding-top: 1rem;
1480 padding-bottom: 1rem;
1666 padding-bottom: 1rem;
@@ -1507,6 +1693,10 @@ Ensure the default browser behavior of the `hidden` attribute.
1507 padding-left: 0.375rem;
1693 padding-left: 0.375rem;
1508 padding-right: 0.375rem;
1694 padding-right: 0.375rem;
1509}
1695}
1696+.py-0\.5 {
1697+ padding-top: 0.125rem;
1698+ padding-bottom: 0.125rem;
1699+}
1510.pt-10 {
1700.pt-10 {
1511 padding-top: 2.5rem;
1701 padding-top: 2.5rem;
1512}
1702}
@@ -1555,9 +1745,9 @@ Ensure the default browser behavior of the `hidden` attribute.
1555.font-mono {
1745.font-mono {
1556 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
1746 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
1557}
1747}
1558−.text-3xl {
1748+.text-sm {
1559− font-size: 1.875rem;
1749+ font-size: 0.875rem;
1560− line-height: 2.25rem;
1750+ line-height: 1.25rem;
1561}
1751}
1562.text-xl {
1752.text-xl {
1563 font-size: 1.25rem;
1753 font-size: 1.25rem;
@@ -1571,9 +1761,9 @@ Ensure the default browser behavior of the `hidden` attribute.
1571 font-size: 1.125rem;
1761 font-size: 1.125rem;
1572 line-height: 1.75rem;
1762 line-height: 1.75rem;
1573}
1763}
1574−.text-sm {
1764+.text-3xl {
1575− font-size: 0.875rem;
1765+ font-size: 1.875rem;
1576− line-height: 1.25rem;
1766+ line-height: 2.25rem;
1577}
1767}
1578.text-base {
1768.text-base {
1579 font-size: 1rem;
1769 font-size: 1rem;
@@ -1594,10 +1784,24 @@ Ensure the default browser behavior of the `hidden` attribute.
1594.leading-3 {
1784.leading-3 {
1595 line-height: .75rem;
1785 line-height: .75rem;
1596}
1786}
1787+.leading-5 {
1788+ line-height: 1.25rem;
1789+}
1790+.leading-8 {
1791+ line-height: 2rem;
1792+}
1793+.text-gray-600 {
1794+ --tw-text-opacity: 1;
1795+ color: rgb(75 85 99 / var(--tw-text-opacity));
1796+}
1597.text-white {
1797.text-white {
1598 --tw-text-opacity: 1;
1798 --tw-text-opacity: 1;
1599 color: rgb(255 255 255 / var(--tw-text-opacity));
1799 color: rgb(255 255 255 / var(--tw-text-opacity));
1600}
1800}
1801+.text-gray-800 {
1802+ --tw-text-opacity: 1;
1803+ color: rgb(31 41 55 / var(--tw-text-opacity));
1804+}
1601.text-gray-700 {
1805.text-gray-700 {
1602 --tw-text-opacity: 1;
1806 --tw-text-opacity: 1;
1603 color: rgb(55 65 81 / var(--tw-text-opacity));
1807 color: rgb(55 65 81 / var(--tw-text-opacity));
@@ -1610,18 +1814,10 @@ Ensure the default browser behavior of the `hidden` attribute.
1610 --tw-text-opacity: 1;
1814 --tw-text-opacity: 1;
1611 color: rgb(22 163 74 / var(--tw-text-opacity));
1815 color: rgb(22 163 74 / var(--tw-text-opacity));
1612}
1816}
1613−.text-gray-800 {
1614− --tw-text-opacity: 1;
1615− color: rgb(31 41 55 / var(--tw-text-opacity));
1616−}
1617.text-gray-500 {
1817.text-gray-500 {
1618 --tw-text-opacity: 1;
1818 --tw-text-opacity: 1;
1619 color: rgb(107 114 128 / var(--tw-text-opacity));
1819 color: rgb(107 114 128 / var(--tw-text-opacity));
1620}
1820}
1621−.text-gray-600 {
1622− --tw-text-opacity: 1;
1623− color: rgb(75 85 99 / var(--tw-text-opacity));
1624−}
1625.text-green-500 {
1821.text-green-500 {
1626 --tw-text-opacity: 1;
1822 --tw-text-opacity: 1;
1627 color: rgb(34 197 94 / var(--tw-text-opacity));
1823 color: rgb(34 197 94 / var(--tw-text-opacity));
@@ -1638,6 +1834,10 @@ Ensure the default browser behavior of the `hidden` attribute.
1638 --tw-text-opacity: 1;
1834 --tw-text-opacity: 1;
1639 color: rgb(252 211 77 / var(--tw-text-opacity));
1835 color: rgb(252 211 77 / var(--tw-text-opacity));
1640}
1836}
1837+.text-gray-300 {
1838+ --tw-text-opacity: 1;
1839+ color: rgb(209 213 219 / var(--tw-text-opacity));
1840+}
1641.underline {
1841.underline {
1642 -webkit-text-decoration-line: underline;
1842 -webkit-text-decoration-line: underline;
1643 text-decoration-line: underline;
1843 text-decoration-line: underline;
@@ -1681,6 +1881,10 @@ Ensure the default browser behavior of the `hidden` attribute.
1681 --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1881 --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1682 box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1882 box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1683}
1883}
1884+.blur {
1885+ --tw-blur: blur(8px);
1886+ filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1887+}
1684.drop-shadow {
1888.drop-shadow {
1685 --tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
1889 --tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
1686 filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1890 filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
@@ -1692,6 +1896,13 @@ Ensure the default browser behavior of the `hidden` attribute.
1692.filter {
1896.filter {
1693 filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1897 filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1694}
1898}
1899+.transition {
1900+ transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-text-decoration-color, -webkit-backdrop-filter;
1901+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
1902+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color, -webkit-backdrop-filter;
1903+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1904+ transition-duration: 150ms;
1905+}
1695.transition-opacity {
1906.transition-opacity {
1696 transition-property: opacity;
1907 transition-property: opacity;
1697 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1908 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
@@ -1707,11 +1918,7 @@ Ensure the default browser behavior of the `hidden` attribute.
1707 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1918 transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1708 transition-duration: 150ms;
1919 transition-duration: 150ms;
1709}
1920}
1710−.transition {
1921+.duration-150 {
1711− transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-text-decoration-color, -webkit-backdrop-filter;
1712− transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
1713− transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color, -webkit-backdrop-filter;
1714− transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
1715 transition-duration: 150ms;
1922 transition-duration: 150ms;
1716}
1923}
1717.hover\:bg-gray-200:hover {
1924.hover\:bg-gray-200:hover {
@@ -1816,22 +2023,30 @@ Ensure the default browser behavior of the `hidden` attribute.
1816 margin-right: auto;
2023 margin-right: auto;
1817 }
2024 }
1818
2025
1819− .md\:min-w-\[256px\] {
2026+ .md\:w-1\/2 {
1820− min-width: 256px;
2027+ width: 50%;
1821 }
2028 }
1822
2029
1823− .md\:max-w-2xl {
2030+ .md\:min-w-\[256px\] {
1824− max-width: 42rem;
2031+ min-width: 256px;
1825 }
2032 }
1826
2033
1827 .md\:max-w-xl {
2034 .md\:max-w-xl {
1828 max-width: 36rem;
2035 max-width: 36rem;
1829 }
2036 }
1830
2037
2038+ .md\:max-w-2xl {
2039+ max-width: 42rem;
2040+ }
2041+
1831 .md\:flex-row {
2042 .md\:flex-row {
1832 flex-direction: row;
2043 flex-direction: row;
1833 }
2044 }
1834
2045
2046+ .md\:flex-nowrap {
2047+ flex-wrap: nowrap;
2048+ }
2049+
1835 .md\:items-start {
2050 .md\:items-start {
1836 align-items: flex-start;
2051 align-items: flex-start;
1837 }
2052 }
ui/src/types/sigil.ts+1−1
@@ -22,7 +22,7 @@ export interface ModeDefs {
22
22
23export const MODE_DEFS: ModeDefs = {
23export const MODE_DEFS: ModeDefs = {
24 geon: {
24 geon: {
25− modes: {},
25+ // modes: {},
26 displayParts: ['ghb'],
26 displayParts: ['ghb'],
27 },
27 },
28 line: {
28 line: {
ui/src/types/store.ts+44
@@ -1,5 +1,12 @@
1import type { ConnectionStatus } from './connection';
1import type { ConnectionStatus } from './connection';
2import type { Patp, Group, Contact, Rolodex } from '@urbit/api';
2import type { Patp, Group, Contact, Rolodex } from '@urbit/api';
3+
4+export function createEmptyContact(): Contact {
5+ return {
6+ nickname: null, bio: null, status: null, color: null, avatar: null,
7+ cover: null, groups: [], 'last-updated': 0,
8+ };
9+}
3export interface Contacts {
10export interface Contacts {
4 [contact: string]: Contact;
11 [contact: string]: Contact;
5};
12};
@@ -7,6 +14,40 @@ export interface Contacts {
7export interface Docs {
14export interface Docs {
8 [doc: string]: string;
15 [doc: string]: string;
9};
16};
17+
18+export interface PalNames {
19+ [patp: Patp]: boolean;
20+}
21+
22+export interface Pal {
23+ tags: string[];
24+ ack: boolean;
25+}
26+
27+export interface PalList {
28+ [patp: Patp]: Pal;
29+}
30+
31+export interface Pals {
32+ incoming: PalNames;
33+ outgoing: PalList;
34+ mutuals: PalList;
35+}
36+
37+export const PAL_STATUSES = ['mutual', 'outgoing', 'incoming', 'unconnected'] as const;
38+export type PalStatus = typeof PAL_STATUSES[number];
39+export interface PalInfo extends Pal {
40+ patp: Patp;
41+ status: PalStatus;
42+}
43+
44+export interface Apps {
45+ allies: {
46+ [ally: string]: string[],
47+ },
48+ set: Set<string>,
49+}
50+
10export interface StoreState {
51export interface StoreState {
11 // Urbit
52 // Urbit
12 // groups: {
53 // groups: {
@@ -14,6 +55,9 @@ export interface StoreState {
14 // };
55 // };
15 contacts: Rolodex;
56 contacts: Rolodex;
16 peers: Patp[];
57 peers: Patp[];
58+ pals: Pals;
59+ palsInstalled: boolean;
60+ apps: Apps,
17 patpQuery: RegExp;
61 patpQuery: RegExp;
18 sigilQuery: string[][];
62 sigilQuery: string[][];
19 searchMode: ('patp' | 'sigil');
63 searchMode: ('patp' | 'sigil');
ui/vite.config.ts+1−1
@@ -7,7 +7,7 @@ import path from 'path';
7export default ({ mode }) => {
7export default ({ mode }) => {
8 Object.assign(process.env, loadEnv(mode, process.cwd()));
8 Object.assign(process.env, loadEnv(mode, process.cwd()));
9 const SHIP_URL = process.env.SHIP_URL || process.env.VITE_SHIP_URL || 'http://localhost:8080';
9 const SHIP_URL = process.env.SHIP_URL || process.env.VITE_SHIP_URL || 'http://localhost:8080';
10− console.log(SHIP_URL);
10+ console.log('SHIP_URL', SHIP_URL);
11
11
12 return defineConfig({
12 return defineConfig({
13 plugins: [
13 plugins: [