diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 53a3fbdf..1c378f2b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -30,6 +30,12 @@ updates: interval: daily time: "04:00" open-pull-requests-limit: 20 +- package-ecosystem: npm + directory: "/terminus-local" + schedule: + interval: daily + time: "04:00" + open-pull-requests-limit: 20 - package-ecosystem: npm directory: "/terminus-community-color-schemes" schedule: diff --git a/HACKING.md b/HACKING.md index 8081f074..06dee86f 100644 --- a/HACKING.md +++ b/HACKING.md @@ -42,6 +42,7 @@ terminus ├─ scripts # Maintenance scripts ├─ terminus-community-color-schemes # Plugin that provides color schemes ├─ terminus-core # Plugin that provides base UI and tab management +└─ 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 @@ -61,7 +62,7 @@ terminus-pluginname | └─ index.ts # Module entry point ├─ package.json ├─ tsconfig.json -└─ webpack.config.js +└─ webpack.config.js ``` # Plugins @@ -115,6 +116,6 @@ export default class MyModule { } ``` -See `terminus-core/src/api.ts`, `terminus-settings/src/api.ts` and `terminus-terminal/src/api.ts` for the available extension points. +See `terminus-core/src/api.ts`, `terminus-settings/src/api.ts`, `terminus-local/src/api.ts` and `terminus-terminal/src/api.ts` for the available extension points. Publish your plugin on NPM with a `terminus-plugin` keyword to make it appear in the Plugin Manager. diff --git a/app/package.json b/app/package.json index 5032e2f6..362b561c 100644 --- a/app/package.json +++ b/app/package.json @@ -57,6 +57,7 @@ "peerDependencies": { "terminus-community-color-schemes": "*", "terminus-core": "*", + "terminus-local": "*", "terminus-plugin-manager": "*", "terminus-serial": "*", "terminus-settings": "*", diff --git a/app/src/plugins.ts b/app/src/plugins.ts index a15fa31d..d23eda7f 100644 --- a/app/src/plugins.ts +++ b/app/src/plugins.ts @@ -65,6 +65,7 @@ const builtinModules = [ 'rxjs', 'rxjs/operators', 'terminus-core', + 'terminus-local', 'terminus-settings', 'terminus-terminal', 'zone.js/dist/zone.js', @@ -128,6 +129,10 @@ export async function findPlugins (): Promise { const name = packageName.substring(PREFIX.length) + if (builtinModules.includes(packageName) && pluginDir !== builtinPluginsPath) { + continue + } + if (foundPlugins.some(x => x.name === name)) { console.info(`Plugin ${packageName} already exists, overriding`) foundPlugins = foundPlugins.filter(x => x.name !== name) diff --git a/package.json b/package.json index a9f5b38a..7e85ce53 100644 --- a/package.json +++ b/package.json @@ -66,13 +66,13 @@ "**/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-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", "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", "start:prod": "electron app --debug", "prod": "cross-env TERMINUS_DEV=1 electron app", - "docs": "typedoc --out docs/api --tsconfig terminus-core/src/tsconfig.typings.json terminus-core/src/index.ts && typedoc --out docs/api/terminal --tsconfig terminus-terminal/tsconfig.typings.json terminus-terminal/src/index.ts && typedoc --out docs/api/settings --tsconfig terminus-settings/tsconfig.typings.json terminus-settings/src/index.ts", + "docs": "typedoc --out docs/api --tsconfig terminus-core/src/tsconfig.typings.json terminus-core/src/index.ts && typedoc --out docs/api/terminal --tsconfig terminus-terminal/tsconfig.typings.json terminus-terminal/src/index.ts && typedoc --out docs/api/local --tsconfig terminus-local/tsconfig.typings.json terminus-local/src/index.ts && typedoc --out docs/api/settings --tsconfig terminus-settings/tsconfig.typings.json terminus-settings/src/index.ts", "lint": "eslint --ext ts */src */lib", "postinstall": "node ./scripts/install-deps.js" }, diff --git a/scripts/build-native.js b/scripts/build-native.js index 534808c4..f3202865 100755 --- a/scripts/build-native.js +++ b/scripts/build-native.js @@ -4,7 +4,7 @@ const path = require('path') const vars = require('./vars') let lifecycles = [] -for (let dir of ['app', 'terminus-core', 'terminus-ssh', 'terminus-terminal']) { +for (let dir of ['app', 'terminus-core', 'terminus-local', 'terminus-ssh', 'terminus-terminal']) { const build = rebuild({ buildPath: path.resolve(__dirname, '../' + dir), electronVersion: vars.electronVersion, diff --git a/scripts/vars.js b/scripts/vars.js index 880871d3..25462484 100755 --- a/scripts/vars.js +++ b/scripts/vars.js @@ -17,6 +17,7 @@ if (exports.version.includes('-c')) { exports.builtinPlugins = [ 'terminus-core', 'terminus-settings', + 'terminus-local', 'terminus-terminal', 'terminus-community-color-schemes', 'terminus-plugin-manager', diff --git a/terminus-core/README.md b/terminus-core/README.md index 3c138e4b..454f3532 100644 --- a/terminus-core/README.md +++ b/terminus-core/README.md @@ -1,7 +1,7 @@ Terminus Core Plugin -------------------- -See also: [Settings plugin API](./settings/), [Terminal plugin API](./terminal/) +See also: [Settings plugin API](./settings/), [Terminal plugin API](./terminal/), [Local terminal API](./local/) * tabbed interface services * toolbar UI diff --git a/terminus-core/src/services/config.service.ts b/terminus-core/src/services/config.service.ts index 4a0f4367..382442ee 100644 --- a/terminus-core/src/services/config.service.ts +++ b/terminus-core/src/services/config.service.ts @@ -105,16 +105,10 @@ export class ConfigService { private constructor ( electron: ElectronService, private hostApp: HostAppService, - @Inject(ConfigProvider) configProviders: ConfigProvider[], + @Inject(ConfigProvider) private configProviders: ConfigProvider[], ) { this.path = path.join(electron.app.getPath('userData'), 'config.yaml') - this.defaults = configProviders.map(provider => { - let defaults = provider.platformDefaults[hostApp.platform] || {} - if (provider.defaults) { - defaults = configMerge(defaults, provider.defaults) - } - return defaults - }).reduce(configMerge) + this.defaults = this.mergeDefaults() this.load() hostApp.configChangeBroadcast$.subscribe(() => { @@ -123,6 +117,17 @@ export class ConfigService { }) } + mergeDefaults (): unknown { + const providers = this.configProviders + return providers.map(provider => { + let defaults = provider.platformDefaults[this.hostApp.platform] || {} + if (provider.defaults) { + defaults = configMerge(defaults, provider.defaults) + } + return defaults + }).reduce(configMerge) + } + getDefaults (): Record { const cleanup = o => { if (o instanceof Array) { diff --git a/terminus-local/.gitignore b/terminus-local/.gitignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/terminus-local/.gitignore @@ -0,0 +1 @@ +dist diff --git a/terminus-local/README.md b/terminus-local/README.md new file mode 100644 index 00000000..25307867 --- /dev/null +++ b/terminus-local/README.md @@ -0,0 +1,23 @@ +Terminus Local Plugin +--------------------- + +* local shells + +Using the API: + +```ts +import { ShellProvider } from 'terminus-local' +``` + +Exporting your subclasses: + +```ts +@NgModule({ + ... + providers: [ + ... + { provide: ShellProvider, useClass: MyShellPlugin, multi: true }, + ... + ] +}) +``` diff --git a/terminus-local/package.json b/terminus-local/package.json new file mode 100644 index 00000000..256f5118 --- /dev/null +++ b/terminus-local/package.json @@ -0,0 +1,48 @@ +{ + "name": "terminus-local", + "version": "1.0.135-nightly.0", + "description": "Terminus' local shell plugin", + "keywords": [ + "terminus-builtin-plugin" + ], + "main": "dist/index.js", + "typings": "typings/index.d.ts", + "scripts": { + "build": "webpack --progress --color --display-modules", + "watch": "webpack --progress --color --watch" + }, + "files": [ + "typings" + ], + "author": "Eugene Pankov", + "license": "MIT", + "dependencies": { + "hterm-umdjs": "1.4.1", + "opentype.js": "^1.3.3" + }, + "devDependencies": { + "@types/deep-equal": "^1.0.0", + "@types/shell-escape": "^0.2.0", + "ansi-colors": "^4.1.1", + "dataurl": "0.1.0", + "deep-equal": "2.0.5", + "mz": "^2.6.0", + "ps-node": "^0.1.6", + "runes": "^0.4.2", + "shell-escape": "^0.2.0", + "slugify": "^1.4.0", + "utils-decorators": "^1.8.1" + }, + "peerDependencies": { + "@angular/animations": "^9.1.9", + "@angular/common": "^9.1.11", + "@angular/core": "^9.1.9", + "@angular/forms": "^9.1.11", + "@angular/platform-browser": "^9.1.11", + "@ng-bootstrap/ng-bootstrap": "^6.1.0", + "rxjs": "^6.5.5", + "terminus-core": "*", + "terminus-settings": "*", + "terminus-terminal": "*" + } +} diff --git a/terminus-local/src/api.ts b/terminus-local/src/api.ts new file mode 100644 index 00000000..4ee176f5 --- /dev/null +++ b/terminus-local/src/api.ts @@ -0,0 +1,57 @@ +export interface Shell { + id: string + name?: string + command: string + args?: string[] + env: Record + + /** + * Base path to which shell's internal FS is relative + * Currently used for WSL only + */ + fsBase?: string + + /** + * SVG icon + */ + icon?: string + + hidden?: boolean +} + +/** + * Extend to add support for more shells + */ +export abstract class ShellProvider { + abstract provide (): Promise +} + + +export interface SessionOptions { + restoreFromPTYID?: string + name?: string + command: string + args?: string[] + cwd?: string + env?: Record + width?: number + height?: number + pauseAfterExit?: boolean + runAsAdministrator?: boolean +} + +export interface Profile { + name: string + color?: string + sessionOptions: SessionOptions + shell?: string + isBuiltin?: boolean + icon?: string + disableDynamicTitle?: boolean +} + +export interface ChildProcess { + pid: number + ppid: number + command: string +} diff --git a/terminus-terminal/src/buttonProvider.ts b/terminus-local/src/buttonProvider.ts similarity index 100% rename from terminus-terminal/src/buttonProvider.ts rename to terminus-local/src/buttonProvider.ts diff --git a/terminus-local/src/cli.ts b/terminus-local/src/cli.ts new file mode 100644 index 00000000..4f620888 --- /dev/null +++ b/terminus-local/src/cli.ts @@ -0,0 +1,119 @@ +import * as path from 'path' +import * as fs from 'mz/fs' +import { Injectable } from '@angular/core' +import { CLIHandler, CLIEvent, HostAppService, AppService, ConfigService } from 'terminus-core' +import { TerminalService } from './services/terminal.service' + +@Injectable() +export class TerminalCLIHandler extends CLIHandler { + firstMatchOnly = true + priority = 0 + + constructor ( + private config: ConfigService, + private hostApp: HostAppService, + private terminal: TerminalService, + ) { + super() + } + + async handle (event: CLIEvent): Promise { + const op = event.argv._[0] + + if (op === 'open') { + this.handleOpenDirectory(path.resolve(event.cwd, event.argv.directory)) + } else if (op === 'run') { + this.handleRunCommand(event.argv.command) + } else if (op === 'profile') { + this.handleOpenProfile(event.argv.profileName) + } else { + return false + } + + return true + } + + private async handleOpenDirectory (directory: string) { + if (directory.length > 1 && (directory.endsWith('/') || directory.endsWith('\\'))) { + directory = directory.substring(0, directory.length - 1) + } + if (await fs.exists(directory)) { + if ((await fs.stat(directory)).isDirectory()) { + this.terminal.openTab(undefined, directory) + this.hostApp.bringToFront() + } + } + } + + private handleRunCommand (command: string[]) { + this.terminal.openTab({ + name: '', + sessionOptions: { + command: command[0], + args: command.slice(1), + }, + }, null, true) + this.hostApp.bringToFront() + } + + private handleOpenProfile (profileName: string) { + const profile = this.config.store.terminal.profiles.find(x => x.name === profileName) + if (!profile) { + console.error('Requested profile', profileName, 'not found') + return + } + this.terminal.openTabWithOptions(profile.sessionOptions) + this.hostApp.bringToFront() + } +} + + +@Injectable() +export class OpenPathCLIHandler extends CLIHandler { + firstMatchOnly = true + priority = -100 + + constructor ( + private terminal: TerminalService, + private hostApp: HostAppService, + ) { + super() + } + + async handle (event: CLIEvent): Promise { + const op = event.argv._[0] + const opAsPath = op ? path.resolve(event.cwd, op) : null + + if (opAsPath && (await fs.lstat(opAsPath)).isDirectory()) { + this.terminal.openTab(undefined, opAsPath) + this.hostApp.bringToFront() + return true + } + + return false + } +} + +@Injectable() +export class AutoOpenTabCLIHandler extends CLIHandler { + firstMatchOnly = true + priority = -1000 + + constructor ( + private app: AppService, + private config: ConfigService, + private terminal: TerminalService, + ) { + super() + } + + async handle (event: CLIEvent): Promise { + if (!event.secondInstance && this.config.store.terminal.autoOpen) { + this.app.ready$.subscribe(() => { + this.terminal.openTab() + }) + return true + } + return false + } +} diff --git a/terminus-terminal/src/components/editProfileModal.component.pug b/terminus-local/src/components/editProfileModal.component.pug similarity index 100% rename from terminus-terminal/src/components/editProfileModal.component.pug rename to terminus-local/src/components/editProfileModal.component.pug diff --git a/terminus-terminal/src/components/editProfileModal.component.ts b/terminus-local/src/components/editProfileModal.component.ts similarity index 95% rename from terminus-terminal/src/components/editProfileModal.component.ts rename to terminus-local/src/components/editProfileModal.component.ts index 8e2e0a43..ab70a2b6 100644 --- a/terminus-terminal/src/components/editProfileModal.component.ts +++ b/terminus-local/src/components/editProfileModal.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { UACService } from '../services/uac.service' -import { Profile } from '../api/interfaces' +import { Profile } from '../api' /** @hidden */ @Component({ diff --git a/terminus-terminal/src/components/environmentEditor.component.pug b/terminus-local/src/components/environmentEditor.component.pug similarity index 100% rename from terminus-terminal/src/components/environmentEditor.component.pug rename to terminus-local/src/components/environmentEditor.component.pug diff --git a/terminus-terminal/src/components/environmentEditor.component.scss b/terminus-local/src/components/environmentEditor.component.scss similarity index 100% rename from terminus-terminal/src/components/environmentEditor.component.scss rename to terminus-local/src/components/environmentEditor.component.scss diff --git a/terminus-terminal/src/components/environmentEditor.component.ts b/terminus-local/src/components/environmentEditor.component.ts similarity index 100% rename from terminus-terminal/src/components/environmentEditor.component.ts rename to terminus-local/src/components/environmentEditor.component.ts diff --git a/terminus-terminal/src/components/shellSettingsTab.component.pug b/terminus-local/src/components/shellSettingsTab.component.pug similarity index 100% rename from terminus-terminal/src/components/shellSettingsTab.component.pug rename to terminus-local/src/components/shellSettingsTab.component.pug diff --git a/terminus-terminal/src/components/shellSettingsTab.component.ts b/terminus-local/src/components/shellSettingsTab.component.ts similarity index 98% rename from terminus-terminal/src/components/shellSettingsTab.component.ts rename to terminus-local/src/components/shellSettingsTab.component.ts index 8f6bfd7f..e2abbb52 100644 --- a/terminus-terminal/src/components/shellSettingsTab.component.ts +++ b/terminus-local/src/components/shellSettingsTab.component.ts @@ -3,7 +3,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { Subscription } from 'rxjs' import { ConfigService, ElectronService, HostAppService, Platform, WIN_BUILD_CONPTY_SUPPORTED, WIN_BUILD_CONPTY_STABLE, isWindowsBuild } from 'terminus-core' import { EditProfileModalComponent } from './editProfileModal.component' -import { Shell, Profile } from '../api/interfaces' +import { Shell, Profile } from '../api' import { TerminalService } from '../services/terminal.service' /** @hidden */ diff --git a/terminus-terminal/src/components/terminalTab.component.ts b/terminus-local/src/components/terminalTab.component.ts similarity index 89% rename from terminus-terminal/src/components/terminalTab.component.ts rename to terminus-local/src/components/terminalTab.component.ts index 96a87c51..a35da2cb 100644 --- a/terminus-terminal/src/components/terminalTab.component.ts +++ b/terminus-local/src/components/terminalTab.component.ts @@ -1,8 +1,8 @@ import { Component, Input, Injector } from '@angular/core' import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core' -import { BaseTerminalTabComponent } from '../api/baseTerminalTab.component' -import { SessionOptions } from '../api/interfaces' -import { Session } from '../services/sessions.service' +import { BaseTerminalTabComponent } from 'terminus-terminal' +import { SessionOptions } from '../api' +import { Session } from '../session' /** @hidden */ @Component({ @@ -52,13 +52,11 @@ export class TerminalTabComponent extends BaseTerminalTabComponent { } initializeSession (columns: number, rows: number): void { - this.sessions.addSession( - this.session!, - Object.assign({}, this.sessionOptions, { - width: columns, - height: rows, - }) - ) + this.session!.start({ + ...this.sessionOptions, + width: columns, + height: rows, + }) this.attachSessionHandlers(true) this.recoveryStateChangedHint.next() diff --git a/terminus-local/src/config.ts b/terminus-local/src/config.ts new file mode 100644 index 00000000..7884f5e6 --- /dev/null +++ b/terminus-local/src/config.ts @@ -0,0 +1,62 @@ +import { ConfigProvider, Platform } from 'terminus-core' + +/** @hidden */ +export class TerminalConfigProvider extends ConfigProvider { + defaults = { + hotkeys: { + 'copy-current-path': [], + shell: { + __nonStructural: true, + }, + profile: { + __nonStructural: true, + }, + }, + terminal: { + autoOpen: false, + customShell: '', + workingDirectory: '', + alwaysUseWorkingDirectory: false, + useConPTY: true, + showDefaultProfiles: true, + environment: {}, + profiles: [], + }, + } + + platformDefaults = { + [Platform.macOS]: { + terminal: { + shell: 'default', + profile: 'user-default', + }, + hotkeys: { + 'new-tab': [ + '⌘-T', + ], + }, + }, + [Platform.Windows]: { + terminal: { + shell: 'clink', + profile: 'cmd-clink', + }, + hotkeys: { + 'new-tab': [ + 'Ctrl-Shift-T', + ], + }, + }, + [Platform.Linux]: { + terminal: { + shell: 'default', + profile: 'user-default', + }, + hotkeys: { + 'new-tab': [ + 'Ctrl-Shift-T', + ], + }, + }, + } +} diff --git a/terminus-local/src/hotkeys.ts b/terminus-local/src/hotkeys.ts new file mode 100644 index 00000000..aeb964d3 --- /dev/null +++ b/terminus-local/src/hotkeys.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core' +import { HotkeyDescription, HotkeyProvider } from 'terminus-core' +import { TerminalService } from './services/terminal.service' + +/** @hidden */ +@Injectable() +export class TerminalHotkeyProvider extends HotkeyProvider { + hotkeys: HotkeyDescription[] = [ + { + id: 'new-tab', + name: 'New tab', + }, + ] + + constructor ( + private terminal: TerminalService, + ) { super() } + + async provide (): Promise { + const profiles = await this.terminal.getProfiles() + return [ + ...this.hotkeys, + ...profiles.map(profile => ({ + id: `profile.${this.terminal.getProfileID(profile)}`, + name: `New tab: ${profile.name}`, + })), + ] + } +} diff --git a/terminus-terminal/src/icons/alpine.svg b/terminus-local/src/icons/alpine.svg similarity index 100% rename from terminus-terminal/src/icons/alpine.svg rename to terminus-local/src/icons/alpine.svg diff --git a/terminus-terminal/src/icons/clink.svg b/terminus-local/src/icons/clink.svg similarity index 100% rename from terminus-terminal/src/icons/clink.svg rename to terminus-local/src/icons/clink.svg diff --git a/terminus-terminal/src/icons/cmd.svg b/terminus-local/src/icons/cmd.svg similarity index 100% rename from terminus-terminal/src/icons/cmd.svg rename to terminus-local/src/icons/cmd.svg diff --git a/terminus-terminal/src/icons/cmder-powershell.svg b/terminus-local/src/icons/cmder-powershell.svg similarity index 100% rename from terminus-terminal/src/icons/cmder-powershell.svg rename to terminus-local/src/icons/cmder-powershell.svg diff --git a/terminus-terminal/src/icons/cmder.svg b/terminus-local/src/icons/cmder.svg similarity index 100% rename from terminus-terminal/src/icons/cmder.svg rename to terminus-local/src/icons/cmder.svg diff --git a/terminus-terminal/src/icons/cygwin.svg b/terminus-local/src/icons/cygwin.svg similarity index 100% rename from terminus-terminal/src/icons/cygwin.svg rename to terminus-local/src/icons/cygwin.svg diff --git a/terminus-terminal/src/icons/debian.svg b/terminus-local/src/icons/debian.svg similarity index 100% rename from terminus-terminal/src/icons/debian.svg rename to terminus-local/src/icons/debian.svg diff --git a/terminus-terminal/src/icons/git-bash.svg b/terminus-local/src/icons/git-bash.svg similarity index 100% rename from terminus-terminal/src/icons/git-bash.svg rename to terminus-local/src/icons/git-bash.svg diff --git a/terminus-terminal/src/icons/linux.svg b/terminus-local/src/icons/linux.svg similarity index 100% rename from terminus-terminal/src/icons/linux.svg rename to terminus-local/src/icons/linux.svg diff --git a/terminus-terminal/src/icons/plus.svg b/terminus-local/src/icons/plus.svg similarity index 100% rename from terminus-terminal/src/icons/plus.svg rename to terminus-local/src/icons/plus.svg diff --git a/terminus-terminal/src/icons/powershell-core.svg b/terminus-local/src/icons/powershell-core.svg similarity index 100% rename from terminus-terminal/src/icons/powershell-core.svg rename to terminus-local/src/icons/powershell-core.svg diff --git a/terminus-terminal/src/icons/powershell.svg b/terminus-local/src/icons/powershell.svg similarity index 100% rename from terminus-terminal/src/icons/powershell.svg rename to terminus-local/src/icons/powershell.svg diff --git a/terminus-terminal/src/icons/profiles.svg b/terminus-local/src/icons/profiles.svg similarity index 100% rename from terminus-terminal/src/icons/profiles.svg rename to terminus-local/src/icons/profiles.svg diff --git a/terminus-terminal/src/icons/suse.svg b/terminus-local/src/icons/suse.svg similarity index 100% rename from terminus-terminal/src/icons/suse.svg rename to terminus-local/src/icons/suse.svg diff --git a/terminus-terminal/src/icons/ubuntu.svg b/terminus-local/src/icons/ubuntu.svg similarity index 100% rename from terminus-terminal/src/icons/ubuntu.svg rename to terminus-local/src/icons/ubuntu.svg diff --git a/terminus-local/src/index.ts b/terminus-local/src/index.ts new file mode 100644 index 00000000..f1344a4e --- /dev/null +++ b/terminus-local/src/index.ts @@ -0,0 +1,129 @@ +import { NgModule } from '@angular/core' +import { BrowserModule } from '@angular/platform-browser' +import { FormsModule } from '@angular/forms' +import { NgbModule } from '@ng-bootstrap/ng-bootstrap' +import { ToastrModule } from 'ngx-toastr' + +import TerminusCorePlugin, { HostAppService, ToolbarButtonProvider, TabRecoveryProvider, ConfigProvider, HotkeysService, HotkeyProvider, TabContextMenuItemProvider, CLIHandler } from 'terminus-core' +import TerminusTerminalModule from 'terminus-terminal' +import { SettingsTabProvider } from 'terminus-settings' + +import { TerminalTabComponent } from './components/terminalTab.component' +import { ShellSettingsTabComponent } from './components/shellSettingsTab.component' +import { EditProfileModalComponent } from './components/editProfileModal.component' +import { EnvironmentEditorComponent } from './components/environmentEditor.component' + +import { TerminalService } from './services/terminal.service' +import { DockMenuService } from './services/dockMenu.service' + +import { ButtonProvider } from './buttonProvider' +import { RecoveryProvider } from './recoveryProvider' +import { ShellProvider } from './api' +import { ShellSettingsTabProvider } from './settings' +import { TerminalConfigProvider } from './config' +import { TerminalHotkeyProvider } from './hotkeys' +import { NewTabContextMenu, SaveAsProfileContextMenu } from './tabContextMenu' + +import { CmderShellProvider } from './shells/cmder' +import { CustomShellProvider } from './shells/custom' +import { Cygwin32ShellProvider } from './shells/cygwin32' +import { Cygwin64ShellProvider } from './shells/cygwin64' +import { GitBashShellProvider } from './shells/gitBash' +import { LinuxDefaultShellProvider } from './shells/linuxDefault' +import { MacOSDefaultShellProvider } from './shells/macDefault' +import { POSIXShellsProvider } from './shells/posix' +import { PowerShellCoreShellProvider } from './shells/powershellCore' +import { WindowsDefaultShellProvider } from './shells/winDefault' +import { WindowsStockShellsProvider } from './shells/windowsStock' +import { WSLShellProvider } from './shells/wsl' + +import { AutoOpenTabCLIHandler, OpenPathCLIHandler, TerminalCLIHandler } from './cli' + +/** @hidden */ +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + NgbModule, + ToastrModule, + TerminusCorePlugin, + TerminusTerminalModule, + ], + providers: [ + { provide: SettingsTabProvider, useClass: ShellSettingsTabProvider, multi: true }, + + { provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true }, + { provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true }, + { provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true }, + { provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true }, + + { provide: ShellProvider, useClass: WindowsDefaultShellProvider, multi: true }, + { provide: ShellProvider, useClass: MacOSDefaultShellProvider, multi: true }, + { provide: ShellProvider, useClass: LinuxDefaultShellProvider, multi: true }, + { provide: ShellProvider, useClass: WindowsStockShellsProvider, multi: true }, + { provide: ShellProvider, useClass: PowerShellCoreShellProvider, multi: true }, + { provide: ShellProvider, useClass: CmderShellProvider, multi: true }, + { provide: ShellProvider, useClass: CustomShellProvider, multi: true }, + { provide: ShellProvider, useClass: Cygwin32ShellProvider, multi: true }, + { provide: ShellProvider, useClass: Cygwin64ShellProvider, multi: true }, + { provide: ShellProvider, useClass: GitBashShellProvider, multi: true }, + { provide: ShellProvider, useClass: POSIXShellsProvider, multi: true }, + { provide: ShellProvider, useClass: WSLShellProvider, multi: true }, + + { provide: TabContextMenuItemProvider, useClass: NewTabContextMenu, multi: true }, + { provide: TabContextMenuItemProvider, useClass: SaveAsProfileContextMenu, multi: true }, + + { provide: CLIHandler, useClass: TerminalCLIHandler, multi: true }, + { provide: CLIHandler, useClass: OpenPathCLIHandler, multi: true }, + { provide: CLIHandler, useClass: AutoOpenTabCLIHandler, multi: true }, + + // For WindowsDefaultShellProvider + PowerShellCoreShellProvider, + WSLShellProvider, + WindowsStockShellsProvider, + ], + entryComponents: [ + TerminalTabComponent, + ShellSettingsTabComponent, + EditProfileModalComponent, + ] as any[], + declarations: [ + TerminalTabComponent, + ShellSettingsTabComponent, + EditProfileModalComponent, + EnvironmentEditorComponent, + ] as any[], + exports: [ + TerminalTabComponent, + EnvironmentEditorComponent, + ], +}) +export default class LocalTerminalModule { // eslint-disable-line @typescript-eslint/no-extraneous-class + private constructor ( + hotkeys: HotkeysService, + terminal: TerminalService, + hostApp: HostAppService, + dockMenu: DockMenuService, + ) { + hotkeys.matchedHotkey.subscribe(async (hotkey) => { + if (hotkey === 'new-tab') { + terminal.openTab() + } + if (hotkey === 'new-window') { + hostApp.newWindow() + } + if (hotkey.startsWith('profile.')) { + const profile = await terminal.getProfileByID(hotkey.split('.')[1]) + if (profile) { + terminal.openTabWithOptions(profile.sessionOptions) + } + } + }) + + dockMenu.update() + } +} + +export { TerminalTabComponent } +export { TerminalService, ShellProvider } +export * from './api' diff --git a/terminus-terminal/src/recoveryProvider.ts b/terminus-local/src/recoveryProvider.ts similarity index 100% rename from terminus-terminal/src/recoveryProvider.ts rename to terminus-local/src/recoveryProvider.ts diff --git a/terminus-terminal/src/services/dockMenu.service.ts b/terminus-local/src/services/dockMenu.service.ts similarity index 100% rename from terminus-terminal/src/services/dockMenu.service.ts rename to terminus-local/src/services/dockMenu.service.ts diff --git a/terminus-terminal/src/services/terminal.service.ts b/terminus-local/src/services/terminal.service.ts similarity index 96% rename from terminus-terminal/src/services/terminal.service.ts rename to terminus-local/src/services/terminal.service.ts index 36f9b11c..4c0b8e6e 100644 --- a/terminus-terminal/src/services/terminal.service.ts +++ b/terminus-local/src/services/terminal.service.ts @@ -3,10 +3,9 @@ import slugify from 'slugify' import { Observable, AsyncSubject } from 'rxjs' import { Injectable, Inject } from '@angular/core' import { AppService, Logger, LogService, ConfigService, SplitTabComponent } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell, SessionOptions, Profile } from '../api/interfaces' import { TerminalTabComponent } from '../components/terminalTab.component' -import { UACService } from './uac.service' +import { ShellProvider, Shell, SessionOptions, Profile } from '../api' +import { UACService } from '../services/uac.service' @Injectable({ providedIn: 'root' }) export class TerminalService { diff --git a/terminus-terminal/src/services/uac.service.ts b/terminus-local/src/services/uac.service.ts similarity index 95% rename from terminus-terminal/src/services/uac.service.ts rename to terminus-local/src/services/uac.service.ts index 8caa1cba..b81d15a8 100644 --- a/terminus-terminal/src/services/uac.service.ts +++ b/terminus-local/src/services/uac.service.ts @@ -1,7 +1,7 @@ import * as path from 'path' import { Injectable } from '@angular/core' import { ElectronService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core' -import { SessionOptions } from '../api/interfaces' +import { SessionOptions } from '../api' /** @hidden */ @Injectable({ providedIn: 'root' }) diff --git a/terminus-terminal/src/services/sessions.service.ts b/terminus-local/src/session.ts similarity index 77% rename from terminus-terminal/src/services/sessions.service.ts rename to terminus-local/src/session.ts index b446cdcf..7e191105 100644 --- a/terminus-terminal/src/services/sessions.service.ts +++ b/terminus-local/src/session.ts @@ -1,13 +1,11 @@ import * as psNode from 'ps-node' import * as fs from 'mz/fs' import * as os from 'os' +import { BaseSession } from 'terminus-terminal' import { ipcRenderer } from 'electron' import { getWorkingDirectoryFromPID } from 'native-process-working-directory' -import { Observable, Subject } from 'rxjs' -import { first } from 'rxjs/operators' -import { Injectable } from '@angular/core' -import { Logger, LogService, ConfigService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core' -import { SessionOptions, ChildProcess } from '../api/interfaces' +import { ConfigService, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'terminus-core' +import { SessionOptions, ChildProcess } from './api' /* eslint-disable block-scoped-var */ @@ -85,65 +83,6 @@ export class PTYProxy { } } -/** - * A session object for a [[BaseTerminalTabComponent]] - * Extend this to implement custom I/O and process management for your terminal tab - */ -export abstract class BaseSession { - open: boolean - name: string - truePID: number - protected output = new Subject() - protected binaryOutput = new Subject() - protected closed = new Subject() - protected destroyed = new Subject() - private initialDataBuffer = Buffer.from('') - private initialDataBufferReleased = false - - get output$ (): Observable { return this.output } - get binaryOutput$ (): Observable { return this.binaryOutput } - get closed$ (): Observable { return this.closed } - get destroyed$ (): Observable { return this.destroyed } - - emitOutput (data: Buffer): void { - if (!this.initialDataBufferReleased) { - this.initialDataBuffer = Buffer.concat([this.initialDataBuffer, data]) - } else { - this.output.next(data.toString()) - this.binaryOutput.next(data) - } - } - - releaseInitialDataBuffer (): void { - this.initialDataBufferReleased = true - this.output.next(this.initialDataBuffer.toString()) - this.binaryOutput.next(this.initialDataBuffer) - this.initialDataBuffer = Buffer.from('') - } - - async destroy (): Promise { - if (this.open) { - this.open = false - this.closed.next() - this.destroyed.next() - this.closed.complete() - this.destroyed.complete() - this.output.complete() - this.binaryOutput.complete() - await this.gracefullyKillProcess() - } - } - - abstract start (options: SessionOptions): void - abstract resize (columns: number, rows: number): void - abstract write (data: Buffer): void - abstract kill (signal?: string): void - abstract getChildProcesses (): Promise - abstract gracefullyKillProcess (): Promise - abstract supportsWorkingDirectory (): boolean - abstract getWorkingDirectory (): Promise -} - /** @hidden */ export class Session extends BaseSession { private pty: PTYProxy|null = null @@ -400,28 +339,3 @@ export class Session extends BaseSession { return data } } - -/** @hidden */ -@Injectable({ providedIn: 'root' }) -export class SessionsService { - sessions = new Map() - logger: Logger - private lastID = 0 - - private constructor ( - log: LogService, - ) { - this.logger = log.create('sessions') - } - - addSession (session: BaseSession, options: SessionOptions): BaseSession { - this.lastID++ - options.name = `session-${this.lastID}` - session.start(options) - session.destroyed$.pipe(first()).subscribe(() => { - this.sessions.delete(session.name) - }) - this.sessions.set(session.name, session) - return session - } -} diff --git a/terminus-local/src/settings.ts b/terminus-local/src/settings.ts new file mode 100644 index 00000000..85465e88 --- /dev/null +++ b/terminus-local/src/settings.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core' +import { SettingsTabProvider } from 'terminus-settings' + +import { ShellSettingsTabComponent } from './components/shellSettingsTab.component' + +/** @hidden */ +@Injectable() +export class ShellSettingsTabProvider extends SettingsTabProvider { + id = 'terminal-shell' + icon = 'list-ul' + title = 'Shell' + + getComponentType (): any { + return ShellSettingsTabComponent + } +} diff --git a/terminus-terminal/src/shells/cmder.ts b/terminus-local/src/shells/cmder.ts similarity index 94% rename from terminus-terminal/src/shells/cmder.ts rename to terminus-local/src/shells/cmder.ts index 07c5dce3..3efd4422 100644 --- a/terminus-terminal/src/shells/cmder.ts +++ b/terminus-local/src/shells/cmder.ts @@ -2,8 +2,7 @@ import * as path from 'path' import { Injectable } from '@angular/core' import { HostAppService, Platform } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /** @hidden */ @Injectable() diff --git a/terminus-terminal/src/shells/custom.ts b/terminus-local/src/shells/custom.ts similarity index 85% rename from terminus-terminal/src/shells/custom.ts rename to terminus-local/src/shells/custom.ts index d1e29e6c..a1bcd1a8 100644 --- a/terminus-terminal/src/shells/custom.ts +++ b/terminus-local/src/shells/custom.ts @@ -1,8 +1,7 @@ import { Injectable } from '@angular/core' import { ConfigService } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /** @hidden */ @Injectable() diff --git a/terminus-terminal/src/shells/cygwin32.ts b/terminus-local/src/shells/cygwin32.ts similarity index 92% rename from terminus-terminal/src/shells/cygwin32.ts rename to terminus-local/src/shells/cygwin32.ts index e8417352..6f43954b 100644 --- a/terminus-terminal/src/shells/cygwin32.ts +++ b/terminus-local/src/shells/cygwin32.ts @@ -2,8 +2,7 @@ import * as path from 'path' import { Injectable } from '@angular/core' import { HostAppService, Platform } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /* eslint-disable block-scoped-var */ diff --git a/terminus-terminal/src/shells/cygwin64.ts b/terminus-local/src/shells/cygwin64.ts similarity index 91% rename from terminus-terminal/src/shells/cygwin64.ts rename to terminus-local/src/shells/cygwin64.ts index c116b84a..9874d9ff 100644 --- a/terminus-terminal/src/shells/cygwin64.ts +++ b/terminus-local/src/shells/cygwin64.ts @@ -2,8 +2,7 @@ import * as path from 'path' import { Injectable } from '@angular/core' import { HostAppService, Platform } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /* eslint-disable block-scoped-var */ diff --git a/terminus-terminal/src/shells/gitBash.ts b/terminus-local/src/shells/gitBash.ts similarity index 92% rename from terminus-terminal/src/shells/gitBash.ts rename to terminus-local/src/shells/gitBash.ts index 2041e174..d0fdca43 100644 --- a/terminus-terminal/src/shells/gitBash.ts +++ b/terminus-local/src/shells/gitBash.ts @@ -2,8 +2,7 @@ import * as path from 'path' import { Injectable } from '@angular/core' import { HostAppService, Platform } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /* eslint-disable block-scoped-var */ diff --git a/terminus-terminal/src/shells/linuxDefault.ts b/terminus-local/src/shells/linuxDefault.ts similarity index 92% rename from terminus-terminal/src/shells/linuxDefault.ts rename to terminus-local/src/shells/linuxDefault.ts index 988a2826..42e70df5 100644 --- a/terminus-terminal/src/shells/linuxDefault.ts +++ b/terminus-local/src/shells/linuxDefault.ts @@ -2,8 +2,7 @@ import * as fs from 'mz/fs' import { Injectable } from '@angular/core' import { HostAppService, Platform, LogService, Logger } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /** @hidden */ @Injectable() diff --git a/terminus-terminal/src/shells/macDefault.ts b/terminus-local/src/shells/macDefault.ts similarity index 92% rename from terminus-terminal/src/shells/macDefault.ts rename to terminus-local/src/shells/macDefault.ts index b7456d67..1961ccac 100644 --- a/terminus-terminal/src/shells/macDefault.ts +++ b/terminus-local/src/shells/macDefault.ts @@ -2,8 +2,7 @@ import { exec } from 'mz/child_process' import { Injectable } from '@angular/core' import { HostAppService, Platform } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /** @hidden */ @Injectable() diff --git a/terminus-terminal/src/shells/posix.ts b/terminus-local/src/shells/posix.ts similarity index 89% rename from terminus-terminal/src/shells/posix.ts rename to terminus-local/src/shells/posix.ts index 6545f113..e0403fa1 100644 --- a/terminus-terminal/src/shells/posix.ts +++ b/terminus-local/src/shells/posix.ts @@ -3,8 +3,7 @@ import slugify from 'slugify' import { Injectable } from '@angular/core' import { HostAppService, Platform } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /** @hidden */ @Injectable() diff --git a/terminus-terminal/src/shells/powershellCore.ts b/terminus-local/src/shells/powershellCore.ts similarity index 91% rename from terminus-terminal/src/shells/powershellCore.ts rename to terminus-local/src/shells/powershellCore.ts index b72e0725..c93d299e 100644 --- a/terminus-terminal/src/shells/powershellCore.ts +++ b/terminus-local/src/shells/powershellCore.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core' import { HostAppService, Platform } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' + +import { ShellProvider, Shell } from '../api' /* eslint-disable block-scoped-var */ diff --git a/terminus-terminal/src/shells/winDefault.ts b/terminus-local/src/shells/winDefault.ts similarity index 93% rename from terminus-terminal/src/shells/winDefault.ts rename to terminus-local/src/shells/winDefault.ts index 729d7f94..e6541f02 100644 --- a/terminus-terminal/src/shells/winDefault.ts +++ b/terminus-local/src/shells/winDefault.ts @@ -1,8 +1,7 @@ import { Injectable } from '@angular/core' import { HostAppService, Platform } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' import { WSLShellProvider } from './wsl' import { PowerShellCoreShellProvider } from './powershellCore' diff --git a/terminus-terminal/src/shells/windowsStock.ts b/terminus-local/src/shells/windowsStock.ts similarity index 94% rename from terminus-terminal/src/shells/windowsStock.ts rename to terminus-local/src/shells/windowsStock.ts index eeae316f..475e2b33 100644 --- a/terminus-terminal/src/shells/windowsStock.ts +++ b/terminus-local/src/shells/windowsStock.ts @@ -2,8 +2,7 @@ import * as path from 'path' import { Injectable } from '@angular/core' import { HostAppService, Platform, ElectronService } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /** @hidden */ @Injectable() diff --git a/terminus-terminal/src/shells/wsl.ts b/terminus-local/src/shells/wsl.ts similarity index 97% rename from terminus-terminal/src/shells/wsl.ts rename to terminus-local/src/shells/wsl.ts index 65b172c4..8aacf0a9 100644 --- a/terminus-terminal/src/shells/wsl.ts +++ b/terminus-local/src/shells/wsl.ts @@ -4,8 +4,7 @@ import slugify from 'slugify' import { Injectable } from '@angular/core' import { HostAppService, Platform, isWindowsBuild, WIN_BUILD_WSL_EXE_DISTRO_FLAG } from 'terminus-core' -import { ShellProvider } from '../api/shellProvider' -import { Shell } from '../api/interfaces' +import { ShellProvider, Shell } from '../api' /* eslint-disable block-scoped-var */ diff --git a/terminus-local/src/tabContextMenu.ts b/terminus-local/src/tabContextMenu.ts new file mode 100644 index 00000000..22a01185 --- /dev/null +++ b/terminus-local/src/tabContextMenu.ts @@ -0,0 +1,132 @@ +import { MenuItemConstructorOptions } from 'electron' +import { Injectable, NgZone } from '@angular/core' +import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, SplitTabComponent, NotificationsService } from 'terminus-core' +import { TerminalTabComponent } from './components/terminalTab.component' +import { UACService } from './services/uac.service' +import { TerminalService } from './services/terminal.service' + +/** @hidden */ +@Injectable() +export class SaveAsProfileContextMenu extends TabContextMenuItemProvider { + constructor ( + private config: ConfigService, + private zone: NgZone, + private notifications: NotificationsService, + ) { + super() + } + + async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise { + if (!(tab instanceof TerminalTabComponent)) { + return [] + } + const items: MenuItemConstructorOptions[] = [ + { + label: 'Save as profile', + click: () => this.zone.run(async () => { + const profile = { + sessionOptions: { + ...tab.sessionOptions, + cwd: await tab.session?.getWorkingDirectory() ?? tab.sessionOptions.cwd, + }, + name: tab.sessionOptions.command, + } + this.config.store.terminal.profiles = [ + ...this.config.store.terminal.profiles, + profile, + ] + this.config.save() + this.notifications.info('Saved') + }), + }, + ] + + return items + } +} + +/** @hidden */ +@Injectable() +export class NewTabContextMenu extends TabContextMenuItemProvider { + weight = 10 + + constructor ( + public config: ConfigService, + private zone: NgZone, + private terminalService: TerminalService, + private uac: UACService, + ) { + super() + } + + async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise { + const profiles = await this.terminalService.getProfiles() + + const items: MenuItemConstructorOptions[] = [ + { + label: 'New terminal', + click: () => this.zone.run(() => { + this.terminalService.openTabWithOptions((tab as any).sessionOptions) + }), + }, + { + label: 'New with profile', + submenu: profiles.map(profile => ({ + label: profile.name, + click: () => this.zone.run(async () => { + let workingDirectory = this.config.store.terminal.workingDirectory + if (this.config.store.terminal.alwaysUseWorkingDirectory !== true && tab instanceof TerminalTabComponent) { + workingDirectory = await tab.session?.getWorkingDirectory() + } + await this.terminalService.openTab(profile, workingDirectory) + }), + })), + }, + ] + + if (this.uac.isAvailable) { + items.push({ + label: 'New admin tab', + submenu: profiles.map(profile => ({ + label: profile.name, + click: () => this.zone.run(async () => { + this.terminalService.openTabWithOptions({ + ...profile.sessionOptions, + runAsAdministrator: true, + }) + }), + })), + }) + } + + if (tab instanceof TerminalTabComponent && tabHeader && this.uac.isAvailable) { + items.push({ + label: 'Duplicate as administrator', + click: () => this.zone.run(async () => { + this.terminalService.openTabWithOptions({ + ...tab.sessionOptions, + runAsAdministrator: true, + }) + }), + }) + } + + if (tab instanceof TerminalTabComponent && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) { + items.push({ + label: 'Focus all panes', + click: () => this.zone.run(() => { + tab.focusAllPanes() + }), + }) + } + + if (tab instanceof TerminalTabComponent && tab.session?.supportsWorkingDirectory()) { + items.push({ + label: 'Copy current path', + click: () => this.zone.run(() => tab.copyCurrentPath()), + }) + } + + return items + } +} diff --git a/terminus-local/tsconfig.json b/terminus-local/tsconfig.json new file mode 100644 index 00000000..a863b98a --- /dev/null +++ b/terminus-local/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.json", + "exclude": ["node_modules", "dist", "typings"], + "compilerOptions": { + "baseUrl": "src" + } +} diff --git a/terminus-local/tsconfig.typings.json b/terminus-local/tsconfig.typings.json new file mode 100644 index 00000000..94ae3c19 --- /dev/null +++ b/terminus-local/tsconfig.typings.json @@ -0,0 +1,16 @@ +{ + "extends": "../tsconfig.json", + "exclude": ["node_modules", "dist", "typings"], + "compilerOptions": { + "baseUrl": "src", + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "./typings", + "paths": { + "terminus-*": ["../../terminus-*"], + "*": [ + "../../app/node_modules/*" + ] + } + } +} diff --git a/terminus-local/webpack.config.js b/terminus-local/webpack.config.js new file mode 100644 index 00000000..3a406025 --- /dev/null +++ b/terminus-local/webpack.config.js @@ -0,0 +1,5 @@ +const config = require('../webpack.plugin.config') +module.exports = config({ + name: 'local', + dirname: __dirname, +}) diff --git a/terminus-local/yarn.lock b/terminus-local/yarn.lock new file mode 100644 index 00000000..bb890e1a --- /dev/null +++ b/terminus-local/yarn.lock @@ -0,0 +1,515 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/deep-equal@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.1.tgz#71cfabb247c22bcc16d536111f50c0ed12476b03" + integrity sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg== + +"@types/shell-escape@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@types/shell-escape/-/shell-escape-0.2.0.tgz#cd2f0df814388599dd07196dcc510de2669d1ed2" + integrity sha512-7kUdtJtUylvyISJbe9FMcvMTjRdP0EvNDO1WbT0lT22k/IPBiPRTpmWaKu5HTWLCGLQRWVHrzVHZktTDvvR23g== + +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + +available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.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" + +connected-domain@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/connected-domain/-/connected-domain-1.0.0.tgz#bfe77238c74be453a79f0cb6058deeb4f2358e93" + integrity sha1-v+dyOMdL5FOnnwy2BY3utPI1jpM= + +dataurl@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dataurl/-/dataurl-0.1.0.tgz#1f4734feddec05ffe445747978d86759c4b33199" + integrity sha1-H0c0/t3sBf/kRXR5eNhnWcSzMZk= + +deep-equal@2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.5.tgz#55cd2fe326d83f9cbf7261ef0e060b3f724c5cb9" + integrity sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw== + dependencies: + call-bind "^1.0.0" + es-get-iterator "^1.1.1" + get-intrinsic "^1.0.1" + is-arguments "^1.0.4" + is-date-object "^1.0.2" + is-regex "^1.1.1" + isarray "^2.0.5" + object-is "^1.1.4" + object-keys "^1.1.1" + object.assign "^4.1.2" + regexp.prototype.flags "^1.3.0" + side-channel "^1.0.3" + which-boxed-primitive "^1.0.1" + which-collection "^1.0.1" + which-typed-array "^1.1.2" + +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" + +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-abstract@^1.18.0-next.2: + 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-get-iterator@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.2.tgz#9234c54aba713486d7ebde0220864af5e2b283f7" + integrity sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.0" + has-symbols "^1.0.1" + is-arguments "^1.1.0" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.5" + isarray "^2.0.5" + +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" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +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.1, get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, 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: + 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-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" + +hterm-umdjs@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/hterm-umdjs/-/hterm-umdjs-1.4.1.tgz#0cd5352eaf927c70b83c36146cf2c2a281dba957" + integrity sha512-r5JOmdDK1bZCmp3cKcuGRLVeum33H+pzD119ZxmQou+QUVe6SAVSz03HvKWVhM2Ao1Biv+fkhFDmnsaRPq0tFg== + +is-arguments@^1.0.4, is-arguments@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + +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.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-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, is-date-object@^1.0.2: + 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-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= + +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.1, 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-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +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: + 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" + +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" + +is-typed-array@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" + foreach "^2.0.5" + has-symbols "^1.0.1" + +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakset@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" + integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +mz@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +object-assign@^4.0.1: + version "4.1.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.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +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-is@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +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, 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" + +opentype.js@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-1.3.3.tgz#65b8645b090a1ad444065b784d442fa19d1061f6" + integrity sha512-/qIY/+WnKGlPIIPhbeNjynfD2PO15G9lA/xqlX2bDH+4lc3Xz5GCQ68mqxj3DdUv6AJqCeaPvuAoH8mVL0zcuA== + dependencies: + string.prototype.codepointat "^0.2.1" + tiny-inflate "^1.0.3" + +ps-node@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ps-node/-/ps-node-0.1.6.tgz#9af67a99d7b1d0132e51a503099d38a8d2ace2c3" + integrity sha1-mvZ6mdex0BMuUaUDCZ04qNKs4sM= + dependencies: + table-parser "^0.1.3" + +regexp.prototype.flags@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" + integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +runes@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/runes/-/runes-0.4.3.tgz#32f7738844bc767b65cc68171528e3373c7bb355" + integrity sha512-K6p9y4ZyL9wPzA+PMDloNQPfoDGTiFYDvdlXznyGKgD10BJpcAosvATKrExRKOrNLgD8E7Um7WGW0lxsnOuNLg== + +shell-escape@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/shell-escape/-/shell-escape-0.2.0.tgz#68fd025eb0490b4f567a027f0bf22480b5f84133" + integrity sha1-aP0CXrBJC09WegJ/C/IkgLX4QTM= + +side-channel@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +slugify@^1.4.0: + version "1.4.7" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.7.tgz#e42359d505afd84a44513280868e31202a79a628" + integrity sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg== + +string.prototype.codepointat@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc" + integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg== + +string.prototype.trimend@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz#6ddd9a8796bc714b489a3ae22246a208f37bfa46" + integrity sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +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.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz#22d45da81015309cd0cdd79787e8919fc5c613e7" + integrity sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +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" + +table-parser@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/table-parser/-/table-parser-0.1.3.tgz#0441cfce16a59481684c27d1b5a67ff15a43c7b0" + integrity sha1-BEHPzhallIFoTCfRtaZ/8VpDx7A= + dependencies: + connected-domain "^1.0.0" + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +tiny-inflate@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" + integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== + +tinyqueue@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" + integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== + +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" + +utils-decorators@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/utils-decorators/-/utils-decorators-1.8.1.tgz#6e7e2cf46c05a9554c05f004e5235696142bd5f7" + integrity sha512-UpqzJj40jdTknZpxdeYL7p+8Ynl3bpHP6yoxoY+RmuDCOaelTiOz4GcDpScPvfZhv/ivHTV1bPJZeQd8tlxczA== + dependencies: + tinyqueue "^2.0.3" + +which-boxed-primitive@^1.0.1, 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" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" diff --git a/terminus-plugin-manager/src/components/pluginsSettingsTab.component.pug b/terminus-plugin-manager/src/components/pluginsSettingsTab.component.pug index 1c522607..a7410f00 100644 --- a/terminus-plugin-manager/src/components/pluginsSettingsTab.component.pug +++ b/terminus-plugin-manager/src/components/pluginsSettingsTab.component.pug @@ -20,6 +20,7 @@ div strong {{plugin.name}} small.text-muted.ml-1(*ngIf='!plugin.isBuiltin') {{plugin.version}} / {{plugin.author}} + small.text-muted.ml-1(*ngIf='plugin.isBuiltin') Built-in small.text-warning.ml-1(*ngIf='!isPluginEnabled(plugin)') Disabled a.text-muted.mb-0((click)='showPluginInfo(plugin)') small {{plugin.description}} diff --git a/terminus-terminal/README.md b/terminus-terminal/README.md index 80ab5c2d..33027a85 100644 --- a/terminus-terminal/README.md +++ b/terminus-terminal/README.md @@ -4,23 +4,3 @@ Terminus Terminal Plugin * terminal tabs * terminal frontends * session management -* shell detection - -Using the API: - -```ts -import { ShellProvider } from 'terminus-terminal' -``` - -Exporting your subclasses: - -```ts -@NgModule({ - ... - providers: [ - ... - { provide: ShellProvider, useClass: MyShellPlugin, multi: true }, - ... - ] -}) -``` diff --git a/terminus-terminal/src/api/baseTerminalTab.component.ts b/terminus-terminal/src/api/baseTerminalTab.component.ts index 32110372..ace6c63d 100644 --- a/terminus-terminal/src/api/baseTerminalTab.component.ts +++ b/terminus-terminal/src/api/baseTerminalTab.component.ts @@ -6,7 +6,7 @@ import { NgZone, OnInit, OnDestroy, Injector, ViewChild, HostBinding, Input, Ele 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 { BaseSession, SessionsService } from '../services/sessions.service' +import { BaseSession } from '../session' import { TerminalFrontendService } from '../services/terminalFrontend.service' import { Frontend } from '../frontends/frontend' @@ -19,7 +19,7 @@ import { TerminalDecorator } from './decorator' */ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit, OnDestroy { static template: string = require('../components/baseTerminalTab.component.pug') - static styles: string[] = [require('../components/terminalTab.component.scss')] + static styles: string[] = [require('../components/baseTerminalTab.component.scss')] static animations: AnimationTriggerMetadata[] = [trigger('slideInOut', [ transition(':enter', [ style({ @@ -83,7 +83,6 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit protected app: AppService protected hostApp: HostAppService protected hotkeys: HotkeysService - protected sessions: SessionsService protected electron: ElectronService protected terminalContainersService: TerminalFrontendService protected notifications: NotificationsService @@ -136,7 +135,6 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit this.app = injector.get(AppService) this.hostApp = injector.get(HostAppService) this.hotkeys = injector.get(HotkeysService) - this.sessions = injector.get(SessionsService) this.electron = injector.get(ElectronService) this.terminalContainersService = injector.get(TerminalFrontendService) this.notifications = injector.get(NotificationsService) diff --git a/terminus-terminal/src/api/interfaces.ts b/terminus-terminal/src/api/interfaces.ts index 8cdb6452..b32648fb 100644 --- a/terminus-terminal/src/api/interfaces.ts +++ b/terminus-terminal/src/api/interfaces.ts @@ -3,29 +3,6 @@ export interface ResizeEvent { rows: number } -export interface SessionOptions { - restoreFromPTYID?: string - name?: string - command: string - args?: string[] - cwd?: string - env?: Record - width?: number - height?: number - pauseAfterExit?: boolean - runAsAdministrator?: boolean -} - -export interface Profile { - name: string - color?: string - sessionOptions: SessionOptions - shell?: string - isBuiltin?: boolean - icon?: string - disableDynamicTitle?: boolean -} - export interface TerminalColorScheme { name: string foreground: string @@ -33,30 +10,3 @@ export interface TerminalColorScheme { cursor: string colors: string[] } - -export interface Shell { - id: string - name?: string - command: string - args?: string[] - env: Record - - /** - * Base path to which shell's internal FS is relative - * Currently used for WSL only - */ - fsBase?: string - - /** - * SVG icon - */ - icon?: string - - hidden?: boolean -} - -export interface ChildProcess { - pid: number - ppid: number - command: string -} diff --git a/terminus-terminal/src/api/shellProvider.ts b/terminus-terminal/src/api/shellProvider.ts deleted file mode 100644 index c03df966..00000000 --- a/terminus-terminal/src/api/shellProvider.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Shell } from './interfaces' - -/** - * Extend to add support for more shells - */ -export abstract class ShellProvider { - abstract provide (): Promise -} diff --git a/terminus-terminal/src/cli.ts b/terminus-terminal/src/cli.ts index a8ac123e..a3b1775e 100644 --- a/terminus-terminal/src/cli.ts +++ b/terminus-terminal/src/cli.ts @@ -1,10 +1,7 @@ -import * as path from 'path' -import * as fs from 'mz/fs' import shellEscape from 'shell-escape' import { Injectable } from '@angular/core' -import { CLIHandler, CLIEvent, HostAppService, AppService, ConfigService } from 'terminus-core' -import { TerminalTabComponent } from './components/terminalTab.component' -import { TerminalService } from './services/terminal.service' +import { CLIHandler, CLIEvent, HostAppService, AppService } from 'terminus-core' +import { BaseTerminalTabComponent } from './api/baseTerminalTab.component' @Injectable() export class TerminalCLIHandler extends CLIHandler { @@ -13,9 +10,7 @@ export class TerminalCLIHandler extends CLIHandler { constructor ( private app: AppService, - private config: ConfigService, private hostApp: HostAppService, - private terminal: TerminalService, ) { super() } @@ -23,113 +18,23 @@ export class TerminalCLIHandler extends CLIHandler { async handle (event: CLIEvent): Promise { const op = event.argv._[0] - if (op === 'open') { - this.handleOpenDirectory(path.resolve(event.cwd, event.argv.directory)) - } else if (op === 'run') { - this.handleRunCommand(event.argv.command) - } else if (op === 'paste') { + if (op === 'paste') { let text = event.argv.text if (event.argv.escape) { text = shellEscape([text]) } this.handlePaste(text) - } else if (op === 'profile') { - this.handleOpenProfile(event.argv.profileName) - } else { - return false + return true } - return true + return false } - private async handleOpenDirectory (directory: string) { - if (directory.length > 1 && (directory.endsWith('/') || directory.endsWith('\\'))) { - directory = directory.substring(0, directory.length - 1) - } - if (await fs.exists(directory)) { - if ((await fs.stat(directory)).isDirectory()) { - this.terminal.openTab(undefined, directory) - this.hostApp.bringToFront() - } - } - } - - private handleRunCommand (command: string[]) { - this.terminal.openTab({ - name: '', - sessionOptions: { - command: command[0], - args: command.slice(1), - }, - }, null, true) - this.hostApp.bringToFront() - } - - private handleOpenProfile (profileName: string) { - const profile = this.config.store.terminal.profiles.find(x => x.name === profileName) - if (!profile) { - console.error('Requested profile', profileName, 'not found') - return - } - this.terminal.openTabWithOptions(profile.sessionOptions) - this.hostApp.bringToFront() - } private handlePaste (text: string) { - if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) { + if (this.app.activeTab instanceof BaseTerminalTabComponent && this.app.activeTab.session) { this.app.activeTab.sendInput(text) this.hostApp.bringToFront() } } } - - -@Injectable() -export class OpenPathCLIHandler extends CLIHandler { - firstMatchOnly = true - priority = -100 - - constructor ( - private terminal: TerminalService, - private hostApp: HostAppService, - ) { - super() - } - - async handle (event: CLIEvent): Promise { - const op = event.argv._[0] - const opAsPath = op ? path.resolve(event.cwd, op) : null - - if (opAsPath && (await fs.lstat(opAsPath)).isDirectory()) { - this.terminal.openTab(undefined, opAsPath) - this.hostApp.bringToFront() - return true - } - - return false - } -} - -@Injectable() -export class AutoOpenTabCLIHandler extends CLIHandler { - firstMatchOnly = true - priority = -1000 - - constructor ( - private app: AppService, - private config: ConfigService, - private terminal: TerminalService, - ) { - super() - } - - async handle (event: CLIEvent): Promise { - if (!event.secondInstance && this.config.store.terminal.autoOpen) { - this.app.ready$.subscribe(() => { - this.terminal.openTab() - }) - return true - } - return false - } -} diff --git a/terminus-terminal/src/components/terminalTab.component.scss b/terminus-terminal/src/components/baseTerminalTab.component.scss similarity index 100% rename from terminus-terminal/src/components/terminalTab.component.scss rename to terminus-terminal/src/components/baseTerminalTab.component.scss diff --git a/terminus-terminal/src/components/terminalSettingsTab.component.ts b/terminus-terminal/src/components/terminalSettingsTab.component.ts index 7761f8fa..e8a6d234 100644 --- a/terminus-terminal/src/components/terminalSettingsTab.component.ts +++ b/terminus-terminal/src/components/terminalSettingsTab.component.ts @@ -1,6 +1,6 @@ +import { execFile } from 'mz/child_process' import { Component } from '@angular/core' import { ConfigService, ElectronService } from 'terminus-core' -import { TerminalService } from '../services/terminal.service' /** @hidden */ @Component({ @@ -10,17 +10,10 @@ export class TerminalSettingsTabComponent { constructor ( public config: ConfigService, private electron: ElectronService, - private terminal: TerminalService, ) { } openWSLVolumeMixer (): void { this.electron.shell.openPath('sndvol.exe') - this.terminal.openTab({ - name: '', - sessionOptions: { - command: 'wsl.exe', - args: ['tput', 'bel'], - }, - }, null, true) + execFile('wsl.exe', ['tput', 'bel']) } } diff --git a/terminus-terminal/src/config.ts b/terminus-terminal/src/config.ts index ec570c09..e08c049c 100644 --- a/terminus-terminal/src/config.ts +++ b/terminus-terminal/src/config.ts @@ -3,18 +3,8 @@ import { ConfigProvider, Platform } from 'terminus-core' /** @hidden */ export class TerminalConfigProvider extends ConfigProvider { defaults = { - hotkeys: { - 'copy-current-path': [], - shell: { - __nonStructural: true, - }, - profile: { - __nonStructural: true, - }, - }, terminal: { frontend: 'xterm', - autoOpen: false, fontSize: 14, fallbackFont: null, linePadding: 0, @@ -26,13 +16,10 @@ export class TerminalConfigProvider extends ConfigProvider { cursorBlink: true, hideTabIndex: false, hideCloseButton: false, - customShell: '', rightClick: 'menu', pasteOnMiddleClick: true, copyOnSelect: false, scrollOnInput: true, - workingDirectory: '', - alwaysUseWorkingDirectory: false, altIsMeta: false, wordSeparator: ' ()[]{}\'"', colorScheme: { @@ -62,12 +49,8 @@ export class TerminalConfigProvider extends ConfigProvider { ], }, customColorSchemes: [], - environment: {}, - profiles: [], - useConPTY: true, recoverTabs: true, warnOnMultilinePaste: true, - showDefaultProfiles: true, searchRegexAlwaysEnabled: false, searchOptions: { regex: false, @@ -83,8 +66,6 @@ export class TerminalConfigProvider extends ConfigProvider { [Platform.macOS]: { terminal: { font: 'Menlo', - shell: 'default', - profile: 'user-default', }, hotkeys: { 'ctrl-c': ['Ctrl-C'], @@ -109,9 +90,6 @@ export class TerminalConfigProvider extends ConfigProvider { 'reset-zoom': [ '⌘-0', ], - 'new-tab': [ - '⌘-T', - ], home: ['⌘-Left', 'Home'], end: ['⌘-Right', 'End'], 'previous-word': ['⌥-Left'], @@ -129,8 +107,6 @@ export class TerminalConfigProvider extends ConfigProvider { [Platform.Windows]: { terminal: { font: 'Consolas', - shell: 'clink', - profile: 'cmd-clink', rightClick: 'paste', pasteOnMiddleClick: false, copyOnSelect: true, @@ -156,9 +132,6 @@ export class TerminalConfigProvider extends ConfigProvider { 'reset-zoom': [ 'Ctrl-0', ], - 'new-tab': [ - 'Ctrl-Shift-T', - ], home: ['Home'], end: ['End'], 'previous-word': ['Ctrl-Left'], @@ -176,8 +149,6 @@ export class TerminalConfigProvider extends ConfigProvider { [Platform.Linux]: { terminal: { font: 'Liberation Mono', - shell: 'default', - profile: 'user-default', }, hotkeys: { 'ctrl-c': ['Ctrl-C'], @@ -200,9 +171,6 @@ export class TerminalConfigProvider extends ConfigProvider { 'reset-zoom': [ 'Ctrl-0', ], - 'new-tab': [ - 'Ctrl-Shift-T', - ], home: ['Home'], end: ['End'], 'previous-word': ['Ctrl-Left'], diff --git a/terminus-terminal/src/features/debug.ts b/terminus-terminal/src/features/debug.ts index ea52545c..1a403b60 100644 --- a/terminus-terminal/src/features/debug.ts +++ b/terminus-terminal/src/features/debug.ts @@ -1,7 +1,7 @@ import * as fs from 'fs' import { Injectable } from '@angular/core' import { TerminalDecorator } from '../api/decorator' -import { TerminalTabComponent } from '../components/terminalTab.component' +import { BaseTerminalTabComponent } from '../api/baseTerminalTab.component' import { ElectronService, HostAppService } from 'terminus-core' /** @hidden */ @@ -14,7 +14,7 @@ export class DebugDecorator extends TerminalDecorator { super() } - attach (terminal: TerminalTabComponent): void { + attach (terminal: BaseTerminalTabComponent): void { let sessionOutputBuffer = '' const bufferLength = 8192 @@ -87,23 +87,23 @@ export class DebugDecorator extends TerminalDecorator { } } - private doSaveState (terminal: TerminalTabComponent) { + private doSaveState (terminal: BaseTerminalTabComponent) { this.saveFile(terminal.frontend!.saveState(), 'state.txt') } - private async doCopyState (terminal: TerminalTabComponent) { + private async doCopyState (terminal: BaseTerminalTabComponent) { const data = '```' + JSON.stringify(terminal.frontend!.saveState()) + '```' this.electron.clipboard.writeText(data) } - private async doLoadState (terminal: TerminalTabComponent) { + private async doLoadState (terminal: BaseTerminalTabComponent) { const data = await this.loadFile() if (data) { terminal.frontend!.restoreState(data) } } - private async doPasteState (terminal: TerminalTabComponent) { + private async doPasteState (terminal: BaseTerminalTabComponent) { let data = this.electron.clipboard.readText() if (data) { if (data.startsWith('`')) { @@ -122,14 +122,14 @@ export class DebugDecorator extends TerminalDecorator { this.electron.clipboard.writeText(data) } - private async doLoadOutput (terminal: TerminalTabComponent) { + private async doLoadOutput (terminal: BaseTerminalTabComponent) { const data = await this.loadFile() if (data) { terminal.frontend?.write(data) } } - private async doPasteOutput (terminal: TerminalTabComponent) { + private async doPasteOutput (terminal: BaseTerminalTabComponent) { let data = this.electron.clipboard.readText() if (data) { if (data.startsWith('`')) { diff --git a/terminus-terminal/src/features/pathDrop.ts b/terminus-terminal/src/features/pathDrop.ts index c3be2009..3f368efd 100644 --- a/terminus-terminal/src/features/pathDrop.ts +++ b/terminus-terminal/src/features/pathDrop.ts @@ -1,11 +1,11 @@ import { Injectable } from '@angular/core' import { TerminalDecorator } from '../api/decorator' -import { TerminalTabComponent } from '../components/terminalTab.component' +import { BaseTerminalTabComponent } from '../api/baseTerminalTab.component' /** @hidden */ @Injectable() export class PathDropDecorator extends TerminalDecorator { - attach (terminal: TerminalTabComponent): void { + attach (terminal: BaseTerminalTabComponent): void { setTimeout(() => { this.subscribeUntilDetached(terminal, terminal.frontend?.dragOver$.subscribe(event => { event.preventDefault() @@ -19,7 +19,7 @@ export class PathDropDecorator extends TerminalDecorator { }) } - private injectPath (terminal: TerminalTabComponent, path: string) { + private injectPath (terminal: BaseTerminalTabComponent, path: string) { if (path.includes(' ')) { path = `"${path}"` } diff --git a/terminus-terminal/src/features/zmodem.ts b/terminus-terminal/src/features/zmodem.ts index d6e15acf..8ee0049b 100644 --- a/terminus-terminal/src/features/zmodem.ts +++ b/terminus-terminal/src/features/zmodem.ts @@ -6,7 +6,7 @@ import { Observable } from 'rxjs' import { filter } from 'rxjs/operators' import { Injectable } from '@angular/core' import { TerminalDecorator } from '../api/decorator' -import { TerminalTabComponent } from '../components/terminalTab.component' +import { BaseTerminalTabComponent } from '../api/baseTerminalTab.component' import { LogService, Logger, ElectronService, HostAppService, HotkeysService } from 'terminus-core' const SPACER = ' ' @@ -29,7 +29,7 @@ export class ZModemDecorator extends TerminalDecorator { this.cancelEvent = hotkeys.hotkey$.pipe(filter(x => x === 'ctrl-c')) } - attach (terminal: TerminalTabComponent): void { + attach (terminal: BaseTerminalTabComponent): void { const sentry = new ZModem.Sentry({ to_terminal: data => { if (!terminal.enablePassthrough) { diff --git a/terminus-terminal/src/hotkeys.ts b/terminus-terminal/src/hotkeys.ts index d6fbb7e4..9b0070d2 100644 --- a/terminus-terminal/src/hotkeys.ts +++ b/terminus-terminal/src/hotkeys.ts @@ -1,6 +1,5 @@ import { Injectable } from '@angular/core' import { HotkeyDescription, HotkeyProvider } from 'terminus-core' -import { TerminalService } from './services/terminal.service' /** @hidden */ @Injectable() @@ -54,10 +53,6 @@ export class TerminalHotkeyProvider extends HotkeyProvider { id: 'reset-zoom', name: 'Reset zoom', }, - { - id: 'new-tab', - name: 'New tab', - }, { id: 'ctrl-c', name: 'Intelligent Ctrl-C (copy/abort)', @@ -76,18 +71,7 @@ export class TerminalHotkeyProvider extends HotkeyProvider { }, ] - constructor ( - private terminal: TerminalService, - ) { super() } - async provide (): Promise { - const profiles = await this.terminal.getProfiles() - return [ - ...this.hotkeys, - ...profiles.map(profile => ({ - id: `profile.${this.terminal.getProfileID(profile)}`, - name: `New tab: ${profile.name}`, - })), - ] + return this.hotkeys } } diff --git a/terminus-terminal/src/index.ts b/terminus-terminal/src/index.ts index 589cec5d..78155ece 100644 --- a/terminus-terminal/src/index.ts +++ b/terminus-terminal/src/index.ts @@ -4,58 +4,35 @@ 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, { ConfigProvider, HotkeysService, HotkeyProvider, TabContextMenuItemProvider, CLIHandler } from 'terminus-core' import { SettingsTabProvider } from 'terminus-settings' import { AppearanceSettingsTabComponent } from './components/appearanceSettingsTab.component' import { ColorSchemeSettingsTabComponent } from './components/colorSchemeSettingsTab.component' -import { TerminalTabComponent } from './components/terminalTab.component' -import { ShellSettingsTabComponent } from './components/shellSettingsTab.component' import { TerminalSettingsTabComponent } from './components/terminalSettingsTab.component' import { ColorPickerComponent } from './components/colorPicker.component' import { ColorSchemePreviewComponent } from './components/colorSchemePreview.component' -import { EditProfileModalComponent } from './components/editProfileModal.component' -import { EnvironmentEditorComponent } from './components/environmentEditor.component' import { SearchPanelComponent } from './components/searchPanel.component' -import { BaseSession } from './services/sessions.service' import { TerminalFrontendService } from './services/terminalFrontend.service' -import { TerminalService } from './services/terminal.service' -import { DockMenuService } from './services/dockMenu.service' -import { ButtonProvider } from './buttonProvider' -import { RecoveryProvider } from './recoveryProvider' import { TerminalDecorator } from './api/decorator' import { TerminalContextMenuItemProvider } from './api/contextMenuProvider' import { TerminalColorSchemeProvider } from './api/colorSchemeProvider' -import { ShellProvider } from './api/shellProvider' -import { TerminalSettingsTabProvider, AppearanceSettingsTabProvider, ColorSchemeSettingsTabProvider, ShellSettingsTabProvider } from './settings' +import { TerminalSettingsTabProvider, AppearanceSettingsTabProvider, ColorSchemeSettingsTabProvider } from './settings' import { DebugDecorator } from './features/debug' import { PathDropDecorator } from './features/pathDrop' import { ZModemDecorator } from './features/zmodem' import { TerminalConfigProvider } from './config' import { TerminalHotkeyProvider } from './hotkeys' import { HyperColorSchemes } from './colorSchemes' -import { NewTabContextMenu, CopyPasteContextMenu, SaveAsProfileContextMenu, LegacyContextMenu } from './tabContextMenu' - -import { CmderShellProvider } from './shells/cmder' -import { CustomShellProvider } from './shells/custom' -import { Cygwin32ShellProvider } from './shells/cygwin32' -import { Cygwin64ShellProvider } from './shells/cygwin64' -import { GitBashShellProvider } from './shells/gitBash' -import { LinuxDefaultShellProvider } from './shells/linuxDefault' -import { MacOSDefaultShellProvider } from './shells/macDefault' -import { POSIXShellsProvider } from './shells/posix' -import { PowerShellCoreShellProvider } from './shells/powershellCore' -import { WindowsDefaultShellProvider } from './shells/winDefault' -import { WindowsStockShellsProvider } from './shells/windowsStock' -import { WSLShellProvider } from './shells/wsl' +import { CopyPasteContextMenu, LegacyContextMenu } from './tabContextMenu' import { hterm } from './frontends/hterm' import { Frontend } from './frontends/frontend' import { HTermFrontend } from './frontends/htermFrontend' import { XTermFrontend, XTermWebGLFrontend } from './frontends/xtermFrontend' -import { AutoOpenTabCLIHandler, OpenPathCLIHandler, TerminalCLIHandler } from './cli' +import { TerminalCLIHandler } from './cli' /** @hidden */ @NgModule({ @@ -69,11 +46,8 @@ import { AutoOpenTabCLIHandler, OpenPathCLIHandler, TerminalCLIHandler } from '. providers: [ { provide: SettingsTabProvider, useClass: AppearanceSettingsTabProvider, multi: true }, { provide: SettingsTabProvider, useClass: ColorSchemeSettingsTabProvider, multi: true }, - { provide: SettingsTabProvider, useClass: ShellSettingsTabProvider, multi: true }, { provide: SettingsTabProvider, useClass: TerminalSettingsTabProvider, multi: true }, - { provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true }, - { provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true }, { provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true }, { provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true }, { provide: TerminalColorSchemeProvider, useClass: HyperColorSchemes, multi: true }, @@ -81,65 +55,32 @@ import { AutoOpenTabCLIHandler, OpenPathCLIHandler, TerminalCLIHandler } from '. { provide: TerminalDecorator, useClass: ZModemDecorator, multi: true }, { provide: TerminalDecorator, useClass: DebugDecorator, multi: true }, - { provide: ShellProvider, useClass: WindowsDefaultShellProvider, multi: true }, - { provide: ShellProvider, useClass: MacOSDefaultShellProvider, multi: true }, - { provide: ShellProvider, useClass: LinuxDefaultShellProvider, multi: true }, - { provide: ShellProvider, useClass: WindowsStockShellsProvider, multi: true }, - { provide: ShellProvider, useClass: PowerShellCoreShellProvider, multi: true }, - { provide: ShellProvider, useClass: CmderShellProvider, multi: true }, - { provide: ShellProvider, useClass: CustomShellProvider, multi: true }, - { provide: ShellProvider, useClass: Cygwin32ShellProvider, multi: true }, - { provide: ShellProvider, useClass: Cygwin64ShellProvider, multi: true }, - { provide: ShellProvider, useClass: GitBashShellProvider, multi: true }, - { provide: ShellProvider, useClass: POSIXShellsProvider, multi: true }, - { provide: ShellProvider, useClass: WSLShellProvider, multi: true }, - - { provide: TabContextMenuItemProvider, useClass: NewTabContextMenu, multi: true }, { provide: TabContextMenuItemProvider, useClass: CopyPasteContextMenu, multi: true }, - { provide: TabContextMenuItemProvider, useClass: SaveAsProfileContextMenu, multi: true }, { provide: TabContextMenuItemProvider, useClass: LegacyContextMenu, multi: true }, { provide: CLIHandler, useClass: TerminalCLIHandler, multi: true }, - { provide: CLIHandler, useClass: OpenPathCLIHandler, multi: true }, - { provide: CLIHandler, useClass: AutoOpenTabCLIHandler, multi: true }, - - // For WindowsDefaultShellProvider - PowerShellCoreShellProvider, - WSLShellProvider, - WindowsStockShellsProvider, ], entryComponents: [ - TerminalTabComponent, AppearanceSettingsTabComponent, ColorSchemeSettingsTabComponent, - ShellSettingsTabComponent, TerminalSettingsTabComponent, - EditProfileModalComponent, ] as any[], declarations: [ ColorPickerComponent, ColorSchemePreviewComponent, - TerminalTabComponent, AppearanceSettingsTabComponent, ColorSchemeSettingsTabComponent, - ShellSettingsTabComponent, TerminalSettingsTabComponent, - EditProfileModalComponent, - EnvironmentEditorComponent, SearchPanelComponent, ] as any[], exports: [ ColorPickerComponent, - EnvironmentEditorComponent, SearchPanelComponent, ], }) export default class TerminalModule { // eslint-disable-line @typescript-eslint/no-extraneous-class private constructor ( hotkeys: HotkeysService, - terminal: TerminalService, - hostApp: HostAppService, - dockMenu: DockMenuService, ) { const events = [ { @@ -165,30 +106,11 @@ export default class TerminalModule { // eslint-disable-line @typescript-eslint/ hotkeys.emitKeyEvent(nativeEvent) } }) - - hotkeys.matchedHotkey.subscribe(async (hotkey) => { - if (hotkey === 'new-tab') { - terminal.openTab() - } - if (hotkey === 'new-window') { - hostApp.newWindow() - } - if (hotkey.startsWith('profile.')) { - const profile = await terminal.getProfileByID(hotkey.split('.')[1]) - if (profile) { - terminal.openTabWithOptions(profile.sessionOptions) - } - } - }) - - dockMenu.update() } } -export { TerminalService, BaseSession, TerminalTabComponent, TerminalFrontendService, TerminalDecorator, TerminalContextMenuItemProvider, TerminalColorSchemeProvider, ShellProvider } +export { TerminalFrontendService, TerminalDecorator, TerminalContextMenuItemProvider, TerminalColorSchemeProvider } export { Frontend, XTermFrontend, XTermWebGLFrontend, HTermFrontend } export { BaseTerminalTabComponent } from './api/baseTerminalTab.component' export * from './api/interfaces' - -// Deprecations -export { TerminalColorScheme as ITerminalColorScheme, Shell as IShell } from './api/interfaces' +export * from './session' diff --git a/terminus-terminal/src/services/terminalFrontend.service.ts b/terminus-terminal/src/services/terminalFrontend.service.ts index a7341feb..38d172b9 100644 --- a/terminus-terminal/src/services/terminalFrontend.service.ts +++ b/terminus-terminal/src/services/terminalFrontend.service.ts @@ -3,7 +3,7 @@ import { ConfigService, ThemesService, HotkeysService } from 'terminus-core' import { Frontend } from '../frontends/frontend' import { HTermFrontend } from '../frontends/htermFrontend' import { XTermFrontend, XTermWebGLFrontend } from '../frontends/xtermFrontend' -import { BaseSession } from '../services/sessions.service' +import { BaseSession } from '../session' @Injectable({ providedIn: 'root' }) export class TerminalFrontendService { diff --git a/terminus-terminal/src/session.ts b/terminus-terminal/src/session.ts new file mode 100644 index 00000000..56410461 --- /dev/null +++ b/terminus-terminal/src/session.ts @@ -0,0 +1,60 @@ +import { Observable, Subject } from 'rxjs' + + +/** + * A session object for a [[BaseTerminalTabComponent]] + * Extend this to implement custom I/O and process management for your terminal tab + */ +export abstract class BaseSession { + open: boolean + name: string + truePID: number + protected output = new Subject() + protected binaryOutput = new Subject() + protected closed = new Subject() + protected destroyed = new Subject() + private initialDataBuffer = Buffer.from('') + private initialDataBufferReleased = false + + get output$ (): Observable { return this.output } + get binaryOutput$ (): Observable { return this.binaryOutput } + get closed$ (): Observable { return this.closed } + get destroyed$ (): Observable { return this.destroyed } + + emitOutput (data: Buffer): void { + if (!this.initialDataBufferReleased) { + this.initialDataBuffer = Buffer.concat([this.initialDataBuffer, data]) + } else { + this.output.next(data.toString()) + this.binaryOutput.next(data) + } + } + + releaseInitialDataBuffer (): void { + this.initialDataBufferReleased = true + this.output.next(this.initialDataBuffer.toString()) + this.binaryOutput.next(this.initialDataBuffer) + this.initialDataBuffer = Buffer.from('') + } + + async destroy (): Promise { + if (this.open) { + this.open = false + this.closed.next() + this.destroyed.next() + this.closed.complete() + this.destroyed.complete() + this.output.complete() + this.binaryOutput.complete() + await this.gracefullyKillProcess() + } + } + + abstract start (options: unknown): void + abstract resize (columns: number, rows: number): void + abstract write (data: Buffer): void + abstract kill (signal?: string): void + abstract gracefullyKillProcess (): Promise + abstract supportsWorkingDirectory (): boolean + abstract getWorkingDirectory (): Promise +} diff --git a/terminus-terminal/src/settings.ts b/terminus-terminal/src/settings.ts index 617745be..c791fd84 100644 --- a/terminus-terminal/src/settings.ts +++ b/terminus-terminal/src/settings.ts @@ -2,7 +2,6 @@ import { Injectable } from '@angular/core' import { SettingsTabProvider } from 'terminus-settings' import { AppearanceSettingsTabComponent } from './components/appearanceSettingsTab.component' -import { ShellSettingsTabComponent } from './components/shellSettingsTab.component' import { TerminalSettingsTabComponent } from './components/terminalSettingsTab.component' import { ColorSchemeSettingsTabComponent } from './components/colorSchemeSettingsTab.component' @@ -30,18 +29,6 @@ export class ColorSchemeSettingsTabProvider extends SettingsTabProvider { } } -/** @hidden */ -@Injectable() -export class ShellSettingsTabProvider extends SettingsTabProvider { - id = 'terminal-shell' - icon = 'list-ul' - title = 'Shell' - - getComponentType (): any { - return ShellSettingsTabComponent - } -} - /** @hidden */ @Injectable() export class TerminalSettingsTabProvider extends SettingsTabProvider { diff --git a/terminus-terminal/src/tabContextMenu.ts b/terminus-terminal/src/tabContextMenu.ts index 5aa9a173..f2076a6e 100644 --- a/terminus-terminal/src/tabContextMenu.ts +++ b/terminus-terminal/src/tabContextMenu.ts @@ -1,138 +1,9 @@ import { MenuItemConstructorOptions } from 'electron' import { Injectable, NgZone, Optional, Inject } from '@angular/core' -import { ConfigService, BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, SplitTabComponent, NotificationsService } from 'terminus-core' -import { TerminalTabComponent } from './components/terminalTab.component' -import { UACService } from './services/uac.service' -import { TerminalService } from './services/terminal.service' +import { BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, NotificationsService } from 'terminus-core' import { BaseTerminalTabComponent } from './api/baseTerminalTab.component' import { TerminalContextMenuItemProvider } from './api/contextMenuProvider' -/** @hidden */ -@Injectable() -export class SaveAsProfileContextMenu extends TabContextMenuItemProvider { - constructor ( - private config: ConfigService, - private zone: NgZone, - private notifications: NotificationsService, - ) { - super() - } - - async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise { - if (!(tab instanceof TerminalTabComponent)) { - return [] - } - const items: MenuItemConstructorOptions[] = [ - { - label: 'Save as profile', - click: () => this.zone.run(async () => { - const profile = { - sessionOptions: { - ...tab.sessionOptions, - cwd: await tab.session?.getWorkingDirectory() ?? tab.sessionOptions.cwd, - }, - name: tab.sessionOptions.command, - } - this.config.store.terminal.profiles = [ - ...this.config.store.terminal.profiles, - profile, - ] - this.config.save() - this.notifications.info('Saved') - }), - }, - ] - - return items - } -} - -/** @hidden */ -@Injectable() -export class NewTabContextMenu extends TabContextMenuItemProvider { - weight = 10 - - constructor ( - public config: ConfigService, - private zone: NgZone, - private terminalService: TerminalService, - private uac: UACService, - ) { - super() - } - - async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise { - const profiles = await this.terminalService.getProfiles() - - const items: MenuItemConstructorOptions[] = [ - { - label: 'New terminal', - click: () => this.zone.run(() => { - this.terminalService.openTabWithOptions((tab as any).sessionOptions) - }), - }, - { - label: 'New with profile', - submenu: profiles.map(profile => ({ - label: profile.name, - click: () => this.zone.run(async () => { - let workingDirectory = this.config.store.terminal.workingDirectory - if (this.config.store.terminal.alwaysUseWorkingDirectory !== true && tab instanceof TerminalTabComponent) { - workingDirectory = await tab.session?.getWorkingDirectory() - } - await this.terminalService.openTab(profile, workingDirectory) - }), - })), - }, - ] - - if (this.uac.isAvailable) { - items.push({ - label: 'New admin tab', - submenu: profiles.map(profile => ({ - label: profile.name, - click: () => this.zone.run(async () => { - this.terminalService.openTabWithOptions({ - ...profile.sessionOptions, - runAsAdministrator: true, - }) - }), - })), - }) - } - - if (tab instanceof TerminalTabComponent && tabHeader && this.uac.isAvailable) { - items.push({ - label: 'Duplicate as administrator', - click: () => this.zone.run(async () => { - this.terminalService.openTabWithOptions({ - ...tab.sessionOptions, - runAsAdministrator: true, - }) - }), - }) - } - - if (tab instanceof BaseTerminalTabComponent && tab.parent instanceof SplitTabComponent && tab.parent.getAllTabs().length > 1) { - items.push({ - label: 'Focus all panes', - click: () => this.zone.run(() => { - tab.focusAllPanes() - }), - }) - } - - if (tab instanceof TerminalTabComponent && tab.session?.supportsWorkingDirectory()) { - items.push({ - label: 'Copy current path', - click: () => this.zone.run(() => tab.copyCurrentPath()), - }) - } - - return items - } -} - /** @hidden */ @Injectable() export class CopyPasteContextMenu extends TabContextMenuItemProvider { diff --git a/webpack.config.js b/webpack.config.js index 28bfce33..cd5f868f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,6 +4,7 @@ module.exports = [ require('./terminus-core/webpack.config.js'), require('./terminus-settings/webpack.config.js'), require('./terminus-terminal/webpack.config.js'), + require('./terminus-local/webpack.config.js'), require('./terminus-community-color-schemes/webpack.config.js'), require('./terminus-plugin-manager/webpack.config.js'), require('./terminus-ssh/webpack.config.js'),