Compare commits

..

1 Commits

Author SHA1 Message Date
Eugene Pankov
44b4f5ccf8 wip 2018-08-18 21:47:11 +02:00
529 changed files with 20149 additions and 41084 deletions

View File

@@ -1,400 +0,0 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "mezner",
"name": "Russell Myers",
"avatar_url": "https://avatars2.githubusercontent.com/u/184085?v=4",
"profile": "http://www.russellmyers.com",
"contributions": [
"code"
]
},
{
"login": "ehwarren",
"name": "Austin Warren",
"avatar_url": "https://avatars1.githubusercontent.com/u/3991658?v=4",
"profile": "http://www.morwire.com",
"contributions": [
"code"
]
},
{
"login": "Drachenkaetzchen",
"name": "Felicia Hummel",
"avatar_url": "https://avatars1.githubusercontent.com/u/162974?v=4",
"profile": "https://github.com/Drachenkaetzchen",
"contributions": [
"code"
]
},
{
"login": "mikemaccana",
"name": "Mike MacCana",
"avatar_url": "https://avatars2.githubusercontent.com/u/172594?v=4",
"profile": "https://github.com/mikemaccana",
"contributions": [
"test",
"design"
]
},
{
"login": "yxuko",
"name": "Yacine Kanzari",
"avatar_url": "https://avatars1.githubusercontent.com/u/1786317?v=4",
"profile": "https://github.com/yxuko",
"contributions": [
"code"
]
},
{
"login": "BBJip",
"name": "BBJip",
"avatar_url": "https://avatars2.githubusercontent.com/u/32908927?v=4",
"profile": "https://github.com/BBJip",
"contributions": [
"code"
]
},
{
"login": "Futagirl",
"name": "Futagirl",
"avatar_url": "https://avatars2.githubusercontent.com/u/33533958?v=4",
"profile": "https://github.com/Futagirl",
"contributions": [
"design"
]
},
{
"login": "levrik",
"name": "Levin Rickert",
"avatar_url": "https://avatars3.githubusercontent.com/u/9491603?v=4",
"profile": "https://www.levrik.io",
"contributions": [
"code"
]
},
{
"login": "kwonoj",
"name": "OJ Kwon",
"avatar_url": "https://avatars2.githubusercontent.com/u/1210596?v=4",
"profile": "https://kwonoj.github.io",
"contributions": [
"code"
]
},
{
"login": "Domain",
"name": "domain",
"avatar_url": "https://avatars2.githubusercontent.com/u/903197?v=4",
"profile": "https://github.com/Domain",
"contributions": [
"plugin",
"code"
]
},
{
"login": "kbjr",
"name": "James Brumond",
"avatar_url": "https://avatars1.githubusercontent.com/u/195127?v=4",
"profile": "http://www.jbrumond.me",
"contributions": [
"plugin"
]
},
{
"login": "Tyriar",
"name": "Daniel Imms",
"avatar_url": "https://avatars0.githubusercontent.com/u/2193314?v=4",
"profile": "http://www.growingwiththeweb.com",
"contributions": [
"code",
"plugin",
"test"
]
},
{
"login": "baflo",
"name": "Florian Bachmann",
"avatar_url": "https://avatars2.githubusercontent.com/u/834350?v=4",
"profile": "https://github.com/baflo",
"contributions": [
"code"
]
},
{
"login": "mischah",
"name": "Michael Kühnel",
"avatar_url": "https://avatars2.githubusercontent.com/u/441011?v=4",
"profile": "http://michael-kuehnel.de",
"contributions": [
"code",
"design"
]
},
{
"login": "NieLeben",
"name": "Tilmann Meyer",
"avatar_url": "https://avatars3.githubusercontent.com/u/47182955?v=4",
"profile": "https://github.com/NieLeben",
"contributions": [
"code"
]
},
{
"login": "PMExtra",
"name": "PM Extra",
"avatar_url": "https://avatars3.githubusercontent.com/u/11289158?v=4",
"profile": "http://www.jubeat.net",
"contributions": [
"bug"
]
},
{
"login": "IgnusG",
"name": "Jonathan",
"avatar_url": "https://avatars1.githubusercontent.com/u/6438760?v=4",
"profile": "https://jjuhas.keybase.pub//",
"contributions": [
"code"
]
},
{
"login": "hammster",
"name": "Hans Koch",
"avatar_url": "https://avatars0.githubusercontent.com/u/1093709?v=4",
"profile": "https://hans-koch.me",
"contributions": [
"code"
]
},
{
"login": "ThePuzzlemaker",
"name": "Dak Smyth",
"avatar_url": "https://avatars3.githubusercontent.com/u/12666617?v=4",
"profile": "http://thepuzzlemaker.info",
"contributions": [
"code"
]
},
{
"login": "yfwz100",
"name": "Wang Zhi",
"avatar_url": "https://avatars2.githubusercontent.com/u/983211?v=4",
"profile": "http://yfwz100.github.io",
"contributions": [
"code"
]
},
{
"login": "jack1142",
"name": "jack1142",
"avatar_url": "https://avatars0.githubusercontent.com/u/6032823?v=4",
"profile": "https://github.com/jack1142",
"contributions": [
"code"
]
},
{
"login": "hdougie",
"name": "Howie Douglas",
"avatar_url": "https://avatars1.githubusercontent.com/u/450799?v=4",
"profile": "https://github.com/hdougie",
"contributions": [
"code"
]
},
{
"login": "ckaczor",
"name": "Chris Kaczor",
"avatar_url": "https://avatars2.githubusercontent.com/u/180906?v=4",
"profile": "https://chriskaczor.com",
"contributions": [
"code"
]
},
{
"login": "boxmein",
"name": "Johannes Kadak",
"avatar_url": "https://avatars1.githubusercontent.com/u/358714?v=4",
"profile": "https://www.boxmein.net",
"contributions": [
"code"
]
},
{
"login": "LeSeulArtichaut",
"name": "LeSeulArtichaut",
"avatar_url": "https://avatars1.githubusercontent.com/u/38361244?v=4",
"profile": "https://github.com/LeSeulArtichaut",
"contributions": [
"code"
]
},
{
"login": "CyrilTaylor",
"name": "Cyril Taylor",
"avatar_url": "https://avatars0.githubusercontent.com/u/12631466?v=4",
"profile": "https://github.com/CyrilTaylor",
"contributions": [
"code"
]
},
{
"login": "nstefanou",
"name": "nstefanou",
"avatar_url": "https://avatars3.githubusercontent.com/u/51129173?v=4",
"profile": "https://github.com/nstefanou",
"contributions": [
"code",
"plugin"
]
},
{
"login": "orin220444",
"name": "orin220444",
"avatar_url": "https://avatars3.githubusercontent.com/u/30747229?v=4",
"profile": "https://github.com/orin220444",
"contributions": [
"code"
]
},
{
"login": "Goobles",
"name": "Gobius Dolhain",
"avatar_url": "https://avatars3.githubusercontent.com/u/8776771?v=4",
"profile": "https://github.com/Goobles",
"contributions": [
"code"
]
},
{
"login": "3l0w",
"name": "Gwilherm Folliot",
"avatar_url": "https://avatars2.githubusercontent.com/u/37798980?v=4",
"profile": "https://github.com/3l0w",
"contributions": [
"code"
]
},
{
"login": "dimitory",
"name": "Dmitry Pronin",
"avatar_url": "https://avatars0.githubusercontent.com/u/475955?v=4",
"profile": "https://github.com/Dimitory",
"contributions": [
"code"
]
},
{
"login": "JonathanBeverley",
"name": "Jonathan Beverley",
"avatar_url": "https://avatars1.githubusercontent.com/u/20328966?v=4",
"profile": "https://github.com/JonathanBeverley",
"contributions": [
"code"
]
},
{
"login": "zend",
"name": "Zenghai Liang",
"avatar_url": "https://avatars1.githubusercontent.com/u/25160?v=4",
"profile": "https://github.com/zend",
"contributions": [
"code"
]
},
{
"login": "matishadow",
"name": "Mateusz Tracz",
"avatar_url": "https://avatars0.githubusercontent.com/u/9083085?v=4",
"profile": "https://about.me/matishadow",
"contributions": [
"code"
]
},
{
"login": "pinpins",
"name": "pinpin",
"avatar_url": "https://avatars3.githubusercontent.com/u/36234677?v=4",
"profile": "https://zergpool.com",
"contributions": [
"code"
]
},
{
"login": "TakuroOnoda",
"name": "Takuro Onoda",
"avatar_url": "https://avatars0.githubusercontent.com/u/1407926?v=4",
"profile": "https://github.com/TakuroOnoda",
"contributions": [
"code"
]
},
{
"login": "frauhottelmann",
"name": "frauhottelmann",
"avatar_url": "https://avatars2.githubusercontent.com/u/902705?v=4",
"profile": "https://github.com/frauhottelmann",
"contributions": [
"code"
]
},
{
"login": "VectorKappa",
"name": "Piotr Patalong",
"avatar_url": "https://avatars.githubusercontent.com/u/29167842?v=4",
"profile": "http://patalong.pl",
"contributions": [
"design"
]
},
{
"login": "clarkwang",
"name": "Clark Wang",
"avatar_url": "https://avatars.githubusercontent.com/u/157076?v=4",
"profile": "https://github.com/clarkwang",
"contributions": [
"code"
]
},
{
"login": "iamchating",
"name": "iamchating",
"avatar_url": "https://avatars.githubusercontent.com/u/7088153?v=4",
"profile": "https://github.com/iamchating",
"contributions": [
"code"
]
},
{
"login": "starxg",
"name": "starxg",
"avatar_url": "https://avatars.githubusercontent.com/u/34997494?v=4",
"profile": "https://github.com/starxg",
"contributions": [
"plugin"
]
},
{
"login": "lambdalisue",
"name": "Alisue",
"avatar_url": "https://avatars.githubusercontent.com/u/546312?v=4",
"profile": "http://hashnote.net/",
"contributions": [
"design"
]
}
],
"contributorsPerLine": 7,
"projectName": "terminus",
"projectOwner": "Eugeny",
"repoType": "github",
"repoHost": "https://github.com",
"commitConvention": "none",
"skipCi": true
}

View File

@@ -1,118 +0,0 @@
parser: '@typescript-eslint/parser'
parserOptions:
project:
- tsconfig.json
- '*/tsconfig.typings.json'
extends:
- 'plugin:@typescript-eslint/all'
plugins:
- '@typescript-eslint'
env:
browser: true
es6: true
node: true
commonjs: true
rules:
'@typescript-eslint/semi':
- error
- never
'@typescript-eslint/indent':
- error
- 4
'@typescript-eslint/explicit-member-accessibility':
- error
- accessibility: no-public
overrides:
parameterProperties: explicit
'@typescript-eslint/no-require-imports': off
'@typescript-eslint/no-parameter-properties': off
'@typescript-eslint/explicit-function-return-type': off
'@typescript-eslint/no-explicit-any': off
'@typescript-eslint/no-magic-numbers': off
'@typescript-eslint/member-delimiter-style': off
'@typescript-eslint/promise-function-async': off
'@typescript-eslint/require-array-sort-compare': off
'@typescript-eslint/no-floating-promises': off
'@typescript-eslint/prefer-readonly': off
'@typescript-eslint/require-await': off
'@typescript-eslint/strict-boolean-expressions': off
'@typescript-eslint/no-misused-promises': off
'@typescript-eslint/typedef': off
'@typescript-eslint/consistent-type-imports': off
'@typescript-eslint/sort-type-union-intersection-members': off
'@typescript-eslint/no-use-before-define':
- error
- classes: false
no-duplicate-imports: error
array-bracket-spacing:
- error
- never
block-scoped-var: error
brace-style: off
'@typescript-eslint/brace-style':
- error
- 1tbs
- allowSingleLine: true
computed-property-spacing:
- error
- never
comma-dangle: off
'@typescript-eslint/comma-dangle':
- error
- always-multiline
curly: error
eol-last: error
eqeqeq:
- error
- smart
max-depth:
- 1
- 5
max-statements:
- 1
- 80
no-multiple-empty-lines: error
no-mixed-spaces-and-tabs: error
no-trailing-spaces: error
'@typescript-eslint/no-unused-vars':
- error
- vars: all
args: after-used
argsIgnorePattern: ^_
no-undef: error
no-var: error
object-curly-spacing: off
'@typescript-eslint/object-curly-spacing':
- error
- always
quote-props:
- warn
- as-needed
- keywords: true
numbers: true
quotes: off
'@typescript-eslint/quotes':
- error
- single
- allowTemplateLiterals: true
'@typescript-eslint/no-confusing-void-expression': off
'@typescript-eslint/no-non-null-assertion': off
'@typescript-eslint/no-unnecessary-condition':
- error
- allowConstantLoopConditions: true
'@typescript-eslint/restrict-template-expressions': off
'@typescript-eslint/prefer-readonly-parameter-types': off
'@typescript-eslint/no-unsafe-member-access': off
'@typescript-eslint/no-unsafe-call': off
'@typescript-eslint/no-unsafe-return': off
'@typescript-eslint/no-unsafe-assignment': off
'@typescript-eslint/naming-convention': off
'@typescript-eslint/lines-between-class-members':
- error
- exceptAfterSingleLine: true
'@typescript-eslint/dot-notation': off
'@typescript-eslint/no-implicit-any-catch': off
'@typescript-eslint/member-ordering': off
'@typescript-eslint/no-var-requires': off
'@typescript-eslint/no-unsafe-argument': off
'@typescript-eslint/restrict-plus-operands': off

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +0,0 @@
github: eugeny
open_collective: terminus
ko_fi: eugeny

View File

@@ -1,74 +0,0 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/app"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-core"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-settings"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-terminal"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-local"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-community-color-schemes"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-electron"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-web"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-plugin-manager"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-ssh"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20

18
.github/stale.yml vendored
View File

@@ -1,18 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 180
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 90
# Issues with these labels will never be considered stale
exemptLabels:
- "T: Enhancement"
- "S: Confirmed"
# Label to use when marking an issue as stale
staleLabel: "S: Stale"
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed in two weeks unless you comment.
Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@@ -1,34 +0,0 @@
name: Docs
on: push
jobs:
build:
runs-on: ubuntu-18.04
if: ${{ github.actor != 'dependabot[bot]' }}
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.1.5
with:
node-version: 14
- name: Build
run: |
eval $(ssh-agent -s)
ssh-add <(echo "$DOCS_PRIVATE_KEY")
yarn cache clean
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
yarn run build:typings
yarn run docs
rsync -e "ssh -o StrictHostKeyChecking=no" -arv docs/api/ root@ajenti.org:/srv/terminus-docs/
env:
DOCS_PRIVATE_KEY: ${{ secrets.DOCS_PRIVATE_KEY }}

View File

@@ -1,31 +0,0 @@
name: Lint
on: [push, pull_request]
jobs:
build:
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.1.5
with:
node-version: 14
- name: Install deps
run: |
npm i -g yarn@1.19.1
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
- name: Build typings
run: yarn run build:typings
- name: Lint
run: yarn run lint

View File

@@ -1,107 +0,0 @@
name: Linux Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Install Node
uses: actions/setup-node@v2.1.5
with:
node-version: 14
- name: Install deps
run: |
sudo apt-get update
sudo apt-get install bsdtar zsh
npm i -g yarn@1.19.1
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
npm run patch
- name: Build native deps
run: scripts/build-native.js
- name: Webpack
run: yarn run build
- name: Prepackage plugins
run: scripts/prepackage-plugins.js
- name: Build packages
run: scripts/build-linux.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
USE_HARD_LINKS: false
# DEBUG: electron-builder,electron-builder:*
- name: Build web resources
run: zsh -c 'tar czf terminus-web.tar.gz (terminus-*|web)/dist'
- name: Upload symbols
run: |
sudo npm install -g @sentry/cli --unsafe-perm
./scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts
run: |
mkdir artifact-deb
mv dist/*.deb artifact-deb/ || true
mkdir artifact-rpm
mv dist/*.rpm artifact-rpm/ || true
mkdir artifact-pacman
mv dist/*.pacman artifact-pacman/ || true
mkdir artifact-snap
mv dist/*.snap artifact-snap/ || true
mkdir artifact-tar.gz
mv dist/*.tar.gz artifact-tar.gz/ || true
mkdir artifact-web
mv terminus-web.tar.gz artifact-web/ || true
- uses: actions/upload-artifact@master
name: Upload DEB
with:
name: Linux DEB
path: artifact-deb
- uses: actions/upload-artifact@master
name: Upload RPM
with:
name: Linux RPM
path: artifact-rpm
- uses: actions/upload-artifact@master
name: Upload Pacman Package
with:
name: Linux Pacman
path: artifact-pacman
- uses: actions/upload-artifact@master
name: Upload Snap
with:
name: Linux Snap
path: artifact-snap
- uses: actions/upload-artifact@master
name: Upload Linux tarball
with:
name: Linux tarball
path: artifact-tar.gz
- uses: actions/upload-artifact@master
name: Upload web tarball
with:
name: Web tarball
path: artifact-web

View File

@@ -1,99 +0,0 @@
name: macOS Build
on: [push, pull_request]
jobs:
build:
runs-on: macos-11.0
strategy:
matrix:
include:
- arch: x86_64
- arch: arm64
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.1.5
with:
node-version: 14
- name: Install deps
run: |
sudo npm i -g yarn@1.22.1
cd app
yarn
cd ..
rm app/node_modules/.yarn-integrity
yarn
- name: Build native deps
run: scripts/build-native.js
env:
ARCH: ${{matrix.arch}}
- name: Build native deps
run: |
rm -rf app/node_modules/cpu-features
rm -rf app/node_modules/ssh2/crypto/build
if: ${{ matrix.arch == 'arm64' }}
- name: Webpack
run: yarn run build
- name: Prepackage plugins
run: scripts/prepackage-plugins.js
env:
ARCH: ${{matrix.arch}}
- run: sed -i '' 's/updateInfo = await/\/\/updateInfo = await/g' node_modules/app-builder-lib/out/targets/ArchiveTarget.js
- name: Build and sign packages
run: scripts/build-macos.js
if: github.repository == 'Eugeny/terminus' && github.event_name == 'push'
env:
ARCH: ${{matrix.arch}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPSTORE_USERNAME: ${{ secrets.APPSTORE_USERNAME }}
APPSTORE_PASSWORD: ${{ secrets.APPSTORE_PASSWORD }}
USE_HARD_LINKS: false
# DEBUG: electron-builder,electron-builder:*
- name: Build packages without signing
run: scripts/build-macos.js
if: github.repository != 'Eugeny/terminus' || github.event_name != 'push'
env:
ARCH: ${{matrix.arch}}
# DEBUG: electron-builder,electron-builder:*
- name: Upload symbols
run: |
sudo npm install -g @sentry/cli --unsafe-perm
./scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts
run: |
mkdir artifact-pkg
mv dist/*.pkg artifact-pkg/
mkdir artifact-zip
mv dist/*.zip artifact-zip/
- uses: actions/upload-artifact@master
name: Upload PKG
with:
name: macOS .pkg (${{matrix.arch}})
path: artifact-pkg
- uses: actions/upload-artifact@master
name: Upload ZIP
with:
name: macOS .zip (${{matrix.arch}})
path: artifact-zip

View File

@@ -1,66 +0,0 @@
name: Windows Build
on: [push, pull_request]
jobs:
build:
runs-on: windows-2016
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
with:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.1.5
with:
node-version: 14
- name: Build
shell: powershell
run: |
npm i -g yarn@1.19.1
yarn
node scripts/build-native.js
yarn run build
node scripts/prepackage-plugins.js
- name: Build and sign packages
run: node scripts/build-windows.js
if: github.repository == 'Eugeny/terminus' && github.event_name == 'push'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
# DEBUG: electron-builder,electron-builder:*
- name: Build packages without signing
run: node scripts/build-windows.js
if: github.repository != 'Eugeny/terminus' || github.event_name != 'push'
- name: Upload symbols
run: |
npm install @sentry/cli
node scripts/sentry-upload.js
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
- name: Package artifacts
run: |
mkdir artifact-setup
mv dist/*-setup.exe artifact-setup/
mkdir artifact-portable
mv dist/*-portable.zip artifact-portable/
- uses: actions/upload-artifact@master
name: Upload installer
with:
name: Installer
path: artifact-setup
- uses: actions/upload-artifact@master
name: Upload portable build
with:
name: Portable build
path: artifact-portable

17
.gitignore vendored
View File

@@ -5,17 +5,11 @@ node_modules
build/files.wxs
dist
*/dist
*/typings
*.tsbuildinfo
*.xcworkspacedata
*.xcuserstate
*.wixpdb
.DS_Store
.DS_Store?
coverage
.nyc_output
npm-debug.log
@@ -23,14 +17,3 @@ npm-debug.log
builtin-plugins
package-lock.json
yarn-error.log
docs/api
.travis.ssh.key
*.code-workspace
.electron-symbols
sentry.properties
sentry-symbols.js
terminus-ssh/util/pagent.exe
*.psd

View File

@@ -1,14 +0,0 @@
pull_request_rules:
- name: automatic merge on CI success and review
conditions:
- "status-success=Windows Build / Build"
- "status-success=macOS Build / Build"
- "status-success=Linux Build / Build"
- "status-success=continuous-integration/appveyor/pr"
- "#approved-reviews-by>=1"
- "#changes-requested-reviews-by=0"
- base=master
actions:
merge:
method: merge
strict: true

View File

@@ -1,36 +1,46 @@
language: node_js
node_js: 15
node_js: 7
services:
- docker
cache:
directories:
- node_modules
- app/node_modules
stages:
- Build
- name: Docs
- build
- name: deploy
if: branch = master
jobs:
include:
- stage: 'Docs'
- stage: build
os: linux
if: branch = master
script:
- '[ -z "${encrypted_4e2fb4889ef8_iv}" ] && exit 0 || true'
- set -e
- openssl aes-256-cbc -K $encrypted_4e2fb4889ef8_key -iv $encrypted_4e2fb4889ef8_iv -in .travis.ssh.key.enc -out .travis.ssh.key -d
- eval "$(ssh-agent -s)"
- chmod 600 .travis.ssh.key
- ssh-add .travis.ssh.key
- yarn
- yarn run docs
- rsync -e "ssh -o StrictHostKeyChecking=no" -arv docs/api/ root@ajenti.org:/srv/terminus-docs/
env: BUILD_FOR=linux
script: ./travis.sh
dist: xenial
- stage: build
os: osx
env: BUILD_FOR=macos
script: ./travis.sh
- stage: deploy
os: linux
env: BUILD_FOR=linux STAGE=deploy
script: ./travis.sh
dist: trusty
sudo: false
addons:
apt:
packages:
- rpm
- wine
- mono-runtime
- yarn
- libsecret-1-dev
sources:
- sourceline: 'deb https://dl.yarnpkg.com/debian/ stable main'
key_url: 'https://dl.yarnpkg.com/debian/pubkey.gpg'

View File

@@ -10,25 +10,27 @@ First, install the dependencies:
```
# macOS/Linux:
yarn
npm install
./scripts/install-deps.js
./scripts/build-native.js
# Windows:
npm -g install windows-build-tools
yarn
npm install
node scripts\install-deps.js
node scripts\build-native.js
```
Now, check if your build is working:
```
yarn run build
npm run build
```
Start Terminus with
```
yarn start
npm start
```
# Project layout
@@ -42,12 +44,9 @@ terminus
├─ scripts # Maintenance scripts
├─ terminus-community-color-schemes # Plugin that provides color schemes
├─ terminus-core # Plugin that provides base UI and tab management
├─ terminus-electron # Plugin that provides Electron-specific functions
└─ terminus-local # Plugin that provides local shells and profiles
├─ terminus-plugin-manager # Plugin that installs other plugins
├─ terminus-settings # Plugin that provides the settings tab
└─ terminus-terminal # Plugin that provides terminal tabs
├─ terminus-web # Plugin that provides web-specific functions
```
# Plugin layout
@@ -64,7 +63,7 @@ terminus-pluginname
| └─ index.ts # Module entry point
├─ package.json
├─ tsconfig.json
└─ webpack.config.js
└─ webpack.config.js
```
# Plugins
@@ -93,11 +92,11 @@ Plugins provide functionality by exporting singular or multi providers:
```javascript
import { NgModule, Injectable } from '@angular/core'
import { ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
import { ToolbarButtonProvider, IToolbarButton } from 'terminus-core'
@Injectable()
export class MyButtonProvider extends ToolbarButtonProvider {
provide (): ToolbarButton[] {
provide (): IToolbarButton[] {
return [{
icon: 'star',
title: 'Foobar',
@@ -118,6 +117,4 @@ export default class MyModule { }
```
See `terminus-core/src/api.ts`, `terminus-settings/src/api.ts`, `terminus-local/src/api.ts` and `terminus-terminal/src/api.ts` for the available extension points.
Publish your plugin on NPM with a `terminus-plugin` keyword to make it appear in the Plugin Manager.
See `terminus-core/src/api.ts`, `terminus-settings/src/api.ts` and `terminus-terminal/src/api.ts` for the available extension points.

189
README.md
View File

@@ -1,187 +1,42 @@
![](docs/readme.png)
![](https://github.com/Eugeny/terminus/raw/master/docs/readme.png)
<p align="center">
<a href="https://github.com/Eugeny/terminus/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/eugeny/terminus/total.svg?label=RELEASE&logo=github&style=for-the-badge"></a> <a href="https://nightly.link/Eugeny/terminus/workflows/windows/master"><img src="https://shields.io/badge/-Nightly-blue?logo=windows&style=for-the-badge"/></a> <a href="https://nightly.link/Eugeny/terminus/workflows/macos/master"><img src="https://shields.io/badge/-Nightly-black?logo=apple&style=for-the-badge"/></a> <a href="https://nightly.link/Eugeny/terminus/workflows/linux/master"><img src="https://shields.io/badge/-Nightly-orange?logo=linux&style=for-the-badge"/></a> <a href="https://gitter.im/terminus-terminal/community"><img alt="Gitter" src="https://img.shields.io/gitter/room/terminus/community.svg?color=magenta&logo=gitter&style=for-the-badge"></a>
</p>
[![Build Status](https://travis-ci.org/Eugeny/terminus.svg?branch=master)](https://travis-ci.org/Eugeny/terminus) [![Build status](https://ci.appveyor.com/api/projects/status/wnnq4hm5mbd9rgoy?svg=true)](https://ci.appveyor.com/project/Eugeny/terminus) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Eugeny/terminus/master/LICENSE) [![Downloads](https://img.shields.io/badge/downloads-latest_release-brightgreen.svg)](https://github.com/Eugeny/terminus/releases/latest)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus?ref=badge_shield)
----
**Terminus** is a highly configurable terminal emulator, SSH and serial client for Windows, macOS and Linux
**Terminus** is a terminal heavily inspired by Hyper. It is, however, designed for people who need to get things done.
* Integrated SSH client and connection manager
* Integrated serial terminal
* Theming and color schemes
* Fully configurable shortcuts and multi-chord shortcuts
* Split panes
* Remembers your tabs
* PowerShell (and PS Core), WSL, Git-Bash, Cygwin, Cmder and CMD support
* Direct file transfer from/to SSH sessions via Zmodem
* Full Unicode support including double-width characters
* Doesn't choke on fast-flowing outputs
* Proper shell experience on Windows including tab completion (via Clink)
* Integrated encrypted container for SSH secrets and configuration
[![Buy me a coffee](https://github.com/Eugeny/terminus/raw/master/docs/kofi.png)](https://ko-fi.com/eugeny)
* Runs on Windows, macOS and Linux
* Theming and color schemes
* Fully configurable shortcuts
* Full Unicode support including double-width characters
* Doesn't choke on fast-flowing outputs
* Proper shell-like experience on Windows including tab completion (via Clink)
* CMD, PowerShell, PowerShell Core, Cygwin, Cmder, Git-Bash and WSL (Bash on Windows) support
* Tab persistence on macOS and Linux
---
# Contents
- [Contents](#contents)
- [What Terminus is and isn't](#what-terminus-is-and-isnt)
- [Terminal features](#terminal-features)
- [SSH Client](#ssh-client)
- [Serial Terminal](#serial-terminal)
- [Portable](#portable)
- [Plugins](#plugins)
- [Themes](#themes)
- [Contributing](#contributing)
<a name="about"></a>
# What Terminus is and isn't
* **Terminus is** an alternative to Windows' standard terminal (conhost), PowerShell ISE, PuTTY or iTerm
* **Terminus is not** a new shell or a MinGW or Cygwin replacement. Neither is it lightweight - if RAM usage is of importance, consider [Conemu](https://conemu.github.io) or [Alacritty](https://github.com/jwilm/alacritty)
<a name="terminal"></a>
# Terminal features
![](docs/readme-terminal.png)
* A V220 terminal + various extensions
* Multiple nested split panes
* Tabs on any side of the window
* Optional dockable window with a global spawn hotkey ("Quake console")
* Progress detection
* Notification on process completion
* Bracketed paste, multiline paste warnings
* Font ligatures
* Custom shell profiles
* Optional RMB paste and copy-on select (PuTTY style)
<a name="ssh"></a>
# SSH Client
![](docs/readme-ssh.png)
* SSH2 client with a connection manager
* X11 and port forwarding
* Automatic jump host management
* Agent forwarding (incl. Pageant and Windows native OpenSSH Agent)
* Login scripts
<a name="serial"></a>
# Serial Terminal
* Saved connections
* Readline input support
* Optional hex byte-by-byte input and hexdump output
* Newline conversion
* Automatic reconnection
<a name="portable"></a>
# Portable
Terminus will run as a portable app on Windows, if you create a `data` folder in the same location where `Terminus.exe` lives.
<a name="plugins"></a>
# Plugins
Plugins and themes can be installed directly from the Settings view inside Terminus.
Plugins can be installed directly from the Settings view inside Terminus.
* [clickable-links](https://github.com/Eugeny/terminus-clickable-links) - makes paths and URLs in the terminal clickable
* [title-control](https://github.com/kbjr/terminus-title-control) - allows modifying the title of the terminal tabs by providing a prefix, suffix, and/or strings to be removed
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - quickly send commands to one or all terminal tabs
* [save-output](https://github.com/Eugeny/terminus-save-output) - record terminal output into a file
* [scrollbar](https://github.com/kbjr/terminus-scrollbar) - adds a scrollbar to hterm tabs
* [sync-config](https://github.com/starxg/terminus-sync-config) - sync the config to Gist or Gitee
<a name="themes"></a>
# Themes
* [hype](https://github.com/Eugeny/terminus-theme-hype) - a Hyper inspired theme
* [relaxed](https://github.com/Relaxed-Theme/relaxed-terminal-themes#terminus) - the Relaxed theme for Terminus
* [gruvbox](https://github.com/porkloin/terminus-theme-gruvbox)
* [windows10](https://www.npmjs.com/package/terminus-theme-windows10)
* [altair](https://github.com/yxuko/terminus-altair)
<a name="contributing"></a>
# Contributing
Pull requests and plugins are welcome!
See [HACKING.md](https://github.com/Eugeny/terminus/blob/master/HACKING.md) and [API docs](http://ajenti.org/terminus-docs/) for information of how the project is laid out, and a very brief plugin development tutorial.
* [theme-hype](https://github.com/Eugeny/terminus-theme-hype) - a Hyper inspired theme
* [shell-selector](https://github.com/Eugeny/terminus-shell-selector) - a quick shell selector pane
* [title-control](https://github.com/kbjr/terminus-scrollbar) - allows modifying the title of the terminal tabs by providing a prefix, suffix, and/or strings to be removed
* [scrollbar](https://github.com/kbjr/terminus-scrollbar) - adds a scrollbar to terminal tabs
---
<a name="contributors"></a>
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
# Contributing
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://www.russellmyers.com"><img src="https://avatars2.githubusercontent.com/u/184085?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Russell Myers</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=mezner" title="Code">💻</a></td>
<td align="center"><a href="http://www.morwire.com"><img src="https://avatars1.githubusercontent.com/u/3991658?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Austin Warren</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=ehwarren" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Drachenkaetzchen"><img src="https://avatars1.githubusercontent.com/u/162974?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Felicia Hummel</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Drachenkaetzchen" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mikemaccana"><img src="https://avatars2.githubusercontent.com/u/172594?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mike MacCana</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=mikemaccana" title="Tests">⚠️</a> <a href="#design-mikemaccana" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/yxuko"><img src="https://avatars1.githubusercontent.com/u/1786317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yacine Kanzari</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=yxuko" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/BBJip"><img src="https://avatars2.githubusercontent.com/u/32908927?v=4?s=100" width="100px;" alt=""/><br /><sub><b>BBJip</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=BBJip" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Futagirl"><img src="https://avatars2.githubusercontent.com/u/33533958?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Futagirl</b></sub></a><br /><a href="#design-Futagirl" title="Design">🎨</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.levrik.io"><img src="https://avatars3.githubusercontent.com/u/9491603?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Levin Rickert</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=levrik" title="Code">💻</a></td>
<td align="center"><a href="https://kwonoj.github.io"><img src="https://avatars2.githubusercontent.com/u/1210596?v=4?s=100" width="100px;" alt=""/><br /><sub><b>OJ Kwon</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=kwonoj" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Domain"><img src="https://avatars2.githubusercontent.com/u/903197?v=4?s=100" width="100px;" alt=""/><br /><sub><b>domain</b></sub></a><br /><a href="#plugin-Domain" title="Plugin/utility libraries">🔌</a> <a href="https://github.com/Eugeny/terminus/commits?author=Domain" title="Code">💻</a></td>
<td align="center"><a href="http://www.jbrumond.me"><img src="https://avatars1.githubusercontent.com/u/195127?v=4?s=100" width="100px;" alt=""/><br /><sub><b>James Brumond</b></sub></a><br /><a href="#plugin-kbjr" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="http://www.growingwiththeweb.com"><img src="https://avatars0.githubusercontent.com/u/2193314?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Imms</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Tyriar" title="Code">💻</a> <a href="#plugin-Tyriar" title="Plugin/utility libraries">🔌</a> <a href="https://github.com/Eugeny/terminus/commits?author=Tyriar" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/baflo"><img src="https://avatars2.githubusercontent.com/u/834350?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Florian Bachmann</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=baflo" title="Code">💻</a></td>
<td align="center"><a href="http://michael-kuehnel.de"><img src="https://avatars2.githubusercontent.com/u/441011?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Kühnel</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=mischah" title="Code">💻</a> <a href="#design-mischah" title="Design">🎨</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/NieLeben"><img src="https://avatars3.githubusercontent.com/u/47182955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tilmann Meyer</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=NieLeben" title="Code">💻</a></td>
<td align="center"><a href="http://www.jubeat.net"><img src="https://avatars3.githubusercontent.com/u/11289158?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PM Extra</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/issues?q=author%3APMExtra" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://jjuhas.keybase.pub//"><img src="https://avatars1.githubusercontent.com/u/6438760?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=IgnusG" title="Code">💻</a></td>
<td align="center"><a href="https://hans-koch.me"><img src="https://avatars0.githubusercontent.com/u/1093709?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hans Koch</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=hammster" title="Code">💻</a></td>
<td align="center"><a href="http://thepuzzlemaker.info"><img src="https://avatars3.githubusercontent.com/u/12666617?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dak Smyth</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=ThePuzzlemaker" title="Code">💻</a></td>
<td align="center"><a href="http://yfwz100.github.io"><img src="https://avatars2.githubusercontent.com/u/983211?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wang Zhi</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=yfwz100" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jack1142"><img src="https://avatars0.githubusercontent.com/u/6032823?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jack1142</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=jack1142" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/hdougie"><img src="https://avatars1.githubusercontent.com/u/450799?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Howie Douglas</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=hdougie" title="Code">💻</a></td>
<td align="center"><a href="https://chriskaczor.com"><img src="https://avatars2.githubusercontent.com/u/180906?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chris Kaczor</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=ckaczor" title="Code">💻</a></td>
<td align="center"><a href="https://www.boxmein.net"><img src="https://avatars1.githubusercontent.com/u/358714?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johannes Kadak</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=boxmein" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/LeSeulArtichaut"><img src="https://avatars1.githubusercontent.com/u/38361244?v=4?s=100" width="100px;" alt=""/><br /><sub><b>LeSeulArtichaut</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=LeSeulArtichaut" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/CyrilTaylor"><img src="https://avatars0.githubusercontent.com/u/12631466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cyril Taylor</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=CyrilTaylor" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nstefanou"><img src="https://avatars3.githubusercontent.com/u/51129173?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nstefanou</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=nstefanou" title="Code">💻</a> <a href="#plugin-nstefanou" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="https://github.com/orin220444"><img src="https://avatars3.githubusercontent.com/u/30747229?v=4?s=100" width="100px;" alt=""/><br /><sub><b>orin220444</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=orin220444" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Goobles"><img src="https://avatars3.githubusercontent.com/u/8776771?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gobius Dolhain</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=Goobles" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/3l0w"><img src="https://avatars2.githubusercontent.com/u/37798980?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gwilherm Folliot</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=3l0w" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Dimitory"><img src="https://avatars0.githubusercontent.com/u/475955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dmitry Pronin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=dimitory" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/JonathanBeverley"><img src="https://avatars1.githubusercontent.com/u/20328966?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan Beverley</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=JonathanBeverley" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/zend"><img src="https://avatars1.githubusercontent.com/u/25160?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zenghai Liang</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=zend" title="Code">💻</a></td>
<td align="center"><a href="https://about.me/matishadow"><img src="https://avatars0.githubusercontent.com/u/9083085?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mateusz Tracz</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=matishadow" title="Code">💻</a></td>
<td align="center"><a href="https://zergpool.com"><img src="https://avatars3.githubusercontent.com/u/36234677?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pinpin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=pinpins" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/TakuroOnoda"><img src="https://avatars0.githubusercontent.com/u/1407926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Takuro Onoda</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=TakuroOnoda" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/frauhottelmann"><img src="https://avatars2.githubusercontent.com/u/902705?v=4?s=100" width="100px;" alt=""/><br /><sub><b>frauhottelmann</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=frauhottelmann" title="Code">💻</a></td>
<td align="center"><a href="http://patalong.pl"><img src="https://avatars.githubusercontent.com/u/29167842?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piotr Patalong</b></sub></a><br /><a href="#design-VectorKappa" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/clarkwang"><img src="https://avatars.githubusercontent.com/u/157076?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Clark Wang</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=clarkwang" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/iamchating"><img src="https://avatars.githubusercontent.com/u/7088153?v=4?s=100" width="100px;" alt=""/><br /><sub><b>iamchating</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=iamchating" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/starxg"><img src="https://avatars.githubusercontent.com/u/34997494?v=4?s=100" width="100px;" alt=""/><br /><sub><b>starxg</b></sub></a><br /><a href="#plugin-starxg" title="Plugin/utility libraries">🔌</a></td>
<td align="center"><a href="http://hashnote.net/"><img src="https://avatars.githubusercontent.com/u/546312?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alisue</b></sub></a><br /><a href="#design-lambdalisue" title="Design">🎨</a></td>
</tr>
</table>
Pull requests and plugins are welcome! Publish your plugin on NPM with a `terminus-plugin` keyword to make it appear in the Plugin Manager.
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
See [HACKING.md](https://github.com/Eugeny/terminus/blob/master/HACKING.md) for a very brief plugin development tutorial!
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
## License
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2FEugeny%2Fterminus?ref=badge_large)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -1 +1,91 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0" y="0" version="1.1" viewBox="0 0 1024 1024" xml:space="preserve" style="enable-background:new 0 0 1024 1024"><style type="text/css">.st0{fill:url(#SVGID_1_)}.st1{opacity:.16;fill:url(#SVGID_2_)}.st2{fill:url(#SVGID_3_)}.st3{opacity:.16;fill:url(#SVGID_4_)}.st4{fill:url(#SVGID_5_)}.st5{opacity:.15;fill:url(#SVGID_6_)}.st6{fill:url(#SVGID_7_)}</style><g><linearGradient id="SVGID_1_" x1="260.967" x2="919.184" y1="871.181" y2="491.16" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#669abd"/><stop offset="1" style="stop-color:#77dbdb"/></linearGradient><polygon points="297.54 934.52 882.6 596.72 882.61 427.82 297.54 765.65" class="st0"/><linearGradient id="SVGID_2_" x1="553.505" x2="626.647" y1="617.828" y2="744.513" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="297.54 934.52 882.6 596.72 882.61 427.82 297.54 765.65" class="st1"/></g><g><linearGradient id="SVGID_3_" x1="114.663" x2="334.091" y1="744.528" y2="871.214" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="151.23 681.18 151.22 850.09 297.54 934.52 297.54 765.65" class="st2"/><linearGradient id="SVGID_4_" x1="260.948" x2="187.806" y1="744.528" y2="871.213" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="151.23 681.18 151.22 850.09 297.54 934.52 297.54 765.65" class="st3"/></g><g><linearGradient id="SVGID_5_" x1="114.663" x2="553.503" y1="237.793" y2="491.157" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="151.23 174.45 151.21 343.36 443.79 512.27 590.08 427.81" class="st4"/><linearGradient id="SVGID_6_" x1="370.656" x2="297.509" y1="301.128" y2="427.822" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="151.23 174.45 151.21 343.36 443.79 512.27 590.08 427.81" class="st5"/></g><linearGradient id="SVGID_7_" x1="78.091" x2="736.337" y1="554.498" y2="174.459" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#ccecff"/><stop offset="1" style="stop-color:#9feced"/></linearGradient><polygon points="297.51 765.64 151.23 681.18 590.08 427.81 151.23 174.45 297.5 90 882.61 427.82" class="st6"/></svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="150mm"
height="150mm"
viewBox="0 0 150 150"
version="1.1"
id="svg8"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="logo.svg"
inkscape:export-filename="/home/eugene/Work/term/build/icons/512x512.png"
inkscape:export-xdpi="86.699997"
inkscape:export-ydpi="86.699997">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="85.897128"
inkscape:cy="375.72042"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="692"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-intersection-paths="true"
inkscape:object-paths="true" />
<metadata
id="metadata5">
<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>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-10.356544,-82.309525)">
<path
inkscape:connector-curvature="0"
id="path138"
style="opacity:0.9;fill:#bfd9f1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.12037313px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="M 33.048081,103.66303 101.30357,143.02426 80.80219,154.86063 33.048089,125.73315 Z"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
id="path116"
style="opacity:0.9;fill:#6666af;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.12037313px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 141.59934,143.95811 0.051,23.16109 -87.420905,49.42651 -0.0034,-22.16232 z"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
id="path118"
style="opacity:0.9;fill:#bfd9f1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.12037313px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 33.233182,182.28294 20.992812,12.1202 0.0034,22.16208 -20.996251,-12.19239 z"
sodipodi:nodetypes="ccccc" />
<path
style="opacity:0.9;fill:#9dbef0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.12649226px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 52.236336,92.196079 -19.484508,11.249681 68.551742,39.5785 -68.366041,39.4708 21.107487,12.18633 68.366044,-39.4708 19.48451,-11.24968 z"
id="path134"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

50
app/bufferizedPTY.js Normal file
View File

@@ -0,0 +1,50 @@
module.exports = function patchPTYModule (path) {
const mod = require(path)
const oldSpawn = mod.spawn
if (mod.patched) {
return mod
}
mod.patched = true
mod.spawn = (file, args, opt) => {
let terminal = oldSpawn(file, args, opt)
let timeout = null
let buffer = ''
let lastFlush = 0
let nextTimeout = 0
const maxWindow = 250
const minWindow = 50
function flush () {
if (buffer) {
terminal.emit('data-buffered', buffer)
}
lastFlush = Date.now()
buffer = ''
}
function reschedule () {
if (timeout) {
clearTimeout(timeout)
}
nextTimeout = Date.now() + minWindow
timeout = setTimeout(() => {
timeout = null
flush()
}, minWindow)
}
terminal.on('data', data => {
buffer += data
if (Date.now() - lastFlush > maxWindow) {
flush()
} else {
if (Date.now() > nextTimeout - (minWindow / 10)) {
reschedule()
}
}
})
return terminal
}
return mod
}

View File

@@ -1,4 +0,0 @@
owner: eugeny
repo: terminus
provider: github
updaterCacheDirName: terminus-updater

View File

@@ -1,5 +1,5 @@
doctype html
html.terminus
html
head
meta(charset='UTF-8')
base(href='index.html')
@@ -8,15 +8,17 @@ html.terminus
window.nodeRequire = require
script(src='./preload.js')
script(src='./bundle.js', defer)
style#custom-css
style.
body { transition: 0.5s background; }
body
style#custom-css
app-root
.preload-logo
div
.terminus-logo
h1.terminus-title Terminus
sup α
sup α
.progress
.bar(style='width: 0%')

View File

@@ -1,273 +0,0 @@
import { app, ipcMain, Menu, Tray, shell, screen, globalShortcut, MenuItemConstructorOptions } from 'electron'
import * as promiseIpc from 'electron-promise-ipc'
import * as remote from '@electron/remote/main'
import * as path from 'path'
import * as fs from 'fs'
import { loadConfig } from './config'
import { Window, WindowOptions } from './window'
import { pluginManager } from './pluginManager'
import { PTYManager } from './pty'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch (_) { }
export class Application {
private tray?: Tray
private ptyManager = new PTYManager()
private windows: Window[] = []
userPluginsPath: string
constructor () {
remote.initialize()
this.useBuiltinGraphics()
this.ptyManager.init(this)
ipcMain.on('app:config-change', (_event, config) => {
this.broadcast('host:config-change', config)
})
ipcMain.on('app:register-global-hotkey', (_event, specs) => {
globalShortcut.unregisterAll()
for (const spec of specs) {
globalShortcut.register(spec, () => {
this.onGlobalHotkey()
})
}
})
;(promiseIpc as any).on('plugin-manager:install', (name, version) => {
return pluginManager.install(this.userPluginsPath, name, version)
})
;(promiseIpc as any).on('plugin-manager:uninstall', (name) => {
return pluginManager.uninstall(this.userPluginsPath, name)
})
const configData = loadConfig()
if (process.platform === 'linux') {
app.commandLine.appendSwitch('no-sandbox')
if (((configData.appearance || {}).opacity || 1) !== 1) {
app.commandLine.appendSwitch('enable-transparent-visuals')
app.disableHardwareAcceleration()
}
}
this.userPluginsPath = path.join(
app.getPath('userData'),
'plugins',
)
if (!fs.existsSync(this.userPluginsPath)) {
fs.mkdirSync(this.userPluginsPath)
}
app.commandLine.appendSwitch('disable-http-cache')
app.commandLine.appendSwitch('max-active-webgl-contexts', '9000')
app.commandLine.appendSwitch('lang', 'EN')
app.allowRendererProcessReuse = false
for (const flag of configData.flags || [['force_discrete_gpu', '0']]) {
app.commandLine.appendSwitch(flag[0], flag[1])
}
}
init (): void {
screen.on('display-metrics-changed', () => this.broadcast('host:display-metrics-changed'))
screen.on('display-added', () => this.broadcast('host:displays-changed'))
screen.on('display-removed', () => this.broadcast('host:displays-changed'))
}
async newWindow (options?: WindowOptions): Promise<Window> {
const window = new Window(this, options)
this.windows.push(window)
window.visible$.subscribe(visible => {
if (visible) {
this.disableTray()
} else {
this.enableTray()
}
})
window.closed$.subscribe(() => {
this.windows = this.windows.filter(x => x !== window)
})
if (process.platform === 'darwin') {
this.setupMenu()
}
await window.ready
return window
}
onGlobalHotkey (): void {
if (this.windows.some(x => x.isFocused() && x.isVisible())) {
for (const window of this.windows) {
window.hide()
}
} else {
for (const window of this.windows) {
window.present()
}
}
}
presentAllWindows (): void {
for (const window of this.windows) {
window.present()
}
}
broadcast (event: string, ...args: any[]): void {
for (const window of this.windows) {
window.send(event, ...args)
}
}
async send (event: string, ...args: any[]): Promise<void> {
if (!this.hasWindows()) {
await this.newWindow()
}
this.windows.filter(w => !w.isDestroyed())[0].send(event, ...args)
}
enableTray (): void {
if (this.tray || process.platform === 'linux') {
return
}
if (process.platform === 'darwin') {
this.tray = new Tray(`${app.getAppPath()}/assets/tray-darwinTemplate.png`)
this.tray.setPressedImage(`${app.getAppPath()}/assets/tray-darwinHighlightTemplate.png`)
} else {
this.tray = new Tray(`${app.getAppPath()}/assets/tray.png`)
}
this.tray.on('click', () => setTimeout(() => this.focus()))
const contextMenu = Menu.buildFromTemplate([{
label: 'Show',
click: () => this.focus(),
}])
if (process.platform !== 'darwin') {
this.tray.setContextMenu(contextMenu)
}
this.tray.setToolTip(`Terminus ${app.getVersion()}`)
}
disableTray (): void {
if (process.platform === 'linux') {
return
}
this.tray?.destroy()
this.tray = null
}
hasWindows (): boolean {
return !!this.windows.length
}
focus (): void {
for (const window of this.windows) {
window.show()
}
}
handleSecondInstance (argv: string[], cwd: string): void {
this.presentAllWindows()
this.windows[this.windows.length - 1].passCliArguments(argv, cwd, true)
}
private useBuiltinGraphics (): void {
if (process.platform === 'win32') {
const keyPath = 'SOFTWARE\\Microsoft\\DirectX\\UserGpuPreferences'
const valueName = app.getPath('exe')
if (!wnr.getRegistryValue(wnr.HK.CU, keyPath, valueName)) {
wnr.setRegistryValue(wnr.HK.CU, keyPath, valueName, wnr.REG.SZ, 'GpuPreference=1;')
}
}
}
private setupMenu () {
const template: MenuItemConstructorOptions[] = [
{
label: 'Application',
submenu: [
{ role: 'about', label: 'About Terminus' },
{ type: 'separator' },
{
label: 'Preferences',
accelerator: 'Cmd+,',
click: async () => {
if (!this.hasWindows()) {
await this.newWindow()
}
this.windows[0].send('host:preferences-menu')
},
},
{ type: 'separator' },
{ role: 'services', submenu: [] },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{
label: 'Quit',
accelerator: 'Cmd+Q',
click () {
app.quit()
},
},
],
},
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
],
},
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'togglefullscreen' },
],
},
{
role: 'window',
submenu: [
{ role: 'minimize' },
{ role: 'zoom' },
{ type: 'separator' },
{ role: 'front' },
],
},
{
role: 'help',
submenu: [
{
label: 'Website',
click () {
shell.openExternal('https://eugeny.github.io/terminus')
},
},
],
},
]
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
}
}

View File

@@ -1,51 +0,0 @@
import { app } from 'electron'
export function parseArgs (argv: string[], cwd: string): any {
if (argv[0].includes('node')) {
argv = argv.slice(1)
}
return require('yargs/yargs')(argv.slice(1))
.usage('terminus [command] [arguments]')
.command('open [directory]', 'open a shell in a directory', {
directory: { type: 'string', 'default': cwd },
})
.command(['run [command...]', '/k'], 'run a command in the terminal', {
command: { type: 'string' },
})
.command('profile [profileName]', 'open a tab with specified profile', {
profileName: { type: 'string' },
})
.command('connect-ssh [connectionName]', 'open a tab for a saved SSH connection', {
connectionName: { type: 'string' },
})
.command('connect-serial [connectionName]', 'open a tab for a saved serial connection', {
connectionName: { type: 'string' },
})
.command('paste [text]', 'paste stdin into the active tab', yargs => {
return yargs.option('escape', {
alias: 'e',
type: 'boolean',
describe: 'Perform shell escaping',
}).positional('text', {
type: 'string',
})
})
.version('version', '', app.getVersion())
.option('debug', {
alias: 'd',
describe: 'Show DevTools on start',
type: 'boolean',
})
.option('hidden', {
describe: 'Start minimized',
type: 'boolean',
})
.option('version', {
alias: 'v',
describe: 'Show version and exit',
type: 'boolean',
})
.help('help')
.parse()
}

View File

@@ -1,13 +0,0 @@
import * as fs from 'fs'
import * as path from 'path'
import * as yaml from 'js-yaml'
import { app } from 'electron'
export function loadConfig (): any {
const configPath = path.join(app.getPath('userData'), 'config.yaml')
if (fs.existsSync(configPath)) {
return yaml.load(fs.readFileSync(configPath, 'utf8'))
} else {
return {}
}
}

View File

@@ -1,72 +0,0 @@
import './portable'
import 'source-map-support/register'
import './sentry'
import './lru'
import { app, ipcMain, Menu } from 'electron'
import { parseArgs } from './cli'
import { Application } from './app'
import electronDebug = require('electron-debug')
if (!process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS = ''
}
const application = new Application()
ipcMain.on('app:new-window', () => {
application.newWindow()
})
app.on('activate', () => {
if (!application.hasWindows()) {
application.newWindow()
} else {
application.focus()
}
})
app.on('window-all-closed', () => {
app.quit()
})
process.on('uncaughtException' as any, err => {
console.log(err)
application.broadcast('uncaughtException', err)
})
app.on('second-instance', (_event, argv, cwd) => {
application.handleSecondInstance(argv, cwd)
})
const argv = parseArgs(process.argv, process.cwd())
if (!app.requestSingleInstanceLock()) {
app.quit()
app.exit(0)
}
if (argv.d) {
electronDebug({
isEnabled: true,
showDevTools: true,
devToolsMode: 'undocked',
})
}
app.on('ready', async () => {
if (process.platform === 'darwin') {
app.dock.setMenu(Menu.buildFromTemplate([
{
label: 'New window',
click () {
this.app.newWindow()
},
},
]))
}
application.init()
const window = await application.newWindow({ hidden: argv.hidden })
await window.ready
window.passCliArguments(process.argv, process.cwd(), false)
})

View File

@@ -1,17 +0,0 @@
import * as LRU from 'lru-cache'
import * as fs from 'fs'
const lru = new LRU({ max: 256, maxAge: 250 })
const origLstat = fs.realpathSync.bind(fs)
// NB: The biggest offender of thrashing realpathSync is the node module system
// itself, which we can't get into via any sane means.
require('fs').realpathSync = function (p) {
let r = lru.get(p)
if (r) {
return r
}
r = origLstat(p)
lru.set(p, r)
return r
}

View File

@@ -1,40 +0,0 @@
import { promisify } from 'util'
export class PluginManager {
npm: any
npmReady?: Promise<void>
async ensureLoaded (): Promise<void> {
if (!this.npmReady) {
this.npmReady = new Promise(resolve => {
const npm = require('npm')
npm.load(err => {
if (err) {
console.error(err)
return
}
npm.config.set('global', false)
this.npm = npm
resolve()
})
})
}
return this.npmReady
}
async install (path: string, name: string, version: string): Promise<void> {
await this.ensureLoaded()
this.npm.prefix = path
return promisify(this.npm.commands.install)([`${name}@${version}`])
}
async uninstall (path: string, name: string): Promise<void> {
await this.ensureLoaded()
this.npm.prefix = path
return promisify(this.npm.commands.remove)([name])
}
}
export const pluginManager = new PluginManager()

View File

@@ -1,14 +0,0 @@
import * as path from 'path'
import * as fs from 'fs'
import * as electron from 'electron'
const appPath = path.dirname(electron.app.getPath('exe'))
if (fs.existsSync(path.join(appPath, 'terminus-data'))) {
fs.renameSync(path.join(appPath, 'terminus-data'), path.join(appPath, 'data'))
}
const portableData = path.join(appPath, 'data')
if (fs.existsSync(portableData)) {
console.log('reset user data to ' + portableData)
electron.app.setPath('userData', portableData)
}

View File

@@ -1,159 +0,0 @@
import * as nodePTY from '@terminus-term/node-pty'
import { StringDecoder } from './stringDecoder'
import { v4 as uuidv4 } from 'uuid'
import { ipcMain } from 'electron'
import { Application } from './app'
class PTYDataQueue {
private buffers: Buffer[] = []
private delta = 0
private maxChunk = 1024
private maxDelta = 1024 * 50
private flowPaused = false
private decoder = new StringDecoder()
constructor (private pty: nodePTY.IPty, private onData: (data: Buffer) => void) { }
push (data: Buffer) {
this.buffers.push(data)
this.maybeEmit()
}
ack (length: number) {
this.delta -= length
this.maybeEmit()
}
private maybeEmit () {
if (this.delta <= this.maxDelta && this.flowPaused) {
this.resume()
return
}
if (this.buffers.length > 0) {
if (this.delta > this.maxDelta && !this.flowPaused) {
this.pause()
return
}
const buffersToSend = []
let totalLength = 0
while (totalLength < this.maxChunk && this.buffers.length) {
totalLength += this.buffers[0].length
buffersToSend.push(this.buffers.shift())
}
if (buffersToSend.length === 0) {
return
}
let toSend = Buffer.concat(buffersToSend)
if (toSend.length > this.maxChunk) {
this.buffers.unshift(toSend.slice(this.maxChunk))
toSend = toSend.slice(0, this.maxChunk)
}
this.emitData(toSend)
this.delta += toSend.length
if (this.buffers.length) {
setImmediate(() => this.maybeEmit())
}
}
}
private emitData (data: Buffer) {
this.onData(this.decoder.write(data))
}
private pause () {
this.pty.pause()
this.flowPaused = true
}
private resume () {
this.pty.resume()
this.flowPaused = false
this.maybeEmit()
}
}
export class PTY {
private pty: nodePTY.IPty
private outputQueue: PTYDataQueue
constructor (private id: string, private app: Application, ...args: any[]) {
this.pty = (nodePTY as any).spawn(...args)
for (const key of ['close', 'exit']) {
(this.pty as any).on(key, (...eventArgs) => this.emit(key, ...eventArgs))
}
this.outputQueue = new PTYDataQueue(this.pty, data => {
setImmediate(() => this.emit('data', data))
})
this.pty.on('data', data => this.outputQueue.push(Buffer.from(data)))
}
getPID (): number {
return this.pty.pid
}
resize (columns: number, rows: number): void {
if ((this.pty as any)._writable) {
this.pty.resize(columns, rows)
}
}
write (buffer: Buffer): void {
if ((this.pty as any)._writable) {
this.pty.write(buffer as any)
}
}
ackData (length: number): void {
this.outputQueue.ack(length)
}
kill (signal?: string): void {
this.pty.kill(signal)
}
private emit (event: string, ...args: any[]) {
this.app.broadcast(`pty:${this.id}:${event}`, ...args)
}
}
export class PTYManager {
private ptys: Record<string, PTY|undefined> = {}
init (app: Application): void {
ipcMain.on('pty:spawn', (event, ...options) => {
const id = uuidv4().toString()
event.returnValue = id
this.ptys[id] = new PTY(id, app, ...options)
})
ipcMain.on('pty:exists', (event, id) => {
event.returnValue = !!this.ptys[id]
})
ipcMain.on('pty:get-pid', (event, id) => {
event.returnValue = this.ptys[id]?.getPID()
})
ipcMain.on('pty:resize', (_event, id, columns, rows) => {
this.ptys[id]?.resize(columns, rows)
})
ipcMain.on('pty:write', (_event, id, data) => {
this.ptys[id]?.write(Buffer.from(data))
})
ipcMain.on('pty:kill', (_event, id, signal) => {
this.ptys[id]?.kill(signal)
})
ipcMain.on('pty:ack-data', (_event, id, length) => {
this.ptys[id]?.ackData(length)
})
}
}

View File

@@ -1,19 +0,0 @@
const { init } = String(process.type) === 'main' ? require('@sentry/electron/dist/main') : require('@sentry/electron/dist/renderer')
const SENTRY_DSN = 'https://4717a0a7ee0b4429bd3a0f06c3d7eec3@sentry.io/181876'
let release = null
try {
release = require('electron').app.getVersion()
} catch {
release = require('@electron/remote').app.getVersion()
}
if (!process.env.TERMINUS_DEV) {
init({
dsn: SENTRY_DSN,
release,
integrations (integrations) {
return integrations.filter(integration => integration.name !== 'Breadcrumbs')
},
})
}

View File

@@ -1,105 +0,0 @@
// based on Joyent's StringDecoder
// https://github.com/nodejs/string_decoder/blob/master/lib/string_decoder.js
export class StringDecoder {
lastNeed: number
lastTotal: number
lastChar: Buffer
constructor () {
this.lastNeed = 0
this.lastTotal = 0
this.lastChar = Buffer.allocUnsafe(4)
}
write (buf: Buffer): Buffer {
if (buf.length === 0) {
return buf
}
let r: Buffer|undefined = undefined
let i = 0
if (this.lastNeed) {
r = this.fillLast(buf)
if (r === undefined) {
return Buffer.from('')
}
i = this.lastNeed
this.lastNeed = 0
}
if (i < buf.length) {
return r ? Buffer.concat([r, this.text(buf, i)]) : this.text(buf, i)
}
return r
}
// For UTF-8, a replacement character is added when ending on a partial
// character.
end (buf?: Buffer): Buffer {
const r = buf?.length ? this.write(buf) : Buffer.from('')
if (this.lastNeed) {
console.log('end', r)
return Buffer.concat([r, Buffer.from('\ufffd')])
}
return r
}
// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a
// partial character, the character's bytes are buffered until the required
// number of bytes are available.
private text (buf: Buffer, i: number) {
const total = this.utf8CheckIncomplete(buf, i)
if (!this.lastNeed) {
return buf.slice(i)
}
this.lastTotal = total
const end = buf.length - (total - this.lastNeed)
buf.copy(this.lastChar, 0, end)
return buf.slice(i, end)
}
// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer
private fillLast (buf: Buffer): Buffer|undefined {
if (this.lastNeed <= buf.length) {
buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed)
return this.lastChar.slice(0, this.lastTotal)
}
buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length)
this.lastNeed -= buf.length
return undefined
}
// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a
// continuation byte. If an invalid byte is detected, -2 is returned.
private utf8CheckByte (byte) {
if (byte <= 0x7F) {return 0} else if (byte >> 5 === 0x06) {return 2} else if (byte >> 4 === 0x0E) {return 3} else if (byte >> 3 === 0x1E) {return 4}
return byte >> 6 === 0x02 ? -1 : -2
}
// Checks at most 3 bytes at the end of a Buffer in order to detect an
// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4)
// needed to complete the UTF-8 character (if applicable) are returned.
private utf8CheckIncomplete (buf, i) {
let j = buf.length - 1
if (j < i) {return 0}
let nb = this.utf8CheckByte(buf[j])
if (nb >= 0) {
if (nb > 0) {this.lastNeed = nb - 1}
return nb
}
if (--j < i || nb === -2) {return 0}
nb = this.utf8CheckByte(buf[j])
if (nb >= 0) {
if (nb > 0) {this.lastNeed = nb - 2}
return nb
}
if (--j < i || nb === -2) {return 0}
nb = this.utf8CheckByte(buf[j])
if (nb >= 0) {
if (nb > 0) {
if (nb === 2) {nb = 0} else {this.lastNeed = nb - 3}
}
return nb
}
return 0
}
}

View File

@@ -1,401 +0,0 @@
import * as glasstron from 'glasstron'
import { Subject, Observable } from 'rxjs'
import { debounceTime } from 'rxjs/operators'
import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen, BrowserWindowConstructorOptions } from 'electron'
import ElectronConfig = require('electron-config')
import * as os from 'os'
import * as path from 'path'
import macOSRelease from 'macos-release'
import * as compareVersions from 'compare-versions'
import type { Application } from './app'
import { parseArgs } from './cli'
import { loadConfig } from './config'
let DwmEnableBlurBehindWindow: any = null
if (process.platform === 'win32') {
DwmEnableBlurBehindWindow = require('windows-blurbehind').DwmEnableBlurBehindWindow
}
export interface WindowOptions {
hidden?: boolean
}
abstract class GlasstronWindow extends BrowserWindow {
blurType: string
abstract setBlur (_: boolean)
}
const macOSVibrancyType = process.platform === 'darwin' ? compareVersions.compare(macOSRelease().version, '10.14', '>=') ? 'fullscreen-ui' : 'dark' : null
export class Window {
ready: Promise<void>
private visible = new Subject<boolean>()
private closed = new Subject<void>()
private window?: GlasstronWindow
private windowConfig: ElectronConfig
private windowBounds?: Rectangle
private closing = false
private lastVibrancy: { enabled: boolean, type?: string } | null = null
private disableVibrancyWhileDragging = false
private configStore: any
get visible$ (): Observable<boolean> { return this.visible }
get closed$ (): Observable<void> { return this.closed }
constructor (private application: Application, options?: WindowOptions) {
this.configStore = loadConfig()
options = options ?? {}
this.windowConfig = new ElectronConfig({ name: 'window' })
this.windowBounds = this.windowConfig.get('windowBoundaries')
const maximized = this.windowConfig.get('maximized')
const bwOptions: BrowserWindowConstructorOptions = {
width: 800,
height: 600,
title: 'Terminus',
minWidth: 400,
minHeight: 300,
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, 'sentry.js'),
backgroundThrottling: false,
enableRemoteModule: true,
contextIsolation: false,
},
maximizable: true,
frame: false,
show: false,
backgroundColor: '#00000000',
}
if (this.windowBounds) {
Object.assign(bwOptions, this.windowBounds)
const closestDisplay = screen.getDisplayNearestPoint( { x: this.windowBounds.x, y: this.windowBounds.y } )
const [left1, top1, right1, bottom1] = [this.windowBounds.x, this.windowBounds.y, this.windowBounds.x + this.windowBounds.width, this.windowBounds.y + this.windowBounds.height]
const [left2, top2, right2, bottom2] = [closestDisplay.bounds.x, closestDisplay.bounds.y, closestDisplay.bounds.x + closestDisplay.bounds.width, closestDisplay.bounds.y + closestDisplay.bounds.height]
if ((left2 > right1 || right2 < left1 || top2 > bottom1 || bottom2 < top1) && !maximized) {
bwOptions.x = closestDisplay.bounds.width / 2 - bwOptions.width / 2
bwOptions.y = closestDisplay.bounds.height / 2 - bwOptions.height / 2
}
}
if ((this.configStore.appearance || {}).frame === 'native') {
bwOptions.frame = true
} else {
if (process.platform === 'darwin') {
bwOptions.titleBarStyle = 'hidden'
}
}
if (process.platform === 'darwin') {
this.window = new BrowserWindow(bwOptions) as GlasstronWindow
} else {
this.window = new glasstron.BrowserWindow(bwOptions)
}
this.window.once('ready-to-show', () => {
if (process.platform === 'darwin') {
this.window.setVibrancy(macOSVibrancyType)
} else if (process.platform === 'win32' && (this.configStore.appearance || {}).vibrancy) {
this.setVibrancy(true)
}
if (!options.hidden) {
if (maximized) {
this.window.maximize()
} else {
this.window.show()
}
this.window.focus()
this.window.moveTop()
}
})
this.window.on('blur', () => {
if (this.configStore.appearance.dock !== 'off' && this.configStore.appearance?.dockHideOnBlur) {
this.hide()
}
})
this.window.loadURL(`file://${app.getAppPath()}/dist/index.html`, { extraHeaders: 'pragma: no-cache\n' })
this.window.webContents.setVisualZoomLevelLimits(1, 1)
this.window.webContents.setZoomFactor(1)
if (process.platform !== 'darwin') {
this.window.setMenu(null)
}
this.setupWindowManagement()
this.ready = new Promise(resolve => {
const listener = event => {
if (event.sender === this.window.webContents) {
ipcMain.removeListener('app:ready', listener as any)
resolve()
}
}
ipcMain.on('app:ready', listener)
})
}
setVibrancy (enabled: boolean, type?: string, userRequested?: boolean): void {
if (userRequested ?? true) {
this.lastVibrancy = { enabled, type }
}
if (process.platform === 'win32') {
if (parseFloat(os.release()) >= 10) {
this.window.blurType = enabled ? type === 'fluent' ? 'acrylic' : 'blurbehind' : null
try {
this.window.setBlur(enabled)
} catch (error) {
console.error('Failed to set window blur', error)
}
} else {
DwmEnableBlurBehindWindow(this.window, enabled)
}
} else if (process.platform === 'linux') {
this.window.setBackgroundColor(enabled ? '#00000000' : '#131d27')
this.window.setBlur(enabled)
} else {
this.window.setVibrancy(enabled ? macOSVibrancyType : null)
}
}
show (): void {
this.window.show()
this.window.moveTop()
}
focus (): void {
this.window.focus()
}
send (event: string, ...args: any[]): void {
if (!this.window) {
return
}
this.window.webContents.send(event, ...args)
if (event === 'host:config-change') {
this.configStore = args[0]
}
}
isDestroyed (): boolean {
return !this.window || this.window.isDestroyed()
}
isFocused (): boolean {
return this.window.isFocused()
}
isVisible (): boolean {
return this.window.isVisible()
}
hide (): void {
if (process.platform === 'darwin') {
// Lose focus
Menu.sendActionToFirstResponder('hide:')
}
this.window.blur()
if (process.platform !== 'darwin') {
this.window.hide()
}
}
present (): void {
if (!this.window.isVisible()) {
// unfocused, invisible
this.window.show()
this.window.focus()
} else {
if (!this.configStore.appearance?.dock || this.configStore.appearance?.dock === 'off') {
// not docked, visible
setTimeout(() => {
this.window.show()
this.window.focus()
})
} else {
if (this.configStore.appearance?.dockAlwaysOnTop) {
// docked, visible, on top
this.window.hide()
} else {
// docked, visible, not on top
this.window.focus()
}
}
}
}
passCliArguments (argv: string[], cwd: string, secondInstance: boolean): void {
this.send('cli', parseArgs(argv, cwd), cwd, secondInstance)
}
private setupWindowManagement () {
this.window.on('show', () => {
this.visible.next(true)
this.send('host:window-shown')
})
this.window.on('hide', () => {
this.visible.next(false)
})
const moveSubscription = new Observable<void>(observer => {
this.window.on('move', () => observer.next())
}).pipe(debounceTime(250)).subscribe(() => {
this.send('host:window-moved')
})
this.window.on('closed', () => {
moveSubscription.unsubscribe()
})
this.window.on('enter-full-screen', () => this.send('host:window-enter-full-screen'))
this.window.on('leave-full-screen', () => this.send('host:window-leave-full-screen'))
this.window.on('close', event => {
if (!this.closing) {
event.preventDefault()
this.send('host:window-close-request')
return
}
this.windowConfig.set('windowBoundaries', this.windowBounds)
this.windowConfig.set('maximized', this.window.isMaximized())
})
this.window.on('closed', () => {
this.destroy()
})
this.window.on('resize', () => {
if (!this.window.isMaximized()) {
this.windowBounds = this.window.getBounds()
}
})
this.window.on('move', () => {
if (!this.window.isMaximized()) {
this.windowBounds = this.window.getBounds()
}
})
this.window.on('focus', () => {
this.send('host:window-focused')
})
ipcMain.on('ready', event => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
this.window.webContents.send('start', {
config: this.configStore,
executable: app.getPath('exe'),
windowID: this.window.id,
isFirstWindow: this.window.id === 1,
userPluginsPath: this.application.userPluginsPath,
})
})
ipcMain.on('window-toggle-maximize', event => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
if (this.window.isMaximized()) {
this.window.unmaximize()
} else {
this.window.maximize()
}
})
ipcMain.on('window-minimize', event => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
this.window.minimize()
})
ipcMain.on('window-set-bounds', (event, bounds) => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
this.window.setBounds(bounds)
})
ipcMain.on('window-set-always-on-top', (event, flag) => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
this.window.setAlwaysOnTop(flag)
})
ipcMain.on('window-set-vibrancy', (event, enabled, type) => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
this.setVibrancy(enabled, type)
})
ipcMain.on('window-set-title', (event, title) => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
this.window.setTitle(title)
})
ipcMain.on('window-bring-to-front', event => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
if (this.window.isMinimized()) {
this.window.restore()
}
this.window.show()
this.window.moveTop()
})
ipcMain.on('window-close', event => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
this.closing = true
this.window.close()
})
this.window.webContents.on('new-window', event => event.preventDefault())
ipcMain.on('window-set-disable-vibrancy-while-dragging', (_event, value) => {
this.disableVibrancyWhileDragging = value
})
let moveEndedTimeout: number|null = null
const onBoundsChange = () => {
if (!this.lastVibrancy?.enabled || !this.disableVibrancyWhileDragging) {
return
}
this.setVibrancy(false, undefined, false)
if (moveEndedTimeout) {
clearTimeout(moveEndedTimeout)
}
moveEndedTimeout = setTimeout(() => {
this.setVibrancy(this.lastVibrancy.enabled, this.lastVibrancy.type)
}, 50)
}
this.window.on('move', onBoundsChange)
this.window.on('resize', onBoundsChange)
}
private destroy () {
this.window = null
this.closed.next()
this.visible.complete()
this.closed.complete()
}
}

300
app/main.js Normal file
View File

@@ -0,0 +1,300 @@
if (process.platform == 'win32' && require('electron-squirrel-startup')) process.exit(0)
const electron = require('electron')
let electronVibrancy
if (process.platform != 'linux') {
electronVibrancy = require('electron-vibrancy')
}
if (process.argv.indexOf('--debug') !== -1) {
require('electron-debug')({enabled: true, showDevTools: 'undocked'})
}
let app = electron.app
let secondInstance = app.makeSingleInstance((argv, cwd) => {
app.window.webContents.send('host:second-instance', argv, cwd)
})
if (secondInstance) {
app.quit()
return
}
const yaml = require('js-yaml')
const path = require('path')
const fs = require('fs')
const Config = require('electron-config')
let windowConfig = new Config({name: 'window'})
if (!process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS = ''
}
setWindowVibrancy = (enabled) => {
if (enabled && !app.window.vibrancyViewID) {
app.window.vibrancyViewID = electronVibrancy.SetVibrancy(app.window, 0)
} else if (!enabled && app.window.vibrancyViewID) {
electronVibrancy.RemoveView(app.window, app.window.vibrancyViewID)
app.window.vibrancyViewID = null
}
}
setupWindowManagement = () => {
app.window.on('show', () => {
app.window.webContents.send('host:window-shown')
if (app.tray) {
app.tray.destroy()
app.tray = null
}
})
app.window.on('hide', (e) => {
if (!app.tray) {
setupTray()
}
})
app.window.on('enter-full-screen', () => app.window.webContents.send('host:window-enter-full-screen'))
app.window.on('leave-full-screen', () => app.window.webContents.send('host:window-leave-full-screen'))
app.window.on('close', (e) => {
windowConfig.set('windowBoundaries', app.window.getBounds())
})
app.window.on('closed', () => {
app.window = null
})
electron.ipcMain.on('window-focus', () => {
app.window.focus()
})
electron.ipcMain.on('window-maximize', () => {
app.window.maximize()
})
electron.ipcMain.on('window-unmaximize', () => {
app.window.unmaximize()
})
electron.ipcMain.on('window-toggle-maximize', () => {
if (app.window.isMaximized()) {
app.window.unmaximize()
} else {
app.window.maximize()
}
})
electron.ipcMain.on('window-minimize', () => {
app.window.minimize()
})
electron.ipcMain.on('window-set-bounds', (event, bounds) => {
app.window.setBounds(bounds)
})
electron.ipcMain.on('window-set-always-on-top', (event, flag) => {
app.window.setAlwaysOnTop(flag)
})
electron.ipcMain.on('window-set-vibrancy', (event, enabled) => {
setWindowVibrancy(enabled)
})
}
setupTray = () => {
if (process.platform == 'darwin') {
app.tray = new electron.Tray(`${app.getAppPath()}/assets/tray-darwinTemplate.png`)
app.tray.setPressedImage(`${app.getAppPath()}/assets/tray-darwinHighlightTemplate.png`)
} else {
app.tray = new electron.Tray(`${app.getAppPath()}/assets/tray.png`)
}
app.tray.on('click', () => {
app.window.show()
app.window.focus()
})
const contextMenu = electron.Menu.buildFromTemplate([{
label: 'Show',
click () {
app.window.show()
app.window.focus()
}
}])
if (process.platform != 'darwin') {
app.tray.setContextMenu(contextMenu)
}
app.tray.setToolTip(`Terminus ${app.getVersion()}`)
}
setupMenu = () => {
let template = [{
label: "Application",
submenu: [
{ role: 'about', label: 'About Terminus' },
{ type: 'separator' },
{
label: 'Preferences',
accelerator: 'Cmd+,',
click () {
app.window.webContents.send('host:preferences-menu')
}
},
{ type: 'separator' },
{ role: 'services', submenu: [] },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideothers' },
{ role: 'unhide' },
{ type: 'separator' },
{
label: 'Quit',
accelerator: 'Cmd+Q',
click () {
app.quit()
}
}
]
},
{
label: "Edit",
submenu: [
{role: 'undo'},
{role: 'redo'},
{type: 'separator'},
{role: 'cut'},
{role: 'copy'},
{role: 'paste'},
{role: 'pasteandmatchstyle'},
{role: 'delete'},
{role: 'selectall'}
]
},
{
label: 'View',
submenu: [
{role: 'reload'},
{role: 'forcereload'},
{role: 'toggledevtools'},
{type: 'separator'},
{role: 'resetzoom'},
{role: 'zoomin'},
{role: 'zoomout'},
{type: 'separator'},
{role: 'togglefullscreen'}
]
},
{
role: 'window',
submenu: [
{role: 'minimize'},
{role: 'zoom'},
{type: 'separator'},
{role: 'front'}
]
},
{
role: 'help',
submenu: [
{
label: 'Website',
click () { electron.shell.openExternal('https://eugeny.github.io/terminus') }
}
]
}]
electron.Menu.setApplicationMenu(electron.Menu.buildFromTemplate(template))
}
start = () => {
let t0 = Date.now()
let configPath = path.join(electron.app.getPath('userData'), 'config.yaml')
let configData
if (fs.existsSync(configPath)) {
configData = yaml.safeLoad(fs.readFileSync(configPath, 'utf8'))
} else {
configData = {}
}
let options = {
width: 800,
height: 600,
title: 'Terminus',
minWidth: 400,
minHeight: 300,
webPreferences: {webSecurity: false},
frame: false,
show: false,
}
Object.assign(options, windowConfig.get('windowBoundaries'))
if ((configData.appearance || {}).frame == 'native') {
options.frame = true
} else {
if (process.platform == 'darwin') {
options.titleBarStyle = 'hiddenInset'
}
}
if (process.platform == 'linux') {
options.backgroundColor = '#131d27'
}
app.commandLine.appendSwitch('disable-http-cache')
app.window = new electron.BrowserWindow(options)
app.window.once('ready-to-show', () => {
if (process.platform == 'darwin') {
app.window.setVibrancy('dark')
} else if (process.platform == 'windows') {
setWindowVibrancy(true)
}
app.window.show()
app.window.focus()
})
app.window.loadURL(`file://${app.getAppPath()}/dist/index.html`, {extraHeaders: "pragma: no-cache\n"})
if (process.platform != 'darwin') {
app.window.setMenu(null)
}
setupWindowManagement()
if (process.platform == 'darwin') {
setupMenu()
} else {
app.window.setMenu(null)
}
console.info(`Host startup: ${Date.now() - t0}ms`)
t0 = Date.now()
electron.ipcMain.on('app:ready', () => {
console.info(`App startup: ${Date.now() - t0}ms`)
})
}
app.on('ready', start)
app.on('activate', () => {
if (!app.window)
start()
else {
app.window.show()
app.window.focus()
}
})
process.on('uncaughtException', function(err) {
console.log(err)
app.window.webContents.send('uncaughtException', err)
})

View File

@@ -1,72 +1,39 @@
{
"name": "terminus",
"description": "A terminal for a modern age",
"private": true,
"repository": "https://github.com/eugeny/terminus",
"author": {
"name": "Eugene Pankov",
"email": "e@ajenti.org"
},
"main": "dist/main.js",
"main": "main.js",
"version": "1.0.0-alpha.1",
"scripts": {
"build": "webpack --progress --color --display-modules",
"watch": "webpack --progress --color --watch"
},
"dependencies": {
"@angular/animations": "^12.0.0",
"@angular/common": "^12.0.0",
"@angular/compiler": "^12.0.0",
"@angular/core": "^12.0.0",
"@angular/forms": "^12.0.0",
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.0.0",
"@electron/remote": "1.2.0",
"@ng-bootstrap/ng-bootstrap": "^9.1.1",
"@terminus-term/node-pty": "0.10.0-terminus.3",
"any-promise": "^1.3.0",
"electron-config": "2.0.0",
"electron-debug": "^3.2.0",
"electron-promise-ipc": "^2.2.4",
"fontmanager-redux": "1.0.0",
"glasstron": "0.0.7",
"js-yaml": "4.1.0",
"keytar": "^7.7.0",
"mz": "^2.7.0",
"native-process-working-directory": "^1.0.2",
"ngx-toastr": "^14.0.0",
"npm": "6",
"rxjs": "^7.1.0",
"ssh2": "^1.1.0",
"yargs": "^17.0.1",
"zone.js": "^0.11.4"
},
"optionalDependencies": {
"macos-native-processlist": "^2.0.0",
"serialport": "^9.1.0",
"windows-blurbehind": "^1.0.1",
"windows-native-registry": "^3.0.0",
"windows-process-tree": "^0.3.0"
"@angular/animations": "6.0.2",
"@angular/common": "6.0.2",
"@angular/compiler": "6.0.2",
"@angular/core": "6.0.2",
"@angular/forms": "6.0.2",
"@angular/platform-browser": "6.0.2",
"@angular/platform-browser-dynamic": "6.0.2",
"@ng-bootstrap/ng-bootstrap": "^2.0.0",
"devtron": "1.4.0",
"electron-config": "0.2.1",
"electron-debug": "^1.0.1",
"electron-is-dev": "0.1.2",
"electron-squirrel-startup": "^1.0.0",
"electron-vibrancy": "^0.1.3",
"js-yaml": "3.8.2",
"mz": "^2.6.0",
"ngx-toastr": "^8.7.3",
"path": "0.12.7",
"rxjs": "^6.1.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@types/mz": "2.7.3",
"@types/node": "15.12.1",
"ngx-filesize": "^2.0.16",
"node-abi": "^2.30.0",
"source-map-support": "^0.5.19"
},
"peerDependencies": {
"terminus-community-color-schemes": "*",
"terminus-core": "*",
"terminus-electron": "*",
"terminus-local": "*",
"terminus-plugin-manager": "*",
"terminus-serial": "*",
"terminus-settings": "*",
"terminus-ssh": "*",
"terminus-terminal": "*"
},
"resolutions": {
"*/node-abi": "^2.20.0"
"@types/mz": "0.0.31"
}
}

View File

@@ -1,24 +1,21 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { ToastrModule } from 'ngx-toastr'
export function getRootModule (plugins: any[]) {
const imports = [
let imports = [
BrowserModule,
...plugins,
NgbModule,
NgbModule.forRoot(),
ToastrModule.forRoot({
positionClass: 'toast-bottom-center',
toastClass: 'toast',
preventDuplicates: true,
extendedTimeOut: 1000,
extendedTimeOut: 5000,
}),
]
const bootstrap = [
...plugins.filter(x => x.bootstrap).map(x => x.bootstrap),
let bootstrap = [
...(plugins.filter(x => x.bootstrap).map(x => x.bootstrap)),
]
if (bootstrap.length === 0) {
@@ -28,7 +25,7 @@ export function getRootModule (plugins: any[]) {
@NgModule({
imports,
bootstrap,
}) class RootModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
}) class RootModule { }
return RootModule
}

View File

@@ -1,9 +1,38 @@
import '../lib/lru'
import 'source-sans-pro/source-sans-pro.css'
import 'source-code-pro/source-code-pro.css'
import '@fortawesome/fontawesome-free/css/solid.css'
import '@fortawesome/fontawesome-free/css/brands.css'
import '@fortawesome/fontawesome-free/css/regular.css'
import '@fortawesome/fontawesome-free/css/fontawesome.css'
import 'source-sans-pro'
import 'font-awesome/css/font-awesome.css'
import 'ngx-toastr/toastr.css'
import './preload.scss'
import * as Raven from 'raven-js'
const SENTRY_DSN = 'https://4717a0a7ee0b4429bd3a0f06c3d7eec3@sentry.io/181876'
Raven.config(
SENTRY_DSN,
{
release: require('electron').remote.app.getVersion(),
dataCallback: (data: any) => {
const normalize = (filename) => {
let splitArray = filename.split('/')
return splitArray[splitArray.length - 1]
}
data.exception.values[0].stacktrace.frames.forEach(frame => {
frame.filename = normalize(frame.filename)
})
data.culprit = data.exception.values[0].stacktrace.frames[0].filename
return data
}
}
)
process.on('uncaughtException', (err) => {
Raven.captureException(err)
console.error(err)
})
const childProcess = require('child_process')
childProcess.spawn = require('electron').remote.require('child_process').spawn
childProcess.exec = require('electron').remote.require('child_process').exec

View File

@@ -1,80 +1,52 @@
import 'zone.js'
import 'core-js/proposals/reflect-metadata'
import 'core-js/es7/reflect'
import 'core-js/core/delay'
import 'rxjs'
import './global.scss'
import './toastr.scss'
import { enableProdMode, NgModuleRef, ApplicationRef } from '@angular/core'
import { enableDebugTools } from '@angular/platform-browser'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { ipcRenderer } from 'electron'
import { getRootModule } from './app.module'
import { findPlugins, initModuleLookup, loadPlugins } from './plugins'
import { BootstrapData, BOOTSTRAP_DATA, PluginInfo } from '../../terminus-core/src/api/mainProcess'
// Always land on the start view
location.hash = ''
;(process as any).enablePromiseAPI = true
import { enableProdMode, NgModuleRef } from '@angular/core'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
if (process.platform === 'win32' && !('HOME' in process.env)) {
process.env.HOME = `${process.env.HOMEDRIVE}${process.env.HOMEPATH}`
import { getRootModule } from './app.module'
import { findPlugins, loadPlugins, IPluginInfo } from './plugins'
if (process.platform === 'win32') {
process.env.HOME = process.env.HOMEDRIVE + process.env.HOMEPATH
}
if (process.env.TERMINUS_DEV && !process.env.TERMINUS_FORCE_ANGULAR_PROD) {
if (require('electron-is-dev')) {
console.warn('Running in debug mode')
} else {
enableProdMode()
}
async function bootstrap (bootstrapData: BootstrapData, plugins: PluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
async function bootstrap (plugins: IPluginInfo[], safeMode = false): Promise<NgModuleRef<any>> {
if (safeMode) {
plugins = plugins.filter(x => x.isBuiltin)
}
const pluginModules = await loadPlugins(plugins, (current, total) => {
(document.querySelector('.progress .bar') as HTMLElement).style.width = `${100 * current / total}%` // eslint-disable-line
let pluginsModules = await loadPlugins(plugins, (current, total) => {
(document.querySelector('.progress .bar') as HTMLElement).style.width = 100 * current / total + '%'
})
const module = getRootModule(pluginModules)
let module = getRootModule(pluginsModules)
window['rootModule'] = module
const moduleRef = await platformBrowserDynamic([
{ provide: BOOTSTRAP_DATA, useValue: bootstrapData },
]).bootstrapModule(module)
if (process.env.TERMINUS_DEV) {
const applicationRef = moduleRef.injector.get(ApplicationRef)
const componentRef = applicationRef.components[0]
enableDebugTools(componentRef)
}
return moduleRef
return await platformBrowserDynamic().bootstrapModule(module)
}
ipcRenderer.once('start', async (_$event, bootstrapData: BootstrapData) => {
console.log('Window bootstrap data:', bootstrapData)
initModuleLookup(bootstrapData.userPluginsPath)
let plugins = await findPlugins()
bootstrapData.installedPlugins = plugins
if (bootstrapData.config.pluginBlacklist) {
plugins = plugins.filter(x => !bootstrapData.config.pluginBlacklist.includes(x.name))
}
plugins = plugins.filter(x => x.name !== 'web')
findPlugins().then(async plugins => {
console.log('Starting with plugins:', plugins)
try {
await bootstrap(bootstrapData, plugins)
await bootstrap(plugins)
} catch (error) {
console.error('Angular bootstrapping error:', error)
console.warn('Trying safe mode')
window['safeModeReason'] = error
try {
await bootstrap(bootstrapData, plugins, true)
} catch (error2) {
console.error('Bootstrap failed:', error2)
await bootstrap(plugins, true)
} catch (error) {
console.error('Bootstrap failed:', error)
}
}
})
ipcRenderer.send('ready')

View File

@@ -1,160 +0,0 @@
body {
min-height: 100vh;
overflow: hidden;
background: #1D272D;
}
.modal-dialog, .modal-backdrop, .no-drag {
-webkit-app-region: no-drag;
}
.selectable {
user-select: text;
}
[ngbradiogroup] input[type="radio"] {
display: none;
}
.btn {
display: inline-flex;
align-items: center;
flex-wrap: nowrap;
& > svg {
pointer-events: none;
}
}
.form-line {
display: flex;
border-top: 1px solid rgba(0, 0, 0, 0.2);
align-items: center;
padding: 10px 0;
margin: 0;
min-height: 64px;
.header {
margin-right: auto;
.title {
}
.description {
font-size: 13px;
opacity: .5;
}
}
&>.form-control, &>.input-group {
width: 33%;
}
}
input[type=range] {
-webkit-appearance: none;
background: transparent;
outline: none;
padding: 0;
&:focus {
border-color: transparent;
}
@mixin thumb() {
-webkit-appearance: none;
display: block;
height: 12px;
width: 12px;
background: #aaa;
border-radius: 6px;
cursor: pointer;
margin-top: -4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.95);
transition: 0.25s background;
&:hover {
background: #777;
}
&:active {
background: #666;
}
}
&::-webkit-slider-thumb { @include thumb(); }
&::-moz-range-thumb { @include thumb(); }
&::-ms-thumb { @include thumb(); }
&::thumb { @include thumb(); }
@mixin track() {
height: 4px;
background: #111;
margin: 3px 0 0;
box-sizing: border-box;
}
&::-webkit-slider-runnable-track { @include track(); }
&:focus::-webkit-slider-runnable-track { @include track(); }
&::-moz-range-track { @include track(); }
&::-ms-track { @include track(); }
}
a[ngbdropdownitem] {
cursor: pointer;
}
ngb-typeahead-window {
max-height: 60vh;
overflow: auto;
}
.hover-reveal {
opacity: 0;
.hover-reveal-parent:hover &,
*:hover > &,
&:hover {
opacity: 1;
}
}
@keyframes terminalShakeFrames {
0% {
transform: translateX(0);
}
25% {
transform: translateX(5px);
}
50% {
transform: translateX(-5px);
}
75% {
transform: translateX(5px);
}
100% {
transform: translateX(0);
}
}
.dropdown {
.dropdown-toggle::after {
vertical-align: 0.15em;
margin-left: .5em;
opacity: .5;
}
.dropdown-item {
i + span {
margin-left: 10px;
}
}
}
.no-wrap {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}

View File

@@ -1,22 +1,54 @@
import * as fs from 'mz/fs'
import * as path from 'path'
import * as remote from '@electron/remote'
import { PluginInfo } from '../../terminus-core/src/api/mainProcess'
const nodeModule = require('module')
const nodeRequire = (global as any).require
const nodeModule = require('module') // eslint-disable-line @typescript-eslint/no-var-requires
declare function delay (ms: number): Promise<void>
const nodeRequire = global['require']
function normalizePath (p: string): string {
function normalizePath (path: string): string {
const cygwinPrefix = '/cygdrive/'
if (p.startsWith(cygwinPrefix)) {
p = p.substring(cygwinPrefix.length).replace('/', '\\')
p = p[0] + ':' + p.substring(1)
if (path.startsWith(cygwinPrefix)) {
path = path.substring(cygwinPrefix.length).replace('/', '\\')
path = path[0] + ':' + path.substring(1)
}
return p
return path
}
const builtinPluginsPath = process.env.TERMINUS_DEV ? path.dirname(remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
nodeRequire.main.paths.map(x => nodeModule.globalPaths.push(normalizePath(x)))
if (process.env.DEV) {
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
}
const builtinPluginsPath = process.env.DEV ? path.dirname(require('electron').remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
const userPluginsPath = path.join(
require('electron').remote.app.getPath('appData'),
'terminus',
'plugins',
)
Object.assign(window, { builtinPluginsPath, userPluginsPath })
nodeModule.globalPaths.unshift(builtinPluginsPath)
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
// nodeModule.globalPaths.unshift(path.join((process as any).resourcesPath, 'app.asar', 'node_modules'))
if (process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.push(normalizePath(x)))
}
export declare type ProgressCallback = (current, total) => void
export interface IPluginInfo {
name: string
description: string
packageName: string
isBuiltin: boolean
version: string
author: string
homepage?: string
path?: string
info?: any
}
const builtinModules = [
'@angular/animations',
@@ -31,96 +63,54 @@ const builtinModules = [
'rxjs',
'rxjs/operators',
'terminus-core',
'terminus-local',
'terminus-settings',
'terminus-terminal',
'zone.js/dist/zone.js',
]
export type ProgressCallback = (current: number, total: number) => void // eslint-disable-line @typescript-eslint/no-type-alias
const cachedBuiltinModules = {}
builtinModules.forEach(m => {
cachedBuiltinModules[m] = nodeRequire(m)
})
export function initModuleLookup (userPluginsPath: string): void {
global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x)))
if (process.env.TERMINUS_DEV) {
nodeModule.globalPaths.unshift(path.dirname(remote.app.getAppPath()))
}
nodeModule.globalPaths.unshift(builtinPluginsPath)
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
// nodeModule.globalPaths.unshift(path.join((process as any).resourcesPath, 'app.asar', 'node_modules'))
if (process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.push(normalizePath(x)))
}
const cachedBuiltinModules = {}
builtinModules.forEach(m => {
cachedBuiltinModules[m] = nodeRequire(m)
})
const originalRequire = (global as any).require
;(global as any).require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalRequire.apply(this, [query])
}
const originalModuleRequire = nodeModule.prototype.require
nodeModule.prototype.require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalModuleRequire.call(this, query)
const originalRequire = nodeRequire('module').prototype.require
nodeRequire('module').prototype.require = function (query) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalRequire.apply(this, arguments)
}
export async function findPlugins (): Promise<PluginInfo[]> {
const paths = nodeModule.globalPaths
let foundPlugins: PluginInfo[] = []
const candidateLocations: { pluginDir: string, packageName: string }[] = []
const PREFIX = 'terminus-'
const processedPaths = []
export async function findPlugins (): Promise<IPluginInfo[]> {
let paths = nodeModule.globalPaths
let foundPlugins: IPluginInfo[] = []
let candidateLocations: { pluginDir: string, packageName: string }[] = []
for (let pluginDir of paths) {
if (processedPaths.includes(pluginDir)) {
continue
}
processedPaths.push(pluginDir)
pluginDir = normalizePath(pluginDir)
if (!await fs.exists(pluginDir)) {
continue
}
const pluginNames = await fs.readdir(pluginDir)
let pluginNames = await fs.readdir(pluginDir)
if (await fs.exists(path.join(pluginDir, 'package.json'))) {
candidateLocations.push({
pluginDir: path.dirname(pluginDir),
packageName: path.basename(pluginDir),
packageName: path.basename(pluginDir)
})
}
for (const packageName of pluginNames) {
if (packageName.startsWith(PREFIX)) {
candidateLocations.push({ pluginDir, packageName })
}
for (let packageName of pluginNames) {
candidateLocations.push({ pluginDir, packageName })
}
}
for (const { pluginDir, packageName } of candidateLocations) {
const pluginPath = path.join(pluginDir, packageName)
const infoPath = path.join(pluginPath, 'package.json')
for (let { pluginDir, packageName } of candidateLocations) {
let pluginPath = path.join(pluginDir, packageName)
let infoPath = path.join(pluginPath, 'package.json')
if (!await fs.exists(infoPath)) {
continue
}
const name = packageName.substring(PREFIX.length)
if (builtinModules.includes(packageName) && pluginDir !== builtinPluginsPath) {
continue
}
console.log(`Found ${name} in ${pluginDir}`)
let name = packageName.substring('terminus-'.length)
if (foundPlugins.some(x => x.name === name)) {
console.info(`Plugin ${packageName} already exists, overriding`)
@@ -128,7 +118,7 @@ export async function findPlugins (): Promise<PluginInfo[]> {
}
try {
const info = JSON.parse(await fs.readFile(infoPath, { encoding: 'utf-8' }))
let info = JSON.parse(await fs.readFile(infoPath, {encoding: 'utf-8'}))
if (!info.keywords || !(info.keywords.includes('terminus-plugin') || info.keywords.includes('terminus-builtin-plugin'))) {
continue
}
@@ -149,27 +139,27 @@ export async function findPlugins (): Promise<PluginInfo[]> {
}
}
foundPlugins.sort((a, b) => a.name > b.name ? 1 : -1)
(window as any).installedPlugins = foundPlugins
return foundPlugins
}
export async function loadPlugins (foundPlugins: PluginInfo[], progress: ProgressCallback): Promise<any[]> {
const plugins: any[] = []
export async function loadPlugins (foundPlugins: IPluginInfo[], progress: ProgressCallback): Promise<any[]> {
let plugins: any[] = []
progress(0, 1)
let index = 0
for (const foundPlugin of foundPlugins) {
for (let foundPlugin of foundPlugins) {
console.info(`Loading ${foundPlugin.name}: ${nodeRequire.resolve(foundPlugin.path)}`)
progress(index, foundPlugins.length)
try {
const packageModule = nodeRequire(foundPlugin.path)
const pluginModule = packageModule.default.forRoot ? packageModule.default.forRoot() : packageModule.default
pluginModule.pluginName = foundPlugin.name
pluginModule.bootstrap = packageModule.bootstrap
let packageModule = nodeRequire(foundPlugin.path)
let pluginModule = packageModule.default.forRoot ? packageModule.default.forRoot() : packageModule.default
pluginModule['pluginName'] = foundPlugin.name
pluginModule['bootstrap'] = packageModule.bootstrap
plugins.push(pluginModule)
await new Promise(x => setTimeout(x, 50))
} catch (error) {
console.error(`Could not load ${foundPlugin.name}:`, error)
}
await delay(1)
index++
}
progress(1, 1)

View File

@@ -7,7 +7,6 @@
height: 100vh;
display: flex;
animation: 0.5s ease-out fadeIn;
background: radial-gradient(#3a66820a 0%, #000e17 30%, black 100%);
&>div {
width: 200px;
@@ -24,7 +23,6 @@
transition: 1s ease-out width;
background: #a1c5e4;
height: 3px;
box-shadow: 0 0 2px #ffffff1f;
}
}
}
@@ -39,8 +37,8 @@
.terminus-logo {
width: 120px;
height: 120px;
width: 160px;
height: 160px;
background: url('../assets/logo.svg');
background-repeat: none;
background-size: contain;
@@ -53,10 +51,24 @@
font-family: 'Source Sans Pro';
text-align: center;
font-weight: normal;
font-size: 32px;
font-size: 42px;
margin: 0;
sup {
color: #842fe0;
}
}
.modal-dialog, .modal-backdrop {
-webkit-app-region: no-drag;
}
[ngbradiogroup] input[type="radio"] {
display: none;
}
body {
min-height: 100vh;
overflow: hidden;
background: rgba(0,0,0,.4);
}

View File

@@ -1,6 +1,6 @@
import { Component } from '@angular/core'
@Component({
template: '<app-root></app-root>',
template: '<app-root></app-root>'
})
export class RootComponent { } // eslint-disable-line @typescript-eslint/no-extraneous-class
export class RootComponent { }

View File

@@ -2,20 +2,12 @@
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 0 50px;
.toast {
box-shadow: 0 1px 0 rgba(0,0,0,.25);
padding: 7px 12px;
padding: 10px;
background-image: none;
width: auto;
flex-basis: auto;
border-radius: 0.5rem;
font-size: 0.75rem;
&.toast-error {
background-color: #BD362F;
}
&.toast-info {
background-color: #555;

View File

@@ -9,6 +9,7 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedParameters": true,
@@ -16,9 +17,8 @@
"lib": [
"dom",
"es2015",
"es2015.iterable",
"es2015.iterable.ts",
"es2017",
"es2019",
"es7"
]
},

View File

@@ -1,31 +0,0 @@
{
"compilerOptions": {
"baseUrl": "./lib",
"module": "commonjs",
"target": "es2017",
"declaration": false,
"noImplicitAny": false,
"removeComments": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"lib": [
"dom",
"es2015",
"es2015.iterable",
"es2017",
"es7"
]
},
"compileOnSave": false,
"exclude": [
"dist",
"src",
"node_modules",
"*/node_modules"
]
}

View File

@@ -2,86 +2,76 @@ const path = require('path')
const webpack = require('webpack')
module.exports = {
name: 'terminus',
target: 'node',
entry: {
'index.ignore': 'file-loader?name=index.html!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
sentry: path.resolve(__dirname, 'lib/sentry.ts'),
preload: path.resolve(__dirname, 'src/entry.preload.ts'),
bundle: path.resolve(__dirname, 'src/entry.ts'),
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
optimization:{
minimize: false,
},
context: __dirname,
devtool: 'source-map',
output: {
path: path.join(__dirname, 'dist'),
pathinfo: true,
filename: '[name].js',
publicPath: 'auto',
},
resolve: {
modules: ['src/', 'node_modules', '../node_modules', 'assets/'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
},
},
},
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{
test: /\.(png|svg)$/,
use: {
loader: 'url-loader',
options: {
limit: 999999,
},
},
},
{
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]',
},
},
},
],
},
externals: {
'@angular/core': 'commonjs @angular/core',
'@angular/compiler': 'commonjs @angular/compiler',
'@angular/platform-browser': 'commonjs @angular/platform-browser',
'@angular/platform-browser-dynamic': 'commonjs @angular/platform-browser-dynamic',
'@angular/forms': 'commonjs @angular/forms',
'@angular/common': 'commonjs @angular/common',
'@ng-bootstrap/ng-bootstrap': 'commonjs @ng-bootstrap/ng-bootstrap',
'@electron/remote': 'commonjs @electron/remote',
child_process: 'commonjs child_process',
electron: 'commonjs electron',
fs: 'commonjs fs',
'ngx-toastr': 'commonjs ngx-toastr',
module: 'commonjs module',
mz: 'commonjs mz',
path: 'commonjs path',
rxjs: 'commonjs rxjs',
'zone.js': 'commonjs zone.js/dist/zone.js',
},
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.DefinePlugin({
'process.type': '"renderer"',
}),
],
name: 'terminus',
target: 'node',
entry: {
'index.ignore': 'file-loader?name=index.html!val-loader!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
'preload': path.resolve(__dirname, 'src/entry.preload.ts'),
'bundle': path.resolve(__dirname, 'src/entry.ts'),
},
context: __dirname,
devtool: 'source-map',
output: {
path: path.join(__dirname, 'dist'),
pathinfo: true,
filename: '[name].js'
},
resolve: {
modules: ['src/', 'node_modules', '../node_modules', 'assets/'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.json'),
}
}
},
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{
test: /\.(png|svg)$/,
use: {
loader: 'file-loader',
options: {
name: 'images/[name].[ext]'
}
}
},
{
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[ext]'
}
}
}
]
},
externals: {
'@angular/core': 'commonjs @angular/core',
'@angular/compiler': 'commonjs @angular/compiler',
'@angular/platform-browser': 'commonjs @angular/platform-browser',
'@angular/platform-browser-dynamic': 'commonjs @angular/platform-browser-dynamic',
'@angular/forms': 'commonjs @angular/forms',
'@angular/common': 'commonjs @angular/common',
'@ng-bootstrap/ng-bootstrap': 'commonjs @ng-bootstrap/ng-bootstrap',
'child_process': 'commonjs child_process',
'electron': 'commonjs electron',
'electron-is-dev': 'commonjs electron-is-dev',
'ngx-toastr': 'commonjs ngx-toastr',
'module': 'commonjs module',
'mz': 'commonjs mz',
'path': 'commonjs path',
'rxjs': 'commonjs rxjs',
'zone.js': 'commonjs zone.js/dist/zone.js',
},
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
],
}

View File

@@ -1,68 +0,0 @@
const path = require('path')
const webpack = require('webpack')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
name: 'terminus-main',
target: 'electron-main',
entry: {
main: path.resolve(__dirname, 'lib/index.ts'),
},
mode: process.env.TERMINUS_DEV ? 'development' : 'production',
context: __dirname,
devtool: 'source-map',
output: {
path: path.join(__dirname, 'dist'),
pathinfo: true,
filename: '[name].js',
},
resolve: {
modules: ['lib/', 'node_modules', '../node_modules'].map(x => path.join(__dirname, x)),
extensions: ['.ts', '.js'],
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'awesome-typescript-loader',
options: {
configFileName: path.resolve(__dirname, 'tsconfig.main.json'),
},
},
},
],
},
externals: {
'any-promise': 'commonjs any-promise',
electron: 'commonjs electron',
'electron-config': 'commonjs electron-config',
'electron-debug': 'commonjs electron-debug',
'electron-promise-ipc': 'commonjs electron-promise-ipc',
'electron-vibrancy': 'commonjs electron-vibrancy',
fs: 'commonjs fs',
glasstron: 'commonjs glasstron',
mz: 'commonjs mz',
npm: 'commonjs npm',
'@terminus-term/node-pty': 'commonjs @terminus-term/node-pty',
path: 'commonjs path',
rxjs: 'commonjs rxjs',
'rxjs/operators': 'commonjs rxjs/operators',
util: 'commonjs util',
'source-map-support': 'commonjs source-map-support',
'windows-swca': 'commonjs windows-swca',
'windows-native-registry': 'commonjs windows-native-registry',
'windows-blurbehind': 'commonjs windows-blurbehind',
'yargs/yargs': 'commonjs yargs/yargs',
},
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.DefinePlugin({
'process.type': '"main"',
}),
],
}
if (process.env.BUNDLE_ANALYZER) {
module.exports.plugins.push(new BundleAnalyzerPlugin())
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,25 +4,23 @@ platform:
- x64
environment:
nodejs_version: "14"
nodejs_version: "7"
cache:
- '%USERPROFILE%\.electron'
version: "{build}"
install:
- ps: Install-Product node $env:nodejs_version $env:platform
- yarn
- npm install
- node scripts/install-deps.js
- node scripts/build-native.js
build_script:
- yarn run build
- npm run build
- node scripts/prepackage-plugins.js
- node scripts/build-windows.js
artifacts:
- path: 'dist\*.exe'
cache:
- node_modules
- "*\\node_modules"
- "%USERPROFILE%\\.electron"
- "%LOCALAPPDATA%\\Yarn"
- path: 'dist\win\*.exe'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 950 B

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -1 +1,124 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0" y="0" version="1.1" viewBox="0 0 1024 1024" xml:space="preserve" style="enable-background:new 0 0 1024 1024"><style type="text/css">.st0{opacity:.8;fill:#00232e}.st1{fill:url(#SVGID_1_)}.st2{opacity:.16;fill:url(#SVGID_2_)}.st3{fill:url(#SVGID_3_)}.st4{opacity:.16;fill:url(#SVGID_4_)}.st5{fill:url(#SVGID_5_)}.st6{opacity:.15;fill:url(#SVGID_6_)}.st7{fill:url(#SVGID_7_)}</style><polygon points="449.5 645.47 407.51 621.23 533.47 548.5 407.51 475.77 449.5 451.53 617.45 548.5" class="st0"/><g><linearGradient id="SVGID_1_" x1="439.007" x2="627.946" y1="603.039" y2="493.955" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#669abd"/><stop offset="1" style="stop-color:#77dbdb"/></linearGradient><polygon points="449.5 621.22 617.45 524.25 617.45 475.77 449.5 572.75" class="st1"/><linearGradient id="SVGID_2_" x1="522.979" x2="543.974" y1="530.315" y2="566.679" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="449.5 621.22 617.45 524.25 617.45 475.77 449.5 572.75" class="st2"/></g><g><linearGradient id="SVGID_3_" x1="397.01" x2="459.996" y1="566.684" y2="603.049" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="407.51 548.5 407.5 596.99 449.5 621.22 449.5 572.75" class="st3"/><linearGradient id="SVGID_4_" x1="439.001" x2="418.006" y1="566.684" y2="603.049" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="407.51 548.5 407.5 596.99 449.5 621.22 449.5 572.75" class="st4"/></g><g><linearGradient id="SVGID_5_" x1="397.01" x2="522.978" y1="421.226" y2="493.954" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#6a8fad"/><stop offset="1" style="stop-color:#669abd"/></linearGradient><polygon points="407.51 403.04 407.5 451.53 491.49 500.01 533.48 475.77" class="st5"/><linearGradient id="SVGID_6_" x1="470.492" x2="449.496" y1="439.407" y2="475.774" gradientUnits="userSpaceOnUse"><stop offset=".559" style="stop-color:#000;stop-opacity:0"/><stop offset="1" style="stop-color:#000"/></linearGradient><polygon points="407.51 403.04 407.5 451.53 491.49 500.01 533.48 475.77" class="st6"/></g><linearGradient id="SVGID_7_" x1="386.512" x2="575.461" y1="512.136" y2="403.047" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#ccecff"/><stop offset="1" style="stop-color:#9feced"/></linearGradient><polygon points="449.5 572.74 407.51 548.5 533.48 475.77 407.51 403.04 449.49 378.8 617.45 475.77" class="st7"/></svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="150mm"
height="150mm"
viewBox="0 0 150 150"
version="1.1"
id="svg8"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="icon.svg"
inkscape:export-filename="/home/eugene/Work/term/build/icons/512x512.png"
inkscape:export-xdpi="86.699997"
inkscape:export-ydpi="86.699997">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient4649">
<stop
style="stop-color:#000316;stop-opacity:1"
offset="0"
id="stop4645" />
<stop
style="stop-color:#190065;stop-opacity:1"
offset="1"
id="stop4647" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4649"
id="linearGradient4651"
x1="89.26284"
y1="85.146751"
x2="89.26284"
y2="229.47229"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.82182032,0,0,0.82182032,15.208802,28.029361)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="85.897128"
inkscape:cy="375.72042"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="692"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-intersection-paths="true"
inkscape:object-paths="true" />
<metadata
id="metadata5">
<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>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-10.356544,-82.309525)">
<rect
id="rect168"
width="123.27305"
height="123.27305"
x="23.72002"
y="95.673004"
style="fill:url(#linearGradient4651);fill-opacity:1;stroke-width:0.21743995"
rx="8.2182035"
ry="8.2182035" />
<path
inkscape:connector-curvature="0"
id="path138"
style="opacity:0.9;fill:#bfd9f1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.82182032px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 47.511243,117.17807 50.067023,28.8724 -15.038249,8.68226 -35.028768,-21.3657 z"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
id="path116"
style="opacity:0.9;fill:#6666af;fill-rule:evenodd;stroke:none;stroke-width:0.82182032px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="m 127.13617,146.73547 0.0374,16.98921 -64.125308,36.25552 -0.0025,-16.25659 z"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
id="path118"
style="opacity:0.9;fill:#bfd9f1;fill-rule:evenodd;stroke:none;stroke-width:0.82182032px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="m 47.647019,174.84764 15.398727,8.89046 0.0025,16.25641 -15.401249,-8.94341 z"
sodipodi:nodetypes="ccccc" />
<path
style="opacity:0.9;fill:#9dbef0;fill-rule:evenodd;stroke:none;stroke-width:0.82630885px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="m 61.586284,108.76679 -14.292349,8.25191 50.284331,29.03177 -50.148115,28.95277 15.482843,8.93896 50.148116,-28.95277 14.29235,-8.25191 z"
id="path134"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -1,3 +0,0 @@
!macro customInit
nsExec::Exec '"$LOCALAPPDATA\terminus\Update.exe" --uninstall -s'
!macroend

View File

@@ -1,7 +0,0 @@
#!/bin/bash
cat > '/usr/bin/${executable}' << END
#!/bin/sh
'/opt/${productFilename}/${executable}' --no-sandbox $@
END
chmod +x '/usr/bin/${executable}'

View File

@@ -1,16 +0,0 @@
const fs = require('fs')
const signHook = require('./afterSignHook')
module.exports = async function (params) {
// notarize the app on Mac OS only.
if (process.platform !== 'darwin' || !process.env.GITHUB_REF || !process.env.GITHUB_REF.startsWith('refs/tags/')) {
return
}
console.log('afterBuild hook triggered')
let pkgName = fs.readdirSync('dist').find(x => x.endsWith('.pkg'))
signHook({
appOutDir: 'dist',
_pathOverride: pkgName,
})
}

View File

@@ -1,35 +0,0 @@
// See: https://medium.com/@TwitterArchiveEraser/notarize-electron-apps-7a5f988406db
const fs = require('fs')
const path = require('path')
const notarizer = require('electron-notarize')
module.exports = async function (params) {
// notarize the app on Mac OS only.
if (process.platform !== 'darwin' || !process.env.GITHUB_REF || !process.env.GITHUB_REF.startsWith('refs/tags/')) {
return
}
console.log('afterSign hook triggered', params)
let appId = 'org.terminus'
let appPath = path.join(params.appOutDir, params._pathOverride || `${params.packager.appInfo.productFilename}.app`)
if (!fs.existsSync(appPath)) {
throw new Error(`Cannot find application at: ${appPath}`)
}
console.log(`Notarizing ${appId} found at ${appPath}`)
try {
await notarizer.notarize({
appBundleId: appId,
appPath: appPath,
appleId: process.env.APPSTORE_USERNAME,
appleIdPassword: process.env.APPSTORE_PASSWORD,
})
} catch (error) {
console.error(error)
}
console.log(`Done notarizing ${appId}`)
}

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.device.microphone</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
</dict>
</plist>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 106 KiB

92
build/windows/icon.svg Normal file
View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1024"
height="1024"
viewBox="0 0 270.93332 270.93333"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="icon.svg"
inkscape:export-filename="D:\Users\Ich\Downloads\64x64.png"
inkscape:export-xdpi="6"
inkscape:export-ydpi="6">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35355339"
inkscape:cx="-57.249603"
inkscape:cy="781.4887"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:window-width="1858"
inkscape:window-height="1050"
inkscape:window-x="54"
inkscape:window-y="1079"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-intersection-paths="true"
inkscape:object-paths="true"
units="px" />
<metadata
id="metadata5">
<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>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-47.511065,70.941737)">
<path
inkscape:connector-curvature="0"
id="path138"
style="opacity:0.9;fill:#bfd9f1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.43524027px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="M 64.949149,-45.402272 213.30911,40.153203 168.74736,65.880709 64.949181,2.5692907 Z"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
id="path116"
style="opacity:0.9;fill:#6666af;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.43524027px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 301.0092,42.179959 -0.003,50.177506 -190.42255,107.635545 -0.003,-48.17143 z"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
id="path118"
style="opacity:0.9;fill:#bfd9f1;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.43524027px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 64.948697,125.47711 45.629963,26.34447 0.005,48.17143 -45.637407,-26.50135 z"
sodipodi:nodetypes="ccccc" />
<path
style="opacity:0.9;fill:#9dbef0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.44854069px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="M 105.39355,-70.939125 64.949149,-45.402272 213.30911,40.153203 64.948697,125.47711 110.57866,151.82158 260.4947,65.557719 301.0092,42.179959 Z"
id="path134"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

215
clink/CHANGES Normal file
View File

@@ -0,0 +1,215 @@
### Changes
##### v0.4.8
- Environment variable 'clink_profile' overrides Clink's profile path (#390).
- Load a clink_inputrc file from Clink's profile directory (fixes #406).
- Bug fixes;
- Redraw issues when prompts end in OSC ANSI codes (#387, #384).
- Fixed 'clink autorun --help' crash.
##### v0.4.7
- Bug fixes;
- Sometimes autorun tries to run clink.exe (#374).
- Autorun would cause cmd.exe to return an error if it wasn't interactive (#373).
##### v0.4.6
- HOME is only set if it is currently unset.
- Readline can be initialised with .inputrc and _inputrc files too (#258).
- Bug fixes;
- Executable completion;
- Paths from PATH were checked twice.
- Incorrect results were returned for words ending in '.' or '-'.
- Directories . and .. were incorrectly displayed.
- Fixed a crash if .bat script's stdout is redirected (#366).
- Occasional crash when injecting Clink (#351).
- Display errors;
- When editing near the window's right-hand edge (#347).
- Double display of multi-line prompts when resizing the terminal (#352).
- Very rare wrap artefacts when making the terminal window larger.
- Doskey emulation (#344).
- Improved 'clink autorun' help (#348).
- Fixed launching Clink when clink.bat is renamed (#357).
##### v0.4.5
- Improved 'clink autorun'. It now defaults to the Current User registry hive.
- 'clink set' gives more details for enumeration-type settings.
- Tab completion for p4vc.
- New settings 'history_expand_mode' to control history expansion in quotes (#317).
- Bug fixes;
- Use full width of the terminal (#337).
- Fixed MinGW compile error (#335).
- Autorun now defaults to the current user's hive (#332).
- Creating clink.html no longer needs Pandoc, plus it looks a bit better (#331).
- Added settings to control history expansion (#326).
- Correct fallback when 'use_altgr_substitute' is off (#325).
- Load history prior to saving it on shutdown (#318).
- Added Shift-Tab documentation and menu completion example (#190).
- Added shim for backwards menu completion (#190).
- Input handling now outputs '\e`Z' for Shift-Tab (#190).
- Updated Readme with current Premake info (#310).
- Guard against there being no buffer to read from (#304).
- Fixed artefacts when resizing conhost's buffer (#139).
- Clear remaining characters if scroll window was too small (#301)
- Escape % characters when expanding aliases (#280).
- Fixed leaking exception filters.
- Clearing the screen doesn't leave artefacts behind.
##### v0.4.4
- Completing .. behaves more like Bash (#277).
- Escape from yes/no question when Ctrl+C is pressed.
- Valid XP executables (#278, #289).
- Fixed n-th argument yank not working as expected (#254).
- Fixed prompt colours sometimes not working (#279, #286).
- Fixed '!0' causing Clink to crash.
- Save/restore cursor position in case Readline moves it.
##### v0.4.3
- Localised Y/N when auto-answering 'terminate?' prompt.
- $* would early out if there was no arguments.
- Disable ANSI code support if third party provides it.
- Installer can now set %CLINK_DIR% to install location.
- Improved output from 'clink set'.
- Support for Windows 10 Technical Preview.
- Ctrl-L now scrolls last line to the top of the window rather than clearing.
- New option to control how newline characters are pasted to the line.
- New options to control history;
- 'history_file_lines' - maximum lines saved to disk.
- 'history_ignore_space' - ignore lines prefixed with whitespace.
- 'history_dupe_mode' - how duplicate entries are handled.
- 'history_io' - load/save history from disk every line.
- Fixed nonfunctional numpad keys.
- Fixed missing WINCH signals if other processes resize the buffer.
- Support Alt codes sent from Conhost.
##### v0.4.2
- Prompt colouring no longer requires third party ANSI code utility.
- Override settings with environment variables prefixed with 'clink'.
- Ctrl-PgUp goes up a directory.
- Updated Go completions (by matrixik).
- Arguments to clink.arg.new_parser() now initialise parser's flags/args (from vladimir-kotikov).
- New clink.arg.add_flags() and clink.arg.add_arguments() functions.
- Removed footer and Alt-H tip for more succinct stdout output.
- Bug fixes;
- Windows XP works again.
- Fixed race condition in lua_execute().
##### v0.4.1
- Bug fixes;
- Various Unicode fixes causing corrupt environment variables.
- Fixed thread resume/suspend causing rare system-wide deadlock.
- Fixed incorrect translation of suffixed slash when completing flags.
- Add --nolog argument to disable file logging. Fix #187 Fix #154
- Added missing escape sequences from doskey emulation.
- Reinstated unix-kill-line key binding.
- Mapped PgUp/Down to search history using line typed so far.
- Added documentation covering escape codes for special keys.
- Added support for Windows' AltGr substitute Ctrl-Alt.
- Support for Readline's 'menu' style completion (see docs).
##### v0.4
- New features;
- Better 'clink.arg' API. Easier, more intuitive, and more powerful.
- Whitespace prefix skips exec matching.
- Added a 'set' verb to easily change settings from the command line.
- Basic support for a shells other than cmd.exe.
- Completion for Go (contributed by Dobroslaw Zybort).
- Setting 'exec_match_style' to -1 disables it entirely.
- Make history persistence optional.
- Alias/doskey completion.
- Very basic support for Powershell.
- View cmd.exe's autorun entry without needing admin rights.
- New key bindings;
- Ctrl-Alt-C : Copy command line to the clipboard.
- Ctrl-Alt-E : Expand environment variable under cursor.
- Ctrl-Alt-U : 'up directory' (formerly Shift-Up).
- Ctrl-U : Adds '..\' to the command line.
- Alt-H : Shows active keymap's key bindings.
- New Lua functions;
- clink.execute().
- clink.get_host_process().
- clink.match_files().
- clink.match_words().
- clink.get_console_aliases().
- Lots of bug fixes, including;
- Better command extraction.
- Fixed cmd.exe command paging and Ctrl-C/Ctrl-Break handling.
- Multiple locale fixes.
- Use localised text for 'Terminate batch job?' prompt.
##### v0.3
- Automatic answering of cmd.exe's 'Terminate batch script?' prompt.
- Coloured prompts (requires ANSICON or ConEmu).
- Added Shift-Up keyboard shortcut to automatically execute 'cd ..'
- Mapped Ctrl-Z to undo, Microsoft style.
- Improved integration of Readline;
- New input handling code (Ctrl-Alt combos now work).
- An implementation of the Termcap library.
- Fully functional Vi-mode support.
- Support for resizable consoles.
- Line wrapping now works correctly (issue 50).
- Adjustable executable match style (issue 65).
- Improved environment variable completion.
- Added settings file to customise Clink.
- New Lua features and functions;
- Matches can now be filtered in Lua before they are display.
- clink.quote_split().
- clink.arg.node_merge().
- clink.get_screen_info() (issue 71).
- clink.split() (for splitting strings).
- clink.chdir().
- clink.get_cwd().
- Functions to query Clink's settings.
- New command line options;
- '--profile <dir>' to override default profile directory.
- '--nohostcheck' disables verification that host is cmd.exe.
- '--pid' specifies the process to inject into.
- Update Mercurial completion (issue 73).
- Start menu shortcut starts in USERPROFILE, like cmd.exe
- Zip distribution is now portable.
##### v0.2.1
- The .history file now merges multiple sessions together.
- Fixed missing y/n, pause, and other prompts.
- Fixed segfault in loader executable.
- Better ConEmu compatibility.
##### v0.2
- Basic argument completion for 'git', 'hg', 'svn', and 'p4'.
- Traditional Bash clear screen ('Ctrl-L') and exit shortcuts ('Ctrl-D').
- Scrollable command window using 'PgUp'/'PgDown' keys.
- Doskey support.
- Automatic quoting of file names with spaces.
- Scriptable custom prompts.
- New argument framework to ease writing context-sensitive match generators.
- History and log file is now saved per-user rather than globally.
- Improved Clink's command line interface ('clink --help').
- More reliable handling of cmd.exe's autorun entry.
- General improvements to executable and directory-command completion.
- Symbolic link support.
- Documentation.
- Windows 8 support.
- Improved hooking so Clink can be shared with other thirdparty utilities that
also hook cmd.exe (ConEmu, ANSICon, etc.).
##### v0.1.1
- Fixed AltGr+<key> on international keyboards.
- Fixed broken completion when directories have a '-' in their name (Mark Hammond)
- The check for single match scenarios now correctly handles case-insensitivity.
##### v0.1
- Initial release.

60
clink/clink.bat Normal file
View File

@@ -0,0 +1,60 @@
:: Copyright (c) 2012 Martin Ridgers
::
:: 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.
@echo off
:: Mimic cmd.exe's behaviour when starting from the start menu.
if /i "%1"=="startmenu" (
cd /d "%userprofile%"
shift /1
)
:: Check for the --profile option.
if /i "%1"=="--profile" (
set clink_profile_arg=--profile "%~2"
shift /1
shift /1
)
:: If the .bat is run without any arguments, then start a cmd.exe instance.
if "%1"=="" (
call :launch
goto :end
)
:: Pass through to appropriate loader.
if /i "%processor_architecture%"=="x86" (
"%~dp0\clink_x86.exe" %*
) else if /i "%processor_architecture%"=="amd64" (
if defined processor_architew6432 (
"%~dp0\clink_x86.exe" %*
) else (
"%~dp0\clink_x64.exe" %*
)
)
:end
set clink_profile_arg=
goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:launch
start "Clink" cmd.exe /s /k ""%~dpnx0" inject %clink_profile_arg%"
exit /b 0

915
clink/clink.html Normal file

File diff suppressed because one or more lines are too long

3420
clink/clink.lua Normal file

File diff suppressed because it is too large Load Diff

BIN
clink/clink_dll_x64.dll Normal file

Binary file not shown.

BIN
clink/clink_dll_x86.dll Normal file

Binary file not shown.

BIN
clink/clink_x64.exe Normal file

Binary file not shown.

BIN
clink/clink_x86.exe Normal file

Binary file not shown.

1
clink/profile/.history Normal file
View File

@@ -0,0 +1 @@
Ls

116
clink/profile/settings Normal file
View File

@@ -0,0 +1,116 @@
# name: Pressing Ctrl-D exits session
# type: bool
# Ctrl-D exits cmd.exe when it is pressed on an empty line.
ctrld_exits = 1
# name: Toggle if pressing Esc clears line
# type: bool
# Clink clears the current line when Esc is pressed (unless Readline's Vi mode
# is enabled).
esc_clears_line = 1
# name: Match display colour
# type: int
# Colour to use when displaying matches. A value less than 0 will be the
# opposite brightness of the default colour.
match_colour = -1
# name: Executable match style
# type: enum
# 0 = PATH only
# 1 = PATH and CWD
# 2 = PATH, CWD, and directories
# Changes how Clink will match executables when there is no path separator on
# the line. 0 = PATH only, 1 = PATH and CWD, 2 = PATH, CWD, and directories. In
# all cases both executables and directories are matched when there is a path
# separator present. A value of -1 will disable executable matching completely.
exec_match_style = 2
# name: Whitespace prefix matches files
# type: bool
# If the line begins with whitespace then Clink bypasses executable matching and
# will match all files and directories instead.
space_prefix_match_files = 1
# name: Colour of the prompt
# type: int
# Surrounds the prompt in ANSI escape codes to set the prompt's colour. Disabled
# when the value is less than 0.
prompt_colour = -1
# name: Auto-answer terminate prompt
# type: enum
# 0 = Disabled
# 1 = Answer 'Y'
# 2 = Answer 'N'
# Automatically answers cmd.exe's 'Terminate batch job (Y/N)?' prompts. 0 =
# disabled, 1 = answer 'Y', 2 = answer 'N'.
terminate_autoanswer = 0
# name: Lines of history saved to disk
# type: int
# When set to a positive integer this is the number of lines of history that
# will persist when Clink saves the command history to disk. Use 0 for infinite
# lines and <0 to disable history persistence.
history_file_lines = 10000
# name: Skip adding lines prefixed with whitespace
# type: bool
# Ignore lines that begin with whitespace when adding lines in to the history.
history_ignore_space = 0
# name: Controls how duplicate entries are handled
# type: enum
# 0 = Always add
# 1 = Ignore
# 2 = Erase previous
# If a line is a duplicate of an existing history entry Clink will erase the
# duplicate when this is set 2. A value of 1 will not add duplicates to the
# history and a value of 0 will always add lines. Note that history is not
# deduplicated when reading/writing to disk.
history_dupe_mode = 2
# name: Read/write history file each line edited
# type: bool
# When non-zero the history will be read from disk before editing a new line and
# written to disk afterwards.
history_io = 0
# name: Sets how command history expansion is applied
# type: enum
# 0 = Off
# 1 = On
# 2 = Not in single quotes
# 3 = Not in double quote
# 4 = Not in any quotes
# The '!' character in an entered line can be interpreted to introduce words
# from the history. This can be enabled and disable by setting this value to 1
# or 0. Values or 2, 3 or 4 will skip any ! character quoted in single, double,
# or both quotes respectively.
history_expand_mode = 4
# name: Support Windows' Ctrl-Alt substitute for AltGr
# type: bool
# Windows provides Ctrl-Alt as a substitute for AltGr, historically to support
# keyboards with no AltGr key. This may collide with some of Readline's
# bindings.
use_altgr_substitute = 1
# name: Strips CR and LF chars on paste
# type: enum
# 0 = Paste unchanged
# 1 = Strip
# 2 = As space
# Setting this to a value >0 will make Clink strip CR and LF characters from
# text pasted into the current line. Set this to 1 to strip all newline
# characters and 2 to replace them with a space.
strip_crlf_on_paste = 2
# name: Enables basic ANSI escape code support
# type: bool
# When printing the prompt, Clink has basic built-in support for SGR ANSI escape
# codes to control the text colours. This is automatically disabled if a third
# party tool is detected that also provides this facility. It can also be
# disabled by setting this to 0.
ansi_code_support = 1

BIN
docs/background.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
docs/dist/assets/background.jpeg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
docs/dist/assets/terminal.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

1
docs/dist/bundle.js vendored Normal file

File diff suppressed because one or more lines are too long

BIN
docs/dist/fonts/background.jpeg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

7
docs/index.html Normal file
View File

@@ -0,0 +1,7 @@
<!DOCTYPE html><html><head><base href="dist/"><meta name="viewport" content="initial-scale=1, minimal-ui, shrink-to-fit=no"><link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400" rel="stylesheet"><script src="bundle.js"></script><title>Terminus</title></head><body><div class="mt-5 mb-5" id="header"><div class="text-center"><h1>Terminus</h1><div class="subtitle mb-3">A terminal for a more modern age</div><a class="btn btn-lg btn-outline-dark mt-4" href="https://github.com/Eugeny/terminus/releases/latest" target="_blank"><strong>DOWNLOAD</strong></a><a class="btn btn-lg btn-outline-secondary mt-4 ml-3" href="https://github.com/Eugeny/terminus" target="_blank"><strong>GITHUB</strong></a></div></div><div class="background-stripe"><div class="overlay overlay1"></div><div class="overlay overlay2"></div><div class="terminal"></div></div><div class="container mt-5 mb-5"><div class="d-flex flex-wrap flex-md-nowrap"><div class="w-100"><div class="feature">windows</div><div class="feature">linux</div><div class="feature">macos</div><br><div class="feature">powershell</div><div class="feature">wsl</div><div class="feature">cygwin</div><div class="feature">git-bash</div><div class="feature">cmder</div><div class="feature">clink</div></div><div class="w-100"><div class="feature">full unicode</div><div class="feature">global hotkey</div><div class="feature">plugins</div><div class="feature">tab recovery</div><div class="feature">custom css</div><div class="feature">themes</div><div class="feature">font ligatures</div><div class="feature">clickable paths</div><div class="feature">tabs on top/bottom</div><div class="feature">vibrancy</div><div class="feature">bracketed paste</div></div></div></div><div class="container mt-5 mb-5"><div class="text-center"><a class="btn btn-lg btn-outline-secondary mt-5" href="/terminus/#header"><strong>BEAM ME UP</strong></a></div></div><div class="background-stripe2"><div class="overlay overlay1"></div></div><script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-3278102-18', 'auto');
ga('send', 'pageview');</script></body></html>

1
docs/index.js Normal file
View File

@@ -0,0 +1 @@
import './styles.scss'

69
docs/index.pug Normal file
View File

@@ -0,0 +1,69 @@
doctype html
html
head
base(href='dist/')
meta(name='viewport', content='initial-scale=1, minimal-ui, shrink-to-fit=no')
link(href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400", rel="stylesheet")
script(src='bundle.js')
title Terminus
body
.mt-5.mb-5#header
.text-center
h1 Terminus
.subtitle.mb-3 A terminal for a more modern age
a.btn.btn-lg.btn-outline-dark.mt-4(href='https://github.com/Eugeny/terminus/releases/latest', target='_blank')
strong DOWNLOAD
a.btn.btn-lg.btn-outline-secondary.mt-4.ml-3(href='https://github.com/Eugeny/terminus', target='_blank')
strong GITHUB
.background-stripe
.overlay.overlay1
.overlay.overlay2
.terminal
.container.mt-5.mb-5
.d-flex.flex-wrap.flex-md-nowrap
.w-100
.feature windows
.feature linux
.feature macos
br
.feature powershell
.feature wsl
.feature cygwin
.feature git-bash
.feature cmder
.feature clink
.w-100
.feature full unicode
.feature global hotkey
.feature plugins
.feature tab recovery
.feature custom css
.feature themes
.feature font ligatures
.feature clickable paths
.feature tabs on top/bottom
.feature vibrancy
.feature bracketed paste
.container.mt-5.mb-5
.text-center
a.btn.btn-lg.btn-outline-secondary.mt-5(href='/terminus/#header')
strong BEAM ME UP
.background-stripe2
.overlay.overlay1
script.
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-3278102-18', 'auto');
ga('send', 'pageview');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

24
docs/package.json Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "docs",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "webpack --progress",
"watch": "webpack --progress --watch"
},
"private": true,
"devDependencies": {
"bootstrap": "^4.1.3",
"css-loader": "^1.0.0",
"file-loader": "^1.1.11",
"node-sass": "^4.9.3",
"pug": "^2.0.3",
"pug-cli": "^1.0.0-alpha6",
"pug-html-loader": "^1.1.5",
"sass-loader": "^7.1.0",
"style-loader": "^0.22.1",
"val-loader": "^1.1.1",
"webpack": "^4.16.5",
"webpack-cli": "^3.1.0"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 862 KiB

141
docs/styles.scss Normal file
View File

@@ -0,0 +1,141 @@
$font-family-sans-serif: "Source Sans Pro";
$border-radius-lg: 0;
$btn-border-width: 3px;
@import "node_modules/bootstrap/scss/bootstrap";
h1 {
font-size: 10vw;
font-weight: 200;
margin: 0;
}
body {
overflow-x: hidden;
}
.subtitle {
font-style: italic;
color: #999;
font-size: 5vw;
font-weight: 300;
}
.background-stripe {
width: 100vw;
background-image: url('./background.jpeg');
background-size: cover;
height: 30vw;
margin: 200px 0 150px;
min-height: 1000px;
position: relative;
.overlay {
position: absolute;
width: 100vw;
width: 1px;
height: 1px;
&.overlay1 {
top: -1px;
left: 0;
border-top: 10vw solid white;
border-right: 100vw solid transparent;
}
&.overlay2 {
bottom: -1px;
right: 0;
border-bottom: 10vw solid white;
border-left: 100vw solid transparent;
}
}
.terminal {
position: absolute;
left: 50%;
top: 5vw;
width: 1304px;
margin-left: -652px;
height: 972px;
border-radius: 9px;
box-shadow: 0 0 100px black;
background: url('./terminal.png');
background-size: cover;
animation: slideIn ease-out 1s;
opacity: .95;
}
@media(max-width: 1500px) {
min-height: 500px;
margin: 200px 0 100px;
.terminal {
width: 652px;
top: -100px;
margin-left: -326px;
height: 486px;
border-radius: 5px;
}
}
@media(max-width: 750px) {
min-height: 250px;
margin: 100px 0 50px;
.terminal {
width: 326px;
top: -50px;
margin-left: -163px;
height: 243px;
border-radius: 3px;
}
}
}
.feature {
font-size: 45px;
line-height: 40px;
opacity: .5;
font-style: italic;
}
@keyframes slideIn {
from {
opacity: 0;
margin-top: 200px;
}
to {
opacity: .95;
margin-top: 0px;
}
}
.background-stripe2 {
width: 100vw;
background-image: url('./background.jpeg');
background-size: cover;
height: 30vw;
margin: 100px 0 0;
position: relative;
.overlay {
position: absolute;
width: 100vw;
width: 1px;
height: 1px;
&.overlay1 {
top: -1px;
right: 0;
border-top: 10vw solid white;
border-left: 100vw solid transparent;
}
}
}

BIN
docs/terminal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

27
docs/webpack.config.js Normal file
View File

@@ -0,0 +1,27 @@
const path = require('path')
module.exports = {
entry: {
'index.ignore': 'file-loader?name=../index.html!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
'bundle': path.resolve(__dirname, 'index.js'),
},
context: __dirname,
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{
test: /\.(jpeg|png)?$/,
use: {
loader: 'file-loader',
options: {
name: 'assets/[name].[ext]'
}
}
}
]
},
}

View File

@@ -1,106 +0,0 @@
---
appId: org.terminus
productName: Terminus
compression: normal
npmRebuild: false
afterSign: "./build/mac/afterSignHook.js"
afterAllArtifactBuild: "./build/mac/afterBuildHook.js"
files:
- '**/*'
- dist
- '!lib'
- '!src'
- '!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme,node.lib}'
- '!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples,docs}'
- '!**/node_modules/@angular/common/locales'
- '!**/node_modules/@angular/compiler/src'
- '!**/node_modules/node-gyp'
- '!**/node_modules/**/*.d.ts'
- '!**/node_modules/**/*.map'
- '!**/node_modules/**/include/node'
- '!**/node_modules/.bin'
- '!**/node_modules/*/*/{esm5,fesm5,esm2015,fesm2015,_esm2015,_fesm2015}'
- '!**/*.{woff,ttf,otf,eot}'
- '!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}'
- '!.editorconfig'
- '!**/._*'
- '!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}'
- '!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}'
- '!**/{appveyor.yml,.travis.yml,circle.yml}'
- '!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}'
- '!**/deps/cpu_features/build'
extraResources:
- builtin-plugins
- extras
asarUnpack:
- dist/*.map
publish:
- provider: github
win:
icon: "./build/windows/icon.ico"
artifactName: terminus-${version}-portable.${ext}
rfc3161TimeStampServer: http://sha256timestamp.ws.symantec.com/sha256/timestamp
nsis:
oneClick: false
artifactName: terminus-${version}-setup.${ext}
installerIcon: "./build/windows/icon.ico"
allowToChangeInstallationDirectory: true
mac:
category: public.app-category.video
icon: "./build/mac/icon.icns"
artifactName: terminus-${version}-macos-${env.ARCH}.${ext}
hardenedRuntime: true
entitlements: "./build/mac/entitlements.plist"
entitlementsInherit: "./build/mac/entitlements.plist"
extendInfo:
NSRequiresAquaSystemAppearance: false
NSCameraUsageDescription: "A subprocess requests access to the device's camera."
NSMicrophoneUsageDescription: "A subprocess requests access to the device's microphone."
NSLocationUsageDescription: "A subprocess requests access to the user's location information."
NSDesktopFolderUsageDescription: "A subprocess requests access to the user's Desktop folder."
NSDocumentsFolderUsageDescription: "A subprocess requests access to the user's Documents folder."
NSDownloadsFolderUsageDescription: "A subprocess requests access to the user's Downloads folder."
NSNetworkVolumesUsageDescription: 'A subprocess requests access to files on a network volume.'
NSRemovableVolumesUsageDescription: 'A subprocess requests access to files on a removable volume.'
linux:
category: Utility
icon: "./build/icons"
artifactName: terminus-${version}-linux.${ext}
executableArgs:
- "--no-sandbox"
desktop:
StartupWMClass: terminus
snap:
plugs:
- default
- system-files
- system-observe
deb:
depends:
- gconf2
- gconf-service
- gnome-keyring
- libnotify4
- libsecret-1-0
- libappindicator1
- libxtst6
- libnss3
afterInstall: build/linux/after-install.tpl
fpm:
- '--replaces'
- 'terminus'
pacman:
depends:
- gnome-keyring
- libsecret
rpm:
depends:
- gnome-keyring
fpm:
- '--rpm-rpmbuild-define'
- '_build_id_links none'
- '--replaces'
- 'terminus'

Binary file not shown.

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSServices</key>
<array>
<dict>
<key>NSBackgroundColorName</key>
<string>background</string>
<key>NSIconName</key>
<string>NSActionTemplate</string>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Open Terminus here</string>
</dict>
<key>NSMessage</key>
<string>runWorkflowAsService</string>
<key>NSRequiredContext</key>
<dict>
<key>NSApplicationIdentifier</key>
<string>com.apple.finder</string>
</dict>
<key>NSSendFileTypes</key>
<array>
<string>public.item</string>
</array>
</dict>
</array>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -1,136 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict/>
<key>files2</key>
<dict>
<key>QuickLook/Thumbnail.png</key>
<dict>
<key>hash</key>
<data>
tv0Qtgo8zZ9+sQPQDNdKJHm7jeQ=
</data>
<key>hash2</key>
<data>
8nlfnBbkORcam9cE84KuxM9Lgf6hYU0jehepX8sSNDU=
</data>
</dict>
<key>document.wflow</key>
<dict>
<key>cdhash</key>
<data>
VK77ipNZktBsDCcUfnfht774juM=
</data>
<key>requirement</key>
<string>identifier document and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = V4JSMC46SY</string>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^.*</key>
<true/>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

Some files were not shown because too many files have changed in this diff Show More