diff --git a/.github/ISSUE_TEMPLATE/kernel-or-runtime-bug-report.md b/.github/ISSUE_TEMPLATE/kernel-or-runtime-bug-report.md
index 081d5def7..e913f8502 100644
--- a/.github/ISSUE_TEMPLATE/kernel-or-runtime-bug-report.md
+++ b/.github/ISSUE_TEMPLATE/kernel-or-runtime-bug-report.md
@@ -30,7 +30,7 @@ If applicable, add screenshots to help explain your problem.
**System (please supply the following information, if relevant):**
- OS: [e.g. macOS, linux64, FreeBSD]
- Vere and Urbit OS versions
- - Your ship's `%base` hash (use `.^(@uv %cz /=base=)` to check)
+ - Your ship's `%base` hash (use `+trouble` to check)
**Additional context**
Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/os1-bug-report.md b/.github/ISSUE_TEMPLATE/os1-bug-report.md
index 3f12e2523..974acf045 100644
--- a/.github/ISSUE_TEMPLATE/os1-bug-report.md
+++ b/.github/ISSUE_TEMPLATE/os1-bug-report.md
@@ -27,13 +27,13 @@ If applicable, add screenshots to help explain your problem. If possible, please
**Desktop (please complete the following information):**
- OS: [e.g. MacOS 10.15.3]
- Browser [e.g. chrome, safari]
- - Base hash of your urbit ship. Run ` .^(@uv %cz /=base=)` in Dojo to see this.
+ - Base hash of your urbit ship. Run `+trouble` in Dojo to see this.
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- - Base hash of your urbit ship. Run ` .^(@uv %cz /=base=)` in Dojo to see this.
+ - Base hash of your urbit ship. Run `+trouble` in Dojo to see this.
**Additional context**
Add any other context about the problem here.
diff --git a/bin/solid.pill b/bin/solid.pill
index 0ca93db1f..b4a336f3e 100644
--- a/bin/solid.pill
+++ b/bin/solid.pill
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ecf3f8593815742e409008421f318b664124e672b1eecd131e4a1e49864a1c2a
-size 6175676
+oid sha256:d7088528dbfd54a913921ade093251d678c4ccebfd0ad85ef2022520266b3954
+size 16451173
diff --git a/pkg/arvo/app/chat-cli.hoon b/pkg/arvo/app/chat-cli.hoon
index 1f298cf3d..6ff7667a5 100644
--- a/pkg/arvo/app/chat-cli.hoon
+++ b/pkg/arvo/app/chat-cli.hoon
@@ -72,7 +72,7 @@
+$ glyph char
++ glyphs "!@#$%^&()-=_+[]\{}'\\:\",.<>?"
::
-+$ nu-security ?(%channel %village %village-with-group)
++$ nu-security ?(%channel %village)
::
+$ command
$% [%target (set target)] :: set messaging target
@@ -81,7 +81,7 @@
::
::
:: create chat
- [%create nu-security path (unit glyph) (unit ?)]
+ [%create nu-security path (unit resource) (unit glyph) (unit ?)]
[%delete path] :: delete chat
[%invite [? path] (set ship)] :: allow
[%banish [? path] (set ship)] :: disallow
@@ -293,8 +293,6 @@
::
++ target-to-path
|= target
- %+ weld
- ?:(in-group ~ /~)
[(scot %p ship) path]
:: +path-to-target: deduces a target from a mailbox path
::
@@ -464,6 +462,7 @@
security
;~ plug
path
+ (punt ;~(pfix ace group))
(punt ;~(pfix ace glyph))
(punt ;~(pfix ace (fuss 'y' 'n')))
==
@@ -535,16 +534,15 @@
:: ;~(pfix ace ;~(plug i.opt $(opt t.opt)))
:: --
::
+ ++ group ;~((glue net) ship sym)
++ tag |*(a=@tas (cold a (jest a))) ::TODO into stdlib
++ ship ;~(pfix sig fed:ag)
++ path ;~(pfix net ;~(plug urs:ab (easy ~))) ::NOTE short only, tmp
:: +mang: un/managed indicator prefix
::
- ++ mang
- ;~ pose
- (cold %| (jest '~/'))
- (cold %& (easy ~))
- ==
+ :: deprecated, as sig prefix is no longer used
+ ::
+ ++ mang (cold %& (easy ~))
:: +tarl: local target, as /path
::
++ tarl (stag our-self path)
@@ -585,7 +583,7 @@
:: +security: security mode
::
++ security
- (perk %channel %village-with-group %village ~)
+ (perk %channel %village ~)
::
:: +glyph: shorthand character
::
@@ -741,15 +739,21 @@
:: +create: new local mailbox
::
++ create
- |= [security=nu-security =path gyf=(unit char) allow-history=(unit ?)]
+ |= $: security=nu-security
+ =path
+ ugroup=(unit resource)
+ gyf=(unit char)
+ allow-history=(unit ?)
+ ==
^- (quip card _state)
- =/ with-group=? ?=(%village-with-group security)
+ =/ with-group=? ?=(^ ugroup)
=/ =target [with-group our-self path]
=/ real-path=^path (target-to-path target)
+ =/ group-path=^path ?~(ugroup ship+real-path (en-path:resource u.ugroup))
=/ =policy
?- security
- %channel *open:policy
- ?(%village %village-with-group) *invite:policy
+ %channel *open:policy
+ %village *invite:policy
==
?^ (scry-for (unit mailbox:store) %chat-store [%mailbox real-path])
=- [[- ~] state]
@@ -767,7 +771,7 @@
(rsh 3 1 (spat path))
''
real-path :: chat
- real-path :: group
+ group-path :: group
policy
~
(fall allow-history %.y)
diff --git a/pkg/arvo/app/chat-hook.hoon b/pkg/arvo/app/chat-hook.hoon
index cdc102c9f..fc8d546ec 100644
--- a/pkg/arvo/app/chat-hook.hoon
+++ b/pkg/arvo/app/chat-hook.hoon
@@ -18,17 +18,19 @@
state-1
state-2
state-3
+ state-4
+ state-5
+ state-6
+ state-7
==
::
-+$ state-3
- $: %3
- state-base
- ==
++$ state-7 [%7 state-base]
++$ state-6 [%6 state-base]
++$ state-5 [%5 state-base]
++$ state-4 [%4 state-base]
++$ state-3 [%3 state-base]
++$ state-2 [%2 state-base]
::
-+$ state-2
- $: %2
- state-base
- ==
+$ state-1
$: %1
loaded-cards=*
@@ -52,7 +54,7 @@
$% [%chat-update update:store]
==
--
-=| state-3
+=| state-7
=* state -
::
%- agent:dbug
@@ -81,8 +83,20 @@
=/ old !<(versioned-state old-vase)
=| cards=(list card)
|-
- ?: ?=(%3 -.old)
+ ?: ?=(%7 -.old)
[cards this(state old)]
+ ?: ?=(%6 -.old)
+ =. cards
+ %+ weld cards
+ ^- (list card)
+ [%pass /s %agent [our.bol %chat-hook] %poke %noun !>(%fix-out-of-sync)]~
+ $(-.old %7)
+ ?: ?=(?(%3 %4 %5) -.old)
+ =. cards
+ %+ weld cards
+ ^- (list card)
+ [%pass /pokeme %agent [our.bol %chat-hook] %poke %noun !>(%fix-dm)]~
+ $(-.old %6)
?: ?=(%2 -.old)
=. cards
%+ weld cards
@@ -319,9 +333,9 @@
^- (quip card _this)
=^ cards state
?+ mark (on-poke:def mark vase)
- %json (poke-json:cc !<(json vase))
- %chat-action (poke-chat-action:cc !<(action:store vase))
- %noun [~ state]
+ %json (poke-json:cc !<(json vase))
+ %chat-action (poke-chat-action:cc !<(action:store vase))
+ %noun (poke-noun:cc !<(?(%fix-dm %fix-out-of-sync) vase))
::
%chat-hook-action
(poke-chat-hook-action:cc !<(action:hook vase))
@@ -383,6 +397,81 @@
|_ bol=bowl:gall
++ grp ~(. grpl bol)
::
+++ poke-noun
+ |= a=?(%fix-dm %fix-out-of-sync)
+ ^- (quip card _state)
+ |^
+ :_ state
+ ?- a
+ %fix-dm (fix-dm %fix-dm)
+ %fix-out-of-sync (fix-out-of-sync %fix-out-of-sync)
+ ==
+ ::
+ ++ fix-out-of-sync
+ |= b=%fix-out-of-sync
+ ^- (list card)
+ %- zing
+ %+ turn ~(tap by synced)
+ |= [=path host=ship]
+ ^- (list card)
+ ?: =(host our.bol) ~
+ ?> ?=([@ @ ~] path)
+ =/ =ship (slav %p i.path)
+ :~ =- [%pass / %agent [our.bol %chat-hook] %poke %chat-hook-action -]
+ !> ^- action:hook
+ [%remove path]
+ ::
+ =- [%pass / %agent [our.bol %chat-hook] %poke %chat-hook-action -]
+ !> ^- action:hook
+ [%add-synced ship path %.y]
+ ==
+ ::
+ ++ fix-dm
+ |= b=%fix-dm
+ ^- (list card)
+ %- zing
+ %+ turn
+ ~(tap by synced)
+ |= [=path host=ship]
+ ^- (list card)
+ ?> ?=([@ @ *] path)
+ =/ =ship (slav %p i.path)
+ ?: =(ship our.bol)
+ :: local dm, no need to do cleanup
+ ~
+ ?: ?=(^ (groups-of-chat path))
+ :: correctly initialized, no need to do cleanup
+ ::
+ ~
+ ?. =((end 3 4 i.t.path) 'dm--')
+ ~
+ :- =- [%pass /fixdm %agent [our.bol %chat-view] %poke %chat-view-action -]
+ !> ^- action:view
+ [%delete path]
+ =/ new-dm /(scot %p our.bol)/(crip (weld "dm--" (trip (scot %p ship))))
+ =/ mailbox=(unit mailbox:store) (chat-scry path)
+ ?~ mailbox
+ ~
+ :~ =- [%pass /fixdm %agent [our.bol %chat-view] %poke %chat-view-action -]
+ !> ^- action:view
+ :* %create
+ %- crip
+ (zing [(trip (scot %p our.bol)) " <-> " (trip (scot %p ship)) ~])
+ ''
+ new-dm
+ ship+new-dm
+ [%invite (silt ~[ship])]
+ (silt ~[ship])
+ %.y
+ %.n
+ ==
+ ::
+ =- [%pass /fixdm %agent [our.bol %chat-store] %poke %chat-action -]
+ !> ^- action:store
+ [%messages new-dm envelopes.u.mailbox]
+ ==
+ --
+::
++ poke-json
|= jon=json
^- (quip card _state)
diff --git a/pkg/arvo/app/file-server.hoon b/pkg/arvo/app/file-server.hoon
index 6732fd30a..fdcf7bfd4 100644
--- a/pkg/arvo/app/file-server.hoon
+++ b/pkg/arvo/app/file-server.hoon
@@ -269,7 +269,7 @@
==
::
++ on-leave on-leave:def
-++ on-peek
+++ on-peek
|= =path
^- (unit (unit cage))
|^
@@ -287,10 +287,9 @@
*@uv
=/ parent (scot %p ship.u.ota)
=+ .^(=cass:clay %cs /[parent]/[desk.u.ota]/1/late/foo)
- %^ end 3 3
+ %^ end 0 25
.^(@uv %cz /[parent]/[desk.u.ota]/(scot %ud ud.cass))
--
-
++ on-agent on-agent:def
++ on-fail on-fail:def
--
diff --git a/pkg/arvo/app/glob.hoon b/pkg/arvo/app/glob.hoon
index 953a99bbd..328264bb7 100644
--- a/pkg/arvo/app/glob.hoon
+++ b/pkg/arvo/app/glob.hoon
@@ -1,7 +1,7 @@
/- glob
/+ default-agent, verb, dbug
|%
-++ hash 0v5.knd3c.vvtvt.h0gg0.8qcau.8iii4
+++ hash 0v3.cus8h.vc64c.rfb3t.22oji.b529a
+$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))]
+$ all-states
$% state-0
@@ -58,7 +58,6 @@
++ on-load
|= old-state=vase
^- (quip card _this)
- ~& > %initting
=+ !<(old=all-states old-state)
?> ?=(%0 -.old)
?~ glob.old
diff --git a/pkg/arvo/app/graph-store.hoon b/pkg/arvo/app/graph-store.hoon
new file mode 100644
index 000000000..ab9a6b971
--- /dev/null
+++ b/pkg/arvo/app/graph-store.hoon
@@ -0,0 +1,572 @@
+/+ store=graph-store, sigs=signatures, res=resource, default-agent, dbug
+~% %graph-store-top ..is ~
+|%
++$ card card:agent:gall
++$ versioned-state
+ $% state-0
+ ==
+::
++$ state-0 [%0 network:store]
+++ orm orm:store
+++ orm-log orm-log:store
+--
+::
+=| state-0
+=* state -
+::
+%- agent:dbug
+^- agent:gall
+~% %graph-store-agent ..card ~
+|_ =bowl:gall
++* this .
+ def ~(. (default-agent this %|) bowl)
+::
+++ on-init [~ this]
+++ on-save !>(state)
+++ on-load
+ |= old=vase
+ ^- (quip card _this)
+ [~ this(state !<(state-0 old))]
+::
+++ on-watch
+ ~/ %graph-store-watch
+ |= =path
+ ^- (quip card _this)
+ |^
+ ?> (team:title our.bowl src.bowl)
+ =/ cards=(list card)
+ ?+ path (on-watch:def path)
+ [%updates ~] ~
+ [%keys ~] (give [%keys ~(key by graphs)])
+ [%tags ~] (give [%tags ~(key by tag-queries)])
+ ==
+ [cards this]
+ ::
+ ++ give
+ |= =update-0:store
+ ^- (list card)
+ [%give %fact ~ [%graph-update !>([%0 now.bowl update-0])]]~
+ --
+::
+++ on-poke
+ ~/ %graph-store-poke
+ |= [=mark =vase]
+ ^- (quip card _this)
+ |^
+ ?> (team:title our.bowl src.bowl)
+ =^ cards state
+ ?+ mark (on-poke:def mark vase)
+ %graph-update (graph-update !<(update:store vase))
+ ==
+ [cards this]
+ ::
+ ++ graph-update
+ |= =update:store
+ ^- (quip card _state)
+ |^
+ ?> ?=(%0 -.update)
+ ?- -.q.update
+ %add-graph (add-graph +.q.update)
+ %remove-graph (remove-graph +.q.update)
+ %add-nodes (add-nodes p.update +.q.update)
+ %remove-nodes (remove-nodes p.update +.q.update)
+ %add-signatures (add-signatures p.update +.q.update)
+ %remove-signatures (remove-signatures p.update +.q.update)
+ %add-tag (add-tag +.q.update)
+ %remove-tag (remove-tag +.q.update)
+ %archive-graph (archive-graph +.q.update)
+ %unarchive-graph (unarchive-graph +.q.update)
+ %run-updates (run-updates +.q.update)
+ %keys ~|('cannot send %keys as poke' !!)
+ %tags ~|('cannot send %tags as poke' !!)
+ %tag-queries ~|('cannot send %tag-queries as poke' !!)
+ ==
+ ::
+ ++ add-graph
+ |= [=resource:store =graph:store mark=(unit mark:store)]
+ ^- (quip card _state)
+ ?< (~(has by archive) resource)
+ ?< (~(has by graphs) resource)
+ ?> (validate-graph graph mark)
+ :_ %_ state
+ graphs (~(put by graphs) resource [graph mark])
+ update-logs (~(put by update-logs) resource (gas:orm-log ~ ~))
+ validators
+ ?~ mark validators
+ (~(put in validators) u.mark)
+ ==
+ %- zing
+ :~ (give [/updates /keys ~] [%add-graph resource graph mark])
+ ?~ mark ~
+ ?: (~(has in validators) u.mark) ~
+ =/ wire (weld /graph (en-path:res resource))
+ =/ =rave:clay [%sing %b [%da now.bowl] /[u.mark]]
+ [%pass wire %arvo %c %warp our.bowl [%home `rave]]~
+ ==
+ ::
+ ++ remove-graph
+ |= =resource:store
+ ^- (quip card _state)
+ ?< (~(has by archive) resource)
+ ?> (~(has by graphs) resource)
+ :- (give [/updates /keys ~] [%remove-graph resource])
+ %_ state
+ graphs (~(del by graphs) resource)
+ update-logs (~(del by update-logs) resource)
+ ==
+ ::
+ ++ add-nodes
+ |= $: =time
+ =resource:store
+ nodes=(map index:store node:store)
+ ==
+ ^- (quip card _state)
+ |^
+ =/ [=graph:store mark=(unit mark:store)]
+ (~(got by graphs) resource)
+ =/ =update-log:store (~(got by update-logs) resource)
+ =. update-log
+ (put:orm-log update-log time [%0 time [%add-nodes resource nodes]])
+ ::
+ :- (give [/updates]~ [%add-nodes resource nodes])
+ %_ state
+ update-logs (~(put by update-logs) resource update-log)
+ graphs
+ %+ ~(put by graphs)
+ resource
+ :_ mark
+ (add-node-list resource graph mark (sort-nodes nodes))
+ ==
+ ::
+ ++ sort-nodes
+ |= nodes=(map index:store node:store)
+ ^- (list [index:store node:store])
+ %+ sort ~(tap by nodes)
+ |= [p=[=index:store *] q=[=index:store *]]
+ ^- ?
+ (lth (lent index.p) (lent index.q))
+ ::
+ ++ add-node-list
+ |= $: =resource:store
+ =graph:store
+ mark=(unit mark:store)
+ node-list=(list [index:store node:store])
+ ==
+ ^- graph:store
+ ?~ node-list graph
+ =* index -.i.node-list
+ =* node +.i.node-list
+ %_ $
+ node-list t.node-list
+ graph (add-node-at-index graph index node mark)
+ ==
+ ::
+ ++ add-node-at-index
+ =| parent-hash=(unit hash:store)
+ |= $: =graph:store
+ =index:store
+ =node:store
+ mark=(unit mark:store)
+ ==
+ ^- graph:store
+ ?< ?=(~ index)
+ ~| "validation of node failed using mark {}"
+ ?> (validate-graph (gas:orm ~ [i.index node]~) mark)
+ =* atom i.index
+ %^ put:orm
+ graph
+ atom
+ :: add child
+ ::
+ ?~ t.index
+ =* p post.node
+ =/ =validated-portion:store
+ [parent-hash author.p time-sent.p contents.p]
+ =/ =hash:store `@ux`(sham validated-portion)
+ ?~ hash.p node(signatures.post *signatures:store)
+ ~| "signatures do not match the calculated hash"
+ ?> (are-signatures-valid:sigs signatures.p hash now.bowl)
+ ~| "hash of post does not match calculated hash"
+ ?> =(hash u.hash.p)
+ node
+ :: recurse children
+ ::
+ =/ parent=node:store
+ ~| "index does not exist to add a node to!"
+ (need (get:orm graph atom))
+ %_ parent
+ children
+ ^- internal-graph:store
+ :- %graph
+ %_ $
+ index t.index
+ parent-hash hash.post.parent
+ graph
+ ?: ?=(%graph -.children.parent)
+ p.children.parent
+ (gas:orm ~ ~)
+ ==
+ ==
+ --
+ ::
+ ++ remove-nodes
+ |= [=time =resource:store indices=(set index:store)]
+ ^- (quip card _state)
+ |^
+ =/ [=graph:store mark=(unit mark:store)]
+ (~(got by graphs) resource)
+ =/ =update-log:store (~(got by update-logs) resource)
+ =. update-log
+ (put:orm-log update-log time [%0 time [%remove-nodes resource indices]])
+ ::
+ :- (give [/updates]~ [%remove-nodes resource indices])
+ %_ state
+ update-logs (~(put by update-logs) resource update-log)
+ graphs
+ %+ ~(put by graphs)
+ resource
+ [(remove-indices resource graph ~(tap in indices)) mark]
+ ==
+ ::
+ ++ remove-indices
+ |= [=resource:store =graph:store indices=(list index:store)]
+ ^- graph:store
+ ?~ indices graph
+ %_ $
+ indices t.indices
+ graph (remove-index graph i.indices)
+ ==
+ ::
+ ++ remove-index
+ |= [=graph:store =index:store]
+ ^- graph:store
+ ?~ index graph
+ =* atom i.index
+ :: last index in list
+ ::
+ ?~ t.index
+ +:`[* graph:store]`(del:orm graph atom)
+ =/ =node:store
+ ~| "parent index does not exist to remove a node from!"
+ (need (get:orm graph atom))
+ ~| "child index does not exist to remove a node from!"
+ ?> ?=(%graph -.children.node)
+ %^ put:orm
+ graph
+ atom
+ node(p.children $(graph p.children.node, index t.index))
+ --
+ ::
+ ++ add-signatures
+ |= [=time =uid:store =signatures:store]
+ ^- (quip card _state)
+ |^
+ =* resource resource.uid
+ =/ [=graph:store mark=(unit mark:store)]
+ (~(got by graphs) resource)
+ =/ =update-log:store (~(got by update-logs) resource)
+ =. update-log
+ (put:orm-log update-log time [%0 time [%add-signatures uid signatures]])
+ ::
+ :- (give [/updates]~ [%add-signatures uid signatures])
+ %_ state
+ update-logs (~(put by update-logs) resource update-log)
+ graphs
+ %+ ~(put by graphs) resource
+ [(add-at-index graph index.uid signatures) mark]
+ ==
+ ::
+ ++ add-at-index
+ |= [=graph:store =index:store =signatures:store]
+ ^- graph:store
+ ?~ index graph
+ =* atom i.index
+ =/ =node:store
+ ~| "node does not exist to add signatures to!"
+ (need (get:orm graph atom))
+ :: last index in list
+ ::
+ %^ put:orm
+ graph
+ atom
+ ?~ t.index
+ ~| "cannot add signatures to a node missing a hash"
+ ?> ?=(^ hash.post.node)
+ ~| "signatures did not match public keys!"
+ ?> (are-signatures-valid:sigs signatures u.hash.post.node now.bowl)
+ node(signatures.post (~(uni in signatures) signatures.post.node))
+ ~| "child graph does not exist to add signatures to!"
+ ?> ?=(%graph -.children.node)
+ node(p.children $(graph p.children.node, index t.index))
+ --
+ ::
+ ++ remove-signatures
+ |= [=time =uid:store =signatures:store]
+ ^- (quip card _state)
+ |^
+ =* resource resource.uid
+ =/ [=graph:store mark=(unit mark:store)]
+ (~(got by graphs) resource)
+ =/ =update-log:store (~(got by update-logs) resource)
+ =. update-log
+ %^ put:orm-log update-log
+ time
+ [%0 time [%remove-signatures uid signatures]]
+ ::
+ :- (give [/updates]~ [%remove-signatures uid signatures])
+ %_ state
+ update-logs (~(put by update-logs) resource update-log)
+ graphs
+ %+ ~(put by graphs) resource
+ [(remove-at-index graph index.uid signatures) mark]
+ ==
+ ::
+ ++ remove-at-index
+ |= [=graph:store =index:store =signatures:store]
+ ^- graph:store
+ ?~ index graph
+ =* atom i.index
+ =/ =node:store
+ ~| "node does not exist to add signatures to!"
+ (need (get:orm graph atom))
+ :: last index in list
+ ::
+ %^ put:orm
+ graph
+ atom
+ ?~ t.index
+ node(signatures.post (~(dif in signatures) signatures.post.node))
+ ~| "child graph does not exist to add signatures to!"
+ ?> ?=(%graph -.children.node)
+ node(p.children $(graph p.children.node, index t.index))
+ --
+ ::
+ ++ add-tag
+ |= [=term =resource:store]
+ ^- (quip card _state)
+ ?> (~(has by graphs) resource)
+ :- (give [/updates /tags ~] [%add-tag term resource])
+ %_ state
+ tag-queries (~(put ju tag-queries) term resource)
+ ==
+ ::
+ ++ remove-tag
+ |= [=term =resource:store]
+ ^- (quip card _state)
+ ?> (~(has by graphs) resource)
+ :- (give [/updates /tags ~] [%remove-tag term resource])
+ %_ state
+ tag-queries (~(del ju tag-queries) term resource)
+ ==
+ ::
+ ++ archive-graph
+ |= =resource:store
+ ^- (quip card _state)
+ ?< (~(has by archive) resource)
+ ?> (~(has by graphs) resource)
+ :- (give [/updates /keys /tags ~] [%archive-graph resource])
+ %_ state
+ archive (~(put by archive) resource (~(got by graphs) resource))
+ graphs (~(del by graphs) resource)
+ update-logs (~(del by update-logs) resource)
+ tag-queries
+ %- ~(run by tag-queries)
+ |= =resources:store
+ (~(del in resources) resource)
+ ==
+ ::
+ ++ unarchive-graph
+ |= =resource:store
+ ^- (quip card _state)
+ ?> (~(has by archive) resource)
+ ?< (~(has by graphs) resource)
+ :- (give [/updates /keys ~] [%unarchive-graph resource])
+ %_ state
+ archive (~(del by archive) resource)
+ graphs (~(put by graphs) resource (~(got by archive) resource))
+ update-logs (~(put by update-logs) resource (gas:orm-log ~ ~))
+ ==
+ ::
+ ++ run-updates
+ |= [=resource:store =update-log:store]
+ ^- (quip card _state)
+ ?< (~(has by archive) resource)
+ ?> (~(has by graphs) resource)
+ :_ state
+ %+ turn (tap:orm-log update-log)
+ |= [=time update=logged-update:store]
+ ^- card
+ ?> ?=(%0 -.update)
+ :* %pass
+ /run-updates/(scot %da time)
+ %agent
+ [our.bowl %graph-store]
+ %poke
+ :- %graph-update
+ !>
+ ^- update:store
+ ?- -.q.update
+ %add-nodes update(resource.q resource)
+ %remove-nodes update(resource.q resource)
+ %add-signatures update(resource.uid.q resource)
+ %remove-signatures update(resource.uid.q resource)
+ ==
+ ==
+ ::
+ ++ validate-graph
+ |= [=graph:store mark=(unit mark:store)]
+ ^- ?
+ ?~ mark %.y
+ ?~ graph %.y
+ =/ =dais:clay
+ .^ =dais:clay
+ %cb
+ /(scot %p our.bowl)/[q.byk.bowl]/(scot %da now.bowl)/[u.mark]
+ ==
+ %+ roll (tap:orm graph)
+ |= [[=atom =node:store] out=?]
+ ?& out
+ =(%& -:(mule |.((vale:dais [atom post.node]))))
+ ?- -.children.node
+ %empty %.y
+ %graph ^$(graph p.children.node)
+ ==
+ ==
+ ::
+ ++ give
+ |= [paths=(list path) update=update-0:store]
+ ^- (list card)
+ [%give %fact paths [%graph-update !>([%0 now.bowl update])]]~
+ --
+ --
+::
+++ on-peek
+ ~/ %graph-store-peek
+ |= =path
+ ^- (unit (unit cage))
+ |^
+ ?> (team:title our.bowl src.bowl)
+ ?+ path (on-peek:def path)
+ [%x %keys ~] ``noun+!>(~(key by graphs))
+ [%x %tags ~] ``noun+!>(~(key by tag-queries))
+ [%x %tag-queries ~] ``noun+!>(tag-queries)
+ [%x %graph @ @ ~]
+ =/ =ship (slav %p i.t.t.path)
+ =/ =term i.t.t.t.path
+ =/ result=(unit marked-graph:store)
+ (~(get by graphs) [ship term])
+ ?~ result [~ ~]
+ ``noun+!>(u.result)
+ ::
+ [%x %graph-subset @ @ @ @ ~]
+ =/ =ship (slav %p i.t.t.path)
+ =/ =term i.t.t.t.path
+ =/ start=(unit atom) (rush i.t.t.t.t.path dem:ag)
+ =/ end=(unit atom) (rush i.t.t.t.t.t.path dem:ag)
+ =/ graph=(unit marked-graph:store)
+ (~(get by graphs) [ship term])
+ ?~ graph [~ ~]
+ ``noun+!>(`graph:store`(subset:orm p.u.graph start end))
+ ::
+ [%x %node @ @ @ *]
+ =/ =ship (slav %p i.t.t.path)
+ =/ =term i.t.t.t.path
+ =/ =index:store
+ (turn t.t.t.t.path |=(=cord (slav %ud cord)))
+ =/ node=(unit node:store) (get-node ship term index)
+ ?~ node [~ ~]
+ ``noun+!>(u.node)
+ ::
+ [%x %post @ @ @ *]
+ =/ =ship (slav %p i.t.t.path)
+ =/ =term i.t.t.t.path
+ =/ =index:store
+ (turn t.t.t.t.path |=(=cord (slav %ud cord)))
+ =/ node=(unit node:store) (get-node ship term index)
+ ?~ node [~ ~]
+ ``noun+!>(post.u.node)
+ ::
+ [%x %node-children @ @ @ *]
+ =/ =ship (slav %p i.t.t.path)
+ =/ =term i.t.t.t.path
+ =/ =index:store
+ (turn t.t.t.t.path |=(=cord (slav %ud cord)))
+ =/ node=(unit node:store) (get-node ship term index)
+ ?~ node [~ ~]
+ ?- -.children.u.node
+ %empty [~ ~]
+ %graph ``noun+!>(p.children.u.node)
+ ==
+ ::
+ [%x %node-children-subset @ @ @ @ @ *]
+ =/ =ship (slav %p i.t.t.path)
+ =/ =term i.t.t.t.path
+ =/ start=(unit atom) (rush i.t.t.t.t.path dem:ag)
+ =/ end=(unit atom) (rush i.t.t.t.t.t.path dem:ag)
+ =/ =index:store
+ (turn t.t.t.t.t.t.path |=(=cord (slav %ud cord)))
+ =/ node=(unit node:store) (get-node ship term index)
+ ?~ node [~ ~]
+ ?- -.children.u.node
+ %empty [~ ~]
+ %graph ``noun+!>(`graph:store`(subset:orm p.children.u.node start end))
+ ==
+ ::
+ [%x %update-log @ @ ~]
+ =/ =ship (slav %p i.t.t.path)
+ =/ =term i.t.t.t.path
+ =/ update-log=(unit update-log:store) (~(get by update-logs) [ship term])
+ ?~ update-log [~ ~]
+ ``noun+!>(u.update-log)
+ ::
+ [%x %peek-update-log @ @ ~]
+ =/ =ship (slav %p i.t.t.path)
+ =/ =term i.t.t.t.path
+ =/ update-log=(unit update-log:store) (~(get by update-logs) [ship term])
+ ?~ update-log [~ ~]
+ =/ result=(unit [time update:store])
+ (peek:orm-log:store u.update-log)
+ ?~ result [~ ~]
+ ``noun+!>([~ -.u.result])
+ ==
+ ::
+ ++ get-node
+ |= [=ship =term =index:store]
+ ^- (unit node:store)
+ =/ parent-graph=(unit marked-graph:store)
+ (~(get by graphs) [ship term])
+ ?~ parent-graph ~
+ =/ node=(unit node:store) ~
+ =/ =graph:store p.u.parent-graph
+ |-
+ ?~ index
+ node
+ ?~ t.index
+ (get:orm graph i.index)
+ =. node (get:orm graph i.index)
+ ?~ node ~
+ ?- -.children.u.node
+ %empty ~
+ %graph $(graph p.children.u.node, index t.index)
+ ==
+ --
+::
+++ on-arvo
+ |= [=wire =sign-arvo]
+ ^- (quip card _this)
+ ?+ -.sign-arvo (on-arvo:def wire sign-arvo)
+ %c
+ :_ this
+ ?> ?=([%graph @ *] wire)
+ =/ =resource:store (de-path:res t.wire)
+ =/ gra=(unit marked-graph:store) (~(get by graphs) resource)
+ ?~ gra ~
+ ?~ q.u.gra ~
+ =/ =rave:clay [%next %b [%da now.bowl] /[u.q.u.gra]]
+ [%pass wire %arvo %c %warp our.bowl [%home `rave]]~
+ ==
+::
+++ on-agent on-agent:def
+++ on-leave on-leave:def
+++ on-fail on-fail:def
+--
diff --git a/pkg/arvo/app/hood.hoon b/pkg/arvo/app/hood.hoon
index e83b30b15..1bc70ed80 100644
--- a/pkg/arvo/app/hood.hoon
+++ b/pkg/arvo/app/hood.hoon
@@ -2,22 +2,16 @@
/+ drum=hood-drum, helm=hood-helm, kiln=hood-kiln
|%
+$ state
- $: %8
- drum=state:drum
- helm=state:helm
- kiln=state:kiln
- ==
-+$ state-7
- $: %7
+ $: %9
drum=state:drum
helm=state:helm
kiln=state:kiln
==
+$ any-state
$% state
- state-7
[ver=?(%1 %2 %3 %4 %5 %6) lac=(map @tas fin-any-state)]
[%7 drum=state:drum helm=state:helm kiln=state:kiln]
+ [%8 drum=state:drum helm=state:helm kiln=state:kiln]
==
+$ any-state-tuple
$: drum=any-state:drum
diff --git a/pkg/arvo/app/metadata-store.hoon b/pkg/arvo/app/metadata-store.hoon
index bf052800d..a0e88e2e6 100644
--- a/pkg/arvo/app/metadata-store.hoon
+++ b/pkg/arvo/app/metadata-store.hoon
@@ -85,15 +85,16 @@
old [%2 +.old]
::
cards
- %+ turn
+ %+ murn
~(tap in ~(key by group-indices.old))
|= =group-path
- ^- card
- =/ rid=resource
- (de-path:resource group-path)
- ?: =(our.bowl entity.rid)
- (poke-md-hook %add-owned group-path)
- (poke-md-hook %add-synced entity.rid group-path)
+ ^- (unit card)
+ =/ rid=(unit resource)
+ (de-path-soft:resource group-path)
+ ?~ rid ~
+ ?: =(our.bowl entity.u.rid)
+ `(poke-md-hook %add-owned group-path)
+ `(poke-md-hook %add-synced entity.u.rid group-path)
==
=/ new-state=state-one
%* . *state-one
@@ -254,6 +255,11 @@
=/ =group-path (stab (slav %t i.t.t.path))
=/ =md-resource [`@tas`i.t.t.t.path (stab (slav %t i.t.t.t.t.path))]
``noun+!>((~(get by associations) [group-path md-resource]))
+ ::
+ [%x %resource @ *]
+ =/ app=@tas i.t.t.path
+ =/ app-path=^path t.t.t.path
+ ``noun+!>((~(get by resource-indices) app app-path))
==
::
++ on-agent on-agent:def
diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon
index d4c5b470c..17470e7e5 100644
--- a/pkg/arvo/app/publish.hoon
+++ b/pkg/arvo/app/publish.hoon
@@ -54,6 +54,7 @@
[%3 state-three]
[%4 state-three]
[%5 state-three]
+ [%6 state-three]
==
::
+$ metadata-delta
@@ -69,7 +70,7 @@
==
--
::
-=| [%5 state-three]
+=| [%6 state-three]
=* state -
%- agent:dbug
%+ verb |
@@ -86,7 +87,6 @@
:_ this
:~ [%pass /view-bind %arvo %e %connect [~ /'publish-view'] %publish]
[%pass /read/paths %arvo %c %warp our.bol q.byk.bol `rav]
- [%pass /permissions %agent [our.bol %permission-store] %watch /updates]
(invite-poke:main [%create /publish])
:* %pass /invites %agent [our.bol %invite-store] %watch
/invitatory/publish
@@ -218,6 +218,26 @@
==
::
%5
+ %= $
+ -.p.old-state %6
+ cards
+ %+ weld cards
+ %+ roll ~(tap by books.p.old-state)
+ |= [[[who=@p book=@tas] nb=notebook] out=(list card)]
+ ^- (list card)
+ ?. =(who our.bol)
+ out
+ =/ rid (de-path:resource writers.nb)
+ =/ grp=(unit group) (scry-group:grup:main rid)
+ ?~ grp out
+ ?: hidden.u.grp
+ out
+ =/ =tag [%publish (cat 3 'writers-' book)]
+ :_ out
+ (group-proxy-poke entity.rid %add-tag rid tag members.u.grp)
+ ==
+ ::
+ %6
[cards this(state p.old-state)]
==
++ convert-notebook-3-4
@@ -995,6 +1015,22 @@
[~ state]
:_ state
%- zing
+ :- ^- (list card)
+ %+ roll ~(tap by books)
+ |= [[[who=@p book=@tas] nb=notebook] out=(list card)]
+ ^- (list card)
+ ?. =(who our.bol)
+ out
+ ?. =(writers.nb path)
+ out
+ =/ rid (de-path:resource writers.nb)
+ =/ grp=(unit group) (scry-group:grup rid)
+ ?~ grp out
+ ?: hidden.u.grp
+ out
+ =/ =tag [%publish (cat 3 'writers-' book)]
+ :_ out
+ (group-proxy-poke entity.rid %add-tag rid tag members.u.grp)
%+ turn ~(tap in ships)
|= who=@p
?. (allowed who %read u.book)
@@ -1226,12 +1262,19 @@
^- [(list card) write=path read=path]
?> ?=(^ group-path.group)
=/ scry-path
- ;:(welp /(scot %p our.bol)/group-store/(scot %da now.bol) [%groups group-path.group] /noun)
- =/ grp .^((unit ^group) %gx scry-path)
+ ;: welp
+ /(scot %p our.bol)/group-store/(scot %da now.bol)
+ [%groups group-path.group]
+ /noun
+ ==
+ =/ rid=resource (de-path:resource group-path.group)
+ =/ grp=(unit ^group) (scry-group:grup rid)
?: use-preexisting.group
?~ grp !!
?. (is-managed group-path.group) !!
- `[group-path.group group-path.group]
+ =/ =tag [%publish (cat 3 'writers-' book)]
+ :_ [group-path.group group-path.group]
+ [(group-proxy-poke entity.rid %add-tag rid tag members.u.grp)]~
::
=/ =policy
*open:policy
@@ -1684,10 +1727,9 @@
?> ?=(^ subscribers.u.book)
=/ cards=(list card)
~[(delete-dir pax)]
-
=/ rid=resource
(de-path:resource writers.u.book)
- =? cards (is-managed:grup rid)
+ =? cards !(is-managed:grup rid)
[(group-poke %remove-group rid ~) cards]
[cards state]
:: %del-note:
@@ -1791,6 +1833,10 @@
?> (team:title our.bol src.bol)
=/ join-wire=wire
/join-group/[(scot %p who.act)]/[book.act]
+ =/ meta=(unit (set path))
+ (metadata-resource-scry %publish /(scot %p who.act)/[book.act])
+ ?^ meta
+ (subscribe-notebook who.act book.act)
=/ rid=resource
[who.act book.act]
=/ =cage
@@ -1811,12 +1857,16 @@
(de-path:resource writers.book)
=/ =group
(need (scry-group:grup rid))
- :_ state(books (~(del by books) who.act book.act))
- :~ `card`[%pass wir %agent [who.act %publish] %leave ~]
- `card`[%give %fact [/primary]~ %publish-primary-delta !>(del)]
- (group-proxy-poke who.act %remove-members rid (sy our.bol ~))
- (group-poke %remove-group rid ~)
- ==
+ =/ cards=(list card)
+ :~ [%pass wir %agent [who.act %publish] %leave ~]
+ [%give %fact [/primary]~ %publish-primary-delta !>(del)]
+ ==
+ =? cards hidden.group
+ %+ weld cards
+ :~ (group-proxy-poke who.act %remove-members rid (sy our.bol ~))
+ (group-poke %remove-group rid ~)
+ ==
+ [cards state(books (~(del by books) who.act book.act))]
:: %read
::
%read
@@ -1952,6 +2002,19 @@
/noun
==
::
+++ metadata-resource-scry
+ |= [app=@tas app-path=path]
+ ^- (unit (set path))
+ ?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~) ~
+ .^ (unit (set path))
+ %gx
+ ;: weld
+ /(scot %p our.bol)/metadata-store/(scot %da now.bol)/resource/[app]
+ app-path
+ /noun
+ ==
+ ==
+::
++ emit-metadata
|= del=metadata-delta
^- (list card)
@@ -2044,9 +2107,11 @@
(emit-updates-and-state host.del book.del data.del del sty)
=/ rid=resource
(de-path:resource writers.data.del)
+ =? cards !=(our.bol entity.rid)
+ :_ cards
+ (group-pull-hook-poke [%add host.del rid])
:_ state
- :* (group-pull-hook-poke [%add host.del rid])
- (metadata-hook-poke [%add-synced host.del writers.data.del])
+ :* (metadata-hook-poke [%add-synced host.del writers.data.del])
cards
==
::
diff --git a/pkg/arvo/gen/graph-store/add-graph.hoon b/pkg/arvo/gen/graph-store/add-graph.hoon
new file mode 100644
index 000000000..a95468928
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/add-graph.hoon
@@ -0,0 +1,10 @@
+:: graph-store|add-graph: add new graph
+::
+/+ *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[=resource mark=(unit mark) ~] ~]
+ ==
+:- %graph-update
+^- update
+[%0 now [%add-graph resource (gas:orm ~ ~) mark]]
diff --git a/pkg/arvo/gen/graph-store/add-post.hoon b/pkg/arvo/gen/graph-store/add-post.hoon
new file mode 100644
index 000000000..643e073c0
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/add-post.hoon
@@ -0,0 +1,20 @@
+:: graph-store|add-post: add post to a graph
+::
+/- *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[[our=ship name=term] contents=(list content) ~] ~]
+ ==
+=/ =post *post
+=: author.post our
+ index.post [now]~
+ time-sent.post now
+ contents.post contents
+==
+::
+:- %graph-update
+^- update
+:+ %0 now
+:+ %add-nodes [our name]
+%- ~(gas by *(map index node))
+~[[[now]~ [post [%empty ~]]]]
diff --git a/pkg/arvo/gen/graph-store/add-signatures.hoon b/pkg/arvo/gen/graph-store/add-signatures.hoon
new file mode 100644
index 000000000..9a536d6d5
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/add-signatures.hoon
@@ -0,0 +1,10 @@
+:: graph-store|add-signatures: add signatures to a node at a particular uid
+::
+/- *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[[=resource =index] =signatures ~] ~]
+ ==
+:- %graph-update
+^- update
+[%0 now [%add-signatures [resource index] signatures]]
diff --git a/pkg/arvo/gen/graph-store/add-tag.hoon b/pkg/arvo/gen/graph-store/add-tag.hoon
new file mode 100644
index 000000000..8c83f1c4e
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/add-tag.hoon
@@ -0,0 +1,10 @@
+:: graph-store|add-tag: tag a particular graph
+::
+/- *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[=term =resource ~] ~]
+ ==
+:- %graph-update
+^- update
+[%0 now [%add-tag term resource]]
diff --git a/pkg/arvo/gen/graph-store/archive-graph.hoon b/pkg/arvo/gen/graph-store/archive-graph.hoon
new file mode 100644
index 000000000..a40a7a533
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/archive-graph.hoon
@@ -0,0 +1,10 @@
+:: graph-store|archive-graph: archive graph
+::
+/- *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[=resource ~] ~]
+ ==
+:- %graph-update
+^- update
+[%0 now [%archive-graph resource]]
diff --git a/pkg/arvo/gen/graph-store/remove-graph.hoon b/pkg/arvo/gen/graph-store/remove-graph.hoon
new file mode 100644
index 000000000..e06305981
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/remove-graph.hoon
@@ -0,0 +1,10 @@
+:: graph-store|remove-graph: remove graph
+::
+/- *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[=resource ~] ~]
+ ==
+:- %graph-update
+^- update
+[%0 now [%remove-graph resource]]
diff --git a/pkg/arvo/gen/graph-store/remove-nodes.hoon b/pkg/arvo/gen/graph-store/remove-nodes.hoon
new file mode 100644
index 000000000..8c5e13603
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/remove-nodes.hoon
@@ -0,0 +1,10 @@
+:: graph-store|remove-nodes: remove nodes from a graph at indices
+::
+/- *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[=resource indices=(set index) ~] ~]
+ ==
+:- %graph-update
+^- update
+[%0 now [%remove-nodes resource indices]]
diff --git a/pkg/arvo/gen/graph-store/remove-signatures.hoon b/pkg/arvo/gen/graph-store/remove-signatures.hoon
new file mode 100644
index 000000000..b9bd658fa
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/remove-signatures.hoon
@@ -0,0 +1,11 @@
+:: graph-store|remove-signatures: remove signatures from a node at a
+:: particular uid
+::
+/- *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[[=resource =index] =signatures ~] ~]
+ ==
+:- %graph-update
+^- update
+[%0 now [%remove-signatures [resource index] signatures]]
diff --git a/pkg/arvo/gen/graph-store/remove-tag.hoon b/pkg/arvo/gen/graph-store/remove-tag.hoon
new file mode 100644
index 000000000..722d4af5c
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/remove-tag.hoon
@@ -0,0 +1,10 @@
+:: graph-store|remove-tag: remove a tag from a particular graph
+::
+/- *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[=term =resource ~] ~]
+ ==
+:- %graph-update
+^- update
+[%0 now [%remove-tag term resource]]
diff --git a/pkg/arvo/gen/graph-store/unarchive-graph.hoon b/pkg/arvo/gen/graph-store/unarchive-graph.hoon
new file mode 100644
index 000000000..1d684de6a
--- /dev/null
+++ b/pkg/arvo/gen/graph-store/unarchive-graph.hoon
@@ -0,0 +1,10 @@
+:: graph-store|unarchive-graph: unarchive graph
+::
+/- *graph-store
+:- %say
+|= $: [now=@da eny=@uvJ =beak]
+ [[=resource ~] ~]
+ ==
+:- %graph-update
+^- update
+[%0 now [%unarchive-graph resource]]
diff --git a/pkg/arvo/gen/hood/moon-cycle-keys.hoon b/pkg/arvo/gen/hood/moon-cycle-keys.hoon
index e9be8a32f..38ca3d364 100644
--- a/pkg/arvo/gen/hood/moon-cycle-keys.hoon
+++ b/pkg/arvo/gen/hood/moon-cycle-keys.hoon
@@ -36,7 +36,7 @@
public-key
=/ cub (pit:nu:crub:crypto 512 (shaz (jam mon life eny)))
=/ =seed:able:jael
- [mon 1 sec:ex:cub ~]
+ [mon life sec:ex:cub ~]
%- %- slog
:~ leaf+"moon: {(scow %p mon)}"
leaf+(scow %uw (jam seed))
diff --git a/pkg/arvo/gen/hood/ota.hoon b/pkg/arvo/gen/hood/ota.hoon
index c91f5b9f3..fcccb68a8 100644
--- a/pkg/arvo/gen/hood/ota.hoon
+++ b/pkg/arvo/gen/hood/ota.hoon
@@ -8,7 +8,11 @@
::
:- %say
|= $: [now=@da eny=@uvJ bec=beak]
- [arg=?(~ [her=@p sud=@tas ~]) ~]
+ [arg=?(~ [%disable ~] [her=@p sud=@tas ~]) ~]
==
+?~ arg
+ :- %kiln-ota-info ~
:- %kiln-ota
-?~(arg ~ `[her sud]:arg)
+?: ?=([%disable ~] arg)
+ ~
+`[her sud]:arg
diff --git a/pkg/arvo/gen/timers.hoon b/pkg/arvo/gen/timers.hoon
index b016fd41c..d448fd6ac 100644
--- a/pkg/arvo/gen/timers.hoon
+++ b/pkg/arvo/gen/timers.hoon
@@ -5,5 +5,5 @@
[%tang >timers< ~]
.^ (list [date=@da =duct])
%bx
- (en-beam:format [p.bec %$ r.bec] /debug/timers)
+ (en-beam:format [p.bec %$ r.bec] /timers/debug)
==
diff --git a/pkg/arvo/lib/graph-store.hoon b/pkg/arvo/lib/graph-store.hoon
new file mode 100644
index 000000000..bf099c89f
--- /dev/null
+++ b/pkg/arvo/lib/graph-store.hoon
@@ -0,0 +1,411 @@
+/- sur=graph-store, pos=post
+/+ res=resource
+=< [sur .]
+=< [pos .]
+=, sur
+=, pos
+|%
+:: NOTE: move these functions to zuse
+++ nu :: parse number as hex
+ |= jon/json
+ ?> ?=({$s *} jon)
+ (rash p.jon hex)
+::
+++ re :: recursive reparsers
+ |* {gar/* sef/_|.(fist:dejs-soft:format)}
+ |= jon/json
+ ^- (unit _gar)
+ =- ~! gar ~! (need -) -
+ ((sef) jon)
+::
+++ dank :: tank
+ ^- $-(json (unit tank))
+ =, ^? dejs-soft:format
+ %+ re *tank |. ~+
+ %- of :~
+ leaf+sa
+ palm+(ot style+(ot mid+sa cap+sa open+sa close+sa ~) lines+(ar dank) ~)
+ rose+(ot style+(ot mid+sa open+sa close+sa ~) lines+(ar dank) ~)
+ ==
+::
+++ orm ((ordered-map atom node) gth)
+++ orm-log ((ordered-map time logged-update) gth)
+::
+++ enjs
+ =, enjs:format
+ |%
+ ++ update
+ |= upd=^update
+ ^- json
+ ?> ?=(%0 -.upd)
+ |^ (frond %graph-update (pairs ~[(encode q.upd)]))
+ ::
+ ++ encode
+ |= upd=update-0
+ ^- [cord json]
+ ?- -.upd
+ %add-graph
+ :- %add-graph
+ %- pairs
+ :~ [%resource (enjs:res resource.upd)]
+ [%graph (graph graph.upd)]
+ [%mark ?~(mark.upd ~ s+u.mark.upd)]
+ ==
+ ::
+ %remove-graph
+ [%remove-graph (enjs:res resource.upd)]
+ ::
+ %add-nodes
+ :- %add-nodes
+ %- pairs
+ :~ [%resource (enjs:res resource.upd)]
+ [%nodes (nodes nodes.upd)]
+ ==
+ ::
+ %remove-nodes
+ :- %remove-nodes
+ %- pairs
+ :~ [%resource (enjs:res resource.upd)]
+ [%indices (indices indices.upd)]
+ ==
+ ::
+ %add-signatures
+ :- %add-signatures
+ %- pairs
+ :~ [%uid (uid uid.upd)]
+ [%signatures (signatures signatures.upd)]
+ ==
+ ::
+ %remove-signatures
+ :- %remove-signatures
+ %- pairs
+ :~ [%uid (uid uid.upd)]
+ [%signatures (signatures signatures.upd)]
+ ==
+ ::
+ %add-tag
+ :- %add-tag
+ %- pairs
+ :~ [%term s+term.upd]
+ [%resource (enjs:res resource.upd)]
+ ==
+ ::
+ %remove-tag
+ :- %remove-tag
+ %- pairs
+ :~ [%term s+term.upd]
+ [%resource (enjs:res resource.upd)]
+ ==
+ ::
+ %archive-graph
+ [%archive-graph (enjs:res resource.upd)]
+ ::
+ %unarchive-graph
+ [%unarchive-graph (enjs:res resource.upd)]
+ ::
+ %keys
+ [%keys [%a (turn ~(tap in resources.upd) enjs:res)]]
+ ::
+ %tags
+ [%tags [%a (turn ~(tap in tags.upd) |=(=term s+term))]]
+ ::
+ %run-updates
+ [%run-updates ~]
+ ::
+ %tag-queries
+ :- %tag-queries
+ %- pairs
+ %+ turn ~(tap by tag-queries.upd)
+ |= [=term =resources]
+ ^- [cord json]
+ [term [%a (turn ~(tap in resources) enjs:res)]]
+ ==
+ ::
+ ++ graph
+ |= g=^graph
+ ^- json
+ :- %a
+ %+ turn (tap:orm g)
+ |= [a=atom n=^node]
+ ^- json
+ :- %a
+ :~ (index [a]~)
+ (node n)
+ ==
+ ::
+ ++ index
+ |= i=^index
+ ^- json
+ =/ j=^tape ""
+ |-
+ ?~ i [%s (crip j)]
+ =/ k=json (numb i.i)
+ ?> ?=(%n -.k)
+ %_ $
+ i t.i
+ j (weld j (weld "/" (trip +.k)))
+ ==
+ ::
+ ++ node
+ |= n=^node
+ ^- json
+ %- pairs
+ :~ [%post (post post.n)]
+ :- %children
+ ?- -.children.n
+ %empty ~
+ %graph (graph +.children.n)
+ ==
+ ==
+ ::
+ ++ post
+ |= p=^post
+ ^- json
+ %- pairs
+ :~ [%author (ship author.p)]
+ [%index (index index.p)]
+ [%time-sent (time time-sent.p)]
+ [%contents [%a (turn contents.p content)]]
+ [%hash ?~(hash.p ~ s+(scot %ux u.hash.p))]
+ [%signatures (signatures signatures.p)]
+ ==
+ ::
+ ++ content
+ |= c=^content
+ ^- json
+ ?- -.c
+ %text (frond %text s+text.c)
+ %url (frond %url s+url.c)
+ %reference (frond %reference (uid uid.c))
+ %code
+ %+ frond %code
+ %- pairs
+ :- [%expression s+expression.c]
+ :_ ~
+ :- %output
+ :: virtualize output rendering, +tank:enjs:format might crash
+ ::
+ =/ result=(each (list json) tang)
+ (mule |.((turn output.c tank)))
+ ?- -.result
+ %& a+p.result
+ %| a+[a+[%s '[[output rendering error]]']~]~
+ ==
+ ==
+ ::
+ ++ nodes
+ |= m=(map ^index ^node)
+ ^- json
+ :- %a
+ %+ turn ~(tap by m)
+ |= [n=^index o=^node]
+ ^- json
+ :- %a
+ :~ (index n)
+ (node o)
+ ==
+ ::
+ ++ indices
+ |= i=(set ^index)
+ ^- json
+ [%a (turn ~(tap in i) index)]
+ ::
+ ++ uid
+ |= u=^uid
+ ^- json
+ %- pairs
+ :~ [%resource (enjs:res resource.u)]
+ [%index (index index.u)]
+ ==
+ ::
+ ++ signatures
+ |= s=^signatures
+ ^- json
+ [%a (turn ~(tap in s) signature)]
+ ::
+ ++ signature
+ |= s=^signature
+ ^- json
+ %- pairs
+ :~ [%signature s+(scot %ux p.s)]
+ [%ship (ship q.s)]
+ [%life (numb r.s)]
+ ==
+ --
+ --
+::
+++ dejs
+ =, dejs:format
+ |%
+ ++ update
+ |= jon=json
+ ^- ^update
+ :- %0
+ :- *time
+ ^- update-0
+ =< (decode jon)
+ |%
+ ++ decode
+ %- of
+ :~ [%add-graph add-graph]
+ [%remove-graph remove-graph]
+ [%add-nodes add-nodes]
+ [%remove-nodes remove-nodes]
+ [%add-signatures add-signatures]
+ [%remove-signatures remove-signatures]
+ [%add-tag add-tag]
+ [%remove-tag remove-tag]
+ [%archive-graph archive-graph]
+ [%unarchive-graph unarchive-graph]
+ [%keys keys]
+ [%tags tags]
+ [%tag-queries tag-queries]
+ [%run-updates run-updates]
+ ==
+ ::
+ ++ add-graph
+ %- ot
+ :~ [%resource dejs:res]
+ [%graph graph]
+ [%mark (mu so)]
+ ==
+ ::
+ ++ graph
+ |= a=json
+ ^- ^graph
+ =/ or-mp ((ordered-map atom ^node) gth)
+ %+ gas:or-mp ~
+ %+ turn ~(tap by ((om node) a))
+ |* [b=cord c=*]
+ ^- [atom ^node]
+ => .(+< [b c]=+<)
+ [(rash b dem) c]
+ ::
+ ++ remove-graph (ot [%resource dejs:res]~)
+ ++ archive-graph (ot [%resource dejs:res]~)
+ ++ unarchive-graph (ot [%resource dejs:res]~)
+ ::
+ ++ add-nodes
+ %- ot
+ :~ [%resource dejs:res]
+ [%nodes nodes]
+ ==
+ ::
+ ++ nodes (op ;~(pfix net (more net dem)) node)
+ ::
+ ++ node
+ %- ot
+ :~ [%post post]
+ :: TODO: support adding nodes with children by supporting the
+ :: graph key
+ [%children (of [%empty ul]~)]
+ ==
+ ::
+ ++ post
+ %- ot
+ :~ [%author (su ;~(pfix sig fed:ag))]
+ [%index index]
+ [%time-sent di]
+ [%contents (ar content)]
+ [%hash (mu nu)]
+ [%signatures (as signature)]
+ ==
+ ::
+ ++ content
+ %- of
+ :~ [%text so]
+ [%url so]
+ [%reference uid]
+ [%code eval]
+ ==
+ ::
+ ++ eval
+ |= a=^json
+ ^- [cord (list tank)]
+ =, ^? dejs-soft:format
+ =+ exp=((ot expression+so ~) a)
+ %- need
+ ?~ exp [~ '' ~]
+ :+ ~ u.exp
+ :: NOTE: when sending, if output is an empty list,
+ :: graph-store will evaluate
+ (fall ((ot output+(ar dank) ~) a) ~)
+ ::
+ ++ remove-nodes
+ %- ot
+ :~ [%resource dejs:res]
+ [%indices (as index)]
+ ==
+ ::
+ ++ add-signatures
+ %- ot
+ :~ [%uid uid]
+ [%signatures (as signature)]
+ ==
+ ::
+ ++ remove-signatures
+ %- ot
+ :~ [%uid uid]
+ [%signatures (as signature)]
+ ==
+ ::
+ ++ signature
+ %- ot
+ :~ [%hash nu]
+ [%ship (su ;~(pfix sig fed:ag))]
+ [%life ni]
+ ==
+ ::
+ ++ uid
+ %- ot
+ :~ [%resource dejs:res]
+ [%index index]
+ ==
+ ::
+ ++ index (su ;~(pfix net (more net dem)))
+ ::
+ ++ add-tag
+ %- ot
+ :~ [%term so]
+ [%resource dejs:res]
+ ==
+ ::
+ ++ remove-tag
+ %- ot
+ :~ [%term so]
+ [%resource dejs:res]
+ ==
+ ::
+ ++ keys
+ |= =json
+ *resources
+ ::
+ ++ tags
+ |= =json
+ *(set term)
+ ::
+ ++ tag-queries
+ |= =json
+ *^tag-queries
+ ::
+ ++ run-updates
+ |= a=json
+ ^- [resource update-log]
+ [*resource *update-log]
+ --
+ --
+::
+++ create
+ |_ [our=ship now=time]
+ ++ post
+ |= [=index contents=(list content)]
+ ^- ^post
+ :* our
+ index
+ now
+ contents
+ ~
+ *signatures
+ ==
+ --
+--
diff --git a/pkg/arvo/lib/graph.hoon b/pkg/arvo/lib/graph.hoon
new file mode 100644
index 000000000..395c0554f
--- /dev/null
+++ b/pkg/arvo/lib/graph.hoon
@@ -0,0 +1,24 @@
+/- *resource
+/+ store=graph-store
+|_ =bowl:gall
+++ scry-for
+ |* [=mold =path]
+ .^ mold
+ %gx
+ (scot %p our.bowl)
+ %graph-store
+ (scot %da now.bowl)
+ (snoc `^path`path %noun)
+ ==
+::
+++ get-graph
+ |= res=resource
+ ^- marked-graph:store
+ %+ scry-for marked-graph:store
+ /graph/(scot %p entity.res)/[name.res]
+::
+++ peek-log
+ |= res=resource
+ ^- (unit time)
+ (scry-for (unit time) /peek-update-log/(scot %p entity.res)/[name.res])
+--
diff --git a/pkg/arvo/lib/hood/drum.hoon b/pkg/arvo/lib/hood/drum.hoon
index 0afec46ce..e7b91cfbe 100644
--- a/pkg/arvo/lib/hood/drum.hoon
+++ b/pkg/arvo/lib/hood/drum.hoon
@@ -104,6 +104,7 @@
%s3-store
%file-server
%glob
+ %graph-store
==
::
++ deft-fish :: default connects
@@ -206,7 +207,7 @@
==
::
++ on-load
- |= [hood-version=?(%1 %2 %3 %4 %5 %6 %7 %8) old=any-state]
+ |= [hood-version=?(%1 %2 %3 %4 %5 %6 %7 %8 %9) old=any-state]
=< se-abet =< se-view
=. sat old
=. dev (~(gut by bin) ost *source)
@@ -233,6 +234,8 @@
=? ..on-load (lte hood-version %8)
=> (se-born | %home %group-push-hook)
(se-born | %home %group-pull-hook)
+ =? ..on-load (lte hood-version %9)
+ (se-born | %home %graph-store)
..on-load
::
++ reap-phat :: ack connect
diff --git a/pkg/arvo/lib/hood/kiln.hoon b/pkg/arvo/lib/hood/kiln.hoon
index 07f04de83..21273f827 100644
--- a/pkg/arvo/lib/hood/kiln.hoon
+++ b/pkg/arvo/lib/hood/kiln.hoon
@@ -208,7 +208,7 @@
::
++ get-germ
|= =desk
- =+ .^(=cass:clay %cw /(scot %p our)/home/(scot %da now))
+ =+ .^(=cass:clay %cw /(scot %p our)/[desk]/(scot %da now))
?- ud.cass
%0 %init
%1 %that
@@ -341,13 +341,22 @@
abet:(spam (render "already syncing" [sud her syd]:hos) ~)
abet:abet:start-sync:(auto hos)
::
+++ ota-info
+ ?~ ota
+ "OTAs disabled"
+ "OTAs enabled from {} on {}"
+::
+++ poke-ota-info
+ |= *
+ =< abet %- spam
+ :~ [%leaf ota-info]
+ [%leaf "use |ota %disable or |ota ~sponsor %kids to reset it"]
+ ==
+::
++ poke-syncs :: print sync config
|= ~
=< abet %- spam
- :- :- %leaf
- ?~ ota
- "OTAs disabled"
- "OTAs from {} on {}"
+ :- [%leaf ota-info]
?: =(0 ~(wyt by syn))
[%leaf "no other syncs configured"]~
%+ turn ~(tap in ~(key by syn))
@@ -416,6 +425,7 @@
%kiln-merge =;(f (f !<(_+<.f vase)) poke-merge)
%kiln-mount =;(f (f !<(_+<.f vase)) poke-mount)
%kiln-ota =;(f (f !<(_+<.f vase)) poke:update)
+ %kiln-ota-info =;(f (f !<(_+<.f vase)) poke-ota-info)
%kiln-permission =;(f (f !<(_+<.f vase)) poke-permission)
%kiln-rm =;(f (f !<(_+<.f vase)) poke-rm)
%kiln-schedule =;(f (f !<(_+<.f vase)) poke-schedule)
diff --git a/pkg/arvo/lib/push-hook.hoon b/pkg/arvo/lib/push-hook.hoon
index 4958085a2..776aa0f59 100644
--- a/pkg/arvo/lib/push-hook.hoon
+++ b/pkg/arvo/lib/push-hook.hoon
@@ -59,7 +59,6 @@
|~ [term tang]
*[(list card) _^|(..on-init)]
:: +resource-for-update: get affected resource from an update
-
++ resource-for-update
|~ vase
*(unit resource)
diff --git a/pkg/arvo/lib/signatures.hoon b/pkg/arvo/lib/signatures.hoon
new file mode 100644
index 000000000..12b58a644
--- /dev/null
+++ b/pkg/arvo/lib/signatures.hoon
@@ -0,0 +1,43 @@
+/- post
+^?
+=< [post .]
+=, post
+|%
+++ sign
+ |= [our=ship now=time =hash]
+ ^- signature
+ =/ =life .^(life %j /=life/(scot %da now)/(scot %p our))
+ =/ =ring .^(ring %j /=vein/(scot %da now)/(scot %ud life))
+ :+ `@ux`(sign:as:(nol:nu:crub:crypto ring) hash)
+ our
+ life
+::
+++ is-signature-valid
+ |= [=signature =hash now=time]
+ ^- ?
+ =/ deed=(unit [a=life b=pass c=(unit @ux)])
+ .^ (unit [life pass (unit @ux)])
+ %j
+ /=deed/(scot %da now)/(scot %p q.signature)/(scot %ud p.signature)
+ ==
+ :: we do not have a public key from ship
+ ::
+ ?~ deed %.y
+ :: we do not have a public key from ship at this life
+ ::
+ ?. =(a.u.deed r.signature) %.y
+ :: verify signature from ship at life
+ ::
+ =(`hash (tear:as:crub:crypto b.u.deed p.signature))
+::
+++ are-signatures-valid
+ |= [=signatures =hash now=time]
+ ^- ?
+ =/ signature-list ~(tap in signatures)
+ |-
+ ?~ signature-list
+ %.y
+ ?: (is-signature-valid i.signature-list hash now)
+ $(signature-list t.signature-list)
+ %.n
+--
diff --git a/pkg/arvo/mar/graph/update.hoon b/pkg/arvo/mar/graph/update.hoon
new file mode 100644
index 000000000..d5f0f4abe
--- /dev/null
+++ b/pkg/arvo/mar/graph/update.hoon
@@ -0,0 +1,13 @@
+/+ *graph-store
+|_ upd=update
+++ grow
+ |%
+ ++ json (update:enjs upd)
+ --
+::
+++ grab
+ |%
+ ++ noun update
+ ++ json update:dejs
+ --
+--
diff --git a/pkg/arvo/mar/graph/validator/chat.hoon b/pkg/arvo/mar/graph/validator/chat.hoon
new file mode 100644
index 000000000..c49373514
--- /dev/null
+++ b/pkg/arvo/mar/graph/validator/chat.hoon
@@ -0,0 +1,17 @@
+/- *post
+|_ i=indexed-post
+++ grow
+ |%
+ ++ noun i
+ --
+++ grab
+ |%
+ ++ noun
+ |= p=*
+ =/ ip ;;(indexed-post p)
+ ?> ?=([@ ~] index.p.ip)
+ ip
+ --
+::
+++ grad %noun
+--
diff --git a/pkg/arvo/sur/graph-store.hoon b/pkg/arvo/sur/graph-store.hoon
new file mode 100644
index 000000000..edcfb4135
--- /dev/null
+++ b/pkg/arvo/sur/graph-store.hoon
@@ -0,0 +1,61 @@
+/- *post
+|%
++$ graph ((mop atom node) gth)
++$ marked-graph [p=graph q=(unit mark)]
+::
++$ node [=post children=internal-graph]
++$ graphs (map resource marked-graph)
+::
++$ tag-queries (jug term resource)
+::
++$ update-log ((mop time logged-update) gth)
++$ update-logs (map resource update-log)
+::
++$ internal-graph
+ $~ [%empty ~]
+ $% [%graph p=graph]
+ [%empty ~]
+ ==
+::
++$ network
+ $: =graphs
+ =tag-queries
+ =update-logs
+ archive=graphs
+ validators=(set mark)
+ ==
+::
++$ update
+ $% [%0 p=time q=update-0]
+ ==
+::
++$ logged-update
+ $% [%0 p=time q=logged-update-0]
+ ==
+::
++$ logged-update-0
+ $% [%add-nodes =resource nodes=(map index node)]
+ [%remove-nodes =resource indices=(set index)]
+ [%add-signatures =uid =signatures]
+ [%remove-signatures =uid =signatures]
+ ==
+::
++$ update-0
+ $% logged-update-0
+ [%add-graph =resource =graph mark=(unit mark)]
+ [%remove-graph =resource]
+ ::
+ [%add-tag =term =resource]
+ [%remove-tag =term =resource]
+ ::
+ [%archive-graph =resource]
+ [%unarchive-graph =resource]
+ [%run-updates =resource =update-log]
+ ::
+ :: NOTE: cannot be sent as pokes
+ ::
+ [%keys =resources]
+ [%tags tags=(set term)]
+ [%tag-queries =tag-queries]
+ ==
+--
diff --git a/pkg/arvo/sur/post.hoon b/pkg/arvo/sur/post.hoon
new file mode 100644
index 000000000..c5c354615
--- /dev/null
+++ b/pkg/arvo/sur/post.hoon
@@ -0,0 +1,37 @@
+/- *resource
+|%
++$ index (list atom)
++$ uid [=resource =index]
+::
+:: +sham (half sha-256) hash of +validated-portion
++$ hash @ux
+::
++$ signature [p=@ux q=ship r=life]
++$ signatures (set signature)
++$ post
+ $: author=ship
+ =index
+ time-sent=time
+ contents=(list content)
+ hash=(unit hash)
+ =signatures
+ ==
+::
++$ indexed-post [a=atom p=post]
+::
++$ validated-portion
+ $: parent-hash=(unit hash)
+ author=ship
+ time-sent=time
+ contents=(list content)
+ ==
+::
++$ content
+ $% [%text text=cord]
+ [%url url=cord]
+ [%code expression=cord output=(list tank)]
+ [%reference =uid]
+ :: TODO: maybe use a cask?
+ ::[%cage =cage]
+ ==
+--
diff --git a/pkg/arvo/sur/pull-hook.hoon b/pkg/arvo/sur/pull-hook.hoon
index 81655a181..1c66648c1 100644
--- a/pkg/arvo/sur/pull-hook.hoon
+++ b/pkg/arvo/sur/pull-hook.hoon
@@ -8,5 +8,4 @@
+$ update
$% [%tracking tracking=(map resource ship)]
==
-::
--
diff --git a/pkg/arvo/sys/hoon.hoon b/pkg/arvo/sys/hoon.hoon
index 824b3a2cd..369977f8a 100644
--- a/pkg/arvo/sys/hoon.hoon
+++ b/pkg/arvo/sys/hoon.hoon
@@ -7762,11 +7762,13 @@
++ teal
|= mod/spec
^- spec
+ ?: ?=(%& -.tik) mod
[%over [%& 3]~ mod]
::
++ tele
|= syn/skin
^- skin
+ ?: ?=(%& -.tik) syn
[%over [%& 3]~ syn]
::
++ gray
diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon
index cbf7485b8..74b3c64b0 100644
--- a/pkg/arvo/sys/vane/ames.hoon
+++ b/pkg/arvo/sys/vane/ames.hoon
@@ -1121,17 +1121,32 @@
?> =(rcvr-life.shut-packet our-life.channel)
:: non-galaxy: update route with heard lane or forwarded lane
::
- =? route.peer-state
- ?: =(%czar (clan:title her.channel))
- %.n
- =/ is-old-direct=? ?=([~ %& *] route.peer-state)
- =/ is-new-direct=? ?=(~ origin.packet)
- :: old direct takes precedence over new indirect
- ::
- |(is-new-direct !is-old-direct)
+ =? route.peer-state !=(%czar (clan:title her.channel))
+ :: if new packet is direct, use that. otherwise, if the new new
+ :: and old lanes are indirect, use the new one. if the new lane
+ :: is indirect but the old lane is direct, then if the lanes are
+ :: identical, don't mark it indirect; if they're not identical,
+ :: use the new lane and mark it indirect.
::
- ?~ origin.packet
+ :: if you mark lane as indirect because you got an indirect
+ :: packet even though you already had a direct identical lane,
+ :: then delayed forwarded packets will come later and reset to
+ :: indirect, so you're unlikely to get a stable direct route
+ :: (unless the forwarder goes offline for a while).
+ ::
+ :: conversely, if you don't accept indirect routes with different
+ :: lanes, then if your lane is stale and they're trying to talk
+ :: to you, your acks will go to the stale lane, and you'll never
+ :: time it out unless you reach out to them. this manifests as
+ :: needing to |hi or dotpost to get a response when the other
+ :: ship has changed lanes.
+ ::
+ ?: ?=(~ origin.packet)
`[direct=%.y lane]
+ ?: ?=([~ %& *] route.peer-state)
+ ?: =(lane.u.route.peer-state u.origin.packet)
+ route.peer-state
+ `[direct=%.n u.origin.packet]
`[direct=%.n u.origin.packet]
:: perform peer-specific handling of packet
::
diff --git a/pkg/arvo/sys/vane/clay.hoon b/pkg/arvo/sys/vane/clay.hoon
index e60c47b00..14d23c8d4 100644
--- a/pkg/arvo/sys/vane/clay.hoon
+++ b/pkg/arvo/sys/vane/clay.hoon
@@ -109,6 +109,10 @@
mut/(list (trel path lobe cage)) :: mutations
== ::
::
+:: Over-the-wire backfill request
+::
++$ fill [=desk =lobe]
+::
:: Ford cache
::
+$ ford-cache
@@ -214,18 +218,29 @@
:: requests, and a possible nako if we've received data from the other ship and
:: are in the process of validating it.
::
-++ rind :: request manager
- $: nix/@ud :: request index
- bom/(map @ud {p/duct q/rave}) :: outstanding
- fod/(map duct @ud) :: current requests
- haw/(map mood (unit cage)) :: simple cache
- == ::
++$ rind :: request manager
+ $: nix=@ud :: request index
+ bom=(map @ud update-state) :: outstanding
+ fod=(map duct @ud) :: current requests
+ haw=(map mood (unit cage)) :: simple cache
+ == ::
+::
+:: Active downloads
+::
++$ update-state
+ $: =duct
+ =rave
+ have=(map lobe blob)
+ need=(list lobe)
+ nako=(qeu (unit nako))
+ busy=_|
+ ==
::
:: Result of a subscription
::
++ sub-result
$% [%blab =mood data=(each cage lobe)]
- [%bleb ins=@ud range=(unit (pair aeon aeon))]
+ [%bleb ver=@ud ins=@ud range=(unit (pair aeon aeon))]
[%balk cage=(unit (each cage lobe)) =mood]
[%blas moods=(set mood)]
[%blub ~]
@@ -246,7 +261,7 @@
:: Generally used when we store a request in our state somewhere.
::
++ cach (unit (unit (each cage lobe))) :: cached result
-+$ wove [for=(unit ship) =rove] :: stored source + req
++$ wove [for=(unit [=ship ver=@ud]) =rove] :: stored source + req
++ rove :: stored request
$% [%sing =mood] :: single request
[%next =mood aeon=(unit aeon) =cach] :: next version of one
@@ -1134,13 +1149,13 @@
:: Give next step in a subscription.
::
++ bleb
- |= {hen/duct ins/@ud hip/(unit (pair aeon aeon))}
+ |= [hen=duct ver=@ud ins=@ud hip=(unit (pair aeon aeon))]
^+ +>
%^ blab hen [%w [%ud ins] ~]
:- %&
?~ hip
[%null [%atom %n ~] ~]
- [%nako !>((make-nako:ze u.hip))]
+ [%nako !>((make-nako:ze ver u.hip))]
::
:: Tell subscriber that subscription is done.
::
@@ -1183,7 +1198,7 @@
=/ =desk p.riff
=/ =wire /warp-index/(scot %p ship)/(scot %tas desk)/(scot %ud index)
=/ =path [%question desk (scot %ud index) ~]
- (emit duct %pass wire %a %plea ship %c path riff)
+ (emit duct %pass wire %a %plea ship %c path [[%1 ~] riff])
::
:: Create a request that cannot be filled immediately.
::
@@ -1210,7 +1225,7 @@
(send-over-ames hen her inx syd `rave)
%= +>+.$
nix.u.ref +(nix.u.ref)
- bom.u.ref (~(put by bom.u.ref) inx [hen rave])
+ bom.u.ref (~(put by bom.u.ref) inx [hen rave ~ ~ ~ |])
fod.u.ref (~(put by fod.u.ref) hen inx)
==
::
@@ -2003,6 +2018,7 @@
:: bob's.
::
?: ?=(%init germ)
+ ?> ?=(~ bob-yaki)
&+`[conflicts=~ new=|+ali-yaki lat=~]
::
=/ bob-yaki (need bob-yaki)
@@ -2589,7 +2605,7 @@
:: and then waiting if the subscription range extends into the future.
::
++ start-request
- |= [for=(unit ship) rav=rave]
+ |= [for=(unit [ship @ud]) rav=rave]
^+ ..start-request
=^ [new-sub=(unit rove) sub-results=(list sub-result)] fod.dom
(try-fill-sub for (rave-to-rove rav))
@@ -2612,9 +2628,9 @@
?> ?=(^ ref)
=+ ruv=(~(get by bom.u.ref) inx)
?~ ruv +>.$
- =/ rav=rave q.u.ruv
+ =/ rav=rave rave.u.ruv
?: ?=(%many -.rav)
- (take-foreign-update inx rut)
+ abet:(apex:(foreign-update inx) rut)
?~ rut
:: nothing here, so cache that
::
@@ -2689,36 +2705,138 @@
!>(;;(@uvI q.page))
--
::
- :: A full foreign update. Validate and apply to our local cache of
- :: their state.
+ :: Respond to backfill request
::
- ++ take-foreign-update
- |= [inx=@ud rut=(unit rand)]
- ^+ ..take-foreign-update
+ :: Maybe should verify the requester is allowed to access this blob?
+ ::
+ ++ give-backfill
+ |= =lobe
+ ^+ ..give-backfill
+ (emit hen %give %boon (~(got by lat.ran) lobe))
+ ::
+ :: Ingest foreign update, requesting missing blobs if necessary
+ ::
+ ++ foreign-update
+ |= inx=@ud
?> ?=(^ ref)
- =/ ruv (~(get by bom.u.ref) inx)
- ?~ ruv
- ~& [%clay-foreign-update-lost her syd inx]
- ..take-foreign-update
- =. hen p.u.ruv
- =/ =rave q.u.ruv
- ?> ?=(%many -.rave)
- |^
- ?~ rut
- done
- =. lim ?.(?=(%da -.to.moat.rave) lim p.to.moat.rave)
- ?> ?=(%nako p.r.u.rut)
- =/ nako ;;(nako q.r.u.rut)
- =. ..take-foreign-update
- =< ?>(?=(^ ref) .)
- (apply-foreign-update nako)
- done
+ =/ [sat=update-state lost=?]
+ =/ ruv (~(get by bom.u.ref) inx)
+ ?~ ruv
+ ~& [%clay-foreign-update-lost her syd inx]
+ [*update-state &]
+ [u.ruv |]
+ =/ done=? |
+ =. hen duct.sat
+ |%
+ ++ abet
+ ^+ ..foreign-update
+ ?: lost
+ ..foreign-update
+ ?: done
+ =: bom.u.ref (~(del by bom.u.ref) inx)
+ fod.u.ref (~(del by fod.u.ref) hen)
+ ==
+ =<(?>(?=(^ ref) .) wake)
+ =. bom.u.ref (~(put by bom.u.ref) inx sat)
+ ..foreign-update
::
- ++ done
- =: bom.u.ref (~(del by bom.u.ref) inx)
- bom.u.ref (~(del by bom.u.ref) hen)
- ==
- =<(?>(?=(^ ref) .) wake)
+ ++ apex
+ |= rut=(unit rand)
+ ^+ ..abet
+ ?: lost ..abet
+ ?~ rut
+ =. nako.sat (~(put to nako.sat) ~)
+ work
+ ?> ?=(%nako p.r.u.rut)
+ =/ nako ;;(nako q.r.u.rut)
+ =/ missing (missing-blobs nako)
+ =. need.sat `(list lobe)`(welp need.sat ~(tap in missing))
+ =. nako.sat (~(put to nako.sat) ~ nako)
+ work
+ ::
+ ++ missing-blobs
+ |= =nako
+ ^- (set lobe)
+ =/ yakis ~(tap in lar.nako)
+ |- ^- (set lobe)
+ =* yaki-loop $
+ ?~ yakis
+ ~
+ =/ lobes=(list [=path =lobe]) ~(tap by q.i.yakis)
+ |- ^- (set lobe)
+ =* blob-loop $
+ ?~ lobes
+ yaki-loop(yakis t.yakis)
+ ?: (~(has by lat.ran) lobe.i.lobes)
+ blob-loop(lobes t.lobes)
+ (~(put in blob-loop(lobes t.lobes)) lobe.i.lobes)
+ ::
+ :: Receive backfill response
+ ::
+ ++ take-backfill
+ |= =blob
+ ^+ ..abet
+ ?: lost ..abet
+ =? need.sat
+ ?& ?=(%delta -.blob)
+ !(~(has by lat.ran) q.q.blob)
+ !(~(has by have.sat) q.q.blob)
+ ==
+ [q.q.blob need.sat]
+ :: We can't put a blob in lat.ran if its parent isn't already
+ :: there. Unions are in reverse order so we don't overwrite
+ :: existing blobs.
+ ::
+ =. ..abet
+ ?: &(?=(%delta -.blob) !(~(has by lat.ran) q.q.blob))
+ ..abet(have.sat (~(uni by (malt [p.blob `^blob`blob] ~)) have.sat))
+ ..abet(lat.ran (~(uni by (malt [p.blob blob] ~)) lat.ran))
+ work(busy.sat |)
+ ::
+ :: Fetch next blob
+ ::
+ ++ work
+ ^+ ..abet
+ ?: busy.sat
+ ..abet
+ |- ^+ ..abet
+ ?: =(~ need.sat)
+ :: NB: if you change to release nakos as we get enough blobs
+ :: for them instead of all at the end, you *must* store the
+ :: `lim` that should be applied after the nako is complete and
+ :: not use the one in the rave, since that will apply to the
+ :: end of subscription.
+ ::
+ =. lat.ran (~(uni by have.sat) lat.ran)
+ |- ^+ ..abet
+ ?: =(~ nako.sat)
+ ..abet
+ =^ next=(unit nako) nako.sat ~(get to nako.sat)
+ ?~ next
+ ..abet(done &)
+ =. ..abet (apply-foreign-update u.next)
+ =. ..foreign-update =<(?>(?=(^ ref) .) wake)
+ $
+ ?> ?=(^ need.sat)
+ :: This is what removes an item from `need`. This happens every
+ :: time we take a backfill response, but it could happen more than
+ :: once if we somehow got this data in the meantime (maybe from
+ :: another desk updating concurrently, or a previous update on this
+ :: same desk).
+ ::
+ ?: ?| (~(has by lat.ran) i.need.sat)
+ (~(has by have.sat) i.need.sat)
+ ==
+ $(need.sat t.need.sat)
+ :: Otherwise, fetch the next blob
+ ::
+ =/ =fill [syd i.need.sat]
+ =/ =wire /back-index/(scot %p her)/[syd]/(scot %ud inx)
+ =/ =path [%backfill syd (scot %ud inx) ~]
+ =. ..foreign-update
+ =< ?>(?=(^ ref) .)
+ (emit hen %pass wire %a %plea her %c path fill)
+ ..abet(busy.sat &)
::
:: When we get a %w foreign update, store this in our state.
::
@@ -2728,7 +2846,7 @@
::
++ apply-foreign-update
|= =nako
- ^+ ..take-foreign-update
+ ^+ ..abet
:: hit: updated commit-hashes by @ud case
:: nut: new commit-hash/commit pairs
:: hut: updated commits by hash
@@ -2765,12 +2883,19 @@
$(aeon +(aeon))
:: produce updated state
::
+ =/ =rave rave:(~(got by bom.u.ref) inx)
+ ?> ?=(%many -.rave)
=: let.dom (max let.nako let.dom)
hit.dom hit
hut.ran hut
lat.ran lat
+ :: Is this correct? Seeems like it should only go to `to` if
+ :: we've gotten all the way to the end. Leaving this
+ :: behavior unchanged for now, but I believe it's wrong.
+ ::
+ lim ?.(?=(%da -.to.moat.rave) lim p.to.moat.rave)
==
- ..take-foreign-update
+ ..abet
--
::
:: fire function if request is in future
@@ -2862,8 +2987,9 @@
:: Try to fill a subscription
::
++ try-fill-sub
- |= [for=(unit ship) rov=rove]
+ |= [far=(unit [=ship ver=@ud]) rov=rove]
^- [[new-sub=(unit rove) (list sub-result)] ford-cache]
+ =/ for=(unit ship) ?~(far ~ `ship.u.far)
?- -.rov
%sing
=/ cache-value=(unit (unit cage))
@@ -3075,6 +3201,7 @@
::
[`rov ~]
=/ to-aeon (case-to-aeon to.moat.rov)
+ =/ ver ?~(far %1 ver.u.far)
?~ to-aeon
:: we're in the middle of the range, so produce what we can,
:: but don't end the subscription
@@ -3092,7 +3219,7 @@
~
:: else changes, so produce them
::
- [%bleb let.dom ?:(track.rov ~ `[u.from-aeon let.dom])]~
+ [%bleb ver let.dom ?:(track.rov ~ `[u.from-aeon let.dom])]~
:: we're past the end of the range, so end subscription
::
:- ~
@@ -3103,7 +3230,7 @@
=/ bleb=(list sub-result)
?: =(lobes.rov new-lobes)
~
- [%bleb +(u.from-aeon) ?:(track.rov ~ `[u.from-aeon u.to-aeon])]~
+ [%bleb ver +(u.from-aeon) ?:(track.rov ~ `[u.from-aeon u.to-aeon])]~
:: end subscription
::
=/ blub=(list sub-result)
@@ -3111,17 +3238,6 @@
(weld bleb blub)
==
::
- ++ drop-me
- ^+ .
- ~| %clay-drop-me-not-implemented
- !!
- :: ?~ mer
- :: .
- :: %- emit(mer ~) ^- move :*
- :: hen.u.mer %give %mere %| %user-interrupt
- :: >sor.u.mer< >our< >cas.u.mer< >gem.u.mer< ~
- :: ==
- ::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: This core has no additional state, and the distinction exists purely for
@@ -3209,7 +3325,7 @@
:: Creates a nako of all the changes between a and b.
::
++ make-nako
- |= {a/aeon b/aeon}
+ |= [ver=@ud a=aeon b=aeon]
^- nako
:+ ?> (lte b let.dom)
|-
@@ -3219,7 +3335,7 @@
b
?: =(0 b)
[~ ~]
- (data-twixt-takos (~(get by hit.dom) a) (aeon-to-tako b))
+ (data-twixt-takos =(0 ver) (~(get by hit.dom) a) (aeon-to-tako b))
::
:: Traverse parentage and find all ancestor hashes
::
@@ -3245,16 +3361,21 @@
:: ones we found before `a`. Then convert the takos to yakis and also get
:: all the data in all the yakis.
::
+ :: What happens if you run an %init merge on a desk that already
+ :: had a commit?
+ ::
++ data-twixt-takos
- |= {a/(unit tako) b/tako}
- ^- {(set yaki) (set plop)}
+ |= [plops=? a=(unit tako) b=tako]
+ ^- [(set yaki) (set plop)]
=+ old=?~(a ~ (reachable-takos u.a))
- =/ yal/(set tako)
+ =/ yal=(set tako)
%- silt
%+ skip
~(tap in (reachable-takos b))
- |=(tak/tako (~(has in old) tak))
+ |=(tak=tako (~(has in old) tak))
:- (silt (turn ~(tap in yal) tako-to-yaki))
+ ?. plops
+ ~
(silt (turn ~(tap in (new-lobes (new-lobes ~ old) yal)) lobe-to-blob))
::
:: Get all the lobes that are referenced in `a` except those that are
@@ -3728,7 +3849,7 @@
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
=| :: instrument state
- $: ver=%3 :: vane version
+ $: ver=%4 :: vane version
ruf=raft :: revision tree
== ::
|= [our=ship now=@da eny=@uvJ ski=sley] :: current invocation
@@ -3939,7 +4060,14 @@
=^ for req
?: ?=(%warp -.req)
[~ req]
- :- ?:(=(our who.req) ~ `who.req)
+ :: ?: =(our who.req)
+ :: [~ [%warp wer.req rif.req]]
+ =^ ver rif.req
+ ?@ -.rif.req
+ [%0 rif.req]
+ [-<.rif.req +.rif.req]
+ ?> ?=(@ -.rif.req)
+ :- ?:(=(our who.req) ~ `[who.req ver])
[%warp wer.req rif.req]
::
?> ?=(%warp -.req)
@@ -3957,8 +4085,14 @@
=* pax path.plea.req
=* res payload.plea.req
::
- ?> ?=({%question *} pax)
- =+ ryf=;;(riff res)
+ ?: ?=([%backfill *] pax)
+ =+ ;;(=fill res)
+ =^ mos ruf
+ =/ den ((de our now ski hen ruf) our desk.fill)
+ abet:(give-backfill:den +.fill)
+ [[[hen %give %done ~] mos] ..^$]
+ ?> ?=([%question *] pax)
+ =+ ryf=;;(riff-any res)
:_ ..^$
:~ [hen %give %done ~]
=/ =wire
@@ -3971,11 +4105,58 @@
!:
|^
|= old=any-state
- ~! [old=old new=*state-3]
+ ~! [old=old new=*state-4]
=? old ?=(%2 -.old) (load-2-to-3 old)
- ?> ?=(%3 -.old)
+ =? old ?=(%3 -.old) (load-3-to-4 old)
+ ?> ?=(%4 -.old)
..^^$(ruf +.old)
::
+ ++ load-3-to-4
+ |= =state-3
+ ^- state-4
+ |^
+ =- state-3(- %4, hoy hoy.-, rom (room-3-to-4 rom.state-3))
+ ^- hoy=(map ship rung)
+ %- ~(run by hoy.state-3)
+ |= =rung-3
+ ^- rung
+ %- ~(run by rus.rung-3)
+ |= =rede-3
+ ^- rede
+ =- rede-3(ref ref.-, qyx (cult-3-to-4 qyx.rede-3))
+ ^- ref=(unit rind)
+ ?~ ref.rede-3
+ ~
+ =- `u.ref.rede-3(bom bom.-)
+ ^- bom=(map @ud update-state)
+ %- ~(run by bom.u.ref.rede-3)
+ |= [=duct =rave]
+ ^- update-state
+ [duct rave ~ ~ ~ |]
+ ::
+ ++ room-3-to-4
+ |= =room-3
+ ^- room
+ =- room-3(dos dos.-)
+ ^- dos=(map desk dojo)
+ %- ~(run by dos.room-3)
+ |= =dojo-3
+ ^- dojo
+ dojo-3(qyx (cult-3-to-4 qyx.dojo-3))
+ ::
+ ++ cult-3-to-4
+ |= =cult-3
+ ^- cult
+ %- malt
+ %+ turn ~(tap by cult-3)
+ |= [=wove-3 ducts=(set duct)]
+ ^- [wove (set duct)]
+ :_ ducts :_ rove.wove-3
+ ?~ for.wove-3
+ ~
+ `[u.for.wove-3 %0]
+ --
+ ::
++ load-2-to-3
|= =state-2
^- state-3
@@ -4005,11 +4186,11 @@
:- %ford-fusion
[leaf+"queued merge canceled due to upgrade to ford fusion" ~]
`[duct %slip %b %drip !>([%mere %| err])]
- ^- rom=room
+ ^- rom=room-3
:- hun.rom.state-2
%- ~(urn by dos.rom.state-2)
|= [=desk =dojo-2]
- ^- dojo
+ ^- dojo-3
=- dojo-2(dom -)
^- dome
=/ fer=(unit reef-cache)
@@ -4019,23 +4200,22 @@
(~(got by hut.ran.state-2) (~(got by hit.dom.dojo-2) let.dom.dojo-2))
`(build-reef desk q.yaki)
[ank let hit lab mim fod=*ford-cache fer=fer]:[dom.dojo-2 .]
- ^- hoy=(map ship rung)
+ ^- hoy=(map ship rung-3)
%- ~(run by hoy.state-2)
|= =rung-2
- ^- rung
+ ^- rung-3
%- ~(run by rus.rung-2)
|= =rede-2
- ^- rede
+ ^- rede-3
=- rede-2(ref ref.-, dom dom.-)
:- ^- dom=dome
[ank let hit lab mim fod=*ford-cache fer=~]:[dom.rede-2 .]
- ^- ref=(unit rind)
+ ^- ref=(unit rind-3)
?~ ref.rede-2
~
- :: TODO: somehow call +wake later to notify subscribers
:- ~
- ^- rind
- =/ rin=rind [nix bom fod haw]:u.ref.rede-2
+ ^- rind-3
+ =/ rin=rind-3 [nix bom fod haw]:u.ref.rede-2
=. rin
=/ pur=(list [inx=@ud =rand *]) ~(tap by pur.u.ref.rede-2)
|- ^+ rin
@@ -4138,8 +4318,46 @@
--
--
::
- +$ any-state $%(state-3 state-2)
- +$ state-3 [%3 raft]
+ +$ any-state $%(state-4 state-3 state-2)
+ +$ state-4 [%4 raft]
+ +$ state-3
+ $: %3
+ rom=room-3
+ hoy=(map ship rung-3)
+ ran=rang
+ mon=(map term beam)
+ hez=(unit duct)
+ cez=(map @ta crew)
+ pud=(unit [=desk =yoki])
+ pun=(list move)
+ ==
+ +$ rung-3 rus=(map desk rede-3)
+ +$ rede-3
+ $: lim/@da
+ ref/(unit rind-3)
+ qyx/cult-3
+ dom/dome
+ per/regs
+ pew/regs
+ ==
+ +$ rind-3
+ $: nix/@ud
+ bom/(map @ud {p/duct q/rave})
+ fod/(map duct @ud)
+ haw/(map mood (unit cage))
+ ==
+ +$ room-3
+ $: hun/duct
+ dos/(map desk dojo-3)
+ ==
+ ++ dojo-3
+ $: qyx/cult-3
+ dom/dome
+ per/regs
+ pew/regs
+ ==
+ +$ cult-3 (jug wove-3 duct)
+ +$ wove-3 [for=(unit ship) =rove]
+$ state-2
$: %2
rom=room-2 :: domestic
@@ -4156,7 +4374,7 @@
dos/(map desk dojo-2) :: native desk
== ::
+$ dojo-2
- $: qyx/cult :: subscribers
+ $: qyx/cult-3 :: subscribers
dom/dome-2 :: desk state
per/regs :: read perms per path
pew/regs :: write perms per path
@@ -4172,7 +4390,7 @@
+$ rede-2
$: lim/@da :: complete to
ref/(unit rind-2) :: outgoing requests
- qyx/cult :: subscribers
+ qyx/cult-3 :: subscribers
dom/dome-2 :: revision state
per/regs :: read perms per path
pew/regs :: write perms per path
@@ -4303,6 +4521,35 @@
[mos ..^$]
==
::
+ ?: ?=([%back-index @ @ @ ~] tea)
+ ?+ +<.q.hin ~| %clay-backfill-index-strange !!
+ %done
+ ?~ error.q.hin
+ [~ ..^$]
+ :: TODO better error handling
+ ::
+ ~& %clay-take-backfill-index-error^our^tea^tag.u.error.q.hin
+ %- (slog tang.u.error.q.hin)
+ [~ ..^$]
+ ::
+ %lost
+ ~| %clay-take-backfill-lost^our
+ :: TODO better error handling
+ !!
+ ::
+ %boon
+ =+ ;; =blob payload.q.hin
+ ::
+ =/ her=ship (slav %p i.t.tea)
+ =/ =desk (slav %tas i.t.t.tea)
+ =/ index=@ud (slav %ud i.t.t.t.tea)
+ ::
+ =^ mos ruf
+ =/ den ((de our now ski hen ruf) her desk)
+ abet:abet:(take-backfill:(foreign-update:den index) blob)
+ [mos ..^$]
+ ==
+ ::
?: ?=([%sinks ~] tea)
?> ?=(%public-keys +<.q.hin)
?. ?=(%breach -.public-keys-result.q.hin)
@@ -4396,7 +4643,9 @@
:+ desk %|
:~ ankh+&+ank.dom.dojo
mime+&+mim.dom.dojo
- ford+&+fod.dom.dojo
+ ford-vases+&+vases.fod.dom.dojo
+ ford-marks+&+marks.fod.dom.dojo
+ ford-casts+&+casts.fod.dom.dojo
==
:~ domestic+|+domestic
foreign+&+hoy.ruf
diff --git a/pkg/arvo/sys/zuse.hoon b/pkg/arvo/sys/zuse.hoon
index e1b3998a0..74a2f7990 100644
--- a/pkg/arvo/sys/zuse.hoon
+++ b/pkg/arvo/sys/zuse.hoon
@@ -861,7 +861,7 @@
$>(%trim vane-task) :: trim state
$>(%vega vane-task) :: report upgrade
{$warp wer/ship rif/riff} :: internal file req
- {$werp who/ship wer/ship rif/riff} :: external file req
+ {$werp who/ship wer/ship rif/riff-any} :: external file req
$>(%plea vane-task) :: ames request
== ::
-- ::able
@@ -967,7 +967,10 @@
who/(pair (set ship) (map @ta crew)) ::
== ::
++ regs (map path rule) :: rules for paths
- ++ riff {p/desk q/(unit rave)} :: request+desist
+ +$ riff [p=desk q=(unit rave)] :: request+desist
+ +$ riff-any
+ $^ [[%1 ~] riff]
+ riff
++ rite :: new permissions
$% {$r red/(unit rule)} :: for read
{$w wit/(unit rule)} :: for write
diff --git a/pkg/arvo/ted/ph/breach-multiple.hoon b/pkg/arvo/ted/ph/breach-multiple.hoon
index 4c76bc8a6..d486f9d43 100644
--- a/pkg/arvo/ted/ph/breach-multiple.hoon
+++ b/pkg/arvo/ted/ph/breach-multiple.hoon
@@ -11,14 +11,14 @@
;< ~ bind:m (spawn az ~marbud)
;< ~ bind:m (real-ship az ~bud)
;< ~ bind:m (real-ship az ~marbud)
-;< file=@t bind:m (touch-file ~bud %base %foo)
+;< file=@t bind:m (touch-file ~bud %kids %foo)
;< ~ bind:m (check-file-touched ~marbud %home file)
;< ~ bind:m (breach-and-hear az ~bud ~marbud)
;< ~ bind:m (real-ship az ~bud)
;< ~ bind:m (breach-and-hear az ~marbud ~bud)
;< ~ bind:m (real-ship az ~marbud)
-;< file=@t bind:m (touch-file ~bud %base %bar)
-;< file=@t bind:m (touch-file ~bud %base %baz)
+;< file=@t bind:m (touch-file ~bud %kids %bar)
+;< file=@t bind:m (touch-file ~bud %kids %baz)
;< ~ bind:m (check-file-touched ~marbud %home file)
;< ~ bind:m end-azimuth
(pure:m *vase)
diff --git a/pkg/arvo/ted/ph/breach-sudden.hoon b/pkg/arvo/ted/ph/breach-sudden.hoon
index ae6af62dd..5491c84c4 100644
--- a/pkg/arvo/ted/ph/breach-sudden.hoon
+++ b/pkg/arvo/ted/ph/breach-sudden.hoon
@@ -13,13 +13,13 @@
;< ~ bind:m (spawn az ~marbud)
;< ~ bind:m (real-ship az ~bud)
;< ~ bind:m (real-ship az ~marbud)
-;< file=@t bind:m (touch-file ~bud %base %foo)
+;< file=@t bind:m (touch-file ~bud %kids %foo)
;< ~ bind:m (check-file-touched ~marbud %home file)
;< ~ bind:m (breach az ~bud)
;< ~ bind:m (real-ship az ~bud)
-;< ~ bind:m (dojo ~bud "|merge %base ~marbud %kids, =gem %this")
-;< file=@t bind:m (touch-file ~bud %base %bar)
-;< file=@t bind:m (touch-file ~bud %base %baz)
+;< ~ bind:m (dojo ~bud "|merge %home ~marbud %kids, =gem %this")
+;< file=@t bind:m (touch-file ~bud %kids %bar)
+;< file=@t bind:m (touch-file ~bud %kids %baz)
;< ~ bind:m (check-file-touched ~marbud %home file)
;< ~ bind:m end-azimuth
(pure:m *vase)
diff --git a/pkg/arvo/ted/ph/breach-sync.hoon b/pkg/arvo/ted/ph/breach-sync.hoon
index 833ececd1..a603c779f 100644
--- a/pkg/arvo/ted/ph/breach-sync.hoon
+++ b/pkg/arvo/ted/ph/breach-sync.hoon
@@ -13,6 +13,10 @@
;< ~ bind:m (real-ship az ~marbud)
;< file=@t bind:m (touch-file ~bud %kids %foo)
;< ~ bind:m (check-file-touched ~marbud %home file)
+:: Merge so that when we unify history with the %this merge later, we
+:: don't get a spurious conflict in %home
+::
+;< ~ bind:m (dojo ~marbud "|merge %kids our %home")
;< ~ bind:m (breach-and-hear az ~bud ~marbud)
;< ~ bind:m (real-ship az ~bud)
;< ~ bind:m (dojo ~bud "|merge %kids ~marbud %kids, =gem %this")
diff --git a/pkg/arvo/ted/ph/child-sync.hoon b/pkg/arvo/ted/ph/child-sync.hoon
index 291ce598e..381a67064 100644
--- a/pkg/arvo/ted/ph/child-sync.hoon
+++ b/pkg/arvo/ted/ph/child-sync.hoon
@@ -7,7 +7,8 @@
;< ~ bind:m start-simple
;< ~ bind:m (raw-ship ~bud ~)
;< ~ bind:m (raw-ship ~marbud ~)
-;< file=@t bind:m (touch-file ~bud %base %foo)
+;< file=@t bind:m (touch-file ~bud %home %foo)
+;< ~ bind:m (dojo ~bud "|merge %kids our %home")
;< ~ bind:m (check-file-touched ~marbud %home file)
;< ~ bind:m end-simple
(pure:m *vase)
diff --git a/pkg/arvo/ted/ph/child-update.hoon b/pkg/arvo/ted/ph/child-update.hoon
index fc0878ad2..d505e74ea 100644
--- a/pkg/arvo/ted/ph/child-update.hoon
+++ b/pkg/arvo/ted/ph/child-update.hoon
@@ -29,15 +29,15 @@
%^ cat 3 (get-val /mar/js/hoon)
' ~& > new-val=new-val .'
=/ js-contents
- %^ cat 3 (get-val /app/publish/js/index/js)
+ %^ cat 3 (get-val /app/landscape/js/channel/js)
'extra'
=/ files
:~ [/sys/zuse/hoon zuse-contents]
[/mar/js/hoon mar-contents]
- [/app/publish/js/index/js js-contents]
+ [/app/landscape/js/channel/js js-contents]
==
;< ~ bind:m (send-events (insert-files:util her desk files))
- (pure:m /app/publish/js/index/js js-contents)
+ (pure:m /app/landscape/js/channel/js js-contents)
::
++ aqua-path
|= =path
diff --git a/pkg/interface/package-lock.json b/pkg/interface/package-lock.json
index c4c1df3b4..27a526c50 100644
--- a/pkg/interface/package-lock.json
+++ b/pkg/interface/package-lock.json
@@ -1393,6 +1393,42 @@
"tslib": "^1.11.1"
}
},
+ "@reach/disclosure": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@reach/disclosure/-/disclosure-0.10.5.tgz",
+ "integrity": "sha512-DCae28vcL7wXJNt8hySI2uaowEJ6KPDJ9U14xQMkMs0/lH7Tz8PoAO3llf7csEXk/4kzjnDpkyobDiEV3pz05g==",
+ "requires": {
+ "@reach/auto-id": "0.10.5",
+ "@reach/utils": "0.10.5",
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "@reach/auto-id": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@reach/auto-id/-/auto-id-0.10.5.tgz",
+ "integrity": "sha512-we4/bwjFxJ3F+2eaddQ1HltbKvJ7AB8clkN719El7Zugpn/vOjfPMOVUiBqTmPGLUvkYrq4tpuFwLvk2HyOVHg==",
+ "requires": {
+ "@reach/utils": "0.10.5",
+ "tslib": "^2.0.0"
+ }
+ },
+ "@reach/utils": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.10.5.tgz",
+ "integrity": "sha512-5E/xxQnUbmpI/LrufBAOXjunl96DnqX6B4zC2MO2KH/dRzLug5gM5VuOwV26egsp0jvsSPxojwciOhS43px3qw==",
+ "requires": {
+ "@types/warning": "^3.0.0",
+ "tslib": "^2.0.0",
+ "warning": "^4.0.3"
+ }
+ },
+ "tslib": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz",
+ "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g=="
+ }
+ }
+ },
"@reach/menu-button": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@reach/menu-button/-/menu-button-0.10.1.tgz",
@@ -1443,6 +1479,53 @@
"tslib": "^1.11.1"
}
},
+ "@reach/tabs": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@reach/tabs/-/tabs-0.10.5.tgz",
+ "integrity": "sha512-oQJxQ9FwFsXo2HxEzJxFU/wP31bPVh4VU54NlhHW9f49uofyYkIKBbAhdF0Zb3TnaFp4cGKPHX39pXBYGPDkAQ==",
+ "requires": {
+ "@reach/auto-id": "0.10.5",
+ "@reach/descendants": "0.10.5",
+ "@reach/utils": "0.10.5",
+ "prop-types": "^15.7.2",
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "@reach/auto-id": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@reach/auto-id/-/auto-id-0.10.5.tgz",
+ "integrity": "sha512-we4/bwjFxJ3F+2eaddQ1HltbKvJ7AB8clkN719El7Zugpn/vOjfPMOVUiBqTmPGLUvkYrq4tpuFwLvk2HyOVHg==",
+ "requires": {
+ "@reach/utils": "0.10.5",
+ "tslib": "^2.0.0"
+ }
+ },
+ "@reach/descendants": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@reach/descendants/-/descendants-0.10.5.tgz",
+ "integrity": "sha512-8HhN4DwS/HsPQ+Ym/Ft/XJ1spXBYdE8hqpnbYR9UcU7Nx3oDbTIdhjA6JXXt23t5avYIx2jRa8YHCtVKSHuiwA==",
+ "requires": {
+ "@reach/utils": "0.10.5",
+ "tslib": "^2.0.0"
+ }
+ },
+ "@reach/utils": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.10.5.tgz",
+ "integrity": "sha512-5E/xxQnUbmpI/LrufBAOXjunl96DnqX6B4zC2MO2KH/dRzLug5gM5VuOwV26egsp0jvsSPxojwciOhS43px3qw==",
+ "requires": {
+ "@types/warning": "^3.0.0",
+ "tslib": "^2.0.0",
+ "warning": "^4.0.3"
+ }
+ },
+ "tslib": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz",
+ "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g=="
+ }
+ }
+ },
"@reach/utils": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.10.1.tgz",
@@ -1555,15 +1638,15 @@
"@styled-system/css": "^5.1.5"
}
},
+ "@tlon/indigo-light": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tlon/indigo-light/-/indigo-light-1.0.3.tgz",
+ "integrity": "sha512-3OPSdf9cejP/TSzWXuBaYbzLtAfBzQnc75SlPLkoPfwpxnv1Bvy9hiWngLY0WnKRR6lMOldnkYQCCuNWeDibYQ=="
+ },
"@tlon/indigo-react": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.1.12.tgz",
- "integrity": "sha512-XBJjHwaslEwZA2r09qnoh84BeVLnd/jwZRkhq71KNABnRD+QRtg/dYNvtswueML4Km89Vx9QBtCIEIeujzrblw==",
- "requires": {
- "@reach/menu-button": "^0.10.0",
- "@styled-system/css": "^5.1.5",
- "@types/styled-system__css": "^5.0.5"
- }
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.1.15.tgz",
+ "integrity": "sha512-Ao+1hAJjN5y1gDyT7GIUgXORPXTIpZKVVtrS++ZGYBemYMSq3oJFMIZertsSZbDHuh/TsVPenJrMUZBpV60law=="
},
"@types/anymatch": {
"version": "1.3.1",
@@ -1667,14 +1750,6 @@
"integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
"dev": true
},
- "@types/styled-system__css": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/@types/styled-system__css/-/styled-system__css-5.0.8.tgz",
- "integrity": "sha512-skv+daDje8vWQ8wnqVV0GCzgWVKx4gI9lJpAxWE77s52Ne6k/SCPP8HGE4BFbWDvK+qi5O3p89BGWVOQ1VHjMg==",
- "requires": {
- "csstype": "^2.6.6"
- }
- },
"@types/tapable": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz",
@@ -3328,7 +3403,8 @@
"csstype": {
"version": "2.6.10",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz",
- "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w=="
+ "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==",
+ "dev": true
},
"cyclist": {
"version": "1.0.1",
@@ -5949,6 +6025,15 @@
"resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz",
"integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg=="
},
+ "markdown-to-jsx": {
+ "version": "6.11.4",
+ "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz",
+ "integrity": "sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw==",
+ "requires": {
+ "prop-types": "^15.6.2",
+ "unquote": "^1.1.0"
+ }
+ },
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -5985,6 +6070,11 @@
"p-is-promise": "^2.0.0"
}
},
+ "memoize-one": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
+ "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
+ },
"memory-fs": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@@ -7491,6 +7581,15 @@
"tiny-warning": "^1.0.0"
}
},
+ "react-window": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.5.tgz",
+ "integrity": "sha512-HeTwlNa37AFa8MDZFZOKcNEkuF2YflA0hpGPiTT9vR7OawEt+GZbfM6wqkBahD3D3pUjIabQYzsnY/BSJbgq6Q==",
+ "requires": {
+ "@babel/runtime": "^7.0.0",
+ "memoize-one": ">=3.1.1 <6"
+ }
+ },
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -9257,6 +9356,11 @@
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
"dev": true
},
+ "unquote": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
+ "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ="
+ },
"unset-value": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-0.1.2.tgz",
@@ -9565,7 +9669,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"aproba": {
"version": "1.2.0",
@@ -9586,12 +9691,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -9606,17 +9713,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -9733,7 +9843,8 @@
"inherits": {
"version": "2.0.4",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"ini": {
"version": "1.3.5",
@@ -9745,6 +9856,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -9759,6 +9871,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -9766,12 +9879,14 @@
"minimist": {
"version": "1.2.5",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"minipass": {
"version": "2.9.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -9790,6 +9905,7 @@
"version": "0.5.3",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"minimist": "^1.2.5"
}
@@ -9851,7 +9967,8 @@
"npm-normalize-package-bin": {
"version": "1.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"npm-packlist": {
"version": "1.4.8",
@@ -9879,7 +9996,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -9891,6 +10009,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"wrappy": "1"
}
@@ -9968,7 +10087,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -10004,6 +10124,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -10023,6 +10144,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -10066,12 +10188,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"yallist": {
"version": "3.1.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
}
}
},
@@ -10552,7 +10676,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"aproba": {
"version": "1.2.0",
@@ -10573,12 +10698,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -10593,17 +10720,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -10720,7 +10850,8 @@
"inherits": {
"version": "2.0.4",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"ini": {
"version": "1.3.5",
@@ -10732,6 +10863,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -10746,6 +10878,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -10753,12 +10886,14 @@
"minimist": {
"version": "1.2.5",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"minipass": {
"version": "2.9.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -10777,6 +10912,7 @@
"version": "0.5.3",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"minimist": "^1.2.5"
}
@@ -10838,7 +10974,8 @@
"npm-normalize-package-bin": {
"version": "1.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"npm-packlist": {
"version": "1.4.8",
@@ -10866,7 +11003,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -10878,6 +11016,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"wrappy": "1"
}
@@ -10955,7 +11094,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -10991,6 +11131,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -11010,6 +11151,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -11053,12 +11195,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"yallist": {
"version": "3.1.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
}
}
},
diff --git a/pkg/interface/package.json b/pkg/interface/package.json
index 2aef19ded..1e21e1322 100644
--- a/pkg/interface/package.json
+++ b/pkg/interface/package.json
@@ -5,13 +5,17 @@
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.10.5",
+ "@reach/disclosure": "^0.10.5",
"@reach/menu-button": "^0.10.1",
- "@tlon/indigo-react": "^1.1.10",
+ "@reach/tabs": "^0.10.5",
+ "@tlon/indigo-light": "^1.0.3",
+ "@tlon/indigo-react": "^1.1.15",
"classnames": "^2.2.6",
"codemirror": "^5.51.0",
"css-loader": "^3.5.3",
"formik": "^2.1.4",
"lodash": "^4.17.15",
+ "markdown-to-jsx": "^6.11.4",
"moment": "^2.20.1",
"mousetrap": "^1.6.5",
"prop-types": "^15.7.2",
@@ -20,6 +24,7 @@
"react-dom": "^16.8.6",
"react-markdown": "^4.3.1",
"react-router-dom": "^5.0.0",
+ "react-window": "^1.8.5",
"remark-disable-tokenizers": "^1.0.24",
"style-loader": "^1.2.1",
"styled-components": "^5.1.0",
diff --git a/pkg/interface/src/App.js b/pkg/interface/src/App.js
index 5d057f9cb..94dc9d076 100644
--- a/pkg/interface/src/App.js
+++ b/pkg/interface/src/App.js
@@ -6,7 +6,8 @@ import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
import './css/indigo-static.css';
import './css/fonts.css';
-import { light, dark, inverted, paperDark } from '@tlon/indigo-react';
+import light from './themes/light';
+import dark from './themes/old-dark';
import LaunchApp from './apps/launch/app';
import ChatApp from './apps/chat/app';
@@ -16,7 +17,7 @@ import LinksApp from './apps/links/app';
import PublishApp from './apps/publish/app';
import StatusBar from './components/StatusBar';
-import NotFound from './components/404';
+import ErrorComponent from './components/Error';
import GlobalStore from './store/store';
import GlobalSubscription from './subscription/global';
@@ -85,7 +86,7 @@ class App extends React.Component {
const associations = this.state.associations ? this.state.associations : { contacts: {} };
const selectedGroups = this.state.selectedGroups ? this.state.selectedGroups : [];
const { state } = this;
- const theme = state.dark ? paperDark : light;
+ const theme = state.dark ? dark : light;
return (
@@ -161,7 +162,11 @@ class App extends React.Component {
/>
)}
/>
-
+ (
+
+ )}
+ />
diff --git a/pkg/interface/src/apps/chat/components/chat.tsx b/pkg/interface/src/apps/chat/components/chat.tsx
index 05ed6923c..a8a94e62b 100644
--- a/pkg/interface/src/apps/chat/components/chat.tsx
+++ b/pkg/interface/src/apps/chat/components/chat.tsx
@@ -94,6 +94,7 @@ interface ChatScreenState {
scrollLocked: boolean;
read: number;
active: boolean;
+ messages: Map;
lastScrollHeight: number | null;
}
@@ -118,6 +119,7 @@ export class ChatScreen extends Component {
scrollLocked: false,
read: props.read,
active: true,
+ messages: new Map(),
// only for FF
lastScrollHeight: null,
};
@@ -594,8 +596,12 @@ export class ChatScreen extends Component {
envelopes={props.envelopes}
contacts={props.contacts}
onEnter={() => this.setState({ scrollLocked: false })}
+ onUnmount={(msg: string) => this.setState({
+ messages: this.state.messages.set(props.station, msg)
+ })}
s3={props.s3}
placeholder="Message..."
+ message={this.state.messages.get(props.station) || ""}
/>
);
diff --git a/pkg/interface/src/apps/chat/components/lib/chat-input.js b/pkg/interface/src/apps/chat/components/lib/chat-input.js
index d84a8c6e6..b507d1114 100644
--- a/pkg/interface/src/apps/chat/components/lib/chat-input.js
+++ b/pkg/interface/src/apps/chat/components/lib/chat-input.js
@@ -40,7 +40,7 @@ export class ChatInput extends Component {
super(props);
this.state = {
- message: '',
+ message: props.message,
patpSearch: null
};
@@ -57,24 +57,6 @@ export class ChatInput extends Component {
this.editor = null;
- // perf testing:
- /* let closure = () => {
- let x = 0;
- for (var i = 0; i < 30; i++) {
- x++;
- props.api.chat.message(
- props.station,
- `~${window.ship}`,
- Date.now(),
- {
- text: `${x}`
- }
- );
- }
- setTimeout(closure, 1000);
- };
- this.closure = closure.bind(this);*/
-
moment.updateLocale('en', {
relativeTime : {
past: function(input) {
@@ -99,6 +81,10 @@ export class ChatInput extends Component {
});
}
+ componentWillUnmount() {
+ this.props.onUnmount(this.state.message);
+ }
+
nextAutocompleteSuggestion(backward = false) {
const { patpSuggestions } = this.state;
let idx = patpSuggestions.findIndex(s => s === this.state.selectedSuggestion);
@@ -150,6 +136,9 @@ export class ChatInput extends Component {
if(patpSearch !== null) {
this.patpAutocomplete(value, false);
}
+ this.setState({
+ message: value
+ });
}
getLetterType(letter) {
@@ -209,29 +198,60 @@ export class ChatInput extends Component {
return;
}
let message = [];
- editorMessage.split(' ').map((each) => {
- if (this.isUrl(each)) {
- if (message.length > 0) {
- message = message.join(' ');
- message = this.getLetterType(message);
- props.api.chat.message(
- props.station,
- `~${window.ship}`,
- Date.now(),
- message
- );
- message = [];
- }
- const URL = this.getLetterType(each);
- props.api.chat.message(
- props.station,
- `~${window.ship}`,
- Date.now(),
- URL
- );
+ let isInCodeBlock = false;
+ let endOfCodeBlock = false;
+ editorMessage.split(/\r?\n/).forEach((line) => {
+ // A line of backticks enters and exits a codeblock
+ if (line.startsWith('```')) {
+ // But we need to check if we've ended a codeblock
+ endOfCodeBlock = isInCodeBlock;
+ isInCodeBlock = (!isInCodeBlock);
} else {
- return message.push(each);
+ endOfCodeBlock = false;
}
+ if (isInCodeBlock) {
+ message.push(`\n${line}`);
+ } else if (endOfCodeBlock) {
+ message.push(`\n${line}\n`);
+ } else {
+ line.split(/\s/).forEach((str) => {
+ if (
+ (str.startsWith('`') && str !== '`')
+ || (str === '`' && !isInCodeBlock)
+ ) {
+ isInCodeBlock = true;
+ } else if (
+ (str.endsWith('`') && str !== '`')
+ || (str === '`' && isInCodeBlock)
+ ) {
+ isInCodeBlock = false;
+ }
+ if (this.isUrl(str) && !isInCodeBlock) {
+ if (message.length > 0) {
+ message = message.join(' ');
+ message = this.getLetterType(message);
+ props.api.chat.message(
+ props.station,
+ `~${window.ship}`,
+ Date.now(),
+ message
+ );
+ message = [];
+ }
+ const URL = this.getLetterType(str);
+ props.api.chat.message(
+ props.station,
+ `~${window.ship}`,
+ Date.now(),
+ URL
+ );
+ } else {
+ message.push(str);
+ }
+ });
+
+ }
+
});
if (message.length > 0) {
@@ -246,8 +266,24 @@ export class ChatInput extends Component {
message = [];
}
- // perf:
- // setTimeout(this.closure, 2000);
+ // perf testing:
+ /*let closure = () => {
+ let x = 0;
+ for (var i = 0; i < 30; i++) {
+ x++;
+ props.api.chat.message(
+ props.station,
+ `~${window.ship}`,
+ Date.now(),
+ {
+ text: `${x}`
+ }
+ );
+ }
+ setTimeout(closure, 1000);
+ };
+ this.closure = closure.bind(this);
+ setTimeout(this.closure, 2000);*/
this.editor.setValue('');
}
@@ -364,6 +400,7 @@ export class ChatInput extends Component {
style={{ flexGrow: 1, maxHeight: '224px', width: 'calc(100% - 72px)' }}
>
{
this.editor = editor;
diff --git a/pkg/interface/src/apps/chat/components/lib/content/code.js b/pkg/interface/src/apps/chat/components/lib/content/code.js
new file mode 100644
index 000000000..99ca099d8
--- /dev/null
+++ b/pkg/interface/src/apps/chat/components/lib/content/code.js
@@ -0,0 +1,28 @@
+import React, { Component } from 'react';
+
+
+export default class CodeContent extends Component {
+
+ render() {
+ const { props } = this;
+ const content = props.content;
+
+ const outputElement =
+ (Boolean(content.code.output) &&
+ content.code.output.length && content.code.output.length > 0) ?
+ (
+
+ {content.code.output[0].join('\n')}
+
+ ) : null;
+
+ return (
+
+
+ {content.code.expression}
+
+ {outputElement}
+
+ );
+ }
+}
diff --git a/pkg/interface/src/apps/chat/components/lib/content/text.js b/pkg/interface/src/apps/chat/components/lib/content/text.js
new file mode 100644
index 000000000..ec1153b8d
--- /dev/null
+++ b/pkg/interface/src/apps/chat/components/lib/content/text.js
@@ -0,0 +1,65 @@
+import React, { Component } from 'react';
+import ReactMarkdown from 'react-markdown';
+import RemarkDisableTokenizers from 'remark-disable-tokenizers';
+import urbitOb from 'urbit-ob';
+import { Link } from 'react-router-dom';
+
+const DISABLED_BLOCK_TOKENS = [
+ 'indentedCode',
+ 'blockquote',
+ 'atxHeading',
+ 'thematicBreak',
+ 'list',
+ 'setextHeading',
+ 'html',
+ 'definition',
+ 'table'
+];
+
+const DISABLED_INLINE_TOKENS = [
+ 'autoLink',
+ 'url',
+ 'email',
+ 'link',
+ 'reference'
+];
+
+const MessageMarkdown = React.memo(props => (
+
+));
+
+
+export default class TextContent extends Component {
+
+ render() {
+ const { props } = this;
+ const content = props.content;
+
+ const group = content.text.match(
+ /([~][/])?(~[a-z]{3,6})(-[a-z]{6})?([/])(([a-z])+([/-])?)+/
+ );
+ if ((group !== null) // matched possible chatroom
+ && (group[2].length > 2) // possible ship?
+ && (urbitOb.isValidPatp(group[2]) // valid patp?
+ && (group[0] === content.text))) { // entire message is room name?
+ return (
+
+ {content.text}
+
+ );
+ } else {
+ return (
+
+ );
+ }
+ }
+}
diff --git a/pkg/interface/src/apps/chat/components/lib/content/url.js b/pkg/interface/src/apps/chat/components/lib/content/url.js
new file mode 100644
index 000000000..e1f59dd5f
--- /dev/null
+++ b/pkg/interface/src/apps/chat/components/lib/content/url.js
@@ -0,0 +1,101 @@
+import React, { Component } from 'react';
+
+
+const IMAGE_REGEX =
+ /(jpg|img|png|gif|tiff|jpeg|JPG|IMG|PNG|TIFF|GIF|webp|WEBP|webm|WEBM|svg|SVG)$/;
+
+const YOUTUBE_REGEX =
+ new RegExp(
+ String(/(?:https?:\/\/(?:[a-z]+.)?)/.source) // protocol
+ + /(?:youtu\.?be(?:\.com)?\/)(?:embed\/)?/.source // short and long-links
+ + /(?:(?:(?:(?:watch\?)?(?:time_continue=(?:[0-9]+))?.+v=)?([a-zA-Z0-9_-]+))(?:\?t\=(?:[0-9a-zA-Z]+))?)/.source // id
+ );
+
+export default class UrlContent extends Component {
+ constructor() {
+ super();
+ this.state = {
+ unfold: false,
+ copied: false
+ };
+ this.unfoldEmbed = this.unfoldEmbed.bind(this);
+ }
+
+ unfoldEmbed(id) {
+ let unfoldState = this.state.unfold;
+ unfoldState = !unfoldState;
+ this.setState({ unfold: unfoldState });
+ const iframe = this.refs.iframe;
+ iframe.setAttribute('src', iframe.getAttribute('data-src'));
+ }
+
+ render() {
+ const { props } = this;
+ const content = props.content;
+ const imgMatch = IMAGE_REGEX.exec(props.content.url);
+ const ytMatch = YOUTUBE_REGEX.exec(props.content.url);
+
+ let contents = content.url;
+ if (imgMatch) {
+ contents = (
+
+ );
+ return (
+
+ {contents}
+
+ );
+ } else if (ytMatch) {
+ contents = (
+
+
+
+ );
+ return (
+
+ );
+ } else {
+ return (
+
+ {contents}
+
+ );
+ }
+ }
+}
diff --git a/pkg/interface/src/apps/chat/components/lib/message-content.js b/pkg/interface/src/apps/chat/components/lib/message-content.js
new file mode 100644
index 000000000..865ab3af2
--- /dev/null
+++ b/pkg/interface/src/apps/chat/components/lib/message-content.js
@@ -0,0 +1,33 @@
+import React, { Component } from 'react';
+
+import TextContent from './content/text';
+import CodeContent from './content/code';
+import UrlContent from './content/url';
+
+
+export default class MessageContent extends Component {
+
+ render() {
+ const { props } = this;
+
+ const content = props.letter;
+
+ if ('code' in content) {
+ return ;
+ } else if ('url' in content) {
+ return ;
+ } else if ('me' in content) {
+ return (
+
+ {content.me}
+
+ );
+ }
+ else if ('text' in content) {
+ return ;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/pkg/interface/src/apps/chat/components/lib/message.js b/pkg/interface/src/apps/chat/components/lib/message.js
index 1c61a9eeb..898b8a8ee 100644
--- a/pkg/interface/src/apps/chat/components/lib/message.js
+++ b/pkg/interface/src/apps/chat/components/lib/message.js
@@ -1,275 +1,127 @@
import React, { Component } from 'react';
-import { Link } from 'react-router-dom';
import { OverlaySigil } from './overlay-sigil';
+import MessageContent from './message-content';
import { uxToHex, cite, writeText } from '../../../../lib/util';
import moment from 'moment';
-import ReactMarkdown from 'react-markdown';
-import RemarkDisableTokenizers from 'remark-disable-tokenizers';
-import urbitOb from 'urbit-ob';
-const DISABLED_BLOCK_TOKENS = [
- 'indentedCode',
- 'blockquote',
- 'atxHeading',
- 'thematicBreak',
- 'list',
- 'setextHeading',
- 'html',
- 'definition',
- 'table'
-];
-
-const DISABLED_INLINE_TOKENS = [
- 'autoLink',
- 'url',
- 'email',
- 'link',
- 'reference'
-];
-
-const MessageMarkdown = React.memo(
- props => ( ));
export class Message extends Component {
constructor() {
super();
this.state = {
- unfold: false,
copied: false
};
- this.unFoldEmbed = this.unFoldEmbed.bind(this);
}
- unFoldEmbed(id) {
- let unfoldState = this.state.unfold;
- unfoldState = !unfoldState;
- this.setState({ unfold: unfoldState });
- const iframe = this.refs.iframe;
- iframe.setAttribute('src', iframe.getAttribute('data-src'));
- }
-
- renderContent() {
- const { props } = this;
- const letter = props.msg.letter;
-
- if ('code' in letter) {
- const outputElement =
- (Boolean(letter.code.output) &&
- letter.code.output.length && letter.code.output.length > 0) ?
- (
-
- {letter.code.output[0].join('\n')}
-
- ) : null;
- return (
-
-
- {letter.code.expression}
-
- {outputElement}
-
- );
- } else if ('url' in letter) {
- const imgMatch =
- /(jpg|img|png|gif|tiff|jpeg|JPG|IMG|PNG|TIFF|GIF|webp|WEBP|svg|SVG)$/
- .exec(letter.url);
- const youTubeRegex = new RegExp(String(/(?:https?:\/\/(?:[a-z]+.)?)/.source) // protocol
- + /(?:youtu\.?be(?:\.com)?\/)(?:embed\/)?/.source // short and long-links
- + /(?:(?:(?:(?:watch\?)?(?:time_continue=(?:[0-9]+))?.+v=)?([a-zA-Z0-9_-]+))(?:\?t\=(?:[0-9a-zA-Z]+))?)/.source // id
- );
- const ytMatch =
- youTubeRegex.exec(letter.url);
- let contents = letter.url;
- if (imgMatch) {
- contents = (
-
- );
- return (
-
- {contents}
-
- );
- } else if (ytMatch) {
- contents = (
-
-
-
- );
- return (
-
- );
- } else {
- return (
-
- {contents}
-
- );
- }
- } else if ('me' in letter) {
- return (
-
- {letter.me}
-
- );
- } else {
- const group = letter.text.match(
- /([~][/])?(~[a-z]{3,6})(-[a-z]{6})?([/])(([a-z])+([/-])?)+/
- );
- if ((group !== null) // matched possible chatroom
- && (group[2].length > 2) // possible ship?
- && (urbitOb.isValidPatp(group[2]) // valid patp?
- && (group[0] === letter.text))) { // entire message is room name?
- return (
-
- {letter.text}
-
- );
- } else {
- return (
-
- );
- }
- }
-}
-
render() {
const { props, state } = this;
+
const pending = props.msg.pending ? ' o-40' : '';
- const datestamp = '~' + moment.unix(props.msg.when / 1000).format('YYYY.M.D');
+ const containerClass =
+ props.renderSigil ?
+ `w-100 f7 pl3 pt4 pr3 cf flex lh-copy ` + pending :
+ 'w-100 pr3 cf hide-child flex' + pending;
+ const timestamp =
+ moment.unix(props.msg.when / 1000).format(
+ props.renderSigil ? 'hh:mm a' : 'hh:mm'
+ );
+
+ return (
+
+ {
+ props.renderSigil ? (
+ this.renderWithSigil(timestamp)
+ ) : (
+ this.renderWithoutSigil(timestamp)
+ )
+ }
+
+ );
+ }
+
+ renderWithSigil(timestamp) {
+ const { props, state } = this;
const paddingTop = props.paddingTop ? { 'paddingTop': '6px' } : '';
+ const datestamp =
+ '~' + moment.unix(props.msg.when / 1000).format('YYYY.M.D');
- if (props.renderSigil) {
- const timestamp = moment.unix(props.msg.when / 1000).format('hh:mm a');
-
- const contact = props.msg.author in props.contacts
- ? props.contacts[props.msg.author] : false;
- let name = `~${props.msg.author}`;
- let color = '#000000';
- let sigilClass = 'mix-blend-diff';
- if (contact) {
- name = (contact.nickname.length > 0)
- ? contact.nickname : `~${props.msg.author}`;
- color = `#${uxToHex(contact.color)}`;
- sigilClass = '';
- }
-
- if (`~${props.msg.author}` === name) {
- name = cite(props.msg.author);
- }
-
- return (
-
-
-
-
-
- {
- writeText(props.msg.author);
- this.setState({ copied: true });
- setTimeout(() => {
- this.setState({ copied: false });
- }, 800);
- }}
- title={`~${props.msg.author}`}
- >
- {state.copied && 'Copied' || name}
-
-
-
{timestamp}
-
{datestamp}
-
- {this.renderContent()}
-
-
- );
- } else {
- const timestamp = moment.unix(props.msg.when / 1000).format('hh:mm');
-
- return (
-
-
{timestamp}
-
- {this.renderContent()}
-
-
- );
+ const contact = props.msg.author in props.contacts
+ ? props.contacts[props.msg.author] : false;
+ let name = `~${props.msg.author}`;
+ let color = '#000000';
+ let sigilClass = 'mix-blend-diff';
+ if (contact) {
+ name = (contact.nickname.length > 0)
+ ? contact.nickname : `~${props.msg.author}`;
+ color = `#${uxToHex(contact.color)}`;
+ sigilClass = '';
}
+
+ if (`~${props.msg.author}` === name) {
+ name = cite(props.msg.author);
+ }
+
+ return (
+
+
+
+
+
+ {
+ writeText(props.msg.author);
+ this.setState({ copied: true });
+ setTimeout(() => {
+ this.setState({ copied: false });
+ }, 800);
+ }}
+ title={`~${props.msg.author}`}
+ >
+ {state.copied && 'Copied' || name}
+
+
+
{timestamp}
+
+ {datestamp}
+
+
+
+
+
+ );
+ }
+
+ renderWithoutSigil(timestamp) {
+ const { props } = this;
+
+ return (
+
+ );
}
}
diff --git a/pkg/interface/src/apps/chat/components/lib/profile-overlay.js b/pkg/interface/src/apps/chat/components/lib/profile-overlay.js
index e679e9e38..b9a690bd7 100644
--- a/pkg/interface/src/apps/chat/components/lib/profile-overlay.js
+++ b/pkg/interface/src/apps/chat/components/lib/profile-overlay.js
@@ -46,7 +46,7 @@ export class ProfileOverlay extends Component {
if (!(top || bottom)) {
bottom = `-${Math.round(OVERLAY_HEIGHT / 2)}px`;
}
- const containerStyle = { top, bottom, left: '100%' };
+ const containerStyle = { top, bottom, left: '100%', maxWidth: '160px' };
const isOwn = window.ship === ship;
@@ -79,7 +79,7 @@ export class ProfileOverlay extends Component {
{contact && contact.nickname && (
-
{contact.nickname}
+
{contact.nickname}
)}
{cite(`~${ship}`)}
{!isOwn && (
diff --git a/pkg/interface/src/apps/chat/components/settings.js b/pkg/interface/src/apps/chat/components/settings.js
index 6109cff9b..5471d42e8 100644
--- a/pkg/interface/src/apps/chat/components/settings.js
+++ b/pkg/interface/src/apps/chat/components/settings.js
@@ -6,6 +6,7 @@ import { Spinner } from '../../../components/Spinner';
import { ChatTabBar } from './lib/chat-tabbar';
import { InviteSearch } from '../../../components/InviteSearch';
import SidebarSwitcher from '../../../components/SidebarSwitch';
+import Toggle from '../../../components/toggle';
export class SettingsScreen extends Component {
constructor(props) {
@@ -195,18 +196,12 @@ export class SettingsScreen extends Component {
} else {
let inclusiveToggle =
;
if (state.targetGroup) {
- // TODO toggle component into /lib
- const inclusiveClasses = state.inclusive
- ? 'relative checked bg-green2 br3 h1 toggle v-mid z-0'
- : 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0';
inclusiveToggle = (
-
+
Add all members to group
diff --git a/pkg/interface/src/apps/chat/components/sidebar.js b/pkg/interface/src/apps/chat/components/sidebar.js
index b94e62c12..d7d63a984 100644
--- a/pkg/interface/src/apps/chat/components/sidebar.js
+++ b/pkg/interface/src/apps/chat/components/sidebar.js
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import Welcome from './lib/welcome';
import { alphabetiseAssociations } from '../../../lib/util';
-import { SidebarInvite } from './lib/sidebar-invite';
+import SidebarInvite from '../../../components/SidebarInvite';
import { GroupItem } from './lib/group-item';
export class Sidebar extends Component {
@@ -51,10 +51,10 @@ export class Sidebar extends Component {
.map((uid) => {
return (
props.api.invite.accept('/chat', uid)}
+ onDecline={() => props.api.invite.decline('/chat', uid)}
/>
);
});
diff --git a/pkg/interface/src/apps/chat/components/skeleton.js b/pkg/interface/src/apps/chat/components/skeleton.js
index 4c31e26e2..8fa175712 100644
--- a/pkg/interface/src/apps/chat/components/skeleton.js
+++ b/pkg/interface/src/apps/chat/components/skeleton.js
@@ -1,6 +1,7 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import { Link } from 'react-router-dom';
+import ErrorBoundary from '../../../components/ErrorBoundary';
export class Skeleton extends Component {
render() {
@@ -61,7 +62,9 @@ export class Skeleton extends Component {
width: 'calc(100% - 300px)'
}}
>
- {this.props.children}
+
+ {this.props.children}
+
diff --git a/pkg/interface/src/apps/groups/components/join.js b/pkg/interface/src/apps/groups/components/join.js
index a9a2e4992..a092dd749 100644
--- a/pkg/interface/src/apps/groups/components/join.js
+++ b/pkg/interface/src/apps/groups/components/join.js
@@ -6,7 +6,6 @@ import urbitOb from 'urbit-ob';
export class JoinScreen extends Component {
constructor(props) {
super(props);
-
this.state = {
group: '',
error: false,
@@ -21,12 +20,11 @@ export class JoinScreen extends Component {
this.componentDidUpdate();
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate() {
const { props, state } = this;
// autojoin by URL, waits for group information
if ((props.ship && props.name) &&
- (prevProps && (prevProps.groups !== props.groups))) {
- console.log('autojoining');
+ (props.contacts && (Object.keys(props.contacts).length > 0) && !state.group)) {
const incomingGroup = `${props.ship}/${props.name}`;
// push to group if already exists
if (`/ship/${incomingGroup}` in props.groups) {
@@ -48,10 +46,8 @@ export class JoinScreen extends Component {
}
}
-
onClickJoin() {
const { props, state } = this;
- console.log('i am joining');
const { group } = state;
const [ship, name] = group.split('/');
@@ -101,14 +97,12 @@ export class JoinScreen extends Component {
Enter a ~ship/group-name
Group names use lowercase, hyphens, and slashes.
Create Group
- Selected ships will be invited to read your notebook. Selected
- groups will be invited to read and write notes.
+ Selected ships or group will be invited to read your notebook. Additional writers can be added from the 'subscribers' panel.
;
- const subsComponent = (this.props.ship.slice(1) !== window.ship)
- ? null
- :
+
+ const group = props.groups[notebook?.['writers-group-path']];
+ const role = group ? roleForShip(group, window.ship) : undefined;
+
+ const subsComponent = (this.props.ship.slice(1) === window.ship) || (role === 'admin')
+ ? (
Subscribers
- ;
+ )
+ : null
const settingsComponent = (this.props.ship.slice(1) !== window.ship)
? null
diff --git a/pkg/interface/src/apps/publish/components/lib/settings.js b/pkg/interface/src/apps/publish/components/lib/settings.js
index ebd46c627..fa13197e5 100644
--- a/pkg/interface/src/apps/publish/components/lib/settings.js
+++ b/pkg/interface/src/apps/publish/components/lib/settings.js
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
-import { writeText } from '../../../../lib/util';
import { Spinner } from '../../../../components/Spinner';
import { InviteSearch } from '../../../../components/InviteSearch';
+import Toggle from '../../../../components/toggle';
export class Settings extends Component {
constructor(props) {
@@ -133,22 +133,16 @@ export class Settings extends Component {
// don't give the option to make inclusive if we don't own the target
// group
const targetOwned = (state.targetGroup)
- ? state.targetGroup.slice(0, window.ship.length+3) === `/~${window.ship}/`
+ ? Boolean(state.targetGroup.includes(`/~${window.ship}/`))
: false;
let inclusiveToggle =
;
if (targetOwned) {
- // TODO toggle component into /lib
- const inclusiveClasses = state.inclusive
- ? 'relative checked bg-green2 br3 h1 toggle v-mid z-0'
- : 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0';
inclusiveToggle = (
-
+
Add all members to group
@@ -201,10 +195,6 @@ export class Settings extends Component {
}
render() {
- const commentsSwitchClasses = (this.state.comments)
- ? 'relative checked bg-green2 br3 h1 toggle v-mid z-0'
- : 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0';
-
if (this.props.host.slice(1) === window.ship) {
return (
@@ -274,11 +264,9 @@ export class Settings extends Component {
/>
);
} else {
- return copyShortcode;
+ return '';
}
}
}
diff --git a/pkg/interface/src/apps/publish/components/lib/sidebar-invite.js b/pkg/interface/src/apps/publish/components/lib/sidebar-invite.js
deleted file mode 100644
index cbaed7421..000000000
--- a/pkg/interface/src/apps/publish/components/lib/sidebar-invite.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import React, { Component } from 'react';
-
-export class SidebarInvite extends Component {
- onAccept() {
- this.props.api.invite.accept('/publish', this.props.uid);
- }
-
- onDecline() {
- this.props.api.invite.decline('/publish', this.props.uid);
- }
-
- render() {
- const { props } = this;
-
- return (
-
- );
- }
-}
-
diff --git a/pkg/interface/src/apps/publish/components/lib/sidebar.js b/pkg/interface/src/apps/publish/components/lib/sidebar.js
index 7948268fe..b801d464d 100644
--- a/pkg/interface/src/apps/publish/components/lib/sidebar.js
+++ b/pkg/interface/src/apps/publish/components/lib/sidebar.js
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
-import { SidebarInvite } from './sidebar-invite';
+import SidebarInvite from '../../../../components/SidebarInvite';
import { Welcome } from './welcome';
import { GroupItem } from './group-item';
import { alphabetiseAssociations } from '../../../../lib/util';
@@ -19,13 +19,13 @@ export class Sidebar extends Component {
const sidebarInvites = !(props.invites && props.invites['/publish'])
? null
: Object.keys(props.invites['/publish'])
- .map((uid, i) => {
+ .map((uid) => {
return (
props.api.invite.accept('/publish', uid)}
+ onDecline={() => props.api.invite.decline('/publish', uid)}
/>
);
});
diff --git a/pkg/interface/src/apps/publish/components/lib/subscribers.js b/pkg/interface/src/apps/publish/components/lib/subscribers.js
index b39e0f745..cdbf357a3 100644
--- a/pkg/interface/src/apps/publish/components/lib/subscribers.js
+++ b/pkg/interface/src/apps/publish/components/lib/subscribers.js
@@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { GroupView } from '../../../../components/Group';
+import { resourceFromPath } from '../../../../lib/group';
export class Subscribers extends Component {
constructor(props) {
@@ -7,6 +8,7 @@ export class Subscribers extends Component {
this.redirect = this.redirect.bind(this);
this.addUser = this.addUser.bind(this);
this.removeUser = this.removeUser.bind(this);
+ this.addAll = this.addAll.bind(this);
}
addUser(who, path) {
@@ -21,6 +23,18 @@ export class Subscribers extends Component {
window.location.href = url;
}
+ addAll() {
+ const path = this.props.notebook['writers-group-path'];
+ const group = path ? this.props.groups[path] : null;
+ const resource = resourceFromPath(path);
+ this.props.api.groups.addTag(
+ resource,
+ { app: 'publish', tag: `writers-${this.props.book}` },
+ [...group.members].map(m => `~${m}`)
+ );
+ }
+
+
render() {
const path = this.props.notebook['writers-group-path'];
const group = path ? this.props.groups[path] : null;
@@ -45,17 +59,25 @@ export class Subscribers extends Component {
];
return (
-
+
+
+ Add all members as writers
+
+
+
);
}
}
diff --git a/pkg/interface/src/apps/publish/components/skeleton.js b/pkg/interface/src/apps/publish/components/skeleton.js
index bf2110916..91a76026f 100644
--- a/pkg/interface/src/apps/publish/components/skeleton.js
+++ b/pkg/interface/src/apps/publish/components/skeleton.js
@@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { Sidebar } from './lib/sidebar';
+import ErrorBoundary from '../../../components/ErrorBoundary';
export class Skeleton extends Component {
render() {
@@ -36,7 +37,9 @@ export class Skeleton extends Component {
flexGrow: 1
}}
>
- {props.children}
+
+ {props.children}
+
diff --git a/pkg/interface/src/components/404.js b/pkg/interface/src/components/404.js
deleted file mode 100644
index fbe74bce8..000000000
--- a/pkg/interface/src/components/404.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react';
-
-const NotFound = () =>
-
404
-
Not found.
- If this is unexpected, email support@tlon.io
or submit an issue .
-
;
-
-export default NotFound;
diff --git a/pkg/interface/src/components/Error.tsx b/pkg/interface/src/components/Error.tsx
new file mode 100644
index 000000000..51b1f4f11
--- /dev/null
+++ b/pkg/interface/src/components/Error.tsx
@@ -0,0 +1,40 @@
+import React, { Component } from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
+
+type ErrorProps = RouteComponentProps & {
+ code?: number | string,
+ description?: string,
+ error?: Error
+};
+
+class ErrorComponent extends Component {
+ render () {
+ const { code, error, history, description } = this.props;
+ return (
+
+
+ {code ? code : 'Error'}
+
+ {description ?
{description}
: null}
+ {error ? (
+
+
“{error.message}”
+
+ Stack trace
+ {error.stack}
+
+
+ ) : null}
+
If this is unexpected, email support@tlon.io
or submit an issue .
+ {history.length > 1
+ ?
history.go(-1) }>Go back
+ :
history.push('/') }>Go home
+ }
+
+ );
+ }
+}
+
+export default withRouter(ErrorComponent);
diff --git a/pkg/interface/src/components/ErrorBoundary.tsx b/pkg/interface/src/components/ErrorBoundary.tsx
new file mode 100644
index 000000000..0c1c8b4c3
--- /dev/null
+++ b/pkg/interface/src/components/ErrorBoundary.tsx
@@ -0,0 +1,35 @@
+import React, { Component } from 'react';
+import { RouteComponentProps, withRouter } from 'react-router-dom';
+import ErrorComponent from './Error';
+
+class ErrorBoundary extends Component<
+ RouteComponentProps,
+ { error?: Error }
+> {
+ constructor(props) {
+ super(props);
+ this.state = { error: undefined };
+ const { history } = this.props;
+ history.listen((location, action) => {
+ if (this.state.error) {
+ this.setState({
+ error: undefined,
+ });
+ }
+ });
+ }
+
+ componentDidCatch(error) {
+ this.setState({ error });
+ return false;
+ }
+
+ render() {
+ if (this.state.error) {
+ return ( )
+ }
+ return this.props.children;
+ }
+}
+
+export default withRouter(ErrorBoundary);
\ No newline at end of file
diff --git a/pkg/interface/src/components/Group.tsx b/pkg/interface/src/components/Group.tsx
index 3f6535f0e..5ac6459a2 100644
--- a/pkg/interface/src/components/Group.tsx
+++ b/pkg/interface/src/components/Group.tsx
@@ -1,5 +1,7 @@
import React, { Component } from 'react';
import _, { capitalize } from 'lodash';
+import { FixedSizeList as List } from 'react-window';
+
import { Dropdown } from '../apps/publish/components/lib/dropdown';
import { cite, deSig } from '../lib/util';
import { roleForShip, resourceFromPath } from '../lib/group';
@@ -143,7 +145,7 @@ export class GroupView extends Component<
isAdmin(): boolean {
const us = `~${window.ship}`;
- const role = roleForShip(this.props.group, us);
+ const role = roleForShip(this.props.group, window.ship);
const resource = resourceFromPath(this.props.resourcePath);
return resource.ship == us || role === 'admin';
}
@@ -156,10 +158,15 @@ export class GroupView extends Component<
return options;
}
const role = roleForShip(group, ship);
+ const myRole = roleForShip(group, window.ship);
if (role === 'admin' || resource.ship === ship) {
return [];
}
- if ('open' in group.policy) {
+ if (
+ 'open' in group.policy // If blacklist, not whitelist
+ && (this.isAdmin()) // And we can ban people (TODO: add || role === 'moderator')
+ && ship !== window.ship // We can't ban ourselves
+ ) {
options.unshift({ text: 'Ban', onSelect: () => this.banUser(ship) });
}
if (this.isAdmin() && !role) {
@@ -199,52 +206,45 @@ export class GroupView extends Component<
});
}
- renderMembers() {
+ memberElements() {
const { group, permissions } = this.props;
const { members } = group;
const isAdmin = this.isAdmin();
- return (
-
-
Members
- {Array.from(members).map((ship) => {
- const role = roleForShip(group, deSig(ship));
- const onRoleRemove =
- role && isAdmin
- ? () => {
- this.removeTag(ship, { tag: role });
- }
- : undefined;
- const [present, missing] = this.getAppTags(ship);
- const options = this.optionsForShip(ship, missing);
+ return Array.from(members).map((ship) => {
+ const role = roleForShip(group, deSig(ship));
+ const onRoleRemove =
+ role && isAdmin
+ ? () => {
+ this.removeTag(ship, { tag: role });
+ }
+ : undefined;
+ const [present, missing] = this.getAppTags(ship);
+ const options = this.optionsForShip(ship, missing);
- return (
-
-
- {((permissions && role) || present.length > 0) && (
-
- {role && (
-
- )}
- {present.map((tag, idx) => (
-
- this.removeTag(ship, tag)
- )}
- description={tag.desc}
- />
- ))}
-
- )}
-
+ return (
+
+ {((permissions && role) || present.length > 0) && (
+
+ {role && (
+
+ )}
+ {present.map((tag, idx) => (
+
+ this.removeTag(ship, tag)
+ )}
+ description={tag.desc}
+ />
+ ))}
- );
- })}
-
- );
+ )}
+
+ );
+ })
}
setInvites(invites: Invites) {
@@ -321,6 +321,7 @@ export class GroupView extends Component<
render() {
const { group, resourcePath, className } = this.props;
const resource = resourceFromPath(resourcePath);
+ const memberElements = this.memberElements();
return (
@@ -332,7 +333,17 @@ export class GroupView extends Component<
{'invite' in group.policy && this.renderInvites(group.policy)}
{'open' in group.policy && this.renderBanned(group.policy)}
- {this.renderMembers()}
+
+
Members
+
+ {({ index, style }) => {memberElements[index]}
}
+
+
{
render() {
const { props } = this;
return (
-
+
- {props.invite.path}
+ {props.invite.text ? props.invite.text : props.invite.path}
Accept Invite
Decline
@@ -36,3 +29,4 @@ export class SidebarInvite extends Component {
}
}
+export default SidebarInvite;
\ No newline at end of file
diff --git a/pkg/interface/src/components/toggle.js b/pkg/interface/src/components/toggle.js
new file mode 100644
index 000000000..cf632a58d
--- /dev/null
+++ b/pkg/interface/src/components/toggle.js
@@ -0,0 +1,20 @@
+import React, { Component } from 'react';
+
+export class Toggle extends Component {
+ render() {
+ const switchClasses = (this.props.boolean)
+ ? 'relative checked bg-green2 br3 h1 toggle v-mid z-0'
+ : 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0';
+
+ return (
+
+ );
+ }
+}
+
+export default Toggle;
diff --git a/pkg/interface/src/reducers/group-update.ts b/pkg/interface/src/reducers/group-update.ts
index ca0bf190c..53cb0b6ba 100644
--- a/pkg/interface/src/reducers/group-update.ts
+++ b/pkg/interface/src/reducers/group-update.ts
@@ -6,6 +6,7 @@ import {
Group,
Tags,
GroupPolicy,
+ GroupPolicyDiff,
OpenPolicyDiff,
OpenPolicy,
InvitePolicyDiff,
@@ -24,7 +25,6 @@ function decodeGroup(group: Enc
): Group {
tags: decodeTags(group.tags),
policy: decodePolicy(group.policy),
};
- console.log(res);
return res;
}
@@ -179,6 +179,8 @@ export default class GroupReducer {
this.openChangePolicy(diff.open, policy);
} else if ('invite' in policy && 'invite' in diff) {
this.inviteChangePolicy(diff.invite, policy);
+ } else if ('replace' in diff) {
+ state.groups[resourcePath].policy = diff.replace;
} else {
console.log('bad policy diff');
}
diff --git a/pkg/interface/src/reducers/invite-update.ts b/pkg/interface/src/reducers/invite-update.ts
index d4b0f670c..cea77f163 100644
--- a/pkg/interface/src/reducers/invite-update.ts
+++ b/pkg/interface/src/reducers/invite-update.ts
@@ -50,7 +50,6 @@ export default class InviteReducer {
accepted(json: InviteUpdate, state: S) {
const data = _.get(json, 'accepted', false);
if (data) {
- console.log(data);
delete state.invites[data.path][data.uid];
}
}
diff --git a/pkg/interface/src/reducers/metadata-update.ts b/pkg/interface/src/reducers/metadata-update.ts
index 43d8b0492..24d26b7bd 100644
--- a/pkg/interface/src/reducers/metadata-update.ts
+++ b/pkg/interface/src/reducers/metadata-update.ts
@@ -11,12 +11,10 @@ export default class MetadataReducer {
reduce(json: Cage, state: S) {
let data = json['metadata-update']
if (data) {
- console.log('data: ', data);
this.associations(data, state);
this.add(data, state);
this.update(data, state);
this.remove(data, state);
- console.log('state: ', state);
}
}
diff --git a/pkg/interface/src/store/publish.js b/pkg/interface/src/store/publish.js
index fc70cbdde..32cfaef74 100644
--- a/pkg/interface/src/store/publish.js
+++ b/pkg/interface/src/store/publish.js
@@ -48,4 +48,3 @@ export default class PublishStore extends BaseStore {
this.responseReducer.reduce(data, this.state);
}
}
-
diff --git a/pkg/interface/src/themes/light.ts b/pkg/interface/src/themes/light.ts
new file mode 100644
index 000000000..483906c3c
--- /dev/null
+++ b/pkg/interface/src/themes/light.ts
@@ -0,0 +1,169 @@
+import baseStyled, { ThemedStyledInterface } from "styled-components";
+
+const base = {
+ white: "rgba(255,255,255,1)",
+ black: "rgba(0,0,0,1)",
+ red: "rgba(255,65,54,1)",
+ yellow: "rgba(255,199,0,1)",
+ green: "rgba(0,159,101,1)",
+ blue: "rgba(0,142,255,1)",
+};
+
+const scales = {
+ white10: "rgba(255,255,255,0.1)",
+ white20: "rgba(255,255,255,0.2)",
+ white30: "rgba(255,255,255,0.3)",
+ white40: "rgba(255,255,255,0.4)",
+ white50: "rgba(255,255,255,0.5)",
+ white60: "rgba(255,255,255,0.6)",
+ white70: "rgba(255,255,255,0.7)",
+ white80: "rgba(255,255,255,0.8)",
+ white90: "rgba(255,255,255,0.9)",
+ white100: "rgba(255,255,255,1)",
+ black10: "rgba(0,0,0,0.1)",
+ black20: "rgba(0,0,0,0.2)",
+ black30: "rgba(0,0,0,0.3)",
+ black40: "rgba(0,0,0,0.4)",
+ black50: "rgba(0,0,0,0.5)",
+ black60: "rgba(0,0,0,0.6)",
+ black70: "rgba(0,0,0,0.7)",
+ black80: "rgba(0,0,0,0.8)",
+ black90: "rgba(0,0,0,0.9)",
+ black100: "rgba(0,0,0,1)",
+ red10: "rgba(255,65,54,0.1)",
+ red20: "rgba(255,65,54,0.2)",
+ red30: "rgba(255,65,54,0.3)",
+ red40: "rgba(255,65,54,0.4)",
+ red50: "rgba(255,65,54,0.5)",
+ red60: "rgba(255,65,54,0.6)",
+ red70: "rgba(255,65,54,0.7)",
+ red80: "rgba(255,65,54,0.8)",
+ red90: "rgba(255,65,54,0.9)",
+ red100: "rgba(255,65,54,1)",
+ yellow10: "rgba(255,199,0,0.1)",
+ yellow20: "rgba(255,199,0,0.2)",
+ yellow30: "rgba(255,199,0,0.3)",
+ yellow40: "rgba(255,199,0,0.4)",
+ yellow50: "rgba(255,199,0,0.5)",
+ yellow60: "rgba(255,199,0,0.6)",
+ yellow70: "rgba(255,199,0,0.7)",
+ yellow80: "rgba(255,199,0,0.8)",
+ yellow90: "rgba(255,199,0,0.9)",
+ yellow100: "rgba(255,199,0,1)",
+ green10: "rgba(0,159,101,0.1)",
+ green20: "rgba(0,159,101,0.2)",
+ green30: "rgba(0,159,101,0.3)",
+ green40: "rgba(0,159,101,0.4)",
+ green50: "rgba(0,159,101,0.5)",
+ green60: "rgba(0,159,101,0.6)",
+ green70: "rgba(0,159,101,0.7)",
+ green80: "rgba(0,159,101,0.8)",
+ green90: "rgba(0,159,101,0.9)",
+ green100: "rgba(0,159,101,1)",
+ blue10: "rgba(0,142,255,0.1)",
+ blue20: "rgba(0,142,255,0.2)",
+ blue30: "rgba(0,142,255,0.3)",
+ blue40: "rgba(0,142,255,0.4)",
+ blue50: "rgba(0,142,255,0.5)",
+ blue60: "rgba(0,142,255,0.6)",
+ blue70: "rgba(0,142,255,0.7)",
+ blue80: "rgba(0,142,255,0.8)",
+ blue90: "rgba(0,142,255,0.9)",
+ blue100: "rgba(0,142,255,1)",
+};
+
+const theme = {
+ colors: {
+ white: base.white,
+ black: base.black,
+
+ gray: scales.black60,
+ lightGray: scales.black30,
+ washedGray: scales.black10,
+
+ red: base.red,
+ lightRed: scales.red30,
+ washedRed: scales.red10,
+
+ yellow: base.yellow,
+ lightYellow: scales.yellow30,
+ washedYellow: scales.yellow10,
+
+ green: base.green,
+ lightGreen: scales.green30,
+ washedGreen: scales.green10,
+
+ blue: base.blue,
+ lightBlue: scales.blue30,
+ washedBlue: scales.blue10,
+
+ none: "rgba(0,0,0,0)",
+ scales: scales,
+ },
+ fonts: {
+ sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`,
+ mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`,
+ },
+ // font-size
+ fontSizes: [
+ 12, // 0
+ 16, // 1
+ 24, // 2
+ 32, // 3
+ 48, // 4
+ 64, // 5
+ ],
+ // font-weight
+ fontWeights: {
+ thin: 300,
+ regular: 400,
+ bold: 600,
+ },
+ // line-height
+ lineHeights: {
+ min: 1.2,
+ short: 1.333333,
+ regular: 1.5,
+ tall: 1.666666,
+ },
+ // border, border-top, border-right, border-bottom, border-left
+ borders: ["none", "1px solid"],
+ // margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap
+ space: [
+ 0, // 0
+ 4, // 1
+ 8, // 2
+ 16, // 3
+ 24, // 4
+ 32, // 5
+ 48, // 6
+ 64, // 7
+ 96, // 8
+ ],
+ // border-radius
+ radii: [
+ 0, // 0
+ 2, // 1
+ 4, // 2
+ 8, // 3
+ 16, // 4
+ ],
+ // width, height, min-width, max-width, min-height, max-height
+ sizes: [
+ 0, // 0
+ 4, // 1
+ 8, // 2
+ 16, // 3
+ 24, // 4
+ 32, // 5
+ 48, // 6
+ 64, // 7
+ 96, // 8
+ ],
+ // z-index
+ zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ breakpoints: ["768px", "1024px", "1440px", "2200px"],
+};
+export type Theme = typeof theme;
+export const styled = baseStyled as ThemedStyledInterface;
+export default theme;
diff --git a/pkg/interface/src/themes/old-dark.ts b/pkg/interface/src/themes/old-dark.ts
new file mode 100644
index 000000000..62d2a6da7
--- /dev/null
+++ b/pkg/interface/src/themes/old-dark.ts
@@ -0,0 +1,184 @@
+import baseStyled, { ThemedStyledInterface } from "styled-components";
+
+const base = {
+ white: "rgba(255,255,255,1)",
+ black: "rgba(0,0,0,1)",
+ red: "rgba(255,65,54,1)",
+ yellow: "rgba(255,199,0,1)",
+ green: "rgba(0,159,101,1)",
+ blue: "rgba(0,142,255,1)",
+};
+
+const scales = {
+ white05: "rgba(255,255,255,0.05)",
+ white10: "rgba(255,255,255,0.1)",
+ white20: "rgba(255,255,255,0.2)",
+ white30: "rgba(255,255,255,0.3)",
+ white40: "rgba(255,255,255,0.4)",
+ white50: "rgba(255,255,255,0.5)",
+ white60: "rgba(255,255,255,0.6)",
+ white70: "rgba(255,255,255,0.7)",
+ white80: "rgba(255,255,255,0.8)",
+ white90: "rgba(255,255,255,0.9)",
+ white100: "rgba(255,255,255,1)",
+ black05: "rgba(0,0,0,0.05)",
+ black10: "rgba(0,0,0,0.1)",
+ black20: "rgba(0,0,0,0.2)",
+ black30: "rgba(0,0,0,0.3)",
+ black40: "rgba(0,0,0,0.4)",
+ black50: "rgba(0,0,0,0.5)",
+ black60: "rgba(0,0,0,0.6)",
+ black70: "rgba(0,0,0,0.7)",
+ black80: "rgba(0,0,0,0.8)",
+ black90: "rgba(0,0,0,0.9)",
+ black100: "rgba(0,0,0,1)",
+ red05: "rgba(255,65,54,0.05)",
+ red10: "rgba(255,65,54,0.1)",
+ red20: "rgba(255,65,54,0.2)",
+ red30: "rgba(255,65,54,0.3)",
+ red40: "rgba(255,65,54,0.4)",
+ red50: "rgba(255,65,54,0.5)",
+ red60: "rgba(255,65,54,0.6)",
+ red70: "rgba(255,65,54,0.7)",
+ red80: "rgba(255,65,54,0.8)",
+ red90: "rgba(255,65,54,0.9)",
+ red100: "rgba(255,65,54,1)",
+ yellow05: "rgba(255,199,0,0.05)",
+ yellow10: "rgba(255,199,0,0.1)",
+ yellow20: "rgba(255,199,0,0.2)",
+ yellow30: "rgba(255,199,0,0.3)",
+ yellow40: "rgba(255,199,0,0.4)",
+ yellow50: "rgba(255,199,0,0.5)",
+ yellow60: "rgba(255,199,0,0.6)",
+ yellow70: "rgba(255,199,0,0.7)",
+ yellow80: "rgba(255,199,0,0.8)",
+ yellow90: "rgba(255,199,0,0.9)",
+ yellow100: "rgba(255,199,0,1)",
+ green05: "rgba(0,159,101,0.05)",
+ green10: "rgba(0,159,101,0.1)",
+ green20: "rgba(0,159,101,0.2)",
+ green30: "rgba(0,159,101,0.3)",
+ green40: "rgba(0,159,101,0.4)",
+ green50: "rgba(0,159,101,0.5)",
+ green60: "rgba(0,159,101,0.6)",
+ green70: "rgba(0,159,101,0.7)",
+ green80: "rgba(0,159,101,0.8)",
+ green90: "rgba(0,159,101,0.9)",
+ green100: "rgba(0,159,101,1)",
+ blue05: "rgba(0,142,255,0.05)",
+ blue10: "rgba(0,142,255,0.1)",
+ blue20: "rgba(0,142,255,0.2)",
+ blue30: "rgba(0,142,255,0.3)",
+ blue40: "rgba(0,142,255,0.4)",
+ blue50: "rgba(0,142,255,0.5)",
+ blue60: "rgba(0,142,255,0.6)",
+ blue70: "rgba(0,142,255,0.7)",
+ blue80: "rgba(0,142,255,0.8)",
+ blue90: "rgba(0,142,255,0.9)",
+ blue100: "rgba(0,142,255,1)",
+};
+
+const util = {
+ cyan: "#00FFFF",
+ magenta: "#FF00FF",
+ yellow: "#FFFF00",
+ black: "#000000",
+ gray0: "#333333"
+};
+
+const theme = {
+ colors: {
+ white: util.gray0,
+ black: base.white,
+
+ gray: scales.white60,
+ lightGray: scales.white30,
+ washedGray: scales.white05,
+
+ red: base.red,
+ lightRed: scales.red30,
+ washedRed: scales.red05,
+
+ yellow: base.yellow,
+ lightYellow: scales.yellow30,
+ washedYellow: scales.yellow10,
+
+ green: base.green,
+ lightGreen: scales.green30,
+ washedGreen: scales.green10,
+
+ blue: base.blue,
+ lightBlue: scales.blue30,
+ washedBlue: scales.blue10,
+
+ none: "rgba(0,0,0,0)",
+
+ scales: scales,
+ util: util,
+ },
+ fonts: {
+ sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`,
+ mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`,
+ },
+ // font-size
+ fontSizes: [
+ 12, // 0
+ 16, // 1
+ 24, // 2
+ 32, // 3
+ 48, // 4
+ 64, // 5
+ ],
+ // font-weight
+ fontWeights: {
+ thin: 300,
+ regular: 400,
+ bold: 600,
+ },
+ // line-height
+ lineHeights: {
+ min: 1.2,
+ short: 1.333333,
+ regular: 1.5,
+ tall: 1.666666,
+ },
+ // border, border-top, border-right, border-bottom, border-left
+ borders: ["none", "1px solid"],
+ // margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap
+ space: [
+ 0, // 0
+ 4, // 1
+ 8, // 2
+ 16, // 3
+ 24, // 4
+ 32, // 5
+ 48, // 6
+ 64, // 7
+ 96, // 8
+ ],
+ // border-radius
+ radii: [
+ 0, // 0
+ 2, // 1
+ 4, // 2
+ 8, // 3
+ ],
+ // width, height, min-width, max-width, min-height, max-height
+ sizes: [
+ 0, // 0
+ 4, // 1
+ 8, // 2
+ 16, // 3
+ 24, // 4
+ 32, // 5
+ 48, // 6
+ 64, // 7
+ 96, // 8
+ ],
+ // z-index
+ zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ breakpoints: ["768px", "1024px", "1440px", "2200px"],
+};
+export type Theme = typeof theme;
+export const styled = baseStyled as ThemedStyledInterface;
+export default theme;