Initial commit
This commit is contained in:
commit
392ba83009
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
5
.urbitrc
Normal file
5
.urbitrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
URBIT_PIERS: [
|
||||||
|
"%URBITPIER%",
|
||||||
|
]
|
||||||
|
};
|
50
README.md
Normal file
50
README.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
Get started building a simple application for Landscape on your [Urbit](http://urbit.org) ship with a few commands.
|
||||||
|
|
||||||
|
This tool is experimental and primarily used internally to develop front-end applications. While Tlon does not officially support this tool, you can always get general programming help for Urbit in the `~dopzod/urbit-help` chat.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This repository is available as a template; to immediately generate your application's repository you can click [here](https://github.com/urbit/create-landscape-app/generate). Clone the generated repository, `npm install` and then run `npm start` to get started (you can also directly clone this repository, if you wish!).
|
||||||
|
|
||||||
|
In order to run your application on your ship, you will need Urbit v.0.10.0 or higher. On your Urbit ship, if you haven't already, mount your pier to Unix with `|mount %`.
|
||||||
|
|
||||||
|
## Using
|
||||||
|
|
||||||
|
Once you're up and running, your tile lives in `tile/tile.js`, which uses [React](https://reactjs.org) to render itself -- you'll want a basic foothold with it first. When you make changes, the `urbit` directory will update with the compiled application and, if you're running `npm run serve`, it will automatically copy itself to your Urbit ship when you save your changes (more information on that below).
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
|
||||||
|
This runs the wizard. Give it an application name and the location of your Urbit ship's desk and it will customise the files so your new application will run on your ship.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
This builds your application and copies it into your Urbit ship's desk. In your Urbit (v.0.8.0 or higher) `|commit %home` (or `%your-desk-name`) to synchronise your changes.
|
||||||
|
|
||||||
|
If this is the first time you're running your application on your Urbit ship, don't forget to `|start %yourapp`.
|
||||||
|
|
||||||
|
### `npm run serve`
|
||||||
|
|
||||||
|
Builds the application and copies it into your Urbit ship's desk, watching for changes. In your Urbit (v.0.8.0 or higher) `|commit %home` (or `%your-desk-name`) to synchronise your changes.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### What is a "tile" vs. "full" app?
|
||||||
|
|
||||||
|
When you run `npm run start`, the wizard will ask you to specify which you want:
|
||||||
|
|
||||||
|
- **tile**: A tile that exists on the Landscape launch screen. A pre-existing example is the "Weather" tile or the "Clock" tile in Landscape.
|
||||||
|
- **full**: A tile that links to a full-screen application: this means that you will work in both `tile.js` (for the tile interface) and `root.js` (and beyond) in the `src` folder.
|
||||||
|
|
||||||
|
No matter which option you specify, the wizard will customise the Hoon boilerplate for you and provide a basic example accordingly.
|
||||||
|
|
||||||
|
### How can I ensure my app fits Landscape design?
|
||||||
|
|
||||||
|
Landscape makes use of the [Indigo](https://urbit.github.io/indigo-react/) CSS framework. The template tile and full application both make use of it as an example for you to get going fast.
|
||||||
|
|
||||||
|
### What if I want to communicate with my ship / provide more functionality besides a front-end?
|
||||||
|
|
||||||
|
By default, your app will provide an example of passing state from ship to front-end with the `peer-[yourappname]tile` arm in the app's .hoon file -- in this case, just sending your ship's name as a data prop. The code is well-commented if you don't want to pass state, or if you want to know how to pass almost anything else from your ship to the Landscape interface.
|
||||||
|
|
||||||
|
In order to do anything substantial, of course, you'll want to know [Hoon](https://urbit.org/docs/tutorials/hoon/). If this is intimidating, don't panic: `create-landscape-app` is a fantastic way to start learning by leveraging your strengths. This repository is intended to be a boilerplate for rapid front-end development; it's also a gradual, incremental introduction to Hoon for web developers by allowing for rapid prototyping and experimentation with the Landscape interface.
|
||||||
|
|
||||||
|
Happy hacking!
|
164
full/gulpfile.js
Normal file
164
full/gulpfile.js
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
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'));
|
||||||
|
}));
|
158
full/src/css/custom.css
Normal file
158
full/src/css/custom.css
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
p, h1, h2, h3, h4, h5, h6, a, input, textarea, button {
|
||||||
|
margin-block-end: unset;
|
||||||
|
margin-block-start: unset;
|
||||||
|
-webkit-margin-before: unset;
|
||||||
|
-webkit-margin-after: unset;
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body-regular-400 {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plus-font {
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-font {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.mono {
|
||||||
|
font-family: "Source Code Pro", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inter {
|
||||||
|
font-family: Inter, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mix-blend-diff {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dark */
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
.bg-black-d {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
.white-d {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.gray1-d {
|
||||||
|
color: #4d4d4d;
|
||||||
|
}
|
||||||
|
.gray2-d {
|
||||||
|
color: #7f7f7f;
|
||||||
|
}
|
||||||
|
.gray3-d {
|
||||||
|
color: #b1b2b3;
|
||||||
|
}
|
||||||
|
.gray4-d {
|
||||||
|
color: #e6e6e6;
|
||||||
|
}
|
||||||
|
.bg-gray0-d {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
.bg-gray1-d {
|
||||||
|
background-color: #4d4d4d;
|
||||||
|
}
|
||||||
|
.b--gray0-d {
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
.b--gray1-d {
|
||||||
|
border-color: #4d4d4d;
|
||||||
|
}
|
||||||
|
.b--gray2-d {
|
||||||
|
border-color: #7f7f7f;
|
||||||
|
}
|
||||||
|
.b--white-d {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
.bb-d {
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
.invert-d {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
.o-80-d {
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
.focus-b--white-d:focus {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.hover-bg-gray1-d:hover {
|
||||||
|
color: #4d4d4d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* responsive */
|
||||||
|
|
||||||
|
@media all and (max-width: 34.375em) {
|
||||||
|
.h-100-minus-40-s {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 34.375em) and (max-width: 46.875em) {
|
||||||
|
.h-100-minus-40-m {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 46.875em) and (max-width: 60em) {
|
||||||
|
.h-100-minus-40-l {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 60em) {
|
||||||
|
.h-100-minus-40-xl {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
}
|
63
full/src/css/fonts.css
Normal file
63
full/src/css/fonts.css
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url("https://media.urbit.org/fonts/Inter-Regular.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url("https://media.urbit.org/fonts/Inter-Italic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url("https://media.urbit.org/fonts/Inter-Bold.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url("https://media.urbit.org/fonts/Inter-BoldItalic.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-extralight.woff");
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-light.woff");
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-regular.woff");
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-medium.woff");
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-semibold.woff");
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Source Code Pro";
|
||||||
|
src: url("https://storage.googleapis.com/media.urbit.org/fonts/scp-bold.woff");
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
1
full/src/css/indigo-static.css
Normal file
1
full/src/css/indigo-static.css
Normal file
File diff suppressed because one or more lines are too long
4
full/src/index.css
Normal file
4
full/src/index.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@import 'css/indigo-static.css';
|
||||||
|
@import 'css/fonts.css';
|
||||||
|
@import 'css/custom.css';
|
||||||
|
|
16
full/src/index.js
Normal file
16
full/src/index.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { Root } from '/components/root';
|
||||||
|
import { api } from '/api';
|
||||||
|
import { store } from '/store';
|
||||||
|
import { subscription } from "/subscription";
|
||||||
|
|
||||||
|
api.setAuthTokens({
|
||||||
|
ship: window.ship
|
||||||
|
});
|
||||||
|
|
||||||
|
subscription.start();
|
||||||
|
|
||||||
|
ReactDOM.render((
|
||||||
|
<Root />
|
||||||
|
), document.querySelectorAll("#root")[0]);
|
49
full/src/js/api.js
Normal file
49
full/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 = "%APPNAME%", 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
%APPNAME%(data) {
|
||||||
|
this.action("%APPNAME%", "json", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
48
full/src/js/components/lib/header-bar.js
Normal file
48
full/src/js/components/lib/header-bar.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import { cite } from '../../lib/util';
|
||||||
|
import { IconHome } from "/components/lib/icons/icon-home";
|
||||||
|
import { Sigil } from "/components/lib/icons/sigil";
|
||||||
|
|
||||||
|
export class HeaderBar extends Component {
|
||||||
|
render() {
|
||||||
|
|
||||||
|
let title = document.title === "Home" ? "" : document.title;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"bg-white bg-gray0-d w-100 justify-between relative tc pt3 db"
|
||||||
|
}
|
||||||
|
style={{ height: 40 }}>
|
||||||
|
<a
|
||||||
|
className="dib gray2 f9 inter absolute left-0"
|
||||||
|
href="/"
|
||||||
|
style={{ top: 14 }}>
|
||||||
|
<IconHome/>
|
||||||
|
<span
|
||||||
|
className="ml2 white-d v-top lh-title"
|
||||||
|
style={{ paddingTop: 3 }}>
|
||||||
|
Home
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<span
|
||||||
|
className="f9 white-d inter dib"
|
||||||
|
style={{
|
||||||
|
verticalAlign: "text-top",
|
||||||
|
paddingTop: 3
|
||||||
|
}}>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
<div className="absolute right-0 lh-copy" style={{ top: 8 }}>
|
||||||
|
<Sigil
|
||||||
|
ship={"~" + window.ship}
|
||||||
|
classes="v-mid mix-blend-diff"
|
||||||
|
size={16}
|
||||||
|
color={"#000000"}
|
||||||
|
/>
|
||||||
|
<span className="mono white-d f9 ml2 c-default">{cite(window.ship)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
15
full/src/js/components/lib/icons/icon-home.js
Normal file
15
full/src/js/components/lib/icons/icon-home.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
|
||||||
|
export class IconHome extends Component {
|
||||||
|
render() {
|
||||||
|
let classes = !!this.props.classes ? this.props.classes : "";
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
className={"invert-d " + classes}
|
||||||
|
src="/~%APPNAME%/img/Home.png"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
9
full/src/js/components/lib/icons/icon-spinner.js
Normal file
9
full/src/js/components/lib/icons/icon-spinner.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
export class IconSpinner extends Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="spinner-pending"></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
32
full/src/js/components/lib/icons/sigil.js
Normal file
32
full/src/js/components/lib/icons/sigil.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { sigil, reactRenderer } from 'urbit-sigil-js';
|
||||||
|
|
||||||
|
|
||||||
|
export class Sigil extends Component {
|
||||||
|
render() {
|
||||||
|
const { props } = this;
|
||||||
|
|
||||||
|
let classes = props.classes || "";
|
||||||
|
|
||||||
|
if (props.ship.length > 14) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={"bg-black dib " + classes}
|
||||||
|
style={{ width: props.size, height: props.size }}>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className={"dib " + classes} style={{ flexBasis: 32, backgroundColor: props.color }}>
|
||||||
|
{sigil({
|
||||||
|
patp: props.ship,
|
||||||
|
renderer: reactRenderer,
|
||||||
|
size: props.size,
|
||||||
|
colors: [props.color, "white"]
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
33
full/src/js/components/root.js
Normal file
33
full/src/js/components/root.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { BrowserRouter, Route } from "react-router-dom";
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { HeaderBar } from "./lib/header-bar.js"
|
||||||
|
|
||||||
|
|
||||||
|
export class Root extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<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={ () => {
|
||||||
|
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>
|
||||||
|
<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>
|
||||||
|
<a className="black no-underline db f8 pt3" href="https://urbit.org/docs">-> Read the docs</a>
|
||||||
|
</div>
|
||||||
|
)}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</BrowserRouter>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
82
full/src/js/lib/util.js
Normal file
82
full/src/js/lib/util.js
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
|
||||||
|
export function uuid() {
|
||||||
|
let str = "0v"
|
||||||
|
str += Math.ceil(Math.random()*8)+"."
|
||||||
|
for (var i = 0; i < 5; i++) {
|
||||||
|
let _str = Math.ceil(Math.random()*10000000).toString(32);
|
||||||
|
_str = ("00000"+_str).substr(-5,5);
|
||||||
|
str += _str+".";
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.slice(0,-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPatTa(str) {
|
||||||
|
const r = /^[a-z,0-9,\-,\.,_,~]+$/.exec(str)
|
||||||
|
return !!r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Goes from:
|
||||||
|
~2018.7.17..23.15.09..5be5 // urbit @da
|
||||||
|
To:
|
||||||
|
(javascript Date object)
|
||||||
|
*/
|
||||||
|
export function daToDate(st) {
|
||||||
|
var dub = function(n) {
|
||||||
|
return parseInt(n) < 10 ? "0" + parseInt(n) : n.toString();
|
||||||
|
};
|
||||||
|
var da = st.split('..');
|
||||||
|
var bigEnd = da[0].split('.');
|
||||||
|
var lilEnd = da[1].split('.');
|
||||||
|
var ds = `${bigEnd[0].slice(1)}-${dub(bigEnd[1])}-${dub(bigEnd[2])}T${dub(lilEnd[0])}:${dub(lilEnd[1])}:${dub(lilEnd[2])}Z`;
|
||||||
|
return new Date(ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Goes from:
|
||||||
|
(javascript Date object)
|
||||||
|
To:
|
||||||
|
~2018.7.17..23.15.09..5be5 // urbit @da
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function dateToDa(d, mil) {
|
||||||
|
var fil = function(n) {
|
||||||
|
return n >= 10 ? n : "0" + n;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
`~${d.getUTCFullYear()}.` +
|
||||||
|
`${(d.getUTCMonth() + 1)}.` +
|
||||||
|
`${fil(d.getUTCDate())}..` +
|
||||||
|
`${fil(d.getUTCHours())}.` +
|
||||||
|
`${fil(d.getUTCMinutes())}.` +
|
||||||
|
`${fil(d.getUTCSeconds())}` +
|
||||||
|
`${mil ? "..0000" : ""}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deSig(ship) {
|
||||||
|
return ship.replace('~', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim patps to match dojo, chat-cli
|
||||||
|
export function cite(ship) {
|
||||||
|
let patp = ship, shortened = "";
|
||||||
|
if (patp.startsWith("~")) {
|
||||||
|
patp = patp.substr(1);
|
||||||
|
}
|
||||||
|
// comet
|
||||||
|
if (patp.length === 56) {
|
||||||
|
shortened = "~" + patp.slice(0, 6) + "_" + patp.slice(50, 56);
|
||||||
|
return shortened;
|
||||||
|
}
|
||||||
|
// moon
|
||||||
|
if (patp.length === 27) {
|
||||||
|
shortened = "~" + patp.slice(14, 20) + "^" + patp.slice(21, 27);
|
||||||
|
return shortened;
|
||||||
|
}
|
||||||
|
return `~${patp}`;
|
||||||
|
}
|
11
full/src/js/reducers/config.js
Normal file
11
full/src/js/reducers/config.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
|
||||||
|
export class ConfigReducer {
|
||||||
|
reduce(json, state) {
|
||||||
|
let data = _.get(json, '%APPNAME%', false);
|
||||||
|
if (data) {
|
||||||
|
state.inbox = data.inbox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
full/src/js/reducers/initial.js
Normal file
11
full/src/js/reducers/initial.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
|
||||||
|
export class InitialReducer {
|
||||||
|
reduce(json, state) {
|
||||||
|
let data = _.get(json, 'initial', false);
|
||||||
|
if (data) {
|
||||||
|
state.inbox = data.inbox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
full/src/js/reducers/update.js
Normal file
17
full/src/js/reducers/update.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
|
||||||
|
export class UpdateReducer {
|
||||||
|
reduce(json, state) {
|
||||||
|
let data = _.get(json, 'update', false);
|
||||||
|
if (data) {
|
||||||
|
this.reduceInbox(_.get(data, 'inbox', false), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reduceInbox(inbox, state) {
|
||||||
|
if (inbox) {
|
||||||
|
state.inbox = inbox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
full/src/js/store.js
Normal file
35
full/src/js/store.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { InitialReducer } from '/reducers/initial';
|
||||||
|
import { ConfigReducer } from '/reducers/config';
|
||||||
|
import { UpdateReducer } from '/reducers/update';
|
||||||
|
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
constructor() {
|
||||||
|
this.state = {
|
||||||
|
inbox: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.initialReducer = new InitialReducer();
|
||||||
|
this.configReducer = new ConfigReducer();
|
||||||
|
this.updateReducer = new UpdateReducer();
|
||||||
|
this.setState = () => { };
|
||||||
|
}
|
||||||
|
|
||||||
|
setStateHandler(setState) {
|
||||||
|
this.setState = setState;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(data) {
|
||||||
|
let json = data.data;
|
||||||
|
|
||||||
|
console.log(json);
|
||||||
|
this.initialReducer.reduce(json, this.state);
|
||||||
|
this.configReducer.reduce(json, this.state);
|
||||||
|
this.updateReducer.reduce(json, this.state);
|
||||||
|
|
||||||
|
this.setState(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let store = new Store();
|
||||||
|
window.store = store;
|
34
full/src/js/subscription.js
Normal file
34
full/src/js/subscription.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { api } from '/api';
|
||||||
|
import { store } from '/store';
|
||||||
|
|
||||||
|
import urbitOb from 'urbit-ob';
|
||||||
|
|
||||||
|
|
||||||
|
export class Subscription {
|
||||||
|
start() {
|
||||||
|
if (api.authTokens) {
|
||||||
|
// this.initialize%APPNAME%();
|
||||||
|
} else {
|
||||||
|
console.error("~~~ ERROR: Must set api.authTokens before operation ~~~");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize%APPNAME%() {
|
||||||
|
// api.bind('/primary', 'PUT', api.authTokens.ship, '%APPNAME%',
|
||||||
|
// 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, '%APPNAME%',
|
||||||
|
this.handleEvent.bind(this),
|
||||||
|
this.handleError.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let subscription = new Subscription();
|
1
full/src/js/vendor/sigils-1.2.5.js
vendored
Normal file
1
full/src/js/vendor/sigils-1.2.5.js
vendored
Normal file
File diff suppressed because one or more lines are too long
20
full/tile/tile.js
Normal file
20
full/tile/tile.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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;
|
113
full/urbit/app/smol.hoon
Normal file
113
full/urbit/app/smol.hoon
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/+ *server, default-agent
|
||||||
|
/= index
|
||||||
|
/^ octs
|
||||||
|
/; as-octs:mimes:html
|
||||||
|
/: /===/app/%APPNAME%/index
|
||||||
|
/| /html/
|
||||||
|
/~ ~
|
||||||
|
==
|
||||||
|
/= tile-js
|
||||||
|
/^ octs
|
||||||
|
/; as-octs:mimes:html
|
||||||
|
/: /===/app/%APPNAME%/js/tile
|
||||||
|
/| /js/
|
||||||
|
/~ ~
|
||||||
|
==
|
||||||
|
/= script
|
||||||
|
/^ octs
|
||||||
|
/; as-octs:mimes:html
|
||||||
|
/: /===/app/%APPNAME%/js/index
|
||||||
|
/| /js/
|
||||||
|
/~ ~
|
||||||
|
==
|
||||||
|
/= style
|
||||||
|
/^ octs
|
||||||
|
/; as-octs:mimes:html
|
||||||
|
/: /===/app/%APPNAME%/css/index
|
||||||
|
/| /css/
|
||||||
|
/~ ~
|
||||||
|
==
|
||||||
|
/= %APPNAME%-png
|
||||||
|
/^ (map knot @)
|
||||||
|
/: /===/app/%APPNAME%/img /_ /png/
|
||||||
|
::
|
||||||
|
|%
|
||||||
|
+$ card card:agent:gall
|
||||||
|
--
|
||||||
|
^- agent:gall
|
||||||
|
=<
|
||||||
|
|_ bol=bowl:gall
|
||||||
|
+* this .
|
||||||
|
%APPNAME%-core +>
|
||||||
|
cc ~(. %APPNAME%-core bol)
|
||||||
|
def ~(. (default-agent this %|) bol)
|
||||||
|
::
|
||||||
|
++ on-init
|
||||||
|
^- (quip card _this)
|
||||||
|
=/ launcha [%launch-action !>([%add %%APPNAME% / '/~%APPNAME%/js/tile.js'])]
|
||||||
|
:_ this
|
||||||
|
:~ [%pass / %arvo %e %connect [~ /'~%APPNAME%'] %%APPNAME%]
|
||||||
|
[%pass /%APPNAME% %agent [our.bol %launch] %poke launcha]
|
||||||
|
==
|
||||||
|
++ on-poke
|
||||||
|
|= [=mark =vase]
|
||||||
|
^- (quip card _this)
|
||||||
|
?> (team:title our.bol src.bol)
|
||||||
|
?+ mark (on-poke:def mark vase)
|
||||||
|
%handle-http-request
|
||||||
|
=+ !<([eyre-id=@ta =inbound-request:eyre] vase)
|
||||||
|
:_ this
|
||||||
|
%+ give-simple-payload:app eyre-id
|
||||||
|
%+ require-authorization:app inbound-request
|
||||||
|
poke-handle-http-request:cc
|
||||||
|
::
|
||||||
|
==
|
||||||
|
::
|
||||||
|
++ on-watch
|
||||||
|
|= =path
|
||||||
|
^- (quip card:agent:gall _this)
|
||||||
|
?: ?=([%http-response *] path)
|
||||||
|
`this
|
||||||
|
?. =(/ path)
|
||||||
|
(on-watch:def path)
|
||||||
|
[[%give %fact ~ %json !>(*json)]~ this]
|
||||||
|
::
|
||||||
|
++ on-agent on-agent:def
|
||||||
|
::
|
||||||
|
++ on-arvo
|
||||||
|
|= [=wire =sign-arvo]
|
||||||
|
^- (quip card _this)
|
||||||
|
?. ?=(%bound +<.sign-arvo)
|
||||||
|
(on-arvo:def wire sign-arvo)
|
||||||
|
[~ this]
|
||||||
|
::
|
||||||
|
++ on-save on-save:def
|
||||||
|
++ on-load on-load:def
|
||||||
|
++ on-leave on-leave:def
|
||||||
|
++ on-peek on-peek:def
|
||||||
|
++ on-fail on-fail:def
|
||||||
|
--
|
||||||
|
::
|
||||||
|
::
|
||||||
|
|_ bol=bowl:gall
|
||||||
|
::
|
||||||
|
++ poke-handle-http-request
|
||||||
|
|= =inbound-request:eyre
|
||||||
|
^- 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)
|
||||||
|
::
|
||||||
|
[%'~%APPNAME%' %img @t *]
|
||||||
|
=/ name=@t i.t.t.site.url
|
||||||
|
=/ img (~(get by %APPNAME%-png) name)
|
||||||
|
?~ img
|
||||||
|
not-found:gen
|
||||||
|
(png-response:gen (as-octs:mimes:html u.img))
|
||||||
|
::
|
||||||
|
[%'~%APPNAME%' *] (html-response:gen index)
|
||||||
|
==
|
||||||
|
::
|
||||||
|
--
|
BIN
full/urbit/app/smol/img/Home.png
Normal file
BIN
full/urbit/app/smol/img/Home.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 679 B |
BIN
full/urbit/app/smol/img/Tile.png
Normal file
BIN
full/urbit/app/smol/img/Tile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
16
full/urbit/app/smol/index.html
Normal file
16
full/urbit/app/smol/index.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>%APPNAME%</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" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root" />
|
||||||
|
<script src="/~/channel/channel.js"></script>
|
||||||
|
<script src="/~modulo/session.js"></script>
|
||||||
|
<script src="/~%APPNAME%/js/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
82
gulpfile.js
Normal file
82
gulpfile.js
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
var gulp = require('gulp');
|
||||||
|
var rollup = require('gulp-better-rollup');
|
||||||
|
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('tile-jsx-transform', function(cb) {
|
||||||
|
return gulp.src('tile/**/*.js')
|
||||||
|
.pipe(sucrase({
|
||||||
|
transforms: ['jsx']
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest('dist'));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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('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('tile-js-bundle-dev', gulp.series('tile-jsx-transform', 'tile-js-imports'));
|
||||||
|
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('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('urbit/**/*', gulp.parallel('urbit-copy'));
|
||||||
|
}));
|
191
install.js
Normal file
191
install.js
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
const prompt = require('prompt')
|
||||||
|
const replace = require('replace-in-file')
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
var Promise = require('promise');
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
// Making the text input a bit legible.
|
||||||
|
|
||||||
|
prompt.colors = false
|
||||||
|
prompt.message = ""
|
||||||
|
|
||||||
|
// The text input takes a "result" object and passes it to one of two functions to do the logistics.
|
||||||
|
|
||||||
|
prompt.get([{
|
||||||
|
name: 'appName',
|
||||||
|
required: true,
|
||||||
|
description: "What's the name of your application? Lowercase and no spaces, please.",
|
||||||
|
message: "Lowercase and no spaces, please.",
|
||||||
|
conform: function(value) {
|
||||||
|
return /^[a-z0-9]+((\-[a-z0-9]+){1,})?$/g.test(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
required: true,
|
||||||
|
description: "Is your app just a tile, or a full application? (tile/full)",
|
||||||
|
message: "Please specify 'tile' or 'full'.",
|
||||||
|
conform: function(value) {
|
||||||
|
if ((value == "tile") || (value == "full")) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pier',
|
||||||
|
required: true,
|
||||||
|
description: "Where is your Urbit pier's desk located? For example, /Users/dev/zod/home"
|
||||||
|
}], function (err, result) {
|
||||||
|
if (result.type == "tile") setupTile(result)
|
||||||
|
else if (result.type == "full") setupFull(result)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Delete the 'full' app folder and rename the tile-only files.
|
||||||
|
|
||||||
|
const setupTile = function (result) {
|
||||||
|
deleteFolderRecursive('full')
|
||||||
|
let deHyphenatedName = result.appName.indexOf('-') > -1 ? result.appName.replace(/-/g, "") : result.appName
|
||||||
|
fs.renameSync('urbit/app/smol.hoon', 'urbit/app/' + deHyphenatedName + '.hoon')
|
||||||
|
// Make a copy of the name without hyphens for the JS naming.
|
||||||
|
let capitalisedAppName = deHyphenatedName.charAt(0).toUpperCase() + deHyphenatedName.slice(1)
|
||||||
|
let appNameOptions = {
|
||||||
|
files: ['gulpfile.js', 'urbit/app/' + deHyphenatedName + '.hoon'],
|
||||||
|
from: /%APPNAME%/g,
|
||||||
|
to: deHyphenatedName
|
||||||
|
}
|
||||||
|
let appNamewithCapitals = {
|
||||||
|
files: 'tile/tile.js',
|
||||||
|
from: [/%APPNAME%Tile/g, /%APPNAME%/g],
|
||||||
|
to: [deHyphenatedName + "Tile", capitalisedAppName]
|
||||||
|
}
|
||||||
|
let urbitPierOptions = {
|
||||||
|
files: '.urbitrc',
|
||||||
|
from: "%URBITPIER%",
|
||||||
|
to: result.pier
|
||||||
|
}
|
||||||
|
replace(appNameOptions).then(changedFiles => console.log(changedFiles)).catch(err => console.error(err))
|
||||||
|
replace(appNamewithCapitals).then(changedFiles => console.log(changedFiles)).catch(err => console.error(err))
|
||||||
|
replace(urbitPierOptions).then(changedFiles => console.log(changedFiles)).catch(err => console.error(err))
|
||||||
|
console.log("All done! Happy hacking.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the tile-specific files and move the full application to root. Rename everything as necessary.
|
||||||
|
|
||||||
|
const deleteFolderRecursive = function (path) {
|
||||||
|
if (fs.existsSync(path)) {
|
||||||
|
fs.readdirSync(path).forEach(function (file, index) {
|
||||||
|
var curPath = path + "/" + file;
|
||||||
|
if (fs.lstatSync(curPath).isDirectory()) {
|
||||||
|
deleteFolderRecursive(curPath);
|
||||||
|
} else {
|
||||||
|
fs.unlinkSync(curPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fs.rmdirSync(path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var promiseAllWait = function (promises) {
|
||||||
|
// this is the same as Promise.all(), except that it will wait for all promises to fulfill before rejecting
|
||||||
|
var all_promises = [];
|
||||||
|
for (var i_promise = 0; i_promise < promises.length; i_promise++) {
|
||||||
|
all_promises.push(
|
||||||
|
promises[i_promise]
|
||||||
|
.then(function (res) {
|
||||||
|
return { res: res };
|
||||||
|
}).catch(function (err) {
|
||||||
|
return { err: err };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(all_promises)
|
||||||
|
.then(function (results) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var is_failure = false;
|
||||||
|
var i_result;
|
||||||
|
for (i_result = 0; i_result < results.length; i_result++) {
|
||||||
|
if (results[i_result].err) {
|
||||||
|
is_failure = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
results[i_result] = results[i_result].res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_failure) {
|
||||||
|
reject(results[i_result].err);
|
||||||
|
} else {
|
||||||
|
resolve(results);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var movePromiser = function (from, to, records) {
|
||||||
|
return fs.move(from, to)
|
||||||
|
.then(function () {
|
||||||
|
records.push({ from: from, to: to });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var moveDir = function (from_dir, to_dir, callback) {
|
||||||
|
return fs.readdir(from_dir)
|
||||||
|
.then(function (children) {
|
||||||
|
return fs.ensureDir(to_dir)
|
||||||
|
.then(function () {
|
||||||
|
var move_promises = [];
|
||||||
|
var moved_records = [];
|
||||||
|
var child;
|
||||||
|
for (var i_child = 0; i_child < children.length; i_child++) {
|
||||||
|
child = children[i_child];
|
||||||
|
move_promises.push(movePromiser(
|
||||||
|
path.join(from_dir, child),
|
||||||
|
path.join(to_dir, child),
|
||||||
|
moved_records
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return promiseAllWait(move_promises)
|
||||||
|
.catch(function (err) {
|
||||||
|
var undo_move_promises = [];
|
||||||
|
for (var i_moved_record = 0; i_moved_record < moved_records.length; i_moved_record++) {
|
||||||
|
undo_move_promises.push(fs.move(moved_records[i_moved_record].to, moved_records[i_moved_record].from));
|
||||||
|
}
|
||||||
|
|
||||||
|
return promiseAllWait(undo_move_promises)
|
||||||
|
.then(function () {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
return fs.rmdir(from_dir);
|
||||||
|
});
|
||||||
|
}).then(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setupFull = function (result) {
|
||||||
|
deleteFolderRecursive('tile')
|
||||||
|
deleteFolderRecursive('urbit')
|
||||||
|
fs.unlinkSync('gulpfile.js')
|
||||||
|
let deHyphenatedName = result.appName.indexOf('-') > -1 ? result.appName.replace(/-/g, "") : result.appName
|
||||||
|
moveDir('full', './', function() {
|
||||||
|
fs.renameSync('urbit/app/smol.hoon', 'urbit/app/' + deHyphenatedName + '.hoon')
|
||||||
|
fs.renameSync('urbit/app/smol/', 'urbit/app/' + deHyphenatedName)
|
||||||
|
let urbitPierOptions = {
|
||||||
|
files: '.urbitrc',
|
||||||
|
from: "%URBITPIER%",
|
||||||
|
to: result.pier
|
||||||
|
}
|
||||||
|
replace(urbitPierOptions).then(changedFiles => console.log(changedFiles)).catch(err => console.error(err))
|
||||||
|
let appNameOptions = {
|
||||||
|
files: ['gulpfile.js', 'urbit/app/' + deHyphenatedName + '.hoon', 'tile/tile.js',
|
||||||
|
'src/js/api.js', 'src/js/subscription.js', 'src/js/components/root.js',
|
||||||
|
'src/js/reducers/config.js', 'urbit/app/' + deHyphenatedName + '/index.html', 'src/js/components/lib/icons/icon-home.js'
|
||||||
|
],
|
||||||
|
from: /%APPNAME%/g,
|
||||||
|
to: deHyphenatedName
|
||||||
|
}
|
||||||
|
replace(appNameOptions).then(changedFiles => console.log(changedFiles)).catch(err => console.error(err))
|
||||||
|
})
|
||||||
|
}
|
6100
package-lock.json
generated
Normal file
6100
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
48
package.json
Normal file
48
package.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "create-landscape-app",
|
||||||
|
"version": "3.0.0",
|
||||||
|
"description": "Get started with a Landscape application.",
|
||||||
|
"main": "node install.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node install.js",
|
||||||
|
"build": "gulp",
|
||||||
|
"serve": "gulp watch"
|
||||||
|
},
|
||||||
|
"author": "Tlon Corp",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "https://github.com/urbit/create-landscape-app",
|
||||||
|
"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",
|
||||||
|
"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",
|
||||||
|
"fs-extra": "^8.1.0",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"moment": "^2.20.1",
|
||||||
|
"mousetrap": "^1.6.3",
|
||||||
|
"mv": "^2.1.1",
|
||||||
|
"promise": "^8.0.3",
|
||||||
|
"prompt": "^1.0.0",
|
||||||
|
"react": "^16.5.2",
|
||||||
|
"react-dom": "^16.8.6",
|
||||||
|
"react-router-dom": "^5.0.0",
|
||||||
|
"replace-in-file": "^4.1.1",
|
||||||
|
"urbit-ob": "^4.1.2",
|
||||||
|
"urbit-sigil-js": "^1.3.13"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"natives": "1.1.3"
|
||||||
|
}
|
||||||
|
}
|
17
tile/tile.js
Normal file
17
tile/tile.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
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">
|
||||||
|
<p className="black white-d absolute f9" style={{ left: 8, top: 8 }}>%APPNAME%</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
window.%APPNAME%Tile = %APPNAME%Tile;
|
71
urbit/app/smol.hoon
Normal file
71
urbit/app/smol.hoon
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/+ *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
|
||||||
|
--
|
Loading…
Reference in New Issue
Block a user