mirror of
https://github.com/Eugeny/tabby.git
synced 2025-09-11 10:54:35 +00:00
Compare commits
46 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0514a7c229 | ||
![]() |
326901b7e8 | ||
![]() |
bbe6b61d63 | ||
![]() |
204c1057db | ||
![]() |
dff6a2470c | ||
![]() |
cbebc09504 | ||
![]() |
f56dd71f43 | ||
![]() |
17f52a257e | ||
![]() |
8d09ddb686 | ||
![]() |
e6fd31e0b0 | ||
![]() |
c6188a49f5 | ||
![]() |
9a60b4d102 | ||
![]() |
7977c1d644 | ||
![]() |
ac85a1d7d3 | ||
![]() |
86b503093c | ||
![]() |
dd3e7a0f89 | ||
![]() |
8905106b48 | ||
![]() |
225760a9a5 | ||
![]() |
37cc37650e | ||
![]() |
17cafbfa52 | ||
![]() |
3931e8088e | ||
![]() |
299ede2eb1 | ||
![]() |
bc5e6e9535 | ||
![]() |
0c15fc2657 | ||
![]() |
5e115c63f1 | ||
![]() |
2bcf23cff1 | ||
![]() |
2c59022b78 | ||
![]() |
358d9f30d2 | ||
![]() |
afd6ce4346 | ||
![]() |
5c7256ffe5 | ||
![]() |
a15e79ad5a | ||
![]() |
f1ecbd1a93 | ||
![]() |
7da941d038 | ||
![]() |
3efc142630 | ||
![]() |
d592469237 | ||
![]() |
b3e63620b3 | ||
![]() |
22b79510ea | ||
![]() |
70cf63f8fa | ||
![]() |
c9067cf8b8 | ||
![]() |
4ccc406768 | ||
![]() |
1c25747de0 | ||
![]() |
8e4c36ec24 | ||
![]() |
444d92d393 | ||
![]() |
c9d75d81e4 | ||
![]() |
98eb68c845 | ||
![]() |
5185e1fe1d |
@@ -298,6 +298,42 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "zend",
|
||||||
|
"name": "Zenghai Liang",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/25160?v=4",
|
||||||
|
"profile": "https://github.com/zend",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "matishadow",
|
||||||
|
"name": "Mateusz Tracz",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/9083085?v=4",
|
||||||
|
"profile": "https://about.me/matishadow",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "pinpins",
|
||||||
|
"name": "pinpin",
|
||||||
|
"avatar_url": "https://avatars3.githubusercontent.com/u/36234677?v=4",
|
||||||
|
"profile": "https://zergpool.com",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "TakuroOnoda",
|
||||||
|
"name": "Takuro Onoda",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/1407926?v=4",
|
||||||
|
"profile": "https://github.com/TakuroOnoda",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js: 11
|
node_js: 10
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- Build
|
- Build
|
||||||
|
@@ -114,6 +114,12 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<td align="center"><a href="https://github.com/3l0w"><img src="https://avatars2.githubusercontent.com/u/37798980?v=4" width="100px;" alt=""/><br /><sub><b>Gwilherm Folliot</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=3l0w" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/3l0w"><img src="https://avatars2.githubusercontent.com/u/37798980?v=4" width="100px;" alt=""/><br /><sub><b>Gwilherm Folliot</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=3l0w" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/Dimitory"><img src="https://avatars0.githubusercontent.com/u/475955?v=4" width="100px;" alt=""/><br /><sub><b>Dmitry Pronin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=dimitory" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/Dimitory"><img src="https://avatars0.githubusercontent.com/u/475955?v=4" width="100px;" alt=""/><br /><sub><b>Dmitry Pronin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=dimitory" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/JonathanBeverley"><img src="https://avatars1.githubusercontent.com/u/20328966?v=4" width="100px;" alt=""/><br /><sub><b>Jonathan Beverley</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=JonathanBeverley" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/JonathanBeverley"><img src="https://avatars1.githubusercontent.com/u/20328966?v=4" width="100px;" alt=""/><br /><sub><b>Jonathan Beverley</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=JonathanBeverley" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/zend"><img src="https://avatars1.githubusercontent.com/u/25160?v=4" width="100px;" alt=""/><br /><sub><b>Zenghai Liang</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=zend" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://about.me/matishadow"><img src="https://avatars0.githubusercontent.com/u/9083085?v=4" width="100px;" alt=""/><br /><sub><b>Mateusz Tracz</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=matishadow" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://zergpool.com"><img src="https://avatars3.githubusercontent.com/u/36234677?v=4" width="100px;" alt=""/><br /><sub><b>pinpin</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=pinpins" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/TakuroOnoda"><img src="https://avatars0.githubusercontent.com/u/1407926?v=4" width="100px;" alt=""/><br /><sub><b>Takuro Onoda</b></sub></a><br /><a href="https://github.com/Eugeny/terminus/commits?author=TakuroOnoda" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@@ -139,9 +139,7 @@ export class Application {
|
|||||||
|
|
||||||
handleSecondInstance (argv: string[], cwd: string): void {
|
handleSecondInstance (argv: string[], cwd: string): void {
|
||||||
this.presentAllWindows()
|
this.presentAllWindows()
|
||||||
for (let window of this.windows) {
|
this.windows[this.windows.length - 1].handleSecondInstance(argv, cwd)
|
||||||
window.handleSecondInstance(argv, cwd)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupMenu () {
|
private setupMenu () {
|
||||||
|
@@ -211,10 +211,8 @@ export class Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSecondInstance (argv: string[], cwd: string): void {
|
handleSecondInstance (argv: string[], cwd: string): void {
|
||||||
if (!this.configStore.appearance?.dock) {
|
|
||||||
this.send('host:second-instance', parseArgs(argv, cwd), cwd)
|
this.send('host:second-instance', parseArgs(argv, cwd), cwd)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private setupWindowManagement () {
|
private setupWindowManagement () {
|
||||||
this.window.on('show', () => {
|
this.window.on('show', () => {
|
||||||
|
@@ -108,7 +108,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
|
|
||||||
"@terminus-term/node-pty@0.10.0-beta.9":
|
"@terminus-term/node-pty@0.10.0-beta9":
|
||||||
version "0.10.0-beta9"
|
version "0.10.0-beta9"
|
||||||
resolved "https://registry.yarnpkg.com/@terminus-term/node-pty/-/node-pty-0.10.0-beta9.tgz#b4caff6b069139add9be959e00b364f8fe3c620d"
|
resolved "https://registry.yarnpkg.com/@terminus-term/node-pty/-/node-pty-0.10.0-beta9.tgz#b4caff6b069139add9be959e00b364f8fe3c620d"
|
||||||
integrity sha512-wnttx12b9gxP9CPB9uqBMQx/Vp4EboUDGOY3xRP0Nvhec6pSF2qFZD6bwMbNzFIopbaohluEYcbEul0jTQcdeQ==
|
integrity sha512-wnttx12b9gxP9CPB9uqBMQx/Vp4EboUDGOY3xRP0Nvhec6pSF2qFZD6bwMbNzFIopbaohluEYcbEul0jTQcdeQ==
|
||||||
@@ -326,9 +326,9 @@ bindings@^1.5.0:
|
|||||||
file-uri-to-path "1.0.0"
|
file-uri-to-path "1.0.0"
|
||||||
|
|
||||||
bl@^3.0.0:
|
bl@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88"
|
resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.1.tgz#1cbb439299609e419b5a74d7fce2f8b37d8e5c6f"
|
||||||
integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==
|
integrity sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream "^3.0.1"
|
readable-stream "^3.0.1"
|
||||||
|
|
||||||
@@ -1315,7 +1315,7 @@ inflight@^1.0.4, inflight@~1.0.6:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
|
inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||||
@@ -1325,6 +1325,11 @@ inherits@2.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||||
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
|
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
|
||||||
|
|
||||||
|
inherits@^2.0.3:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
|
ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
|
||||||
version "1.3.5"
|
version "1.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
|
||||||
@@ -2695,9 +2700,9 @@ read@1, read@~1.0.1, read@~1.0.7:
|
|||||||
util-deprecate "~1.0.1"
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
readable-stream@^3.0.1, readable-stream@^3.1.1:
|
readable-stream@^3.0.1, readable-stream@^3.1.1:
|
||||||
version "3.4.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||||
integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
|
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||||
dependencies:
|
dependencies:
|
||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
@@ -2832,6 +2837,11 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2,
|
|||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||||
|
|
||||||
|
safe-buffer@~5.2.0:
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
|
|
||||||
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
@@ -3069,11 +3079,11 @@ string-width@^4.1.0, string-width@^4.2.0:
|
|||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
string_decoder@^1.1.1:
|
string_decoder@^1.1.1:
|
||||||
version "1.2.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d"
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||||
integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==
|
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.1.0"
|
safe-buffer "~5.2.0"
|
||||||
|
|
||||||
string_decoder@~0.10.x:
|
string_decoder@~0.10.x:
|
||||||
version "0.10.31"
|
version "0.10.31"
|
||||||
|
@@ -4,11 +4,15 @@ title-bar(
|
|||||||
)
|
)
|
||||||
|
|
||||||
.content(
|
.content(
|
||||||
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top"'
|
[class.tabs-on-top]='config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left"',
|
||||||
|
[class.tabs-on-side]='hasVerticalTabs()',
|
||||||
)
|
)
|
||||||
.tab-bar
|
.tab-bar
|
||||||
.inset.background(*ngIf='hostApp.platform == Platform.macOS && config.store.appearance.frame == "thin" && config.store.appearance.tabsLocation == "top"')
|
.inset.background(*ngIf='hostApp.platform == Platform.macOS \
|
||||||
|
&& config.store.appearance.frame == "thin" \
|
||||||
|
&& (config.store.appearance.tabsLocation == "top" || config.store.appearance.tabsLocation == "left")')
|
||||||
.tabs(
|
.tabs(
|
||||||
|
*ngIf='config.store.appearance.tabsLocation != "bottom"'
|
||||||
dnd-sortable-container,
|
dnd-sortable-container,
|
||||||
[sortableData]='app.tabs',
|
[sortableData]='app.tabs',
|
||||||
)
|
)
|
||||||
@@ -24,6 +28,7 @@ title-bar(
|
|||||||
[active]='tab == app.activeTab',
|
[active]='tab == app.activeTab',
|
||||||
[hasActivity]='tab.activity$|async',
|
[hasActivity]='tab.activity$|async',
|
||||||
@animateTab,
|
@animateTab,
|
||||||
|
[@.disabled]='hasVerticalTabs()',
|
||||||
(click)='app.selectTab(tab)',
|
(click)='app.selectTab(tab)',
|
||||||
[class.fully-draggable]='hostApp.platform != Platform.macOS',
|
[class.fully-draggable]='hostApp.platform != Platform.macOS',
|
||||||
[class.drag-region]='hostApp.platform == Platform.macOS && !tabsDragging',
|
[class.drag-region]='hostApp.platform == Platform.macOS && !tabsDragging',
|
||||||
@@ -87,7 +92,8 @@ title-bar(
|
|||||||
)
|
)
|
||||||
|
|
||||||
window-controls.background(
|
window-controls.background(
|
||||||
*ngIf='config.store.appearance.frame == "thin" && (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)',
|
*ngIf='config.store.appearance.frame == "thin" \
|
||||||
|
&& (hostApp.platform == Platform.Windows || hostApp.platform == Platform.Linux)',
|
||||||
)
|
)
|
||||||
|
|
||||||
start-page(*ngIf='ready && app.tabs.length == 0')
|
start-page(*ngIf='ready && app.tabs.length == 0')
|
||||||
|
@@ -15,10 +15,18 @@
|
|||||||
|
|
||||||
$tabs-height: 38px;
|
$tabs-height: 38px;
|
||||||
$tab-border-radius: 4px;
|
$tab-border-radius: 4px;
|
||||||
|
$side-tab-width: 200px;
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
display: flex;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
height: 100%;
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
flex: auto;
|
flex: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
@@ -26,15 +34,50 @@ $tab-border-radius: 4px;
|
|||||||
&.tabs-on-top {
|
&.tabs-on-top {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.tabs-on-side {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
|
||||||
|
&.tabs-on-top {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content.tabs-on-side > .tab-bar {
|
||||||
|
height: 100%;
|
||||||
|
width: $side-tab-width;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
flex-direction: column;
|
||||||
|
background: rgba(0, 0, 0, 0.25);
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
width: $side-tab-width;
|
||||||
|
flex: none;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
tab-header {
|
||||||
|
flex: 0 0 $tabs-height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-space {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.tab-bar {
|
.tab-bar {
|
||||||
flex: none;
|
flex: none;
|
||||||
height: $tabs-height;
|
height: $tabs-height;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.btn-tab-bar {
|
.btn-tab-bar {
|
||||||
line-height: $tabs-height + 2px;
|
line-height: $tabs-height + 2px;
|
||||||
|
height: $tabs-height;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -74,7 +117,10 @@ $tab-border-radius: 4px;
|
|||||||
|
|
||||||
& > .inset {
|
& > .inset {
|
||||||
width: 85px;
|
width: 85px;
|
||||||
|
height: $tabs-height;
|
||||||
flex: none;
|
flex: none;
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-app-region: drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
window-controls {
|
window-controls {
|
||||||
|
@@ -184,6 +184,10 @@ export class AppRootComponent {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasVerticalTabs () {
|
||||||
|
return this.config.store.appearance.tabsLocation === 'left' || this.config.store.appearance.tabsLocation === 'right'
|
||||||
|
}
|
||||||
|
|
||||||
async updateApp () {
|
async updateApp () {
|
||||||
if ((await this.electron.showMessageBox(
|
if ((await this.electron.showMessageBox(
|
||||||
this.hostApp.getWindow(),
|
this.hostApp.getWindow(),
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
.progressbar([style.width]='progress + "%"', *ngIf='progress != null')
|
.progressbar([style.width]='progress + "%"', *ngIf='progress != null')
|
||||||
.index(
|
.index(*ngIf='!config.store.terminal.hideTabIndex',
|
||||||
#handle,
|
#handle,
|
||||||
[style.background-color]='tab.color',
|
[style.background-color]='tab.color',
|
||||||
) {{index + 1}}
|
) {{index + 1}}
|
||||||
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
|
.name([title]='tab.customTitle || tab.title') {{tab.customTitle || tab.title}}
|
||||||
button((click)='app.closeTab(tab, true)') ×
|
button(*ngIf='!config.store.terminal.hideCloseButton',(click)='app.closeTab(tab, true)') ×
|
||||||
|
@@ -13,6 +13,11 @@ $tabs-height: 38px;
|
|||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.vertical {
|
||||||
|
flex: none;
|
||||||
|
height: $tabs-height;
|
||||||
|
}
|
||||||
|
|
||||||
.index {
|
.index {
|
||||||
flex: none;
|
flex: none;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@@ -9,6 +9,7 @@ import { HotkeysService } from '../services/hotkeys.service'
|
|||||||
import { ElectronService } from '../services/electron.service'
|
import { ElectronService } from '../services/electron.service'
|
||||||
import { AppService } from '../services/app.service'
|
import { AppService } from '../services/app.service'
|
||||||
import { HostAppService, Platform } from '../services/hostApp.service'
|
import { HostAppService, Platform } from '../services/hostApp.service'
|
||||||
|
import { ConfigService } from '../services/config.service'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
export interface SortableComponentProxy {
|
export interface SortableComponentProxy {
|
||||||
@@ -31,6 +32,7 @@ export class TabHeaderComponent {
|
|||||||
|
|
||||||
private constructor (
|
private constructor (
|
||||||
public app: AppService,
|
public app: AppService,
|
||||||
|
public config: ConfigService,
|
||||||
private electron: ElectronService,
|
private electron: ElectronService,
|
||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
private ngbModal: NgbModal,
|
private ngbModal: NgbModal,
|
||||||
|
@@ -43,14 +43,28 @@ ngb-tabset.vertical(type='pills', [activeId]='activeTab')
|
|||||||
ngbButton,
|
ngbButton,
|
||||||
[value]='"top"'
|
[value]='"top"'
|
||||||
)
|
)
|
||||||
| On the top
|
| Top
|
||||||
label.btn.btn-secondary(ngbButtonLabel)
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
input(
|
input(
|
||||||
type='radio',
|
type='radio',
|
||||||
ngbButton,
|
ngbButton,
|
||||||
[value]='"bottom"'
|
[value]='"bottom"'
|
||||||
)
|
)
|
||||||
| At the bottom
|
| Bottom
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"left"'
|
||||||
|
)
|
||||||
|
| Left
|
||||||
|
label.btn.btn-secondary(ngbButtonLabel)
|
||||||
|
input(
|
||||||
|
type='radio',
|
||||||
|
ngbButton,
|
||||||
|
[value]='"right"'
|
||||||
|
)
|
||||||
|
| Right
|
||||||
|
|
||||||
.form-line
|
.form-line
|
||||||
.header
|
.header
|
||||||
|
@@ -65,7 +65,7 @@ export class SettingsTabComponent extends BaseTabComponent {
|
|||||||
const onConfigChange = () => {
|
const onConfigChange = () => {
|
||||||
this.configFile = config.readRaw()
|
this.configFile = config.readRaw()
|
||||||
this.padWindowControls = hostApp.platform === Platform.macOS
|
this.padWindowControls = hostApp.platform === Platform.macOS
|
||||||
&& config.store.appearance.tabsLocation === 'bottom'
|
&& config.store.appearance.tabsLocation !== 'top'
|
||||||
}
|
}
|
||||||
|
|
||||||
this.configSubscription = config.changed$.subscribe(onConfigChange)
|
this.configSubscription = config.changed$.subscribe(onConfigChange)
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --progress --color",
|
"build": "webpack --progress --color",
|
||||||
"watch": "webpack --progress --color --watch",
|
"watch": "webpack --progress --color --watch",
|
||||||
"postinstall": "xcopy /i node_modules\\ssh2\\util\\pagent.exe util\\"
|
"postinstall": "xcopy /i /y node_modules\\ssh2\\util\\pagent.exe util\\"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
@@ -23,8 +23,9 @@
|
|||||||
"@types/ssh2": "^0.5.35",
|
"@types/ssh2": "^0.5.35",
|
||||||
"ansi-colors": "^4.1.1",
|
"ansi-colors": "^4.1.1",
|
||||||
"cli-spinner": "^0.2.10",
|
"cli-spinner": "^0.2.10",
|
||||||
|
"run-script-os": "^1.1.3",
|
||||||
"ssh2": "^0.8.2",
|
"ssh2": "^0.8.2",
|
||||||
"ssh2-streams": "^0.4.2",
|
"ssh2-streams": "Eugeny/ssh2-streams#75f6d3425d071ac73a18fd46e2f5e738bfe897c5",
|
||||||
"sshpk": "^1.16.1",
|
"sshpk": "^1.16.1",
|
||||||
"temp": "^0.9.1",
|
"temp": "^0.9.1",
|
||||||
"terminus-terminal": "^1.0.98-nightly.0"
|
"terminus-terminal": "^1.0.98-nightly.0"
|
||||||
|
@@ -246,7 +246,7 @@ export class SSHSession extends BaseSession {
|
|||||||
fw.targetPort,
|
fw.targetPort,
|
||||||
(err, stream) => {
|
(err, stream) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote has rejected the forwaded connection via ${fw}: ${err}`)
|
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote has rejected the forwarded connection via ${fw}: ${err}`)
|
||||||
socket.destroy()
|
socket.destroy()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -263,7 +263,7 @@ export class SSHSession extends BaseSession {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.emitServiceMessage(colors.bgGreen.black(' -> ') + ` Forwaded ${fw}`)
|
this.emitServiceMessage(colors.bgGreen.black(' -> ') + ` Forwarded ${fw}`)
|
||||||
this.forwardedPorts.push(fw)
|
this.forwardedPorts.push(fw)
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Failed to forward port ${fw}: ${e}`)
|
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Failed to forward port ${fw}: ${e}`)
|
||||||
@@ -280,7 +280,7 @@ export class SSHSession extends BaseSession {
|
|||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.emitServiceMessage(colors.bgGreen.black(' <- ') + ` Forwaded ${fw}`)
|
this.emitServiceMessage(colors.bgGreen.black(' <- ') + ` Forwarded ${fw}`)
|
||||||
this.forwardedPorts.push(fw)
|
this.forwardedPorts.push(fw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,8 @@ h3 Connections
|
|||||||
.mr-auto
|
.mr-auto
|
||||||
div {{connection.name}}
|
div {{connection.name}}
|
||||||
.text-muted {{connection.host}}
|
.text-muted {{connection.host}}
|
||||||
|
button.btn.btn-outline-info.ml-1((click)='$event.stopPropagation(); copyConnection(connection)')
|
||||||
|
i.fas.fa-copy
|
||||||
button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteConnection(connection)')
|
button.btn.btn-outline-danger.ml-1((click)='$event.stopPropagation(); deleteConnection(connection)')
|
||||||
i.fas.fa-trash
|
i.fas.fa-trash
|
||||||
|
|
||||||
|
@@ -46,6 +46,19 @@ export class SSHSettingsTabComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copyConnection (connection: SSHConnection) {
|
||||||
|
const modal = this.ngbModal.open(EditConnectionModalComponent)
|
||||||
|
modal.componentInstance.connection = Object.assign({
|
||||||
|
name: `${name} Copy`,
|
||||||
|
}, connection)
|
||||||
|
modal.result.then(result => {
|
||||||
|
this.connections.push(result)
|
||||||
|
this.config.store.ssh.connections = this.connections
|
||||||
|
this.config.save()
|
||||||
|
this.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
editConnection (connection: SSHConnection) {
|
editConnection (connection: SSHConnection) {
|
||||||
const modal = this.ngbModal.open(EditConnectionModalComponent, { size: 'lg' })
|
const modal = this.ngbModal.open(EditConnectionModalComponent, { size: 'lg' })
|
||||||
modal.componentInstance.connection = Object.assign({}, connection)
|
modal.componentInstance.connection = Object.assign({}, connection)
|
||||||
|
@@ -22,6 +22,11 @@ try {
|
|||||||
var windowsProcessTreeNative = require('windows-process-tree/build/Release/windows_process_tree.node') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
|
var windowsProcessTreeNative = require('windows-process-tree/build/Release/windows_process_tree.node') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
|
||||||
} catch { }
|
} catch { }
|
||||||
|
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-type-alias
|
||||||
|
export type SSHLogCallback = (message: string) => void
|
||||||
|
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class SSHService {
|
export class SSHService {
|
||||||
private logger: Logger
|
private logger: Logger
|
||||||
@@ -46,33 +51,24 @@ export class SSHService {
|
|||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
async connectSession (session: SSHSession, logCallback?: (s: any) => void): Promise<void> {
|
async loadPrivateKeyForSession (session: SSHSession, logCallback?: SSHLogCallback): Promise<string|null> {
|
||||||
let privateKey: string|null = null
|
let privateKey: string|null = null
|
||||||
let privateKeyPath = session.connection.privateKey
|
let privateKeyPath = session.connection.privateKey
|
||||||
|
|
||||||
if (!logCallback) {
|
|
||||||
logCallback = () => null
|
|
||||||
}
|
|
||||||
|
|
||||||
const log = (s: any) => {
|
|
||||||
logCallback!(s)
|
|
||||||
this.logger.info(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!privateKeyPath) {
|
if (!privateKeyPath) {
|
||||||
const userKeyPath = path.join(process.env.HOME as string, '.ssh', 'id_rsa')
|
const userKeyPath = path.join(process.env.HOME as string, '.ssh', 'id_rsa')
|
||||||
if (await fs.exists(userKeyPath)) {
|
if (await fs.exists(userKeyPath)) {
|
||||||
log('Using user\'s default private key')
|
logCallback?.('Using user\'s default private key')
|
||||||
privateKeyPath = userKeyPath
|
privateKeyPath = userKeyPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (privateKeyPath) {
|
if (privateKeyPath) {
|
||||||
log('Loading private key from ' + colors.bgWhite.blackBright(' ' + privateKeyPath + ' '))
|
logCallback?.('Loading private key from ' + colors.bgWhite.blackBright(' ' + privateKeyPath + ' '))
|
||||||
try {
|
try {
|
||||||
privateKey = (await fs.readFile(privateKeyPath)).toString()
|
privateKey = (await fs.readFile(privateKeyPath)).toString()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(colors.bgRed.black(' X ') + 'Could not read the private key file')
|
logCallback?.(colors.bgRed.black(' X ') + 'Could not read the private key file')
|
||||||
this.toastr.error('Could not read the private key file')
|
this.toastr.error('Could not read the private key file')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +79,7 @@ export class SSHService {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof sshpk.KeyEncryptedError) {
|
if (e instanceof sshpk.KeyEncryptedError) {
|
||||||
const modal = this.ngbModal.open(PromptModalComponent)
|
const modal = this.ngbModal.open(PromptModalComponent)
|
||||||
log(colors.bgYellow.yellow.black(' ! ') + ' Key requires passphrase')
|
logCallback?.(colors.bgYellow.yellow.black(' ! ') + ' Key requires passphrase')
|
||||||
modal.componentInstance.prompt = 'Private key passphrase'
|
modal.componentInstance.prompt = 'Private key passphrase'
|
||||||
modal.componentInstance.password = true
|
modal.componentInstance.password = true
|
||||||
let passphrase = ''
|
let passphrase = ''
|
||||||
@@ -116,8 +112,9 @@ export class SSHService {
|
|||||||
'ssh-keygen.exe',
|
'ssh-keygen.exe',
|
||||||
)
|
)
|
||||||
await execFile('icacls', [temp.path, '/inheritance:r'])
|
await execFile('icacls', [temp.path, '/inheritance:r'])
|
||||||
let sid = await execFile('whoami', ['/user', '/nh'])
|
let sid = await execFile('whoami', ['/user', '/nh', '/fo', 'csv'])
|
||||||
sid = sid[0].split(' ')[0]
|
sid = sid[0].split(',')[0]
|
||||||
|
sid = sid.substring(1, sid.length - 1)
|
||||||
await execFile('icacls', [temp.path, '/grant:r', `${sid}:(R,W)`])
|
await execFile('icacls', [temp.path, '/grant:r', `${sid}:(R,W)`])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +127,20 @@ export class SSHService {
|
|||||||
fs.unlink(temp.path)
|
fs.unlink(temp.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return privateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectSession (session: SSHSession, logCallback?: SSHLogCallback): Promise<void> {
|
||||||
|
if (!logCallback) {
|
||||||
|
logCallback = () => null
|
||||||
|
}
|
||||||
|
|
||||||
|
const log = (s: any) => {
|
||||||
|
logCallback!(s)
|
||||||
|
this.logger.info(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
let privateKey: string|null = null
|
||||||
|
|
||||||
const ssh = new Client()
|
const ssh = new Client()
|
||||||
session.ssh = ssh
|
session.ssh = ssh
|
||||||
@@ -197,7 +208,7 @@ export class SSHService {
|
|||||||
if (await fs.exists(WINDOWS_OPENSSH_AGENT_PIPE)) {
|
if (await fs.exists(WINDOWS_OPENSSH_AGENT_PIPE)) {
|
||||||
agent = WINDOWS_OPENSSH_AGENT_PIPE
|
agent = WINDOWS_OPENSSH_AGENT_PIPE
|
||||||
} else {
|
} else {
|
||||||
const pageantRunning = new Promise<boolean>(resolve => {
|
const pageantRunning = await new Promise<boolean>(resolve => {
|
||||||
windowsProcessTreeNative.getProcessList(list => { // eslint-disable-line block-scoped-var
|
windowsProcessTreeNative.getProcessList(list => { // eslint-disable-line block-scoped-var
|
||||||
resolve(list.some(x => x.name === 'pageant.exe'))
|
resolve(list.some(x => x.name === 'pageant.exe'))
|
||||||
}, 0)
|
}, 0)
|
||||||
@@ -212,6 +223,7 @@ export class SSHService {
|
|||||||
|
|
||||||
const authMethodsLeft = ['none']
|
const authMethodsLeft = ['none']
|
||||||
if (!session.connection.auth || session.connection.auth === 'publicKey') {
|
if (!session.connection.auth || session.connection.auth === 'publicKey') {
|
||||||
|
privateKey = await this.loadPrivateKeyForSession(session, log)
|
||||||
if (!privateKey) {
|
if (!privateKey) {
|
||||||
log('\r\nPrivate key auth selected, but no key is loaded\r\n')
|
log('\r\nPrivate key auth selected, but no key is loaded\r\n')
|
||||||
} else {
|
} else {
|
||||||
|
@@ -157,12 +157,25 @@ rimraf@~2.6.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.1.3"
|
glob "^7.1.3"
|
||||||
|
|
||||||
|
run-script-os@^1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/run-script-os/-/run-script-os-1.1.3.tgz#1069b418307f4fd36ff056b5eda309c273fca8b0"
|
||||||
|
integrity sha512-xPlzE6533nvWVea5z7e5J7+JAIepfpxTu/HLGxcjJYlemVukOCWJBaRCod/DWXJFRIWEFOgSGbjd2m1QWTJi5w==
|
||||||
|
|
||||||
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
ssh2-streams@^0.4.2, ssh2-streams@~0.4.10:
|
ssh2-streams@Eugeny/ssh2-streams#75f6d3425d071ac73a18fd46e2f5e738bfe897c5:
|
||||||
|
version "0.4.10"
|
||||||
|
resolved "https://codeload.github.com/Eugeny/ssh2-streams/tar.gz/75f6d3425d071ac73a18fd46e2f5e738bfe897c5"
|
||||||
|
dependencies:
|
||||||
|
asn1 "~0.2.0"
|
||||||
|
bcrypt-pbkdf "^1.0.2"
|
||||||
|
streamsearch "~0.1.2"
|
||||||
|
|
||||||
|
ssh2-streams@~0.4.10:
|
||||||
version "0.4.10"
|
version "0.4.10"
|
||||||
resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34"
|
resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34"
|
||||||
integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==
|
integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==
|
||||||
|
@@ -156,16 +156,28 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
|||||||
this.resetZoom()
|
this.resetZoom()
|
||||||
break
|
break
|
||||||
case 'previous-word':
|
case 'previous-word':
|
||||||
this.sendInput('\x1bb')
|
this.sendInput({
|
||||||
|
[Platform.Windows]: '\x1b[1;5D',
|
||||||
|
[Platform.macOS]: '\x1bb',
|
||||||
|
[Platform.Linux]: '\x1bb',
|
||||||
|
}[this.hostApp.platform])
|
||||||
break
|
break
|
||||||
case 'next-word':
|
case 'next-word':
|
||||||
this.sendInput('\x1bf')
|
this.sendInput({
|
||||||
|
[Platform.Windows]: '\x1b[1;5C',
|
||||||
|
[Platform.macOS]: '\x1bf',
|
||||||
|
[Platform.Linux]: '\x1bf',
|
||||||
|
}[this.hostApp.platform])
|
||||||
break
|
break
|
||||||
case 'delete-previous-word':
|
case 'delete-previous-word':
|
||||||
this.sendInput('\x1b\x7f')
|
this.sendInput('\x1b\x7f')
|
||||||
break
|
break
|
||||||
case 'delete-next-word':
|
case 'delete-next-word':
|
||||||
this.sendInput('\x1bd')
|
this.sendInput({
|
||||||
|
[Platform.Windows]: '\x1bd\x1b[3;5~',
|
||||||
|
[Platform.macOS]: '\x1bd',
|
||||||
|
[Platform.Linux]: '\x1bd',
|
||||||
|
}[this.hostApp.platform])
|
||||||
break
|
break
|
||||||
case 'search':
|
case 'search':
|
||||||
this.showSearchPanel = true
|
this.showSearchPanel = true
|
||||||
@@ -348,7 +360,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
|||||||
|
|
||||||
this.topPadded = this.hostApp.platform === Platform.macOS
|
this.topPadded = this.hostApp.platform === Platform.macOS
|
||||||
&& this.config.store.appearance.frame === 'thin'
|
&& this.config.store.appearance.frame === 'thin'
|
||||||
&& this.config.store.appearance.tabsLocation === 'bottom'
|
&& this.config.store.appearance.tabsLocation !== 'top'
|
||||||
|
|
||||||
if (this.config.store.terminal.background === 'colorScheme') {
|
if (this.config.store.terminal.background === 'colorScheme') {
|
||||||
if (this.config.store.terminal.colorScheme.background) {
|
if (this.config.store.terminal.colorScheme.background) {
|
||||||
|
@@ -109,6 +109,24 @@ h3.mb-3 Appearance
|
|||||||
(ngModelChange)='config.save()',
|
(ngModelChange)='config.save()',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.form-line
|
||||||
|
.header
|
||||||
|
.title Hide tab index
|
||||||
|
|
||||||
|
toggle(
|
||||||
|
[(ngModel)]='config.store.terminal.hideTabIndex',
|
||||||
|
(ngModelChange)='config.save();',
|
||||||
|
)
|
||||||
|
|
||||||
|
.form-line
|
||||||
|
.header
|
||||||
|
.title Hide tab close button
|
||||||
|
|
||||||
|
toggle(
|
||||||
|
[(ngModel)]='config.store.terminal.hideCloseButton',
|
||||||
|
(ngModelChange)='config.save();',
|
||||||
|
)
|
||||||
|
|
||||||
.form-line
|
.form-line
|
||||||
.header
|
.header
|
||||||
.title Fallback font
|
.title Fallback font
|
||||||
|
@@ -26,7 +26,7 @@ button.btn.btn-link(
|
|||||||
.mr-2
|
.mr-2
|
||||||
|
|
||||||
button.btn.btn-link(
|
button.btn.btn-link(
|
||||||
(click)='options.caseSensitive = !options.caseSensitive',
|
(click)='options.caseSensitive = !options.caseSensitive; saveSearchOptions()',
|
||||||
[class.active]='options.caseSensitive',
|
[class.active]='options.caseSensitive',
|
||||||
ngbTooltip='Case sensitivity',
|
ngbTooltip='Case sensitivity',
|
||||||
placement='bottom'
|
placement='bottom'
|
||||||
@@ -34,14 +34,14 @@ button.btn.btn-link(
|
|||||||
i.fa.fa-fw.fa-font
|
i.fa.fa-fw.fa-font
|
||||||
|
|
||||||
button.btn.btn-link(
|
button.btn.btn-link(
|
||||||
(click)='options.regex = !options.regex',
|
(click)='options.regex = !options.regex; saveSearchOptions()',
|
||||||
[class.active]='options.regex',
|
[class.active]='options.regex',
|
||||||
ngbTooltip='Regular expression',
|
ngbTooltip='Regular expression',
|
||||||
placement='bottom'
|
placement='bottom'
|
||||||
)
|
)
|
||||||
i.fa.fa-fw.fa-asterisk
|
i.fa.fa-fw.fa-asterisk
|
||||||
button.btn.btn-link(
|
button.btn.btn-link(
|
||||||
(click)='options.wholeWord = !options.wholeWord',
|
(click)='options.wholeWord = !options.wholeWord; saveSearchOptions()',
|
||||||
[class.active]='options.wholeWord',
|
[class.active]='options.wholeWord',
|
||||||
ngbTooltip='Whole word',
|
ngbTooltip='Whole word',
|
||||||
placement='bottom'
|
placement='bottom'
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { Component, Input, Output, EventEmitter } from '@angular/core'
|
import { Component, Input, Output, EventEmitter } from '@angular/core'
|
||||||
import { ToastrService } from 'ngx-toastr'
|
import { ToastrService } from 'ngx-toastr'
|
||||||
import { Frontend, SearchOptions } from '../frontends/frontend'
|
import { Frontend, SearchOptions } from '../frontends/frontend'
|
||||||
|
import { ConfigService } from 'terminus-core'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'search-panel',
|
selector: 'search-panel',
|
||||||
@@ -13,12 +14,14 @@ export class SearchPanelComponent {
|
|||||||
notFound = false
|
notFound = false
|
||||||
options: SearchOptions = {
|
options: SearchOptions = {
|
||||||
incremental: true,
|
incremental: true,
|
||||||
|
...this.config.store.terminal.searchOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Output() close = new EventEmitter()
|
@Output() close = new EventEmitter()
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private toastr: ToastrService,
|
private toastr: ToastrService,
|
||||||
|
public config: ConfigService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
onQueryChange (): void {
|
onQueryChange (): void {
|
||||||
@@ -45,4 +48,12 @@ export class SearchPanelComponent {
|
|||||||
this.toastr.error('Not found')
|
this.toastr.error('Not found')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveSearchOptions (): void {
|
||||||
|
this.config.store.terminal.searchOptions.regex = this.options.regex
|
||||||
|
this.config.store.terminal.searchOptions.caseSensitive = this.options.caseSensitive
|
||||||
|
this.config.store.terminal.searchOptions.wholeWord = this.options.wholeWord
|
||||||
|
|
||||||
|
this.config.save()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,8 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
ligatures: false,
|
ligatures: false,
|
||||||
cursor: 'block',
|
cursor: 'block',
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
|
hideTabIndex: false,
|
||||||
|
hideCloseButton: false,
|
||||||
customShell: '',
|
customShell: '',
|
||||||
rightClick: 'menu',
|
rightClick: 'menu',
|
||||||
pasteOnMiddleClick: true,
|
pasteOnMiddleClick: true,
|
||||||
@@ -65,6 +67,12 @@ export class TerminalConfigProvider extends ConfigProvider {
|
|||||||
recoverTabs: true,
|
recoverTabs: true,
|
||||||
warnOnMultilinePaste: true,
|
warnOnMultilinePaste: true,
|
||||||
showDefaultProfiles: true,
|
showDefaultProfiles: true,
|
||||||
|
searchRegexAlwaysEnabled: false,
|
||||||
|
searchOptions: {
|
||||||
|
regex: false,
|
||||||
|
wholeWord: false,
|
||||||
|
caseSensitive: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user