mirror of
https://github.com/Eugeny/tabby.git
synced 2025-09-08 17:41:50 +00:00
Compare commits
36 Commits
all-contri
...
all-contri
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1fe961e1f1 | ||
![]() |
41bd8a6012 | ||
![]() |
5bfa72573b | ||
![]() |
facd52ff4e | ||
![]() |
8826127913 | ||
![]() |
fe58e4705c | ||
![]() |
dcba109e07 | ||
![]() |
a7e6fa7b1d | ||
![]() |
8e29b4ac97 | ||
![]() |
9379f91895 | ||
![]() |
d2a54d0d96 | ||
![]() |
fac6ec572f | ||
![]() |
539ad0bddc | ||
![]() |
a494d9c800 | ||
![]() |
5075c836f9 | ||
![]() |
1112766ac0 | ||
![]() |
b97c334660 | ||
![]() |
46a1dd39f0 | ||
![]() |
9c68274d46 | ||
![]() |
89b3f3892e | ||
![]() |
37226f66e3 | ||
![]() |
38b8702373 | ||
![]() |
bd4b11813c | ||
![]() |
38302622b8 | ||
![]() |
312b365851 | ||
![]() |
2b5976f202 | ||
![]() |
1e6f6af5ed | ||
![]() |
901181f681 | ||
![]() |
fd9505c18f | ||
![]() |
d6d273b2fb | ||
![]() |
d98ab296c7 | ||
![]() |
fe6836d996 | ||
![]() |
d3adbd03d4 | ||
![]() |
30b51e45bc | ||
![]() |
bb6ed52291 | ||
![]() |
9978e0b86f |
@@ -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,
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
]
|
||||
|
@@ -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'),
|
||||
|
@@ -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",
|
||||
|
@@ -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')}
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
@@ -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'
|
||||
|
@@ -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 {
|
||||
|
@@ -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)
|
||||
}
|
||||
|
||||
|
@@ -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 () {
|
||||
|
@@ -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'
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
}
|
||||
|
94
tabby-terminal/src/api/connectableTerminalTab.component.ts
Normal file
94
tabby-terminal/src/api/connectableTerminalTab.component.ts
Normal 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()
|
||||
}
|
||||
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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)),
|
||||
)
|
||||
|
@@ -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 }
|
||||
|
22
tabby-terminal/src/middleware/utf8Splitter.ts
Normal file
22
tabby-terminal/src/middleware/utf8Splitter.ts
Normal 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()
|
||||
}
|
||||
}
|
@@ -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'),
|
||||
|
@@ -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'))
|
||||
|
20
yarn.lock
20
yarn.lock
@@ -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"
|
||||
|
Reference in New Issue
Block a user