mirror of
https://github.com/Eugeny/tabby.git
synced 2025-06-09 22:10:09 +00:00
use new selector in serial plugin
This commit is contained in:
parent
28c58d4ec0
commit
db43381f0d
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
.list-group.mt-3(*ngIf='filteredOptions.length')
|
.list-group.mt-3(*ngIf='filteredOptions.length')
|
||||||
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
a.list-group-item.list-group-item-action.d-flex.align-items-center(
|
||||||
|
#item,
|
||||||
(click)='selectOption(option)',
|
(click)='selectOption(option)',
|
||||||
[class.active]='selectedIndex == i',
|
[class.active]='selectedIndex == i',
|
||||||
*ngFor='let option of filteredOptions; let i = index'
|
*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 { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { SelectorOption } from '../api/selector'
|
import { SelectorOption } from '../api/selector'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
template: require('./selectorModal.component.pug'),
|
template: require('./selectorModal.component.pug'),
|
||||||
// styles: [require('./selectorModal.component.scss')],
|
styles: [require('./selectorModal.component.scss')],
|
||||||
})
|
})
|
||||||
export class SelectorModalComponent<T> {
|
export class SelectorModalComponent<T> {
|
||||||
@Input() options: SelectorOption<T>[]
|
@Input() options: SelectorOption<T>[]
|
||||||
@ -13,6 +13,7 @@ export class SelectorModalComponent<T> {
|
|||||||
@Input() filter = ''
|
@Input() filter = ''
|
||||||
@Input() name: string
|
@Input() name: string
|
||||||
@Input() selectedIndex = 0
|
@Input() selectedIndex = 0
|
||||||
|
@ViewChildren('item') itemChildren: QueryList<ElementRef>
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public modalInstance: NgbActiveModal,
|
public modalInstance: NgbActiveModal,
|
||||||
@ -22,7 +23,7 @@ export class SelectorModalComponent<T> {
|
|||||||
this.onFilterChange()
|
this.onFilterChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('keyup', ['$event']) onKeyUp (event: KeyboardEvent): void {
|
@HostListener('keypress', ['$event']) onKeyUp (event: KeyboardEvent): void {
|
||||||
if (event.key === 'ArrowUp') {
|
if (event.key === 'ArrowUp') {
|
||||||
this.selectedIndex--
|
this.selectedIndex--
|
||||||
}
|
}
|
||||||
@ -37,6 +38,10 @@ export class SelectorModalComponent<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.selectedIndex = (this.selectedIndex + this.filteredOptions.length) % this.filteredOptions.length
|
this.selectedIndex = (this.selectedIndex + this.filteredOptions.length) % this.filteredOptions.length
|
||||||
|
Array.from(this.itemChildren)[this.selectedIndex]?.nativeElement.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'nearest',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterChange (): void {
|
onFilterChange (): void {
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable, Injector } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
|
||||||
import { HotkeysService, ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
|
import { HotkeysService, ToolbarButtonProvider, ToolbarButton } from 'terminus-core'
|
||||||
import { SerialModalComponent } from './components/serialModal.component'
|
import { SerialService } from './services/serial.service'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ButtonProvider extends ToolbarButtonProvider {
|
export class ButtonProvider extends ToolbarButtonProvider {
|
||||||
constructor (
|
constructor (
|
||||||
private ngbModal: NgbModal,
|
private injector: Injector,
|
||||||
hotkeys: HotkeysService,
|
hotkeys: HotkeysService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
@ -20,7 +19,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
activate () {
|
activate () {
|
||||||
this.ngbModal.open(SerialModalComponent)
|
this.injector.get(SerialService).showConnectionSelector()
|
||||||
}
|
}
|
||||||
|
|
||||||
provide (): ToolbarButton[] {
|
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
|
serialPort: any
|
||||||
private homeEndSubscription: Subscription
|
private homeEndSubscription: Subscription
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||||
constructor (
|
constructor (
|
||||||
injector: Injector,
|
injector: Injector,
|
||||||
private serial: SerialService,
|
|
||||||
) {
|
) {
|
||||||
super(injector)
|
super(injector)
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.session = this.serial.createSession(this.connection)
|
this.session = this.injector.get(SerialService).createSession(this.connection)
|
||||||
this.session.serviceMessage$.subscribe(msg => {
|
this.session.serviceMessage$.subscribe(msg => {
|
||||||
this.write('\r\n' + colors.black.bgWhite(' serial ') + ' ' + msg + '\r\n')
|
this.write('\r\n' + colors.black.bgWhite(' serial ') + ' ' + msg + '\r\n')
|
||||||
this.session.resize(this.size.columns, this.size.rows)
|
this.session.resize(this.size.columns, this.size.rows)
|
||||||
@ -80,11 +80,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
|
|||||||
spinner.start()
|
spinner.start()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.serialPort = await this.serial.connectSession(this.session, (message: string) => {
|
this.serialPort = await this.injector.get(SerialService).connectSession(this.session)
|
||||||
spinner.stop(true)
|
|
||||||
this.write(message + '\r\n')
|
|
||||||
spinner.start()
|
|
||||||
})
|
|
||||||
spinner.stop(true)
|
spinner.stop(true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
spinner.stop(true)
|
spinner.stop(true)
|
||||||
|
@ -8,7 +8,6 @@ import { SettingsTabProvider } from 'terminus-settings'
|
|||||||
import TerminusTerminalModule from 'terminus-terminal'
|
import TerminusTerminalModule from 'terminus-terminal'
|
||||||
|
|
||||||
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
|
import { EditConnectionModalComponent } from './components/editConnectionModal.component'
|
||||||
import { SerialModalComponent } from './components/serialModal.component'
|
|
||||||
import { SerialSettingsTabComponent } from './components/serialSettingsTab.component'
|
import { SerialSettingsTabComponent } from './components/serialSettingsTab.component'
|
||||||
import { SerialTabComponent } from './components/serialTab.component'
|
import { SerialTabComponent } from './components/serialTab.component'
|
||||||
|
|
||||||
@ -37,13 +36,11 @@ import { SerialHotkeyProvider } from './hotkeys'
|
|||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
EditConnectionModalComponent,
|
EditConnectionModalComponent,
|
||||||
SerialModalComponent,
|
|
||||||
SerialSettingsTabComponent,
|
SerialSettingsTabComponent,
|
||||||
SerialTabComponent,
|
SerialTabComponent,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
EditConnectionModalComponent,
|
EditConnectionModalComponent,
|
||||||
SerialModalComponent,
|
|
||||||
SerialSettingsTabComponent,
|
SerialSettingsTabComponent,
|
||||||
SerialTabComponent,
|
SerialTabComponent,
|
||||||
],
|
],
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { Injectable, NgZone } from '@angular/core'
|
import { Injectable, NgZone } from '@angular/core'
|
||||||
import SerialPort from 'serialport'
|
import SerialPort from 'serialport'
|
||||||
import { ToastrService } from 'ngx-toastr'
|
import { ToastrService } from 'ngx-toastr'
|
||||||
import { LogService } from 'terminus-core'
|
import { LogService, AppService, SelectorOption, ConfigService } from 'terminus-core'
|
||||||
import { SerialConnection, SerialSession, SerialPortInfo } from '../api'
|
import { SettingsTabComponent } from 'terminus-settings'
|
||||||
|
import { SerialConnection, SerialSession, SerialPortInfo, BAUD_RATES } from '../api'
|
||||||
|
import { SerialTabComponent } from '../components/serialTab.component'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class SerialService {
|
export class SerialService {
|
||||||
@ -10,6 +12,8 @@ export class SerialService {
|
|||||||
private log: LogService,
|
private log: LogService,
|
||||||
private zone: NgZone,
|
private zone: NgZone,
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
|
private app: AppService,
|
||||||
|
private config: ConfigService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async listPorts (): Promise<SerialPortInfo[]> {
|
async listPorts (): Promise<SerialPortInfo[]> {
|
||||||
@ -25,7 +29,7 @@ export class SerialService {
|
|||||||
return session
|
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,
|
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,
|
dataBits: session.connection.databits, stopBits: session.connection.stopbits, parity: session.connection.parity,
|
||||||
rtscts: session.connection.rtscts, xon: session.connection.xon, xoff: session.connection.xoff,
|
rtscts: session.connection.rtscts, xon: session.connection.xon, xoff: session.connection.xoff,
|
||||||
@ -57,4 +61,107 @@ export class SerialService {
|
|||||||
})
|
})
|
||||||
return serial
|
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…
x
Reference in New Issue
Block a user