mirror of
https://github.com/Eugeny/tabby.git
synced 2024-12-09 06:20:22 +08:00
added serial port detection
This commit is contained in:
parent
f5ffdc1707
commit
10b21ee085
@ -7,6 +7,7 @@ export { ConfigProvider } from './configProvider'
|
||||
export { HotkeyProvider, HotkeyDescription } from './hotkeyProvider'
|
||||
export { Theme } from './theme'
|
||||
export { TabContextMenuItemProvider } from './tabContextMenuProvider'
|
||||
export { SelectorOption } from './selector'
|
||||
|
||||
export { AppService } from '../services/app.service'
|
||||
export { ConfigService } from '../services/config.service'
|
||||
|
5
terminus-core/src/api/selector.ts
Normal file
5
terminus-core/src/api/selector.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface SelectorOption<T> {
|
||||
name: string
|
||||
description?: string
|
||||
result: T
|
||||
}
|
18
terminus-core/src/components/selectorModal.component.pug
Normal file
18
terminus-core/src/components/selectorModal.component.pug
Normal file
@ -0,0 +1,18 @@
|
||||
.modal-body
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='filter',
|
||||
autofocus,
|
||||
[placeholder]='name',
|
||||
(ngModelChange)='onFilterChange()',
|
||||
(keyup.enter)='onFilterEnter()',
|
||||
(keyup.escape)='close()'
|
||||
)
|
||||
|
||||
.list-group.mt-3(*ngIf='filteredOptions.length')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
(click)='selectOption(option)',
|
||||
*ngFor='let option of filteredOptions'
|
||||
)
|
||||
.mr-2 {{option.name}}
|
||||
.text-muted {{option.description}}
|
47
terminus-core/src/components/selectorModal.component.ts
Normal file
47
terminus-core/src/components/selectorModal.component.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { SelectorOption } from '../api/selector'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./selectorModal.component.pug'),
|
||||
// styles: [require('./selectorModal.component.scss')],
|
||||
})
|
||||
export class SelectorModalComponent<T> {
|
||||
@Input() options: SelectorOption<T>[]
|
||||
@Input() filteredOptions: SelectorOption<T>[]
|
||||
@Input() filter = ''
|
||||
@Input() name: string
|
||||
|
||||
constructor (
|
||||
public modalInstance: NgbActiveModal,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
this.onFilterChange()
|
||||
}
|
||||
|
||||
onFilterChange () {
|
||||
const f = this.filter.trim().toLowerCase()
|
||||
if (!f) {
|
||||
this.filteredOptions = this.options
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
this.filteredOptions = this.options.filter(x => (x.name + (x.description || '')).toLowerCase().includes(f))
|
||||
}
|
||||
}
|
||||
|
||||
onFilterEnter () {
|
||||
if (this.filteredOptions.length === 1) {
|
||||
this.selectOption(this.filteredOptions[0])
|
||||
}
|
||||
}
|
||||
|
||||
selectOption (option: SelectorOption<T>) {
|
||||
this.modalInstance.close(option.result)
|
||||
}
|
||||
|
||||
close () {
|
||||
this.modalInstance.dismiss()
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ import { TitleBarComponent } from './components/titleBar.component'
|
||||
import { ToggleComponent } from './components/toggle.component'
|
||||
import { WindowControlsComponent } from './components/windowControls.component'
|
||||
import { RenameTabModalComponent } from './components/renameTabModal.component'
|
||||
import { SelectorModalComponent } from './components/selectorModal.component'
|
||||
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
|
||||
import { SplitTabSpannerComponent } from './components/splitTabSpanner.component'
|
||||
import { WelcomeTabComponent } from './components/welcomeTab.component'
|
||||
@ -82,6 +83,7 @@ const PROVIDERS = [
|
||||
SafeModeModalComponent,
|
||||
AutofocusDirective,
|
||||
FastHtmlBindDirective,
|
||||
SelectorModalComponent,
|
||||
SplitTabComponent,
|
||||
SplitTabSpannerComponent,
|
||||
WelcomeTabComponent,
|
||||
@ -89,6 +91,7 @@ const PROVIDERS = [
|
||||
entryComponents: [
|
||||
RenameTabModalComponent,
|
||||
SafeModeModalComponent,
|
||||
SelectorModalComponent,
|
||||
SplitTabComponent,
|
||||
WelcomeTabComponent,
|
||||
],
|
||||
|
@ -2,9 +2,12 @@
|
||||
import { Observable, Subject, AsyncSubject } from 'rxjs'
|
||||
import { takeUntil } from 'rxjs/operators'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
import { BaseTabComponent } from '../components/baseTab.component'
|
||||
import { SplitTabComponent } from '../components/splitTab.component'
|
||||
import { SelectorModalComponent } from '../components/selectorModal.component'
|
||||
import { SelectorOption } from '../api/selector'
|
||||
|
||||
import { ConfigService } from './config.service'
|
||||
import { HostAppService } from './hostApp.service'
|
||||
@ -69,6 +72,7 @@ export class AppService {
|
||||
private hostApp: HostAppService,
|
||||
private tabRecovery: TabRecoveryService,
|
||||
private tabsService: TabsService,
|
||||
private ngbModal: NgbModal,
|
||||
) {
|
||||
if (hostApp.getWindow().id === 1) {
|
||||
if (config.store.terminal.recoverTabs) {
|
||||
@ -315,4 +319,12 @@ export class AppService {
|
||||
stopObservingTabCompletion (tab: BaseTabComponent) {
|
||||
this.completionObservers.delete(tab)
|
||||
}
|
||||
|
||||
showSelector <T> (name: string, options: SelectorOption<T>[]): Promise<T> {
|
||||
const modal = this.ngbModal.open(SelectorModalComponent)
|
||||
const instance: SelectorModalComponent<T> = modal.componentInstance
|
||||
instance.name = name
|
||||
instance.options = options
|
||||
return modal.result as Promise<T>
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,15 @@ export interface SerialConnection {
|
||||
color?: string
|
||||
}
|
||||
|
||||
export const BAUD_RATES = [
|
||||
110, 150, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600,
|
||||
]
|
||||
|
||||
export interface SerialPortInfo {
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export class SerialSession extends BaseSession {
|
||||
scripts?: LoginScript[]
|
||||
serial: SerialPort
|
||||
|
@ -24,14 +24,16 @@
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='connection.port',
|
||||
[ngbTypeahead]='portsAutocomplete',
|
||||
[resultFormatter]='portsFormatter',
|
||||
)
|
||||
|
||||
.form-group
|
||||
label Baud Rate
|
||||
input.form-control(
|
||||
type='number',
|
||||
select.form-control(
|
||||
[(ngModel)]='connection.baudrate',
|
||||
)
|
||||
option([value]='x', *ngFor='let x of baudRates') {{x}}
|
||||
|
||||
ngb-tab(id='advanced')
|
||||
ng-template(ngbTabTitle) Advanced
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { map } from 'rxjs/operators'
|
||||
import { ElectronService, HostAppService } from 'terminus-core'
|
||||
import { SerialConnection, LoginScript } from '../api'
|
||||
import { SerialConnection, LoginScript, SerialPortInfo, BAUD_RATES } from '../api'
|
||||
import { SerialService } from '../services/serial.service'
|
||||
// import { PromptModalComponent } from './promptModal.component'
|
||||
|
||||
/** @hidden */
|
||||
@ -10,17 +12,32 @@ import { SerialConnection, LoginScript } from '../api'
|
||||
})
|
||||
export class EditConnectionModalComponent {
|
||||
connection: SerialConnection
|
||||
foundPorts: SerialPortInfo[]
|
||||
baudRates = BAUD_RATES
|
||||
|
||||
constructor (
|
||||
private modalInstance: NgbActiveModal,
|
||||
private electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
// private ngbModal: NgbModal,
|
||||
private serial: SerialService,
|
||||
) {
|
||||
}
|
||||
|
||||
portsAutocomplete = text$ => text$.pipe(map(() => {
|
||||
return this.foundPorts.map(x => x.name)
|
||||
}))
|
||||
|
||||
portsFormatter = port => {
|
||||
const p = this.foundPorts.find(x => x.name === port)
|
||||
if (p?.description) {
|
||||
return `${port} (${p.description})`
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
async ngOnInit () {
|
||||
this.connection.scripts = this.connection.scripts || []
|
||||
this.foundPorts = await this.serial.listPorts()
|
||||
}
|
||||
|
||||
save () {
|
||||
|
@ -30,3 +30,11 @@
|
||||
)
|
||||
.mr-2 {{connection.name}}
|
||||
.text-muted {{connection.port}}
|
||||
|
||||
.list-group.mt-3(*ngIf='foundPorts.length')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
(click)='connectFoundPort(port)',
|
||||
*ngFor='let port of foundPorts'
|
||||
)
|
||||
.mr-2 {{port.name}}
|
||||
.text-muted {{port.description}}
|
||||
|
@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr'
|
||||
import { ConfigService, AppService } from 'terminus-core'
|
||||
import { SettingsTabComponent } from 'terminus-settings'
|
||||
import { SerialService } from '../services/serial.service'
|
||||
import { SerialConnection, SerialConnectionGroup } from '../api'
|
||||
import { SerialConnection, SerialConnectionGroup, SerialPortInfo, BAUD_RATES } from '../api'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
@ -18,6 +18,7 @@ export class SerialModalComponent {
|
||||
lastConnection: SerialConnection|null = null
|
||||
childGroups: SerialConnectionGroup[]
|
||||
groupCollapsed: {[id: string]: boolean} = {}
|
||||
foundPorts: SerialPortInfo[] = []
|
||||
|
||||
constructor (
|
||||
public modalInstance: NgbActiveModal,
|
||||
@ -27,12 +28,14 @@ export class SerialModalComponent {
|
||||
private toastr: ToastrService,
|
||||
) { }
|
||||
|
||||
ngOnInit () {
|
||||
async ngOnInit () {
|
||||
this.connections = this.config.store.serial.connections
|
||||
if (window.localStorage.lastSerialConnection) {
|
||||
this.lastConnection = JSON.parse(window.localStorage.lastSerialConnection)
|
||||
}
|
||||
this.refresh()
|
||||
|
||||
this.foundPorts = await this.serial.listPorts()
|
||||
}
|
||||
|
||||
quickConnect () {
|
||||
@ -105,4 +108,12 @@ export class SerialModalComponent {
|
||||
group.connections.push(connection)
|
||||
}
|
||||
}
|
||||
|
||||
async connectFoundPort (port: SerialPortInfo) {
|
||||
const rate = await this.app.showSelector('Baud rate', BAUD_RATES.map(x => ({
|
||||
name: x.toString(), result: x,
|
||||
})))
|
||||
this.quickTarget = `${port.name}@${rate}`
|
||||
this.quickConnect()
|
||||
}
|
||||
}
|
||||
|
@ -2,19 +2,23 @@ import { Injectable, NgZone } from '@angular/core'
|
||||
import SerialPort from 'serialport'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { AppService, LogService } from 'terminus-core'
|
||||
import { SerialConnection, SerialSession } from '../api'
|
||||
import { SerialConnection, SerialSession, SerialPortInfo } from '../api'
|
||||
import { SerialTabComponent } from '../components/serialTab.component'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SerialService {
|
||||
|
||||
private constructor (
|
||||
private log: LogService,
|
||||
private app: AppService,
|
||||
private zone: NgZone,
|
||||
private toastr: ToastrService,
|
||||
) {
|
||||
) { }
|
||||
|
||||
async listPorts (): Promise<SerialPortInfo[]> {
|
||||
return (await SerialPort.list()).map(x => ({
|
||||
name: x.path,
|
||||
description: x.manufacturer || x.serialNumber ? `${x.manufacturer || ''} ${x.serialNumber || ''}` : undefined,
|
||||
}))
|
||||
}
|
||||
|
||||
async openTab (connection: SerialConnection): Promise<SerialTabComponent> {
|
||||
|
Loading…
Reference in New Issue
Block a user