Compare commits

...

53 Commits

Author SHA1 Message Date
Eugene Pankov
0f6855d978 only leave a process running on macOS- fixes #4442 2021-08-19 09:35:42 +02:00
Eugene Pankov
3102f39706 Update yarn.lock 2021-08-18 23:57:58 +02:00
Eugene Pankov
46ac0a6caf Update yarn.lock 2021-08-18 23:56:23 +02:00
Eugene Pankov
68e1db040a bumped node-pty 2021-08-18 23:56:20 +02:00
Eugene Pankov
e34772b8b8 throw build errors in CI 2021-08-18 23:29:35 +02:00
Eugene Pankov
cf6558ec6a lint 2021-08-16 20:11:22 +02:00
Eugene Pankov
f5f88d3d9d fixed touchbar activity indicators 2021-08-16 20:10:34 +02:00
Eugene Pankov
64955bfcd6 fixed macos-release imports 2021-08-16 20:10:30 +02:00
Eugene Pankov
9fa9021a81 fixed xterm cursor blinker leak - fixes #4166 2021-08-16 20:10:22 +02:00
Eugene Pankov
43183401b7 dropped nested corejs dep 2021-08-16 20:10:15 +02:00
dependabot[bot]
880b9ce82b Bump macos-release from 2.5.0 to 3.0.0
Bumps [macos-release](https://github.com/sindresorhus/macos-release) from 2.5.0 to 3.0.0.
- [Release notes](https://github.com/sindresorhus/macos-release/releases)
- [Commits](https://github.com/sindresorhus/macos-release/compare/v2.5.0...v3.0.0)

---
updated-dependencies:
- dependency-name: macos-release
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-16 20:10:04 +02:00
Eugene Pankov
3584af524b telnet: added potentially missing WILL responses 2021-08-15 22:31:40 +02:00
Eugene Pankov
af174933d6 telnet: fixed size negotiation order 2021-08-15 19:58:28 +02:00
Eugene Pankov
c4490717c0 lint 2021-08-15 19:42:26 +02:00
Eugene Pankov
70b6be7301 autocomplete profile colors - fixes #4231 2021-08-15 16:16:26 +02:00
Eugene Pankov
8d5b0fe863 macOS: don't exit when the last window is closed - fixes #4263 2021-08-15 14:42:48 +02:00
Eugene Pankov
03045eb952 fixed titlebar not adjusting to macOS fullscreen mode - fixes #4274 2021-08-15 14:40:40 +02:00
Eugene Pankov
f7b0272be5 allow type aliases 2021-08-15 14:23:26 +02:00
Eugene Pankov
4fa16c8a20 fixed split attachment - fixes #4374 2021-08-15 14:13:29 +02:00
Eugene Pankov
855a7bbe14 include platform arch in github issues 2021-08-15 13:47:56 +02:00
Eugene Pankov
2b3694f517 theme tweaks 2021-08-15 13:47:46 +02:00
Eugene Pankov
101177a865 fixed jump hosts using a partial profile - fixes #3294, fixes #4307, fixes #4396 2021-08-15 13:38:11 +02:00
Eugene Pankov
8b33f98c79 reduce hotkey timeout 2021-08-15 13:25:14 +02:00
Eugene Pankov
98e52f50a9 lint 2021-08-14 23:37:42 +02:00
Eugene Pankov
7551201796 fixed recovery of custom tab titles - fixes #4406 2021-08-14 23:36:41 +02:00
Eugene Pankov
3fe2dccb94 fixed instantiating saved layouts - fixes #4413 2021-08-14 23:34:01 +02:00
Eugene Pankov
f53eb31274 theme tweaks 2021-08-14 23:27:13 +02:00
Eugene Pankov
81663f351a show icon colors in profile selector - fixes #4405 2021-08-14 23:14:14 +02:00
Eugeny
bf5d037cff Merge pull request #4385 from Eugeny/dependabot/npm_and_yarn/electron-13.1.9
Bump electron from 13.1.8 to 13.1.9
2021-08-11 10:52:47 +02:00
Eugeny
53d9af3279 Merge pull request #4386 from Eugeny/dependabot/npm_and_yarn/electron-rebuild-3.1.1
Bump electron-rebuild from 2.3.5 to 3.1.1
2021-08-11 10:52:33 +02:00
Eugeny
b7dd354313 Merge pull request #4394 from Eugeny/dependabot/npm_and_yarn/webpack-5.50.0
Bump webpack from 5.48.0 to 5.50.0
2021-08-11 10:52:14 +02:00
dependabot[bot]
d8bc9ce859 Bump webpack from 5.48.0 to 5.50.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.48.0 to 5.50.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.48.0...v5.50.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-11 04:05:45 +00:00
dependabot[bot]
1bb9358f77 Bump electron-rebuild from 2.3.5 to 3.1.1
Bumps [electron-rebuild](https://github.com/electron/electron-rebuild) from 2.3.5 to 3.1.1.
- [Release notes](https://github.com/electron/electron-rebuild/releases)
- [Changelog](https://github.com/electron/electron-rebuild/blob/master/.releaserc.json)
- [Commits](https://github.com/electron/electron-rebuild/compare/v2.3.5...v3.1.1)

---
updated-dependencies:
- dependency-name: electron-rebuild
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-10 04:05:35 +00:00
dependabot[bot]
fa77ff3995 Bump electron from 13.1.8 to 13.1.9
Bumps [electron](https://github.com/electron/electron) from 13.1.8 to 13.1.9.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v13.1.8...v13.1.9)

---
updated-dependencies:
- dependency-name: electron
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-10 04:04:55 +00:00
Eugene Pankov
1ae8d9c643 removed hterm - #4295 2021-08-08 22:06:08 +02:00
Eugene Pankov
a560f0c96e Update README.md 2021-08-08 18:04:21 +02:00
Eugene Pankov
434bacf185 fixed hotkey race condition 2021-08-08 17:54:53 +02:00
Eugene Pankov
79de7ec015 selector ui tweaks 2021-08-08 17:54:39 +02:00
Eugene Pankov
dfdb3b051b fixed hotkey IDs for profiles with "." in name - fixes #4367 2021-08-07 19:35:11 +02:00
Eugene Pankov
9fbf9136fc moved touchbar handling into main process 2021-08-07 19:34:37 +02:00
Eugene Pankov
25fdba7104 throttle global hotkey - fixes #4371 2021-08-07 10:25:49 +02:00
Eugeny
c91707e94f Merge pull request #4331 from Eugeny/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-4.29.0
Bump @typescript-eslint/eslint-plugin from 4.28.5 to 4.29.0
2021-08-06 10:12:53 +02:00
Eugeny
d665eef430 Merge pull request #4349 from Eugeny/dependabot/npm_and_yarn/app/yargs-17.1.0
Bump yargs from 17.0.1 to 17.1.0 in /app
2021-08-06 10:12:42 +02:00
Eugeny
4579e839cd Merge pull request #4350 from Eugeny/dependabot/npm_and_yarn/app/angular/cdk-12.2.0
Bump @angular/cdk from 12.1.2 to 12.2.0 in /app
2021-08-06 10:12:14 +02:00
Eugeny
6e952180ec Merge pull request #4351 from Eugeny/dependabot/npm_and_yarn/fortawesome/fontawesome-free-5.15.4
Bump @fortawesome/fontawesome-free from 5.15.3 to 5.15.4
2021-08-06 10:12:04 +02:00
Eugeny
a947254ca8 Merge pull request #4360 from Eugeny/dependabot/npm_and_yarn/graceful-fs-4.2.8
Bump graceful-fs from 4.2.6 to 4.2.8
2021-08-06 10:11:05 +02:00
Eugeny
1eb4a7fc26 Merge pull request #4361 from Eugeny/dependabot/github_actions/actions/setup-node-2.4.0
Bump actions/setup-node from 2.2.0 to 2.4.0
2021-08-06 10:10:48 +02:00
dependabot[bot]
0471fcec15 Bump actions/setup-node from 2.2.0 to 2.4.0
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 2.2.0 to 2.4.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v2.2.0...v2.4.0)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-06 04:19:59 +00:00
dependabot[bot]
4110d09dab Bump graceful-fs from 4.2.6 to 4.2.8
Bumps [graceful-fs](https://github.com/isaacs/node-graceful-fs) from 4.2.6 to 4.2.8.
- [Release notes](https://github.com/isaacs/node-graceful-fs/releases)
- [Commits](https://github.com/isaacs/node-graceful-fs/compare/v4.2.6...v4.2.8)

---
updated-dependencies:
- dependency-name: graceful-fs
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-06 04:07:47 +00:00
dependabot[bot]
144924e579 Bump @fortawesome/fontawesome-free from 5.15.3 to 5.15.4
Bumps [@fortawesome/fontawesome-free](https://github.com/FortAwesome/Font-Awesome) from 5.15.3 to 5.15.4.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/master/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/compare/5.15.3...5.15.4)

---
updated-dependencies:
- dependency-name: "@fortawesome/fontawesome-free"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-05 04:03:37 +00:00
dependabot[bot]
6902ccdb95 Bump @angular/cdk from 12.1.2 to 12.2.0 in /app
Bumps [@angular/cdk](https://github.com/angular/components) from 12.1.2 to 12.2.0.
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/master/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/12.1.2...12.2.0)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-05 04:01:54 +00:00
dependabot[bot]
7ed5aff168 Bump yargs from 17.0.1 to 17.1.0 in /app
Bumps [yargs](https://github.com/yargs/yargs) from 17.0.1 to 17.1.0.
- [Release notes](https://github.com/yargs/yargs/releases)
- [Changelog](https://github.com/yargs/yargs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/yargs/compare/v17.0.1...v17.1.0)

---
updated-dependencies:
- dependency-name: yargs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-05 04:01:42 +00:00
dependabot[bot]
acf418b52f Bump @typescript-eslint/eslint-plugin from 4.28.5 to 4.29.0
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.28.5 to 4.29.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.29.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-04 17:45:36 +00:00
62 changed files with 735 additions and 5033 deletions

View File

@@ -121,3 +121,8 @@ rules:
'@typescript-eslint/no-unsafe-argument': off
'@typescript-eslint/restrict-plus-operands': off
'@typescript-eslint/space-infix-ops': off
'@typescript-eslint/no-type-alias':
- error
- allowAliases: in-unions-and-intersections
allowLiterals: always
allowCallbacks: always

View File

@@ -11,7 +11,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.2.0
uses: actions/setup-node@v2.4.0
with:
node-version: 14
@@ -46,7 +46,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.2.0
uses: actions/setup-node@v2.4.0
with:
node-version: 14
@@ -139,7 +139,7 @@ jobs:
fetch-depth: 0
- name: Install Node
uses: actions/setup-node@v2.2.0
uses: actions/setup-node@v2.4.0
with:
node-version: 14
@@ -245,7 +245,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.2.0
uses: actions/setup-node@v2.4.0
with:
node-version: 14

View File

@@ -12,7 +12,7 @@ jobs:
fetch-depth: 0
- name: Installing Node
uses: actions/setup-node@v2.2.0
uses: actions/setup-node@v2.4.0
with:
node-version: 14

View File

@@ -103,10 +103,10 @@ Tabby will run as a portable app on Windows, if you create a `data` folder in th
Plugins and themes can be installed directly from the Settings view inside Tabby.
* [clickable-links](https://github.com/Eugeny/tabby-clickable-links) - makes paths and URLs in the terminal clickable
* [docker](https://github.com/Eugeny/tabby-docker) - connect to Docker containers
* [title-control](https://github.com/kbjr/terminus-title-control) - allows modifying the title of the terminal tabs by providing a prefix, suffix, and/or strings to be removed
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - quickly send commands to one or all terminal tabs
* [save-output](https://github.com/Eugeny/tabby-save-output) - record terminal output into a file
* [scrollbar](https://github.com/kbjr/terminus-scrollbar) - adds a scrollbar to hterm tabs
* [sync-config](https://github.com/starxg/terminus-sync-config) - sync the config to Gist or Gitee
<a name="themes"></a>

View File

@@ -3,6 +3,7 @@ import * as promiseIpc from 'electron-promise-ipc'
import * as remote from '@electron/remote/main'
import * as path from 'path'
import * as fs from 'fs'
import { Subject, throttleTime } from 'rxjs'
import { loadConfig } from './config'
import { Window, WindowOptions } from './window'
@@ -19,6 +20,7 @@ export class Application {
private tray?: Tray
private ptyManager = new PTYManager()
private windows: Window[] = []
private globalHotkey$ = new Subject<void>()
userPluginsPath: string
constructor () {
@@ -33,12 +35,14 @@ export class Application {
ipcMain.on('app:register-global-hotkey', (_event, specs) => {
globalShortcut.unregisterAll()
for (const spec of specs) {
globalShortcut.register(spec, () => {
this.onGlobalHotkey()
})
globalShortcut.register(spec, () => this.globalHotkey$.next())
}
})
this.globalHotkey$.pipe(throttleTime(100)).subscribe(() => {
this.onGlobalHotkey()
})
;(promiseIpc as any).on('plugin-manager:install', (name, version) => {
return pluginManager.install(this.userPluginsPath, name, version)
})

View File

@@ -27,7 +27,9 @@ app.on('activate', () => {
})
app.on('window-all-closed', () => {
app.quit()
if (process.platform !== 'darwin') {
app.quit()
}
})
process.on('uncaughtException' as any, err => {

View File

@@ -1,4 +1,4 @@
import * as nodePTY from 'node-pty'
import * as nodePTY from '@tabby-gang/node-pty'
import { StringDecoder } from './stringDecoder'
import { v4 as uuidv4 } from 'uuid'
import { ipcMain } from 'electron'

View File

@@ -1,7 +1,7 @@
import * as glasstron from 'glasstron'
import { Subject, Observable, debounceTime } from 'rxjs'
import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen, BrowserWindowConstructorOptions } from 'electron'
import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen, BrowserWindowConstructorOptions, TouchBar, nativeImage } from 'electron'
import ElectronConfig = require('electron-config')
import * as os from 'os'
import * as path from 'path'
@@ -28,6 +28,8 @@ abstract class GlasstronWindow extends BrowserWindow {
const macOSVibrancyType = process.platform === 'darwin' ? compareVersions.compare(macOSRelease().version, '10.14', '>=') ? 'fullscreen-ui' : 'dark' : null
const activityIcon = nativeImage.createFromPath(`${app.getAppPath()}/assets/activity.png`)
export class Window {
ready: Promise<void>
private visible = new Subject<boolean>()
@@ -39,6 +41,7 @@ export class Window {
private lastVibrancy: { enabled: boolean, type?: string } | null = null
private disableVibrancyWhileDragging = false
private configStore: any
private touchBarControl: any
get visible$ (): Observable<boolean> { return this.visible }
get closed$ (): Observable<void> { return this.closed }
@@ -127,7 +130,15 @@ export class Window {
this.window.webContents.setVisualZoomLevelLimits(1, 1)
this.window.webContents.setZoomFactor(1)
if (process.platform !== 'darwin') {
if (process.platform === 'darwin') {
this.touchBarControl = new TouchBar.TouchBarSegmentedControl({
segments: [],
change: index => this.send('touchbar-selection', index),
})
this.window.setTouchBar(new TouchBar({
items: [this.touchBarControl],
}))
} else {
this.window.setMenu(null)
}
@@ -357,6 +368,14 @@ export class Window {
this.window.close()
})
ipcMain.on('window-set-touch-bar', (_event, segments, selectedIndex) => {
this.touchBarControl.segments = segments.map(s => ({
label: s.label,
icon: s.hasActivity ? activityIcon : undefined,
}))
this.touchBarControl.selectedIndex = selectedIndex
})
this.window.webContents.on('new-window', event => event.preventDefault())
ipcMain.on('window-set-disable-vibrancy-while-dragging', (_event, value) => {

View File

@@ -14,7 +14,7 @@
"watch": "webpack --progress --color --watch"
},
"dependencies": {
"@angular/cdk": "^12.1.2",
"@angular/cdk": "^12.2.0",
"@electron/remote": "1.2.0",
"any-promise": "^1.3.0",
"electron-config": "2.0.0",
@@ -26,12 +26,12 @@
"keytar": "^7.7.0",
"mz": "^2.7.0",
"native-process-working-directory": "^1.0.2",
"node-pty": "^0.10.1",
"@tabby-gang/node-pty": "^0.11.0-beta.200",
"npm": "6",
"rxjs": "^7.2.0",
"source-map-support": "^0.5.19",
"v8-compile-cache": "^2.3.0",
"yargs": "^17.0.1"
"yargs": "^17.1.0"
},
"optionalDependencies": {
"macos-native-processlist": "^2.0.0",

View File

@@ -58,7 +58,7 @@ nodeModule.prototype.require = function (query: string) {
return originalModuleRequire.call(this, query)
}
export type ProgressCallback = (current: number, total: number) => void // eslint-disable-line @typescript-eslint/no-type-alias
export type ProgressCallback = (current: number, total: number) => void
export function initModuleLookup (userPluginsPath: string): void {
global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x)))

View File

@@ -44,7 +44,8 @@ module.exports = {
glasstron: 'commonjs glasstron',
mz: 'commonjs mz',
npm: 'commonjs npm',
'node-pty': 'commonjs node-pty',
'node:os': 'commonjs os',
'@tabby-gang/node-pty': 'commonjs @tabby-gang/node-pty',
path: 'commonjs path',
util: 'commonjs util',
'source-map-support': 'commonjs source-map-support',

View File

@@ -2,10 +2,10 @@
# yarn lockfile v1
"@angular/cdk@^12.1.2":
version "12.1.2"
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-12.1.2.tgz#5c2407324d860737374d873bd4381bf7f90f8a61"
integrity sha512-ALupZejZDsVYcbNZcEH1cV8SDgVBL40FAwDnlSZxCgd0HOBHH0ZqQV+8z0uCQeMatoNM+SwmJ8Y1JXYh9Bqfiw==
"@angular/cdk@^12.2.0":
version "12.2.0"
resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-12.2.0.tgz#7c6de53522ef7cf911d86e187f3df2a90e8fee49"
integrity sha512-Dts+KIMz6EdzQxaWBFcNwgWAHVPkI5pnOGMidKKVOmjezSUN6mhfBKq8emgsddJMRAqz/1VHMAEaRkp0VoBKiA==
dependencies:
tslib "^2.2.0"
optionalDependencies:
@@ -96,6 +96,13 @@
dependencies:
debug "^4.3.1"
"@tabby-gang/node-pty@^0.11.0-beta.200":
version "0.11.0-beta.200"
resolved "https://registry.yarnpkg.com/@tabby-gang/node-pty/-/node-pty-0.11.0-beta.200.tgz#485cd6d85a04f4b272b81a9862578d7fc38cdfb5"
integrity sha512-32ANParjnd38SzvICaLYvEBlTZAE2sqsgEZPK6ITgd38FcCsS/yvvsDZcjkclbxApnMM2rJDaYjsZMa0lr9Iyg==
dependencies:
nan "^2.14.0"
"@types/mz@2.7.4":
version "2.7.4"
resolved "https://registry.yarnpkg.com/@types/mz/-/mz-2.7.4.tgz#f9d1535cb5171199b28ae6abd6ec29e856551401"
@@ -2095,13 +2102,6 @@ node-gyp@^5.0.2, node-gyp@^5.1.0:
tar "^4.4.12"
which "^1.3.1"
node-pty@^0.10.1:
version "0.10.1"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.1.tgz#cd05d03a2710315ec40221232ec04186f6ac2c6d"
integrity sha512-JTdtUS0Im/yRsWJSx7yiW9rtpfmxqxolrtnyKwPLI+6XqTAPW/O2MjS8FYL4I5TsMbH2lVgDb2VMjp+9LoQGNg==
dependencies:
nan "^2.14.0"
noop-logger@^0.1.1:
version "0.1.1"
resolved "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz"
@@ -3408,12 +3408,7 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tslib@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
tslib@^2.2.0:
tslib@^2.0.0, tslib@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
@@ -3750,10 +3745,10 @@ yargs@^14.2.3:
y18n "^4.0.0"
yargs-parser "^15.0.1"
yargs@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb"
integrity sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==
yargs@^17.1.0:
version "17.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.1.0.tgz#0cd9827a0572c9a1795361c4d1530e53ada168cf"
integrity sha512-SQr7qqmQ2sNijjJGHL4u7t8vyDZdZ3Ahkmo4sc1w5xI9TBX0QDdG/g4SFnxtWOsGLjwHQue57eFALfwFCnixgg==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"

View File

@@ -7,7 +7,7 @@
"@angular/forms": "^12.0.0",
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.0.0",
"@fortawesome/fontawesome-free": "^5.15.3",
"@fortawesome/fontawesome-free": "^5.15.4",
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
"@sentry/cli": "^1.67.2",
"@sentry/electron": "^2.5.1",
@@ -19,7 +19,7 @@
"@types/node": "16.0.1",
"@types/sortablejs": "^1.10.7",
"@types/webpack-env": "^1.16.2",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.28.5",
"apply-loader": "2.0.0",
"axios": "^0.21.1",
@@ -28,20 +28,20 @@
"core-js": "^3.15.2",
"cross-env": "7.0.3",
"css-loader": "^6.2.0",
"electron": "13.1.8",
"electron": "13.1.9",
"electron-builder": "22.10.5",
"electron-download": "^4.1.1",
"electron-installer-snap": "^5.1.0",
"electron-notarize": "^1.0.1",
"electron-rebuild": "^2.3.5",
"electron-rebuild": "^3.1.1",
"eslint": "^7.32.0",
"eslint-plugin-import": "^2.23.4",
"file-loader": "^6.2.0",
"graceful-fs": "^4.2.6",
"graceful-fs": "^4.2.8",
"html-loader": "2.1.2",
"json-loader": "0.5.7",
"lru-cache": "^6.0.0",
"macos-release": "^2.5.0",
"macos-release": "^3.0.0",
"ngx-sortablejs": "^11.1.0",
"ngx-toastr": "^14.0.0",
"node-abi": "^2.30.0",
@@ -71,7 +71,7 @@
"typedoc": "^0.21.5",
"typescript": "^4.3.5",
"val-loader": "4.0.0",
"webpack": "^5.48.0",
"webpack": "^5.50.0",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.7.0",
"yaml-loader": "0.6.0",

View File

@@ -13,6 +13,10 @@ const configs = [
;(async () => {
for (const c of configs) {
log.info('build', c)
await promisify(webpack)(require(c))
const stats = await promisify(webpack)(require(c))
console.log(stats.toString({ colors: true }))
if (stats.hasErrors()) {
process.exit(1)
}
}
})()

View File

@@ -20,7 +20,7 @@ sh.cd('web')
sh.exec(`${npx} yarn install --force`)
sh.cd('..')
vars.builtinPlugins.forEach(plugin => {
vars.allPackages.forEach(plugin => {
log.info('deps', plugin)
sh.cd(plugin)
sh.exec(`${npx} yarn install --force`)

View File

@@ -9,7 +9,7 @@ sh.exec(`${sentryCli} releases new ${vars.version}`)
if (process.platform === 'darwin') {
for (const path of [
'app/node_modules/@serialport/bindings/build/Release/bindings.node',
'app/node_modules/node-pty/build/Release/pty.node',
'app/node_modules/@tabby-gang/node-pty/build/Release/pty.node',
'app/node_modules/fontmanager-redux/build/Release/fontmanager.node',
'app/node_modules/macos-native-processlist/build/Release/native.node',
]) {

View File

@@ -19,7 +19,6 @@
"devDependencies": {
"@types/js-yaml": "^4.0.0",
"bootstrap": "^4.1.3",
"core-js": "^3.1.2",
"deep-equal": "^2.0.5",
"deepmerge": "^4.1.1",
"electron-updater": "^4.0.6",

View File

@@ -4,5 +4,6 @@ export interface SelectorOption<T> {
result?: T
icon?: string
freeInputPattern?: string
color?: string
callback?: (string?) => void
}

View File

@@ -1,6 +1,6 @@
title-bar(
*ngIf='ready && !hostWindow.isFullScreen && config.store.appearance.frame == "full" && config.store.appearance.dock == "off"',
[class.inset]='hostApp.platform == Platform.macOS && !hostWindow.isFullScreen'
*ngIf='ready && !hostWindow.isFullscreen && config.store.appearance.frame == "full" && config.store.appearance.dock == "off"',
[class.inset]='hostApp.platform == Platform.macOS && !hostWindow.isFullscreen'
)
.content(
@@ -10,7 +10,7 @@ title-bar(
)
.tab-bar
.inset.background(*ngIf='hostApp.platform == Platform.macOS \
&& !hostWindow.isFullScreen \
&& !hostWindow.isFullscreen \
&& config.store.appearance.frame == "thin" \
&& (config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left")')
.tabs(

View File

@@ -7,7 +7,7 @@
(ngModelChange)='onFilterChange()'
)
.list-group(*ngIf='filteredOptions.length')
.list-group.list-group-light(*ngIf='filteredOptions.length')
a.list-group-item.list-group-item-action.d-flex.align-items-center(
#item,
(click)='selectOption(option)',
@@ -16,11 +16,13 @@
)
i.icon(
class='fa-fw {{option.icon}}',
style='color: {{option.color}}',
*ngIf='!iconIsSVG(option.icon)'
)
.icon(
[fastHtmlBind]='option.icon',
style='color: {{option.color}}',
*ngIf='iconIsSVG(option.icon)'
)
.mr-2.title {{getOptionText(option)}}
.text-muted {{option.description}}
.title.mr-2 {{getOptionText(option)}}
.description.no-wrap.text-muted {{option.description}}

View File

@@ -16,6 +16,11 @@
.title {
margin-left: 10px;
flex: none;
}
.description {
flex: 1 1 0;
}
input {

View File

@@ -6,8 +6,8 @@ import { TabsService, NewTabParameters } from '../services/tabs.service'
import { HotkeysService } from '../services/hotkeys.service'
import { TabRecoveryService } from '../services/tabRecovery.service'
export type SplitOrientation = 'v' | 'h' // eslint-disable-line @typescript-eslint/no-type-alias
export type SplitDirection = 'r' | 't' | 'b' | 'l' // eslint-disable-line @typescript-eslint/no-type-alias
export type SplitOrientation = 'v' | 'h'
export type SplitDirection = 'r' | 't' | 'b' | 'l'
/**
* Describes a horizontal or vertical split row or column
@@ -126,16 +126,21 @@ export interface SplitSpannerInfo {
/**
* Represents a tab drop zone
*/
export interface SplitDropZoneInfo {
container?: SplitContainer
position?: number
relativeTo?: BaseTabComponent|SplitContainer
side?: SplitDirection
export type SplitDropZoneInfo = {
x: number
y: number
w: number
h: number
}
} & ({
type: 'absolute'
container: SplitContainer
position: number
} | {
type: 'relative'
relativeTo?: BaseTabComponent|SplitContainer
side: SplitDirection
})
/**
* Split tab is a tab that contains other tabs and allows further splitting them
@@ -585,10 +590,10 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
return
}
if (zone.container) {
this.add(tab, zone.container.children[zone.position!], zone.container.orientation === 'h' ? 'r' : 'b')
if (zone.type === 'relative') {
this.add(tab, zone.relativeTo ?? null, zone.side)
} else {
this.add(tab, null, zone.side!)
this.add(tab, zone.container.children[zone.position], zone.container.orientation === 'h' ? 'r' : 'b')
}
this.tabAdopted.next(tab)
}
@@ -649,6 +654,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
y: y + thickness,
w: thickness,
h: h - thickness * 2,
type: 'relative',
side: 'l',
})
this._dropZones.push({
@@ -656,6 +662,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
y: y - thickness / 2,
w,
h: thickness,
type: 'relative',
side: 't',
})
this._dropZones.push({
@@ -663,6 +670,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
y: y + thickness,
w: thickness,
h: h - thickness * 2,
type: 'relative',
side: 'r',
})
this._dropZones.push({
@@ -670,6 +678,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
y: y + h - thickness / 2,
w,
h: thickness,
type: 'relative',
side: 'b',
})
}
@@ -714,6 +723,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
if (i !== root.ratios.length - 1) {
// Spanner area
this._dropZones.push({
type: 'relative',
relativeTo: root.children[i],
side: root.orientation === 'v' ? 'b': 'r',
x: root.orientation === 'v' ? childX + thickness : childX + offset - thickness / 2,
@@ -730,6 +740,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
y: childY + thickness,
w: thickness,
h: childH - thickness * 2,
type: 'relative',
relativeTo: child,
side: 'l',
})
@@ -738,6 +749,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
y: childY + thickness,
w: thickness,
h: childH - thickness * 2,
type: 'relative',
relativeTo: child,
side: 'r',
})
@@ -747,6 +759,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
y: childY,
w: childW - thickness * 2,
h: thickness,
type: 'relative',
relativeTo: child,
side: 't',
})
@@ -755,6 +768,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
y: childY + childH - thickness,
w: childW - thickness * 2,
h: thickness,
type: 'relative',
relativeTo: child,
side: 'b',
})

View File

@@ -34,7 +34,7 @@ export class SplitTabDropZoneComponent extends SelfPositioningComponent {
) {
super(element)
this.subscribeUntilDestroyed(app.tabDragActive$, tab => {
this.isActive = !!tab && tab !== this.parent && tab !== this.dropZone.container?.children[this.dropZone.position!]
this.isActive = !!tab && tab !== this.parent && (this.dropZone.type === 'relative' || tab !== this.dropZone.container.children[this.dropZone.position])
this.layout()
})
}

View File

@@ -3,7 +3,7 @@ div
h1.tabby-title Tabby
sup α
.list-group
.list-group.list-group-light
a.list-group-item.list-group-item-action.d-flex(
*ngFor='let button of getButtons(); trackBy: buttonsTrackBy',
(click)='button.click()',
@@ -13,10 +13,10 @@ div
footer.d-flex.align-items-center
.btn-group.mr-auto
button.btn.btn-secondary((click)='homeBase.openGitHub()')
button.btn.btn-dark((click)='homeBase.openGitHub()')
i.fab.fa-github
span GitHub
button.btn.btn-secondary((click)='homeBase.reportBug()')
button.btn.btn-dark((click)='homeBase.reportBug()')
i.fas.fa-bug
span Report a problem

View File

@@ -60,6 +60,7 @@ export class TabHeaderComponent extends BaseComponent {
modal.result.then(result => {
this.tab.setTitle(result)
this.tab.customTitle = result
this.app.emitTabsChanged()
}).catch(() => null)
}

View File

@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core'
import { ProfilesService } from './services/profiles.service'
import { HotkeyDescription, HotkeyProvider } from './api/hotkeyProvider'
import { PartialProfile, Profile } from './api'
/** @hidden */
@Injectable()
@@ -193,9 +194,13 @@ export class AppHotkeyProvider extends HotkeyProvider {
return [
...this.hotkeys,
...profiles.map(profile => ({
id: `profile.${profile.id}`,
id: `profile.${AppHotkeyProvider.getProfileHotkeyName(profile)}`,
name: `New tab: ${profile.name}`,
})),
]
}
static getProfileHotkeyName (profile: PartialProfile<Profile>): string {
return profile.id!.replace(/\./g, '-')
}
}

View File

@@ -151,8 +151,9 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
hotkeys.hotkey$.subscribe(async (hotkey) => {
if (hotkey.startsWith('profile.')) {
const id = hotkey.split('.')[1]
const profile = (await profilesService.getProfiles()).find(x => x.id === id)
const id = hotkey.substring(hotkey.indexOf('.') + 1)
const profiles = await profilesService.getProfiles()
const profile = profiles.find(x => AppHotkeyProvider.getProfileHotkeyName(x) === id)
if (profile) {
profilesService.openNewTabForProfile(profile)
}

View File

@@ -174,6 +174,9 @@ export class AppService {
* @param inputs Properties to be assigned on the new tab component instance
*/
openNewTab <T extends BaseTabComponent> (params: NewTabParameters<T>): T {
if (params.type as any === SplitTabComponent) {
return this.openNewTabRaw(params)
}
const splitTab = this.tabsService.create({ type: SplitTabComponent })
const tab = this.tabsService.create(params)
splitTab.addTab(tab, null, 'r')

View File

@@ -2,7 +2,7 @@ import { Injectable, Inject } from '@angular/core'
import * as mixpanel from 'mixpanel'
import { v4 as uuidv4 } from 'uuid'
import { ConfigService } from './config.service'
import { PlatformService, BOOTSTRAP_DATA, BootstrapData } from '../api'
import { PlatformService, BOOTSTRAP_DATA, BootstrapData, HostAppService } from '../api'
@Injectable({ providedIn: 'root' })
export class HomeBaseService {
@@ -13,6 +13,7 @@ export class HomeBaseService {
private constructor (
private config: ConfigService,
private platform: PlatformService,
private hostApp: HostAppService,
@Inject(BOOTSTRAP_DATA) private bootstrapData: BootstrapData,
) {
this.appVersion = platform.getAppVersion()
@@ -28,20 +29,11 @@ export class HomeBaseService {
reportBug (): void {
let body = `Version: ${this.appVersion}\n`
body += `Platform: ${process.platform} ${this.platform.getOSRelease()}\n`
const label = {
aix: 'OS: IBM AIX',
android: 'OS: Android',
darwin: 'OS: macOS',
freebsd: 'OS: FreeBSD',
linux: 'OS: Linux',
openbsd: 'OS: OpenBSD',
sunos: 'OS: Solaris',
win32: 'OS: Windows',
}[process.platform]
body += `Platform: ${this.hostApp.platform} ${process.arch} ${this.platform.getOSRelease()}\n`
const plugins = this.bootstrapData.installedPlugins.filter(x => !x.isBuiltin).map(x => x.name)
body += `Plugins: ${plugins.join(', ') || 'none'}\n\n`
this.platform.openExternal(`https://github.com/Eugeny/tabby/issues/new?body=${encodeURIComponent(body)}&labels=${label}`)
body += `Plugins: ${plugins.join(', ') || 'none'}\n`
body += `Frontend: ${this.config.store.terminal?.frontend}\n\n`
this.platform.openExternal(`https://github.com/Eugeny/tabby/issues/new?body=${encodeURIComponent(body)}`)
}
enableAnalytics (): void {

View File

@@ -72,20 +72,21 @@ export class HotkeysService {
@Inject(HotkeyProvider) private hotkeyProviders: HotkeyProvider[],
hostApp: HostAppService,
) {
const events = ['keydown', 'keyup']
events.forEach(eventType => {
document.addEventListener(eventType, (nativeEvent: KeyboardEvent) => {
this._keyEvent.next(nativeEvent)
this.pushKeyEvent(eventType, nativeEvent)
if (hostApp.platform === Platform.Web && this.matchActiveHotkey(true) !== null) {
nativeEvent.preventDefault()
nativeEvent.stopPropagation()
}
})
})
this.config.ready$.toPromise().then(async () => {
const hotkeys = await this.getHotkeyDescriptions()
this.hotkeyDescriptions = hotkeys
const events = ['keydown', 'keyup']
events.forEach(eventType => {
document.addEventListener(eventType, (nativeEvent: KeyboardEvent) => {
this._keyEvent.next(nativeEvent)
this.pushKeyEvent(eventType, nativeEvent)
if (hostApp.platform === Platform.Web && this.matchActiveHotkey(true) !== null) {
nativeEvent.preventDefault()
nativeEvent.stopPropagation()
}
})
})
})
// deprecated
@@ -121,7 +122,7 @@ export class HotkeysService {
}
for (const [key, time] of this.pressedKeyTimestamps.entries()) {
if (time < performance.now() - 5000) {
if (time < performance.now() - 2000) {
this.removePressedKey(key)
}
}

View File

@@ -84,8 +84,9 @@ export class ProfilesService {
selectorOptionForProfile <P extends Profile, T> (profile: PartialProfile<P>): SelectorOption<T> {
const fullProfile = this.getConfigProxyForProfile(profile)
return {
icon: profile.icon,
name: profile.group ? `${fullProfile.group} / ${fullProfile.name}` : fullProfile.name,
icon: profile.icon,
color: profile.color,
description: this.providerForProfile(fullProfile)?.getDescription(fullProfile),
}
}
@@ -99,6 +100,7 @@ export class ProfilesService {
let options: SelectorOption<void>[] = recentProfiles.map(p => ({
...this.selectorOptionForProfile(p),
icon: 'fas fa-history',
color: p.color,
callback: async () => {
if (p.id) {
p = (await this.getProfiles()).find(x => x.id === p.id) ?? p

View File

@@ -34,6 +34,7 @@ export class TabRecoveryService {
const token = await tab.getRecoveryToken()
if (token) {
token.tabTitle = tab.title
token.tabCustomTitle = tab.customTitle
if (tab.color) {
token.tabColor = tab.color
}
@@ -55,6 +56,7 @@ export class TabRecoveryService {
tab.inputs = tab.inputs ?? {}
tab.inputs.color = token.tabColor ?? null
tab.inputs.title = token.tabTitle || ''
tab.inputs.customTitle = token.tabCustomTitle || ''
tab.inputs.disableDynamicTitle = token.disableDynamicTitle
return tab
} catch (error) {

View File

@@ -2,7 +2,6 @@ import { Injectable, ComponentFactoryResolver, Injector } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component'
import { TabRecoveryService } from './tabRecovery.service'
// eslint-disable-next-line @typescript-eslint/no-type-alias
export interface TabComponentType<T extends BaseTabComponent> {
// eslint-disable-next-line @typescript-eslint/prefer-function-type
new (...args: any[]): T

View File

@@ -13,6 +13,7 @@ import { TabsService } from './services/tabs.service'
import { HotkeysService } from './services/hotkeys.service'
import { PromptModalComponent } from './components/promptModal.component'
import { SplitLayoutProfilesService } from './profiles'
import { TAB_COLORS } from './utils'
/** @hidden */
@Injectable()
@@ -89,16 +90,6 @@ export class TabManagementContextMenu extends TabContextMenuItemProvider {
}
}
const COLORS = [
{ name: 'No color', value: null },
{ name: 'Blue', value: '#0275d8' },
{ name: 'Green', value: '#5cb85c' },
{ name: 'Orange', value: '#f0ad4e' },
{ name: 'Purple', value: '#613d7c' },
{ name: 'Red', value: '#d9534f' },
{ name: 'Yellow', value: '#ffd500' },
]
/** @hidden */
@Injectable()
export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
@@ -127,8 +118,8 @@ export class CommonOptionsContextMenu extends TabContextMenuItemProvider {
},
{
label: 'Color',
sublabel: COLORS.find(x => x.value === tab.color)?.name,
submenu: COLORS.map(color => ({
sublabel: TAB_COLORS.find(x => x.value === tab.color)?.name,
submenu: TAB_COLORS.map(color => ({
label: color.name,
type: 'radio',
checked: tab.color === color.value,

View File

@@ -230,26 +230,25 @@ hotkey-input-modal {
}
}
.list-group.list-group-flush .list-group-item:not(.list-group-item-action) {
.list-group.list-group-flush .list-group-item {
background: transparent;
border-color: rgba(0, 0, 0, 0.2);
&:not(:last-child) {
border-bottom: none;
}
&.list-group-item-action {
&:hover, &.active {
background: $list-group-hover-bg;
}
}
}
.list-group-light {
.list-group-item {
background: transparent;
border: none;
border-top: 1px solid rgba(0, 21, 43, .4);
&:first-child {
border-top: none;
}
&.list-group-item-action {
&:hover, &.active {
background: $list-group-hover-bg;

View File

@@ -26,8 +26,8 @@ $purple: #613d7c !default;
$content-bg: rgba(39, 49, 60, 0.65); //#1D272D;
$content-bg-solid: #1D272D;
$table-bg: rgba(255,255,255,.05);
$table-bg-hover: rgba(255,255,255,.1);
$table-bg: rgba(255,255,255,.025);
$table-bg-hover: rgba(255,255,255,.05);
$table-border-color: rgba(255,255,255,.1);
$theme-colors: (
@@ -88,7 +88,7 @@ $list-group-item-padding-y: 0.8rem;
$list-group-item-padding-x: 1rem;
$list-group-hover-bg: $table-bg-hover;
$list-group-active-bg: rgba(255,255,255,.2);
$list-group-active-bg: rgba(255,255,255,.05);
$list-group-active-color: $component-active-color;
$list-group-active-border-color: translate;

View File

@@ -54,3 +54,13 @@ export class ResettableTimeout {
}
}
}
export const TAB_COLORS = [
{ name: 'No color', value: null },
{ name: 'Blue', value: '#0275d8' },
{ name: 'Green', value: '#5cb85c' },
{ name: 'Orange', value: '#f0ad4e' },
{ name: 'Purple', value: '#613d7c' },
{ name: 'Red', value: '#d9534f' },
{ name: 'Yellow', value: '#ffd500' },
]

View File

@@ -50,11 +50,6 @@ call-bind@^1.0.0, call-bind@^1.0.2:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
core-js@^3.1.2:
version "3.15.2"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61"
integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==
debug@4:
version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { App, IpcRenderer, Shell, Dialog, Clipboard, GlobalShortcut, Screen, Remote, AutoUpdater, TouchBar, BrowserWindow, Menu, MenuItem, NativeImage, PowerSaveBlocker } from 'electron'
import { App, IpcRenderer, Shell, Dialog, Clipboard, GlobalShortcut, Screen, Remote, AutoUpdater, TouchBar, BrowserWindow, Menu, MenuItem, PowerSaveBlocker } from 'electron'
import * as remote from '@electron/remote'
export interface MessageBoxResponse {
@@ -15,7 +15,6 @@ export class ElectronService {
dialog: Dialog
clipboard: Clipboard
globalShortcut: GlobalShortcut
nativeImage: typeof NativeImage
screen: Screen
remote: Remote
process: any
@@ -38,7 +37,6 @@ export class ElectronService {
this.screen = remote.screen
this.dialog = remote.dialog
this.globalShortcut = remote.globalShortcut
this.nativeImage = remote.nativeImage
this.autoUpdater = remote.autoUpdater
this.powerSaveBlocker = remote.powerSaveBlocker
this.TouchBar = remote.TouchBar

View File

@@ -12,9 +12,9 @@ export interface Bounds {
@Injectable({ providedIn: 'root' })
export class ElectronHostWindow extends HostWindowService {
get isFullscreen (): boolean { return this._isFullScreen}
get isFullscreen (): boolean { return this._isFullscreen }
private _isFullScreen = false
private _isFullscreen = false
constructor (
zone: NgZone,
@@ -23,28 +23,26 @@ export class ElectronHostWindow extends HostWindowService {
) {
super()
electron.ipcRenderer.on('host:window-enter-full-screen', () => zone.run(() => {
this._isFullScreen = true
this._isFullscreen = true
}))
electron.ipcRenderer.on('host:window-leave-full-screen', () => zone.run(() => {
this._isFullScreen = false
this._isFullscreen = false
}))
electron.ipcRenderer.on('host:window-shown', () => {
zone.run(() => this.windowShown.next())
})
electron.ipcRenderer.on('host:window-shown', () => zone.run(() => this.windowShown.next()))
electron.ipcRenderer.on('host:window-close-request', () => {
zone.run(() => this.windowCloseRequest.next())
})
electron.ipcRenderer.on('host:window-close-request', () => zone.run(() => {
this.windowCloseRequest.next()
}))
electron.ipcRenderer.on('host:window-moved', () => {
zone.run(() => this.windowMoved.next())
})
electron.ipcRenderer.on('host:window-moved', () => zone.run(() => {
this.windowMoved.next()
}))
electron.ipcRenderer.on('host:window-focused', () => {
zone.run(() => this.windowFocused.next())
})
electron.ipcRenderer.on('host:window-focused', () => zone.run(() => {
this.windowFocused.next()
}))
}
getWindow (): BrowserWindow {
@@ -64,7 +62,7 @@ export class ElectronHostWindow extends HostWindowService {
}
toggleFullscreen (): void {
this.getWindow().setFullScreen(!this._isFullScreen)
this.getWindow().setFullScreen(!this._isFullscreen)
}
minimize (): void {

View File

@@ -1,56 +1,29 @@
import { SegmentedControlSegment, TouchBarSegmentedControl } from 'electron'
import { ipcRenderer } from 'electron'
import { Injectable, NgZone } from '@angular/core'
import { AppService, HostAppService, Platform } from 'tabby-core'
import { ElectronService } from '../services/electron.service'
import { ElectronHostWindow } from './hostWindow.service'
/** @hidden */
@Injectable({ providedIn: 'root' })
export class TouchbarService {
private tabsSegmentedControl: TouchBarSegmentedControl
private tabSegments: SegmentedControlSegment[] = []
private constructor (
private app: AppService,
private hostApp: HostAppService,
private hostWindow: ElectronHostWindow,
private electron: ElectronService,
private zone: NgZone,
) {
if (this.hostApp.platform !== Platform.macOS) {
return
}
app.tabsChanged$.subscribe(() => this.updateTabs())
app.activeTabChange$.subscribe(() => this.updateTabs())
app.tabsChanged$.subscribe(() => this.update())
app.activeTabChange$.subscribe(() => this.update())
const activityIconPath = `${electron.app.getAppPath()}/assets/activity.png`
const activityIcon = this.electron.nativeImage.createFromPath(activityIconPath)
app.tabOpened$.subscribe(tab => {
tab.titleChange$.subscribe(title => {
const segment = this.tabSegments[app.tabs.indexOf(tab)]
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (segment) {
segment.label = this.shortenTitle(title)
this.tabsSegmentedControl.segments = this.tabSegments
}
})
tab.activity$.subscribe(hasActivity => {
const showIcon = this.app.activeTab !== tab && hasActivity
const segment = this.tabSegments[app.tabs.indexOf(tab)]
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (segment) {
segment.icon = showIcon ? activityIcon : undefined
}
})
tab.titleChange$.subscribe(() => this.update())
tab.activity$.subscribe(() => this.update())
})
}
updateTabs (): void {
this.tabSegments = this.app.tabs.map(tab => ({
label: this.shortenTitle(tab.title),
ipcRenderer.on('touchbar-selection', (_event, index) => this.zone.run(() => {
this.app.selectTab(this.app.tabs[index])
}))
this.tabsSegmentedControl.segments = this.tabSegments
this.tabsSegmentedControl.selectedIndex = this.app.activeTab ? this.app.tabs.indexOf(this.app.activeTab) : 0
}
update (): void {
@@ -58,20 +31,12 @@ export class TouchbarService {
return
}
this.tabsSegmentedControl = new this.electron.TouchBar.TouchBarSegmentedControl({
segments: this.tabSegments,
selectedIndex: this.app.activeTab ? this.app.tabs.indexOf(this.app.activeTab) : undefined,
change: (selectedIndex) => this.zone.run(() => {
this.app.selectTab(this.app.tabs[selectedIndex])
}),
})
const tabSegments = this.app.tabs.map(tab => ({
label: this.shortenTitle(tab.title),
hasActivity: this.app.activeTab !== tab && tab.hasActivity,
}))
const touchBar = new this.electron.TouchBar({
items: [
this.tabsSegmentedControl,
],
})
this.hostWindow.setTouchBar(touchBar)
ipcRenderer.send('window-set-touch-bar', tabSegments, this.app.activeTab ? this.app.tabs.indexOf(this.app.activeTab) : undefined)
}
private shortenTitle (title: string): string {

View File

@@ -17,7 +17,6 @@
"author": "Eugene Pankov",
"license": "MIT",
"dependencies": {
"hterm-umdjs": "1.4.1",
"opentype.js": "^1.3.3"
},
"devDependencies": {

View File

@@ -174,11 +174,6 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hterm-umdjs@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/hterm-umdjs/-/hterm-umdjs-1.4.1.tgz#0cd5352eaf927c70b83c36146cf2c2a281dba957"
integrity sha512-r5JOmdDK1bZCmp3cKcuGRLVeum33H+pzD119ZxmQou+QUVe6SAVSz03HvKWVhM2Ao1Biv+fkhFDmnsaRPq0tFg==
is-arguments@^1.0.4, is-arguments@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9"

View File

@@ -46,7 +46,10 @@
input.form-control.w-50(
type='text',
[(ngModel)]='profile.color',
placeholder='#000000'
placeholder='#000000',
alwaysVisibleTypeahead,
[ngbTypeahead]='colorsAutocomplete',
[resultFormatter]='colorsFormatter'
)
.form-line

View File

@@ -2,7 +2,7 @@
import { Observable, OperatorFunction, debounceTime, map, distinctUntilChanged } from 'rxjs'
import { Component, Input, ViewChild, ViewContainerRef, ComponentFactoryResolver, Injector } from '@angular/core'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigProxy, ConfigService, Profile, ProfileProvider, ProfileSettingsComponent, ProfilesService } from 'tabby-core'
import { ConfigProxy, ConfigService, Profile, ProfileProvider, ProfileSettingsComponent, ProfilesService, TAB_COLORS } from 'tabby-core'
const iconsData = require('../../../tabby-core/src/icons.json')
const iconsClassList = Object.keys(iconsData).map(
@@ -39,6 +39,20 @@ export class EditProfileModalComponent<P extends Profile> {
)].sort() as string[]
}
colorsAutocomplete = text$ => text$.pipe(
debounceTime(200),
distinctUntilChanged(),
map((q: string) =>
TAB_COLORS
.filter(x => !q || x.name.toLowerCase().startsWith(q.toLowerCase()))
.map(x => x.value)
)
)
colorsFormatter = value => {
return TAB_COLORS.find(x => x.value === value)?.name ?? value
}
ngOnInit () {
this._profile = this.profile
this.profile = this.profilesService.getConfigProxyForProfile(this.profile)

View File

@@ -16,7 +16,7 @@
div(*ngIf='!sftp') Connecting
div(*ngIf='sftp')
div(*ngIf='fileList === null') Loading
.list-group.list-group-light(*ngIf='fileList !== null')
.list-group.list-group-flush(*ngIf='fileList !== null')
.list-group-item.list-group-item-action.d-flex.align-items-center(
*ngIf='path !== "/"',
(click)='goUp()'

View File

@@ -2,7 +2,7 @@
import { Component, ViewChild } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, FileProvidersService, Platform, HostAppService, PromptModalComponent } from 'tabby-core'
import { ConfigService, FileProvidersService, Platform, HostAppService, PromptModalComponent, PartialProfile } from 'tabby-core'
import { LoginScriptsSettingsComponent } from 'tabby-terminal'
import { PasswordStorageService } from '../services/passwordStorage.service'
import { ForwardedPortConfig, SSHAlgorithmType, SSHProfile } from '../api'
@@ -20,7 +20,7 @@ export class SSHProfileSettingsComponent {
supportedAlgorithms = supportedAlgorithms
algorithms: Record<string, Record<string, boolean>> = {}
jumpHosts: SSHProfile[]
jumpHosts: PartialProfile<SSHProfile>[]
@ViewChild('loginScriptsSettings') loginScriptsSettings: LoginScriptsSettingsComponent|null
constructor (

View File

@@ -2,7 +2,7 @@ import colors from 'ansi-colors'
import { Component, Injector, HostListener } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { first } from 'rxjs'
import { Platform, RecoveryToken } from 'tabby-core'
import { PartialProfile, Platform, ProfilesService, RecoveryToken } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal'
import { SSHService } from '../services/ssh.service'
import { SSHSession } from '../session/ssh'
@@ -32,6 +32,7 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
injector: Injector,
public ssh: SSHService,
private ngbModal: NgbModal,
private profilesService: ProfilesService,
) {
super(injector)
}
@@ -78,13 +79,16 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
async setupOneSession (session: SSHSession): Promise<void> {
if (session.profile.options.jumpHost) {
const jumpConnection: SSHProfile|null = this.config.store.profiles.find(x => x.id === session.profile.options.jumpHost)
const jumpConnection: PartialProfile<SSHProfile>|null = this.config.store.profiles.find(x => x.id === session.profile.options.jumpHost)
if (!jumpConnection) {
throw new Error(`${session.profile.options.host}: jump host "${session.profile.options.jumpHost}" not found in your config`)
}
const jumpSession = new SSHSession(this.injector, jumpConnection)
const jumpSession = new SSHSession(
this.injector,
this.profilesService.getConfigProxyForProfile(jumpConnection)
)
await this.setupOneSession(jumpSession)

View File

@@ -170,11 +170,13 @@ export class TelnetSession extends BaseSession {
}
if (command === TelnetCommands.DO) {
if (option === TelnetOptions.NEGO_WINDOW_SIZE) {
this.resize(0, 0)
this.emitSize()
this.emitTelnet(TelnetCommands.WILL, option)
} else if (option === TelnetOptions.ECHO) {
this.echoEnabled = true
this.emitTelnet(TelnetCommands.WILL, option)
} else if (option === TelnetOptions.TERMINAL_TYPE) {
this.emitTelnet(TelnetCommands.WILL, option)
this.emitTelnetSuboption(option, Buffer.from([0, ...Buffer.from('XTERM-256COLOR')]))
} else {
this.logger.debug('(!) Unhandled option')
@@ -210,10 +212,18 @@ export class TelnetSession extends BaseSession {
this.lastHeight = h
}
if (this.lastWidth && this.lastHeight && this.telnetProtocol) {
this.emitSize()
}
}
private emitSize () {
if (this.lastWidth && this.lastHeight) {
this.emitTelnetSuboption(TelnetOptions.NEGO_WINDOW_SIZE, Buffer.from([
this.lastWidth >> 8, this.lastWidth & 0xff,
this.lastHeight >> 8, this.lastHeight & 0xff,
]))
} else {
this.emitTelnet(TelnetCommands.WONT, TelnetOptions.NEGO_WINDOW_SIZE)
}
}

View File

@@ -17,7 +17,6 @@
"author": "Eugene Pankov",
"license": "MIT",
"dependencies": {
"hterm-umdjs": "1.4.1",
"opentype.js": "^1.3.3"
},
"devDependencies": {
@@ -32,7 +31,7 @@
"ps-node": "^0.1.6",
"runes": "^0.4.2",
"utils-decorators": "^1.8.1",
"xterm": "^4.9.0-beta.7",
"xterm": "npm:@tabby-gang/xterm@4.14.0-beta.0",
"xterm-addon-fit": "^0.5.0",
"xterm-addon-ligatures": "^0.5.0",
"xterm-addon-search": "^0.8.0",

View File

@@ -6,9 +6,9 @@ import { Subject, Observable, interval, debounce } from 'rxjs'
import { PassThrough, Readable, Writable } from 'stream'
import { ReadLine, createInterface as createReadline, clearLine } from 'readline'
export type InputMode = null | 'local-echo' | 'readline' | 'readline-hex' // eslint-disable-line @typescript-eslint/no-type-alias
export type OutputMode = null | 'hex' // eslint-disable-line @typescript-eslint/no-type-alias
export type NewlineMode = null | 'cr' | 'lf' | 'crlf' // eslint-disable-line @typescript-eslint/no-type-alias
export type InputMode = null | 'local-echo' | 'readline' | 'readline-hex'
export type OutputMode = null | 'hex'
export type NewlineMode = null | 'cr' | 'lf' | 'crlf'
export interface StreamProcessingOptions {
inputMode?: InputMode

View File

@@ -9,7 +9,6 @@ h3.mb-3 Terminal
[(ngModel)]='config.store.terminal.frontend',
(ngModelChange)='config.save()',
)
option(value='hterm') hterm
option(value='xterm') xterm
option(value='xterm-webgl') xterm (WebGL)

View File

@@ -1,120 +0,0 @@
/* eslint-disable */
/** @hidden */
export const hterm = require('hterm-umdjs')
hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory()
/** @hidden */
export const preferenceManager = new hterm.hterm.PreferenceManager('default')
hterm.hterm.VT.ESC['k'] = function (parseState) {
parseState.resetArguments()
function parseOSC (ps) {
if (!this.parseUntilStringTerminator_(ps) || ps.func === parseOSC) {
return
}
this.terminal.setWindowTitle(ps.args[0])
}
parseState.func = parseOSC
}
preferenceManager.set('background-color', '#1D272D')
preferenceManager.set('color-palette-overrides', {
0: '#1D272D',
})
hterm.hterm.Terminal.prototype.showOverlay = () => null
hterm.hterm.Terminal.prototype.setCSS = function (css) {
const doc = this.scrollPort_.document_
if (!doc.querySelector('#user-css')) {
const node = doc.createElement('style')
node.id = 'user-css'
doc.head.appendChild(node)
}
doc.querySelector('#user-css').innerText = css
}
const oldCharWidthDisregardAmbiguous = hterm.lib.wc.charWidthDisregardAmbiguous
hterm.lib.wc.charWidthDisregardAmbiguous = codepoint => {
if ((codepoint >= 0x1f300 && codepoint <= 0x1f64f) ||
(codepoint >= 0x1f680 && codepoint <= 0x1f6ff)) {
return 2
}
return oldCharWidthDisregardAmbiguous(codepoint)
}
hterm.hterm.Terminal.prototype.applyCursorShape = function () {
const modes = [
[hterm.hterm.Terminal.cursorShape.BLOCK, true],
[this.defaultCursorShape || hterm.hterm.Terminal.cursorShape.BLOCK, false],
[hterm.hterm.Terminal.cursorShape.BLOCK, false],
[hterm.hterm.Terminal.cursorShape.UNDERLINE, true],
[hterm.hterm.Terminal.cursorShape.UNDERLINE, false],
[hterm.hterm.Terminal.cursorShape.BEAM, true],
[hterm.hterm.Terminal.cursorShape.BEAM, false],
]
const modeNumber = this.cursorMode || 1
if (modeNumber >= modes.length) {
console.warn('Unknown cursor style: ' + modeNumber)
return
}
setTimeout(() => {
this.setCursorShape(modes[modeNumber][0])
this.setCursorBlink(modes[modeNumber][1])
})
setTimeout(() => {
this.setCursorVisible(true)
})
}
hterm.hterm.VT.CSI[' q'] = function (parseState) {
const arg = parseState.args[0]
this.terminal.cursorMode = arg
this.terminal.applyCursorShape()
}
hterm.hterm.VT.OSC['4'] = function (parseState) {
const args: string[] = parseState.args[0].split(';')
const pairCount = args.length / 2
const colorPalette = this.terminal.getTextAttributes().colorPalette
const responseArray: string[] = []
for (let pairNumber = 0; pairNumber < pairCount; ++pairNumber) {
const colorIndex = parseInt(args[pairNumber * 2])
let colorValue = args[pairNumber * 2 + 1]
if (colorIndex >= colorPalette.length) {
continue
}
if (colorValue === '?') {
colorValue = hterm.lib.colors.rgbToX11(colorPalette[colorIndex])
if (colorValue) {
responseArray.push(colorIndex.toString() + ';' + colorValue)
}
continue
}
colorValue = hterm.lib.colors.x11ToCSS(colorValue)
if (colorValue) {
this.terminal.colorPaletteOverrides[colorIndex] = colorValue
colorPalette[colorIndex] = colorValue
}
}
if (responseArray.length) {
this.terminal.io.sendString('\x1b]4;' + responseArray.join(';') + '\x07')
}
}
const _collapseToEnd = Selection.prototype.collapseToEnd
Selection.prototype.collapseToEnd = function () {
try {
_collapseToEnd.apply(this)
} catch (e) { }
}

View File

@@ -1,35 +0,0 @@
a {
cursor: pointer;
}
a:hover {
text-decoration: underline;
}
x-screen {
transition: 0.125s ease background;
background: transparent;
&::-webkit-scrollbar {
width: 3px;
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
}
}
x-row > span {
display: inline-block;
height: inherit;
&.wc-node {
vertical-align: top;
}
}
@font-face {
font-family: "monospace-fallback";
src: url(../fonts/SourceCodePro.otf) format("opentype");
}

View File

@@ -1,302 +0,0 @@
import { Injector } from '@angular/core'
import { ConfigService, getCSSFontFamily, ThemesService } from 'tabby-core'
import { Frontend, SearchOptions } from './frontend'
import { hterm, preferenceManager } from './hterm'
/** @hidden */
export class HTermFrontend extends Frontend {
term: any
io: any
private htermIframe: HTMLElement
private initialized = false
private configuredFontSize = 0
private configuredLinePadding = 0
private configuredBackgroundColor = 'transparent'
private zoom = 0
private configService: ConfigService
private themesService: ThemesService
constructor (injector: Injector) {
super(injector)
this.configService = injector.get(ConfigService)
this.themesService = injector.get(ThemesService)
}
async attach (host: HTMLElement): Promise<void> {
if (!this.initialized) {
this.init()
this.initialized = true
preferenceManager.set('background-color', 'transparent')
this.term.decorate(host)
this.htermIframe = this.term.scrollPort_.iframe_
} else {
host.appendChild(this.htermIframe)
}
}
getSelection (): string {
return this.term.getSelectionText()
}
copySelection (): void {
this.term.copySelectionToClipboard()
}
selectAll (): void {
const content = this.term.getDocument().body.children[0]
const selection = content.ownerDocument.defaultView.getSelection()
selection.setBaseAndExtent(content, 0, content, 1)
}
clearSelection (): void {
this.term.getDocument().getSelection().removeAllRanges()
}
focus (): void {
setTimeout(() => {
this.term.scrollPort_.resize()
this.term.scrollPort_.focus()
}, 100)
}
write (data: string): void {
this.io.writeUTF8(data)
}
clear (): void {
this.term.wipeContents()
this.term.onVTKeystroke('\f')
}
configure (): void {
const config = this.configService.store
this.configuredFontSize = config.terminal.fontSize
this.configuredLinePadding = config.terminal.linePadding
this.setFontSize()
preferenceManager.set('font-family', getCSSFontFamily(config))
preferenceManager.set('enable-bold', true)
// preferenceManager.set('audible-bell-sound', '')
preferenceManager.set('desktop-notification-bell', config.terminal.bell === 'notification')
preferenceManager.set('enable-clipboard-notice', false)
preferenceManager.set('receive-encoding', 'raw')
preferenceManager.set('send-encoding', 'raw')
preferenceManager.set('ctrl-plus-minus-zero-zoom', false)
preferenceManager.set('scrollbar-visible', process.platform === 'darwin')
preferenceManager.set('copy-on-select', config.terminal.copyOnSelect)
preferenceManager.set('pass-meta-v', false)
preferenceManager.set('alt-is-meta', config.terminal.altIsMeta)
preferenceManager.set('alt-sends-what', 'browser-key')
preferenceManager.set('alt-gr-mode', 'ctrl-alt')
preferenceManager.set('pass-alt-number', true)
preferenceManager.set('cursor-blink', config.terminal.cursorBlink)
preferenceManager.set('clear-selection-after-copy', true)
preferenceManager.set('scroll-on-output', false)
preferenceManager.set('scroll-on-keystroke', config.terminal.scrollOnInput)
if (config.terminal.colorScheme.foreground) {
preferenceManager.set('foreground-color', config.terminal.colorScheme.foreground)
}
if (config.terminal.background === 'colorScheme') {
if (config.terminal.colorScheme.background) {
preferenceManager.set('background-color', config.terminal.colorScheme.background)
}
} else {
preferenceManager.set('background-color', config.appearance.vibrancy ? 'transparent' : this.themesService.findCurrentTheme().terminalBackground)
}
this.configuredBackgroundColor = preferenceManager.get('background-color')
if (!this.term) {
return
}
let css = require('./hterm.userCSS.scss') // eslint-disable-line
if (!config.terminal.ligatures) {
css += `
* {
font-feature-settings: "liga" 0;
font-variant-ligatures: none;
}
`
} else {
css += `
* {
font-feature-settings: "liga" 1;
font-variant-ligatures: initial;
}
`
}
css += config.appearance.css
this.term.setCSS(css)
if (config.terminal.colorScheme.colors) {
preferenceManager.set(
'color-palette-overrides',
Object.assign([], config.terminal.colorScheme.colors, this.term.colorPaletteOverrides)
)
}
if (config.terminal.colorScheme.cursor) {
preferenceManager.set('cursor-color', config.terminal.colorScheme.cursor)
}
this.term.setBracketedPaste(config.terminal.bracketedPaste)
this.term.defaultCursorShape = {
block: hterm.hterm.Terminal.cursorShape.BLOCK,
underline: hterm.hterm.Terminal.cursorShape.UNDERLINE,
beam: hterm.hterm.Terminal.cursorShape.BEAM,
}[config.terminal.cursor]
this.term.applyCursorShape()
this.term.setCursorBlink(config.terminal.cursorBlink)
if (config.terminal.cursorBlink) {
this.term.onCursorBlink_()
}
}
setZoom (zoom: number): void {
this.zoom = zoom
this.setFontSize()
}
visualBell (): void {
preferenceManager.set('background-color', 'rgba(128,128,128,.25)')
setTimeout(() => {
preferenceManager.set('background-color', this.configuredBackgroundColor)
}, 125)
}
scrollToBottom (): void {
this.term.scrollEnd()
}
findNext (_term: string, _searchOptions?: SearchOptions): boolean {
return false
}
findPrevious (_term: string, _searchOptions?: SearchOptions): boolean {
return false
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
saveState (): any { }
// eslint-disable-next-line @typescript-eslint/no-empty-function
restoreState (_state: string): void { }
private setFontSize () {
const size = this.configuredFontSize * Math.pow(1.1, this.zoom)
preferenceManager.set('font-size', size)
if (this.term) {
setTimeout(() => {
this.term.scrollPort_.characterSize = this.term.scrollPort_.measureCharacterSize()
this.term.setFontSize(size)
})
}
}
private init () {
this.term = new hterm.hterm.Terminal()
this.term.colorPaletteOverrides = []
this.term.onTerminalReady = () => {
this.term.installKeyboard()
this.term.scrollPort_.setCtrlVPaste(true)
this.io = this.term.io.push()
this.io.onVTKeystroke = this.io.sendString = data => this.input.next(Buffer.from(data, 'utf-8'))
this.io.onTerminalResize = (columns, rows) => {
this.resize.next({ columns, rows })
}
this.ready.next()
this.ready.complete()
this.term.scrollPort_.document_.addEventListener('dragOver', event => {
this.dragOver.next(event)
})
this.term.scrollPort_.document_.addEventListener('drop', event => {
this.drop.next(event)
})
}
this.term.setWindowTitle = title => this.title.next(title)
const _setAlternateMode = this.term.setAlternateMode.bind(this.term)
this.term.setAlternateMode = (state) => {
_setAlternateMode(state)
this.alternateScreenActive.next(state)
}
this.term.primaryScreen_.syncSelectionCaret = () => null
this.term.alternateScreen_.syncSelectionCaret = () => null
this.term.primaryScreen_.terminal = this.term
this.term.alternateScreen_.terminal = this.term
this.term.scrollPort_.onPaste_ = (event) => {
event.preventDefault()
}
const _resize = this.term.scrollPort_.resize.bind(this.term.scrollPort_)
this.term.scrollPort_.resize = () => {
if (this.enableResizing) {
_resize()
}
}
const _onMouse = this.term.onMouse_.bind(this.term)
this.term.onMouse_ = (event) => {
this.mouseEvent.next(event)
if (event.type === 'mousedown' && event.which === 3) {
event.preventDefault()
event.stopPropagation()
return
}
if (event.type === 'mousewheel' && event.altKey) {
event.preventDefault()
}
_onMouse(event)
}
this.term.ringBell = () => this.bell.next()
for (const screen of [this.term.primaryScreen_, this.term.alternateScreen_]) {
const _insertString = screen.insertString.bind(screen)
screen.insertString = (data) => {
_insertString(data)
this.contentUpdated.next()
}
const _deleteChars = screen.deleteChars.bind(screen)
screen.deleteChars = (count) => {
const ret = _deleteChars(count)
this.contentUpdated.next()
return ret
}
const _expandSelection = screen.expandSelection.bind(screen)
screen.expandSelection = (selection) => {
// Drop whitespace at the end of selection
const range = selection.getRangeAt(0)
if (range.endOffset > 0 && range.endContainer.nodeType === 3 && range.endContainer.textContent !== '') {
while (/[\s\S]+\s$/.test(range.endContainer.textContent.substr(0, range.endOffset))) {
range.setEnd(range.endContainer, range.endOffset - 1)
}
}
_expandSelection(selection)
}
}
const _measureCharacterSize = this.term.scrollPort_.measureCharacterSize.bind(this.term.scrollPort_)
this.term.scrollPort_.measureCharacterSize = () => {
const size = _measureCharacterSize()
size.height += this.configuredLinePadding
return size
}
const _onCursorBlink = this.term.onCursorBlink_.bind(this.term)
this.term.onCursorBlink_ = () => {
this.term.cursorNode_.style.opacity = '0'
_onCursorBlink()
}
}
}

View File

@@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { ToastrModule } from 'ngx-toastr'
import TabbyCorePlugin, { ConfigProvider, HotkeysService, HotkeyProvider, TabContextMenuItemProvider, CLIHandler } from 'tabby-core'
import TabbyCorePlugin, { ConfigProvider, HotkeyProvider, TabContextMenuItemProvider, CLIHandler } from 'tabby-core'
import { SettingsTabProvider } from 'tabby-settings'
import { AppearanceSettingsTabComponent } from './components/appearanceSettingsTab.component'
@@ -29,9 +29,7 @@ import { TerminalConfigProvider } from './config'
import { TerminalHotkeyProvider } from './hotkeys'
import { CopyPasteContextMenu, MiscContextMenu, LegacyContextMenu } from './tabContextMenu'
import { hterm } from './frontends/hterm'
import { Frontend } from './frontends/frontend'
import { HTermFrontend } from './frontends/htermFrontend'
import { XTermFrontend, XTermWebGLFrontend } from './frontends/xtermFrontend'
import { TerminalCLIHandler } from './cli'
@@ -83,37 +81,10 @@ import { TerminalCLIHandler } from './cli'
LoginScriptsSettingsComponent,
],
})
export default class TerminalModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
constructor (
hotkeys: HotkeysService,
) {
const events = [
{
name: 'keydown',
htermHandler: 'onKeyDown_',
},
{
name: 'keyup',
htermHandler: 'onKeyUp_',
},
]
events.forEach(event => {
const oldHandler = hterm.hterm.Keyboard.prototype[event.htermHandler]
hterm.hterm.Keyboard.prototype[event.htermHandler] = function (nativeEvent) {
hotkeys.pushKeyEvent(event.name, nativeEvent)
if (hotkeys.matchActiveHotkey(true) !== null) {
oldHandler.bind(this)(nativeEvent)
} else {
nativeEvent.stopPropagation()
nativeEvent.preventDefault()
}
}
})
}
}
export default class TerminalModule { } // eslint-disable-line @typescript-eslint/no-extraneous-class
export { TerminalFrontendService, TerminalDecorator, TerminalContextMenuItemProvider, TerminalColorSchemeProvider }
export { Frontend, XTermFrontend, XTermWebGLFrontend, HTermFrontend }
export { Frontend, XTermFrontend, XTermWebGLFrontend }
export { BaseTerminalTabComponent } from './api/baseTerminalTab.component'
export * from './api/interfaces'
export * from './api/streamProcessing'

View File

@@ -1,7 +1,6 @@
import { Injectable, Injector } from '@angular/core'
import { ConfigService } from 'tabby-core'
import { Frontend } from '../frontends/frontend'
import { HTermFrontend } from '../frontends/htermFrontend'
import { XTermFrontend, XTermWebGLFrontend } from '../frontends/xtermFrontend'
import { BaseSession } from '../session'
@@ -17,12 +16,11 @@ export class TerminalFrontendService {
getFrontend (session?: BaseSession|null): Frontend {
if (!session) {
const frontend: Frontend = new {
const cls = {
xterm: XTermFrontend,
'xterm-webgl': XTermWebGLFrontend,
hterm: HTermFrontend,
}[this.config.store.terminal.frontend](this.injector)
return frontend
}[this.config.store.terminal.frontend] ?? XTermFrontend
return new cls(this.injector)
}
if (!this.containers.has(session)) {
this.containers.set(

View File

@@ -3,7 +3,6 @@ module.exports = config({
name: 'terminal',
dirname: __dirname,
externals: [
'hterm-umdjs',
'opentype.js',
],
})

View File

@@ -239,11 +239,6 @@ hexer@^1.5.0:
process "^0.10.0"
xtend "^4.0.0"
hterm-umdjs@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/hterm-umdjs/-/hterm-umdjs-1.4.1.tgz#0cd5352eaf927c70b83c36146cf2c2a281dba957"
integrity sha512-r5JOmdDK1bZCmp3cKcuGRLVeum33H+pzD119ZxmQou+QUVe6SAVSz03HvKWVhM2Ao1Biv+fkhFDmnsaRPq0tFg==
is-arguments@^1.0.4, is-arguments@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9"
@@ -603,10 +598,10 @@ xterm-addon-webgl@^0.11.0:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.11.1.tgz#33dd250ab52e9f51d2ff52396447962e6f53e24c"
integrity sha512-xF6DnEoV+rPtzetMBXBZVe1kLKtus7AKdEcyfq2eMHQzhaRvC+pfnU+XiCXC85kueguqu2UkBHXZs5mihK9jOQ==
xterm@^4.9.0-beta.7:
version "4.13.0"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.13.0.tgz#7998de1e2ad92c4796fe45807be4f31061f3d9d1"
integrity sha512-HVW1gdoLOTnkMaqQCr2r3mQy4fX9iSa5gWxKZ2UTYdLa4iqavv7QxJ8n1Ypse32shPVkhTYPLS6vHEFZp5ghzw==
"xterm@npm:@tabby-gang/xterm@4.14.0-beta.0":
version "4.14.0-beta.0"
resolved "https://registry.yarnpkg.com/@tabby-gang/xterm/-/xterm-4.14.0-beta.0.tgz#6a446917179db1d1c864cd094baddcd05881e71c"
integrity sha512-Aj1oXfJlkjjcaqxQJDshzUeiB6Cc/K3PA+gVuAtVcuFfscjArZvhSrGbaWgioUhYk5xcamxgh55fnXsvFEf9sA==
yallist@^4.0.0:
version "4.0.0"

File diff suppressed because it is too large Load Diff

View File

@@ -86,24 +86,6 @@ Tabby.registerModule('crypto', {
return a.equals(b)
},
})
Tabby.registerMock('hterm-umdjs', {
hterm: {
PreferenceManager: class { set () {} },
VT: {
ESC: {},
CSI: {},
OSC: {},
},
Terminal: class {},
Keyboard: class {},
},
lib: {
wc: {},
Storage: {
Memory: class {},
},
},
})
Tabby.registerMock('dns', {})
Tabby.registerMock('socksv5', {})
Tabby.registerMock('util', require('util/'))

674
yarn.lock

File diff suppressed because it is too large Load Diff