mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-26 00:12:28 +03:00
Merge pull request #5219 from urbit/m/dist-webterm
dist: webterm: standalone package
This commit is contained in:
commit
f690752180
@ -1,100 +0,0 @@
|
||||
import { Box, Col } from '@tlon/indigo-react';
|
||||
import React, { Component } from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { Route } from 'react-router-dom';
|
||||
import withState from '~/logic/lib/withState';
|
||||
import useHarkState from '~/logic/state/hark';
|
||||
import Api from './api';
|
||||
import { History } from './components/history';
|
||||
import { Input } from './components/input';
|
||||
import './css/custom.css';
|
||||
import Store from './store';
|
||||
import Subscription from './subscription';
|
||||
|
||||
class TermApp extends Component<any, any> {
|
||||
store: Store;
|
||||
api: any;
|
||||
subscription: any;
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.store = new Store();
|
||||
this.store.setStateHandler(this.setState.bind(this));
|
||||
|
||||
this.state = this.store.state;
|
||||
}
|
||||
|
||||
resetControllers() {
|
||||
this.api = null;
|
||||
this.subscription = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.resetControllers();
|
||||
// eslint-disable-next-line new-cap
|
||||
const channel = new (window as any).channel();
|
||||
this.api = new Api(this.props.ship, channel);
|
||||
this.store.api = this.api;
|
||||
|
||||
this.subscription = new Subscription(this.store, this.api, channel);
|
||||
this.subscription.start();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscription.delete();
|
||||
this.store.clear();
|
||||
this.resetControllers();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Helmet defer={false}>
|
||||
<title>{ this.props.notificationsCount ? `(${String(this.props.notificationsCount) }) `: '' }Landscape</title>
|
||||
</Helmet>
|
||||
<Box
|
||||
height='100%'
|
||||
>
|
||||
<Route
|
||||
exact
|
||||
path="/~term/"
|
||||
render={(props) => {
|
||||
return (
|
||||
<Box
|
||||
width='100%'
|
||||
height='100%'
|
||||
display='flex'
|
||||
>
|
||||
<Col
|
||||
p={3}
|
||||
backgroundColor='white'
|
||||
width='100%'
|
||||
minHeight={0}
|
||||
minWidth={0}
|
||||
color='lightGray'
|
||||
borderRadius={2}
|
||||
mx={['0','3']}
|
||||
mb={['0','3']}
|
||||
border={['0','1']}
|
||||
cursor='text'
|
||||
>
|
||||
{/* @ts-ignore declare props in later pass */}
|
||||
<History log={this.state.lines.slice(0, -1)} />
|
||||
<Input
|
||||
ship={this.props.ship}
|
||||
cursor={this.state.cursor}
|
||||
api={this.api}
|
||||
store={this.store}
|
||||
line={this.state.lines.slice(-1)[0]}
|
||||
/>
|
||||
</Col>
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withState(TermApp, [[useHarkState]]);
|
94
pkg/interface/webterm/app.tsx
Normal file
94
pkg/interface/webterm/app.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import { Box, Col } from '@tlon/indigo-react';
|
||||
import React, { Component } from 'react';
|
||||
import dark from '@tlon/indigo-dark';
|
||||
import light from '@tlon/indigo-light';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import Api from './api';
|
||||
import { History } from './components/history';
|
||||
import { Input } from './components/input';
|
||||
import './css/custom.css';
|
||||
import Store from './store';
|
||||
import Subscription from './subscription';
|
||||
import Channel from './lib/channel';
|
||||
|
||||
class TermApp extends Component<any, any> {
|
||||
store: Store;
|
||||
api: any;
|
||||
subscription: any;
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.store = new Store();
|
||||
this.store.setStateHandler(this.setState.bind(this));
|
||||
|
||||
this.state = this.store.state;
|
||||
}
|
||||
|
||||
resetControllers() {
|
||||
this.api = null;
|
||||
this.subscription = null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.resetControllers();
|
||||
// eslint-disable-next-line new-cap
|
||||
const channel = new Channel();
|
||||
this.api = new Api(window.ship, channel);
|
||||
this.store.api = this.api;
|
||||
|
||||
this.subscription = new Subscription(this.store, this.api, channel);
|
||||
this.subscription.start();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.subscription.delete();
|
||||
this.store.clear();
|
||||
this.resetControllers();
|
||||
}
|
||||
|
||||
getTheme() {
|
||||
const { props } = this;
|
||||
return ((props.dark && props?.display?.theme == 'auto') ||
|
||||
props?.display?.theme == 'dark'
|
||||
) ? dark : light;
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = this.getTheme();
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<Box
|
||||
width='100%'
|
||||
height='100%'
|
||||
p={['0','3']}
|
||||
style={{ boxSizing: 'border-box' }}
|
||||
>
|
||||
<Col
|
||||
p={3}
|
||||
backgroundColor='white'
|
||||
width='100%'
|
||||
height='100%'
|
||||
minHeight={0}
|
||||
minWidth={0}
|
||||
color='lightGray'
|
||||
borderRadius={2}
|
||||
border={['0','1']}
|
||||
cursor='text'
|
||||
style={{ boxSizing: 'border-box' }}
|
||||
>
|
||||
{/* @ts-ignore declare props in later pass */}
|
||||
<History log={this.state.lines.slice(0, -1)} />
|
||||
<Input
|
||||
ship={this.props.ship}
|
||||
cursor={this.state.cursor}
|
||||
api={this.api}
|
||||
store={this.store}
|
||||
line={this.state.lines.slice(-1)[0]}
|
||||
/>
|
||||
</Col>
|
||||
</Box>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TermApp;
|
@ -101,14 +101,14 @@ belt = { met: 'bac' };
|
||||
autoFocus
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
color='black'
|
||||
color='lightGray'
|
||||
minHeight={0}
|
||||
display='inline-block'
|
||||
width='100%'
|
||||
spellCheck="false"
|
||||
tabindex={0}
|
||||
wrap="off"
|
||||
className="mono"
|
||||
fontFamily="mono"
|
||||
id="term"
|
||||
cursor={this.props.cursor}
|
||||
onKeyDown={this.keyPress}
|
11
pkg/interface/webterm/config/urbitrc-sample
Normal file
11
pkg/interface/webterm/config/urbitrc-sample
Normal file
@ -0,0 +1,11 @@
|
||||
module.exports = {
|
||||
URBIT_PIERS: [
|
||||
"/Users/user/ships/zod/home",
|
||||
],
|
||||
herb: false,
|
||||
URL: 'http://localhost:80',
|
||||
/* FLEET: {
|
||||
'zod': "http://localhost:8080',
|
||||
'bus': 'http://localhost:8081'
|
||||
} */
|
||||
};
|
106
pkg/interface/webterm/config/webpack.dev.js
Normal file
106
pkg/interface/webterm/config/webpack.dev.js
Normal file
@ -0,0 +1,106 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
// const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const urbitrc = require('./urbitrc');
|
||||
const _ = require('lodash');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const GIT_DESC = execSync('git describe --always', { encoding: 'utf8' }).trim();
|
||||
|
||||
let devServer = {
|
||||
contentBase: path.join(__dirname, '../dist'),
|
||||
hot: true,
|
||||
port: 9000,
|
||||
host: '0.0.0.0',
|
||||
disableHostCheck: true,
|
||||
historyApiFallback: true,
|
||||
publicPath: '/apps/webterm/'
|
||||
};
|
||||
|
||||
const router = _.mapKeys(urbitrc.FLEET || {}, (value, key) => `${key}.localhost:9000`);
|
||||
|
||||
if(urbitrc.URL) {
|
||||
devServer = {
|
||||
...devServer,
|
||||
index: 'index.html',
|
||||
proxy: [{
|
||||
changeOrigin: true,
|
||||
target: urbitrc.URL,
|
||||
router,
|
||||
context: (path) => {
|
||||
return !path.startsWith('/apps/webterm');
|
||||
}
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: {
|
||||
app: './index.js'
|
||||
// serviceworker: './src/serviceworker.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(j|t)sx?$/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env', '@babel/typescript', ['@babel/preset-react', {
|
||||
runtime: 'automatic',
|
||||
development: true,
|
||||
importSource: '@welldone-software/why-did-you-render'
|
||||
}]],
|
||||
plugins: [
|
||||
'@babel/transform-runtime',
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'react-hot-loader/babel'
|
||||
]
|
||||
}
|
||||
},
|
||||
exclude: /node_modules\/(?!(@tlon\/indigo-dark|@tlon\/indigo-light|@tlon\/indigo-react)\/).*/
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: [
|
||||
// Creates `style` nodes from JS strings
|
||||
'style-loader',
|
||||
// Translates CSS into CommonJS
|
||||
'css-loader',
|
||||
// Compiles Sass to CSS
|
||||
'sass-loader'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.tsx']
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
devServer: devServer,
|
||||
plugins: [
|
||||
// new CleanWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'Terminal',
|
||||
template: './index.html'
|
||||
})
|
||||
],
|
||||
watch: true,
|
||||
output: {
|
||||
filename: (pathData) => {
|
||||
return pathData.chunk.name === 'app' ? 'index.js' : '[name].js';
|
||||
},
|
||||
chunkFilename: '[name].js',
|
||||
path: path.resolve(__dirname, '../dist'),
|
||||
publicPath: '/apps/webterm/',
|
||||
globalObject: 'this'
|
||||
},
|
||||
optimization: {
|
||||
minimize: false,
|
||||
usedExports: true
|
||||
}
|
||||
};
|
77
pkg/interface/webterm/config/webpack.prod.js
Normal file
77
pkg/interface/webterm/config/webpack.prod.js
Normal file
@ -0,0 +1,77 @@
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const GIT_DESC = execSync('git describe --always', { encoding: 'utf8' }).trim();
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
entry: {
|
||||
app: './index.js',
|
||||
// serviceworker: './src/serviceworker.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(j|t)sx?$/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env', '@babel/typescript', '@babel/preset-react'],
|
||||
plugins: [
|
||||
'lodash',
|
||||
'@babel/transform-runtime',
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
'@babel/plugin-proposal-class-properties'
|
||||
]
|
||||
}
|
||||
},
|
||||
exclude: /node_modules\/(?!(@tlon\/indigo-dark|@tlon\/indigo-light|@tlon\/indigo-react)\/).*/
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: [
|
||||
// Creates `style` nodes from JS strings
|
||||
'style-loader',
|
||||
// Translates CSS into CommonJS
|
||||
'css-loader',
|
||||
// Compiles Sass to CSS
|
||||
'sass-loader'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.tsx']
|
||||
},
|
||||
devtool: 'source-map',
|
||||
// devServer: {
|
||||
// contentBase: path.join(__dirname, './'),
|
||||
// hot: true,
|
||||
// port: 9000,
|
||||
// historyApiFallback: true
|
||||
// },
|
||||
plugins: [
|
||||
new MomentLocalesPlugin(),
|
||||
new CleanWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'Terminal',
|
||||
template: './index.html'
|
||||
})
|
||||
],
|
||||
output: {
|
||||
filename: (pathData) => {
|
||||
return pathData.chunk.name === 'app' ? 'index.[contenthash].js' : '[name].js';
|
||||
},
|
||||
path: path.resolve(__dirname, '../dist'),
|
||||
publicPath: '/apps/webterm/'
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
usedExports: true
|
||||
}
|
||||
};
|
@ -1,6 +1,13 @@
|
||||
body, #root {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input#term {
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.blink {
|
26
pkg/interface/webterm/index.html
Normal file
26
pkg/interface/webterm/index.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Terminal</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no,maximum-scale=1"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-touch-fullscreen" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<link rel="apple-touch-icon" href="/~landscape/img/touch_icon.png">
|
||||
<link rel="icon" type="image/png" href="/~landscape/img/Favicon.png">
|
||||
<link rel="manifest"
|
||||
href='data:application/manifest+json,{
|
||||
"name": "Terminal",
|
||||
"short_name": "Terminal",
|
||||
"description": "A%20terminal%20for%20your%20Urbit.",
|
||||
"display": "standalone",
|
||||
"background_color": "%23FFFFFF",
|
||||
"theme_color": "%23000000"}' />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="/session.js"></script>
|
||||
</body>
|
||||
</html>
|
5
pkg/interface/webterm/index.js
Normal file
5
pkg/interface/webterm/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import TermApp from './App';
|
||||
|
||||
ReactDOM.render(<TermApp />, document.getElementById('root'));
|
290
pkg/interface/webterm/lib/channel.js
Normal file
290
pkg/interface/webterm/lib/channel.js
Normal file
@ -0,0 +1,290 @@
|
||||
export default class Channel {
|
||||
constructor() {
|
||||
this.init();
|
||||
this.deleteOnUnload();
|
||||
|
||||
// a way to handle channel errors
|
||||
//
|
||||
//
|
||||
this.onChannelError = (err) => {
|
||||
console.error('event source error: ', err);
|
||||
};
|
||||
this.onChannelOpen = (e) => {
|
||||
console.log('open', e);
|
||||
};
|
||||
}
|
||||
|
||||
init() {
|
||||
this.debounceInterval = 500;
|
||||
// unique identifier: current time and random number
|
||||
//
|
||||
this.uid =
|
||||
new Date().getTime().toString() +
|
||||
"-" +
|
||||
Math.random().toString(16).slice(-6);
|
||||
|
||||
this.requestId = 1;
|
||||
|
||||
// the currently connected EventSource
|
||||
//
|
||||
this.eventSource = null;
|
||||
|
||||
// the id of the last EventSource event we received
|
||||
//
|
||||
this.lastEventId = 0;
|
||||
|
||||
// this last event id acknowledgment sent to the server
|
||||
//
|
||||
this.lastAcknowledgedEventId = 0;
|
||||
|
||||
// a registry of requestId to successFunc/failureFunc
|
||||
//
|
||||
// These functions are registered during a +poke and are executed
|
||||
// in the onServerEvent()/onServerError() callbacks. Only one of
|
||||
// the functions will be called, and the outstanding poke will be
|
||||
// removed after calling the success or failure function.
|
||||
//
|
||||
|
||||
this.outstandingPokes = new Map();
|
||||
|
||||
// a registry of requestId to subscription functions.
|
||||
//
|
||||
// These functions are registered during a +subscribe and are
|
||||
// executed in the onServerEvent()/onServerError() callbacks. The
|
||||
// event function will be called whenever a new piece of data on this
|
||||
// subscription is available, which may be 0, 1, or many times. The
|
||||
// disconnect function may be called exactly once.
|
||||
//
|
||||
this.outstandingSubscriptions = new Map();
|
||||
|
||||
this.outstandingJSON = [];
|
||||
|
||||
this.debounceTimer = null;
|
||||
}
|
||||
|
||||
resetDebounceTimer() {
|
||||
if (this.debounceTimer) {
|
||||
clearTimeout(this.debounceTimer);
|
||||
this.debounceTimer = null;
|
||||
}
|
||||
this.debounceTimer = setTimeout(() => {
|
||||
this.sendJSONToChannel();
|
||||
}, this.debounceInterval)
|
||||
}
|
||||
|
||||
setOnChannelError(onError = (err) => {}) {
|
||||
this.onChannelError = onError;
|
||||
}
|
||||
|
||||
setOnChannelOpen(onOpen = (e) => {}) {
|
||||
this.onChannelOpen = onOpen;
|
||||
}
|
||||
|
||||
deleteOnUnload() {
|
||||
window.addEventListener("beforeunload", (event) => {
|
||||
this.delete();
|
||||
});
|
||||
}
|
||||
|
||||
clearQueue() {
|
||||
clearTimeout(this.debounceTimer);
|
||||
this.debounceTimer = null;
|
||||
this.sendJSONToChannel();
|
||||
}
|
||||
|
||||
// sends a poke to an app on an urbit ship
|
||||
//
|
||||
poke(ship, app, mark, json, successFunc, failureFunc) {
|
||||
let id = this.nextId();
|
||||
this.outstandingPokes.set(
|
||||
id,
|
||||
{
|
||||
success: successFunc,
|
||||
fail: failureFunc
|
||||
}
|
||||
);
|
||||
|
||||
const j = {
|
||||
id,
|
||||
action: "poke",
|
||||
ship,
|
||||
app,
|
||||
mark,
|
||||
json
|
||||
};
|
||||
|
||||
this.sendJSONToChannel(j);
|
||||
}
|
||||
|
||||
// subscribes to a path on an specific app and ship.
|
||||
//
|
||||
// Returns a subscription id, which is the same as the same internal id
|
||||
// passed to your Urbit.
|
||||
subscribe(
|
||||
ship,
|
||||
app,
|
||||
path,
|
||||
connectionErrFunc = () => {},
|
||||
eventFunc = () => {},
|
||||
quitFunc = () => {},
|
||||
subAckFunc = () => {},
|
||||
) {
|
||||
let id = this.nextId();
|
||||
this.outstandingSubscriptions.set(
|
||||
id,
|
||||
{
|
||||
err: connectionErrFunc,
|
||||
event: eventFunc,
|
||||
quit: quitFunc,
|
||||
subAck: subAckFunc
|
||||
}
|
||||
);
|
||||
|
||||
const json = {
|
||||
id,
|
||||
action: "subscribe",
|
||||
ship,
|
||||
app,
|
||||
path
|
||||
}
|
||||
|
||||
this.resetDebounceTimer();
|
||||
|
||||
this.outstandingJSON.push(json);
|
||||
return id;
|
||||
}
|
||||
|
||||
// quit the channel
|
||||
//
|
||||
delete() {
|
||||
let id = this.nextId();
|
||||
clearInterval(this.ackTimer);
|
||||
navigator.sendBeacon(this.channelURL(), JSON.stringify([{
|
||||
id,
|
||||
action: "delete"
|
||||
}]));
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
// unsubscribe to a specific subscription
|
||||
//
|
||||
unsubscribe(subscription) {
|
||||
let id = this.nextId();
|
||||
this.sendJSONToChannel({
|
||||
id,
|
||||
action: "unsubscribe",
|
||||
subscription
|
||||
});
|
||||
}
|
||||
|
||||
// sends a JSON command command to the server.
|
||||
//
|
||||
sendJSONToChannel(j) {
|
||||
let req = new XMLHttpRequest();
|
||||
req.open("PUT", this.channelURL());
|
||||
req.setRequestHeader("Content-Type", "application/json");
|
||||
|
||||
if (this.lastEventId == this.lastAcknowledgedEventId) {
|
||||
if (j) {
|
||||
this.outstandingJSON.push(j);
|
||||
}
|
||||
|
||||
if (this.outstandingJSON.length > 0) {
|
||||
let x = JSON.stringify(this.outstandingJSON);
|
||||
req.send(x);
|
||||
}
|
||||
} else {
|
||||
// we add an acknowledgment to clear the server side queue
|
||||
//
|
||||
// The server side puts messages it sends us in a queue until we
|
||||
// acknowledge that we received it.
|
||||
//
|
||||
let payload = [
|
||||
...this.outstandingJSON,
|
||||
{action: "ack", "event-id": this.lastEventId}
|
||||
];
|
||||
if (j) {
|
||||
payload.push(j)
|
||||
}
|
||||
let x = JSON.stringify(payload);
|
||||
req.send(x);
|
||||
|
||||
this.lastAcknowledgedEventId = this.lastEventId;
|
||||
}
|
||||
this.outstandingJSON = [];
|
||||
|
||||
this.connectIfDisconnected();
|
||||
}
|
||||
|
||||
// connects to the EventSource if we are not currently connected
|
||||
//
|
||||
connectIfDisconnected() {
|
||||
if (this.eventSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.eventSource = new EventSource(this.channelURL(), {withCredentials:true});
|
||||
this.eventSource.onmessage = e => {
|
||||
this.lastEventId = parseInt(e.lastEventId, 10);
|
||||
|
||||
let obj = JSON.parse(e.data);
|
||||
let pokeFuncs = this.outstandingPokes.get(obj.id);
|
||||
let subFuncs = this.outstandingSubscriptions.get(obj.id);
|
||||
|
||||
if (obj.response == "poke" && !!pokeFuncs) {
|
||||
let funcs = pokeFuncs;
|
||||
if (obj.hasOwnProperty("ok")) {
|
||||
funcs["success"]();
|
||||
} else if (obj.hasOwnProperty("err")) {
|
||||
funcs["fail"](obj.err);
|
||||
} else {
|
||||
console.error("Invalid poke response: ", obj);
|
||||
}
|
||||
this.outstandingPokes.delete(obj.id);
|
||||
|
||||
} else if (obj.response == "subscribe" ||
|
||||
(obj.response == "poke" && !!subFuncs)) {
|
||||
let funcs = subFuncs;
|
||||
|
||||
if (obj.hasOwnProperty("err")) {
|
||||
funcs["err"](obj.err);
|
||||
this.outstandingSubscriptions.delete(obj.id);
|
||||
} else if (obj.hasOwnProperty("ok")) {
|
||||
funcs["subAck"](obj);
|
||||
}
|
||||
} else if (obj.response == "diff") {
|
||||
// ensure we ack before channel clogs
|
||||
if((this.lastEventId - this.lastAcknowledgedEventId) > 30) {
|
||||
this.clearQueue();
|
||||
}
|
||||
|
||||
let funcs = subFuncs;
|
||||
funcs["event"](obj.json);
|
||||
} else if (obj.response == "quit") {
|
||||
let funcs = subFuncs;
|
||||
funcs["quit"](obj);
|
||||
this.outstandingSubscriptions.delete(obj.id);
|
||||
} else {
|
||||
console.log("Unrecognized response: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
this.eventSource.onopen = this.onChannelOpen;
|
||||
|
||||
this.eventSource.onerror = e => {
|
||||
this.delete();
|
||||
this.init();
|
||||
this.onChannelError(e);
|
||||
}
|
||||
}
|
||||
|
||||
channelURL() {
|
||||
return "/~/channel/" + this.uid;
|
||||
}
|
||||
|
||||
nextId() {
|
||||
return this.requestId++;
|
||||
}
|
||||
}
|
24922
pkg/interface/webterm/package-lock.json
generated
Normal file
24922
pkg/interface/webterm/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
134
pkg/interface/webterm/package.json
Normal file
134
pkg/interface/webterm/package.json
Normal file
@ -0,0 +1,134 @@
|
||||
{
|
||||
"name": "interface",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@reach/disclosure": "^0.10.5",
|
||||
"@reach/menu-button": "^0.10.5",
|
||||
"@reach/tabs": "^0.10.5",
|
||||
"@react-spring/web": "^9.1.1",
|
||||
"@tlon/indigo-dark": "^1.0.6",
|
||||
"@tlon/indigo-light": "^1.0.7",
|
||||
"@tlon/indigo-react": "^1.2.23",
|
||||
"@tlon/sigil-js": "^1.4.3",
|
||||
"@urbit/api": "^1.1.1",
|
||||
"@urbit/http-api": "^1.2.1",
|
||||
"any-ascii": "^0.1.7",
|
||||
"aws-sdk": "^2.830.0",
|
||||
"big-integer": "^1.6.48",
|
||||
"classnames": "^2.2.6",
|
||||
"codemirror": "^5.59.2",
|
||||
"css-loader": "^3.6.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"formik": "^2.1.5",
|
||||
"immer": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.1",
|
||||
"mousetrap": "^1.6.5",
|
||||
"mousetrap-global-bind": "^1.1.0",
|
||||
"normalize-wheel": "1.0.1",
|
||||
"oembed-parser": "^1.4.5",
|
||||
"prop-types": "^15.7.2",
|
||||
"querystring": "^0.2.0",
|
||||
"react": "^16.14.0",
|
||||
"react-codemirror2": "^6.0.1",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-oembed-container": "^1.0.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-use-gesture": "^9.1.3",
|
||||
"react-virtuoso": "^0.20.3",
|
||||
"react-visibility-sensor": "^5.1.1",
|
||||
"remark": "^12.0.0",
|
||||
"remark-breaks": "^2.0.2",
|
||||
"remark-disable-tokenizers": "1.1.0",
|
||||
"stacktrace-js": "^2.0.2",
|
||||
"style-loader": "^1.3.0",
|
||||
"styled-components": "^5.1.1",
|
||||
"styled-system": "^5.1.5",
|
||||
"suncalc": "^1.8.0",
|
||||
"unist-util-visit": "^3.0.0",
|
||||
"urbit-ob": "^5.0.1",
|
||||
"workbox-core": "^6.0.2",
|
||||
"workbox-precaching": "^6.0.2",
|
||||
"workbox-recipes": "^6.0.2",
|
||||
"workbox-routing": "^6.0.2",
|
||||
"yup": "^0.29.3",
|
||||
"zustand": "^3.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
|
||||
"@babel/plugin-transform-runtime": "^7.12.10",
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@babel/preset-react": "^7.12.10",
|
||||
"@babel/preset-typescript": "^7.12.7",
|
||||
"@storybook/addon-actions": "^6.2.9",
|
||||
"@storybook/addon-essentials": "^6.2.9",
|
||||
"@storybook/addon-links": "^6.2.9",
|
||||
"@storybook/react": "^6.2.9",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/react": "^16.14.2",
|
||||
"@types/react-dom": "^16.9.10",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"@types/styled-components": "^5.1.7",
|
||||
"@types/styled-system": "^5.1.10",
|
||||
"@types/yup": "^0.29.11",
|
||||
"@typescript-eslint/eslint-plugin": "^4.15.0",
|
||||
"@typescript-eslint/parser": "^4.24.0",
|
||||
"@urbit/eslint-config": "^1.0.0",
|
||||
"@welldone-software/why-did-you-render": "^6.1.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"babel-plugin-root-import": "^6.6.0",
|
||||
"chromatic": "^5.8.3",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^7.26.0",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^4.5.1",
|
||||
"husky": "^6.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"lint-staged": "^11.0.0",
|
||||
"loki": "^0.28.1",
|
||||
"moment-locales-webpack-plugin": "^1.2.0",
|
||||
"react-hot-loader": "^4.13.0",
|
||||
"sass": "^1.32.5",
|
||||
"sass-loader": "^8.0.2",
|
||||
"storybook-addon-designs": "^6.0.0",
|
||||
"ts-mdast": "^1.0.0",
|
||||
"typescript": "^4.2.4",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint ./src/**/*.{ts,tsx}",
|
||||
"lint-file": "eslint",
|
||||
"tsc": "tsc",
|
||||
"tsc:watch": "tsc --watch",
|
||||
"build:dev": "cross-env NODE_ENV=development webpack --config config/webpack.dev.js",
|
||||
"build:prod": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js",
|
||||
"start": "webpack-dev-server --config config/webpack.dev.js",
|
||||
"test": "tsc && jest",
|
||||
"jest": "jest",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook",
|
||||
"chromatic": "chromatic --exit-zero-on-changes",
|
||||
"hook-lint": "eslint --cache --fix"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"lint-staged": {
|
||||
"*.{js,ts,tsx}": "eslint --cache --fix"
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { saveAs } from 'file-saver';
|
||||
import bel from '../../../logic/lib/bel';
|
||||
import bel from './lib/bel';
|
||||
|
||||
export default class Store {
|
||||
state: any;
|
1
pkg/webterm/desk.bill
Normal file
1
pkg/webterm/desk.bill
Normal file
@ -0,0 +1 @@
|
||||
~
|
9
pkg/webterm/desk.docket
Normal file
9
pkg/webterm/desk.docket
Normal file
@ -0,0 +1,9 @@
|
||||
:~ title+'Web Terminal'
|
||||
info+'A web interface for dill, through herm.'
|
||||
color+0xff.ffff
|
||||
glob-http+'https://bootstrap.urbit.org/glob-0v4.8ui32.ui10d.t0v4d.n9g1s.1ftua.glob'
|
||||
base+'webterm'
|
||||
version+[0 0 1]
|
||||
website+'https://tlon.io'
|
||||
license+'MIT'
|
||||
==
|
1
pkg/webterm/sys.kelvin
Normal file
1
pkg/webterm/sys.kelvin
Normal file
@ -0,0 +1 @@
|
||||
[%zuse 420]
|
Loading…
Reference in New Issue
Block a user