Init bartinfo app

This commit is contained in:
ronreg-ribdev 2020-05-12 00:10:53 -07:00
parent 392ba83009
commit d79b7554bf
29 changed files with 165 additions and 313 deletions

View File

@ -1,5 +1,5 @@
module.exports = {
URBIT_PIERS: [
"%URBITPIER%",
"../zod/home",
]
};

View File

@ -1,164 +0,0 @@
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 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('./urbit/app/%APPNAME%/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('./urbit/app/%APPNAME%/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('./urbit/app/%APPNAME%/js/'))
.on('end', cb);
});
gulp.task('js-minify', function () {
return gulp.src('./urbit/app/%APPNAME%/js/index.js')
.pipe(minify())
.pipe(gulp.dest('./urbit/app/%APPNAME%/js/'));
});
gulp.task('tile-js-minify', function () {
return gulp.src('./urbit/app/%APPNAME%/js/tile.js')
.pipe(minify())
.pipe(gulp.dest('./urbit/app/%APPNAME%/js/'));
});
gulp.task('urbit-copy', function () {
let ret = gulp.src('urbit/**/*');
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',
),
'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('urbit/**/*', gulp.parallel('urbit-copy'));
}));

View File

@ -1,20 +0,0 @@
import React, { Component } from 'react';
import _ from 'lodash';
export default class %APPNAME%Tile extends Component {
render() {
return (
<div className="w-100 h-100 relative bg-white bg-gray0-d ba b--black b--gray1-d">
<a className="w-100 h-100 db pa2 no-underline" href="/~%APPNAME%">
<p className="black white-d absolute f9" style={{ left: 8, top: 8 }}>%APPNAME%</p>
<img className="absolute" src="/~%APPNAME%/img/Tile.png" style={{top: 39, left: 39}}/>
</a>
</div>
);
}
}
window.%APPNAME%Tile = %APPNAME%Tile;

View File

@ -1,5 +1,8 @@
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');
@ -18,6 +21,25 @@ 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('./urbit/app/bartinfo/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({
@ -26,6 +48,32 @@ gulp.task('tile-jsx-transform', function(cb) {
.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('./urbit/app/bartinfo/js/'))
.on('end', cb);
});
gulp.task('tile-js-imports', function(cb) {
return gulp.src('dist/tile.js')
@ -49,14 +97,21 @@ gulp.task('tile-js-imports', function(cb) {
console.log(e);
cb();
})
.pipe(gulp.dest('./urbit/app/%APPNAME%/js/'))
.pipe(gulp.dest('./urbit/app/bartinfo/js/'))
.on('end', cb);
});
gulp.task('tile-js-minify', function () {
return gulp.src('./urbit/app/%APPNAME%/js/tile.js')
gulp.task('js-minify', function () {
return gulp.src('./urbit/app/bartinfo/js/index.js')
.pipe(minify())
.pipe(gulp.dest('./urbit/app/%APPNAME%/js/'));
.pipe(gulp.dest('./urbit/app/bartinfo/js/'));
});
gulp.task('tile-js-minify', function () {
return gulp.src('./urbit/app/bartinfo/js/tile.js')
.pipe(minify())
.pipe(gulp.dest('./urbit/app/bartinfo/js/'));
});
gulp.task('urbit-copy', function () {
@ -69,14 +124,41 @@ gulp.task('urbit-copy', function () {
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('tile-js-bundle-prod',
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-prod', gulp.series('tile-js-bundle-prod', 'urbit-copy'));
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',
),
'urbit-copy'
)
);
gulp.task('default', gulp.series('bundle-dev'));
gulp.task('default', gulp.series('tile-js-bundle-dev', 'urbit-copy'));
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('urbit/**/*', gulp.parallel('urbit-copy'));
}));

52
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "create-landscape-app",
"version": "2.0.0",
"version": "3.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -401,7 +401,8 @@
},
"kind-of": {
"version": "6.0.2",
"resolved": "",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
}
}
@ -1480,7 +1481,8 @@
},
"kind-of": {
"version": "6.0.2",
"resolved": "",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
}
}
@ -1643,7 +1645,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@ -1664,12 +1667,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -1684,17 +1689,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -1811,7 +1819,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -1823,6 +1832,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -1837,6 +1847,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -1844,12 +1855,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -1868,6 +1881,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -1948,7 +1962,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -1960,6 +1975,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -2045,7 +2061,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@ -2081,6 +2098,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -2100,6 +2118,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -2143,12 +2162,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
}
}
},
@ -5185,7 +5206,8 @@
},
"kind-of": {
"version": "6.0.2",
"resolved": "",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
}
}

View File

@ -8,7 +8,7 @@ class UrbitApi {
this.bindPaths = [];
}
bind(path, method, ship = this.authTokens.ship, appl = "%APPNAME%", success, fail) {
bind(path, method, ship = this.authTokens.ship, appl = "bartinfo", success, fail) {
this.bindPaths = _.uniq([...this.bindPaths, path]);
window.subscriptionId = window.urb.subscribe(ship, appl, path,
@ -29,8 +29,8 @@ class UrbitApi {
});
}
%APPNAME%(data) {
this.action("%APPNAME%", "json", data);
bartinfo(data) {
this.action("bartinfo", "json", data);
}
action(appl, mark, data) {

View File

@ -6,7 +6,7 @@ export class IconHome extends Component {
return (
<img
className={"invert-d " + classes}
src="/~%APPNAME%/img/Home.png"
src="/~bartinfo/img/Home.png"
width={16}
height={16}
/>

View File

@ -15,12 +15,12 @@ export class Root extends Component {
<BrowserRouter>
<div className="absolute h-100 w-100 bg-gray0-d ph4-m ph4-l ph4-xl pb4-m pb4-l pb4-xl">
<HeaderBar/>
<Route exact path="/~%APPNAME%" render={ () => {
<Route exact path="/~bartinfo" render={ () => {
return (
<div className="cf w-100 flex flex-column pa4 ba-m ba-l ba-xl b--gray2 br1 h-100 h-100-minus-40-s h-100-minus-40-m h-100-minus-40-l h-100-minus-40-xl f9 white-d overflow-x-hidden">
<h1 className="mt0 f8 fw4">%APPNAME%</h1>
<h1 className="mt0 f8 fw4">bartinfo</h1>
<p className="lh-copy measure pt3">Welcome to your Landscape application.</p>
<p className="lh-copy measure pt3">To get started, edit <code>src/index.js</code>, <code>tile/tile.js</code> or <code>urbit/app/%APPNAME%.hoon</code> and <code>|commit %home</code> on your Urbit ship to see your changes.</p>
<p className="lh-copy measure pt3">To get started, edit <code>src/index.js</code>, <code>tile/tile.js</code> or <code>urbit/app/bartinfo.hoon</code> and <code>|commit %home</code> on your Urbit ship to see your changes.</p>
<a className="black no-underline db f8 pt3" href="https://urbit.org/docs">-> Read the docs</a>
</div>
)}}

View File

@ -3,7 +3,7 @@ import _ from 'lodash';
export class ConfigReducer {
reduce(json, state) {
let data = _.get(json, '%APPNAME%', false);
let data = _.get(json, 'bartinfo', false);
if (data) {
state.inbox = data.inbox;
}

View File

@ -7,14 +7,14 @@ import urbitOb from 'urbit-ob';
export class Subscription {
start() {
if (api.authTokens) {
// this.initialize%APPNAME%();
// this.initializebartinfo();
} else {
console.error("~~~ ERROR: Must set api.authTokens before operation ~~~");
}
}
// initialize%APPNAME%() {
// api.bind('/primary', 'PUT', api.authTokens.ship, '%APPNAME%',
// initializebartinfo() {
// api.bind('/primary', 'PUT', api.authTokens.ship, 'bartinfo',
// this.handleEvent.bind(this),
// this.handleError.bind(this));
// }
@ -25,7 +25,7 @@ export class Subscription {
handleError(err) {
console.error(err);
api.bind('/primary', 'PUT', api.authTokens.ship, '%APPNAME%',
api.bind('/primary', 'PUT', api.authTokens.ship, 'bartinfo',
this.handleEvent.bind(this),
this.handleError.bind(this));
}

View File

@ -2,16 +2,19 @@ import React, { Component } from 'react';
import _ from 'lodash';
export default class %APPNAME%Tile extends Component {
export default class bartinfoTile extends Component {
render() {
return (
<div className="w-100 h-100 relative bg-white bg-gray0-d ba b--black b--gray1-d">
<p className="black white-d absolute f9" style={{ left: 8, top: 8 }}>%APPNAME%</p>
<a className="w-100 h-100 db pa2 no-underline" href="/~bartinfo">
<p className="black white-d absolute f9" style={{ left: 8, top: 8 }}>bartinfo</p>
<img className="absolute" src="/~bartinfo/img/Tile.png" style={{top: 39, left: 39}}/>
</a>
</div>
);
}
}
window.%APPNAME%Tile = %APPNAME%Tile;
window.bartinfoTile = bartinfoTile;

View File

@ -2,34 +2,34 @@
/= index
/^ octs
/; as-octs:mimes:html
/: /===/app/%APPNAME%/index
/: /===/app/bartinfo/index
/| /html/
/~ ~
==
/= tile-js
/^ octs
/; as-octs:mimes:html
/: /===/app/%APPNAME%/js/tile
/: /===/app/bartinfo/js/tile
/| /js/
/~ ~
==
/= script
/^ octs
/; as-octs:mimes:html
/: /===/app/%APPNAME%/js/index
/: /===/app/bartinfo/js/index
/| /js/
/~ ~
==
/= style
/^ octs
/; as-octs:mimes:html
/: /===/app/%APPNAME%/css/index
/: /===/app/bartinfo/css/index
/| /css/
/~ ~
==
/= %APPNAME%-png
/= bartinfo-png
/^ (map knot @)
/: /===/app/%APPNAME%/img /_ /png/
/: /===/app/bartinfo/img /_ /png/
::
|%
+$ card card:agent:gall
@ -38,16 +38,16 @@
=<
|_ bol=bowl:gall
+* this .
%APPNAME%-core +>
cc ~(. %APPNAME%-core bol)
bartinfo-core +>
cc ~(. bartinfo-core bol)
def ~(. (default-agent this %|) bol)
::
++ on-init
^- (quip card _this)
=/ launcha [%launch-action !>([%add %%APPNAME% / '/~%APPNAME%/js/tile.js'])]
=/ launcha [%launch-action !>([%add %bartinfo / '/~bartinfo/js/tile.js'])]
:_ this
:~ [%pass / %arvo %e %connect [~ /'~%APPNAME%'] %%APPNAME%]
[%pass /%APPNAME% %agent [our.bol %launch] %poke launcha]
:~ [%pass / %arvo %e %connect [~ /'~bartinfo'] %bartinfo]
[%pass /bartinfo %agent [our.bol %launch] %poke launcha]
==
++ on-poke
|= [=mark =vase]
@ -96,18 +96,18 @@
^- simple-payload:http
=+ url=(parse-request-line url.request.inbound-request)
?+ site.url not-found:gen
[%'~%APPNAME%' %css %index ~] (css-response:gen style)
[%'~%APPNAME%' %js %tile ~] (js-response:gen tile-js)
[%'~%APPNAME%' %js %index ~] (js-response:gen script)
[%'~bartinfo' %css %index ~] (css-response:gen style)
[%'~bartinfo' %js %tile ~] (js-response:gen tile-js)
[%'~bartinfo' %js %index ~] (js-response:gen script)
::
[%'~%APPNAME%' %img @t *]
[%'~bartinfo' %img @t *]
=/ name=@t i.t.t.site.url
=/ img (~(get by %APPNAME%-png) name)
=/ img (~(get by bartinfo-png) name)
?~ img
not-found:gen
(png-response:gen (as-octs:mimes:html u.img))
::
[%'~%APPNAME%' *] (html-response:gen index)
[%'~bartinfo' *] (html-response:gen index)
==
::
--

View File

Before

Width:  |  Height:  |  Size: 679 B

After

Width:  |  Height:  |  Size: 679 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,16 +1,16 @@
<!doctype html>
<html>
<head>
<title>%APPNAME%</title>
<title>bartinfo</title>
<meta charset="utf-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<link rel="stylesheet" href="/~%APPNAME%/css/index.css" />
<link rel="stylesheet" href="/~bartinfo/css/index.css" />
</head>
<body>
<div id="root" />
<script src="/~/channel/channel.js"></script>
<script src="/~modulo/session.js"></script>
<script src="/~%APPNAME%/js/index.js"></script>
<script src="/~bartinfo/js/index.js"></script>
</body>
</html>

View File

@ -1,71 +0,0 @@
/+ *server, default-agent, verb
/= tile-js
/^ octs
/; as-octs:mimes:html
/: /===/app/%APPNAME%/js/tile
/| /js/
/~ ~
==
=, format
::
%+ verb |
^- agent:gall
|_ =bowl:gall
+* this .
def ~(. (default-agent this %|) bowl)
::
++ on-init
^- (quip card:agent:gall _this)
=/ launcha
[%launch-action !>([%add %%APPNAME% /%APPNAME%tile '/~%APPNAME%/js/tile.js'])]
:_ this
:~ [%pass / %arvo %e %connect [~ /'~%APPNAME%'] %%APPNAME%]
[%pass /%APPNAME% %agent [our.bowl %launch] %poke launcha]
==
++ on-save on-save:def
++ on-load on-load:def
++ on-poke
|= [=mark =vase]
^- (quip card:agent:gall _this)
?. ?=(%handle-http-request mark)
(on-poke:def mark vase)
=+ !<([eyre-id=@ta =inbound-request:eyre] vase)
:_ this
%+ give-simple-payload:app eyre-id
%+ require-authorization:app inbound-request
|= =inbound-request:eyre
=/ request-line (parse-request-line url.request.inbound-request)
=/ back-path (flop site.request-line)
=/ name=@t
=/ back-path (flop site.request-line)
?~ back-path
''
i.back-path
::
?~ back-path
not-found:gen
?: =(name 'tile')
(js-response:gen tile-js)
not-found:gen
::
++ on-watch
|= =path
^- (quip card:agent:gall _this)
?: ?=([%http-response *] path)
`this
?. =([/%APPNAME%tile *] path)
(on-watch:def path)
[[%give %fact ~ %json !>(*json)]~ this]
::
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-agent on-agent:def
++ on-arvo
|= [=wire =sign-arvo]
^- (quip card:agent:gall _this)
?. ?=(%bound +<.sign-arvo)
(on-arvo:def wire sign-arvo)
[~ this]
::
++ on-fail on-fail:def
--