Compare commits

...

46 Commits

Author SHA1 Message Date
Eugene Pankov
1ae027f17c ci 2018-10-03 22:44:04 +01:00
Eugene
5360b2f292 Merge pull request #443 from vsailev/master
Restore Bash on Windows
2018-10-03 21:57:36 +01:00
vsailev
d806fb6e1e Restore Bash on Windows 2018-10-03 21:15:16 +01:00
Eugene Pankov
57de182013 Merge branch 'master' of github.com:Eugeny/terminus 2018-09-25 10:48:24 +02:00
Eugene Pankov
3fd57e81a6 install terminus into /usr/bin on Debian (fixes #433) 2018-09-25 10:48:00 +02:00
Eugene Pankov
eed01e76ad fixed cwd for new tabs on windows (fixes #378) 2018-09-23 09:25:49 -07:00
Eugene Pankov
7f8d012a8a shell context menu integration (fixes #362) 2018-09-23 09:23:20 -07:00
Eugene Pankov
9ad1371c2b theme fix 2018-09-23 09:11:42 -07:00
Eugene Pankov
0d37f4736a build fix 2018-09-23 17:09:11 +02:00
Eugene Pankov
e71d404c2b added CLI option to paste text into terminal 2018-09-23 16:33:57 +02:00
Eugene Pankov
0545471f3c added Automator workflows 2018-09-23 15:38:57 +02:00
Eugene Pankov
a04c60046e new light theme 2018-09-23 14:10:19 +02:00
Eugene Pankov
5c2003cc2f added a portable build 2018-09-22 13:14:51 +02:00
Eugene Pankov
dc864781e4 fixed focus detection (fixes #185) 2018-09-22 12:27:34 +02:00
Eugene Pankov
e315654d0a build fix 2018-09-22 12:19:14 +02:00
Eugene Pankov
5863ea0de1 fixed exiting fullscreen with vibrancy (fixes #407) 2018-09-22 12:03:43 +02:00
Eugene Pankov
cce49c69d6 bumps 2018-09-22 12:00:58 +02:00
Eugene Pankov
3e41d0df4e yarn 2018-09-22 11:48:57 +02:00
Eugene Pankov
507b69acb4 sensible shell defaults for Windows (fixes #431) 2018-09-22 11:48:51 +02:00
Eugene Pankov
6b08341760 support multiple WSL distributions 2018-09-22 11:37:37 +02:00
Eugene Pankov
62bf681598 fixed visual bell (fixes #419) 2018-09-20 13:04:50 +02:00
Eugene Pankov
e403a423b7 window management fixes (fixes #426) 2018-09-20 13:02:07 +02:00
Eugene Pankov
6b49cdc974 properly pass cwd from a second instance 2018-09-20 13:01:42 +02:00
Eugene Pankov
5fe71b8169 hide menu on Linux and Windows 2018-09-20 13:01:25 +02:00
Eugene Pankov
6bc2d18f3c faster tab creation 2018-09-20 12:46:50 +02:00
Eugene Pankov
6fa5ab5eb2 scroll terminal to bottom on user input 2018-09-20 12:12:57 +02:00
Eugene Pankov
2411713501 include proper tags in github issues 2018-09-20 12:12:45 +02:00
Eugene Pankov
947e0f8b66 theme tweaks 2018-09-20 12:12:38 +02:00
Eugene Pankov
f8bc94fe78 theme tweak 2018-09-20 11:55:23 +02:00
Eugene Pankov
5a32a45b19 theme tweaks 2018-09-20 11:46:47 +02:00
Eugene Pankov
62222e67fb lint 2018-09-20 11:46:24 +02:00
Eugene Pankov
6aab782326 removed window close menu on macos 2018-09-20 11:46:17 +02:00
Eugene Pankov
cec349d021 faster tab switching 2018-09-20 11:42:51 +02:00
Eugene
c8b40647a9 Update README.md 2018-09-20 10:56:31 +02:00
Eugene
67ed830e97 Merge pull request #421 from vsailev/master
Update clink to v0.4.9
2018-09-16 06:16:11 -07:00
vsailev
975d4d62ef Update clink to v0.4.9 2018-09-16 11:05:40 +01:00
Eugene
275791517b Merge pull request #418 from BBJip/master
Save window state on close.
2018-09-12 22:43:55 -07:00
BBJip
9db452f489 save window state on close. 2018-09-13 01:25:08 +02:00
Eugene
5094262e68 Merge pull request #416 from Domain/master
Biding scripts
2018-09-12 05:07:56 -07:00
Domain
17cf0f59c9 Biding scripts 2018-09-12 20:05:45 +08:00
Eugene
aa805b912b Merge pull request #415 from Domain/master
Add keep alive in advanced setting
2018-09-12 05:01:38 -07:00
Domain
4b3cbc5639 Add keep alive in advanced setting 2018-09-12 10:40:58 +08:00
Eugene Pankov
6a59db1a36 build fix 2018-09-11 14:44:11 -07:00
Eugene Pankov
2d43e29bcd bumped hterm 2018-09-11 14:44:04 -07:00
Eugene Pankov
bf5e460bca autodetect powershell core path (fixes #382) 2018-09-11 13:52:33 -07:00
Eugene Pankov
d574f634c9 bumped plugin versions 2018-09-11 13:37:14 -07:00
82 changed files with 2137 additions and 431 deletions

View File

@@ -28,6 +28,7 @@ Plugins can be installed directly from the Settings view inside Terminus.
* [shell-selector](https://github.com/Eugeny/terminus-shell-selector) - a quick shell selector pane
* [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
* [scrollbar](https://github.com/kbjr/terminus-scrollbar) - adds a scrollbar to terminal tabs
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - quicklky send commands to one or all terminal tabs
---

View File

@@ -21,7 +21,9 @@ export class Application {
this.enableTray()
}
})
this.setupMenu()
if (process.platform === 'darwin') {
this.setupMenu()
}
await window.ready
return window
}
@@ -147,7 +149,6 @@ export class Application {
role: 'window',
submenu: [
{ role: 'minimize' },
{ role: 'close' },
{ role: 'zoom' },
{ type: 'separator' },
{ role: 'front' },

View File

@@ -13,11 +13,27 @@ export function parseArgs (argv, cwd) {
.command('run [command...]', 'run a command in the terminal', {
command: { type: 'string' },
})
.version('v', 'Show version and exit', app.getVersion())
.alias('d', 'debug')
.describe('d', 'Show DevTools on start')
.alias('h', 'help')
.help('h')
.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('version', {
alias: 'v',
describe: 'Show version and exit',
type: 'boolean'
})
.help('help')
.strict()
.parse(argv.slice(1))
}

View File

@@ -26,13 +26,17 @@ app.on('activate', () => {
}
})
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.send('host:second-instance', parseArgs(argv, cwd))
application.send('host:second-instance', parseArgs(argv, cwd), cwd)
})
const argv = parseArgs(process.argv, process.cwd())

View File

@@ -1,5 +1,5 @@
import { Subject, Observable } from 'rxjs'
import { BrowserWindow, app, ipcMain } from 'electron'
import { BrowserWindow, app, ipcMain, Rectangle, Menu } from 'electron'
import ElectronConfig = require('electron-config')
import * as yaml from 'js-yaml'
import * as fs from 'fs'
@@ -16,6 +16,7 @@ export class Window {
private window: BrowserWindow
private vibrancyViewID: number
private windowConfig: ElectronConfig
private windowBounds: Rectangle
get visible$ (): Observable<boolean> { return this.visible }
@@ -29,7 +30,9 @@ export class Window {
}
this.windowConfig = new ElectronConfig({ name: 'window' })
this.windowBounds = this.windowConfig.get('windowBoundaries')
let maximized = this.windowConfig.get('maximized')
let options: Electron.BrowserWindowConstructorOptions = {
width: 800,
height: 600,
@@ -40,7 +43,7 @@ export class Window {
frame: false,
show: false,
}
Object.assign(options, this.windowConfig.get('windowBoundaries'))
Object.assign(options, this.windowBounds)
if ((configData.appearance || {}).frame === 'native') {
options.frame = true
@@ -65,7 +68,11 @@ export class Window {
} else if (process.platform === 'win32' && (configData.appearance || {}).vibrancy) {
this.setVibrancy(true)
}
this.window.show()
if (maximized) {
this.window.maximize()
} else {
this.window.show()
}
this.window.focus()
})
this.window.loadURL(`file://${app.getAppPath()}/dist/index.html?${this.window.id}`, { extraHeaders: 'pragma: no-cache\n' })
@@ -122,13 +129,26 @@ export class Window {
this.window.on('leave-full-screen', () => this.window.webContents.send('host:window-leave-full-screen'))
this.window.on('close', () => {
this.windowConfig.set('windowBoundaries', this.window.getBounds())
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()
}
})
ipcMain.on('window-focus', () => {
this.window.focus()
})
@@ -164,6 +184,14 @@ export class Window {
ipcMain.on('window-set-vibrancy', (_event, enabled) => {
this.setVibrancy(enabled)
})
ipcMain.on('window-set-title', (_event, title) => {
this.window.setTitle(title)
})
ipcMain.on('window-popup-context-menu', (_event, menuDefinition) => {
Menu.buildFromTemplate(menuDefinition).popup({ window: this.window })
})
}
private destroy () {

View File

@@ -4,8 +4,12 @@ body {
background: #1D272D;
}
.modal-dialog, .modal-backdrop {
-webkit-app-region: no-drag;
.modal-dialog, .modal-backdrop, .no-drag {
-webkit-app-region: no-drag;
}
.selectable {
user-select: text;
}
[ngbradiogroup] input[type="radio"] {

View File

@@ -4,7 +4,7 @@ platform:
- x64
environment:
nodejs_version: "7"
nodejs_version: "10"
cache:
- '%USERPROFILE%\.electron'
@@ -24,3 +24,4 @@ build_script:
artifacts:
- path: 'dist\win\*.exe'
- path: 'dist\*.exe'

View File

@@ -0,0 +1,4 @@
#!/bin/bash
# Link to the binary
ln -sf '/opt/${productFilename}/${executable}' '/usr/bin/${executable}'

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,31 @@
<?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.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,136 @@
<?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>

View File

@@ -0,0 +1,226 @@
<?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>AMApplicationBuild</key>
<string>444.38</string>
<key>AMApplicationVersion</key>
<string>2.9</string>
<key>AMDocumentVersion</key>
<string>2</string>
<key>actions</key>
<array>
<dict>
<key>action</key>
<dict>
<key>AMAccepts</key>
<dict>
<key>Container</key>
<string>List</string>
<key>Optional</key>
<true/>
<key>Types</key>
<array>
<string>com.apple.cocoa.string</string>
</array>
</dict>
<key>AMActionVersion</key>
<string>2.0.3</string>
<key>AMApplication</key>
<array>
<string>Automator</string>
</array>
<key>AMParameterProperties</key>
<dict>
<key>COMMAND_STRING</key>
<dict/>
<key>CheckedForUserDefaultShell</key>
<dict/>
<key>inputMethod</key>
<dict/>
<key>shell</key>
<dict/>
<key>source</key>
<dict/>
</dict>
<key>AMProvides</key>
<dict>
<key>Container</key>
<string>List</string>
<key>Types</key>
<array>
<string>com.apple.cocoa.string</string>
</array>
</dict>
<key>ActionBundlePath</key>
<string>/System/Library/Automator/Run Shell Script.action</string>
<key>ActionName</key>
<string>Run Shell Script</string>
<key>ActionParameters</key>
<dict>
<key>COMMAND_STRING</key>
<string>/Applications/Terminus.app/Contents/MacOS/terminus open "$1"</string>
<key>CheckedForUserDefaultShell</key>
<true/>
<key>inputMethod</key>
<integer>1</integer>
<key>shell</key>
<string>/bin/sh</string>
<key>source</key>
<string></string>
</dict>
<key>BundleIdentifier</key>
<string>com.apple.RunShellScript</string>
<key>CFBundleVersion</key>
<string>2.0.3</string>
<key>CanShowSelectedItemsWhenRun</key>
<false/>
<key>CanShowWhenRun</key>
<true/>
<key>Category</key>
<array>
<string>AMCategoryUtilities</string>
</array>
<key>Class Name</key>
<string>RunShellScriptAction</string>
<key>InputUUID</key>
<string>CDBAB8D4-B8B8-4FBB-9115-B4082FB99E1C</string>
<key>Keywords</key>
<array>
<string>Shell</string>
<string>Script</string>
<string>Command</string>
<string>Run</string>
<string>Unix</string>
</array>
<key>OutputUUID</key>
<string>96B5890B-A95F-4BF2-8E5A-38E07A849C33</string>
<key>UUID</key>
<string>62251AFB-502C-4EA0-821C-D9B8CA96E6EE</string>
<key>UnlocalizedApplications</key>
<array>
<string>Automator</string>
</array>
<key>arguments</key>
<dict>
<key>0</key>
<dict>
<key>default value</key>
<integer>0</integer>
<key>name</key>
<string>inputMethod</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>0</string>
</dict>
<key>1</key>
<dict>
<key>default value</key>
<string></string>
<key>name</key>
<string>source</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>1</string>
</dict>
<key>2</key>
<dict>
<key>default value</key>
<false/>
<key>name</key>
<string>CheckedForUserDefaultShell</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>2</string>
</dict>
<key>3</key>
<dict>
<key>default value</key>
<string></string>
<key>name</key>
<string>COMMAND_STRING</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>3</string>
</dict>
<key>4</key>
<dict>
<key>default value</key>
<string>/bin/sh</string>
<key>name</key>
<string>shell</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>4</string>
</dict>
</dict>
<key>isViewVisible</key>
<true/>
<key>location</key>
<string>529.000000:305.000000</string>
<key>nibPath</key>
<string>/System/Library/Automator/Run Shell Script.action/Contents/Resources/Base.lproj/main.nib</string>
</dict>
<key>isViewVisible</key>
<true/>
</dict>
</array>
<key>connectors</key>
<dict/>
<key>workflowMetaData</key>
<dict>
<key>applicationBundleID</key>
<string>com.apple.finder</string>
<key>applicationBundleIDsByPath</key>
<dict>
<key>/System/Library/CoreServices/Finder.app</key>
<string>com.apple.finder</string>
</dict>
<key>applicationPath</key>
<string>/System/Library/CoreServices/Finder.app</string>
<key>applicationPaths</key>
<array>
<string>/System/Library/CoreServices/Finder.app</string>
</array>
<key>inputTypeIdentifier</key>
<string>com.apple.Automator.fileSystemObject</string>
<key>outputTypeIdentifier</key>
<string>com.apple.Automator.nothing</string>
<key>presentationMode</key>
<integer>15</integer>
<key>processesInput</key>
<integer>0</integer>
<key>serviceApplicationBundleID</key>
<string>com.apple.finder</string>
<key>serviceApplicationPath</key>
<string>/System/Library/CoreServices/Finder.app</string>
<key>serviceInputTypeIdentifier</key>
<string>com.apple.Automator.fileSystemObject</string>
<key>serviceOutputTypeIdentifier</key>
<string>com.apple.Automator.nothing</string>
<key>serviceProcessesInput</key>
<integer>0</integer>
<key>systemImageName</key>
<string>NSActionTemplate</string>
<key>useAutomaticInputType</key>
<integer>0</integer>
<key>workflowTypeIdentifier</key>
<string>com.apple.Automator.servicesMenu</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,31 @@
<?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>Paste path into Terminus</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.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,136 @@
<?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>
DwLo2M9xZ+aZGtMzRCGHhHB/wMY=
</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>

View File

@@ -0,0 +1,226 @@
<?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>AMApplicationBuild</key>
<string>444.38</string>
<key>AMApplicationVersion</key>
<string>2.9</string>
<key>AMDocumentVersion</key>
<string>2</string>
<key>actions</key>
<array>
<dict>
<key>action</key>
<dict>
<key>AMAccepts</key>
<dict>
<key>Container</key>
<string>List</string>
<key>Optional</key>
<true/>
<key>Types</key>
<array>
<string>com.apple.cocoa.string</string>
</array>
</dict>
<key>AMActionVersion</key>
<string>2.0.3</string>
<key>AMApplication</key>
<array>
<string>Automator</string>
</array>
<key>AMParameterProperties</key>
<dict>
<key>COMMAND_STRING</key>
<dict/>
<key>CheckedForUserDefaultShell</key>
<dict/>
<key>inputMethod</key>
<dict/>
<key>shell</key>
<dict/>
<key>source</key>
<dict/>
</dict>
<key>AMProvides</key>
<dict>
<key>Container</key>
<string>List</string>
<key>Types</key>
<array>
<string>com.apple.cocoa.string</string>
</array>
</dict>
<key>ActionBundlePath</key>
<string>/System/Library/Automator/Run Shell Script.action</string>
<key>ActionName</key>
<string>Run Shell Script</string>
<key>ActionParameters</key>
<dict>
<key>COMMAND_STRING</key>
<string>/Applications/Terminus.app/Contents/MacOS/terminus paste --escape "$1"</string>
<key>CheckedForUserDefaultShell</key>
<true/>
<key>inputMethod</key>
<integer>1</integer>
<key>shell</key>
<string>/bin/sh</string>
<key>source</key>
<string></string>
</dict>
<key>BundleIdentifier</key>
<string>com.apple.RunShellScript</string>
<key>CFBundleVersion</key>
<string>2.0.3</string>
<key>CanShowSelectedItemsWhenRun</key>
<false/>
<key>CanShowWhenRun</key>
<true/>
<key>Category</key>
<array>
<string>AMCategoryUtilities</string>
</array>
<key>Class Name</key>
<string>RunShellScriptAction</string>
<key>InputUUID</key>
<string>CDBAB8D4-B8B8-4FBB-9115-B4082FB99E1C</string>
<key>Keywords</key>
<array>
<string>Shell</string>
<string>Script</string>
<string>Command</string>
<string>Run</string>
<string>Unix</string>
</array>
<key>OutputUUID</key>
<string>96B5890B-A95F-4BF2-8E5A-38E07A849C33</string>
<key>UUID</key>
<string>62251AFB-502C-4EA0-821C-D9B8CA96E6EE</string>
<key>UnlocalizedApplications</key>
<array>
<string>Automator</string>
</array>
<key>arguments</key>
<dict>
<key>0</key>
<dict>
<key>default value</key>
<integer>0</integer>
<key>name</key>
<string>inputMethod</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>0</string>
</dict>
<key>1</key>
<dict>
<key>default value</key>
<string></string>
<key>name</key>
<string>source</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>1</string>
</dict>
<key>2</key>
<dict>
<key>default value</key>
<false/>
<key>name</key>
<string>CheckedForUserDefaultShell</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>2</string>
</dict>
<key>3</key>
<dict>
<key>default value</key>
<string></string>
<key>name</key>
<string>COMMAND_STRING</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>3</string>
</dict>
<key>4</key>
<dict>
<key>default value</key>
<string>/bin/sh</string>
<key>name</key>
<string>shell</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>4</string>
</dict>
</dict>
<key>isViewVisible</key>
<true/>
<key>location</key>
<string>529.000000:305.000000</string>
<key>nibPath</key>
<string>/System/Library/Automator/Run Shell Script.action/Contents/Resources/Base.lproj/main.nib</string>
</dict>
<key>isViewVisible</key>
<true/>
</dict>
</array>
<key>connectors</key>
<dict/>
<key>workflowMetaData</key>
<dict>
<key>applicationBundleID</key>
<string>com.apple.finder</string>
<key>applicationBundleIDsByPath</key>
<dict>
<key>/System/Library/CoreServices/Finder.app</key>
<string>com.apple.finder</string>
</dict>
<key>applicationPath</key>
<string>/System/Library/CoreServices/Finder.app</string>
<key>applicationPaths</key>
<array>
<string>/System/Library/CoreServices/Finder.app</string>
</array>
<key>inputTypeIdentifier</key>
<string>com.apple.Automator.fileSystemObject</string>
<key>outputTypeIdentifier</key>
<string>com.apple.Automator.nothing</string>
<key>presentationMode</key>
<integer>15</integer>
<key>processesInput</key>
<integer>0</integer>
<key>serviceApplicationBundleID</key>
<string>com.apple.finder</string>
<key>serviceApplicationPath</key>
<string>/System/Library/CoreServices/Finder.app</string>
<key>serviceInputTypeIdentifier</key>
<string>com.apple.Automator.fileSystemObject</string>
<key>serviceOutputTypeIdentifier</key>
<string>com.apple.Automator.nothing</string>
<key>serviceProcessesInput</key>
<integer>0</integer>
<key>systemImageName</key>
<string>NSActionTemplate</string>
<key>useAutomaticInputType</key>
<integer>0</integer>
<key>workflowTypeIdentifier</key>
<string>com.apple.Automator.servicesMenu</string>
</dict>
</dict>
</plist>

View File

@@ -1,6 +1,10 @@
### Changes
##### v0.4.9
- Fixed broken Doskey on Win10 (#438, #451)
##### v0.4.8
- Environment variable 'clink_profile' overrides Clink's profile path (#390).

View File

@@ -136,7 +136,7 @@ h6 { font-size: 1.00em; }
<!-- ----------------------------------------------------- -->
<body onload="go();">
<div id="header">
<div id="title">Clink v0.4.8</div>
<div id="title">Clink v0.4.9</div>
<div id="url">
<a href="http://github.com/mridgers/clink">
http://github.com/mridgers/clink
@@ -697,6 +697,10 @@ The current cursor position within the line buffer.
### Changes
##### v0.4.9
- Fixed broken Doskey on Win10 (#438, #451)
##### v0.4.8
- Environment variable 'clink_profile' overrides Clink's profile path (#390).

BIN
extras/clink/clink_x64.exe Normal file

Binary file not shown.

BIN
extras/clink/clink_x86.exe Normal file

Binary file not shown.

View File

@@ -16,7 +16,7 @@
"core-js": "2.4.1",
"cross-env": "4.0.0",
"css-loader": "0.28.0",
"electron": "3.0.0-beta.9",
"electron": "3.0.0",
"electron-builder": "^20.27.1",
"electron-builder-squirrel-windows": "17.0.1",
"electron-installer-snap": "^3.0.0",
@@ -34,7 +34,7 @@
"json-loader": "0.5.4",
"less": "2.7.1",
"less-loader": "2.2.3",
"node-abi": "^2.4.1",
"node-abi": "^2.4.4",
"node-gyp": "^3.6.2",
"node-sass": "^4.5.3",
"npmlog": "4.1.0",
@@ -73,17 +73,21 @@
],
"extraResources": [
"builtin-plugins",
"clink"
"extras"
],
"win": {
"icon": "./build/windows/icon.ico",
"publish": [
"github"
]
],
"artifactName": "terminus-${version}-setup.exe"
},
"squirrelWindows": {
"iconUrl": "https://github.com/Eugeny/terminus/raw/master/build/windows/icon.ico",
"artifactName": "terminus-${version}-${os}-${arch}.exe"
"artifactName": "terminus-${version}-setup.exe"
},
"portable": {
"artifactName": "terminus-${version}-portable.exe"
},
"mac": {
"category": "public.app-category.video",
@@ -96,11 +100,12 @@
}
},
"dmg": {
"artifactName": "terminus-${version}-${os}-${arch}.dmg"
"artifactName": "terminus-${version}-macos.dmg"
},
"linux": {
"category": "Utilities",
"icon": "./build/icons",
"artifactName": "terminus-${version}-linux.${ext}",
"publish": [
"github"
]
@@ -116,19 +121,18 @@
"libnss3",
"tmux"
],
"artifactName": "terminus-${version}-${os}-${arch}.deb"
"afterInstall": "build/linux/after-install.tpl"
},
"rpm": {
"depends": [
"screen",
"gnome-python2-gnomekeyring"
],
"artifactName": "terminus-${version}-${os}-${arch}.rpm"
]
}
},
"scripts": {
"build": "webpack --color --config app/webpack.main.config.js && webpack --color --config app/webpack.config.js && webpack --color --config terminus-core/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-terminal/webpack.config.js && webpack --color --config terminus-settings/webpack.config.js && webpack --color --config terminus-plugin-manager/webpack.config.js && webpack --color --config terminus-community-color-schemes/webpack.config.js && webpack --color --config terminus-ssh/webpack.config.js",
"watch": "DEV=1 webpack --progress --color --watch",
"watch": "cross-env DEV=1 webpack --progress --color --watch",
"start": "cross-env DEV=1 electron app --debug",
"prod": "cross-env DEV=1 electron app",
"lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts",

View File

@@ -4,7 +4,7 @@ const vars = require('./vars')
builder({
dir: true,
win: ['squirrel'],
win: ['squirrel', 'portable'],
config: {
extraMetadata: {
version: vars.version,

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-community-color-schemes",
"version": "1.0.0-alpha.48",
"version": "1.0.0-alpha.55",
"description": "Community color schemes for Terminus",
"keywords": [
"terminus-builtin-plugin"

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-core",
"version": "1.0.0-alpha.48",
"version": "1.0.0-alpha.55",
"description": "Terminus core",
"keywords": [
"terminus-builtin-plugin"
@@ -27,6 +27,7 @@
"electron-updater": "^2.8.9",
"ng2-dnd": "^5.0.2",
"ngx-perfect-scrollbar": "^6.0.0",
"shell-escape": "^0.2.0",
"universal-analytics": "^0.4.17"
},
"peerDependencies": {
@@ -42,6 +43,7 @@
"dependencies": {
"deepmerge": "^1.5.0",
"js-yaml": "^3.9.0",
"winreg": "^1.2.4",
"winston": "^2.4.0"
},
"false": {}

View File

@@ -13,4 +13,5 @@ export { Logger, LogService } from '../services/log.service'
export { HomeBaseService } from '../services/homeBase.service'
export { HotkeysService } from '../services/hotkeys.service'
export { HostAppService, Platform } from '../services/hostApp.service'
export { ShellIntegrationService } from '../services/shellIntegration.service'
export { ThemesService } from '../services/themes.service'

View File

@@ -4,7 +4,7 @@
height: 100vh;
flex-direction: column;
overflow: hidden;
-webkit-user-select: none;
user-select: none;
-webkit-user-drag: none;
-webkit-font-smoothing: antialiased;
will-change: transform;

View File

@@ -157,6 +157,7 @@ export class AppRootComponent {
if (this.hostApp.getWindow().isFocused()) {
// focused
this.electron.loseFocus()
this.hostApp.getWindow().blur()
if (this.hostApp.platform !== Platform.macOS) {
this.hostApp.getWindow().hide()
}

View File

@@ -20,4 +20,4 @@ footer.d-flex.align-items-center
i.fa.fa-bug
span Report a problem
.form-control-static Version: {{homeBase.appVersion}}
.form-control-static.selectable.no-drag Version: {{homeBase.appVersion}}

View File

@@ -33,6 +33,4 @@ a, button {
width: 16px;
height: 16px;
margin-right: 10px;
fill: white;
fill-opacity: 0.75;
}

View File

@@ -13,8 +13,6 @@ $tabs-height: 36px;
overflow: hidden;
transition: 0.125s ease-out all;
.index {
flex: none;
font-weight: bold;
@@ -44,12 +42,13 @@ $tabs-height: 36px;
flex: none;
background: transparent;
opacity: 0;
-webkit-app-region: no-drag;
$button-size: 26px;
width: $button-size;
height: $button-size;
border-radius: $button-size / 2;
line-height: $button-size;
line-height: $button-size * 0.9;
align-self: center;
margin-right: 10px;

View File

@@ -9,7 +9,7 @@ button.btn.btn-secondary.btn-maximize(
svg(version='1.1', width='10', height='10')
path(d='M 0,0 0,10 10,10 10,0 Z M 1,1 9,1 9,9 1,9 Z')
button.btn.btn-secondary.btn-close(
(click)='hostApp.quit()',
(click)='hostApp.getWindow().close()',
)
svg(version='1.1', width='10', height='10')
path(d='M 0,0 0,0.7 4.3,5 0,9.3 0,10 0.7,10 5,5.7 9.3,10 10,10 10,9.3 5.7,5 10,0.7 10,0 9.3,0 5,4.3 0.7,0 Z')

View File

@@ -14,6 +14,7 @@ import { LogService } from './services/log.service'
import { HomeBaseService } from './services/homeBase.service'
import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service'
import { DockingService } from './services/docking.service'
import { ShellIntegrationService } from './services/shellIntegration.service'
import { TabRecoveryService } from './services/tabRecovery.service'
import { ThemesService } from './services/themes.service'
import { TouchbarService } from './services/touchbar.service'
@@ -36,7 +37,7 @@ import { HotkeyProvider } from './api/hotkeyProvider'
import { ConfigProvider } from './api/configProvider'
import { Theme } from './api/theme'
import { StandardTheme, StandardCompactTheme } from './theme'
import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
import { CoreConfigProvider } from './config'
import 'perfect-scrollbar/css/perfect-scrollbar.css'
@@ -51,6 +52,7 @@ const PROVIDERS = [
HostAppService,
HotkeysService,
LogService,
ShellIntegrationService,
TabRecoveryService,
ThemesService,
TouchbarService,
@@ -58,6 +60,7 @@ const PROVIDERS = [
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
{ provide: Theme, useClass: StandardTheme, multi: true },
{ provide: Theme, useClass: StandardCompactTheme, multi: true },
{ provide: Theme, useClass: PaperTheme, multi: true },
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true }}
]

View File

@@ -50,7 +50,7 @@ export class AppService {
tab.titleChange$.subscribe(title => {
if (tab === this.activeTab) {
this.hostApp.getWindow().setTitle(title)
this.hostApp.setTitle(title)
}
})
return tab
@@ -74,8 +74,8 @@ export class AppService {
this.activeTabChange.next(tab)
if (this.activeTab) {
this.activeTab.emitFocused()
this.hostApp.setTitle(this.activeTab.title)
}
this.hostApp.getWindow().setTitle(this.activeTab.title)
}
toggleLastTab () {

View File

@@ -25,12 +25,14 @@ export class HomeBaseService {
reportBug () {
let body = `Version: ${this.appVersion}\n`
body += `Platform: ${os.platform()} ${os.release()}\n\n`
body += `Platform: ${os.platform()} ${os.release()}\n`
let label = {
darwin: 'macOS',
windows: 'Windows',
linux: 'Linux',
darwin: 'OS: macOS',
windows: 'OS: Windows',
linux: 'OS: Linux',
}[os.platform()]
let plugins = (window as any).installedPlugins.filter(x => !x.isBuiltin).map(x => x.name)
body += `Plugins: ${plugins.join(', ')}\n\n`
this.electron.shell.openExternal(`https://github.com/eugeny/terminus/issues/new?body=${encodeURIComponent(body)}&labels=${label}`)
}

View File

@@ -1,4 +1,5 @@
import * as path from 'path'
import shellEscape = require('shell-escape')
import { Observable, Subject } from 'rxjs'
import { Injectable, NgZone, EventEmitter } from '@angular/core'
import { ElectronService } from './electron.service'
@@ -25,6 +26,7 @@ export class HostAppService {
private secondInstance = new Subject<void>()
private cliOpenDirectory = new Subject<string>()
private cliRunCommand = new Subject<string[]>()
private cliPaste = new Subject<string>()
private configChangeBroadcast = new Subject<void>()
private logger: Logger
private windowId: number
@@ -33,6 +35,7 @@ export class HostAppService {
get secondInstance$ (): Observable<void> { return this.secondInstance }
get cliOpenDirectory$ (): Observable<string> { return this.cliOpenDirectory }
get cliRunCommand$ (): Observable<string[]> { return this.cliRunCommand }
get cliPaste$ (): Observable<string> { return this.cliPaste }
get configChangeBroadcast$ (): Observable<void> { return this.configChangeBroadcast }
constructor (
@@ -76,6 +79,12 @@ export class HostAppService {
this.cliOpenDirectory.next(path.resolve(cwd, argv.directory))
} else if (op === 'run') {
this.cliRunCommand.next(argv.command)
} else if (op === 'paste') {
let text = argv.text
if (argv.escape) {
text = shellEscape([text])
}
this.cliPaste.next(text)
}
}))
@@ -106,7 +115,7 @@ export class HostAppService {
toggleFullscreen () {
let window = this.getWindow()
window.setFullScreen(!window.isFullScreen())
window.setFullScreen(!this.isFullScreen)
}
openDevTools () {
@@ -151,6 +160,18 @@ export class HostAppService {
}
}
setTitle (title: string) {
this.electron.ipcRenderer.send('window-set-title', title)
}
setTouchBar (touchBar: Electron.TouchBar) {
this.getWindow().setTouchBar(touchBar)
}
popupContextMenu (menuDefinition: Electron.MenuItemConstructorOptions[]) {
this.electron.ipcRenderer.send('window-popup-context-menu', menuDefinition)
}
broadcastConfigChange () {
this.electron.ipcRenderer.send('app:config-change')
}

View File

@@ -0,0 +1,85 @@
import * as path from 'path'
import * as fs from 'mz/fs'
import { exec } from 'mz/child_process'
import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
import { HostAppService, Platform } from './hostApp.service'
let Registry = null
try {
Registry = require('winreg')
} catch (_) { } // tslint:disable-line no-empty
@Injectable()
export class ShellIntegrationService {
private automatorWorkflows = ['Open Terminus here.workflow', 'Paste path into Terminus.workflow']
private automatorWorkflowsLocation: string
private automatorWorkflowsDestination: string
private registryKeys = [
{
path: '\\Software\\Classes\\Directory\\Background\\shell\\Open Terminus here',
command: 'open "%V"'
},
{
path: '\\Software\\Classes\\*\\shell\\Paste path into Terminus',
command: 'paste "%V"'
},
]
constructor (
private electron: ElectronService,
private hostApp: HostAppService,
) {
if (this.hostApp.platform === Platform.macOS) {
this.automatorWorkflowsLocation = path.join(
path.dirname(path.dirname(this.electron.app.getPath('exe'))),
'Resources',
'extras',
'automator-workflows',
)
this.automatorWorkflowsDestination = path.join(process.env.HOME, 'Library', 'Services')
}
}
async isInstalled (): Promise<boolean> {
if (this.hostApp.platform === Platform.macOS) {
return await fs.exists(path.join(this.automatorWorkflowsDestination, this.automatorWorkflows[0]))
} else if (this.hostApp.platform === Platform.Windows) {
return await new Promise<boolean>(resolve => {
let reg = new Registry({ hive: Registry.HKCU, key: this.registryKeys[0].path, arch: 'x64' })
reg.keyExists((err, exists) => resolve(!err && exists))
})
}
return true
}
async install () {
if (this.hostApp.platform === Platform.macOS) {
for (let wf of this.automatorWorkflows) {
await exec(`cp -r "${this.automatorWorkflowsLocation}/${wf}" "${this.automatorWorkflowsDestination}"`)
}
} else if (this.hostApp.platform === Platform.Windows) {
for (let registryKey of this.registryKeys) {
let reg = new Registry({ hive: Registry.HKCU, key: registryKey.path, arch: 'x64' })
await new Promise(resolve => {
reg.set('Icon', Registry.REG_SZ, this.electron.app.getPath('exe'), () => {
reg.create(() => {
let cmd = new Registry({
hive: Registry.HKCU,
key: registryKey.path + '\\command',
arch: 'x64'
})
cmd.create(() => {
cmd.set(
'',
Registry.REG_SZ,
this.electron.app.getPath('exe') + ' ' + registryKey.command,
() => resolve()
)
})
})
})
})
}
}
}
}

View File

@@ -10,6 +10,7 @@ import { IToolbarButton, ToolbarButtonProvider } from '../api'
export class TouchbarService {
private tabsSegmentedControl: TouchBarSegmentedControl
private tabSegments: SegmentedControlSegment[] = []
private nsImageCache: {[id: string]: Electron.NativeImage} = {}
constructor (
private app: AppService,
@@ -53,18 +54,24 @@ export class TouchbarService {
...buttons.map(button => this.getButton(button))
]
})
this.hostApp.getWindow().setTouchBar(touchBar)
this.hostApp.setTouchBar(touchBar)
}
private getButton (button: IToolbarButton): Electron.TouchBarButton {
return new this.electron.TouchBar.TouchBarButton({
label: button.touchBarNSImage ? null : this.shortenTitle(button.touchBarTitle || button.title),
icon: button.touchBarNSImage ?
this.electron.nativeImage.createFromNamedImage(button.touchBarNSImage, [0, 0, 1]) : null,
icon: button.touchBarNSImage ? this.getCachedNSImage(button.touchBarNSImage) : null,
click: () => this.zone.run(() => button.click()),
})
}
private getCachedNSImage (name: string) {
if (!this.nsImageCache[name]) {
this.nsImageCache[name] = this.electron.nativeImage.createFromNamedImage(name, [0, 0, 1])
}
return this.nsImageCache[name]
}
private shortenTitle (title: string): string {
if (title.length > 15) {
title = title.substring(0, 15) + '...'

View File

@@ -0,0 +1,398 @@
$black: #002b36;
$base02: #073642;
$base01: #586e75;
$base00: #657b83;
$base0: #839496;
$base1: #93a1a1;
$base2: #eee8d5;
$white: #fdf6e3;
$yellow: #b58900;
$orange: #cb4b16;
$red: #dc322f;
$pink: #d33682;
$purple: #6c71c4;
$blue: #268bd2;
$teal: #2aa198;
$green: #859900;
$tab-border-radius: 5px;
$button-hover-bg: rgba(0, 0, 0, .125);
$button-active-bg: rgba(0, 0, 0, .25);
$theme-colors: (
"primary": $orange,
"secondary": $base0
);
$content-bg: rgba($white, 0.65);
$content-bg-solid: $white;
$body-bg: $base2;
$body-bg2: $base1;
$body-color: $black;
$font-family-sans-serif: "Source Sans Pro";
$font-size-base: 14rem / 16;
$btn-border-radius: 0;
$nav-tabs-border-width: 0;
$nav-tabs-border-radius: 0;
$nav-tabs-link-hover-border-color: $body-bg;
$nav-tabs-active-link-hover-color: $white;
$nav-tabs-active-link-hover-bg: $blue;
$nav-tabs-active-link-hover-border-color: darken($blue, 30%);
$nav-pills-border-radius: 0;
$input-bg: $base2;
$input-disabled-bg: $base1;
$input-color: $body-color;
$input-color-placeholder: $base1;
$input-border-color: $base1;
//$input-box-shadow: inset 0 1px 1px rgba($black,.075);
$input-border-radius: 0;
$custom-select-border-radius: 0;
$input-bg-focus: $input-bg;
//$input-border-focus: lighten($brand-primary, 25%);
//$input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6);
$input-color-focus: $input-color;
$input-group-addon-bg: $body-bg;
$input-group-addon-border-color: $input-border-color;
$modal-content-bg: $content-bg-solid;
$modal-content-border-color: $body-bg;
$modal-header-border-color: transparent;
$modal-footer-border-color: transparent;
$popover-bg: $body-bg;
$dropdown-bg: $body-bg;
$dropdown-link-color: $body-color;
$dropdown-link-hover-color: #333;
$dropdown-link-hover-bg: $body-bg2;
//$dropdown-link-active-color: $component-active-color;
//$dropdown-link-active-bg: $component-active-bg;
$dropdown-link-disabled-color: #333;
$dropdown-header-color: #333;
$list-group-color: $body-color;
$list-group-bg: rgba($black,.05);
$list-group-border-color: rgba($black,.1);
$list-group-hover-bg: rgba($black,.1);
$list-group-link-active-bg: rgba($black,.2);
$list-group-action-color: $body-color;
$list-group-action-bg: rgba($black,.05);
$list-group-action-active-bg: $list-group-link-active-bg;
$list-group-border-radius: 0;
$pre-bg: $dropdown-bg;
$pre-color: $dropdown-link-color;
$alert-danger-bg: $body-bg;
$alert-danger-text: $red;
$alert-danger-border: $red;
$headings-font-weight: lighter;
$headings-color: $base0;
@import '~bootstrap/scss/bootstrap.scss';
window-controls {
svg {
transition: 0.25s fill;
fill: $base01;
}
button:hover {
background: rgba($black, 0.125);
svg {
fill: $black;
}
}
.btn-close:hover {
background: #8a2828;
}
}
$border-color: $base1;
body {
background: $body-bg;
&.vibrant {
background: rgba(255, 255, 255,.4) !important;
}
}
app-root {
&> .content {
.tab-bar {
height: 40px;
.btn-tab-bar {
background: transparent;
line-height: 42px;
svg {
fill: $black;
fill-opacity: 0.75;
}
&:hover { background: rgba(0, 0, 0, .125) !important; }
&:active { background: rgba(0, 0, 0, .25) !important; }
}
&>.tabs {
tab-header {
border-left: 1px solid transparent;
border-right: 1px solid transparent;
color: $base01;
transition: 0.125s ease-out width;
.index {
color: rgba($black, 0.4);
}
button {
color: $body-color;
border: none;
transition: 0.25s all;
&:hover { background: $button-hover-bg !important; }
&:active { background: $button-active-bg !important; }
}
.progressbar {
background: $blue;
}
&.active {
color: $black;
background: $content-bg;
border-left: 1px solid $border-color;
border-right: 1px solid $border-color;
}
}
}
}
&.tabs-on-top .tab-bar {
&>.background {
border-bottom: 1px solid $border-color;
}
tab-header {
border-bottom: 1px solid $border-color;
&.active {
border-bottom-color: transparent;
}
&.has-activity:not(.active) {
background: linear-gradient(to bottom, rgba(208, 0, 0, 0) 95%, #36beff 96%);
}
}
}
&:not(.tabs-on-top) .tab-bar {
&>.background {
border-top: 1px solid $border-color;
}
tab-header {
border-top: 1px solid $border-color;
&.active {
margin-top: -1px;
}
&.has-activity:not(.active) {
background: linear-gradient(to top, rgba(208, 0, 0, 0) 95%, #36beff 96%);
}
}
}
}
&.platform-win32, &.platform-linux {
border: 1px solid #111;
&>.content .tab-bar .tabs tab-header:first-child {
border-left: none;
}
}
}
tab-body {
background: $content-bg;
}
settings-tab > ngb-tabset {
border-right: 1px solid $body-bg;
& > .nav {
background: rgba(0, 0, 0, 0.25);
& > .nav-item > .nav-link {
border: none;
padding: 10px 50px 10px 20px;
font-size: 14px;
&:not(.active) {
color: $body-color;
}
}
}
}
multi-hotkey-input {
.item {
background: $body-bg2;
border: 1px solid $blue;
border-radius: 3px;
margin-right: 5px;
.body {
padding: 3px 0 2px;
.stroke {
padding: 0 6px;
border-right: 1px solid $content-bg;
}
}
.remove {
padding: 3px 8px 2px;
}
}
.add {
color: #777;
padding: 4px 10px 0;
}
.add, .item .body, .item .remove {
&:hover { background: darken($body-bg2, 5%); }
&:active { background: darken($body-bg2, 15%); }
}
}
hotkey-input-modal {
.input {
background: $input-bg;
padding: 10px;
font-size: 24px;
line-height: 27px;
height: 55px;
.stroke {
background: $body-bg2;
border: 1px solid $blue;
border-radius: 3px;
margin-right: 10px;
padding: 3px 10px;
}
}
.timeout {
background: $input-bg;
div {
background: $blue;
}
}
}
.form-group label {
margin-bottom: 2px;
}
.nav-tabs {
.nav-link {
transition: 0.25s all;
border-bottom-color: $nav-tabs-border-color;
}
}
ngb-tabset .tab-content {
padding-top: 20px;
}
[ngbradiogroup] > label.active {
background: $blue;
}
.btn {
i + * {
margin-left: 5px;
}
&.btn-lg i + * {
margin-left: 10px;
}
}
.input-group-addon + .form-control {
border-left: none;
}
.input-group > select.form-control {
flex-direction: row;
}
.list-group-item {
transition: 0.25s background;
&:not(:first-child) {
border-top: none;
}
i + * {
margin-left: 10px;
}
}
select.form-control {
-webkit-appearance: none;
background-image: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24' viewBox='0 0 24 24'><path fill='#444' d='M7.406 7.828l4.594 4.594 4.594-4.594 1.406 1.406-6 6-6-6z'></path></svg>");
background-position: 100% 50%;
background-repeat: no-repeat;
padding-right: 30px;
}
checkbox i.on {
color: $blue;
}
toggle {
.body {
border-color: $base0 !important;
.toggle {
background: $base0 !important;
}
}
&.active .body .toggle {
background: theme-colors(primary) !important;
}
}
.list-group-item svg {
fill: $black;
}
.terminus-title {
color: $base01;
}
.terminus-logo {
filter: saturate(0);
}
start-page footer {
background: $white !important;
}

View File

@@ -36,8 +36,7 @@ $btn-secondary-border: #444;
//$btn-warning-bg: rgba($orange, .5);
$nav-tabs-border-color: $body-bg;
$nav-tabs-border-width: 1px;
$nav-tabs-border-width: 0;
$nav-tabs-border-radius: 0;
$nav-tabs-link-hover-border-color: $body-bg;
$nav-tabs-active-link-hover-color: $white;
@@ -138,10 +137,10 @@ app-root {
border-left: 1px solid transparent;
border-right: 1px solid transparent;
transition: 0.25s all;
transition: 0.125s ease-out width;
.index {
color: #888;
color: rgba(255, 255, 255, 0.4);
}
button {
@@ -223,10 +222,7 @@ settings-tab > ngb-tabset {
background: rgba(0, 0, 0, 0.25);
& > .nav-item > .nav-link {
border-left: none;
border-right: none;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
border: none;
padding: 10px 50px 10px 20px;
font-size: 14px;
@@ -237,11 +233,6 @@ settings-tab > ngb-tabset {
color: $white;
}
}
&.active {
border-top-color: $nav-tabs-active-link-hover-border-color;
border-bottom-color: $nav-tabs-active-link-hover-border-color;
}
}
}
}
@@ -365,3 +356,8 @@ checkbox i.on {
toggle.active .body .toggle {
background: $blue;
}
.list-group-item svg {
fill: white;
fill-opacity: 0.75;
}

View File

@@ -14,3 +14,10 @@ export class StandardCompactTheme extends Theme {
css = require('./theme.compact.scss')
terminalBackground = '#1D272D'
}
@Injectable()
export class PaperTheme extends Theme {
name = 'Paper'
css = require('./theme.paper.scss')
terminalBackground = '#1D272D'
}

View File

@@ -460,6 +460,10 @@ semver@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
shell-escape@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/shell-escape/-/shell-escape-0.2.0.tgz#68fd025eb0490b4f567a027f0bf22480b5f84133"
sntp@2.x.x:
version "2.1.0"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
@@ -545,6 +549,10 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
winreg@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b"
winston@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee"

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-plugin-manager",
"version": "1.0.0-alpha.48",
"version": "1.0.0-alpha.55",
"description": "Terminus' plugin manager",
"keywords": [
"terminus-builtin-plugin"

View File

@@ -72,7 +72,7 @@ div(*ngIf='npmInstalled')
)
.list-group(*ngIf='availablePlugins$')
.list-group.mb-4(*ngIf='availablePlugins$')
ng-container(*ngFor='let plugin of (availablePlugins$|async|orderBy:"name")')
.list-group-item.flex-column.align-items-start(*ngIf='!isAlreadyInstalled(plugin)')
.d-flex.w-100

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-settings",
"version": "1.0.0-alpha.48",
"version": "1.0.0-alpha.55",
"description": "Terminus terminal settings page",
"keywords": [
"terminus-builtin-plugin"

View File

@@ -19,6 +19,14 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
i.fa.fa-bug
span Report a problem
.form-line(*ngIf='!isShellIntegrationInstalled')
.header
.title Shell integration
.description Allows quickly opening a terminal in the selected folder
button.btn.btn-primary((click)='installShellIntegration()')
i.fa.fa-check
span Install
.form-line
.header
.title Theme

View File

@@ -1,7 +1,19 @@
import * as yaml from 'js-yaml'
import { Subscription } from 'rxjs'
import { Component, Inject, Input } from '@angular/core'
import { ElectronService, DockingService, ConfigService, IHotkeyDescription, HotkeyProvider, BaseTabComponent, Theme, HostAppService, Platform, HomeBaseService } from 'terminus-core'
import {
ElectronService,
DockingService,
ConfigService,
IHotkeyDescription,
HotkeyProvider,
BaseTabComponent,
Theme,
HostAppService,
Platform,
HomeBaseService,
ShellIntegrationService
} from 'terminus-core'
import { SettingsTabProvider } from '../api'
@@ -21,6 +33,7 @@ export class SettingsTabComponent extends BaseTabComponent {
Platform = Platform
configDefaults: any
configFile: string
isShellIntegrationInstalled = false
private configSubscription: Subscription
constructor (
@@ -29,6 +42,7 @@ export class SettingsTabComponent extends BaseTabComponent {
public docking: DockingService,
public hostApp: HostAppService,
public homeBase: HomeBaseService,
public shellIntegration: ShellIntegrationService,
@Inject(HotkeyProvider) hotkeyProviders: HotkeyProvider[],
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
@Inject(Theme) public themes: Theme[],
@@ -47,6 +61,10 @@ export class SettingsTabComponent extends BaseTabComponent {
})
}
async ngOnInit () {
this.isShellIntegrationInstalled = await this.shellIntegration.isInstalled()
}
getRecoveryToken (): any {
return { type: 'app:settings' }
}
@@ -75,4 +93,9 @@ export class SettingsTabComponent extends BaseTabComponent {
return false
}
}
async installShellIntegration () {
await this.shellIntegration.install()
this.isShellIntegrationInstalled = true
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-ssh",
"version": "1.0.0-alpha.48",
"version": "1.0.0-alpha.55",
"description": "SSH connection manager for Terminus",
"keywords": [
"terminus-builtin-plugin"

View File

@@ -14,6 +14,9 @@ export interface SSHConnection {
privateKey?: string
group?: string
scripts?: LoginScript[]
keepaliveInterval?: number
keepaliveCountMax?: number
readyTimeout?: number
}
export class SSHSession extends BaseSession {

View File

@@ -58,6 +58,34 @@
button.btn.btn-secondary((click)='selectPrivateKey()')
i.fa.fa-folder-open
ngb-tab(id='advanced')
ng-template(ngbTabTitle)
| Advanced Setting
ng-template(ngbTabContent)
.form-group
label Keep Alive Interval (Milliseconds)
input.form-control(
type='number',
placeholder='0',
[(ngModel)]='connection.keepaliveInterval',
)
.form-group
label Max Keep Alive Count
input.form-control(
type='number',
placeholder='3',
[(ngModel)]='connection.keepaliveCountMax',
)
.form-group
label Ready Timeout (Milliseconds)
input.form-control(
type='number',
placeholder='20000',
[(ngModel)]='connection.readyTimeout',
)
ngb-tab(id='scripts')
ng-template(ngbTabTitle)
| Login Scripts
@@ -71,12 +99,12 @@
td
input.form-control(
type='text',
value='{{script.expect}}',
[(ngModel)]='script.expect'
)
td
input.form-control(
type='text',
value='{{script.send}}',
[(ngModel)]='script.send'
)
td
.input-group.flex-nowrap

View File

@@ -110,6 +110,9 @@ export class SSHService {
tryKeyboard: true,
agent,
agentForward: !!agent,
keepaliveInterval: connection.keepaliveInterval,
keepaliveCountMax: connection.keepaliveCountMax,
readyTimeout: connection.readyTimeout,
})
let keychainPasswordUsed = false

View File

@@ -1,6 +1,6 @@
{
"name": "terminus-terminal",
"version": "1.0.0-alpha.48",
"version": "1.0.0-alpha.55",
"description": "Terminus' terminal emulation core",
"keywords": [
"terminus-builtin-plugin"
@@ -41,7 +41,7 @@
"@types/async-lock": "0.0.19",
"async-lock": "^1.0.0",
"font-manager": "0.3.0",
"hterm-umdjs": "1.1.3",
"hterm-umdjs": "1.4.1",
"mz": "^2.6.0",
"node-pty-tmp": "0.7.2",
"ps-node": "^0.1.6",

View File

@@ -15,30 +15,6 @@ export class ButtonProvider extends ToolbarButtonProvider {
hotkeys: HotkeysService,
) {
super()
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
if (hotkey === 'new-tab') {
terminal.openTab()
}
})
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
if (hotkey === 'new-window') {
hostApp.newWindow()
}
})
hostApp.cliOpenDirectory$.subscribe(async directory => {
if (await fs.exists(directory)) {
if ((await fs.stat(directory)).isDirectory()) {
this.terminal.openTab(null, directory)
}
}
})
hostApp.cliRunCommand$.subscribe(async command => {
this.terminal.openTab({
id: '',
command: command[0],
args: command.slice(1),
}, null, true)
})
if (!electron.remote.process.env.DEV) {
setImmediate(async () => {
let argv: string[] = electron.remote.process.argv

View File

@@ -199,7 +199,7 @@ export class TerminalTabComponent extends BaseTabComponent {
}
})
this.contextMenu = this.electron.remote.Menu.buildFromTemplate([
this.contextMenu = [
{
label: 'New terminal',
click: () => {
@@ -227,7 +227,7 @@ export class TerminalTabComponent extends BaseTabComponent {
})
}
},
])
]
}
detachTermContainerHandlers () {
@@ -249,9 +249,7 @@ export class TerminalTabComponent extends BaseTabComponent {
if (event.type === 'mousedown') {
if (event.which === 3) {
if (this.config.store.terminal.rightClick === 'menu') {
this.contextMenu.popup({
async: true,
})
this.hostApp.popupContextMenu(this.contextMenu)
} else if (this.config.store.terminal.rightClick === 'paste') {
this.paste()
}
@@ -292,6 +290,7 @@ export class TerminalTabComponent extends BaseTabComponent {
sendInput (data: string) {
this.session.write(data)
this.frontend.scrollToBottom()
}
write (data: string) {

View File

@@ -52,6 +52,7 @@ export abstract class Frontend {
abstract write (data: string): void
abstract clear (): void
abstract visualBell (): void
abstract scrollToBottom (): void
abstract configure (configStore: any): void
abstract setZoom (zoom: number): void

View File

@@ -8,6 +8,7 @@ export class HTermFrontend extends Frontend {
private initialized = false
private configuredFontSize = 0
private configuredLinePadding = 0
private configuredBackgroundColor = 'transparent'
private zoom = 0
attach (host: HTMLElement) {
@@ -87,6 +88,8 @@ export class HTermFrontend extends Frontend {
preferenceManager.set('background-color', 'transparent')
}
this.configuredBackgroundColor = preferenceManager.get('background-color')
if (config.terminal.colorScheme.colors) {
preferenceManager.set(
'color-palette-overrides',
@@ -134,13 +137,16 @@ export class HTermFrontend extends Frontend {
}
visualBell (): void {
const color = preferenceManager.get('background-color')
preferenceManager.set('background-color', 'rgba(128,128,128,.25)')
setTimeout(() => {
preferenceManager.set('background-color', color)
preferenceManager.set('background-color', this.configuredBackgroundColor)
}, 125)
}
scrollToBottom (): void {
this.term.scrollEnd()
}
private setFontSize () {
preferenceManager.set('font-size', this.configuredFontSize * Math.pow(1.1, this.zoom))
}

View File

@@ -77,6 +77,10 @@ export class XTermFrontend extends Frontend {
(this.xterm as any).bell()
}
scrollToBottom (): void {
this.xterm.scrollToBottom()
}
configure (config: any): void {
this.xterm.setOption('fontFamily', `"${config.terminal.font}", "monospace-fallback", monospace`)
this.xterm.setOption('bellStyle', config.terminal.bell)

View File

@@ -1,9 +1,12 @@
import * as fs from 'mz/fs'
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { ToastrModule } from 'ngx-toastr'
import TerminusCorePlugin from 'terminus-core'
import { HostAppService } from 'terminus-core'
import { ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, AppService, ConfigService } from 'terminus-core'
import { SettingsTabProvider } from 'terminus-settings'
@@ -35,6 +38,8 @@ import { GitBashShellProvider } from './shells/gitBash'
import { LinuxDefaultShellProvider } from './shells/linuxDefault'
import { MacOSDefaultShellProvider } from './shells/macDefault'
import { POSIXShellsProvider } from './shells/posix'
import { PowerShellCoreShellProvider } from './shells/powershellCore'
import { WindowsDefaultShellProvider } from './shells/winDefault'
import { WindowsStockShellsProvider } from './shells/windowsStock'
import { WSLShellProvider } from './shells/wsl'
@@ -64,16 +69,23 @@ import { hterm } from './hterm'
{ provide: SessionPersistenceProvider, useClass: ScreenPersistenceProvider, multi: true },
{ provide: SessionPersistenceProvider, useClass: TMuxPersistenceProvider, multi: true },
{ provide: ShellProvider, useClass: CmderShellProvider, multi: true },
{ provide: ShellProvider, useClass: WindowsStockShellsProvider, multi: true },
{ provide: ShellProvider, useClass: WindowsDefaultShellProvider, multi: true },
{ provide: ShellProvider, useClass: MacOSDefaultShellProvider, multi: true },
{ provide: ShellProvider, useClass: LinuxDefaultShellProvider, multi: true },
{ provide: ShellProvider, useClass: WindowsStockShellsProvider, multi: true },
{ provide: ShellProvider, useClass: CmderShellProvider, multi: true },
{ provide: ShellProvider, useClass: CustomShellProvider, multi: true },
{ provide: ShellProvider, useClass: Cygwin32ShellProvider, multi: true },
{ provide: ShellProvider, useClass: Cygwin64ShellProvider, multi: true },
{ provide: ShellProvider, useClass: GitBashShellProvider, multi: true },
{ provide: ShellProvider, useClass: POSIXShellsProvider, multi: true },
{ provide: ShellProvider, useClass: PowerShellCoreShellProvider, multi: true },
{ provide: ShellProvider, useClass: WSLShellProvider, multi: true },
// For WindowsDefaultShellProvider
PowerShellCoreShellProvider,
WSLShellProvider,
WindowsStockShellsProvider
],
entryComponents: [
TerminalTabComponent,
@@ -91,6 +103,7 @@ export default class TerminalModule {
config: ConfigService,
hotkeys: HotkeysService,
terminal: TerminalService,
hostApp: HostAppService,
) {
let events = [
{
@@ -121,6 +134,36 @@ export default class TerminalModule {
terminal.openTab()
})
}
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
if (hotkey === 'new-tab') {
terminal.openTab()
}
})
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
if (hotkey === 'new-window') {
hostApp.newWindow()
}
})
hostApp.cliOpenDirectory$.subscribe(async directory => {
if (await fs.exists(directory)) {
if ((await fs.stat(directory)).isDirectory()) {
terminal.openTab(null, directory)
}
}
})
hostApp.cliRunCommand$.subscribe(async command => {
terminal.openTab({
id: '',
command: command[0],
args: command.slice(1),
}, null, true)
})
hostApp.cliPaste$.subscribe(text => {
if (app.activeTab instanceof TerminalTabComponent && app.activeTab.session) {
(app.activeTab as TerminalTabComponent).sendInput(text)
}
})
}
}

View File

@@ -38,9 +38,9 @@ export class TerminalService {
if (!cwd) {
if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) {
cwd = await this.app.activeTab.session.getWorkingDirectory()
} else {
cwd = this.config.store.terminal.workingDirectory || null
}
cwd = cwd || this.config.store.terminal.workingDirectory
cwd = cwd || null
}
if (!shell) {
let shells = await this.shells$.toPromise()

View File

@@ -0,0 +1,47 @@
import { Injectable } from '@angular/core'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider, IShell } from '../api'
let Registry = null
try {
Registry = require('winreg')
} catch (_) { } // tslint:disable-line no-empty
@Injectable()
export class PowerShellCoreShellProvider extends ShellProvider {
constructor (
private hostApp: HostAppService,
) {
super()
}
async provide (): Promise<IShell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}
let pwshPath = await new Promise<string>(resolve => {
let reg = new Registry({ hive: Registry.HKLM, key: '\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\pwsh.exe', arch: 'x64' })
reg.get('', (err, item) => {
if (err || !item) {
return resolve(null)
}
resolve(item.value)
})
})
if (!pwshPath) {
return []
}
return [{
id: 'powershell-core',
name: 'PowerShell Core',
command: pwshPath,
env: {
TERM: 'cygwin',
}
}]
}
}

View File

@@ -0,0 +1,49 @@
import { Injectable } from '@angular/core'
import { HostAppService, Platform } from 'terminus-core'
import { ShellProvider, IShell } from '../api'
import { WSLShellProvider } from './wsl'
import { PowerShellCoreShellProvider } from './powershellCore'
import { WindowsStockShellsProvider } from './windowsStock'
@Injectable()
export class WindowsDefaultShellProvider extends ShellProvider {
private providers: ShellProvider[]
constructor (
psc: PowerShellCoreShellProvider,
wsl: WSLShellProvider,
stock: WindowsStockShellsProvider,
private hostApp: HostAppService,
) {
super()
this.providers = [
psc,
wsl,
stock,
]
}
async provide (): Promise<IShell[]> {
if (this.hostApp.platform !== Platform.Windows) {
return []
}
// Figure out a sensible default
let shellLists = await Promise.all(this.providers.map(x => x.provide()))
for (let list of shellLists) {
if (list.length) {
let shell = list[list.length - 1]
return [{
...shell,
id: 'default',
name: 'User default',
}]
}
}
return []
}
}

View File

@@ -27,6 +27,7 @@ export class WindowsStockShellsProvider extends ShellProvider {
path.join(
path.dirname(this.electron.app.getPath('exe')),
'resources',
'extras',
'clink',
`clink_${process.arch}.exe`,
),
@@ -36,20 +37,12 @@ export class WindowsStockShellsProvider extends ShellProvider {
{ id: 'cmd', name: 'CMD (stock)', command: 'cmd.exe' },
{
id: 'powershell',
name: 'Windows PowerShell',
name: 'PowerShell',
command: 'powershell.exe',
env: {
TERM: 'cygwin',
}
},
{
id: 'powershell-core',
name: 'PowerShell Core',
command: 'pwsh.exe',
env: {
TERM: 'cygwin',
}
},
]
}
}

View File

@@ -1,4 +1,5 @@
import * as fs from 'mz/fs'
import { exec } from 'mz/child_process'
import { Injectable } from '@angular/core'
import { HostAppService, Platform } from 'terminus-core'
@@ -17,18 +18,57 @@ export class WSLShellProvider extends ShellProvider {
return []
}
const wslPath = `${process.env.windir}\\system32\\bash.exe`
const bashPath = `${process.env.windir}\\system32\\bash.exe`
const wslPath = `${process.env.windir}\\system32\\wsl.exe`
const wslConfigPath = `${process.env.windir}\\system32\\wslconfig.exe`
if (!await fs.exists(wslPath)) {
return []
if (await fs.exists(bashPath)) {
return [{
id: 'wsl',
name: 'WSL / Bash on Windows',
command: bashPath,
env: {
TERM: 'xterm-color',
COLORTERM: 'truecolor',
}
}]
} else {
return []
}
}
return [{
let lines = (await exec(`${wslConfigPath} /l`, { encoding: 'ucs2' }))[0].toString().split('\n').splice(1)
let shells: IShell[] = [{
id: 'wsl',
name: 'WSL / Bash on Windows',
name: 'WSL / Default distro',
command: wslPath,
env: {
TERM: 'xterm-color',
COLORTERM: 'truecolor',
}
}]
for (let line of lines) {
line = line.trim()
if (!line) {
continue
}
if (line.endsWith('(Default)')) {
line = line.substring(0, line.length - ' (Default)'.length)
}
shells.push({
id: `wsl-${line}`,
name: `WSL / ${line}`,
command: wslPath,
args: ['-d', line],
env: {
TERM: 'xterm-color',
COLORTERM: 'truecolor',
}
})
}
return shells
}
}

View File

@@ -68,9 +68,9 @@ font-manager@0.3.0:
dependencies:
nan ">=2.10.0"
hterm-umdjs@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/hterm-umdjs/-/hterm-umdjs-1.1.3.tgz#8b57bcaded5ba9541d6c8e32a82b34abb93e885e"
hterm-umdjs@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/hterm-umdjs/-/hterm-umdjs-1.4.1.tgz#0cd5352eaf927c70b83c36146cf2c2a281dba957"
json5@^0.5.0:
version "0.5.1"

718
yarn.lock

File diff suppressed because it is too large Load Diff