mirror of
https://github.com/Eugeny/tabby.git
synced 2025-06-25 13:59:52 +00:00
.
This commit is contained in:
parent
c894410fdb
commit
7a4806bcc9
@ -7,8 +7,6 @@ import { Inject, Injectable } from '@angular/core'
|
|||||||
import { LinkHandler } from './api'
|
import { LinkHandler } from './api'
|
||||||
import { TerminalDecorator, TerminalTabComponent } from '../terminal/api'
|
import { TerminalDecorator, TerminalTabComponent } from '../terminal/api'
|
||||||
|
|
||||||
const debounceDelay = 500
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LinkHighlighterDecorator extends TerminalDecorator {
|
export class LinkHighlighterDecorator extends TerminalDecorator {
|
||||||
@ -17,40 +15,11 @@ export class LinkHighlighterDecorator extends TerminalDecorator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attach (terminal: TerminalTabComponent): void {
|
attach (terminal: TerminalTabComponent): void {
|
||||||
const Screen = terminal.hterm.screen_.constructor
|
terminal.contentUpdated$
|
||||||
if (Screen._linkHighlighterInstalled) {
|
.debounceTime(1000)
|
||||||
return
|
.subscribe(() => {
|
||||||
}
|
this.insertLinks(terminal.hterm.screen_)
|
||||||
Screen._linkHighlighterInstalled = true
|
})
|
||||||
|
|
||||||
const oldInsertString = Screen.prototype.insertString
|
|
||||||
const oldDeleteChars = Screen.prototype.deleteChars
|
|
||||||
let self = this
|
|
||||||
Screen.prototype.insertString = function (content) {
|
|
||||||
let ret = oldInsertString.bind(this)(content)
|
|
||||||
self.debounceInsertLinks(this)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
Screen.prototype.deleteChars = function (count) {
|
|
||||||
let ret = oldDeleteChars.bind(this)(count)
|
|
||||||
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) {
|
insertLinks (screen) {
|
||||||
|
@ -19,12 +19,14 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
hterm: any
|
hterm: any
|
||||||
configSubscription: Subscription
|
configSubscription: Subscription
|
||||||
focusedSubscription: Subscription
|
focusedSubscription: Subscription
|
||||||
startupTime: number
|
|
||||||
title$ = new BehaviorSubject('')
|
title$ = new BehaviorSubject('')
|
||||||
size$ = new ReplaySubject<ResizeEvent>(1)
|
size$ = new ReplaySubject<ResizeEvent>(1)
|
||||||
input$ = new Subject<string>()
|
input$ = new Subject<string>()
|
||||||
output$ = new Subject<string>()
|
output$ = new Subject<string>()
|
||||||
contentUpdated$ = new Subject<void>()
|
contentUpdated$ = new Subject<void>()
|
||||||
|
alternateScreenActive$ = new BehaviorSubject(false)
|
||||||
|
mouseEvent$ = new Subject<Event>()
|
||||||
|
private io: any
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
@ -33,7 +35,6 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
@Inject(TerminalDecorator) private decorators: TerminalDecorator[],
|
@Inject(TerminalDecorator) private decorators: TerminalDecorator[],
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.startupTime = performance.now()
|
|
||||||
this.configSubscription = config.change.subscribe(() => {
|
this.configSubscription = config.change.subscribe(() => {
|
||||||
this.configure()
|
this.configure()
|
||||||
})
|
})
|
||||||
@ -53,18 +54,13 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
|
|
||||||
this.hterm.onTerminalReady = () => {
|
this.hterm.onTerminalReady = () => {
|
||||||
this.hterm.installKeyboard()
|
this.hterm.installKeyboard()
|
||||||
let io = this.hterm.io.push()
|
this.io = this.hterm.io.push()
|
||||||
this.attachIOHandlers(io)
|
this.attachIOHandlers(this.io)
|
||||||
const dataSubscription = this.model.session.dataAvailable.subscribe((data) => {
|
const dataSubscription = this.model.session.dataAvailable.subscribe((data) => {
|
||||||
if (performance.now() - this.startupTime > 500) {
|
|
||||||
this.zone.run(() => {
|
|
||||||
this.model.displayActivity()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.output$.next(data)
|
this.output$.next(data)
|
||||||
})
|
})
|
||||||
io.writeUTF8(data)
|
this.write(data)
|
||||||
})
|
})
|
||||||
const closedSubscription = this.model.session.closed.subscribe(() => {
|
const closedSubscription = this.model.session.closed.subscribe(() => {
|
||||||
dataSubscription.unsubscribe()
|
dataSubscription.unsubscribe()
|
||||||
@ -75,6 +71,12 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
}
|
}
|
||||||
this.hterm.decorate(this.elementRef.nativeElement)
|
this.hterm.decorate(this.elementRef.nativeElement)
|
||||||
this.configure()
|
this.configure()
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.output$.subscribe(() => {
|
||||||
|
this.model.displayActivity()
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
attachHTermHandlers (hterm: any) {
|
attachHTermHandlers (hterm: any) {
|
||||||
@ -85,23 +87,54 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldInsertString = hterm.screen_.insertString.bind(hterm.screen_)
|
const _decorate = hterm.scrollPort_.decorate.bind(hterm.scrollPort_)
|
||||||
hterm.screen_.insertString = (data) => {
|
hterm.scrollPort_.decorate = (...args) => {
|
||||||
oldInsertString(data)
|
_decorate(...args)
|
||||||
this.contentUpdated$.next()
|
hterm.scrollPort_.screen_.style.cssText += `; padding-right: ${hterm.scrollPort_.screen_.offsetWidth - hterm.scrollPort_.screen_.clientWidth}px;`
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldDeleteChars = hterm.screen_.deleteChars.bind(hterm.screen_)
|
const _setAlternateMode = hterm.setAlternateMode.bind(hterm)
|
||||||
hterm.screen_.deleteChars = (count) => {
|
hterm.setAlternateMode = (state) => {
|
||||||
let ret = oldDeleteChars(count)
|
_setAlternateMode(state)
|
||||||
this.contentUpdated$.next()
|
this.alternateScreenActive$.next(state)
|
||||||
return ret
|
}
|
||||||
|
|
||||||
|
const _onPaste_ = hterm.onPaste_.bind(hterm)
|
||||||
|
hterm.onPaste_ = (event) => {
|
||||||
|
event.text = event.text.trim()
|
||||||
|
_onPaste_(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
const _onMouse_ = hterm.onMouse_.bind(hterm)
|
||||||
|
hterm.onMouse_ = (event) => {
|
||||||
|
this.mouseEvent$.next(event)
|
||||||
|
if ((event.ctrlKey || event.metaKey) && event.type === 'mousewheel') {
|
||||||
|
event.preventDefault()
|
||||||
|
let delta = Math.round(event.wheelDeltaY / 50)
|
||||||
|
this.sendInput(((delta > 0) ? '\u001bOA' : '\u001bOB').repeat(Math.abs(delta)))
|
||||||
|
}
|
||||||
|
_onMouse_(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let screen of [hterm.primaryScreen_, hterm.alternateScreen_]) {
|
||||||
|
const _insertString = screen.insertString.bind(screen)
|
||||||
|
screen.insertString = (data) => {
|
||||||
|
_insertString(data)
|
||||||
|
this.contentUpdated$.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
const _deleteChars = screen.deleteChars.bind(screen)
|
||||||
|
screen.deleteChars = (count) => {
|
||||||
|
let ret = _deleteChars(count)
|
||||||
|
this.contentUpdated$.next()
|
||||||
|
return ret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attachIOHandlers (io: any) {
|
attachIOHandlers (io: any) {
|
||||||
io.onVTKeystroke = io.sendString = (data) => {
|
io.onVTKeystroke = io.sendString = (data) => {
|
||||||
this.model.session.write(data)
|
this.sendInput(data)
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
this.input$.next(data)
|
this.input$.next(data)
|
||||||
})
|
})
|
||||||
@ -115,6 +148,14 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendInput (data: string) {
|
||||||
|
this.model.session.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
write (data: string) {
|
||||||
|
this.io.writeUTF8(data)
|
||||||
|
}
|
||||||
|
|
||||||
configure () {
|
configure () {
|
||||||
let config = this.config.full()
|
let config = this.config.full()
|
||||||
preferenceManager.set('font-family', config.terminal.font)
|
preferenceManager.set('font-family', config.terminal.font)
|
||||||
@ -133,5 +174,12 @@ export class TerminalTabComponent extends BaseTabComponent<TerminalTab> {
|
|||||||
})
|
})
|
||||||
this.focusedSubscription.unsubscribe()
|
this.focusedSubscription.unsubscribe()
|
||||||
this.configSubscription.unsubscribe()
|
this.configSubscription.unsubscribe()
|
||||||
|
this.title$.complete()
|
||||||
|
this.size$.complete()
|
||||||
|
this.input$.complete()
|
||||||
|
this.output$.complete()
|
||||||
|
this.contentUpdated$.complete()
|
||||||
|
this.alternateScreenActive$.complete()
|
||||||
|
this.mouseEvent$.complete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,16 +27,4 @@ preferenceManager.set('color-palette-overrides', {
|
|||||||
0: '#1D272D',
|
0: '#1D272D',
|
||||||
})
|
})
|
||||||
|
|
||||||
const oldDecorate = hterm.hterm.ScrollPort.prototype.decorate
|
|
||||||
hterm.hterm.ScrollPort.prototype.decorate = function (...args) {
|
|
||||||
oldDecorate.bind(this)(...args)
|
|
||||||
this.screen_.style.cssText += `; padding-right: ${this.screen_.offsetWidth - this.screen_.clientWidth}px;`
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldPaste = hterm.hterm.Terminal.prototype.onPaste_
|
|
||||||
hterm.hterm.Terminal.prototype.onPaste_ = function (e) {
|
|
||||||
e.text = e.text.trim()
|
|
||||||
oldPaste.bind(this)(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
hterm.hterm.Terminal.prototype.showOverlay = () => null
|
hterm.hterm.Terminal.prototype.showOverlay = () => null
|
||||||
|
@ -30,6 +30,7 @@ import { hterm } from './hterm'
|
|||||||
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true },
|
{ provide: TabRecoveryProvider, useClass: RecoveryProvider, multi: true },
|
||||||
SessionsService,
|
SessionsService,
|
||||||
{ provide: SessionPersistenceProvider, useClass: ScreenPersistenceProvider },
|
{ provide: SessionPersistenceProvider, useClass: ScreenPersistenceProvider },
|
||||||
|
// { provide: SessionPersistenceProvider, useValue: null },
|
||||||
{ provide: SettingsTabProvider, useClass: TerminalSettingsProvider, multi: true },
|
{ provide: SettingsTabProvider, useClass: TerminalSettingsProvider, multi: true },
|
||||||
{ provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true },
|
{ provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true },
|
||||||
],
|
],
|
||||||
|
@ -47,9 +47,12 @@ export class ScreenPersistenceProvider extends SessionPersistenceProvider {
|
|||||||
term xterm-color
|
term xterm-color
|
||||||
bindkey "^[OH" beginning-of-line
|
bindkey "^[OH" beginning-of-line
|
||||||
bindkey "^[OF" end-of-line
|
bindkey "^[OF" end-of-line
|
||||||
|
bindkey "\\027[?1049h" stuff ----alternate enter-----
|
||||||
|
bindkey "\\027[?1049l" stuff ----alternate leave-----
|
||||||
termcapinfo xterm* 'hs:ts=\\E]0;:fs=\\007:ds=\\E]0;\\007'
|
termcapinfo xterm* 'hs:ts=\\E]0;:fs=\\007:ds=\\E]0;\\007'
|
||||||
defhstatus "^Et"
|
defhstatus "^Et"
|
||||||
hardstatus off
|
hardstatus off
|
||||||
|
altscreen on
|
||||||
`, 'utf-8')
|
`, 'utf-8')
|
||||||
let recoveryId = `term-tab-${Date.now()}`
|
let recoveryId = `term-tab-${Date.now()}`
|
||||||
let args = ['-d', '-m', '-c', configPath, '-U', '-S', recoveryId, '--', options.command].concat(options.args || [])
|
let args = ['-d', '-m', '-c', configPath, '-U', '-S', recoveryId, '--', options.command].concat(options.args || [])
|
||||||
|
@ -130,8 +130,10 @@ export class SessionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createNewSession (options: SessionOptions) : Promise<Session> {
|
async createNewSession (options: SessionOptions) : Promise<Session> {
|
||||||
let recoveryId = await this.persistence.startSession(options)
|
if (this.persistence) {
|
||||||
options = await this.persistence.attachSession(recoveryId)
|
let recoveryId = await this.persistence.startSession(options)
|
||||||
|
options = await this.persistence.attachSession(recoveryId)
|
||||||
|
}
|
||||||
let session = this.addSession(options)
|
let session = this.addSession(options)
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
@ -142,7 +144,9 @@ export class SessionsService {
|
|||||||
let session = new Session(options)
|
let session = new Session(options)
|
||||||
const destroySubscription = session.destroyed.subscribe(() => {
|
const destroySubscription = session.destroyed.subscribe(() => {
|
||||||
delete this.sessions[session.name]
|
delete this.sessions[session.name]
|
||||||
this.persistence.terminateSession(session.recoveryId)
|
if (this.persistence) {
|
||||||
|
this.persistence.terminateSession(session.recoveryId)
|
||||||
|
}
|
||||||
destroySubscription.unsubscribe()
|
destroySubscription.unsubscribe()
|
||||||
})
|
})
|
||||||
this.sessions[session.name] = session
|
this.sessions[session.name] = session
|
||||||
@ -150,6 +154,9 @@ export class SessionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async recover (recoveryId: string) : Promise<Session> {
|
async recover (recoveryId: string) : Promise<Session> {
|
||||||
|
if (!this.persistence) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
const options = await this.persistence.attachSession(recoveryId)
|
const options = await this.persistence.attachSession(recoveryId)
|
||||||
if (!options) {
|
if (!options) {
|
||||||
return null
|
return null
|
||||||
|
Loading…
x
Reference in New Issue
Block a user