mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-15 01:52:42 +03:00
Merge branch 'matilde/soto' (#1952)
* origin/matilde/soto: soto: style changes interface: add soto source code drum: Boot with %soto arvo: adds soto application mar/sole: add %tab conversion Signed-off-by: Jared Tobin <jared@tlon.io>
This commit is contained in:
commit
5258579ee2
171
pkg/arvo/app/soto.hoon
Normal file
171
pkg/arvo/app/soto.hoon
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
::
|
||||||
|
:: Soto: A Dojo relay for Urbit's Landscape interface
|
||||||
|
:: Relays sole-effects to subscribers and forwards sole-action pokes
|
||||||
|
::
|
||||||
|
/- sole
|
||||||
|
/+ *server, *soto
|
||||||
|
/= index
|
||||||
|
/^ octs
|
||||||
|
/; as-octs:mimes:html
|
||||||
|
/: /===/app/soto/index
|
||||||
|
/| /html/
|
||||||
|
/~ ~
|
||||||
|
==
|
||||||
|
/= tile-js
|
||||||
|
/^ octs
|
||||||
|
/; as-octs:mimes:html
|
||||||
|
/: /===/app/soto/js/tile
|
||||||
|
/| /js/
|
||||||
|
/~ ~
|
||||||
|
==
|
||||||
|
/= script
|
||||||
|
/^ octs
|
||||||
|
/; as-octs:mimes:html
|
||||||
|
/: /===/app/soto/js/index
|
||||||
|
/| /js/
|
||||||
|
/~ ~
|
||||||
|
==
|
||||||
|
/= style
|
||||||
|
/^ octs
|
||||||
|
/; as-octs:mimes:html
|
||||||
|
/: /===/app/soto/css/index
|
||||||
|
/| /css/
|
||||||
|
/~ ~
|
||||||
|
==
|
||||||
|
/= soto-png
|
||||||
|
/^ (map knot @)
|
||||||
|
/: /===/app/soto/img /_ /png/
|
||||||
|
::
|
||||||
|
|%
|
||||||
|
+$ state
|
||||||
|
:: bon: bone from Dojo peer
|
||||||
|
::
|
||||||
|
$% [%0 bon=bone]
|
||||||
|
==
|
||||||
|
::
|
||||||
|
+$ move [bone card]
|
||||||
|
::
|
||||||
|
+$ poke
|
||||||
|
$% [%launch-action [@tas path @t]]
|
||||||
|
[%sole-action sole-action]
|
||||||
|
[%json json]
|
||||||
|
==
|
||||||
|
::
|
||||||
|
+$ card
|
||||||
|
$% [%poke wire dock poke]
|
||||||
|
[%peer wire dock path]
|
||||||
|
[%http-response =http-event:http]
|
||||||
|
[%connect wire binding:eyre term]
|
||||||
|
[%diff diff]
|
||||||
|
==
|
||||||
|
::
|
||||||
|
+$ diff
|
||||||
|
$% [%json json]
|
||||||
|
[%sole-effect sole-effect]
|
||||||
|
==
|
||||||
|
::
|
||||||
|
--
|
||||||
|
::
|
||||||
|
|_ [bol=bowl:gall sta=state]
|
||||||
|
::
|
||||||
|
++ this .
|
||||||
|
::
|
||||||
|
++ prep
|
||||||
|
|= old=(unit state)
|
||||||
|
^- (quip move _this)
|
||||||
|
=/ launcha=poke
|
||||||
|
[%launch-action [%soto /sototile '/~soto/js/tile.js']]
|
||||||
|
?~ old
|
||||||
|
:_ this
|
||||||
|
:~
|
||||||
|
[ost.bol %connect / [~ /'~soto'] %soto]
|
||||||
|
[ost.bol %poke /soto [our.bol %launch] launcha]
|
||||||
|
==
|
||||||
|
[~ this(sta u.old)]
|
||||||
|
::
|
||||||
|
++ peer-sototile
|
||||||
|
|= wir=wire
|
||||||
|
^- (quip move _this)
|
||||||
|
:_ this
|
||||||
|
[ost.bol %diff %json *json]~
|
||||||
|
::
|
||||||
|
:: Peering Dojo when peered by front-end, initiating the session
|
||||||
|
::
|
||||||
|
++ peer-primary
|
||||||
|
|= wir=wire
|
||||||
|
^- (quip move _this)
|
||||||
|
?> (team:title our.bol src.bol)
|
||||||
|
:: poke/peer need same wire
|
||||||
|
::
|
||||||
|
:_ this(bon.sta ost.bol)
|
||||||
|
[ost.bol %peer / [our.bol %dojo] /sole]~
|
||||||
|
::
|
||||||
|
++ poke-json
|
||||||
|
|= =json
|
||||||
|
^- (quip move _this)
|
||||||
|
?> (team:title our.bol src.bol)
|
||||||
|
(poke-sole-action (json-to-action json))
|
||||||
|
::
|
||||||
|
++ poke-sole-action
|
||||||
|
|= act=sole-action
|
||||||
|
^- (quip move _this)
|
||||||
|
:: poke/peer need same wire
|
||||||
|
::
|
||||||
|
:_ this
|
||||||
|
[bon.sta %poke / [our.bol %dojo] [%sole-action act]]~
|
||||||
|
::
|
||||||
|
++ diff-sole-effect
|
||||||
|
|= [=wire fec=sole-effect]
|
||||||
|
^- (quip move _this)
|
||||||
|
:_ this
|
||||||
|
[bon.sta %diff %json (effect-to-json fec)]~
|
||||||
|
::
|
||||||
|
++ bound
|
||||||
|
|= [wir=wire success=? binding=binding:eyre]
|
||||||
|
^- (quip move _this)
|
||||||
|
[~ this]
|
||||||
|
::
|
||||||
|
++ poke-handle-http-request
|
||||||
|
%- (require-authorization:app ost.bol move this)
|
||||||
|
|= =inbound-request:eyre
|
||||||
|
^- (quip move _this)
|
||||||
|
::
|
||||||
|
=+ request-line=(parse-request-line url.request.inbound-request)
|
||||||
|
=/ name=@t
|
||||||
|
=+ back-path=(flop site.request-line)
|
||||||
|
?~ back-path
|
||||||
|
''
|
||||||
|
i.back-path
|
||||||
|
?: =(name 'tile')
|
||||||
|
[[ost.bol %http-response (js-response:app tile-js)]~ this]
|
||||||
|
?+ site.request-line
|
||||||
|
:_ this
|
||||||
|
[ost.bol %http-response not-found:app]~
|
||||||
|
::
|
||||||
|
:: styling
|
||||||
|
::
|
||||||
|
[%'~soto' %css %index ~]
|
||||||
|
:_ this
|
||||||
|
[ost.bol %http-response (css-response:app style)]~
|
||||||
|
::
|
||||||
|
:: javascript
|
||||||
|
::
|
||||||
|
[%'~soto' %js %index ~]
|
||||||
|
:_ this
|
||||||
|
[ost.bol %http-response (js-response:app script)]~
|
||||||
|
::
|
||||||
|
:: images
|
||||||
|
::
|
||||||
|
[%'~soto' %img *]
|
||||||
|
=/ img (as-octs:mimes:html (~(got by soto-png) `@ta`name))
|
||||||
|
:_ this
|
||||||
|
[ost.bol %http-response (png-response:app img)]~
|
||||||
|
::
|
||||||
|
:: index page
|
||||||
|
::
|
||||||
|
[%'~soto' *]
|
||||||
|
:_ this
|
||||||
|
[ost.bol %http-response (html-response:app index)]~
|
||||||
|
==
|
||||||
|
::
|
||||||
|
--
|
1
pkg/arvo/app/soto/css/index.css
Normal file
1
pkg/arvo/app/soto/css/index.css
Normal file
File diff suppressed because one or more lines are too long
BIN
pkg/arvo/app/soto/img/Tile.png
Normal file
BIN
pkg/arvo/app/soto/img/Tile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
16
pkg/arvo/app/soto/index.html
Normal file
16
pkg/arvo/app/soto/index.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Dojo</title>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
|
||||||
|
<link rel="stylesheet" href="/~soto/css/index.css" />
|
||||||
|
</head>
|
||||||
|
<body class="bg-black">
|
||||||
|
<div id="root" />
|
||||||
|
<script src="/~/channel/channel.js"></script>
|
||||||
|
<script src="/~modulo/session.js"></script>
|
||||||
|
<script src="/~soto/js/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
pkg/arvo/app/soto/js/index.js
Normal file
1
pkg/arvo/app/soto/js/index.js
Normal file
File diff suppressed because one or more lines are too long
1
pkg/arvo/app/soto/js/tile.js
Normal file
1
pkg/arvo/app/soto/js/tile.js
Normal file
File diff suppressed because one or more lines are too long
@ -111,6 +111,7 @@
|
|||||||
%chat-hook
|
%chat-hook
|
||||||
%chat-view
|
%chat-view
|
||||||
%chat-cli
|
%chat-cli
|
||||||
|
%soto
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ deft-fish :: default connects
|
++ deft-fish :: default connects
|
||||||
|
98
pkg/arvo/lib/soto.hoon
Normal file
98
pkg/arvo/lib/soto.hoon
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/+ *sole
|
||||||
|
::
|
||||||
|
::TODO revert after #1946
|
||||||
|
::
|
||||||
|
|%
|
||||||
|
::
|
||||||
|
++ json-to-action
|
||||||
|
|= jon=json ^- sole-action
|
||||||
|
%- need %. jon
|
||||||
|
=> [dejs-soft:format ..sole-action]
|
||||||
|
|^ (fo %ret (of det+change tab+ni ~))
|
||||||
|
++ fo
|
||||||
|
|* [a=term b=fist]
|
||||||
|
|=(c=json ?.(=([%s a] c) (b c) (some [a ~])))
|
||||||
|
::
|
||||||
|
++ ra
|
||||||
|
|* [a=[term fist] b=fist]
|
||||||
|
|= c=json %. c
|
||||||
|
?.(=(%a -.c) b (pe -.a (ar +.a)))
|
||||||
|
::
|
||||||
|
++ ke :: callbacks
|
||||||
|
|* [gar=* sef=(trap fist)]
|
||||||
|
|= jon=json ^- (unit _gar)
|
||||||
|
=- ~! gar ~! (need -) -
|
||||||
|
((sef) jon)
|
||||||
|
::
|
||||||
|
++ change (ot ler+(at ni ni ~) ted+(pe 0v0 edit) ~)
|
||||||
|
++ char (cu taft so)
|
||||||
|
++ edit
|
||||||
|
%+ ke *sole-edit |. ~+
|
||||||
|
%+ fo %nop
|
||||||
|
%+ ra mor+edit
|
||||||
|
(of del+ni set+(cu tuba sa) ins+(ot at+ni cha+char ~) ~)
|
||||||
|
--
|
||||||
|
::
|
||||||
|
++ effect-to-json
|
||||||
|
|= sef=sole-effect
|
||||||
|
|^ ^- json
|
||||||
|
=, enjs:format
|
||||||
|
?+ -.sef
|
||||||
|
~|(unsupported-effect+-.sef !!)
|
||||||
|
$mor [%a (turn p.sef |=(a/sole-effect ^$(sef a)))]
|
||||||
|
$err (frond %hop (numb p.sef))
|
||||||
|
$txt (frond %txt (tape p.sef))
|
||||||
|
$tan (frond %tan (tape (wush 160 p.sef)))
|
||||||
|
$det (frond %det json:~(grow mar-sole-change +.sef))
|
||||||
|
::
|
||||||
|
$pro
|
||||||
|
%+ frond %pro
|
||||||
|
(pairs vis+b+vis.sef tag+s+tag.sef cad+(tape (purge cad.sef)) ~)
|
||||||
|
::
|
||||||
|
$tab
|
||||||
|
:- %a
|
||||||
|
%+ turn p.sef
|
||||||
|
|= [=cord =^tank]
|
||||||
|
%- pairs
|
||||||
|
:~ match+s+cord
|
||||||
|
info+(tape ~(ram re tank))
|
||||||
|
==
|
||||||
|
::
|
||||||
|
?($bel $clr $nex)
|
||||||
|
(frond %act %s -.sef)
|
||||||
|
==
|
||||||
|
++ mar-sole-change
|
||||||
|
|_ cha=sole-change
|
||||||
|
++ grow
|
||||||
|
|% ++ json
|
||||||
|
^- ^json
|
||||||
|
=, enjs:format
|
||||||
|
=; edi
|
||||||
|
=,(cha (pairs ted+(edi ted) ler+a+~[(numb own.ler) (numb his.ler)] ~))
|
||||||
|
|= det=sole-edit
|
||||||
|
?- -.det
|
||||||
|
$nop [%s 'nop']
|
||||||
|
$mor [%a (turn p.det ..$)]
|
||||||
|
$del (frond %del (numb p.det))
|
||||||
|
$set (frond %set (tape (tufa p.det)))
|
||||||
|
$ins (frond %ins (pairs at+(numb p.det) cha+s+(tuft q.det) ~))
|
||||||
|
==
|
||||||
|
--
|
||||||
|
--
|
||||||
|
++ wush
|
||||||
|
|= [wid=@u tan=tang]
|
||||||
|
^- tape
|
||||||
|
%- of-wall:format
|
||||||
|
%+ turn (flop tan)
|
||||||
|
|= =tank
|
||||||
|
~! wid
|
||||||
|
~! tank
|
||||||
|
(of-wall:format (wash 0^wid tank))
|
||||||
|
::
|
||||||
|
++ purge
|
||||||
|
|= a=styx ^- tape
|
||||||
|
%- zing %+ turn a
|
||||||
|
|= a=_?>(?=(^ a) i.a)
|
||||||
|
?@(a (trip a) ^$(a q.a))
|
||||||
|
--
|
||||||
|
--
|
@ -15,7 +15,7 @@
|
|||||||
|= jon/^json ^- sole-action
|
|= jon/^json ^- sole-action
|
||||||
%- need %. jon
|
%- need %. jon
|
||||||
=> [dejs-soft:format ..sole-action]
|
=> [dejs-soft:format ..sole-action]
|
||||||
|^ (fo %ret (of det+change ~))
|
|^ (fo %ret (of det+change tab+ni ~))
|
||||||
++ fo
|
++ fo
|
||||||
|* {a/term b/fist}
|
|* {a/term b/fist}
|
||||||
|=(c/json ?.(=([%s a] c) (b c) (some [a ~])))
|
|=(c/json ?.(=([%s a] c) (b c) (some [a ~])))
|
||||||
|
@ -63,6 +63,15 @@
|
|||||||
$pro
|
$pro
|
||||||
%+ frond %pro
|
%+ frond %pro
|
||||||
(pairs vis+b+vis.sef tag+s+tag.sef cad+(tape (purge cad.sef)) ~)
|
(pairs vis+b+vis.sef tag+s+tag.sef cad+(tape (purge cad.sef)) ~)
|
||||||
|
::
|
||||||
|
$tab
|
||||||
|
:- %a
|
||||||
|
%+ turn p.sef
|
||||||
|
|= [=cord =^tank]
|
||||||
|
%- pairs
|
||||||
|
:~ match+s+cord
|
||||||
|
info+(tape ~(ram re tank))
|
||||||
|
==
|
||||||
::
|
::
|
||||||
?($bel $clr $nex)
|
?($bel $clr $nex)
|
||||||
(frond %act %s -.sef)
|
(frond %act %s -.sef)
|
||||||
|
186
pkg/interface/soto/gulpfile.js
Normal file
186
pkg/interface/soto/gulpfile.js
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
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/soto/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' ],
|
||||||
|
'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/soto/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/soto/js/'))
|
||||||
|
.on('end', cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
gulp.task('js-minify', function () {
|
||||||
|
return gulp.src('../../arvo/app/soto/js/index.js')
|
||||||
|
.pipe(minify())
|
||||||
|
.pipe(gulp.dest('../../arvo/app/soto/js/'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('tile-js-minify', function () {
|
||||||
|
return gulp.src('../../arvo/app/soto/js/tile.js')
|
||||||
|
.pipe(minify())
|
||||||
|
.pipe(gulp.dest('../../arvo/app/soto/js/'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('rename-index-min', function () {
|
||||||
|
return gulp.src('../../arvo/app/soto/js/index-min.js')
|
||||||
|
.pipe(rename('index.js'))
|
||||||
|
.pipe(gulp.dest('../../arvo/app/soto/js/'))
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('rename-tile-min', function () {
|
||||||
|
return gulp.src('../../arvo/app/soto/js/tile-min.js')
|
||||||
|
.pipe(rename('tile.js'))
|
||||||
|
.pipe(gulp.dest('../../arvo/app/soto/js/'))
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('clean-min', function () {
|
||||||
|
return del(['../../arvo/app/soto/js/index-min.js', '../../arvo/app/soto/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'));
|
||||||
|
}));
|
5919
pkg/interface/soto/package-lock.json
generated
Normal file
5919
pkg/interface/soto/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
42
pkg/interface/soto/package.json
Normal file
42
pkg/interface/soto/package.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "soto",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Dojo relay for Urbit.",
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp",
|
||||||
|
"serve": "gulp watch"
|
||||||
|
},
|
||||||
|
"author": "Tlon Corp",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "https://github.com/urbit/urbit",
|
||||||
|
"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",
|
||||||
|
"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": {
|
||||||
|
"classnames": "^2.2.6",
|
||||||
|
"del": "^5.1.0",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"moment": "^2.20.1",
|
||||||
|
"mousetrap": "^1.6.3",
|
||||||
|
"react": "^16.5.2",
|
||||||
|
"react-dom": "^16.8.6",
|
||||||
|
"react-router-dom": "^5.0.0",
|
||||||
|
"urbit-ob": "^4.1.2"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"natives": "1.1.3"
|
||||||
|
}
|
||||||
|
}
|
45
pkg/interface/soto/src/css/custom.css
Normal file
45
pkg/interface/soto/src/css/custom.css
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
p, input {
|
||||||
|
margin-block-end: unset;
|
||||||
|
margin-block-start: unset;
|
||||||
|
-webkit-margin-before: unset;
|
||||||
|
-webkit-margin-after: unset;
|
||||||
|
font-family: "Source Code Pro", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea, select, input, button {
|
||||||
|
outline: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
border: none;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body-regular {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body-large {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-regular {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-small-mono {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-family: "Source Code Pro", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mono {
|
||||||
|
font-family: "Source Code Pro", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
background-color: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
63
pkg/interface/soto/src/css/fonts.css
Normal file
63
pkg/interface/soto/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;
|
||||||
|
}
|
||||||
|
|
2843
pkg/interface/soto/src/css/indigo-static.css
Normal file
2843
pkg/interface/soto/src/css/indigo-static.css
Normal file
File diff suppressed because it is too large
Load Diff
38
pkg/interface/soto/src/css/spinner.css
Normal file
38
pkg/interface/soto/src/css/spinner.css
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
.spinner-pending {
|
||||||
|
position: relative;
|
||||||
|
content: "";
|
||||||
|
border-radius: 100%;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
|
||||||
|
background-color: rgba(255,255,255,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-pending::after {
|
||||||
|
content: "";
|
||||||
|
background-color: rgba(128,128,128,1);
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 100%;
|
||||||
|
clip: rect(0, 16px, 16px, 8px);
|
||||||
|
|
||||||
|
animation: spin 1s cubic-bezier(0.745, 0.045, 0.355, 1.000) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {transform:rotate(0deg)}
|
||||||
|
25% {transform:rotate(90deg)}
|
||||||
|
50% {transform:rotate(180deg)}
|
||||||
|
75% {transform:rotate(270deg)}
|
||||||
|
100% {transform:rotate(360deg)}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-nostart {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 100%;
|
||||||
|
content:'';
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
5
pkg/interface/soto/src/index.css
Normal file
5
pkg/interface/soto/src/index.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@import 'css/indigo-static.css';
|
||||||
|
@import 'css/fonts.css';
|
||||||
|
@import 'css/spinner.css';
|
||||||
|
@import 'css/custom.css';
|
||||||
|
|
15
pkg/interface/soto/src/index.js
Normal file
15
pkg/interface/soto/src/index.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Root } from '/components/root';
|
||||||
|
import { api } from '/api';
|
||||||
|
import { subscription } from "/subscription";
|
||||||
|
|
||||||
|
api.setAuthTokens({
|
||||||
|
ship: window.ship
|
||||||
|
});
|
||||||
|
|
||||||
|
subscription.start();
|
||||||
|
|
||||||
|
ReactDOM.render((
|
||||||
|
<Root />
|
||||||
|
), document.querySelectorAll("#root")[0]);
|
49
pkg/interface/soto/src/js/api.js
Normal file
49
pkg/interface/soto/src/js/api.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
class UrbitApi {
|
||||||
|
setAuthTokens(authTokens) {
|
||||||
|
this.authTokens = authTokens;
|
||||||
|
this.bindPaths = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(path, method, ship = this.authTokens.ship, appl = "soto", success, fail) {
|
||||||
|
this.bindPaths = _.uniq([...this.bindPaths, path]);
|
||||||
|
|
||||||
|
window.subscriptionId = window.urb.subscribe(ship, appl, path,
|
||||||
|
(err) => {
|
||||||
|
fail(err);
|
||||||
|
},
|
||||||
|
(event) => {
|
||||||
|
success({
|
||||||
|
data: event,
|
||||||
|
from: {
|
||||||
|
ship,
|
||||||
|
path
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
fail(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
soto(data) {
|
||||||
|
this.action("soto", "json", data); //TODO: revert after #1946
|
||||||
|
}
|
||||||
|
|
||||||
|
action(appl, mark, data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
window.urb.poke(ship, appl, mark, data,
|
||||||
|
(json) => {
|
||||||
|
resolve(json);
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export let api = new UrbitApi();
|
||||||
|
window.api = api;
|
22
pkg/interface/soto/src/js/components/history.js
Normal file
22
pkg/interface/soto/src/js/components/history.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
export class History extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-column-reverse overflow-container"
|
||||||
|
style={{ height: 'calc(100% - 1rem)', resize: 'vertical' }}>
|
||||||
|
<div style={{ marginTop: 'auto'}}>
|
||||||
|
{this.props.commandLog.map((text, index) => {
|
||||||
|
return <p className="mono" key={index}>{text}</p>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default History;
|
85
pkg/interface/soto/src/js/components/input.js
Normal file
85
pkg/interface/soto/src/js/components/input.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { store } from '../store';
|
||||||
|
import { api } from '../api';
|
||||||
|
|
||||||
|
export class Input extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.keyPress = this.keyPress.bind(this);
|
||||||
|
this.inputRef = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.inputRef.current.setSelectionRange(this.props.cursor, this.props.cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPress = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let ignoredKeys = ["Meta", "Alt", "Control", "Escape", "Shift",
|
||||||
|
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8",
|
||||||
|
"F9", "F10", "F11", "F12"
|
||||||
|
];
|
||||||
|
|
||||||
|
// submit on enter
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
api.soto("ret");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((e.key === "Backspace") && (this.props.cursor > 0)) {
|
||||||
|
store.doEdit({ del: this.props.cursor - 1});
|
||||||
|
return store.setState({ cursor: this.props.cursor - 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (e.key.startsWith("Arrow")) {
|
||||||
|
|
||||||
|
if (e.key === "ArrowLeft") {
|
||||||
|
if (this.props.cursor > 0) {
|
||||||
|
store.setState({ cursor: this.props.cursor - 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (e.key === "ArrowRight") {
|
||||||
|
if (this.props.cursor < this.props.input.length) {
|
||||||
|
store.setState({ cursor: this.props.cursor + 1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// tab completion
|
||||||
|
else if (e.key === "Tab") {
|
||||||
|
api.soto({tab: this.props.cursor});
|
||||||
|
}
|
||||||
|
|
||||||
|
// capture and transmit most characters
|
||||||
|
else if (ignoredKeys.indexOf(e.key) === -1) {
|
||||||
|
store.doEdit({ ins: { cha: e.key, at: this.props.cursor } });
|
||||||
|
store.setState({ cursor: this.props.cursor + 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row flex-grow-1">
|
||||||
|
<div className="flex-shrink-0">~{this.props.ship}:dojo
|
||||||
|
</div>
|
||||||
|
<span id="prompt">
|
||||||
|
{this.props.prompt}
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
autoCorrect="false"
|
||||||
|
autoFocus={true}
|
||||||
|
className="mono ml1 flex-auto dib w-100"
|
||||||
|
cursor={this.props.cursor}
|
||||||
|
onKeyDown={this.keyPress}
|
||||||
|
onPaste={e => {e.preventDefault()}}
|
||||||
|
ref={this.inputRef}
|
||||||
|
defaultValue={this.props.input}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Input;
|
32
pkg/interface/soto/src/js/components/lib/header-bar.js
Normal file
32
pkg/interface/soto/src/js/components/lib/header-bar.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { IconHome } from '/components/lib/icons/icon-home';
|
||||||
|
import { IconSpinner } from '/components/lib/icons/icon-spinner';
|
||||||
|
|
||||||
|
export class HeaderBar extends Component {
|
||||||
|
render() {
|
||||||
|
let spin = (this.props.spinner)
|
||||||
|
? <div className="absolute"
|
||||||
|
style={{width: 16, height: 16, top: 16, right: 16}}>
|
||||||
|
<IconSpinner/>
|
||||||
|
</div>
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-black w-100 justify-between b--gray0 bb"
|
||||||
|
style={{ height: 48, padding: 8}}>
|
||||||
|
<a className="db"
|
||||||
|
style={{ background: '#1A1A1A',
|
||||||
|
borderRadius: 16,
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
top: 8 }}
|
||||||
|
href='/'>
|
||||||
|
<IconHome />
|
||||||
|
</a>
|
||||||
|
{spin}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
export class IconHome extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<img src="/~launch/img/Home.png" width={32} height={32} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
export class IconSpinner extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="spinner-pending"></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
136
pkg/interface/soto/src/js/components/lib/sole.js
Normal file
136
pkg/interface/soto/src/js/components/lib/sole.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// 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++;
|
||||||
|
} 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;
|
36
pkg/interface/soto/src/js/components/root.js
Normal file
36
pkg/interface/soto/src/js/components/root.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { BrowserRouter, Route } from "react-router-dom";
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { HeaderBar } from './lib/header-bar';
|
||||||
|
import { History } from './history';
|
||||||
|
import { Input } from './input';
|
||||||
|
import { api } from '../api';
|
||||||
|
import { store } from '../store';
|
||||||
|
|
||||||
|
export class Root extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = store.state;
|
||||||
|
store.setStateHandler(this.setState.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<BrowserRouter>
|
||||||
|
<div style={{height: "100vh"}}>
|
||||||
|
<HeaderBar/>
|
||||||
|
<Route exact path="/~soto" render={ () => {
|
||||||
|
return (
|
||||||
|
<div className="pa3 bg-black mono gray3 w-100"
|
||||||
|
style={{lineHeight: "1.4", height: "calc(100% - 48px)", cursor: "text"}}>
|
||||||
|
<History commandLog={this.state.txt}/>
|
||||||
|
<Input ship={ship} cursor={this.state.cursor} prompt={this.state.prompt} input={this.state.input}/>
|
||||||
|
</div>
|
||||||
|
)}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</BrowserRouter>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
84
pkg/interface/soto/src/js/store.js
Normal file
84
pkg/interface/soto/src/js/store.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { Share } from './components/lib/sole';
|
||||||
|
import { api } from './api';
|
||||||
|
export const buffer = new Share;
|
||||||
|
|
||||||
|
|
||||||
|
export class Store {
|
||||||
|
constructor() {
|
||||||
|
this.state = {
|
||||||
|
txt: [],
|
||||||
|
prompt: '',
|
||||||
|
cursor: 0,
|
||||||
|
input: ""
|
||||||
|
}
|
||||||
|
this.sync = this.sync.bind(this);
|
||||||
|
this.print = this.print.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(data) {
|
||||||
|
// recursive handler
|
||||||
|
if (data.data) {
|
||||||
|
var dojoReply = data.data;
|
||||||
|
} else {
|
||||||
|
var dojoReply = data;
|
||||||
|
}
|
||||||
|
// %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':
|
||||||
|
for (let suggestion of dojoReply.tab) {
|
||||||
|
let match = suggestion.match;
|
||||||
|
let info = suggestion.info;
|
||||||
|
this.print(match + " " + 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':
|
||||||
|
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) {
|
||||||
|
let detSend = buffer.transmit(ted);
|
||||||
|
this.sync(ted);
|
||||||
|
return api.soto({det: detSend});
|
||||||
|
}
|
||||||
|
|
||||||
|
print(txt) {
|
||||||
|
let textLog = this.state.txt;
|
||||||
|
textLog.push(txt);
|
||||||
|
return this.setState({ txt: textLog });
|
||||||
|
}
|
||||||
|
|
||||||
|
sync(ted) {
|
||||||
|
return this.setState({ input: buffer.buf,
|
||||||
|
cursor: buffer.transpose(ted, this.state.cursor)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setStateHandler(setState) {
|
||||||
|
this.setState = setState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let store = new Store();
|
||||||
|
window.store = store;
|
32
pkg/interface/soto/src/js/subscription.js
Normal file
32
pkg/interface/soto/src/js/subscription.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { api } from './api';
|
||||||
|
import { store } from './store';
|
||||||
|
|
||||||
|
|
||||||
|
export class Subscription {
|
||||||
|
start() {
|
||||||
|
if (api.authTokens) {
|
||||||
|
this.initializesoto();
|
||||||
|
} else {
|
||||||
|
console.error("~~~ ERROR: Must set api.authTokens before operation ~~~");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializesoto() {
|
||||||
|
api.bind('/primary', 'PUT', api.authTokens.ship, 'soto',
|
||||||
|
this.handleEvent.bind(this),
|
||||||
|
this.handleError.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(diff) {
|
||||||
|
store.handleEvent(diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleError(err) {
|
||||||
|
console.error(err);
|
||||||
|
api.bind('/primary', 'PUT', api.authTokens.ship, 'soto',
|
||||||
|
this.handleEvent.bind(this),
|
||||||
|
this.handleError.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let subscription = new Subscription();
|
27
pkg/interface/soto/tile/tile.js
Normal file
27
pkg/interface/soto/tile/tile.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
|
||||||
|
export default class sotoTile extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="w-100 h-100 relative" style={{background: "#1a1a1a"}}>
|
||||||
|
<a className="w-100 h-100 db no-underline" href="/~soto">
|
||||||
|
<p className="gray label-regular b absolute"
|
||||||
|
style={{ left: 8, top: 4 }}>
|
||||||
|
Dojo
|
||||||
|
</p>
|
||||||
|
<img src="~soto/img/Tile.png"
|
||||||
|
className="absolute"
|
||||||
|
style={{ left: 30, top: 30, height: "174px", width: "174px"}}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
window.sotoTile = sotoTile;
|
Loading…
Reference in New Issue
Block a user