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
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-electron"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-web"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 20
- package-ecosystem: npm
directory: "/terminus-plugin-manager"
schedule:

View File

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

View File

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

View File

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

View File

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

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.setZoomFactor(1)
@@ -297,6 +297,8 @@ export class Window {
this.window.webContents.send('start', {
config: this.configStore,
executable: app.getPath('exe'),
windowID: this.window.id,
isFirstWindow: this.window.id === 1,
})
})

View File

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

View File

@@ -16,10 +16,15 @@ export function getRootModule (plugins: any[]) {
extendedTimeOut: 1000,
}),
]
const 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) {
throw new Error('Did not find any bootstrap components. Are there any plugins installed?')
}
@@ -27,6 +32,7 @@ export function getRootModule (plugins: any[]) {
@NgModule({
imports,
bootstrap,
providers,
}) class RootModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
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 { 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
location.hash = ''
@@ -39,11 +39,9 @@ async function bootstrap (plugins: PluginInfo[], bootstrapData: BootstrapData, s
})
const module = getRootModule(pluginModules)
window['rootModule'] = module
const moduleRef = await platformBrowserDynamic().bootstrapModule(module, {
providers: [
{ provide: 'bootstrapData', useValue: bootstrapData },
],
})
const moduleRef = await platformBrowserDynamic([
{ provide: BOOTSTRAP_DATA, useValue: bootstrapData },
]).bootstrapModule(module)
if (process.env.TERMINUS_DEV) {
const applicationRef = moduleRef.injector.get(ApplicationRef)
const componentRef = applicationRef.components[0]
@@ -54,12 +52,12 @@ async function bootstrap (plugins: PluginInfo[], bootstrapData: BootstrapData, s
ipcRenderer.once('start', async (_$event, bootstrapData: BootstrapData) => {
console.log('Window bootstrap data:', bootstrapData)
;(window as any).bootstrapData = bootstrapData
let plugins = await findPlugins()
if (bootstrapData.config.pluginBlacklist) {
plugins = plugins.filter(x => !bootstrapData.config.pluginBlacklist.includes(x.name))
}
plugins = plugins.filter(x => x.name !== 'web')
console.log('Starting with plugins:', plugins)
try {
await bootstrap(plugins, bootstrapData)

View File

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

View File

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

View File

@@ -66,7 +66,7 @@
"**/graceful-fs": "^4.2.4"
},
"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",
"watch": "cross-env TERMINUS_DEV=1 webpack --progress --color --watch",
"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'), '{}')
sh.cd(target)
vars.builtinPlugins.forEach(plugin => {
if (plugin === 'terminus-web') {
continue
}
log.info('install', plugin)
sh.cp('-r', path.join('..', plugin), '.')
sh.rm('-rf', path.join(plugin, 'node_modules'))

View File

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

View File

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

View File

@@ -10,18 +10,19 @@ export { Theme } from './theme'
export { TabContextMenuItemProvider } from './tabContextMenuProvider'
export { SelectorOption } from './selector'
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 { 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 { Logger, LogService } from '../services/log.service'
export { Logger, ConsoleLogger, LogService } from '../services/log.service'
export { HomeBaseService } from '../services/homeBase.service'
export { HotkeysService } from '../services/hotkeys.service'
export { HostAppService, Platform } from '../services/hostApp.service'
export { HostAppService, Platform, Bounds } from '../services/hostApp.service'
export { NotificationsService } from '../services/notifications.service'
export { ShellIntegrationService } from '../services/shellIntegration.service'
export { ThemesService } from '../services/themes.service'
export { TabsService } from '../services/tabs.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 {
config: Record<string, any>
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 { TabHeaderComponent } from '../components/tabHeader.component'
import { MenuItemOptions } from './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 {
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 { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ElectronService } from '../services/electron.service'
import { HostAppService, Platform } from '../services/hostApp.service'
import { HotkeysService } from '../services/hotkeys.service'
import { Logger, LogService } from '../services/log.service'
import { ConfigService } from '../services/config.service'
import { DockingService } from '../services/docking.service'
import { ThemesService } from '../services/themes.service'
import { UpdaterService } from '../services/updater.service'
import { TouchbarService } from '../services/touchbar.service'
import { BaseTabComponent } from './baseTab.component'
import { SafeModeModalComponent } from './safeModeModal.component'
import { AppService, ToolbarButton, ToolbarButtonProvider } from '../api'
import { AppService, PlatformService, ToolbarButton, ToolbarButtonProvider } from '../api'
/** @hidden */
@Component({
@@ -67,21 +64,19 @@ export class AppRootComponent {
private logger: Logger
private constructor (
private docking: DockingService,
private hotkeys: HotkeysService,
private updater: UpdaterService,
private touchbar: TouchbarService,
public hostApp: HostAppService,
public config: ConfigService,
public app: AppService,
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
electron: ElectronService,
platform: PlatformService,
log: LogService,
ngbModal: NgbModal,
_themes: ThemesService,
) {
this.logger = log.create('main')
this.logger.info('v', electron.app.getVersion())
this.logger.info('v', platform.getAppVersion())
this.leftToolbarButtons = this.getToolbarButtons(false)
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.app.closeWindow()
})
@@ -144,27 +134,8 @@ export class AppRootComponent {
}
}, 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.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
})
@@ -224,9 +195,4 @@ export class AppRootComponent {
.filter(button => (button.weight ?? 0) > 0 === aboveZero)
.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 */
import type { MenuItemConstructorOptions } from 'electron'
import { Component, Input, Optional, Inject, HostBinding, HostListener, ViewChild, ElementRef, NgZone } from '@angular/core'
import { SortableComponent } from 'ng2-dnd'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
@@ -7,11 +6,12 @@ import { TabContextMenuItemProvider } from '../api/tabContextMenuProvider'
import { BaseTabComponent } from './baseTab.component'
import { RenameTabModalComponent } from './renameTabModal.component'
import { HotkeysService } from '../services/hotkeys.service'
import { ElectronService } from '../services/electron.service'
import { AppService } from '../services/app.service'
import { HostAppService, Platform } from '../services/hostApp.service'
import { ConfigService } from '../services/config.service'
import { BaseComponent } from './base.component'
import { MenuItemOptions } from '../api/menu'
import { PlatformService } from '../api/platform'
/** @hidden */
export interface SortableComponentProxy {
@@ -34,10 +34,10 @@ export class TabHeaderComponent extends BaseComponent {
private constructor (
public app: AppService,
public config: ConfigService,
private electron: ElectronService,
private hostApp: HostAppService,
private ngbModal: NgbModal,
private hotkeys: HotkeysService,
private platform: PlatformService,
private zone: NgZone,
@Inject(SortableComponent) private parentDraggable: SortableComponentProxy,
@Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
@@ -76,8 +76,8 @@ export class TabHeaderComponent extends BaseComponent {
}).catch(() => null)
}
async buildContextMenu (): Promise<MenuItemConstructorOptions[]> {
let items: MenuItemConstructorOptions[] = []
async buildContextMenu (): Promise<MenuItemOptions[]> {
let items: MenuItemOptions[] = []
for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this.tab, this)))) {
items.push({ type: 'separator' })
items = items.concat(section)
@@ -105,16 +105,8 @@ export class TabHeaderComponent extends BaseComponent {
}
}
@HostListener('auxclick', ['$event']) async onAuxClick ($event: MouseEvent) {
if ($event.which === 3) {
@HostListener('contextmenu', ['$event']) async onContextMenu ($event: MouseEvent) {
$event.preventDefault()
const contextMenu = this.electron.Menu.buildFromTemplate(await this.buildContextMenu())
contextMenu.popup({
x: $event.pageX,
y: $event.pageY,
})
}
this.platform.popupContextMenu(await this.buildContextMenu(), $event)
}
}

View File

@@ -7,6 +7,7 @@ export class CoreConfigProvider extends ConfigProvider {
[Platform.macOS]: require('./configDefaults.macos.yaml'),
[Platform.Windows]: require('./configDefaults.windows.yaml'),
[Platform.Linux]: require('./configDefaults.linux.yaml'),
[Platform.Web]: require('./configDefaults.windows.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 { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { FormsModule } from '@angular/forms'
@@ -24,7 +24,7 @@ import { WelcomeTabComponent } from './components/welcomeTab.component'
import { AutofocusDirective } from './directives/autofocus.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 { ConfigService } from './services/config.service'
@@ -38,6 +38,10 @@ import { LastCLIHandler } from './cli'
import 'perfect-scrollbar/css/perfect-scrollbar.css'
import 'ng2-dnd/bundles/style.css'
function initialize (config: ConfigService) {
return () => config.ready$.toPromise()
}
const PROVIDERS = [
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
{ provide: Theme, useClass: StandardTheme, multi: true },
@@ -50,6 +54,7 @@ const PROVIDERS = [
{ provide: TabRecoveryProvider, useClass: SplitTabRecoveryProvider, multi: true },
{ provide: CLIHandler, useClass: LastCLIHandler, multi: true },
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
{ provide: APP_INITIALIZER, useFactory: initialize, deps: [ConfigService], multi: true },
]
/** @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 * from './api'

View File

@@ -1,7 +1,7 @@
import { Observable, Subject, AsyncSubject } from 'rxjs'
import { takeUntil } from 'rxjs/operators'
import { Injectable } from '@angular/core'
import { Injectable, Inject } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { BaseTabComponent } from '../components/baseTab.component'
@@ -9,6 +9,7 @@ import { SplitTabComponent } from '../components/splitTab.component'
import { SelectorModalComponent } from '../components/selectorModal.component'
import { SelectorOption } from '../api/selector'
import { RecoveryToken } from '../api/tabRecovery'
import { BootstrapData, BOOTSTRAP_DATA } from '../api/mainProcess'
import { ConfigService } from './config.service'
import { HostAppService } from './hostApp.service'
@@ -75,6 +76,7 @@ export class AppService {
private tabRecovery: TabRecoveryService,
private tabsService: TabsService,
private ngbModal: NgbModal,
@Inject(BOOTSTRAP_DATA) private bootstrapData: BootstrapData,
) {
this.tabsChanged$.subscribe(() => {
this.tabRecovery.saveTabs(this.tabs)
@@ -83,19 +85,18 @@ export class AppService {
this.tabRecovery.saveTabs(this.tabs)
}, 30000)
if (hostApp.getWindow().id === 1) {
config.ready$.toPromise().then(async () => {
if (this.bootstrapData.isFirstWindow) {
if (config.store.terminal.recoverTabs) {
this.tabRecovery.recoverTabs().then(tabs => {
const tabs = await this.tabRecovery.recoverTabs()
for (const tab of tabs) {
this.openNewTabRaw(tab.type, tab.options)
}
this.tabRecovery.enabled = true
})
} else {
}
/** Continue to store the tabs even if the setting is currently off */
this.tabRecovery.enabled = true
}
}
})
hostApp.windowFocused$.subscribe(() => this._activeTab?.emitFocused())
@@ -118,7 +119,7 @@ export class AppService {
this.tabsChanged.next()
this.tabOpened.next(tab)
if (this.hostApp.getWindow().id === 1) {
if (this.bootstrapData.isFirstWindow) {
tab.recoveryStateChangedHint$.subscribe(() => {
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 path from 'path'
import * as fs from 'fs'
import { Injectable, Inject } from '@angular/core'
import { ConfigProvider } from '../api/configProvider'
import { ElectronService } from './electron.service'
import { PlatformService } from '../api/platform'
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) {
return v instanceof Object && !(v instanceof Array) &&
@@ -89,11 +88,10 @@ export class ConfigService {
*/
restartRequested: boolean
/**
* Full config file path
*/
path: string
/** Fires once when the config is loaded */
get ready$ (): Observable<void> { return this.ready }
private ready = new AsyncSubject<void>()
private changed = new Subject<void>()
private _store: any
private defaults: any
@@ -103,26 +101,24 @@ export class ConfigService {
/** @hidden */
private constructor (
electron: ElectronService,
private hostApp: HostAppService,
private platform: PlatformService,
@Inject(ConfigProvider) private configProviders: ConfigProvider[],
) {
this.path = path.join(electron.app.getPath('userData'), 'config.yaml')
this.defaults = this.mergeDefaults()
this.load()
hostApp.configChangeBroadcast$.subscribe(() => {
this.load()
this.emitChange()
})
this.init()
}
mergeDefaults (): unknown {
const providers = this.configProviders
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) {
defaults = configMerge(defaults, provider.defaults)
defaults = configMerge(provider.defaults, defaults)
}
return defaults
}).reduce(configMerge)
@@ -147,19 +143,20 @@ export class ConfigService {
return cleanup(this.defaults)
}
load (): void {
if (fs.existsSync(this.path)) {
this._store = yaml.load(fs.readFileSync(this.path, 'utf8'))
async load (): Promise<void> {
const content = await this.platform.loadConfig()
if (content) {
this._store = yaml.load(content)
} else {
this._store = {}
}
this.store = new ConfigProxy(this._store, this.defaults)
}
save (): void {
async save (): Promise<void> {
// Scrub undefined values
this._store = JSON.parse(JSON.stringify(this._store))
fs.writeFileSync(this.path, yaml.dump(this._store), 'utf8')
const cleanStore = JSON.parse(JSON.stringify(this._store))
await this.platform.saveConfig(yaml.dump(cleanStore))
this.emitChange()
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 {
this.changed.next()
}

View File

@@ -1,11 +1,19 @@
import type { Display } from 'electron'
import { Injectable } from '@angular/core'
import { ConfigService } from '../services/config.service'
import { ElectronService } from '../services/electron.service'
import { HostAppService, Bounds } from '../services/hostApp.service'
@Injectable({ providedIn: 'root' })
export class DockingService {
export abstract class Screen {
id: number
name?: string
}
export abstract class DockingService {
abstract dock (): void
abstract getScreens (): Screen[]
}
export class ElectronDockingService {
/** @hidden */
private constructor (
private electron: ElectronService,
@@ -68,10 +76,6 @@ export class DockingService {
})
}
getCurrentScreen (): Display {
return this.electron.screen.getDisplayNearestPoint(this.electron.screen.getCursorScreenPoint())
}
getScreens (): Display[] {
const primaryDisplayID = this.electron.screen.getPrimaryDisplay().id
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 () {
const [x, y] = this.hostApp.getWindow().getPosition()
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 { ElectronService } from './electron.service'
import { ConfigService } from './config.service'
import * as mixpanel from 'mixpanel'
import { v4 as uuidv4 } from 'uuid'
import { ConfigService } from './config.service'
import { PlatformService } from '../api'
@Injectable({ providedIn: 'root' })
export class HomeBaseService {
@@ -12,10 +11,10 @@ export class HomeBaseService {
/** @hidden */
private constructor (
private electron: ElectronService,
private config: ConfigService,
private platform: PlatformService,
) {
this.appVersion = electron.app.getVersion()
this.appVersion = platform.getAppVersion()
if (this.config.store.enableAnalytics && !this.config.store.enableWelcomeTab) {
this.enableAnalytics()
@@ -23,12 +22,12 @@ export class HomeBaseService {
}
openGitHub (): void {
this.electron.shell.openExternal('https://github.com/eugeny/terminus')
this.platform.openExternal('https://github.com/eugeny/terminus')
}
reportBug (): void {
let body = `Version: ${this.appVersion}\n`
body += `Platform: ${os.platform()} ${os.release()}\n`
body += `Platform: ${process.platform} ${this.platform.getOSRelease()}\n`
const label = {
aix: 'OS: IBM AIX',
android: 'OS: Android',
@@ -38,10 +37,10 @@ export class HomeBaseService {
openbsd: 'OS: OpenBSD',
sunos: 'OS: Solaris',
win32: 'OS: Windows',
}[os.platform()]
}[process.platform]
const plugins = (window as any).installedPlugins.filter(x => !x.isBuiltin).map(x => x.name)
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 {
@@ -60,7 +59,7 @@ export class HomeBaseService {
return {
distinct_id: window.localStorage.analyticsUserID,
platform: process.platform,
os: os.release(),
os: this.platform.getOSRelease(),
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 { Injectable, NgZone, EventEmitter, Injector } from '@angular/core'
import { Injectable, NgZone, EventEmitter, Injector, Inject } from '@angular/core'
import { ElectronService } from './electron.service'
import { Logger, LogService } from './log.service'
import { CLIHandler } from '../api/cli'
import { BootstrapData, BOOTSTRAP_DATA } from '../api/mainProcess'
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 {
Linux = 'Linux',
macOS = 'macOS',
Windows = 'Windows',
Web = 'Web',
}
export interface Bounds {
@@ -31,6 +27,7 @@ export interface Bounds {
@Injectable({ providedIn: 'root' })
export class HostAppService {
platform: Platform
configPlatform: Platform
/**
* Fired once the window is visible
@@ -47,7 +44,6 @@ export class HostAppService {
private displayMetricsChanged = new Subject<void>()
private displaysChanged = new Subject<void>()
private logger: Logger
private windowId: number
/**
* Fired when Preferences is selected in the macOS menu
@@ -75,18 +71,20 @@ export class HostAppService {
private constructor (
private zone: NgZone,
private electron: ElectronService,
@Inject(BOOTSTRAP_DATA) private bootstrapData: BootstrapData,
injector: Injector,
log: LogService,
) {
this.logger = log.create('hostApp')
this.platform = {
this.configPlatform = this.platform = {
win32: Platform.Windows,
darwin: Platform.macOS,
linux: Platform.Linux,
}[process.platform]
this.windowId = parseInt(location.search.substring(1))
this.logger.info('Window ID:', this.windowId)
if (process.env.XWEB) {
this.platform = Platform.Web
}
electron.ipcRenderer.on('host:preferences-menu', () => this.zone.run(() => this.preferencesMenu.next()))
@@ -158,7 +156,7 @@ export class HostAppService {
* Returns the current remote [[BrowserWindow]]
*/
getWindow (): BrowserWindow {
return this.electron.BrowserWindow.fromId(this.windowId)!
return this.electron.BrowserWindow.fromId(this.bootstrapData.windowID)!
}
newWindow (): void {
@@ -202,19 +200,6 @@ export class HostAppService {
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 {
this.electron.ipcRenderer.send('window-set-title', title ?? 'Terminus')
}
@@ -223,10 +208,6 @@ export class HostAppService {
this.getWindow().setTouchBar(touchBar)
}
popupContextMenu (menuDefinition: MenuItemConstructorOptions[]): void {
this.electron.Menu.buildFromTemplate(menuDefinition).popup({})
}
/**
* Notifies other windows of config file changes
*/
@@ -250,20 +231,6 @@ export class HostAppService {
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 {
if (this.isPortable) {
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 { stringifyKeySequence, EventData } from './hotkeys.util'
import { ConfigService } from './config.service'
import { ElectronService } from './electron.service'
import { HostAppService } from './hostApp.service'
export interface PartialHotkeyMatch {
@@ -35,7 +34,6 @@ export class HotkeysService {
private constructor (
private zone: NgZone,
private hostApp: HostAppService,
private electron: ElectronService,
private config: ConfigService,
@Inject(HotkeyProvider) private hotkeyProviders: HotkeyProvider[],
) {
@@ -52,10 +50,12 @@ export class HotkeysService {
this.config.changed$.subscribe(() => {
this.registerGlobalHotkey()
})
this.config.ready$.toPromise().then(() => {
this.registerGlobalHotkey()
this.getHotkeyDescriptions().then(hotkeys => {
this.hotkeyDescriptions = hotkeys
})
})
// deprecated
this.hotkey$.subscribe(h => this.matchedHotkey.emit(h))
@@ -183,7 +183,6 @@ export class HotkeysService {
}
private registerGlobalHotkey () {
this.electron.globalShortcut.unregisterAll()
let value = this.config.store.hotkeys['toggle-window'] || []
if (typeof value === 'string') {
value = [value]

View File

@@ -1,38 +1,5 @@
import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
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,
) {}
export abstract class Logger {
constructor (protected name: string) { }
debug (...args: any[]): void {
this.doLog('debug', ...args)
@@ -54,26 +21,15 @@ export class Logger {
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)
this.winstonLogger[level](...args)
}
}
@Injectable({ providedIn: 'root' })
export class LogService {
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)
}
export abstract class LogService {
abstract create (name: string): Logger
}

View File

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

View File

@@ -1,137 +1,4 @@
import axios from 'axios'
import { Injectable } from '@angular/core'
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()
}
}
}
export abstract class UpdaterService {
abstract check (): Promise<boolean>
abstract update (): Promise<void>
}

View File

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

View File

@@ -2,20 +2,6 @@
# 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":
version "4.0.1"
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"
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:
version "6.0.2"
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"
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:
version "4.5.3"
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"
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:
version "3.12.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112"
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:
version "4.3.1"
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"
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:
version "10.0.0"
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"
debug "4"
inherits@^2.0.3, 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-plain-object@^2.0.4:
version "2.0.4"
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:
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:
version "3.0.1"
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"
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:
version "1.0.4"
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"
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:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@@ -313,7 +173,7 @@ mixpanel@^0.13.0:
dependencies:
https-proxy-agent "5.0.0"
ms@2.1.2, ms@^2.1.1:
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
@@ -332,24 +192,12 @@ ngx-perfect-scrollbar@^10.1.0:
resize-observer-polyfill "^1.5.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:
version "1.5.0"
resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz#821d224ed8ff61990c23f26db63048cdc75b6b83"
integrity sha512-NrNHJn5mUGupSiheBTy6x+6SXCFbLlm8fVZh9moIzw/LgqElN5q4ncR4pbCBCYuCJ8Kcl9mYM0NgDxvW+b4LxA==
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@3.6.0, readable-stream@^3.4.0:
readable-stream@3.6.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==
@@ -358,29 +206,11 @@ readable-stream@3.6.0, readable-stream@^3.4.0:
string_decoder "^1.1.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:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
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:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
@@ -405,18 +235,6 @@ shallow-clone@^3.0.0:
dependencies:
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:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -424,23 +242,6 @@ string_decoder@^1.1.1:
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==
tslib@^2.0.0:
version "2.1.0"
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"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
util-deprecate@^1.0.1, 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=
@@ -461,29 +262,6 @@ uuid@^8.0.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
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:
version "4.0.0"
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 path from 'path'
import { Injectable } from '@angular/core'
import { TerminalColorSchemeProvider } from './api/colorSchemeProvider'
import { TerminalColorScheme } from './api/interfaces'
import { TerminalColorSchemeProvider, TerminalColorScheme } from 'terminus-terminal'
/** @hidden */
@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 { exec } from 'mz/child_process'
import { Injectable } from '@angular/core'
import { ElectronService } from './electron.service'
import { HostAppService, Platform } from './hostApp.service'
import { ElectronService } from '../../../terminus-core/src/services/electron.service'
import { HostAppService, Platform } from '../../../terminus-core/src/services/hostApp.service'
/* eslint-disable block-scoped-var */

View File

@@ -1,8 +1,6 @@
import { SegmentedControlSegment, TouchBarSegmentedControl } from 'electron'
import { Injectable, NgZone } from '@angular/core'
import { AppService } from './app.service'
import { ElectronService } from './electron.service'
import { HostAppService, Platform } from './hostApp.service'
import { AppService, HostAppService, Platform, ElectronService } from 'terminus-core'
/** @hidden */
@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 {
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

View File

@@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
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 { SettingsTabProvider } from 'terminus-settings'
@@ -104,6 +104,7 @@ export default class LocalTerminalModule { // eslint-disable-line @typescript-es
terminal: TerminalService,
hostApp: HostAppService,
dockMenu: DockMenuService,
config: ConfigService,
) {
hotkeys.matchedHotkey.subscribe(async (hotkey) => {
if (hotkey === 'new-tab') {
@@ -120,7 +121,9 @@ export default class LocalTerminalModule { // eslint-disable-line @typescript-es
}
})
config.ready$.toPromise().then(() => {
dockMenu.update()
})
}
}

View File

@@ -26,11 +26,13 @@ export class TerminalService {
log: LogService,
) {
this.logger = log.create('terminal')
this.reloadShells()
config.ready$.toPromise().then(() => {
this.reloadShells()
config.changed$.subscribe(() => {
this.reloadShells()
})
})
}
async getProfiles ({ includeHidden, skipDefault }: { includeHidden?: boolean, skipDefault?: boolean } = {}): Promise<Profile[]> {

View File

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

View File

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

View File

@@ -19,7 +19,6 @@
"devDependencies": {
"@types/semver": "^7.1.0",
"axios": "^0.21.1",
"electron-promise-ipc": "^2.2.4",
"mz": "^2.6.0",
"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 { Component, Input } from '@angular/core'
import { ConfigService, ElectronService } from 'terminus-core'
import { ConfigService, PlatformService } from 'terminus-core'
import { PluginInfo, PluginManagerService } from '../services/pluginManager.service'
enum BusyState { Installing = 'Installing', Uninstalling = 'Uninstalling' }
@@ -27,8 +27,8 @@ export class PluginsSettingsTabComponent {
@Input() errorMessage: string
constructor (
private electron: ElectronService,
private config: ConfigService,
private platform: PlatformService,
public pluginManager: PluginManagerService
) {
}
@@ -57,7 +57,7 @@ export class PluginsSettingsTabComponent {
}
openPluginsFolder (): void {
this.electron.shell.openPath(this.pluginManager.userPluginsPath)
this.platform.openPath(this.pluginManager.userPluginsPath)
}
searchAvailable (query: string) {
@@ -101,7 +101,7 @@ export class PluginsSettingsTabComponent {
}
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) {

View File

@@ -1,9 +1,8 @@
import axios from 'axios'
import promiseIpc from 'electron-promise-ipc'
import { Observable, from } from 'rxjs'
import { map } from 'rxjs/operators'
import { Injectable } from '@angular/core'
import { Logger, LogService } from 'terminus-core'
import { Logger, LogService, PlatformService } from 'terminus-core'
const NAME_PREFIX = 'terminus-'
const KEYWORD = 'terminus-plugin'
@@ -34,6 +33,7 @@ export class PluginManagerService {
private constructor (
log: LogService,
private platform: PlatformService,
) {
this.logger = log.create('pluginManager')
}
@@ -63,7 +63,7 @@ export class PluginManagerService {
async installPlugin (plugin: PluginInfo): Promise<void> {
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.push(plugin)
} catch (err) {
@@ -74,7 +74,7 @@ export class PluginManagerService {
async uninstallPlugin (plugin: PluginInfo): Promise<void> {
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)
} catch (err) {
this.logger.error(err)

View File

@@ -19,123 +19,11 @@ axios@^0.21.1:
dependencies:
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:
version "1.13.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
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:
version "6.0.0"
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"
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:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
@@ -194,29 +52,6 @@ semver@^7.1.1:
dependencies:
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:
version "1.6.0"
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
@@ -231,16 +66,6 @@ thenify-all@^1.0.0:
dependencies:
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:
version "4.0.0"
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)
if (this.timeoutProgress === 100) {
clearInterval(this.keyTimeoutInterval!)
this.modalInstance.close(this.value)
}
}, 25)
@@ -74,13 +75,14 @@ export class HotkeyInputModalComponent extends BaseComponent {
}
ngOnDestroy (): void {
clearInterval(this.keyTimeoutInterval!)
this.hotkeys.clearCurrentKeystrokes()
this.hotkeys.enable()
clearInterval(this.keyTimeoutInterval!)
super.ngOnDestroy()
}
close (): void {
clearInterval(this.keyTimeoutInterval!)
this.modalInstance.dismiss()
}
}

View File

@@ -40,7 +40,7 @@ button.btn.btn-outline-warning.btn-block(*ngIf='config.restartRequested', '(clic
i.fas.fa-sync
span Update
.form-line(*ngIf='hostApp.platform !== Platform.Linux')
.form-line(*ngIf='platform.isShellIntegrationSupported()')
.header
.title Shell integration
.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()')
i.fas.fa-exclamation-triangle.mr-2
| 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
| Show config file

View File

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

View File

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

View File

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

View File

@@ -61,7 +61,7 @@ h3.mt-5 Options
(ngModelChange)='config.save()',
)
.form-line
.form-line(*ngIf='hostApp.platform === Platform.Windows')
.header
.title WinSCP path
.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 { Component } from '@angular/core'
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 { SSHConnection } from '../api'
import { EditConnectionModalComponent } from './editConnectionModal.component'
@@ -23,11 +23,12 @@ export class SSHSettingsTabComponent {
childGroups: SSHConnectionGroup[]
groupCollapsed: Record<string, boolean> = {}
filter = ''
Platform = Platform
constructor (
public config: ConfigService,
public hostApp: HostAppService,
private electron: ElectronService,
private hostApp: HostAppService,
private ngbModal: NgbModal,
private passwordStorage: PasswordStorageService,
) {

View File

@@ -10,7 +10,7 @@ import { exec } from 'child_process'
import * as path from 'path'
import * as sshpk from 'sshpk'
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 { ALGORITHM_BLACKLIST, ForwardedPort, SSHConnection, SSHSession } from '../api'
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'
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' })
export class SSHService {
private logger: Logger
@@ -42,6 +33,7 @@ export class SSHService {
private notifications: NotificationsService,
private app: AppService,
private config: ConfigService,
private platform: PlatformService,
) {
this.logger = log.create('ssh')
}
@@ -197,13 +189,7 @@ export class SSHService {
if (await fs.exists(WINDOWS_OPENSSH_AGENT_PIPE)) {
agent = WINDOWS_OPENSSH_AGENT_PIPE
} else {
// eslint-disable-next-line @typescript-eslint/no-shadow
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) {
if (await this.platform.isProcessRunning('pageant.exe')) {
agent = 'pageant'
}
}

View File

@@ -1,27 +1,20 @@
import type { MenuItemConstructorOptions } from 'electron'
import { execFile } from 'child_process'
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 { PasswordStorageService } from './services/passwordStorage.service'
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 */
@Injectable()
export class WinSCPContextMenu extends TabContextMenuItemProvider {
weight = 10
private detectedPath?: string
private detectedPath: string | null
constructor (
private hostApp: HostAppService,
private config: ConfigService,
private platform: PlatformService,
private passwordStorage: PasswordStorageService,
) {
super()
@@ -30,14 +23,10 @@ export class WinSCPContextMenu extends TabContextMenuItemProvider {
return
}
const key = wnr.getRegistryKey(wnr.HK.CR, 'WinSCP.Url\\DefaultIcon')
if (key?.['']) {
this.detectedPath = key[''].value?.split(',')[0]
this.detectedPath = this.detectedPath?.substring(1, this.detectedPath.length - 1)
}
this.detectedPath = platform.getWinSCPPath()
}
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> {
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
if (this.hostApp.platform !== Platform.Windows || tabHeader) {
return []
}
@@ -81,6 +70,6 @@ export class WinSCPContextMenu extends TabContextMenuItemProvider {
args.push('/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 { first } from 'rxjs/operators'
import colors from 'ansi-colors'
import { NgZone, OnInit, OnDestroy, Injector, ViewChild, HostBinding, Input, ElementRef, InjectFlags } from '@angular/core'
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 { TerminalFrontendService } from '../services/terminalFrontend.service'
@@ -84,6 +83,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
protected hostApp: HostAppService
protected hotkeys: HotkeysService
protected electron: ElectronService
protected platform: PlatformService
protected terminalContainersService: TerminalFrontendService
protected notifications: NotificationsService
protected log: LogService
@@ -136,6 +136,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
this.hostApp = injector.get(HostAppService)
this.hotkeys = injector.get(HotkeysService)
this.electron = injector.get(ElectronService)
this.platform = injector.get(PlatformService)
this.terminalContainersService = injector.get(TerminalFrontendService)
this.notifications = injector.get(NotificationsService)
this.log = injector.get(LogService)
@@ -312,8 +313,8 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
}
}
async buildContextMenu (): Promise<MenuItemConstructorOptions[]> {
let items: MenuItemConstructorOptions[] = []
async buildContextMenu (): Promise<MenuItemOptions[]> {
let items: MenuItemOptions[] = []
for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this)))) {
items = items.concat(section)
items.push({ type: 'separator' })
@@ -498,6 +499,16 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
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 {
this.detachTermContainerHandlers()
@@ -531,13 +542,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
return
}
if (event.which === 3 || event.which === 1 && event.ctrlKey) {
if (this.config.store.terminal.rightClick === 'menu') {
this.hostApp.popupContextMenu(await this.buildContextMenu())
} else if (this.config.store.terminal.rightClick === 'paste') {
this.paste()
}
event.preventDefault()
event.stopPropagation()
this.handleRightClick(event)
return
}
}

View File

@@ -1,4 +1,4 @@
import type { MenuItemConstructorOptions } from 'electron'
import type { MenuItemOptions } from 'terminus-core'
import { BaseTerminalTabComponent } from './baseTerminalTab.component'
/**
@@ -8,5 +8,5 @@ import { BaseTerminalTabComponent } from './baseTerminalTab.component'
export abstract class TerminalContextMenuItemProvider {
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 { debounce } from 'utils-decorators/dist/cjs'
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 { ConfigService, HostAppService, Platform, getCSSFontFamily } from 'terminus-core'
import { ConfigService, getCSSFontFamily, PlatformService } from 'terminus-core'
/** @hidden */
@Component({
@@ -17,26 +15,12 @@ export class AppearanceSettingsTabComponent {
fonts: string[] = []
constructor (
private hostApp: HostAppService,
public config: ConfigService,
private platform: PlatformService,
) { }
async ngOnInit () {
if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) {
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()
})
}
this.fonts = await this.platform.listFonts()
}
fontAutocomplete = (text$: Observable<string>) => {

View File

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

View File

@@ -1,19 +1,22 @@
import { execFile } from 'mz/child_process'
import { Component } from '@angular/core'
import { ConfigService, ElectronService } from 'terminus-core'
import { ConfigService, HostAppService, Platform, PlatformService } from 'terminus-core'
/** @hidden */
@Component({
template: require('./terminalSettingsTab.component.pug'),
})
export class TerminalSettingsTabComponent {
Platform = Platform
constructor (
public config: ConfigService,
private electron: ElectronService,
public hostApp: HostAppService,
private platform: PlatformService,
) { }
openWSLVolumeMixer (): void {
this.electron.shell.openPath('sndvol.exe')
this.platform.openPath('sndvol.exe')
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 { ResizeEvent } from '../api/interfaces'
import { ConfigService, ThemesService, HotkeysService } from 'terminus-core'
export interface SearchOptions {
regex?: boolean
@@ -13,10 +13,6 @@ export interface SearchOptions {
* Extend to add support for a different VT frontend implementation
*/
export abstract class Frontend {
configService: ConfigService
themesService: ThemesService
hotkeysService: HotkeysService
enableResizing = true
protected ready = new AsyncSubject<void>()
protected title = new ReplaySubject<string>(1)
@@ -40,6 +36,8 @@ export abstract class Frontend {
get dragOver$ (): Observable<DragEvent> { return this.dragOver }
get drop$ (): Observable<DragEvent> { return this.drop }
constructor (protected injector: Injector) { }
destroy (): void {
for (const o of [
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 { hterm, preferenceManager } from './hterm'
import { getCSSFontFamily } from 'terminus-core'
/** @hidden */
export class HTermFrontend extends Frontend {
@@ -13,6 +14,15 @@ export class HTermFrontend extends Frontend {
private configuredBackgroundColor = 'transparent'
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> {
if (!this.initialized) {
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 { Terminal, ITheme } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
@@ -39,8 +40,18 @@ export class XTermFrontend extends Frontend {
private opened = false
private resizeObserver?: any
constructor () {
super()
private configService: ConfigService
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({
allowTransparency: true,
windowsMode: process.platform === 'win32',
@@ -88,10 +99,12 @@ export class XTermFrontend extends Frontend {
}
this.xterm.attachCustomKeyEventHandler((event: KeyboardEvent) => {
if (this.hostApp.platform !== Platform.Web) {
if (event.getModifierState('Meta') && event.key.toLowerCase() === 'v') {
event.preventDefault()
return false
}
}
if (event.getModifierState('Meta') && event.key.startsWith('Arrow')) {
return false
}
@@ -167,6 +180,10 @@ export class XTermFrontend extends Frontend {
host.addEventListener('mousedown', 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('contextmenu', event => {
event.preventDefault()
event.stopPropagation()
})
this.resizeObserver = new window['ResizeObserver'](() => setTimeout(() => this.resizeHandler()))
this.resizeObserver.observe(host)
@@ -190,12 +207,12 @@ export class XTermFrontend extends Frontend {
copySelection (): void {
const text = this.getSelection()
if (text.length < 1024 * 32) {
require('@electron/remote').clipboard.write({
this.platformService.setClipboard({
text: this.getSelection(),
html: this.getSelectionAsHTML(),
})
} else {
require('@electron/remote').clipboard.write({
this.platformService.setClipboard({
text: this.getSelection(),
})
}

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
import { MenuItemConstructorOptions } from 'electron'
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 { TerminalContextMenuItemProvider } from './api/contextMenuProvider'
@@ -16,7 +15,7 @@ export class CopyPasteContextMenu extends TabContextMenuItemProvider {
super()
}
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> {
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
if (tabHeader) {
return []
}
@@ -56,12 +55,12 @@ export class LegacyContextMenu extends TabContextMenuItemProvider {
super()
}
async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> {
async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
if (!this.contextMenuProviders) {
return []
}
if (tab instanceof BaseTerminalTabComponent) {
let items: MenuItemConstructorOptions[] = []
let items: MenuItemOptions[] = []
for (const p of this.contextMenuProviders) {
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",
"es7",
"es2015",
"es2017"
"es2017",
"es2019"
],
"paths": {
"terminus-*": ["../../terminus-*/src"]

View File

@@ -2,6 +2,8 @@ module.exports = [
require('./app/webpack.config.js'),
require('./app/webpack.main.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-terminal/webpack.config.js'),
require('./terminus-local/webpack.config.js'),

View File

@@ -43,6 +43,7 @@ module.exports = options => {
},
module: {
rules: [
...options.rules ?? [],
{
test: /\.ts$/,
use: {
@@ -64,7 +65,8 @@ module.exports = options => {
},
},
{ 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: ['style-loader', 'css-loader'], exclude: /component\.css/ },
{ test: /\.yaml$/, use: ['json-loader', 'yaml-loader'] },
@@ -81,6 +83,7 @@ module.exports = options => {
],
},
externals: [
'@electron/remote',
'any-promise',
'child_process',
'electron-promise-ipc',