started separating terminus-electron and terminus-web

This commit is contained in:
Eugene Pankov
2021-05-24 17:48:12 +02:00
parent c19e131d8c
commit 012986dc7e
94 changed files with 1899 additions and 972 deletions

View File

@@ -42,6 +42,18 @@ updates:
interval: daily interval: daily
time: "04:00" time: "04:00"
open-pull-requests-limit: 20 open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-electron"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-web"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm - package-ecosystem: npm
directory: "/terminus-plugin-manager" directory: "/terminus-plugin-manager"
schedule: schedule:

View File

@@ -42,10 +42,12 @@ terminus
├─ scripts # Maintenance scripts ├─ scripts # Maintenance scripts
├─ terminus-community-color-schemes # Plugin that provides color schemes ├─ terminus-community-color-schemes # Plugin that provides color schemes
├─ terminus-core # Plugin that provides base UI and tab management ├─ terminus-core # Plugin that provides base UI and tab management
├─ terminus-electron # Plugin that provides Electron-specific functions
└─ terminus-local # Plugin that provides local shells and profiles └─ terminus-local # Plugin that provides local shells and profiles
├─ terminus-plugin-manager # Plugin that installs other plugins ├─ terminus-plugin-manager # Plugin that installs other plugins
├─ terminus-settings # Plugin that provides the settings tab ├─ terminus-settings # Plugin that provides the settings tab
└─ terminus-terminal # Plugin that provides terminal tabs └─ terminus-terminal # Plugin that provides terminal tabs
├─ terminus-web # Plugin that provides web-specific functions
``` ```
# Plugin layout # Plugin layout

View File

@@ -1,4 +0,0 @@
export interface BootstrapData {
config: Record<string, any>
executable: string
}

View File

@@ -1,5 +1,5 @@
doctype html doctype html
html html.terminus
head head
meta(charset='UTF-8') meta(charset='UTF-8')
base(href='index.html') base(href='index.html')

View File

@@ -7,6 +7,12 @@ import { Window, WindowOptions } from './window'
import { pluginManager } from './pluginManager' import { pluginManager } from './pluginManager'
import { PTYManager } from './pty' import { PTYManager } from './pty'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch (_) { }
export class Application { export class Application {
private tray?: Tray private tray?: Tray
private ptyManager = new PTYManager() private ptyManager = new PTYManager()
@@ -14,6 +20,7 @@ export class Application {
constructor () { constructor () {
remote.initialize() remote.initialize()
this.useBuiltinGraphics()
this.ptyManager.init(this) this.ptyManager.init(this)
ipcMain.on('app:config-change', (_event, config) => { ipcMain.on('app:config-change', (_event, config) => {
@@ -161,6 +168,16 @@ export class Application {
this.windows[this.windows.length - 1].passCliArguments(argv, cwd, true) this.windows[this.windows.length - 1].passCliArguments(argv, cwd, true)
} }
private useBuiltinGraphics (): void {
if (process.platform === 'win32') {
const keyPath = 'SOFTWARE\\Microsoft\\DirectX\\UserGpuPreferences'
const valueName = app.getPath('exe')
if (!wnr.getRegistryValue(wnr.HK.CU, keyPath, valueName)) {
wnr.setRegistryValue(wnr.HK.CU, keyPath, valueName, wnr.REG.SZ, 'GpuPreference=1;')
}
}
}
private setupMenu () { private setupMenu () {
const template: MenuItemConstructorOptions[] = [ const template: MenuItemConstructorOptions[] = [
{ {

View File

@@ -122,7 +122,7 @@ export class Window {
} }
}) })
this.window.loadURL(`file://${app.getAppPath()}/dist/index.html?${this.window.id}`, { extraHeaders: 'pragma: no-cache\n' }) this.window.loadURL(`file://${app.getAppPath()}/dist/index.html`, { extraHeaders: 'pragma: no-cache\n' })
this.window.webContents.setVisualZoomLevelLimits(1, 1) this.window.webContents.setVisualZoomLevelLimits(1, 1)
this.window.webContents.setZoomFactor(1) this.window.webContents.setZoomFactor(1)
@@ -297,6 +297,8 @@ export class Window {
this.window.webContents.send('start', { this.window.webContents.send('start', {
config: this.configStore, config: this.configStore,
executable: app.getPath('exe'), executable: app.getPath('exe'),
windowID: this.window.id,
isFirstWindow: this.window.id === 1,
}) })
}) })

View File

@@ -57,6 +57,7 @@
"peerDependencies": { "peerDependencies": {
"terminus-community-color-schemes": "*", "terminus-community-color-schemes": "*",
"terminus-core": "*", "terminus-core": "*",
"terminus-electron": "*",
"terminus-local": "*", "terminus-local": "*",
"terminus-plugin-manager": "*", "terminus-plugin-manager": "*",
"terminus-serial": "*", "terminus-serial": "*",

View File

@@ -16,10 +16,15 @@ export function getRootModule (plugins: any[]) {
extendedTimeOut: 1000, extendedTimeOut: 1000,
}), }),
] ]
const bootstrap = [ const bootstrap = [
...plugins.filter(x => x.bootstrap).map(x => x.bootstrap), ...plugins.filter(x => x.bootstrap).map(x => x.bootstrap),
] ]
const providers = [
...plugins.filter(x => x.providers).map(x => x.providers),
].flat()
if (bootstrap.length === 0) { if (bootstrap.length === 0) {
throw new Error('Did not find any bootstrap components. Are there any plugins installed?') throw new Error('Did not find any bootstrap components. Are there any plugins installed?')
} }
@@ -27,6 +32,7 @@ export function getRootModule (plugins: any[]) {
@NgModule({ @NgModule({
imports, imports,
bootstrap, bootstrap,
providers,
}) class RootModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class }) class RootModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
return RootModule return RootModule

42
app/src/entry-web.ts Normal file
View File

@@ -0,0 +1,42 @@
import 'zone.js'
import 'core-js/proposals/reflect-metadata'
import 'core-js/features/array/flat'
import 'rxjs'
import './global.scss'
import './toastr.scss'
import { enableProdMode, NgModuleRef, ApplicationRef } from '@angular/core'
import { enableDebugTools } from '@angular/platform-browser'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { getRootModule } from './app.module'
import { BootstrapData, BOOTSTRAP_DATA } from '../../terminus-core/src/api/mainProcess'
window['bootstrapTerminus'] = async function bootstrap (packageModules: any[], bootstrapData: BootstrapData, debugMode = false): Promise<NgModuleRef<any>> {
const pluginModules = []
for (const packageModule of packageModules) {
const pluginModule = packageModule.default.forRoot ? packageModule.default.forRoot() : packageModule.default
pluginModule.pluginName = packageModule.pluginName
pluginModule.bootstrap = packageModule.bootstrap
pluginModules.push(pluginModule)
}
if (!debugMode) {
enableProdMode()
}
const module = getRootModule(pluginModules)
window['rootModule'] = module
const moduleRef = await platformBrowserDynamic([
{ provide: BOOTSTRAP_DATA, useValue: bootstrapData },
]).bootstrapModule(module)
if (debugMode) {
const applicationRef = moduleRef.injector.get(ApplicationRef)
const componentRef = applicationRef.components[0]
enableDebugTools(componentRef)
}
return moduleRef
}

View File

@@ -12,7 +12,7 @@ import { ipcRenderer } from 'electron'
import { getRootModule } from './app.module' import { getRootModule } from './app.module'
import { findPlugins, loadPlugins, PluginInfo } from './plugins' import { findPlugins, loadPlugins, PluginInfo } from './plugins'
import { BootstrapData } from '../common' import { BootstrapData, BOOTSTRAP_DATA } from '../../terminus-core/src/api/mainProcess'
// Always land on the start view // Always land on the start view
location.hash = '' location.hash = ''
@@ -39,11 +39,9 @@ async function bootstrap (plugins: PluginInfo[], bootstrapData: BootstrapData, s
}) })
const module = getRootModule(pluginModules) const module = getRootModule(pluginModules)
window['rootModule'] = module window['rootModule'] = module
const moduleRef = await platformBrowserDynamic().bootstrapModule(module, { const moduleRef = await platformBrowserDynamic([
providers: [ { provide: BOOTSTRAP_DATA, useValue: bootstrapData },
{ provide: 'bootstrapData', useValue: bootstrapData }, ]).bootstrapModule(module)
],
})
if (process.env.TERMINUS_DEV) { if (process.env.TERMINUS_DEV) {
const applicationRef = moduleRef.injector.get(ApplicationRef) const applicationRef = moduleRef.injector.get(ApplicationRef)
const componentRef = applicationRef.components[0] const componentRef = applicationRef.components[0]
@@ -54,12 +52,12 @@ async function bootstrap (plugins: PluginInfo[], bootstrapData: BootstrapData, s
ipcRenderer.once('start', async (_$event, bootstrapData: BootstrapData) => { ipcRenderer.once('start', async (_$event, bootstrapData: BootstrapData) => {
console.log('Window bootstrap data:', bootstrapData) console.log('Window bootstrap data:', bootstrapData)
;(window as any).bootstrapData = bootstrapData
let plugins = await findPlugins() let plugins = await findPlugins()
if (bootstrapData.config.pluginBlacklist) { if (bootstrapData.config.pluginBlacklist) {
plugins = plugins.filter(x => !bootstrapData.config.pluginBlacklist.includes(x.name)) plugins = plugins.filter(x => !bootstrapData.config.pluginBlacklist.includes(x.name))
} }
plugins = plugins.filter(x => x.name !== 'web')
console.log('Starting with plugins:', plugins) console.log('Starting with plugins:', plugins)
try { try {
await bootstrap(plugins, bootstrapData) await bootstrap(plugins, bootstrapData)

View File

@@ -9,6 +9,7 @@ module.exports = {
sentry: path.resolve(__dirname, 'lib/sentry.ts'), sentry: path.resolve(__dirname, 'lib/sentry.ts'),
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'),
'bundle-web': path.resolve(__dirname, 'src/entry-web.ts'),
}, },
mode: process.env.TERMINUS_DEV ? 'development' : 'production', mode: process.env.TERMINUS_DEV ? 'development' : 'production',
optimization:{ optimization:{
@@ -41,9 +42,9 @@ module.exports = {
{ {
test: /\.(png|svg)$/, test: /\.(png|svg)$/,
use: { use: {
loader: 'file-loader', loader: 'url-loader',
options: { options: {
name: 'images/[name].[ext]', limit: 999999,
}, },
}, },
}, },
@@ -66,6 +67,7 @@ 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',
'@electron/remote': 'commonjs @electron/remote',
child_process: 'commonjs child_process', child_process: 'commonjs child_process',
electron: 'commonjs electron', electron: 'commonjs electron',
fs: 'commonjs fs', fs: 'commonjs fs',

View File

@@ -51,6 +51,7 @@ module.exports = {
util: 'commonjs util', util: 'commonjs util',
'source-map-support': 'commonjs source-map-support', 'source-map-support': 'commonjs source-map-support',
'windows-swca': 'commonjs windows-swca', 'windows-swca': 'commonjs windows-swca',
'windows-native-registry': 'commonjs windows-native-registry',
'windows-blurbehind': 'commonjs windows-blurbehind', 'windows-blurbehind': 'commonjs windows-blurbehind',
'yargs/yargs': 'commonjs yargs/yargs', 'yargs/yargs': 'commonjs yargs/yargs',
}, },

View File

@@ -66,7 +66,7 @@
"**/graceful-fs": "^4.2.4" "**/graceful-fs": "^4.2.4"
}, },
"scripts": { "scripts": {
"build": "npm run build:typings && 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-local/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 && webpack --color --config terminus-serial/webpack.config.js", "build": "npm run build:typings && 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-local/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 && webpack --color --config terminus-serial/webpack.config.js && webpack --color --config terminus-electron/webpack.config.js && webpack --color --config terminus-web/webpack.config.js",
"build:typings": "node scripts/build-typings.js", "build:typings": "node scripts/build-typings.js",
"watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch", "watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch",
"start": "cross-env TERMINUS_DEV=1 electron app --debug --inspect", "start": "cross-env TERMINUS_DEV=1 electron app --debug --inspect",

View File

@@ -11,6 +11,9 @@ sh.mkdir('-p', target)
fs.writeFileSync(path.join(target, 'package.json'), '{}') fs.writeFileSync(path.join(target, 'package.json'), '{}')
sh.cd(target) sh.cd(target)
vars.builtinPlugins.forEach(plugin => { vars.builtinPlugins.forEach(plugin => {
if (plugin === 'terminus-web') {
continue
}
log.info('install', plugin) log.info('install', plugin)
sh.cp('-r', path.join('..', plugin), '.') sh.cp('-r', path.join('..', plugin), '.')
sh.rm('-rf', path.join(plugin, 'node_modules')) sh.rm('-rf', path.join(plugin, 'node_modules'))

View File

@@ -19,7 +19,9 @@ exports.builtinPlugins = [
'terminus-settings', 'terminus-settings',
'terminus-terminal', 'terminus-terminal',
'terminus-local', 'terminus-local',
'terminus-web',
'terminus-community-color-schemes', 'terminus-community-color-schemes',
'terminus-electron',
'terminus-plugin-manager', 'terminus-plugin-manager',
'terminus-ssh', 'terminus-ssh',
'terminus-serial', 'terminus-serial',

View File

@@ -17,10 +17,7 @@
"author": "Eugene Pankov", "author": "Eugene Pankov",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@electron/remote": "1.1.0",
"@types/js-yaml": "^4.0.0", "@types/js-yaml": "^4.0.0",
"@types/winston": "^2.3.6",
"axios": "^0.21.1",
"bootstrap": "^4.1.3", "bootstrap": "^4.1.3",
"clone-deep": "^4.0.1", "clone-deep": "^4.0.1",
"core-js": "^3.1.2", "core-js": "^3.1.2",
@@ -31,8 +28,7 @@
"ng2-dnd": "^5.0.2", "ng2-dnd": "^5.0.2",
"ngx-perfect-scrollbar": "^10.1.0", "ngx-perfect-scrollbar": "^10.1.0",
"readable-stream": "3.6.0", "readable-stream": "3.6.0",
"uuid": "^8.0.0", "uuid": "^8.0.0"
"winston": "^3.3.3"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/animations": "^9.1.9", "@angular/animations": "^9.1.9",

View File

@@ -10,18 +10,19 @@ export { Theme } from './theme'
export { TabContextMenuItemProvider } from './tabContextMenuProvider' export { TabContextMenuItemProvider } from './tabContextMenuProvider'
export { SelectorOption } from './selector' export { SelectorOption } from './selector'
export { CLIHandler, CLIEvent } from './cli' export { CLIHandler, CLIEvent } from './cli'
export { BootstrapData } from './main-process' export { PlatformService, ClipboardContent } from './platform'
export { MenuItemOptions } from './menu'
export { BootstrapData, BOOTSTRAP_DATA } from './mainProcess'
export { AppService } from '../services/app.service' export { AppService } from '../services/app.service'
export { ConfigService } from '../services/config.service' export { ConfigService } from '../services/config.service'
export { DockingService } from '../services/docking.service' export { DockingService, Screen } from '../services/docking.service'
export { ElectronService } from '../services/electron.service' export { ElectronService } from '../services/electron.service'
export { Logger, LogService } from '../services/log.service' export { Logger, ConsoleLogger, 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, Bounds } from '../services/hostApp.service'
export { NotificationsService } from '../services/notifications.service' export { NotificationsService } from '../services/notifications.service'
export { ShellIntegrationService } from '../services/shellIntegration.service'
export { ThemesService } from '../services/themes.service' export { ThemesService } from '../services/themes.service'
export { TabsService } from '../services/tabs.service' export { TabsService } from '../services/tabs.service'
export { UpdaterService } from '../services/updater.service' export { UpdaterService } from '../services/updater.service'

View File

@@ -1,4 +0,0 @@
export interface BootstrapData {
config: Record<string, any>
executable: string
}

View File

@@ -1,4 +1,8 @@
export const BOOTSTRAP_DATA = 'BOOTSTRAP_DATA'
export interface BootstrapData { export interface BootstrapData {
config: Record<string, any> config: Record<string, any>
executable: string executable: string
isFirstWindow: boolean
windowID: number
} }

View File

@@ -0,0 +1,9 @@
export interface MenuItemOptions {
type?: ('normal' | 'separator' | 'submenu' | 'checkbox' | 'radio')
label?: string
sublabel?: string
enabled?: boolean
checked?: boolean
submenu?: MenuItemOptions[]
click?: () => void
}

View File

@@ -0,0 +1,69 @@
import { MenuItemOptions } from './menu'
/* eslint-disable @typescript-eslint/no-unused-vars */
export interface ClipboardContent {
text: string
html?: string
}
export abstract class PlatformService {
supportsWindowControls = false
abstract setClipboard (content: ClipboardContent): void
abstract loadConfig (): Promise<string>
abstract saveConfig (content: string): Promise<void>
getConfigPath (): string|null {
return null
}
showItemInFolder (path: string): void {
throw new Error('Not implemented')
}
async isProcessRunning (name: string): Promise<boolean> {
return false
}
async installPlugin (name: string, version: string): Promise<void> {
throw new Error('Not implemented')
}
async uninstallPlugin (name: string): Promise<void> {
throw new Error('Not implemented')
}
getWinSCPPath (): string|null {
throw new Error('Not implemented')
}
exec (app: string, argv: string[]): void {
throw new Error('Not implemented')
}
isShellIntegrationSupported (): boolean {
return false
}
async isShellIntegrationInstalled (): Promise<boolean> {
return false
}
async installShellIntegration (): Promise<void> {
throw new Error('Not implemented')
}
async uninstallShellIntegration (): Promise<void> {
throw new Error('Not implemented')
}
openPath (path: string): void {
throw new Error('Not implemented')
}
abstract getOSRelease (): string
abstract getAppVersion (): string
abstract openExternal (url: string): void
abstract listFonts (): Promise<string[]>
abstract popupContextMenu (menu: MenuItemOptions[], event?: MouseEvent): void
}

View File

@@ -1,6 +1,6 @@
import type { MenuItemConstructorOptions } from 'electron'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'
import { TabHeaderComponent } from '../components/tabHeader.component' import { TabHeaderComponent } from '../components/tabHeader.component'
import { MenuItemOptions } from './menu'
/** /**
* Extend to add items to the tab header's context menu * Extend to add items to the tab header's context menu
@@ -8,5 +8,5 @@ import { TabHeaderComponent } from '../components/tabHeader.component'
export abstract class TabContextMenuItemProvider { export abstract class TabContextMenuItemProvider {
weight = 0 weight = 0
abstract getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> abstract getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]>
} }

View File

@@ -3,19 +3,16 @@ import { Component, Inject, Input, HostListener, HostBinding } from '@angular/co
import { trigger, style, animate, transition, state } from '@angular/animations' import { trigger, style, animate, transition, state } from '@angular/animations'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ElectronService } from '../services/electron.service'
import { HostAppService, Platform } from '../services/hostApp.service' import { HostAppService, Platform } from '../services/hostApp.service'
import { HotkeysService } from '../services/hotkeys.service' import { HotkeysService } from '../services/hotkeys.service'
import { Logger, LogService } from '../services/log.service' import { Logger, LogService } from '../services/log.service'
import { ConfigService } from '../services/config.service' import { ConfigService } from '../services/config.service'
import { DockingService } from '../services/docking.service'
import { ThemesService } from '../services/themes.service' import { ThemesService } from '../services/themes.service'
import { UpdaterService } from '../services/updater.service' import { UpdaterService } from '../services/updater.service'
import { TouchbarService } from '../services/touchbar.service'
import { BaseTabComponent } from './baseTab.component' import { BaseTabComponent } from './baseTab.component'
import { SafeModeModalComponent } from './safeModeModal.component' import { SafeModeModalComponent } from './safeModeModal.component'
import { AppService, ToolbarButton, ToolbarButtonProvider } from '../api' import { AppService, PlatformService, ToolbarButton, ToolbarButtonProvider } from '../api'
/** @hidden */ /** @hidden */
@Component({ @Component({
@@ -67,21 +64,19 @@ export class AppRootComponent {
private logger: Logger private logger: Logger
private constructor ( private constructor (
private docking: DockingService,
private hotkeys: HotkeysService, private hotkeys: HotkeysService,
private updater: UpdaterService, private updater: UpdaterService,
private touchbar: TouchbarService,
public hostApp: HostAppService, public hostApp: HostAppService,
public config: ConfigService, public config: ConfigService,
public app: AppService, public app: AppService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[], @Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
electron: ElectronService, platform: PlatformService,
log: LogService, log: LogService,
ngbModal: NgbModal, ngbModal: NgbModal,
_themes: ThemesService, _themes: ThemesService,
) { ) {
this.logger = log.create('main') this.logger = log.create('main')
this.logger.info('v', electron.app.getVersion()) this.logger.info('v', platform.getAppVersion())
this.leftToolbarButtons = this.getToolbarButtons(false) this.leftToolbarButtons = this.getToolbarButtons(false)
this.rightToolbarButtons = this.getToolbarButtons(true) this.rightToolbarButtons = this.getToolbarButtons(true)
@@ -123,11 +118,6 @@ export class AppRootComponent {
} }
}) })
this.docking.dock()
this.hostApp.shown.subscribe(() => {
this.docking.dock()
})
this.hostApp.windowCloseRequest$.subscribe(async () => { this.hostApp.windowCloseRequest$.subscribe(async () => {
this.app.closeWindow() this.app.closeWindow()
}) })
@@ -144,27 +134,8 @@ export class AppRootComponent {
} }
}, 3600 * 12 * 1000) }, 3600 * 12 * 1000)
this.touchbar.update()
this.hostApp.useBuiltinGraphics()
config.changed$.subscribe(() => this.updateVibrancy())
this.updateVibrancy()
let lastProgress: number|null = null
this.app.tabOpened$.subscribe(tab => { this.app.tabOpened$.subscribe(tab => {
this.unsortedTabs.push(tab) this.unsortedTabs.push(tab)
tab.progress$.subscribe(progress => {
if (lastProgress === progress) {
return
}
if (progress !== null) {
this.hostApp.getWindow().setProgressBar(progress / 100.0, { mode: 'normal' })
} else {
this.hostApp.getWindow().setProgressBar(-1, { mode: 'none' })
}
lastProgress = progress
})
this.noTabs = false this.noTabs = false
}) })
@@ -224,9 +195,4 @@ export class AppRootComponent {
.filter(button => (button.weight ?? 0) > 0 === aboveZero) .filter(button => (button.weight ?? 0) > 0 === aboveZero)
.sort((a: ToolbarButton, b: ToolbarButton) => (a.weight ?? 0) - (b.weight ?? 0)) .sort((a: ToolbarButton, b: ToolbarButton) => (a.weight ?? 0) - (b.weight ?? 0))
} }
private updateVibrancy () {
this.hostApp.setVibrancy(this.config.store.appearance.vibrancy, this.config.store.appearance.vibrancyType)
this.hostApp.getWindow().setOpacity(this.config.store.appearance.opacity)
}
} }

View File

@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import type { MenuItemConstructorOptions } from 'electron'
import { Component, Input, Optional, Inject, HostBinding, HostListener, ViewChild, ElementRef, NgZone } from '@angular/core' import { Component, Input, Optional, Inject, HostBinding, HostListener, ViewChild, ElementRef, NgZone } from '@angular/core'
import { SortableComponent } from 'ng2-dnd' import { SortableComponent } from 'ng2-dnd'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@@ -7,11 +6,12 @@ import { TabContextMenuItemProvider } from '../api/tabContextMenuProvider'
import { BaseTabComponent } from './baseTab.component' import { BaseTabComponent } from './baseTab.component'
import { RenameTabModalComponent } from './renameTabModal.component' import { RenameTabModalComponent } from './renameTabModal.component'
import { HotkeysService } from '../services/hotkeys.service' import { HotkeysService } from '../services/hotkeys.service'
import { ElectronService } from '../services/electron.service'
import { AppService } from '../services/app.service' import { AppService } from '../services/app.service'
import { HostAppService, Platform } from '../services/hostApp.service' import { HostAppService, Platform } from '../services/hostApp.service'
import { ConfigService } from '../services/config.service' import { ConfigService } from '../services/config.service'
import { BaseComponent } from './base.component' import { BaseComponent } from './base.component'
import { MenuItemOptions } from '../api/menu'
import { PlatformService } from '../api/platform'
/** @hidden */ /** @hidden */
export interface SortableComponentProxy { export interface SortableComponentProxy {
@@ -34,10 +34,10 @@ export class TabHeaderComponent extends BaseComponent {
private constructor ( private constructor (
public app: AppService, public app: AppService,
public config: ConfigService, public config: ConfigService,
private electron: ElectronService,
private hostApp: HostAppService, private hostApp: HostAppService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
private hotkeys: HotkeysService, private hotkeys: HotkeysService,
private platform: PlatformService,
private zone: NgZone, private zone: NgZone,
@Inject(SortableComponent) private parentDraggable: SortableComponentProxy, @Inject(SortableComponent) private parentDraggable: SortableComponentProxy,
@Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[], @Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
@@ -76,8 +76,8 @@ export class TabHeaderComponent extends BaseComponent {
}).catch(() => null) }).catch(() => null)
} }
async buildContextMenu (): Promise<MenuItemConstructorOptions[]> { async buildContextMenu (): Promise<MenuItemOptions[]> {
let items: MenuItemConstructorOptions[] = [] let items: MenuItemOptions[] = []
for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this.tab, this)))) { for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this.tab, this)))) {
items.push({ type: 'separator' }) items.push({ type: 'separator' })
items = items.concat(section) items = items.concat(section)
@@ -105,16 +105,8 @@ export class TabHeaderComponent extends BaseComponent {
} }
} }
@HostListener('auxclick', ['$event']) async onAuxClick ($event: MouseEvent) { @HostListener('contextmenu', ['$event']) async onContextMenu ($event: MouseEvent) {
if ($event.which === 3) { $event.preventDefault()
$event.preventDefault() this.platform.popupContextMenu(await this.buildContextMenu(), $event)
const contextMenu = this.electron.Menu.buildFromTemplate(await this.buildContextMenu())
contextMenu.popup({
x: $event.pageX,
y: $event.pageY,
})
}
} }
} }

View File

@@ -7,6 +7,7 @@ export class CoreConfigProvider extends ConfigProvider {
[Platform.macOS]: require('./configDefaults.macos.yaml'), [Platform.macOS]: require('./configDefaults.macos.yaml'),
[Platform.Windows]: require('./configDefaults.windows.yaml'), [Platform.Windows]: require('./configDefaults.windows.yaml'),
[Platform.Linux]: require('./configDefaults.linux.yaml'), [Platform.Linux]: require('./configDefaults.linux.yaml'),
[Platform.Web]: require('./configDefaults.windows.yaml'),
} }
defaults = require('./configDefaults.yaml') defaults = require('./configDefaults.yaml')
} }

View File

@@ -1,4 +1,4 @@
import { NgModule, ModuleWithProviders } from '@angular/core' import { NgModule, ModuleWithProviders, APP_INITIALIZER } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser' import { BrowserModule } from '@angular/platform-browser'
import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
@@ -24,7 +24,7 @@ import { WelcomeTabComponent } from './components/welcomeTab.component'
import { AutofocusDirective } from './directives/autofocus.directive' import { AutofocusDirective } from './directives/autofocus.directive'
import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive' import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
import { Theme, CLIHandler, BootstrapData, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider } from './api' import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider } from './api'
import { AppService } from './services/app.service' import { AppService } from './services/app.service'
import { ConfigService } from './services/config.service' import { ConfigService } from './services/config.service'
@@ -38,6 +38,10 @@ import { LastCLIHandler } from './cli'
import 'perfect-scrollbar/css/perfect-scrollbar.css' import 'perfect-scrollbar/css/perfect-scrollbar.css'
import 'ng2-dnd/bundles/style.css' import 'ng2-dnd/bundles/style.css'
function initialize (config: ConfigService) {
return () => config.ready$.toPromise()
}
const PROVIDERS = [ 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 },
@@ -50,6 +54,7 @@ const PROVIDERS = [
{ provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true }, { provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true },
{ provide: CLIHandler, useClass: LastCLIHandler, multi: true }, { provide: CLIHandler, useClass: LastCLIHandler, multi: true },
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } }, { provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
{ provide: APP_INITIALIZER, useFactory: initialize, deps: [ConfigService], multi: true },
] ]
/** @hidden */ /** @hidden */
@@ -110,10 +115,6 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
} }
} }
export function getBootstrapData (): BootstrapData {
return (window as any).bootstrapData
}
export { AppRootComponent as bootstrap } export { AppRootComponent as bootstrap }
export * from './api' export * from './api'

View File

@@ -1,7 +1,7 @@
import { Observable, Subject, AsyncSubject } from 'rxjs' import { Observable, Subject, AsyncSubject } from 'rxjs'
import { takeUntil } from 'rxjs/operators' import { takeUntil } from 'rxjs/operators'
import { Injectable } from '@angular/core' import { Injectable, Inject } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'
@@ -9,6 +9,7 @@ import { SplitTabComponent } from '../components/splitTab.component'
import { SelectorModalComponent } from '../components/selectorModal.component' import { SelectorModalComponent } from '../components/selectorModal.component'
import { SelectorOption } from '../api/selector' import { SelectorOption } from '../api/selector'
import { RecoveryToken } from '../api/tabRecovery' import { RecoveryToken } from '../api/tabRecovery'
import { BootstrapData, BOOTSTRAP_DATA } from '../api/mainProcess'
import { ConfigService } from './config.service' import { ConfigService } from './config.service'
import { HostAppService } from './hostApp.service' import { HostAppService } from './hostApp.service'
@@ -75,6 +76,7 @@ export class AppService {
private tabRecovery: TabRecoveryService, private tabRecovery: TabRecoveryService,
private tabsService: TabsService, private tabsService: TabsService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
@Inject(BOOTSTRAP_DATA) private bootstrapData: BootstrapData,
) { ) {
this.tabsChanged$.subscribe(() => { this.tabsChanged$.subscribe(() => {
this.tabRecovery.saveTabs(this.tabs) this.tabRecovery.saveTabs(this.tabs)
@@ -83,19 +85,18 @@ export class AppService {
this.tabRecovery.saveTabs(this.tabs) this.tabRecovery.saveTabs(this.tabs)
}, 30000) }, 30000)
if (hostApp.getWindow().id === 1) { config.ready$.toPromise().then(async () => {
if (config.store.terminal.recoverTabs) { if (this.bootstrapData.isFirstWindow) {
this.tabRecovery.recoverTabs().then(tabs => { if (config.store.terminal.recoverTabs) {
const tabs = await this.tabRecovery.recoverTabs()
for (const tab of tabs) { for (const tab of tabs) {
this.openNewTabRaw(tab.type, tab.options) this.openNewTabRaw(tab.type, tab.options)
} }
this.tabRecovery.enabled = true }
})
} else {
/** Continue to store the tabs even if the setting is currently off */ /** Continue to store the tabs even if the setting is currently off */
this.tabRecovery.enabled = true this.tabRecovery.enabled = true
} }
} })
hostApp.windowFocused$.subscribe(() => this._activeTab?.emitFocused()) hostApp.windowFocused$.subscribe(() => this._activeTab?.emitFocused())
@@ -118,7 +119,7 @@ export class AppService {
this.tabsChanged.next() this.tabsChanged.next()
this.tabOpened.next(tab) this.tabOpened.next(tab)
if (this.hostApp.getWindow().id === 1) { if (this.bootstrapData.isFirstWindow) {
tab.recoveryStateChangedHint$.subscribe(() => { tab.recoveryStateChangedHint$.subscribe(() => {
this.tabRecovery.saveTabs(this.tabs) this.tabRecovery.saveTabs(this.tabs)
}) })

View File

@@ -1,13 +1,12 @@
import { Observable, Subject } from 'rxjs' import { Observable, Subject, AsyncSubject } from 'rxjs'
import * as yaml from 'js-yaml' import * as yaml from 'js-yaml'
import * as path from 'path'
import * as fs from 'fs'
import { Injectable, Inject } from '@angular/core' import { Injectable, Inject } from '@angular/core'
import { ConfigProvider } from '../api/configProvider' import { ConfigProvider } from '../api/configProvider'
import { ElectronService } from './electron.service' import { PlatformService } from '../api/platform'
import { HostAppService } from './hostApp.service' import { HostAppService } from './hostApp.service'
const deepmerge = require('deepmerge')
const configMerge = (a, b) => require('deepmerge')(a, b, { arrayMerge: (_d, s) => s }) // eslint-disable-line @typescript-eslint/no-var-requires const configMerge = (a, b) => deepmerge(a, b, { arrayMerge: (_d, s) => s }) // eslint-disable-line @typescript-eslint/no-var-requires
function isStructuralMember (v) { function isStructuralMember (v) {
return v instanceof Object && !(v instanceof Array) && return v instanceof Object && !(v instanceof Array) &&
@@ -89,11 +88,10 @@ export class ConfigService {
*/ */
restartRequested: boolean restartRequested: boolean
/** /** Fires once when the config is loaded */
* Full config file path get ready$ (): Observable<void> { return this.ready }
*/
path: string
private ready = new AsyncSubject<void>()
private changed = new Subject<void>() private changed = new Subject<void>()
private _store: any private _store: any
private defaults: any private defaults: any
@@ -103,26 +101,24 @@ export class ConfigService {
/** @hidden */ /** @hidden */
private constructor ( private constructor (
electron: ElectronService,
private hostApp: HostAppService, private hostApp: HostAppService,
private platform: PlatformService,
@Inject(ConfigProvider) private configProviders: ConfigProvider[], @Inject(ConfigProvider) private configProviders: ConfigProvider[],
) { ) {
this.path = path.join(electron.app.getPath('userData'), 'config.yaml')
this.defaults = this.mergeDefaults() this.defaults = this.mergeDefaults()
this.load() this.init()
hostApp.configChangeBroadcast$.subscribe(() => {
this.load()
this.emitChange()
})
} }
mergeDefaults (): unknown { mergeDefaults (): unknown {
const providers = this.configProviders const providers = this.configProviders
return providers.map(provider => { return providers.map(provider => {
let defaults = provider.platformDefaults[this.hostApp.platform] || {} let defaults = provider.platformDefaults[this.hostApp.configPlatform] ?? {}
defaults = configMerge(
defaults,
provider.platformDefaults[this.hostApp.platform] ?? {},
)
if (provider.defaults) { if (provider.defaults) {
defaults = configMerge(defaults, provider.defaults) defaults = configMerge(provider.defaults, defaults)
} }
return defaults return defaults
}).reduce(configMerge) }).reduce(configMerge)
@@ -147,19 +143,20 @@ export class ConfigService {
return cleanup(this.defaults) return cleanup(this.defaults)
} }
load (): void { async load (): Promise<void> {
if (fs.existsSync(this.path)) { const content = await this.platform.loadConfig()
this._store = yaml.load(fs.readFileSync(this.path, 'utf8')) if (content) {
this._store = yaml.load(content)
} else { } else {
this._store = {} this._store = {}
} }
this.store = new ConfigProxy(this._store, this.defaults) this.store = new ConfigProxy(this._store, this.defaults)
} }
save (): void { async save (): Promise<void> {
// Scrub undefined values // Scrub undefined values
this._store = JSON.parse(JSON.stringify(this._store)) const cleanStore = JSON.parse(JSON.stringify(this._store))
fs.writeFileSync(this.path, yaml.dump(this._store), 'utf8') await this.platform.saveConfig(yaml.dump(cleanStore))
this.emitChange() this.emitChange()
this.hostApp.broadcastConfigChange(JSON.parse(JSON.stringify(this.store))) this.hostApp.broadcastConfigChange(JSON.parse(JSON.stringify(this.store)))
} }
@@ -214,6 +211,17 @@ export class ConfigService {
}) })
} }
private async init () {
await this.load()
this.ready.next()
this.ready.complete()
this.hostApp.configChangeBroadcast$.subscribe(() => {
this.load()
this.emitChange()
})
}
private emitChange (): void { private emitChange (): void {
this.changed.next() this.changed.next()
} }

View File

@@ -1,11 +1,19 @@
import type { Display } from 'electron' import type { Display } from 'electron'
import { Injectable } from '@angular/core'
import { ConfigService } from '../services/config.service' import { ConfigService } from '../services/config.service'
import { ElectronService } from '../services/electron.service' import { ElectronService } from '../services/electron.service'
import { HostAppService, Bounds } from '../services/hostApp.service' import { HostAppService, Bounds } from '../services/hostApp.service'
@Injectable({ providedIn: 'root' }) export abstract class Screen {
export class DockingService { id: number
name?: string
}
export abstract class DockingService {
abstract dock (): void
abstract getScreens (): Screen[]
}
export class ElectronDockingService {
/** @hidden */ /** @hidden */
private constructor ( private constructor (
private electron: ElectronService, private electron: ElectronService,
@@ -68,10 +76,6 @@ export class DockingService {
}) })
} }
getCurrentScreen (): Display {
return this.electron.screen.getDisplayNearestPoint(this.electron.screen.getCursorScreenPoint())
}
getScreens (): Display[] { getScreens (): Display[] {
const primaryDisplayID = this.electron.screen.getPrimaryDisplay().id const primaryDisplayID = this.electron.screen.getPrimaryDisplay().id
return this.electron.screen.getAllDisplays().sort((a, b) => return this.electron.screen.getAllDisplays().sort((a, b) =>
@@ -85,6 +89,10 @@ export class DockingService {
}) })
} }
private getCurrentScreen (): Display {
return this.electron.screen.getDisplayNearestPoint(this.electron.screen.getCursorScreenPoint())
}
private repositionWindow () { private repositionWindow () {
const [x, y] = this.hostApp.getWindow().getPosition() const [x, y] = this.hostApp.getWindow().getPosition()
for (const screen of this.electron.screen.getAllDisplays()) { for (const screen of this.electron.screen.getAllDisplays()) {

View File

@@ -1,9 +1,8 @@
import * as os from 'os'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
import { ConfigService } from './config.service'
import * as mixpanel from 'mixpanel' import * as mixpanel from 'mixpanel'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { ConfigService } from './config.service'
import { PlatformService } from '../api'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class HomeBaseService { export class HomeBaseService {
@@ -12,10 +11,10 @@ export class HomeBaseService {
/** @hidden */ /** @hidden */
private constructor ( private constructor (
private electron: ElectronService,
private config: ConfigService, private config: ConfigService,
private platform: PlatformService,
) { ) {
this.appVersion = electron.app.getVersion() this.appVersion = platform.getAppVersion()
if (this.config.store.enableAnalytics && !this.config.store.enableWelcomeTab) { if (this.config.store.enableAnalytics && !this.config.store.enableWelcomeTab) {
this.enableAnalytics() this.enableAnalytics()
@@ -23,12 +22,12 @@ export class HomeBaseService {
} }
openGitHub (): void { openGitHub (): void {
this.electron.shell.openExternal('https://github.com/eugeny/terminus') this.platform.openExternal('https://github.com/eugeny/terminus')
} }
reportBug (): void { reportBug (): void {
let body = `Version: ${this.appVersion}\n` let body = `Version: ${this.appVersion}\n`
body += `Platform: ${os.platform()} ${os.release()}\n` body += `Platform: ${process.platform} ${this.platform.getOSRelease()}\n`
const label = { const label = {
aix: 'OS: IBM AIX', aix: 'OS: IBM AIX',
android: 'OS: Android', android: 'OS: Android',
@@ -38,10 +37,10 @@ export class HomeBaseService {
openbsd: 'OS: OpenBSD', openbsd: 'OS: OpenBSD',
sunos: 'OS: Solaris', sunos: 'OS: Solaris',
win32: 'OS: Windows', win32: 'OS: Windows',
}[os.platform()] }[process.platform]
const plugins = (window as any).installedPlugins.filter(x => !x.isBuiltin).map(x => x.name) const plugins = (window as any).installedPlugins.filter(x => !x.isBuiltin).map(x => x.name)
body += `Plugins: ${plugins.join(', ') || 'none'}\n\n` body += `Plugins: ${plugins.join(', ') || 'none'}\n\n`
this.electron.shell.openExternal(`https://github.com/eugeny/terminus/issues/new?body=${encodeURIComponent(body)}&labels=${label}`) this.platform.openExternal(`https://github.com/eugeny/terminus/issues/new?body=${encodeURIComponent(body)}&labels=${label}`)
} }
enableAnalytics (): void { enableAnalytics (): void {
@@ -60,7 +59,7 @@ export class HomeBaseService {
return { return {
distinct_id: window.localStorage.analyticsUserID, distinct_id: window.localStorage.analyticsUserID,
platform: process.platform, platform: process.platform,
os: os.release(), os: this.platform.getOSRelease(),
version: this.appVersion, version: this.appVersion,
} }
} }

View File

@@ -1,21 +1,17 @@
import type { BrowserWindow, TouchBar, MenuItemConstructorOptions } from 'electron' import type { BrowserWindow, TouchBar } from 'electron'
import { Observable, Subject } from 'rxjs' import { Observable, Subject } from 'rxjs'
import { Injectable, NgZone, EventEmitter, Injector } from '@angular/core' import { Injectable, NgZone, EventEmitter, Injector, Inject } from '@angular/core'
import { ElectronService } from './electron.service' import { ElectronService } from './electron.service'
import { Logger, LogService } from './log.service' import { Logger, LogService } from './log.service'
import { CLIHandler } from '../api/cli' import { CLIHandler } from '../api/cli'
import { BootstrapData, BOOTSTRAP_DATA } from '../api/mainProcess'
import { isWindowsBuild, WIN_BUILD_FLUENT_BG_SUPPORTED } from '../utils' import { isWindowsBuild, WIN_BUILD_FLUENT_BG_SUPPORTED } from '../utils'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch (_) { }
export enum Platform { export enum Platform {
Linux = 'Linux', Linux = 'Linux',
macOS = 'macOS', macOS = 'macOS',
Windows = 'Windows', Windows = 'Windows',
Web = 'Web',
} }
export interface Bounds { export interface Bounds {
@@ -31,6 +27,7 @@ export interface Bounds {
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class HostAppService { export class HostAppService {
platform: Platform platform: Platform
configPlatform: Platform
/** /**
* Fired once the window is visible * Fired once the window is visible
@@ -47,7 +44,6 @@ export class HostAppService {
private displayMetricsChanged = new Subject<void>() private displayMetricsChanged = new Subject<void>()
private displaysChanged = new Subject<void>() private displaysChanged = new Subject<void>()
private logger: Logger private logger: Logger
private windowId: number
/** /**
* Fired when Preferences is selected in the macOS menu * Fired when Preferences is selected in the macOS menu
@@ -75,18 +71,20 @@ export class HostAppService {
private constructor ( private constructor (
private zone: NgZone, private zone: NgZone,
private electron: ElectronService, private electron: ElectronService,
@Inject(BOOTSTRAP_DATA) private bootstrapData: BootstrapData,
injector: Injector, injector: Injector,
log: LogService, log: LogService,
) { ) {
this.logger = log.create('hostApp') this.logger = log.create('hostApp')
this.platform = { this.configPlatform = this.platform = {
win32: Platform.Windows, win32: Platform.Windows,
darwin: Platform.macOS, darwin: Platform.macOS,
linux: Platform.Linux, linux: Platform.Linux,
}[process.platform] }[process.platform]
this.windowId = parseInt(location.search.substring(1)) if (process.env.XWEB) {
this.logger.info('Window ID:', this.windowId) this.platform = Platform.Web
}
electron.ipcRenderer.on('host:preferences-menu', () => this.zone.run(() => this.preferencesMenu.next())) electron.ipcRenderer.on('host:preferences-menu', () => this.zone.run(() => this.preferencesMenu.next()))
@@ -158,7 +156,7 @@ export class HostAppService {
* Returns the current remote [[BrowserWindow]] * Returns the current remote [[BrowserWindow]]
*/ */
getWindow (): BrowserWindow { getWindow (): BrowserWindow {
return this.electron.BrowserWindow.fromId(this.windowId)! return this.electron.BrowserWindow.fromId(this.bootstrapData.windowID)!
} }
newWindow (): void { newWindow (): void {
@@ -202,19 +200,6 @@ export class HostAppService {
this.electron.ipcRenderer.send('window-set-always-on-top', flag) this.electron.ipcRenderer.send('window-set-always-on-top', flag)
} }
/**
* Sets window vibrancy mode (Windows, macOS)
*
* @param type `null`, or `fluent` when supported (Windowd only)
*/
setVibrancy (enable: boolean, type: string|null): void {
if (this.platform === Platform.Windows && !isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED)) {
type = null
}
document.body.classList.toggle('vibrant', enable)
this.electron.ipcRenderer.send('window-set-vibrancy', enable, type)
}
setTitle (title?: string): void { setTitle (title?: string): void {
this.electron.ipcRenderer.send('window-set-title', title ?? 'Terminus') this.electron.ipcRenderer.send('window-set-title', title ?? 'Terminus')
} }
@@ -223,10 +208,6 @@ export class HostAppService {
this.getWindow().setTouchBar(touchBar) this.getWindow().setTouchBar(touchBar)
} }
popupContextMenu (menuDefinition: MenuItemConstructorOptions[]): void {
this.electron.Menu.buildFromTemplate(menuDefinition).popup({})
}
/** /**
* Notifies other windows of config file changes * Notifies other windows of config file changes
*/ */
@@ -250,20 +231,6 @@ export class HostAppService {
this.electron.ipcRenderer.send('app:register-global-hotkey', specs) this.electron.ipcRenderer.send('app:register-global-hotkey', specs)
} }
useBuiltinGraphics (): void {
const keyPath = 'SOFTWARE\\Microsoft\\DirectX\\UserGpuPreferences'
const valueName = this.electron.app.getPath('exe')
if (this.platform === Platform.Windows) {
if (!wnr.getRegistryValue(wnr.HK.CU, keyPath, valueName)) {
wnr.setRegistryValue(wnr.HK.CU, keyPath, valueName, wnr.REG.SZ, 'GpuPreference=1;')
}
}
}
setTrafficLightInset (x: number, y: number): void {
this.getWindow().setTrafficLightPosition({ x, y })
}
relaunch (): void { relaunch (): void {
if (this.isPortable) { if (this.isPortable) {
this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE }) this.electron.app.relaunch({ execPath: process.env.PORTABLE_EXECUTABLE_FILE })

View File

@@ -3,7 +3,6 @@ import { Observable, Subject } from 'rxjs'
import { HotkeyDescription, HotkeyProvider } from '../api/hotkeyProvider' import { HotkeyDescription, HotkeyProvider } from '../api/hotkeyProvider'
import { stringifyKeySequence, EventData } from './hotkeys.util' import { stringifyKeySequence, EventData } from './hotkeys.util'
import { ConfigService } from './config.service' import { ConfigService } from './config.service'
import { ElectronService } from './electron.service'
import { HostAppService } from './hostApp.service' import { HostAppService } from './hostApp.service'
export interface PartialHotkeyMatch { export interface PartialHotkeyMatch {
@@ -35,7 +34,6 @@ export class HotkeysService {
private constructor ( private constructor (
private zone: NgZone, private zone: NgZone,
private hostApp: HostAppService, private hostApp: HostAppService,
private electron: ElectronService,
private config: ConfigService, private config: ConfigService,
@Inject(HotkeyProvider) private hotkeyProviders: HotkeyProvider[], @Inject(HotkeyProvider) private hotkeyProviders: HotkeyProvider[],
) { ) {
@@ -52,9 +50,11 @@ export class HotkeysService {
this.config.changed$.subscribe(() => { this.config.changed$.subscribe(() => {
this.registerGlobalHotkey() this.registerGlobalHotkey()
}) })
this.registerGlobalHotkey() this.config.ready$.toPromise().then(() => {
this.getHotkeyDescriptions().then(hotkeys => { this.registerGlobalHotkey()
this.hotkeyDescriptions = hotkeys this.getHotkeyDescriptions().then(hotkeys => {
this.hotkeyDescriptions = hotkeys
})
}) })
// deprecated // deprecated
@@ -183,7 +183,6 @@ export class HotkeysService {
} }
private registerGlobalHotkey () { private registerGlobalHotkey () {
this.electron.globalShortcut.unregisterAll()
let value = this.config.store.hotkeys['toggle-window'] || [] let value = this.config.store.hotkeys['toggle-window'] || []
if (typeof value === 'string') { if (typeof value === 'string') {
value = [value] value = [value]

View File

@@ -1,38 +1,5 @@
import { Injectable } from '@angular/core' export abstract class Logger {
import { ElectronService } from './electron.service' constructor (protected name: string) { }
import type * as winston from 'winston'
import * as fs from 'fs'
import * as path from 'path'
const initializeWinston = (electron: ElectronService) => {
const logDirectory = electron.app.getPath('userData')
// eslint-disable-next-line
const winston = require('winston')
if (!fs.existsSync(logDirectory)) {
fs.mkdirSync(logDirectory)
}
return winston.createLogger({
transports: [
new winston.transports.File({
level: 'debug',
filename: path.join(logDirectory, 'log.txt'),
format: winston.format.simple(),
handleExceptions: false,
maxsize: 5242880,
maxFiles: 5,
}),
],
exitOnError: false,
})
}
export class Logger {
constructor (
private winstonLogger: winston.Logger,
private name: string,
) {}
debug (...args: any[]): void { debug (...args: any[]): void {
this.doLog('debug', ...args) this.doLog('debug', ...args)
@@ -54,26 +21,15 @@ export class Logger {
this.doLog('log', ...args) this.doLog('log', ...args)
} }
private doLog (level: string, ...args: any[]): void { protected abstract doLog (level: string, ...args: any[]): void
}
export class ConsoleLogger extends Logger {
protected doLog (level: string, ...args: any[]): void {
console[level](`%c[${this.name}]`, 'color: #aaa', ...args) console[level](`%c[${this.name}]`, 'color: #aaa', ...args)
this.winstonLogger[level](...args)
} }
} }
@Injectable({ providedIn: 'root' }) export abstract class LogService {
export class LogService { abstract create (name: string): Logger
private log: winston.Logger
/** @hidden */
private constructor (electron: ElectronService) {
if (!process.env.XWEB) {
this.log = initializeWinston(electron)
} else {
this.log = console as any
}
}
create (name: string): Logger {
return new Logger(this.log, name)
}
} }

View File

@@ -1,21 +1,25 @@
import { Inject, Injectable } from '@angular/core' import { Inject, Injectable } from '@angular/core'
import { Subject, Observable } from 'rxjs'
import { ConfigService } from '../services/config.service' import { ConfigService } from '../services/config.service'
import { Theme } from '../api/theme' import { Theme } from '../api/theme'
import { HostAppService, Platform } from './hostApp.service'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ThemesService { export class ThemesService {
get themeChanged$ (): Observable<Theme> { return this.themeChanged }
private themeChanged = new Subject<Theme>()
private styleElement: HTMLElement|null = null private styleElement: HTMLElement|null = null
/** @hidden */ /** @hidden */
private constructor ( private constructor (
private config: ConfigService, private config: ConfigService,
private hostApp: HostAppService,
@Inject(Theme) private themes: Theme[], @Inject(Theme) private themes: Theme[],
) { ) {
this.applyCurrentTheme() config.ready$.toPromise().then(() => {
config.changed$.subscribe(() => {
this.applyCurrentTheme() this.applyCurrentTheme()
config.changed$.subscribe(() => {
this.applyCurrentTheme()
})
}) })
} }
@@ -35,12 +39,7 @@ export class ThemesService {
} }
this.styleElement.textContent = theme.css this.styleElement.textContent = theme.css
document.querySelector('style#custom-css')!.innerHTML = this.config.store.appearance.css document.querySelector('style#custom-css')!.innerHTML = this.config.store.appearance.css
if (this.hostApp.platform === Platform.macOS) { this.themeChanged.next(theme)
this.hostApp.setTrafficLightInset(
theme.macOSWindowButtonsInsetX ?? 14,
theme.macOSWindowButtonsInsetY ?? 22,
)
}
} }
private applyCurrentTheme (): void { private applyCurrentTheme (): void {

View File

@@ -1,137 +1,4 @@
import axios from 'axios' export abstract class UpdaterService {
abstract check (): Promise<boolean>
import { Injectable } from '@angular/core' abstract update (): Promise<void>
import { Logger, LogService } from './log.service'
import { ElectronService } from './electron.service'
import { ConfigService } from './config.service'
import { HostAppService } from './hostApp.service'
const UPDATES_URL = 'https://api.github.com/repos/eugeny/terminus/releases/latest'
/** @hidden */
@Injectable({ providedIn: 'root' })
export class UpdaterService {
private logger: Logger
private downloaded: Promise<boolean>
private electronUpdaterAvailable = true
private updateURL: string
private constructor (
log: LogService,
config: ConfigService,
private electron: ElectronService,
private hostApp: HostAppService,
) {
this.logger = log.create('updater')
if (process.platform === 'linux') {
this.electronUpdaterAvailable = false
return
}
electron.autoUpdater.on('update-available', () => {
this.logger.info('Update available')
})
electron.autoUpdater.once('update-not-available', () => {
this.logger.info('No updates')
})
electron.autoUpdater.once('error', err => {
this.logger.error(err)
})
this.downloaded = new Promise<boolean>(resolve => {
electron.autoUpdater.once('update-downloaded', () => resolve(true))
})
if (config.store.enableAutomaticUpdates && this.electronUpdaterAvailable && !process.env.TERMINUS_DEV) {
this.logger.debug('Checking for updates')
try {
electron.autoUpdater.setFeedURL({
url: `https://update.electronjs.org/eugeny/terminus/${process.platform}-${process.arch}/${electron.app.getVersion()}`,
})
electron.autoUpdater.checkForUpdates()
} catch (e) {
this.electronUpdaterAvailable = false
this.logger.info('Electron updater unavailable, falling back', e)
}
}
}
async check (): Promise<boolean> {
if (this.electronUpdaterAvailable) {
return new Promise((resolve, reject) => {
// eslint-disable-next-line @typescript-eslint/init-declarations, prefer-const
let cancel
const onNoUpdate = () => {
cancel()
resolve(false)
}
const onUpdate = () => {
cancel()
resolve(this.downloaded)
}
const onError = (err) => {
cancel()
reject(err)
}
cancel = () => {
this.electron.autoUpdater.off('error', onError)
this.electron.autoUpdater.off('update-not-available', onNoUpdate)
this.electron.autoUpdater.off('update-available', onUpdate)
}
this.electron.autoUpdater.on('error', onError)
this.electron.autoUpdater.on('update-not-available', onNoUpdate)
this.electron.autoUpdater.on('update-available', onUpdate)
try {
this.electron.autoUpdater.checkForUpdates()
} catch (e) {
this.electronUpdaterAvailable = false
this.logger.info('Electron updater unavailable, falling back', e)
}
})
this.electron.autoUpdater.on('update-available', () => {
this.logger.info('Update available')
})
this.electron.autoUpdater.once('update-not-available', () => {
this.logger.info('No updates')
})
} else {
this.logger.debug('Checking for updates through fallback method.')
const response = await axios.get(UPDATES_URL)
const data = response.data
const version = data.tag_name.substring(1)
if (this.electron.app.getVersion() !== version) {
this.logger.info('Update available')
this.updateURL = data.html_url
return true
}
this.logger.info('No updates')
return false
}
return this.downloaded
}
async update (): Promise<void> {
if (!this.electronUpdaterAvailable) {
this.electron.shell.openExternal(this.updateURL)
} else {
if ((await this.electron.showMessageBox(
this.hostApp.getWindow(),
{
type: 'warning',
message: 'Installing the update will close all tabs and restart Terminus.',
buttons: ['Cancel', 'Update'],
defaultId: 1,
}
)).response === 1) {
await this.downloaded
this.electron.autoUpdater.quitAndInstall()
}
}
}
} }

View File

@@ -1,5 +1,4 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import type { MenuItemConstructorOptions } from 'electron'
import { Injectable, NgZone } from '@angular/core' import { Injectable, NgZone } from '@angular/core'
import { Subscription } from 'rxjs' import { Subscription } from 'rxjs'
import { AppService } from './services/app.service' import { AppService } from './services/app.service'
@@ -7,6 +6,7 @@ import { BaseTabComponent } from './components/baseTab.component'
import { TabHeaderComponent } from './components/tabHeader.component' import { TabHeaderComponent } from './components/tabHeader.component'
import { SplitTabComponent, SplitDirection } from './components/splitTab.component' import { SplitTabComponent, SplitDirection } from './components/splitTab.component'
import { TabContextMenuItemProvider } from './api/tabContextMenuProvider' import { TabContextMenuItemProvider } from './api/tabContextMenuProvider'
import { MenuItemOptions } from './api/menu'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
@@ -20,8 +20,8 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
super() super()
} }
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
let items: MenuItemConstructorOptions[] = [ let items: MenuItemOptions[] = [
{ {
label: 'Close', label: 'Close',
click: () => this.zone.run(() => { click: () => this.zone.run(() => {
@@ -76,7 +76,7 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
click: () => this.zone.run(() => { click: () => this.zone.run(() => {
(tab.parent as SplitTabComponent).splitTab(tab, dir) (tab.parent as SplitTabComponent).splitTab(tab, dir)
}), }),
})) as MenuItemConstructorOptions[], })) as MenuItemOptions[],
}) })
} }
} }
@@ -106,8 +106,8 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
super() super()
} }
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
let items: MenuItemConstructorOptions[] = [] let items: MenuItemOptions[] = []
if (tabHeader) { if (tabHeader) {
items = [ items = [
...items, ...items,
@@ -129,7 +129,7 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
click: () => this.zone.run(() => { click: () => this.zone.run(() => {
tab.color = color.value tab.color = color.value
}), }),
})) as MenuItemConstructorOptions[], })) as MenuItemOptions[],
}, },
] ]
} }
@@ -147,15 +147,14 @@ export class TaskCompletionContextMenu extends TabContextMenuItemProvider {
super() super()
} }
async getItems (tab: BaseTabComponent): Promise<MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent): Promise<MenuItemOptions[]> {
const process = await tab.getCurrentProcess() const process = await tab.getCurrentProcess()
const items: MenuItemConstructorOptions[] = [] const items: MenuItemOptions[] = []
const extTab: (BaseTabComponent & { __completionNotificationEnabled?: boolean, __outputNotificationSubscription?: Subscription|null }) = tab const extTab: (BaseTabComponent & { __completionNotificationEnabled?: boolean, __outputNotificationSubscription?: Subscription|null }) = tab
if (process) { if (process) {
items.push({ items.push({
id: 'process-name',
enabled: false, enabled: false,
label: 'Current process: ' + process.name, label: 'Current process: ' + process.name,
}) })

View File

@@ -2,20 +2,6 @@
# yarn lockfile v1 # yarn lockfile v1
"@dabh/diagnostics@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31"
integrity sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==
dependencies:
colorspace "1.1.x"
enabled "2.0.x"
kuler "^2.0.0"
"@electron/remote@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@electron/remote/-/remote-1.1.0.tgz#167d119c7c03c7778b556fdc4f1f38a44b23f1c2"
integrity sha512-yr8gZTkIgJYKbFqExI4QZqMSjn1kL/us9Dl46+TH1EZdhgRtsJ6HDfdsIxu0QEc6Hv+DMAXs69rgquH+8FDk4w==
"@types/js-yaml@^4.0.0": "@types/js-yaml@^4.0.0":
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.1.tgz#5544730b65a480b18ace6b6ce914e519cec2d43b" resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.1.tgz#5544730b65a480b18ace6b6ce914e519cec2d43b"
@@ -26,13 +12,6 @@
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.5.tgz#74deebbbcb1e86634dbf10a5b5e8798626f5a597" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.5.tgz#74deebbbcb1e86634dbf10a5b5e8798626f5a597"
integrity sha512-iotVxtCCsPLRAvxMFFgxL8HD2l4mAZ2Oin7/VJ2ooWO0VOK4EGOGmZWZn1uCq7RofR3I/1IOSjCHlFT71eVK0Q== integrity sha512-iotVxtCCsPLRAvxMFFgxL8HD2l4mAZ2Oin7/VJ2ooWO0VOK4EGOGmZWZn1uCq7RofR3I/1IOSjCHlFT71eVK0Q==
"@types/winston@^2.3.6":
version "2.4.4"
resolved "https://registry.yarnpkg.com/@types/winston/-/winston-2.4.4.tgz#48cc744b7b42fad74b9a2e8490e0112bd9a3d08d"
integrity sha512-BVGCztsypW8EYwJ+Hq+QNYiT/MUyCif0ouBH+flrY66O5W+KIXAMML6E/0fJpm7VjIzgangahl5S03bJJQGrZw==
dependencies:
winston "*"
agent-base@6: agent-base@6:
version "6.0.2" version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@@ -45,18 +24,6 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
async@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
bootstrap@^4.1.3: bootstrap@^4.1.3:
version "4.5.3" version "4.5.3"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.3.tgz#c6a72b355aaf323920be800246a6e4ef30997fe6" resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.3.tgz#c6a72b355aaf323920be800246a6e4ef30997fe6"
@@ -79,62 +46,11 @@ clone-deep@^4.0.1:
kind-of "^6.0.2" kind-of "^6.0.2"
shallow-clone "^3.0.0" shallow-clone "^3.0.0"
color-convert@^1.9.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
color-name@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^1.5.2:
version "1.5.4"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6"
integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
color@3.0.x:
version "3.0.0"
resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a"
integrity sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==
dependencies:
color-convert "^1.9.1"
color-string "^1.5.2"
colors@^1.2.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
colorspace@1.1.x:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5"
integrity sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==
dependencies:
color "3.0.x"
text-hex "1.0.x"
core-js@^3.1.2: core-js@^3.1.2:
version "3.12.1" version "3.12.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112"
integrity sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw== integrity sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw==
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
debug@4: debug@4:
version "4.3.1" version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
@@ -168,31 +84,6 @@ electron-updater@^4.0.6:
lodash.isequal "^4.5.0" lodash.isequal "^4.5.0"
semver "^7.3.5" semver "^7.3.5"
enabled@2.0.x:
version "2.0.0"
resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2"
integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==
fast-safe-stringify@^2.0.4:
version "2.0.7"
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
fecha@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41"
integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==
fn.name@1.x.x:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
follow-redirects@^1.10.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
fs-extra@^10.0.0: fs-extra@^10.0.0:
version "10.0.0" version "10.0.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1"
@@ -215,16 +106,11 @@ https-proxy-agent@5.0.0:
agent-base "6" agent-base "6"
debug "4" debug "4"
inherits@^2.0.3, inherits@~2.0.3: inherits@^2.0.3:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
is-arrayish@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
is-plain-object@^2.0.4: is-plain-object@^2.0.4:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
@@ -232,16 +118,6 @@ is-plain-object@^2.0.4:
dependencies: dependencies:
isobject "^3.0.1" isobject "^3.0.1"
is-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isobject@^3.0.1: isobject@^3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
@@ -268,11 +144,6 @@ kind-of@^6.0.2:
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
kuler@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
lazy-val@^1.0.4: lazy-val@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.4.tgz#882636a7245c2cfe6e0a4e3ba6c5d68a137e5c65" resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.4.tgz#882636a7245c2cfe6e0a4e3ba6c5d68a137e5c65"
@@ -288,17 +159,6 @@ lodash.isequal@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
logform@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2"
integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==
dependencies:
colors "^1.2.1"
fast-safe-stringify "^2.0.4"
fecha "^4.2.0"
ms "^2.1.1"
triple-beam "^1.3.0"
lru-cache@^6.0.0: lru-cache@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@@ -313,7 +173,7 @@ mixpanel@^0.13.0:
dependencies: dependencies:
https-proxy-agent "5.0.0" https-proxy-agent "5.0.0"
ms@2.1.2, ms@^2.1.1: ms@2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
@@ -332,24 +192,12 @@ ngx-perfect-scrollbar@^10.1.0:
resize-observer-polyfill "^1.5.0" resize-observer-polyfill "^1.5.0"
tslib "^2.0.0" tslib "^2.0.0"
one-time@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45"
integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==
dependencies:
fn.name "1.x.x"
perfect-scrollbar@1.5.0: perfect-scrollbar@1.5.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz#821d224ed8ff61990c23f26db63048cdc75b6b83" resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz#821d224ed8ff61990c23f26db63048cdc75b6b83"
integrity sha512-NrNHJn5mUGupSiheBTy6x+6SXCFbLlm8fVZh9moIzw/LgqElN5q4ncR4pbCBCYuCJ8Kcl9mYM0NgDxvW+b4LxA== integrity sha512-NrNHJn5mUGupSiheBTy6x+6SXCFbLlm8fVZh9moIzw/LgqElN5q4ncR4pbCBCYuCJ8Kcl9mYM0NgDxvW+b4LxA==
process-nextick-args@~2.0.0: readable-stream@3.6.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
readable-stream@3.6.0, readable-stream@^3.4.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -358,29 +206,11 @@ readable-stream@3.6.0, readable-stream@^3.4.0:
string_decoder "^1.1.1" string_decoder "^1.1.1"
util-deprecate "^1.0.1" util-deprecate "^1.0.1"
readable-stream@^2.3.7:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
resize-observer-polyfill@^1.5.0: resize-observer-polyfill@^1.5.0:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@~5.2.0: safe-buffer@~5.2.0:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
@@ -405,18 +235,6 @@ shallow-clone@^3.0.0:
dependencies: dependencies:
kind-of "^6.0.2" kind-of "^6.0.2"
simple-swizzle@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=
dependencies:
is-arrayish "^0.3.1"
stack-trace@0.0.x:
version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=
string_decoder@^1.1.1: string_decoder@^1.1.1:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -424,23 +242,6 @@ string_decoder@^1.1.1:
dependencies: dependencies:
safe-buffer "~5.2.0" safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
text-hex@1.0.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==
triple-beam@^1.2.0, triple-beam@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
tslib@^2.0.0: tslib@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
@@ -451,7 +252,7 @@ universalify@^2.0.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
util-deprecate@^1.0.1, util-deprecate@~1.0.1: util-deprecate@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
@@ -461,29 +262,6 @@ uuid@^8.0.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
winston-transport@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59"
integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==
dependencies:
readable-stream "^2.3.7"
triple-beam "^1.2.0"
winston@*, winston@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170"
integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==
dependencies:
"@dabh/diagnostics" "^2.0.2"
async "^3.1.0"
is-stream "^2.0.0"
logform "^2.2.0"
one-time "^1.0.0"
readable-stream "^3.4.0"
stack-trace "0.0.x"
triple-beam "^1.3.0"
winston-transport "^4.4.0"
yallist@^4.0.0: yallist@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"

1
terminus-electron/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
dist

View File

@@ -0,0 +1,27 @@
{
"name": "terminus-electron",
"version": "1.0.135-nightly.0",
"description": "Electron-specific bindings",
"keywords": [
"terminus-builtin-plugin"
],
"main": "dist/index.js",
"typings": "typings/index.d.ts",
"scripts": {
"build": "webpack --progress --color",
"watch": "webpack --progress --color --watch"
},
"files": [
"dist"
],
"author": "Eugene Pankov",
"license": "MIT",
"peerDependencies": {
"@angular/core": "^9.1.9"
},
"devDependencies": {
"axios": "^0.21.1",
"winston": "^3.3.3",
"electron-promise-ipc": "^2.2.4"
}
}

View File

@@ -1,8 +1,7 @@
import * as fs from 'mz/fs' import * as fs from 'mz/fs'
import * as path from 'path' import * as path from 'path'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { TerminalColorSchemeProvider } from './api/colorSchemeProvider' import { TerminalColorSchemeProvider, TerminalColorScheme } from 'terminus-terminal'
import { TerminalColorScheme } from './api/interfaces'
/** @hidden */ /** @hidden */
@Injectable() @Injectable()

View File

@@ -0,0 +1,77 @@
import { NgModule } from '@angular/core'
import { PlatformService, LogService, UpdaterService, DockingService, HostAppService, ThemesService, Platform, AppService, ConfigService, ElectronService, WIN_BUILD_FLUENT_BG_SUPPORTED, isWindowsBuild } from 'terminus-core'
import { TerminalColorSchemeProvider } from 'terminus-terminal'
import { HyperColorSchemes } from './colorSchemes'
import { ElectronPlatformService } from './services/platform'
import { ElectronLogService } from './services/log.service'
import { ElectronUpdaterService } from './services/updater.service'
import { TouchbarService } from './services/touchbar.service'
import { ElectronDockingService } from './services/docking.service'
@NgModule({
providers: [
{ provide: TerminalColorSchemeProvider, useClass: HyperColorSchemes, multi: true },
{ provide: PlatformService, useClass: ElectronPlatformService },
{ provide: LogService, useClass: ElectronLogService },
{ provide: UpdaterService, useClass: ElectronUpdaterService },
{ provide: DockingService, useClass: ElectronDockingService },
],
})
export default class ElectronModule {
constructor (
private config: ConfigService,
private hostApp: HostAppService,
private electron: ElectronService,
touchbar: TouchbarService,
docking: DockingService,
themeService: ThemesService,
app: AppService
) {
config.ready$.toPromise().then(() => {
touchbar.update()
docking.dock()
hostApp.shown.subscribe(() => {
docking.dock()
})
this.updateVibrancy()
})
themeService.themeChanged$.subscribe(theme => {
if (hostApp.platform === Platform.macOS) {
hostApp.getWindow().setTrafficLightPosition({
x: theme.macOSWindowButtonsInsetX ?? 14,
y: theme.macOSWindowButtonsInsetY ?? 22,
})
}
})
let lastProgress: number|null = null
app.tabOpened$.subscribe(tab => {
tab.progress$.subscribe(progress => {
if (lastProgress === progress) {
return
}
if (progress !== null) {
hostApp.getWindow().setProgressBar(progress / 100.0, { mode: 'normal' })
} else {
hostApp.getWindow().setProgressBar(-1, { mode: 'none' })
}
lastProgress = progress
})
})
config.changed$.subscribe(() => this.updateVibrancy())
}
private updateVibrancy () {
let vibrancyType = this.config.store.appearance.vibrancyType
if (this.hostApp.platform === Platform.Windows && !isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED)) {
vibrancyType = null
}
document.body.classList.toggle('vibrant', this.config.store.appearance.vibrancy)
this.electron.ipcRenderer.send('window-set-vibrancy', this.config.store.appearance.vibrancy, vibrancyType)
this.hostApp.getWindow().setOpacity(this.config.store.appearance.opacity)
}
}

View File

@@ -0,0 +1,97 @@
import { Injectable } from '@angular/core'
import type { Display } from 'electron'
import { ConfigService, ElectronService, HostAppService, Bounds, DockingService, Screen } from 'terminus-core'
@Injectable()
export class ElectronDockingService extends DockingService {
constructor (
private electron: ElectronService,
private config: ConfigService,
private hostApp: HostAppService,
) {
super()
hostApp.displaysChanged$.subscribe(() => this.repositionWindow())
hostApp.displayMetricsChanged$.subscribe(() => this.repositionWindow())
}
dock (): void {
const dockSide = this.config.store.appearance.dock
if (dockSide === 'off') {
this.hostApp.setAlwaysOnTop(false)
return
}
let display = this.electron.screen.getAllDisplays()
.filter(x => x.id === this.config.store.appearance.dockScreen)[0]
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!display) {
display = this.getCurrentScreen()
}
const newBounds: Bounds = { x: 0, y: 0, width: 0, height: 0 }
const fill = this.config.store.appearance.dockFill <= 1 ? this.config.store.appearance.dockFill : 1
const space = this.config.store.appearance.dockSpace <= 1 ? this.config.store.appearance.dockSpace : 1
const [minWidth, minHeight] = this.hostApp.getWindow().getMinimumSize()
if (dockSide === 'left' || dockSide === 'right') {
newBounds.width = Math.max(minWidth, Math.round(fill * display.bounds.width))
newBounds.height = Math.round(display.bounds.height * space)
}
if (dockSide === 'top' || dockSide === 'bottom') {
newBounds.width = Math.round(display.bounds.width * space)
newBounds.height = Math.max(minHeight, Math.round(fill * display.bounds.height))
}
if (dockSide === 'right') {
newBounds.x = display.bounds.x + display.bounds.width - newBounds.width
} else if (dockSide === 'left') {
newBounds.x = display.bounds.x
} else {
newBounds.x = display.bounds.x + Math.round(display.bounds.width / 2 * (1 - space))
}
if (dockSide === 'bottom') {
newBounds.y = display.bounds.y + display.bounds.height - newBounds.height
} else if (dockSide === 'top') {
newBounds.y = display.bounds.y
} else {
newBounds.y = display.bounds.y + Math.round(display.bounds.height / 2 * (1 - space))
}
const alwaysOnTop = this.config.store.appearance.dockAlwaysOnTop
this.hostApp.setAlwaysOnTop(alwaysOnTop)
setImmediate(() => {
this.hostApp.setBounds(newBounds)
})
}
getScreens (): Screen[] {
const primaryDisplayID = this.electron.screen.getPrimaryDisplay().id
return this.electron.screen.getAllDisplays().sort((a, b) =>
a.bounds.x === b.bounds.x ? a.bounds.y - b.bounds.y : a.bounds.x - b.bounds.x
).map((display, index) => {
return {
...display,
id: display.id,
name: display.id === primaryDisplayID ? 'Primary Display' : `Display ${index + 1}`,
}
})
}
private getCurrentScreen (): Display {
return this.electron.screen.getDisplayNearestPoint(this.electron.screen.getCursorScreenPoint())
}
private repositionWindow () {
const [x, y] = this.hostApp.getWindow().getPosition()
for (const screen of this.electron.screen.getAllDisplays()) {
const bounds = screen.bounds
if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
return
}
}
const screen = this.electron.screen.getPrimaryDisplay()
this.hostApp.getWindow().setPosition(screen.bounds.x, screen.bounds.y)
}
}

View File

@@ -0,0 +1,54 @@
import * as fs from 'fs'
import * as path from 'path'
import * as winston from 'winston'
import { Injectable } from '@angular/core'
import { ConsoleLogger, Logger, ElectronService } from 'terminus-core'
const initializeWinston = (electron: ElectronService) => {
const logDirectory = electron.app.getPath('userData')
// eslint-disable-next-line
const winston = require('winston')
if (!fs.existsSync(logDirectory)) {
fs.mkdirSync(logDirectory)
}
return winston.createLogger({
transports: [
new winston.transports.File({
level: 'debug',
filename: path.join(logDirectory, 'log.txt'),
format: winston.format.simple(),
handleExceptions: false,
maxsize: 5242880,
maxFiles: 5,
}),
],
exitOnError: false,
})
}
export class WinstonAndConsoleLogger extends ConsoleLogger {
constructor (private winstonLogger: winston.Logger, name: string) {
super(name)
}
protected doLog (level: string, ...args: any[]): void {
super.doLog(level, ...args)
this.winstonLogger[level](...args)
}
}
@Injectable({ providedIn: 'root' })
export class ElectronLogService {
private log: winston.Logger
/** @hidden */
constructor (electron: ElectronService) {
this.log = initializeWinston(electron)
}
create (name: string): Logger {
return new WinstonAndConsoleLogger(this.log, name)
}
}

View File

@@ -0,0 +1,146 @@
import * as path from 'path'
import * as fs from 'mz/fs'
import * as os from 'os'
import promiseIpc from 'electron-promise-ipc'
import { execFile } from 'mz/child_process'
import { Injectable } from '@angular/core'
import { PlatformService, ClipboardContent, HostAppService, Platform, ElectronService, MenuItemOptions } from 'terminus-core'
const fontManager = require('fontmanager-redux') // eslint-disable-line
/* eslint-disable block-scoped-var */
try {
// eslint-disable-next-line no-var
var windowsProcessTreeNative = require('windows-process-tree/build/Release/windows_process_tree.node')
// eslint-disable-next-line no-var
var wnr = require('windows-native-registry')
} catch { }
@Injectable()
export class ElectronPlatformService extends PlatformService {
supportsWindowControls = true
private userPluginsPath: string = (window as any).userPluginsPath
private configPath: string
constructor (
private hostApp: HostAppService,
private electron: ElectronService,
) {
super()
this.configPath = path.join(electron.app.getPath('userData'), 'config.yaml')
}
setClipboard (content: ClipboardContent): void {
require('@electron/remote').clipboard.write(content)
}
async installPlugin (name: string, version: string): Promise<void> {
await (promiseIpc as any).send('plugin-manager:install', this.userPluginsPath, name, version)
}
async uninstallPlugin (name: string): Promise<void> {
await (promiseIpc as any).send('plugin-manager:uninstall', this.userPluginsPath, name)
}
async isProcessRunning (name: string): Promise<boolean> {
if (this.hostApp.platform === Platform.Windows) {
return new Promise<boolean>(resolve => {
windowsProcessTreeNative.getProcessList(list => { // eslint-disable-line block-scoped-var
resolve(list.some(x => x.name === name))
}, 0)
})
} else {
throw new Error('Not supported')
}
}
getWinSCPPath (): string|null {
const key = wnr.getRegistryKey(wnr.HK.CR, 'WinSCP.Url\\DefaultIcon')
if (key?.['']) {
let detectedPath = key[''].value?.split(',')[0]
detectedPath = detectedPath?.substring(1, detectedPath.length - 1)
return detectedPath
}
return null
}
exec (app: string, argv: string[]): void {
execFile(app, argv)
}
isShellIntegrationSupported (): boolean {
return this.hostApp.platform !== Platform.Linux
}
async isShellIntegrationInstalled (): Promise<boolean> {
return false
}
async installShellIntegration (): Promise<void> {
throw new Error('Not implemented')
}
async uninstallShellIntegration (): Promise<void> {
throw new Error('Not implemented')
}
async loadConfig (): Promise<string> {
if (await fs.exists(this.configPath)) {
return fs.readFileSync(this.configPath, 'utf8')
} else {
return ''
}
}
async saveConfig (content: string): Promise<void> {
await fs.writeFile(this.configPath, content, 'utf8')
}
getConfigPath (): string|null {
return this.configPath
}
showItemInFolder (p: string): void {
this.electron.shell.showItemInFolder(p)
}
openExternal (url: string): void {
this.electron.shell.openExternal(url)
}
openPath (p: string): void {
this.electron.shell.openPath(p)
}
getOSRelease (): string {
return os.release()
}
getAppVersion (): string {
return this.electron.app.getVersion()
}
async listFonts (): Promise<string[]> {
if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) {
let fonts = await new Promise<any[]>((resolve) => fontManager.findFonts({ monospace: true }, resolve))
fonts = fonts.map(x => x.family.trim())
return fonts
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (this.hostApp.platform === Platform.Linux) {
const stdout = (await execFile('fc-list', [':spacing=mono']))[0]
const fonts = stdout.toString()
.split('\n')
.filter(x => !!x)
.map(x => x.split(':')[1].trim())
.map(x => x.split(',')[0].trim())
fonts.sort()
return fonts
}
return []
}
popupContextMenu (menu: MenuItemOptions[], _event?: MouseEvent): void {
this.electron.Menu.buildFromTemplate(menu).popup({})
}
}

View File

@@ -2,8 +2,8 @@ import * as path from 'path'
import * as fs from 'mz/fs' import * as fs from 'mz/fs'
import { exec } from 'mz/child_process' import { exec } from 'mz/child_process'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service' import { ElectronService } from '../../../terminus-core/src/services/electron.service'
import { HostAppService, Platform } from './hostApp.service' import { HostAppService, Platform } from '../../../terminus-core/src/services/hostApp.service'
/* eslint-disable block-scoped-var */ /* eslint-disable block-scoped-var */

View File

@@ -1,8 +1,6 @@
import { SegmentedControlSegment, TouchBarSegmentedControl } from 'electron' import { SegmentedControlSegment, TouchBarSegmentedControl } from 'electron'
import { Injectable, NgZone } from '@angular/core' import { Injectable, NgZone } from '@angular/core'
import { AppService } from './app.service' import { AppService, HostAppService, Platform, ElectronService } from 'terminus-core'
import { ElectronService } from './electron.service'
import { HostAppService, Platform } from './hostApp.service'
/** @hidden */ /** @hidden */
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })

View File

@@ -0,0 +1,134 @@
import { Injectable } from '@angular/core'
import axios from 'axios'
import { Logger, LogService, ElectronService, ConfigService, HostAppService, UpdaterService } from 'terminus-core'
const UPDATES_URL = 'https://api.github.com/repos/eugeny/terminus/releases/latest'
@Injectable()
export class ElectronUpdaterService extends UpdaterService {
private logger: Logger
private downloaded: Promise<boolean>
private electronUpdaterAvailable = true
private updateURL: string
constructor (
log: LogService,
config: ConfigService,
private electron: ElectronService,
private hostApp: HostAppService,
) {
super()
this.logger = log.create('updater')
if (process.platform === 'linux') {
this.electronUpdaterAvailable = false
return
}
electron.autoUpdater.on('update-available', () => {
this.logger.info('Update available')
})
electron.autoUpdater.once('update-not-available', () => {
this.logger.info('No updates')
})
electron.autoUpdater.once('error', err => {
this.logger.error(err)
})
this.downloaded = new Promise<boolean>(resolve => {
electron.autoUpdater.once('update-downloaded', () => resolve(true))
})
if (config.store.enableAutomaticUpdates && this.electronUpdaterAvailable && !process.env.TERMINUS_DEV) {
this.logger.debug('Checking for updates')
try {
electron.autoUpdater.setFeedURL({
url: `https://update.electronjs.org/eugeny/terminus/${process.platform}-${process.arch}/${electron.app.getVersion()}`,
})
electron.autoUpdater.checkForUpdates()
} catch (e) {
this.electronUpdaterAvailable = false
this.logger.info('Electron updater unavailable, falling back', e)
}
}
}
async check (): Promise<boolean> {
if (this.electronUpdaterAvailable) {
return new Promise((resolve, reject) => {
// eslint-disable-next-line @typescript-eslint/init-declarations, prefer-const
let cancel
const onNoUpdate = () => {
cancel()
resolve(false)
}
const onUpdate = () => {
cancel()
resolve(this.downloaded)
}
const onError = (err) => {
cancel()
reject(err)
}
cancel = () => {
this.electron.autoUpdater.off('error', onError)
this.electron.autoUpdater.off('update-not-available', onNoUpdate)
this.electron.autoUpdater.off('update-available', onUpdate)
}
this.electron.autoUpdater.on('error', onError)
this.electron.autoUpdater.on('update-not-available', onNoUpdate)
this.electron.autoUpdater.on('update-available', onUpdate)
try {
this.electron.autoUpdater.checkForUpdates()
} catch (e) {
this.electronUpdaterAvailable = false
this.logger.info('Electron updater unavailable, falling back', e)
}
})
this.electron.autoUpdater.on('update-available', () => {
this.logger.info('Update available')
})
this.electron.autoUpdater.once('update-not-available', () => {
this.logger.info('No updates')
})
} else {
this.logger.debug('Checking for updates through fallback method.')
const response = await axios.get(UPDATES_URL)
const data = response.data
const version = data.tag_name.substring(1)
if (this.electron.app.getVersion() !== version) {
this.logger.info('Update available')
this.updateURL = data.html_url
return true
}
this.logger.info('No updates')
return false
}
return this.downloaded
}
async update (): Promise<void> {
if (!this.electronUpdaterAvailable) {
this.electron.shell.openExternal(this.updateURL)
} else {
if ((await this.electron.showMessageBox(
this.hostApp.getWindow(),
{
type: 'warning',
message: 'Installing the update will close all tabs and restart Terminus.',
buttons: ['Cancel', 'Update'],
defaultId: 1,
}
)).response === 1) {
await this.downloaded
this.electron.autoUpdater.quitAndInstall()
}
}
}
}

View File

@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"exclude": ["node_modules", "dist"],
"compilerOptions": {
"baseUrl": "src"
}
}

View File

@@ -0,0 +1,14 @@
{
"extends": "../tsconfig.json",
"exclude": ["node_modules", "dist", "typings"],
"compilerOptions": {
"baseUrl": "src",
"emitDeclarationOnly": true,
"declaration": true,
"declarationDir": "./typings",
"paths": {
"terminus-*": ["../../terminus-*"],
"*": ["../../app/node_modules/*"]
}
}
}

View File

@@ -0,0 +1,5 @@
const config = require('../webpack.plugin.config')
module.exports = config({
name: 'electron',
dirname: __dirname,
})

477
terminus-electron/yarn.lock Normal file
View File

@@ -0,0 +1,477 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@dabh/diagnostics@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31"
integrity sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==
dependencies:
colorspace "1.1.x"
enabled "2.0.x"
kuler "^2.0.0"
async@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
dependencies:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
color-convert@^1.9.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
color-name@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-string@^1.5.2:
version "1.5.5"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==
dependencies:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
color@3.0.x:
version "3.0.0"
resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a"
integrity sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==
dependencies:
color-convert "^1.9.1"
color-string "^1.5.2"
colors@^1.2.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
colorspace@1.1.x:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5"
integrity sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==
dependencies:
color "3.0.x"
text-hex "1.0.x"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
dependencies:
object-keys "^1.0.12"
electron-promise-ipc@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/electron-promise-ipc/-/electron-promise-ipc-2.2.4.tgz#b82daf86ca6d0f0b8655937fdbe9a554590deeea"
integrity sha512-xCkFEeuru9l7H/+m1gpK4F1utexvTT7+n1PTquP2MVTpmBmpgFBlLqSXC7TqwpROkHRm9wGpaCJEx0djonnSEg==
dependencies:
is-electron-renderer "^2.0.1"
object.entries "^1.1.3"
serialize-error "^5.0.0"
uuid "^3.0.1"
enabled@2.0.x:
version "2.0.0"
resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2"
integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==
es-abstract@^1.18.0-next.1:
version "1.18.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4"
integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==
dependencies:
call-bind "^1.0.2"
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
get-intrinsic "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.2"
is-callable "^1.2.3"
is-negative-zero "^2.0.1"
is-regex "^1.1.2"
is-string "^1.0.5"
object-inspect "^1.9.0"
object-keys "^1.1.1"
object.assign "^4.1.2"
string.prototype.trimend "^1.0.4"
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.0"
es-to-primitive@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
dependencies:
is-callable "^1.1.4"
is-date-object "^1.0.1"
is-symbol "^1.0.2"
fast-safe-stringify@^2.0.4:
version "2.0.7"
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
fecha@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.1.tgz#0a83ad8f86ef62a091e22bb5a039cd03d23eecce"
integrity sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==
fn.name@1.x.x:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
follow-redirects@^1.10.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
has-bigints@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
has-symbols@^1.0.1, has-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
is-arrayish@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
is-bigint@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a"
integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==
is-boolean-object@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8"
integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==
dependencies:
call-bind "^1.0.2"
is-callable@^1.1.4, is-callable@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e"
integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==
is-date-object@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5"
integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==
is-electron-renderer@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-electron-renderer/-/is-electron-renderer-2.0.1.tgz#a469d056f975697c58c98c6023eb0aa79af895a2"
integrity sha1-pGnQVvl1aXxYyYxgI+sKp5r4laI=
is-negative-zero@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
is-number-object@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb"
integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==
is-regex@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f"
integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==
dependencies:
call-bind "^1.0.2"
has-symbols "^1.0.2"
is-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
is-string@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f"
integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==
is-symbol@^1.0.2, is-symbol@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
dependencies:
has-symbols "^1.0.2"
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
kuler@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
logform@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2"
integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==
dependencies:
colors "^1.2.1"
fast-safe-stringify "^2.0.4"
fecha "^4.2.0"
ms "^2.1.1"
triple-beam "^1.3.0"
ms@^2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
object-inspect@^1.9.0:
version "1.10.3"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369"
integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==
object-keys@^1.0.12, object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object.assign@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"
has-symbols "^1.0.1"
object-keys "^1.1.1"
object.entries@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.3.tgz#c601c7f168b62374541a07ddbd3e2d5e4f7711a6"
integrity sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"
es-abstract "^1.18.0-next.1"
has "^1.0.3"
one-time@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45"
integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==
dependencies:
fn.name "1.x.x"
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
readable-stream@^2.3.7:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.4.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
serialize-error@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac"
integrity sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==
dependencies:
type-fest "^0.8.0"
simple-swizzle@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=
dependencies:
is-arrayish "^0.3.1"
stack-trace@0.0.x:
version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=
string.prototype.trimend@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.3"
string.prototype.trimstart@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.3"
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
text-hex@1.0.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==
triple-beam@^1.2.0, triple-beam@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
type-fest@^0.8.0:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
unbox-primitive@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==
dependencies:
function-bind "^1.1.1"
has-bigints "^1.0.1"
has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2"
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
uuid@^3.0.1:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
dependencies:
is-bigint "^1.0.1"
is-boolean-object "^1.1.0"
is-number-object "^1.0.4"
is-string "^1.0.5"
is-symbol "^1.0.3"
winston-transport@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59"
integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==
dependencies:
readable-stream "^2.3.7"
triple-beam "^1.2.0"
winston@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170"
integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==
dependencies:
"@dabh/diagnostics" "^2.0.2"
async "^3.1.0"
is-stream "^2.0.0"
logform "^2.2.0"
one-time "^1.0.0"
readable-stream "^3.4.0"
stack-trace "0.0.x"
triple-beam "^1.3.0"
winston-transport "^4.4.0"

View File

@@ -24,7 +24,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
ngOnInit (): void { ngOnInit (): void {
this.logger = this.log.create('terminalTab') this.logger = this.log.create('terminalTab')
this.session = new Session(this.config) this.session = new Session(this.injector)
const isConPTY = isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY const isConPTY = isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY

View File

@@ -4,7 +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 TerminusCorePlugin, { HostAppService, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, TabContextMenuItemProvider, CLIHandler } from 'terminus-core' import TerminusCorePlugin, { HostAppService, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, TabContextMenuItemProvider, CLIHandler, ConfigService } from 'terminus-core'
import TerminusTerminalModule from 'terminus-terminal' import TerminusTerminalModule from 'terminus-terminal'
import { SettingsTabProvider } from 'terminus-settings' import { SettingsTabProvider } from 'terminus-settings'
@@ -104,6 +104,7 @@ export default class LocalTerminalModule { // eslint-disable-line @typescript-es
terminal: TerminalService, terminal: TerminalService,
hostApp: HostAppService, hostApp: HostAppService,
dockMenu: DockMenuService, dockMenu: DockMenuService,
config: ConfigService,
) { ) {
hotkeys.matchedHotkey.subscribe(async (hotkey) => { hotkeys.matchedHotkey.subscribe(async (hotkey) => {
if (hotkey === 'new-tab') { if (hotkey === 'new-tab') {
@@ -120,7 +121,9 @@ export default class LocalTerminalModule { // eslint-disable-line @typescript-es
} }
}) })
dockMenu.update() config.ready$.toPromise().then(() => {
dockMenu.update()
})
} }
} }

View File

@@ -26,10 +26,12 @@ export class TerminalService {
log: LogService, log: LogService,
) { ) {
this.logger = log.create('terminal') this.logger = log.create('terminal')
this.reloadShells()
config.changed$.subscribe(() => { config.ready$.toPromise().then(() => {
this.reloadShells() this.reloadShells()
config.changed$.subscribe(() => {
this.reloadShells()
})
}) })
} }

View File

@@ -1,7 +1,8 @@
import * as psNode from 'ps-node' import * as psNode from 'ps-node'
import * as fs from 'mz/fs' import * as fs from 'mz/fs'
import * as os from 'os' import * as os from 'os'
import { ConfigService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild, getBootstrapData } from 'terminus-core' import { Injector } from '@angular/core'
import { HostAppService, ConfigService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild, Platform, BootstrapData, BOOTSTRAP_DATA } from 'terminus-core'
import { BaseSession } from 'terminus-terminal' import { BaseSession } from 'terminus-terminal'
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import { getWorkingDirectoryFromPID } from 'native-process-working-directory' import { getWorkingDirectoryFromPID } from 'native-process-working-directory'
@@ -91,9 +92,15 @@ export class Session extends BaseSession {
private guessedCWD: string|null = null private guessedCWD: string|null = null
private reportedCWD: string private reportedCWD: string
private initialCWD: string|null = null private initialCWD: string|null = null
private config: ConfigService
private hostApp: HostAppService
private bootstrapData: BootstrapData
constructor (private config: ConfigService) { constructor (injector: Injector) {
super() super()
this.config = injector.get(ConfigService)
this.hostApp = injector.get(HostAppService)
this.bootstrapData = injector.get(BOOTSTRAP_DATA) as BootstrapData
} }
start (options: SessionOptions): void { start (options: SessionOptions): void {
@@ -115,13 +122,13 @@ export class Session extends BaseSession {
...this.config.store.terminal.environment || {}, ...this.config.store.terminal.environment || {},
} }
if (process.platform === 'win32') { if (this.hostApp.platform === Platform.Windows) {
env.COMSPEC = getBootstrapData().executable env.COMSPEC = this.bootstrapData.executable
} }
delete env[''] delete env['']
if (process.platform === 'darwin' && !process.env.LC_ALL) { if (this.hostApp.platform === Platform.macOS && !process.env.LC_ALL) {
const locale = process.env.LC_CTYPE ?? 'en_US.UTF-8' const locale = process.env.LC_CTYPE ?? 'en_US.UTF-8'
Object.assign(env, { Object.assign(env, {
LANG: locale, LANG: locale,
@@ -177,7 +184,7 @@ export class Session extends BaseSession {
let data = Buffer.from(array) let data = Buffer.from(array)
data = this.processOSC1337(data) data = this.processOSC1337(data)
this.emitOutput(data) this.emitOutput(data)
if (process.platform === 'win32') { if (this.hostApp.platform === Platform.Windows) {
this.guessWindowsCWD(data.toString()) this.guessWindowsCWD(data.toString())
} }
}) })
@@ -229,7 +236,7 @@ export class Session extends BaseSession {
if (!this.truePID) { if (!this.truePID) {
return [] return []
} }
if (process.platform === 'darwin') { if (this.hostApp.platform === Platform.macOS) {
const processes = await macOSNativeProcessList.getProcessList() const processes = await macOSNativeProcessList.getProcessList()
return processes.filter(x => x.ppid === this.truePID).map(p => ({ return processes.filter(x => x.ppid === this.truePID).map(p => ({
pid: p.pid, pid: p.pid,
@@ -237,7 +244,7 @@ export class Session extends BaseSession {
command: p.name, command: p.name,
})) }))
} }
if (process.platform === 'win32') { if (this.hostApp.platform === Platform.Windows) {
return new Promise<ChildProcess[]>(resolve => { return new Promise<ChildProcess[]>(resolve => {
windowsProcessTree.getProcessTree(this.truePID, tree => { windowsProcessTree.getProcessTree(this.truePID, tree => {
resolve(tree ? tree.children.map(child => ({ resolve(tree ? tree.children.map(child => ({
@@ -259,7 +266,7 @@ export class Session extends BaseSession {
} }
async gracefullyKillProcess (): Promise<void> { async gracefullyKillProcess (): Promise<void> {
if (process.platform === 'win32') { if (this.hostApp.platform === Platform.Windows) {
this.kill() this.kill()
} else { } else {
await new Promise<void>((resolve) => { await new Promise<void>((resolve) => {
@@ -302,7 +309,7 @@ export class Session extends BaseSession {
cwd = await fs.realpath(cwd) cwd = await fs.realpath(cwd)
} catch {} } catch {}
if (process.platform === 'win32' && (cwd === this.initialCWD || cwd === process.env.WINDIR)) { if (this.hostApp.platform === Platform.Windows && (cwd === this.initialCWD || cwd === process.env.WINDIR)) {
// shell doesn't truly change its process' CWD // shell doesn't truly change its process' CWD
cwd = null cwd = null
} }

View File

@@ -1,6 +1,5 @@
import { MenuItemConstructorOptions } from 'electron'
import { Injectable, NgZone } from '@angular/core' import { Injectable, NgZone } from '@angular/core'
import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, SplitTabComponent, NotificationsService } from 'terminus-core' import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, SplitTabComponent, NotificationsService, MenuItemOptions } from 'terminus-core'
import { TerminalTabComponent } from './components/terminalTab.component' import { TerminalTabComponent } from './components/terminalTab.component'
import { UACService } from './services/uac.service' import { UACService } from './services/uac.service'
import { TerminalService } from './services/terminal.service' import { TerminalService } from './services/terminal.service'
@@ -16,11 +15,11 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
super() super()
} }
async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
if (!(tab instanceof TerminalTabComponent)) { if (!(tab instanceof TerminalTabComponent)) {
return [] return []
} }
const items: MenuItemConstructorOptions[] = [ const items: MenuItemOptions[] = [
{ {
label: 'Save as profile', label: 'Save as profile',
click: () => this.zone.run(async () => { click: () => this.zone.run(async () => {
@@ -59,10 +58,10 @@ export class NewTabContextMenu extends TabContextMenuItemProvider {
super() super()
} }
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
const profiles = await this.terminalService.getProfiles() const profiles = await this.terminalService.getProfiles()
const items: MenuItemConstructorOptions[] = [ const items: MenuItemOptions[] = [
{ {
label: 'New terminal', label: 'New terminal',
click: () => this.zone.run(() => { click: () => this.zone.run(() => {

View File

@@ -19,7 +19,6 @@
"devDependencies": { "devDependencies": {
"@types/semver": "^7.1.0", "@types/semver": "^7.1.0",
"axios": "^0.21.1", "axios": "^0.21.1",
"electron-promise-ipc": "^2.2.4",
"mz": "^2.6.0", "mz": "^2.6.0",
"semver": "^7.1.1" "semver": "^7.1.1"
}, },

View File

@@ -4,7 +4,7 @@ import { debounceTime, distinctUntilChanged, first, tap, flatMap, map } from 'rx
import semverGt from 'semver/functions/gt' import semverGt from 'semver/functions/gt'
import { Component, Input } from '@angular/core' import { Component, Input } from '@angular/core'
import { ConfigService, ElectronService } from 'terminus-core' import { ConfigService, PlatformService } from 'terminus-core'
import { PluginInfo, PluginManagerService } from '../services/pluginManager.service' import { PluginInfo, PluginManagerService } from '../services/pluginManager.service'
enum BusyState { Installing = 'Installing', Uninstalling = 'Uninstalling' } enum BusyState { Installing = 'Installing', Uninstalling = 'Uninstalling' }
@@ -27,8 +27,8 @@ export class PluginsSettingsTabComponent {
@Input() errorMessage: string @Input() errorMessage: string
constructor ( constructor (
private electron: ElectronService,
private config: ConfigService, private config: ConfigService,
private platform: PlatformService,
public pluginManager: PluginManagerService public pluginManager: PluginManagerService
) { ) {
} }
@@ -57,7 +57,7 @@ export class PluginsSettingsTabComponent {
} }
openPluginsFolder (): void { openPluginsFolder (): void {
this.electron.shell.openPath(this.pluginManager.userPluginsPath) this.platform.openPath(this.pluginManager.userPluginsPath)
} }
searchAvailable (query: string) { searchAvailable (query: string) {
@@ -101,7 +101,7 @@ export class PluginsSettingsTabComponent {
} }
showPluginInfo (plugin: PluginInfo) { showPluginInfo (plugin: PluginInfo) {
this.electron.shell.openExternal('https://www.npmjs.com/package/' + plugin.packageName) this.platform.openExternal('https://www.npmjs.com/package/' + plugin.packageName)
} }
isPluginEnabled (plugin: PluginInfo) { isPluginEnabled (plugin: PluginInfo) {

View File

@@ -1,9 +1,8 @@
import axios from 'axios' import axios from 'axios'
import promiseIpc from 'electron-promise-ipc'
import { Observable, from } from 'rxjs' import { Observable, from } from 'rxjs'
import { map } from 'rxjs/operators' import { map } from 'rxjs/operators'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Logger, LogService } from 'terminus-core' import { Logger, LogService, PlatformService } from 'terminus-core'
const NAME_PREFIX = 'terminus-' const NAME_PREFIX = 'terminus-'
const KEYWORD = 'terminus-plugin' const KEYWORD = 'terminus-plugin'
@@ -34,6 +33,7 @@ export class PluginManagerService {
private constructor ( private constructor (
log: LogService, log: LogService,
private platform: PlatformService,
) { ) {
this.logger = log.create('pluginManager') this.logger = log.create('pluginManager')
} }
@@ -63,7 +63,7 @@ export class PluginManagerService {
async installPlugin (plugin: PluginInfo): Promise<void> { async installPlugin (plugin: PluginInfo): Promise<void> {
try { try {
await (promiseIpc as any).send('plugin-manager:install', this.userPluginsPath, plugin.packageName, plugin.version) await this.platform.installPlugin(plugin.packageName, plugin.version)
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName) this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
this.installedPlugins.push(plugin) this.installedPlugins.push(plugin)
} catch (err) { } catch (err) {
@@ -74,7 +74,7 @@ export class PluginManagerService {
async uninstallPlugin (plugin: PluginInfo): Promise<void> { async uninstallPlugin (plugin: PluginInfo): Promise<void> {
try { try {
await (promiseIpc as any).send('plugin-manager:uninstall', this.userPluginsPath, plugin.packageName) await this.platform.uninstallPlugin(plugin.packageName)
this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName) this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
} catch (err) { } catch (err) {
this.logger.error(err) this.logger.error(err)

View File

@@ -19,123 +19,11 @@ axios@^0.21.1:
dependencies: dependencies:
follow-redirects "^1.10.0" follow-redirects "^1.10.0"
call-bind@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce"
integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==
dependencies:
function-bind "^1.1.1"
get-intrinsic "^1.0.0"
define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
dependencies:
object-keys "^1.0.12"
electron-promise-ipc@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/electron-promise-ipc/-/electron-promise-ipc-2.2.4.tgz#b82daf86ca6d0f0b8655937fdbe9a554590deeea"
integrity sha512-xCkFEeuru9l7H/+m1gpK4F1utexvTT7+n1PTquP2MVTpmBmpgFBlLqSXC7TqwpROkHRm9wGpaCJEx0djonnSEg==
dependencies:
is-electron-renderer "^2.0.1"
object.entries "^1.1.3"
serialize-error "^5.0.0"
uuid "^3.0.1"
es-abstract@^1.18.0-next.1:
version "1.18.0-next.1"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68"
integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==
dependencies:
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
is-callable "^1.2.2"
is-negative-zero "^2.0.0"
is-regex "^1.1.1"
object-inspect "^1.8.0"
object-keys "^1.1.1"
object.assign "^4.1.1"
string.prototype.trimend "^1.0.1"
string.prototype.trimstart "^1.0.1"
es-to-primitive@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
dependencies:
is-callable "^1.1.4"
is-date-object "^1.0.1"
is-symbol "^1.0.2"
follow-redirects@^1.10.0: follow-redirects@^1.10.0:
version "1.13.1" version "1.13.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
get-intrinsic@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49"
integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
has-symbols@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
is-callable@^1.1.4, is-callable@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9"
integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==
is-date-object@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
is-electron-renderer@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-electron-renderer/-/is-electron-renderer-2.0.1.tgz#a469d056f975697c58c98c6023eb0aa79af895a2"
integrity sha1-pGnQVvl1aXxYyYxgI+sKp5r4laI=
is-negative-zero@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
is-regex@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
dependencies:
has-symbols "^1.0.1"
is-symbol@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
dependencies:
has-symbols "^1.0.1"
lru-cache@^6.0.0: lru-cache@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@@ -157,36 +45,6 @@ object-assign@^4.0.1:
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-inspect@^1.8.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a"
integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==
object-keys@^1.0.12, object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object.assign@^4.1.1:
version "4.1.2"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"
has-symbols "^1.0.1"
object-keys "^1.1.1"
object.entries@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.3.tgz#c601c7f168b62374541a07ddbd3e2d5e4f7711a6"
integrity sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"
es-abstract "^1.18.0-next.1"
has "^1.0.3"
semver@^7.1.1: semver@^7.1.1:
version "7.3.5" version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
@@ -194,29 +52,6 @@ semver@^7.1.1:
dependencies: dependencies:
lru-cache "^6.0.0" lru-cache "^6.0.0"
serialize-error@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac"
integrity sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==
dependencies:
type-fest "^0.8.0"
string.prototype.trimend@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b"
integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"
string.prototype.trimstart@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa"
integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"
thenify-all@^1.0.0: thenify-all@^1.0.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
@@ -231,16 +66,6 @@ thenify-all@^1.0.0:
dependencies: dependencies:
any-promise "^1.0.0" any-promise "^1.0.0"
type-fest@^0.8.0:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
uuid@^3.0.1:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
yallist@^4.0.0: yallist@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"

View File

@@ -67,6 +67,7 @@ export class HotkeyInputModalComponent extends BaseComponent {
} }
this.timeoutProgress = Math.min(100, (performance.now() - this.lastKeyEvent) * 100 / INPUT_TIMEOUT) this.timeoutProgress = Math.min(100, (performance.now() - this.lastKeyEvent) * 100 / INPUT_TIMEOUT)
if (this.timeoutProgress === 100) { if (this.timeoutProgress === 100) {
clearInterval(this.keyTimeoutInterval!)
this.modalInstance.close(this.value) this.modalInstance.close(this.value)
} }
}, 25) }, 25)
@@ -74,13 +75,14 @@ export class HotkeyInputModalComponent extends BaseComponent {
} }
ngOnDestroy (): void { ngOnDestroy (): void {
clearInterval(this.keyTimeoutInterval!)
this.hotkeys.clearCurrentKeystrokes() this.hotkeys.clearCurrentKeystrokes()
this.hotkeys.enable() this.hotkeys.enable()
clearInterval(this.keyTimeoutInterval!)
super.ngOnDestroy() super.ngOnDestroy()
} }
close (): void { close (): void {
clearInterval(this.keyTimeoutInterval!)
this.modalInstance.dismiss() this.modalInstance.dismiss()
} }
} }

View File

@@ -40,7 +40,7 @@ button.btn.btn-outline-warning.btn-block(*ngIf='config.restartRequested', '(clic
i.fas.fa-sync i.fas.fa-sync
span Update span Update
.form-line(*ngIf='hostApp.platform !== Platform.Linux') .form-line(*ngIf='platform.isShellIntegrationSupported()')
.header .header
.title Shell integration .title Shell integration
.description Allows quickly opening a terminal in the selected folder .description Allows quickly opening a terminal in the selected folder
@@ -101,7 +101,10 @@ button.btn.btn-outline-warning.btn-block(*ngIf='config.restartRequested', '(clic
button.btn.btn-primary(disabled, *ngIf='!isConfigFileValid()') button.btn.btn-primary(disabled, *ngIf='!isConfigFileValid()')
i.fas.fa-exclamation-triangle.mr-2 i.fas.fa-exclamation-triangle.mr-2
| Invalid syntax | Invalid syntax
button.btn.btn-secondary.ml-auto((click)='showConfigFile()') button.btn.btn-secondary.ml-auto(
*ngIf='platform.getConfigPath()',
(click)='showConfigFile()'
)
i.fas.fa-external-link-square-alt.mr-2 i.fas.fa-external-link-square-alt.mr-2
| Show config file | Show config file

View File

@@ -3,14 +3,13 @@ import * as yaml from 'js-yaml'
import { debounce } from 'utils-decorators/dist/cjs' import { debounce } from 'utils-decorators/dist/cjs'
import { Component, Inject, Input, HostBinding, NgZone } from '@angular/core' import { Component, Inject, Input, HostBinding, NgZone } from '@angular/core'
import { import {
ElectronService,
ConfigService, ConfigService,
BaseTabComponent, BaseTabComponent,
HostAppService, HostAppService,
Platform, Platform,
HomeBaseService, HomeBaseService,
ShellIntegrationService,
UpdaterService, UpdaterService,
PlatformService,
} from 'terminus-core' } from 'terminus-core'
import { SettingsTabProvider } from '../api' import { SettingsTabProvider } from '../api'
@@ -35,10 +34,9 @@ export class SettingsTabComponent extends BaseTabComponent {
constructor ( constructor (
public config: ConfigService, public config: ConfigService,
private electron: ElectronService,
public hostApp: HostAppService, public hostApp: HostAppService,
public homeBase: HomeBaseService, public homeBase: HomeBaseService,
public shellIntegration: ShellIntegrationService, public platform: PlatformService,
public zone: NgZone, public zone: NgZone,
private updater: UpdaterService, private updater: UpdaterService,
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[], @Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
@@ -61,16 +59,16 @@ export class SettingsTabComponent extends BaseTabComponent {
} }
async ngOnInit () { async ngOnInit () {
this.isShellIntegrationInstalled = await this.shellIntegration.isInstalled() this.isShellIntegrationInstalled = await this.platform.isShellIntegrationInstalled()
} }
async toggleShellIntegration () { async toggleShellIntegration () {
if (!this.isShellIntegrationInstalled) { if (!this.isShellIntegrationInstalled) {
await this.shellIntegration.install() await this.platform.installShellIntegration()
} else { } else {
await this.shellIntegration.remove() await this.platform.uninstallShellIntegration()
} }
this.isShellIntegrationInstalled = await this.shellIntegration.isInstalled() this.isShellIntegrationInstalled = await this.platform.isShellIntegrationInstalled()
} }
ngOnDestroy () { ngOnDestroy () {
@@ -96,7 +94,7 @@ export class SettingsTabComponent extends BaseTabComponent {
} }
showConfigFile () { showConfigFile () {
this.electron.shell.showItemInFolder(this.config.path) this.platform.showItemInFolder(this.platform.getConfigPath()!)
} }
isConfigFileValid () { isConfigFileValid () {

View File

@@ -9,7 +9,7 @@ h3.mb-3 Window
) )
option(*ngFor='let theme of themes', [ngValue]='theme.name') {{theme.name}} option(*ngFor='let theme of themes', [ngValue]='theme.name') {{theme.name}}
.form-line .form-line(*ngIf='platform.supportsWindowControls')
.header .header
.title(*ngIf='hostApp.platform !== Platform.macOS') Acrylic background .title(*ngIf='hostApp.platform !== Platform.macOS') Acrylic background
.title(*ngIf='hostApp.platform === Platform.macOS') Vibrancy .title(*ngIf='hostApp.platform === Platform.macOS') Vibrancy
@@ -43,7 +43,7 @@ h3.mb-3 Window
) )
| Fluent | Fluent
.form-line .form-line(*ngIf='platform.supportsWindowControls')
.header .header
.title Opacity .title Opacity
input( input(
@@ -55,7 +55,7 @@ h3.mb-3 Window
step='0.01' step='0.01'
) )
.form-line .form-line(*ngIf='platform.supportsWindowControls')
.header .header
.title Window frame .title Window frame
.description Whether a custom window or an OS native window should be used .description Whether a custom window or an OS native window should be used
@@ -87,7 +87,7 @@ h3.mb-3 Window
) )
| Full | Full
.form-line .form-line(*ngIf='docking')
.header .header
.title Dock the terminal .title Dock the terminal
.description Snaps the window to a side of the screen .description Snaps the window to a side of the screen
@@ -133,7 +133,7 @@ h3.mb-3 Window
) )
| Bottom | Bottom
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Display on .title Display on
.description Snaps the window to a side of the screen .description Snaps the window to a side of the screen
@@ -158,7 +158,7 @@ h3.mb-3 Window
) )
| {{screen.name}} | {{screen.name}}
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Dock always on top .title Dock always on top
.description Keep docked terminal always on top .description Keep docked terminal always on top
@@ -167,7 +167,7 @@ h3.mb-3 Window
(ngModelChange)='saveConfiguration(); docking.dock()', (ngModelChange)='saveConfiguration(); docking.dock()',
) )
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Docked terminal size .title Docked terminal size
input( input(
@@ -179,7 +179,7 @@ h3.mb-3 Window
step='0.01' step='0.01'
) )
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Docked terminal space .title Docked terminal space
input( input(
@@ -191,7 +191,7 @@ h3.mb-3 Window
step='0.01' step='0.01'
) )
.ml-5.form-line(*ngIf='config.store.appearance.dock != "off"') .ml-5.form-line(*ngIf='docking && config.store.appearance.dock != "off"')
.header .header
.title Hide dock on blur .title Hide dock on blur
.description Hides the docked terminal when you click away. .description Hides the docked terminal when you click away.

View File

@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { debounce } from 'utils-decorators/dist/cjs' import { debounce } from 'utils-decorators/dist/cjs'
import { Component, Inject, NgZone } from '@angular/core' import { Component, Inject, NgZone, Optional } from '@angular/core'
import { import {
DockingService, DockingService,
ConfigService, ConfigService,
@@ -10,6 +10,8 @@ import {
isWindowsBuild, isWindowsBuild,
WIN_BUILD_FLUENT_BG_SUPPORTED, WIN_BUILD_FLUENT_BG_SUPPORTED,
BaseComponent, BaseComponent,
Screen,
PlatformService,
} from 'terminus-core' } from 'terminus-core'
@@ -19,24 +21,28 @@ import {
template: require('./windowSettingsTab.component.pug'), template: require('./windowSettingsTab.component.pug'),
}) })
export class WindowSettingsTabComponent extends BaseComponent { export class WindowSettingsTabComponent extends BaseComponent {
screens: any[] screens: Screen[]
Platform = Platform Platform = Platform
isFluentVibrancySupported = false isFluentVibrancySupported = false
constructor ( constructor (
public config: ConfigService, public config: ConfigService,
public docking: DockingService,
public hostApp: HostAppService, public hostApp: HostAppService,
public platform: PlatformService,
public zone: NgZone, public zone: NgZone,
@Inject(Theme) public themes: Theme[], @Inject(Theme) public themes: Theme[],
@Optional() public docking?: DockingService,
) { ) {
super() super()
this.screens = this.docking.getScreens()
this.themes = config.enabledServices(this.themes) this.themes = config.enabledServices(this.themes)
this.subscribeUntilDestroyed(hostApp.displaysChanged$, () => { if (this.docking) {
this.zone.run(() => this.screens = this.docking.getScreens()) this.subscribeUntilDestroyed(hostApp.displaysChanged$, () => {
}) this.zone.run(() => this.screens = this.docking!.getScreens())
})
this.screens = this.docking.getScreens()
}
this.isFluentVibrancySupported = isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED) this.isFluentVibrancySupported = isWindowsBuild(WIN_BUILD_FLUENT_BG_SUPPORTED)
} }

View File

@@ -61,7 +61,7 @@ h3.mt-5 Options
(ngModelChange)='config.save()', (ngModelChange)='config.save()',
) )
.form-line .form-line(*ngIf='hostApp.platform === Platform.Windows')
.header .header
.title WinSCP path .title WinSCP path
.descriptions When WinSCP is detected, you can launch an SCP session from the context menu. .descriptions When WinSCP is detected, you can launch an SCP session from the context menu.

View File

@@ -2,7 +2,7 @@
import deepClone from 'clone-deep' import deepClone from 'clone-deep'
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, ElectronService, HostAppService } from 'terminus-core' import { ConfigService, ElectronService, HostAppService, Platform } from 'terminus-core'
import { PasswordStorageService } from '../services/passwordStorage.service' import { PasswordStorageService } from '../services/passwordStorage.service'
import { SSHConnection } from '../api' import { SSHConnection } from '../api'
import { EditConnectionModalComponent } from './editConnectionModal.component' import { EditConnectionModalComponent } from './editConnectionModal.component'
@@ -23,11 +23,12 @@ export class SSHSettingsTabComponent {
childGroups: SSHConnectionGroup[] childGroups: SSHConnectionGroup[]
groupCollapsed: Record<string, boolean> = {} groupCollapsed: Record<string, boolean> = {}
filter = '' filter = ''
Platform = Platform
constructor ( constructor (
public config: ConfigService, public config: ConfigService,
public hostApp: HostAppService,
private electron: ElectronService, private electron: ElectronService,
private hostApp: HostAppService,
private ngbModal: NgbModal, private ngbModal: NgbModal,
private passwordStorage: PasswordStorageService, private passwordStorage: PasswordStorageService,
) { ) {

View File

@@ -10,7 +10,7 @@ import { exec } from 'child_process'
import * as path from 'path' import * as path from 'path'
import * as sshpk from 'sshpk' import * as sshpk from 'sshpk'
import { Subject, Observable } from 'rxjs' import { Subject, Observable } from 'rxjs'
import { HostAppService, Platform, Logger, LogService, AppService, SelectorOption, ConfigService, NotificationsService } from 'terminus-core' import { HostAppService, Platform, Logger, LogService, AppService, SelectorOption, ConfigService, NotificationsService, PlatformService } from 'terminus-core'
import { SettingsTabComponent } from 'terminus-settings' import { SettingsTabComponent } from 'terminus-settings'
import { ALGORITHM_BLACKLIST, ForwardedPort, SSHConnection, SSHSession } from '../api' import { ALGORITHM_BLACKLIST, ForwardedPort, SSHConnection, SSHSession } from '../api'
import { PromptModalComponent } from '../components/promptModal.component' import { PromptModalComponent } from '../components/promptModal.component'
@@ -20,15 +20,6 @@ import { ChildProcess } from 'node:child_process'
const WINDOWS_OPENSSH_AGENT_PIPE = '\\\\.\\pipe\\openssh-ssh-agent' const WINDOWS_OPENSSH_AGENT_PIPE = '\\\\.\\pipe\\openssh-ssh-agent'
try {
var windowsProcessTreeNative = require('windows-process-tree/build/Release/windows_process_tree.node') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch { }
// eslint-disable-next-line @typescript-eslint/no-type-alias
export type SSHLogCallback = (message: string) => void
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class SSHService { export class SSHService {
private logger: Logger private logger: Logger
@@ -42,6 +33,7 @@ export class SSHService {
private notifications: NotificationsService, private notifications: NotificationsService,
private app: AppService, private app: AppService,
private config: ConfigService, private config: ConfigService,
private platform: PlatformService,
) { ) {
this.logger = log.create('ssh') this.logger = log.create('ssh')
} }
@@ -197,13 +189,7 @@ export class SSHService {
if (await fs.exists(WINDOWS_OPENSSH_AGENT_PIPE)) { if (await fs.exists(WINDOWS_OPENSSH_AGENT_PIPE)) {
agent = WINDOWS_OPENSSH_AGENT_PIPE agent = WINDOWS_OPENSSH_AGENT_PIPE
} else { } else {
// eslint-disable-next-line @typescript-eslint/no-shadow if (await this.platform.isProcessRunning('pageant.exe')) {
const pageantRunning = await new Promise<boolean>(resolve => {
windowsProcessTreeNative.getProcessList(list => { // eslint-disable-line block-scoped-var
resolve(list.some(x => x.name === 'pageant.exe'))
}, 0)
})
if (pageantRunning) {
agent = 'pageant' agent = 'pageant'
} }
} }

View File

@@ -1,27 +1,20 @@
import type { MenuItemConstructorOptions } from 'electron'
import { execFile } from 'child_process'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, HostAppService, Platform } from 'terminus-core' import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, HostAppService, Platform, PlatformService, MenuItemOptions } from 'terminus-core'
import { SSHTabComponent } from './components/sshTab.component' import { SSHTabComponent } from './components/sshTab.component'
import { PasswordStorageService } from './services/passwordStorage.service' import { PasswordStorageService } from './services/passwordStorage.service'
import { SSHConnection } from './api' import { SSHConnection } from './api'
/* eslint-disable block-scoped-var */
try {
var wnr = require('windows-native-registry') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
} catch { }
/** @hidden */ /** @hidden */
@Injectable() @Injectable()
export class WinSCPContextMenu extends TabContextMenuItemProvider { export class WinSCPContextMenu extends TabContextMenuItemProvider {
weight = 10 weight = 10
private detectedPath?: string private detectedPath: string | null
constructor ( constructor (
private hostApp: HostAppService, private hostApp: HostAppService,
private config: ConfigService, private config: ConfigService,
private platform: PlatformService,
private passwordStorage: PasswordStorageService, private passwordStorage: PasswordStorageService,
) { ) {
super() super()
@@ -30,14 +23,10 @@ export class WinSCPContextMenu extends TabContextMenuItemProvider {
return return
} }
const key = wnr.getRegistryKey(wnr.HK.CR, 'WinSCP.Url\\DefaultIcon') this.detectedPath = platform.getWinSCPPath()
if (key?.['']) {
this.detectedPath = key[''].value?.split(',')[0]
this.detectedPath = this.detectedPath?.substring(1, this.detectedPath.length - 1)
}
} }
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
if (this.hostApp.platform !== Platform.Windows || tabHeader) { if (this.hostApp.platform !== Platform.Windows || tabHeader) {
return [] return []
} }
@@ -81,6 +70,6 @@ export class WinSCPContextMenu extends TabContextMenuItemProvider {
args.push('/privatekey') args.push('/privatekey')
args.push(connection.privateKey) args.push(connection.privateKey)
} }
execFile(path, args) this.platform.exec(path, args)
} }
} }

View File

@@ -1,10 +1,9 @@
import type { MenuItemConstructorOptions } from 'electron'
import { Observable, Subject, Subscription } from 'rxjs' import { Observable, Subject, Subscription } from 'rxjs'
import { first } from 'rxjs/operators' import { first } from 'rxjs/operators'
import colors from 'ansi-colors' import colors from 'ansi-colors'
import { NgZone, OnInit, OnDestroy, Injector, ViewChild, HostBinding, Input, ElementRef, InjectFlags } from '@angular/core' import { NgZone, OnInit, OnDestroy, Injector, ViewChild, HostBinding, Input, ElementRef, InjectFlags } from '@angular/core'
import { trigger, transition, style, animate, AnimationTriggerMetadata } from '@angular/animations' import { trigger, transition, style, animate, AnimationTriggerMetadata } from '@angular/animations'
import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, NotificationsService, Platform, LogService, Logger, TabContextMenuItemProvider, SplitTabComponent, SubscriptionContainer } from 'terminus-core' import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, NotificationsService, Platform, LogService, Logger, TabContextMenuItemProvider, SplitTabComponent, SubscriptionContainer, MenuItemOptions, PlatformService } from 'terminus-core'
import { BaseSession } from '../session' import { BaseSession } from '../session'
import { TerminalFrontendService } from '../services/terminalFrontend.service' import { TerminalFrontendService } from '../services/terminalFrontend.service'
@@ -84,6 +83,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
protected hostApp: HostAppService protected hostApp: HostAppService
protected hotkeys: HotkeysService protected hotkeys: HotkeysService
protected electron: ElectronService protected electron: ElectronService
protected platform: PlatformService
protected terminalContainersService: TerminalFrontendService protected terminalContainersService: TerminalFrontendService
protected notifications: NotificationsService protected notifications: NotificationsService
protected log: LogService protected log: LogService
@@ -136,6 +136,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
this.hostApp = injector.get(HostAppService) this.hostApp = injector.get(HostAppService)
this.hotkeys = injector.get(HotkeysService) this.hotkeys = injector.get(HotkeysService)
this.electron = injector.get(ElectronService) this.electron = injector.get(ElectronService)
this.platform = injector.get(PlatformService)
this.terminalContainersService = injector.get(TerminalFrontendService) this.terminalContainersService = injector.get(TerminalFrontendService)
this.notifications = injector.get(NotificationsService) this.notifications = injector.get(NotificationsService)
this.log = injector.get(LogService) this.log = injector.get(LogService)
@@ -312,8 +313,8 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
} }
} }
async buildContextMenu (): Promise<MenuItemConstructorOptions[]> { async buildContextMenu (): Promise<MenuItemOptions[]> {
let items: MenuItemConstructorOptions[] = [] let items: MenuItemOptions[] = []
for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this)))) { for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this)))) {
items = items.concat(section) items = items.concat(section)
items.push({ type: 'separator' }) items.push({ type: 'separator' })
@@ -498,6 +499,16 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
this.termContainerSubscriptions.cancelAll() this.termContainerSubscriptions.cancelAll()
} }
protected async handleRightClick (event: MouseEvent): Promise<void> {
event.preventDefault()
event.stopPropagation()
if (this.config.store.terminal.rightClick === 'menu') {
this.platform.popupContextMenu(await this.buildContextMenu(), event)
} else if (this.config.store.terminal.rightClick === 'paste') {
this.paste()
}
}
protected attachTermContainerHandlers (): void { protected attachTermContainerHandlers (): void {
this.detachTermContainerHandlers() this.detachTermContainerHandlers()
@@ -531,13 +542,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
return return
} }
if (event.which === 3 || event.which === 1 && event.ctrlKey) { if (event.which === 3 || event.which === 1 && event.ctrlKey) {
if (this.config.store.terminal.rightClick === 'menu') { this.handleRightClick(event)
this.hostApp.popupContextMenu(await this.buildContextMenu())
} else if (this.config.store.terminal.rightClick === 'paste') {
this.paste()
}
event.preventDefault()
event.stopPropagation()
return return
} }
} }

View File

@@ -1,4 +1,4 @@
import type { MenuItemConstructorOptions } from 'electron' import type { MenuItemOptions } from 'terminus-core'
import { BaseTerminalTabComponent } from './baseTerminalTab.component' import { BaseTerminalTabComponent } from './baseTerminalTab.component'
/** /**
@@ -8,5 +8,5 @@ import { BaseTerminalTabComponent } from './baseTerminalTab.component'
export abstract class TerminalContextMenuItemProvider { export abstract class TerminalContextMenuItemProvider {
weight: number weight: number
abstract getItems (tab: BaseTerminalTabComponent): Promise<MenuItemConstructorOptions[]> abstract getItems (tab: BaseTerminalTabComponent): Promise<MenuItemOptions[]>
} }

View File

@@ -2,11 +2,9 @@
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { debounce } from 'utils-decorators/dist/cjs' import { debounce } from 'utils-decorators/dist/cjs'
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators' import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'
import { exec } from 'mz/child_process'
const fontManager = require('fontmanager-redux') // eslint-disable-line
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { ConfigService, HostAppService, Platform, getCSSFontFamily } from 'terminus-core' import { ConfigService, getCSSFontFamily, PlatformService } from 'terminus-core'
/** @hidden */ /** @hidden */
@Component({ @Component({
@@ -17,26 +15,12 @@ export class AppearanceSettingsTabComponent {
fonts: string[] = [] fonts: string[] = []
constructor ( constructor (
private hostApp: HostAppService,
public config: ConfigService, public config: ConfigService,
private platform: PlatformService,
) { } ) { }
async ngOnInit () { async ngOnInit () {
if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) { this.fonts = await this.platform.listFonts()
const fonts = await new Promise<any[]>((resolve) => fontManager.findFonts({ monospace: true }, resolve))
this.fonts = fonts.map(x => x.family.trim())
this.fonts.sort()
}
if (this.hostApp.platform === Platform.Linux) {
exec('fc-list :spacing=mono').then(([stdout, _]) => {
this.fonts = stdout.toString()
.split('\n')
.filter(x => !!x)
.map(x => x.split(':')[1].trim())
.map(x => x.split(',')[0].trim())
this.fonts.sort()
})
}
} }
fontAutocomplete = (text$: Observable<string>) => { fontAutocomplete = (text$: Observable<string>) => {

View File

@@ -1,6 +1,6 @@
h3.mb-3 Terminal h3.mb-3 Terminal
.form-line .form-line(*ngIf='hostApp.platform !== Platform.Web')
.header .header
.title Frontend .title Frontend
.description Switches terminal frontend implementation (experimental) .description Switches terminal frontend implementation (experimental)
@@ -86,7 +86,7 @@ h3.mb-3 Terminal
(ngModelChange)='config.save()', (ngModelChange)='config.save()',
) )
.form-line .form-line(*ngIf='hostApp.platform !== Platform.Web')
.header .header
.title Auto-open a terminal on app start .title Auto-open a terminal on app start

View File

@@ -1,19 +1,22 @@
import { execFile } from 'mz/child_process' import { execFile } from 'mz/child_process'
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { ConfigService, ElectronService } from 'terminus-core' import { ConfigService, HostAppService, Platform, PlatformService } from 'terminus-core'
/** @hidden */ /** @hidden */
@Component({ @Component({
template: require('./terminalSettingsTab.component.pug'), template: require('./terminalSettingsTab.component.pug'),
}) })
export class TerminalSettingsTabComponent { export class TerminalSettingsTabComponent {
Platform = Platform
constructor ( constructor (
public config: ConfigService, public config: ConfigService,
private electron: ElectronService, public hostApp: HostAppService,
private platform: PlatformService,
) { } ) { }
openWSLVolumeMixer (): void { openWSLVolumeMixer (): void {
this.electron.shell.openPath('sndvol.exe') this.platform.openPath('sndvol.exe')
execFile('wsl.exe', ['tput', 'bel']) execFile('wsl.exe', ['tput', 'bel'])
} }
} }

View File

@@ -1,6 +1,6 @@
import { Injector } from '@angular/core'
import { Observable, Subject, AsyncSubject, ReplaySubject, BehaviorSubject } from 'rxjs' import { Observable, Subject, AsyncSubject, ReplaySubject, BehaviorSubject } from 'rxjs'
import { ResizeEvent } from '../api/interfaces' import { ResizeEvent } from '../api/interfaces'
import { ConfigService, ThemesService, HotkeysService } from 'terminus-core'
export interface SearchOptions { export interface SearchOptions {
regex?: boolean regex?: boolean
@@ -13,10 +13,6 @@ export interface SearchOptions {
* Extend to add support for a different VT frontend implementation * Extend to add support for a different VT frontend implementation
*/ */
export abstract class Frontend { export abstract class Frontend {
configService: ConfigService
themesService: ThemesService
hotkeysService: HotkeysService
enableResizing = true enableResizing = true
protected ready = new AsyncSubject<void>() protected ready = new AsyncSubject<void>()
protected title = new ReplaySubject<string>(1) protected title = new ReplaySubject<string>(1)
@@ -40,6 +36,8 @@ export abstract class Frontend {
get dragOver$ (): Observable<DragEvent> { return this.dragOver } get dragOver$ (): Observable<DragEvent> { return this.dragOver }
get drop$ (): Observable<DragEvent> { return this.drop } get drop$ (): Observable<DragEvent> { return this.drop }
constructor (protected injector: Injector) { }
destroy (): void { destroy (): void {
for (const o of [ for (const o of [
this.ready, this.ready,

View File

@@ -1,6 +1,7 @@
import { Injector } from '@angular/core'
import { ConfigService, getCSSFontFamily, ThemesService } from 'terminus-core'
import { Frontend, SearchOptions } from './frontend' import { Frontend, SearchOptions } from './frontend'
import { hterm, preferenceManager } from './hterm' import { hterm, preferenceManager } from './hterm'
import { getCSSFontFamily } from 'terminus-core'
/** @hidden */ /** @hidden */
export class HTermFrontend extends Frontend { export class HTermFrontend extends Frontend {
@@ -13,6 +14,15 @@ export class HTermFrontend extends Frontend {
private configuredBackgroundColor = 'transparent' private configuredBackgroundColor = 'transparent'
private zoom = 0 private zoom = 0
private configService: ConfigService
private themesService: ThemesService
constructor (injector: Injector) {
super(injector)
this.configService = injector.get(ConfigService)
this.themesService = injector.get(ThemesService)
}
async attach (host: HTMLElement): Promise<void> { async attach (host: HTMLElement): Promise<void> {
if (!this.initialized) { if (!this.initialized) {
this.init() this.init()

View File

@@ -1,4 +1,5 @@
import { getCSSFontFamily } from 'terminus-core' import { Injector } from '@angular/core'
import { ConfigService, getCSSFontFamily, HostAppService, HotkeysService, Platform, PlatformService } from 'terminus-core'
import { Frontend, SearchOptions } from './frontend' import { Frontend, SearchOptions } from './frontend'
import { Terminal, ITheme } from 'xterm' import { Terminal, ITheme } from 'xterm'
import { FitAddon } from 'xterm-addon-fit' import { FitAddon } from 'xterm-addon-fit'
@@ -39,8 +40,18 @@ export class XTermFrontend extends Frontend {
private opened = false private opened = false
private resizeObserver?: any private resizeObserver?: any
constructor () { private configService: ConfigService
super() private hotkeysService: HotkeysService
private platformService: PlatformService
private hostApp: HostAppService
constructor (injector: Injector) {
super(injector)
this.configService = injector.get(ConfigService)
this.hotkeysService = injector.get(HotkeysService)
this.platformService = injector.get(PlatformService)
this.hostApp = injector.get(HostAppService)
this.xterm = new Terminal({ this.xterm = new Terminal({
allowTransparency: true, allowTransparency: true,
windowsMode: process.platform === 'win32', windowsMode: process.platform === 'win32',
@@ -88,9 +99,11 @@ export class XTermFrontend extends Frontend {
} }
this.xterm.attachCustomKeyEventHandler((event: KeyboardEvent) => { this.xterm.attachCustomKeyEventHandler((event: KeyboardEvent) => {
if (event.getModifierState('Meta') && event.key.toLowerCase() === 'v') { if (this.hostApp.platform !== Platform.Web) {
event.preventDefault() if (event.getModifierState('Meta') && event.key.toLowerCase() === 'v') {
return false event.preventDefault()
return false
}
} }
if (event.getModifierState('Meta') && event.key.startsWith('Arrow')) { if (event.getModifierState('Meta') && event.key.startsWith('Arrow')) {
return false return false
@@ -167,6 +180,10 @@ export class XTermFrontend extends Frontend {
host.addEventListener('mousedown', event => this.mouseEvent.next(event)) host.addEventListener('mousedown', event => this.mouseEvent.next(event))
host.addEventListener('mouseup', event => this.mouseEvent.next(event)) host.addEventListener('mouseup', event => this.mouseEvent.next(event))
host.addEventListener('mousewheel', event => this.mouseEvent.next(event as MouseEvent)) host.addEventListener('mousewheel', event => this.mouseEvent.next(event as MouseEvent))
host.addEventListener('contextmenu', event => {
event.preventDefault()
event.stopPropagation()
})
this.resizeObserver = new window['ResizeObserver'](() => setTimeout(() => this.resizeHandler())) this.resizeObserver = new window['ResizeObserver'](() => setTimeout(() => this.resizeHandler()))
this.resizeObserver.observe(host) this.resizeObserver.observe(host)
@@ -190,12 +207,12 @@ export class XTermFrontend extends Frontend {
copySelection (): void { copySelection (): void {
const text = this.getSelection() const text = this.getSelection()
if (text.length < 1024 * 32) { if (text.length < 1024 * 32) {
require('@electron/remote').clipboard.write({ this.platformService.setClipboard({
text: this.getSelection(), text: this.getSelection(),
html: this.getSelectionAsHTML(), html: this.getSelectionAsHTML(),
}) })
} else { } else {
require('@electron/remote').clipboard.write({ this.platformService.setClipboard({
text: this.getSelection(), text: this.getSelection(),
}) })
} }

View File

@@ -25,7 +25,6 @@ import { PathDropDecorator } from './features/pathDrop'
import { ZModemDecorator } from './features/zmodem' import { ZModemDecorator } from './features/zmodem'
import { TerminalConfigProvider } from './config' import { TerminalConfigProvider } from './config'
import { TerminalHotkeyProvider } from './hotkeys' import { TerminalHotkeyProvider } from './hotkeys'
import { HyperColorSchemes } from './colorSchemes'
import { CopyPasteContextMenu, LegacyContextMenu } from './tabContextMenu' import { CopyPasteContextMenu, LegacyContextMenu } from './tabContextMenu'
import { hterm } from './frontends/hterm' import { hterm } from './frontends/hterm'
@@ -50,7 +49,6 @@ import { TerminalCLIHandler } from './cli'
{ provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true }, { provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true },
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true }, { provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
{ provide: TerminalColorSchemeProvider, useClass: HyperColorSchemes, multi: true },
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true }, { provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
{ provide: TerminalDecorator, useClass: ZModemDecorator, multi: true }, { provide: TerminalDecorator, useClass: ZModemDecorator, multi: true },
{ provide: TerminalDecorator, useClass: DebugDecorator, multi: true }, { provide: TerminalDecorator, useClass: DebugDecorator, multi: true },

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core' import { Injectable, Injector } from '@angular/core'
import { ConfigService, ThemesService, HotkeysService } from 'terminus-core' import { ConfigService } from 'terminus-core'
import { Frontend } from '../frontends/frontend' import { Frontend } from '../frontends/frontend'
import { HTermFrontend } from '../frontends/htermFrontend' import { HTermFrontend } from '../frontends/htermFrontend'
import { XTermFrontend, XTermWebGLFrontend } from '../frontends/xtermFrontend' import { XTermFrontend, XTermWebGLFrontend } from '../frontends/xtermFrontend'
@@ -12,8 +12,7 @@ export class TerminalFrontendService {
/** @hidden */ /** @hidden */
private constructor ( private constructor (
private config: ConfigService, private config: ConfigService,
private themes: ThemesService, private injector: Injector,
private hotkeys: HotkeysService,
) { } ) { }
getFrontend (session?: BaseSession|null): Frontend { getFrontend (session?: BaseSession|null): Frontend {
@@ -22,10 +21,7 @@ export class TerminalFrontendService {
xterm: XTermFrontend, xterm: XTermFrontend,
'xterm-webgl': XTermWebGLFrontend, 'xterm-webgl': XTermWebGLFrontend,
hterm: HTermFrontend, hterm: HTermFrontend,
}[this.config.store.terminal.frontend]() }[this.config.store.terminal.frontend](this.injector)
frontend.configService = this.config
frontend.themesService = this.themes
frontend.hotkeysService = this.hotkeys
return frontend return frontend
} }
if (!this.containers.has(session)) { if (!this.containers.has(session)) {

View File

@@ -1,6 +1,5 @@
import { MenuItemConstructorOptions } from 'electron'
import { Injectable, NgZone, Optional, Inject } from '@angular/core' import { Injectable, NgZone, Optional, Inject } from '@angular/core'
import { BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, NotificationsService } from 'terminus-core' import { BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, NotificationsService, MenuItemOptions } from 'terminus-core'
import { BaseTerminalTabComponent } from './api/baseTerminalTab.component' import { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
import { TerminalContextMenuItemProvider } from './api/contextMenuProvider' import { TerminalContextMenuItemProvider } from './api/contextMenuProvider'
@@ -16,7 +15,7 @@ export class CopyPasteContextMenu extends TabContextMenuItemProvider {
super() super()
} }
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
if (tabHeader) { if (tabHeader) {
return [] return []
} }
@@ -56,12 +55,12 @@ export class LegacyContextMenu extends TabContextMenuItemProvider {
super() super()
} }
async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> { async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
if (!this.contextMenuProviders) { if (!this.contextMenuProviders) {
return [] return []
} }
if (tab instanceof BaseTerminalTabComponent) { if (tab instanceof BaseTerminalTabComponent) {
let items: MenuItemConstructorOptions[] = [] let items: MenuItemOptions[] = []
for (const p of this.contextMenuProviders) { for (const p of this.contextMenuProviders) {
items = items.concat(await p.getItems(tab)) items = items.concat(await p.getItems(tab))
} }

1
terminus-web/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
dist

27
terminus-web/package.json Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "terminus-web",
"version": "1.0.135-nightly.0",
"description": "Web-specific bindings",
"keywords": [
"terminus-builtin-plugin"
],
"main": "dist/index.js",
"typings": "typings/index.d.ts",
"scripts": {
"build": "webpack --progress --color",
"watch": "webpack --progress --color --watch"
},
"files": [
"dist"
],
"author": "Eugene Pankov",
"license": "MIT",
"peerDependencies": {
"@angular/core": "^9.1.9"
},
"devDependencies": {
"@vaadin/vaadin-context-menu": "^5.0.0",
"bootstrap": "^4.1.3",
"copy-text-to-clipboard": "^3.0.1"
}
}

17
terminus-web/src/index.ts Normal file
View File

@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core'
import { LogService, PlatformService, UpdaterService } from 'terminus-core'
import { WebPlatformService } from './platform'
import { ConsoleLogService } from './services/log.service'
import { NullUpdaterService } from './services/updater.service'
import './styles.scss'
@NgModule({
providers: [
{ provide: PlatformService, useClass: WebPlatformService },
{ provide: LogService, useClass: ConsoleLogService },
{ provide: UpdaterService, useClass: NullUpdaterService },
],
})
export default class WebModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class

View File

@@ -0,0 +1,76 @@
import '@vaadin/vaadin-context-menu/vaadin-context-menu.js'
import copyToClipboard from 'copy-text-to-clipboard'
import { Injectable } from '@angular/core'
import { PlatformService, ClipboardContent, MenuItemOptions } from 'terminus-core'
// eslint-disable-next-line no-duplicate-imports
import type { ContextMenuElement, ContextMenuItem } from '@vaadin/vaadin-context-menu/vaadin-context-menu.js'
import './styles.scss'
@Injectable()
export class WebPlatformService extends PlatformService {
private menu: ContextMenuElement
private contextMenuHandlers = new Map<ContextMenuItem, () => void>()
constructor () {
super()
this.menu = window.document.createElement('vaadin-context-menu')
this.menu.addEventListener('item-selected', e => {
this.contextMenuHandlers.get(e.detail.value)?.()
})
document.body.appendChild(this.menu)
console.log(require('./styles.scss'))
}
setClipboard (content: ClipboardContent): void {
copyToClipboard(content.text)
}
async loadConfig (): Promise<string> {
return window['terminusConfig']
}
async saveConfig (content: string): Promise<void> {
console.log('config save', content)
}
getOSRelease (): string {
return '1.0'
}
openExternal (url: string): void {
window.open(url)
}
getAppVersion (): string {
return '1.0-web'
}
async listFonts (): Promise<string[]> {
return []
}
popupContextMenu (menu: MenuItemOptions[], event?: MouseEvent): void {
this.contextMenuHandlers.clear()
this.menu.items = menu
.filter(x => x.type !== 'separator')
.map(x => this.remapMenuItem(x))
setTimeout(() => {
this.menu.open(event)
}, 10)
}
private remapMenuItem (item: MenuItemOptions): ContextMenuItem {
const cmi = {
text: item.label,
disabled: !(item.enabled ?? true),
checked: item.checked,
children: item.submenu?.map(i => this.remapMenuItem(i)),
}
if (item.click) {
this.contextMenuHandlers.set(cmi, item.click)
}
return cmi
}
}

View File

@@ -0,0 +1,9 @@
import { Injectable } from '@angular/core'
import { ConsoleLogger, Logger } from 'terminus-core'
@Injectable({ providedIn: 'root' })
export class ConsoleLogService {
create (name: string): Logger {
return new ConsoleLogger(name)
}
}

View File

@@ -0,0 +1,10 @@
import { UpdaterService } from 'terminus-core'
export class NullUpdaterService extends UpdaterService {
async check (): Promise<boolean> {
return false
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
async update (): Promise<void> { }
}

View File

@@ -0,0 +1,11 @@
@import "../../terminus-core/src/theme.vars.scss";
html.terminus {
--lumo-primary-text-color: #{$body-color};
--lumo-base-color: #{$body-bg};
--lumo-body-text-color: #{$body-color};
--lumo-tint-5pct: #{$body-bg};
--lumo-font-family: #{$font-family-sans-serif};
--lumo-font-size-m: #{$font-size-base};
--lumo-box-shadow-m: #{$dropdown-box-shadow};
}

View File

@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"exclude": ["node_modules", "dist"],
"compilerOptions": {
"baseUrl": "src"
}
}

View File

@@ -0,0 +1,14 @@
{
"extends": "../tsconfig.json",
"exclude": ["node_modules", "dist", "typings"],
"compilerOptions": {
"baseUrl": "src",
"emitDeclarationOnly": true,
"declaration": true,
"declarationDir": "./typings",
"paths": {
"terminus-*": ["../../terminus-*"],
"*": ["../../app/node_modules/*"]
}
}
}

View File

@@ -0,0 +1,5 @@
const config = require('../webpack.plugin.config')
module.exports = config({
name: 'web',
dirname: __dirname,
})

178
terminus-web/yarn.lock Normal file
View File

@@ -0,0 +1,178 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@polymer/iron-flex-layout@^3.0.0-pre.26":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@polymer/iron-flex-layout/-/iron-flex-layout-3.0.1.tgz#36f9e1a8eb792d279b2bc75d362628721ad37f0c"
integrity sha512-7gB869czArF+HZcPTVSgvA7tXYFze9EKckvM95NB7SqYF+NnsQyhoXgKnpFwGyo95lUjUW9TFDLUwDXnCYFtkw==
dependencies:
"@polymer/polymer" "^3.0.0"
"@polymer/iron-icon@^3.0.0":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@polymer/iron-icon/-/iron-icon-3.0.1.tgz#93211c39d8825fe4965a68419566036c1df291eb"
integrity sha512-QLPwirk+UPZNaLnMew9VludXA4CWUCenRewgEcGYwdzVgDPCDbXxy6vRJjmweZobMQv/oVLppT2JZtJFnPxX6g==
dependencies:
"@polymer/iron-flex-layout" "^3.0.0-pre.26"
"@polymer/iron-meta" "^3.0.0-pre.26"
"@polymer/polymer" "^3.0.0"
"@polymer/iron-iconset-svg@^3.0.0":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@polymer/iron-iconset-svg/-/iron-iconset-svg-3.0.1.tgz#568d6e7dbc120299dae63be3600aeba0d30ddbea"
integrity sha512-XNwURbNHRw6u2fJe05O5fMYye6GSgDlDqCO+q6K1zAnKIrpgZwf2vTkBd5uCcZwsN0FyCB3mvNZx4jkh85dRDw==
dependencies:
"@polymer/iron-meta" "^3.0.0-pre.26"
"@polymer/polymer" "^3.0.0"
"@polymer/iron-media-query@^3.0.0":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@polymer/iron-media-query/-/iron-media-query-3.0.1.tgz#5cd8a1c1e8c9b8bafd3dd5da14e0f8d2cfa76d83"
integrity sha512-czUX1pm1zfmfcZtq5J57XFkcobBv08Y50exp0/3v8Bos5VL/jv2tU0RwiTfDBxUMhjicGbgwEBFQPY2V5DMzyw==
dependencies:
"@polymer/polymer" "^3.0.0"
"@polymer/iron-meta@^3.0.0-pre.26":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@polymer/iron-meta/-/iron-meta-3.0.1.tgz#7f140628d127b0a284f882f1bb323a261bc125f5"
integrity sha512-pWguPugiLYmWFV9UWxLWzZ6gm4wBwQdDy4VULKwdHCqR7OP7u98h+XDdGZsSlDPv6qoryV/e3tGHlTIT0mbzJA==
dependencies:
"@polymer/polymer" "^3.0.0"
"@polymer/polymer@^3.0.0":
version "3.4.1"
resolved "https://registry.yarnpkg.com/@polymer/polymer/-/polymer-3.4.1.tgz#333bef25711f8411bb5624fb3eba8212ef8bee96"
integrity sha512-KPWnhDZibtqKrUz7enIPOiO4ZQoJNOuLwqrhV2MXzIt3VVnUVJVG5ORz4Z2sgO+UZ+/UZnPD0jqY+jmw/+a9mQ==
dependencies:
"@webcomponents/shadycss" "^1.9.1"
"@vaadin/vaadin-context-menu@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-context-menu/-/vaadin-context-menu-5.0.0.tgz#c8ef7a78f107c9824ef90c9331159d5f2818fdac"
integrity sha512-+OIFseHPRy1QraQFLUT/jxCKlvsOVg/NaaHhfonTZdwrO31CTpKGZFCDB0Gvos2W9WdXa6WI12DRJLZF7Wcr0g==
dependencies:
"@polymer/iron-media-query" "^3.0.0"
"@polymer/polymer" "^3.0.0"
"@vaadin/vaadin-element-mixin" "^2.4.1"
"@vaadin/vaadin-item" "^3.0.0"
"@vaadin/vaadin-list-box" "^2.0.0"
"@vaadin/vaadin-lumo-styles" "^1.6.1"
"@vaadin/vaadin-material-styles" "^1.3.2"
"@vaadin/vaadin-overlay" "^3.5.0"
"@vaadin/vaadin-themable-mixin" "^1.6.2"
"@vaadin/vaadin-development-mode-detector@^2.0.0":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-development-mode-detector/-/vaadin-development-mode-detector-2.0.4.tgz#f49c8009856bead92d248377c36b295b5aae78e5"
integrity sha512-S+PaFrZpK8uBIOnIHxjntTrgumd5ztuCnZww96ydGKXgo9whXfZsbMwDuD/102a/IuPUMyF+dh/n3PbWzJ6igA==
"@vaadin/vaadin-element-mixin@^2.4.0", "@vaadin/vaadin-element-mixin@^2.4.1":
version "2.4.2"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-element-mixin/-/vaadin-element-mixin-2.4.2.tgz#3c8040a8e756bc274b7777723b1fba2b9895cd41"
integrity sha512-VSDVK0XUsFe/RohpwSzQwgqb2Pwpok6sDNhIDS4CARr3HPhq2voMzT/FowFbkEy0J1hFtN/ZfC7tkv3kdEKKIQ==
dependencies:
"@polymer/polymer" "^3.0.0"
"@vaadin/vaadin-development-mode-detector" "^2.0.0"
"@vaadin/vaadin-usage-statistics" "^2.1.0"
"@vaadin/vaadin-item@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-item/-/vaadin-item-3.0.0.tgz#abbeadd752dd46351217b94351c05bf93d6fad1c"
integrity sha512-AcSqaOd2LJr51JWT3j7GcdbU54oBHAE8xlfeN0O5OdCcsAQJLekkNJ3uxt8Kr3ZP99nnEFTZ1WKcQtEufSAVhA==
dependencies:
"@polymer/polymer" "^3.0.0"
"@vaadin/vaadin-element-mixin" "^2.4.1"
"@vaadin/vaadin-lumo-styles" "^1.6.1"
"@vaadin/vaadin-material-styles" "^1.3.2"
"@vaadin/vaadin-themable-mixin" "^1.6.2"
"@vaadin/vaadin-list-box@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-list-box/-/vaadin-list-box-2.0.0.tgz#783e1abf1dd50609a7a00a6de2acd2394a1d808e"
integrity sha512-3WU7oU3cgrp7jPet1aAjAIJSQqdVbKAqIPxOH3LsLX7QQAYnWvUwQY+UApPHiJIjpnKF0PfYiIZe1o6adqKivg==
dependencies:
"@polymer/polymer" "^3.0.0"
"@vaadin/vaadin-element-mixin" "^2.4.1"
"@vaadin/vaadin-item" "^3.0.0"
"@vaadin/vaadin-list-mixin" "^2.5.0"
"@vaadin/vaadin-lumo-styles" "^1.6.1"
"@vaadin/vaadin-material-styles" "^1.3.2"
"@vaadin/vaadin-themable-mixin" "^1.6.1"
"@vaadin/vaadin-list-mixin@^2.5.0":
version "2.5.1"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-list-mixin/-/vaadin-list-mixin-2.5.1.tgz#f6ab60cc658900d3eb7bfff18cf42d769374b659"
integrity sha512-XcMzQ0hJnK/AAiV+bW95nwJgmMIrXUBiSDwM+uvfurcBKqPyM4pm3sj8imh8zXSTfpN4HSjMnrLWU1ZfR330vg==
dependencies:
"@polymer/polymer" "^3.0.0"
"@vaadin/vaadin-element-mixin" "^2.4.1"
"@vaadin/vaadin-lumo-styles@^1.3.0", "@vaadin/vaadin-lumo-styles@^1.6.1":
version "1.6.1"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-lumo-styles/-/vaadin-lumo-styles-1.6.1.tgz#2099227b0f646ead16f7289e704b6a793594bf5c"
integrity sha512-Yh9ZcekpY7byXP1QJnfx94rVvK71xHBEspsVV7LL7YMvqXU4EAYuzQGYsljryV4PGS9PFPD6sqbGqhEkIhHPnQ==
dependencies:
"@polymer/iron-icon" "^3.0.0"
"@polymer/iron-iconset-svg" "^3.0.0"
"@polymer/polymer" "^3.0.0"
"@vaadin/vaadin-material-styles@^1.2.0", "@vaadin/vaadin-material-styles@^1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-material-styles/-/vaadin-material-styles-1.3.2.tgz#d2c1bd290db16721152ae672dbe052c381686696"
integrity sha512-EFrvGScoxhLNrPnWtT2Ia77whjF2TD4jrcyeh1jv9joCA2n5SUba+4XJciVSGmopqqQato6lwRnZSvMLJX7cyw==
dependencies:
"@polymer/polymer" "^3.0.0"
"@vaadin/vaadin-overlay@^3.5.0":
version "3.5.1"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-overlay/-/vaadin-overlay-3.5.1.tgz#c4391b3c6c1f7a512b0a6f0dd96f11480feed402"
integrity sha512-0g+poK/BXF92L2lSKrHMY5rcKzUxCBZNzP/NDwgi4a86nbjL7CAKKZdno7Yl+j8UsTR76nOEw4fAYTFi86B0qg==
dependencies:
"@polymer/polymer" "^3.0.0"
"@vaadin/vaadin-element-mixin" "^2.4.0"
"@vaadin/vaadin-lumo-styles" "^1.3.0"
"@vaadin/vaadin-material-styles" "^1.2.0"
"@vaadin/vaadin-themable-mixin" "^1.6.1"
"@vaadin/vaadin-themable-mixin@^1.6.1", "@vaadin/vaadin-themable-mixin@^1.6.2":
version "1.6.2"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-themable-mixin/-/vaadin-themable-mixin-1.6.2.tgz#8d619722819ba850af777579a550ff8b1d2b960f"
integrity sha512-PZZOZnke3KUlZsDrRVbWxAGEeFBPRyRayNRCvip0XnQK+Zs3cLuRgdgbdro3Ir9LZ3Izsw6HqA6XNMKffEP67A==
dependencies:
"@polymer/polymer" "^3.0.0"
lit-element "^2.0.0"
"@vaadin/vaadin-usage-statistics@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@vaadin/vaadin-usage-statistics/-/vaadin-usage-statistics-2.1.0.tgz#9c0fd71dded80f401bcdfbcb3f45b5640fc4256d"
integrity sha512-e81nbqY5zsaYhLJuOVkJkB/Um1pGK5POIqIlTNhUfjeoyGaJ63tiX8+D5n6F+GgVxUTLUarsKa6SKRcQel0AzA==
dependencies:
"@vaadin/vaadin-development-mode-detector" "^2.0.0"
"@webcomponents/shadycss@^1.9.1":
version "1.10.2"
resolved "https://registry.yarnpkg.com/@webcomponents/shadycss/-/shadycss-1.10.2.tgz#40e03cab6dc5e12f199949ba2b79e02f183d1e7b"
integrity sha512-9Iseu8bRtecb0klvv+WXZOVZatsRkbaH7M97Z+f+Pt909R4lDfgUODAnra23DOZTpeMTAkVpf4m/FZztN7Ox1A==
bootstrap@^4.1.3:
version "4.6.0"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.0.tgz#97b9f29ac98f98dfa43bf7468262d84392552fd7"
integrity sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==
copy-text-to-clipboard@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c"
integrity sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==
lit-element@^2.0.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-2.5.1.tgz#3fa74b121a6cd22902409ae3859b7847d01aa6b6"
integrity sha512-ogu7PiJTA33bEK0xGu1dmaX5vhcRjBXCFexPja0e7P7jqLhTpNKYRPmE+GmiCaRVAbiQKGkUgkh/i6+bh++dPQ==
dependencies:
lit-html "^1.1.1"
lit-html@^1.1.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.4.1.tgz#0c6f3ee4ad4eb610a49831787f0478ad8e9ae5e0"
integrity sha512-B9btcSgPYb1q4oSOb/PrOT6Z/H+r6xuNzfH4lFli/AWhYwdtrgQkQWBbIc6mdnf6E2IL3gDXdkkqNktpU0OZQA==

View File

@@ -23,7 +23,8 @@
"es6", "es6",
"es7", "es7",
"es2015", "es2015",
"es2017" "es2017",
"es2019"
], ],
"paths": { "paths": {
"terminus-*": ["../../terminus-*/src"] "terminus-*": ["../../terminus-*/src"]

View File

@@ -2,6 +2,8 @@ module.exports = [
require('./app/webpack.config.js'), require('./app/webpack.config.js'),
require('./app/webpack.main.config.js'), require('./app/webpack.main.config.js'),
require('./terminus-core/webpack.config.js'), require('./terminus-core/webpack.config.js'),
require('./terminus-electron/webpack.config.js'),
require('./terminus-web/webpack.config.js'),
require('./terminus-settings/webpack.config.js'), require('./terminus-settings/webpack.config.js'),
require('./terminus-terminal/webpack.config.js'), require('./terminus-terminal/webpack.config.js'),
require('./terminus-local/webpack.config.js'), require('./terminus-local/webpack.config.js'),

View File

@@ -43,6 +43,7 @@ module.exports = options => {
}, },
module: { module: {
rules: [ rules: [
...options.rules ?? [],
{ {
test: /\.ts$/, test: /\.ts$/,
use: { use: {
@@ -64,7 +65,8 @@ module.exports = options => {
}, },
}, },
{ test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] },
{ test: /\.scss$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'] }, { test: /\.scss$/, use: ['@terminus-term/to-string-loader', 'css-loader', 'sass-loader'], include: /(theme.*|component)\.scss/ },
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'], exclude: /(theme.*|component)\.scss/ },
{ test: /\.css$/, use: ['@terminus-term/to-string-loader', 'css-loader'], include: /component\.css/ }, { test: /\.css$/, use: ['@terminus-term/to-string-loader', 'css-loader'], include: /component\.css/ },
{ test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /component\.css/ }, { test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /component\.css/ },
{ test: /\.yaml$/, use: ['json-loader', 'yaml-loader'] }, { test: /\.yaml$/, use: ['json-loader', 'yaml-loader'] },
@@ -81,6 +83,7 @@ module.exports = options => {
], ],
}, },
externals: [ externals: [
'@electron/remote',
'any-promise', 'any-promise',
'child_process', 'child_process',
'electron-promise-ipc', 'electron-promise-ipc',