mirror of
https://github.com/Eugeny/tabby.git
synced 2025-08-31 21:51:50 +00:00
Compare commits
63 Commits
v1.0.0-alp
...
v1.0.0-alp
Author | SHA1 | Date | |
---|---|---|---|
![]() |
eed01e76ad | ||
![]() |
7f8d012a8a | ||
![]() |
9ad1371c2b | ||
![]() |
0d37f4736a | ||
![]() |
e71d404c2b | ||
![]() |
0545471f3c | ||
![]() |
a04c60046e | ||
![]() |
5c2003cc2f | ||
![]() |
dc864781e4 | ||
![]() |
e315654d0a | ||
![]() |
5863ea0de1 | ||
![]() |
cce49c69d6 | ||
![]() |
3e41d0df4e | ||
![]() |
507b69acb4 | ||
![]() |
6b08341760 | ||
![]() |
62bf681598 | ||
![]() |
e403a423b7 | ||
![]() |
6b49cdc974 | ||
![]() |
5fe71b8169 | ||
![]() |
6bc2d18f3c | ||
![]() |
6fa5ab5eb2 | ||
![]() |
2411713501 | ||
![]() |
947e0f8b66 | ||
![]() |
f8bc94fe78 | ||
![]() |
5a32a45b19 | ||
![]() |
62222e67fb | ||
![]() |
6aab782326 | ||
![]() |
cec349d021 | ||
![]() |
c8b40647a9 | ||
![]() |
67ed830e97 | ||
![]() |
975d4d62ef | ||
![]() |
275791517b | ||
![]() |
9db452f489 | ||
![]() |
5094262e68 | ||
![]() |
17cf0f59c9 | ||
![]() |
aa805b912b | ||
![]() |
4b3cbc5639 | ||
![]() |
6a59db1a36 | ||
![]() |
2d43e29bcd | ||
![]() |
bf5e460bca | ||
![]() |
d574f634c9 | ||
![]() |
4ceb8ff897 | ||
![]() |
0f8c27e536 | ||
![]() |
d6fb71dca2 | ||
![]() |
38e450f70a | ||
![]() |
18c0a585a2 | ||
![]() |
8a1c33b82a | ||
![]() |
f81bda1686 | ||
![]() |
c285b89b6c | ||
![]() |
6dc46bb970 | ||
![]() |
7d25816751 | ||
![]() |
d6f163b048 | ||
![]() |
f357dab8f0 | ||
![]() |
0eaf857b02 | ||
![]() |
f367ea6c74 | ||
![]() |
dc4b984ed0 | ||
![]() |
4b7b692ace | ||
![]() |
0749096d9f | ||
![]() |
5b76947d70 | ||
![]() |
98a7801803 | ||
![]() |
ce2c72393b | ||
![]() |
2ea2c02845 | ||
![]() |
4b1ba7863f |
@@ -26,8 +26,9 @@ 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
|
* [clickable-links](https://github.com/Eugeny/terminus-clickable-links) - makes paths and URLs in the terminal clickable
|
||||||
* [theme-hype](https://github.com/Eugeny/terminus-theme-hype) - a Hyper inspired theme
|
* [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
|
* [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
|
* [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
|
* [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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
172
app/lib/app.ts
Normal file
172
app/lib/app.ts
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import { app, ipcMain, Menu, Tray, shell } from 'electron'
|
||||||
|
import { Window } from './window'
|
||||||
|
|
||||||
|
export class Application {
|
||||||
|
private tray: Tray
|
||||||
|
private windows: Window[] = []
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
ipcMain.on('app:config-change', () => {
|
||||||
|
this.broadcast('host:config-change')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async newWindow (): Promise<Window> {
|
||||||
|
let window = new Window()
|
||||||
|
this.windows.push(window)
|
||||||
|
window.visible$.subscribe(visible => {
|
||||||
|
if (visible) {
|
||||||
|
this.disableTray()
|
||||||
|
} else {
|
||||||
|
this.enableTray()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
this.setupMenu()
|
||||||
|
}
|
||||||
|
await window.ready
|
||||||
|
return window
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcast (event, ...args) {
|
||||||
|
for (let window of this.windows) {
|
||||||
|
window.send(event, ...args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async send (event, ...args) {
|
||||||
|
if (!this.hasWindows()) {
|
||||||
|
await this.newWindow()
|
||||||
|
}
|
||||||
|
this.windows[0].send(event, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
enableTray () {
|
||||||
|
if (this.tray) {
|
||||||
|
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', () => 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 () {
|
||||||
|
if (this.tray) {
|
||||||
|
this.tray.destroy()
|
||||||
|
this.tray = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasWindows () {
|
||||||
|
return !!this.windows.length
|
||||||
|
}
|
||||||
|
|
||||||
|
focus () {
|
||||||
|
for (let window of this.windows) {
|
||||||
|
window.show()
|
||||||
|
window.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupMenu () {
|
||||||
|
let template: Electron.MenuItemConstructorOptions[] = [
|
||||||
|
{
|
||||||
|
label: 'Application',
|
||||||
|
submenu: [
|
||||||
|
{ role: 'about', label: 'About Terminus' },
|
||||||
|
{ type: 'separator' },
|
||||||
|
{
|
||||||
|
label: 'Preferences',
|
||||||
|
accelerator: 'Cmd+,',
|
||||||
|
async click () {
|
||||||
|
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: '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 () {
|
||||||
|
shell.openExternal('https://eugeny.github.io/terminus')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
|
||||||
|
}
|
||||||
|
}
|
@@ -1,23 +0,0 @@
|
|||||||
import { app } from 'electron'
|
|
||||||
|
|
||||||
export function parseArgs (argv, cwd) {
|
|
||||||
if (argv[0].includes('node')) {
|
|
||||||
argv = argv.slice(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return require('yargs')
|
|
||||||
.usage('terminus [command] [arguments]')
|
|
||||||
.command('open [directory]', 'open a shell in a directory', {
|
|
||||||
directory: { type: 'string', 'default': 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')
|
|
||||||
.strict()
|
|
||||||
.parse(argv.slice(1))
|
|
||||||
}
|
|
39
app/lib/cli.ts
Normal file
39
app/lib/cli.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { app } from 'electron'
|
||||||
|
|
||||||
|
export function parseArgs (argv, cwd) {
|
||||||
|
if (argv[0].includes('node')) {
|
||||||
|
argv = argv.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return require('yargs')
|
||||||
|
.usage('terminus [command] [arguments]')
|
||||||
|
.command('open [directory]', 'open a shell in a directory', {
|
||||||
|
directory: { type: 'string', 'default': cwd },
|
||||||
|
})
|
||||||
|
.command('run [command...]', 'run a command in the terminal', {
|
||||||
|
command: { 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('version', {
|
||||||
|
alias: 'v',
|
||||||
|
describe: 'Show version and exit',
|
||||||
|
type: 'boolean'
|
||||||
|
})
|
||||||
|
.help('help')
|
||||||
|
.strict()
|
||||||
|
.parse(argv.slice(1))
|
||||||
|
}
|
304
app/lib/index.js
304
app/lib/index.js
@@ -1,304 +0,0 @@
|
|||||||
import { app, ipcMain, BrowserWindow, Menu, Tray, shell } from 'electron'
|
|
||||||
import * as path from 'path'
|
|
||||||
import electronDebug from 'electron-debug'
|
|
||||||
import * as fs from 'fs'
|
|
||||||
import * as yaml from 'js-yaml'
|
|
||||||
import './lru'
|
|
||||||
import { parseArgs } from './cli'
|
|
||||||
import ElectronConfig from 'electron-config'
|
|
||||||
if (process.platform === 'win32' && require('electron-squirrel-startup')) process.exit(0)
|
|
||||||
|
|
||||||
let electronVibrancy
|
|
||||||
if (process.platform !== 'linux') {
|
|
||||||
electronVibrancy = require('electron-vibrancy')
|
|
||||||
}
|
|
||||||
|
|
||||||
let windowConfig = new ElectronConfig({ name: 'window' })
|
|
||||||
|
|
||||||
if (!process.env.TERMINUS_PLUGINS) {
|
|
||||||
process.env.TERMINUS_PLUGINS = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setupTray = () => {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
app.tray = new Tray(`${app.getAppPath()}/assets/tray-darwinTemplate.png`)
|
|
||||||
app.tray.setPressedImage(`${app.getAppPath()}/assets/tray-darwinHighlightTemplate.png`)
|
|
||||||
} else {
|
|
||||||
app.tray = new Tray(`${app.getAppPath()}/assets/tray.png`)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.tray.on('click', () => {
|
|
||||||
app.window.show()
|
|
||||||
app.window.focus()
|
|
||||||
})
|
|
||||||
|
|
||||||
const contextMenu = 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()}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const 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', () => {
|
|
||||||
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', () => {
|
|
||||||
windowConfig.set('windowBoundaries', app.window.getBounds())
|
|
||||||
})
|
|
||||||
|
|
||||||
app.window.on('closed', () => {
|
|
||||||
app.window = null
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('window-focus', () => {
|
|
||||||
app.window.focus()
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('window-maximize', () => {
|
|
||||||
app.window.maximize()
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('window-unmaximize', () => {
|
|
||||||
app.window.unmaximize()
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('window-toggle-maximize', () => {
|
|
||||||
if (app.window.isMaximized()) {
|
|
||||||
app.window.unmaximize()
|
|
||||||
} else {
|
|
||||||
app.window.maximize()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('window-minimize', () => {
|
|
||||||
app.window.minimize()
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('window-set-bounds', (event, bounds) => {
|
|
||||||
app.window.setBounds(bounds)
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('window-set-always-on-top', (event, flag) => {
|
|
||||||
app.window.setAlwaysOnTop(flag)
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.on('window-set-vibrancy', (event, enabled) => {
|
|
||||||
setWindowVibrancy(enabled)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const 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 () {
|
|
||||||
shell.openExternal('https://eugeny.github.io/terminus')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}]
|
|
||||||
|
|
||||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = () => {
|
|
||||||
let t0 = Date.now()
|
|
||||||
|
|
||||||
let configPath = path.join(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 === 'win32' && (configData.appearance || {}).vibrancy) {
|
|
||||||
options.transparent = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.platform === 'linux') {
|
|
||||||
options.backgroundColor = '#131d27'
|
|
||||||
}
|
|
||||||
|
|
||||||
app.commandLine.appendSwitch('disable-http-cache')
|
|
||||||
|
|
||||||
app.window = new BrowserWindow(options)
|
|
||||||
app.window.once('ready-to-show', () => {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
app.window.setVibrancy('dark')
|
|
||||||
} else if (process.platform === 'win32' && (configData.appearance || {}).vibrancy) {
|
|
||||||
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()
|
|
||||||
ipcMain.on('app:ready', () => {
|
|
||||||
console.info(`App startup: ${Date.now() - t0}ms`)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('second-instance', (event, argv, cwd) => {
|
|
||||||
app.window.webContents.send('host:second-instance', parseArgs(argv, cwd))
|
|
||||||
})
|
|
||||||
|
|
||||||
const argv = parseArgs(process.argv, process.cwd())
|
|
||||||
|
|
||||||
if (!app.requestSingleInstanceLock()) {
|
|
||||||
app.quit()
|
|
||||||
process.exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argv.d) {
|
|
||||||
electronDebug({ enabled: true, showDevTools: 'undocked' })
|
|
||||||
}
|
|
||||||
|
|
||||||
app.on('ready', start)
|
|
65
app/lib/index.ts
Normal file
65
app/lib/index.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import './lru'
|
||||||
|
import { app, ipcMain, Menu } from 'electron'
|
||||||
|
import electronDebug = require('electron-debug')
|
||||||
|
import { parseArgs } from './cli'
|
||||||
|
import { Application } from './app'
|
||||||
|
if (process.platform === 'win32' && require('electron-squirrel-startup')) process.exit(0)
|
||||||
|
|
||||||
|
if (!process.env.TERMINUS_PLUGINS) {
|
||||||
|
process.env.TERMINUS_PLUGINS = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const application = new Application()
|
||||||
|
|
||||||
|
app.commandLine.appendSwitch('disable-http-cache')
|
||||||
|
|
||||||
|
ipcMain.on('app:new-window', () => {
|
||||||
|
console.log('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.send('host:second-instance', parseArgs(argv, cwd), cwd)
|
||||||
|
})
|
||||||
|
|
||||||
|
const argv = parseArgs(process.argv, process.cwd())
|
||||||
|
|
||||||
|
if (!app.requestSingleInstanceLock()) {
|
||||||
|
app.quit()
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv.d) {
|
||||||
|
electronDebug({ enabled: true, showDevTools: 'undocked' })
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on('ready', () => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
app.dock.setMenu(Menu.buildFromTemplate([
|
||||||
|
{
|
||||||
|
label: 'New window',
|
||||||
|
click () {
|
||||||
|
this.app.newWindow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
application.newWindow()
|
||||||
|
})
|
@@ -6,10 +6,10 @@ let origLstat = fs.realpathSync.bind(fs)
|
|||||||
// NB: The biggest offender of thrashing realpathSync is the node module system
|
// NB: The biggest offender of thrashing realpathSync is the node module system
|
||||||
// itself, which we can't get into via any sane means.
|
// itself, which we can't get into via any sane means.
|
||||||
require('fs').realpathSync = function (p) {
|
require('fs').realpathSync = function (p) {
|
||||||
let r = lru.get(p)
|
let r = lru.get(p)
|
||||||
if (r) return r
|
if (r) return r
|
||||||
|
|
||||||
r = origLstat(p)
|
r = origLstat(p)
|
||||||
lru.set(p, r)
|
lru.set(p, r)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
201
app/lib/window.ts
Normal file
201
app/lib/window.ts
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
import { Subject, Observable } from 'rxjs'
|
||||||
|
import { BrowserWindow, app, ipcMain, Rectangle, Menu } from 'electron'
|
||||||
|
import ElectronConfig = require('electron-config')
|
||||||
|
import * as yaml from 'js-yaml'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
import * as path from 'path'
|
||||||
|
|
||||||
|
let electronVibrancy: any
|
||||||
|
if (process.platform !== 'linux') {
|
||||||
|
electronVibrancy = require('electron-vibrancy')
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Window {
|
||||||
|
ready: Promise<void>
|
||||||
|
private visible = new Subject<boolean>()
|
||||||
|
private window: BrowserWindow
|
||||||
|
private vibrancyViewID: number
|
||||||
|
private windowConfig: ElectronConfig
|
||||||
|
private windowBounds: Rectangle
|
||||||
|
|
||||||
|
get visible$ (): Observable<boolean> { return this.visible }
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
let configPath = path.join(app.getPath('userData'), 'config.yaml')
|
||||||
|
let configData
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
configData = yaml.safeLoad(fs.readFileSync(configPath, 'utf8'))
|
||||||
|
} else {
|
||||||
|
configData = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
title: 'Terminus',
|
||||||
|
minWidth: 400,
|
||||||
|
minHeight: 300,
|
||||||
|
webPreferences: { webSecurity: false },
|
||||||
|
frame: false,
|
||||||
|
show: false,
|
||||||
|
}
|
||||||
|
Object.assign(options, this.windowBounds)
|
||||||
|
|
||||||
|
if ((configData.appearance || {}).frame === 'native') {
|
||||||
|
options.frame = true
|
||||||
|
} else {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
options.titleBarStyle = 'hiddenInset'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === 'win32' && (configData.appearance || {}).vibrancy) {
|
||||||
|
options.transparent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === 'linux') {
|
||||||
|
options.backgroundColor = '#131d27'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.window = new BrowserWindow(options)
|
||||||
|
this.window.once('ready-to-show', () => {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
this.window.setVibrancy('dark')
|
||||||
|
} else if (process.platform === 'win32' && (configData.appearance || {}).vibrancy) {
|
||||||
|
this.setVibrancy(true)
|
||||||
|
}
|
||||||
|
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' })
|
||||||
|
|
||||||
|
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)
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipcMain.on('app:ready', listener)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setVibrancy (enabled: boolean) {
|
||||||
|
if (enabled && !this.vibrancyViewID) {
|
||||||
|
this.vibrancyViewID = electronVibrancy.SetVibrancy(this.window, 0)
|
||||||
|
} else if (!enabled && this.vibrancyViewID) {
|
||||||
|
electronVibrancy.RemoveView(this.window, this.vibrancyViewID)
|
||||||
|
this.vibrancyViewID = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show () {
|
||||||
|
this.window.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
focus () {
|
||||||
|
this.window.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
send (event, ...args) {
|
||||||
|
this.window.webContents.send(event, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupWindowManagement () {
|
||||||
|
this.window.on('show', () => {
|
||||||
|
this.visible.next(true)
|
||||||
|
this.window.webContents.send('host:window-shown')
|
||||||
|
})
|
||||||
|
|
||||||
|
this.window.on('hide', () => {
|
||||||
|
this.visible.next(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.window.on('enter-full-screen', () => this.window.webContents.send('host:window-enter-full-screen'))
|
||||||
|
this.window.on('leave-full-screen', () => this.window.webContents.send('host:window-leave-full-screen'))
|
||||||
|
|
||||||
|
this.window.on('close', () => {
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('window-maximize', () => {
|
||||||
|
this.window.maximize()
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('window-unmaximize', () => {
|
||||||
|
this.window.unmaximize()
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('window-toggle-maximize', () => {
|
||||||
|
if (this.window.isMaximized()) {
|
||||||
|
this.window.unmaximize()
|
||||||
|
} else {
|
||||||
|
this.window.maximize()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('window-minimize', () => {
|
||||||
|
this.window.minimize()
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('window-set-bounds', (_event, bounds) => {
|
||||||
|
this.window.setBounds(bounds)
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.on('window-set-always-on-top', (_event, flag) => {
|
||||||
|
this.window.setAlwaysOnTop(flag)
|
||||||
|
})
|
||||||
|
|
||||||
|
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 () {
|
||||||
|
this.window = null
|
||||||
|
this.visible.complete()
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
import '../lib/lru.js'
|
import '../lib/lru'
|
||||||
import 'source-sans-pro'
|
import 'source-sans-pro'
|
||||||
import 'font-awesome/css/font-awesome.css'
|
import 'font-awesome/css/font-awesome.css'
|
||||||
import 'ngx-toastr/toastr.css'
|
import 'ngx-toastr/toastr.css'
|
||||||
@@ -29,8 +29,8 @@ Raven.config(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException' as any, (err) => {
|
||||||
Raven.captureException(err)
|
Raven.captureException(err as any)
|
||||||
console.error(err)
|
console.error(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -4,8 +4,12 @@ body {
|
|||||||
background: #1D272D;
|
background: #1D272D;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-dialog, .modal-backdrop {
|
.modal-dialog, .modal-backdrop, .no-drag {
|
||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectable {
|
||||||
|
user-select: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
[ngbradiogroup] input[type="radio"] {
|
[ngbradiogroup] input[type="radio"] {
|
||||||
|
31
app/tsconfig.main.json
Normal file
31
app/tsconfig.main.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"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,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"es2015",
|
||||||
|
"es2015.iterable",
|
||||||
|
"es2017",
|
||||||
|
"es7"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"compileOnSave": false,
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"node_modules",
|
||||||
|
"*/node_modules"
|
||||||
|
]
|
||||||
|
}
|
@@ -6,8 +6,8 @@ module.exports = {
|
|||||||
target: 'node',
|
target: 'node',
|
||||||
entry: {
|
entry: {
|
||||||
'index.ignore': 'file-loader?name=index.html!val-loader!pug-html-loader!' + path.resolve(__dirname, './index.pug'),
|
'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'),
|
preload: path.resolve(__dirname, 'src/entry.preload.ts'),
|
||||||
'bundle': path.resolve(__dirname, 'src/entry.ts'),
|
bundle: path.resolve(__dirname, 'src/entry.ts'),
|
||||||
},
|
},
|
||||||
mode: process.env.DEV ? 'development' : 'production',
|
mode: process.env.DEV ? 'development' : 'production',
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
@@ -15,7 +15,7 @@ module.exports = {
|
|||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
pathinfo: true,
|
pathinfo: true,
|
||||||
filename: '[name].js'
|
filename: '[name].js',
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
modules: ['src/', 'node_modules', '../node_modules', 'assets/'].map(x => path.join(__dirname, x)),
|
modules: ['src/', 'node_modules', '../node_modules', 'assets/'].map(x => path.join(__dirname, x)),
|
||||||
@@ -29,8 +29,8 @@ module.exports = {
|
|||||||
loader: 'awesome-typescript-loader',
|
loader: 'awesome-typescript-loader',
|
||||||
options: {
|
options: {
|
||||||
configFileName: path.resolve(__dirname, 'tsconfig.json'),
|
configFileName: path.resolve(__dirname, 'tsconfig.json'),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
|
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
|
||||||
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
|
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
|
||||||
@@ -39,20 +39,20 @@ module.exports = {
|
|||||||
use: {
|
use: {
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
name: 'images/[name].[ext]'
|
name: 'images/[name].[ext]',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
test: /\.(ttf|eot|otf|woff|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||||
use: {
|
use: {
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
name: 'fonts/[name].[ext]'
|
name: 'fonts/[name].[ext]',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
externals: {
|
externals: {
|
||||||
'@angular/core': 'commonjs @angular/core',
|
'@angular/core': 'commonjs @angular/core',
|
||||||
@@ -62,15 +62,15 @@ module.exports = {
|
|||||||
'@angular/forms': 'commonjs @angular/forms',
|
'@angular/forms': 'commonjs @angular/forms',
|
||||||
'@angular/common': 'commonjs @angular/common',
|
'@angular/common': 'commonjs @angular/common',
|
||||||
'@ng-bootstrap/ng-bootstrap': 'commonjs @ng-bootstrap/ng-bootstrap',
|
'@ng-bootstrap/ng-bootstrap': 'commonjs @ng-bootstrap/ng-bootstrap',
|
||||||
'child_process': 'commonjs child_process',
|
child_process: 'commonjs child_process',
|
||||||
'electron': 'commonjs electron',
|
electron: 'commonjs electron',
|
||||||
'electron-is-dev': 'commonjs electron-is-dev',
|
'electron-is-dev': 'commonjs electron-is-dev',
|
||||||
'fs': 'commonjs fs',
|
fs: 'commonjs fs',
|
||||||
'ngx-toastr': 'commonjs ngx-toastr',
|
'ngx-toastr': 'commonjs ngx-toastr',
|
||||||
'module': 'commonjs module',
|
module: 'commonjs module',
|
||||||
'mz': 'commonjs mz',
|
mz: 'commonjs mz',
|
||||||
'path': 'commonjs path',
|
path: 'commonjs path',
|
||||||
'rxjs': 'commonjs rxjs',
|
rxjs: 'commonjs rxjs',
|
||||||
'zone.js': 'commonjs zone.js/dist/zone.js',
|
'zone.js': 'commonjs zone.js/dist/zone.js',
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@@ -5,7 +5,7 @@ module.exports = {
|
|||||||
name: 'terminus-main',
|
name: 'terminus-main',
|
||||||
target: 'node',
|
target: 'node',
|
||||||
entry: {
|
entry: {
|
||||||
main: path.resolve(__dirname, 'lib/index.js'),
|
main: path.resolve(__dirname, 'lib/index.ts'),
|
||||||
},
|
},
|
||||||
mode: process.env.DEV ? 'development' : 'production',
|
mode: process.env.DEV ? 'development' : 'production',
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
@@ -22,12 +22,11 @@ module.exports = {
|
|||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /lib[\\/].*\.js$/,
|
test: /\.ts$/,
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
use: {
|
||||||
loader: 'babel-loader',
|
loader: 'awesome-typescript-loader',
|
||||||
options: {
|
options: {
|
||||||
presets: ['babel-preset-es2015'],
|
configFileName: path.resolve(__dirname, 'tsconfig.main.json'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -24,3 +24,4 @@ build_script:
|
|||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: 'dist\win\*.exe'
|
- path: 'dist\win\*.exe'
|
||||||
|
- path: 'dist\*.exe'
|
||||||
|
Binary file not shown.
Binary file not shown.
@@ -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 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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>
|
Binary file not shown.
@@ -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>
|
@@ -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 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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>
|
Binary file not shown.
@@ -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>
|
@@ -1,6 +1,10 @@
|
|||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
|
##### v0.4.9
|
||||||
|
|
||||||
|
- Fixed broken Doskey on Win10 (#438, #451)
|
||||||
|
|
||||||
##### v0.4.8
|
##### v0.4.8
|
||||||
|
|
||||||
- Environment variable 'clink_profile' overrides Clink's profile path (#390).
|
- Environment variable 'clink_profile' overrides Clink's profile path (#390).
|
@@ -136,7 +136,7 @@ h6 { font-size: 1.00em; }
|
|||||||
<!-- ----------------------------------------------------- -->
|
<!-- ----------------------------------------------------- -->
|
||||||
<body onload="go();">
|
<body onload="go();">
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<div id="title">Clink v0.4.8</div>
|
<div id="title">Clink v0.4.9</div>
|
||||||
<div id="url">
|
<div id="url">
|
||||||
<a href="http://github.com/mridgers/clink">
|
<a href="http://github.com/mridgers/clink">
|
||||||
http://github.com/mridgers/clink
|
http://github.com/mridgers/clink
|
||||||
@@ -697,6 +697,10 @@ The current cursor position within the line buffer.
|
|||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
|
##### v0.4.9
|
||||||
|
|
||||||
|
- Fixed broken Doskey on Win10 (#438, #451)
|
||||||
|
|
||||||
##### v0.4.8
|
##### v0.4.8
|
||||||
|
|
||||||
- Environment variable 'clink_profile' overrides Clink's profile path (#390).
|
- Environment variable 'clink_profile' overrides Clink's profile path (#390).
|
Binary file not shown.
Binary file not shown.
BIN
extras/clink/clink_x64.exe
Normal file
BIN
extras/clink/clink_x64.exe
Normal file
Binary file not shown.
BIN
extras/clink/clink_x86.exe
Normal file
BIN
extras/clink/clink_x86.exe
Normal file
Binary file not shown.
28
package.json
28
package.json
@@ -4,6 +4,7 @@
|
|||||||
"@types/electron-config": "^0.2.1",
|
"@types/electron-config": "^0.2.1",
|
||||||
"@types/electron-debug": "^1.1.0",
|
"@types/electron-debug": "^1.1.0",
|
||||||
"@types/fs-promise": "1.0.1",
|
"@types/fs-promise": "1.0.1",
|
||||||
|
"@types/js-yaml": "^3.11.2",
|
||||||
"@types/node": "7.0.5",
|
"@types/node": "7.0.5",
|
||||||
"@types/webpack-env": "1.13.0",
|
"@types/webpack-env": "1.13.0",
|
||||||
"apply-loader": "0.1.0",
|
"apply-loader": "0.1.0",
|
||||||
@@ -15,7 +16,7 @@
|
|||||||
"core-js": "2.4.1",
|
"core-js": "2.4.1",
|
||||||
"cross-env": "4.0.0",
|
"cross-env": "4.0.0",
|
||||||
"css-loader": "0.28.0",
|
"css-loader": "0.28.0",
|
||||||
"electron": "3.0.0-beta.5",
|
"electron": "3.0.0",
|
||||||
"electron-builder": "^20.27.1",
|
"electron-builder": "^20.27.1",
|
||||||
"electron-builder-squirrel-windows": "17.0.1",
|
"electron-builder-squirrel-windows": "17.0.1",
|
||||||
"electron-installer-snap": "^3.0.0",
|
"electron-installer-snap": "^3.0.0",
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
"json-loader": "0.5.4",
|
"json-loader": "0.5.4",
|
||||||
"less": "2.7.1",
|
"less": "2.7.1",
|
||||||
"less-loader": "2.2.3",
|
"less-loader": "2.2.3",
|
||||||
"node-abi": "^2.4.1",
|
"node-abi": "^2.4.4",
|
||||||
"node-gyp": "^3.6.2",
|
"node-gyp": "^3.6.2",
|
||||||
"node-sass": "^4.5.3",
|
"node-sass": "^4.5.3",
|
||||||
"npmlog": "4.1.0",
|
"npmlog": "4.1.0",
|
||||||
@@ -72,17 +73,21 @@
|
|||||||
],
|
],
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
"builtin-plugins",
|
"builtin-plugins",
|
||||||
"clink"
|
"extras"
|
||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
"icon": "./build/windows/icon.ico",
|
"icon": "./build/windows/icon.ico",
|
||||||
"publish": [
|
"publish": [
|
||||||
"github"
|
"github"
|
||||||
]
|
],
|
||||||
|
"artifactName": "terminus-${version}-setup.exe"
|
||||||
},
|
},
|
||||||
"squirrelWindows": {
|
"squirrelWindows": {
|
||||||
"iconUrl": "https://github.com/Eugeny/terminus/raw/master/build/windows/icon.ico",
|
"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": {
|
"mac": {
|
||||||
"category": "public.app-category.video",
|
"category": "public.app-category.video",
|
||||||
@@ -95,11 +100,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dmg": {
|
"dmg": {
|
||||||
"artifactName": "terminus-${version}-${os}-${arch}.dmg"
|
"artifactName": "terminus-${version}-macos.dmg"
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"category": "Utilities",
|
"category": "Utilities",
|
||||||
"icon": "./build/icons",
|
"icon": "./build/icons",
|
||||||
|
"artifactName": "terminus-${version}-linux.${ext}",
|
||||||
"publish": [
|
"publish": [
|
||||||
"github"
|
"github"
|
||||||
]
|
]
|
||||||
@@ -114,20 +120,18 @@
|
|||||||
"libxtst6",
|
"libxtst6",
|
||||||
"libnss3",
|
"libnss3",
|
||||||
"tmux"
|
"tmux"
|
||||||
],
|
]
|
||||||
"artifactName": "terminus-${version}-${os}-${arch}.deb"
|
|
||||||
},
|
},
|
||||||
"rpm": {
|
"rpm": {
|
||||||
"depends": [
|
"depends": [
|
||||||
"screen",
|
"screen",
|
||||||
"gnome-python2-gnomekeyring"
|
"gnome-python2-gnomekeyring"
|
||||||
],
|
]
|
||||||
"artifactName": "terminus-${version}-${os}-${arch}.rpm"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "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",
|
"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",
|
"start": "cross-env DEV=1 electron app --debug",
|
||||||
"prod": "cross-env DEV=1 electron app",
|
"prod": "cross-env DEV=1 electron app",
|
||||||
"lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts",
|
"lint": "tslint -c tslint.json -t stylish terminus-*/src/**/*.ts terminus-*/src/*.ts app/src/*.ts",
|
||||||
|
@@ -4,7 +4,7 @@ const vars = require('./vars')
|
|||||||
|
|
||||||
builder({
|
builder({
|
||||||
dir: true,
|
dir: true,
|
||||||
win: ['squirrel'],
|
win: ['squirrel', 'portable'],
|
||||||
config: {
|
config: {
|
||||||
extraMetadata: {
|
extraMetadata: {
|
||||||
version: vars.version,
|
version: vars.version,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-community-color-schemes",
|
"name": "terminus-community-color-schemes",
|
||||||
"version": "1.0.0-alpha.48",
|
"version": "1.0.0-alpha.55",
|
||||||
"description": "Community color schemes for Terminus",
|
"description": "Community color schemes for Terminus",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-builtin-plugin"
|
"terminus-builtin-plugin"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-core",
|
"name": "terminus-core",
|
||||||
"version": "1.0.0-alpha.48",
|
"version": "1.0.0-alpha.55",
|
||||||
"description": "Terminus core",
|
"description": "Terminus core",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-builtin-plugin"
|
"terminus-builtin-plugin"
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
"electron-updater": "^2.8.9",
|
"electron-updater": "^2.8.9",
|
||||||
"ng2-dnd": "^5.0.2",
|
"ng2-dnd": "^5.0.2",
|
||||||
"ngx-perfect-scrollbar": "^6.0.0",
|
"ngx-perfect-scrollbar": "^6.0.0",
|
||||||
|
"shell-escape": "^0.2.0",
|
||||||
"universal-analytics": "^0.4.17"
|
"universal-analytics": "^0.4.17"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"deepmerge": "^1.5.0",
|
"deepmerge": "^1.5.0",
|
||||||
"js-yaml": "^3.9.0",
|
"js-yaml": "^3.9.0",
|
||||||
|
"winreg": "^1.2.4",
|
||||||
"winston": "^2.4.0"
|
"winston": "^2.4.0"
|
||||||
},
|
},
|
||||||
"false": {}
|
"false": {}
|
||||||
|
@@ -13,4 +13,5 @@ export { Logger, LogService } from '../services/log.service'
|
|||||||
export { HomeBaseService } from '../services/homeBase.service'
|
export { HomeBaseService } from '../services/homeBase.service'
|
||||||
export { HotkeysService } from '../services/hotkeys.service'
|
export { HotkeysService } from '../services/hotkeys.service'
|
||||||
export { HostAppService, Platform } from '../services/hostApp.service'
|
export { HostAppService, Platform } from '../services/hostApp.service'
|
||||||
|
export { ShellIntegrationService } from '../services/shellIntegration.service'
|
||||||
export { ThemesService } from '../services/themes.service'
|
export { ThemesService } from '../services/themes.service'
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
-webkit-user-select: none;
|
user-select: none;
|
||||||
-webkit-user-drag: none;
|
-webkit-user-drag: none;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
|
@@ -142,9 +142,9 @@ export class AppRootComponent {
|
|||||||
this.unsortedTabs.push(tab)
|
this.unsortedTabs.push(tab)
|
||||||
tab.progress$.subscribe(progress => {
|
tab.progress$.subscribe(progress => {
|
||||||
if (progress !== null) {
|
if (progress !== null) {
|
||||||
this.hostApp.getWindow().setProgressBar(progress / 100.0, 'normal')
|
this.hostApp.getWindow().setProgressBar(progress / 100.0, { mode: 'normal' })
|
||||||
} else {
|
} else {
|
||||||
this.hostApp.getWindow().setProgressBar(-1, 'none')
|
this.hostApp.getWindow().setProgressBar(-1, { mode: 'none' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -154,26 +154,27 @@ export class AppRootComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onGlobalHotkey () {
|
onGlobalHotkey () {
|
||||||
if (this.electron.app.window.isFocused()) {
|
if (this.hostApp.getWindow().isFocused()) {
|
||||||
// focused
|
// focused
|
||||||
this.electron.loseFocus()
|
this.electron.loseFocus()
|
||||||
|
this.hostApp.getWindow().blur()
|
||||||
if (this.hostApp.platform !== Platform.macOS) {
|
if (this.hostApp.platform !== Platform.macOS) {
|
||||||
this.electron.app.window.hide()
|
this.hostApp.getWindow().hide()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!this.electron.app.window.isVisible()) {
|
if (!this.hostApp.getWindow().isVisible()) {
|
||||||
// unfocused, invisible
|
// unfocused, invisible
|
||||||
this.electron.app.window.show()
|
this.hostApp.getWindow().show()
|
||||||
this.electron.app.window.focus()
|
this.hostApp.getWindow().focus()
|
||||||
} else {
|
} else {
|
||||||
if (this.config.store.appearance.dock === 'off') {
|
if (this.config.store.appearance.dock === 'off') {
|
||||||
// not docked, visible
|
// not docked, visible
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.electron.app.window.focus()
|
this.hostApp.getWindow().focus()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// docked, visible
|
// docked, visible
|
||||||
this.electron.app.window.hide()
|
this.hostApp.getWindow().hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +224,7 @@ export class AppRootComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateVibrancy () {
|
private updateVibrancy () {
|
||||||
this.hostApp.setVibrancy(this.config.store.appearance.vibrancy)
|
this.hostApp.setVibrancy(this.config.store.appearance.vibrancy)
|
||||||
this.hostApp.getWindow().setOpacity(this.config.store.appearance.opacity)
|
this.hostApp.getWindow().setOpacity(this.config.store.appearance.opacity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,8 @@ export abstract class BaseTabComponent {
|
|||||||
protected progress = new Subject<number>()
|
protected progress = new Subject<number>()
|
||||||
protected activity = new Subject<boolean>()
|
protected activity = new Subject<boolean>()
|
||||||
|
|
||||||
|
private progressClearTimeout: number
|
||||||
|
|
||||||
get focused$ (): Observable<void> { return this.focused }
|
get focused$ (): Observable<void> { return this.focused }
|
||||||
get blurred$ (): Observable<void> { return this.blurred }
|
get blurred$ (): Observable<void> { return this.blurred }
|
||||||
get titleChange$ (): Observable<string> { return this.titleChange }
|
get titleChange$ (): Observable<string> { return this.titleChange }
|
||||||
@@ -40,6 +42,14 @@ export abstract class BaseTabComponent {
|
|||||||
|
|
||||||
setProgress (progress: number) {
|
setProgress (progress: number) {
|
||||||
this.progress.next(progress)
|
this.progress.next(progress)
|
||||||
|
if (progress) {
|
||||||
|
if (this.progressClearTimeout) {
|
||||||
|
clearTimeout(this.progressClearTimeout)
|
||||||
|
}
|
||||||
|
this.progressClearTimeout = setTimeout(() => {
|
||||||
|
this.setProgress(null)
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
displayActivity (): void {
|
displayActivity (): void {
|
||||||
|
@@ -20,4 +20,4 @@ footer.d-flex.align-items-center
|
|||||||
i.fa.fa-bug
|
i.fa.fa-bug
|
||||||
span Report a problem
|
span Report a problem
|
||||||
|
|
||||||
.form-control-static Version: {{homeBase.appVersion}}
|
.form-control-static.selectable.no-drag Version: {{homeBase.appVersion}}
|
||||||
|
@@ -33,6 +33,4 @@ a, button {
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
fill: white;
|
|
||||||
fill-opacity: 0.75;
|
|
||||||
}
|
}
|
||||||
|
@@ -13,8 +13,6 @@ $tabs-height: 36px;
|
|||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
transition: 0.125s ease-out all;
|
|
||||||
|
|
||||||
.index {
|
.index {
|
||||||
flex: none;
|
flex: none;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -44,12 +42,13 @@ $tabs-height: 36px;
|
|||||||
flex: none;
|
flex: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
|
||||||
$button-size: 26px;
|
$button-size: 26px;
|
||||||
width: $button-size;
|
width: $button-size;
|
||||||
height: $button-size;
|
height: $button-size;
|
||||||
border-radius: $button-size / 2;
|
border-radius: $button-size / 2;
|
||||||
line-height: $button-size;
|
line-height: $button-size * 0.9;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
@@ -79,7 +78,8 @@ $tabs-height: 36px;
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
height: 5px;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
transition: 0.25s width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ button.btn.btn-secondary.btn-maximize(
|
|||||||
svg(version='1.1', width='10', height='10')
|
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')
|
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(
|
button.btn.btn-secondary.btn-close(
|
||||||
(click)='hostApp.quit()',
|
(click)='hostApp.getWindow().close()',
|
||||||
)
|
)
|
||||||
svg(version='1.1', width='10', height='10')
|
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')
|
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')
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
hotkeys:
|
hotkeys:
|
||||||
|
new-window:
|
||||||
|
- 'Ctrl-Shift-N'
|
||||||
toggle-window:
|
toggle-window:
|
||||||
- 'Ctrl+Space'
|
- 'Ctrl+Space'
|
||||||
toggle-fullscreen:
|
toggle-fullscreen:
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
hotkeys:
|
hotkeys:
|
||||||
|
new-window:
|
||||||
|
- '⌘-N'
|
||||||
toggle-window:
|
toggle-window:
|
||||||
- 'Ctrl+Space'
|
- 'Ctrl+Space'
|
||||||
toggle-fullscreen:
|
toggle-fullscreen:
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
hotkeys:
|
hotkeys:
|
||||||
|
new-window:
|
||||||
|
- 'Ctrl-Shift-N'
|
||||||
toggle-window:
|
toggle-window:
|
||||||
- 'Ctrl+Space'
|
- 'Ctrl+Space'
|
||||||
toggle-fullscreen:
|
toggle-fullscreen:
|
||||||
|
15
terminus-core/src/directives/autofocus.directive.ts
Normal file
15
terminus-core/src/directives/autofocus.directive.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Directive, AfterViewInit, ElementRef } from '@angular/core'
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[autofocus]'
|
||||||
|
})
|
||||||
|
export class AutofocusDirective implements AfterViewInit {
|
||||||
|
constructor (private el: ElementRef) { }
|
||||||
|
|
||||||
|
ngAfterViewInit () {
|
||||||
|
this.el.nativeElement.blur()
|
||||||
|
setTimeout(() => {
|
||||||
|
this.el.nativeElement.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -14,6 +14,7 @@ import { LogService } from './services/log.service'
|
|||||||
import { HomeBaseService } from './services/homeBase.service'
|
import { HomeBaseService } from './services/homeBase.service'
|
||||||
import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service'
|
import { HotkeysService, AppHotkeyProvider } from './services/hotkeys.service'
|
||||||
import { DockingService } from './services/docking.service'
|
import { DockingService } from './services/docking.service'
|
||||||
|
import { ShellIntegrationService } from './services/shellIntegration.service'
|
||||||
import { TabRecoveryService } from './services/tabRecovery.service'
|
import { TabRecoveryService } from './services/tabRecovery.service'
|
||||||
import { ThemesService } from './services/themes.service'
|
import { ThemesService } from './services/themes.service'
|
||||||
import { TouchbarService } from './services/touchbar.service'
|
import { TouchbarService } from './services/touchbar.service'
|
||||||
@@ -30,11 +31,13 @@ import { ToggleComponent } from './components/toggle.component'
|
|||||||
import { WindowControlsComponent } from './components/windowControls.component'
|
import { WindowControlsComponent } from './components/windowControls.component'
|
||||||
import { RenameTabModalComponent } from './components/renameTabModal.component'
|
import { RenameTabModalComponent } from './components/renameTabModal.component'
|
||||||
|
|
||||||
|
import { AutofocusDirective } from './directives/autofocus.directive'
|
||||||
|
|
||||||
import { HotkeyProvider } from './api/hotkeyProvider'
|
import { HotkeyProvider } from './api/hotkeyProvider'
|
||||||
import { ConfigProvider } from './api/configProvider'
|
import { ConfigProvider } from './api/configProvider'
|
||||||
import { Theme } from './api/theme'
|
import { Theme } from './api/theme'
|
||||||
|
|
||||||
import { StandardTheme, StandardCompactTheme } from './theme'
|
import { StandardTheme, StandardCompactTheme, PaperTheme } from './theme'
|
||||||
import { CoreConfigProvider } from './config'
|
import { CoreConfigProvider } from './config'
|
||||||
|
|
||||||
import 'perfect-scrollbar/css/perfect-scrollbar.css'
|
import 'perfect-scrollbar/css/perfect-scrollbar.css'
|
||||||
@@ -49,6 +52,7 @@ const PROVIDERS = [
|
|||||||
HostAppService,
|
HostAppService,
|
||||||
HotkeysService,
|
HotkeysService,
|
||||||
LogService,
|
LogService,
|
||||||
|
ShellIntegrationService,
|
||||||
TabRecoveryService,
|
TabRecoveryService,
|
||||||
ThemesService,
|
ThemesService,
|
||||||
TouchbarService,
|
TouchbarService,
|
||||||
@@ -56,6 +60,7 @@ const PROVIDERS = [
|
|||||||
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
|
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
|
||||||
{ provide: Theme, useClass: StandardTheme, multi: true },
|
{ provide: Theme, useClass: StandardTheme, multi: true },
|
||||||
{ provide: Theme, useClass: StandardCompactTheme, multi: true },
|
{ provide: Theme, useClass: StandardCompactTheme, multi: true },
|
||||||
|
{ provide: Theme, useClass: PaperTheme, multi: true },
|
||||||
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
|
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
|
||||||
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true }}
|
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true }}
|
||||||
]
|
]
|
||||||
@@ -80,6 +85,7 @@ const PROVIDERS = [
|
|||||||
WindowControlsComponent,
|
WindowControlsComponent,
|
||||||
RenameTabModalComponent,
|
RenameTabModalComponent,
|
||||||
SafeModeModalComponent,
|
SafeModeModalComponent,
|
||||||
|
AutofocusDirective,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
RenameTabModalComponent,
|
RenameTabModalComponent,
|
||||||
@@ -88,6 +94,7 @@ const PROVIDERS = [
|
|||||||
exports: [
|
exports: [
|
||||||
CheckboxComponent,
|
CheckboxComponent,
|
||||||
ToggleComponent,
|
ToggleComponent,
|
||||||
|
AutofocusDirective,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export default class AppModule {
|
export default class AppModule {
|
||||||
|
@@ -3,6 +3,7 @@ import { Injectable, ComponentFactoryResolver, Injector } from '@angular/core'
|
|||||||
import { BaseTabComponent } from '../components/baseTab.component'
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
import { Logger, LogService } from './log.service'
|
import { Logger, LogService } from './log.service'
|
||||||
import { ConfigService } from './config.service'
|
import { ConfigService } from './config.service'
|
||||||
|
import { HostAppService } from './hostApp.service'
|
||||||
|
|
||||||
export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
|
export declare type TabComponentType = new (...args: any[]) => BaseTabComponent
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ export class AppService {
|
|||||||
constructor (
|
constructor (
|
||||||
private componentFactoryResolver: ComponentFactoryResolver,
|
private componentFactoryResolver: ComponentFactoryResolver,
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
|
private hostApp: HostAppService,
|
||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
log: LogService,
|
log: LogService,
|
||||||
) {
|
) {
|
||||||
@@ -37,15 +39,21 @@ export class AppService {
|
|||||||
openNewTab (type: TabComponentType, inputs?: any): BaseTabComponent {
|
openNewTab (type: TabComponentType, inputs?: any): BaseTabComponent {
|
||||||
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(type)
|
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(type)
|
||||||
let componentRef = componentFactory.create(this.injector)
|
let componentRef = componentFactory.create(this.injector)
|
||||||
componentRef.instance.hostView = componentRef.hostView
|
let tab = componentRef.instance
|
||||||
Object.assign(componentRef.instance, inputs || {})
|
tab.hostView = componentRef.hostView
|
||||||
|
Object.assign(tab, inputs || {})
|
||||||
|
|
||||||
this.tabs.push(componentRef.instance)
|
this.tabs.push(tab)
|
||||||
this.selectTab(componentRef.instance)
|
this.selectTab(tab)
|
||||||
this.tabsChanged.next()
|
this.tabsChanged.next()
|
||||||
this.tabOpened.next(componentRef.instance)
|
this.tabOpened.next(tab)
|
||||||
|
|
||||||
return componentRef.instance
|
tab.titleChange$.subscribe(title => {
|
||||||
|
if (tab === this.activeTab) {
|
||||||
|
this.hostApp.setTitle(title)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return tab
|
||||||
}
|
}
|
||||||
|
|
||||||
selectTab (tab: BaseTabComponent) {
|
selectTab (tab: BaseTabComponent) {
|
||||||
@@ -66,6 +74,7 @@ export class AppService {
|
|||||||
this.activeTabChange.next(tab)
|
this.activeTabChange.next(tab)
|
||||||
if (this.activeTab) {
|
if (this.activeTab) {
|
||||||
this.activeTab.emitFocused()
|
this.activeTab.emitFocused()
|
||||||
|
this.hostApp.setTitle(this.activeTab.title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,5 +131,6 @@ export class AppService {
|
|||||||
emitReady () {
|
emitReady () {
|
||||||
this.ready.next(null)
|
this.ready.next(null)
|
||||||
this.ready.complete()
|
this.ready.complete()
|
||||||
|
this.hostApp.emitReady()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -63,7 +63,7 @@ export class ConfigService {
|
|||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
electron: ElectronService,
|
electron: ElectronService,
|
||||||
hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
@Inject(ConfigProvider) configProviders: ConfigProvider[],
|
@Inject(ConfigProvider) configProviders: ConfigProvider[],
|
||||||
) {
|
) {
|
||||||
this.path = path.join(electron.app.getPath('userData'), 'config.yaml')
|
this.path = path.join(electron.app.getPath('userData'), 'config.yaml')
|
||||||
@@ -78,6 +78,11 @@ export class ConfigService {
|
|||||||
return defaults
|
return defaults
|
||||||
}).reduce(configMerge)
|
}).reduce(configMerge)
|
||||||
this.load()
|
this.load()
|
||||||
|
|
||||||
|
hostApp.configChangeBroadcast$.subscribe(() => {
|
||||||
|
this.load()
|
||||||
|
this.emitChange()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaults () {
|
getDefaults () {
|
||||||
@@ -96,6 +101,7 @@ export class ConfigService {
|
|||||||
save (): void {
|
save (): void {
|
||||||
fs.writeFileSync(this.path, yaml.safeDump(this._store), 'utf8')
|
fs.writeFileSync(this.path, yaml.safeDump(this._store), 'utf8')
|
||||||
this.emitChange()
|
this.emitChange()
|
||||||
|
this.hostApp.broadcastConfigChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
readRaw (): string {
|
readRaw (): string {
|
||||||
|
@@ -76,12 +76,8 @@ export class DockingService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getWindow () {
|
|
||||||
return this.electron.app.window
|
|
||||||
}
|
|
||||||
|
|
||||||
repositionWindow () {
|
repositionWindow () {
|
||||||
let [x, y] = this.getWindow().getPosition()
|
let [x, y] = this.hostApp.getWindow().getPosition()
|
||||||
for (let screen of this.electron.screen.getAllDisplays()) {
|
for (let screen of this.electron.screen.getAllDisplays()) {
|
||||||
let bounds = screen.bounds
|
let bounds = screen.bounds
|
||||||
if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
|
if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
|
||||||
@@ -89,6 +85,6 @@ export class DockingService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let screen = this.electron.screen.getPrimaryDisplay()
|
let screen = this.electron.screen.getPrimaryDisplay()
|
||||||
this.getWindow().setPosition(screen.bounds.x, screen.bounds.y)
|
this.hostApp.getWindow().setPosition(screen.bounds.x, screen.bounds.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { TouchBar } from 'electron'
|
import { TouchBar, BrowserWindow } from 'electron'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ElectronService {
|
export class ElectronService {
|
||||||
@@ -13,6 +13,7 @@ export class ElectronService {
|
|||||||
screen: any
|
screen: any
|
||||||
remote: any
|
remote: any
|
||||||
TouchBar: typeof TouchBar
|
TouchBar: typeof TouchBar
|
||||||
|
BrowserWindow: typeof BrowserWindow
|
||||||
private electron: any
|
private electron: any
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
@@ -27,6 +28,7 @@ export class ElectronService {
|
|||||||
this.globalShortcut = this.remote.globalShortcut
|
this.globalShortcut = this.remote.globalShortcut
|
||||||
this.nativeImage = this.remote.nativeImage
|
this.nativeImage = this.remote.nativeImage
|
||||||
this.TouchBar = this.remote.TouchBar
|
this.TouchBar = this.remote.TouchBar
|
||||||
|
this.BrowserWindow = this.remote.BrowserWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteRequire (name: string): any {
|
remoteRequire (name: string): any {
|
||||||
|
@@ -25,12 +25,14 @@ export class HomeBaseService {
|
|||||||
|
|
||||||
reportBug () {
|
reportBug () {
|
||||||
let body = `Version: ${this.appVersion}\n`
|
let body = `Version: ${this.appVersion}\n`
|
||||||
body += `Platform: ${os.platform()} ${os.release()}\n\n`
|
body += `Platform: ${os.platform()} ${os.release()}\n`
|
||||||
let label = {
|
let label = {
|
||||||
darwin: 'macOS',
|
darwin: 'OS: macOS',
|
||||||
windows: 'Windows',
|
windows: 'OS: Windows',
|
||||||
linux: 'Linux',
|
linux: 'OS: Linux',
|
||||||
}[os.platform()]
|
}[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}`)
|
this.electron.shell.openExternal(`https://github.com/eugeny/terminus/issues/new?body=${encodeURIComponent(body)}&labels=${label}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
import shellEscape = require('shell-escape')
|
||||||
import { Observable, Subject } from 'rxjs'
|
import { Observable, Subject } from 'rxjs'
|
||||||
import { Injectable, NgZone, EventEmitter } from '@angular/core'
|
import { Injectable, NgZone, EventEmitter } from '@angular/core'
|
||||||
import { ElectronService } from '../services/electron.service'
|
import { ElectronService } from './electron.service'
|
||||||
import { Logger, LogService } from '../services/log.service'
|
import { Logger, LogService } from './log.service'
|
||||||
|
|
||||||
export enum Platform {
|
export enum Platform {
|
||||||
Linux, macOS, Windows,
|
Linux, macOS, Windows,
|
||||||
@@ -19,19 +20,23 @@ export interface Bounds {
|
|||||||
export class HostAppService {
|
export class HostAppService {
|
||||||
platform: Platform
|
platform: Platform
|
||||||
nodePlatform: string
|
nodePlatform: string
|
||||||
ready = new EventEmitter<any>()
|
|
||||||
shown = new EventEmitter<any>()
|
shown = new EventEmitter<any>()
|
||||||
isFullScreen = false
|
isFullScreen = false
|
||||||
private preferencesMenu = new Subject<void>()
|
private preferencesMenu = new Subject<void>()
|
||||||
private secondInstance = new Subject<void>()
|
private secondInstance = new Subject<void>()
|
||||||
private cliOpenDirectory = new Subject<string>()
|
private cliOpenDirectory = new Subject<string>()
|
||||||
private cliRunCommand = new Subject<string[]>()
|
private cliRunCommand = new Subject<string[]>()
|
||||||
|
private cliPaste = new Subject<string>()
|
||||||
|
private configChangeBroadcast = new Subject<void>()
|
||||||
private logger: Logger
|
private logger: Logger
|
||||||
|
private windowId: number
|
||||||
|
|
||||||
get preferencesMenu$ (): Observable<void> { return this.preferencesMenu }
|
get preferencesMenu$ (): Observable<void> { return this.preferencesMenu }
|
||||||
get secondInstance$ (): Observable<void> { return this.secondInstance }
|
get secondInstance$ (): Observable<void> { return this.secondInstance }
|
||||||
get cliOpenDirectory$ (): Observable<string> { return this.cliOpenDirectory }
|
get cliOpenDirectory$ (): Observable<string> { return this.cliOpenDirectory }
|
||||||
get cliRunCommand$ (): Observable<string[]> { return this.cliRunCommand }
|
get cliRunCommand$ (): Observable<string[]> { return this.cliRunCommand }
|
||||||
|
get cliPaste$ (): Observable<string> { return this.cliPaste }
|
||||||
|
get configChangeBroadcast$ (): Observable<void> { return this.configChangeBroadcast }
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
@@ -46,9 +51,12 @@ export class HostAppService {
|
|||||||
linux: Platform.Linux
|
linux: Platform.Linux
|
||||||
}[this.nodePlatform]
|
}[this.nodePlatform]
|
||||||
|
|
||||||
|
this.windowId = parseInt(location.search.substring(1))
|
||||||
|
this.logger.info('Window ID:', this.windowId)
|
||||||
|
|
||||||
electron.ipcRenderer.on('host:preferences-menu', () => this.zone.run(() => this.preferencesMenu.next()))
|
electron.ipcRenderer.on('host:preferences-menu', () => this.zone.run(() => this.preferencesMenu.next()))
|
||||||
|
|
||||||
electron.ipcRenderer.on('uncaughtException', ($event, err) => {
|
electron.ipcRenderer.on('uncaughtException', (_$event, err) => {
|
||||||
this.logger.error('Unhandled exception:', err)
|
this.logger.error('Unhandled exception:', err)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -64,23 +72,33 @@ export class HostAppService {
|
|||||||
this.zone.run(() => this.shown.emit())
|
this.zone.run(() => this.shown.emit())
|
||||||
})
|
})
|
||||||
|
|
||||||
electron.ipcRenderer.on('host:second-instance', ($event, argv: any, cwd: string) => this.zone.run(() => {
|
electron.ipcRenderer.on('host:second-instance', (_$event, argv: any, cwd: string) => this.zone.run(() => {
|
||||||
this.logger.info('Second instance', argv)
|
this.logger.info('Second instance', argv)
|
||||||
const op = argv._[0]
|
const op = argv._[0]
|
||||||
if (op === 'open') {
|
if (op === 'open') {
|
||||||
this.cliOpenDirectory.next(path.resolve(cwd, argv.directory))
|
this.cliOpenDirectory.next(path.resolve(cwd, argv.directory))
|
||||||
} else if (op === 'run') {
|
} else if (op === 'run') {
|
||||||
this.cliRunCommand.next(argv.command)
|
this.cliRunCommand.next(argv.command)
|
||||||
|
} else if (op === 'paste') {
|
||||||
|
let text = argv.text
|
||||||
|
if (argv.escape) {
|
||||||
|
text = shellEscape([text])
|
||||||
|
}
|
||||||
|
this.cliPaste.next(text)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
this.ready.subscribe(() => {
|
electron.ipcRenderer.on('host:config-change', () => this.zone.run(() => {
|
||||||
electron.ipcRenderer.send('app:ready')
|
this.configChangeBroadcast.next()
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
getWindow () {
|
getWindow () {
|
||||||
return this.electron.app.window
|
return this.electron.BrowserWindow.fromId(this.windowId)
|
||||||
|
}
|
||||||
|
|
||||||
|
newWindow () {
|
||||||
|
this.electron.ipcRenderer.send('app:new-window')
|
||||||
}
|
}
|
||||||
|
|
||||||
getShell () {
|
getShell () {
|
||||||
@@ -97,7 +115,7 @@ export class HostAppService {
|
|||||||
|
|
||||||
toggleFullscreen () {
|
toggleFullscreen () {
|
||||||
let window = this.getWindow()
|
let window = this.getWindow()
|
||||||
window.setFullScreen(!window.isFullScreen())
|
window.setFullScreen(!this.isFullScreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
openDevTools () {
|
openDevTools () {
|
||||||
@@ -142,6 +160,26 @@ 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')
|
||||||
|
}
|
||||||
|
|
||||||
|
emitReady () {
|
||||||
|
this.electron.ipcRenderer.send('app:ready')
|
||||||
|
}
|
||||||
|
|
||||||
quit () {
|
quit () {
|
||||||
this.logger.info('Quitting')
|
this.logger.info('Quitting')
|
||||||
this.electron.app.quit()
|
this.electron.app.quit()
|
||||||
|
@@ -174,6 +174,10 @@ export class HotkeysService {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppHotkeyProvider extends HotkeyProvider {
|
export class AppHotkeyProvider extends HotkeyProvider {
|
||||||
hotkeys: IHotkeyDescription[] = [
|
hotkeys: IHotkeyDescription[] = [
|
||||||
|
{
|
||||||
|
id: 'new-window',
|
||||||
|
name: 'New window',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'toggle-window',
|
id: 'toggle-window',
|
||||||
name: 'Toggle terminal window',
|
name: 'Toggle terminal window',
|
||||||
|
85
terminus-core/src/services/shellIntegration.service.ts
Normal file
85
terminus-core/src/services/shellIntegration.service.ts
Normal 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()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -3,15 +3,18 @@ import { TouchBarSegmentedControl, SegmentedControlSegment } from 'electron'
|
|||||||
import { AppService } from './app.service'
|
import { AppService } from './app.service'
|
||||||
import { ConfigService } from './config.service'
|
import { ConfigService } from './config.service'
|
||||||
import { ElectronService } from './electron.service'
|
import { ElectronService } from './electron.service'
|
||||||
|
import { HostAppService } from './hostApp.service'
|
||||||
import { IToolbarButton, ToolbarButtonProvider } from '../api'
|
import { IToolbarButton, ToolbarButtonProvider } from '../api'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TouchbarService {
|
export class TouchbarService {
|
||||||
private tabsSegmentedControl: TouchBarSegmentedControl
|
private tabsSegmentedControl: TouchBarSegmentedControl
|
||||||
private tabSegments: SegmentedControlSegment[] = []
|
private tabSegments: SegmentedControlSegment[] = []
|
||||||
|
private nsImageCache: {[id: string]: Electron.NativeImage} = {}
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private app: AppService,
|
private app: AppService,
|
||||||
|
private hostApp: HostAppService,
|
||||||
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
@@ -51,18 +54,24 @@ export class TouchbarService {
|
|||||||
...buttons.map(button => this.getButton(button))
|
...buttons.map(button => this.getButton(button))
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
this.electron.app.window.setTouchBar(touchBar)
|
this.hostApp.setTouchBar(touchBar)
|
||||||
}
|
}
|
||||||
|
|
||||||
private getButton (button: IToolbarButton): Electron.TouchBarButton {
|
private getButton (button: IToolbarButton): Electron.TouchBarButton {
|
||||||
return new this.electron.TouchBar.TouchBarButton({
|
return new this.electron.TouchBar.TouchBarButton({
|
||||||
label: button.touchBarNSImage ? null : this.shortenTitle(button.touchBarTitle || button.title),
|
label: button.touchBarNSImage ? null : this.shortenTitle(button.touchBarTitle || button.title),
|
||||||
icon: button.touchBarNSImage ?
|
icon: button.touchBarNSImage ? this.getCachedNSImage(button.touchBarNSImage) : null,
|
||||||
this.electron.nativeImage.createFromNamedImage(button.touchBarNSImage, [0, 0, 1]) : null,
|
|
||||||
click: () => this.zone.run(() => button.click()),
|
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 {
|
private shortenTitle (title: string): string {
|
||||||
if (title.length > 15) {
|
if (title.length > 15) {
|
||||||
title = title.substring(0, 15) + '...'
|
title = title.substring(0, 15) + '...'
|
||||||
|
398
terminus-core/src/theme.paper.scss
Normal file
398
terminus-core/src/theme.paper.scss
Normal 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;
|
||||||
|
}
|
@@ -36,8 +36,7 @@ $btn-secondary-border: #444;
|
|||||||
//$btn-warning-bg: rgba($orange, .5);
|
//$btn-warning-bg: rgba($orange, .5);
|
||||||
|
|
||||||
|
|
||||||
$nav-tabs-border-color: $body-bg;
|
$nav-tabs-border-width: 0;
|
||||||
$nav-tabs-border-width: 1px;
|
|
||||||
$nav-tabs-border-radius: 0;
|
$nav-tabs-border-radius: 0;
|
||||||
$nav-tabs-link-hover-border-color: $body-bg;
|
$nav-tabs-link-hover-border-color: $body-bg;
|
||||||
$nav-tabs-active-link-hover-color: $white;
|
$nav-tabs-active-link-hover-color: $white;
|
||||||
@@ -83,6 +82,10 @@ $list-group-border-color: rgba(255,255,255,.1);
|
|||||||
$list-group-hover-bg: rgba(255,255,255,.1);
|
$list-group-hover-bg: rgba(255,255,255,.1);
|
||||||
$list-group-link-active-bg: rgba(255,255,255,.2);
|
$list-group-link-active-bg: rgba(255,255,255,.2);
|
||||||
|
|
||||||
|
$list-group-action-color: $body-color;
|
||||||
|
$list-group-action-bg: rgba(255,255,255,.05);
|
||||||
|
$list-group-action-active-bg: $list-group-link-active-bg;
|
||||||
|
|
||||||
$pre-bg: $dropdown-bg;
|
$pre-bg: $dropdown-bg;
|
||||||
$pre-color: $dropdown-link-color;
|
$pre-color: $dropdown-link-color;
|
||||||
|
|
||||||
@@ -124,9 +127,9 @@ app-root {
|
|||||||
&> .content {
|
&> .content {
|
||||||
.tab-bar {
|
.tab-bar {
|
||||||
.btn-tab-bar {
|
.btn-tab-bar {
|
||||||
&:not(:hover):not(:active) {
|
background: transparent;
|
||||||
background: transparent;
|
&:hover { background: rgba(0, 0, 0, .25) !important; }
|
||||||
}
|
&:active { background: rgba(0, 0, 0, .5) !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
&>.tabs {
|
&>.tabs {
|
||||||
@@ -134,10 +137,10 @@ app-root {
|
|||||||
border-left: 1px solid transparent;
|
border-left: 1px solid transparent;
|
||||||
border-right: 1px solid transparent;
|
border-right: 1px solid transparent;
|
||||||
|
|
||||||
transition: 0.25s all;
|
transition: 0.125s ease-out width;
|
||||||
|
|
||||||
.index {
|
.index {
|
||||||
color: #888;
|
color: rgba(255, 255, 255, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@@ -219,10 +222,7 @@ settings-tab > ngb-tabset {
|
|||||||
background: rgba(0, 0, 0, 0.25);
|
background: rgba(0, 0, 0, 0.25);
|
||||||
|
|
||||||
& > .nav-item > .nav-link {
|
& > .nav-item > .nav-link {
|
||||||
border-left: none;
|
border: none;
|
||||||
border-right: none;
|
|
||||||
border-top: 1px solid transparent;
|
|
||||||
border-bottom: 1px solid transparent;
|
|
||||||
padding: 10px 50px 10px 20px;
|
padding: 10px 50px 10px 20px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
@@ -233,11 +233,6 @@ settings-tab > ngb-tabset {
|
|||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
|
||||||
border-top-color: $nav-tabs-active-link-hover-border-color;
|
|
||||||
border-bottom-color: $nav-tabs-active-link-hover-border-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,3 +356,8 @@ checkbox i.on {
|
|||||||
toggle.active .body .toggle {
|
toggle.active .body .toggle {
|
||||||
background: $blue;
|
background: $blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-group-item svg {
|
||||||
|
fill: white;
|
||||||
|
fill-opacity: 0.75;
|
||||||
|
}
|
||||||
|
@@ -14,3 +14,10 @@ export class StandardCompactTheme extends Theme {
|
|||||||
css = require('./theme.compact.scss')
|
css = require('./theme.compact.scss')
|
||||||
terminalBackground = '#1D272D'
|
terminalBackground = '#1D272D'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PaperTheme extends Theme {
|
||||||
|
name = 'Paper'
|
||||||
|
css = require('./theme.paper.scss')
|
||||||
|
terminalBackground = '#1D272D'
|
||||||
|
}
|
||||||
|
@@ -460,6 +460,10 @@ semver@^5.4.1:
|
|||||||
version "5.4.1"
|
version "5.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
|
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:
|
sntp@2.x.x:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
|
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"
|
core-util-is "1.0.2"
|
||||||
extsprintf "^1.2.0"
|
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:
|
winston@^2.4.0:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee"
|
resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-plugin-manager",
|
"name": "terminus-plugin-manager",
|
||||||
"version": "1.0.0-alpha.48",
|
"version": "1.0.0-alpha.55",
|
||||||
"description": "Terminus' plugin manager",
|
"description": "Terminus' plugin manager",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-builtin-plugin"
|
"terminus-builtin-plugin"
|
||||||
|
@@ -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")')
|
ng-container(*ngFor='let plugin of (availablePlugins$|async|orderBy:"name")')
|
||||||
.list-group-item.flex-column.align-items-start(*ngIf='!isAlreadyInstalled(plugin)')
|
.list-group-item.flex-column.align-items-start(*ngIf='!isAlreadyInstalled(plugin)')
|
||||||
.d-flex.w-100
|
.d-flex.w-100
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-settings",
|
"name": "terminus-settings",
|
||||||
"version": "1.0.0-alpha.48",
|
"version": "1.0.0-alpha.55",
|
||||||
"description": "Terminus terminal settings page",
|
"description": "Terminus terminal settings page",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-builtin-plugin"
|
"terminus-builtin-plugin"
|
||||||
|
@@ -19,6 +19,14 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
|
|||||||
i.fa.fa-bug
|
i.fa.fa-bug
|
||||||
span Report a problem
|
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
|
.form-line
|
||||||
.header
|
.header
|
||||||
.title Theme
|
.title Theme
|
||||||
|
@@ -1,7 +1,19 @@
|
|||||||
import * as yaml from 'js-yaml'
|
import * as yaml from 'js-yaml'
|
||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { Component, Inject, Input } from '@angular/core'
|
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'
|
import { SettingsTabProvider } from '../api'
|
||||||
|
|
||||||
@@ -21,6 +33,7 @@ export class SettingsTabComponent extends BaseTabComponent {
|
|||||||
Platform = Platform
|
Platform = Platform
|
||||||
configDefaults: any
|
configDefaults: any
|
||||||
configFile: string
|
configFile: string
|
||||||
|
isShellIntegrationInstalled = false
|
||||||
private configSubscription: Subscription
|
private configSubscription: Subscription
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
@@ -29,6 +42,7 @@ export class SettingsTabComponent extends BaseTabComponent {
|
|||||||
public docking: DockingService,
|
public docking: DockingService,
|
||||||
public hostApp: HostAppService,
|
public hostApp: HostAppService,
|
||||||
public homeBase: HomeBaseService,
|
public homeBase: HomeBaseService,
|
||||||
|
public shellIntegration: ShellIntegrationService,
|
||||||
@Inject(HotkeyProvider) hotkeyProviders: HotkeyProvider[],
|
@Inject(HotkeyProvider) hotkeyProviders: HotkeyProvider[],
|
||||||
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
|
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
|
||||||
@Inject(Theme) public themes: Theme[],
|
@Inject(Theme) public themes: Theme[],
|
||||||
@@ -47,6 +61,10 @@ export class SettingsTabComponent extends BaseTabComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async ngOnInit () {
|
||||||
|
this.isShellIntegrationInstalled = await this.shellIntegration.isInstalled()
|
||||||
|
}
|
||||||
|
|
||||||
getRecoveryToken (): any {
|
getRecoveryToken (): any {
|
||||||
return { type: 'app:settings' }
|
return { type: 'app:settings' }
|
||||||
}
|
}
|
||||||
@@ -75,4 +93,9 @@ export class SettingsTabComponent extends BaseTabComponent {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async installShellIntegration () {
|
||||||
|
await this.shellIntegration.install()
|
||||||
|
this.isShellIntegrationInstalled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-ssh",
|
"name": "terminus-ssh",
|
||||||
"version": "1.0.0-alpha.48",
|
"version": "1.0.0-alpha.55",
|
||||||
"description": "SSH connection manager for Terminus",
|
"description": "SSH connection manager for Terminus",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-builtin-plugin"
|
"terminus-builtin-plugin"
|
||||||
|
@@ -1,5 +1,10 @@
|
|||||||
import { BaseSession } from 'terminus-terminal'
|
import { BaseSession } from 'terminus-terminal'
|
||||||
|
|
||||||
|
export interface LoginScript {
|
||||||
|
expect?: string
|
||||||
|
send: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface SSHConnection {
|
export interface SSHConnection {
|
||||||
name?: string
|
name?: string
|
||||||
host: string
|
host: string
|
||||||
@@ -7,18 +12,45 @@ export interface SSHConnection {
|
|||||||
user: string
|
user: string
|
||||||
password?: string
|
password?: string
|
||||||
privateKey?: string
|
privateKey?: string
|
||||||
|
group?: string
|
||||||
|
scripts?: LoginScript[]
|
||||||
|
keepaliveInterval?: number
|
||||||
|
keepaliveCountMax?: number
|
||||||
|
readyTimeout?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SSHSession extends BaseSession {
|
export class SSHSession extends BaseSession {
|
||||||
constructor (private shell: any) {
|
scripts?: LoginScript[]
|
||||||
|
|
||||||
|
constructor (private shell: any, conn: SSHConnection) {
|
||||||
super()
|
super()
|
||||||
|
this.scripts = conn.scripts || []
|
||||||
}
|
}
|
||||||
|
|
||||||
start () {
|
start () {
|
||||||
this.open = true
|
this.open = true
|
||||||
|
|
||||||
this.shell.on('data', data => {
|
this.shell.on('data', data => {
|
||||||
this.emitOutput(data.toString())
|
let dataString = data.toString()
|
||||||
|
this.emitOutput(dataString)
|
||||||
|
|
||||||
|
if (this.scripts) {
|
||||||
|
let found = false
|
||||||
|
for (let script of this.scripts) {
|
||||||
|
if (dataString.includes(script.expect)) {
|
||||||
|
console.log('Executing script:', script.send)
|
||||||
|
this.shell.write(script.send + '\n')
|
||||||
|
this.scripts = this.scripts.filter(x => x !== script)
|
||||||
|
found = true
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
this.executeUnconditionalScripts()
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.shell.on('end', () => {
|
this.shell.on('end', () => {
|
||||||
@@ -26,6 +58,8 @@ export class SSHSession extends BaseSession {
|
|||||||
this.destroy()
|
this.destroy()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.executeUnconditionalScripts()
|
||||||
}
|
}
|
||||||
|
|
||||||
resize (columns, rows) {
|
resize (columns, rows) {
|
||||||
@@ -51,4 +85,23 @@ export class SSHSession extends BaseSession {
|
|||||||
async getWorkingDirectory (): Promise<string> {
|
async getWorkingDirectory (): Promise<string> {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private executeUnconditionalScripts () {
|
||||||
|
if (this.scripts) {
|
||||||
|
for (let script of this.scripts) {
|
||||||
|
if (!script.expect) {
|
||||||
|
console.log('Executing script:', script.send)
|
||||||
|
this.shell.write(script.send + '\n')
|
||||||
|
this.scripts = this.scripts.filter(x => x !== script)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISSHConnectionGroup {
|
||||||
|
name: string
|
||||||
|
connections: SSHConnection[]
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,139 @@
|
|||||||
.modal-body
|
.modal-body
|
||||||
.form-group
|
ngb-tabset(type='pills', [activeId]='basic')
|
||||||
label Name
|
ngb-tab(id='basic')
|
||||||
input.form-control(
|
ng-template(ngbTabTitle)
|
||||||
type='text',
|
| Basic Setting
|
||||||
[(ngModel)]='connection.name',
|
ng-template(ngbTabContent)
|
||||||
)
|
.form-group
|
||||||
|
label Name
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
autofocus,
|
||||||
|
[(ngModel)]='connection.name',
|
||||||
|
)
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label Host
|
label Group
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type='text',
|
type='text',
|
||||||
[(ngModel)]='connection.host',
|
placeholder='Ungrouped',
|
||||||
)
|
[(ngModel)]='connection.group',
|
||||||
|
)
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label Port
|
label Host
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type='number',
|
type='text',
|
||||||
placeholder='22',
|
[(ngModel)]='connection.host',
|
||||||
[(ngModel)]='connection.port',
|
)
|
||||||
)
|
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label Username
|
label Port
|
||||||
input.form-control(
|
input.form-control(
|
||||||
type='text',
|
type='number',
|
||||||
[(ngModel)]='connection.user',
|
placeholder='22',
|
||||||
)
|
[(ngModel)]='connection.port',
|
||||||
|
)
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label Private key
|
label Username
|
||||||
.input-group
|
input.form-control(
|
||||||
input.form-control(
|
type='text',
|
||||||
type='text',
|
[(ngModel)]='connection.user',
|
||||||
placeholder='Key file path',
|
)
|
||||||
[(ngModel)]='connection.privateKey'
|
|
||||||
)
|
.alert.alert-info.d-flex.bg-transparent.text-white.align-items-center(*ngIf='hasSavedPassword')
|
||||||
.input-group-btn
|
.mr-auto There is a saved password for this connection
|
||||||
button.btn.btn-secondary((click)='selectPrivateKey()')
|
button.btn.btn-danger.ml-4((click)='clearSavedPassword()') Forget
|
||||||
i.fa.fa-folder-open
|
|
||||||
|
.form-group
|
||||||
|
label Private key
|
||||||
|
.input-group
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
placeholder='Key file path',
|
||||||
|
[(ngModel)]='connection.privateKey'
|
||||||
|
)
|
||||||
|
.input-group-btn
|
||||||
|
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
|
||||||
|
ng-template(ngbTabContent)
|
||||||
|
table
|
||||||
|
tr
|
||||||
|
th String to expect
|
||||||
|
th String to be sent
|
||||||
|
th Actions
|
||||||
|
tr(*ngFor='let script of connection.scripts')
|
||||||
|
td
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
[(ngModel)]='script.expect'
|
||||||
|
)
|
||||||
|
td
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
[(ngModel)]='script.send'
|
||||||
|
)
|
||||||
|
td
|
||||||
|
.input-group.flex-nowrap
|
||||||
|
button.btn.btn-outline-info.ml-0((click)='moveScriptUp(script)')
|
||||||
|
i.fa.fa-arrow-up
|
||||||
|
button.btn.btn-outline-info.ml-0((click)='moveScriptDown(script)')
|
||||||
|
i.fa.fa-arrow-down
|
||||||
|
button.btn.btn-outline-danger.ml-0((click)='deleteScript(script)')
|
||||||
|
i.fa.fa-trash-o
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
placeholder='Enter a string to expect',
|
||||||
|
[(ngModel)]='newScript.expect'
|
||||||
|
)
|
||||||
|
td
|
||||||
|
input.form-control(
|
||||||
|
type='text',
|
||||||
|
placeholder='Enter a string to be sent',
|
||||||
|
[(ngModel)]='newScript.send'
|
||||||
|
)
|
||||||
|
td
|
||||||
|
.input-group.flex-nowrap
|
||||||
|
button.btn.btn-outline-info.ml-0((click)='addScript()')
|
||||||
|
i.fa.fa-check
|
||||||
|
button.btn.btn-outline-danger.ml-0((click)='clearScript()')
|
||||||
|
i.fa.fa-trash-o
|
||||||
|
|
||||||
.modal-footer
|
.modal-footer
|
||||||
button.btn.btn-outline-primary((click)='save()') Save
|
button.btn.btn-outline-primary((click)='save()') Save
|
||||||
button.btn.btn-outline-danger((click)='cancel()') Cancel
|
button.btn.btn-outline-danger((click)='cancel()') Cancel
|
||||||
|
@@ -1,19 +1,34 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ElectronService, HostAppService } from 'terminus-core'
|
import { ElectronService, HostAppService } from 'terminus-core'
|
||||||
import { SSHConnection } from '../api'
|
import { PasswordStorageService } from '../services/passwordStorage.service'
|
||||||
|
import { SSHConnection, LoginScript } from '../api'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: require('./editConnectionModal.component.pug'),
|
template: require('./editConnectionModal.component.pug'),
|
||||||
})
|
})
|
||||||
export class EditConnectionModalComponent {
|
export class EditConnectionModalComponent {
|
||||||
connection: SSHConnection
|
connection: SSHConnection
|
||||||
|
newScript: LoginScript
|
||||||
|
hasSavedPassword: boolean
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private modalInstance: NgbActiveModal,
|
private modalInstance: NgbActiveModal,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
) { }
|
private passwordStorage: PasswordStorageService,
|
||||||
|
) {
|
||||||
|
this.newScript = { expect: '', send: '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit () {
|
||||||
|
this.hasSavedPassword = !!(await this.passwordStorage.loadPassword(this.connection))
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSavedPassword () {
|
||||||
|
this.hasSavedPassword = false
|
||||||
|
this.passwordStorage.deletePassword(this.connection)
|
||||||
|
}
|
||||||
|
|
||||||
selectPrivateKey () {
|
selectPrivateKey () {
|
||||||
let path = this.electron.dialog.showOpenDialog(
|
let path = this.electron.dialog.showOpenDialog(
|
||||||
@@ -34,4 +49,39 @@ export class EditConnectionModalComponent {
|
|||||||
cancel () {
|
cancel () {
|
||||||
this.modalInstance.dismiss()
|
this.modalInstance.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
moveScriptUp (script: LoginScript) {
|
||||||
|
let index = this.connection.scripts.indexOf(script)
|
||||||
|
if (index > 0) {
|
||||||
|
this.connection.scripts.splice(index, 1)
|
||||||
|
this.connection.scripts.splice(index - 1, 0, script)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moveScriptDown (script: LoginScript) {
|
||||||
|
let index = this.connection.scripts.indexOf(script)
|
||||||
|
if (index >= 0 && index < this.connection.scripts.length - 1) {
|
||||||
|
this.connection.scripts.splice(index, 1)
|
||||||
|
this.connection.scripts.splice(index + 1, 0, script)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteScript (script: LoginScript) {
|
||||||
|
if (confirm(`Delete?`)) {
|
||||||
|
this.connection.scripts = this.connection.scripts.filter(x => x !== script)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addScript () {
|
||||||
|
if (!this.connection.scripts) {
|
||||||
|
this.connection.scripts = []
|
||||||
|
}
|
||||||
|
this.connection.scripts.push({...this.newScript})
|
||||||
|
this.clearScript()
|
||||||
|
}
|
||||||
|
|
||||||
|
clearScript () {
|
||||||
|
this.newScript.expect = ''
|
||||||
|
this.newScript.send = ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
.modal-body
|
.modal-body
|
||||||
input.form-control(
|
input.form-control(
|
||||||
[type]='password ? "password" : "text"',
|
[type]='password ? "password" : "text"',
|
||||||
|
autofocus,
|
||||||
[(ngModel)]='value',
|
[(ngModel)]='value',
|
||||||
#input,
|
#input,
|
||||||
[placeholder]='prompt',
|
[placeholder]='prompt',
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
[(ngModel)]='quickTarget',
|
[(ngModel)]='quickTarget',
|
||||||
autofocus,
|
autofocus,
|
||||||
placeholder='Quick connect: [user@]host[:port]',
|
placeholder='Quick connect: [user@]host[:port]',
|
||||||
|
(ngModelChange)='refresh()',
|
||||||
(keyup.enter)='quickConnect()'
|
(keyup.enter)='quickConnect()'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,13 +13,16 @@
|
|||||||
i.fa.fa-fw.fa-history
|
i.fa.fa-fw.fa-history
|
||||||
span {{lastConnection.name}}
|
span {{lastConnection.name}}
|
||||||
|
|
||||||
.list-group.mt-3
|
.list-group.mt-3.connections-list
|
||||||
a.list-group-item.list-group-item-action(*ngFor='let connection of connections', (click)='connect(connection)')
|
ng-container(*ngFor='let group of childGroups')
|
||||||
i.fa.fa-fw.fa-globe
|
.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||||
span {{connection.name}}
|
(click)='groupCollapsed[group.name] = !groupCollapsed[group.name]'
|
||||||
a.list-group-item.list-group-item-action((click)='manageConnections()')
|
)
|
||||||
i.fa.fa-fw.fa-wrench
|
.fa.fa-fw.fa-chevron-right(*ngIf='groupCollapsed[group.name]')
|
||||||
span Manage connections
|
.fa.fa-fw.fa-chevron-down(*ngIf='!groupCollapsed[group.name]')
|
||||||
|
.ml-2 {{group.name || "Ungrouped"}}
|
||||||
//.modal-footer
|
ng-container(*ngIf='!groupCollapsed[group.name]')
|
||||||
button.btn.btn-outline-primary((click)='close()') Cancel
|
.list-group-item.list-group-item-action.pl-5.d-flex.align-items-center(
|
||||||
|
*ngFor='let connection of group.connections',
|
||||||
|
(click)='connect(connection)'
|
||||||
|
) {{connection.name}}
|
||||||
|
5
terminus-ssh/src/components/sshModal.component.scss
Normal file
5
terminus-ssh/src/components/sshModal.component.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.list-group.connections-list {
|
||||||
|
display: block;
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
@@ -4,16 +4,19 @@ import { ToastrService } from 'ngx-toastr'
|
|||||||
import { ConfigService, AppService } from 'terminus-core'
|
import { ConfigService, AppService } from 'terminus-core'
|
||||||
import { SettingsTabComponent } from 'terminus-settings'
|
import { SettingsTabComponent } from 'terminus-settings'
|
||||||
import { SSHService } from '../services/ssh.service'
|
import { SSHService } from '../services/ssh.service'
|
||||||
import { SSHConnection } from '../api'
|
import { SSHConnection, ISSHConnectionGroup } from '../api'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: require('./sshModal.component.pug'),
|
template: require('./sshModal.component.pug'),
|
||||||
//styles: [require('./sshModal.component.scss')],
|
styles: [require('./sshModal.component.scss')],
|
||||||
})
|
})
|
||||||
export class SSHModalComponent {
|
export class SSHModalComponent {
|
||||||
connections: SSHConnection[]
|
connections: SSHConnection[]
|
||||||
|
childFolders: ISSHConnectionGroup[]
|
||||||
quickTarget: string
|
quickTarget: string
|
||||||
lastConnection: SSHConnection
|
lastConnection: SSHConnection
|
||||||
|
childGroups: ISSHConnectionGroup[]
|
||||||
|
groupCollapsed: {[id: string]: boolean} = {}
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public modalInstance: NgbActiveModal,
|
public modalInstance: NgbActiveModal,
|
||||||
@@ -28,6 +31,7 @@ export class SSHModalComponent {
|
|||||||
if (window.localStorage.lastConnection) {
|
if (window.localStorage.lastConnection) {
|
||||||
this.lastConnection = JSON.parse(window.localStorage.lastConnection)
|
this.lastConnection = JSON.parse(window.localStorage.lastConnection)
|
||||||
}
|
}
|
||||||
|
this.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
quickConnect () {
|
quickConnect () {
|
||||||
@@ -65,4 +69,26 @@ export class SSHModalComponent {
|
|||||||
close () {
|
close () {
|
||||||
this.modalInstance.close()
|
this.modalInstance.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refresh () {
|
||||||
|
this.childGroups = []
|
||||||
|
|
||||||
|
let connections = this.connections
|
||||||
|
if (this.quickTarget) {
|
||||||
|
connections = connections.filter(connection => (connection.name + connection.group).toLowerCase().includes(this.quickTarget))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let connection of connections) {
|
||||||
|
connection.group = connection.group || null
|
||||||
|
let group = this.childGroups.find(x => x.name === connection.group)
|
||||||
|
if (!group) {
|
||||||
|
group = {
|
||||||
|
name: connection.group,
|
||||||
|
connections: [],
|
||||||
|
}
|
||||||
|
this.childGroups.push(group)
|
||||||
|
}
|
||||||
|
group.connections.push(connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,25 @@
|
|||||||
h3 Connections
|
h3 Connections
|
||||||
|
|
||||||
.list-group.mt-3.mb-3
|
.list-group.mt-3.mb-3
|
||||||
.list-group-item(*ngFor='let connection of connections')
|
ng-container(*ngFor='let group of childGroups')
|
||||||
.d-flex.w-100
|
.list-group-item.list-group-item-action.d-flex.align-items-center((click)='groupCollapsed[group.name] = !groupCollapsed[group.name]')
|
||||||
.mr-auto
|
.fa.fa-fw.fa-chevron-right(*ngIf='groupCollapsed[group.name]')
|
||||||
div
|
.fa.fa-fw.fa-chevron-down(*ngIf='!groupCollapsed[group.name]')
|
||||||
span {{connection.name}}
|
span.ml-3.mr-auto {{group.name || "Ungrouped"}}
|
||||||
.text-muted {{connection.host}}
|
button.btn.btn-outline-info.ml-2((click)='editGroup(group)')
|
||||||
button.btn.btn-outline-info.ml-2((click)='editConnection(connection)')
|
|
||||||
i.fa.fa-pencil
|
i.fa.fa-pencil
|
||||||
button.btn.btn-outline-danger.ml-1((click)='deleteConnection(connection)')
|
button.btn.btn-outline-danger.ml-1((click)='deleteGroup(group)')
|
||||||
i.fa.fa-trash-o
|
i.fa.fa-trash-o
|
||||||
|
ng-container(*ngIf='!groupCollapsed[group.name]')
|
||||||
|
.list-group-item.pl-5.d-flex.align-items-center(*ngFor='let connection of group.connections')
|
||||||
|
.mr-auto
|
||||||
|
div {{connection.name}}
|
||||||
|
.text-muted {{connection.host}}
|
||||||
|
button.btn.btn-outline-info.ml-2((click)='editConnection(connection)')
|
||||||
|
i.fa.fa-pencil
|
||||||
|
button.btn.btn-outline-danger.ml-1((click)='deleteConnection(connection)')
|
||||||
|
i.fa.fa-trash-o
|
||||||
|
|
||||||
button.btn.btn-outline-primary((click)='createConnection()') Add connection
|
button.btn.btn-outline-primary((click)='createConnection()')
|
||||||
|
div.fa.fa-fw.fa-globe
|
||||||
|
span.ml-2 Add connection
|
||||||
|
@@ -1,20 +1,24 @@
|
|||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ConfigService } from 'terminus-core'
|
import { ConfigService } from 'terminus-core'
|
||||||
import { SSHConnection } from '../api'
|
import { SSHConnection, ISSHConnectionGroup } from '../api'
|
||||||
import { EditConnectionModalComponent } from './editConnectionModal.component'
|
import { EditConnectionModalComponent } from './editConnectionModal.component'
|
||||||
|
import { PromptModalComponent } from './promptModal.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: require('./sshSettingsTab.component.pug'),
|
template: require('./sshSettingsTab.component.pug'),
|
||||||
})
|
})
|
||||||
export class SSHSettingsTabComponent {
|
export class SSHSettingsTabComponent {
|
||||||
connections: SSHConnection[]
|
connections: SSHConnection[]
|
||||||
|
childGroups: ISSHConnectionGroup[]
|
||||||
|
groupCollapsed: {[id: string]: boolean} = {}
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
private ngbModal: NgbModal,
|
private ngbModal: NgbModal,
|
||||||
) {
|
) {
|
||||||
this.connections = this.config.store.ssh.connections
|
this.connections = this.config.store.ssh.connections
|
||||||
|
this.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
createConnection () {
|
createConnection () {
|
||||||
@@ -24,12 +28,14 @@ export class SSHSettingsTabComponent {
|
|||||||
port: 22,
|
port: 22,
|
||||||
user: 'root',
|
user: 'root',
|
||||||
}
|
}
|
||||||
|
|
||||||
let modal = this.ngbModal.open(EditConnectionModalComponent)
|
let modal = this.ngbModal.open(EditConnectionModalComponent)
|
||||||
modal.componentInstance.connection = connection
|
modal.componentInstance.connection = connection
|
||||||
modal.result.then(result => {
|
modal.result.then(result => {
|
||||||
this.connections.push(result)
|
this.connections.push(result)
|
||||||
this.config.store.ssh.connections = this.connections
|
this.config.store.ssh.connections = this.connections
|
||||||
this.config.save()
|
this.config.save()
|
||||||
|
this.refresh()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +45,7 @@ export class SSHSettingsTabComponent {
|
|||||||
modal.result.then(result => {
|
modal.result.then(result => {
|
||||||
Object.assign(connection, result)
|
Object.assign(connection, result)
|
||||||
this.config.save()
|
this.config.save()
|
||||||
|
this.refresh()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +54,49 @@ export class SSHSettingsTabComponent {
|
|||||||
this.connections = this.connections.filter(x => x !== connection)
|
this.connections = this.connections.filter(x => x !== connection)
|
||||||
this.config.store.ssh.connections = this.connections
|
this.config.store.ssh.connections = this.connections
|
||||||
this.config.save()
|
this.config.save()
|
||||||
|
this.refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editGroup (group: ISSHConnectionGroup) {
|
||||||
|
let modal = this.ngbModal.open(PromptModalComponent)
|
||||||
|
modal.componentInstance.prompt = 'New group name'
|
||||||
|
modal.componentInstance.value = group.name
|
||||||
|
modal.result.then(result => {
|
||||||
|
if (result) {
|
||||||
|
for (let connection of this.connections.filter(x => x.group === group.name)) {
|
||||||
|
connection.group = result
|
||||||
|
}
|
||||||
|
this.config.save()
|
||||||
|
this.refresh()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteGroup (group: ISSHConnectionGroup) {
|
||||||
|
if (confirm(`Delete "${group}"?`)) {
|
||||||
|
for (let connection of this.connections.filter(x => x.group === group.name)) {
|
||||||
|
connection.group = null
|
||||||
|
}
|
||||||
|
this.config.save()
|
||||||
|
this.refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh () {
|
||||||
|
this.childGroups = []
|
||||||
|
|
||||||
|
for (let connection of this.connections) {
|
||||||
|
connection.group = connection.group || null
|
||||||
|
let group = this.childGroups.find(x => x.name === connection.group)
|
||||||
|
if (!group) {
|
||||||
|
group = {
|
||||||
|
name: connection.group,
|
||||||
|
connections: [],
|
||||||
|
}
|
||||||
|
this.childGroups.push(group)
|
||||||
|
}
|
||||||
|
group.connections.push(connection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import { FormsModule } from '@angular/forms'
|
|||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ToastrModule } from 'ngx-toastr'
|
import { ToastrModule } from 'ngx-toastr'
|
||||||
import { ToolbarButtonProvider, ConfigProvider } from 'terminus-core'
|
import { ToolbarButtonProvider, ConfigProvider } from 'terminus-core'
|
||||||
|
import TerminusCoreModule from 'terminus-core'
|
||||||
import { SettingsTabProvider } from 'terminus-settings'
|
import { SettingsTabProvider } from 'terminus-settings'
|
||||||
|
|
||||||
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
|
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
|
||||||
@@ -23,6 +24,7 @@ import { SSHSettingsTabProvider } from './settings'
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ToastrModule,
|
ToastrModule,
|
||||||
|
TerminusCoreModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
PasswordStorageService,
|
PasswordStorageService,
|
||||||
|
@@ -55,7 +55,7 @@ export class SSHService {
|
|||||||
modal.componentInstance.password = true
|
modal.componentInstance.password = true
|
||||||
try {
|
try {
|
||||||
privateKeyPassphrase = await modal.result
|
privateKeyPassphrase = await modal.result
|
||||||
} catch (_err) { }
|
} catch (_err) { } // tslint:disable-line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,6 +110,9 @@ export class SSHService {
|
|||||||
tryKeyboard: true,
|
tryKeyboard: true,
|
||||||
agent,
|
agent,
|
||||||
agentForward: !!agent,
|
agentForward: !!agent,
|
||||||
|
keepaliveInterval: connection.keepaliveInterval,
|
||||||
|
keepaliveCountMax: connection.keepaliveCountMax,
|
||||||
|
readyTimeout: connection.readyTimeout,
|
||||||
})
|
})
|
||||||
|
|
||||||
let keychainPasswordUsed = false
|
let keychainPasswordUsed = false
|
||||||
@@ -148,7 +151,7 @@ export class SSHService {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
let session = new SSHSession(shell)
|
let session = new SSHSession(shell, connection)
|
||||||
|
|
||||||
return this.zone.run(() => this.app.openNewTab(
|
return this.zone.run(() => this.app.openNewTab(
|
||||||
TerminalTabComponent,
|
TerminalTabComponent,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "terminus-terminal",
|
"name": "terminus-terminal",
|
||||||
"version": "1.0.0-alpha.48",
|
"version": "1.0.0-alpha.55",
|
||||||
"description": "Terminus' terminal emulation core",
|
"description": "Terminus' terminal emulation core",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"terminus-builtin-plugin"
|
"terminus-builtin-plugin"
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"@types/async-lock": "0.0.19",
|
"@types/async-lock": "0.0.19",
|
||||||
"async-lock": "^1.0.0",
|
"async-lock": "^1.0.0",
|
||||||
"font-manager": "0.3.0",
|
"font-manager": "0.3.0",
|
||||||
"hterm-umdjs": "1.1.3",
|
"hterm-umdjs": "1.4.1",
|
||||||
"mz": "^2.6.0",
|
"mz": "^2.6.0",
|
||||||
"node-pty-tmp": "0.7.2",
|
"node-pty-tmp": "0.7.2",
|
||||||
"ps-node": "^0.1.6",
|
"ps-node": "^0.1.6",
|
||||||
|
@@ -15,25 +15,6 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
hotkeys: HotkeysService,
|
hotkeys: HotkeysService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
|
|
||||||
if (hotkey === 'new-tab') {
|
|
||||||
this.terminal.openTab()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
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) {
|
if (!electron.remote.process.env.DEV) {
|
||||||
setImmediate(async () => {
|
setImmediate(async () => {
|
||||||
let argv: string[] = electron.remote.process.argv
|
let argv: string[] = electron.remote.process.argv
|
||||||
|
@@ -199,7 +199,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.contextMenu = this.electron.remote.Menu.buildFromTemplate([
|
this.contextMenu = [
|
||||||
{
|
{
|
||||||
label: 'New terminal',
|
label: 'New terminal',
|
||||||
click: () => {
|
click: () => {
|
||||||
@@ -227,7 +227,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
])
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
detachTermContainerHandlers () {
|
detachTermContainerHandlers () {
|
||||||
@@ -249,9 +249,7 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
if (event.type === 'mousedown') {
|
if (event.type === 'mousedown') {
|
||||||
if (event.which === 3) {
|
if (event.which === 3) {
|
||||||
if (this.config.store.terminal.rightClick === 'menu') {
|
if (this.config.store.terminal.rightClick === 'menu') {
|
||||||
this.contextMenu.popup({
|
this.hostApp.popupContextMenu(this.contextMenu)
|
||||||
async: true,
|
|
||||||
})
|
|
||||||
} else if (this.config.store.terminal.rightClick === 'paste') {
|
} else if (this.config.store.terminal.rightClick === 'paste') {
|
||||||
this.paste()
|
this.paste()
|
||||||
}
|
}
|
||||||
@@ -292,14 +290,17 @@ export class TerminalTabComponent extends BaseTabComponent {
|
|||||||
|
|
||||||
sendInput (data: string) {
|
sendInput (data: string) {
|
||||||
this.session.write(data)
|
this.session.write(data)
|
||||||
|
this.frontend.scrollToBottom()
|
||||||
}
|
}
|
||||||
|
|
||||||
write (data: string) {
|
write (data: string) {
|
||||||
let percentageMatch = /(\d+(\.\d+)?)%/.exec(data)
|
let percentageMatch = /(^|[^\d])(\d+(\.\d+)?)%([^\d]|$)/.exec(data)
|
||||||
if (percentageMatch) {
|
if (percentageMatch) {
|
||||||
let percentage = percentageMatch[2] ? parseFloat(percentageMatch[1]) : parseInt(percentageMatch[1])
|
let percentage = percentageMatch[3] ? parseFloat(percentageMatch[2]) : parseInt(percentageMatch[2])
|
||||||
this.setProgress(percentage)
|
if (percentage > 0 && percentage <= 100) {
|
||||||
console.log('Detected progress:', percentage)
|
this.setProgress(percentage)
|
||||||
|
console.log('Detected progress:', percentage)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setProgress(null)
|
this.setProgress(null)
|
||||||
}
|
}
|
||||||
|
@@ -52,6 +52,7 @@ export abstract class Frontend {
|
|||||||
abstract write (data: string): void
|
abstract write (data: string): void
|
||||||
abstract clear (): void
|
abstract clear (): void
|
||||||
abstract visualBell (): void
|
abstract visualBell (): void
|
||||||
|
abstract scrollToBottom (): void
|
||||||
|
|
||||||
abstract configure (configStore: any): void
|
abstract configure (configStore: any): void
|
||||||
abstract setZoom (zoom: number): void
|
abstract setZoom (zoom: number): void
|
||||||
|
@@ -8,6 +8,7 @@ export class HTermFrontend extends Frontend {
|
|||||||
private initialized = false
|
private initialized = false
|
||||||
private configuredFontSize = 0
|
private configuredFontSize = 0
|
||||||
private configuredLinePadding = 0
|
private configuredLinePadding = 0
|
||||||
|
private configuredBackgroundColor = 'transparent'
|
||||||
private zoom = 0
|
private zoom = 0
|
||||||
|
|
||||||
attach (host: HTMLElement) {
|
attach (host: HTMLElement) {
|
||||||
@@ -87,6 +88,8 @@ export class HTermFrontend extends Frontend {
|
|||||||
preferenceManager.set('background-color', 'transparent')
|
preferenceManager.set('background-color', 'transparent')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.configuredBackgroundColor = preferenceManager.get('background-color')
|
||||||
|
|
||||||
if (config.terminal.colorScheme.colors) {
|
if (config.terminal.colorScheme.colors) {
|
||||||
preferenceManager.set(
|
preferenceManager.set(
|
||||||
'color-palette-overrides',
|
'color-palette-overrides',
|
||||||
@@ -134,13 +137,16 @@ export class HTermFrontend extends Frontend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visualBell (): void {
|
visualBell (): void {
|
||||||
const color = preferenceManager.get('background-color')
|
|
||||||
preferenceManager.set('background-color', 'rgba(128,128,128,.25)')
|
preferenceManager.set('background-color', 'rgba(128,128,128,.25)')
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
preferenceManager.set('background-color', color)
|
preferenceManager.set('background-color', this.configuredBackgroundColor)
|
||||||
}, 125)
|
}, 125)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollToBottom (): void {
|
||||||
|
this.term.scrollEnd()
|
||||||
|
}
|
||||||
|
|
||||||
private setFontSize () {
|
private setFontSize () {
|
||||||
preferenceManager.set('font-size', this.configuredFontSize * Math.pow(1.1, this.zoom))
|
preferenceManager.set('font-size', this.configuredFontSize * Math.pow(1.1, this.zoom))
|
||||||
}
|
}
|
||||||
|
@@ -77,6 +77,10 @@ export class XTermFrontend extends Frontend {
|
|||||||
(this.xterm as any).bell()
|
(this.xterm as any).bell()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollToBottom (): void {
|
||||||
|
this.xterm.scrollToBottom()
|
||||||
|
}
|
||||||
|
|
||||||
configure (config: any): void {
|
configure (config: any): void {
|
||||||
this.xterm.setOption('fontFamily', `"${config.terminal.font}", "monospace-fallback", monospace`)
|
this.xterm.setOption('fontFamily', `"${config.terminal.font}", "monospace-fallback", monospace`)
|
||||||
this.xterm.setOption('bellStyle', config.terminal.bell)
|
this.xterm.setOption('bellStyle', config.terminal.bell)
|
||||||
|
@@ -1,9 +1,12 @@
|
|||||||
|
import * as fs from 'mz/fs'
|
||||||
|
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { BrowserModule } from '@angular/platform-browser'
|
import { BrowserModule } from '@angular/platform-browser'
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ToastrModule } from 'ngx-toastr'
|
import { ToastrModule } from 'ngx-toastr'
|
||||||
import TerminusCorePlugin from 'terminus-core'
|
import TerminusCorePlugin from 'terminus-core'
|
||||||
|
import { HostAppService } from 'terminus-core'
|
||||||
|
|
||||||
import { ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, AppService, ConfigService } from 'terminus-core'
|
import { ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, AppService, ConfigService } from 'terminus-core'
|
||||||
import { SettingsTabProvider } from 'terminus-settings'
|
import { SettingsTabProvider } from 'terminus-settings'
|
||||||
@@ -35,6 +38,8 @@ import { GitBashShellProvider } from './shells/gitBash'
|
|||||||
import { LinuxDefaultShellProvider } from './shells/linuxDefault'
|
import { LinuxDefaultShellProvider } from './shells/linuxDefault'
|
||||||
import { MacOSDefaultShellProvider } from './shells/macDefault'
|
import { MacOSDefaultShellProvider } from './shells/macDefault'
|
||||||
import { POSIXShellsProvider } from './shells/posix'
|
import { POSIXShellsProvider } from './shells/posix'
|
||||||
|
import { PowerShellCoreShellProvider } from './shells/powershellCore'
|
||||||
|
import { WindowsDefaultShellProvider } from './shells/winDefault'
|
||||||
import { WindowsStockShellsProvider } from './shells/windowsStock'
|
import { WindowsStockShellsProvider } from './shells/windowsStock'
|
||||||
import { WSLShellProvider } from './shells/wsl'
|
import { WSLShellProvider } from './shells/wsl'
|
||||||
|
|
||||||
@@ -64,16 +69,23 @@ import { hterm } from './hterm'
|
|||||||
{ provide: SessionPersistenceProvider, useClass: ScreenPersistenceProvider, multi: true },
|
{ provide: SessionPersistenceProvider, useClass: ScreenPersistenceProvider, multi: true },
|
||||||
{ provide: SessionPersistenceProvider, useClass: TMuxPersistenceProvider, multi: true },
|
{ provide: SessionPersistenceProvider, useClass: TMuxPersistenceProvider, multi: true },
|
||||||
|
|
||||||
{ provide: ShellProvider, useClass: CmderShellProvider, multi: true },
|
{ provide: ShellProvider, useClass: WindowsDefaultShellProvider, multi: true },
|
||||||
{ provide: ShellProvider, useClass: WindowsStockShellsProvider, multi: true },
|
|
||||||
{ provide: ShellProvider, useClass: MacOSDefaultShellProvider, multi: true },
|
{ provide: ShellProvider, useClass: MacOSDefaultShellProvider, multi: true },
|
||||||
{ provide: ShellProvider, useClass: LinuxDefaultShellProvider, 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: CustomShellProvider, multi: true },
|
||||||
{ provide: ShellProvider, useClass: Cygwin32ShellProvider, multi: true },
|
{ provide: ShellProvider, useClass: Cygwin32ShellProvider, multi: true },
|
||||||
{ provide: ShellProvider, useClass: Cygwin64ShellProvider, multi: true },
|
{ provide: ShellProvider, useClass: Cygwin64ShellProvider, multi: true },
|
||||||
{ provide: ShellProvider, useClass: GitBashShellProvider, multi: true },
|
{ provide: ShellProvider, useClass: GitBashShellProvider, multi: true },
|
||||||
{ provide: ShellProvider, useClass: POSIXShellsProvider, multi: true },
|
{ provide: ShellProvider, useClass: POSIXShellsProvider, multi: true },
|
||||||
|
{ provide: ShellProvider, useClass: PowerShellCoreShellProvider, multi: true },
|
||||||
{ provide: ShellProvider, useClass: WSLShellProvider, multi: true },
|
{ provide: ShellProvider, useClass: WSLShellProvider, multi: true },
|
||||||
|
|
||||||
|
// For WindowsDefaultShellProvider
|
||||||
|
PowerShellCoreShellProvider,
|
||||||
|
WSLShellProvider,
|
||||||
|
WindowsStockShellsProvider
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
TerminalTabComponent,
|
TerminalTabComponent,
|
||||||
@@ -91,6 +103,7 @@ export default class TerminalModule {
|
|||||||
config: ConfigService,
|
config: ConfigService,
|
||||||
hotkeys: HotkeysService,
|
hotkeys: HotkeysService,
|
||||||
terminal: TerminalService,
|
terminal: TerminalService,
|
||||||
|
hostApp: HostAppService,
|
||||||
) {
|
) {
|
||||||
let events = [
|
let events = [
|
||||||
{
|
{
|
||||||
@@ -121,6 +134,36 @@ export default class TerminalModule {
|
|||||||
terminal.openTab()
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -38,9 +38,9 @@ export class TerminalService {
|
|||||||
if (!cwd) {
|
if (!cwd) {
|
||||||
if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) {
|
if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) {
|
||||||
cwd = await this.app.activeTab.session.getWorkingDirectory()
|
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) {
|
if (!shell) {
|
||||||
let shells = await this.shells$.toPromise()
|
let shells = await this.shells$.toPromise()
|
||||||
|
@@ -4,7 +4,6 @@ import { HostAppService, Platform } from 'terminus-core'
|
|||||||
|
|
||||||
import { ShellProvider, IShell } from '../api'
|
import { ShellProvider, IShell } from '../api'
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CmderShellProvider extends ShellProvider {
|
export class CmderShellProvider extends ShellProvider {
|
||||||
constructor (
|
constructor (
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user