Compare commits

..

36 Commits

Author SHA1 Message Date
allcontributors[bot]
1fe961e1f1 update README.pt-BR.md [skip ci] 2023-05-22 15:26:23 +00:00
allcontributors[bot]
41bd8a6012 update .all-contributorsrc [skip ci] 2023-05-22 15:25:44 +00:00
allcontributors[bot]
5bfa72573b update README.es-ES.md [skip ci] 2023-05-22 15:25:41 +00:00
allcontributors[bot]
facd52ff4e update README.ja-JP.md [skip ci] 2023-05-22 15:24:43 +00:00
allcontributors[bot]
8826127913 update README.id-ID.md [skip ci] 2023-05-22 15:24:23 +00:00
allcontributors[bot]
fe58e4705c update README.de-DE.md [skip ci] 2023-05-22 15:24:14 +00:00
allcontributors[bot]
dcba109e07 update README.it-IT.md [skip ci] 2023-05-22 15:24:13 +00:00
allcontributors[bot]
a7e6fa7b1d update README.ko-KR.md [skip ci] 2023-05-22 15:24:12 +00:00
allcontributors[bot]
8e29b4ac97 update README.ru-RU.md [skip ci] 2023-05-22 15:24:11 +00:00
allcontributors[bot]
9379f91895 update README.zh-CN.md [skip ci] 2023-05-22 15:24:10 +00:00
allcontributors[bot]
d2a54d0d96 update README.md [skip ci] 2023-05-22 15:24:09 +00:00
Eugene
fac6ec572f Merge pull request #8416 from Clem-Fern/connectable-refactoring
Refactoring connectable tab
2023-05-16 21:35:21 +02:00
Clem Fern
539ad0bddc ref(tabby-terminal): remove deprecated Reconnectable interface 2023-05-16 19:13:00 +02:00
Eugene Pankov
a494d9c800 fixed #8313 - recover tab icons and colors ealier during start 2023-05-15 23:09:13 +02:00
Eugene Pankov
5075c836f9 fixed #8263 - make placeholders more legible 2023-05-15 22:57:40 +02:00
Eugene Pankov
1112766ac0 fixed #8404 - missing SFTP progressbar 2023-05-15 22:55:05 +02:00
Eugene Pankov
b97c334660 fixed #8417 - ensure that serial output is chunked at utf8 char boundaries 2023-05-15 22:17:33 +02:00
Eugene Pankov
46a1dd39f0 fixed #8419 2023-05-15 21:58:53 +02:00
Eugene Pankov
9c68274d46 updated contributors 2023-05-15 17:17:09 +02:00
Clem Fern
89b3f3892e ref(connectable tab) unused import 2023-05-12 20:32:58 +02:00
Clem Fern
37226f66e3 ref(connectable tab) ngOnInit logger 2023-05-12 20:27:58 +02:00
Clem Fern
38b8702373 ref(connectable tab) getRecoveryToken method 2023-05-12 20:06:40 +02:00
Clem Fern
bd4b11813c ref(connectable tab) on frontend ready 2023-05-12 20:01:06 +02:00
Clem Fern
38302622b8 lint 2023-05-12 19:46:43 +02:00
Clem Fern
312b365851 ref(connectable tab) on session destroyed 2023-05-12 19:39:44 +02:00
Clem Fern
2b5976f202 ref(connectable tab) reconnect context menu & hotkey n°2 2023-05-12 19:30:24 +02:00
Clem Fern
1e6f6af5ed ref(connectable tab) reconnect context menu & hotkey 2023-05-12 19:09:33 +02:00
Clem Fern
901181f681 ref(tabby-terminal): create ConnectableTerminalTabComponent class 2023-05-11 21:49:32 +02:00
Clem Fern
fd9505c18f ref(tabby-terminal): make Reconnectable interface deprecated 2023-05-11 21:17:12 +02:00
Eugene Pankov
d6d273b2fb Merge branch 'master' of github.com:Eugeny/tabby 2023-04-29 23:01:21 -07:00
Eugene Pankov
d98ab296c7 #6096 export serialtab and serialsession classes 2023-04-29 23:01:17 -07:00
Eugene
fe6836d996 Merge pull request #8332 from Clem-Fern/fix#8297 2023-04-28 22:54:56 +02:00
Clem Fern
d3adbd03d4 fix(tabby-terminal): prevent copying on select when search find next/previous 2023-04-28 20:03:55 +02:00
Eugene
30b51e45bc Merge pull request #8331 from Clem-Fern/fix#8297 2023-04-28 18:42:48 +02:00
Clem Fern
bb6ed52291 fix(tabby-terminal): set selected text into search box on opening 2023-04-28 17:17:58 +02:00
Eugene
9978e0b86f Merge pull request #8321 from Eugeny/all-contributors/add-Kapocsi 2023-04-28 03:58:56 +02:00
34 changed files with 267 additions and 175 deletions

View File

@@ -1166,6 +1166,24 @@
"contributions": [
"doc"
]
},
{
"login": "dylhack",
"name": "Dylan Hackworth",
"avatar_url": "https://avatars.githubusercontent.com/u/27179786?v=4",
"profile": "https://dylhack.dev/",
"contributions": [
"financial"
]
},
{
"login": "echo304",
"name": "Sangboak Lee",
"avatar_url": "https://avatars.githubusercontent.com/u/16456651?v=4",
"profile": "https://github.com/echo304",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@@ -319,6 +319,8 @@ Dank geht an diese wunderbaren Menschen ([emoji key](https://allcontributors.org
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -321,6 +321,8 @@ Gracias a estas maravillosas personas ([emoji key](https://allcontributors.org/d
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -318,6 +318,8 @@ Terima kasih kepada mereka yang telah membantu ([emoji key](https://allcontribut
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -314,6 +314,8 @@ Grazie a queste persone meravigliose ([emoji key](https://allcontributors.org/do
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -329,6 +329,8 @@ Windows上では、`Tabby.exe`がある場所と同じ場所に`data`フォル
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -308,6 +308,8 @@ Pull requests and plugins are welcome!
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -328,6 +328,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -322,6 +322,8 @@ Obrigado vai para essas pessoas maravilhosas ([emoji key](https://allcontributor
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -314,6 +314,8 @@ Pull-запросы и плагины приветствуются!
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -313,6 +313,8 @@
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://kapocsi.ca"><img src="https://avatars.githubusercontent.com/u/84490604?v=4?s=100" width="100px;" alt="Thomas Kapocsi"/><br /><sub><b>Thomas Kapocsi</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=Kapocsi" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylhack.dev/"><img src="https://avatars.githubusercontent.com/u/27179786?v=4?s=100" width="100px;" alt="Dylan Hackworth"/><br /><sub><b>Dylan Hackworth</b></sub></a><br /><a href="#financial-dylhack" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/echo304"><img src="https://avatars.githubusercontent.com/u/16456651?v=4?s=100" width="100px;" alt="Sangboak Lee"/><br /><sub><b>Sangboak Lee</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=echo304" title="Code">💻</a></td>
</tr>
</tbody>
</table>

View File

@@ -4,4 +4,6 @@ export const PLUGIN_BLACKLIST = [
'terminus-clickable-links', // now bundled with Tabby
'tabby-clickable-links', // now bundled with Tabby
'terminus-clickable-ips', // broken, functionality now bundled with Tabby
'terminus-elastic-quick-commands', // broken and abandoned, fork of quick-commands
'terminus-elastic-quick-cmds', // broken and abandoned, fork of quick-commands
]

View File

@@ -27,6 +27,8 @@ const cachedBuiltinModules = {
'@angular/compiler': require('@angular/compiler'),
'@angular/core': require('@angular/core'),
'@angular/forms': require('@angular/forms'),
'@angular/localize': require('@angular/localize'),
'@angular/localize/init': require('@angular/localize/init'),
'@angular/platform-browser': require('@angular/platform-browser'),
'@angular/platform-browser/animations': require('@angular/platform-browser/animations'),
'@angular/platform-browser-dynamic': require('@angular/platform-browser-dynamic'),

View File

@@ -7,6 +7,7 @@
"@angular/compiler-cli": "^15.2.6",
"@angular/core": "^15.2.6",
"@angular/forms": "^15.2.6",
"@angular/localize": "^15.2.9",
"@angular/platform-browser": "^15.2.6",
"@angular/platform-browser-dynamic": "^15.2.6",
"@biesbjerg/ngx-translate-extract-marker": "^1.0.0",

View File

@@ -51,7 +51,7 @@ title-bar(
#activeTransfersDropdown='ngbDropdown'
)
button.btn.btn-secondary.btn-tab-bar(
*ngIf='activeTransfers.length > 0',
[hidden]='activeTransfers.length == 0',
title='File transfers',
ngbDropdownToggle
) !{require('../icons/transfers.svg')}

View File

@@ -787,7 +787,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
get icon (): string|null {
return this.getFocusedTab()?.icon ?? null
return this.getFocusedTab()?.icon ?? this.getAllTabs()[0]?.icon ?? null
}
set icon (icon: string|null) {
@@ -797,7 +797,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}
get color (): string|null {
return this.getFocusedTab()?.color ?? null
return this.getFocusedTab()?.color ?? this.getAllTabs()[0]?.color ?? null
}
set color (color: string|null) {

View File

@@ -6,12 +6,7 @@
.icon(*ngIf='!isDownload(transfer)') !{require('../icons/upload.svg')}
.main
label.no-wrap([title]='transfer.getName()') {{transfer.getName()}}
.status(*ngIf='transfer.isComplete()')
ngb-progressbar(type='success', [value]='100')
.status(*ngIf='transfer.isCancelled()')
ngb-progressbar(type='danger', [value]='100')
.status(*ngIf='!transfer.isComplete() && !transfer.isCancelled()')
ngb-progressbar(type='info', [value]='getProgress(transfer)')
ngb-progressbar([type]='transfer.isComplete() ? "success" : transfer.isCancelled() ? "danger" : "info"', [value]='getProgress(transfer)')
.metadata
.size {{transfer.getSize()|filesize}}
.speed(*ngIf='transfer.getSpeed()') {{transfer.getSpeed()|filesize}}/s

View File

@@ -8,6 +8,8 @@ import { DragDropModule } from '@angular/cdk/drag-drop'
import { TranslateModule, TranslateCompiler, TranslateService, MissingTranslationHandler } from '@ngx-translate/core'
import { TranslateMessageFormatCompiler, MESSAGE_FORMAT_CONFIG } from 'ngx-translate-messageformat-compiler'
import '@angular/localize/init'
import { AppRootComponent } from './components/appRoot.component'
import { CheckboxComponent } from './components/checkbox.component'
import { TabBodyComponent } from './components/tabBody.component'

View File

@@ -81,6 +81,8 @@ $modal-footer-border-color: transparent;
$form-check-input-width: 1.4em;
$form-switch-width: 2.5em;
$input-placeholder-color: var(--theme-fg-more-2);
@import '~bootstrap/scss/bootstrap.scss';
@import "./theme.vendor.scss";
@@ -171,6 +173,13 @@ body {
}
}
.dropdown-menu {
--bs-dropdown-bg: var(--theme-bg-more);
}
.progress {
--bs-progress-height: 3px;
}
tab-body {
terminal-toolbar {

View File

@@ -3,7 +3,7 @@ import { SerialPortStream } from '@serialport/stream'
import { LogService, NotificationsService } from 'tabby-core'
import { Subject, Observable } from 'rxjs'
import { Injector, NgZone } from '@angular/core'
import { BaseSession, BaseTerminalProfile, LoginScriptsOptions, SessionMiddleware, StreamProcessingOptions, TerminalStreamProcessor } from 'tabby-terminal'
import { BaseSession, BaseTerminalProfile, LoginScriptsOptions, SessionMiddleware, StreamProcessingOptions, TerminalStreamProcessor, UTF8SplitterMiddleware } from 'tabby-terminal'
import { SerialService } from './services/serial.service'
export interface SerialProfile extends BaseTerminalProfile {
@@ -64,6 +64,8 @@ export class SerialSession extends BaseSession {
this.middleware.unshift(new SlowFeedMiddleware())
}
this.middleware.push(new UTF8SplitterMiddleware())
this.setLoginScriptsOptions(profile.options)
}

View File

@@ -2,9 +2,8 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import colors from 'ansi-colors'
import { Component, Injector } from '@angular/core'
import { first } from 'rxjs'
import { GetRecoveryTokenOptions, Platform, SelectorService } from 'tabby-core'
import { BaseTerminalTabComponent, Reconnectable } from 'tabby-terminal'
import { Platform, SelectorService } from 'tabby-core'
import { BaseTerminalTabComponent, ConnectableTerminalTabComponent } from 'tabby-terminal'
import { SerialSession, BAUD_RATES, SerialProfile } from '../api'
/** @hidden */
@@ -14,7 +13,7 @@ import { SerialSession, BAUD_RATES, SerialProfile } from '../api'
styleUrls: ['./serialTab.component.scss', ...BaseTerminalTabComponent.styles],
animations: BaseTerminalTabComponent.animations,
})
export class SerialTabComponent extends BaseTerminalTabComponent<SerialProfile> implements Reconnectable {
export class SerialTabComponent extends ConnectableTerminalTabComponent<SerialProfile> {
session: SerialSession|null = null
Platform = Platform
@@ -28,8 +27,6 @@ export class SerialTabComponent extends BaseTerminalTabComponent<SerialProfile>
}
ngOnInit () {
this.logger = this.log.create('terminalTab')
this.subscribeUntilDestroyed(this.hotkeys.hotkey$, hotkey => {
if (!this.hasFocus) {
return
@@ -54,12 +51,9 @@ export class SerialTabComponent extends BaseTerminalTabComponent<SerialProfile>
})
}
protected onFrontendReady (): void {
this.initializeSession()
super.onFrontendReady()
}
async initializeSession () {
super.initializeSession()
const session = new SerialSession(this.injector, this.profile)
this.setSession(session)
@@ -82,38 +76,16 @@ export class SerialTabComponent extends BaseTerminalTabComponent<SerialProfile>
this.write(`\r\n${colors.black.bgWhite(' Serial ')} ${msg}\r\n`)
this.session?.resize(this.size.columns, this.size.rows)
})
this.attachSessionHandler(this.session!.destroyed$, () => {
if (this.frontend) {
// Session was closed abruptly
this.write('\r\n' + colors.black.bgWhite(' SERIAL ') + ` session closed\r\n`)
if (this.profile.behaviorOnSessionEnd === 'reconnect') {
this.reconnect()
} else if (this.profile.behaviorOnSessionEnd === 'keep' || this.profile.behaviorOnSessionEnd === 'auto' && !this.isSessionExplicitlyTerminated()) {
this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n')
this.input$.pipe(first()).subscribe(() => {
if (!this.session?.open) {
this.reconnect()
}
})
}
}
})
super.attachSessionHandlers()
}
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<any> {
return {
type: 'app:serial-tab',
profile: this.profile,
savedState: options?.includeState && this.frontend?.saveState(),
}
}
protected onSessionDestroyed (): void {
if (this.frontend) {
// Session was closed abruptly
this.write('\r\n' + colors.black.bgWhite(' SERIAL ') + ` session closed\r\n`)
async reconnect (): Promise<void> {
this.session?.destroy()
await this.initializeSession()
this.session?.releaseInitialDataBuffer()
super.onSessionDestroyed()
}
}
async changeBaudRate () {

View File

@@ -36,3 +36,6 @@ import { SerialProfilesService } from './profiles'
],
})
export default class SerialModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
export { SerialTabComponent }
export { SerialSession } from './api'

View File

@@ -2,9 +2,8 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import colors from 'ansi-colors'
import { Component, Injector, HostListener } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { first } from 'rxjs'
import { GetRecoveryTokenOptions, Platform, ProfilesService, RecoveryToken } from 'tabby-core'
import { BaseTerminalTabComponent, Reconnectable } from 'tabby-terminal'
import { Platform, ProfilesService } from 'tabby-core'
import { BaseTerminalTabComponent, ConnectableTerminalTabComponent } from 'tabby-terminal'
import { SSHService } from '../services/ssh.service'
import { KeyboardInteractivePrompt, SSHSession } from '../session/ssh'
import { SSHPortForwardingModalComponent } from './sshPortForwardingModal.component'
@@ -22,7 +21,7 @@ import { SSHMultiplexerService } from '../services/sshMultiplexer.service'
],
animations: BaseTerminalTabComponent.animations,
})
export class SSHTabComponent extends BaseTerminalTabComponent<SSHProfile> implements Reconnectable {
export class SSHTabComponent extends ConnectableTerminalTabComponent<SSHProfile> {
Platform = Platform
sshSession: SSHSession|null = null
session: SSHShellSession|null = null
@@ -30,7 +29,6 @@ export class SSHTabComponent extends BaseTerminalTabComponent<SSHProfile> implem
sftpPath = '/'
enableToolbar = true
activeKIPrompt: KeyboardInteractivePrompt|null = null
private reconnectOffered = false
constructor (
injector: Injector,
@@ -46,8 +44,6 @@ export class SSHTabComponent extends BaseTerminalTabComponent<SSHProfile> implem
}
ngOnInit (): void {
this.logger = this.log.create('terminalTab')
this.subscribeUntilDestroyed(this.hotkeys.hotkey$, hotkey => {
if (!this.hasFocus) {
return
@@ -73,11 +69,6 @@ export class SSHTabComponent extends BaseTerminalTabComponent<SSHProfile> implem
super.ngOnInit()
}
protected onFrontendReady (): void {
this.initializeSession()
super.onFrontendReady()
}
async setupOneSession (injector: Injector, profile: SSHProfile, multiplex = true): Promise<SSHSession> {
let session = await this.sshMultiplexer.getSession(profile)
if (!multiplex || !session || !profile.options.reuseSession) {
@@ -150,29 +141,13 @@ export class SSHTabComponent extends BaseTerminalTabComponent<SSHProfile> implem
return session
}
protected attachSessionHandlers (): void {
const session = this.session!
this.attachSessionHandler(session.destroyed$, () => {
if (this.frontend) {
// Session was closed abruptly
this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` ${this.sshSession?.profile.options.host}: session closed\r\n`)
protected onSessionDestroyed (): void {
if (this.frontend) {
// Session was closed abruptly
this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` ${this.sshSession?.profile.options.host}: session closed\r\n`)
if (this.profile.behaviorOnSessionEnd === 'reconnect') {
this.reconnect()
} else if (this.profile.behaviorOnSessionEnd === 'keep' || this.profile.behaviorOnSessionEnd === 'auto' && !this.isSessionExplicitlyTerminated()) {
if (!this.reconnectOffered) {
this.reconnectOffered = true
this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n')
this.input$.pipe(first()).subscribe(() => {
if (!this.session?.open && this.reconnectOffered) {
this.reconnect()
}
})
}
}
}
})
super.attachSessionHandlers()
super.onSessionDestroyed()
}
}
private async initializeSessionMaybeMultiplex (multiplex = true): Promise<void> {
@@ -196,7 +171,7 @@ export class SSHTabComponent extends BaseTerminalTabComponent<SSHProfile> implem
}
async initializeSession (): Promise<void> {
this.reconnectOffered = false
await super.initializeSession()
try {
await this.initializeSessionMaybeMultiplex(true)
} catch {
@@ -209,25 +184,11 @@ export class SSHTabComponent extends BaseTerminalTabComponent<SSHProfile> implem
}
}
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<RecoveryToken> {
return {
type: 'app:ssh-tab',
profile: this.profile,
savedState: options?.includeState && this.frontend?.saveState(),
}
}
showPortForwarding (): void {
const modal = this.ngbModal.open(SSHPortForwardingModalComponent).componentInstance as SSHPortForwardingModalComponent
modal.session = this.sshSession!
}
async reconnect (): Promise<void> {
this.session?.destroy()
await this.initializeSession()
this.session?.releaseInitialDataBuffer()
}
async canClose (): Promise<boolean> {
if (!this.session?.open) {
return true

View File

@@ -2,8 +2,8 @@ import { Observable, Subject } from 'rxjs'
import stripAnsi from 'strip-ansi'
import { ClientChannel } from 'ssh2'
import { Injector } from '@angular/core'
import { LogService, UTF8Splitter } from 'tabby-core'
import { BaseSession } from 'tabby-terminal'
import { LogService } from 'tabby-core'
import { BaseSession, UTF8SplitterMiddleware } from 'tabby-terminal'
import { SSHSession } from './ssh'
import { SSHProfile } from '../api'
@@ -13,7 +13,6 @@ export class SSHShellSession extends BaseSession {
get serviceMessage$ (): Observable<string> { return this.serviceMessage }
private serviceMessage = new Subject<string>()
private ssh: SSHSession|null
private decoder = new UTF8Splitter()
constructor (
injector: Injector,
@@ -24,6 +23,7 @@ export class SSHShellSession extends BaseSession {
this.ssh = ssh
this.setLoginScriptsOptions(this.profile.options)
this.ssh.serviceMessage$.subscribe(m => this.serviceMessage.next(m))
this.middleware.push(new UTF8SplitterMiddleware())
}
async start (): Promise<void> {
@@ -61,14 +61,10 @@ export class SSHShellSession extends BaseSession {
})
this.shell.on('data', data => {
this.emitOutput(this.decoder.write(data))
this.emitOutput(data)
})
this.shell.on('end', () => {
const remainder = this.decoder.flush()
if (remainder.length) {
this.emitOutput(remainder)
}
this.logger.info('Shell session ended')
if (this.open) {
this.destroy()

View File

@@ -1,9 +1,8 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import colors from 'ansi-colors'
import { Component, Injector } from '@angular/core'
import { first } from 'rxjs'
import { GetRecoveryTokenOptions, Platform, RecoveryToken } from 'tabby-core'
import { BaseTerminalTabComponent, Reconnectable } from 'tabby-terminal'
import { Platform } from 'tabby-core'
import { BaseTerminalTabComponent, ConnectableTerminalTabComponent } from 'tabby-terminal'
import { TelnetProfile, TelnetSession } from '../session'
@@ -14,10 +13,9 @@ import { TelnetProfile, TelnetSession } from '../session'
styleUrls: ['./telnetTab.component.scss', ...BaseTerminalTabComponent.styles],
animations: BaseTerminalTabComponent.animations,
})
export class TelnetTabComponent extends BaseTerminalTabComponent<TelnetProfile> implements Reconnectable {
export class TelnetTabComponent extends ConnectableTerminalTabComponent<TelnetProfile> {
Platform = Platform
session: TelnetSession|null = null
private reconnectOffered = false
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor (
@@ -28,8 +26,6 @@ export class TelnetTabComponent extends BaseTerminalTabComponent<TelnetProfile>
}
ngOnInit (): void {
this.logger = this.log.create('telnetTab')
this.subscribeUntilDestroyed(this.hotkeys.hotkey$, hotkey => {
if (this.hasFocus && hotkey === 'restart-telnet-session') {
this.reconnect()
@@ -39,38 +35,17 @@ export class TelnetTabComponent extends BaseTerminalTabComponent<TelnetProfile>
super.ngOnInit()
}
protected onFrontendReady (): void {
this.initializeSession()
super.onFrontendReady()
}
protected onSessionDestroyed (): void {
if (this.frontend) {
// Session was closed abruptly
this.write('\r\n' + colors.black.bgWhite(' TELNET ') + ` ${this.session?.profile.options.host}: session closed\r\n`)
protected attachSessionHandlers (): void {
const session = this.session!
this.attachSessionHandler(session.destroyed$, () => {
if (this.frontend) {
// Session was closed abruptly
this.write('\r\n' + colors.black.bgWhite(' TELNET ') + ` ${this.session?.profile.options.host}: session closed\r\n`)
if (this.profile.behaviorOnSessionEnd === 'reconnect') {
this.reconnect()
} else if (this.profile.behaviorOnSessionEnd === 'keep' || this.profile.behaviorOnSessionEnd === 'auto' && !this.isSessionExplicitlyTerminated()) {
if (!this.reconnectOffered) {
this.reconnectOffered = true
this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n')
this.input$.pipe(first()).subscribe(() => {
if (!this.session?.open && this.reconnectOffered) {
this.reconnect()
}
})
}
}
}
})
super.attachSessionHandlers()
super.onSessionDestroyed()
}
}
async initializeSession (): Promise<void> {
this.reconnectOffered = false
await super.initializeSession()
const session = new TelnetSession(this.injector, this.profile)
this.setSession(session)
@@ -96,20 +71,6 @@ export class TelnetTabComponent extends BaseTerminalTabComponent<TelnetProfile>
}
}
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<RecoveryToken> {
return {
type: 'app:telnet-tab',
profile: this.profile,
savedState: options?.includeState && this.frontend?.saveState(),
}
}
async reconnect (): Promise<void> {
this.session?.destroy()
await this.initializeSession()
this.session?.releaseInitialDataBuffer()
}
async canClose (): Promise<boolean> {
if (!this.session?.open) {
return true

View File

@@ -9,7 +9,7 @@ import { BaseSession } from '../session'
import { Frontend } from '../frontends/frontend'
import { XTermFrontend, XTermWebGLFrontend } from '../frontends/xtermFrontend'
import { ResizeEvent, BaseTerminalProfile, isReconnectable } from './interfaces'
import { ResizeEvent, BaseTerminalProfile } from './interfaces'
import { TerminalDecorator } from './decorator'
import { SearchPanelComponent } from '../components/searchPanel.component'
import { MultifocusService } from '../services/multifocus.service'
@@ -212,6 +212,11 @@ export class BaseTerminalTabComponent<P extends BaseTerminalProfile> extends Bas
this.showSearchPanel = true
setImmediate(() => {
const input = this.element.nativeElement.querySelector('.search-input')
const selectedText = (this.frontend?.getSelection() ?? '').trim()
if (input && selectedText.length) {
input.value = selectedText
}
input?.focus()
input?.select()
})
@@ -307,11 +312,6 @@ export class BaseTerminalTabComponent<P extends BaseTerminalProfile> extends Bas
case 'scroll-to-bottom':
this.frontend?.scrollToBottom()
break
case 'reconnect-tab':
if (isReconnectable(this)) {
this.reconnect()
}
break
}
})
@@ -779,7 +779,7 @@ export class BaseTerminalTabComponent<P extends BaseTerminalProfile> extends Bas
})
this.attachSessionHandler(this.session.destroyed$, () => {
this.setSession(null)
this.onSessionDestroyed()
})
this.attachSessionHandler(this.session.oscProcessor.copyRequested$, content => {
@@ -788,6 +788,13 @@ export class BaseTerminalTabComponent<P extends BaseTerminalProfile> extends Bas
})
}
/**
* Method called when session is destroyed. Set the session to null
*/
protected onSessionDestroyed (): void {
this.setSession(null)
}
protected detachSessionHandlers (): void {
this.sessionHandlers.cancelAll()
}

View File

@@ -0,0 +1,94 @@
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import { Injector, Component } from '@angular/core'
import { first } from 'rxjs'
import { BaseTerminalProfile } from './interfaces'
import { BaseTerminalTabComponent } from './baseTerminalTab.component'
import { GetRecoveryTokenOptions, RecoveryToken } from 'tabby-core'
/**
* A class to base your custom connectable terminal tabs on
*/
@Component({ template: '' })
export abstract class ConnectableTerminalTabComponent<P extends BaseTerminalProfile> extends BaseTerminalTabComponent<P> {
protected reconnectOffered = false
constructor (protected injector: Injector) {
super(injector)
this.subscribeUntilDestroyed(this.hotkeys.hotkey$, hotkey => {
if (this.hasFocus && hotkey === 'reconnect-tab') {
this.reconnect()
}
})
}
ngOnInit (): void {
this.logger = this.log.create(`${this.profile.type}Tab`)
super.ngOnInit()
}
protected onFrontendReady (): void {
this.initializeSession()
super.onFrontendReady()
}
/**
* Initialize Connectable Session.
* Set reconnectOffered to false
*/
async initializeSession (): Promise<void> {
this.reconnectOffered = false
}
/**
* Method called when session is destroyed. Handle the tab behavior on session end for connectable tab
*/
protected onSessionDestroyed (): void {
super.onSessionDestroyed()
if (this.frontend) {
if (this.profile.behaviorOnSessionEnd === 'reconnect') {
this.reconnect()
} else if (this.profile.behaviorOnSessionEnd === 'keep' || this.profile.behaviorOnSessionEnd === 'auto' && !this.isSessionExplicitlyTerminated()) {
this.offerReconnection()
}
}
}
/**
* Offering reconnection to the user if it hasn't been done yet.
* Set reconnectOffered to true
*/
offerReconnection (): void {
if (!this.reconnectOffered) {
this.reconnectOffered = true
this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n')
this.input$.pipe(first()).subscribe(() => {
if (!this.session?.open && this.reconnectOffered) {
this.reconnect()
}
})
}
}
async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<RecoveryToken> {
return {
type: `app:${this.profile.type}-tab`,
profile: this.profile,
savedState: options?.includeState && this.frontend?.saveState(),
}
}
async reconnect (): Promise<void> {
this.session?.destroy()
await this.initializeSession()
this.session?.releaseInitialDataBuffer()
}
}

View File

@@ -19,12 +19,3 @@ export interface TerminalColorScheme {
export interface BaseTerminalProfile extends Profile {
terminalColorScheme?: TerminalColorScheme
}
export interface Reconnectable {
reconnect: () => Promise<void>;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function isReconnectable (object: any): object is Reconnectable {
return 'reconnect' in object
}

View File

@@ -69,6 +69,7 @@ export class XTermFrontend extends Frontend {
private resizeHandler: () => void
private configuredTheme: ITheme = {}
private copyOnSelect = false
private preventNextOnSelectionChangeEvent = false
private search = new SearchAddon()
private searchState: SearchState = { resultCount: 0 }
private fitAddon = new FitAddon()
@@ -116,8 +117,11 @@ export class XTermFrontend extends Frontend {
this.title.next(title)
})
this.xterm.onSelectionChange(() => {
if (this.copyOnSelect && this.getSelection()) {
this.copySelection()
if (this.getSelection()) {
if (this.copyOnSelect && !this.preventNextOnSelectionChangeEvent) {
this.copySelection()
}
this.preventNextOnSelectionChangeEvent = false
}
})
this.xterm.onBell(() => {
@@ -444,12 +448,18 @@ export class XTermFrontend extends Frontend {
}
findNext (term: string, searchOptions?: SearchOptions): SearchState {
if (this.copyOnSelect) {
this.preventNextOnSelectionChangeEvent = true
}
return this.wrapSearchResult(
this.search.findNext(term, this.getSearchOptions(searchOptions)),
)
}
findPrevious (term: string, searchOptions?: SearchOptions): SearchState {
if (this.copyOnSelect) {
this.preventNextOnSelectionChangeEvent = true
}
return this.wrapSearchResult(
this.search.findPrevious(term, this.getSearchOptions(searchOptions)),
)

View File

@@ -90,10 +90,12 @@ export default class TerminalModule { } // eslint-disable-line @typescript-eslin
export { TerminalDecorator, TerminalContextMenuItemProvider, TerminalColorSchemeProvider }
export { Frontend, XTermFrontend, XTermWebGLFrontend }
export { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
export { ConnectableTerminalTabComponent } from './api/connectableTerminalTab.component'
export * from './api/interfaces'
export * from './middleware/streamProcessing'
export * from './middleware/loginScriptProcessing'
export * from './middleware/oscProcessing'
export * from './middleware/utf8Splitter'
export * from './api/middleware'
export * from './session'
export { LoginScriptsSettingsComponent, StreamProcessingSettingsComponent }

View File

@@ -0,0 +1,22 @@
import { UTF8Splitter } from 'tabby-core'
import { SessionMiddleware } from '../api/middleware'
/**
* Ensures that the session output is chunked at UTF8 character boundaries.
*/
export class UTF8SplitterMiddleware extends SessionMiddleware {
private decoder = new UTF8Splitter()
feedFromSession (data: Buffer): void {
super.feedFromSession(this.decoder.write(data))
}
close (): void {
const remainder = this.decoder.flush()
if (remainder.length) {
super.feedFromSession(remainder)
}
super.close()
}
}

View File

@@ -1,9 +1,9 @@
import { Injectable, Optional, Inject } from '@angular/core'
import { BaseTabComponent, TabContextMenuItemProvider, NotificationsService, MenuItemOptions, TranslateService, SplitTabComponent } from 'tabby-core'
import { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
import { isReconnectable } from './api/interfaces'
import { TerminalContextMenuItemProvider } from './api/contextMenuProvider'
import { MultifocusService } from './services/multifocus.service'
import { ConnectableTerminalTabComponent } from './api/connectableTerminalTab.component'
/** @hidden */
@Injectable()
@@ -97,7 +97,7 @@ export class ReconnectContextMenu extends TabContextMenuItemProvider {
) { super() }
async getItems (tab: BaseTabComponent): Promise<MenuItemOptions[]> {
if (isReconnectable(tab)) {
if (tab instanceof ConnectableTerminalTabComponent) {
return [
{
label: this.translate.instant('Reconnect'),

View File

@@ -138,6 +138,8 @@ Tabby.registerModule('@angular/platform-browser', require('@angular/platform-bro
Tabby.registerModule('@angular/platform-browser/animations', require('@angular/platform-browser/animations'))
Tabby.registerModule('@angular/platform-browser-dynamic', require('@angular/platform-browser-dynamic'))
Tabby.registerModule('@angular/animations', require('@angular/animations'))
Tabby.registerModule('@angular/localize', require('@angular/localize'))
Tabby.registerModule('@angular/localize/init', require('@angular/localize/init'))
Tabby.registerModule('@ng-bootstrap/ng-bootstrap', require('@ng-bootstrap/ng-bootstrap'))
Tabby.registerModule('ngx-toastr', require('ngx-toastr'))
Tabby.registerModule('deepmerge', require('deepmerge'))

View File

@@ -75,6 +75,15 @@
dependencies:
tslib "^2.3.0"
"@angular/localize@^15.2.9":
version "15.2.9"
resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-15.2.9.tgz#9fd960b5b6daab597c816b33e2f00977719c673f"
integrity sha512-7ZGK3BWwIukSK5ORWjM3y/FYj7/ZJFl1RO1GCeL/tHD4nq0kd3q3pYvcpnoi9HGl+q8AkL24xdsfzgCFo8SB0g==
dependencies:
"@babel/core" "7.19.3"
glob "8.1.0"
yargs "^17.2.1"
"@angular/platform-browser-dynamic@^15.2.6":
version "15.2.6"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-15.2.6.tgz#e3b347524b547385e90aad9bed59b5f14e6716cb"
@@ -4084,6 +4093,17 @@ glob-to-regexp@^0.4.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^5.0.1"
once "^1.3.0"
glob@^8.0.1, glob@^8.0.3:
version "8.0.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e"