mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-21 19:07:59 +00:00
.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
export { Tab } from './tab'
|
export { Tab } from './tab'
|
||||||
export { TabRecoveryProviderType, ITabRecoveryProvider } from './tabRecovery'
|
export { TabRecoveryProvider } from './tabRecovery'
|
||||||
export { ToolbarButtonProviderType, IToolbarButton, IToolbarButtonProvider } from './toolbarButtonProvider'
|
export { ToolbarButtonProvider, IToolbarButton } from './toolbarButtonProvider'
|
||||||
|
|
||||||
export { AppService } from 'services/app'
|
export { AppService } from 'services/app'
|
||||||
export { PluginsService } from 'services/plugins'
|
export { PluginsService } from 'services/plugins'
|
||||||
|
export { ElectronService } from 'services/electron'
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
import { Tab } from './tab'
|
import { Tab } from './tab'
|
||||||
|
|
||||||
export interface ITabRecoveryProvider {
|
export abstract class TabRecoveryProvider {
|
||||||
recover (recoveryToken: any): Tab
|
abstract recover (recoveryToken: any): Tab
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TabRecoveryProviderType = 'app:TabRecoveryProviderType'
|
|
||||||
|
@@ -5,8 +5,6 @@ export interface IToolbarButton {
|
|||||||
click: () => void
|
click: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IToolbarButtonProvider {
|
export abstract class ToolbarButtonProvider {
|
||||||
provide (): IToolbarButton[]
|
abstract provide (): IToolbarButton[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ToolbarButtonProviderType = 'app:ToolbarButtonProviderType'
|
|
||||||
|
@@ -27,6 +27,7 @@ import { TitleBarComponent } from 'components/titleBar'
|
|||||||
let plugins = [
|
let plugins = [
|
||||||
require('./settings').default,
|
require('./settings').default,
|
||||||
require('./terminal').default,
|
require('./terminal').default,
|
||||||
|
require('./link-highlighter').default,
|
||||||
]
|
]
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Component, trigger, style, animate, transition, state } from '@angular/core'
|
import { Component, Inject, trigger, style, animate, transition, state } from '@angular/core'
|
||||||
import { ToasterConfig } from 'angular2-toaster'
|
import { ToasterConfig } from 'angular2-toaster'
|
||||||
|
|
||||||
import { ElectronService } from 'services/electron'
|
import { ElectronService } from 'services/electron'
|
||||||
@@ -8,9 +8,8 @@ import { LogService } from 'services/log'
|
|||||||
import { QuitterService } from 'services/quitter'
|
import { QuitterService } from 'services/quitter'
|
||||||
import { ConfigService } from 'services/config'
|
import { ConfigService } from 'services/config'
|
||||||
import { DockingService } from 'services/docking'
|
import { DockingService } from 'services/docking'
|
||||||
import { PluginsService } from 'services/plugins'
|
|
||||||
|
|
||||||
import { AppService, IToolbarButton, IToolbarButtonProvider, ToolbarButtonProviderType } from 'api'
|
import { AppService, IToolbarButton, ToolbarButtonProvider } from 'api'
|
||||||
|
|
||||||
import 'angular2-toaster/lib/toaster.css'
|
import 'angular2-toaster/lib/toaster.css'
|
||||||
import 'global.less'
|
import 'global.less'
|
||||||
@@ -49,8 +48,8 @@ export class AppRootComponent {
|
|||||||
public hostApp: HostAppService,
|
public hostApp: HostAppService,
|
||||||
public hotkeys: HotkeysService,
|
public hotkeys: HotkeysService,
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
private plugins: PluginsService,
|
|
||||||
public app: AppService,
|
public app: AppService,
|
||||||
|
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
||||||
log: LogService,
|
log: LogService,
|
||||||
_quitter: QuitterService,
|
_quitter: QuitterService,
|
||||||
) {
|
) {
|
||||||
@@ -129,10 +128,9 @@ export class AppRootComponent {
|
|||||||
|
|
||||||
getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
|
getToolbarButtons (aboveZero: boolean): IToolbarButton[] {
|
||||||
let buttons: IToolbarButton[] = []
|
let buttons: IToolbarButton[] = []
|
||||||
this.plugins.getAll<IToolbarButtonProvider>(ToolbarButtonProviderType)
|
this.toolbarButtonProviders.forEach((provider) => {
|
||||||
.forEach((provider) => {
|
buttons = buttons.concat(provider.provide())
|
||||||
buttons = buttons.concat(provider.provide())
|
})
|
||||||
})
|
|
||||||
return buttons
|
return buttons
|
||||||
.filter((button) => (button.weight > 0) === aboveZero)
|
.filter((button) => (button.weight > 0) === aboveZero)
|
||||||
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
.sort((a: IToolbarButton, b: IToolbarButton) => (a.weight || 0) - (b.weight || 0))
|
||||||
|
6
app/src/link-highlighter/api.ts
Normal file
6
app/src/link-highlighter/api.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export abstract class LinkHandler {
|
||||||
|
regex: string
|
||||||
|
convert (uri: string): string { return uri }
|
||||||
|
verify (_uri: string): boolean { return true }
|
||||||
|
abstract handle (uri: string): void
|
||||||
|
}
|
126
app/src/link-highlighter/decorator.ts
Normal file
126
app/src/link-highlighter/decorator.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
This plugin is based on Hyperterm Hyperlinks:
|
||||||
|
https://github.com/zeit/hyperlinks/blob/master/index.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@angular/core'
|
||||||
|
import { LinkHandler } from './api'
|
||||||
|
import { TerminalDecorator } from '../terminal/api'
|
||||||
|
|
||||||
|
const debounceDelay = 500
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LinkHighlighterDecorator extends TerminalDecorator {
|
||||||
|
constructor (@Inject(LinkHandler) private handlers: LinkHandler[]) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
decorate (terminal): void {
|
||||||
|
const Screen = terminal.screen_.constructor
|
||||||
|
if (Screen._linkHighlighterInstalled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Screen._linkHighlighterInstalled = true
|
||||||
|
|
||||||
|
const oldInsertString = Screen.prototype.insertString
|
||||||
|
const oldDeleteChars = Screen.prototype.deleteChars
|
||||||
|
let self = this
|
||||||
|
Screen.prototype.insertString = function (...args) {
|
||||||
|
let ret = oldInsertString.bind(this)(...args)
|
||||||
|
self.debounceInsertLinks(this)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
Screen.prototype.deleteChars = function (...args) {
|
||||||
|
let ret = oldDeleteChars.bind(this)(...args)
|
||||||
|
self.debounceInsertLinks(this)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debounceInsertLinks (screen) {
|
||||||
|
if (screen.__insertLinksTimeout) {
|
||||||
|
screen.__insertLinksRebounce = true
|
||||||
|
} else {
|
||||||
|
screen.__insertLinksTimeout = window.setTimeout(() => {
|
||||||
|
this.insertLinks(screen)
|
||||||
|
screen.__insertLinksTimeout = null
|
||||||
|
if (screen.__insertLinksRebounce) {
|
||||||
|
screen.__insertLinksRebounce = false
|
||||||
|
this.debounceInsertLinks(screen)
|
||||||
|
}
|
||||||
|
}, debounceDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertLinks (screen) {
|
||||||
|
if ('#text' === screen.cursorNode_.nodeName) {
|
||||||
|
// replace text node to element
|
||||||
|
const cursorNode = document.createElement('span');
|
||||||
|
cursorNode.textContent = screen.cursorNode_.textContent;
|
||||||
|
screen.cursorRowNode_.replaceChild(cursorNode, screen.cursorNode_);
|
||||||
|
screen.cursorNode_ = cursorNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const traverse = (parentNode: Node) => {
|
||||||
|
Array.from(parentNode.childNodes).forEach((node) => {
|
||||||
|
if (node.nodeName == '#text') {
|
||||||
|
parentNode.replaceChild(this.urlizeNode(node), node)
|
||||||
|
} else if (node.nodeName != 'A') {
|
||||||
|
traverse(node)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
screen.rowsArray.forEach((x) => traverse(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
urlizeNode (node) {
|
||||||
|
let matches = []
|
||||||
|
this.handlers.forEach((handler) => {
|
||||||
|
let regex = new RegExp(handler.regex, 'gi')
|
||||||
|
let match
|
||||||
|
while (match = regex.exec(node.textContent)) {
|
||||||
|
let uri = handler.convert(match[0])
|
||||||
|
if (!handler.verify(uri)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
matches.push({
|
||||||
|
start: regex.lastIndex - match[0].length,
|
||||||
|
end: regex.lastIndex,
|
||||||
|
text: match[0],
|
||||||
|
uri,
|
||||||
|
handler
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (matches.length == 0) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.sort((a, b) => a.start < b.start ? -1 : 1)
|
||||||
|
|
||||||
|
let span = document.createElement('span')
|
||||||
|
let position = 0
|
||||||
|
matches.forEach((match) => {
|
||||||
|
if (match.start < position) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (match.start > position) {
|
||||||
|
span.appendChild(document.createTextNode(node.textContent.slice(position, match.start)))
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = document.createElement('a')
|
||||||
|
a.textContent = match.text
|
||||||
|
a.addEventListener('click', () => {
|
||||||
|
match.handler.handle(match.uri)
|
||||||
|
})
|
||||||
|
span.appendChild(a)
|
||||||
|
|
||||||
|
position = match.end
|
||||||
|
})
|
||||||
|
span.appendChild(document.createTextNode(node.textContent.slice(position)))
|
||||||
|
return span
|
||||||
|
}
|
||||||
|
}
|
36
app/src/link-highlighter/handlers.ts
Normal file
36
app/src/link-highlighter/handlers.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import * as fs from 'fs'
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { LinkHandler } from './api'
|
||||||
|
import { ElectronService } from 'api'
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class URLHandler extends LinkHandler {
|
||||||
|
regex = 'http(s)?://[^\\s;\'"]+[^,;\\s]'
|
||||||
|
|
||||||
|
constructor (private electron: ElectronService) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
handle (uri: string) {
|
||||||
|
this.electron.shell.openExternal(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FileHandler extends LinkHandler {
|
||||||
|
regex = '/[^\\s.,;\'"]+'
|
||||||
|
|
||||||
|
constructor (private electron: ElectronService) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
verify (uri: string) {
|
||||||
|
return fs.existsSync(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle (uri: string) {
|
||||||
|
this.electron.shell.openExternal('file://' + uri)
|
||||||
|
}
|
||||||
|
}
|
20
app/src/link-highlighter/index.ts
Normal file
20
app/src/link-highlighter/index.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { NgModule } from '@angular/core'
|
||||||
|
|
||||||
|
import { LinkHandler } from './api'
|
||||||
|
import { FileHandler, URLHandler } from './handlers'
|
||||||
|
import { TerminalDecorator } from '../terminal/api'
|
||||||
|
import { LinkHighlighterDecorator } from './decorator'
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: LinkHandler, useClass: FileHandler, multi: true },
|
||||||
|
{ provide: LinkHandler, useClass: URLHandler, multi: true },
|
||||||
|
{ provide: TerminalDecorator, useClass: LinkHighlighterDecorator, multi: true },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
class LinkHighlighterModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default LinkHighlighterModule
|
@@ -1,139 +0,0 @@
|
|||||||
import * as fs from 'fs'
|
|
||||||
import { ElectronService } from 'services/electron'
|
|
||||||
|
|
||||||
const debounceDelay = 500
|
|
||||||
|
|
||||||
abstract class Handler {
|
|
||||||
constructor (protected plugin) { }
|
|
||||||
regex: string
|
|
||||||
convert (uri: string): string { return uri }
|
|
||||||
verify (_uri: string): boolean { return true }
|
|
||||||
abstract handle (uri: string): void
|
|
||||||
}
|
|
||||||
|
|
||||||
class URLHandler extends Handler {
|
|
||||||
regex = 'http(s)?://[^\\s;\'"]+[^.,;\\s]'
|
|
||||||
|
|
||||||
handle (uri: string) {
|
|
||||||
this.plugin.electron.shell.openExternal(uri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FileHandler extends Handler {
|
|
||||||
regex = '/[^\\s.,;\'"]+'
|
|
||||||
|
|
||||||
verify (uri: string) {
|
|
||||||
return fs.existsSync(uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
handle (uri: string) {
|
|
||||||
this.plugin.electron.shell.openExternal('file://' + uri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class HyperlinksPlugin {
|
|
||||||
handlers = []
|
|
||||||
handlerClasses = [
|
|
||||||
URLHandler,
|
|
||||||
FileHandler,
|
|
||||||
]
|
|
||||||
electron: ElectronService
|
|
||||||
|
|
||||||
constructor ({ electron }) {
|
|
||||||
this.electron = electron
|
|
||||||
this.handlers = this.handlerClasses.map((x) => new x(this))
|
|
||||||
}
|
|
||||||
|
|
||||||
preTerminalInit ({ terminal }) {
|
|
||||||
const oldInsertString = terminal.screen_.constructor.prototype.insertString
|
|
||||||
const oldDeleteChars = terminal.screen_.constructor.prototype.deleteChars
|
|
||||||
terminal.screen_.insertString = (...args) => {
|
|
||||||
let ret = oldInsertString.bind(terminal.screen_)(...args)
|
|
||||||
this.debounceInsertLinks(terminal.screen_)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
terminal.screen_.deleteChars = (...args) => {
|
|
||||||
let ret = oldDeleteChars.bind(terminal.screen_)(...args)
|
|
||||||
this.debounceInsertLinks(terminal.screen_)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debounceInsertLinks (screen) {
|
|
||||||
if (screen.__insertLinksTimeout) {
|
|
||||||
screen.__insertLinksRebounce = true
|
|
||||||
} else {
|
|
||||||
screen.__insertLinksTimeout = window.setTimeout(() => {
|
|
||||||
this.insertLinks(screen)
|
|
||||||
screen.__insertLinksTimeout = null
|
|
||||||
if (screen.__insertLinksRebounce) {
|
|
||||||
screen.__insertLinksRebounce = false
|
|
||||||
this.debounceInsertLinks(screen)
|
|
||||||
}
|
|
||||||
}, debounceDelay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
insertLinks (screen) {
|
|
||||||
const traverse = (parentNode: Node) => {
|
|
||||||
Array.from(parentNode.childNodes).forEach((node) => {
|
|
||||||
if (node.nodeName == '#text') {
|
|
||||||
parentNode.replaceChild(this.urlizeNode(node), node)
|
|
||||||
} else if (node.nodeName != 'A') {
|
|
||||||
traverse(node)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
screen.rowsArray.forEach((x) => traverse(x))
|
|
||||||
}
|
|
||||||
|
|
||||||
urlizeNode (node) {
|
|
||||||
let matches = []
|
|
||||||
this.handlers.forEach((handler) => {
|
|
||||||
let regex = new RegExp(handler.regex, 'gi')
|
|
||||||
let match
|
|
||||||
while (match = regex.exec(node.textContent)) {
|
|
||||||
let uri = handler.convert(match[0])
|
|
||||||
if (!handler.verify(uri)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
matches.push({
|
|
||||||
start: regex.lastIndex - match[0].length,
|
|
||||||
end: regex.lastIndex,
|
|
||||||
text: match[0],
|
|
||||||
uri,
|
|
||||||
handler
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (matches.length == 0) {
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
matches.sort((a, b) => a.start < b.start ? -1 : 1)
|
|
||||||
|
|
||||||
let span = document.createElement('span')
|
|
||||||
let position = 0
|
|
||||||
matches.forEach((match) => {
|
|
||||||
if (match.start < position) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (match.start > position) {
|
|
||||||
span.appendChild(document.createTextNode(node.textContent.slice(position, match.start)))
|
|
||||||
}
|
|
||||||
|
|
||||||
let a = document.createElement('a')
|
|
||||||
a.textContent = match.text
|
|
||||||
a.addEventListener('click', () => {
|
|
||||||
match.handler.handle(match.uri)
|
|
||||||
})
|
|
||||||
span.appendChild(a)
|
|
||||||
|
|
||||||
position = match.end
|
|
||||||
})
|
|
||||||
span.appendChild(document.createTextNode(node.textContent.slice(position)))
|
|
||||||
return span
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Inject, Injectable } from '@angular/core'
|
||||||
import { Logger, LogService } from 'services/log'
|
import { Logger, LogService } from 'services/log'
|
||||||
import { Tab } from 'api/tab'
|
import { Tab } from 'api/tab'
|
||||||
import { PluginsService } from 'services/plugins'
|
import { TabRecoveryProvider } from 'api/tabRecovery'
|
||||||
import { ITabRecoveryProvider, TabRecoveryProviderType } from 'api/tabRecovery'
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -13,7 +12,7 @@ export class AppService {
|
|||||||
logger: Logger
|
logger: Logger
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private plugins: PluginsService,
|
@Inject(TabRecoveryProvider) private tabRecoveryProviders: TabRecoveryProvider[],
|
||||||
log: LogService,
|
log: LogService,
|
||||||
) {
|
) {
|
||||||
this.logger = log.create('app')
|
this.logger = log.create('app')
|
||||||
@@ -83,9 +82,8 @@ export class AppService {
|
|||||||
|
|
||||||
restoreTabs () {
|
restoreTabs () {
|
||||||
if (window.localStorage.tabsRecovery) {
|
if (window.localStorage.tabsRecovery) {
|
||||||
let providers = this.plugins.getAll<ITabRecoveryProvider>(TabRecoveryProviderType)
|
|
||||||
JSON.parse(window.localStorage.tabsRecovery).forEach((token) => {
|
JSON.parse(window.localStorage.tabsRecovery).forEach((token) => {
|
||||||
for (let provider of providers) {
|
for (let provider of this.tabRecoveryProviders) {
|
||||||
try {
|
try {
|
||||||
let tab = provider.recover(token)
|
let tab = provider.recover(token)
|
||||||
if (tab) {
|
if (tab) {
|
||||||
@@ -93,8 +91,8 @@ export class AppService {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} catch (_) { }
|
} catch (_) { }
|
||||||
this.logger.warn('Cannot restore tab from the token:', token)
|
|
||||||
}
|
}
|
||||||
|
this.logger.warn('Cannot restore tab from the token:', token)
|
||||||
})
|
})
|
||||||
this.saveTabs()
|
this.saveTabs()
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { IToolbarButtonProvider, IToolbarButton, AppService } from 'api'
|
import { ToolbarButtonProvider, IToolbarButton, AppService } from 'api'
|
||||||
import { SettingsTab } from './tab'
|
import { SettingsTab } from './tab'
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ButtonProvider implements IToolbarButtonProvider {
|
export class ButtonProvider extends ToolbarButtonProvider {
|
||||||
constructor (
|
constructor (
|
||||||
private app: AppService,
|
private app: AppService,
|
||||||
) { }
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
provide (): IToolbarButton[] {
|
provide (): IToolbarButton[] {
|
||||||
return [{
|
return [{
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser'
|
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
|
import { BrowserModule } from '@angular/platform-browser'
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ import { HotkeyHintComponent } from './components/hotkeyHint'
|
|||||||
import { HotkeyInputModalComponent } from './components/hotkeyInputModal'
|
import { HotkeyInputModalComponent } from './components/hotkeyInputModal'
|
||||||
import { SettingsPaneComponent } from './components/settingsPane'
|
import { SettingsPaneComponent } from './components/settingsPane'
|
||||||
|
|
||||||
import { PluginsService, ToolbarButtonProviderType, TabRecoveryProviderType } from 'api'
|
import { ToolbarButtonProvider, TabRecoveryProvider } from 'api'
|
||||||
|
|
||||||
import { ButtonProvider } from './buttonProvider'
|
import { ButtonProvider } from './buttonProvider'
|
||||||
import { RecoveryProvider } from './recoveryProvider'
|
import { RecoveryProvider } from './recoveryProvider'
|
||||||
@@ -22,8 +22,8 @@ import { RecoveryProvider } from './recoveryProvider'
|
|||||||
NgbModule,
|
NgbModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ButtonProvider,
|
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
|
||||||
RecoveryProvider,
|
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true }
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
HotkeyInputModalComponent,
|
HotkeyInputModalComponent,
|
||||||
@@ -38,14 +38,6 @@ import { RecoveryProvider } from './recoveryProvider'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
class SettingsModule {
|
class SettingsModule {
|
||||||
constructor (
|
|
||||||
plugins: PluginsService,
|
|
||||||
buttonProvider: ButtonProvider,
|
|
||||||
recoveryProvider: RecoveryProvider,
|
|
||||||
) {
|
|
||||||
plugins.register(ToolbarButtonProviderType, buttonProvider, 1)
|
|
||||||
plugins.register(TabRecoveryProviderType, recoveryProvider)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Tab, ITabRecoveryProvider } from 'api'
|
import { Tab, TabRecoveryProvider } from 'api'
|
||||||
import { SettingsTab } from './tab'
|
import { SettingsTab } from './tab'
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RecoveryProvider implements ITabRecoveryProvider {
|
export class RecoveryProvider extends TabRecoveryProvider {
|
||||||
recover (recoveryToken: any): Tab {
|
recover (recoveryToken: any): Tab {
|
||||||
if (recoveryToken.type == 'app:settings') {
|
if (recoveryToken.type == 'app:settings') {
|
||||||
return new SettingsTab()
|
return new SettingsTab()
|
||||||
|
3
app/src/terminal/api.ts
Normal file
3
app/src/terminal/api.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export abstract class TerminalDecorator {
|
||||||
|
abstract decorate (terminal): void
|
||||||
|
}
|
@@ -1,16 +1,16 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { IToolbarButtonProvider, IToolbarButton, AppService } from 'api'
|
import { ToolbarButtonProvider, IToolbarButton, AppService } from 'api'
|
||||||
import { SessionsService } from './services/sessions'
|
import { SessionsService } from './services/sessions'
|
||||||
import { TerminalTab } from './tab'
|
import { TerminalTab } from './tab'
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ButtonProvider implements IToolbarButtonProvider {
|
export class ButtonProvider extends ToolbarButtonProvider {
|
||||||
constructor (
|
constructor (
|
||||||
private app: AppService,
|
private app: AppService,
|
||||||
private sessions: SessionsService,
|
private sessions: SessionsService,
|
||||||
) {
|
) {
|
||||||
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
provide (): IToolbarButton[] {
|
provide (): IToolbarButton[] {
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { Subscription } from 'rxjs'
|
import { Subscription } from 'rxjs'
|
||||||
import { Component, NgZone, Output, EventEmitter, ElementRef } from '@angular/core'
|
import { Component, NgZone, Output, Inject, EventEmitter, ElementRef } from '@angular/core'
|
||||||
|
|
||||||
import { ConfigService } from 'services/config'
|
import { ConfigService } from 'services/config'
|
||||||
import { PluginsService } from 'services/plugins'
|
|
||||||
|
|
||||||
import { BaseTabComponent } from 'components/baseTab'
|
import { BaseTabComponent } from 'components/baseTab'
|
||||||
import { TerminalTab } from '../tab'
|
import { TerminalTab } from '../tab'
|
||||||
|
import { TerminalDecorator } from '../api'
|
||||||
|
|
||||||
import { hterm, preferenceManager } from '../hterm'
|
import { hterm, preferenceManager } from '../hterm'
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
private elementRef: ElementRef,
|
private elementRef: ElementRef,
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
private plugins: PluginsService,
|
@Inject(TerminalDecorator) private decorators: TerminalDecorator[],
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.startupTime = performance.now()
|
this.startupTime = performance.now()
|
||||||
@@ -42,7 +42,9 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.terminal = new hterm.hterm.Terminal()
|
this.terminal = new hterm.hterm.Terminal()
|
||||||
//this.pluginDispatcher.emit('preTerminalInit', { terminal: this.terminal })
|
this.decorators.forEach((decorator) => {
|
||||||
|
decorator.decorate(this.terminal)
|
||||||
|
})
|
||||||
this.terminal.setWindowTitle = (title) => {
|
this.terminal.setWindowTitle = (title) => {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.title = title
|
this.title = title
|
||||||
@@ -77,7 +79,6 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
}
|
}
|
||||||
this.terminal.decorate(this.elementRef.nativeElement)
|
this.terminal.decorate(this.elementRef.nativeElement)
|
||||||
this.configure()
|
this.configure()
|
||||||
//this.pluginDispatcher.emit('postTerminalInit', { terminal: this.terminal })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configure () {
|
configure () {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser'
|
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
|
import { BrowserModule } from '@angular/platform-browser'
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
|
|
||||||
import { PluginsService, ToolbarButtonProviderType, TabRecoveryProviderType } from 'api'
|
import { ToolbarButtonProvider, TabRecoveryProvider } from 'api'
|
||||||
|
|
||||||
import { TerminalTabComponent } from './components/terminalTab'
|
import { TerminalTabComponent } from './components/terminalTab'
|
||||||
import { SessionsService } from './services/sessions'
|
import { SessionsService } from './services/sessions'
|
||||||
@@ -16,9 +16,9 @@ import { RecoveryProvider } from './recoveryProvider'
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ButtonProvider,
|
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
|
||||||
|
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true },
|
||||||
SessionsService,
|
SessionsService,
|
||||||
RecoveryProvider,
|
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
TerminalTabComponent,
|
TerminalTabComponent,
|
||||||
@@ -28,14 +28,6 @@ import { RecoveryProvider } from './recoveryProvider'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
class TerminalModule {
|
class TerminalModule {
|
||||||
constructor (
|
|
||||||
plugins: PluginsService,
|
|
||||||
buttonProvider: ButtonProvider,
|
|
||||||
recoveryProvider: RecoveryProvider,
|
|
||||||
) {
|
|
||||||
plugins.register(ToolbarButtonProviderType, buttonProvider)
|
|
||||||
plugins.register(TabRecoveryProviderType, recoveryProvider)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,12 +1,14 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Tab, ITabRecoveryProvider } from 'api'
|
import { Tab, TabRecoveryProvider } from 'api'
|
||||||
import { TerminalTab } from './tab'
|
import { TerminalTab } from './tab'
|
||||||
import { SessionsService } from './services/sessions'
|
import { SessionsService } from './services/sessions'
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RecoveryProvider implements ITabRecoveryProvider {
|
export class RecoveryProvider extends TabRecoveryProvider {
|
||||||
constructor (private sessions: SessionsService) { }
|
constructor (private sessions: SessionsService) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
recover (recoveryToken: any): Tab {
|
recover (recoveryToken: any): Tab {
|
||||||
if (recoveryToken.type == 'app:terminal') {
|
if (recoveryToken.type == 'app:terminal') {
|
||||||
|
Reference in New Issue
Block a user