mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-09-20 06:58:16 +03:00
dbug: implement debug dashboard
This commit is contained in:
parent
ac494a265e
commit
959884c9cd
716
pkg/arvo/app/dbug.hoon
Normal file
716
pkg/arvo/app/dbug.hoon
Normal file
@ -0,0 +1,716 @@
|
|||||||
|
:: dbug: debug dashboard server
|
||||||
|
::
|
||||||
|
/- spider
|
||||||
|
/+ server, default-agent, verb, dbug
|
||||||
|
::
|
||||||
|
|%
|
||||||
|
+$ state-0 [%0 passcode=(unit @t)]
|
||||||
|
+$ card card:agent:gall
|
||||||
|
--
|
||||||
|
::
|
||||||
|
=| state-0
|
||||||
|
=* state -
|
||||||
|
::
|
||||||
|
%+ verb |
|
||||||
|
%- agent:dbug
|
||||||
|
^- agent:gall
|
||||||
|
=<
|
||||||
|
|_ =bowl:gall
|
||||||
|
+* this .
|
||||||
|
do ~(. +> bowl)
|
||||||
|
def ~(. (default-agent this %|) bowl)
|
||||||
|
::
|
||||||
|
++ on-init
|
||||||
|
^- (quip card _this)
|
||||||
|
:_ this
|
||||||
|
[%pass /connect %arvo %e %connect [~ /'~debug'] dap.bowl]~
|
||||||
|
::
|
||||||
|
++ on-save !>(state)
|
||||||
|
::
|
||||||
|
++ on-load
|
||||||
|
|= old=vase
|
||||||
|
^- (quip card _this)
|
||||||
|
[~ this(state !<(state-0 old))]
|
||||||
|
::
|
||||||
|
++ on-watch
|
||||||
|
|= =path
|
||||||
|
^- (quip card _this)
|
||||||
|
?. ?=([%http-response *] path)
|
||||||
|
(on-watch:def path)
|
||||||
|
[~ this]
|
||||||
|
::
|
||||||
|
++ on-poke
|
||||||
|
|= [=mark =vase]
|
||||||
|
^- (quip card _this)
|
||||||
|
?: ?=(%noun mark)
|
||||||
|
?> (team:title [our src]:bowl)
|
||||||
|
[~ this(passcode !<((unit @t) vase))]
|
||||||
|
?. ?=(%handle-http-request mark)
|
||||||
|
(on-poke:def mark vase)
|
||||||
|
=+ !<([eyre-id=@ta =inbound-request:eyre] vase)
|
||||||
|
:: !!
|
||||||
|
:_ this
|
||||||
|
%+ give-simple-payload:app:server eyre-id
|
||||||
|
%+ authorize-http-request:do inbound-request
|
||||||
|
handle-http-request:do
|
||||||
|
::
|
||||||
|
++ on-arvo
|
||||||
|
|= [=wire =sign-arvo]
|
||||||
|
^- (quip card _this)
|
||||||
|
?. ?=([%e %bound *] sign-arvo)
|
||||||
|
(on-arvo:def wire sign-arvo)
|
||||||
|
~? !accepted.sign-arvo
|
||||||
|
[dap.bowl "bind rejected!" binding.sign-arvo]
|
||||||
|
[~ this]
|
||||||
|
::
|
||||||
|
++ on-peek on-peek:def
|
||||||
|
++ on-leave on-leave:def
|
||||||
|
++ on-agent on-agent:def
|
||||||
|
++ on-fail on-fail:def
|
||||||
|
--
|
||||||
|
::
|
||||||
|
|_ =bowl:gall
|
||||||
|
::
|
||||||
|
:: serving
|
||||||
|
::
|
||||||
|
++ authorize-http-request
|
||||||
|
=, server
|
||||||
|
:: if no passcode configured, only allow host ship to view
|
||||||
|
::
|
||||||
|
?~ passcode require-authorization:app
|
||||||
|
|= $: =inbound-request:eyre
|
||||||
|
handler=$-(inbound-request:eyre simple-payload:http)
|
||||||
|
==
|
||||||
|
?: authenticated.inbound-request
|
||||||
|
(handler inbound-request)
|
||||||
|
:: else, allow randos access,
|
||||||
|
:: on the condition they provide a correct ?passcode= url parameter
|
||||||
|
::
|
||||||
|
=; pass=(unit @t)
|
||||||
|
?: =(passcode pass)
|
||||||
|
(handler inbound-request)
|
||||||
|
(require-authorization:app inbound-request handler)
|
||||||
|
=/ from-url=(unit @t)
|
||||||
|
=- (~(get by -) 'passcode')
|
||||||
|
%- ~(gas by *(map @t @t))
|
||||||
|
args:(parse-request-line url.request.inbound-request)
|
||||||
|
?^ from-url from-url
|
||||||
|
:: try the referer field instead
|
||||||
|
::
|
||||||
|
=/ ref-url=(unit @t)
|
||||||
|
(get-header:http 'referer' header-list.request.inbound-request)
|
||||||
|
?~ ref-url ~
|
||||||
|
?~ (find "passcode={(trip u.passcode)}" (trip u.ref-url)) ~
|
||||||
|
passcode
|
||||||
|
::
|
||||||
|
++ handle-http-request
|
||||||
|
=, server
|
||||||
|
|= =inbound-request:eyre
|
||||||
|
^- simple-payload:http
|
||||||
|
=/ =request-line
|
||||||
|
%- parse-request-line
|
||||||
|
url.request.inbound-request
|
||||||
|
=* req-head header-list.request.inbound-request
|
||||||
|
::TODO handle POST
|
||||||
|
?. ?=(%'GET' method.request.inbound-request)
|
||||||
|
not-found:gen
|
||||||
|
(handle-get-request req-head request-line)
|
||||||
|
::
|
||||||
|
++ handle-get-request
|
||||||
|
=, server
|
||||||
|
|= [headers=header-list:http request-line]
|
||||||
|
^- simple-payload:http
|
||||||
|
=? site ?=([%'~debug' *] site) t.site
|
||||||
|
?~ ext
|
||||||
|
$(ext `%html, site [%index ~]) ::NOTE hack
|
||||||
|
:: if not json, serve static file
|
||||||
|
::
|
||||||
|
?. ?=([~ %json] ext)
|
||||||
|
=/ file=(unit octs)
|
||||||
|
(get-file-at /app/debug site u.ext)
|
||||||
|
?~ file not-found:gen
|
||||||
|
?+ u.ext not-found:gen
|
||||||
|
%html (html-response:gen u.file)
|
||||||
|
%js (js-response:gen u.file)
|
||||||
|
%css (css-response:gen u.file)
|
||||||
|
%png (png-response:gen u.file)
|
||||||
|
==
|
||||||
|
:: get data matching the json and convert it
|
||||||
|
::
|
||||||
|
=; json=(unit json)
|
||||||
|
?~ json not-found:gen
|
||||||
|
%- json-response:gen
|
||||||
|
=, html
|
||||||
|
(as-octt:mimes (en-json u.json))
|
||||||
|
=, enjs:format
|
||||||
|
?+ site ~
|
||||||
|
:: /app.json: {appname: running, ...}
|
||||||
|
::
|
||||||
|
[%app ~]
|
||||||
|
%- some
|
||||||
|
%- pairs
|
||||||
|
%+ turn all:apps
|
||||||
|
|= app=term
|
||||||
|
[app b+(running:apps app)]
|
||||||
|
::
|
||||||
|
:: /app/[appname].json: {state: }
|
||||||
|
::
|
||||||
|
[%app @ ~]
|
||||||
|
=* app i.t.site
|
||||||
|
::TODO ?. (dbugable:apps app) ~
|
||||||
|
%- some
|
||||||
|
%- pairs
|
||||||
|
:~ :- 'state'
|
||||||
|
(tank (sell (state:apps app)))
|
||||||
|
::
|
||||||
|
:- 'subscriptions'
|
||||||
|
%- pairs
|
||||||
|
=+ (subscriptions:apps app)
|
||||||
|
|^ ~['in'^(incoming in) 'out'^(outgoing out)]
|
||||||
|
::
|
||||||
|
++ incoming
|
||||||
|
|= =bitt:gall
|
||||||
|
^- json
|
||||||
|
:- %a
|
||||||
|
%+ turn ~(tap by bitt)
|
||||||
|
|= [d=duct [s=^ship p=^path]]
|
||||||
|
%- pairs
|
||||||
|
~! (ship s)
|
||||||
|
:~ 'duct'^a+(turn d path)
|
||||||
|
'ship'^(ship s)
|
||||||
|
'path'^(path p)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ outgoing
|
||||||
|
|= =boat:gall
|
||||||
|
^- json
|
||||||
|
:- %a
|
||||||
|
%+ turn ~(tap by boat)
|
||||||
|
|= [[w=wire s=^ship t=term] [a=? p=^path]]
|
||||||
|
%- pairs
|
||||||
|
:~ 'wire'^(path w)
|
||||||
|
'ship'^(ship s)
|
||||||
|
'app'^s+t
|
||||||
|
'acked'^b+a
|
||||||
|
'path'^(path p)
|
||||||
|
==
|
||||||
|
--
|
||||||
|
==
|
||||||
|
::
|
||||||
|
:: /spider.json
|
||||||
|
::
|
||||||
|
[%spider %threads ~]
|
||||||
|
%- some
|
||||||
|
:: turn flat stack descriptors into object (tree) representing stacks
|
||||||
|
::
|
||||||
|
|^ (tree-to-json build-thread-tree)
|
||||||
|
::
|
||||||
|
+$ tree
|
||||||
|
$~ ~
|
||||||
|
(map tid:spider tree)
|
||||||
|
::
|
||||||
|
++ build-thread-tree
|
||||||
|
%+ roll tree:threads
|
||||||
|
|= [stack=(list tid:spider) =tree]
|
||||||
|
?~ stack tree
|
||||||
|
%+ ~(put by tree) i.stack
|
||||||
|
%_ $
|
||||||
|
stack t.stack
|
||||||
|
tree (~(gut by tree) i.stack ~)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ tree-to-json
|
||||||
|
|= =tree
|
||||||
|
o+(~(run by tree) tree-to-json)
|
||||||
|
--
|
||||||
|
::
|
||||||
|
:: /azimuth/status
|
||||||
|
::
|
||||||
|
:: /ames/peer.json
|
||||||
|
::
|
||||||
|
[%ames %peer ~]
|
||||||
|
=/ [known=(list [^ship *]) alien=(list [^ship *])]
|
||||||
|
%+ skid ~(tap by peers:v-ames)
|
||||||
|
|= [^ship kind=?(%alien %known)]
|
||||||
|
?=(%known kind)
|
||||||
|
%- some
|
||||||
|
%- pairs
|
||||||
|
::NOTE would do (cork head ship) but can't get that to compile...
|
||||||
|
:~ 'known'^a+(turn (turn known head) ship)
|
||||||
|
'alien'^a+(turn (turn alien head) ship)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
:: /ames/peer/[shipname].json
|
||||||
|
::
|
||||||
|
[%ames %peer @ ~]
|
||||||
|
=/ who=^ship
|
||||||
|
(rash i.t.t.site fed:ag)
|
||||||
|
%- some
|
||||||
|
=, v-ames
|
||||||
|
(peer-to-json (peer who))
|
||||||
|
::
|
||||||
|
:: /behn/timers.json
|
||||||
|
::
|
||||||
|
[%behn %timers ~]
|
||||||
|
%- some
|
||||||
|
:- %a
|
||||||
|
%+ turn timers:v-behn
|
||||||
|
|= [date=@da =duct]
|
||||||
|
%- pairs
|
||||||
|
:~ 'date'^(time date)
|
||||||
|
'duct'^a+(turn duct path)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
:: /clay/commits.json
|
||||||
|
::
|
||||||
|
[%clay %commits ~]
|
||||||
|
(some commits-json:v-clay)
|
||||||
|
::
|
||||||
|
:: /eyre/bindings.json
|
||||||
|
::
|
||||||
|
[%eyre %bindings ~]
|
||||||
|
%- some
|
||||||
|
:- %a
|
||||||
|
%+ turn bindings:v-eyre
|
||||||
|
=, eyre
|
||||||
|
|= [binding =duct =action]
|
||||||
|
%- pairs
|
||||||
|
:~ 'location'^s+(cat 3 (fall site '*') (spat path))
|
||||||
|
::
|
||||||
|
:- 'action'
|
||||||
|
:- %s
|
||||||
|
?+ -.action -.action
|
||||||
|
%gen :((cury cat 3) '+' (spat [desk path]:generator.action))
|
||||||
|
%app (cat 3 ':' app.action)
|
||||||
|
==
|
||||||
|
==
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ get-file-at
|
||||||
|
|= [base=path file=path ext=@ta]
|
||||||
|
^- (unit octs)
|
||||||
|
?. ?=(?(%html %css %js %png) ext)
|
||||||
|
~
|
||||||
|
=/ =path
|
||||||
|
:* (scot %p our.bowl)
|
||||||
|
q.byk.bowl
|
||||||
|
(scot %da now.bowl)
|
||||||
|
(snoc (weld base file) ext)
|
||||||
|
==
|
||||||
|
?. .^(? %cu path) ~
|
||||||
|
%- some
|
||||||
|
%- as-octs:mimes:html
|
||||||
|
.^(@ %cx path)
|
||||||
|
::
|
||||||
|
:: applications
|
||||||
|
::
|
||||||
|
++ apps
|
||||||
|
|%
|
||||||
|
++ all
|
||||||
|
^- (list term)
|
||||||
|
%+ murn
|
||||||
|
(scry (list path) %ct %home /app)
|
||||||
|
|= =path
|
||||||
|
^- (unit term)
|
||||||
|
?. ?=([%app @ %hoon ~] path) ~
|
||||||
|
`i.t.path
|
||||||
|
::
|
||||||
|
++ running
|
||||||
|
|= app=term
|
||||||
|
(scry ? %gu app ~)
|
||||||
|
::
|
||||||
|
++ dbugable
|
||||||
|
|= app=term
|
||||||
|
^- ?
|
||||||
|
!! ::TODO how to check if it supports the /dbug scries?
|
||||||
|
::
|
||||||
|
++ state
|
||||||
|
|= app=term
|
||||||
|
(scry-dbug vase app /dbug/state)
|
||||||
|
::
|
||||||
|
++ subscriptions
|
||||||
|
=, gall
|
||||||
|
|= app=term
|
||||||
|
^- [out=boat in=bitt]
|
||||||
|
(scry-dbug ,[boat bitt] app /dbug/subscriptions)
|
||||||
|
::
|
||||||
|
++ scry-dbug
|
||||||
|
|* [=mold app=term =path]
|
||||||
|
(scry mold %gx app (snoc `^path`path %noun))
|
||||||
|
::
|
||||||
|
::TODO but why? we can't tell if it's on or not
|
||||||
|
++ poke-verb-toggle
|
||||||
|
|= app=term
|
||||||
|
^- card
|
||||||
|
(poke /verb/[app] app %verb !>(%loud))
|
||||||
|
--
|
||||||
|
::
|
||||||
|
:: threads
|
||||||
|
::
|
||||||
|
++ threads
|
||||||
|
|%
|
||||||
|
::NOTE every (list tid:spider) represents a stack,
|
||||||
|
:: with a unique tid at the end
|
||||||
|
++ tree
|
||||||
|
(scry (list (list tid:spider)) %gx %spider /tree/noun)
|
||||||
|
::
|
||||||
|
++ poke-kill
|
||||||
|
|= =tid:spider
|
||||||
|
^- card
|
||||||
|
(poke /spider/kill/[tid] %spider %spider-stop !>([tid |]))
|
||||||
|
--
|
||||||
|
::
|
||||||
|
:: ames
|
||||||
|
::
|
||||||
|
++ v-ames
|
||||||
|
|%
|
||||||
|
++ peers
|
||||||
|
(scry (map ship ?(%alien %known)) %a %peers ~)
|
||||||
|
::
|
||||||
|
++ peer
|
||||||
|
|= who=ship
|
||||||
|
(scry ship-state:ames %a %peer /(scot %p who))
|
||||||
|
::
|
||||||
|
++ peer-to-json
|
||||||
|
=, ames
|
||||||
|
=, enjs:format
|
||||||
|
|= =ship-state
|
||||||
|
|^ ^- json
|
||||||
|
%+ frond -.ship-state
|
||||||
|
?- -.ship-state
|
||||||
|
%alien (alien +.ship-state)
|
||||||
|
%known (known +.ship-state)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ alien
|
||||||
|
|= alien-agenda
|
||||||
|
%- pairs
|
||||||
|
:~ 'messages'^(numb (lent messages))
|
||||||
|
'packets'^(numb ~(wyt in packets))
|
||||||
|
'heeds'^(set-array heeds from-duct)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
:: json for known peer is structures to closely match the peer-state type.
|
||||||
|
:: where an index is specified, the array is generally sorted by those.
|
||||||
|
::
|
||||||
|
:: { life: 123,
|
||||||
|
:: route: { direct: true, lane: 'something' },
|
||||||
|
:: qos: { kind: 'status', last-contact: 123456 }, // ms timestamp
|
||||||
|
:: snd: [
|
||||||
|
:: { bone: 123, // index
|
||||||
|
:: duct: ['/paths', ...]
|
||||||
|
:: current: 123,
|
||||||
|
:: next: 123,
|
||||||
|
:: unsent-messages: [123, ...], // size in bytes
|
||||||
|
:: queued-message-acks: [{
|
||||||
|
:: message-num: 123, // index
|
||||||
|
:: ack: 'ok'
|
||||||
|
:: }, ...],
|
||||||
|
:: packet-pump-state: {
|
||||||
|
:: next-wake: 123456, // ms timestamp
|
||||||
|
:: live: [{
|
||||||
|
:: message-num: 123, // index
|
||||||
|
:: fragment-num: 123, // index
|
||||||
|
:: num-fragments: 123,
|
||||||
|
:: last-sent: 123456, // ms timestamp
|
||||||
|
:: retries: 123,
|
||||||
|
:: skips: 123
|
||||||
|
:: }, ...],
|
||||||
|
:: metrics: {
|
||||||
|
:: rto: 123, // seconds
|
||||||
|
:: rtt: 123, // seconds
|
||||||
|
:: rttvar: 123,
|
||||||
|
:: ssthresh: 123,
|
||||||
|
:: num-live: 123,
|
||||||
|
:: cwnd: 123,
|
||||||
|
:: counter: 123
|
||||||
|
:: }
|
||||||
|
:: }
|
||||||
|
:: },
|
||||||
|
:: ...],
|
||||||
|
:: rcv: [
|
||||||
|
:: { bone: 123, // index
|
||||||
|
:: duct: ['/paths', ...] // index
|
||||||
|
:: last-acked: 123,
|
||||||
|
:: last-heard: 123,
|
||||||
|
:: pending-vane-ack: [123, ...],
|
||||||
|
:: live-messages: [{
|
||||||
|
:: message-num: 123, // index
|
||||||
|
:: num-received: 122,
|
||||||
|
:: num-fragments: 123,
|
||||||
|
:: fragments: [123, ...]
|
||||||
|
:: }, ...],
|
||||||
|
:: nax: [123, ...]
|
||||||
|
:: },
|
||||||
|
:: ...],
|
||||||
|
:: nax: [{
|
||||||
|
:: bone: 123, // index
|
||||||
|
:: duct: ['/paths', ...],
|
||||||
|
:: message-num: 123
|
||||||
|
:: }, ...],
|
||||||
|
:: heeds: [['/paths', ...] ...]
|
||||||
|
:: }
|
||||||
|
::
|
||||||
|
++ known
|
||||||
|
|= peer-state
|
||||||
|
%- pairs
|
||||||
|
:~ 'life'^(numb life)
|
||||||
|
::
|
||||||
|
:- 'route'
|
||||||
|
%+ maybe route
|
||||||
|
|= [direct=? =lane]
|
||||||
|
%- pairs
|
||||||
|
:~ 'direct'^b+direct
|
||||||
|
'lane'^?-(-.lane %& (ship p.lane), %| s+(scot %x p.lane))
|
||||||
|
==
|
||||||
|
::
|
||||||
|
:- 'qos'
|
||||||
|
%- pairs
|
||||||
|
:~ 'kind'^s+-.qos
|
||||||
|
'last-contact'^(time last-contact.qos)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
:- 'snd'
|
||||||
|
:- %a
|
||||||
|
%+ turn (sort ~(tap by snd) aor) :: sort by bone
|
||||||
|
(cury snd-with-bone ossuary)
|
||||||
|
::
|
||||||
|
:- 'rcv'
|
||||||
|
:- %a
|
||||||
|
%+ turn (sort ~(tap by rcv) aor) :: sort by bone
|
||||||
|
(cury rcv-with-bone ossuary)
|
||||||
|
::
|
||||||
|
:- 'nax'
|
||||||
|
:- %a
|
||||||
|
%+ turn (sort ~(tap in nax) aor) :: sort by bone
|
||||||
|
|= [=bone =message-num]
|
||||||
|
%- pairs
|
||||||
|
:* 'message-num'^(numb message-num)
|
||||||
|
(bone-to-pairs bone ossuary)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
'heeds'^(set-array heeds from-duct)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ snd-with-bone
|
||||||
|
|= [=ossuary =bone message-pump-state]
|
||||||
|
^- json
|
||||||
|
%- pairs
|
||||||
|
:* 'current'^(numb current)
|
||||||
|
'next'^(numb next)
|
||||||
|
::
|
||||||
|
:- 'unsent-messages' :: as byte sizes
|
||||||
|
(set-array unsent-messages (cork (cury met 3) numb))
|
||||||
|
::
|
||||||
|
'unsent-fragments'^(numb (lent unsent-fragments)) :: as lent
|
||||||
|
::
|
||||||
|
:- 'queued-message-acks'
|
||||||
|
:- %a
|
||||||
|
%+ turn (sort ~(tap by queued-message-acks) aor) :: sort by msg nr
|
||||||
|
|= [=message-num =ack]
|
||||||
|
%- pairs
|
||||||
|
:~ 'message-num'^(numb message-num)
|
||||||
|
'ack'^s+-.ack
|
||||||
|
==
|
||||||
|
::
|
||||||
|
:- 'packet-pump-state'
|
||||||
|
%- pairs
|
||||||
|
=, packet-pump-state
|
||||||
|
:~ 'next-wake'^(maybe next-wake time)
|
||||||
|
::
|
||||||
|
:- 'live'
|
||||||
|
:- %a
|
||||||
|
%+ turn (sort ~(tap in live) aor) :: sort by msg nr & frg nr
|
||||||
|
|= [live-packet-key live-packet-val]
|
||||||
|
%- pairs
|
||||||
|
:~ 'message-num'^(numb message-num)
|
||||||
|
'fragment-num'^(numb fragment-num)
|
||||||
|
'num-fragments'^(numb num-fragments)
|
||||||
|
'last-sent'^(time last-sent)
|
||||||
|
'retries'^(numb retries)
|
||||||
|
'skips'^(numb skips)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
:- 'metrics'
|
||||||
|
%- pairs
|
||||||
|
=, metrics
|
||||||
|
:~ 'rto'^(numb (div rto ~s1)) ::TODO milliseconds?
|
||||||
|
'rtt'^(numb (div rtt ~s1))
|
||||||
|
'rttvar'^(numb (div rttvar ~s1))
|
||||||
|
'ssthresh'^(numb ssthresh)
|
||||||
|
'num-live'^(numb num-live)
|
||||||
|
'cwnd'^(numb cwnd)
|
||||||
|
'counter'^(numb counter)
|
||||||
|
==
|
||||||
|
==
|
||||||
|
::
|
||||||
|
(bone-to-pairs bone ossuary)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ rcv-with-bone
|
||||||
|
|= [=ossuary =bone message-sink-state]
|
||||||
|
^- json
|
||||||
|
%- pairs
|
||||||
|
:* 'last-acked'^(numb last-acked)
|
||||||
|
'last-heard'^(numb last-heard)
|
||||||
|
::
|
||||||
|
:- 'pending-vane-ack'
|
||||||
|
=- a+(turn - numb)
|
||||||
|
(sort (turn ~(tap in pending-vane-ack) head) aor) :: sort by msg #
|
||||||
|
::
|
||||||
|
:- 'live-messages'
|
||||||
|
:- %a
|
||||||
|
%+ turn (sort ~(tap by live-messages) aor) :: sort by msg #
|
||||||
|
|= [=message-num partial-rcv-message]
|
||||||
|
%- pairs
|
||||||
|
:~ 'message-num'^(numb message-num)
|
||||||
|
'num-received'^(numb num-received)
|
||||||
|
'num-fragments'^(numb num-fragments)
|
||||||
|
'fragments'^(set-array ~(key by fragments) numb)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
'nax'^(set-array nax numb)
|
||||||
|
::
|
||||||
|
(bone-to-pairs bone ossuary)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ bone-to-pairs
|
||||||
|
|= [=bone ossuary]
|
||||||
|
^- (list [@t json])
|
||||||
|
:~ 'bone'^(numb bone)
|
||||||
|
'duct'^(from-duct (~(gut by by-bone) bone ~))
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ maybe
|
||||||
|
|* [unit=(unit) enjs=$-(* json)]
|
||||||
|
^- json
|
||||||
|
?~ unit ~
|
||||||
|
(enjs u.unit)
|
||||||
|
::
|
||||||
|
++ set-array
|
||||||
|
|* [set=(set) enjs=$-(* json)]
|
||||||
|
^- json
|
||||||
|
a+(turn ~(tap in set) enjs)
|
||||||
|
::
|
||||||
|
++ from-duct
|
||||||
|
|= =duct
|
||||||
|
a+(turn duct path)
|
||||||
|
--
|
||||||
|
--
|
||||||
|
::
|
||||||
|
:: behn
|
||||||
|
::
|
||||||
|
++ v-behn
|
||||||
|
|%
|
||||||
|
++ timers
|
||||||
|
(scry ,(list [date=@da =duct]) %b %timers ~)
|
||||||
|
--
|
||||||
|
::
|
||||||
|
:: clay
|
||||||
|
::
|
||||||
|
::TODO depends on new clay changes (%s care)
|
||||||
|
++ v-clay
|
||||||
|
=, clay
|
||||||
|
|%
|
||||||
|
++ start-path /(scot %p our.bowl)/home/(scot %da now.bowl)
|
||||||
|
::
|
||||||
|
+$ commit
|
||||||
|
[=tako parents=(list tako) children=(list tako) wen=@da content-hash=@uvI]
|
||||||
|
::
|
||||||
|
++ commits-json
|
||||||
|
^- json
|
||||||
|
=+ .^(desks=(set desk) %cd start-path)
|
||||||
|
=/ heads=(list [tako desk])
|
||||||
|
%+ turn ~(tap in desks)
|
||||||
|
|= =desk
|
||||||
|
=+ .^(=dome %cv /(scot %p our.bowl)/[desk]/(scot %da now.bowl))
|
||||||
|
=/ =tako (~(got by hit.dome) let.dome)
|
||||||
|
[tako desk]
|
||||||
|
=/ yakis=(set yaki)
|
||||||
|
%- silt
|
||||||
|
^- (list yaki)
|
||||||
|
%- zing
|
||||||
|
%+ turn heads
|
||||||
|
|= [=tako =desk]
|
||||||
|
(trace-tako tako)
|
||||||
|
=/ commits=(list commit) (yakis-to-commits ~(tap in yakis))
|
||||||
|
=, enjs:format
|
||||||
|
%: pairs
|
||||||
|
head+(pairs (turn heads |=([=tako =desk] (scot %uv tako)^s+desk)))
|
||||||
|
commits+(commits-to-json commits)
|
||||||
|
~
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ yakis-to-commits
|
||||||
|
|= yakis=(list yaki)
|
||||||
|
^- (list commit)
|
||||||
|
%+ turn yakis
|
||||||
|
|= =yaki
|
||||||
|
:* r.yaki p.yaki
|
||||||
|
=/ candidates
|
||||||
|
%+ turn
|
||||||
|
(skim yakis |=(can=^yaki (lien p.can |=(=tako =(r.yaki tako)))))
|
||||||
|
|= can=^yaki
|
||||||
|
r.can
|
||||||
|
~(tap in (silt candidates))
|
||||||
|
t.yaki
|
||||||
|
.^(@uvI %cs (weld start-path /hash/(scot %uv r.yaki)))
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ trace-tako
|
||||||
|
|= =tako
|
||||||
|
~+
|
||||||
|
^- (list yaki)
|
||||||
|
=+ .^(=yaki %cs (weld start-path /yaki/(scot %uv tako)))
|
||||||
|
:- yaki
|
||||||
|
(zing (turn p.yaki trace-tako))
|
||||||
|
::
|
||||||
|
++ commits-to-json
|
||||||
|
|= commits=(list commit)
|
||||||
|
^- json
|
||||||
|
:- %a
|
||||||
|
%+ turn
|
||||||
|
%+ sort commits
|
||||||
|
|= [a=commit b=commit]
|
||||||
|
(gte wen.a wen.b)
|
||||||
|
|= =commit
|
||||||
|
(commit-to-json commit)
|
||||||
|
::
|
||||||
|
++ commit-to-json
|
||||||
|
|= =commit
|
||||||
|
^- json
|
||||||
|
=, enjs:format
|
||||||
|
%: pairs
|
||||||
|
'commitHash'^(tako-to-json tako.commit)
|
||||||
|
parents+a+(turn parents.commit tako-to-json)
|
||||||
|
children+a+(turn children.commit tako-to-json)
|
||||||
|
'contentHash'^(tako-to-json content-hash.commit)
|
||||||
|
~
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ tako-to-json
|
||||||
|
|= =tako
|
||||||
|
^- json
|
||||||
|
s+(scot %uv tako)
|
||||||
|
--
|
||||||
|
::
|
||||||
|
:: eyre
|
||||||
|
::
|
||||||
|
++ v-eyre
|
||||||
|
|%
|
||||||
|
++ bindings
|
||||||
|
=, eyre
|
||||||
|
(scry ,(list [=binding =duct =action]) %e %bindings ~)
|
||||||
|
--
|
||||||
|
::
|
||||||
|
:: helpers
|
||||||
|
::
|
||||||
|
++ poke
|
||||||
|
|= [=wire app=term =mark =vase]
|
||||||
|
^- card
|
||||||
|
[%pass wire %agent [our.bowl app] %poke mark vase]
|
||||||
|
::
|
||||||
|
++ scry
|
||||||
|
|* [=mold care=term =desk =path]
|
||||||
|
.^(mold care (scot %p our.bowl) desk (scot %da now.bowl) path)
|
||||||
|
--
|
183
pkg/interface/dbug/gulpfile.js
Normal file
183
pkg/interface/dbug/gulpfile.js
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
var gulp = require('gulp');
|
||||||
|
var cssimport = require('gulp-cssimport');
|
||||||
|
var rollup = require('gulp-better-rollup');
|
||||||
|
var cssnano = require('cssnano');
|
||||||
|
var postcss = require('gulp-postcss');
|
||||||
|
var sucrase = require('@sucrase/gulp-plugin');
|
||||||
|
var minify = require('gulp-minify');
|
||||||
|
var rename = require('gulp-rename');
|
||||||
|
var del = require('del');
|
||||||
|
|
||||||
|
var resolve = require('rollup-plugin-node-resolve');
|
||||||
|
var commonjs = require('rollup-plugin-commonjs');
|
||||||
|
var rootImport = require('rollup-plugin-root-import');
|
||||||
|
var globals = require('rollup-plugin-node-globals');
|
||||||
|
|
||||||
|
/***
|
||||||
|
Main config options
|
||||||
|
***/
|
||||||
|
|
||||||
|
var urbitrc = require('../urbitrc');
|
||||||
|
|
||||||
|
/***
|
||||||
|
End main config options
|
||||||
|
***/
|
||||||
|
|
||||||
|
gulp.task('css-bundle', function() {
|
||||||
|
let plugins = [
|
||||||
|
cssnano()
|
||||||
|
];
|
||||||
|
return gulp
|
||||||
|
.src('src/index.css')
|
||||||
|
.pipe(cssimport())
|
||||||
|
.pipe(postcss(plugins))
|
||||||
|
.pipe(gulp.dest('../../arvo/app/debug/css'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('jsx-transform', function(cb) {
|
||||||
|
return gulp.src('src/**/*.js')
|
||||||
|
.pipe(sucrase({
|
||||||
|
transforms: ['jsx']
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('dist'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('tile-jsx-transform', function(cb) {
|
||||||
|
return gulp.src('tile/**/*.js')
|
||||||
|
.pipe(sucrase({
|
||||||
|
transforms: ['jsx']
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('dist'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('js-imports', function(cb) {
|
||||||
|
return gulp.src('dist/index.js')
|
||||||
|
.pipe(rollup({
|
||||||
|
plugins: [
|
||||||
|
commonjs({
|
||||||
|
namedExports: {
|
||||||
|
'node_modules/react/index.js': [ 'Component', 'createRef', 'createElement', 'useState', 'useRef', 'useEffect', 'Fragment' ],
|
||||||
|
'node_modules/react-is/index.js': [ 'isValidElementType' ],
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
rootImport({
|
||||||
|
root: `${__dirname}/dist/js`,
|
||||||
|
useEntry: 'prepend',
|
||||||
|
extensions: '.js'
|
||||||
|
}),
|
||||||
|
globals(),
|
||||||
|
resolve()
|
||||||
|
]
|
||||||
|
}, 'umd'))
|
||||||
|
.on('error', function(e){
|
||||||
|
console.log(e);
|
||||||
|
cb();
|
||||||
|
})
|
||||||
|
.pipe(gulp.dest('../../arvo/app/debug/js/'))
|
||||||
|
.on('end', cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('tile-js-imports', function(cb) {
|
||||||
|
return gulp.src('dist/tile.js')
|
||||||
|
.pipe(rollup({
|
||||||
|
plugins: [
|
||||||
|
commonjs({
|
||||||
|
namedExports: {
|
||||||
|
'node_modules/react/index.js': [ 'Component' ],
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
rootImport({
|
||||||
|
root: `${__dirname}/dist/js`,
|
||||||
|
useEntry: 'prepend',
|
||||||
|
extensions: '.js'
|
||||||
|
}),
|
||||||
|
globals(),
|
||||||
|
resolve()
|
||||||
|
]
|
||||||
|
}, 'umd'))
|
||||||
|
.on('error', function(e){
|
||||||
|
console.log(e);
|
||||||
|
cb();
|
||||||
|
})
|
||||||
|
.pipe(gulp.dest('../../arvo/app/debug/js/'))
|
||||||
|
.on('end', cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('js-minify', function () {
|
||||||
|
return gulp.src('../../arvo/app/debug/js/index.js')
|
||||||
|
.pipe(minify())
|
||||||
|
.pipe(gulp.dest('../../arvo/app/debug/js/'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('tile-js-minify', function () {
|
||||||
|
return gulp.src('../../arvo/app/debug/js/tile.js')
|
||||||
|
.pipe(minify())
|
||||||
|
.pipe(gulp.dest('../../arvo/app/debug/js/'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('rename-index-min', function() {
|
||||||
|
return gulp.src('../../arvo/app/debug/js/index-min.js')
|
||||||
|
.pipe(rename('index.js'))
|
||||||
|
.pipe(gulp.dest('../../arvo/app/debug/js/'))
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('rename-tile-min', function() {
|
||||||
|
return gulp.src('../../arvo/app/debug/js/tile-min.js')
|
||||||
|
.pipe(rename('tile.js'))
|
||||||
|
.pipe(gulp.dest('../../arvo/app/debug/js/'))});
|
||||||
|
|
||||||
|
gulp.task('clean-min', function() {
|
||||||
|
return del(['../../arvo/app/debug/js/index-min.js', '../../arvo/app/debug/js/tile-min.js'], {force: true})
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('urbit-copy', function () {
|
||||||
|
let ret = gulp.src('../../arvo/**/*');
|
||||||
|
|
||||||
|
urbitrc.URBIT_PIERS.forEach(function(pier) {
|
||||||
|
ret = ret.pipe(gulp.dest(pier));
|
||||||
|
});
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('js-bundle-dev', gulp.series('jsx-transform', 'js-imports'));
|
||||||
|
gulp.task('tile-js-bundle-dev', gulp.series('tile-jsx-transform', 'tile-js-imports'));
|
||||||
|
gulp.task('js-bundle-prod', gulp.series('jsx-transform', 'js-imports', 'js-minify'))
|
||||||
|
gulp.task('tile-js-bundle-prod',
|
||||||
|
gulp.series('tile-jsx-transform', 'tile-js-imports', 'tile-js-minify'));
|
||||||
|
|
||||||
|
gulp.task('bundle-dev',
|
||||||
|
gulp.series(
|
||||||
|
gulp.parallel(
|
||||||
|
'css-bundle',
|
||||||
|
'js-bundle-dev',
|
||||||
|
'tile-js-bundle-dev'
|
||||||
|
),
|
||||||
|
'urbit-copy'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
gulp.task('bundle-prod',
|
||||||
|
gulp.series(
|
||||||
|
gulp.parallel(
|
||||||
|
'css-bundle',
|
||||||
|
'js-bundle-prod',
|
||||||
|
'tile-js-bundle-prod',
|
||||||
|
),
|
||||||
|
'rename-index-min',
|
||||||
|
'rename-tile-min',
|
||||||
|
'clean-min',
|
||||||
|
'urbit-copy'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
gulp.task('default', gulp.series('bundle-dev'));
|
||||||
|
|
||||||
|
gulp.task('watch', gulp.series('default', function() {
|
||||||
|
gulp.watch('tile/**/*.js', gulp.parallel('tile-js-bundle-dev'));
|
||||||
|
|
||||||
|
gulp.watch('src/**/*.js', gulp.parallel('js-bundle-dev'));
|
||||||
|
gulp.watch('src/**/*.css', gulp.parallel('css-bundle'));
|
||||||
|
|
||||||
|
gulp.watch('../../arvo/**/*', gulp.parallel('urbit-copy'));
|
||||||
|
}));
|
BIN
pkg/interface/dbug/package-lock.json
generated
Normal file
BIN
pkg/interface/dbug/package-lock.json
generated
Normal file
Binary file not shown.
43
pkg/interface/dbug/package.json
Normal file
43
pkg/interface/dbug/package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "urbit-apps",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@sucrase/gulp-plugin": "^2.0.0",
|
||||||
|
"cssnano": "^4.1.10",
|
||||||
|
"gulp": "^4.0.0",
|
||||||
|
"gulp-better-rollup": "^4.0.1",
|
||||||
|
"gulp-cssimport": "^7.0.0",
|
||||||
|
"gulp-minify": "^3.1.0",
|
||||||
|
"gulp-postcss": "^8.0.0",
|
||||||
|
"gulp-rename": "^1.4.0",
|
||||||
|
"moment": "^2.24.0",
|
||||||
|
"rollup": "^1.6.0",
|
||||||
|
"rollup-plugin-commonjs": "^9.2.0",
|
||||||
|
"rollup-plugin-node-globals": "^1.4.0",
|
||||||
|
"rollup-plugin-node-resolve": "^4.0.0",
|
||||||
|
"rollup-plugin-root-import": "^0.2.3",
|
||||||
|
"sucrase": "^3.8.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@gitgraph/react": "^1.5.4",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
|
"del": "^5.1.0",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"mousetrap": "^1.6.3",
|
||||||
|
"react": "^16.5.2",
|
||||||
|
"react-dom": "^16.8.6",
|
||||||
|
"react-router-dom": "^5.0.0",
|
||||||
|
"urbit-ob": "^5.0.0",
|
||||||
|
"urbit-sigil-js": "^1.3.2"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"natives": "1.1.3"
|
||||||
|
}
|
||||||
|
}
|
217
pkg/interface/dbug/src/css/custom.css
Normal file
217
pkg/interface/dbug/src/css/custom.css
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, h1, h2, h3, h4, h5, h6, a, input, textarea, button {
|
||||||
|
margin-block-end: unset;
|
||||||
|
margin-block-start: unset;
|
||||||
|
-webkit-margin-before: unset;
|
||||||
|
-webkit-margin-after: unset;
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, summary {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inter {
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clamp-3 {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clamp-message {
|
||||||
|
max-width: calc(100% - 36px - 1.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clamp-attachment {
|
||||||
|
overflow: scroll;
|
||||||
|
max-height: 10em;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lh-16 {
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mono {
|
||||||
|
font-family: "Source Code Pro", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-ship {
|
||||||
|
line-height: 2.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-welcome-green {
|
||||||
|
background-color: #ECF6F2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c-default {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m0a {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mix-blend-diff {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
}
|
||||||
|
|
||||||
|
.focus-b--black:focus {
|
||||||
|
border-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed-container {
|
||||||
|
position: relative;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-bottom: 56.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed-container iframe, .embed-container object, .embed-container embed {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* spinner */
|
||||||
|
|
||||||
|
.spin-active {
|
||||||
|
animation: spin 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {transform: rotate(0deg);}
|
||||||
|
25% {transform: rotate(90deg);}
|
||||||
|
50% {transform: rotate(180deg);}
|
||||||
|
75% {transform: rotate(270deg);}
|
||||||
|
100% {transform: rotate(360deg);}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* toggler checkbox */
|
||||||
|
|
||||||
|
.toggle::after {
|
||||||
|
content: "";
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
background: white;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle.checked::after {
|
||||||
|
left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* responsive */
|
||||||
|
@media all and (max-width: 34.375em) {
|
||||||
|
.dn-s {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.flex-basis-100-s, .flex-basis-full-s {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
.h-100-m-40-s {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
.black-s {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 34.375em) {
|
||||||
|
.db-ns {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.flex-basis-30-ns {
|
||||||
|
flex-basis: 30vw;
|
||||||
|
}
|
||||||
|
.h-100-m-40-ns {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
.bg-black-d {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
.white-d {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.gray1-d {
|
||||||
|
color: #4d4d4d;
|
||||||
|
}
|
||||||
|
.gray2-d {
|
||||||
|
color: #7f7f7f;
|
||||||
|
}
|
||||||
|
.gray3-d {
|
||||||
|
color: #b1b2b3;
|
||||||
|
}
|
||||||
|
.gray4-d {
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
.bg-gray0-d {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
.bg-gray1-d {
|
||||||
|
background-color: #4d4d4d;
|
||||||
|
}
|
||||||
|
.b--gray0-d {
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
.b--gray1-d {
|
||||||
|
border-color: #4d4d4d;
|
||||||
|
}
|
||||||
|
.b--gray2-d {
|
||||||
|
border-color: #7f7f7f;
|
||||||
|
}
|
||||||
|
.b--white-d {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
.bb-d {
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
.invert-d {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
.o-60-d {
|
||||||
|
opacity: .6;
|
||||||
|
}
|
||||||
|
.focus-b--white-d:focus {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.hover-bg-gray1-d:hover {
|
||||||
|
background-color: #4d4d4d;
|
||||||
|
}
|
||||||
|
}
|
63
pkg/interface/dbug/src/css/fonts.css
Normal file
63
pkg/interface/dbug/src/css/fonts.css
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url("https://media.urbit.org/fonts/Inter-Regular.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url("https://media.urbit.org/fonts/Inter-Italic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url("https://media.urbit.org/fonts/Inter-Bold.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url("https://media.urbit.org/fonts/Inter-BoldItalic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-extralight.woff");
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-light.woff");
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-regular.woff");
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-medium.woff");
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-semibold.woff");
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-bold.woff");
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
1
pkg/interface/dbug/src/css/indigo-static.css
Normal file
1
pkg/interface/dbug/src/css/indigo-static.css
Normal file
File diff suppressed because one or more lines are too long
4
pkg/interface/dbug/src/index.css
Normal file
4
pkg/interface/dbug/src/index.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@import "css/indigo-static.css";
|
||||||
|
@import "css/fonts.css";
|
||||||
|
@import "css/custom.css";
|
||||||
|
|
16
pkg/interface/dbug/src/index.js
Normal file
16
pkg/interface/dbug/src/index.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Root } from '/components/root';
|
||||||
|
import { api } from '/api';
|
||||||
|
import { store } from '/store';
|
||||||
|
import { subscription } from "/subscription";
|
||||||
|
|
||||||
|
api.setAuthTokens({
|
||||||
|
ship: window.ship
|
||||||
|
});
|
||||||
|
|
||||||
|
subscription.start();
|
||||||
|
|
||||||
|
ReactDOM.render((
|
||||||
|
<Root />
|
||||||
|
), document.querySelectorAll("#root")[0]);
|
210
pkg/interface/dbug/src/js/api.js
Normal file
210
pkg/interface/dbug/src/js/api.js
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { stringToTa } from '/lib/util';
|
||||||
|
import { store } from '/store';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
|
||||||
|
class UrbitApi {
|
||||||
|
setAuthTokens(authTokens) {
|
||||||
|
this.authTokens = authTokens;
|
||||||
|
this.bindPaths = [];
|
||||||
|
|
||||||
|
this.bind = this.bind.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(path, method, ship = this.authTokens.ship, app, success, fail, quit) {
|
||||||
|
this.bindPaths = _.uniq([...this.bindPaths, path]);
|
||||||
|
|
||||||
|
window.subscriptionId = window.urb.subscribe(ship, app, path,
|
||||||
|
(err) => {
|
||||||
|
fail(err);
|
||||||
|
},
|
||||||
|
(event) => {
|
||||||
|
success({
|
||||||
|
data: event,
|
||||||
|
from: {
|
||||||
|
ship,
|
||||||
|
path
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(qui) => {
|
||||||
|
quit(qui);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
action(appl, mark, data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
window.urb.poke(ship, appl, mark, data,
|
||||||
|
(json) => {
|
||||||
|
resolve(json);
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dbugAction(data) {
|
||||||
|
return this.action("dbug", "dbug-action", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bindToVerb(app) {
|
||||||
|
return this.bind('/verb/events', 'PUT', this.authTokens.ship, app,
|
||||||
|
(result) => {
|
||||||
|
console.log('verb result', result);
|
||||||
|
result.data.app = app;
|
||||||
|
store.handleEvent({data: { local: { verbResult: result.data }}});
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
store.handleEvent({data: { local: { verbStatus: {
|
||||||
|
app: app,
|
||||||
|
msg: 'failed to establish verb connection to ' + app
|
||||||
|
}}}});
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
store.handleEvent({data: { local: { verbStatus: {
|
||||||
|
app: app,
|
||||||
|
msg: 'verb connection to ' + app + ' was dropped'
|
||||||
|
}}}});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getJson(path, localTransform, onFail) {
|
||||||
|
let source = '/~debug' + path + '.json';
|
||||||
|
const query = window.location.href.split('?')[1];
|
||||||
|
if (query) source = source + '?' + query;
|
||||||
|
fetch(source)
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('Network response not ok');
|
||||||
|
onFail();
|
||||||
|
} else {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
store.handleEvent({data: { local: localTransform(data) }});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(`JSON fetch on ${source} failed:`, error);
|
||||||
|
onFail();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapLocal(name) {
|
||||||
|
return (data) => {
|
||||||
|
console.log('got data', data);
|
||||||
|
let e = {};
|
||||||
|
e[name] = data;
|
||||||
|
e['status'] = null; // clear previous status
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
showStatus(what) {
|
||||||
|
return () => {
|
||||||
|
store.handleEvent({data: { local: { 'status': what }}});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// apps
|
||||||
|
|
||||||
|
getApps() {
|
||||||
|
//TODO onfail render "failed to fetch apps"
|
||||||
|
//TODO generic "fail" local event that prints to status bar?
|
||||||
|
this.getJson('/app',
|
||||||
|
this.wrapLocal('apps'),
|
||||||
|
this.showStatus('error fetching apps')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAppDetails(app) {
|
||||||
|
this.getJson('/app/'+app, (data) => {
|
||||||
|
data.app = app;
|
||||||
|
return this.wrapLocal('app')(data);
|
||||||
|
},
|
||||||
|
() => { // on fail
|
||||||
|
store.handleEvent({data: { local: { 'appFailed': app } }});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// spider
|
||||||
|
|
||||||
|
getThreads() {
|
||||||
|
console.log('getting threads');
|
||||||
|
this.getJson('/spider/threads',
|
||||||
|
this.wrapLocal('threads'),
|
||||||
|
this.showStatus('error fetching threads')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ames
|
||||||
|
|
||||||
|
getPeers() {
|
||||||
|
this.getJson('/ames/peer',
|
||||||
|
this.wrapLocal('amesPeers'),
|
||||||
|
this.showStatus('error fetching ames peers')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPeer(who) {
|
||||||
|
this.getJson(`/ames/peer/${who}`, (data) => {
|
||||||
|
data.who = who;
|
||||||
|
return this.wrapLocal('amesPeer')(data);
|
||||||
|
},
|
||||||
|
this.showStatus('error fetching ames details for ' + who)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// behn
|
||||||
|
|
||||||
|
getTimers() {
|
||||||
|
this.getJson('/behn/timers',
|
||||||
|
this.wrapLocal('behnTimers'),
|
||||||
|
this.showStatus('error fetching behn timers')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clay
|
||||||
|
|
||||||
|
getCommits() {
|
||||||
|
this.getJson('/clay/commits',
|
||||||
|
this.wrapLocal('clayCommits'),
|
||||||
|
this.showStatus('error fetching clay commits')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eyre
|
||||||
|
|
||||||
|
getBindings() {
|
||||||
|
this.getJson('/eyre/bindings',
|
||||||
|
this.wrapLocal('eyreBindings'),
|
||||||
|
this.showStatus('error fetching eyre bindings')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// local
|
||||||
|
|
||||||
|
sidebarToggle() {
|
||||||
|
let sidebarBoolean = true;
|
||||||
|
if (store.state.sidebarShown === true) {
|
||||||
|
sidebarBoolean = false;
|
||||||
|
}
|
||||||
|
store.handleEvent({
|
||||||
|
data: {
|
||||||
|
local: {
|
||||||
|
'sidebarToggle': sidebarBoolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export let api = new UrbitApi();
|
||||||
|
window.api = api;
|
8
pkg/interface/dbug/src/js/components/loading.js
Normal file
8
pkg/interface/dbug/src/js/components/loading.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { MessageScreen } from '/components/lib/message-screen';
|
||||||
|
|
||||||
|
export class LoadingScreen extends Component {
|
||||||
|
render() {
|
||||||
|
return (<MessageScreen text="Loading..."/>);
|
||||||
|
}
|
||||||
|
}
|
111
pkg/interface/dbug/src/js/components/root.js
Normal file
111
pkg/interface/dbug/src/js/components/root.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { BrowserRouter, Switch, Route, Link } from "react-router-dom";
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import { api } from '/api';
|
||||||
|
import { subscription } from '/subscription';
|
||||||
|
import { store } from '/store';
|
||||||
|
import { Skeleton } from '/components/skeleton';
|
||||||
|
import { MessageScreen } from '/components/lib/message-screen';
|
||||||
|
import { Apps } from '/views/apps';
|
||||||
|
import { Spider } from '/views/spider';
|
||||||
|
import { Ames } from '/views/ames';
|
||||||
|
import { Behn } from '/views/behn';
|
||||||
|
import { Clay } from '/views/clay';
|
||||||
|
import { Eyre } from '/views/eyre';
|
||||||
|
import { makeRoutePath } from '../lib/util';
|
||||||
|
|
||||||
|
export class Root extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = store.state;
|
||||||
|
store.setStateHandler(this.setState.bind(this));
|
||||||
|
console.log('built root');
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
// preload spinner asset
|
||||||
|
new Image().src = "/~debug/img/Spinner.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { state } = this;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BrowserRouter><Switch>
|
||||||
|
<Route exact path="/~debug"
|
||||||
|
render={(props) => {
|
||||||
|
return (
|
||||||
|
<Skeleton status={state.status} selected="">
|
||||||
|
<MessageScreen text="select a component on the left" />
|
||||||
|
</Skeleton>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route exact path={makeRoutePath('apps')}
|
||||||
|
render={(props) => {
|
||||||
|
return (
|
||||||
|
<Skeleton status={state.status} selected="apps">
|
||||||
|
<Apps apps={state.apps} {...props}/>
|
||||||
|
</Skeleton>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route exact path={makeRoutePath('spider')}
|
||||||
|
render={(props) => {
|
||||||
|
return (
|
||||||
|
<Skeleton status={state.status} selected="spider">
|
||||||
|
<Spider threads={state.threads} {...props}/>
|
||||||
|
</Skeleton>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route exact path={makeRoutePath('ames')}
|
||||||
|
render={(props) => {
|
||||||
|
return (
|
||||||
|
<Skeleton status={state.status} selected="ames">
|
||||||
|
<Ames peers={state.peers} {...props}/>
|
||||||
|
</Skeleton>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route exact path={makeRoutePath('behn')}
|
||||||
|
render={(props) => {
|
||||||
|
return (
|
||||||
|
<Skeleton status={state.status} selected="behn">
|
||||||
|
<Behn timers={state.timers} {...props}/>
|
||||||
|
</Skeleton>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route exact path={makeRoutePath('clay')}
|
||||||
|
render={(props) => {
|
||||||
|
return (
|
||||||
|
<Skeleton status={state.status} selected="clay">
|
||||||
|
<Clay commits={state.commits} {...props}/>
|
||||||
|
</Skeleton>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route exact path={makeRoutePath('eyre')}
|
||||||
|
render={(props) => {
|
||||||
|
return (
|
||||||
|
<Skeleton status={state.status} selected="eyre">
|
||||||
|
<Eyre bindings={state.bindings} {...props}/>
|
||||||
|
</Skeleton>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Switch></BrowserRouter>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
48
pkg/interface/dbug/src/js/components/searchable-list.js
Normal file
48
pkg/interface/dbug/src/js/components/searchable-list.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
export class SearchableList extends Component {
|
||||||
|
// expected props:
|
||||||
|
// items: [{key: 'some key', jsx: <w/e>}, ...]
|
||||||
|
// placeholder: ''
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
query: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
this.updateQuery = this.updateQuery.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateQuery(event) {
|
||||||
|
this.setState({ query: event.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { state, props } = this;
|
||||||
|
|
||||||
|
const searchBar = (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder={props.placeholder}
|
||||||
|
onChange={this.updateQuery}
|
||||||
|
value={state.query}
|
||||||
|
style={{border: '1px solid black'}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
let items = props.items.filter(item => {
|
||||||
|
return (state.query === '') || item.key.includes(state.query);
|
||||||
|
})
|
||||||
|
if (items.length === 0) {
|
||||||
|
items = 'none';
|
||||||
|
} else {
|
||||||
|
items = items.map(item => (<div style={{marginTop: '4px'}}>{item.jsx}</div>));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<div style={{border: '1px solid grey', padding: '4px'}}>
|
||||||
|
<div>{searchBar}</div>
|
||||||
|
<div>{items}</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
}
|
81
pkg/interface/dbug/src/js/components/skeleton.js
Normal file
81
pkg/interface/dbug/src/js/components/skeleton.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { HeaderBar } from './lib/header-bar';
|
||||||
|
import { ChannelsSidebar } from './lib/channel-sidebar';
|
||||||
|
import { makeRoutePath } from '../lib/util';
|
||||||
|
|
||||||
|
class SidebarItem extends Component {
|
||||||
|
render() {
|
||||||
|
const { props } = this;
|
||||||
|
|
||||||
|
let selectedClass = (props.selected)
|
||||||
|
? "bg-gray5 bg-gray1-d"
|
||||||
|
: "pointer hover-bg-gray5 hover-bg-gray1-d";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link to={makeRoutePath(props.what, true)} key="what">
|
||||||
|
<div className={"w-100 v-mid f9 ph4 z1 pv1 " + selectedClass}>
|
||||||
|
<p className="f9 dib">{props.what}</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Skeleton extends Component {
|
||||||
|
render() {
|
||||||
|
const { props } = this;
|
||||||
|
|
||||||
|
let items = [
|
||||||
|
'apps',
|
||||||
|
'spider',
|
||||||
|
'ames',
|
||||||
|
'behn',
|
||||||
|
//TODO 'clay',
|
||||||
|
'eyre'
|
||||||
|
];
|
||||||
|
items = items.map(what => {
|
||||||
|
return (<SidebarItem what={what} selected={props.selected === what}/>);
|
||||||
|
});
|
||||||
|
|
||||||
|
let rightPanelHide = this.props.rightPanelHide
|
||||||
|
? "dn-s" : "";
|
||||||
|
|
||||||
|
const status = props.status
|
||||||
|
? (<div style={{
|
||||||
|
position: 'absolute', right: '16px', bottom: '16px',
|
||||||
|
padding: '8px', border: '1px solid #e22'
|
||||||
|
}}>
|
||||||
|
{props.status}
|
||||||
|
</div>)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="absolute h-100 w-100 mono">
|
||||||
|
<div className="cf w-100 h-100 flex">
|
||||||
|
<div className="bn br-m br-l br-xl b--gray4 b--gray1-d lh-copy h-100 flex-shrink-0 mw5-m mw5-l mw5-xl pt3 pt0-m pt0-l pt0-xl relative">
|
||||||
|
<a className="db dn-m dn-l dn-xl f8 pb3 pl3" href="/">⟵ Landscape</a>
|
||||||
|
<div className="overflow-y-scroll h-100">
|
||||||
|
<div className="w-100 bg-transparent">
|
||||||
|
<Link
|
||||||
|
className="dib f9 pointer green2 gray4-d pa4"
|
||||||
|
to={"/~chat/join/~/~dopzod/urbit-help"}>
|
||||||
|
Get help
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
{items}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{status}
|
||||||
|
<div className={"h-100 w-100 flex-auto overflow-scroll relative " + rightPanelHide} style={{
|
||||||
|
flexGrow: 1,
|
||||||
|
padding: '8px'
|
||||||
|
}}>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
37
pkg/interface/dbug/src/js/components/summary.js
Normal file
37
pkg/interface/dbug/src/js/components/summary.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
export class Summary extends Component {
|
||||||
|
// expected props:
|
||||||
|
// id: 'id'
|
||||||
|
// summary: <jsx>
|
||||||
|
// details: <jsx>
|
||||||
|
// onOpen: function(id)
|
||||||
|
// onClose: function(id)
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.onToggle = this.onToggle.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onToggle(event) {
|
||||||
|
console.log('toggle for', this.props.id);
|
||||||
|
if (event.target.open) {
|
||||||
|
if (this.props.onOpen) this.props.onOpen(this.props.id);
|
||||||
|
} else {
|
||||||
|
if (this.props.onClose) this.props.onClose(this.props.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { props } = this;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<details onToggle={this.onToggle} {...props} style={{border: '1px solid black', padding: '4px', ...props.style}}>
|
||||||
|
<summary>
|
||||||
|
{props.summary}
|
||||||
|
</summary>
|
||||||
|
<div style={{borderTop: '1px solid black'}}>{props.details}</div>
|
||||||
|
</details>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
32
pkg/interface/dbug/src/js/lib/util.js
Normal file
32
pkg/interface/dbug/src/js/lib/util.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
export function makeRoutePath(resource, includeQuery = false) {
|
||||||
|
let query = window.location.href.split('?')[1];
|
||||||
|
if (includeQuery && query) {
|
||||||
|
query = '?' + query;
|
||||||
|
} else {
|
||||||
|
query = '';
|
||||||
|
}
|
||||||
|
return '/~debug/' + resource + query;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function msToDa(ms, mil) {
|
||||||
|
const d = new Date(ms);
|
||||||
|
var fil = function(n) {
|
||||||
|
return n >= 10 ? n : "0" + n;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
`~${d.getUTCFullYear()}.` +
|
||||||
|
`${(d.getUTCMonth() + 1)}.` +
|
||||||
|
`${fil(d.getUTCDate())}..` +
|
||||||
|
`${fil(d.getUTCHours())}.` +
|
||||||
|
`${fil(d.getUTCMinutes())}.` +
|
||||||
|
`${fil(d.getUTCSeconds())}` +
|
||||||
|
`${mil ? "..0000" : ""}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderDuct(duct) {
|
||||||
|
return duct.reduce((a, b) => a + b + ' ', '');
|
||||||
|
}
|
142
pkg/interface/dbug/src/js/reducers/local.js
Normal file
142
pkg/interface/dbug/src/js/reducers/local.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export class LocalReducer {
|
||||||
|
reduce(json, state) {
|
||||||
|
const data = _.get(json, 'local', false);
|
||||||
|
if (data) {
|
||||||
|
this.status(data, state);
|
||||||
|
//
|
||||||
|
this.apps(data, state);
|
||||||
|
this.app(data, state);
|
||||||
|
this.appFailed(data, state);
|
||||||
|
this.verbResult(data, state);
|
||||||
|
this.verbStatus(data, state);
|
||||||
|
//
|
||||||
|
this.threads(data, state);
|
||||||
|
//
|
||||||
|
this.amesPeers(data, state);
|
||||||
|
this.amesPeer(data, state);
|
||||||
|
//
|
||||||
|
this.behnTimers(data, state);
|
||||||
|
//
|
||||||
|
this.clayCommits(data, state);
|
||||||
|
//
|
||||||
|
this.eyreBindings(data, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status(obj, state) {
|
||||||
|
const data = _.get(obj, 'status', false);
|
||||||
|
if (data) {
|
||||||
|
state.status = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// apps
|
||||||
|
|
||||||
|
apps(obj, state) {
|
||||||
|
const data = _.get(obj, 'apps', false);
|
||||||
|
if (data) {
|
||||||
|
state.apps = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app(obj, state) {
|
||||||
|
const data = _.get(obj, 'app', false);
|
||||||
|
if (data) {
|
||||||
|
state.apps[data.app] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appFailed(obj, state) {
|
||||||
|
const data = _.get(obj, 'appFailed', false);
|
||||||
|
if (data) {
|
||||||
|
console.log('loading app deets failed', data);
|
||||||
|
state.apps[data] = { noDebug: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verbResult(obj, state) {
|
||||||
|
const data = _.get(obj, 'verbResult', false);
|
||||||
|
if (data) {
|
||||||
|
if (!state.apps[data.app]) state.apps[data.app] = {};
|
||||||
|
if (!state.apps[data.app].events) state.apps[data.app].events = [];
|
||||||
|
let msg = 'some event';
|
||||||
|
if (data['on-init']) msg = '+on-init';
|
||||||
|
if (data['on-load']) msg = '+on-load';
|
||||||
|
if (data['on-poke']) msg = '+on-poke with mark ' + data['on-poke'];
|
||||||
|
if (data['on-watch']) msg = '+on-watch at path ' + data['on-watch'];
|
||||||
|
if (data['on-leave']) msg = '+on-leave on path ' + data['on-leave'];
|
||||||
|
if (data['on-agent']) msg = '+on-agent at wire ' + data['on-agent'].wire +
|
||||||
|
' with sign ' + data['on-agent'].sign;
|
||||||
|
if (data['on-arvo']) msg = '+on-arvo at wire ' + data['on-arvo'].wire +
|
||||||
|
' from vane ' + data['on-arvo'].vane +
|
||||||
|
' with sign ' + data['on-arvo'].sign;
|
||||||
|
if (data['on-fail']) msg = '+on-fail on ' + data['on-fail'];
|
||||||
|
state.apps[data.app].events.push(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verbStatus(obj, state) {
|
||||||
|
const data = _.get(obj, 'verbStatus', false);
|
||||||
|
if (data) {
|
||||||
|
if (!state.apps[data.app]) state.apps[data.app] = {};
|
||||||
|
if (!state.apps[data.app].events) state.apps[data.app].events = [];
|
||||||
|
state.apps[data.app].events.push(data.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// spider
|
||||||
|
|
||||||
|
threads(obj, state) {
|
||||||
|
const data = _.get(obj, 'threads', false);
|
||||||
|
if (data) {
|
||||||
|
state.threads = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ames
|
||||||
|
|
||||||
|
amesPeers(obj, state) {
|
||||||
|
const data = _.get(obj, 'amesPeers', false);
|
||||||
|
if (data) {
|
||||||
|
state.peers.known = data.known;
|
||||||
|
state.peers.alien = data.alien;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
amesPeer(obj, state) {
|
||||||
|
const data = _.get(obj, 'amesPeer', false);
|
||||||
|
if (data) {
|
||||||
|
state.peers.deets[data.who] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// behn
|
||||||
|
|
||||||
|
behnTimers(obj, state) {
|
||||||
|
const data = _.get(obj, 'behnTimers', false);
|
||||||
|
if (data) {
|
||||||
|
state.timers = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clay
|
||||||
|
|
||||||
|
clayCommits(obj, state) {
|
||||||
|
const data = _.get(obj, 'clayCommits', false);
|
||||||
|
if (data) {
|
||||||
|
console.log('clay comms', data);
|
||||||
|
state.commits = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eyre
|
||||||
|
|
||||||
|
eyreBindings(obj, state) {
|
||||||
|
const data = _.get(obj, 'eyreBindings', false);
|
||||||
|
if (data) {
|
||||||
|
state.bindings = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
pkg/interface/dbug/src/js/store.js
Normal file
45
pkg/interface/dbug/src/js/store.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { InitialReducer } from '/reducers/initial';
|
||||||
|
import { LocalReducer } from '/reducers/local.js';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
constructor() {
|
||||||
|
this.state = {
|
||||||
|
status: null,
|
||||||
|
apps: {},
|
||||||
|
threads: {},
|
||||||
|
peers: { known: [], alien: [], deets: {}},
|
||||||
|
timers: [],
|
||||||
|
commits: [],
|
||||||
|
bindings: [],
|
||||||
|
sidebarShown: true
|
||||||
|
};
|
||||||
|
|
||||||
|
this.initialReducer = new InitialReducer();
|
||||||
|
this.localReducer = new LocalReducer();
|
||||||
|
this.setState = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
setStateHandler(setState) {
|
||||||
|
this.setState = setState;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(data) {
|
||||||
|
let json;
|
||||||
|
if (data.data) {
|
||||||
|
json = data.data;
|
||||||
|
} else {
|
||||||
|
json = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('event', json);
|
||||||
|
this.initialReducer.reduce(json, this.state);
|
||||||
|
this.localReducer.reduce(json, this.state);
|
||||||
|
|
||||||
|
this.setState(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let store = new Store();
|
||||||
|
window.store = store;
|
31
pkg/interface/dbug/src/js/subscription.js
Normal file
31
pkg/interface/dbug/src/js/subscription.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { api } from '/api';
|
||||||
|
import { store } from '/store';
|
||||||
|
|
||||||
|
export class Subscription {
|
||||||
|
start() {
|
||||||
|
if (api.authTokens) {
|
||||||
|
//
|
||||||
|
} else {
|
||||||
|
console.error("~~~ ERROR: Must set api.authTokens before operation ~~~");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(diff) {
|
||||||
|
store.handleEvent(diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleError(err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleQuitSilently(quit) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
handleQuitAndResubscribe(quit) {
|
||||||
|
// TODO: resubscribe
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export let subscription = new Subscription();
|
297
pkg/interface/dbug/src/js/views/ames.js
Normal file
297
pkg/interface/dbug/src/js/views/ames.js
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Spinner } from '../components/lib/icons/icon-spinner';
|
||||||
|
import { Subscriptions } from '../components/subscriptions';
|
||||||
|
import { Route, Link } from 'react-router-dom';
|
||||||
|
import { msToDa, renderDuct } from '../lib/util';
|
||||||
|
import urbitOb from 'urbit-ob';
|
||||||
|
import { Summary } from '../components/summary';
|
||||||
|
import { SearchableList } from '../components/searchable-list';
|
||||||
|
|
||||||
|
export class Ames extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
opened: new Set()
|
||||||
|
};
|
||||||
|
|
||||||
|
this.loadPeers = this.loadPeers.bind(this);
|
||||||
|
this.loadPeerDetails = this.loadPeerDetails.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { known, alien } = this.props.peers;
|
||||||
|
if (known.length === 0 && alien.length === 0) {
|
||||||
|
this.loadPeers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
const { props, state } = this;
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPeers() {
|
||||||
|
api.getPeers();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPeerDetails(who) {
|
||||||
|
api.getPeer(who);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDucts(ducts) {
|
||||||
|
const items = ducts.map(duct => {
|
||||||
|
return {
|
||||||
|
key: duct.join(' '),
|
||||||
|
jsx: (<div>{renderDuct(duct)}</div>)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return <SearchableList placeholder="duct" items={items}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO use classes for styling?
|
||||||
|
render() {
|
||||||
|
const { props, state } = this;
|
||||||
|
const { known, alien, deets } = props.peers;
|
||||||
|
|
||||||
|
const renderDetails = (who) => {
|
||||||
|
const peer = deets[who];
|
||||||
|
console.log('deets', props.peers, deets, peer, who);
|
||||||
|
if (!peer) {
|
||||||
|
return 'Loading...';
|
||||||
|
} else if (peer.alien) {
|
||||||
|
return (<>
|
||||||
|
Pending messages: {peer.alien.messages}
|
||||||
|
Pending packets: {peer.alien.packets}
|
||||||
|
Heeds: {this.renderDucts(peer.alien.heeds)}
|
||||||
|
</>);
|
||||||
|
} else if (peer.known) {
|
||||||
|
const p = peer.known;
|
||||||
|
|
||||||
|
const status = (<>
|
||||||
|
<h4 style={{marginTop: '1em'}}>status</h4>
|
||||||
|
<table><tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="inter">Life</td>
|
||||||
|
<td>{p.life}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="inter">Route</td>
|
||||||
|
<td>
|
||||||
|
{ p.route
|
||||||
|
? `${p.route.direct ? '' : 'in'}direct, on lane ${p.route.lane}`
|
||||||
|
: 'none'
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="inter">QoS</td>
|
||||||
|
<td>
|
||||||
|
{p.qos.kind},
|
||||||
|
last contact {msToDa(p.qos['last-contact'])}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</>);
|
||||||
|
|
||||||
|
const sndItems = p.snd.map(snd => {
|
||||||
|
const unsent = snd['unsent-messages'].reduce((a, b) => {
|
||||||
|
return a + b + ' bytes, ';
|
||||||
|
}, 'unsent msg sizes: ');
|
||||||
|
const queuedAcks = snd['queued-message-acks'].map(qa => {
|
||||||
|
return {key: qa['message-num'], jsx: (
|
||||||
|
qa['message-num'] + ': ' + qa.ack
|
||||||
|
)};
|
||||||
|
});
|
||||||
|
const m = snd['packet-pump-state'].metrics;
|
||||||
|
const pumpMetrics = (<>
|
||||||
|
<table><tbody>
|
||||||
|
<tr class="inter">
|
||||||
|
<td>rto</td>
|
||||||
|
<td>rtt</td>
|
||||||
|
<td>rttvar</td>
|
||||||
|
<td>ssthresh</td>
|
||||||
|
<td>num-live</td>
|
||||||
|
<td>cwnd</td>
|
||||||
|
<td>counter</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{m.rto}</td>
|
||||||
|
<td>{m.rtt}</td>
|
||||||
|
<td>{m.rttvar}</td>
|
||||||
|
<td>{m.ssthresh}</td>
|
||||||
|
<td>{m['num-live']}</td>
|
||||||
|
<td>{m.cwnd}</td>
|
||||||
|
<td>{m.counter}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</>);
|
||||||
|
|
||||||
|
const liveItems = snd['packet-pump-state'].live.map(live => {
|
||||||
|
return {key: live['message-num']+','+live['fragment-num'], jsx: (
|
||||||
|
<table><tbody>
|
||||||
|
<tr>
|
||||||
|
<td>message-num</td>
|
||||||
|
<td>fragment-num</td>
|
||||||
|
<td>num-fragments</td>
|
||||||
|
<td>last-sent</td>
|
||||||
|
<td>retries</td>
|
||||||
|
<td>skips</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{live['message-num']}</td>
|
||||||
|
<td>{live['fragment-num']}</td>
|
||||||
|
<td>{live['num-fragments']}</td>
|
||||||
|
<td>{msToDa(live['last-sent'])}</td>
|
||||||
|
<td>{live.retries}</td>
|
||||||
|
<td>{live.skips}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
)};
|
||||||
|
});
|
||||||
|
const live = (
|
||||||
|
<SearchableList placeholder="msg-num,frag-num" items={liveItems} />
|
||||||
|
);
|
||||||
|
|
||||||
|
const summary = (<>
|
||||||
|
{renderDuct(snd.duct)}
|
||||||
|
<table><tbody>
|
||||||
|
<tr class="inter">
|
||||||
|
<td>bone</td>
|
||||||
|
<td>current</td>
|
||||||
|
<td>next</td>
|
||||||
|
<td>next wake</td>
|
||||||
|
<td>total unsent</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{snd.bone}</td>
|
||||||
|
<td>{snd.current}</td>
|
||||||
|
<td>{snd.next}</td>
|
||||||
|
<td>{msToDa(snd['packet-pump-state']['next-wake'])}</td>
|
||||||
|
<td>
|
||||||
|
{snd['unsent-messages'].reduce((a,b) => a+b, 0)} bytes
|
||||||
|
({snd['unsent-messages'].length} messages)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</>);
|
||||||
|
const details = (<>
|
||||||
|
{pumpMetrics}
|
||||||
|
{unsent}
|
||||||
|
{queuedAcks}
|
||||||
|
{live}
|
||||||
|
</>);
|
||||||
|
return {key: snd.bone + ', ' + renderDuct(snd.duct), jsx: (
|
||||||
|
<Summary summary={summary} details={details} />
|
||||||
|
)};
|
||||||
|
});
|
||||||
|
const snd = (<>
|
||||||
|
<h4 style={{marginTop: '1em'}}>snd</h4>
|
||||||
|
<SearchableList placeholder="bone, duct" items={sndItems} />
|
||||||
|
</>);
|
||||||
|
|
||||||
|
const rcvItems = p.rcv.map(rcv => {
|
||||||
|
console.log('rcv', rcv);
|
||||||
|
const pendingVaneAcks = rcv['pending-vane-ack'].reduce((a, b) => {
|
||||||
|
return a + b + ', ';
|
||||||
|
}, 'pending vane acks: ');
|
||||||
|
const nax = rcv.nax.reduce((a, b) => {
|
||||||
|
return a + b + ', ';
|
||||||
|
}, 'nacks: ');
|
||||||
|
const liveItems = rcv['live-messages'].map(live => {
|
||||||
|
return {key: live['message-num'], jsx: (<>
|
||||||
|
Message #{live['message-num']}<br/>
|
||||||
|
{live['num-received']} out of {live['num-fragments']} fragments received:<br/>
|
||||||
|
{live.fragments.reduce((a, b) => a + b + ', ', '')}
|
||||||
|
</>)};
|
||||||
|
});
|
||||||
|
const liveMessages = (<>
|
||||||
|
Live messages:<br/>
|
||||||
|
<SearchableList placeholder="message num" items={liveItems} />
|
||||||
|
</>);
|
||||||
|
|
||||||
|
const summary = (<>
|
||||||
|
{renderDuct(rcv.duct)}
|
||||||
|
<table><tbody>
|
||||||
|
<tr>
|
||||||
|
<td>bone</td>
|
||||||
|
<td>last-acked</td>
|
||||||
|
<td>last-heard</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{rcv.bone}</td>
|
||||||
|
<td>{rcv['last-acked']}</td>
|
||||||
|
<td>{rcv['last-heard']}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</>);
|
||||||
|
const details = (<>
|
||||||
|
{pendingVaneAcks}<br/>
|
||||||
|
{nax}<br/>
|
||||||
|
{liveMessages}
|
||||||
|
</>);
|
||||||
|
return {key: rcv.bone + ', ' + renderDuct(rcv.duct), jsx: (
|
||||||
|
<Summary summary={summary} details={details} />
|
||||||
|
)};
|
||||||
|
});
|
||||||
|
const rcv = (<>
|
||||||
|
<h4 style={{marginTop: '1em'}}>rcv</h4>
|
||||||
|
<SearchableList placeholder="bone, duct" items={rcvItems} />
|
||||||
|
</>);
|
||||||
|
|
||||||
|
const naxItems = p.nax.map(nack => {
|
||||||
|
return {key: nack.bone, jsx: (
|
||||||
|
<div>
|
||||||
|
bone {nack.bone}, message #{nack['message-num']}, duct:<br/>
|
||||||
|
{renderDuct(nack.duct)}
|
||||||
|
</div>
|
||||||
|
)};
|
||||||
|
});
|
||||||
|
const nax = (<>
|
||||||
|
<h4 style={{marginTop: '1em'}}>nax</h4>
|
||||||
|
<SearchableList placeholder="bone" items={naxItems} />
|
||||||
|
</>);
|
||||||
|
|
||||||
|
const heeds = (<>
|
||||||
|
<h4 style={{marginTop: '1em'}}>heeds</h4>
|
||||||
|
{this.renderDucts(p.heeds)}
|
||||||
|
</>);
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
{status}
|
||||||
|
{snd}
|
||||||
|
{rcv}
|
||||||
|
{nax}
|
||||||
|
{heeds}
|
||||||
|
</>);
|
||||||
|
} else {
|
||||||
|
console.log('weird peer', peer);
|
||||||
|
return '???';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const knownItems = known.map(who => {
|
||||||
|
return {key: '~'+who, jsx: (<Summary
|
||||||
|
id={who}
|
||||||
|
summary={'~'+who + ' (known)'}
|
||||||
|
details={renderDetails(who)}
|
||||||
|
onOpen={this.loadPeerDetails}
|
||||||
|
/>)};
|
||||||
|
});
|
||||||
|
|
||||||
|
const alienItems = alien.map(who => {
|
||||||
|
return {key: '~'+who, jsx: (<Summary
|
||||||
|
id={who}
|
||||||
|
summary={'~'+who + ' (alien)'}
|
||||||
|
details={renderDetails(who)}
|
||||||
|
onOpen={this.loadPeerDetails}
|
||||||
|
/>)};
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = [...knownItems, ...alienItems];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SearchableList placeholder="ship name" items={items} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
104
pkg/interface/dbug/src/js/views/apps.js
Normal file
104
pkg/interface/dbug/src/js/views/apps.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Spinner } from '../components/lib/icons/icon-spinner';
|
||||||
|
import { Subscriptions } from '../components/subscriptions';
|
||||||
|
import { Route, Link } from 'react-router-dom';
|
||||||
|
import { makeRoutePath, isPatTa, deSig } from '../lib/util';
|
||||||
|
import urbitOb from 'urbit-ob';
|
||||||
|
import { SearchableList } from '../components/searchable-list';
|
||||||
|
import { Summary } from '../components/summary';
|
||||||
|
|
||||||
|
export class Apps extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {};
|
||||||
|
|
||||||
|
this.loadApps = this.loadApps.bind(this);
|
||||||
|
this.loadAppDetails = this.loadAppDetails.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (Object.keys(this.props.apps).length === 0) {
|
||||||
|
this.loadApps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
const { props, state } = this;
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
loadApps() {
|
||||||
|
api.getApps();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAppDetails(app) {
|
||||||
|
api.getAppDetails(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO use classes for styling?
|
||||||
|
render() {
|
||||||
|
const { props, state } = this;
|
||||||
|
|
||||||
|
console.log('render', props.apps, Object.keys(props.apps));
|
||||||
|
const apps = Object.keys(props.apps).sort().map(app => {
|
||||||
|
const appData = props.apps[app];
|
||||||
|
const haveDeets = (typeof appData === 'object');
|
||||||
|
const running = haveDeets
|
||||||
|
? true
|
||||||
|
: appData;
|
||||||
|
const runStyle = running
|
||||||
|
? {borderLeft: '3px solid green'}
|
||||||
|
: {borderLeft: '3px solid grey'}
|
||||||
|
|
||||||
|
let deets = null;
|
||||||
|
if (!haveDeets) {
|
||||||
|
deets = running
|
||||||
|
? "Loading..."
|
||||||
|
: "App not running.";
|
||||||
|
} else if (appData.noDebug) {
|
||||||
|
deets = "App doesn't use /lib/dbug";
|
||||||
|
} else {
|
||||||
|
const data = appData;
|
||||||
|
const events = (data.events || []).map(e => {
|
||||||
|
return {key: e, jsx: (<>
|
||||||
|
{e}<br/>
|
||||||
|
</>)};
|
||||||
|
})
|
||||||
|
deets = (<>
|
||||||
|
<div style={{maxHeight: '500px', overflow: 'scroll'}}>
|
||||||
|
<pre>{data.state.join('\n')}</pre>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Subscriptions {...data.subscriptions} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onClick={()=>{api.bindToVerb(app)}}>listen to verb</button>
|
||||||
|
<SearchableList placeholder="event description" items={events} />
|
||||||
|
</div>
|
||||||
|
</>)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOpen = running
|
||||||
|
? this.loadAppDetails
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return {key: app, jsx: (
|
||||||
|
<Summary id={app} summary={'%'+app} details={deets} onOpen={onOpen} style={runStyle} />
|
||||||
|
)};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"h-100 w-100 pa3 pt4 overflow-x-hidden " +
|
||||||
|
"bg-gray0-d white-d flex flex-column"
|
||||||
|
}>
|
||||||
|
<div className="w-100 dn-m dn-l dn-xl inter pt1 pb6 f8">
|
||||||
|
refresh?
|
||||||
|
</div>
|
||||||
|
<SearchableList placeholder="app name" items={apps} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
56
pkg/interface/dbug/src/js/views/behn.js
Normal file
56
pkg/interface/dbug/src/js/views/behn.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Spinner } from '../components/lib/icons/icon-spinner';
|
||||||
|
import { Subscriptions } from '../components/subscriptions';
|
||||||
|
import { Route, Link } from 'react-router-dom';
|
||||||
|
import { msToDa, renderDuct } from '../lib/util';
|
||||||
|
import urbitOb from 'urbit-ob';
|
||||||
|
import { SearchableList } from '../components/searchable-list';
|
||||||
|
|
||||||
|
export class Behn extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {};
|
||||||
|
|
||||||
|
this.loadTimers = this.loadTimers.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { timers } = this.props;
|
||||||
|
if (timers.length === 0) {
|
||||||
|
this.loadTimers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
const { props, state } = this;
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTimers() {
|
||||||
|
api.getTimers();
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO use classes for styling?
|
||||||
|
render() {
|
||||||
|
const { props, state } = this;
|
||||||
|
|
||||||
|
const items = props.timers.map(timer => {
|
||||||
|
const duct = renderDuct(timer.duct);
|
||||||
|
return {key: duct, jsx: (<div class="flex">
|
||||||
|
<div class="flex-auto" style={{maxWidth: '50%'}}>
|
||||||
|
{msToDa(timer.date)}
|
||||||
|
</div>
|
||||||
|
<div class="flex-auto" style={{maxWidth: '50%'}}>
|
||||||
|
{duct}
|
||||||
|
</div>
|
||||||
|
</div>)};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table><tbody>
|
||||||
|
<SearchableList placeholder="duct" items={items} />
|
||||||
|
</tbody></table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
143
pkg/interface/dbug/src/js/views/clay.js
Normal file
143
pkg/interface/dbug/src/js/views/clay.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import { Gitgraph, templateExtend, TemplateName } from "@gitgraph/react";
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { BrowserRouter, Route } from "react-router-dom";
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export class Clay extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.clickety = this.clickety.bind(this);
|
||||||
|
this.clickCommit = this.clickCommit.bind(this);
|
||||||
|
this.submit = this.submit.bind(this);
|
||||||
|
this.graph = this.graph.bind(this);
|
||||||
|
this.template = templateExtend(TemplateName.Metro, {
|
||||||
|
branch: {
|
||||||
|
lineWidth: 6,
|
||||||
|
},
|
||||||
|
commit: {
|
||||||
|
spacing: 40,
|
||||||
|
dot: {
|
||||||
|
size: 10,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
displayHash: false,
|
||||||
|
displayAuthor: false,
|
||||||
|
font: "normal 16pt monospace",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
api.getCommits();
|
||||||
|
}
|
||||||
|
|
||||||
|
clickety() {
|
||||||
|
let { commits, gitgraph } = this.props;
|
||||||
|
if ( !commits.commits ) return;
|
||||||
|
|
||||||
|
let commitMap = {};
|
||||||
|
|
||||||
|
commits.commits.forEach(commit => {
|
||||||
|
commitMap[commit.commitHash] = commit;
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = commits.commits.map(com => {
|
||||||
|
console.log(com.commitHash,commits.head);
|
||||||
|
let ref = [];
|
||||||
|
if (com.commitHash in commits.head) {
|
||||||
|
ref = ["HEAD", commits.head[com.commitHash]];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
refs: ref,
|
||||||
|
hash: com.commitHash.slice(2), // lop off 0v for more unique hash
|
||||||
|
parents: com.parents.map(par => {return par.slice(2);}),
|
||||||
|
onMessageClick: this.clickCommit,
|
||||||
|
subject: "commit: " +
|
||||||
|
com.commitHash.slice(-5) +
|
||||||
|
", content: " +
|
||||||
|
com.contentHash.slice(-5) +
|
||||||
|
", parents: " +
|
||||||
|
com.parents.map(par => {return par.slice(-5);}),
|
||||||
|
author: {
|
||||||
|
name: "me",
|
||||||
|
email: "me",
|
||||||
|
timestamp: 1500000000000,
|
||||||
|
} } });
|
||||||
|
gitgraph.import(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
clickCommit(commit, args) {
|
||||||
|
console.log("click", commit);
|
||||||
|
let val = commit.refs.slice(-1)[0];
|
||||||
|
if (!val) {
|
||||||
|
return
|
||||||
|
} else if (this.bobDesk.value == "") {
|
||||||
|
this.bobDesk.value = val;
|
||||||
|
} else {
|
||||||
|
this.aliDesk.value = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
//TODO hook up
|
||||||
|
api.pottery( {
|
||||||
|
ali: this.aliDesk.value,
|
||||||
|
bob: this.bobDesk.value,
|
||||||
|
germ: this.germ.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
graph(gitgraph) {
|
||||||
|
this.setState({gitgraph: gitgraph});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
let textAreaClasses =
|
||||||
|
"f7 mono ba bg-gray0-d white-d pa3 mb2 db " +
|
||||||
|
"focus-b--black focus-b--white-d b--gray3 b--gray2-d nowrap "
|
||||||
|
|
||||||
|
const inputs = (<>
|
||||||
|
<textarea
|
||||||
|
ref={ e => { this.bobDesk = e; } }
|
||||||
|
className={textAreaClasses}
|
||||||
|
placeholder="target desk"
|
||||||
|
spellCheck="false"
|
||||||
|
rows={1}
|
||||||
|
/>
|
||||||
|
<textarea
|
||||||
|
ref={ e => { this.aliDesk = e; } }
|
||||||
|
className={textAreaClasses}
|
||||||
|
placeholder="source desk"
|
||||||
|
spellCheck="false"
|
||||||
|
rows={1}
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
ref={ e => { this.germ = e; } }
|
||||||
|
className={textAreaClasses}>
|
||||||
|
<option value="mate">%mate: conflict if changed same lines</option>
|
||||||
|
<option value="meet">%meet: conflict if changed same files</option>
|
||||||
|
<option value="meld">%meld: annotate conflicts</option>
|
||||||
|
<option value="fine">%fine: fast-forward (requires ancestor)</option>
|
||||||
|
<option value="this">%this: use target desk's data</option>
|
||||||
|
<option value="that">%that: use source desk's data</option>
|
||||||
|
<option value="init">%init: start new desk (danger!)</option>
|
||||||
|
</select>
|
||||||
|
<button
|
||||||
|
className={textAreaClasses}
|
||||||
|
onClick={this.submit}>
|
||||||
|
Merge!
|
||||||
|
</button>
|
||||||
|
</>);
|
||||||
|
|
||||||
|
this.clickety();
|
||||||
|
return (
|
||||||
|
<div className="cf w-100 flex flex-column pa4 ba-m ba-l ba-xl b--gray2 br1 h-100 h-100-minus-40-m h-100-minus-40-l h-100-minus-40-xl f9 white-d">
|
||||||
|
<Gitgraph options={{template: this.template}}>{this.graph}</Gitgraph>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
56
pkg/interface/dbug/src/js/views/eyre.js
Normal file
56
pkg/interface/dbug/src/js/views/eyre.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Spinner } from '../components/lib/icons/icon-spinner';
|
||||||
|
import { Subscriptions } from '../components/subscriptions';
|
||||||
|
import { Route, Link } from 'react-router-dom';
|
||||||
|
import { msToDa, renderDuct } from '../lib/util';
|
||||||
|
import urbitOb from 'urbit-ob';
|
||||||
|
import { SearchableList } from '../components/searchable-list';
|
||||||
|
|
||||||
|
export class Eyre extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {};
|
||||||
|
|
||||||
|
this.loadBindings = this.loadBindings.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { bindings } = this.props;
|
||||||
|
if (bindings.length === 0) {
|
||||||
|
this.loadBindings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
const { props, state } = this;
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
loadBindings() {
|
||||||
|
api.getBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO use classes for styling?
|
||||||
|
render() {
|
||||||
|
const { props, state } = this;
|
||||||
|
|
||||||
|
const items = props.bindings.map(binding => {
|
||||||
|
|
||||||
|
return {key: binding.location + ' ' + binding.action, jsx: (<div class="flex">
|
||||||
|
<div class="flex-auto" style={{maxWidth: '50%'}}>
|
||||||
|
{binding.location}
|
||||||
|
</div>
|
||||||
|
<div class="flex-auto" style={{maxWidth: '50%'}}>
|
||||||
|
{binding.action}
|
||||||
|
</div>
|
||||||
|
</div>)};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table><tbody>
|
||||||
|
<SearchableList placeholder="binding" items={items} />
|
||||||
|
</tbody></table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
59
pkg/interface/dbug/src/js/views/spider.js
Normal file
59
pkg/interface/dbug/src/js/views/spider.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Spinner } from '../components/lib/icons/icon-spinner';
|
||||||
|
import { Subscriptions } from '../components/subscriptions';
|
||||||
|
import { Route, Link } from 'react-router-dom';
|
||||||
|
import { msToDa, renderDuct } from '../lib/util';
|
||||||
|
import urbitOb from 'urbit-ob';
|
||||||
|
import { SearchableList } from '../components/searchable-list';
|
||||||
|
|
||||||
|
export class Spider extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {};
|
||||||
|
|
||||||
|
this.loadThreads = this.loadThreads.bind(this);
|
||||||
|
this.renderThreads = this.renderThreads.bind(this);
|
||||||
|
this.killThread = this.killThread.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { threads } = this.props;
|
||||||
|
if (Object.keys(threads).length === 0) {
|
||||||
|
this.loadThreads();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
const { props, state } = this;
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
loadThreads() {
|
||||||
|
api.getThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
killThread(tid) {
|
||||||
|
api.killThread(tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderThreads(threads) {
|
||||||
|
console.log('rendering threads', threads);
|
||||||
|
return Object.keys(threads).map(thread => {
|
||||||
|
const kids = this.renderThreads(threads[thread]);
|
||||||
|
return (<>
|
||||||
|
<div>
|
||||||
|
<button style={{margin: '4px'}} onClick={()=>{this.killThread(thread)}}>kill</button>
|
||||||
|
{thread}
|
||||||
|
</div>
|
||||||
|
<div style={{paddingLeft: '16px'}}>{kids}</div>
|
||||||
|
</>);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (Object.keys(this.props.threads).length === 0)
|
||||||
|
return 'no running threads';
|
||||||
|
return this.renderThreads(this.props.threads);
|
||||||
|
}
|
||||||
|
}
|
11
pkg/interface/dbug/tile/tile.js
Normal file
11
pkg/interface/dbug/tile/tile.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export default class DebugTile extends Component {
|
||||||
|
render() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.debugTile = DebugTile;
|
Loading…
Reference in New Issue
Block a user