mirror of
https://github.com/urbit/shrub.git
synced 2024-11-28 05:22:27 +03:00
Merge remote-tracking branch 'origin/release/next-dill' into release/next-sys
This commit is contained in:
commit
eadc63938c
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c6371d3d2e283b26e64b9045c42de9de9c5df424d5f114b268a83f4ceab1c75b
|
||||
size 6302447
|
||||
oid sha256:bd48196a12826df04820aac63e5bb4ee0035d4effb9d2eca9ff06f22156052e4
|
||||
size 6313147
|
||||
|
101
pkg/arvo/app/herm.hoon
Normal file
101
pkg/arvo/app/herm.hoon
Normal file
@ -0,0 +1,101 @@
|
||||
:: herm: stand-in for term.c with http interface
|
||||
::
|
||||
/+ default-agent, dbug, verb
|
||||
=, able:jael
|
||||
|%
|
||||
+$ state-0 [%0 ~]
|
||||
--
|
||||
::
|
||||
=| state-0
|
||||
=* state -
|
||||
%+ verb |
|
||||
%- agent:dbug
|
||||
^- agent:gall
|
||||
=> |%
|
||||
++ request-tube
|
||||
|= [bowl:gall from=mark to=mark next=?]
|
||||
^- card:agent:gall
|
||||
:* %pass /tube/[from]/[to]
|
||||
%arvo %c %warp
|
||||
our q.byk ~
|
||||
::
|
||||
?: next
|
||||
[%next %c da+now /[from]/[to]]
|
||||
[%sing %c da+now /[from]/[to]]
|
||||
==
|
||||
--
|
||||
|_ =bowl:gall
|
||||
+* this .
|
||||
def ~(. (default-agent this %|) bowl)
|
||||
::
|
||||
++ on-init
|
||||
^- (quip card:agent:gall _this)
|
||||
:_ this
|
||||
:: set up dill session subscription,
|
||||
:: and ensure the tubes we use are in cache
|
||||
::
|
||||
:~ [%pass [%view %$ ~] %arvo %d %view ~]
|
||||
(request-tube bowl %blit %json |)
|
||||
(request-tube bowl %json %belt |)
|
||||
==
|
||||
::
|
||||
++ on-save !>([%0 ~])
|
||||
++ on-load
|
||||
|= old=vase
|
||||
^- (quip card:agent:gall _this)
|
||||
[~ this(state [%0 ~])]
|
||||
::
|
||||
++ on-watch
|
||||
|= =path
|
||||
^- (quip card:agent:gall _this)
|
||||
?> ?=([%session @ ~] path)
|
||||
:_ this
|
||||
:: scry prompt and cursor position out of dill for initial response
|
||||
::
|
||||
=/ base=^path
|
||||
/dx/(scot %p our.bowl)//(scot %da now.bowl)/sessions
|
||||
:~ [%give %fact ~ %blit !>(.^(blit:dill (weld base //line)))]
|
||||
[%give %fact ~ %blit !>(`blit:dill`hop+.^(@ud (weld base //cursor)))]
|
||||
==
|
||||
::
|
||||
++ on-arvo
|
||||
|= [=wire =sign-arvo]
|
||||
^- (quip card:agent:gall _this)
|
||||
?+ wire !!
|
||||
:: pass on dill blits for the session
|
||||
::
|
||||
[%view %$ ~]
|
||||
?. ?=([%d %blit *] sign-arvo)
|
||||
~| [%unexpected-sign [- +<]:sign-arvo]
|
||||
!!
|
||||
:_ this
|
||||
%+ turn p.sign-arvo
|
||||
|= =blit:dill
|
||||
[%give %fact [%session %$ ~]~ %blit !>(blit)]
|
||||
::
|
||||
:: ensure the tubes we need remain in cache
|
||||
::
|
||||
[%tube @ @ ~]
|
||||
=* from i.t.wire
|
||||
=* to i.t.t.wire
|
||||
?. ?=([%c %writ *] sign-arvo)
|
||||
~| [%unexpected-sign [- +<]:sign-arvo]
|
||||
!!
|
||||
:_ this
|
||||
[(request-tube bowl from to &)]~
|
||||
==
|
||||
::
|
||||
++ on-poke
|
||||
|= [=mark =vase]
|
||||
^- (quip card:agent:gall _this)
|
||||
?. ?=(%belt mark)
|
||||
~| [%unexpected-mark mark]
|
||||
!!
|
||||
:_ this
|
||||
[%pass [%belt %$ ~] %arvo %d %belt !<(belt:dill vase)]~
|
||||
::
|
||||
++ on-leave on-leave:def
|
||||
++ on-peek on-peek:def
|
||||
++ on-agent on-agent:def
|
||||
++ on-fail on-fail:def
|
||||
--
|
@ -12,6 +12,7 @@
|
||||
[%3 *]
|
||||
[%4 state-zero]
|
||||
[%5 state-zero]
|
||||
[%6 state-zero]
|
||||
==
|
||||
::
|
||||
+$ state-zero
|
||||
@ -21,7 +22,7 @@
|
||||
==
|
||||
--
|
||||
::
|
||||
=| [%5 state-zero]
|
||||
=| [%6 state-zero]
|
||||
=* state -
|
||||
%- agent:dbug
|
||||
^- agent:gall
|
||||
@ -36,27 +37,42 @@
|
||||
%_ new-state
|
||||
tiles
|
||||
%- ~(gas by *tiles:store)
|
||||
%+ turn `(list term)`[%weather %clock %dojo ~]
|
||||
%+ turn `(list term)`[%weather %clock %term ~]
|
||||
|= =term
|
||||
:- term
|
||||
^- tile:store
|
||||
?+ term [[%custom ~] %.y]
|
||||
%dojo [[%basic 'Dojo' '/~landscape/img/Dojo.png' '/~dojo'] %.y]
|
||||
?+ term [[%custom ~] %.y]
|
||||
%term [[%basic 'Terminal' '/~landscape/img/term.png' '/~term'] %.y]
|
||||
==
|
||||
tile-ordering [%weather %clock %dojo ~]
|
||||
tile-ordering [%weather %clock %term ~]
|
||||
==
|
||||
[~ this(state [%5 new-state])]
|
||||
[~ this(state [%6 new-state])]
|
||||
::
|
||||
++ on-save !>(state)
|
||||
++ on-load
|
||||
|= old=vase
|
||||
^- (quip card _this)
|
||||
=/ old-state !<(versioned-state old)
|
||||
|-
|
||||
=| cards=(list card)
|
||||
|- ^- (quip card _this)
|
||||
?: ?=(%6 -.old-state)
|
||||
[cards this(state old-state)]
|
||||
?: ?=(%5 -.old-state)
|
||||
`this(state old-state)
|
||||
:: replace %dojo with %term
|
||||
::
|
||||
=. tiles.old-state
|
||||
%+ ~(put by (~(del by tiles.old-state) %dojo))
|
||||
%term
|
||||
:_ is-shown:(~(gut by tiles.old-state) %dojo *tile:store)
|
||||
[%basic 'Terminal' '/~landscape/img/term.png' '/~term']
|
||||
=. tile-ordering.old-state
|
||||
%+ turn tile-ordering.old-state
|
||||
|=(t=term ?:(=(%dojo t) %term t))
|
||||
$(old-state [%6 +.old-state])
|
||||
?: ?=(%4 -.old-state)
|
||||
:- [%pass / %arvo %e %disconnect [~ /]]~
|
||||
=. cards
|
||||
%+ snoc cards
|
||||
[%pass / %arvo %e %disconnect [~ /]]
|
||||
=. tiles.old-state
|
||||
(~(del by tiles.old-state) %chat)
|
||||
=. tiles.old-state
|
||||
@ -65,7 +81,7 @@
|
||||
(~(del by tiles.old-state) %links)
|
||||
=. tile-ordering.old-state
|
||||
(skip tile-ordering.old-state |=(=term ?=(?(%links %chat %publish) term)))
|
||||
this(state [%5 +.old-state])
|
||||
$(old-state [%5 +.old-state])
|
||||
=/ new-state *state-zero
|
||||
=. new-state
|
||||
%_ new-state
|
||||
@ -80,18 +96,22 @@
|
||||
==
|
||||
tile-ordering [%weather %clock %dojo ~]
|
||||
==
|
||||
:_ this(state [%5 new-state])
|
||||
%+ welp
|
||||
:~ [%pass / %arvo %e %disconnect [~ /]]
|
||||
:* %pass /srv %agent [our.bowl %file-server]
|
||||
%poke %file-server-action
|
||||
!>([%serve-dir / /app/landscape %.n %.y])
|
||||
==
|
||||
==
|
||||
%+ turn ~(tap by wex.bowl)
|
||||
|= [[=wire =ship =term] *]
|
||||
^- card
|
||||
[%pass wire %agent [ship term] %leave ~]
|
||||
%_ $
|
||||
old-state [%5 new-state]
|
||||
::
|
||||
cards
|
||||
%+ welp
|
||||
:~ [%pass / %arvo %e %disconnect [~ /]]
|
||||
:* %pass /srv %agent [our.bowl %file-server]
|
||||
%poke %file-server-action
|
||||
!>([%serve-dir / /app/landscape %.n %.y])
|
||||
==
|
||||
==
|
||||
%+ turn ~(tap by wex.bowl)
|
||||
|= [[=wire =ship =term] *]
|
||||
^- card
|
||||
[%pass wire %agent [ship term] %leave ~]
|
||||
==
|
||||
::
|
||||
++ on-poke
|
||||
|= [=mark =vase]
|
||||
|
@ -1,74 +1,4 @@
|
||||
:: soto [tombstone]: former dojo relay for urbit's landscape interface
|
||||
::
|
||||
:: soto [landscape]: A Dojo relay for Urbit's Landscape interface
|
||||
::
|
||||
:: Relays sole-effects to subscribers and forwards sole-action pokes
|
||||
::
|
||||
/- sole
|
||||
/+ *soto, default-agent
|
||||
|%
|
||||
+$ card card:agent:gall
|
||||
::
|
||||
+$ versioned-state
|
||||
$@ state-null
|
||||
state-zero
|
||||
::
|
||||
+$ state-null ~
|
||||
::
|
||||
+$ state-zero [%0 ~]
|
||||
--
|
||||
=| state-zero
|
||||
=* state -
|
||||
^- agent:gall
|
||||
|_ bol=bowl:gall
|
||||
+* this .
|
||||
soto-core +>
|
||||
sc ~(. soto-core bol)
|
||||
def ~(. (default-agent this %|) bol)
|
||||
::
|
||||
++ on-init
|
||||
:_ this
|
||||
:_ ~
|
||||
:* %pass /srv %agent [our.bol %file-server]
|
||||
%poke %file-server-action
|
||||
!>([%serve-dir /'~dojo' /app/landscape %.n %.y])
|
||||
==
|
||||
++ on-save !>(state)
|
||||
::
|
||||
++ on-load
|
||||
|= old-vase=vase
|
||||
=/ old
|
||||
!<(versioned-state old-vase)
|
||||
?^ old
|
||||
[~ this(state old)]
|
||||
:_ this(state [%0 ~])
|
||||
:~ [%pass /bind/soto %arvo %e %disconnect [~ /'~dojo']]
|
||||
:* %pass /srv %agent [our.bol %file-server]
|
||||
%poke %file-server-action
|
||||
!>([%serve-dir /'~dojo' /app/landscape %.n %.y])
|
||||
==
|
||||
==
|
||||
::
|
||||
++ on-poke on-poke:def
|
||||
++ on-watch
|
||||
|= pax=path
|
||||
^- (quip card _this)
|
||||
?+ pax (on-watch:def pax)
|
||||
[%sototile ~]
|
||||
:_ this
|
||||
[%give %fact ~ %json !>(~)]~
|
||||
==
|
||||
::
|
||||
++ on-agent on-agent:def
|
||||
::
|
||||
++ on-arvo
|
||||
|= [wir=wire sin=sign-arvo]
|
||||
^- (quip card _this)
|
||||
?: ?=(%bound +<.sin)
|
||||
[~ this]
|
||||
(on-arvo:def wir sin)
|
||||
::
|
||||
++ on-fail on-fail:def
|
||||
++ on-leave on-leave:def
|
||||
++ on-peek on-peek:def
|
||||
::
|
||||
--
|
||||
/+ default-agent
|
||||
(default-agent *agent:gall %|)
|
||||
|
@ -91,7 +91,7 @@
|
||||
%chat-hook
|
||||
%chat-view
|
||||
%chat-cli
|
||||
%soto
|
||||
%herm
|
||||
%contact-store
|
||||
%contact-hook
|
||||
%contact-view
|
||||
@ -242,7 +242,7 @@
|
||||
=> (se-born | %home %group-push-hook)
|
||||
(se-born | %home %group-pull-hook)
|
||||
=? ..on-load (lte hood-version %9)
|
||||
(se-born | %home %graph-store)
|
||||
(se-born | %home %graph-store)
|
||||
=? ..on-load (lte hood-version %10)
|
||||
=> (se-born | %home %graph-push-hook)
|
||||
(se-born | %home %graph-pull-hook)
|
||||
@ -251,7 +251,8 @@
|
||||
=> (se-born | %home %hark-group-hook)
|
||||
=> (se-born | %home %hark-chat-hook)
|
||||
=> (se-born | %home %hark-store)
|
||||
(se-born | %home %observe-hook)
|
||||
=> (se-born | %home %observe-hook)
|
||||
(se-born | %home %herm)
|
||||
..on-load
|
||||
::
|
||||
++ reap-phat :: ack connect
|
||||
@ -567,7 +568,6 @@
|
||||
++ se-show :: show buffer, raw
|
||||
|= lin/(pair @ud stub)
|
||||
^+ +>
|
||||
=. p.lin (add p.lin (lent-stye:klr q.lin))
|
||||
?: =(mir lin) +>
|
||||
=. +> ?:(=(p.mir p.lin) +> (se-blit %hop p.lin))
|
||||
=. +> ?:(=(q.mir q.lin) +> (se-blit %pom q.lin))
|
||||
@ -1131,25 +1131,10 @@
|
||||
(fall p.q.a p.q.b)
|
||||
(fall q.q.a q.q.b)
|
||||
::
|
||||
++ lent-stye
|
||||
|= a/stub ^- @
|
||||
(roll (lnts-stye a) add)
|
||||
::
|
||||
++ lent-char
|
||||
|= a/stub ^- @
|
||||
(roll (lnts-char a) add)
|
||||
::
|
||||
++ lnts-stye :: stub pair head lengths
|
||||
|= a/stub ^- (list @)
|
||||
%+ turn a
|
||||
|= a/(pair stye (list @c))
|
||||
;: add :: presumes impl of cvrt:ansi in %dill
|
||||
(mul 5 2) :: bg
|
||||
(mul 5 2) :: fg
|
||||
=+ b=~(wyt in p.p.a) :: effect
|
||||
?:(=(0 b) 0 (mul 4 +(b)))
|
||||
==
|
||||
::
|
||||
++ lnts-char :: stub pair tail lengths
|
||||
|= a/stub ^- (list @)
|
||||
%+ turn a
|
||||
|
29
pkg/arvo/mar/belt.hoon
Normal file
29
pkg/arvo/mar/belt.hoon
Normal file
@ -0,0 +1,29 @@
|
||||
:: belt: runtime belt structure
|
||||
::
|
||||
|_ =belt:dill
|
||||
++ grad %noun
|
||||
:: +grab: convert from
|
||||
::
|
||||
++ grab
|
||||
|%
|
||||
++ noun belt:dill
|
||||
++ json
|
||||
^- $-(^json belt:dill)
|
||||
=, dejs:format
|
||||
%- of
|
||||
:~ aro+(su (perk %d %l %r %u ~))
|
||||
bac+ul
|
||||
ctl+(cu taft so)
|
||||
del+ul
|
||||
met+(cu taft so)
|
||||
ret+ul
|
||||
txt+(ar (cu taft so))
|
||||
==
|
||||
--
|
||||
:: +grow: convert to
|
||||
::
|
||||
++ grow
|
||||
|%
|
||||
++ noun belt
|
||||
--
|
||||
--
|
58
pkg/arvo/mar/blit.hoon
Normal file
58
pkg/arvo/mar/blit.hoon
Normal file
@ -0,0 +1,58 @@
|
||||
:: blit: runtime blit structure
|
||||
::
|
||||
/+ base64
|
||||
::
|
||||
|_ =blit:dill
|
||||
++ grad %noun
|
||||
:: +grab: convert from
|
||||
::
|
||||
++ grab
|
||||
|%
|
||||
++ noun blit:dill
|
||||
--
|
||||
:: +grow: convert to
|
||||
::
|
||||
++ grow
|
||||
|%
|
||||
++ noun blit
|
||||
++ json
|
||||
^- ^json
|
||||
=, enjs:format
|
||||
%+ frond -.blit
|
||||
?- -.blit
|
||||
%bel b+&
|
||||
%clr b+&
|
||||
%hop (numb p.blit)
|
||||
%lin a+(turn p.blit |=(c=@c s+(tuft c)))
|
||||
%mor b+&
|
||||
%url s+p.blit
|
||||
::
|
||||
%sag
|
||||
%- pairs
|
||||
:~ 'path'^(path p.blit)
|
||||
'file'^s+(en:base64 (as-octs:mimes:html (jam q.blit)))
|
||||
==
|
||||
::
|
||||
%sav
|
||||
%- pairs
|
||||
:~ 'path'^(path p.blit)
|
||||
'file'^s+(en:base64 (as-octs:mimes:html q.blit))
|
||||
==
|
||||
::
|
||||
%klr
|
||||
:- %a
|
||||
%+ turn p.blit
|
||||
|= [=stye text=(list @c)]
|
||||
%- pairs
|
||||
:~ 'text'^a+(turn text |=(c=@c s+(tuft c)))
|
||||
::
|
||||
:- 'stye'
|
||||
%- pairs
|
||||
:~ 'back'^[?~(. ~ s+.)]:p.q.stye
|
||||
'fore'^[?~(. ~ s+.)]:q.q.stye
|
||||
'deco'^a+(turn ~(tap in p.stye) |=(d=deco ?~(d ~ s+d)))
|
||||
==
|
||||
==
|
||||
==
|
||||
--
|
||||
--
|
@ -8,9 +8,10 @@
|
||||
-- ::
|
||||
=> |% :: console protocol
|
||||
++ axle ::
|
||||
$: %3 ::
|
||||
$: %4 ::TODO replace ducts with session ids ::
|
||||
hey/(unit duct) :: default duct
|
||||
dug/(map duct axon) :: conversations
|
||||
eye=(jug duct duct) :: outside listeners
|
||||
lit/? :: boot in lite mode
|
||||
$= veb :: vane verbosities
|
||||
$~ (~(put by *(map @tas log-level)) %hole %soft) :: quiet packet crashes
|
||||
@ -21,7 +22,7 @@
|
||||
tem/(unit (list dill-belt)) :: pending, reverse
|
||||
wid/_80 :: terminal width
|
||||
pos/@ud :: cursor position
|
||||
see/(list @c) :: current line
|
||||
see=$%([%lin (list @c)] [%klr stub]) :: current line
|
||||
== ::
|
||||
+$ log-level ?(%hush %soft %loud) :: none, line, full
|
||||
-- => ::
|
||||
@ -151,7 +152,11 @@
|
||||
::
|
||||
++ done :: return gift
|
||||
|= git/gift:able
|
||||
+>(moz :_(moz [hen %give git]))
|
||||
=- +>.$(moz (weld - moz))
|
||||
%+ turn
|
||||
:- hen
|
||||
~(tap in (~(get ju eye.all) hen))
|
||||
|=(=duct [duct %give git])
|
||||
::
|
||||
++ deal :: pass to %gall
|
||||
|= [=wire =deal:gall]
|
||||
@ -161,7 +166,7 @@
|
||||
|= [=wire =note]
|
||||
+>(moz :_(moz [hen %pass wire note]))
|
||||
::
|
||||
++ from :: receive belt
|
||||
++ from :: receive blit
|
||||
|= bit/dill-blit
|
||||
^+ +>
|
||||
?: ?=($mor -.bit)
|
||||
@ -172,86 +177,33 @@
|
||||
%+ done %blit
|
||||
:~ [%lin p.bit]
|
||||
[%mor ~]
|
||||
[%lin see]
|
||||
see
|
||||
[%hop pos]
|
||||
==
|
||||
?: ?=($klr -.bit)
|
||||
%+ done %blit
|
||||
:~ [%lin (cvrt:ansi p.bit)]
|
||||
:~ [%klr p.bit]
|
||||
[%mor ~]
|
||||
[%lin see]
|
||||
see
|
||||
[%hop pos]
|
||||
==
|
||||
?: ?=($pro -.bit)
|
||||
(done(see p.bit) %blit [[%lin p.bit] [%hop pos] ~])
|
||||
=. see [%lin p.bit]
|
||||
(done %blit [see [%hop pos] ~])
|
||||
?: ?=($pom -.bit)
|
||||
=. see (cvrt:ansi p.bit)
|
||||
(done %blit [[%lin see] [%hop pos] ~])
|
||||
::NOTE treat "styled prompt" without style as plain prompt,
|
||||
:: to allow rendering by older runtimes
|
||||
::TODO remove me once v0.10.9+ has high/guaranteed adoption
|
||||
::
|
||||
?: (levy p.bit (cork head |*(s=stye =(*stye s))))
|
||||
$(bit [%pro (zing (turn p.bit tail))])
|
||||
=. see [%klr p.bit]
|
||||
(done %blit [see [%hop pos] ~])
|
||||
?: ?=($hop -.bit)
|
||||
(done(pos p.bit) %blit [bit ~])
|
||||
?: ?=($qit -.bit)
|
||||
(dump %logo ~)
|
||||
(done %blit [bit ~])
|
||||
::
|
||||
++ ansi
|
||||
|%
|
||||
++ cvrt :: stub to (list @c)
|
||||
|= a/stub :: with ANSI codes
|
||||
^- (list @c)
|
||||
%- zing %+ turn a
|
||||
|= a/(pair stye (list @c))
|
||||
^- (list @c)
|
||||
;: weld
|
||||
?: =(0 ~(wyt in p.p.a)) ~
|
||||
`(list @c)`(zing (turn ~(tap in p.p.a) ef))
|
||||
(bg p.q.p.a)
|
||||
(fg q.q.p.a)
|
||||
q.a
|
||||
?~(p.p.a ~ (ef ~))
|
||||
(bg ~)
|
||||
(fg ~)
|
||||
==
|
||||
::
|
||||
++ ef |=(a/^deco (scap (deco a))) :: ANSI effect
|
||||
::
|
||||
++ fg |=(a/^tint (scap (tint a))) :: ANSI foreground
|
||||
::
|
||||
++ bg :: ANSI background
|
||||
|= a/^tint
|
||||
%- scap
|
||||
=>((tint a) [+(p) q]) :: (add 10 fg)
|
||||
::
|
||||
++ scap :: ANSI escape seq
|
||||
|= a/$@(@ (pair @ @))
|
||||
%- (list @c)
|
||||
:+ 27 '[' :: "\033[{a}m"
|
||||
?@(a :~(a 'm') :~(p.a q.a 'm'))
|
||||
::
|
||||
++ deco :: ANSI effects
|
||||
|= a/^deco ^- @
|
||||
?- a
|
||||
~ '0'
|
||||
$br '1'
|
||||
$un '4'
|
||||
$bl '5'
|
||||
==
|
||||
::
|
||||
++ tint :: ANSI colors (fg)
|
||||
|= a/^tint
|
||||
^- (pair @ @)
|
||||
:- '3'
|
||||
?- a
|
||||
$k '0'
|
||||
$r '1'
|
||||
$g '2'
|
||||
$y '3'
|
||||
$b '4'
|
||||
$m '5'
|
||||
$c '6'
|
||||
$w '7'
|
||||
~ '9'
|
||||
==
|
||||
--
|
||||
:: XX move
|
||||
::
|
||||
++ sein
|
||||
@ -398,7 +350,7 @@
|
||||
=* duc (need hey.all)
|
||||
=/ app %hood
|
||||
=/ see (tuba "<awaiting {(trip app)}, this may take a minute>")
|
||||
=/ zon=axon [app input=[~ ~] width=80 cursor=(lent see) see]
|
||||
=/ zon=axon [app input=[~ ~] width=80 cursor=(lent see) lin+see]
|
||||
::
|
||||
=^ moz all abet:(~(into as duc zon) ~)
|
||||
[moz ..^$]
|
||||
@ -422,7 +374,29 @@
|
||||
=. veb.all (~(put by veb.all) tag.task level.task)
|
||||
[~ ..^$]
|
||||
::
|
||||
?: ?=(%view -.task)
|
||||
:: crash on viewing non-existent session
|
||||
::
|
||||
~| [%no-session session.task]
|
||||
?> =(~ session.task)
|
||||
=/ session (need hey.all)
|
||||
=/ =axon (~(got by dug.all) session)
|
||||
:: register the viewer and send them the prompt line
|
||||
::
|
||||
:- [hen %give %blit [see.axon]~]~
|
||||
..^$(eye.all (~(put ju eye.all) session hen))
|
||||
::
|
||||
?: ?=(%flee -.task)
|
||||
:- ~
|
||||
~| [%no-session session.task]
|
||||
?> =(~ session.task)
|
||||
=/ session (need hey.all)
|
||||
..^$(eye.all (~(del ju eye.all) session hen))
|
||||
::
|
||||
=/ nus (ax hen)
|
||||
=? nus &(?=(~ nus) ?=(^ hey.all))
|
||||
::TODO allow specifying target session in task
|
||||
(ax u.hey.all)
|
||||
?~ nus
|
||||
:: :hen is an unrecognized duct
|
||||
:: could be before %boot (or %boot failed)
|
||||
@ -441,7 +415,7 @@
|
||||
++ axle-1
|
||||
$: $1
|
||||
hey/(unit duct)
|
||||
dug/(map duct axon)
|
||||
dug/(map duct axon-3)
|
||||
lit/?
|
||||
$= hef
|
||||
$: a/(unit mass)
|
||||
@ -457,10 +431,11 @@
|
||||
$~ (~(put by *(map @tas log-level)) %hole %soft)
|
||||
(map @tas log-level)
|
||||
==
|
||||
::
|
||||
++ axle-2
|
||||
$: %2
|
||||
hey/(unit duct)
|
||||
dug/(map duct axon)
|
||||
dug/(map duct axon-3)
|
||||
lit/?
|
||||
dog/_|
|
||||
$= hef
|
||||
@ -478,29 +453,68 @@
|
||||
(map @tas log-level)
|
||||
==
|
||||
::
|
||||
++ axle-any
|
||||
$%(axle-1 axle-2 axle)
|
||||
+$ axle-3
|
||||
$: %3
|
||||
hey=(unit duct)
|
||||
dug=(map duct axon-3)
|
||||
lit=?
|
||||
$= veb
|
||||
$~ (~(put by *(map @tas log-level)) %hole %soft)
|
||||
(map @tas log-level)
|
||||
==
|
||||
+$ axon-3
|
||||
$: ram=term
|
||||
tem=(unit (list dill-belt))
|
||||
wid=_80
|
||||
pos=@ud
|
||||
see=(list @c)
|
||||
==
|
||||
::
|
||||
+$ axle-any
|
||||
$%(axle-1 axle-2 axle-3 axle)
|
||||
--
|
||||
::
|
||||
|= old=axle-any
|
||||
?- -.old
|
||||
%1 $(old [%2 [hey dug lit dog=& hef veb]:old])
|
||||
%2 $(old [%3 [hey dug lit veb]:old])
|
||||
%3 ..^$(all old)
|
||||
%3 =- $(old [%4 hey.old - ~ lit.old veb.old])
|
||||
(~(run by dug.old) |=(a=axon-3 a(see lin+see.a)))
|
||||
%4 ..^$(all old)
|
||||
==
|
||||
::
|
||||
++ scry
|
||||
|= {fur/(unit (set monk)) ren/@tas why/shop syd/desk lot/coin tyl/path}
|
||||
^- (unit (unit cage))
|
||||
?. ?=(%& -.why) ~
|
||||
=* his p.why
|
||||
::TODO don't special-case whey scry
|
||||
::
|
||||
?: &(=(ren %$) =(tyl /whey))
|
||||
=/ maz=(list mass)
|
||||
:~ hey+&+hey.all
|
||||
dug+&+dug.all
|
||||
==
|
||||
``mass+!>(maz)
|
||||
[~ ~]
|
||||
:: only respond for the local identity, %$ desk, current timestamp
|
||||
::
|
||||
?. ?& =(&+our why)
|
||||
=([%$ %da now] lot)
|
||||
=(%$ syd)
|
||||
==
|
||||
~
|
||||
:: /dx/sessions//line blit current line (prompt) of default session
|
||||
:: /dx/sessions//cursor @ud current cursor position of default session
|
||||
::TODO support asking for specific sessions once session ids are real
|
||||
::
|
||||
?. ?=(%x ren) ~
|
||||
?+ tyl ~
|
||||
[%sessions %$ *]
|
||||
?~ hey.all [~ ~]
|
||||
?~ session=(~(get by dug.all) u.hey.all) [~ ~]
|
||||
?+ t.t.tyl ~
|
||||
[%line ~] ``blit+!>(`blit`see.u.session)
|
||||
[%cursor ~] ``atom+!>(pos.u.session)
|
||||
==
|
||||
==
|
||||
::
|
||||
++ stay all
|
||||
::
|
||||
|
@ -1120,6 +1120,7 @@
|
||||
{$boot lit/? p/*} :: weird %dill boot
|
||||
{$crop p/@ud} :: trim kernel state
|
||||
$>(%crud vane-task) :: error with trace
|
||||
[%flee session=~] :: unwatch session
|
||||
{$flog p/flog} :: wrapped error
|
||||
{$flow p/@tas q/(list gill:gall)} :: terminal config
|
||||
{$hail ~} :: terminal refresh
|
||||
@ -1134,6 +1135,7 @@
|
||||
{$talk p/tank} ::
|
||||
{$text p/tape} ::
|
||||
{$veer p/@ta q/path r/@t} :: install vane
|
||||
[%view session=~] :: watch session blits
|
||||
$>(%trim vane-task) :: trim state
|
||||
$>(%vega vane-task) :: report upgrade
|
||||
{$verb ~} :: verbose mode
|
||||
@ -1157,6 +1159,7 @@
|
||||
$% {$bel ~} :: make a noise
|
||||
{$clr ~} :: clear the screen
|
||||
{$hop p/@ud} :: set cursor position
|
||||
[%klr p=stub] :: set styled line
|
||||
{$lin p/(list @c)} :: set current line
|
||||
{$mor ~} :: newline
|
||||
{$sag p/path q/*} :: save to jamfile
|
||||
|
5
pkg/interface/package-lock.json
generated
5
pkg/interface/package-lock.json
generated
@ -4851,6 +4851,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-saver": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz",
|
||||
"integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw=="
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
|
@ -16,6 +16,7 @@
|
||||
"classnames": "^2.2.6",
|
||||
"codemirror": "^5.55.0",
|
||||
"css-loader": "^3.5.3",
|
||||
"file-saver": "^2.0.2",
|
||||
"formik": "^2.1.4",
|
||||
"lodash": "^4.17.15",
|
||||
"markdown-to-jsx": "^6.11.4",
|
||||
|
1
pkg/interface/src/logic/lib/bel.js
Normal file
1
pkg/interface/src/logic/lib/bel.js
Normal file
File diff suppressed because one or more lines are too long
@ -1,3 +1,3 @@
|
||||
const defaultApps = ['chat', 'dojo', 'groups', 'link', 'publish'];
|
||||
const defaultApps = ['chat', 'term', 'groups', 'link', 'publish'];
|
||||
|
||||
export default defaultApps;
|
||||
|
@ -1,119 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import { cite } from '~/logic/lib/util';
|
||||
import { Spinner } from '~/views/components/Spinner';
|
||||
|
||||
export class Input extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
awaiting: false,
|
||||
type: 'Sending to Dojo'
|
||||
};
|
||||
this.keyPress = this.keyPress.bind(this);
|
||||
this.inputRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (
|
||||
!document.activeElement == document.body
|
||||
|| document.activeElement == this.inputRef.current
|
||||
) {
|
||||
this.inputRef.current.focus();
|
||||
this.inputRef.current.setSelectionRange(this.props.cursor, this.props.cursor);
|
||||
}
|
||||
}
|
||||
|
||||
keyPress(e) {
|
||||
if ((e.getModifierState('Control') || event.getModifierState('Meta'))
|
||||
&& e.key === 'v') {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const allowedKeys = [
|
||||
'Enter', 'Backspace', 'ArrowLeft', 'ArrowRight', 'Tab'
|
||||
];
|
||||
|
||||
if ((e.key.length > 1) && (!(allowedKeys.includes(e.key)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// submit on enter
|
||||
if (e.key === 'Enter') {
|
||||
this.setState({ awaiting: true, type: 'Sending to Dojo' });
|
||||
this.props.api.soto('ret').then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
} else if ((e.key === 'Backspace') && (this.props.cursor > 0)) {
|
||||
this.props.store.doEdit({ del: this.props.cursor - 1 });
|
||||
return this.props.store.setState({ cursor: this.props.cursor - 1 });
|
||||
} else if (e.key === 'Backspace') {
|
||||
return;
|
||||
} else if (e.key.startsWith('Arrow')) {
|
||||
if (e.key === 'ArrowLeft') {
|
||||
if (this.props.cursor > 0) {
|
||||
this.props.store.setState({ cursor: this.props.cursor - 1 });
|
||||
}
|
||||
} else if (e.key === 'ArrowRight') {
|
||||
if (this.props.cursor < this.props.input.length) {
|
||||
this.props.store.setState({ cursor: this.props.cursor + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tab completion
|
||||
else if (e.key === 'Tab') {
|
||||
this.setState({ awaiting: true, type: 'Getting suggestions' });
|
||||
this.props.api.soto({ tab: this.props.cursor }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}
|
||||
|
||||
// capture and transmit most characters
|
||||
else {
|
||||
this.props.store.doEdit({ ins: { cha: e.key, at: this.props.cursor } });
|
||||
this.props.store.setState({ cursor: this.props.cursor + 1 });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="flex flex-row flex-grow-1 relative">
|
||||
<div className="flex-shrink-0"><span class="dn-s">{cite(this.props.ship)}:</span>dojo
|
||||
</div>
|
||||
<span id="prompt">
|
||||
{this.props.prompt}
|
||||
</span>
|
||||
<input
|
||||
autoFocus
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
tabindex="0"
|
||||
wrap="off"
|
||||
className="mono ml1 flex-auto dib w-100"
|
||||
id="dojo"
|
||||
cursor={this.props.cursor}
|
||||
onClick={e => this.props.store.setState({ cursor: e.target.selectionEnd })}
|
||||
onKeyDown={this.keyPress}
|
||||
onPaste={(e) => {
|
||||
const clipboardData = e.clipboardData || window.clipboardData;
|
||||
const paste = Array.from(clipboardData.getData('Text'));
|
||||
paste.reduce(async (previous, next) => {
|
||||
await previous;
|
||||
this.setState({ cursor: this.props.cursor + 1 });
|
||||
return this.props.store.doEdit({ ins: { cha: next, at: this.props.cursor } });
|
||||
}, Promise.resolve());
|
||||
e.preventDefault();
|
||||
}}
|
||||
ref={this.inputRef}
|
||||
defaultValue={this.props.input}
|
||||
/>
|
||||
<Spinner awaiting={this.state.awaiting} text={`${this.state.type}...`} classes="absolute right-0 bottom-0 inter pa ba pa2 b--gray1-d" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Input;
|
@ -1,157 +0,0 @@
|
||||
// See /lib/sole/hoon
|
||||
|
||||
const str = JSON.stringify;
|
||||
|
||||
export class Share {
|
||||
constructor(buf, ven, leg) {
|
||||
if (buf == null) {
|
||||
buf = '';
|
||||
}
|
||||
this.buf = buf;
|
||||
if (ven == null) {
|
||||
ven = [0, 0];
|
||||
}
|
||||
this.ven = ven;
|
||||
if (leg == null) {
|
||||
leg = [];
|
||||
}
|
||||
this.leg = leg;
|
||||
}
|
||||
|
||||
abet() {
|
||||
return {
|
||||
buf: this.buf,
|
||||
leg: this.leg.slice(),
|
||||
ven: this.ven.slice()
|
||||
};
|
||||
}
|
||||
|
||||
apply(ted) {
|
||||
switch (false) {
|
||||
case 'nop' !== ted: return;
|
||||
case !ted.map: return ted.map(this.apply, this);
|
||||
default: switch (Object.keys(ted)[0]) {
|
||||
case 'set': return this.buf = ted.set;
|
||||
case 'del': return this.buf = this.buf.slice(0, ted.del) + this.buf.slice(ted.del + 1);
|
||||
case 'ins':
|
||||
var { at, cha } = ted.ins;
|
||||
return this.buf = this.buf.slice(0, at) + cha + this.buf.slice(at);
|
||||
default: throw `%sole-edit -lost.${str(ted)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transmute(sin, dex) {
|
||||
switch (false) {
|
||||
case (sin !== 'nop') && (dex !== 'nop'): return dex;
|
||||
case !sin.reduce:
|
||||
return sin.reduce(((dex, syn) => this.transmute(syn, dex)), dex);
|
||||
case !dex.map: return dex.map(dax => this.transmute(sin, dax));
|
||||
case dex.set === undefined: return dex;
|
||||
default: switch (Object.keys(sin)[0]) {
|
||||
case 'set': return 'nop';
|
||||
case 'del':
|
||||
if (sin.del === dex.del) {
|
||||
return 'nop';
|
||||
}
|
||||
dex = { ...dex };
|
||||
switch (Object.keys(dex)[0]) {
|
||||
case 'del': if (sin.del < dex.del) {
|
||||
dex.del--;
|
||||
}
|
||||
break;
|
||||
case 'ins': if (sin.del < dex.ins.at) {
|
||||
dex.ins.at--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return dex;
|
||||
case 'ins':
|
||||
dex = { ...dex };
|
||||
var { at, cha } = sin.ins;
|
||||
switch (Object.keys(dex)[0]) {
|
||||
case 'del': if (at < dex.del) {
|
||||
dex.del++;
|
||||
}
|
||||
break;
|
||||
case 'ins': if ((at < dex.ins.at) ||
|
||||
((at === dex.ins.at) && !(cha <= dex.ins.cha))) {
|
||||
dex.ins.at++;
|
||||
} else if (at >= dex.ins.at) {
|
||||
dex.ins.at = at; // NOTE possibly unpredictable behaviour
|
||||
dex.ins.at++; // for sole inserts that aren't tabs
|
||||
}
|
||||
break;
|
||||
}
|
||||
return dex;
|
||||
default: throw `%sole-edit -lost.${str(sin)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commit(ted) {
|
||||
this.ven[0]++;
|
||||
this.leg.push(ted);
|
||||
return this.apply(ted);
|
||||
}
|
||||
|
||||
inverse(ted) {
|
||||
switch (false) {
|
||||
case 'nop' !== ted: return ted;
|
||||
case !ted.map:
|
||||
return ted.map((tad) => {
|
||||
const res = this.inverse(tad);
|
||||
this.apply(tad);
|
||||
return res;
|
||||
}).reverse();
|
||||
default: switch (Object.keys(ted)[0]) {
|
||||
case 'set': return { set: this.buf };
|
||||
case 'ins': return { del: ted.ins };
|
||||
case 'del': return { ins: { at: ted.del, cha: this.buf[ted.del] } };
|
||||
default: throw `%sole-edit -lost.${str(ted)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
receive({ ler, ted }) {
|
||||
if (!(ler[1] === this.ven[1])) {
|
||||
throw `-out-of-sync.[${str(ler)} ${str(this.ven)}]`;
|
||||
}
|
||||
this.leg = this.leg.slice((this.leg.length + ler[0]) - this.ven[0]);
|
||||
const dat = this.transmute(this.leg, ted);
|
||||
this.ven[1]++;
|
||||
this.apply(dat);
|
||||
return dat;
|
||||
}
|
||||
|
||||
remit() {
|
||||
throw 'stub';
|
||||
}
|
||||
|
||||
transmit(ted) {
|
||||
const act = { ted, ler: [this.ven[1], this.ven[0]] };
|
||||
this.commit(ted);
|
||||
return act;
|
||||
}
|
||||
|
||||
transceive({ ler, ted }) {
|
||||
const old = new Share(this.buf);
|
||||
const dat = this.receive({ ler, ted });
|
||||
return old.inverse(dat);
|
||||
}
|
||||
|
||||
transpose(ted, pos) {
|
||||
if (pos === undefined) {
|
||||
return this.transpose(this.leg, ted);
|
||||
} else {
|
||||
let left;
|
||||
return ((left =
|
||||
(this.transmute(
|
||||
ted, { ins: { at: pos } })).ins) != null ?
|
||||
left : { at: 0 }
|
||||
).at;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default Share;
|
@ -1,11 +0,0 @@
|
||||
input#dojo {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* responsive */
|
||||
@media all and (max-width: 34.375em) {
|
||||
.h-100-m40-s {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
import Share from './components/lib/sole';
|
||||
export default class Store {
|
||||
constructor() {
|
||||
this.state = this.initialState();
|
||||
this.sync = this.sync.bind(this);
|
||||
this.print = this.print.bind(this);
|
||||
this.buffer = new Share();
|
||||
}
|
||||
|
||||
initialState() {
|
||||
return {
|
||||
txt: [],
|
||||
prompt: '',
|
||||
cursor: 0,
|
||||
input: ''
|
||||
};
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.handleEvent({
|
||||
data: { clear: true }
|
||||
});
|
||||
}
|
||||
|
||||
handleEvent(data) {
|
||||
// recursive handler
|
||||
if (data.data) {
|
||||
var dojoReply = data.data;
|
||||
} else {
|
||||
var dojoReply = data;
|
||||
}
|
||||
|
||||
if (dojoReply.clear) {
|
||||
this.setState(this.initialState(), (() => {
|
||||
return;
|
||||
}));
|
||||
}
|
||||
|
||||
// %mor sole-effects are nested, so throw back to handler
|
||||
if (dojoReply.map) {
|
||||
return dojoReply.map(reply => this.handleEvent(reply));
|
||||
}
|
||||
|
||||
switch (Object.keys(dojoReply)[0]) {
|
||||
case 'txt':
|
||||
return this.print(dojoReply.txt);
|
||||
case 'tab':
|
||||
this.print(dojoReply.tab.match + ' ' + dojoReply.tab.info);
|
||||
return;
|
||||
case 'tan':
|
||||
return dojoReply.tan.split('\n').map(this.print);
|
||||
case 'pro':
|
||||
return this.setState({ prompt: dojoReply.pro.cad });
|
||||
case 'hop':
|
||||
return this.setState({ cursor: dojoReply.hop });
|
||||
case 'det':
|
||||
this.buffer.receive(dojoReply.det);
|
||||
return this.sync(dojoReply.det.ted);
|
||||
case 'act':
|
||||
switch (dojoReply.act) {
|
||||
case 'clr': return this.setState({ txt: [] });
|
||||
case 'nex': return this.setState({
|
||||
input: '',
|
||||
cursor: 0
|
||||
});
|
||||
}
|
||||
break;
|
||||
default: console.log(dojoReply);
|
||||
}
|
||||
}
|
||||
|
||||
doEdit(ted) {
|
||||
const detSend = this.buffer.transmit(ted);
|
||||
this.sync(ted);
|
||||
return this.api.soto({ det: detSend });
|
||||
}
|
||||
|
||||
print(txt) {
|
||||
const textLog = this.state.txt;
|
||||
textLog.push(txt);
|
||||
return this.setState({ txt: textLog });
|
||||
}
|
||||
|
||||
sync(ted) {
|
||||
return this.setState({
|
||||
input: this.buffer.buf,
|
||||
cursor: this.buffer.transpose(ted, this.state.cursor)
|
||||
});
|
||||
}
|
||||
|
||||
setStateHandler(setState) {
|
||||
this.setState = setState;
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ export default class BasicTile extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<Tile
|
||||
bg={props.title === 'Dojo' ? '#000000' : 'white'}
|
||||
bg={props.title === 'Terminal' ? '#000000' : 'white'}
|
||||
to={props.linkedUrl}
|
||||
>
|
||||
<Text color={props.title === 'Dojo' ? '#ffffff' : 'black'}>
|
||||
{props.title === 'Dojo'
|
||||
<Text color={props.title === 'Terminal' ? '#ffffff' : 'black'}>
|
||||
{props.title === 'Terminal'
|
||||
? <Icon
|
||||
icon='ChevronEast'
|
||||
color='#fff'
|
||||
|
@ -5,10 +5,9 @@ export default class Api {
|
||||
this.ship = ship;
|
||||
this.channel = channel;
|
||||
this.bindPaths = [];
|
||||
this.dojoId = 'soto-' + Math.random().toString(36).substring(2);
|
||||
}
|
||||
|
||||
bind(path, method, ship = this.ship, appl = 'dojo', success, fail) {
|
||||
bind(path, method, ship = this.ship, appl = 'herm', success, fail) {
|
||||
this.bindPaths = _.uniq([...this.bindPaths, path]);
|
||||
|
||||
window.subscriptionId = this.channel.subscribe(ship, appl, path,
|
||||
@ -29,8 +28,8 @@ export default class Api {
|
||||
});
|
||||
}
|
||||
|
||||
soto(data) {
|
||||
return this.action('dojo', 'sole-action', { id: this.dojoId, dat: data });
|
||||
belt(belt) {
|
||||
return this.action('herm', 'belt', belt);
|
||||
}
|
||||
|
||||
action(appl, mark, data) {
|
@ -11,7 +11,7 @@ import Subscription from './subscription';
|
||||
|
||||
import './css/custom.css';
|
||||
|
||||
export default class DojoApp extends Component {
|
||||
export default class TermApp extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.store = new Store();
|
||||
@ -45,14 +45,14 @@ export default class DojoApp extends Component {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>OS1 - Dojo</title>
|
||||
<title>OS1 - Terminal</title>
|
||||
</Helmet>
|
||||
<div
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<Route
|
||||
exact
|
||||
path="/~dojo/"
|
||||
path="/~term/"
|
||||
render={(props) => {
|
||||
return (
|
||||
<div className="w-100 h-100 flex-m flex-l flex-xl">
|
||||
@ -72,14 +72,13 @@ export default class DojoApp extends Component {
|
||||
cursor: 'text'
|
||||
}}
|
||||
>
|
||||
<History commandLog={this.state.txt} />
|
||||
<History log={this.state.lines.slice(0, -1)} />
|
||||
<Input
|
||||
ship={this.props.ship}
|
||||
cursor={this.state.cursor}
|
||||
prompt={this.state.prompt}
|
||||
input={this.state.input}
|
||||
api={this.api}
|
||||
store={this.store}
|
||||
line={this.state.lines.slice(-1)[0]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import Line from './line';
|
||||
|
||||
export class History extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -12,14 +14,8 @@ export class History extends Component {
|
||||
style={{ resize: 'none' }}
|
||||
>
|
||||
<div style={{ marginTop: 'auto' }}>
|
||||
{this.props.commandLog.map((text, index) => {
|
||||
return (
|
||||
<p className="mono" key={index}
|
||||
style={{ overflowWrap: 'break-word', whiteSpace: 'pre' }}
|
||||
>
|
||||
{text}
|
||||
</p>
|
||||
);
|
||||
{this.props.log.map((line, i) => {
|
||||
return <Line key={i} line={line} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
112
pkg/interface/src/views/apps/term/components/input.js
Normal file
112
pkg/interface/src/views/apps/term/components/input.js
Normal file
@ -0,0 +1,112 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Row, Box, BaseInput } from '@tlon/indigo-react';
|
||||
import { cite } from '~/logic/lib/util';
|
||||
import { Spinner } from '~/views/components/Spinner';
|
||||
|
||||
export class Input extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
this.keyPress = this.keyPress.bind(this);
|
||||
this.paste = this.paste.bind(this);
|
||||
this.click = this.click.bind(this);
|
||||
this.inputRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (
|
||||
!document.activeElement == document.body
|
||||
|| document.activeElement == this.inputRef.current
|
||||
) {
|
||||
this.inputRef.current.focus();
|
||||
this.inputRef.current.setSelectionRange(this.props.cursor, this.props.cursor);
|
||||
}
|
||||
}
|
||||
|
||||
keyPress(e) {
|
||||
let key = e.key;
|
||||
// let paste and leap events pass
|
||||
if ((e.getModifierState('Control') || event.getModifierState('Meta'))
|
||||
&& (e.key === 'v' || e.key === '/')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let belt = null;
|
||||
if (key === 'ArrowLeft') belt = {aro: 'l'};
|
||||
else if (key === 'ArrowRight') belt = {aro: 'r'};
|
||||
else if (key === 'ArrowUp') belt = {aro: 'u'};
|
||||
else if (key === 'ArrowDown') belt = {aro: 'd'};
|
||||
else if (key === 'Backspace') belt = {bac: null};
|
||||
else if (key === 'Delete') belt = {del: null};
|
||||
else if (key === 'Tab') belt = {ctl: 'i'};
|
||||
else if (key === 'Enter') belt = {ret: null};
|
||||
else if (key.length === 1) belt = {txt: [key]};
|
||||
else belt = null;
|
||||
|
||||
if (belt && e.getModifierState('Control')) {
|
||||
if (belt.txt !== undefined) belt = {ctl: belt.txt[0]};
|
||||
} else
|
||||
if (belt &&
|
||||
(e.getModifierState('Meta') || e.getModifierState('Alt'))) {
|
||||
if (belt.bac !== undefined) belt = {met: 'bac'};
|
||||
}
|
||||
|
||||
if (belt !== null) {
|
||||
this.props.api.belt(belt);
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
paste(e) {
|
||||
const clipboardData = e.clipboardData || window.clipboardData;
|
||||
const clipboardText = clipboardData.getData('Text');
|
||||
this.props.api.belt({ txt: [...clipboardText] });
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
click(e) {
|
||||
// prevent desynced cursor movement
|
||||
e.preventDefault();
|
||||
e.target.setSelectionRange(this.props.cursor, this.props.cursor);
|
||||
}
|
||||
|
||||
render() {
|
||||
const line = this.props.line;
|
||||
let prompt = 'connecting...';
|
||||
if (line) {
|
||||
if (line.lin) {
|
||||
prompt = line.lin.join('');
|
||||
}
|
||||
//TODO render prompt style
|
||||
else if (line.klr) {
|
||||
prompt = line.klr.reduce((l, p) => (l + p.text.join('')), '');
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Row flexGrow='1' position='relative'>
|
||||
<Box flexShrink='0' className="w-100">
|
||||
<BaseInput
|
||||
autoFocus
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
tabindex="0"
|
||||
wrap="off"
|
||||
className="mono ml1 flex-auto dib w-100"
|
||||
id="term"
|
||||
cursor={this.props.cursor}
|
||||
onKeyDown={this.keyPress}
|
||||
onClick={this.click}
|
||||
onPaste={this.paste}
|
||||
ref={this.inputRef}
|
||||
defaultValue="connecting..."
|
||||
value={prompt}
|
||||
/>
|
||||
</Box>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Input;
|
69
pkg/interface/src/views/apps/term/components/line.js
Normal file
69
pkg/interface/src/views/apps/term/components/line.js
Normal file
@ -0,0 +1,69 @@
|
||||
import React, { Component, useMemo } from 'react';
|
||||
import { Box, Text } from '@tlon/indigo-react';
|
||||
|
||||
export default React.memo(({line}) => {
|
||||
|
||||
// line body to jsx
|
||||
//NOTE lines are lists of characters that might span multiple codepoints
|
||||
//
|
||||
let text = '';
|
||||
if (line.lin) {
|
||||
text = line.lin.join('');
|
||||
}
|
||||
else if (line.klr) {
|
||||
text = line.klr.map((part, i) => {
|
||||
let prop = part.stye.deco.reduce((prop, deco) => {
|
||||
switch (deco) {
|
||||
case null: return prop;
|
||||
case 'br': return {bold: true, ...prop};
|
||||
case 'bl': return {className: 'blink', ...prop};
|
||||
case 'un': return {style: {textDecoration: 'underline'}, ...prop};
|
||||
default: console.log('weird deco', deco); return prop;
|
||||
}
|
||||
}, {});
|
||||
switch (part.stye.fore) {
|
||||
case null: break;
|
||||
case 'r': prop.color = 'red'; break;
|
||||
case 'g': prop.color = 'green'; break;
|
||||
case 'b': prop.color = 'blue'; break;
|
||||
case 'c': prop.color = 'cyan'; break;
|
||||
case 'm': prop.color = 'purple'; break;
|
||||
case 'y': prop.color = 'yellow'; break;
|
||||
case 'k': prop.color = 'black'; break;
|
||||
case 'w': prop.color = 'white'; break;
|
||||
default: console.log('weird fore', part.stye.fore);
|
||||
}
|
||||
switch (part.stye.back) {
|
||||
case null: break;
|
||||
case 'r': prop.backgroundColor = 'red'; break;
|
||||
case 'g': prop.backgroundColor = 'green'; break;
|
||||
case 'b': prop.backgroundColor = 'blue'; break;
|
||||
case 'c': prop.backgroundColor = 'cyan'; break;
|
||||
case 'm': prop.backgroundColor = 'purple'; break;
|
||||
case 'y': prop.backgroundColor = 'yellow'; break;
|
||||
case 'k': prop.backgroundColor = 'black'; break;
|
||||
case 'w': prop.backgroundColor = 'white'; break;
|
||||
default: console.log('weird back', part.stye.back);
|
||||
}
|
||||
if (Object.keys(prop).length === 0)
|
||||
{
|
||||
return part.text;
|
||||
} else {
|
||||
return (<Text mono fontSize='inherit' key={i} {...prop}>
|
||||
{part.text.join('')}
|
||||
</Text>);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// render line
|
||||
//
|
||||
return (
|
||||
<Text mono display='block' fontSize='14px'
|
||||
style={{ overflowWrap: 'break-word', whiteSpace: 'pre-wrap' }}
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
);
|
||||
|
||||
});
|
23
pkg/interface/src/views/apps/term/css/custom.css
Normal file
23
pkg/interface/src/views/apps/term/css/custom.css
Normal file
@ -0,0 +1,23 @@
|
||||
input#term {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.blink {
|
||||
animation: 4s ease-in-out infinite opacity_blink;
|
||||
}
|
||||
|
||||
@keyframes opacity_blink {
|
||||
0% { opacity: 0; }
|
||||
10% { opacity: 1; }
|
||||
80% { opacity: 1; }
|
||||
90% { opacity: 0; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
|
||||
/* responsive */
|
||||
@media all and (max-width: 34.375em) {
|
||||
.h-100-m40-s {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
90
pkg/interface/src/views/apps/term/store.js
Normal file
90
pkg/interface/src/views/apps/term/store.js
Normal file
@ -0,0 +1,90 @@
|
||||
import { saveAs } from 'file-saver';
|
||||
import bel from '../../../logic/lib/bel'
|
||||
|
||||
export default class Store {
|
||||
constructor() {
|
||||
this.state = this.initialState();
|
||||
}
|
||||
|
||||
initialState() {
|
||||
return {
|
||||
lines: [''],
|
||||
cursor: 0,
|
||||
};
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.setState(this.initialState())
|
||||
}
|
||||
|
||||
handleEvent(data) {
|
||||
// process slogs
|
||||
//
|
||||
if (data.slog) {
|
||||
this.state.lines.splice(this.state.lines.length-1, 0, {lin: [data.slog]});
|
||||
this.setState({ lines: this.state.lines });
|
||||
return;
|
||||
}
|
||||
|
||||
// process blits
|
||||
//
|
||||
const blit = data.data;
|
||||
switch (Object.keys(blit)[0]) {
|
||||
case 'bel':
|
||||
bel.play();
|
||||
break;
|
||||
case 'clr':
|
||||
this.state.lines = this.state.lines.slice(-1);
|
||||
this.setState({ lines: this.state.lines });
|
||||
break;
|
||||
case 'hop':
|
||||
// since lines are lists of characters that might span multiple
|
||||
// codepoints, we need to calculate the byte-wise cursor position
|
||||
// to avoid incorrect cursor rendering.
|
||||
//
|
||||
let line = this.state.lines[this.state.lines.length - 1];
|
||||
let hops;
|
||||
if (line.lin) {
|
||||
hops = line.lin.slice(0, blit.hop);
|
||||
}
|
||||
else if (line.klr) {
|
||||
hops = line.klr.reduce((h, p) => {
|
||||
if (h.length >= blit.hop) return h;
|
||||
return [...h, ...p.text.slice(0, blit.hop - h.length)]
|
||||
}, []);
|
||||
}
|
||||
this.setState({ cursor: hops.join('').length });
|
||||
break;
|
||||
case 'lin':
|
||||
this.state.lines[this.state.lines.length - 1] = blit;
|
||||
this.setState({ lines: this.state.lines });
|
||||
break;
|
||||
case 'klr':
|
||||
this.state.lines[this.state.lines.length - 1] = blit;
|
||||
this.setState({ lines: this.state.lines });
|
||||
break;
|
||||
case 'mor':
|
||||
this.state.lines.push('');
|
||||
this.setState({ lines: this.state.lines });
|
||||
break;
|
||||
case 'sag':
|
||||
blit.sav = blit.sag;
|
||||
case 'sav':
|
||||
let name = blit.sav.path.split('/').slice(-2).join('.');
|
||||
let buff = new Buffer(blit.sav.file, 'base64');
|
||||
let blob = new Blob([buff], {type: 'application/octet-stream'});
|
||||
saveAs(blob, name);
|
||||
break;
|
||||
case 'url':
|
||||
//TODO too invasive? just print as <a>?
|
||||
window.open(blit.url);
|
||||
break;
|
||||
default: console.log('weird blit', blit);
|
||||
}
|
||||
}
|
||||
|
||||
setStateHandler(setState) {
|
||||
this.setState = setState;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ export default class Subscription {
|
||||
}
|
||||
|
||||
slog.onmessage = e => {
|
||||
this.handleEvent({ txt: e.data });
|
||||
this.handleEvent({ slog: e.data });
|
||||
}
|
||||
|
||||
slog.onerror = e => {
|
||||
@ -72,7 +72,7 @@ export default class Subscription {
|
||||
}
|
||||
|
||||
firstRound() {
|
||||
this.subscribe('/sole/' + this.api.dojoId, 'dojo');
|
||||
this.subscribe('/session/', 'herm');
|
||||
}
|
||||
|
||||
handleEvent(diff) {
|
@ -117,7 +117,13 @@ export class Omnibox extends Component {
|
||||
const { props } = this;
|
||||
this.setState({ results: this.initialResults(), query: '' }, () => {
|
||||
props.api.local.setOmnibox();
|
||||
if (defaultApps.includes(app.toLowerCase()) || app === 'profile' || app === 'Links' || app === 'home' || app === 'inbox') {
|
||||
if (defaultApps.includes(app.toLowerCase())
|
||||
|| app === 'profile'
|
||||
|| app === 'Links'
|
||||
|| app === 'Terminal'
|
||||
|| app === 'home'
|
||||
|| app === 'inbox')
|
||||
{
|
||||
props.history.push(link);
|
||||
} else {
|
||||
window.location.href = link;
|
||||
|
@ -33,8 +33,12 @@ export class OmniboxResult extends Component {
|
||||
const inviteCount = Object.keys(invites?.['contacts'] || {});
|
||||
|
||||
let graphic = <div />;
|
||||
if (defaultApps.includes(icon.toLowerCase()) || icon.toLowerCase() === 'links') {
|
||||
icon = (icon === 'Link') ? 'Links' : icon;
|
||||
if (defaultApps.includes(icon.toLowerCase())
|
||||
|| icon.toLowerCase() === 'links'
|
||||
|| icon.toLowerCase() === 'terminal')
|
||||
{
|
||||
icon = (icon === 'Link') ? 'Links' :
|
||||
(icon === 'Terminal') ? 'Dojo' : icon;
|
||||
graphic = <Icon display="inline-block" verticalAlign="middle" icon={icon} mr='2' size='16px' color={iconFill} />;
|
||||
} else if (icon === 'inbox') {
|
||||
graphic = <Box display='flex' verticalAlign='middle'>
|
||||
|
@ -4,7 +4,7 @@ import { Route, Switch } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import LaunchApp from '~/views/apps/launch/app';
|
||||
import DojoApp from '~/views/apps/dojo/app';
|
||||
import TermApp from '~/views/apps/term/app';
|
||||
import Landscape from '~/views/landscape/index';
|
||||
import Profile from '~/views/apps/profile/profile';
|
||||
import ErrorComponent from '~/views/components/Error';
|
||||
@ -35,9 +35,9 @@ export const Content = (props) => {
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path='/~dojo'
|
||||
path='/~term'
|
||||
render={p => (
|
||||
<DojoApp
|
||||
<TermApp
|
||||
history={p.history}
|
||||
location={p.location}
|
||||
match={p.match}
|
||||
|
Loading…
Reference in New Issue
Block a user