mirror of
https://github.com/sorenisanerd/gotty.git
synced 2024-11-09 15:24:25 +00:00
Merge branch 'master' of https://github.com/sorenisanerd/gotty into process-http-headers
This commit is contained in:
commit
936288058d
@ -25,7 +25,7 @@
|
|||||||
"avatar_url": "https://avatars.githubusercontent.com/u/160090?v=4",
|
"avatar_url": "https://avatars.githubusercontent.com/u/160090?v=4",
|
||||||
"profile": "http://linux2go.dk/",
|
"profile": "http://linux2go.dk/",
|
||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"bug"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -322,7 +322,7 @@
|
|||||||
"avatar_url": "https://avatars.githubusercontent.com/u/42201?v=4",
|
"avatar_url": "https://avatars.githubusercontent.com/u/42201?v=4",
|
||||||
"profile": "https://rusnak.io/",
|
"profile": "https://rusnak.io/",
|
||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"bug"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -333,6 +333,195 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "jkandasa",
|
||||||
|
"name": "Jeeva Kandasamy",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/1004403?v=4",
|
||||||
|
"profile": "https://github.com/jkandasa",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "hardliner66",
|
||||||
|
"name": "Steve Biedermann",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/2937272?v=4",
|
||||||
|
"profile": "https://twitch.tv/iamhardliner",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "xgdgsc",
|
||||||
|
"name": "xgdgsc",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/1189869?v=4",
|
||||||
|
"profile": "https://github.com/xgdgsc",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "flechaig",
|
||||||
|
"name": "flechaig",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/10887132?v=4",
|
||||||
|
"profile": "https://github.com/flechaig",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Fan-SJ",
|
||||||
|
"name": "Fan-SJ",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/49977708?v=4",
|
||||||
|
"profile": "https://github.com/Fan-SJ",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "dmartin",
|
||||||
|
"name": "Dustin Martin",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/1657652?v=4",
|
||||||
|
"profile": "https://github.com/dmartin",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "ahmetb",
|
||||||
|
"name": "Ahmet Alp Balkan",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/159209?v=4",
|
||||||
|
"profile": "https://ahmet.dev/",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "CoconutMacaroon",
|
||||||
|
"name": "CoconutMacaroon",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/45187468?v=4",
|
||||||
|
"profile": "https://github.com/CoconutMacaroon",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "DannyBen",
|
||||||
|
"name": "Danny Ben Shitrit",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/2405099?v=4",
|
||||||
|
"profile": "https://github.dannyben.com/",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "George-NG",
|
||||||
|
"name": "George-NG",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/28577165?v=4",
|
||||||
|
"profile": "https://github.com/George-NG",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "ghthor",
|
||||||
|
"name": "Will Owens",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/160298?v=4",
|
||||||
|
"profile": "https://github.com/ghthor",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "jpillora",
|
||||||
|
"name": "Jaime Pillora",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/633843?v=4",
|
||||||
|
"profile": "https://jpillora.com/",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "kaisawind",
|
||||||
|
"name": "kaisawind",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/4010613?v=4",
|
||||||
|
"profile": "https://github.com/kaisawind",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "linyinli",
|
||||||
|
"name": "linyinli",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/42955482?v=4",
|
||||||
|
"profile": "https://github.com/linyinli",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "LucaMarconato",
|
||||||
|
"name": "LucaMarconato",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/2664412?v=4",
|
||||||
|
"profile": "https://github.com/LucaMarconato",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "masterkain",
|
||||||
|
"name": "Kain",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/12844?v=4",
|
||||||
|
"profile": "https://audiobox.fm/",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Nexuist",
|
||||||
|
"name": "Andi Andreas",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/1498061?v=4",
|
||||||
|
"profile": "http://duro.me/",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "qigj",
|
||||||
|
"name": "qigj",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/56585735?v=4",
|
||||||
|
"profile": "https://github.com/qigj",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "shuaiyy",
|
||||||
|
"name": "shuaiyy",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/19821321?v=4",
|
||||||
|
"profile": "https://github.com/shuaiyy",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "v20z",
|
||||||
|
"name": "v20z",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/2884824?v=4",
|
||||||
|
"profile": "https://github.com/v20z",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Yann-Qiu",
|
||||||
|
"name": "Yanfeng Qiu",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/56961747?v=4",
|
||||||
|
"profile": "https://github.com/Yann-Qiu",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7
|
"contributorsPerLine": 7
|
||||||
|
47
.github/workflows/main.yaml
vendored
47
.github/workflows/main.yaml
vendored
@ -1,24 +1,49 @@
|
|||||||
name: Go
|
name: "Unit and Build Tests"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
- push
|
push:
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
bundle-up-to-date:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- run: "make clean"
|
||||||
|
- run: "make assets"
|
||||||
|
- name: "Make sure gotty.js bundle is up-to-date"
|
||||||
|
run: "diffsize=$(git diff bindata/static/js/gotty.js | wc -l); test $diffsize == 0"
|
||||||
|
|
||||||
|
|
||||||
|
cross-compile-test:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.16
|
go-version: 1.19
|
||||||
|
|
||||||
- name: Test
|
- name: "Build & test"
|
||||||
run: make test
|
run: "make tools test cross_compile"
|
||||||
|
|
||||||
- name: Build
|
- name: Upload artifacts
|
||||||
run: make tools cross_compile
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: binaries
|
||||||
|
path: builds/pkg/*/gotty
|
37
.github/workflows/pre-release.yaml
vendored
37
.github/workflows/pre-release.yaml
vendored
@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
name: "pre-release"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- "master"
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
pre-release:
|
|
||||||
name: "Pre Release"
|
|
||||||
runs-on: "ubuntu-latest"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: 1.16
|
|
||||||
|
|
||||||
|
|
||||||
- name: "Build & test"
|
|
||||||
run: "make tools release-artifacts"
|
|
||||||
|
|
||||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
|
||||||
with:
|
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
automatic_release_tag: "latest"
|
|
||||||
prerelease: true
|
|
||||||
title: "Development Build"
|
|
||||||
files: |
|
|
||||||
LICENSE
|
|
||||||
builds/dist/*
|
|
30
.github/workflows/quick-build.yaml
vendored
Normal file
30
.github/workflows/quick-build.yaml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: "Quick build test"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
quick-build-test:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
|
||||||
|
- name: "Build"
|
||||||
|
run: "touch bindata/* bindata/*/* ; make"
|
||||||
|
|
||||||
|
- name: Upload linux/amd64 artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: gotty-linux-amd64
|
||||||
|
path: gotty
|
40
.github/workflows/release.yml
vendored
Normal file
40
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: "tagged-release"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tagged-release:
|
||||||
|
name: "Tagged Release"
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
|
||||||
|
- name: "Build & test"
|
||||||
|
run: "make tools test release-artifacts"
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: binaries
|
||||||
|
path: builds/pkg/*/gotty
|
||||||
|
|
||||||
|
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||||
|
with:
|
||||||
|
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
prerelease: false
|
||||||
|
draft: true
|
||||||
|
files: |
|
||||||
|
LICENSE
|
||||||
|
builds/dist/*
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
gotty
|
gotty
|
||||||
bindata/static
|
|
||||||
builds
|
builds
|
||||||
|
js/dist
|
||||||
js/node_modules/*
|
js/node_modules/*
|
||||||
|
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch GoTTY",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "debug",
|
||||||
|
"program": "${workspaceFolder}",
|
||||||
|
"args": ["-a", "127.0.0.1", "-w", "bash"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.13.1
|
FROM golang:1.16
|
||||||
|
|
||||||
WORKDIR /gotty
|
WORKDIR /gotty
|
||||||
COPY . /gotty
|
COPY . /gotty
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,7 +1,7 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2015-2017 Iwasaki Yudai
|
Copyright (c) 2015-2017 Iwasaki Yudai
|
||||||
Copyright (c) 2021 Søren L. Hansen
|
Copyright (c) 2021-2022 Søren L. Hansen
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
69
Makefile
69
Makefile
@ -2,6 +2,8 @@ OUTPUT_DIR = ./builds
|
|||||||
GIT_COMMIT = `git rev-parse HEAD | cut -c1-7`
|
GIT_COMMIT = `git rev-parse HEAD | cut -c1-7`
|
||||||
VERSION = $(shell git describe --tags)
|
VERSION = $(shell git describe --tags)
|
||||||
BUILD_OPTIONS = -ldflags "-X main.Version=$(VERSION)"
|
BUILD_OPTIONS = -ldflags "-X main.Version=$(VERSION)"
|
||||||
|
WEBPACK_MODE = production
|
||||||
|
export CGO_ENABLED=0
|
||||||
|
|
||||||
gotty: main.go assets server/*.go webtty/*.go backend/*.go Makefile
|
gotty: main.go assets server/*.go webtty/*.go backend/*.go Makefile
|
||||||
go build ${BUILD_OPTIONS}
|
go build ${BUILD_OPTIONS}
|
||||||
@ -9,52 +11,39 @@ gotty: main.go assets server/*.go webtty/*.go backend/*.go Makefile
|
|||||||
docker:
|
docker:
|
||||||
docker build . -t gotty-bash:$(VERSION)
|
docker build . -t gotty-bash:$(VERSION)
|
||||||
|
|
||||||
.PHONY: assets
|
.PHONY: all docker assets
|
||||||
assets: bindata/static/js/gotty.js bindata/static/index.html bindata/static/icon.svg bindata/static/favicon.ico bindata/static/css/index.css bindata/static/css/xterm.css bindata/static/css/xterm_customize.css bindata/static/manifest.json bindata/static/icon_192.png
|
assets: bindata/static/js/gotty.js.map \
|
||||||
|
bindata/static/js/gotty.js \
|
||||||
|
bindata/static/index.html \
|
||||||
|
bindata/static/icon.svg \
|
||||||
|
bindata/static/favicon.ico \
|
||||||
|
bindata/static/css/index.css \
|
||||||
|
bindata/static/css/xterm.css \
|
||||||
|
bindata/static/css/xterm_customize.css \
|
||||||
|
bindata/static/manifest.json \
|
||||||
|
bindata/static/icon_192.png
|
||||||
|
|
||||||
.PHONY: all
|
all: gotty
|
||||||
all: gotty docker
|
|
||||||
|
|
||||||
bindata/static:
|
bindata/static bindata/static/css bindata/static/js:
|
||||||
mkdir -p bindata/static
|
mkdir -p $@
|
||||||
|
|
||||||
bindata/static/icon.svg: bindata/static resources/icon.svg
|
bindata/static/%: resources/% | bindata/static/css
|
||||||
cp resources/icon.svg bindata/static/icon.svg
|
cp "$<" "$@"
|
||||||
|
|
||||||
bindata/static/index.html: bindata/static resources/index.html
|
bindata/static/css/%.css: resources/%.css | bindata/static
|
||||||
cp resources/index.html bindata/static/index.html
|
cp "$<" "$@"
|
||||||
|
|
||||||
bindata/static/manifest.json: bindata/static resources/manifest.json
|
bindata/static/css/xterm.css: js/node_modules/xterm/css/xterm.css | bindata/static
|
||||||
cp resources/manifest.json bindata/static/manifest.json
|
cp "$<" "$@"
|
||||||
|
|
||||||
bindata/static/favicon.ico: bindata/static resources/favicon.ico
|
|
||||||
cp resources/favicon.ico bindata/static/favicon.ico
|
|
||||||
|
|
||||||
bindata/static/icon_192.png: bindata/static resources/icon_192.png
|
|
||||||
cp resources/icon_192.png bindata/static/icon_192.png
|
|
||||||
|
|
||||||
bindata/static/js: bindata/static
|
|
||||||
mkdir -p bindata/static/js
|
|
||||||
|
|
||||||
bindata/static/css: bindata/static
|
|
||||||
mkdir -p bindata/static/css
|
|
||||||
|
|
||||||
bindata/static/css/index.css: bindata/static/css resources/index.css
|
|
||||||
cp resources/index.css bindata/static/css/index.css
|
|
||||||
|
|
||||||
bindata/static/css/xterm_customize.css: bindata/static/css resources/xterm_customize.css
|
|
||||||
cp resources/xterm_customize.css bindata/static/css/xterm_customize.css
|
|
||||||
|
|
||||||
bindata/static/css/xterm.css: bindata/static/css js/node_modules/xterm/css/xterm.css
|
|
||||||
cp js/node_modules/xterm/css/xterm.css bindata/static/css/xterm.css
|
|
||||||
|
|
||||||
js/node_modules/xterm/dist/xterm.css:
|
js/node_modules/xterm/dist/xterm.css:
|
||||||
cd js && \
|
cd js && \
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
bindata/static/js/gotty.js: js/src/* js/node_modules/webpack
|
bindata/static/js/gotty.js.map bindata/static/js/gotty.js: js/src/* | js/node_modules/webpack
|
||||||
cd js && \
|
cd js && \
|
||||||
npx webpack
|
npx webpack --mode=$(WEBPACK_MODE)
|
||||||
|
|
||||||
js/node_modules/webpack:
|
js/node_modules/webpack:
|
||||||
cd js && \
|
cd js && \
|
||||||
@ -66,8 +55,8 @@ README-options:
|
|||||||
rm options.txt.tmp
|
rm options.txt.tmp
|
||||||
|
|
||||||
tools:
|
tools:
|
||||||
go get github.com/mitchellh/gox
|
go install github.com/mitchellh/gox@latest
|
||||||
go get github.com/tcnksm/ghr
|
go install github.com/tcnksm/ghr@latest
|
||||||
|
|
||||||
test:
|
test:
|
||||||
if [ `go fmt $(go list ./... | grep -v /vendor/) | wc -l` -gt 0 ]; then echo "go fmt error"; exit 1; fi
|
if [ `go fmt $(go list ./... | grep -v /vendor/) | wc -l` -gt 0 ]; then echo "go fmt error"; exit 1; fi
|
||||||
@ -89,4 +78,8 @@ release:
|
|||||||
ghr -draft ${VERSION} ${OUTPUT_DIR}/dist # -c ${GIT_COMMIT} --delete --prerelease -u sorenisanerd -r gotty ${VERSION}
|
ghr -draft ${VERSION} ${OUTPUT_DIR}/dist # -c ${GIT_COMMIT} --delete --prerelease -u sorenisanerd -r gotty ${VERSION}
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -fr gotty builds js/dist bindata/static
|
rm -fr gotty builds js/dist bindata/static js/node_modules
|
||||||
|
|
||||||
|
addcontributors:
|
||||||
|
gh issue list -s all -L 1000 --json author -t "$$(echo '{{ range . }}{{ .author.login }}\n{{ end }}')" | sort | uniq | xargs -Ifoo all-contributors add foo bug --commitTemplate '<%= (newContributor ? "Add" : "Update") %> @<%= username %> as a contributor'
|
||||||
|
gh pr list -s all -L 1000 --json author -t "$$(echo '{{ range . }}{{ .author.login }}\n{{ end }}')" | sort | uniq | xargs -Ifoo all-contributors add foo code --commitTemplate '<%= (newContributor ? "Add" : "Update") %> @<%= username %> as a contributor'
|
74
NEWS.md
74
NEWS.md
@ -1,4 +1,76 @@
|
|||||||
# GoTTY releases
|
## 1.5.0 (2022-09-01)
|
||||||
|
|
||||||
|
* Add @ahmetb as a contributor ([276767a](https://github.com/sorenisanerd/gotty/commit/276767a))
|
||||||
|
* Add @CoconutMacaroon as a contributor ([74c1318](https://github.com/sorenisanerd/gotty/commit/74c1318))
|
||||||
|
* Add @DannyBen as a contributor ([fcfa161](https://github.com/sorenisanerd/gotty/commit/fcfa161))
|
||||||
|
* Add @dmartin as a contributor ([6b2ae89](https://github.com/sorenisanerd/gotty/commit/6b2ae89))
|
||||||
|
* Add @Fan-SJ as a contributor ([c2428c8](https://github.com/sorenisanerd/gotty/commit/c2428c8))
|
||||||
|
* Add @flechaig as a contributor ([2c4004d](https://github.com/sorenisanerd/gotty/commit/2c4004d))
|
||||||
|
* Add @George-NG as a contributor ([270ae45](https://github.com/sorenisanerd/gotty/commit/270ae45))
|
||||||
|
* Add @ghthor as a contributor ([1a6bccd](https://github.com/sorenisanerd/gotty/commit/1a6bccd))
|
||||||
|
* Add @jpillora as a contributor ([f52fbd7](https://github.com/sorenisanerd/gotty/commit/f52fbd7))
|
||||||
|
* Add @kaisawind as a contributor ([accff3a](https://github.com/sorenisanerd/gotty/commit/accff3a))
|
||||||
|
* Add @linyinli as a contributor ([8014af3](https://github.com/sorenisanerd/gotty/commit/8014af3))
|
||||||
|
* Add @LucaMarconato as a contributor ([3bd9836](https://github.com/sorenisanerd/gotty/commit/3bd9836))
|
||||||
|
* Add @masterkain as a contributor ([369e2f7](https://github.com/sorenisanerd/gotty/commit/369e2f7))
|
||||||
|
* Add @Nexuist as a contributor ([ca691bc](https://github.com/sorenisanerd/gotty/commit/ca691bc))
|
||||||
|
* Add @qigj as a contributor ([5a052e7](https://github.com/sorenisanerd/gotty/commit/5a052e7))
|
||||||
|
* Add @shuaiyy as a contributor ([95e1bbd](https://github.com/sorenisanerd/gotty/commit/95e1bbd))
|
||||||
|
* Add @v20z as a contributor ([d594bef](https://github.com/sorenisanerd/gotty/commit/d594bef))
|
||||||
|
* Add @xgdgsc as a contributor ([c197990](https://github.com/sorenisanerd/gotty/commit/c197990))
|
||||||
|
* Add @Yann-Qiu as a contributor ([7b994ec](https://github.com/sorenisanerd/gotty/commit/7b994ec))
|
||||||
|
* Update @flechaig as a contributor ([9877e9c](https://github.com/sorenisanerd/gotty/commit/9877e9c))
|
||||||
|
* Update @flechaig as a contributor ([e03ea9c](https://github.com/sorenisanerd/gotty/commit/e03ea9c))
|
||||||
|
* Update @prusnak as a contributor ([3c45888](https://github.com/sorenisanerd/gotty/commit/3c45888))
|
||||||
|
* Update @prusnak as a contributor ([8d7f5fc](https://github.com/sorenisanerd/gotty/commit/8d7f5fc))
|
||||||
|
* Update @sorenisanerd as a contributor ([34f516b](https://github.com/sorenisanerd/gotty/commit/34f516b))
|
||||||
|
* Update @sorenisanerd as a contributor ([89a04d1](https://github.com/sorenisanerd/gotty/commit/89a04d1))
|
||||||
|
* Update @xgdgsc as a contributor ([b3c5d03](https://github.com/sorenisanerd/gotty/commit/b3c5d03))
|
||||||
|
* Update @xgdgsc as a contributor ([09f7e95](https://github.com/sorenisanerd/gotty/commit/09f7e95))
|
||||||
|
* Add make target to add contributors ([1bbfd5e](https://github.com/sorenisanerd/gotty/commit/1bbfd5e))
|
||||||
|
* Add missing import "strings" ([d0e3ffb](https://github.com/sorenisanerd/gotty/commit/d0e3ffb))
|
||||||
|
* add slash ([7706bf2](https://github.com/sorenisanerd/gotty/commit/7706bf2))
|
||||||
|
* Always disable CGO ([7a96f37](https://github.com/sorenisanerd/gotty/commit/7a96f37)), closes [#39](https://github.com/sorenisanerd/gotty/issues/39)
|
||||||
|
* Bump terser from 5.12.1 to 5.14.2 in /js ([3ae13e0](https://github.com/sorenisanerd/gotty/commit/3ae13e0))
|
||||||
|
* Create a release when a new tag is pushed ([d8fe975](https://github.com/sorenisanerd/gotty/commit/d8fe975))
|
||||||
|
* Ensure --quiet flag is honored ([7d431a7](https://github.com/sorenisanerd/gotty/commit/7d431a7)), closes [#45](https://github.com/sorenisanerd/gotty/issues/45)
|
||||||
|
* Refresh dependencies, drop node-sass ([94e5873](https://github.com/sorenisanerd/gotty/commit/94e5873))
|
||||||
|
* Run tests on pull requests ([316d5ff](https://github.com/sorenisanerd/gotty/commit/316d5ff))
|
||||||
|
|
||||||
|
|
||||||
|
## 1.4.0 (2022-05-30)
|
||||||
|
|
||||||
|
* Add @hardliner66 as a contributor ([1ca998e](https://github.com/sorenisanerd/gotty/commit/1ca998e))
|
||||||
|
* Add @jkandasa as a contributor ([cd23910](https://github.com/sorenisanerd/gotty/commit/cd23910))
|
||||||
|
* Add backend tests ([603c650](https://github.com/sorenisanerd/gotty/commit/603c650))
|
||||||
|
* Add generated data to git ([a9fbc07](https://github.com/sorenisanerd/gotty/commit/a9fbc07))
|
||||||
|
* add quiet flag to disable logging ([4109b11](https://github.com/sorenisanerd/gotty/commit/4109b11))
|
||||||
|
* Add references to @yudai ([bffd821](https://github.com/sorenisanerd/gotty/commit/bffd821)), closes [#8](https://github.com/sorenisanerd/gotty/issues/8)
|
||||||
|
* Add rule to build gotty.js.map ([82c3acf](https://github.com/sorenisanerd/gotty/commit/82c3acf))
|
||||||
|
* Apply font size and family in xterm ([f157dbe](https://github.com/sorenisanerd/gotty/commit/f157dbe)), closes [#21](https://github.com/sorenisanerd/gotty/issues/21)
|
||||||
|
* Avoid HTTP 401 error on manifest.json due to CORS ([817b5c8](https://github.com/sorenisanerd/gotty/commit/817b5c8))
|
||||||
|
* Bump browserslist from 4.16.4 to 4.16.6 in /js ([8deba62](https://github.com/sorenisanerd/gotty/commit/8deba62))
|
||||||
|
* Disable arg passing by default ([5c8eb10](https://github.com/sorenisanerd/gotty/commit/5c8eb10)), closes [#17](https://github.com/sorenisanerd/gotty/issues/17)
|
||||||
|
* Do not include ALL of bootstrap ([b63ea16](https://github.com/sorenisanerd/gotty/commit/b63ea16))
|
||||||
|
* Ensure defaults for booleans is set correctly ([28f8e61](https://github.com/sorenisanerd/gotty/commit/28f8e61)), closes [#16](https://github.com/sorenisanerd/gotty/issues/16)
|
||||||
|
* Fix existing tests ([d674aa1](https://github.com/sorenisanerd/gotty/commit/d674aa1)), closes [#13](https://github.com/sorenisanerd/gotty/issues/13)
|
||||||
|
* Fix warnings from Markdown linter ([aa86a34](https://github.com/sorenisanerd/gotty/commit/aa86a34))
|
||||||
|
* go fmt ([dcb153c](https://github.com/sorenisanerd/gotty/commit/dcb153c))
|
||||||
|
* Improve webtty test coverage ([f61763f](https://github.com/sorenisanerd/gotty/commit/f61763f))
|
||||||
|
* Make client request base64 encoding ([dd3603c](https://github.com/sorenisanerd/gotty/commit/dd3603c))
|
||||||
|
* Make sure we read the full message ([1eed97f](https://github.com/sorenisanerd/gotty/commit/1eed97f))
|
||||||
|
* Publish artifacts on push to master ([6c62ab7](https://github.com/sorenisanerd/gotty/commit/6c62ab7))
|
||||||
|
* Remove hterm ([163fd05](https://github.com/sorenisanerd/gotty/commit/163fd05))
|
||||||
|
* Run tests on push ([55674f1](https://github.com/sorenisanerd/gotty/commit/55674f1))
|
||||||
|
* Run tests on push to all branches ([679a324](https://github.com/sorenisanerd/gotty/commit/679a324))
|
||||||
|
* update go version in Dockerfile ([fd2fb99](https://github.com/sorenisanerd/gotty/commit/fd2fb99))
|
||||||
|
* Update js dependencies ([26fc412](https://github.com/sorenisanerd/gotty/commit/26fc412))
|
||||||
|
* Update xterm.js and other js libs ([81afdc7](https://github.com/sorenisanerd/gotty/commit/81afdc7)), closes [#18](https://github.com/sorenisanerd/gotty/issues/18)
|
||||||
|
* Use bootstrap components for up- and downloads ([7f05f2f](https://github.com/sorenisanerd/gotty/commit/7f05f2f))
|
||||||
|
* Use Go's built-in embed mechanism ([f66f0d0](https://github.com/sorenisanerd/gotty/commit/f66f0d0)), closes [#7](https://github.com/sorenisanerd/gotty/issues/7)
|
||||||
|
* feat(zmodem): Allow file uploads/downloads ([782991c](https://github.com/sorenisanerd/gotty/commit/782991c))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## v1.3.0
|
## v1.3.0
|
||||||
|
|
||||||
|
44
README.md
44
README.md
@ -1,6 +1,6 @@
|
|||||||
# ![](https://raw.githubusercontent.com/sorenisanerd/gotty/master/resources/favicon.ico) GoTTY - Share your terminal as a web application
|
# ![](https://raw.githubusercontent.com/sorenisanerd/gotty/master/resources/favicon.ico) GoTTY - Share your terminal as a web application
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
[![All Contributors](https://img.shields.io/badge/all_contributors-36-orange.svg?style=flat-square)](#contributors-)
|
[![All Contributors](https://img.shields.io/badge/all_contributors-57-orange.svg?style=flat-square)](#contributors-)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
[![GitHub release](http://img.shields.io/github/release/sorenisanerd/gotty.svg?style=flat-square)][release]
|
[![GitHub release](http://img.shields.io/github/release/sorenisanerd/gotty.svg?style=flat-square)][release]
|
||||||
@ -73,7 +73,6 @@ By default, GoTTY starts a web server at port 8080. Open the URL on your web bro
|
|||||||
--width value Static width of the screen, 0(default) means dynamically resize (default: 0) [$GOTTY_WIDTH]
|
--width value Static width of the screen, 0(default) means dynamically resize (default: 0) [$GOTTY_WIDTH]
|
||||||
--height value Static height of the screen, 0(default) means dynamically resize (default: 0) [$GOTTY_HEIGHT]
|
--height value Static height of the screen, 0(default) means dynamically resize (default: 0) [$GOTTY_HEIGHT]
|
||||||
--ws-origin value A regular expression that matches origin URLs to be accepted by WebSocket. No cross origin requests are acceptable by default [$GOTTY_WS_ORIGIN]
|
--ws-origin value A regular expression that matches origin URLs to be accepted by WebSocket. No cross origin requests are acceptable by default [$GOTTY_WS_ORIGIN]
|
||||||
--term value Terminal name to use on the browser, one of xterm or hterm. (default: "xterm") [$GOTTY_TERM]
|
|
||||||
--enable-webgl Enable WebGL renderer (default: true) [$GOTTY_ENABLE_WEBGL]
|
--enable-webgl Enable WebGL renderer (default: true) [$GOTTY_ENABLE_WEBGL]
|
||||||
--close-signal value Signal sent to the command process when gotty close it (default: SIGHUP) (default: 1) [$GOTTY_CLOSE_SIGNAL]
|
--close-signal value Signal sent to the command process when gotty close it (default: SIGHUP) (default: 1) [$GOTTY_CLOSE_SIGNAL]
|
||||||
--close-timeout value Time in seconds to force kill process after client is disconnected (default: -1) (default: -1) [$GOTTY_CLOSE_TIMEOUT]
|
--close-timeout value Time in seconds to force kill process after client is disconnected (default: -1) (default: -1) [$GOTTY_CLOSE_TIMEOUT]
|
||||||
@ -82,7 +81,7 @@ By default, GoTTY starts a web server at port 8080. Open the URL on your web bro
|
|||||||
--version, -v print the version (default: false)
|
--version, -v print the version (default: false)
|
||||||
```
|
```
|
||||||
### Config File
|
### Config File
|
||||||
You can customize default options and your terminal (hterm) by providing a config file to the `gotty` command. GoTTY loads a profile file at `~/.gotty` by default when it exists.
|
You can customize default options and your terminal by providing a config file to the `gotty` command. GoTTY loads a profile file at `~/.gotty` by default when it exists.
|
||||||
|
|
||||||
```
|
```
|
||||||
// Listen at port 9000 by default
|
// Listen at port 9000 by default
|
||||||
@ -91,12 +90,6 @@ port = "9000"
|
|||||||
// Enable TSL/SSL by default
|
// Enable TSL/SSL by default
|
||||||
enable_tls = true
|
enable_tls = true
|
||||||
|
|
||||||
// hterm preferences
|
|
||||||
// Smaller font and a little bit bluer background color
|
|
||||||
preferences {
|
|
||||||
font_size = 5
|
|
||||||
background_color = "rgb(16, 16, 32)"
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [`.gotty`](https://github.com/sorenisanerd/gotty/blob/master/.gotty) file in this repository for the list of configuration options.
|
See the [`.gotty`](https://github.com/sorenisanerd/gotty/blob/master/.gotty) file in this repository for the list of configuration options.
|
||||||
@ -164,7 +157,7 @@ To build the frontend part (JS files and other static files), you need `npm`.
|
|||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
GoTTY uses [xterm.js](https://xtermjs.org/) and [hterm](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-hterm) to run a JavaScript based terminal on web browsers. GoTTY itself provides a websocket server that simply relays output from the TTY to clients and receives input from clients and forwards it to the TTY. This hterm + websocket idea is inspired by [Wetty](https://github.com/krishnasrinivas/wetty).
|
GoTTY uses [xterm.js](https://xtermjs.org/) to run a JavaScript based terminal on web browsers. GoTTY itself provides a websocket server that simply relays output from the TTY to clients and receives input from clients and forwards it to the TTY. This xterm + websocket idea is inspired by [Wetty](https://github.com/krishnasrinivas/wetty).
|
||||||
|
|
||||||
## Alternatives
|
## Alternatives
|
||||||
|
|
||||||
@ -198,7 +191,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://yudai.arielworks.com/"><img src="https://avatars.githubusercontent.com/u/33192?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Iwasaki Yudai</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=yudai" title="Code">💻</a></td>
|
<td align="center"><a href="https://yudai.arielworks.com/"><img src="https://avatars.githubusercontent.com/u/33192?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Iwasaki Yudai</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=yudai" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://linux2go.dk/"><img src="https://avatars.githubusercontent.com/u/160090?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Soren L. Hansen</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=sorenisanerd" title="Code">💻</a></td>
|
<td align="center"><a href="http://linux2go.dk/"><img src="https://avatars.githubusercontent.com/u/160090?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Soren L. Hansen</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Asorenisanerd" title="Bug reports">🐛</a></td>
|
||||||
<td align="center"><a href="https://github.com/uovobw"><img src="https://avatars.githubusercontent.com/u/1194751?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrea Lusuardi</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=uovobw" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/uovobw"><img src="https://avatars.githubusercontent.com/u/1194751?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrea Lusuardi</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=uovobw" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/moul"><img src="https://avatars.githubusercontent.com/u/94029?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Manfred Touron</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=moul" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/moul"><img src="https://avatars.githubusercontent.com/u/94029?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Manfred Touron</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=moul" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/svanellewee"><img src="https://avatars.githubusercontent.com/u/1567439?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stephan</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=svanellewee" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/svanellewee"><img src="https://avatars.githubusercontent.com/u/1567439?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stephan</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=svanellewee" title="Code">💻</a></td>
|
||||||
@ -239,10 +232,37 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<td align="center"><a href="https://www.shingt.com/"><img src="https://avatars.githubusercontent.com/u/1391330?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shinichi Goto</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=shingt" title="Code">💻</a></td>
|
<td align="center"><a href="https://www.shingt.com/"><img src="https://avatars.githubusercontent.com/u/1391330?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shinichi Goto</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=shingt" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://twitter.com/_yogeshsingh"><img src="https://avatars.githubusercontent.com/u/8512357?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ygit</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=ygit" title="Code">💻</a></td>
|
<td align="center"><a href="https://twitter.com/_yogeshsingh"><img src="https://avatars.githubusercontent.com/u/8512357?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ygit</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=ygit" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://forum.cachem.fr/viewforum.php?f=21"><img src="https://avatars.githubusercontent.com/u/3392684?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stéphane</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Anephaste" title="Bug reports">🐛</a></td>
|
<td align="center"><a href="http://forum.cachem.fr/viewforum.php?f=21"><img src="https://avatars.githubusercontent.com/u/3392684?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stéphane</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Anephaste" title="Bug reports">🐛</a></td>
|
||||||
<td align="center"><a href="https://rusnak.io/"><img src="https://avatars.githubusercontent.com/u/42201?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pavol Rusnak</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=prusnak" title="Code">💻</a></td>
|
<td align="center"><a href="https://rusnak.io/"><img src="https://avatars.githubusercontent.com/u/42201?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pavol Rusnak</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Aprusnak" title="Bug reports">🐛</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://github.com/devanlai"><img src="https://avatars.githubusercontent.com/u/1348448?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Devan Lai</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=devanlai" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/devanlai"><img src="https://avatars.githubusercontent.com/u/1348448?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Devan Lai</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=devanlai" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/jkandasa"><img src="https://avatars.githubusercontent.com/u/1004403?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeeva Kandasamy</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=jkandasa" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://twitch.tv/iamhardliner"><img src="https://avatars.githubusercontent.com/u/2937272?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Steve Biedermann</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/commits?author=hardliner66" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/xgdgsc"><img src="https://avatars.githubusercontent.com/u/1189869?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xgdgsc</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Axgdgsc" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/flechaig"><img src="https://avatars.githubusercontent.com/u/10887132?v=4?s=100" width="100px;" alt=""/><br /><sub><b>flechaig</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Aflechaig" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Fan-SJ"><img src="https://avatars.githubusercontent.com/u/49977708?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fan-SJ</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3AFan-SJ" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/dmartin"><img src="https://avatars.githubusercontent.com/u/1657652?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dustin Martin</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Admartin" title="Bug reports">🐛</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://ahmet.dev/"><img src="https://avatars.githubusercontent.com/u/159209?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ahmet Alp Balkan</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Aahmetb" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/CoconutMacaroon"><img src="https://avatars.githubusercontent.com/u/45187468?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CoconutMacaroon</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3ACoconutMacaroon" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.dannyben.com/"><img src="https://avatars.githubusercontent.com/u/2405099?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Danny Ben Shitrit</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3ADannyBen" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/George-NG"><img src="https://avatars.githubusercontent.com/u/28577165?v=4?s=100" width="100px;" alt=""/><br /><sub><b>George-NG</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3AGeorge-NG" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/ghthor"><img src="https://avatars.githubusercontent.com/u/160298?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Will Owens</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Aghthor" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://jpillora.com/"><img src="https://avatars.githubusercontent.com/u/633843?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jaime Pillora</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Ajpillora" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/kaisawind"><img src="https://avatars.githubusercontent.com/u/4010613?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kaisawind</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Akaisawind" title="Bug reports">🐛</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/linyinli"><img src="https://avatars.githubusercontent.com/u/42955482?v=4?s=100" width="100px;" alt=""/><br /><sub><b>linyinli</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Alinyinli" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/LucaMarconato"><img src="https://avatars.githubusercontent.com/u/2664412?v=4?s=100" width="100px;" alt=""/><br /><sub><b>LucaMarconato</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3ALucaMarconato" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://audiobox.fm/"><img src="https://avatars.githubusercontent.com/u/12844?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kain</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Amasterkain" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="http://duro.me/"><img src="https://avatars.githubusercontent.com/u/1498061?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andi Andreas</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3ANexuist" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/qigj"><img src="https://avatars.githubusercontent.com/u/56585735?v=4?s=100" width="100px;" alt=""/><br /><sub><b>qigj</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Aqigj" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/shuaiyy"><img src="https://avatars.githubusercontent.com/u/19821321?v=4?s=100" width="100px;" alt=""/><br /><sub><b>shuaiyy</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Ashuaiyy" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/v20z"><img src="https://avatars.githubusercontent.com/u/2884824?v=4?s=100" width="100px;" alt=""/><br /><sub><b>v20z</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3Av20z" title="Bug reports">🐛</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/Yann-Qiu"><img src="https://avatars.githubusercontent.com/u/56961747?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yanfeng Qiu</b></sub></a><br /><a href="https://github.com/sorenisanerd/gotty/issues?q=author%3AYann-Qiu" title="Bug reports">🐛</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
13
bindata/static/css/index.css
Normal file
13
bindata/static/css/index.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
html, body, #terminal {
|
||||||
|
background: black;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0%;
|
||||||
|
margin: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress .progress-bar {
|
||||||
|
transition: unset;
|
||||||
|
transition-duration: 0.1s;
|
||||||
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
190
bindata/static/css/xterm.css
Normal file
190
bindata/static/css/xterm.css
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
||||||
|
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||||
|
* https://github.com/chjj/term.js
|
||||||
|
* @license MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* Originally forked from (with the author's permission):
|
||||||
|
* Fabrice Bellard's javascript vt100 for jslinux:
|
||||||
|
* http://bellard.org/jslinux/
|
||||||
|
* Copyright (c) 2011 Fabrice Bellard
|
||||||
|
* The original design remains. The terminal itself
|
||||||
|
* has been extended to include xterm CSI codes, among
|
||||||
|
* other features.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default styles for xterm.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
.xterm {
|
||||||
|
cursor: text;
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.focus,
|
||||||
|
.xterm:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-helpers {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
/**
|
||||||
|
* The z-index of the helpers must be higher than the canvases in order for
|
||||||
|
* IMEs to appear on top.
|
||||||
|
*/
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-helper-textarea {
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
left: -9999em;
|
||||||
|
top: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
z-index: -5;
|
||||||
|
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .composition-view {
|
||||||
|
/* TODO: Composition position got messed up somewhere */
|
||||||
|
background: #000;
|
||||||
|
color: #FFF;
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .composition-view.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-viewport {
|
||||||
|
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
||||||
|
background-color: #000;
|
||||||
|
overflow-y: scroll;
|
||||||
|
cursor: default;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-screen {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-screen canvas {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-scroll-area {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-char-measure-element {
|
||||||
|
display: inline-block;
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -9999em;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.enable-mouse-events {
|
||||||
|
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.xterm-cursor-pointer,
|
||||||
|
.xterm .xterm-cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm.column-select.focus {
|
||||||
|
/* Column selection mode */
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .xterm-accessibility,
|
||||||
|
.xterm .xterm-message {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm .live-region {
|
||||||
|
position: absolute;
|
||||||
|
left: -9999px;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-dim {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-underline {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-strikethrough {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-screen .xterm-decoration-container .xterm-decoration {
|
||||||
|
z-index: 6;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-decoration-overview-ruler {
|
||||||
|
z-index: 7;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-decoration-top {
|
||||||
|
z-index: 2;
|
||||||
|
position: relative;
|
||||||
|
}
|
19
bindata/static/css/xterm_customize.css
Normal file
19
bindata/static/css/xterm_customize.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.terminal {
|
||||||
|
font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, Menlo, Terminal, monospace, "Apple Symbols";
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-overlay {
|
||||||
|
font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, Menlo, Terminal, monospace, "Apple Symbols";
|
||||||
|
border-radius: 15px;
|
||||||
|
font-size: xx-large;
|
||||||
|
color: black;
|
||||||
|
background: white;
|
||||||
|
opacity: 0.75;
|
||||||
|
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
user-select: none;
|
||||||
|
transition: opacity 180ms ease-in;
|
||||||
|
}
|
BIN
bindata/static/favicon.ico
Normal file
BIN
bindata/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
66
bindata/static/icon.svg
Normal file
66
bindata/static/icon.svg
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 708.4149 708.41411"
|
||||||
|
height="708.41412"
|
||||||
|
width="708.41492"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"><metadata
|
||||||
|
id="metadata8"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs6"><clipPath
|
||||||
|
id="clipPath18"
|
||||||
|
clipPathUnits="userSpaceOnUse"><path
|
||||||
|
id="path16"
|
||||||
|
d="M 0,2000 H 2000 V 0 H 0 Z" /></clipPath></defs><g
|
||||||
|
transform="matrix(1.3333333,0,0,-1.3333333,-979.12558,1687.5405)"
|
||||||
|
id="g10"><g
|
||||||
|
id="g12"><g
|
||||||
|
clip-path="url(#clipPath18)"
|
||||||
|
id="g14"><g
|
||||||
|
transform="translate(772.1414,1265.6554)"
|
||||||
|
id="g20"><path
|
||||||
|
id="path22"
|
||||||
|
style="fill:#de3c28;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 c -20.875,0 -37.797,-16.922 -37.797,-37.797 v -126.348 -25.917 -212.342 L 345.882,0 Z" /></g><g
|
||||||
|
transform="translate(956.8504,734.3448)"
|
||||||
|
id="g24"><path
|
||||||
|
id="path26"
|
||||||
|
style="fill:#af1419;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 h 260.209 c 26.839,0 48.596,21.757 48.596,48.596 v 275.281 z" /></g><g
|
||||||
|
transform="translate(1265.6552,1075.5929)"
|
||||||
|
id="g28"><path
|
||||||
|
id="path30"
|
||||||
|
style="fill:#db0918;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 v 25.918 126.348 c 0,20.875 -16.922,37.796 -37.796,37.796 h -109.836 l -383.679,-402.404 v -80.31 c 0,-26.839 21.757,-48.596 48.596,-48.596 h 173.91 L 0,-17.371 Z" /></g><g
|
||||||
|
transform="translate(1169.2147,909.6954)"
|
||||||
|
id="g32"><path
|
||||||
|
id="path34"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 h -138.227 c -9.129,0 -16.528,7.4 -16.528,16.529 0,9.128 7.399,16.529 16.528,16.529 H 0 c 9.129,0 16.528,-7.401 16.528,-16.529 C 16.528,7.4 9.129,0 0,0" /></g><g
|
||||||
|
transform="translate(898.8318,1033.5314)"
|
||||||
|
id="g36"><path
|
||||||
|
id="path38"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 c 2.86,-0.72 5.65,-1.45 8.37,-2.2 -0.63,-8.56 -7.76,-15.3 -16.479,-15.3 -9.13,0 -16.531,7.4 -16.531,16.53 v 8.54 c 1.861,-0.83 3.72,-1.549 5.601,-2.15 C -15.199,4.18 -8.85,2.37 0,0" /></g><g
|
||||||
|
transform="translate(907.2527,1150.7814)"
|
||||||
|
id="g40"><path
|
||||||
|
id="path42"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 v -65.81 c -4.45,1.59 -9.98,2.38 -16.601,2.38 -6.21,0 -11.7,-0.88 -16.46,-2.64 V 0 c 0,9.13 7.401,16.53 16.531,16.53 C -7.4,16.53 0,9.13 0,0" /></g><g
|
||||||
|
transform="translate(874.1922,868.8272)"
|
||||||
|
id="g44"><path
|
||||||
|
id="path46"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 v 65.811 c 4.45,-1.59 9.98,-2.38 16.601,-2.38 6.21,0 11.699,0.88 16.46,2.639 V 0 C 33.061,-9.13 25.66,-16.529 16.53,-16.529 7.4,-16.529 0,-9.13 0,0" /></g><g
|
||||||
|
transform="translate(966.6326,994.1915)"
|
||||||
|
id="g48"><path
|
||||||
|
id="path50"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
d="m 0,0 c -3.53,7.28 -8.45,13.3 -14.75,18.06 -6.301,4.76 -13.95,8.8 -22.94,12.14 -6.661,2.47 -13.911,4.78 -21.741,6.94 -2.719,0.75 -5.509,1.48 -8.37,2.2 -8.849,2.37 -15.199,4.18 -19.039,5.42 -1.881,0.6 -3.74,1.32 -5.6,2.15 -1.951,0.87 -3.88,1.87 -5.8,2.99 -3.76,2.19 -6.71,4.81 -8.861,7.85 -2.139,3.05 -3.219,6.67 -3.219,10.86 0,6.75 3.16,12.54 9.46,17.34 2.49,1.9 5.299,3.43 8.42,4.57 4.759,1.76 10.25,2.64 16.46,2.64 6.62,0 12.15,-0.79 16.6,-2.38 3.01,-1.05 5.52,-2.48 7.54,-4.26 5.01,-4.42 9.3,-10.59 12.88,-18.49 2.77,-5.52 5.34,-9.49 7.71,-11.92 2.37,-2.43 5.84,-3.64 10.399,-3.64 5.011,0 9.191,2.02 12.54,6.07 3.351,4.04 5.031,8.59 5.031,13.63 0,5.52 -1.34,11.19 -4.021,17 -2.689,5.8 -6.939,11.35 -12.75,16.63 -5.809,5.28 -13.119,9.52 -21.929,12.71 -5.23,1.89 -11.031,3.22 -17.4,3.99 -4.37,0.53 -9.011,0.79 -13.921,0.79 -6.779,0 -13.159,-0.48 -19.139,-1.45 -7.361,-1.19 -14.121,-3.11 -20.29,-5.76 -11.18,-4.81 -19.72,-11.69 -25.621,-20.63 -5.899,-8.95 -8.849,-19.18 -8.849,-30.7 0,-12.09 2.79,-22.2 8.38,-30.34 5.59,-8.14 13.15,-14.56 22.67,-19.28 9.52,-4.71 21.349,-8.83 35.48,-12.35 10.55,-2.57 18.98,-4.99 25.279,-7.28 6.301,-2.28 11.441,-5.59 15.42,-9.92 3.981,-4.33 5.971,-9.97 5.971,-16.92 0,-8.76 -3.69,-16.06 -11.061,-21.92 -7.379,-5.85 -16.969,-8.78 -28.769,-8.78 -8.59,0 -15.521,1.45 -20.79,4.36 -5.28,2.9 -9.37,6.59 -12.28,11.06 -2.901,4.47 -5.611,10.04 -8.111,16.71 -2.059,5.61 -4.54,9.85 -7.449,12.7 -2.9,2.86 -6.46,4.29 -10.66,4.29 -5.181,0 -9.45,-1.84 -12.811,-5.5 -3.349,-3.66 -5.029,-8.07 -5.029,-13.21 0,-8.85 2.8,-17.96 8.39,-27.34 5.58,-9.38 12.849,-16.87 21.79,-22.49 12.61,-7.8 28.61,-11.7 48.019,-11.7 16.181,0 30.181,2.85 41.981,8.56 11.809,5.71 20.769,13.61 26.9,23.7 6.119,10.09 9.18,21.47 9.18,34.129 C 5.3,-16.21 3.54,-7.28 0,0" /></g></g></g></g></svg>
|
After Width: | Height: | Size: 5.3 KiB |
BIN
bindata/static/icon_192.png
Normal file
BIN
bindata/static/icon_192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
22
bindata/static/index.html
Normal file
22
bindata/static/index.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>{{ .title }}</title>
|
||||||
|
<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
|
||||||
|
<link rel="icon" href="favicon.ico">
|
||||||
|
<link rel="icon" href="icon.svg" type="image/svg+xml">
|
||||||
|
<link rel="stylesheet" href="./css/index.css" />
|
||||||
|
<link rel="stylesheet" href="./css/xterm.css" />
|
||||||
|
<link rel="stylesheet" href="./css/xterm_customize.css" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="terminal"></div>
|
||||||
|
<script src="./auth_token.js"></script>
|
||||||
|
<script src="./config.js"></script>
|
||||||
|
<script src="./js/gotty.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
3
bindata/static/js/gotty.js
Normal file
3
bindata/static/js/gotty.js
Normal file
File diff suppressed because one or more lines are too long
7
bindata/static/js/gotty.js.LICENSE.txt
Normal file
7
bindata/static/js/gotty.js.LICENSE.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*!
|
||||||
|
* Bootstrap v5.2.0 (https://getbootstrap.com/)
|
||||||
|
* Copyright 2011-2022 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
|
1
bindata/static/js/gotty.js.map
Normal file
1
bindata/static/js/gotty.js.map
Normal file
File diff suppressed because one or more lines are too long
650
bindata/static/js/gotty.licenses.txt
Normal file
650
bindata/static/js/gotty.licenses.txt
Normal file
@ -0,0 +1,650 @@
|
|||||||
|
@popperjs/core
|
||||||
|
MIT
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2019 Federico Zivolo
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
bootstrap
|
||||||
|
MIT
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2011-2022 Twitter, Inc.
|
||||||
|
Copyright (c) 2011-2022 The Bootstrap Authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
crc-32
|
||||||
|
Apache-2.0
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright (C) 2014-present SheetJS LLC
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
css-loader
|
||||||
|
MIT
|
||||||
|
Copyright JS Foundation and other contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
'Software'), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
jsx-runtime
|
||||||
|
MIT
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-present Jason Miller
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
preact
|
||||||
|
MIT
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-present Jason Miller
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
style-loader
|
||||||
|
MIT
|
||||||
|
Copyright JS Foundation and other contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
'Software'), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
xterm
|
||||||
|
MIT
|
||||||
|
Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
||||||
|
Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
|
||||||
|
Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
xterm-addon-fit
|
||||||
|
MIT
|
||||||
|
Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
xterm-addon-web-links
|
||||||
|
MIT
|
||||||
|
Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
xterm-addon-webgl
|
||||||
|
MIT
|
||||||
|
Copyright (c) 2018, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
zmodem.js
|
||||||
|
Apache-2.0
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
15
bindata/static/manifest.json
Normal file
15
bindata/static/manifest.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"short_name": "{{ .title }}",
|
||||||
|
"name": "{{ .title }}",
|
||||||
|
"start_url": "./",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "./icon_192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"display": "minimal-ui",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#000000"
|
||||||
|
}
|
4431
js/package-lock.json
generated
4431
js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,21 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "gotty",
|
"name": "gotty",
|
||||||
"version": "2.0.0",
|
"version": "1.4.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/bootstrap": "^5.1.9",
|
||||||
|
"compression-webpack-plugin": "^9.2.0",
|
||||||
"license-loader": "^0.5.0",
|
"license-loader": "^0.5.0",
|
||||||
|
"license-webpack-plugin": "^4.0.2",
|
||||||
|
"purgecss": "^4.1.3",
|
||||||
|
"sass": "^1.54.5",
|
||||||
|
"sass-loader": "^12.6.0",
|
||||||
|
"terser-webpack-plugin": "^5.3.1",
|
||||||
"ts-loader": "^8.3.0",
|
"ts-loader": "^8.3.0",
|
||||||
"typescript": "^4.3.2",
|
"typescript": "^4.3.2",
|
||||||
"webpack": "^5.38.1",
|
"webpack": "^5.72.0",
|
||||||
"webpack-cli": "^4.7.0"
|
"webpack-cli": "^4.7.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@popperjs/core": "^2.11.5",
|
||||||
|
"bootstrap": "^5.1.3",
|
||||||
"css-loader": "^5.2.6",
|
"css-loader": "^5.2.6",
|
||||||
"libapps": "github:yudai/libapps#release-hterm-1.70",
|
"debounce": "^1.2.1",
|
||||||
|
"preact": "^10.7.1",
|
||||||
|
"react-bootstrap": "^2.2.3",
|
||||||
"style-loader": "^2.0.0",
|
"style-loader": "^2.0.0",
|
||||||
"xterm": "^4.12.0",
|
"xterm": "^4.12.0",
|
||||||
"xterm-addon-fit": "^0.5.0",
|
"xterm-addon-fit": "^0.5.0",
|
||||||
"xterm-addon-web-links": "^0.4.0",
|
"xterm-addon-web-links": "^0.4.0",
|
||||||
"xterm-addon-webgl": "^0.10.0"
|
"xterm-addon-webgl": "^0.10.0",
|
||||||
|
"zmodem.js": "^0.1.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
63
js/src/MyModal.tsx
Normal file
63
js/src/MyModal.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { createRef, Component, ComponentChildren } from "preact";
|
||||||
|
import { Modal } from "bootstrap";
|
||||||
|
import './bootstrap.scss';
|
||||||
|
|
||||||
|
interface ModalProps {
|
||||||
|
children: ComponentChildren;
|
||||||
|
buttons?: ComponentChildren;
|
||||||
|
title: string;
|
||||||
|
dismissHandler?: (hideModal?: () => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MyModal extends Component<ModalProps, {}> {
|
||||||
|
ref = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
Modal.getOrCreateInstance(this.ref.current!).show();
|
||||||
|
this.ref.current?.addEventListener('hide.bs.modal', () => { this.props.dismissHandler && this.props.dismissHandler(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
hide(): void {
|
||||||
|
Modal.getOrCreateInstance(this.ref.current!).hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <div class="modal fade" ref={this.ref} tabIndex={-1} aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">{this.props.title}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
{this.props.buttons}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ButtonProps {
|
||||||
|
priority: "primary" | "secondary" | "danger"
|
||||||
|
clickHandler?: () => void
|
||||||
|
children: ComponentChildren
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Button(props:ButtonProps) {
|
||||||
|
let classes: string = "btn btn-" + props.priority
|
||||||
|
|
||||||
|
return <button type="button" disabled={props.disabled} class={classes} onClick={ () => { props.clickHandler ? props.clickHandler() : null; }}>{ props.children}</button>
|
||||||
|
}
|
25
js/src/bootstrap.scss
vendored
Normal file
25
js/src/bootstrap.scss
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
@import "mixins/banner";
|
||||||
|
@include bsBanner("");
|
||||||
|
|
||||||
|
|
||||||
|
// scss-docs-start import-stack
|
||||||
|
// Configuration
|
||||||
|
@import "functions";
|
||||||
|
@import "variables";
|
||||||
|
@import "maps";
|
||||||
|
@import "mixins";
|
||||||
|
@import "utilities";
|
||||||
|
|
||||||
|
// Layout & components
|
||||||
|
@import "root";
|
||||||
|
@import "reboot";
|
||||||
|
@import "type";
|
||||||
|
@import "images";
|
||||||
|
@import "containers";
|
||||||
|
@import "grid";
|
||||||
|
@import "buttons";
|
||||||
|
@import "transitions";
|
||||||
|
@import "progress";
|
||||||
|
@import "close";
|
||||||
|
@import "modal";
|
||||||
|
@import "spinners";
|
@ -1,94 +0,0 @@
|
|||||||
import * as bare from "libapps";
|
|
||||||
|
|
||||||
export class Hterm {
|
|
||||||
elem: HTMLElement;
|
|
||||||
|
|
||||||
term: bare.hterm.Terminal;
|
|
||||||
io: bare.hterm.IO;
|
|
||||||
|
|
||||||
columns: number;
|
|
||||||
rows: number;
|
|
||||||
|
|
||||||
// to "show" the current message when removeMessage() is called
|
|
||||||
message: string;
|
|
||||||
|
|
||||||
constructor(elem: HTMLElement) {
|
|
||||||
this.elem = elem;
|
|
||||||
bare.hterm.defaultStorage = new bare.lib.Storage.Memory();
|
|
||||||
this.term = new bare.hterm.Terminal();
|
|
||||||
this.term.getPrefs().set("send-encoding", "raw");
|
|
||||||
this.term.decorate(this.elem);
|
|
||||||
|
|
||||||
this.io = this.term.io.push();
|
|
||||||
this.term.installKeyboard();
|
|
||||||
};
|
|
||||||
|
|
||||||
info(): { columns: number, rows: number } {
|
|
||||||
return { columns: this.columns, rows: this.rows };
|
|
||||||
};
|
|
||||||
|
|
||||||
output(data: string) {
|
|
||||||
if (this.term.io != null) {
|
|
||||||
this.term.io.writeUTF8(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
showMessage(message: string, timeout: number) {
|
|
||||||
this.message = message;
|
|
||||||
if (timeout > 0) {
|
|
||||||
this.term.io.showOverlay(message, timeout);
|
|
||||||
} else {
|
|
||||||
this.term.io.showOverlay(message, null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
removeMessage(): void {
|
|
||||||
// there is no hideOverlay(), so show the same message with 0 sec
|
|
||||||
this.term.io.showOverlay(this.message, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
setWindowTitle(title: string) {
|
|
||||||
this.term.setWindowTitle(title);
|
|
||||||
};
|
|
||||||
|
|
||||||
setPreferences(value: object) {
|
|
||||||
Object.keys(value).forEach((key) => {
|
|
||||||
if (key != "EnableWebGL") {
|
|
||||||
this.term.getPrefs().set(key, value[key]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onInput(callback: (input: string) => void) {
|
|
||||||
this.io.onVTKeystroke = (data) => {
|
|
||||||
callback(data);
|
|
||||||
};
|
|
||||||
this.io.sendString = (data) => {
|
|
||||||
callback(data);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
onResize(callback: (colmuns: number, rows: number) => void) {
|
|
||||||
this.io.onTerminalResize = (columns: number, rows: number) => {
|
|
||||||
this.columns = columns;
|
|
||||||
this.rows = rows;
|
|
||||||
callback(columns, rows);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
deactivate(): void {
|
|
||||||
this.io.onVTKeystroke = function () { };
|
|
||||||
this.io.sendString = function () { };
|
|
||||||
this.io.onTerminalResize = function () { };
|
|
||||||
this.term.uninstallKeyboard();
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
|
||||||
this.removeMessage();
|
|
||||||
this.term.installKeyboard();
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
this.term.uninstallKeyboard();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
import { Hterm } from "./hterm";
|
import { OurXterm } from "./xterm";
|
||||||
import { Xterm } from "./xterm";
|
|
||||||
import { Terminal, WebTTY, protocols } from "./webtty";
|
import { Terminal, WebTTY, protocols } from "./webtty";
|
||||||
import { ConnectionFactory } from "./websocket";
|
import { ConnectionFactory } from "./websocket";
|
||||||
|
|
||||||
@ -11,11 +10,8 @@ const elem = document.getElementById("terminal")
|
|||||||
|
|
||||||
if (elem !== null) {
|
if (elem !== null) {
|
||||||
var term: Terminal;
|
var term: Terminal;
|
||||||
if (gotty_term == "hterm") {
|
term = new OurXterm(elem);
|
||||||
term = new Hterm(elem);
|
|
||||||
} else {
|
|
||||||
term = new Xterm(elem);
|
|
||||||
}
|
|
||||||
const httpsEnabled = window.location.protocol == "https:";
|
const httpsEnabled = window.location.protocol == "https:";
|
||||||
const url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws';
|
const url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws';
|
||||||
const args = window.location.search;
|
const args = window.location.search;
|
||||||
@ -23,6 +19,9 @@ if (elem !== null) {
|
|||||||
const wt = new WebTTY(term, factory, args, gotty_auth_token);
|
const wt = new WebTTY(term, factory, args, gotty_auth_token);
|
||||||
const closer = wt.open();
|
const closer = wt.open();
|
||||||
|
|
||||||
|
// According to https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event
|
||||||
|
// this event is unreliable and in some cases (Firefox is mentioned), having an
|
||||||
|
// "unload" event handler can have unwanted side effects. Consider commenting it out.
|
||||||
window.addEventListener("unload", () => {
|
window.addEventListener("unload", () => {
|
||||||
closer();
|
closer();
|
||||||
term.close();
|
term.close();
|
||||||
|
@ -15,7 +15,6 @@ export class ConnectionFactory {
|
|||||||
export class Connection {
|
export class Connection {
|
||||||
bare: WebSocket;
|
bare: WebSocket;
|
||||||
|
|
||||||
|
|
||||||
constructor(url: string, protocols: string[]) {
|
constructor(url: string, protocols: string[]) {
|
||||||
this.bare = new WebSocket(url, protocols);
|
this.bare = new WebSocket(url, protocols);
|
||||||
}
|
}
|
||||||
|
162
js/src/webtty.ts
162
js/src/webtty.ts
@ -1,162 +0,0 @@
|
|||||||
export const protocols = ["webtty"];
|
|
||||||
|
|
||||||
export const msgInputUnknown = '0';
|
|
||||||
export const msgInput = '1';
|
|
||||||
export const msgPing = '2';
|
|
||||||
export const msgResizeTerminal = '3';
|
|
||||||
|
|
||||||
export const msgUnknownOutput = '0';
|
|
||||||
export const msgOutput = '1';
|
|
||||||
export const msgPong = '2';
|
|
||||||
export const msgSetWindowTitle = '3';
|
|
||||||
export const msgSetPreferences = '4';
|
|
||||||
export const msgSetReconnect = '5';
|
|
||||||
export const msgSetBufferSize = '6';
|
|
||||||
|
|
||||||
|
|
||||||
export interface Terminal {
|
|
||||||
info(): { columns: number, rows: number };
|
|
||||||
output(data: string): void;
|
|
||||||
showMessage(message: string, timeout: number): void;
|
|
||||||
removeMessage(): void;
|
|
||||||
setWindowTitle(title: string): void;
|
|
||||||
setPreferences(value: object): void;
|
|
||||||
onInput(callback: (input: string) => void): void;
|
|
||||||
onResize(callback: (colmuns: number, rows: number) => void): void;
|
|
||||||
reset(): void;
|
|
||||||
deactivate(): void;
|
|
||||||
close(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Connection {
|
|
||||||
open(): void;
|
|
||||||
close(): void;
|
|
||||||
send(data: string): void;
|
|
||||||
isOpen(): boolean;
|
|
||||||
onOpen(callback: () => void): void;
|
|
||||||
onReceive(callback: (data: string) => void): void;
|
|
||||||
onClose(callback: () => void): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConnectionFactory {
|
|
||||||
create(): Connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class WebTTY {
|
|
||||||
term: Terminal;
|
|
||||||
connectionFactory: ConnectionFactory;
|
|
||||||
args: string;
|
|
||||||
authToken: string;
|
|
||||||
reconnect: number;
|
|
||||||
bufSize: number;
|
|
||||||
|
|
||||||
constructor(term: Terminal, connectionFactory: ConnectionFactory, args: string, authToken: string) {
|
|
||||||
this.term = term;
|
|
||||||
this.connectionFactory = connectionFactory;
|
|
||||||
this.args = args;
|
|
||||||
this.authToken = authToken;
|
|
||||||
this.reconnect = -1;
|
|
||||||
this.bufSize = 1024;
|
|
||||||
};
|
|
||||||
|
|
||||||
open() {
|
|
||||||
let connection = this.connectionFactory.create();
|
|
||||||
let pingTimer: NodeJS.Timeout;
|
|
||||||
let reconnectTimeout: NodeJS.Timeout;
|
|
||||||
|
|
||||||
const setup = () => {
|
|
||||||
connection.onOpen(() => {
|
|
||||||
const termInfo = this.term.info();
|
|
||||||
|
|
||||||
connection.send(JSON.stringify(
|
|
||||||
{
|
|
||||||
Arguments: this.args,
|
|
||||||
AuthToken: this.authToken,
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
|
|
||||||
const resizeHandler = (colmuns: number, rows: number) => {
|
|
||||||
connection.send(
|
|
||||||
msgResizeTerminal + JSON.stringify(
|
|
||||||
{
|
|
||||||
columns: colmuns,
|
|
||||||
rows: rows
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.term.onResize(resizeHandler);
|
|
||||||
resizeHandler(termInfo.columns, termInfo.rows);
|
|
||||||
|
|
||||||
this.term.onInput(
|
|
||||||
(input: string) => {
|
|
||||||
// Leave room for message type id
|
|
||||||
let effectiveBufferSize = this.bufSize - 1;
|
|
||||||
|
|
||||||
// Split input into buffer sized chunks
|
|
||||||
for (let i = 0; i < Math.ceil(input.length/effectiveBufferSize); i++) {
|
|
||||||
let inputChunk = input.substring(i*effectiveBufferSize, Math.min((i+1)*effectiveBufferSize, input.length))
|
|
||||||
connection.send(msgInput + inputChunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
pingTimer = setInterval(() => {
|
|
||||||
connection.send(msgPing)
|
|
||||||
}, 30 * 1000);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.onReceive((data) => {
|
|
||||||
const payload = data.slice(1);
|
|
||||||
switch (data[0]) {
|
|
||||||
case msgOutput:
|
|
||||||
this.term.output(atob(payload));
|
|
||||||
break;
|
|
||||||
case msgPong:
|
|
||||||
break;
|
|
||||||
case msgSetWindowTitle:
|
|
||||||
this.term.setWindowTitle(payload);
|
|
||||||
break;
|
|
||||||
case msgSetPreferences:
|
|
||||||
const preferences = JSON.parse(payload);
|
|
||||||
this.term.setPreferences(preferences);
|
|
||||||
break;
|
|
||||||
case msgSetReconnect:
|
|
||||||
const autoReconnect = JSON.parse(payload);
|
|
||||||
console.log("Enabling reconnect: " + autoReconnect + " seconds")
|
|
||||||
this.reconnect = autoReconnect;
|
|
||||||
break;
|
|
||||||
case msgSetBufferSize:
|
|
||||||
const bufSize = JSON.parse(payload);
|
|
||||||
this.bufSize = bufSize;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.onClose(() => {
|
|
||||||
clearInterval(pingTimer);
|
|
||||||
this.term.deactivate();
|
|
||||||
this.term.showMessage("Connection Closed", 0);
|
|
||||||
if (this.reconnect > 0) {
|
|
||||||
reconnectTimeout = setTimeout(() => {
|
|
||||||
connection = this.connectionFactory.create();
|
|
||||||
this.term.reset();
|
|
||||||
setup();
|
|
||||||
}, this.reconnect * 1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
setup();
|
|
||||||
return () => {
|
|
||||||
clearTimeout(reconnectTimeout);
|
|
||||||
connection.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
270
js/src/webtty.tsx
Normal file
270
js/src/webtty.tsx
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
export const protocols = ["webtty"];
|
||||||
|
|
||||||
|
export const msgInputUnknown = '0';
|
||||||
|
export const msgInput = '1';
|
||||||
|
export const msgPing = '2';
|
||||||
|
export const msgResizeTerminal = '3';
|
||||||
|
export const msgSetEncoding = '4';
|
||||||
|
|
||||||
|
export const msgUnknownOutput = '0';
|
||||||
|
export const msgOutput = '1';
|
||||||
|
export const msgPong = '2';
|
||||||
|
export const msgSetWindowTitle = '3';
|
||||||
|
export const msgSetPreferences = '4';
|
||||||
|
export const msgSetReconnect = '5';
|
||||||
|
export const msgSetBufferSize = '6';
|
||||||
|
|
||||||
|
|
||||||
|
export interface Terminal {
|
||||||
|
/*
|
||||||
|
* Get dimensions of the terminal
|
||||||
|
*/
|
||||||
|
info(): { columns: number, rows: number };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process output from the server side
|
||||||
|
*/
|
||||||
|
output(data: Uint8Array): void;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Display a message overlay on the terminal
|
||||||
|
*/
|
||||||
|
showMessage(message: string, timeout: number): void;
|
||||||
|
|
||||||
|
// Don't think we need this anymore
|
||||||
|
// getMessage(): HTMLElement;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove message shown by shoMessage. You only need to call
|
||||||
|
* this if you want to dismiss it sooner than the timeout.
|
||||||
|
*/
|
||||||
|
removeMessage(): void;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set window title
|
||||||
|
*/
|
||||||
|
setWindowTitle(title: string): void;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set preferences. TODO: Add typings
|
||||||
|
*/
|
||||||
|
setPreferences(value: object): void;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets an input (e.g. user types something) handler
|
||||||
|
*/
|
||||||
|
onInput(callback: (input: string) => void): void;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets a resize handler
|
||||||
|
*/
|
||||||
|
onResize(callback: (colmuns: number, rows: number) => void): void;
|
||||||
|
|
||||||
|
reset(): void;
|
||||||
|
deactivate(): void;
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Connection {
|
||||||
|
open(): void;
|
||||||
|
close(): void;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This takes fucking strings??
|
||||||
|
*/
|
||||||
|
send(s: string): void;
|
||||||
|
|
||||||
|
isOpen(): boolean;
|
||||||
|
onOpen(callback: () => void): void;
|
||||||
|
onReceive(callback: (data: string) => void): void;
|
||||||
|
onClose(callback: () => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConnectionFactory {
|
||||||
|
create(): Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebTTY {
|
||||||
|
/*
|
||||||
|
* A terminal instance that implements the Terminal interface.
|
||||||
|
* This made a lot of sense when we had both HTerm and xterm, but
|
||||||
|
* now I wonder if the abstraction makes sense. Keeping it for now,
|
||||||
|
* though.
|
||||||
|
*/
|
||||||
|
term: Terminal;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ConnectionFactory and connection instance. We pass the factory
|
||||||
|
* in instead of just a connection so that we can reconnect.
|
||||||
|
*/
|
||||||
|
connectionFactory: ConnectionFactory;
|
||||||
|
connection: Connection;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Arguments passed in by the user. We forward them to the backend
|
||||||
|
* where they are appended to the command line.
|
||||||
|
*/
|
||||||
|
args: string;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An authentication token. The client gets this from `/auth_token.js`.
|
||||||
|
*/
|
||||||
|
authToken: string;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If connection is dropped, reconnect after `reconnect` seconds.
|
||||||
|
* -1 means do not reconnect.
|
||||||
|
*/
|
||||||
|
reconnect: number;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The server's buffer size. If a single message exceeds this size, it will
|
||||||
|
* be truncated on the server, so we track it here so that we can split messages
|
||||||
|
* into chunks small enough that we don't hurt the server's feelings.
|
||||||
|
*/
|
||||||
|
bufSize: number;
|
||||||
|
|
||||||
|
constructor(term: Terminal, connectionFactory: ConnectionFactory, args: string, authToken: string) {
|
||||||
|
this.term = term;
|
||||||
|
this.connectionFactory = connectionFactory;
|
||||||
|
this.args = args;
|
||||||
|
this.authToken = authToken;
|
||||||
|
this.reconnect = -1;
|
||||||
|
this.bufSize = 1024;
|
||||||
|
};
|
||||||
|
|
||||||
|
open() {
|
||||||
|
let connection = this.connectionFactory.create();
|
||||||
|
let pingTimer: NodeJS.Timeout;
|
||||||
|
let reconnectTimeout: NodeJS.Timeout;
|
||||||
|
this.connection = connection;
|
||||||
|
|
||||||
|
const setup = () => {
|
||||||
|
connection.onOpen(() => {
|
||||||
|
const termInfo = this.term.info();
|
||||||
|
|
||||||
|
this.initializeConnection(this.args, this.authToken);
|
||||||
|
|
||||||
|
this.term.onResize((columns: number, rows: number) => {
|
||||||
|
this.sendResizeTerminal(columns, rows);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.sendResizeTerminal(termInfo.columns, termInfo.rows);
|
||||||
|
|
||||||
|
this.sendSetEncoding("base64");
|
||||||
|
|
||||||
|
this.term.onInput(
|
||||||
|
(input: string | Uint8Array) => {
|
||||||
|
this.sendInput(input);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
pingTimer = setInterval(() => {
|
||||||
|
this.sendPing()
|
||||||
|
}, 30 * 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onReceive((data) => {
|
||||||
|
const payload = data.slice(1);
|
||||||
|
switch (data[0]) {
|
||||||
|
case msgOutput:
|
||||||
|
this.term.output(Uint8Array.from(atob(payload), c => c.charCodeAt(0)));
|
||||||
|
break;
|
||||||
|
case msgPong:
|
||||||
|
break;
|
||||||
|
case msgSetWindowTitle:
|
||||||
|
this.term.setWindowTitle(payload);
|
||||||
|
break;
|
||||||
|
case msgSetPreferences:
|
||||||
|
const preferences = JSON.parse(payload);
|
||||||
|
this.term.setPreferences(preferences);
|
||||||
|
break;
|
||||||
|
case msgSetReconnect:
|
||||||
|
const autoReconnect = JSON.parse(payload);
|
||||||
|
console.log("Enabling reconnect: " + autoReconnect + " seconds")
|
||||||
|
this.reconnect = autoReconnect;
|
||||||
|
break;
|
||||||
|
case msgSetBufferSize:
|
||||||
|
const bufSize = JSON.parse(payload);
|
||||||
|
this.bufSize = bufSize;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onClose(() => {
|
||||||
|
clearInterval(pingTimer);
|
||||||
|
this.term.deactivate();
|
||||||
|
this.term.showMessage("Connection Closed", 0);
|
||||||
|
if (this.reconnect > 0) {
|
||||||
|
reconnectTimeout = setTimeout(() => {
|
||||||
|
connection = this.connectionFactory.create();
|
||||||
|
this.term.reset();
|
||||||
|
setup();
|
||||||
|
}, this.reconnect * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
setup();
|
||||||
|
return () => {
|
||||||
|
clearTimeout(reconnectTimeout);
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private initializeConnection(args, authToken) {
|
||||||
|
this.connection.send(JSON.stringify(
|
||||||
|
{
|
||||||
|
Arguments: args,
|
||||||
|
AuthToken: authToken,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sendInput sends data to the server. It accepts strings or Uint8Arrays.
|
||||||
|
* strings will be encoded as UTF-8. Uint8Arrays are passed along as-is.
|
||||||
|
*/
|
||||||
|
private sendInput(input: string | Uint8Array) {
|
||||||
|
let effectiveBufferSize = this.bufSize - 1;
|
||||||
|
let dataString: string;
|
||||||
|
|
||||||
|
if (typeof input === "string") {
|
||||||
|
dataString = input;
|
||||||
|
} else {
|
||||||
|
dataString = String.fromCharCode(...input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account for base64 encoding
|
||||||
|
let maxChunkSize = Math.floor(effectiveBufferSize / 4) * 3;
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.ceil(dataString.length / maxChunkSize); i++) {
|
||||||
|
let inputChunk = dataString.substring(i * maxChunkSize, Math.min((i + 1) * maxChunkSize, dataString.length))
|
||||||
|
this.connection.send(msgInput + btoa(inputChunk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendPing(): void {
|
||||||
|
this.connection.send(msgPing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendResizeTerminal(colmuns: number, rows: number) {
|
||||||
|
this.connection.send(
|
||||||
|
msgResizeTerminal + JSON.stringify(
|
||||||
|
{
|
||||||
|
columns: colmuns,
|
||||||
|
rows: rows
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sendSetEncoding(encoding: "base64" | "null") {
|
||||||
|
this.connection.send(msgSetEncoding + encoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -2,27 +2,40 @@ import { Terminal, IDisposable } from "xterm";
|
|||||||
import { FitAddon } from 'xterm-addon-fit';
|
import { FitAddon } from 'xterm-addon-fit';
|
||||||
import { WebLinksAddon } from 'xterm-addon-web-links';
|
import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||||
import { WebglAddon } from 'xterm-addon-webgl';
|
import { WebglAddon } from 'xterm-addon-webgl';
|
||||||
import { lib } from "libapps"
|
import { ZModemAddon } from "./zmodem";
|
||||||
|
|
||||||
export class Xterm {
|
export class OurXterm {
|
||||||
|
// The HTMLElement that contains our terminal
|
||||||
elem: HTMLElement;
|
elem: HTMLElement;
|
||||||
|
|
||||||
|
// The xtermjs.XTerm
|
||||||
term: Terminal;
|
term: Terminal;
|
||||||
|
|
||||||
resizeListener: () => void;
|
resizeListener: () => void;
|
||||||
decoder: lib.UTF8Decoder;
|
|
||||||
|
|
||||||
message: HTMLElement;
|
message: HTMLElement;
|
||||||
messageTimeout: number;
|
messageTimeout: number;
|
||||||
messageTimer: NodeJS.Timeout;
|
messageTimer: NodeJS.Timeout;
|
||||||
|
|
||||||
onResizeHandler: IDisposable;
|
onResizeHandler: IDisposable;
|
||||||
onDataHandler: IDisposable;
|
onDataHandler: IDisposable;
|
||||||
|
|
||||||
fitAddOn: FitAddon;
|
fitAddOn: FitAddon;
|
||||||
|
zmodemAddon: ZModemAddon;
|
||||||
|
toServer: (data: string | Uint8Array) => void;
|
||||||
|
encoder: TextEncoder
|
||||||
|
|
||||||
constructor(elem: HTMLElement) {
|
constructor(elem: HTMLElement) {
|
||||||
this.elem = elem;
|
this.elem = elem;
|
||||||
this.term = new Terminal();
|
this.term = new Terminal();
|
||||||
this.fitAddOn = new FitAddon();
|
this.fitAddOn = new FitAddon();
|
||||||
|
this.zmodemAddon = new ZModemAddon({
|
||||||
|
toTerminal: (x: Uint8Array) => this.term.write(x),
|
||||||
|
toServer: (x: Uint8Array) => this.sendInput(x)
|
||||||
|
});
|
||||||
this.term.loadAddon(new WebLinksAddon());
|
this.term.loadAddon(new WebLinksAddon());
|
||||||
this.term.loadAddon(this.fitAddOn);
|
this.term.loadAddon(this.fitAddOn);
|
||||||
|
this.term.loadAddon(this.zmodemAddon);
|
||||||
|
|
||||||
this.message = elem.ownerDocument.createElement("div");
|
this.message = elem.ownerDocument.createElement("div");
|
||||||
this.message.className = "xterm-overlay";
|
this.message.className = "xterm-overlay";
|
||||||
@ -37,21 +50,29 @@ export class Xterm {
|
|||||||
this.term.open(elem);
|
this.term.open(elem);
|
||||||
this.term.focus();
|
this.term.focus();
|
||||||
this.resizeListener();
|
this.resizeListener();
|
||||||
window.addEventListener("resize", () => { this.resizeListener(); });
|
|
||||||
|
|
||||||
this.decoder = new lib.UTF8Decoder()
|
window.addEventListener("resize", () => { this.resizeListener(); });
|
||||||
};
|
};
|
||||||
|
|
||||||
info(): { columns: number, rows: number } {
|
info(): { columns: number, rows: number } {
|
||||||
return { columns: this.term.cols, rows: this.term.rows };
|
return { columns: this.term.cols, rows: this.term.rows };
|
||||||
};
|
};
|
||||||
|
|
||||||
output(data: string) {
|
// This gets called from the Websocket's onReceive handler
|
||||||
this.term.write(this.decoder.decode(data));
|
output(data: Uint8Array) {
|
||||||
|
this.zmodemAddon.consume(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getMessage(): HTMLElement {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
showMessage(message: string, timeout: number) {
|
showMessage(message: string, timeout: number) {
|
||||||
this.message.textContent = message;
|
this.message.innerHTML = message;
|
||||||
|
this.showMessageElem(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
showMessageElem(timeout: number) {
|
||||||
this.elem.appendChild(this.message);
|
this.elem.appendChild(this.message);
|
||||||
|
|
||||||
if (this.messageTimer) {
|
if (this.messageTimer) {
|
||||||
@ -59,7 +80,11 @@ export class Xterm {
|
|||||||
}
|
}
|
||||||
if (timeout > 0) {
|
if (timeout > 0) {
|
||||||
this.messageTimer = setTimeout(() => {
|
this.messageTimer = setTimeout(() => {
|
||||||
this.elem.removeChild(this.message);
|
try {
|
||||||
|
this.elem.removeChild(this.message);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}, timeout);
|
}, timeout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -86,11 +111,23 @@ export class Xterm {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onInput(callback: (input: string) => void) {
|
sendInput(data: Uint8Array) {
|
||||||
this.onDataHandler = this.term.onData((data) => {
|
return this.toServer(data)
|
||||||
callback(data);
|
}
|
||||||
});
|
|
||||||
|
|
||||||
|
onInput(callback: (input: string) => void) {
|
||||||
|
this.encoder = new TextEncoder()
|
||||||
|
this.toServer = callback;
|
||||||
|
|
||||||
|
// I *think* we're ok like this, but if not, we can dispose
|
||||||
|
// of the previous handler and put the new one in place.
|
||||||
|
if (this.onDataHandler !== undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onDataHandler = this.term.onData((input) => {
|
||||||
|
this.toServer(this.encoder.encode(input));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onResize(callback: (colmuns: number, rows: number) => void) {
|
onResize(callback: (colmuns: number, rows: number) => void) {
|
||||||
@ -114,4 +151,16 @@ export class Xterm {
|
|||||||
window.removeEventListener("resize", this.resizeListener);
|
window.removeEventListener("resize", this.resizeListener);
|
||||||
this.term.dispose();
|
this.term.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disableStdin(): void {
|
||||||
|
this.term.options.disableStdin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
enableStdin(): void {
|
||||||
|
this.term.options.disableStdin = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus(): void {
|
||||||
|
this.term.focus();
|
||||||
|
}
|
||||||
}
|
}
|
245
js/src/zmodem.tsx
Normal file
245
js/src/zmodem.tsx
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
import { ITerminalAddon, Terminal } from "xterm";
|
||||||
|
import { Browser, Detection, Offer, Sentry, Session } from 'zmodem.js/src/zmodem_browser';
|
||||||
|
import { MyModal, Button } from "./MyModal";
|
||||||
|
import { Component, ComponentChildren, createRef, render } from "preact";
|
||||||
|
|
||||||
|
export class ZModemAddon implements ITerminalAddon {
|
||||||
|
term: Terminal;
|
||||||
|
elem: HTMLDivElement;
|
||||||
|
sentry: Sentry;
|
||||||
|
toTerminal: (data: Uint8Array) => void;
|
||||||
|
toServer: (data: Uint8Array) => void;
|
||||||
|
|
||||||
|
constructor(props: {
|
||||||
|
toTerminal: (data: Uint8Array) => void,
|
||||||
|
toServer: (data: Uint8Array) => void
|
||||||
|
}) {
|
||||||
|
this.createElement();
|
||||||
|
this.toTerminal = props.toTerminal;
|
||||||
|
this.toServer = props.toServer;
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private createElement() {
|
||||||
|
this.elem = document.createElement("div");
|
||||||
|
document.body.prepend(this.elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(data: Uint8Array) {
|
||||||
|
this.sentry.consume(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
activate(terminal: Terminal): void {
|
||||||
|
this.term = terminal
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
render(<></>, this.elem);
|
||||||
|
|
||||||
|
this.sentry = new Sentry({
|
||||||
|
'to_terminal': (d: Uint8Array) => this.toTerminal(d),
|
||||||
|
'on_detect': (detection: Detection) => this.onDetect(detection),
|
||||||
|
'sender': (x: Uint8Array) => { this.toServer(x) },
|
||||||
|
'on_retract': () => this.reset(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private reset() {
|
||||||
|
this.init();
|
||||||
|
this.term.options.disableStdin = false;
|
||||||
|
this.term.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private onDetect(detection: Detection) {
|
||||||
|
var zsession = detection.confirm();
|
||||||
|
|
||||||
|
this.term.options.disableStdin = true;
|
||||||
|
|
||||||
|
zsession.on('session_end', () => { this.reset() });
|
||||||
|
|
||||||
|
if (zsession.type === "send") {
|
||||||
|
this.send(zsession);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
zsession.on("offer", (xfer: any) => this.onOffer(xfer));
|
||||||
|
zsession.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private send(zsession: Session) {
|
||||||
|
render(<SendFileModal session={zsession} />, this.elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
private onOffer(xfer: Offer) {
|
||||||
|
render(<ReceiveFileModal xfer={xfer} onFinish={() => this.reset()} />, this.elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renders a bootstrap progress bar
|
||||||
|
function Progress(props: { min: number, max: number, now: number, children?: ComponentChildren }) {
|
||||||
|
let { min, max, now } = props;
|
||||||
|
let percentage = "0";
|
||||||
|
|
||||||
|
if ((typeof min === "number") &&
|
||||||
|
(typeof max === "number") &&
|
||||||
|
(typeof now === "number") &&
|
||||||
|
(min != max)) {
|
||||||
|
percentage = (100 * (now - min) / (max - min)).toFixed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div class="progress">
|
||||||
|
<div class="progress-bar" role="progressbar" style={"width: " + percentage + "%"} aria-valuenow={now} aria-valuemin={min} aria-valuemax={max}>{props.children}</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReceiveFileModalProps {
|
||||||
|
xfer: Offer;
|
||||||
|
onFinish?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReceiveFileModalState {
|
||||||
|
state: "notstarted" | "started" | "skipped" | "done"
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ReceiveFileModal extends Component<ReceiveFileModalProps, ReceiveFileModalState> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.setState({ state: "notstarted" })
|
||||||
|
}
|
||||||
|
|
||||||
|
accept() {
|
||||||
|
this.setState({ state: "started" });
|
||||||
|
|
||||||
|
let timerID = setInterval(
|
||||||
|
() => this.forceUpdate(),
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
this.props.xfer.accept().then((payloads: any) => {
|
||||||
|
// All done, so stop updating the progress bar
|
||||||
|
// and perform a final render.
|
||||||
|
clearInterval(timerID);
|
||||||
|
this.forceUpdate();
|
||||||
|
|
||||||
|
if (this.state.state != "skipped") {
|
||||||
|
Browser.save_to_disk(
|
||||||
|
payloads,
|
||||||
|
this.props.xfer.get_details().name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.setState({ state: "done" })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
console.log('finished');
|
||||||
|
if (this.props.onFinish) this.props.onFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
progress() {
|
||||||
|
if (this.state.state !== "notstarted") {
|
||||||
|
return <Progress min={0} max={this.props.xfer.get_details().size} now={this.props.xfer.get_offset()} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skip() {
|
||||||
|
this.props.xfer.skip()
|
||||||
|
this.setState({ state: "skipped" })
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons() {
|
||||||
|
switch (this.state.state) {
|
||||||
|
case "notstarted":
|
||||||
|
return <>
|
||||||
|
<Button priority="primary" clickHandler={() => { this.accept(); }}>Accept</Button>
|
||||||
|
<Button priority="secondary" clickHandler={() => { this.skip(); }}>Decline</Button>
|
||||||
|
</>
|
||||||
|
case "started":
|
||||||
|
return <>
|
||||||
|
<Button priority="danger" clickHandler={() => { this.skip(); }}>Cancel</Button>
|
||||||
|
</>
|
||||||
|
case "skipped":
|
||||||
|
return <>
|
||||||
|
<Button priority="danger" disabled={true}>Skipping...</Button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.state != "done")
|
||||||
|
return <MyModal title="Incoming file"
|
||||||
|
buttons={this.buttons()}>
|
||||||
|
Accept <code>{this.props.xfer.get_details().name}</code> ({this.props.xfer.get_details().size.toLocaleString(undefined, { maximumFractionDigits: 0 })} bytes)?
|
||||||
|
{this.progress()}
|
||||||
|
</MyModal>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class SendFileModal extends Component<SendFileModalProps, SendFileModalState> {
|
||||||
|
filePickerRef = createRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
constructor(props: SendFileModalProps) {
|
||||||
|
super(props)
|
||||||
|
this.setState({ state: "notstarted" })
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons() {
|
||||||
|
switch (this.state.state) {
|
||||||
|
case "started":
|
||||||
|
return <>
|
||||||
|
<Button priority="primary" clickHandler={() => { this.send(); }} disabled={true}>
|
||||||
|
Sending...
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
case "notstarted":
|
||||||
|
return <>
|
||||||
|
<Button priority="primary" clickHandler={() => { this.send(); }}>Send</Button>
|
||||||
|
</>
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send() {
|
||||||
|
Browser.send_files(this.props.session,
|
||||||
|
this.filePickerRef.current!.files, {
|
||||||
|
on_offer_response: (f, xfer) => { this.setState({ state: "started" }) },
|
||||||
|
}).then(() => {
|
||||||
|
this.setState({ state: "done" })
|
||||||
|
this.props.session.close()
|
||||||
|
if (this.props.onFinish !== undefined) {
|
||||||
|
this.props.onFinish();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(e => console.log(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.state != "done")
|
||||||
|
return <MyModal title="Send file(s)"
|
||||||
|
buttons={this.buttons()}>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="formFileMultiple" class="form-label">
|
||||||
|
Remote requested file transfer
|
||||||
|
</label>
|
||||||
|
<input ref={this.filePickerRef} class="form-control form-control-sm" type="file" id="formFileMultiple" multiple />
|
||||||
|
</div>
|
||||||
|
</MyModal>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SendFileModalProps {
|
||||||
|
onFinish?: () => void;
|
||||||
|
session: Session;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SendFileModalState {
|
||||||
|
state: "notstarted" | "started" | "done"
|
||||||
|
currentFile: any
|
||||||
|
}
|
@ -1,16 +1,18 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist/",
|
"outDir": "./dist/",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "preact",
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"noUnusedLocals" : true,
|
"noUnusedLocals" : true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"alwaysStrict": true,
|
"alwaysStrict": true,
|
||||||
"outDir": "./dist/",
|
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"target": "es5",
|
"target": "esnext",
|
||||||
"module": "commonJS",
|
"module": "commonJS",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"types": ["preact", "node"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"*": ["./typings/*"]
|
"*": ["./typings/*"]
|
||||||
}
|
}
|
||||||
|
51
js/typings/libapps/index.d.ts
vendored
51
js/typings/libapps/index.d.ts
vendored
@ -1,51 +0,0 @@
|
|||||||
export declare namespace hterm {
|
|
||||||
export class Terminal {
|
|
||||||
io: IO;
|
|
||||||
onTerminalReady: () => void;
|
|
||||||
|
|
||||||
constructor();
|
|
||||||
getPrefs(): Prefs;
|
|
||||||
decorate(HTMLElement);
|
|
||||||
installKeyboard(): void;
|
|
||||||
uninstallKeyboard(): void;
|
|
||||||
setWindowTitle(title: string): void;
|
|
||||||
reset(): void;
|
|
||||||
softReset(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class IO {
|
|
||||||
writeUTF8: ((data: string) => void);
|
|
||||||
writeUTF16: ((data: string) => void);
|
|
||||||
onVTKeystroke: ((data: string) => void) | null;
|
|
||||||
sendString: ((data: string) => void) | null;
|
|
||||||
onTerminalResize: ((columns: number, rows: number) => void) | null;
|
|
||||||
|
|
||||||
push(): IO;
|
|
||||||
writeUTF(data: string);
|
|
||||||
showOverlay(message: string, timeout: number | null);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Prefs {
|
|
||||||
set(key: string, value: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export var defaultStorage: lib.Storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare namespace lib {
|
|
||||||
export interface Storage {
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Memory {
|
|
||||||
new (): Storage;
|
|
||||||
Memory(): Storage
|
|
||||||
}
|
|
||||||
|
|
||||||
export var Storage: {
|
|
||||||
Memory: Memory
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UTF8Decoder {
|
|
||||||
decode(str: string)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,6 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
|
const LicenseWebpackPlugin = require('license-webpack-plugin').LicenseWebpackPlugin;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: "./src/main.ts",
|
entry: "./src/main.ts",
|
||||||
@ -12,6 +14,9 @@ module.exports = {
|
|||||||
resolve: {
|
resolve: {
|
||||||
extensions: [".ts", ".tsx", ".js"],
|
extensions: [".ts", ".tsx", ".js"],
|
||||||
},
|
},
|
||||||
|
plugins: [
|
||||||
|
new LicenseWebpackPlugin()
|
||||||
|
],
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
@ -24,10 +29,21 @@ module.exports = {
|
|||||||
use: ["style-loader", "css-loader"],
|
use: ["style-loader", "css-loader"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.scss$/i,
|
||||||
include: /node_modules/,
|
use: ["style-loader", "css-loader", {
|
||||||
loader: 'license-loader'
|
loader: "sass-loader",
|
||||||
|
options: {
|
||||||
|
sassOptions: {
|
||||||
|
includePaths: ["node_modules/bootstrap/scss"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: true,
|
||||||
|
minimizer: [new TerserPlugin()],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
6
main.go
6
main.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -65,6 +66,11 @@ func main() {
|
|||||||
|
|
||||||
utils.ApplyFlags(cliFlags, flagMappings, c, appOptions, backendOptions)
|
utils.ApplyFlags(cliFlags, flagMappings, c, appOptions, backendOptions)
|
||||||
|
|
||||||
|
if appOptions.Quiet {
|
||||||
|
log.SetFlags(0)
|
||||||
|
log.SetOutput(ioutil.Discard)
|
||||||
|
}
|
||||||
|
|
||||||
if c.IsSet("credential") {
|
if c.IsSet("credential") {
|
||||||
appOptions.EnableBasicAuth = true
|
appOptions.EnableBasicAuth = true
|
||||||
}
|
}
|
||||||
|
@ -5,3 +5,9 @@ html, body, #terminal {
|
|||||||
padding: 0%;
|
padding: 0%;
|
||||||
margin: 0%;
|
margin: 0%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress .progress-bar {
|
||||||
|
transition: unset;
|
||||||
|
transition-duration: 0.1s;
|
||||||
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
@ -1,18 +1,22 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
|
||||||
<title>{{ .title }}</title>
|
<head>
|
||||||
<link rel="manifest" href="manifest.json">
|
<title>{{ .title }}</title>
|
||||||
<link rel="icon" href="favicon.ico">
|
<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
|
||||||
<link rel="icon" href="icon.svg" type="image/svg+xml">
|
<link rel="icon" href="favicon.ico">
|
||||||
<link rel="stylesheet" href="./css/index.css" />
|
<link rel="icon" href="icon.svg" type="image/svg+xml">
|
||||||
<link rel="stylesheet" href="./css/xterm.css" />
|
<link rel="stylesheet" href="./css/index.css" />
|
||||||
<link rel="stylesheet" href="./css/xterm_customize.css" />
|
<link rel="stylesheet" href="./css/xterm.css" />
|
||||||
</head>
|
<link rel="stylesheet" href="./css/xterm_customize.css" />
|
||||||
<body>
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<div id="terminal"></div>
|
</head>
|
||||||
<script src="./auth_token.js"></script>
|
|
||||||
<script src="./config.js"></script>
|
<body>
|
||||||
<script src="./js/gotty.js"></script>
|
<div id="terminal"></div>
|
||||||
</body>
|
<script src="./auth_token.js"></script>
|
||||||
|
<script src="./config.js"></script>
|
||||||
|
<script src="./js/gotty.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -154,13 +154,6 @@ func (server *Server) processWSConn(ctx context.Context, conn *websocket.Conn, h
|
|||||||
if server.options.Height > 0 {
|
if server.options.Height > 0 {
|
||||||
opts = append(opts, webtty.WithFixedRows(server.options.Height))
|
opts = append(opts, webtty.WithFixedRows(server.options.Height))
|
||||||
}
|
}
|
||||||
if server.options.Preferences == nil {
|
|
||||||
server.options.Preferences = &HtermPrefernces{}
|
|
||||||
}
|
|
||||||
// Awkward hack until HtermPreferences can be phased out
|
|
||||||
server.options.Preferences.EnableWebGL = server.options.EnableWebGL
|
|
||||||
opts = append(opts, webtty.WithMasterPreferences(server.options.Preferences))
|
|
||||||
|
|
||||||
tty, err := webtty.New(&wsWrapper{conn}, slave, opts...)
|
tty, err := webtty.New(&wsWrapper{conn}, slave, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to create webtty")
|
return errors.Wrapf(err, "failed to create webtty")
|
||||||
@ -236,7 +229,7 @@ func (server *Server) handleAuthToken(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (server *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
|
func (server *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/javascript")
|
w.Header().Set("Content-Type", "application/javascript")
|
||||||
w.Write([]byte("var gotty_term = '" + server.options.Term + "';"))
|
w.Write([]byte("var gotty_term = 'xterm';"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// titleVariables merges maps in a specified order.
|
// titleVariables merges maps in a specified order.
|
||||||
|
@ -5,33 +5,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Address string `hcl:"address" flagName:"address" flagSName:"a" flagDescribe:"IP address to listen" default:"0.0.0.0"`
|
Address string `hcl:"address" flagName:"address" flagSName:"a" flagDescribe:"IP address to listen" default:"0.0.0.0"`
|
||||||
Port string `hcl:"port" flagName:"port" flagSName:"p" flagDescribe:"Port number to liten" default:"8080"`
|
Port string `hcl:"port" flagName:"port" flagSName:"p" flagDescribe:"Port number to liten" default:"8080"`
|
||||||
Path string `hcl:"path" flagName:"path" flagSName:"m" flagDescribe:"Base path" default:"/"`
|
Path string `hcl:"path" flagName:"path" flagSName:"m" flagDescribe:"Base path" default:"/"`
|
||||||
PermitWrite bool `hcl:"permit_write" flagName:"permit-write" flagSName:"w" flagDescribe:"Permit clients to write to the TTY (BE CAREFUL)" default:"false"`
|
PermitWrite bool `hcl:"permit_write" flagName:"permit-write" flagSName:"w" flagDescribe:"Permit clients to write to the TTY (BE CAREFUL)" default:"false"`
|
||||||
EnableBasicAuth bool `hcl:"enable_basic_auth" default:"false"`
|
EnableBasicAuth bool `hcl:"enable_basic_auth" default:"false"`
|
||||||
Credential string `hcl:"credential" flagName:"credential" flagSName:"c" flagDescribe:"Credential for Basic Authentication (ex: user:pass, default disabled)" default:""`
|
Credential string `hcl:"credential" flagName:"credential" flagSName:"c" flagDescribe:"Credential for Basic Authentication (ex: user:pass, default disabled)" default:""`
|
||||||
EnableRandomUrl bool `hcl:"enable_random_url" flagName:"random-url" flagSName:"r" flagDescribe:"Add a random string to the URL" default:"false"`
|
EnableRandomUrl bool `hcl:"enable_random_url" flagName:"random-url" flagSName:"r" flagDescribe:"Add a random string to the URL" default:"false"`
|
||||||
RandomUrlLength int `hcl:"random_url_length" flagName:"random-url-length" flagDescribe:"Random URL length" default:"8"`
|
RandomUrlLength int `hcl:"random_url_length" flagName:"random-url-length" flagDescribe:"Random URL length" default:"8"`
|
||||||
EnableTLS bool `hcl:"enable_tls" flagName:"tls" flagSName:"t" flagDescribe:"Enable TLS/SSL" default:"false"`
|
EnableTLS bool `hcl:"enable_tls" flagName:"tls" flagSName:"t" flagDescribe:"Enable TLS/SSL" default:"false"`
|
||||||
TLSCrtFile string `hcl:"tls_crt_file" flagName:"tls-crt" flagDescribe:"TLS/SSL certificate file path" default:"~/.gotty.crt"`
|
TLSCrtFile string `hcl:"tls_crt_file" flagName:"tls-crt" flagDescribe:"TLS/SSL certificate file path" default:"~/.gotty.crt"`
|
||||||
TLSKeyFile string `hcl:"tls_key_file" flagName:"tls-key" flagDescribe:"TLS/SSL key file path" default:"~/.gotty.key"`
|
TLSKeyFile string `hcl:"tls_key_file" flagName:"tls-key" flagDescribe:"TLS/SSL key file path" default:"~/.gotty.key"`
|
||||||
EnableTLSClientAuth bool `hcl:"enable_tls_client_auth" default:"false"`
|
EnableTLSClientAuth bool `hcl:"enable_tls_client_auth" default:"false"`
|
||||||
TLSCACrtFile string `hcl:"tls_ca_crt_file" flagName:"tls-ca-crt" flagDescribe:"TLS/SSL CA certificate file for client certifications" default:"~/.gotty.ca.crt"`
|
TLSCACrtFile string `hcl:"tls_ca_crt_file" flagName:"tls-ca-crt" flagDescribe:"TLS/SSL CA certificate file for client certifications" default:"~/.gotty.ca.crt"`
|
||||||
IndexFile string `hcl:"index_file" flagName:"index" flagDescribe:"Custom index.html file" default:""`
|
IndexFile string `hcl:"index_file" flagName:"index" flagDescribe:"Custom index.html file" default:""`
|
||||||
TitleFormat string `hcl:"title_format" flagName:"title-format" flagSName:"" flagDescribe:"Title format of browser window" default:"{{ .command }}@{{ .hostname }}"`
|
TitleFormat string `hcl:"title_format" flagName:"title-format" flagSName:"" flagDescribe:"Title format of browser window" default:"{{ .command }}@{{ .hostname }}"`
|
||||||
EnableReconnect bool `hcl:"enable_reconnect" flagName:"reconnect" flagDescribe:"Enable reconnection" default:"false"`
|
EnableReconnect bool `hcl:"enable_reconnect" flagName:"reconnect" flagDescribe:"Enable reconnection" default:"false"`
|
||||||
ReconnectTime int `hcl:"reconnect_time" flagName:"reconnect-time" flagDescribe:"Time to reconnect" default:"10"`
|
ReconnectTime int `hcl:"reconnect_time" flagName:"reconnect-time" flagDescribe:"Time to reconnect" default:"10"`
|
||||||
MaxConnection int `hcl:"max_connection" flagName:"max-connection" flagDescribe:"Maximum connection to gotty" default:"0"`
|
MaxConnection int `hcl:"max_connection" flagName:"max-connection" flagDescribe:"Maximum connection to gotty" default:"0"`
|
||||||
Once bool `hcl:"once" flagName:"once" flagDescribe:"Accept only one client and exit on disconnection" default:"false"`
|
Once bool `hcl:"once" flagName:"once" flagDescribe:"Accept only one client and exit on disconnection" default:"false"`
|
||||||
Timeout int `hcl:"timeout" flagName:"timeout" flagDescribe:"Timeout seconds for waiting a client(0 to disable)" default:"0"`
|
Timeout int `hcl:"timeout" flagName:"timeout" flagDescribe:"Timeout seconds for waiting a client(0 to disable)" default:"0"`
|
||||||
PermitArguments bool `hcl:"permit_arguments" flagName:"permit-arguments" flagDescribe:"Permit clients to send command line arguments in URL (e.g. http://example.com:8080/?arg=AAA&arg=BBB)" default:"false"`
|
PermitArguments bool `hcl:"permit_arguments" flagName:"permit-arguments" flagDescribe:"Permit clients to send command line arguments in URL (e.g. http://example.com:8080/?arg=AAA&arg=BBB)" default:"false"`
|
||||||
Preferences *HtermPrefernces `hcl:"preferences"`
|
Width int `hcl:"width" flagName:"width" flagDescribe:"Static width of the screen, 0(default) means dynamically resize" default:"0"`
|
||||||
Width int `hcl:"width" flagName:"width" flagDescribe:"Static width of the screen, 0(default) means dynamically resize" default:"0"`
|
Height int `hcl:"height" flagName:"height" flagDescribe:"Static height of the screen, 0(default) means dynamically resize" default:"0"`
|
||||||
Height int `hcl:"height" flagName:"height" flagDescribe:"Static height of the screen, 0(default) means dynamically resize" default:"0"`
|
WSOrigin string `hcl:"ws_origin" flagName:"ws-origin" flagDescribe:"A regular expression that matches origin URLs to be accepted by WebSocket. No cross origin requests are acceptable by default" default:""`
|
||||||
WSOrigin string `hcl:"ws_origin" flagName:"ws-origin" flagDescribe:"A regular expression that matches origin URLs to be accepted by WebSocket. No cross origin requests are acceptable by default" default:""`
|
EnableWebGL bool `hcl:"enable_webgl" flagName:"enable-webgl" flagDescribe:"Enable WebGL renderer" default:"true"`
|
||||||
Term string `hcl:"term" flagName:"term" flagDescribe:"Terminal name to use on the browser, one of xterm or hterm." default:"xterm"`
|
Quiet bool `hcl:"quiet" flagName:"quiet" flagDescribe:"Don't log" default:"false"`
|
||||||
EnableWebGL bool `hcl:"enable_webgl" flagName:"enable-webgl" flagDescribe:"Enable WebGL renderer" default:"true"`
|
|
||||||
|
|
||||||
TitleVariables map[string]interface{}
|
TitleVariables map[string]interface{}
|
||||||
}
|
}
|
||||||
@ -42,61 +41,3 @@ func (options *Options) Validate() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type HtermPrefernces struct {
|
|
||||||
AltGrMode *string `hcl:"alt_gr_mode" json:"alt-gr-mode,omitempty"`
|
|
||||||
AltBackspaceIsMetaBackspace bool `hcl:"alt_backspace_is_meta_backspace" json:"alt-backspace-is-meta-backspace,omitempty"`
|
|
||||||
AltIsMeta bool `hcl:"alt_is_meta" json:"alt-is-meta,omitempty"`
|
|
||||||
AltSendsWhat string `hcl:"alt_sends_what" json:"alt-sends-what,omitempty"`
|
|
||||||
AudibleBellSound string `hcl:"audible_bell_sound" json:"audible-bell-sound,omitempty"`
|
|
||||||
DesktopNotificationBell bool `hcl:"desktop_notification_bell" json:"desktop-notification-bell,omitempty"`
|
|
||||||
BackgroundColor string `hcl:"background_color" json:"background-color,omitempty"`
|
|
||||||
BackgroundImage string `hcl:"background_image" json:"background-image,omitempty"`
|
|
||||||
BackgroundSize string `hcl:"background_size" json:"background-size,omitempty"`
|
|
||||||
BackgroundPosition string `hcl:"background_position" json:"background-position,omitempty"`
|
|
||||||
BackspaceSendsBackspace bool `hcl:"backspace_sends_backspace" json:"backspace-sends-backspace,omitempty"`
|
|
||||||
CharacterMapOverrides map[string]map[string]string `hcl:"character_map_overrides" json:"character-map-overrides,omitempty"`
|
|
||||||
CloseOnExit bool `hcl:"close_on_exit" json:"close-on-exit,omitempty"`
|
|
||||||
CursorBlink bool `hcl:"cursor_blink" json:"cursor-blink,omitempty"`
|
|
||||||
CursorBlinkCycle [2]int `hcl:"cursor_blink_cycle" json:"cursor-blink-cycle,omitempty"`
|
|
||||||
CursorColor string `hcl:"cursor_color" json:"cursor-color,omitempty"`
|
|
||||||
ColorPaletteOverrides []*string `hcl:"color_palette_overrides" json:"color-palette-overrides,omitempty"`
|
|
||||||
CopyOnSelect bool `hcl:"copy_on_select" json:"copy-on-select,omitempty"`
|
|
||||||
UseDefaultWindowCopy bool `hcl:"use_default_window_copy" json:"use-default-window-copy,omitempty"`
|
|
||||||
ClearSelectionAfterCopy bool `hcl:"clear_selection_after_copy" json:"clear-selection-after-copy,omitempty"`
|
|
||||||
CtrlPlusMinusZeroZoom bool `hcl:"ctrl_plus_minus_zero_zoom" json:"ctrl-plus-minus-zero-zoom,omitempty"`
|
|
||||||
CtrlCCopy bool `hcl:"ctrl_c_copy" json:"ctrl-c-copy,omitempty"`
|
|
||||||
CtrlVPaste bool `hcl:"ctrl_v_paste" json:"ctrl-v-paste,omitempty"`
|
|
||||||
EastAsianAmbiguousAsTwoColumn bool `hcl:"east_asian_ambiguous_as_two_column" json:"east-asian-ambiguous-as-two-column,omitempty"`
|
|
||||||
Enable8BitControl *bool `hcl:"enable_8_bit_control" json:"enable-8-bit-control,omitempty"`
|
|
||||||
EnableBold *bool `hcl:"enable_bold" json:"enable-bold,omitempty"`
|
|
||||||
EnableBoldAsBright bool `hcl:"enable_bold_as_bright" json:"enable-bold-as-bright,omitempty"`
|
|
||||||
EnableClipboardNotice bool `hcl:"enable_clipboard_notice" json:"enable-clipboard-notice,omitempty"`
|
|
||||||
EnableClipboardWrite bool `hcl:"enable_clipboard_write" json:"enable-clipboard-write,omitempty"`
|
|
||||||
EnableDec12 bool `hcl:"enable_dec12" json:"enable-dec12,omitempty"`
|
|
||||||
EnableWebGL bool `json:"EnableWebGL,omitempty"`
|
|
||||||
Environment map[string]string `hcl:"environment" json:"environment,omitempty"`
|
|
||||||
FontFamily string `hcl:"font_family" json:"font-family,omitempty"`
|
|
||||||
FontSize int `hcl:"font_size" json:"font-size,omitempty"`
|
|
||||||
FontSmoothing string `hcl:"font_smoothing" json:"font-smoothing,omitempty"`
|
|
||||||
ForegroundColor string `hcl:"foreground_color" json:"foreground-color,omitempty"`
|
|
||||||
HomeKeysScroll bool `hcl:"home_keys_scroll" json:"home-keys-scroll,omitempty"`
|
|
||||||
Keybindings map[string]string `hcl:"keybindings" json:"keybindings,omitempty"`
|
|
||||||
MaxStringSequence int `hcl:"max_string_sequence" json:"max-string-sequence,omitempty"`
|
|
||||||
MediaKeysAreFkeys bool `hcl:"media_keys_are_fkeys" json:"media-keys-are-fkeys,omitempty"`
|
|
||||||
MetaSendsEscape bool `hcl:"meta_sends_escape" json:"meta-sends-escape,omitempty"`
|
|
||||||
MousePasteButton *int `hcl:"mouse_paste_button" json:"mouse-paste-button,omitempty"`
|
|
||||||
PageKeysScroll bool `hcl:"page_keys_scroll" json:"page-keys-scroll,omitempty"`
|
|
||||||
PassAltNumber *bool `hcl:"pass_alt_number" json:"pass-alt-number,omitempty"`
|
|
||||||
PassCtrlNumber *bool `hcl:"pass_ctrl_number" json:"pass-ctrl-number,omitempty"`
|
|
||||||
PassMetaNumber *bool `hcl:"pass_meta_number" json:"pass-meta-number,omitempty"`
|
|
||||||
PassMetaV bool `hcl:"pass_meta_v" json:"pass-meta-v,omitempty"`
|
|
||||||
ReceiveEncoding string `hcl:"receive_encoding" json:"receive-encoding,omitempty"`
|
|
||||||
ScrollOnKeystroke bool `hcl:"scroll_on_keystroke" json:"scroll-on-keystroke,omitempty"`
|
|
||||||
ScrollOnOutput bool `hcl:"scroll_on_output" json:"scroll-on-output,omitempty"`
|
|
||||||
ScrollbarVisible bool `hcl:"scrollbar_visible" json:"scrollbar-visible,omitempty"`
|
|
||||||
ScrollWheelMoveMultiplier int `hcl:"scroll_wheel_move_multiplier" json:"scroll-wheel-move-multiplier,omitempty"`
|
|
||||||
SendEncoding string `hcl:"send_encoding" json:"send-encoding,omitempty"`
|
|
||||||
ShiftInsertPaste bool `hcl:"shift_insert_paste" json:"shift-insert-paste,omitempty"`
|
|
||||||
UserCss string `hcl:"user_css" json:"user-css,omitempty"`
|
|
||||||
}
|
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
noesctmpl "text/template"
|
noesctmpl "text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -111,7 +112,12 @@ func (server *Server) Run(ctx context.Context, options ...RunOption) error {
|
|||||||
if server.options.EnableRandomUrl {
|
if server.options.EnableRandomUrl {
|
||||||
path = "/" + randomstring.Generate(server.options.RandomUrlLength) + "/"
|
path = "/" + randomstring.Generate(server.options.RandomUrlLength) + "/"
|
||||||
}
|
}
|
||||||
|
if !strings.HasPrefix(path, "/") {
|
||||||
|
path = "/" + path
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(path, "/") {
|
||||||
|
path = path + "/"
|
||||||
|
}
|
||||||
handlers := server.setupHandlers(cctx, cancel, path, counter)
|
handlers := server.setupHandlers(cctx, cancel, path, counter)
|
||||||
srv, err := server.setupHTTPServer(handlers)
|
srv, err := server.setupHTTPServer(handlers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type wsWrapper struct {
|
type wsWrapper struct {
|
||||||
@ -28,6 +31,11 @@ func (wsw *wsWrapper) Read(p []byte) (n int, err error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return reader.Read(p)
|
b, err := ioutil.ReadAll(reader)
|
||||||
|
if len(b) > len(p) {
|
||||||
|
return 0, errors.Wrapf(err, "Client message exceeded buffer size")
|
||||||
|
}
|
||||||
|
n = copy(p, b)
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
webtty/codecs.go
Normal file
19
webtty/codecs.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package webtty
|
||||||
|
|
||||||
|
type Decoder interface {
|
||||||
|
Decode(dst, src []byte) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Encoder interface {
|
||||||
|
Encode(dst, src []byte) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NullCodec struct{}
|
||||||
|
|
||||||
|
func (NullCodec) Encode(dst, src []byte) (int, error) {
|
||||||
|
return copy(dst, src), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NullCodec) Decode(dst, src []byte) (int, error) {
|
||||||
|
return copy(dst, src), nil
|
||||||
|
}
|
@ -13,6 +13,8 @@ const (
|
|||||||
Ping = '2'
|
Ping = '2'
|
||||||
// Notify that the browser size has been changed
|
// Notify that the browser size has been changed
|
||||||
ResizeTerminal = '3'
|
ResizeTerminal = '3'
|
||||||
|
// Change encoding
|
||||||
|
SetEncoding = '4'
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -24,6 +24,7 @@ type WebTTY struct {
|
|||||||
rows int
|
rows int
|
||||||
reconnect int // in seconds
|
reconnect int // in seconds
|
||||||
masterPrefs []byte
|
masterPrefs []byte
|
||||||
|
decoder Decoder
|
||||||
|
|
||||||
bufferSize int
|
bufferSize int
|
||||||
writeMutex sync.Mutex
|
writeMutex sync.Mutex
|
||||||
@ -43,6 +44,7 @@ func New(masterConn Master, slave Slave, options ...Option) (*WebTTY, error) {
|
|||||||
rows: 0,
|
rows: 0,
|
||||||
|
|
||||||
bufferSize: 1024,
|
bufferSize: 1024,
|
||||||
|
decoder: &NullCodec{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
@ -176,7 +178,13 @@ func (wt *WebTTY) handleMasterReadEvent(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := wt.slave.Write(data[1:])
|
var decodedBuffer = make([]byte, len(data))
|
||||||
|
n, err := wt.decoder.Decode(decodedBuffer, data[1:])
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to decode received data")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = wt.slave.Write(decodedBuffer[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to write received data to slave")
|
return errors.Wrapf(err, "failed to write received data to slave")
|
||||||
}
|
}
|
||||||
@ -187,6 +195,14 @@ func (wt *WebTTY) handleMasterReadEvent(data []byte) error {
|
|||||||
return errors.Wrapf(err, "failed to return Pong message to master")
|
return errors.Wrapf(err, "failed to return Pong message to master")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SetEncoding:
|
||||||
|
switch string(data[1:]) {
|
||||||
|
case "base64":
|
||||||
|
wt.decoder = base64.StdEncoding
|
||||||
|
case "null":
|
||||||
|
wt.decoder = NullCodec{}
|
||||||
|
}
|
||||||
|
|
||||||
case ResizeTerminal:
|
case ResizeTerminal:
|
||||||
if wt.columns != 0 && wt.rows != 0 {
|
if wt.columns != 0 && wt.rows != 0 {
|
||||||
break
|
break
|
||||||
|
Loading…
Reference in New Issue
Block a user