Merge branch 'master' into profiles-rework

This commit is contained in:
Clem 2023-08-14 17:08:27 +02:00
commit d2752382aa
20 changed files with 77 additions and 56 deletions

View File

@ -16,7 +16,7 @@ export { BootstrapData, PluginInfo, BOOTSTRAP_DATA } from './mainProcess'
export { HostWindowService } from './hostWindow' export { HostWindowService } from './hostWindow'
export { HostAppService, Platform } from './hostApp' export { HostAppService, Platform } from './hostApp'
export { FileProvider } from './fileProvider' export { FileProvider } from './fileProvider'
export { ProfileProvider, Profile, PartialProfile, ProfileSettingsComponent, ProfileGroup, PartialProfileGroup } from './profileProvider' export { ProfileProvider, ConnectableProfileProvider, Profile, ConnectableProfile, PartialProfile, ProfileSettingsComponent, ProfileGroup, PartialProfileGroup } from './profileProvider'
export { PromptModalComponent } from '../components/promptModal.component' export { PromptModalComponent } from '../components/promptModal.component'
export * from './commands' export * from './commands'

View File

@ -21,6 +21,10 @@ export interface Profile {
isTemplate: boolean isTemplate: boolean
} }
export interface ConnectableProfile extends Profile {
clearServiceMessagesOnConnect: boolean
}
export type PartialProfile<T extends Profile> = Omit<Omit<Omit<{ export type PartialProfile<T extends Profile> = Omit<Omit<Omit<{
[K in keyof T]?: T[K] [K in keyof T]?: T[K]
}, 'options'>, 'type'>, 'name'> & { }, 'options'>, 'type'>, 'name'> & {
@ -54,7 +58,6 @@ export interface ProfileSettingsComponent<P extends Profile> {
export abstract class ProfileProvider<P extends Profile> { export abstract class ProfileProvider<P extends Profile> {
id: string id: string
name: string name: string
supportsQuickConnect = false
settingsComponent?: new (...args: any[]) => ProfileSettingsComponent<P> settingsComponent?: new (...args: any[]) => ProfileSettingsComponent<P>
configDefaults = {} configDefaults = {}
@ -68,6 +71,11 @@ export abstract class ProfileProvider<P extends Profile> {
abstract getDescription (profile: PartialProfile<P>): string abstract getDescription (profile: PartialProfile<P>): string
deleteProfile (profile: P): void { }
}
export abstract class ConnectableProfileProvider<P extends ConnectableProfile> extends ProfileProvider<P> {
quickConnect (query: string): PartialProfile<P>|null { quickConnect (query: string): PartialProfile<P>|null {
return null return null
} }
@ -76,5 +84,4 @@ export abstract class ProfileProvider<P extends Profile> {
return null return null
} }
deleteProfile (profile: P): void { }
} }

View File

@ -37,7 +37,7 @@ import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
import { DropZoneDirective } from './directives/dropZone.directive' import { DropZoneDirective } from './directives/dropZone.directive'
import { CdkAutoDropGroup } from './directives/cdkAutoDropGroup.directive' import { CdkAutoDropGroup } from './directives/cdkAutoDropGroup.directive'
import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ProfilesService, ProfileProvider, SelectorOption, Profile, SelectorService, CommandProvider } from './api' import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ProfilesService, ProfileProvider, ConnectableProfileProvider, SelectorOption, Profile, SelectorService, CommandProvider } from './api'
import { AppService } from './services/app.service' import { AppService } from './services/app.service'
import { ConfigService } from './services/config.service' import { ConfigService } from './services/config.service'
@ -214,7 +214,7 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
callback: () => this.profilesService.openNewTabForProfile(p), callback: () => this.profilesService.openNewTabForProfile(p),
})) }))
if (provider.supportsQuickConnect) { if (provider instanceof ConnectableProfileProvider) {
options.push({ options.push({
name: this.translate.instant('Quick connect'), name: this.translate.instant('Quick connect'),
freeInputPattern: this.translate.instant('Connect to "%s"...'), freeInputPattern: this.translate.instant('Connect to "%s"...'),

View File

@ -397,6 +397,13 @@ export class ConfigService {
config.groups = groups config.groups = groups
config.version = 5 config.version = 5
} }
if (config.version < 6) {
if (config.ssh.clearServiceMessagesOnConnect === false) {
config.profileDefaults.ssh.clearServiceMessagesOnConnect = false
delete config.ssh?.clearServiceMessagesOnConnect
}
config.version = 6
}
} }
private async maybeDecryptConfig (store) { private async maybeDecryptConfig (store) {

View File

@ -2,7 +2,7 @@ import { Injectable, Inject } from '@angular/core'
import { TranslateService } from '@ngx-translate/core' import { TranslateService } from '@ngx-translate/core'
import { NewTabParameters } from './tabs.service' import { NewTabParameters } from './tabs.service'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'
import { PartialProfile, PartialProfileGroup, Profile, ProfileGroup, ProfileProvider } from '../api/profileProvider' import { ConnectableProfileProvider, PartialProfile, PartialProfileGroup, Profile, ProfileGroup, ProfileProvider } from '../api/profileProvider'
import { SelectorOption } from '../api/selector' import { SelectorOption } from '../api/selector'
import { AppService } from './app.service' import { AppService } from './app.service'
import { configMerge, ConfigProxy, ConfigService } from './config.service' import { configMerge, ConfigProxy, ConfigService } from './config.service'
@ -230,7 +230,7 @@ export class ProfilesService {
selectorOptionForProfile <P extends Profile, T> (profile: PartialProfile<P>): SelectorOption<T> { selectorOptionForProfile <P extends Profile, T> (profile: PartialProfile<P>): SelectorOption<T> {
const fullProfile = this.getConfigProxyForProfile(profile) const fullProfile = this.getConfigProxyForProfile(profile)
const provider = this.providerForProfile(fullProfile) const provider = this.providerForProfile(fullProfile)
const freeInputEquivalent = provider?.intoQuickConnectString(fullProfile) ?? undefined const freeInputEquivalent = provider instanceof ConnectableProfileProvider ? provider.intoQuickConnectString(fullProfile) ?? undefined : undefined
return { return {
...profile, ...profile,
group: this.resolveProfileGroupName(profile.group ?? ''), group: this.resolveProfileGroupName(profile.group ?? ''),
@ -307,18 +307,20 @@ export class ProfilesService {
}) })
} catch { } } catch { }
this.getProviders().filter(x => x.supportsQuickConnect).forEach(provider => { this.getProviders().forEach(provider => {
options.push({ if (provider instanceof ConnectableProfileProvider) {
name: this.translate.instant('Quick connect'), options.push({
freeInputPattern: this.translate.instant('Connect to "%s"...'), name: this.translate.instant('Quick connect'),
description: `(${provider.name.toUpperCase()})`, freeInputPattern: this.translate.instant('Connect to "%s"...'),
icon: 'fas fa-arrow-right', description: `(${provider.name.toUpperCase()})`,
weight: provider.id !== this.config.store.defaultQuickConnectProvider ? 1 : 0, icon: 'fas fa-arrow-right',
callback: query => { weight: provider.id !== this.config.store.defaultQuickConnectProvider ? 1 : 0,
const profile = provider.quickConnect(query) callback: query => {
resolve(profile) const profile = provider.quickConnect(query)
}, resolve(profile)
}) },
})
}
}) })
await this.selector.show(this.translate.instant('Select profile or enter an address'), options) await this.selector.show(this.translate.instant('Select profile or enter an address'), options)
@ -336,7 +338,7 @@ export class ProfilesService {
async quickConnect (query: string): Promise<PartialProfile<Profile>|null> { async quickConnect (query: string): Promise<PartialProfile<Profile>|null> {
for (const provider of this.getProviders()) { for (const provider of this.getProviders()) {
if (provider.supportsQuickConnect) { if (provider instanceof ConnectableProfileProvider) {
const profile = provider.quickConnect(query) const profile = provider.quickConnect(query)
if (profile) { if (profile) {
return profile return profile

View File

@ -3,10 +3,10 @@ import { SerialPortStream } from '@serialport/stream'
import { LogService, NotificationsService } from 'tabby-core' import { LogService, NotificationsService } from 'tabby-core'
import { Subject, Observable } from 'rxjs' import { Subject, Observable } from 'rxjs'
import { Injector, NgZone } from '@angular/core' import { Injector, NgZone } from '@angular/core'
import { BaseSession, BaseTerminalProfile, InputProcessingOptions, InputProcessor, LoginScriptsOptions, SessionMiddleware, StreamProcessingOptions, TerminalStreamProcessor, UTF8SplitterMiddleware } from 'tabby-terminal' import { BaseSession, ConnectableTerminalProfile, InputProcessingOptions, InputProcessor, LoginScriptsOptions, SessionMiddleware, StreamProcessingOptions, TerminalStreamProcessor, UTF8SplitterMiddleware } from 'tabby-terminal'
import { SerialService } from './services/serial.service' import { SerialService } from './services/serial.service'
export interface SerialProfile extends BaseTerminalProfile { export interface SerialProfile extends ConnectableTerminalProfile {
options: SerialProfileOptions options: SerialProfileOptions
} }

View File

@ -2,14 +2,14 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import slugify from 'slugify' import slugify from 'slugify'
import deepClone from 'clone-deep' import deepClone from 'clone-deep'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ProfileProvider, NewTabParameters, SelectorService, HostAppService, Platform, TranslateService } from 'tabby-core' import { NewTabParameters, SelectorService, HostAppService, Platform, TranslateService, ConnectableProfileProvider } from 'tabby-core'
import { SerialProfileSettingsComponent } from './components/serialProfileSettings.component' import { SerialProfileSettingsComponent } from './components/serialProfileSettings.component'
import { SerialTabComponent } from './components/serialTab.component' import { SerialTabComponent } from './components/serialTab.component'
import { SerialService } from './services/serial.service' import { SerialService } from './services/serial.service'
import { BAUD_RATES, SerialProfile } from './api' import { BAUD_RATES, SerialProfile } from './api'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class SerialProfilesService extends ProfileProvider<SerialProfile> { export class SerialProfilesService extends ConnectableProfileProvider<SerialProfile> {
id = 'serial' id = 'serial'
name = _('Serial') name = _('Serial')
settingsComponent = SerialProfileSettingsComponent settingsComponent = SerialProfileSettingsComponent
@ -32,6 +32,7 @@ export class SerialProfilesService extends ProfileProvider<SerialProfile> {
slowSend: false, slowSend: false,
input: { backspace: 'backspace' }, input: { backspace: 'backspace' },
}, },
clearServiceMessagesOnConnect: false,
} }
constructor ( constructor (

View File

@ -77,9 +77,15 @@
) )
option(ngValue='auto', translate) Auto option(ngValue='auto', translate) Auto
option(ngValue='keep', translate) Keep option(ngValue='keep', translate) Keep
option(*ngIf='profile.type == "serial" || profile.type == "telnet" || profile.type == "ssh"', ngValue='reconnect', translate) Reconnect option(*ngIf='isConnectable()', ngValue='reconnect', translate) Reconnect
option(ngValue='close', translate) Close option(ngValue='close', translate) Close
.form-line(*ngIf='isConnectable()')
.header
.title(translate) Clear terminal after connection
toggle(
[(ngModel)]='profile.clearServiceMessagesOnConnect',
)
.mb-4 .mb-4
.col-12.col-lg-8(*ngIf='this.profileProvider.settingsComponent') .col-12.col-lg-8(*ngIf='this.profileProvider.settingsComponent')

View File

@ -2,7 +2,7 @@
import { Observable, OperatorFunction, debounceTime, map, distinctUntilChanged } from 'rxjs' import { Observable, OperatorFunction, debounceTime, map, distinctUntilChanged } from 'rxjs'
import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, Injector } from '@angular/core' import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, Injector } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigProxy, PartialProfileGroup, Profile, ProfileProvider, ProfileSettingsComponent, ProfilesService, TAB_COLORS, ProfileGroup } from 'tabby-core' import { ConfigProxy, PartialProfileGroup, Profile, ProfileProvider, ProfileSettingsComponent, ProfilesService, TAB_COLORS, ProfileGroup, ConnectableProfileProvider } from 'tabby-core'
const iconsData = require('../../../tabby-core/src/icons.json') const iconsData = require('../../../tabby-core/src/icons.json')
const iconsClassList = Object.keys(iconsData).map( const iconsClassList = Object.keys(iconsData).map(
@ -103,4 +103,9 @@ export class EditProfileModalComponent<P extends Profile> {
cancel () { cancel () {
this.modalInstance.dismiss() this.modalInstance.dismiss()
} }
isConnectable (): boolean {
return this.profileProvider instanceof ConnectableProfileProvider
}
} }

View File

@ -2,7 +2,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import deepClone from 'clone-deep' import deepClone from 'clone-deep'
import { Component, Inject } from '@angular/core' import { Component, Inject } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider, TranslateService, Platform, ProfileGroup, PartialProfileGroup } from 'tabby-core' import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider, TranslateService, Platform, ProfileGroup, PartialProfileGroup, ConnectableProfileProvider } from 'tabby-core'
import { EditProfileModalComponent } from './editProfileModal.component' import { EditProfileModalComponent } from './editProfileModal.component'
import { EditProfileGroupModalComponent, EditProfileGroupModalComponentResult } from './editProfileGroupModal.component' import { EditProfileGroupModalComponent, EditProfileGroupModalComponentResult } from './editProfileGroupModal.component'
@ -341,7 +341,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
} }
getQuickConnectProviders (): ProfileProvider<Profile>[] { getQuickConnectProviders (): ProfileProvider<Profile>[] {
return this.profileProviders.filter(x => x.supportsQuickConnect) return this.profileProviders.filter(x => x instanceof ConnectableProfileProvider)
} }
/** /**

View File

@ -1,4 +1,4 @@
import { BaseTerminalProfile, InputProcessingOptions, LoginScriptsOptions } from 'tabby-terminal' import { ConnectableTerminalProfile, InputProcessingOptions, LoginScriptsOptions } from 'tabby-terminal'
export enum SSHAlgorithmType { export enum SSHAlgorithmType {
HMAC = 'hmac', HMAC = 'hmac',
@ -7,7 +7,7 @@ export enum SSHAlgorithmType {
HOSTKEY = 'serverHostKey', HOSTKEY = 'serverHostKey',
} }
export interface SSHProfile extends BaseTerminalProfile { export interface SSHProfile extends ConnectableTerminalProfile {
options: SSHProfileOptions options: SSHProfileOptions
} }

View File

@ -61,12 +61,4 @@ h3 SSH
(ngModelChange)='config.save()' (ngModelChange)='config.save()'
) )
.form-line
.header
.title(translate) Clear terminal after connection
toggle(
[(ngModel)]='config.store.ssh.clearServiceMessagesOnConnect',
(ngModelChange)='config.save()',
)
.alert.alert-info(translate) SSH connection management is now done through the "Profiles & connections" tab .alert.alert-info(translate) SSH connection management is now done through the "Profiles & connections" tab

View File

@ -83,7 +83,7 @@ export class SSHTabComponent extends ConnectableTerminalTabComponent<SSHProfile>
const jumpSession = await this.setupOneSession( const jumpSession = await this.setupOneSession(
this.injector, this.injector,
this.profilesService.getConfigProxyForProfile(jumpConnection), this.profilesService.getConfigProxyForProfile<SSHProfile>(jumpConnection),
) )
jumpSession.ref() jumpSession.ref()
@ -163,10 +163,6 @@ export class SSHTabComponent extends ConnectableTerminalTabComponent<SSHProfile>
await session.start() await session.start()
if (this.config.store.ssh.clearServiceMessagesOnConnect) {
this.frontend?.clear()
}
this.session?.resize(this.size.columns, this.size.rows) this.session?.resize(this.size.columns, this.size.rows)
} }

View File

@ -11,7 +11,6 @@ export class SSHConfigProvider extends ConfigProvider {
x11Display: null, x11Display: null,
knownHosts: [], knownHosts: [],
verifyHostKeys: true, verifyHostKeys: true,
clearServiceMessagesOnConnect: true,
}, },
hotkeys: { hotkeys: {
'restart-ssh-session': [], 'restart-ssh-session': [],

View File

@ -1,5 +1,5 @@
import { Injectable, InjectFlags, Injector } from '@angular/core' import { Injectable, InjectFlags, Injector } from '@angular/core'
import { ProfileProvider, NewTabParameters, PartialProfile, TranslateService } from 'tabby-core' import { NewTabParameters, PartialProfile, TranslateService, ConnectableProfileProvider } from 'tabby-core'
import * as ALGORITHMS from 'ssh2/lib/protocol/constants' import * as ALGORITHMS from 'ssh2/lib/protocol/constants'
import { SSHProfileSettingsComponent } from './components/sshProfileSettings.component' import { SSHProfileSettingsComponent } from './components/sshProfileSettings.component'
import { SSHTabComponent } from './components/sshTab.component' import { SSHTabComponent } from './components/sshTab.component'
@ -8,10 +8,9 @@ import { ALGORITHM_BLACKLIST, SSHAlgorithmType, SSHProfile } from './api'
import { SSHProfileImporter } from './api/importer' import { SSHProfileImporter } from './api/importer'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class SSHProfilesService extends ProfileProvider<SSHProfile> { export class SSHProfilesService extends ConnectableProfileProvider<SSHProfile> {
id = 'ssh' id = 'ssh'
name = 'SSH' name = 'SSH'
supportsQuickConnect = true
settingsComponent = SSHProfileSettingsComponent settingsComponent = SSHProfileSettingsComponent
configDefaults = { configDefaults = {
options: { options: {
@ -45,6 +44,7 @@ export class SSHProfilesService extends ProfileProvider<SSHProfile> {
reuseSession: true, reuseSession: true,
input: { backspace: 'backspace' }, input: { backspace: 'backspace' },
}, },
clearServiceMessagesOnConnect: true,
} }
constructor ( constructor (

View File

@ -34,7 +34,7 @@ export class SSHMultiplexerService {
if (!jumpConnection) { if (!jumpConnection) {
return key return key
} }
const jumpProfile = this.profilesService.getConfigProxyForProfile(jumpConnection) const jumpProfile = this.profilesService.getConfigProxyForProfile<SSHProfile>(jumpConnection)
key += '$' + await this.getMultiplexerKey(jumpProfile) key += '$' + await this.getMultiplexerKey(jumpProfile)
} }
return key return key

View File

@ -1,11 +1,11 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ProfileProvider, NewTabParameters, PartialProfile, TranslateService } from 'tabby-core' import { NewTabParameters, PartialProfile, TranslateService, ConnectableProfileProvider } from 'tabby-core'
import { TelnetProfileSettingsComponent } from './components/telnetProfileSettings.component' import { TelnetProfileSettingsComponent } from './components/telnetProfileSettings.component'
import { TelnetTabComponent } from './components/telnetTab.component' import { TelnetTabComponent } from './components/telnetTab.component'
import { TelnetProfile } from './session' import { TelnetProfile } from './session'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class TelnetProfilesService extends ProfileProvider<TelnetProfile> { export class TelnetProfilesService extends ConnectableProfileProvider<TelnetProfile> {
id = 'telnet' id = 'telnet'
name = 'Telnet' name = 'Telnet'
supportsQuickConnect = true supportsQuickConnect = true
@ -21,6 +21,7 @@ export class TelnetProfilesService extends ProfileProvider<TelnetProfile> {
scripts: [], scripts: [],
input: { backspace: 'backspace' }, input: { backspace: 'backspace' },
}, },
clearServiceMessagesOnConnect: false,
} }
constructor (private translate: TranslateService) { super() } constructor (private translate: TranslateService) { super() }

View File

@ -3,11 +3,11 @@ import colors from 'ansi-colors'
import stripAnsi from 'strip-ansi' import stripAnsi from 'strip-ansi'
import { Injector } from '@angular/core' import { Injector } from '@angular/core'
import { LogService } from 'tabby-core' import { LogService } from 'tabby-core'
import { BaseSession, BaseTerminalProfile, InputProcessingOptions, InputProcessor, LoginScriptsOptions, SessionMiddleware, StreamProcessingOptions, TerminalStreamProcessor } from 'tabby-terminal' import { BaseSession, ConnectableTerminalProfile, InputProcessingOptions, InputProcessor, LoginScriptsOptions, SessionMiddleware, StreamProcessingOptions, TerminalStreamProcessor } from 'tabby-terminal'
import { Subject, Observable } from 'rxjs' import { Subject, Observable } from 'rxjs'
export interface TelnetProfile extends BaseTerminalProfile { export interface TelnetProfile extends ConnectableTerminalProfile {
options: TelnetProfileOptions options: TelnetProfileOptions
} }

View File

@ -4,7 +4,7 @@ import { Injector, Component } from '@angular/core'
import { first } from 'rxjs' import { first } from 'rxjs'
import { BaseTerminalProfile } from './interfaces' import { ConnectableTerminalProfile } from './interfaces'
import { BaseTerminalTabComponent } from './baseTerminalTab.component' import { BaseTerminalTabComponent } from './baseTerminalTab.component'
import { GetRecoveryTokenOptions, RecoveryToken } from 'tabby-core' import { GetRecoveryTokenOptions, RecoveryToken } from 'tabby-core'
@ -13,7 +13,7 @@ import { GetRecoveryTokenOptions, RecoveryToken } from 'tabby-core'
* A class to base your custom connectable terminal tabs on * A class to base your custom connectable terminal tabs on
*/ */
@Component({ template: '' }) @Component({ template: '' })
export abstract class ConnectableTerminalTabComponent<P extends BaseTerminalProfile> extends BaseTerminalTabComponent<P> { export abstract class ConnectableTerminalTabComponent<P extends ConnectableTerminalProfile> extends BaseTerminalTabComponent<P> {
protected reconnectOffered = false protected reconnectOffered = false
protected isDisconnectedByHand = false protected isDisconnectedByHand = false
@ -57,6 +57,9 @@ export abstract class ConnectableTerminalTabComponent<P extends BaseTerminalProf
async initializeSession (): Promise<void> { async initializeSession (): Promise<void> {
this.reconnectOffered = false this.reconnectOffered = false
this.isDisconnectedByHand = false this.isDisconnectedByHand = false
if (this.profile.clearServiceMessagesOnConnect) {
this.frontend?.clear()
}
} }
/** /**

View File

@ -1,4 +1,4 @@
import { Profile } from 'tabby-core' import { ConnectableProfile, Profile } from 'tabby-core'
export interface ResizeEvent { export interface ResizeEvent {
columns: number columns: number
@ -19,3 +19,5 @@ export interface TerminalColorScheme {
export interface BaseTerminalProfile extends Profile { export interface BaseTerminalProfile extends Profile {
terminalColorScheme?: TerminalColorScheme terminalColorScheme?: TerminalColorScheme
} }
export interface ConnectableTerminalProfile extends BaseTerminalProfile, ConnectableProfile {}