mirror of
https://github.com/Eugeny/tabby.git
synced 2025-10-04 14:04:56 +00:00
started separating terminus-electron and terminus-web
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
import type { MenuItemConstructorOptions } from 'electron'
|
||||
import { Observable, Subject, Subscription } from 'rxjs'
|
||||
import { first } from 'rxjs/operators'
|
||||
import colors from 'ansi-colors'
|
||||
import { NgZone, OnInit, OnDestroy, Injector, ViewChild, HostBinding, Input, ElementRef, InjectFlags } from '@angular/core'
|
||||
import { trigger, transition, style, animate, AnimationTriggerMetadata } from '@angular/animations'
|
||||
import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, NotificationsService, Platform, LogService, Logger, TabContextMenuItemProvider, SplitTabComponent, SubscriptionContainer } from 'terminus-core'
|
||||
import { AppService, ConfigService, BaseTabComponent, ElectronService, HostAppService, HotkeysService, NotificationsService, Platform, LogService, Logger, TabContextMenuItemProvider, SplitTabComponent, SubscriptionContainer, MenuItemOptions, PlatformService } from 'terminus-core'
|
||||
|
||||
import { BaseSession } from '../session'
|
||||
import { TerminalFrontendService } from '../services/terminalFrontend.service'
|
||||
@@ -84,6 +83,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
protected hostApp: HostAppService
|
||||
protected hotkeys: HotkeysService
|
||||
protected electron: ElectronService
|
||||
protected platform: PlatformService
|
||||
protected terminalContainersService: TerminalFrontendService
|
||||
protected notifications: NotificationsService
|
||||
protected log: LogService
|
||||
@@ -136,6 +136,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
this.hostApp = injector.get(HostAppService)
|
||||
this.hotkeys = injector.get(HotkeysService)
|
||||
this.electron = injector.get(ElectronService)
|
||||
this.platform = injector.get(PlatformService)
|
||||
this.terminalContainersService = injector.get(TerminalFrontendService)
|
||||
this.notifications = injector.get(NotificationsService)
|
||||
this.log = injector.get(LogService)
|
||||
@@ -312,8 +313,8 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
}
|
||||
}
|
||||
|
||||
async buildContextMenu (): Promise<MenuItemConstructorOptions[]> {
|
||||
let items: MenuItemConstructorOptions[] = []
|
||||
async buildContextMenu (): Promise<MenuItemOptions[]> {
|
||||
let items: MenuItemOptions[] = []
|
||||
for (const section of await Promise.all(this.contextMenuProviders.map(x => x.getItems(this)))) {
|
||||
items = items.concat(section)
|
||||
items.push({ type: 'separator' })
|
||||
@@ -498,6 +499,16 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
this.termContainerSubscriptions.cancelAll()
|
||||
}
|
||||
|
||||
protected async handleRightClick (event: MouseEvent): Promise<void> {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
if (this.config.store.terminal.rightClick === 'menu') {
|
||||
this.platform.popupContextMenu(await this.buildContextMenu(), event)
|
||||
} else if (this.config.store.terminal.rightClick === 'paste') {
|
||||
this.paste()
|
||||
}
|
||||
}
|
||||
|
||||
protected attachTermContainerHandlers (): void {
|
||||
this.detachTermContainerHandlers()
|
||||
|
||||
@@ -531,13 +542,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
||||
return
|
||||
}
|
||||
if (event.which === 3 || event.which === 1 && event.ctrlKey) {
|
||||
if (this.config.store.terminal.rightClick === 'menu') {
|
||||
this.hostApp.popupContextMenu(await this.buildContextMenu())
|
||||
} else if (this.config.store.terminal.rightClick === 'paste') {
|
||||
this.paste()
|
||||
}
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.handleRightClick(event)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { MenuItemConstructorOptions } from 'electron'
|
||||
import type { MenuItemOptions } from 'terminus-core'
|
||||
import { BaseTerminalTabComponent } from './baseTerminalTab.component'
|
||||
|
||||
/**
|
||||
@@ -8,5 +8,5 @@ import { BaseTerminalTabComponent } from './baseTerminalTab.component'
|
||||
export abstract class TerminalContextMenuItemProvider {
|
||||
weight: number
|
||||
|
||||
abstract getItems (tab: BaseTerminalTabComponent): Promise<MenuItemConstructorOptions[]>
|
||||
abstract getItems (tab: BaseTerminalTabComponent): Promise<MenuItemOptions[]>
|
||||
}
|
||||
|
@@ -1,64 +0,0 @@
|
||||
import * as fs from 'mz/fs'
|
||||
import * as path from 'path'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { TerminalColorSchemeProvider } from './api/colorSchemeProvider'
|
||||
import { TerminalColorScheme } from './api/interfaces'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class HyperColorSchemes extends TerminalColorSchemeProvider {
|
||||
async getSchemes (): Promise<TerminalColorScheme[]> {
|
||||
const pluginsPath = path.join(process.env.HOME!, '.hyper_plugins', 'node_modules')
|
||||
if (!await fs.exists(pluginsPath)) {
|
||||
return []
|
||||
}
|
||||
const plugins = await fs.readdir(pluginsPath)
|
||||
|
||||
const themes: TerminalColorScheme[] = []
|
||||
|
||||
plugins.forEach(plugin => {
|
||||
try {
|
||||
const module = (global as any).require(path.join(pluginsPath, plugin))
|
||||
if (module.decorateConfig) {
|
||||
let config: any = {}
|
||||
try {
|
||||
config = module.decorateConfig({})
|
||||
} catch {
|
||||
console.warn('Could not load Hyper theme:', plugin)
|
||||
return
|
||||
}
|
||||
if (config.colors) {
|
||||
themes.push({
|
||||
name: plugin,
|
||||
foreground: config.foregroundColor,
|
||||
background: config.backgroundColor,
|
||||
cursor: config.cursorColor,
|
||||
colors: config.colors.black ? [
|
||||
config.colors.black,
|
||||
config.colors.red,
|
||||
config.colors.green,
|
||||
config.colors.yellow,
|
||||
config.colors.blue,
|
||||
config.colors.magenta,
|
||||
config.colors.cyan,
|
||||
config.colors.white,
|
||||
config.colors.lightBlack,
|
||||
config.colors.lightRed,
|
||||
config.colors.lightGreen,
|
||||
config.colors.lightYellow,
|
||||
config.colors.lightBlue,
|
||||
config.colors.lightMagenta,
|
||||
config.colors.lightCyan,
|
||||
config.colors.lightWhite,
|
||||
] : config.colors,
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.debug('Skipping Hyper plugin', plugin, err)
|
||||
}
|
||||
})
|
||||
|
||||
return themes
|
||||
}
|
||||
}
|
@@ -2,11 +2,9 @@
|
||||
import { Observable } from 'rxjs'
|
||||
import { debounce } from 'utils-decorators/dist/cjs'
|
||||
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'
|
||||
import { exec } from 'mz/child_process'
|
||||
const fontManager = require('fontmanager-redux') // eslint-disable-line
|
||||
|
||||
import { Component } from '@angular/core'
|
||||
import { ConfigService, HostAppService, Platform, getCSSFontFamily } from 'terminus-core'
|
||||
import { ConfigService, getCSSFontFamily, PlatformService } from 'terminus-core'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
@@ -17,26 +15,12 @@ export class AppearanceSettingsTabComponent {
|
||||
fonts: string[] = []
|
||||
|
||||
constructor (
|
||||
private hostApp: HostAppService,
|
||||
public config: ConfigService,
|
||||
private platform: PlatformService,
|
||||
) { }
|
||||
|
||||
async ngOnInit () {
|
||||
if (this.hostApp.platform === Platform.Windows || this.hostApp.platform === Platform.macOS) {
|
||||
const fonts = await new Promise<any[]>((resolve) => fontManager.findFonts({ monospace: true }, resolve))
|
||||
this.fonts = fonts.map(x => x.family.trim())
|
||||
this.fonts.sort()
|
||||
}
|
||||
if (this.hostApp.platform === Platform.Linux) {
|
||||
exec('fc-list :spacing=mono').then(([stdout, _]) => {
|
||||
this.fonts = stdout.toString()
|
||||
.split('\n')
|
||||
.filter(x => !!x)
|
||||
.map(x => x.split(':')[1].trim())
|
||||
.map(x => x.split(',')[0].trim())
|
||||
this.fonts.sort()
|
||||
})
|
||||
}
|
||||
this.fonts = await this.platform.listFonts()
|
||||
}
|
||||
|
||||
fontAutocomplete = (text$: Observable<string>) => {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
h3.mb-3 Terminal
|
||||
|
||||
.form-line
|
||||
.form-line(*ngIf='hostApp.platform !== Platform.Web')
|
||||
.header
|
||||
.title Frontend
|
||||
.description Switches terminal frontend implementation (experimental)
|
||||
@@ -86,7 +86,7 @@ h3.mb-3 Terminal
|
||||
(ngModelChange)='config.save()',
|
||||
)
|
||||
|
||||
.form-line
|
||||
.form-line(*ngIf='hostApp.platform !== Platform.Web')
|
||||
.header
|
||||
.title Auto-open a terminal on app start
|
||||
|
||||
|
@@ -1,19 +1,22 @@
|
||||
import { execFile } from 'mz/child_process'
|
||||
import { Component } from '@angular/core'
|
||||
import { ConfigService, ElectronService } from 'terminus-core'
|
||||
import { ConfigService, HostAppService, Platform, PlatformService } from 'terminus-core'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./terminalSettingsTab.component.pug'),
|
||||
})
|
||||
export class TerminalSettingsTabComponent {
|
||||
Platform = Platform
|
||||
|
||||
constructor (
|
||||
public config: ConfigService,
|
||||
private electron: ElectronService,
|
||||
public hostApp: HostAppService,
|
||||
private platform: PlatformService,
|
||||
) { }
|
||||
|
||||
openWSLVolumeMixer (): void {
|
||||
this.electron.shell.openPath('sndvol.exe')
|
||||
this.platform.openPath('sndvol.exe')
|
||||
execFile('wsl.exe', ['tput', 'bel'])
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { Injector } from '@angular/core'
|
||||
import { Observable, Subject, AsyncSubject, ReplaySubject, BehaviorSubject } from 'rxjs'
|
||||
import { ResizeEvent } from '../api/interfaces'
|
||||
import { ConfigService, ThemesService, HotkeysService } from 'terminus-core'
|
||||
|
||||
export interface SearchOptions {
|
||||
regex?: boolean
|
||||
@@ -13,10 +13,6 @@ export interface SearchOptions {
|
||||
* Extend to add support for a different VT frontend implementation
|
||||
*/
|
||||
export abstract class Frontend {
|
||||
configService: ConfigService
|
||||
themesService: ThemesService
|
||||
hotkeysService: HotkeysService
|
||||
|
||||
enableResizing = true
|
||||
protected ready = new AsyncSubject<void>()
|
||||
protected title = new ReplaySubject<string>(1)
|
||||
@@ -40,6 +36,8 @@ export abstract class Frontend {
|
||||
get dragOver$ (): Observable<DragEvent> { return this.dragOver }
|
||||
get drop$ (): Observable<DragEvent> { return this.drop }
|
||||
|
||||
constructor (protected injector: Injector) { }
|
||||
|
||||
destroy (): void {
|
||||
for (const o of [
|
||||
this.ready,
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { Injector } from '@angular/core'
|
||||
import { ConfigService, getCSSFontFamily, ThemesService } from 'terminus-core'
|
||||
import { Frontend, SearchOptions } from './frontend'
|
||||
import { hterm, preferenceManager } from './hterm'
|
||||
import { getCSSFontFamily } from 'terminus-core'
|
||||
|
||||
/** @hidden */
|
||||
export class HTermFrontend extends Frontend {
|
||||
@@ -13,6 +14,15 @@ export class HTermFrontend extends Frontend {
|
||||
private configuredBackgroundColor = 'transparent'
|
||||
private zoom = 0
|
||||
|
||||
private configService: ConfigService
|
||||
private themesService: ThemesService
|
||||
|
||||
constructor (injector: Injector) {
|
||||
super(injector)
|
||||
this.configService = injector.get(ConfigService)
|
||||
this.themesService = injector.get(ThemesService)
|
||||
}
|
||||
|
||||
async attach (host: HTMLElement): Promise<void> {
|
||||
if (!this.initialized) {
|
||||
this.init()
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { getCSSFontFamily } from 'terminus-core'
|
||||
import { Injector } from '@angular/core'
|
||||
import { ConfigService, getCSSFontFamily, HostAppService, HotkeysService, Platform, PlatformService } from 'terminus-core'
|
||||
import { Frontend, SearchOptions } from './frontend'
|
||||
import { Terminal, ITheme } from 'xterm'
|
||||
import { FitAddon } from 'xterm-addon-fit'
|
||||
@@ -39,8 +40,18 @@ export class XTermFrontend extends Frontend {
|
||||
private opened = false
|
||||
private resizeObserver?: any
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
private configService: ConfigService
|
||||
private hotkeysService: HotkeysService
|
||||
private platformService: PlatformService
|
||||
private hostApp: HostAppService
|
||||
|
||||
constructor (injector: Injector) {
|
||||
super(injector)
|
||||
this.configService = injector.get(ConfigService)
|
||||
this.hotkeysService = injector.get(HotkeysService)
|
||||
this.platformService = injector.get(PlatformService)
|
||||
this.hostApp = injector.get(HostAppService)
|
||||
|
||||
this.xterm = new Terminal({
|
||||
allowTransparency: true,
|
||||
windowsMode: process.platform === 'win32',
|
||||
@@ -88,9 +99,11 @@ export class XTermFrontend extends Frontend {
|
||||
}
|
||||
|
||||
this.xterm.attachCustomKeyEventHandler((event: KeyboardEvent) => {
|
||||
if (event.getModifierState('Meta') && event.key.toLowerCase() === 'v') {
|
||||
event.preventDefault()
|
||||
return false
|
||||
if (this.hostApp.platform !== Platform.Web) {
|
||||
if (event.getModifierState('Meta') && event.key.toLowerCase() === 'v') {
|
||||
event.preventDefault()
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (event.getModifierState('Meta') && event.key.startsWith('Arrow')) {
|
||||
return false
|
||||
@@ -167,6 +180,10 @@ export class XTermFrontend extends Frontend {
|
||||
host.addEventListener('mousedown', event => this.mouseEvent.next(event))
|
||||
host.addEventListener('mouseup', event => this.mouseEvent.next(event))
|
||||
host.addEventListener('mousewheel', event => this.mouseEvent.next(event as MouseEvent))
|
||||
host.addEventListener('contextmenu', event => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
})
|
||||
|
||||
this.resizeObserver = new window['ResizeObserver'](() => setTimeout(() => this.resizeHandler()))
|
||||
this.resizeObserver.observe(host)
|
||||
@@ -190,12 +207,12 @@ export class XTermFrontend extends Frontend {
|
||||
copySelection (): void {
|
||||
const text = this.getSelection()
|
||||
if (text.length < 1024 * 32) {
|
||||
require('@electron/remote').clipboard.write({
|
||||
this.platformService.setClipboard({
|
||||
text: this.getSelection(),
|
||||
html: this.getSelectionAsHTML(),
|
||||
})
|
||||
} else {
|
||||
require('@electron/remote').clipboard.write({
|
||||
this.platformService.setClipboard({
|
||||
text: this.getSelection(),
|
||||
})
|
||||
}
|
||||
|
@@ -25,7 +25,6 @@ import { PathDropDecorator } from './features/pathDrop'
|
||||
import { ZModemDecorator } from './features/zmodem'
|
||||
import { TerminalConfigProvider } from './config'
|
||||
import { TerminalHotkeyProvider } from './hotkeys'
|
||||
import { HyperColorSchemes } from './colorSchemes'
|
||||
import { CopyPasteContextMenu, LegacyContextMenu } from './tabContextMenu'
|
||||
|
||||
import { hterm } from './frontends/hterm'
|
||||
@@ -50,7 +49,6 @@ import { TerminalCLIHandler } from './cli'
|
||||
|
||||
{ provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true },
|
||||
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
|
||||
{ provide: TerminalColorSchemeProvider, useClass: HyperColorSchemes, multi: true },
|
||||
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
|
||||
{ provide: TerminalDecorator, useClass: ZModemDecorator, multi: true },
|
||||
{ provide: TerminalDecorator, useClass: DebugDecorator, multi: true },
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ConfigService, ThemesService, HotkeysService } from 'terminus-core'
|
||||
import { Injectable, Injector } from '@angular/core'
|
||||
import { ConfigService } from 'terminus-core'
|
||||
import { Frontend } from '../frontends/frontend'
|
||||
import { HTermFrontend } from '../frontends/htermFrontend'
|
||||
import { XTermFrontend, XTermWebGLFrontend } from '../frontends/xtermFrontend'
|
||||
@@ -12,8 +12,7 @@ export class TerminalFrontendService {
|
||||
/** @hidden */
|
||||
private constructor (
|
||||
private config: ConfigService,
|
||||
private themes: ThemesService,
|
||||
private hotkeys: HotkeysService,
|
||||
private injector: Injector,
|
||||
) { }
|
||||
|
||||
getFrontend (session?: BaseSession|null): Frontend {
|
||||
@@ -22,10 +21,7 @@ export class TerminalFrontendService {
|
||||
xterm: XTermFrontend,
|
||||
'xterm-webgl': XTermWebGLFrontend,
|
||||
hterm: HTermFrontend,
|
||||
}[this.config.store.terminal.frontend]()
|
||||
frontend.configService = this.config
|
||||
frontend.themesService = this.themes
|
||||
frontend.hotkeysService = this.hotkeys
|
||||
}[this.config.store.terminal.frontend](this.injector)
|
||||
return frontend
|
||||
}
|
||||
if (!this.containers.has(session)) {
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { MenuItemConstructorOptions } from 'electron'
|
||||
import { Injectable, NgZone, Optional, Inject } from '@angular/core'
|
||||
import { BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, NotificationsService } from 'terminus-core'
|
||||
import { BaseTabComponent, TabContextMenuItemProvider, TabHeaderComponent, NotificationsService, MenuItemOptions } from 'terminus-core'
|
||||
import { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
|
||||
import { TerminalContextMenuItemProvider } from './api/contextMenuProvider'
|
||||
|
||||
@@ -16,7 +15,7 @@ export class CopyPasteContextMenu extends TabContextMenuItemProvider {
|
||||
super()
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> {
|
||||
async getItems (tab: BaseTabComponent, tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
|
||||
if (tabHeader) {
|
||||
return []
|
||||
}
|
||||
@@ -56,12 +55,12 @@ export class LegacyContextMenu extends TabContextMenuItemProvider {
|
||||
super()
|
||||
}
|
||||
|
||||
async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<MenuItemConstructorOptions[]> {
|
||||
async getItems (tab: BaseTabComponent, _tabHeader?: TabHeaderComponent): Promise<MenuItemOptions[]> {
|
||||
if (!this.contextMenuProviders) {
|
||||
return []
|
||||
}
|
||||
if (tab instanceof BaseTerminalTabComponent) {
|
||||
let items: MenuItemConstructorOptions[] = []
|
||||
let items: MenuItemOptions[] = []
|
||||
for (const p of this.contextMenuProviders) {
|
||||
items = items.concat(await p.getItems(tab))
|
||||
}
|
||||
|
Reference in New Issue
Block a user