mirror of
https://github.com/Eugeny/tabby
synced 2024-11-15 09:27:24 +00:00
use new selector in serial plugin
This commit is contained in:
parent
28c58d4ec0
commit
db43381f0d
10 changed files with 130 additions and 160 deletions
|
@ -9,6 +9,7 @@
|
|||
|
||||
.list-group.mt-3(*ngIf='filteredOptions.length')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
#item,
|
||||
(click)='selectOption(option)',
|
||||
[class.active]='selectedIndex == i',
|
||||
*ngFor='let option of filteredOptions; let i = index'
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
.list-group {
|
||||
max-height: 70vh;
|
||||
overflow: auto;
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import { Component, Input, HostListener } from '@angular/core'
|
||||
import { Component, Input, HostListener, ViewChildren, QueryList, ElementRef } 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')],
|
||||
styles: [require('./selectorModal.component.scss')],
|
||||
})
|
||||
export class SelectorModalComponent<T> {
|
||||
@Input() options: SelectorOption<T>[]
|
||||
|
@ -13,6 +13,7 @@ export class SelectorModalComponent<T> {
|
|||
@Input() filter = ''
|
||||
@Input() name: string
|
||||
@Input() selectedIndex = 0
|
||||
@ViewChildren('item') itemChildren: QueryList<ElementRef>
|
||||
|
||||
constructor (
|
||||
public modalInstance: NgbActiveModal,
|
||||
|
@ -22,7 +23,7 @@ export class SelectorModalComponent<T> {
|
|||
this.onFilterChange()
|
||||
}
|
||||
|
||||
@HostListener('keyup', ['$event']) onKeyUp (event: KeyboardEvent): void {
|
||||
@HostListener('keypress', ['$event']) onKeyUp (event: KeyboardEvent): void {
|
||||
if (event.key === 'ArrowUp') {
|
||||
this.selectedIndex--
|
||||
}
|
||||
|
@ -37,6 +38,10 @@ export class SelectorModalComponent<T> {
|
|||
}
|
||||
|
||||
this.selectedIndex = (this.selectedIndex + this.filteredOptions.length) % this.filteredOptions.length
|
||||
Array.from(this.itemChildren)[this.selectedIndex]?.nativeElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
})
|
||||
}
|
||||
|
||||
onFilterChange (): void {
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Injectable } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { Injectable, Injector } from '@angular/core'
|
||||
import { HotkeysService, ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
|
||||
import { SerialModalComponent } from './components/serialModal.component'
|
||||
import { SerialService } from './services/serial.service'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
export class ButtonProvider extends ToolbarButtonProvider {
|
||||
constructor (
|
||||
private ngbModal: NgbModal,
|
||||
private injector: Injector,
|
||||
hotkeys: HotkeysService,
|
||||
) {
|
||||
super()
|
||||
|
@ -20,7 +19,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||
}
|
||||
|
||||
activate () {
|
||||
this.ngbModal.open(SerialModalComponent)
|
||||
this.injector.get(SerialService).showConnectionSelector()
|
||||
}
|
||||
|
||||
provide (): ToolbarButton[] {
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
.modal-body
|
||||
input.form-control(
|
||||
type='text',
|
||||
[(ngModel)]='quickTarget',
|
||||
autofocus,
|
||||
placeholder='Quick connect: path@baudrate',
|
||||
(ngModelChange)='refresh()',
|
||||
(keyup.enter)='quickConnect()'
|
||||
)
|
||||
|
||||
.list-group.mt-3(*ngIf='lastConnection')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center((click)='connect(lastConnection)')
|
||||
i.fas.fa-fw.fa-history
|
||||
.mr-auto {{lastConnection.name}}
|
||||
button.btn.btn-outline-danger.btn-sm((click)='clearLastConnection(); $event.stopPropagation()')
|
||||
i.fas.fa-trash
|
||||
|
||||
.list-group.mt-3.connections-list(*ngIf='connections.length')
|
||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||
*ngFor='let connection of connections',
|
||||
(click)='connect(connection)'
|
||||
)
|
||||
.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}}
|
|
@ -1,5 +0,0 @@
|
|||
.list-group.connections-list {
|
||||
display: block;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { Component, NgZone } from '@angular/core'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { ConfigService, AppService } from 'terminus-core'
|
||||
import { SettingsTabComponent } from 'terminus-settings'
|
||||
import { SerialService } from '../services/serial.service'
|
||||
import { SerialConnection, SerialPortInfo, BAUD_RATES } from '../api'
|
||||
import { SerialTabComponent } from './serialTab.component'
|
||||
|
||||
/** @hidden */
|
||||
@Component({
|
||||
template: require('./serialModal.component.pug'),
|
||||
styles: [require('./serialModal.component.scss')],
|
||||
})
|
||||
export class SerialModalComponent {
|
||||
connections: SerialConnection[]
|
||||
quickTarget: string
|
||||
lastConnection: SerialConnection|null = null
|
||||
foundPorts: SerialPortInfo[] = []
|
||||
|
||||
constructor (
|
||||
public modalInstance: NgbActiveModal,
|
||||
private config: ConfigService,
|
||||
private serial: SerialService,
|
||||
private app: AppService,
|
||||
private zone: NgZone,
|
||||
private toastr: ToastrService,
|
||||
) { }
|
||||
|
||||
async ngOnInit () {
|
||||
this.connections = this.config.store.serial.connections
|
||||
if (window.localStorage.lastSerialConnection) {
|
||||
this.lastConnection = JSON.parse(window.localStorage.lastSerialConnection)
|
||||
}
|
||||
this.foundPorts = await this.serial.listPorts()
|
||||
}
|
||||
|
||||
quickConnect () {
|
||||
let path = this.quickTarget
|
||||
let baudrate = 115200
|
||||
if (this.quickTarget.includes('@')) {
|
||||
baudrate = parseInt(path.split('@')[1])
|
||||
path = path.split('@')[0]
|
||||
}
|
||||
const connection: SerialConnection = {
|
||||
name: this.quickTarget,
|
||||
port: path,
|
||||
baudrate: baudrate,
|
||||
databits: 8,
|
||||
parity: 'none',
|
||||
rtscts: false,
|
||||
stopbits: 1,
|
||||
xany: false,
|
||||
xoff: false,
|
||||
xon: false,
|
||||
}
|
||||
window.localStorage.lastSerialConnection = JSON.stringify(connection)
|
||||
this.connect(connection)
|
||||
}
|
||||
|
||||
clearLastConnection () {
|
||||
window.localStorage.lastSerialConnection = null
|
||||
this.lastConnection = null
|
||||
}
|
||||
|
||||
async connect (connection: SerialConnection) {
|
||||
this.close()
|
||||
|
||||
try {
|
||||
const tab = this.zone.run(() => this.app.openNewTab(
|
||||
SerialTabComponent,
|
||||
{ connection }
|
||||
) as SerialTabComponent)
|
||||
if (connection.color) {
|
||||
(this.app.getParentTab(tab) || tab).color = connection.color
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.app.activeTab.emitFocused()
|
||||
})
|
||||
} catch (error) {
|
||||
this.toastr.error(`Could not connect: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
manageConnections () {
|
||||
this.close()
|
||||
this.app.openNewTab(SettingsTabComponent, { activeTab: 'serial' })
|
||||
}
|
||||
|
||||
close () {
|
||||
this.modalInstance.close()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
|
@ -21,9 +21,9 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
|
|||
serialPort: any
|
||||
private homeEndSubscription: Subscription
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||
constructor (
|
||||
injector: Injector,
|
||||
private serial: SerialService,
|
||||
) {
|
||||
super(injector)
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
|
|||
return
|
||||
}
|
||||
|
||||
this.session = this.serial.createSession(this.connection)
|
||||
this.session = this.injector.get(SerialService).createSession(this.connection)
|
||||
this.session.serviceMessage$.subscribe(msg => {
|
||||
this.write('\r\n' + colors.black.bgWhite(' serial ') + ' ' + msg + '\r\n')
|
||||
this.session.resize(this.size.columns, this.size.rows)
|
||||
|
@ -80,11 +80,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
|
|||
spinner.start()
|
||||
|
||||
try {
|
||||
this.serialPort = await this.serial.connectSession(this.session, (message: string) => {
|
||||
spinner.stop(true)
|
||||
this.write(message + '\r\n')
|
||||
spinner.start()
|
||||
})
|
||||
this.serialPort = await this.injector.get(SerialService).connectSession(this.session)
|
||||
spinner.stop(true)
|
||||
} catch (e) {
|
||||
spinner.stop(true)
|
||||
|
|
|
@ -8,7 +8,6 @@ import { SettingsTabProvider } from 'terminus-settings'
|
|||
import TerminusTerminalModule from 'terminus-terminal'
|
||||
|
||||
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
|
||||
import { SerialModalComponent } from './components/serialModal.component'
|
||||
import { SerialSettingsTabComponent } from './components/serialSettingsTab.component'
|
||||
import { SerialTabComponent } from './components/serialTab.component'
|
||||
|
||||
|
@ -37,13 +36,11 @@ import { SerialHotkeyProvider } from './hotkeys'
|
|||
],
|
||||
entryComponents: [
|
||||
EditConnectionModalComponent,
|
||||
SerialModalComponent,
|
||||
SerialSettingsTabComponent,
|
||||
SerialTabComponent,
|
||||
],
|
||||
declarations: [
|
||||
EditConnectionModalComponent,
|
||||
SerialModalComponent,
|
||||
SerialSettingsTabComponent,
|
||||
SerialTabComponent,
|
||||
],
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { Injectable, NgZone } from '@angular/core'
|
||||
import SerialPort from 'serialport'
|
||||
import { ToastrService } from 'ngx-toastr'
|
||||
import { LogService } from 'terminus-core'
|
||||
import { SerialConnection, SerialSession, SerialPortInfo } from '../api'
|
||||
import { LogService, AppService, SelectorOption, ConfigService } from 'terminus-core'
|
||||
import { SettingsTabComponent } from 'terminus-settings'
|
||||
import { SerialConnection, SerialSession, SerialPortInfo, BAUD_RATES } from '../api'
|
||||
import { SerialTabComponent } from '../components/serialTab.component'
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SerialService {
|
||||
|
@ -10,6 +12,8 @@ export class SerialService {
|
|||
private log: LogService,
|
||||
private zone: NgZone,
|
||||
private toastr: ToastrService,
|
||||
private app: AppService,
|
||||
private config: ConfigService,
|
||||
) { }
|
||||
|
||||
async listPorts (): Promise<SerialPortInfo[]> {
|
||||
|
@ -25,7 +29,7 @@ export class SerialService {
|
|||
return session
|
||||
}
|
||||
|
||||
async connectSession (session: SerialSession, _?: (s: any) => void): Promise<SerialPort> {
|
||||
async connectSession (session: SerialSession): Promise<SerialPort> {
|
||||
const serial = new SerialPort(session.connection.port, { autoOpen: false, baudRate: session.connection.baudrate,
|
||||
dataBits: session.connection.databits, stopBits: session.connection.stopbits, parity: session.connection.parity,
|
||||
rtscts: session.connection.rtscts, xon: session.connection.xon, xoff: session.connection.xoff,
|
||||
|
@ -57,4 +61,107 @@ export class SerialService {
|
|||
})
|
||||
return serial
|
||||
}
|
||||
|
||||
async showConnectionSelector (): Promise<void> {
|
||||
const options: SelectorOption<void>[] = []
|
||||
const lastConnection = JSON.parse(window.localStorage.lastSerialConnection)
|
||||
const foundPorts = await this.listPorts()
|
||||
|
||||
if (lastConnection) {
|
||||
options.push({
|
||||
name: lastConnection.name,
|
||||
icon: 'history',
|
||||
callback: () => this.connect(lastConnection),
|
||||
})
|
||||
options.push({
|
||||
name: 'Clear last connection',
|
||||
icon: 'eraser',
|
||||
callback: () => {
|
||||
window.localStorage.lastSerialConnection = null
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
for (const port of foundPorts) {
|
||||
options.push({
|
||||
name: port.name,
|
||||
description: port.description,
|
||||
icon: 'arrow-right',
|
||||
callback: () => this.connectFoundPort(port),
|
||||
})
|
||||
}
|
||||
|
||||
for (const connection of this.config.store.serial.connections) {
|
||||
options.push({
|
||||
name: connection.name,
|
||||
description: connection.port,
|
||||
callback: () => this.connect(connection),
|
||||
})
|
||||
}
|
||||
|
||||
options.push({
|
||||
name: 'Manage connections',
|
||||
icon: 'cog',
|
||||
callback: () => this.app.openNewTab(SettingsTabComponent, { activeTab: 'serial' }),
|
||||
})
|
||||
|
||||
options.push({
|
||||
name: 'Quick connect',
|
||||
freeInputPattern: 'Open device: %s...',
|
||||
icon: 'arrow-right',
|
||||
callback: query => this.quickConnect(query),
|
||||
})
|
||||
|
||||
|
||||
await this.app.showSelector('Open a serial port', options)
|
||||
}
|
||||
|
||||
async connect (connection: SerialConnection): Promise<SerialTabComponent> {
|
||||
try {
|
||||
const tab = this.app.openNewTab(
|
||||
SerialTabComponent,
|
||||
{ connection }
|
||||
) as SerialTabComponent
|
||||
if (connection.color) {
|
||||
(this.app.getParentTab(tab) || tab).color = connection.color
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.app.activeTab.emitFocused()
|
||||
})
|
||||
return tab
|
||||
} catch (error) {
|
||||
this.toastr.error(`Could not connect: ${error}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
quickConnect (query: string): Promise<SerialTabComponent> {
|
||||
let path = query
|
||||
let baudrate = 115200
|
||||
if (query.includes('@')) {
|
||||
baudrate = parseInt(path.split('@')[1])
|
||||
path = path.split('@')[0]
|
||||
}
|
||||
const connection: SerialConnection = {
|
||||
name: query,
|
||||
port: path,
|
||||
baudrate: baudrate,
|
||||
databits: 8,
|
||||
parity: 'none',
|
||||
rtscts: false,
|
||||
stopbits: 1,
|
||||
xany: false,
|
||||
xoff: false,
|
||||
xon: false,
|
||||
}
|
||||
window.localStorage.lastSerialConnection = JSON.stringify(connection)
|
||||
return this.connect(connection)
|
||||
}
|
||||
|
||||
async connectFoundPort (port: SerialPortInfo): Promise<SerialTabComponent> {
|
||||
const rate = await this.app.showSelector('Baud rate', BAUD_RATES.map(x => ({
|
||||
name: x.toString(), result: x,
|
||||
})))
|
||||
return this.quickConnect(`${port.name}@${rate}`)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue