mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-15 01:52:42 +03:00
commit
6783da2c4c
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:325711d85d65daa9c0e9ca74de0a4cd1227898c95d2aa0c9f7caa2d83404d41d
|
||||
size 6326025
|
||||
oid sha256:419c83939dc6080665477b923097a1b72edd3fdf26f36c4b21345bb024c56b86
|
||||
size 6346065
|
||||
|
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
|
||||
@ -237,10 +237,11 @@
|
||||
=> (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)
|
||||
=> (se-born | %home %graph-pull-hook)
|
||||
(se-born | %home %herm)
|
||||
..on-load
|
||||
::
|
||||
++ reap-phat :: ack connect
|
||||
|
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 ::
|
||||
$: %4 ::
|
||||
$: %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
|
||||
@ -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)
|
||||
@ -369,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)
|
||||
@ -404,6 +431,7 @@
|
||||
$~ (~(put by *(map @tas log-level)) %hole %soft)
|
||||
(map @tas log-level)
|
||||
==
|
||||
::
|
||||
++ axle-2
|
||||
$: %2
|
||||
hey/(unit duct)
|
||||
@ -450,7 +478,7 @@
|
||||
?- -.old
|
||||
%1 $(old [%2 [hey dug lit dog=& hef veb]:old])
|
||||
%2 $(old [%3 [hey dug lit veb]:old])
|
||||
%3 =- $(old [%4 hey.old - lit.old veb.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)
|
||||
==
|
||||
@ -458,15 +486,35 @@
|
||||
++ 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
|
||||
|
5
pkg/interface/package-lock.json
generated
5
pkg/interface/package-lock.json
generated
@ -4876,6 +4876,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",
|
||||
|
@ -15,6 +15,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'
|
||||
|
@ -73,20 +73,20 @@ function DragTileBasic(props: {
|
||||
style: any;
|
||||
}) {
|
||||
const { basic: tile } = props.tile;
|
||||
const isDojo = useMemo(() => tile.title === "Dojo", [tile.title]);
|
||||
const isTerm = useMemo(() => tile.title === "Terminal", [tile.title]);
|
||||
return (
|
||||
<DragTileBox
|
||||
tile={{ type: props.tile }}
|
||||
index={props.index}
|
||||
bg={
|
||||
"white" // isDojo ? "black" : "white"
|
||||
"white" // isTerm ? "black" : "white"
|
||||
}
|
||||
style={props.style}
|
||||
>
|
||||
<Image width="48px" height="48px" src={tile.iconUrl} invert={isDojo} />
|
||||
<Image width="48px" height="48px" src={tile.iconUrl} invert={isTerm} />
|
||||
<Text
|
||||
color={
|
||||
"black" // isDojo ? "white" : "black"
|
||||
"black" // isTerm ? "white" : "black"
|
||||
}
|
||||
>
|
||||
{tile.title}
|
||||
|
@ -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,12 @@ 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') {
|
||||
if (defaultApps.includes(app.toLowerCase())
|
||||
|| app === 'profile'
|
||||
|| app === 'Links'
|
||||
|| app === 'Terminal'
|
||||
|| app === 'home')
|
||||
{
|
||||
props.history.push(link);
|
||||
} else {
|
||||
window.location.href = link;
|
||||
|
@ -30,8 +30,12 @@ export class OmniboxResult extends Component {
|
||||
const sigilFill = (this.state.hovered || (selected === link)) ? '#3a8ff7' : '#ffffff';
|
||||
|
||||
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 === 'logout') {
|
||||
graphic = <Icon display="inline-block" verticalAlign="middle" icon='ArrowWest' mr='2' size='16px' color={iconFill} />;
|
||||
|
@ -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';
|
||||
@ -34,9 +34,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