Compare commits

..

9 Commits

Author SHA1 Message Date
allcontributors[bot]
af15e70205 update README.es-ES.md [skip ci] 2024-09-03 08:27:11 +00:00
allcontributors[bot]
1665510d98 update .all-contributorsrc [skip ci] 2024-09-03 08:27:11 +00:00
allcontributors[bot]
7a7312c1b6 update README.pt-BR.md [skip ci] 2024-09-03 08:27:08 +00:00
allcontributors[bot]
648503a64b update README.de-DE.md [skip ci] 2024-09-03 08:26:07 +00:00
allcontributors[bot]
3af87f0994 update README.it-IT.md [skip ci] 2024-09-03 08:26:06 +00:00
allcontributors[bot]
21a623a1ee update README.ko-KR.md [skip ci] 2024-09-03 08:26:04 +00:00
allcontributors[bot]
afd9b460f8 update README.ru-RU.md [skip ci] 2024-09-03 08:26:04 +00:00
allcontributors[bot]
d345e2036a update README.zh-CN.md [skip ci] 2024-09-03 08:26:03 +00:00
allcontributors[bot]
e5777604cf update README.md [skip ci] 2024-09-03 08:26:02 +00:00
103 changed files with 3370 additions and 2329 deletions

View File

@@ -1330,12 +1330,12 @@
] ]
}, },
{ {
"login": "SelfHosted-Club", "login": "joerg",
"name": "SelfHosted", "name": "Jörg Herzinger",
"avatar_url": "https://avatars.githubusercontent.com/u/128927132?v=4", "avatar_url": "https://avatars.githubusercontent.com/u/166924?v=4",
"profile": "https://www.selfhosted.sg/", "profile": "https://github.com/joerg",
"contributions": [ "contributions": [
"financial" "code"
] ]
} }
], ],

View File

@@ -2,7 +2,7 @@ name: Package-Build
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
Lint: Lint:
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout - name: Checkout
@@ -17,7 +17,6 @@ jobs:
- name: Install deps - name: Install deps
run: | run: |
sudo apt-get update && sudo apt-get install -y libfontconfig1-dev
npm i -g yarn npm i -g yarn
cd app cd app
yarn yarn
@@ -32,21 +31,15 @@ jobs:
run: yarn run lint run: yarn run lint
macOS-Build: macOS-Build:
runs-on: macos-15 runs-on: macos-12
needs: Lint needs: Lint
strategy: strategy:
matrix: matrix:
include: include:
- arch: x86_64 - arch: x86_64
rust_triple: x86_64-apple-darwin
- arch: arm64 - arch: arm64
rust_triple: aarch64-apple-darwin
fail-fast: false fail-fast: false
env:
ARCH: ${{matrix.arch}}
RUST_TARGET_TRIPLE: ${{matrix.rust_triple}}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -58,14 +51,20 @@ jobs:
with: with:
node-version: 18 node-version: 18
- run: rustup target add ${{matrix.rust_triple}}
- name: Install deps - name: Install deps
run: | run: |
sudo -H pip3 install setuptools
sudo npm i -g yarn
yarn --network-timeout 1000000 yarn --network-timeout 1000000
env: env:
ARCH: ${{matrix.arch}} ARCH: ${{matrix.arch}}
- name: Fix cross build
run: |
rm -rf app/node_modules/cpu-features
rm -rf app/node_modules/ssh2/crypto/build
if: matrix.arch == 'arm64'
- name: Webpack - name: Webpack
run: yarn run build run: yarn run build
@@ -81,7 +80,7 @@ jobs:
- name: Build and sign packages - name: Build and sign packages
run: scripts/build-macos.mjs run: scripts/build-macos.mjs
if: github.event_name == 'push' && (github.ref_protected || startsWith(github.ref, 'refs/tags')) if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
env: env:
ARCH: ${{matrix.arch}} ARCH: ${{matrix.arch}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -96,7 +95,7 @@ jobs:
- name: Build packages without signing - name: Build packages without signing
run: scripts/build-macos.mjs run: scripts/build-macos.mjs
if: "! (github.event_name == 'push' && (github.ref_protected || startsWith(github.ref, 'refs/tags')))" if: "! (github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
env: env:
ARCH: ${{matrix.arch}} ARCH: ${{matrix.arch}}
# DEBUG: electron-builder,electron-builder:* # DEBUG: electron-builder,electron-builder:*
@@ -130,31 +129,25 @@ jobs:
path: artifact-zip path: artifact-zip
Linux-Build: Linux-Build:
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
needs: Lint needs: Lint
strategy: strategy:
matrix: matrix:
include: include:
- build-arch: x64 - build-arch: x64
arch: amd64 arch: amd64
rust_triple: x86_64-unknown-linux-gnu
- build-arch: arm64 - build-arch: arm64
arch: arm64 arch: arm64
rust_triple: aarch64-unknown-linux-gnu
triplet: aarch64-linux-gnu- triplet: aarch64-linux-gnu-
- build-arch: arm - build-arch: arm
arch: armhf arch: armhf
rust_triple: arm-unknown-linux-gnueabihf
triplet: arm-linux-gnueabihf- triplet: arm-linux-gnueabihf-
fail-fast: false
env: env:
CC: ${{matrix.triplet}}gcc CC: ${{matrix.triplet}}gcc
CXX: ${{matrix.triplet}}g++ CXX: ${{matrix.triplet}}g++
ARCH: ${{matrix.build-arch}} ARCH: ${{matrix.build-arch}}
npm_config_arch: ${{matrix.build-arch}} npm_config_arch: ${{matrix.build-arch}}
npm_config_target_arch: ${{matrix.build-arch}} npm_config_target_arch: ${{matrix.build-arch}}
RUST_TARGET_TRIPLE: ${{matrix.rust_triple}}
steps: steps:
- name: Checkout - name: Checkout
@@ -167,12 +160,10 @@ jobs:
with: with:
node-version: 18 node-version: 18
- run: rustup target add ${{matrix.rust_triple}}
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install libfontconfig1-dev libarchive-tools zsh crossbuild-essential-${{matrix.arch}} sudo apt-get install libarchive-tools zsh crossbuild-essential-${{matrix.arch}}
- name: Setup tar to run as root - name: Setup tar to run as root
run: sudo chmod u+s "$(command -v tar)" run: sudo chmod u+s "$(command -v tar)"
@@ -243,14 +234,14 @@ jobs:
- name: Upload packages to packagecloud.io - name: Upload packages to packagecloud.io
uses: TykTechnologies/packagecloud-action@main uses: TykTechnologies/packagecloud-action@main
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
env: env:
PACKAGECLOUD_TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }} PACKAGECLOUD_TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }}
with: with:
repo: 'eugeny/tabby' repo: 'eugeny/tabby'
dir: 'dist' dir: 'dist'
rpmvers: 'el/9 el/8 ol/6 ol/7' rpmvers: 'el/9 el/8 ol/6 ol/7'
debvers: 'ubuntu/bionic ubuntu/focal ubuntu/hirsute ubuntu/impish ubuntu/jammy ubuntu/kinetic ubuntu/noble ubuntu/oracular debian/jessie debian/stretch debian/buster' debvers: 'ubuntu/bionic ubuntu/focal ubuntu/hirsute ubuntu/impish ubuntu/jammy ubuntu/kinetic ubuntu/noble debian/jessie debian/stretch debian/buster'
- uses: actions/upload-artifact@master - uses: actions/upload-artifact@master
name: Upload AppImage (${{matrix.arch}}) name: Upload AppImage (${{matrix.arch}})
@@ -289,45 +280,28 @@ jobs:
path: tabby-web.tar.gz path: tabby-web.tar.gz
if: matrix.build-arch == 'x64' if: matrix.build-arch == 'x64'
Windows-Build: Windows-Build:
runs-on: windows-latest runs-on: windows-2022
needs: Lint needs: Lint
strategy: strategy:
matrix: matrix:
include: include:
- arch: x64 - arch: x64
rust_triple: x86_64-pc-windows-msvc
- arch: arm64 - arch: arm64
rust_triple: aarch64-pc-windows-msvc
fail-fast: false fail-fast: false
env:
RUST_TARGET_TRIPLE: ${{matrix.rust_triple}}
ARCH: ${{matrix.arch}}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Code signing with Software Trust Manager
uses: digicert/ssm-code-signing@v1.0.0
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags'))
- name: Installing Node - name: Installing Node
uses: actions/setup-node@v3.7.0 uses: actions/setup-node@v3.7.0
with: with:
node-version: 18 node-version: 18
- run: npm i -g npx
- run: rustup target add ${{matrix.rust_triple}}
- name: Update node-gyp
run: |
npm install --global node-gyp@10.2.0
npm prefix -g | % {npm config set node_gyp "$_\node_modules\node-gyp\bin\node-gyp.js"}
- name: Build - name: Build
shell: powershell shell: powershell
run: | run: |
@@ -338,48 +312,20 @@ jobs:
env: env:
ARCH: ${{matrix.arch}} ARCH: ${{matrix.arch}}
- name: Decode certificate
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags'))
env:
SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64 }}
run: |
SM_CLIENT_CERT_FILE=$RUNNER_TEMP/certificate.p12
echo "$SM_CLIENT_CERT_FILE_B64" | base64 --decode > $SM_CLIENT_CERT_FILE
echo "SM_CLIENT_CERT_FILE=$SM_CLIENT_CERT_FILE" >> "$GITHUB_ENV"
shell: bash
- name: Build and sign packages - name: Build and sign packages
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags')) run: node scripts/build-windows.mjs
shell: powershell if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
run: |
Get-FileHash $env:SM_CLIENT_CERT_FILE -Algorithm MD5
smksp_registrar.exe list
smctl.exe healthcheck
smctl.exe keypair ls
smctl windows certsync --keypair-alias $env:SM_KEYPAIR_ALIAS
smctl.exe certificate ls
C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user
smksp_cert_sync.exe
# not used but necessary for electron-builder to run
$env:WIN_CSC_LINK=$env:SM_CLIENT_CERT_FILE
$env:WIN_CSC_KEY_PASSWORD=$env:SM_CLIENT_CERT_PASSWORD
node scripts/build-windows.mjs
env: env:
ARCH: ${{matrix.arch}} ARCH: ${{matrix.arch}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }} KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }}
SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
SM_PUBLISHER_NAME: ${{ secrets.SM_PUBLISHER_NAME }} WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
SM_API_KEY: ${{ vars.SM_API_KEY }} DEBUG: electron-builder,electron-builder:*
SM_HOST: ${{ vars.SM_HOST }}
SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ vars.SM_CODE_SIGNING_CERT_SHA1_HASH }}
SM_KEYPAIR_ALIAS: ${{ vars.SM_KEYPAIR_ALIAS }}
# DEBUG: electron-builder,electron-builder:*
- name: Build packages without signing - name: Build packages without signing
run: node scripts/build-windows.mjs run: node scripts/build-windows.mjs
if: "! (github.event_name == 'push' && (startsWith(github.ref, 'refs/tags')))" if: "! (github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
env: env:
ARCH: ${{matrix.arch}} ARCH: ${{matrix.arch}}

View File

@@ -18,7 +18,6 @@ jobs:
- name: Build - name: Build
run: | run: |
sudo apt update && sudo apt install libfontconfig1-dev
yarn cache clean yarn cache clean
cd app cd app
yarn yarn

1
.gitignore vendored
View File

@@ -33,6 +33,7 @@ docs/api
sentry.properties sentry.properties
sentry-symbols.js sentry-symbols.js
tabby-ssh/util/pagent.exe
*.psd *.psd
crowdin.yml crowdin.yml

View File

@@ -1 +0,0 @@
https://null.page/funding.json

View File

@@ -66,7 +66,7 @@ Diese README ist auch verfügbar in: <a href="./README.md">:gb: English</a> ·
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* Ein VT220-Terminal + verschiedene Erweiterungen * Ein V220-Terminal + verschiedene Erweiterungen
* Mehrere verschachtelte, geteilte Fenster * Mehrere verschachtelte, geteilte Fenster
* Tabs auf jeder Seite des Fensters * Tabs auf jeder Seite des Fensters
* Optional andockbares Fenster mit einem globalen Spawn-Hotkey ("Quake-Konsole") * Optional andockbares Fenster mit einem globalen Spawn-Hotkey ("Quake-Konsole")
@@ -342,8 +342,7 @@ Dank geht an diese wunderbaren Menschen ([emoji key](https://allcontributors.org
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/joerg"><img src="https://avatars.githubusercontent.com/u/166924?v=4?s=100" width="100px;" alt="Jörg Herzinger"/><br /><sub><b>Jörg Herzinger</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=joerg" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geodic"><img src="https://avatars.githubusercontent.com/u/64704703?v=4?s=100" width="100px;" alt="geodic"/><br /><sub><b>geodic</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=geodic" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -67,7 +67,7 @@ Este fichero README está disponible en: <a href="./README.md">:gb: English</a>
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* Un terminal VT220 + varias extensiones * Un terminal V220 + varias extensiones
* Múltiples paneles divididos anidados * Múltiples paneles divididos anidados
* Pestañas en cualquier lado de la ventana * Pestañas en cualquier lado de la ventana
* Ventana acoplable opcional con una tecla de acceso directo global ("consola de Quake") * Ventana acoplable opcional con una tecla de acceso directo global ("consola de Quake")
@@ -344,7 +344,7 @@ Gracias a estas maravillosas personas ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/joerg"><img src="https://avatars.githubusercontent.com/u/166924?v=4?s=100" width="100px;" alt="Jörg Herzinger"/><br /><sub><b>Jörg Herzinger</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=joerg" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -67,7 +67,7 @@ This README is also available in: <a href="./README.md">:gb: English</a> · <a
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* Terminal VT220 + berbagai macam ekstensi * Terminal V220 + berbagai macam ekstensi
* Beberapa pembagian panel * Beberapa pembagian panel
* Tab di sisi mana pun dari jendela * Tab di sisi mana pun dari jendela
* Jendela dockable opsional dengan hotkey spawn global ("Quake console") * Jendela dockable opsional dengan hotkey spawn global ("Quake console")
@@ -341,8 +341,6 @@ Terima kasih kepada mereka yang telah membantu ([emoji key](https://allcontribut
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geodic"><img src="https://avatars.githubusercontent.com/u/64704703?v=4?s=100" width="100px;" alt="geodic"/><br /><sub><b>geodic</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=geodic" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -68,7 +68,7 @@ Questo README è disponibile anche in: <a href="./README.md">:gb: English</a>
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* Un terminale VT220 + vari estensioni * Un terminale V220 + vari estensioni
* Suddivisione in pannelli * Suddivisione in pannelli
* Schede su qualsiasi lato della finestra * Schede su qualsiasi lato della finestra
* Finestra agganciabile opzionale con un tasto di scelta rapida ("Quake console") * Finestra agganciabile opzionale con un tasto di scelta rapida ("Quake console")
@@ -337,8 +337,7 @@ Grazie a queste persone meravigliose ([emoji key](https://allcontributors.org/do
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/joerg"><img src="https://avatars.githubusercontent.com/u/166924?v=4?s=100" width="100px;" alt="Jörg Herzinger"/><br /><sub><b>Jörg Herzinger</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=joerg" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geodic"><img src="https://avatars.githubusercontent.com/u/64704703?v=4?s=100" width="100px;" alt="geodic"/><br /><sub><b>geodic</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=geodic" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -74,7 +74,7 @@
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* VT220ターミナル各種拡張機能 * V220ターミナル各種拡張機能
* 複数ネストされたペイン分割に対応 * 複数ネストされたペイン分割に対応
* ウィンドウ内に自由に配置可能なタブ * ウィンドウ内に自由に配置可能なタブ
* グローバルホットキーで呼び出せるドックウィンドウ機能("Quakeコンソール" * グローバルホットキーで呼び出せるドックウィンドウ機能("Quakeコンソール"
@@ -352,8 +352,6 @@ Windows上では、`Tabby.exe`がある場所と同じ場所に`data`フォル
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geodic"><img src="https://avatars.githubusercontent.com/u/64704703?v=4?s=100" width="100px;" alt="geodic"/><br /><sub><b>geodic</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=geodic" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -66,7 +66,7 @@ This README is also available in: <a href="./README.md">:gb: English</a> · <a
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* A VT220 터미널 + 다양한 확장 * A V220 터미널 + 다양한 확장
* 여러 개의 분할 창 중첩 * 여러 개의 분할 창 중첩
* 모든 측면에 탭이 위치함 * 모든 측면에 탭이 위치함
* 전역 스폰 단축키가 있는 도킹 가능한 윈도우 ("Quake console") * 전역 스폰 단축키가 있는 도킹 가능한 윈도우 ("Quake console")
@@ -336,8 +336,7 @@ Pull requests and plugins are welcome!
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/joerg"><img src="https://avatars.githubusercontent.com/u/166924?v=4?s=100" width="100px;" alt="Jörg Herzinger"/><br /><sub><b>Jörg Herzinger</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=joerg" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geodic"><img src="https://avatars.githubusercontent.com/u/64704703?v=4?s=100" width="100px;" alt="geodic"/><br /><sub><b>geodic</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=geodic" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -2,7 +2,7 @@
<p align="center"> <p align="center">
<a href="https://github.com/Eugeny/tabby/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/eugeny/tabby/total.svg?label=DOWNLOADS&logo=github&style=for-the-badge"></a> &nbsp; <a href="https://nightly.link/Eugeny/tabby/workflows/build/master"><img src="https://shields.io/badge/-Nightly%20Builds-orange?logo=hackthebox&logoColor=fff&style=for-the-badge"/></a> &nbsp; <a href="https://discord.gg/Vn7BjmzhtF"><img alt="Discord" src="https://img.shields.io/discord/1280890060195233934?style=for-the-badge&color=blue&logo=discord&logoColor=white&label=Discord"></a> &nbsp <a href="https://translate.tabby.sh/"><img alt="Translate" src="https://shields.io/badge/Translate-UI-white?logo=googletranslate&style=for-the-badge&color=white&logoColor=fff"></a> <a href="https://github.com/Eugeny/tabby/releases/latest"><img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/eugeny/tabby/total.svg?label=DOWNLOADS&logo=github&style=for-the-badge"></a> &nbsp; <a href="https://nightly.link/Eugeny/tabby/workflows/build/master"><img src="https://shields.io/badge/-Nightly%20Builds-orange?logo=hackthebox&logoColor=fff&style=for-the-badge"/></a> &nbsp; <a href="https://matrix.to/#/#tabby-general:matrix.org"><img alt="Matrix" src="https://img.shields.io/matrix/tabby-general:matrix.org?logo=matrix&style=for-the-badge&color=magenta"></a> &nbsp <a href="https://translate.tabby.sh/"><img alt="Translate" src="https://shields.io/badge/Translate-UI-white?logo=googletranslate&style=for-the-badge&color=white&logoColor=fff"></a> &nbsp; <a href="https://twitter.com/eugeeeeny"><img alt="Twitter" src="https://shields.io/badge/Subscribe-News-blue?logo=twitter&style=for-the-badge&color=blue"></a>
</p> </p>
<p align="center"> <p align="center">
@@ -72,7 +72,7 @@ This README is also available in: <a href="./README.es-ES.md">:es: Spanish</a>
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* A VT220 terminal + various extensions * A V220 terminal + various extensions
* Multiple nested split panes * Multiple nested split panes
* Tabs on any side of the window * Tabs on any side of the window
* Optional dockable window with a global spawn hotkey ("Quake console") * Optional dockable window with a global spawn hotkey ("Quake console")
@@ -360,8 +360,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/joerg"><img src="https://avatars.githubusercontent.com/u/166924?v=4?s=100" width="100px;" alt="Jörg Herzinger"/><br /><sub><b>Jörg Herzinger</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=joerg" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geodic"><img src="https://avatars.githubusercontent.com/u/64704703?v=4?s=100" width="100px;" alt="geodic"/><br /><sub><b>geodic</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=geodic" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -75,7 +75,7 @@ Ten plik README jest również dostępny w językach: <a href="./README.md">:gb
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* Konsola VT220 + wiele rozszerzeń * Konsola V220 + wiele rozszerzeń
* Wiele nakładających się podzielonych okien * Wiele nakładających się podzielonych okien
* Okna na każdej stronie ekranu * Okna na każdej stronie ekranu
* Opcjonalne dokowanie okna za pomocą skrótu ("Quake console") * Opcjonalne dokowanie okna za pomocą skrótu ("Quake console")

View File

@@ -67,7 +67,7 @@ Esse README também está disponível em: <a href="./README.md">:gb: English</a
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* Um terminal VT220 + várias extensões * Um terminal V220 + várias extensões
* Múltiplos painéis divididos aninhados * Múltiplos painéis divididos aninhados
* Guias em qualquer lado da janela * Guias em qualquer lado da janela
* Opção de minimizar para a barra de tarefas com uma tecla de atalho global ("Quake console") * Opção de minimizar para a barra de tarefas com uma tecla de atalho global ("Quake console")
@@ -345,8 +345,7 @@ Obrigado vai para essas pessoas maravilhosas ([emoji key](https://allcontributor
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/joerg"><img src="https://avatars.githubusercontent.com/u/166924?v=4?s=100" width="100px;" alt="Jörg Herzinger"/><br /><sub><b>Jörg Herzinger</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=joerg" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geodic"><img src="https://avatars.githubusercontent.com/u/64704703?v=4?s=100" width="100px;" alt="geodic"/><br /><sub><b>geodic</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=geodic" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -67,7 +67,7 @@
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* Терминал VT220 + различные дополнения; * Терминал V220 + различные дополнения;
* Деление окна на несколько панелей; * Деление окна на несколько панелей;
* Вкладки на любой стороне окна; * Вкладки на любой стороне окна;
* Опционально закрепляемое окно с глобальной горячей клавишей для вызова («Quake console»); * Опционально закрепляемое окно с глобальной горячей клавишей для вызова («Quake console»);
@@ -337,8 +337,7 @@ Pull-запросы и плагины приветствуются!
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/joerg"><img src="https://avatars.githubusercontent.com/u/166924?v=4?s=100" width="100px;" alt="Jörg Herzinger"/><br /><sub><b>Jörg Herzinger</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=joerg" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geodic"><img src="https://avatars.githubusercontent.com/u/64704703?v=4?s=100" width="100px;" alt="geodic"/><br /><sub><b>geodic</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=geodic" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -66,7 +66,7 @@
![](docs/readme-terminal.png) ![](docs/readme-terminal.png)
* 一个 VT220 终端 + 各种插件 * 一个 V220 终端 + 各种插件
* 多个嵌套的拆分窗格 * 多个嵌套的拆分窗格
* 可以将标签页设置在窗口的任意一侧 * 可以将标签页设置在窗口的任意一侧
* 带有全局生成热键的可选可停靠窗口“Quake console” * 带有全局生成热键的可选可停靠窗口“Quake console”
@@ -336,8 +336,7 @@
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/GeminiLn"><img src="https://avatars.githubusercontent.com/u/12425057?v=4?s=100" width="100px;" alt="Yu Qin"/><br /><sub><b>Yu Qin</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=GeminiLn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/fireblue"><img src="https://avatars.githubusercontent.com/u/1034929?v=4?s=100" width="100px;" alt="fireblue"/><br /><sub><b>fireblue</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=fireblue" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/marko1616"><img src="https://avatars.githubusercontent.com/u/45327989?v=4?s=100" width="100px;" alt="marko1616"/><br /><sub><b>marko1616</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=marko1616" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.selfhosted.sg/"><img src="https://avatars.githubusercontent.com/u/128927132?v=4?s=100" width="100px;" alt="SelfHosted"/><br /><sub><b>SelfHosted</b></sub></a><br /><a href="#financial-SelfHosted-Club" title="Financial">💵</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/joerg"><img src="https://avatars.githubusercontent.com/u/166924?v=4?s=100" width="100px;" alt="Jörg Herzinger"/><br /><sub><b>Jörg Herzinger</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=joerg" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geodic"><img src="https://avatars.githubusercontent.com/u/64704703?v=4?s=100" width="100px;" alt="geodic"/><br /><sub><b>geodic</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=geodic" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -108,7 +108,7 @@ export class Window {
this.webContents = this.window.webContents this.webContents = this.window.webContents
this.window.webContents.once('did-finish-load', () => { this.window.once('ready-to-show', () => {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
this.window.setVibrancy(macOSVibrancyType) this.window.setVibrancy(macOSVibrancyType)
} else if (process.platform === 'win32' && this.configStore.appearance?.vibrancy) { } else if (process.platform === 'win32' && this.configStore.appearance?.vibrancy) {
@@ -139,7 +139,7 @@ export class Window {
enableRemote(this.window.webContents) enableRemote(this.window.webContents)
this.window.loadFile(path.join(app.getAppPath(), 'dist', 'index.html')) this.window.loadURL(`file://${app.getAppPath()}/dist/index.html`, { extraHeaders: 'pragma: no-cache\n' })
this.window.webContents.setVisualZoomLevelLimits(1, 1) this.window.webContents.setVisualZoomLevelLimits(1, 1)
this.window.webContents.setZoomFactor(1) this.window.webContents.setZoomFactor(1)

View File

@@ -16,7 +16,7 @@
}, },
"dependencies": { "dependencies": {
"@electron/remote": "^2", "@electron/remote": "^2",
"node-pty": "^1.1.0-beta.14", "node-pty": "^1.0",
"any-promise": "^1.3.0", "any-promise": "^1.3.0",
"electron-config": "2.0.0", "electron-config": "2.0.0",
"electron-debug": "^3.2.0", "electron-debug": "^3.2.0",
@@ -30,7 +30,6 @@
"native-process-working-directory": "^1.0.2", "native-process-working-directory": "^1.0.2",
"npm": "6", "npm": "6",
"rxjs": "^7.5.7", "rxjs": "^7.5.7",
"russh": "0.1.4",
"source-map-support": "^0.5.20", "source-map-support": "^0.5.20",
"v8-compile-cache": "^2.3.0", "v8-compile-cache": "^2.3.0",
"yargs": "^17.7.2" "yargs": "^17.7.2"
@@ -65,8 +64,6 @@
}, },
"resolutions": { "resolutions": {
"*/node-abi": "^3", "*/node-abi": "^3",
"node-gyp": "^10.0.0", "node-gyp": "^10.0.0"
"nan": "2.22.0",
"node-addon-api": "^8.3.0"
} }
} }

View File

@@ -28,11 +28,6 @@
wrap-ansi "^8.1.0" wrap-ansi "^8.1.0"
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
"@napi-rs/cli@^2.18.3":
version "2.18.4"
resolved "https://registry.yarnpkg.com/@napi-rs/cli/-/cli-2.18.4.tgz#12bebfb7995902fa7ab43cc0b155a7f5a2caa873"
integrity sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==
"@ngx-translate/core@^14.0.0": "@ngx-translate/core@^14.0.0":
version "14.0.0" version "14.0.0"
resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-14.0.0.tgz#af421d0e1a28376843f0fed375cd2fae7630a5ff" resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-14.0.0.tgz#af421d0e1a28376843f0fed375cd2fae7630a5ff"
@@ -1495,26 +1490,25 @@ glasstron@0.1.1:
x11 "^2.3.0" x11 "^2.3.0"
glob@^10.2.2, glob@^10.3.10: glob@^10.2.2, glob@^10.3.10:
version "10.4.5" version "10.3.10"
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b"
integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==
dependencies: dependencies:
foreground-child "^3.1.0" foreground-child "^3.1.0"
jackspeak "^3.1.2" jackspeak "^2.3.5"
minimatch "^9.0.4" minimatch "^9.0.1"
minipass "^7.1.2" minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
package-json-from-dist "^1.0.0" path-scurry "^1.10.1"
path-scurry "^1.11.1"
glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.2.3" version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies: dependencies:
fs.realpath "^1.0.0" fs.realpath "^1.0.0"
inflight "^1.0.4" inflight "^1.0.4"
inherits "2" inherits "2"
minimatch "^3.1.1" minimatch "^3.0.4"
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
@@ -1937,10 +1931,10 @@ isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
jackspeak@^3.1.2: jackspeak@^2.3.5:
version "3.4.3" version "2.3.6"
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8"
integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==
dependencies: dependencies:
"@isaacs/cliui" "^8.0.2" "@isaacs/cliui" "^8.0.2"
optionalDependencies: optionalDependencies:
@@ -2292,18 +2286,13 @@ lowercase-keys@^1.0.0:
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
lru-cache@^10.0.1: lru-cache@^10.0.1, "lru-cache@^9.1.1 || ^10.0.0":
version "10.0.2" version "10.0.2"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.2.tgz#34504678cc3266b09b8dfd6fab4e1515258271b7" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.2.tgz#34504678cc3266b09b8dfd6fab4e1515258271b7"
integrity sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg== integrity sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==
dependencies: dependencies:
semver "^7.3.5" semver "^7.3.5"
lru-cache@^10.2.0:
version "10.4.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
lru-cache@^4.0.1: lru-cache@^4.0.1:
version "4.1.5" version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@@ -2423,17 +2412,10 @@ minimatch@^3.0.4:
dependencies: dependencies:
brace-expansion "^1.1.7" brace-expansion "^1.1.7"
minimatch@^3.1.1: minimatch@^9.0.1:
version "3.1.2" version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
dependencies:
brace-expansion "^1.1.7"
minimatch@^9.0.4:
version "9.0.5"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
dependencies: dependencies:
brace-expansion "^2.0.1" brace-expansion "^2.0.1"
@@ -2506,11 +2488,6 @@ minipass@^5.0.0:
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==
minipass@^7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
minizlib@^1.3.3: minizlib@^1.3.3:
version "1.3.3" version "1.3.3"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
@@ -2600,10 +2577,10 @@ mz@^2.7.0:
object-assign "^4.0.1" object-assign "^4.0.1"
thenify-all "^1.0.0" thenify-all "^1.0.0"
nan@2.22.0, nan@^2.17.0: nan@^2.17.0:
version "2.22.0" version "2.20.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3"
integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==
napi-build-utils@^1.0.1: napi-build-utils@^1.0.1:
version "1.0.2" version "1.0.2"
@@ -2648,10 +2625,20 @@ node-abi@^3.3.0:
dependencies: dependencies:
semver "^7.3.5" semver "^7.3.5"
node-addon-api@3.1.0, node-addon-api@6.1.0, node-addon-api@^3.0.2, node-addon-api@^3.1.0, node-addon-api@^4.0.0, node-addon-api@^4.3.0, node-addon-api@^7.1.0, node-addon-api@^8.3.0: node-addon-api@3.1.0, node-addon-api@^3.0.2, node-addon-api@^3.1.0:
version "8.3.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-8.3.0.tgz#ec3763f18befc1cdf66d11e157ce44d5eddc0603" resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz"
integrity sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg== integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
node-addon-api@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76"
integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==
node-addon-api@^4.0.0, node-addon-api@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
node-fetch-npm@^2.0.2: node-fetch-npm@^2.0.2:
version "2.0.4" version "2.0.4"
@@ -2683,12 +2670,12 @@ node-gyp@^10.0.0, node-gyp@^5.0.2, node-gyp@^5.1.0:
tar "^6.1.2" tar "^6.1.2"
which "^4.0.0" which "^4.0.0"
node-pty@^1.1.0-beta.14: node-pty@^1.0:
version "1.1.0-beta9" version "1.0.0"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta9.tgz#ed643cb3b398d031b4e31c216e8f3b0042435f1d" resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.0.0.tgz#7daafc0aca1c4ca3de15c61330373af4af5861fd"
integrity sha512-/Ue38pvXJdgRZ3+me1FgfglLd301GhJN0NStiotdt61tm43N5htUyR/IXOUzOKuNaFmCwIhy6nwb77Ky41LMbw== integrity sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==
dependencies: dependencies:
node-addon-api "^7.1.0" nan "^2.17.0"
nopt@^4.0.3: nopt@^4.0.3:
version "4.0.3" version "4.0.3"
@@ -3110,11 +3097,6 @@ p-try@^2.0.0:
resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
package-json-from-dist@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00"
integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==
package-json@^4.0.0: package-json@^4.0.0:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed"
@@ -3227,12 +3209,12 @@ path-parse@^1.0.6:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-scurry@^1.11.1: path-scurry@^1.10.1:
version "1.11.1" version "1.10.1"
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698"
integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==
dependencies: dependencies:
lru-cache "^10.2.0" lru-cache "^9.1.1 || ^10.0.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-type@^2.0.0: path-type@^2.0.0:
@@ -3621,13 +3603,6 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies: dependencies:
aproba "^1.1.1" aproba "^1.1.1"
russh@0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/russh/-/russh-0.1.4.tgz#89b9f4452f0a1a3673d4d5799cea17bdebd5e71a"
integrity sha512-8b78Gtd2D0b6HZ/2s2nGNv3VVTDmzR1fHXEY0g462kMicir2ChHgBIzdSzr1/HrMFdFqCGHnHBcdsuTp+Zshzw==
dependencies:
"@napi-rs/cli" "^2.18.3"
rxjs@^7.5.2, rxjs@^7.5.7: rxjs@^7.5.2, rxjs@^7.5.7:
version "7.5.7" version "7.5.7"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39"

View File

@@ -2,12 +2,14 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.cs.allow-jit</key> <key>com.apple.security.cs.allow-jit</key>
<true/> <true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key> <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/> <true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.device.microphone</key> <key>com.apple.security.device.microphone</key>
<true/> <true/>
<key>com.apple.security.device.camera</key> <key>com.apple.security.device.camera</key>

View File

@@ -32,8 +32,7 @@ extraResources:
- extras - extras
asarUnpack: asarUnpack:
- 'dist/*.map' - 'dist/*.map'
- node_modules/keytar/build/Release/*.node
- node_modules/**/*.node
win: win:
icon: "./build/windows/icon.ico" icon: "./build/windows/icon.ico"
artifactName: tabby-${version}-portable-${env.ARCH}.${ext} artifactName: tabby-${version}-portable-${env.ARCH}.${ext}
@@ -52,7 +51,6 @@ mac:
entitlements: "./build/mac/entitlements.plist" entitlements: "./build/mac/entitlements.plist"
entitlementsInherit: "./build/mac/entitlements.plist" entitlementsInherit: "./build/mac/entitlements.plist"
extendInfo: extendInfo:
ElectronTeamID: ${teamId}
NSRequiresAquaSystemAppearance: false NSRequiresAquaSystemAppearance: false
NSCameraUsageDescription: "A subprocess requests access to the device's camera." NSCameraUsageDescription: "A subprocess requests access to the device's camera."
NSMicrophoneUsageDescription: "A subprocess requests access to the device's microphone." NSMicrophoneUsageDescription: "A subprocess requests access to the device's microphone."
@@ -62,7 +60,6 @@ mac:
NSDownloadsFolderUsageDescription: "A subprocess requests access to the user's Downloads folder." NSDownloadsFolderUsageDescription: "A subprocess requests access to the user's Downloads folder."
NSNetworkVolumesUsageDescription: 'A subprocess requests access to files on a network volume.' NSNetworkVolumesUsageDescription: 'A subprocess requests access to files on a network volume.'
NSRemovableVolumesUsageDescription: 'A subprocess requests access to files on a removable volume.' NSRemovableVolumesUsageDescription: 'A subprocess requests access to files on a removable volume.'
NSAppleEventsUsageDescription: 'A subprocess requests permission to send AppleScript events to another application.'
linux: linux:
category: "Utility;TerminalEmulator;System" category: "Utility;TerminalEmulator;System"

File diff suppressed because it is too large Load Diff

View File

@@ -1,64 +1,63 @@
@echo off :: Copyright (c) 2012 Martin Ridgers
rem -- Copyright (c) 2012 Martin Ridgers :: License: http://opensource.org/licenses/MIT
rem -- Portions Copyright (c) 2020-2024 Christopher Antos
rem -- License: http://opensource.org/licenses/MIT
@echo off
setlocal enableextensions setlocal enableextensions
set clink_profile_arg= set clink_profile_arg=
set clink_quiet_arg= set clink_quiet_arg=
rem -- Mimic cmd.exe's behaviour when starting from the start menu. :: Mimic cmd.exe's behaviour when starting from the start menu.
if /i "%~1"=="startmenu" ( if /i "%1"=="startmenu" (
cd /d "%userprofile%" cd /d "%userprofile%"
shift shift
) )
rem -- Check for the --profile option. :: Check for the --profile option.
if /i "%~1"=="--profile" ( if /i "%1"=="--profile" (
set clink_profile_arg=--profile "%~2" set clink_profile_arg=--profile "%~2"
shift shift
shift shift
) )
rem -- Check for the --quiet option. :: Check for the --quiet option.
if /i "%~1"=="--quiet" ( if /i "%1"=="--quiet" (
set clink_quiet_arg= --quiet set clink_quiet_arg= --quiet
shift shift
) )
rem -- If the .bat is run without any arguments, then start a cmd.exe instance. :: If the .bat is run without any arguments, then start a cmd.exe instance.
if _%1==_ ( if "%1"=="" (
call :launch call :launch
goto :end goto :end
) )
rem -- Test for autorun. :: Test for autorun.
if defined CLINK_NOAUTORUN if /i "%~1"=="inject" if /i "%~2"=="--autorun" goto :end if defined CLINK_NOAUTORUN if /i "%1"=="inject" if /i "%2"=="--autorun" goto :end
rem -- Forward to appropriate loader, and endlocal before inject tags the prompt. :: Endlocal before inject tags the prompt.
endlocal
:: Pass through to appropriate loader.
if /i "%processor_architecture%"=="x86" ( if /i "%processor_architecture%"=="x86" (
endlocal
"%~dp0\clink_x86.exe" %* "%~dp0\clink_x86.exe" %*
) else if /i "%processor_architecture%"=="arm64" ( ) else if /i "%processor_architecture%"=="arm64" (
endlocal
"%~dp0\clink_arm64.exe" %* "%~dp0\clink_arm64.exe" %*
) else if /i "%processor_architecture%"=="amd64" ( ) else if /i "%processor_architecture%"=="amd64" (
if defined processor_architew6432 ( if defined processor_architew6432 (
endlocal
"%~dp0\clink_x86.exe" %* "%~dp0\clink_x86.exe" %*
) else ( ) else (
endlocal
"%~dp0\clink_x64.exe" %* "%~dp0\clink_x64.exe" %*
) )
) )
goto :end :end
goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:launch :launch
setlocal enableextensions setlocal
set WT_PROFILE_ID= set WT_PROFILE_ID=
set WT_SESSION= set WT_SESSION=
start "Clink" cmd.exe /s /k ""%~dpnx0" inject %clink_profile_arg%%clink_quiet_arg%" start "Clink" cmd.exe /s /k ""%~dpnx0" inject %clink_profile_arg%%clink_quiet_arg%"
endlocal endlocal
exit /b 0
:end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -4,10 +4,10 @@
# Override the built-in Readline defaults with ones that provide a more # Override the built-in Readline defaults with ones that provide a more
# enhanced Clink experience. # enhanced Clink experience.
set colored-completion-prefix on colored-completion-prefix on
set colored-stats on colored-stats on
set mark-symlinked-directories on mark-symlinked-directories on
set completion-auto-query-items on completion-auto-query-items on
set history-point-at-end-of-anchored-search on history-point-at-end-of-anchored-search on
set search-ignore-case on search-ignore-case on

View File

@@ -4,26 +4,28 @@
# Override built-in default settings with ones that provide a more # Override built-in default settings with ones that provide a more
# enhanced Clink experience. # enhanced Clink experience.
autosuggest.enable = True
clink.default_bindings = windows clink.default_bindings = windows
cmd.ctrld_exits = False cmd.ctrld_exits = False
color.arginfo = sgr 38;5;172 color.arginfo = sgr 38;5;172
color.argmatcher = sgr 1;38;5;40 color.argmatcher = sgr 1;38;5;40
color.cmd = bold color.cmd = sgr 1;38;5;231
color.cmdredir = sgr 38;5;172 color.cmdredir = sgr 38;5;172
color.cmdsep = sgr 38;5;135 color.cmdsep = sgr 38;5;214
color.comment_row = sgr 38;5;87;48;5;18 color.comment_row = sgr 38;5;87;48;5;18
color.description = sgr 38;5;39 color.description = sgr 38;5;39
color.doskey = sgr 1;38;5;75 color.doskey = sgr 1;38;5;75
color.executable = sgr 1;38;5;33 color.executable = sgr 1;38;5;33
color.filtered = bold color.filtered = sgr 38;5;231
color.flag = sgr 38;5;117 color.flag = sgr 38;5;117
color.hidden = sgr 38;5;160 color.hidden = sgr 38;5;160
color.histexpand = sgr 97;48;5;55 color.histexpand = sgr 97;48;5;55
color.horizscroll = sgr 38;5;16;48;5;30 color.horizscroll = sgr 38;5;16;48;5;30
color.input = sgr 38;5;214 color.input = sgr 38;5;222
color.readonly = sgr 38;5;28 color.readonly = sgr 38;5;28
color.selected_completion = sgr 7 color.selected_completion = sgr 38;5;16;48;5;254
color.selection = sgr 38;5;16;48;5;179 color.selection = sgr 38;5;16;48;5;179
color.suggestion = sgr 38;5;239
color.unrecognized = sgr 38;5;203 color.unrecognized = sgr 38;5;203
history.max_lines = 25000 history.max_lines = 25000
history.time_stamp = show history.time_stamp = show

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Afrikaans\n" "Language-Team: Afrikaans\n"
"Language: af_ZA\n" "Language: af_ZA\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Bulgarian\n" "Language-Team: Bulgarian\n"
"Language: bg_BG\n" "Language: bg_BG\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Czech\n" "Language-Team: Czech\n"
"Language: cs_CZ\n" "Language: cs_CZ\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Danish\n" "Language-Team: Danish\n"
"Language: da_DK\n" "Language: da_DK\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"
@@ -211,7 +211,7 @@ msgstr "Sløring"
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:28 #: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:28
msgid "Bold font weight" msgid "Bold font weight"
msgstr "Fed skriftvægt" msgstr ""
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:132 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:132
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:80 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:80

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: German\n" "Language-Team: German\n"
"Language: de_DE\n" "Language: de_DE\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: English, United Kingdom\n" "Language-Team: English, United Kingdom\n"
"Language: en_GB\n" "Language: en_GB\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Spanish\n" "Language-Team: Spanish\n"
"Language: es_ES\n" "Language: es_ES\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: French\n" "Language-Team: French\n"
"Language: fr_FR\n" "Language: fr_FR\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Croatian\n" "Language-Team: Croatian\n"
"Language: hr_HR\n" "Language: hr_HR\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Indonesian\n" "Language-Team: Indonesian\n"
"Language: id_ID\n" "Language: id_ID\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Italian\n" "Language-Team: Italian\n"
"Language: it_IT\n" "Language: it_IT\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"
@@ -23,7 +23,7 @@ msgstr "{name} copia"
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:77 #: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:77
msgid "A second font family used to display characters missing in the main font" msgid "A second font family used to display characters missing in the main font"
msgstr "Un set di caratteri secondario usato per mostrare quelli mancanti del font principale" msgstr "Una famiglia di font secondaria usata per visualizzare quelle mancanti nella font principale"
#: tabby-core/src/components/transfersMenu.component.ts:49 #: tabby-core/src/components/transfersMenu.component.ts:49
msgid "Abort all" msgid "Abort all"
@@ -192,7 +192,7 @@ msgstr "Modalità tasto backspace"
#: tabby-serial/src/components/serialTab.component.ts:93 #: tabby-serial/src/components/serialTab.component.ts:93
#: tabby-serial/src/profiles.ts:88 #: tabby-serial/src/profiles.ts:88
msgid "Baud rate" msgid "Baud rate"
msgstr "Velocità trasmissione" msgstr "Velocità di trasmissione"
#: tabby-terminal/src/hotkeys.ts:22 #: tabby-terminal/src/hotkeys.ts:22
msgid "Beginning of the line" msgid "Beginning of the line"
@@ -479,7 +479,7 @@ msgstr "Crea cartella"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:90 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:90
msgid "Current" msgid "Current"
msgstr "Attuale" msgstr "Corrente"
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsForMode.component.html:3 #: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsForMode.component.html:3
msgid "Current color scheme" msgid "Current color scheme"
@@ -487,7 +487,7 @@ msgstr "Tema in uso"
#: locale/tmp-html/tabby-ssh/src/components/hostKeyPromptModal.component.html:17 #: locale/tmp-html/tabby-ssh/src/components/hostKeyPromptModal.component.html:17
msgid "Current host key fingerprint" msgid "Current host key fingerprint"
msgstr "Firma chiave host attuale" msgstr "Firma della chiave host attuale"
#: tabby-core/src/tabContextMenu.ts:184 #: tabby-core/src/tabContextMenu.ts:184
msgid "Current process: {name}" msgid "Current process: {name}"
@@ -495,7 +495,7 @@ msgstr "Processo attuale: {name}"
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:51 #: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:51
msgid "Cursor shape" msgid "Cursor shape"
msgstr "Forma cursore" msgstr "Forma del cursore"
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsForMode.component.html:46 #: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsForMode.component.html:46
msgid "Custom" msgid "Custom"
@@ -531,7 +531,7 @@ msgstr "\"Connetti a\" predefinito"
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:93 #: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:93
msgid "Default connection type used by quick connect feature (ex. SSH, Telnet)" msgid "Default connection type used by quick connect feature (ex. SSH, Telnet)"
msgstr "Tipo di connessione predefinita usato dalla funzione di connessione rapida (es. SSH, Telnet)" msgstr "Tipo di connessione predefinito usato dalla funzione di connessione rapida (es. SSH, Telnet)"
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:8 #: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:8
msgid "Default profile for new tabs" msgid "Default profile for new tabs"
@@ -791,7 +791,7 @@ msgstr "Cancella configurazione"
#: locale/tmp-html/tabby-settings/src/components/vaultSettingsTab.component.html:12 #: locale/tmp-html/tabby-settings/src/components/vaultSettingsTab.component.html:12
msgid "Erase the Vault" msgid "Erase the Vault"
msgstr "Cancella cassaforte" msgstr "Cancella la cassaforte"
#: locale/tmp-html/tabby-plugin-manager/src/components/pluginsSettingsTab.component.html:6 #: locale/tmp-html/tabby-plugin-manager/src/components/pluginsSettingsTab.component.html:6
msgid "Error in {plugin}:" msgid "Error in {plugin}:"
@@ -1189,7 +1189,7 @@ msgstr "Lingua"
#: locale/tmp-html/tabby-ssh/src/components/hostKeyPromptModal.component.html:11 #: locale/tmp-html/tabby-ssh/src/components/hostKeyPromptModal.component.html:11
msgid "Last known host key fingerprint" msgid "Last known host key fingerprint"
msgstr "Ultima firma nota chiave host" msgstr "Ultima firma nota della chiave host"
#: tabby-ssh/src/tabContextMenu.ts:32 #: tabby-ssh/src/tabContextMenu.ts:32
msgid "Launch WinSCP" msgid "Launch WinSCP"
@@ -1439,7 +1439,7 @@ msgstr "Nelle discussioni di GitHub"
#: locale/tmp-html/tabby-settings/src/components/editProfileModal.component.html:47 #: locale/tmp-html/tabby-settings/src/components/editProfileModal.component.html:47
msgid "Only close the tab when session is explicitly terminated" msgid "Only close the tab when session is explicitly terminated"
msgstr "Chiudi la scheda solo quando la sessione è esplicitamente terminata" msgstr "Chiude la scheda solo quando la sessione è esplicitamente terminata"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:46 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:46
msgid "Opacity" msgid "Opacity"
@@ -1476,11 +1476,11 @@ msgstr "Arancione"
#: tabby-electron/src/shells/macDefault.ts:25 #: tabby-electron/src/shells/macDefault.ts:25
msgid "OS default" msgid "OS default"
msgstr "Predefinito S.O." msgstr "Predefinito del OS"
#: tabby-electron/src/shells/winDefault.ts:43 #: tabby-electron/src/shells/winDefault.ts:43
msgid "OS default ({name})" msgid "OS default ({name})"
msgstr "Predefinito S.O. ({name})" msgstr "Predefinito del OS ({name})"
#: tabby-terminal/src/components/streamProcessingSettings.component.ts:46 #: tabby-terminal/src/components/streamProcessingSettings.component.ts:46
msgid "Output is shown as a hexdump" msgid "Output is shown as a hexdump"
@@ -1678,7 +1678,7 @@ msgstr "Connessione raw socket"
#: locale/tmp-html/tabby-ssh/src/components/sshProfileSettings.component.html:166 #: locale/tmp-html/tabby-ssh/src/components/sshProfileSettings.component.html:166
msgid "Ready Timeout (Milliseconds)" msgid "Ready Timeout (Milliseconds)"
msgstr "Tempo stato pronto (millisecondi)" msgstr "Tempo di preparazione (in millisecondi)"
#: tabby-core/src/services/profiles.service.ts:235 #: tabby-core/src/services/profiles.service.ts:235
#: tabby-core/src/services/profiles.service.ts:249 #: tabby-core/src/services/profiles.service.ts:249
@@ -1694,7 +1694,7 @@ msgstr "Recente"
#: tabby-terminal/src/tabContextMenu.ts:115 #: tabby-terminal/src/tabContextMenu.ts:115
#: tabby-terminal/src/tabContextMenu.ts:119 #: tabby-terminal/src/tabContextMenu.ts:119
msgid "Reconnect" msgid "Reconnect"
msgstr "Riconnetti" msgstr "Riconetti"
#: tabby-terminal/src/hotkeys.ts:102 #: tabby-terminal/src/hotkeys.ts:102
msgid "Reconnect current tab (Serial/Telnet/SSH)" msgid "Reconnect current tab (Serial/Telnet/SSH)"
@@ -1723,7 +1723,7 @@ msgstr "Remoto"
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:124 #: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:124
msgid "Remove whitespace and newlines around the copied text" msgid "Remove whitespace and newlines around the copied text"
msgstr "Rimuovi spazi bianchi e a ritorni a capo dal testo copiato" msgstr "Rimuovi spazi bianchi e a riorni a capo dal testo copiato"
#: locale/tmp-html/tabby-settings/src/components/vaultSettingsTab.component.html:26 #: locale/tmp-html/tabby-settings/src/components/vaultSettingsTab.component.html:26
#: tabby-core/src/tabContextMenu.ts:120 #: tabby-core/src/tabContextMenu.ts:120
@@ -2011,7 +2011,7 @@ msgstr "Visualizza predefiniti"
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:146 #: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:146
msgid "Show Mixer" msgid "Show Mixer"
msgstr "Visualizza mixer" msgstr "Visualizza Mixer"
#: tabby-core/src/hotkeys.ts:56 #: tabby-core/src/hotkeys.ts:56
msgid "Show pane labels (for rearranging)" msgid "Show pane labels (for rearranging)"
@@ -2382,7 +2382,7 @@ msgstr "Carica come nuova configurazione"
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:39 #: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:39
msgid "Use {altKeyName} as the Meta key" msgid "Use {altKeyName} as the Meta key"
msgstr "Come chiave meta usa {altKeyName} " msgstr "Usa {altKeyName} come chiave Meta"
#: locale/tmp-html/tabby-local/src/components/shellSettingsTab.component.html:5 #: locale/tmp-html/tabby-local/src/components/shellSettingsTab.component.html:5
msgid "Use ConPTY" msgid "Use ConPTY"
@@ -2554,7 +2554,7 @@ msgstr "Giallo"
#: locale/tmp-html/tabby-settings/src/components/setVaultPassphraseModal.component.html:4 #: locale/tmp-html/tabby-settings/src/components/setVaultPassphraseModal.component.html:4
msgid "You can change it later, but it's unrecoverable if forgotten." msgid "You can change it later, but it's unrecoverable if forgotten."
msgstr "Si può modificare più tardi, ma è irrecuperabile se dimenticata." msgstr "Si può cambiare più tardi, ma è irrecuperabile se dimenticata."
#: locale/tmp-html/tabby-ssh/src/components/hostKeyPromptModal.component.html:7 #: locale/tmp-html/tabby-ssh/src/components/hostKeyPromptModal.component.html:7
msgid "You could be under a man-in-the-middle attack right now, or the host key could have just been changed." msgid "You could be under a man-in-the-middle attack right now, or the host key could have just been changed."

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Japanese\n" "Language-Team: Japanese\n"
"Language: ja_JP\n" "Language: ja_JP\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"
@@ -185,7 +185,7 @@ msgstr "透過の種類"
#: locale/tmp-html/tabby-terminal/src/components/inputProcessingSettings.component.html:4 #: locale/tmp-html/tabby-terminal/src/components/inputProcessingSettings.component.html:4
msgid "Backspace key mode" msgid "Backspace key mode"
msgstr "Backspaceキー" msgstr "BackSpaceキー"
#: locale/tmp-html/tabby-serial/src/components/serialProfileSettings.component.html:14 #: locale/tmp-html/tabby-serial/src/components/serialProfileSettings.component.html:14
#: tabby-serial/src/components/serialTab.component.ts:93 #: tabby-serial/src/components/serialTab.component.ts:93
@@ -526,7 +526,7 @@ msgstr "垂直方向の分割サイズを縮小"
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:92 #: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:92
msgid "Default \"Connect to\" type" msgid "Default \"Connect to\" type"
msgstr "既定の接続方式" msgstr "既定の接続"
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:93 #: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:93
msgid "Default connection type used by quick connect feature (ex. SSH, Telnet)" msgid "Default connection type used by quick connect feature (ex. SSH, Telnet)"
@@ -630,7 +630,7 @@ msgstr "動的なタブ名を無効にする"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:218 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:218
msgid "Disable fluent background while dragging" msgid "Disable fluent background while dragging"
msgstr "ドラッグ中はFluent背景を無効する" msgstr "ドラッグ中はFluentを無効する"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:204 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:204
msgid "Disable GPU acceleration" msgid "Disable GPU acceleration"
@@ -757,7 +757,7 @@ msgstr "アップデートが利用可能になったら自動的にインスト
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:211 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:211
msgid "Enable fluent background option" msgid "Enable fluent background option"
msgstr "Fluent背景オプションを有効にする" msgstr "Fluent背景設定を有効にする"
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:16 #: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:16
msgid "Enable font ligatures" msgid "Enable font ligatures"
@@ -806,7 +806,7 @@ msgstr "例:"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:212 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:212
msgid "Experimental Windows 10 background style known to cause issues" msgid "Experimental Windows 10 background style known to cause issues"
msgstr "不具合が報告されているWindows10風の実験的な背景" msgstr "Windows10では問題が発生する場合があります。"
#: locale/tmp-html/tabby-settings/src/components/vaultSettingsTab.component.html:28 #: locale/tmp-html/tabby-settings/src/components/vaultSettingsTab.component.html:28
msgid "Export" msgid "Export"
@@ -834,7 +834,7 @@ msgstr "固定"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:219 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:219
msgid "Fluent background sometimes causes drag lag" msgid "Fluent background sometimes causes drag lag"
msgstr "Fluent背景はドラッグ中にラグを発生させることがあります" msgstr "Fluentはドラッグ中にラグを発生させる場合があります"
#: tabby-terminal/src/tabContextMenu.ts:82 #: tabby-terminal/src/tabContextMenu.ts:82
msgid "Focus all panes" msgid "Focus all panes"
@@ -1052,7 +1052,7 @@ msgstr "ホットキー"
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:83 #: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:83
msgid "How Tabby presents itself through environment vars" msgid "How Tabby presents itself through environment vars"
msgstr "環境変数を利用してTabbyを他の端末として認識させます" msgstr "環境変数を利用してTabbyを他の端末のように扱わせます"
#: locale/tmp-html/tabby-ssh/src/components/sshProfileSettings.component.html:24 #: locale/tmp-html/tabby-ssh/src/components/sshProfileSettings.component.html:24
msgid "HTTP proxy" msgid "HTTP proxy"
@@ -1299,7 +1299,7 @@ msgstr "\"未分類\" に移動"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:197 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:197
msgid "Moving the mouse over an inactive pane will cause it to activate" msgid "Moving the mouse over an inactive pane will cause it to activate"
msgstr "マウスの移動に合わせてアクティブなペインを切り替えます。" msgstr "マウスの移動アクティブなペインを切り替えることができます。"
#: locale/tmp-html/tabby-settings/src/components/editProfileGroupModal.component.html:9 #: locale/tmp-html/tabby-settings/src/components/editProfileGroupModal.component.html:9
#: locale/tmp-html/tabby-settings/src/components/editProfileModal.component.html:12 #: locale/tmp-html/tabby-settings/src/components/editProfileModal.component.html:12
@@ -1366,7 +1366,7 @@ msgstr "新しいタブ: {profile}"
#: tabby-local/src/buttonProvider.ts:20 #: tabby-local/src/buttonProvider.ts:20
#: tabby-local/src/tabContextMenu.ts:27 #: tabby-local/src/tabContextMenu.ts:27
msgid "New terminal" msgid "New terminal"
msgstr "新しい端末" msgstr "新しいターミナル"
#: tabby-electron/src/hotkeys.ts:10 #: tabby-electron/src/hotkeys.ts:10
msgid "New window" msgid "New window"
@@ -1956,7 +1956,7 @@ msgstr "Tabbyを %COMSPEC% に設定"
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:69 #: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:69
msgid "Set to 0 to disable recent profiles" msgid "Set to 0 to disable recent profiles"
msgstr "セレクターに最近使用したプロファイルを表示したくない場合は0に設定してください" msgstr "最近使用したプロファイルをセレクターに表示しない場合は0に設定してください"
#: locale/tmp-html/tabby-ssh/src/components/sshSettingsTab.component.html:36 #: locale/tmp-html/tabby-ssh/src/components/sshSettingsTab.component.html:36
msgid "Sets the SSH agent's named pipe path." msgid "Sets the SSH agent's named pipe path."
@@ -1994,7 +1994,7 @@ msgstr "複数行の貼り付けをする際に確認画面を表示します"
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:75 #: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:75
msgid "Show built-in profiles in selector" msgid "Show built-in profiles in selector"
msgstr "標準プロファイルをセレクターに表示" msgstr "セレクターに標準プロファイルを表示する"
#: tabby-core/src/hotkeys.ts:12 #: tabby-core/src/hotkeys.ts:12
msgid "Show command selector" msgid "Show command selector"
@@ -2089,7 +2089,7 @@ msgstr "ソースコード"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:13 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:13
msgid "Spaciness" msgid "Spaciness"
msgstr "UI間隔" msgstr "UI間隔"
#: tabby-core/src/tabContextMenu.ts:75 #: tabby-core/src/tabContextMenu.ts:75
msgid "Split" msgid "Split"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Korean\n" "Language-Team: Korean\n"
"Language: ko_KR\n" "Language: ko_KR\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Polish\n" "Language-Team: Polish\n"
"Language: pl_PL\n" "Language: pl_PL\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Portuguese, Brazilian\n" "Language-Team: Portuguese, Brazilian\n"
"Language: pt_BR\n" "Language: pt_BR\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Portuguese\n" "Language-Team: Portuguese\n"
"Language: pt_PT\n" "Language: pt_PT\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Russian\n" "Language-Team: Russian\n"
"Language: ru_RU\n" "Language: ru_RU\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"
@@ -660,7 +660,7 @@ msgstr "Отключиться от {host}?"
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:30 #: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:30
msgid "Display images via Sixel escape sequences" msgid "Display images via Sixel escape sequences"
msgstr "Отображать изображения при помощи управляющей последовательности Sixel" msgstr "Отображать изображения при помощи сиксельной управляющей последовательности"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:85 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:85
msgid "Display on" msgid "Display on"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Serbian (Cyrillic)\n" "Language-Team: Serbian (Cyrillic)\n"
"Language: sr_SP\n" "Language: sr_SP\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"
@@ -342,7 +342,7 @@ msgstr ""
#: locale/tmp-html/tabby-serial/src/components/serialProfileSettings.component.html:81 #: locale/tmp-html/tabby-serial/src/components/serialProfileSettings.component.html:81
#: locale/tmp-html/tabby-ssh/src/components/sshProfileSettings.component.html:216 #: locale/tmp-html/tabby-ssh/src/components/sshProfileSettings.component.html:216
msgid "Colors" msgid "Colors"
msgstr "Боје" msgstr ""
#: tabby-core/src/hotkeys.ts:72 #: tabby-core/src/hotkeys.ts:72
msgid "Combine all tabs into the current tab" msgid "Combine all tabs into the current tab"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Swedish\n" "Language-Team: Swedish\n"
"Language: sv_SE\n" "Language: sv_SE\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Turkish\n" "Language-Team: Turkish\n"
"Language: tr_TR\n" "Language: tr_TR\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Ukrainian\n" "Language-Team: Ukrainian\n"
"Language: uk_UA\n" "Language: uk_UA\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"
@@ -100,12 +100,12 @@ msgstr "Дозволяє швидко відкрити термінал у ви
#: locale/tmp-html/tabby-core/src/components/welcomeTab.component.html:25 #: locale/tmp-html/tabby-core/src/components/welcomeTab.component.html:25
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsTab.component.html:11 #: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsTab.component.html:11
msgid "Always dark" msgid "Always dark"
msgstr "Завжди темна" msgstr "Завжди темно"
#: locale/tmp-html/tabby-core/src/components/welcomeTab.component.html:27 #: locale/tmp-html/tabby-core/src/components/welcomeTab.component.html:27
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsTab.component.html:13 #: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsTab.component.html:13
msgid "Always light" msgid "Always light"
msgstr "Завжди світла" msgstr "Завжди світити"
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:2 #: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:2
#: tabby-terminal/src/settings.ts:14 #: tabby-terminal/src/settings.ts:14

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Chinese Simplified\n" "Language-Team: Chinese Simplified\n"
"Language: zh_CN\n" "Language: zh_CN\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"
@@ -43,7 +43,7 @@ msgstr "辅助功能"
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:27 #: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:27
msgid "Acrylic background" msgid "Acrylic background"
msgstr "毛玻璃背景" msgstr "亚克力背景"
#: locale/tmp-html/tabby-local/src/components/commandLineEditor.component.html:24 #: locale/tmp-html/tabby-local/src/components/commandLineEditor.component.html:24
#: locale/tmp-html/tabby-local/src/components/environmentEditor.component.html:11 #: locale/tmp-html/tabby-local/src/components/environmentEditor.component.html:11

View File

@@ -10,7 +10,7 @@ msgstr ""
"Project-Id-Version: tabby\n" "Project-Id-Version: tabby\n"
"Language-Team: Chinese Traditional\n" "Language-Team: Chinese Traditional\n"
"Language: zh_TW\n" "Language: zh_TW\n"
"PO-Revision-Date: 2024-12-24 22:58\n" "PO-Revision-Date: 2024-07-10 09:04\n"
#: tabby-local/src/components/terminalTab.component.ts:113 #: tabby-local/src/components/terminalTab.component.ts:113
msgid "\"{command}\" is still running. Close?" msgid "\"{command}\" is still running. Close?"
@@ -1390,7 +1390,7 @@ msgstr "無色彩"
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:85 #: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:85
msgid "No modifier" msgid "No modifier"
msgstr "無修飾鍵" msgstr "不可修改"
#: locale/tmp-html/tabby-serial/src/components/serialProfileSettings.component.html:41 #: locale/tmp-html/tabby-serial/src/components/serialProfileSettings.component.html:41
msgid "None" msgid "None"

View File

@@ -39,11 +39,11 @@
"cross-env": "7.0.3", "cross-env": "7.0.3",
"css-loader": "^6.7.3", "css-loader": "^6.7.3",
"deep-equal": "2.0.5", "deep-equal": "2.0.5",
"electron": "^32.2.7", "electron": "^29",
"electron-builder": "^24.6.4", "electron-builder": "^24.6.4",
"electron-download": "^4.1.1", "electron-download": "^4.1.1",
"electron-installer-snap": "^5.1.0", "electron-installer-snap": "^5.1.0",
"@electron/rebuild": "^3.7.1", "electron-rebuild": "^3.2.9",
"eslint": "^8.48.0", "eslint": "^8.48.0",
"eslint-import-resolver-typescript": "^3.6.0", "eslint-import-resolver-typescript": "^3.6.0",
"eslint-plugin-import": "^2.28.1", "eslint-plugin-import": "^2.28.1",
@@ -55,7 +55,7 @@
"lru-cache": "^6.0.0", "lru-cache": "^6.0.0",
"macos-release": "^3.3.0", "macos-release": "^3.3.0",
"ngx-toastr": "^16.0.2", "ngx-toastr": "^16.0.2",
"node-abi": "^3.71.0", "node-abi": "^3.65.0",
"npmlog": "6.0.2", "npmlog": "6.0.2",
"npx": "^10.2.2", "npx": "^10.2.2",
"patch-package": "^6.4.7", "patch-package": "^6.4.7",
@@ -76,6 +76,7 @@
"source-code-pro": "^2.38.0", "source-code-pro": "^2.38.0",
"source-map-loader": "^4.0.1", "source-map-loader": "^4.0.1",
"source-sans-pro": "3.6.0", "source-sans-pro": "3.6.0",
"ssh2": "^1.14.0",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"svg-inline-loader": "^0.8.2", "svg-inline-loader": "^0.8.2",
"thenby": "^1.3.4", "thenby": "^1.3.4",
@@ -96,8 +97,7 @@
"*/pug": "^3", "*/pug": "^3",
"lzma-native": "^8.0.6", "lzma-native": "^8.0.6",
"**/graceful-fs": "^4.2.4", "**/graceful-fs": "^4.2.4",
"nan": "2.22.0", "nan": "2.17.0"
"node-gyp": "^10.0.0"
}, },
"scripts": { "scripts": {
"build": "npm run build:typings && node scripts/build-modules.mjs", "build": "npm run build:typings && node scripts/build-modules.mjs",

39
patches/ssh2+1.11.0.patch Normal file
View File

@@ -0,0 +1,39 @@
diff --git a/node_modules/ssh2/lib/protocol/keyParser.js b/node_modules/ssh2/lib/protocol/keyParser.js
index 9860e3f..ee82e51 100644
--- a/node_modules/ssh2/lib/protocol/keyParser.js
+++ b/node_modules/ssh2/lib/protocol/keyParser.js
@@ -15,6 +15,7 @@ const {
sign: sign_,
verify: verify_,
} = require('crypto');
+const { createVerify: createVerifyDSS } = require('browserify-sign')
const supportedOpenSSLCiphers = getCiphers();
const { Ber } = require('asn1');
@@ -404,6 +405,17 @@ const BaseKey = {
return new Error('No public key available');
if (!algo || typeof algo !== 'string')
algo = this[SYM_HASH_ALGO];
+
+ if (algo === 'dss1') {
+ const verifier = createVerifyDSS('DSA-SHA1');
+ verifier.update(data);
+ try {
+ return verifier.verify(pem, signature);
+ } catch (ex) {
+ return ex;
+ }
+ }
+
try {
return verify_(algo, data, pem, signature);
} catch (ex) {
@@ -1343,7 +1355,7 @@ function parseDER(data, baseType, comment, fullType) {
return new Error('Malformed OpenSSH public key');
pubPEM = genOpenSSLDSAPub(p, q, g, y);
pubSSH = genOpenSSHDSAPub(p, q, g, y);
- algo = 'sha1';
+ algo = 'dss1';
break;
}
case 'ssh-ed25519': {

View File

@@ -25,7 +25,7 @@ builder({
}, },
] : undefined, ] : undefined,
}, },
publish: (process.env.KEYGEN_TOKEN && isTag) ? 'always' : 'never', publish: process.env.KEYGEN_TOKEN ? isTag ? 'always' : 'onTagOrDraft' : 'never',
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
process.exit(1) process.exit(1)

View File

@@ -24,7 +24,6 @@ builder({
config: { config: {
extraMetadata: { extraMetadata: {
version: vars.version, version: vars.version,
teamId: process.env.APPLE_TEAM_ID,
}, },
mac: { mac: {
identity: !process.env.CI || process.env.CSC_LINK ? undefined : null, identity: !process.env.CI || process.env.CSC_LINK ? undefined : null,
@@ -42,7 +41,7 @@ builder({
}, },
] : undefined, ] : undefined,
}, },
publish: (process.env.KEYGEN_TOKEN && isTag) ? 'always' : 'never', publish: process.env.KEYGEN_TOKEN ? isTag ? 'always' : 'onTagOrDraft' : 'never',
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
process.exit(1) process.exit(1)

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
import { rebuild } from '@electron/rebuild' import { rebuild } from 'electron-rebuild'
import * as path from 'path' import * as path from 'path'
import * as vars from './vars.mjs' import * as vars from './vars.mjs'

View File

@@ -2,15 +2,11 @@
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
import { build as builder } from 'electron-builder' import { build as builder } from 'electron-builder'
import * as vars from './vars.mjs' import * as vars from './vars.mjs'
import { execSync } from 'child_process'
const isTag = (process.env.GITHUB_REF || process.env.BUILD_SOURCEBRANCH || '').startsWith('refs/tags/') const isTag = (process.env.GITHUB_REF || process.env.BUILD_SOURCEBRANCH || '').startsWith('refs/tags/')
const keypair = process.env.SM_KEYPAIR_ALIAS
process.env.ARCH = process.env.ARCH || process.arch process.env.ARCH = process.env.ARCH || process.arch
console.log('Signing enabled:', !!keypair)
builder({ builder({
dir: true, dir: true,
win: ['nsis', 'zip'], win: ['nsis', 'zip'],
@@ -26,33 +22,8 @@ builder({
channel: `latest-${process.env.ARCH}`, channel: `latest-${process.env.ARCH}`,
}, },
] : undefined, ] : undefined,
forceCodeSigning: !!keypair,
win: {
certificateSha1: process.env.SM_CODE_SIGNING_CERT_SHA1_HASH,
publisherName: process.env.SM_PUBLISHER_NAME,
signingHashAlgorithms: ['sha256'],
sign: keypair ? async function (configuration) {
console.log('Signing', configuration)
if (configuration.path) {
try {
const out = execSync(
`smctl sign --keypair-alias=${keypair} --input "${String(configuration.path)}"`
)
if (out.toString().includes('FAILED')) {
throw new Error(out.toString())
}
console.log(out.toString())
} catch (e) {
console.error(`Failed to sign ${configuration.path}`)
console.error(e)
process.exit(1)
}
}
} : undefined,
},
}, },
publish: process.env.KEYGEN_TOKEN ? isTag ? 'always' : 'onTagOrDraft' : 'never',
publish: (process.env.KEYGEN_TOKEN && isTag) ? 'always' : 'never',
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
process.exit(1) process.exit(1)

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
import { rebuild } from '@electron/rebuild' import { rebuild } from 'electron-rebuild'
import sh from 'shelljs' import sh from 'shelljs'
import path from 'node:path' import path from 'node:path'
import fs from 'node:fs' import fs from 'node:fs'

View File

@@ -63,24 +63,22 @@ export abstract class FileTransfer {
} }
export abstract class FileDownload extends FileTransfer { export abstract class FileDownload extends FileTransfer {
abstract write (buffer: Uint8Array): Promise<void> abstract write (buffer: Buffer): Promise<void>
} }
export abstract class FileUpload extends FileTransfer { export abstract class FileUpload extends FileTransfer {
abstract read (): Promise<Uint8Array> abstract read (): Promise<Buffer>
async readAll (): Promise<Uint8Array> { async readAll (): Promise<Buffer> {
const result = new Uint8Array(this.getSize()) const buffers: Buffer[] = []
let pos = 0
while (true) { while (true) {
const buf = await this.read() const buf = await this.read()
if (!buf.length) { if (!buf.length) {
break break
} }
result.set(buf, pos) buffers.push(Buffer.from(buf))
pos += buf.length
} }
return result return Buffer.concat(buffers)
} }
} }
@@ -263,12 +261,12 @@ export class HTMLFileUpload extends FileUpload {
return this.file.size return this.file.size
} }
async read (): Promise<Uint8Array> { async read (): Promise<Buffer> {
const result: any = await this.reader.read() const result: any = await this.reader.read()
if (result.done || !result.value) { if (result.done || !result.value) {
return new Uint8Array(0) return Buffer.from('')
} }
const chunk = new Uint8Array(result.value) const chunk = Buffer.from(result.value)
this.increaseProgress(chunk.length) this.increaseProgress(chunk.length)
return chunk return chunk
} }

View File

@@ -31,8 +31,6 @@ hotkeys:
__nonStructural: true __nonStructural: true
profile-selectors: profile-selectors:
__nonStructural: true __nonStructural: true
group-selectors:
__nonStructural: true
profiles: [] profiles: []
groups: [] groups: []
profileDefaults: profileDefaults:

View File

@@ -264,7 +264,6 @@ export class AppHotkeyProvider extends HotkeyProvider {
async provide (): Promise<HotkeyDescription[]> { async provide (): Promise<HotkeyDescription[]> {
const profiles = await this.profilesService.getProfiles() const profiles = await this.profilesService.getProfiles()
const groups = await this.profilesService.getProfileGroups()
return [ return [
...this.hotkeys, ...this.hotkeys,
...profiles.map(profile => ({ ...profiles.map(profile => ({
@@ -275,10 +274,6 @@ export class AppHotkeyProvider extends HotkeyProvider {
id: `profile-selectors.${provider.id}`, id: `profile-selectors.${provider.id}`,
name: this.translate.instant('Show {type} profile selector', { type: provider.name }), name: this.translate.instant('Show {type} profile selector', { type: provider.name }),
})), })),
...groups.map(group => ({
id: `group-selectors.${group.id}`,
name: this.translate.instant('Show profile selector for group {name}', { name: group.name }),
})),
] ]
} }

View File

@@ -37,7 +37,7 @@ import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
import { DropZoneDirective } from './directives/dropZone.directive' import { DropZoneDirective } from './directives/dropZone.directive'
import { CdkAutoDropGroup } from './directives/cdkAutoDropGroup.directive' import { CdkAutoDropGroup } from './directives/cdkAutoDropGroup.directive'
import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ProfilesService, ProfileProvider, QuickConnectProfileProvider, SelectorOption, Profile, SelectorService, CommandProvider, PartialProfileGroup, ProfileGroup } from './api' import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ProfilesService, ProfileProvider, QuickConnectProfileProvider, SelectorOption, Profile, SelectorService, CommandProvider } from './api'
import { AppService } from './services/app.service' import { AppService } from './services/app.service'
import { ConfigService } from './services/config.service' import { ConfigService } from './services/config.service'
@@ -46,7 +46,7 @@ import { HotkeysService } from './services/hotkeys.service'
import { CustomMissingTranslationHandler, LocaleService } from './services/locale.service' import { CustomMissingTranslationHandler, LocaleService } from './services/locale.service'
import { CommandService } from './services/commands.service' import { CommandService } from './services/commands.service'
import { NewTheme } from './theme' import { StandardTheme, StandardCompactTheme, PaperTheme, NewTheme } from './theme'
import { CoreConfigProvider } from './config' import { CoreConfigProvider } from './config'
import { AppHotkeyProvider } from './hotkeys' import { AppHotkeyProvider } from './hotkeys'
import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu, ProfilesContextMenu } from './tabContextMenu' import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu, ProfilesContextMenu } from './tabContextMenu'
@@ -60,6 +60,9 @@ export function TranslateMessageFormatCompilerFactory (): TranslateMessageFormat
const PROVIDERS = [ const PROVIDERS = [
{ provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true }, { provide: HotkeyProvider, useClass: AppHotkeyProvider, multi: true },
{ provide: Theme, useClass: StandardTheme, multi: true },
{ provide: Theme, useClass: StandardCompactTheme, multi: true },
{ provide: Theme, useClass: PaperTheme, multi: true },
{ provide: Theme, useClass: NewTheme, multi: true }, { provide: Theme, useClass: NewTheme, multi: true },
{ provide: ConfigProvider, useClass: CoreConfigProvider, multi: true }, { provide: ConfigProvider, useClass: CoreConfigProvider, multi: true },
{ provide: TabContextMenuItemProvider, useClass: CommonOptionsContextMenu, multi: true }, { provide: TabContextMenuItemProvider, useClass: CommonOptionsContextMenu, multi: true },
@@ -178,24 +181,20 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
if (profile) { if (profile) {
profilesService.openNewTabForProfile(profile) profilesService.openNewTabForProfile(profile)
} }
} else if (hotkey.startsWith('profile-selectors.')) { }
if (hotkey.startsWith('profile-selectors.')) {
const id = hotkey.substring(hotkey.indexOf('.') + 1) const id = hotkey.substring(hotkey.indexOf('.') + 1)
const provider = profilesService.getProviders().find(x => x.id === id) const provider = profilesService.getProviders().find(x => x.id === id)
if (!provider) { if (!provider) {
return return
} }
this.showSelector(provider).catch(() => null) this.showSelector(provider).catch(() => null)
} else if (hotkey.startsWith('group-selectors.')) { }
const id = hotkey.substring(hotkey.indexOf('.') + 1) if (hotkey === 'command-selector') {
const groups = await this.profilesService.getProfileGroups({ includeProfiles: true })
const group = groups.find(x => x.id === id)
if (!group) {
return
}
this.showGroupSelector(group).catch(() => null)
} else if (hotkey === 'command-selector') {
commands.showSelector().catch(() => null) commands.showSelector().catch(() => null)
} else if (hotkey === 'profile-selector') { }
if (hotkey === 'profile-selector') {
commands.run('core:profile-selector', {}) commands.run('core:profile-selector', {})
} }
}) })
@@ -233,21 +232,6 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
await this.selector.show(this.translate.instant('Select profile'), options) await this.selector.show(this.translate.instant('Select profile'), options)
} }
async showGroupSelector (group: PartialProfileGroup<ProfileGroup>): Promise<void> {
if (this.selector.active) {
return
}
const profiles = group.profiles ?? []
const options: SelectorOption<void>[] = profiles.map(p => ({
...this.profilesService.selectorOptionForProfile(p),
callback: () => this.profilesService.openNewTabForProfile(p),
}))
await this.selector.show(this.translate.instant('Select profile'), options)
}
static forRoot (): ModuleWithProviders<AppModule> { static forRoot (): ModuleWithProviders<AppModule> {
return { return {
ngModule: AppModule, ngModule: AppModule,

View File

@@ -27,8 +27,12 @@ export class HomeBaseService {
this.platform.openExternal('https://github.com/Eugeny/tabby') this.platform.openExternal('https://github.com/Eugeny/tabby')
} }
openDiscord (): void { openDiscussions (): void {
this.platform.openExternal('https://discord.gg/Vn7BjmzhtF') this.platform.openExternal('https://github.com/Eugeny/tabby/discussions')
}
openTwitter (): void {
this.platform.openExternal('https://twitter.com/eugeeeeny')
} }
openTranslations (): void { openTranslations (): void {

View File

@@ -487,12 +487,6 @@ export class ProfilesService {
delete profile.group delete profile.group
} }
} }
if (this.config.store.hotkeys['group-selectors'].hasOwnProperty(group.id)) {
const groupSelectorsHotkeys = { ...this.config.store.hotkeys['group-selectors'] }
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete groupSelectorsHotkeys[group.id]
this.config.store.hotkeys['group-selectors'] = groupSelectorsHotkeys
}
} }
/** /**

View File

@@ -306,7 +306,7 @@ export class VaultFileProvider extends FileProvider {
id, id,
description: `${description} (${transfer.getName()})`, description: `${description} (${transfer.getName()})`,
}, },
value: Buffer.from(await transfer.readAll()).toString('base64'), value: (await transfer.readAll()).toString('base64'),
}) })
return `${this.prefix}${id}` return `${this.prefix}${id}`
} }

View File

@@ -0,0 +1,36 @@
@import './theme.scss';
app-root {
.tabs-on-side .tab-bar {
height: 100% !important;
}
.tab-bar {
height: 27px !important;
.btn-tab-bar {
line-height: 29px !important;
height: 27px !important;
align-items: center;
svg {
height: 14px;
}
}
.inset {
width: 70 !important;
}
}
terminaltab .content {
margin: 5px !important;
}
ssh-tab .content {
margin: 5px !important;
}
serial-tab .content {
margin: 5px !important;
}
}

View File

@@ -0,0 +1,407 @@
$black: #002b36;
$base02: #073642;
$base01: #586e75;
$base00: #657b83;
$base0: #839496;
$base1: #93a1a1;
$base2: #eee8d5;
$white: #fdf6e3;
$yellow: #b58900;
$orange: #cb4b16;
$red: #dc322f;
$pink: #d33682;
$purple: #6c71c4;
$blue: #268bd2;
$teal: #2aa198;
$green: #859900;
$tab-border-radius: 5px;
$button-hover-bg: rgba(0, 0, 0, .125);
$button-active-bg: rgba(0, 0, 0, .25);
$primary: #fd7e14;
$secondary: #495057;
$content-bg: rgba($white, 0.65);
$content-bg-solid: $white;
$body-bg: $base2;
$body-bg2: $base1;
$body-color: $black;
$font-family-sans-serif: "Source Sans Pro";
$font-size-base: 14rem / 16;
$btn-border-radius: 0;
$nav-tabs-border-width: 0;
$nav-tabs-border-radius: 0;
$nav-tabs-link-hover-border-color: $body-bg;
$nav-tabs-active-link-hover-color: $white;
$nav-tabs-active-link-hover-bg: $blue;
$nav-tabs-active-link-hover-border-color: darken($blue, 30%);
$nav-pills-border-radius: 0;
$input-bg: $base2;
$input-disabled-bg: $base1;
$input-color: $body-color;
$input-color-placeholder: $base1;
$input-border-color: $base1;
//$input-box-shadow: inset 0 1px 1px rgba($black,.075);
$input-border-radius: 0;
$custom-select-border-radius: 0;
$input-bg-focus: $input-bg;
//$input-border-focus: lighten($brand-primary, 25%);
//$input-box-shadow-focus: $input-box-shadow, rgba($input-border-focus, .6);
$input-color-focus: $input-color;
$input-group-addon-bg: $body-bg;
$input-group-addon-border-color: $input-border-color;
$modal-content-bg: $content-bg-solid;
$modal-content-border-color: $body-bg;
$modal-header-border-color: transparent;
$modal-footer-border-color: transparent;
$popover-bg: $body-bg;
$dropdown-bg: $body-bg;
$dropdown-link-color: $body-color;
$dropdown-link-hover-color: #333;
$dropdown-link-hover-bg: $body-bg2;
//$dropdown-link-active-color: $component-active-color;
//$dropdown-link-active-bg: $component-active-bg;
$dropdown-link-disabled-color: #333;
$dropdown-header-color: #333;
$list-group-color: $body-color;
$list-group-bg: rgba($black,.05);
$list-group-border-color: rgba($black,.1);
$list-group-hover-bg: rgba($black,.1);
$list-group-link-active-bg: rgba($black,.2);
$list-group-action-color: $body-color;
$list-group-action-bg: rgba($black,.05);
$list-group-action-active-bg: $list-group-link-active-bg;
$list-group-border-radius: 0;
$pre-bg: $dropdown-bg;
$pre-color: $dropdown-link-color;
$headings-font-weight: lighter;
$headings-color: $base0;
@import '~bootstrap/scss/bootstrap.scss';
window-controls {
svg {
transition: 0.25s fill;
fill: $base01;
}
button:hover {
background: rgba($black, 0.125);
svg {
fill: $black;
}
}
.btn-close:hover {
background: #8a2828;
}
}
$border-color: $base1;
app-root {
background: $body-bg;
&.vibrant {
background: rgba(255, 255, 255,.4) !important;
}
&> .content {
.tab-bar {
.btn-tab-bar {
background: transparent;
line-height: 42px;
align-items: center;
svg, path {
fill: $black;
fill-opacity: 0.75;
}
&:hover { background: rgba(0, 0, 0, .125) !important; }
&:active { background: rgba(0, 0, 0, .25) !important; }
}
&>.tabs {
tab-header {
border-left: 1px solid transparent;
border-right: 1px solid transparent;
color: $base01;
transition: 0.125s ease-out width;
.index {
color: rgba($black, 0.4);
}
button {
color: $body-color;
border: none;
transition: 0.25s all;
&:hover { background: $button-hover-bg !important; }
&:active { background: $button-active-bg !important; }
}
.progressbar {
background: $blue;
}
.activity-indicator {
background:rgba(0, 0, 0, 0.2);
}
&.active {
color: $black;
background: $content-bg;
border-left: 1px solid $border-color;
border-right: 1px solid $border-color;
}
}
}
}
&.tabs-on-top .tab-bar {
&>.background {
border-bottom: 1px solid $border-color;
}
tab-header {
border-bottom: 1px solid $border-color;
&.active {
border-bottom-color: transparent;
}
}
}
&:not(.tabs-on-top) .tab-bar {
&>.background {
border-top: 1px solid $border-color;
}
tab-header {
border-top: 1px solid $border-color;
&.active {
margin-top: -1px;
}
}
}
}
&.platform-win32, &.platform-linux {
border: 1px solid #111;
&>.content .tab-bar .tabs tab-header:first-child {
border-left: none;
}
}
}
tab-body {
background: $content-bg;
}
settings-tab > .content {
& > .nav {
background: rgba(0, 0, 0, 0.25);
border-right: 1px solid $body-bg;
& > .nav-item > .nav-link {
border: none;
padding: 10px 50px 10px 20px;
font-size: 14px;
&:not(.active) {
color: $body-color;
}
}
}
}
multi-hotkey-input {
.item {
background: $body-bg2;
border: 1px solid $blue;
border-radius: 3px;
margin-right: 5px;
.body {
padding: 3px 0 2px;
.stroke {
padding: 0 6px;
border-right: 1px solid $content-bg;
}
}
.remove {
padding: 3px 8px 2px;
}
}
.item:has(.duplicate) {
background-color: map-get($theme-colors, 'danger');
border: 1px solid map-get($theme-colors, 'danger');
}
.add {
color: #777;
padding: 4px 10px 0;
}
.add, .item .body, .item .remove {
&:hover { background: darken($body-bg2, 5%); }
&:active { background: darken($body-bg2, 15%); }
}
.add:has(.duplicate), .item:has(.duplicate) .body, .item:has(.duplicate) .remove {
&:hover { background: darken(map-get($theme-colors, 'danger'), 5%); }
&:active { background: darken(map-get($theme-colors, 'danger'), 15%); }
}
}
hotkey-input-modal {
.input {
background: $input-bg;
padding: 10px;
font-size: 24px;
line-height: 27px;
height: 55px;
.stroke {
background: $body-bg2;
border: 1px solid $blue;
border-radius: 3px;
margin-right: 10px;
padding: 3px 10px;
}
}
.timeout {
background: $input-bg;
div {
background: $blue;
}
}
}
.mb-3 label {
margin-bottom: 2px;
}
.nav-tabs {
.nav-link {
transition: 0.25s all;
border-bottom-color: $nav-tabs-border-color;
}
}
.btn-check:checked + label {
background: $blue;
}
.btn {
i + * {
margin-left: 5px;
}
&.btn-lg i + * {
margin-left: 10px;
}
}
.input-group-addon + .form-control {
border-left: none;
}
.input-group > select.form-control {
flex-direction: row;
}
.list-group-item {
transition: 0.25s background;
&:not(:first-child) {
border-top: none;
}
i + * {
margin-left: 10px;
}
}
select.form-control {
-webkit-appearance: none;
background-image: url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='24' height='24' viewBox='0 0 24 24'><path fill='#444' d='M7.406 7.828l4.594 4.594 4.594-4.594 1.406 1.406-6 6-6-6z'></path></svg>");
background-position: 100% 50%;
background-repeat: no-repeat;
padding-right: 30px;
}
checkbox i.on {
color: $blue;
}
toggle {
.body {
border-color: $base0 !important;
.toggle {
background: $base0 !important;
}
}
&.active .body .toggle {
background: map-get($theme-colors, primary) !important;
}
}
.list-group-item svg {
fill: $black;
}
.tabby-title {
color: $base01;
}
.tabby-logo {
filter: saturate(0);
}
start-page footer {
background: $white !important;
}
terminal-toolbar {
background: #ffffff4a !important;
border-bottom: 1px solid #00000026 !important;
}
.bg-dark{
background-color: $base2 !important;
}
split-tab-spanner {
background: rgba(0, 0, 0, .2);
&:hover, &.active {
background: rgba(255, 255, 255, .125);
}
}

428
tabby-core/src/theme.scss Normal file
View File

@@ -0,0 +1,428 @@
@import "./theme.vars";
// ---------
$button-hover-bg: rgba(0, 0, 0, .25);
$button-active-bg: rgba(0, 0, 0, .5);
@import '~bootstrap/scss/bootstrap.scss';
@import "./theme.vendor.scss";
window-controls {
svg {
transition: 0.25s fill;
fill: #aaa;
}
button:hover svg {
fill: white;
}
.btn-close:hover {
background: #8a2828;
}
}
$border-color: #111;
app-root {
background: $body-bg;
&.vibrant {
background: rgba(0,0,0,.65);
}
&> .content {
.tab-bar {
.btn-tab-bar {
background: transparent;
&:hover { background: rgba(0, 0, 0, .25) !important; }
&:active, &[aria-expanded-true] { background: rgba(0, 0, 0, .5) !important; }
&:focus {
box-shadow: none;
}
&::after {
display: none;
}
}
&>.tabs {
tab-header {
border-left: 1px solid transparent;
border-right: 1px solid transparent;
transition: 0.125s ease-out width;
.index {
color: rgba(255, 255, 255, 0.4);
}
.icon {
opacity: .75;
}
button {
color: $body-color;
border: none;
transition: 0.25s all;
right: 5px;
&:hover { background: $button-active-bg !important; }
&:active { background: $button-active-bg !important; }
}
.progressbar {
background: $green;
}
.activity-indicator {
background:rgba(255, 255, 255, 0.2);
}
&.active {
color: white;
background: $content-bg;
border-left: 1px solid $border-color;
border-right: 1px solid $border-color;
}
}
}
}
&.tabs-on-top .tab-bar {
&>.background {
border-bottom: 1px solid $border-color;
}
tab-header {
border-bottom: 1px solid $border-color;
&.active {
border-bottom-color: transparent;
}
}
}
&:not(.tabs-on-top) .tab-bar {
&>.background {
border-top: 1px solid $border-color;
}
tab-header {
border-top: 1px solid $border-color;
&.active {
margin-top: -1px;
}
}
}
}
&.platform-win32, &.platform-linux {
border: 1px solid #111;
&>.content {
margin: -1px; // expand the content into the border
.tab-bar .tabs tab-header:first-child {
border-left: none;
}
}
}
}
tab-body {
background: $content-bg;
terminal-toolbar .btn, .toolbar-pin-button {
font-weight: bold;
}
}
multi-hotkey-input {
.item {
background: $body-bg2;
border: 1px solid $blue;
border-radius: 3px;
margin-right: 5px;
.body {
padding: 3px 0 2px;
.stroke {
padding: 0 6px;
border-right: 1px solid $content-bg;
}
}
.remove {
padding: 3px 8px 2px;
}
}
.item:has(.duplicate) {
background-color: map-get($theme-colors, 'danger');
border: 1px solid map-get($theme-colors, 'danger');
}
.add {
color: #777;
padding: 4px 10px 0;
}
.add, .item .body, .item .remove {
&:hover { background: darken($body-bg2, 5%); }
&:active { background: darken($body-bg2, 15%); }
}
.add:has(.duplicate), .item:has(.duplicate) .body, .item:has(.duplicate) .remove {
&:hover { background: darken(map-get($theme-colors, 'danger'), 5%); }
&:active { background: darken(map-get($theme-colors, 'danger'), 15%); }
}
}
hotkey-input-modal {
.input {
background: $input-bg;
padding: 10px;
font-size: 24px;
line-height: 27px;
height: 55px;
.stroke {
background: $body-bg2;
border: 1px solid $blue;
border-radius: 3px;
margin-right: 10px;
padding: 3px 10px;
}
}
.timeout {
background: $input-bg;
div {
background: $blue;
}
}
}
.mb-3 label {
margin-bottom: 2px;
}
.btn-check:checked + label {
background: $blue;
}
.btn {
i + * {
margin-left: 5px;
}
&.btn-lg i + * {
margin-left: 10px;
}
}
.input-group-addon + .form-control {
border-left: none;
}
.input-group > select.form-control {
flex-direction: row;
}
.list-group-item {
// transition: 0.0625s background ease;
i + * {
margin-left: 10px;
}
}
.list-group.list-group-flush .list-group-item {
background: transparent;
border: none;
&:not(:last-child) {
border-bottom: none;
}
&.list-group-item-action {
&:hover, &.active {
background: $list-group-hover-bg;
}
}
}
.list-group-light {
.list-group-item {
border: none !important;
outline: none !important;
background: transparent;
border-radius: $border-radius;
margin: 0 !important;
&.list-group-item-action {
&:hover, &.active {
background: $component-active-bg;
color: $component-active-color;
}
}
}
}
checkbox i.on {
color: $blue;
}
.modal .modal-footer {
background: rgba(0, 0, 0, .25);
.btn {
font-weight: bold;
padding: 0.375rem 1.5rem;
}
}
.list-group-item svg {
fill: white;
fill-opacity: 0.75;
}
*::-webkit-scrollbar {
background: rgba(0, 0, 0, .125);
width: 10px;
margin: 5px;
}
*::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, .25);
}
*::-webkit-scrollbar-corner,
*::-webkit-resizer {
opacity: 0;
}
search-panel {
background: #131d27 !important;
input {
border-radius: 0 !important;
}
}
.btn {
cursor: pointer;
justify-content: flex-start;
overflow: hidden;
&.disabled,
&:disabled {
cursor: not-allowed;
}
}
.btn-warning:not(:disabled):not(.disabled) {
&.active, &:active {
color: $gray-900;
}
}
.btn-secondary:not(:disabled):not(.disabled) {
&.active, &:active {
background: #191e23;
align-items: center;
}
}
.btn-link {
text-decoration: none;
&:hover, &[aria-expanded=true], &:active, &.active {
color: $link-hover-color;
border-radius: $btn-border-radius;
}
&[aria-expanded=true], &:active, &.active {
background: rgba(255, 255, 255, 0.1);
}
}
.btn-group .btn.active {
border-color: transparent !important;
}
.nav-tabs {
margin-bottom: 10px;
&.nav-justified .nav-link {
margin-right: 5px;
}
.nav-link {
border: none;
border-bottom: $nav-tabs-border-width solid transparent;
text-transform: uppercase;
font-weight: bold;
padding: 5px 0;
margin-right: 20px;
uib-tab-heading > i {
font-size: 18px;
}
// @include hover-focus {
// color: $nav-tabs-link-active-color;
// }
&.disabled {
color: $nav-link-disabled-color;
border-color: transparent;
}
}
.nav-item:last-child .nav-link {
margin-right: 0;
}
.nav-link.active,
.nav-item.show .nav-link {
color: $nav-tabs-link-active-color;
border-color: $nav-tabs-link-active-border-color;
}
}
hr {
border-color: $list-group-border-color;
}
.dropdown-menu {
box-shadow: $dropdown-box-shadow;
}
ngx-colors-panel .opened {
background: $body-bg !important;
button {
color: $body-color !important;
}
.button svg {
fill: white;
}
}
split-tab-spanner {
background: rgba(0, 0, 0, .2);
&:hover, &.active {
background: rgba(255, 255, 255, .125);
}
}

View File

@@ -2,6 +2,32 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Theme } from './api' import { Theme } from './api'
/** @hidden */
@Injectable()
export class StandardTheme extends Theme {
name = _('Standard (legacy)')
css = require('./theme.scss')
terminalBackground = '#222a33'
}
/** @hidden */
@Injectable()
export class StandardCompactTheme extends Theme {
name = _('Compact (legacy)')
css = require('./theme.compact.scss')
terminalBackground = '#222a33'
macOSWindowButtonsInsetX = 8
macOSWindowButtonsInsetY = 6
}
/** @hidden */
@Injectable()
export class PaperTheme extends Theme {
name = _('Paper (legacy)')
css = require('./theme.paper.scss')
terminalBackground = '#f7f1e0'
}
/** @hidden */ /** @hidden */
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class NewTheme extends Theme { export class NewTheme extends Theme {

View File

@@ -24,7 +24,6 @@
"devDependencies": { "devDependencies": {
"electron-promise-ipc": "^2.2.4", "electron-promise-ipc": "^2.2.4",
"ps-node": "^0.1.6", "ps-node": "^0.1.6",
"ssh-config": "^5.0.0",
"tmp-promise": "^3.0.2", "tmp-promise": "^3.0.2",
"which": "^3.0.0", "which": "^3.0.0",
"winston": "^3.3.3" "winston": "^3.3.3"

View File

@@ -300,12 +300,12 @@ class ElectronFileUpload extends FileUpload {
private size: number private size: number
private mode: number private mode: number
private file: fs.FileHandle private file: fs.FileHandle
private buffer: Uint8Array private buffer: Buffer
private powerSaveBlocker = 0 private powerSaveBlocker = 0
constructor (private filePath: string, private electron: ElectronService) { constructor (private filePath: string, private electron: ElectronService) {
super() super()
this.buffer = new Uint8Array(256 * 1024) this.buffer = Buffer.alloc(256 * 1024)
this.powerSaveBlocker = electron.powerSaveBlocker.start('prevent-app-suspension') this.powerSaveBlocker = electron.powerSaveBlocker.start('prevent-app-suspension')
} }
@@ -328,7 +328,7 @@ class ElectronFileUpload extends FileUpload {
return this.size return this.size
} }
async read (): Promise<Uint8Array> { async read (): Promise<Buffer> {
const result = await this.file.read(this.buffer, 0, this.buffer.length, null) const result = await this.file.read(this.buffer, 0, this.buffer.length, null)
this.increaseProgress(result.bytesRead) this.increaseProgress(result.bytesRead)
return this.buffer.slice(0, result.bytesRead) return this.buffer.slice(0, result.bytesRead)
@@ -370,7 +370,7 @@ class ElectronFileDownload extends FileDownload {
return this.size return this.size
} }
async write (buffer: Uint8Array): Promise<void> { async write (buffer: Buffer): Promise<void> {
let pos = 0 let pos = 0
while (pos < buffer.length) { while (pos < buffer.length) {
const result = await this.file.write(buffer, pos, buffer.length - pos, null) const result = await this.file.write(buffer, pos, buffer.length - pos, null)

View File

@@ -49,24 +49,19 @@ export class EditSFTPContextMenu extends SFTPContextMenuItemProvider {
this.platform.openPath(tempPath) this.platform.openPath(tempPath)
const events = new Subject<string>() const events = new Subject<string>()
fs.chmodSync(tempPath, 0o700) const watcher = fs.watch(tempPath, event => events.next(event))
events.pipe(debounceTime(1000), debounce(async event => {
// skip the first burst of events if (event === 'rename') {
setTimeout(() => { watcher.close()
const watcher = fs.watch(tempPath, event => events.next(event)) }
events.pipe(debounceTime(1000), debounce(async event => { const upload = await this.platform.startUpload({ multiple: false }, [tempPath])
if (event === 'rename') { if (!upload.length) {
watcher.close() return
} }
const upload = await this.platform.startUpload({ multiple: false }, [tempPath]) await sftp.upload(item.fullPath, upload[0])
if (!upload.length) { await sftp.chmod(item.fullPath, item.mode)
return })).subscribe()
} watcher.on('close', () => events.complete())
await sftp.upload(item.fullPath, upload[0]) sftp.closed$.subscribe(() => watcher.close())
await sftp.chmod(item.fullPath, item.mode)
})).subscribe()
watcher.on('close', () => events.complete())
sftp.closed$.subscribe(() => watcher.close())
}, 1000)
} }
} }

View File

@@ -1,3 +1,4 @@
import at from 'core-js-pure/actual/array/at'
import * as fs from 'fs/promises' import * as fs from 'fs/promises'
import * as fsSync from 'fs' import * as fsSync from 'fs'
import * as path from 'path' import * as path from 'path'
@@ -5,297 +6,125 @@ import slugify from 'slugify'
import * as yaml from 'js-yaml' import * as yaml from 'js-yaml'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { PartialProfile } from 'tabby-core' import { PartialProfile } from 'tabby-core'
import { import { SSHProfileImporter, PortForwardType, SSHProfile, SSHProfileOptions, AutoPrivateKeyLocator } from 'tabby-ssh'
SSHProfileImporter,
PortForwardType,
SSHProfile,
AutoPrivateKeyLocator,
ForwardedPortConfig,
} from 'tabby-ssh'
import { ElectronService } from './services/electron.service' import { ElectronService } from './services/electron.service'
import SSHConfig, { LineType } from 'ssh-config'
// Enum to delineate the properties in SSHProfile options
enum SSHProfilePropertyNames {
Host = 'host',
Port = 'port',
User = 'user',
X11 = 'x11',
PrivateKeys = 'privateKeys',
KeepaliveInterval = 'keepaliveInterval',
KeepaliveCountMax = 'keepaliveCountMax',
ReadyTimeout = 'readyTimeout',
JumpHost = 'jumpHost',
AgentForward = 'agentForward',
ProxyCommand = 'proxyCommand',
ForwardedPorts = 'forwardedPorts',
}
// Data structure to map the (lowercase) ssh-config attributes (as keys) to a tuple
// containing the name of the corresponding SSHProfile attribute
const decodeFields: Record<string, SSHProfilePropertyNames> = {
hostname: SSHProfilePropertyNames.Host,
user: SSHProfilePropertyNames.User,
port: SSHProfilePropertyNames.Port,
forwardx11: SSHProfilePropertyNames.X11,
serveraliveinterval: SSHProfilePropertyNames.KeepaliveInterval,
serveralivecountmax: SSHProfilePropertyNames.KeepaliveCountMax,
connecttimeout: SSHProfilePropertyNames.ReadyTimeout,
proxyjump: SSHProfilePropertyNames.JumpHost,
forwardagent: SSHProfilePropertyNames.AgentForward,
identityfile: SSHProfilePropertyNames.PrivateKeys,
proxycommand: SSHProfilePropertyNames.ProxyCommand,
localforward: SSHProfilePropertyNames.ForwardedPorts,
remoteforward: SSHProfilePropertyNames.ForwardedPorts,
dynamicforward: SSHProfilePropertyNames.ForwardedPorts,
}
// Function to use the above to return details corresponding to the supplied SSHProperty name.
// If the name of the supplied SSH Config file Property is valid, and one that we process,
// then we get back the name of the corresponding Property in the SSHProfile object
function decodeTarget (SSHProperty: string): string {
const lower = SSHProperty.toLowerCase()
if (lower in decodeFields) {
return decodeFields[lower]
}
return ''
}
// Function to combine SSHConfig values into a single string. This is used to smash
// together the proxyCommand values which are split on whitespace and presented as
// an array of objects in the SSHConfig object
function convertSSHConfigValuesToString (arg: string | string[] | object[]): string {
if (typeof arg === 'string') { return arg }
let allStrings = true
for (const item of arg) {
if (typeof item !== 'string') {
allStrings = false
break
}
}
if (allStrings) {
return arg.join(' ')
}
// Have to explicitly unwrap the arg into a list of objects to avoid Typescript grumbles
const objList: object[] = []
for (const item of arg) {
if ( typeof item === 'object' && 'val' in item ) {
objList.push(item)
}
}
return objList.filter(obj => 'val' in obj)
.map(obj => 'val' in obj ? obj.val as string: '')
.join(' ')
}
// Function to read in the SSH config file and return it as a string
async function readSSHConfigFile (filePath: string): Promise<string> {
try {
return await fs.readFile(filePath, 'utf8')
} catch (err) {
console.error('Error reading SSH config file:', err)
return ''
}
}
// Function to take an ssh-config entry and convert it into an SSHProfile
function convertHostToSSHProfile (host: string, settings: Record<string, string | string[] | object[] >): PartialProfile<SSHProfile> {
// inline function to generate an id for this profile
const deriveID = (name: string) => 'openssh-config:' + slugify(name)
// Start point of the profile, with an ID, name, type and group
const thisProfile: PartialProfile<SSHProfile> = {
id: deriveID(host),
name: `${host} (.ssh/config)`,
type: 'ssh',
group: 'Imported from .ssh/config',
}
const options = {}
function convertToForwardedPortDescriptor (forwardType: PortForwardType.Local | PortForwardType.Remote | PortForwardType.Dynamic, details: string): ForwardedPortConfig {
const detailsParts = details.split(/\s/)
const bindParts = detailsParts[0].trim().split(':')
if (bindParts.length === 1) {
bindParts.unshift('127.0.0.1')
}
let tgtParts = ['', '22']
if ( detailsParts.length > 1 ) {
tgtParts = detailsParts[1].trim().split(':')
}
return {
host: bindParts[0],
port: parseInt(bindParts[1]),
targetAddress: tgtParts[0],
targetPort: parseInt(tgtParts[1]),
type: forwardType,
description: details,
}
}
// for each ssh-config key in turn...
for (const key in settings) {
// decode a target attribute and an action
const targetName = decodeTarget(key)
switch (targetName) {
// The following have single string values
case SSHProfilePropertyNames.User:
case SSHProfilePropertyNames.Host:
case SSHProfilePropertyNames.JumpHost:
const basicString = settings[key]
if (typeof basicString === 'string') {
if (targetName === SSHProfilePropertyNames.JumpHost) {
options[targetName] = deriveID(basicString)
} else {
options[targetName] = basicString
}
} else {
console.log('Unexpected value in settings for ' + key)
}
break
// The following have single integer values
case SSHProfilePropertyNames.Port:
case SSHProfilePropertyNames.KeepaliveInterval:
case SSHProfilePropertyNames.KeepaliveCountMax:
case SSHProfilePropertyNames.ReadyTimeout:
const numberString = settings[key]
if (typeof numberString === 'string') {
options[targetName] = parseInt(numberString, 10)
} else {
console.log('Unexpected value in settings for ' + key)
}
break
// The following have single yes/no values
case SSHProfilePropertyNames.X11:
case SSHProfilePropertyNames.AgentForward:
let booleanString = settings[key]
booleanString = typeof booleanString === 'string' ? booleanString.toLowerCase() : ''
if ( booleanString === 'yes' || booleanString === 'no' ) {
options[targetName] = booleanString === 'yes'
} else {
console.log('Unexpected value in settings for ' + key)
}
break
// ProxyCommand will be an array if unquoted and containing multiple words,
// or a simple string otherwise
case SSHProfilePropertyNames.ProxyCommand:
const proxyCommand = convertSSHConfigValuesToString(settings[key])
options[targetName] = proxyCommand
break
// IdentityFile may have multiple values and the need to have '~' converted to the
// path to the HOME directory
case SSHProfilePropertyNames.PrivateKeys:
const processedKeys: string [] = (settings[key] as string[]).map( s => {
let retVal: string = s
if (s.startsWith('~/')) {
retVal = path.join(process.env.HOME ?? '~', s.slice(2))
}
return retVal
})
options[targetName] = processedKeys
break
// The port forwarding directives all end up in the same space, but with a different value
// in the SSHProfileOptions object
case SSHProfilePropertyNames.ForwardedPorts:
const forwardTypeString = key.toLowerCase()
let forwardType: PortForwardType | null = null
switch (forwardTypeString) {
case 'localforward':
forwardType = PortForwardType.Local
break
case 'remoteforward':
forwardType = PortForwardType.Remote
break
case 'dynamicforward':
forwardType = PortForwardType.Dynamic
break
}
if (forwardType) {
options[targetName] ??= []
for (const forwarderDetails of settings[key]) {
if (typeof forwarderDetails === 'string') {
options[targetName].push(convertToForwardedPortDescriptor(forwardType, forwarderDetails))
}
}
}
break
}
}
thisProfile.options = options
return thisProfile
}
function convertToSSHProfiles (config: SSHConfig): PartialProfile<SSHProfile>[] {
const myMap = new Map<string, PartialProfile<SSHProfile>>()
function noWildCardsInName (name: string) {
return !/[?*]/.test(name)
}
for (const entry of config) {
// Each entry represents a line in the SSH Config. If the line is a 'Host' line,
// then it will also contain the configuration for that identifiable Host.
// There may be more than one host per line and some 'Hosts' have wildcards in their
// names
// If this is a genuine entry rather than a Comment...
// ... and there is a 'Host' Parameter
if (entry.type === LineType.DIRECTIVE && entry.param === 'Host') {
// for each Name in this entry
const hostList: string[] = []
// if there is more than one host specified on this line, then the names will be
// in an array
if (typeof entry.value === 'string') {
hostList.push(entry.value)
} else if (Array.isArray(entry.value)) {
for (const item of entry.value) {
hostList.push(item.val)
}
}
// for each Host identified on this line, check that there are no wildcards in the
// name and that we've not seen the name before.
// If that is the case, then get the full configuration for this name.
// If that has a 'Hostname' property (if that's missing, the name is not usable
// for our purposes) then convert the configuration into an SSHProfile and stash it
for (const host of hostList) {
if (noWildCardsInName(host)) {
if (!(host in myMap)) {
// NOTE: SSHConfig.compute() lies about the return types
const configuration: Record<string, string | string[] | object[]> = config.compute(host)
if (configuration['HostName']) {
myMap[host] = convertHostToSSHProfile(host, configuration)
}
}
}
}
}
}
// Convert the values from the map into a list of Partial SSHProfiles sorted
// by Hostname
return Object.keys(myMap).sort().map(key => myMap[key])
}
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class OpenSSHImporter extends SSHProfileImporter { export class OpenSSHImporter extends SSHProfileImporter {
async getProfiles (): Promise<PartialProfile<SSHProfile>[]> { async getProfiles (): Promise<PartialProfile<SSHProfile>[]> {
const deriveID = name => 'openssh-config:' + slugify(name)
const results: PartialProfile<SSHProfile>[] = []
const configPath = path.join(process.env.HOME ?? '~', '.ssh', 'config') const configPath = path.join(process.env.HOME ?? '~', '.ssh', 'config')
try { try {
const sshConfigContent = await readSSHConfigFile(configPath) const lines = (await fs.readFile(configPath, 'utf8')).split('\n')
const config: SSHConfig = SSHConfig.parse(sshConfigContent) const globalOptions: Partial<SSHProfileOptions> = {}
return convertToSSHProfiles(config) let currentProfile: PartialProfile<SSHProfile>|null = null
for (let line of lines) {
if (line.trim().startsWith('#') || !line.trim()) {
continue
}
if (line.toLowerCase().startsWith('host ')) {
if (currentProfile) {
results.push(currentProfile)
}
const name = line.substr(5).trim()
currentProfile = {
id: deriveID(name),
name: `${name} (.ssh/config)`,
type: 'ssh',
group: 'Imported from .ssh/config',
options: {
...globalOptions,
host: name,
},
}
} else {
const target: Partial<SSHProfileOptions> = currentProfile?.options ?? globalOptions
line = line.trim()
const idx = /\s/.exec(line)?.index ?? -1
if (idx === -1) {
continue
}
const key = line.substr(0, idx).trim()
const value = line.substr(idx + 1).trim()
if (key === 'IdentityFile') {
target.privateKeys = value.split(',').map(s => s.trim()).map(s => {
if (s.startsWith('~')) {
s = path.join(process.env.HOME ?? '~', s.slice(2))
}
return s
})
} else if (key === 'RemoteForward') {
const bind = value.split(/\s/)[0].trim()
const tgt = value.split(/\s/)[1].trim()
target.forwardedPorts ??= []
target.forwardedPorts.push({
type: PortForwardType.Remote,
description: value,
host: bind.split(':')[0] ?? '127.0.0.1',
port: parseInt(bind.split(':')[1] ?? bind),
targetAddress: tgt.split(':')[0],
targetPort: parseInt(tgt.split(':')[1]),
})
} else if (key === 'LocalForward') {
const bind = value.split(/\s/)[0].trim()
const tgt = value.split(/\s/)[1].trim()
target.forwardedPorts ??= []
target.forwardedPorts.push({
type: PortForwardType.Local,
description: value,
host: bind.includes(':') ? bind.split(':')[0] : '127.0.0.1',
port: parseInt(at(bind.split(':'), -1)),
targetAddress: tgt.split(':')[0],
targetPort: parseInt(tgt.split(':')[1]),
})
} else if (key === 'DynamicForward') {
const bind = value.trim()
target.forwardedPorts ??= []
target.forwardedPorts.push({
type: PortForwardType.Dynamic,
description: value,
host: bind.includes(':') ? bind.split(':')[0] : '127.0.0.1',
port: parseInt(at(bind.split(':'), -1)),
targetAddress: '',
targetPort: 22,
})
} else {
const mappedKey = {
hostname: 'host',
host: 'host',
port: 'port',
user: 'user',
forwardx11: 'x11',
serveraliveinterval: 'keepaliveInterval',
serveralivecountmax: 'keepaliveCountMax',
proxycommand: 'proxyCommand',
proxyjump: 'jumpHost',
}[key.toLowerCase()]
if (mappedKey) {
target[mappedKey] = value
}
}
}
}
if (currentProfile) {
results.push(currentProfile)
}
for (const p of results) {
if (p.options?.proxyCommand) {
p.options.proxyCommand = p.options.proxyCommand
.replace('%h', p.options.host ?? '')
.replace('%p', (p.options.port ?? 22).toString())
}
if (p.options?.jumpHost) {
p.options.jumpHost = deriveID(p.options.jumpHost)
}
}
return results
} catch (e) { } catch (e) {
if (e.code === 'ENOENT') { if (e.code === 'ENOENT') {
return [] return []

View File

@@ -413,11 +413,6 @@ simple-swizzle@^0.2.2:
dependencies: dependencies:
is-arrayish "^0.3.1" is-arrayish "^0.3.1"
ssh-config@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ssh-config/-/ssh-config-5.0.1.tgz#44ee7db10d3340c79780afd142af05cf641408b9"
integrity sha512-Bh9CRGFq7pLpWFPmLOyirzYhbpme8FXZe3lZckWvmABdcIEiGB8tNbmEEZdppnr6EiQ0WcGTMoYDp8Tjomq9gw==
stack-trace@0.0.x: stack-trace@0.0.x:
version "0.0.10" version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"

View File

@@ -1,38 +1,27 @@
ul.nav-tabs(ngbNav, #nav='ngbNav') command-line-editor([model]='profile.options')
li(ngbNavItem)
a(ngbNavLink, translate) General
ng-template(ngbNavContent)
command-line-editor([model]='profile.options')
.form-line(*ngIf='uac?.isAvailable') .form-line(*ngIf='uac?.isAvailable')
.header .header
.title(translate) Run as administrator .title(translate) Run as administrator
toggle( toggle(
[(ngModel)]='profile.options.runAsAdministrator', [(ngModel)]='profile.options.runAsAdministrator',
) )
.mb-3 .mb-3
label(translate) Working directory label(translate) Working directory
.input-group .input-group
input.form-control( input.form-control(
type='text', type='text',
placeholder='Home directory', placeholder='Home directory',
[(ngModel)]='profile.options.cwd' [(ngModel)]='profile.options.cwd'
) )
button.btn.btn-secondary((click)='pickWorkingDirectory()') button.btn.btn-secondary((click)='pickWorkingDirectory()')
i.fas.fa-folder-open i.fas.fa-folder-open
.mb-3 .mb-3
label(translate) Environment label(translate) Environment
environment-editor( environment-editor(
type='text', type='text',
[(model)]='profile.options.env', [(model)]='profile.options.env',
) )
li(ngbNavItem)
a(ngbNavLink, translate) Colors
ng-template(ngbNavContent)
color-scheme-selector([(model)]='profile.terminalColorScheme')
div([ngbNavOutlet]='nav')

View File

@@ -40,11 +40,11 @@
div(translate) Report a problem div(translate) Report a problem
small.text-muted(translate) Generate a pre-filled GitHub issue small.text-muted(translate) Generate a pre-filled GitHub issue
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openDiscord()') button.list-group-item.list-group-item-action.link-card((click)='homeBase.openDiscussions()')
i.fab.fa-fw.fa-discord i.fas.fa-fw.fa-comments
div div
div(translate) Community div(translate) Ask a question
small.text-muted(translate) On Discord small.text-muted(translate) On GitHub Discussions
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openGitHub()') button.list-group-item.list-group-item-action.link-card((click)='homeBase.openGitHub()')
i.fab.fa-fw.fa-github i.fab.fa-fw.fa-github
@@ -58,6 +58,12 @@
div(translate) What's new div(translate) What's new
small.text-muted(translate) Show release notes small.text-muted(translate) Show release notes
button.list-group-item.list-group-item-action.link-card((click)='homeBase.openTwitter()')
i.fab.fa-fw.fa-twitter
div
div(translate) Subscribe to updates
small.text-muted(translate) Tabby news and updates on Twitter
h3(translate) Application settings h3(translate) Application settings
.form-line .form-line

View File

@@ -123,7 +123,7 @@ export class VaultSettingsTabComponent extends BaseComponent {
} }
await this.vault.updateSecret(secret, { await this.vault.updateSecret(secret, {
...secret, ...secret,
value: Buffer.from(await transfers[0].readAll()).toString('base64'), value: (await transfers[0].readAll()).toString('base64'),
}) })
this.loadVault() this.loadVault()
} }

View File

@@ -1,6 +1,6 @@
h3.mb-3(translate) Window h3.mb-3(translate) Window
.form-line(*ngIf='themes.length > 1') .form-line
.header .header
.title(translate) Theme .title(translate) Theme
select.form-control( select.form-control(

View File

@@ -11,17 +11,22 @@
"build": "webpack --progress --color", "build": "webpack --progress --color",
"watch": "webpack --progress --color --watch", "watch": "webpack --progress --color --watch",
"postinstall": "run-script-os", "postinstall": "run-script-os",
"postinstall:darwin:linux": "exit" "postinstall:darwin:linux": "exit",
"postinstall:win32": "xcopy /i /y ..\\node_modules\\ssh2\\util\\pagent.exe util\\"
}, },
"files": [ "files": [
"dist", "dist",
"util/pagent.exe",
"typings" "typings"
], ],
"author": "Eugene Pankov", "author": "Eugene Pankov",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/node": "20.3.1", "@types/node": "20.3.1",
"@types/ssh2": "^0.5.46",
"ansi-colors": "^4.1.1", "ansi-colors": "^4.1.1",
"diffie-hellman": "^5.0.3",
"sshpk": "Eugeny/node-sshpk#c2b71d1243714d2daf0988f84c3323d180817136",
"strip-ansi": "^7.0.0" "strip-ansi": "^7.0.0"
}, },
"dependencies": { "dependencies": {
@@ -40,8 +45,5 @@
"tabby-core": "*", "tabby-core": "*",
"tabby-settings": "*", "tabby-settings": "*",
"tabby-terminal": "*" "tabby-terminal": "*"
},
"resolutions": {
"glob": "7.2.3"
} }
} }

View File

@@ -1,45 +1,20 @@
import * as russh from 'russh' import * as ALGORITHMS from 'ssh2/lib/protocol/constants'
import { SSHAlgorithmType } from './api' import { ALGORITHM_BLACKLIST, SSHAlgorithmType } from './api'
export const supportedAlgorithms = { // Counteracts https://github.com/mscdex/ssh2/commit/f1b5ac3c81734c194740016eab79a699efae83d8
[SSHAlgorithmType.KEX]: russh.getSupportedKexAlgorithms().filter(x => x !== 'none'), ALGORITHMS.DEFAULT_CIPHER.push('aes128-gcm')
[SSHAlgorithmType.HOSTKEY]: russh.getSupportedKeyTypes().filter(x => x !== 'none'), ALGORITHMS.DEFAULT_CIPHER.push('aes256-gcm')
[SSHAlgorithmType.CIPHER]: russh.getSupportedCiphers().filter(x => x !== 'clear'), ALGORITHMS.SUPPORTED_CIPHER.push('aes128-gcm')
[SSHAlgorithmType.HMAC]: russh.getSupportedMACs().filter(x => x !== 'none'), ALGORITHMS.SUPPORTED_CIPHER.push('aes256-gcm')
}
export const defaultAlgorithms = { export const supportedAlgorithms: Record<string, string> = {}
[SSHAlgorithmType.KEX]: [
'curve25519-sha256', for (const k of Object.values(SSHAlgorithmType)) {
'curve25519-sha256@libssh.org', const supportedAlg = {
'diffie-hellman-group16-sha512', [SSHAlgorithmType.KEX]: 'SUPPORTED_KEX',
'diffie-hellman-group14-sha256', [SSHAlgorithmType.HOSTKEY]: 'SUPPORTED_SERVER_HOST_KEY',
'ext-info-c', [SSHAlgorithmType.CIPHER]: 'SUPPORTED_CIPHER',
'ext-info-s', [SSHAlgorithmType.HMAC]: 'SUPPORTED_MAC',
'kex-strict-c-v00@openssh.com', }[k]
'kex-strict-s-v00@openssh.com', supportedAlgorithms[k] = ALGORITHMS[supportedAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x)).sort()
],
[SSHAlgorithmType.HOSTKEY]: [
'ssh-ed25519',
'ecdsa-sha2-nistp256',
'ecdsa-sha2-nistp521',
'rsa-sha2-256',
'rsa-sha2-512',
'ssh-rsa',
],
[SSHAlgorithmType.CIPHER]: [
'chacha20-poly1305@openssh.com',
'aes256-gcm@openssh.com',
'aes256-ctr',
'aes192-ctr',
'aes128-ctr',
],
[SSHAlgorithmType.HMAC]: [
'hmac-sha2-512-etm@openssh.com',
'hmac-sha2-256-etm@openssh.com',
'hmac-sha2-512',
'hmac-sha2-256',
'hmac-sha1-etm@openssh.com',
'hmac-sha1',
],
} }

View File

@@ -1,4 +1,5 @@
export * from './contextMenu' export * from './contextMenu'
export * from './interfaces' export * from './interfaces'
export * from './importer' export * from './importer'
export * from './proxyStream'
export { SSHMultiplexerService } from '../services/sshMultiplexer.service' export { SSHMultiplexerService } from '../services/sshMultiplexer.service'

View File

@@ -51,3 +51,13 @@ export interface ForwardedPortConfig {
targetPort: number targetPort: number
description: string description: string
} }
export let ALGORITHM_BLACKLIST = [
// cause native crashes in node crypto, use EC instead
'diffie-hellman-group-exchange-sha256',
'diffie-hellman-group-exchange-sha1',
]
if (!process.env.TABBY_ENABLE_SSH_ALG_BLACKLIST) {
ALGORITHM_BLACKLIST = []
}

View File

@@ -0,0 +1,61 @@
import { Observable, Subject } from 'rxjs'
import { Duplex } from 'stream'
export class SSHProxyStreamSocket extends Duplex {
constructor (private parent: SSHProxyStream) {
super({
allowHalfOpen: false,
})
}
_read (size: number): void {
this.parent.requestData(size)
}
_write (chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void {
this.parent.consumeInput(chunk).then(() => callback(null), e => callback(e))
}
_destroy (error: Error|null, callback: (error: Error|null) => void): void {
this.parent.handleStopRequest(error).then(() => callback(null), e => callback(e))
}
}
export abstract class SSHProxyStream {
get message$ (): Observable<string> { return this.message }
get destroyed$ (): Observable<Error|null> { return this.destroyed }
get socket (): SSHProxyStreamSocket|null { return this._socket }
private message = new Subject<string>()
private destroyed = new Subject<Error|null>()
private _socket: SSHProxyStreamSocket|null = null
async start (): Promise<SSHProxyStreamSocket> {
if (!this._socket) {
this._socket = new SSHProxyStreamSocket(this)
}
return this._socket
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
abstract requestData (size: number): void
abstract consumeInput (data: Buffer): Promise<void>
protected emitMessage (message: string): void {
this.message.next(message)
}
protected emitOutput (data: Buffer): void {
this._socket?.push(data)
}
async handleStopRequest (error: Error|null): Promise<void> {
this.destroyed.next(error)
this.destroyed.complete()
this.message.complete()
}
stop (error?: Error): void {
this._socket?.destroy(error)
}
}

View File

@@ -14,19 +14,11 @@ input.form-control.mt-2(
) )
.d-flex.mt-3 .d-flex.mt-3
checkbox( button.btn.btn-secondary(
*ngIf='isPassword()',
[(ngModel)]='remember',
[text]='"Save password"|translate'
)
.ms-auto
button.btn.btn-secondary.me-3(
*ngIf='step > 0', *ngIf='step > 0',
(click)='previous()' (click)='previous()'
) )
.ms-auto
button.btn.btn-primary( button.btn.btn-primary(
(click)='next()' (click)='next()'
) )

View File

@@ -1,7 +1,6 @@
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core' import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core'
import { KeyboardInteractivePrompt } from '../session/ssh' import { KeyboardInteractivePrompt } from '../session/ssh'
import { SSHProfile } from '../api'
import { PasswordStorageService } from '../services/passwordStorage.service'
@Component({ @Component({
selector: 'keyboard-interactive-auth-panel', selector: 'keyboard-interactive-auth-panel',
@@ -10,17 +9,13 @@ import { PasswordStorageService } from '../services/passwordStorage.service'
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class KeyboardInteractiveAuthComponent { export class KeyboardInteractiveAuthComponent {
@Input() profile: SSHProfile
@Input() prompt: KeyboardInteractivePrompt @Input() prompt: KeyboardInteractivePrompt
@Input() step = 0 @Input() step = 0
@Output() done = new EventEmitter() @Output() done = new EventEmitter()
@ViewChild('input') input: ElementRef @ViewChild('input') input: ElementRef
remember = false
constructor (private passwordStorage: PasswordStorageService) {}
isPassword (): boolean { isPassword (): boolean {
return this.prompt.isAPasswordPrompt(this.step) return this.prompt.prompts[this.step].prompt.toLowerCase().includes('password') || !this.prompt.prompts[this.step].echo
} }
previous (): void { previous (): void {
@@ -31,10 +26,6 @@ export class KeyboardInteractiveAuthComponent {
} }
next (): void { next (): void {
if (this.isPassword() && this.remember) {
this.passwordStorage.savePassword(this.profile, this.prompt.responses[this.step])
}
if (this.step === this.prompt.prompts.length - 1) { if (this.step === this.prompt.prompts.length - 1) {
this.prompt.respond() this.prompt.respond()
this.done.emit() this.done.emit()

View File

@@ -51,7 +51,6 @@ sftp-panel.bg-dark(
keyboard-interactive-auth-panel.bg-dark( keyboard-interactive-auth-panel.bg-dark(
*ngIf='activeKIPrompt', *ngIf='activeKIPrompt',
[prompt]='activeKIPrompt', [prompt]='activeKIPrompt',
[profile]='profile',
(click)='$event.stopPropagation()', (click)='$event.stopPropagation()',
(done)='activeKIPrompt = null; frontend?.focus()' (done)='activeKIPrompt = null; frontend?.focus()'
) )

View File

@@ -1,4 +1,3 @@
import * as russh from 'russh'
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker' import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
import colors from 'ansi-colors' import colors from 'ansi-colors'
import { Component, Injector, HostListener } from '@angular/core' import { Component, Injector, HostListener } from '@angular/core'
@@ -95,21 +94,17 @@ export class SSHTabComponent extends ConnectableTerminalTabComponent<SSHProfile>
} }
}) })
if (!(jumpSession.ssh instanceof russh.AuthenticatedSSHClient)) { session.jumpStream = await new Promise((resolve, reject) => jumpSession.ssh.forwardOut(
throw new Error('Jump session is not authenticated yet somehow') '127.0.0.1', 0, profile.options.host, profile.options.port ?? 22,
} (err, stream) => {
if (err) {
try { jumpSession.emitServiceMessage(colors.bgRed.black(' X ') + ` Could not set up port forward on ${jumpConnection.name}`)
session.jumpChannel = await jumpSession.ssh.openTCPForwardChannel({ reject(err)
addressToConnectTo: profile.options.host, return
portToConnectTo: profile.options.port ?? 22, }
originatorAddress: '127.0.0.1', resolve(stream)
originatorPort: 0, },
}) ))
} catch (err) {
jumpSession.emitServiceMessage(colors.bgRed.black(' X ') + ` Could not set up port forward on ${jumpConnection.name}`)
throw err
}
} }
} }
@@ -130,7 +125,7 @@ export class SSHTabComponent extends ConnectableTerminalTabComponent<SSHProfile>
}) })
if (!session.open) { if (!session.open) {
this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` Connecting to ${session.profile.name}\r\n`) this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` Connecting to ${session.profile.options.host}\r\n`)
this.startSpinner(this.translate.instant(_('Connecting'))) this.startSpinner(this.translate.instant(_('Connecting')))

View File

@@ -1,3 +1,5 @@
import './polyfills'
import { NgModule } from '@angular/core' import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common' import { CommonModule } from '@angular/common'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'

View File

@@ -0,0 +1,12 @@
import 'ssh2'
const nodeCrypto = require('crypto')
const browserDH = require('diffie-hellman/browser')
nodeCrypto.createDiffieHellmanGroup = browserDH.createDiffieHellmanGroup
nodeCrypto.createDiffieHellman = browserDH.createDiffieHellman
// Declare function missing from @types
declare module 'ssh2' {
interface Client {
setNoDelay: (enable?: boolean) => this
}
}

View File

@@ -1,11 +1,11 @@
import { Injectable, InjectFlags, Injector } from '@angular/core' import { Injectable, InjectFlags, Injector } from '@angular/core'
import { NewTabParameters, PartialProfile, TranslateService, QuickConnectProfileProvider } from 'tabby-core' import { NewTabParameters, PartialProfile, TranslateService, QuickConnectProfileProvider } from 'tabby-core'
import * as ALGORITHMS from 'ssh2/lib/protocol/constants'
import { SSHProfileSettingsComponent } from './components/sshProfileSettings.component' import { SSHProfileSettingsComponent } from './components/sshProfileSettings.component'
import { SSHTabComponent } from './components/sshTab.component' import { SSHTabComponent } from './components/sshTab.component'
import { PasswordStorageService } from './services/passwordStorage.service' import { PasswordStorageService } from './services/passwordStorage.service'
import { SSHAlgorithmType, SSHProfile } from './api' import { ALGORITHM_BLACKLIST, SSHAlgorithmType, SSHProfile } from './api'
import { SSHProfileImporter } from './api/importer' import { SSHProfileImporter } from './api/importer'
import { defaultAlgorithms } from './algorithms'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class SSHProfilesService extends QuickConnectProfileProvider<SSHProfile> { export class SSHProfilesService extends QuickConnectProfileProvider<SSHProfile> {
@@ -29,10 +29,10 @@ export class SSHProfilesService extends QuickConnectProfileProvider<SSHProfile>
agentForward: false, agentForward: false,
warnOnClose: null, warnOnClose: null,
algorithms: { algorithms: {
hmac: [] as string[], hmac: [],
kex: [] as string[], kex: [],
cipher: [] as string[], cipher: [],
serverHostKey: [] as string[], serverHostKey: [],
}, },
proxyCommand: null, proxyCommand: null,
forwardedPorts: [], forwardedPorts: [],
@@ -54,7 +54,13 @@ export class SSHProfilesService extends QuickConnectProfileProvider<SSHProfile>
) { ) {
super() super()
for (const k of Object.values(SSHAlgorithmType)) { for (const k of Object.values(SSHAlgorithmType)) {
this.configDefaults.options.algorithms[k] = [...defaultAlgorithms[k]] const defaultAlg = {
[SSHAlgorithmType.KEX]: 'DEFAULT_KEX',
[SSHAlgorithmType.HOSTKEY]: 'DEFAULT_SERVER_HOST_KEY',
[SSHAlgorithmType.CIPHER]: 'DEFAULT_CIPHER',
[SSHAlgorithmType.HMAC]: 'DEFAULT_MAC',
}[k]
this.configDefaults.options.algorithms[k] = ALGORITHMS[defaultAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x))
this.configDefaults.options.algorithms[k].sort() this.configDefaults.options.algorithms[k].sort()
} }
} }

View File

@@ -1,9 +1,15 @@
// import * as fs from 'fs/promises' import * as shellQuote from 'shell-quote'
import * as net from 'net'
import * as fs from 'fs/promises'
import * as tmp from 'tmp-promise' import * as tmp from 'tmp-promise'
import socksv5 from '@luminati-io/socksv5'
import { Duplex } from 'stream'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { spawn } from 'child_process'
import { ChildProcess } from 'node:child_process'
import { ConfigService, HostAppService, Platform, PlatformService } from 'tabby-core' import { ConfigService, HostAppService, Platform, PlatformService } from 'tabby-core'
import { SSHSession } from '../session/ssh' import { SSHSession } from '../session/ssh'
import { SSHProfile } from '../api' import { SSHProfile, SSHProxyStream, SSHProxyStreamSocket } from '../api'
import { PasswordStorageService } from './passwordStorage.service' import { PasswordStorageService } from './passwordStorage.service'
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
@@ -49,7 +55,7 @@ export class SSHService {
let tmpFile: tmp.FileResult|null = null let tmpFile: tmp.FileResult|null = null
if (session.activePrivateKey) { if (session.activePrivateKey) {
tmpFile = await tmp.file() tmpFile = await tmp.file()
// await fs.writeFile(tmpFile.path, session.activePrivateKey) await fs.writeFile(tmpFile.path, session.activePrivateKey)
const winSCPcom = path.slice(0, -3) + 'com' const winSCPcom = path.slice(0, -3) + 'com'
await this.platform.exec(winSCPcom, ['/keygen', tmpFile.path, `/output=${tmpFile.path}`]) await this.platform.exec(winSCPcom, ['/keygen', tmpFile.path, `/output=${tmpFile.path}`])
args.push(`/privatekey=${tmpFile.path}`) args.push(`/privatekey=${tmpFile.path}`)
@@ -58,3 +64,171 @@ export class SSHService {
tmpFile?.cleanup() tmpFile?.cleanup()
} }
} }
export class ProxyCommandStream extends SSHProxyStream {
private process: ChildProcess|null
constructor (private command: string) {
super()
}
async start (): Promise<SSHProxyStreamSocket> {
const argv = shellQuote.parse(this.command)
this.process = spawn(argv[0], argv.slice(1), {
windowsHide: true,
stdio: ['pipe', 'pipe', 'pipe'],
})
this.process.on('error', error => {
this.stop(new Error(`Proxy command has failed to start: ${error.message}`))
})
this.process.on('exit', code => {
this.stop(new Error(`Proxy command has exited with code ${code}`))
})
this.process.stdout?.on('data', data => {
this.emitOutput(data)
})
this.process.stdout?.on('error', (err) => {
this.stop(err)
})
this.process.stderr?.on('data', data => {
this.emitMessage(data.toString())
})
return super.start()
}
requestData (size: number): void {
this.process?.stdout?.read(size)
}
async consumeInput (data: Buffer): Promise<void> {
const process = this.process
if (process) {
await new Promise(resolve => process.stdin?.write(data, resolve))
}
}
async stop (error?: Error): Promise<void> {
this.process?.kill()
super.stop(error)
}
}
export class SocksProxyStream extends SSHProxyStream {
private client: Duplex|null
private header: Buffer|null
constructor (private profile: SSHProfile) {
super()
}
async start (): Promise<SSHProxyStreamSocket> {
this.client = await new Promise((resolve, reject) => {
const connector = socksv5.connect({
host: this.profile.options.host,
port: this.profile.options.port,
proxyHost: this.profile.options.socksProxyHost ?? '127.0.0.1',
proxyPort: this.profile.options.socksProxyPort ?? 5000,
auths: [socksv5.auth.None()],
strictLocalDNS: false,
}, s => {
resolve(s)
this.header = s.read()
if (this.header) {
this.emitOutput(this.header)
}
})
connector.on('error', (err) => {
reject(err)
this.stop(new Error(`SOCKS connection failed: ${err.message}`))
})
})
this.client?.on('data', data => {
if (!this.header || data !== this.header) {
// socksv5 doesn't reliably emit the first data event
this.emitOutput(data)
this.header = null
}
})
this.client?.on('close', error => {
this.stop(error)
})
return super.start()
}
requestData (size: number): void {
this.client?.read(size)
}
async consumeInput (data: Buffer): Promise<void> {
return new Promise((resolve, reject) => {
this.client?.write(data, undefined, err => err ? reject(err) : resolve())
})
}
async stop (error?: Error): Promise<void> {
this.client?.destroy()
super.stop(error)
}
}
export class HTTPProxyStream extends SSHProxyStream {
private client: Duplex|null
private connected = false
constructor (private profile: SSHProfile) {
super()
}
async start (): Promise<SSHProxyStreamSocket> {
this.client = await new Promise((resolve, reject) => {
const connector = net.createConnection({
host: this.profile.options.httpProxyHost!,
port: this.profile.options.httpProxyPort!,
}, () => resolve(connector))
connector.on('error', error => {
reject(error)
this.stop(new Error(`Proxy connection failed: ${error.message}`))
})
})
this.client?.write(Buffer.from(`CONNECT ${this.profile.options.host}:${this.profile.options.port} HTTP/1.1\r\n\r\n`))
this.client?.on('data', (data: Buffer) => {
if (this.connected) {
this.emitOutput(data)
} else {
if (data.slice(0, 5).equals(Buffer.from('HTTP/'))) {
const idx = data.indexOf('\n\n')
const headers = data.slice(0, idx).toString()
const code = parseInt(headers.split(' ')[1])
if (code >= 200 && code < 300) {
this.emitMessage('Connected')
this.emitOutput(data.slice(idx + 2))
this.connected = true
} else {
this.stop(new Error(`Connection failed, code ${code}`))
}
}
}
})
this.client?.on('close', error => {
this.stop(error)
})
return super.start()
}
requestData (size: number): void {
this.client?.read(size)
}
async consumeInput (data: Buffer): Promise<void> {
return new Promise((resolve, reject) => {
this.client?.write(data, undefined, err => err ? reject(err) : resolve())
})
}
async stop (error?: Error): Promise<void> {
this.client?.destroy()
super.stop(error)
}
}

View File

@@ -1,9 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ import * as C from 'constants'
import { Subject, Observable } from 'rxjs' import { Subject, Observable } from 'rxjs'
import { posix as posixPath } from 'path' import { posix as posixPath } from 'path'
import { Injector } from '@angular/core' import { Injector, NgZone } from '@angular/core'
import { FileDownload, FileUpload, Logger, LogService } from 'tabby-core' import { FileDownload, FileUpload, Logger, LogService, wrapPromise } from 'tabby-core'
import * as russh from 'russh' import { SFTPWrapper } from 'ssh2'
import { promisify } from 'util'
import type { FileEntry, Stats } from 'ssh2-streams'
export interface SFTPFile { export interface SFTPFile {
name: string name: string
@@ -19,37 +22,63 @@ export class SFTPFileHandle {
position = 0 position = 0
constructor ( constructor (
private inner: russh.SFTPFile|null, private sftp: SFTPWrapper,
private handle: Buffer,
private zone: NgZone,
) { } ) { }
async read (): Promise<Uint8Array> { read (): Promise<Buffer> {
if (!this.inner) { const buffer = Buffer.alloc(256 * 1024)
return Promise.resolve(new Uint8Array(0)) return wrapPromise(this.zone, new Promise((resolve, reject) => {
} while (true) {
return this.inner.read(256 * 1024) const wait = this.sftp.read(this.handle, buffer, 0, buffer.length, this.position, (err, read) => {
if (err) {
reject(err)
return
}
this.position += read
resolve(buffer.slice(0, read))
})
if (!wait) {
break
}
}
}))
} }
async write (chunk: Uint8Array): Promise<void> { write (chunk: Buffer): Promise<void> {
if (!this.inner) { return wrapPromise(this.zone, new Promise<void>((resolve, reject) => {
throw new Error('File handle is closed') while (true) {
} const wait = this.sftp.write(this.handle, chunk, 0, chunk.length, this.position, err => {
await this.inner.writeAll(chunk) if (err) {
reject(err)
return
}
this.position += chunk.length
resolve()
})
if (!wait) {
break
}
}
}))
} }
async close (): Promise<void> { close (): Promise<void> {
await this.inner?.shutdown() return wrapPromise(this.zone, promisify(this.sftp.close.bind(this.sftp))(this.handle))
this.inner = null
} }
} }
export class SFTPSession { export class SFTPSession {
get closed$ (): Observable<void> { return this.closed } get closed$ (): Observable<void> { return this.closed }
private closed = new Subject<void>() private closed = new Subject<void>()
private zone: NgZone
private logger: Logger private logger: Logger
constructor (private sftp: russh.SFTP, injector: Injector) { constructor (private sftp: SFTPWrapper, injector: Injector) {
this.zone = injector.get(NgZone)
this.logger = injector.get(LogService).create('sftp') this.logger = injector.get(LogService).create('sftp')
sftp.closed$.subscribe(() => { sftp.on('close', () => {
this.closed.next() this.closed.next()
this.closed.complete() this.closed.complete()
}) })
@@ -57,64 +86,67 @@ export class SFTPSession {
async readdir (p: string): Promise<SFTPFile[]> { async readdir (p: string): Promise<SFTPFile[]> {
this.logger.debug('readdir', p) this.logger.debug('readdir', p)
const entries = await this.sftp.readDirectory(p) const entries = await wrapPromise(this.zone, promisify<FileEntry[]>(f => this.sftp.readdir(p, f))())
return entries.map(entry => this._makeFile( return entries.map(entry => this._makeFile(
posixPath.join(p, entry.name), entry, posixPath.join(p, entry.filename), entry,
)) ))
} }
readlink (p: string): Promise<string> { readlink (p: string): Promise<string> {
this.logger.debug('readlink', p) this.logger.debug('readlink', p)
return this.sftp.readlink(p) return wrapPromise(this.zone, promisify<string>(f => this.sftp.readlink(p, f))())
} }
async stat (p: string): Promise<SFTPFile> { async stat (p: string): Promise<SFTPFile> {
this.logger.debug('stat', p) this.logger.debug('stat', p)
const stats = await this.sftp.stat(p) const stats = await wrapPromise(this.zone, promisify<Stats>(f => this.sftp.stat(p, f))())
return { return {
name: posixPath.basename(p), name: posixPath.basename(p),
fullPath: p, fullPath: p,
isDirectory: stats.type === russh.SFTPFileType.Directory, isDirectory: stats.isDirectory(),
isSymlink: stats.type === russh.SFTPFileType.Symlink, isSymlink: stats.isSymbolicLink(),
mode: stats.permissions ?? 0, mode: stats.mode,
size: stats.size, size: stats.size,
modified: new Date((stats.mtime ?? 0) * 1000), modified: new Date(stats.mtime * 1000),
} }
} }
async open (p: string, mode: number): Promise<SFTPFileHandle> { async open (p: string, mode: string): Promise<SFTPFileHandle> {
this.logger.debug('open', p, mode) this.logger.debug('open', p)
const handle = await this.sftp.open(p, mode) const handle = await wrapPromise(this.zone, promisify<Buffer>(f => this.sftp.open(p, mode, f))())
return new SFTPFileHandle(handle) return new SFTPFileHandle(this.sftp, handle, this.zone)
} }
async rmdir (p: string): Promise<void> { async rmdir (p: string): Promise<void> {
await this.sftp.removeDirectory(p) this.logger.debug('rmdir', p)
await promisify((f: any) => this.sftp.rmdir(p, f))()
} }
async mkdir (p: string): Promise<void> { async mkdir (p: string): Promise<void> {
await this.sftp.createDirectory(p) this.logger.debug('mkdir', p)
await promisify((f: any) => this.sftp.mkdir(p, f))()
} }
async rename (oldPath: string, newPath: string): Promise<void> { async rename (oldPath: string, newPath: string): Promise<void> {
this.logger.debug('rename', oldPath, newPath) this.logger.debug('rename', oldPath, newPath)
await this.sftp.rename(oldPath, newPath) await promisify((f: any) => this.sftp.rename(oldPath, newPath, f))()
} }
async unlink (p: string): Promise<void> { async unlink (p: string): Promise<void> {
await this.sftp.removeFile(p) this.logger.debug('unlink', p)
await promisify((f: any) => this.sftp.unlink(p, f))()
} }
async chmod (p: string, mode: string|number): Promise<void> { async chmod (p: string, mode: string|number): Promise<void> {
this.logger.debug('chmod', p, mode) this.logger.debug('chmod', p, mode)
await this.sftp.chmod(p, mode) await promisify((f: any) => this.sftp.chmod(p, mode, f))()
} }
async upload (path: string, transfer: FileUpload): Promise<void> { async upload (path: string, transfer: FileUpload): Promise<void> {
this.logger.info('Uploading into', path) this.logger.info('Uploading into', path)
const tempPath = path + '.tabby-upload' const tempPath = path + '.tabby-upload'
try { try {
const handle = await this.open(tempPath, russh.OPEN_WRITE | russh.OPEN_CREATE) const handle = await this.open(tempPath, 'w')
while (true) { while (true) {
const chunk = await transfer.read() const chunk = await transfer.read()
if (!chunk.length) { if (!chunk.length) {
@@ -122,13 +154,15 @@ export class SFTPSession {
} }
await handle.write(chunk) await handle.write(chunk)
} }
await handle.close() handle.close()
await this.unlink(path).catch(() => null) try {
await this.unlink(path)
} catch { }
await this.rename(tempPath, path) await this.rename(tempPath, path)
transfer.close() transfer.close()
} catch (e) { } catch (e) {
transfer.cancel() transfer.cancel()
this.unlink(tempPath).catch(() => null) this.unlink(tempPath)
throw e throw e
} }
} }
@@ -136,7 +170,7 @@ export class SFTPSession {
async download (path: string, transfer: FileDownload): Promise<void> { async download (path: string, transfer: FileDownload): Promise<void> {
this.logger.info('Downloading', path) this.logger.info('Downloading', path)
try { try {
const handle = await this.open(path, russh.OPEN_READ) const handle = await this.open(path, 'r')
while (true) { while (true) {
const chunk = await handle.read() const chunk = await handle.read()
if (!chunk.length) { if (!chunk.length) {
@@ -152,15 +186,15 @@ export class SFTPSession {
} }
} }
private _makeFile (p: string, entry: russh.SFTPDirectoryEntry): SFTPFile { private _makeFile (p: string, entry: FileEntry): SFTPFile {
return { return {
fullPath: p, fullPath: p,
name: posixPath.basename(p), name: posixPath.basename(p),
isDirectory: entry.metadata.type === russh.SFTPFileType.Directory, isDirectory: (entry.attrs.mode & C.S_IFDIR) === C.S_IFDIR,
isSymlink: entry.metadata.type === russh.SFTPFileType.Symlink, isSymlink: (entry.attrs.mode & C.S_IFLNK) === C.S_IFLNK,
mode: entry.metadata.permissions ?? 0, mode: entry.attrs.mode,
size: entry.metadata.size, size: entry.attrs.size,
modified: new Date((entry.metadata.mtime ?? 0) * 1000), modified: new Date(entry.attrs.mtime * 1000),
} }
} }
} }

View File

@@ -1,15 +1,15 @@
import { Observable, Subject } from 'rxjs' import { Observable, Subject } from 'rxjs'
import stripAnsi from 'strip-ansi' import stripAnsi from 'strip-ansi'
import { ClientChannel } from 'ssh2'
import { Injector } from '@angular/core' import { Injector } from '@angular/core'
import { LogService } from 'tabby-core' import { LogService } from 'tabby-core'
import { BaseSession, UTF8SplitterMiddleware, InputProcessor } from 'tabby-terminal' import { BaseSession, UTF8SplitterMiddleware, InputProcessor } from 'tabby-terminal'
import { SSHSession } from './ssh' import { SSHSession } from './ssh'
import { SSHProfile } from '../api' import { SSHProfile } from '../api'
import * as russh from 'russh'
export class SSHShellSession extends BaseSession { export class SSHShellSession extends BaseSession {
shell?: russh.Channel shell?: ClientChannel
get serviceMessage$ (): Observable<string> { return this.serviceMessage } get serviceMessage$ (): Observable<string> { return this.serviceMessage }
private serviceMessage = new Subject<string>() private serviceMessage = new Subject<string>()
private ssh: SSHSession|null private ssh: SSHSession|null
@@ -53,11 +53,19 @@ export class SSHShellSession extends BaseSession {
this.loginScriptProcessor?.executeUnconditionalScripts() this.loginScriptProcessor?.executeUnconditionalScripts()
this.shell.data$.subscribe(data => { this.shell.on('greeting', greeting => {
this.emitOutput(Buffer.from(data)) this.emitServiceMessage(`Shell greeting: ${greeting}`)
}) })
this.shell.eof$.subscribe(() => { this.shell.on('banner', banner => {
this.emitServiceMessage(`Shell banner: ${banner}`)
})
this.shell.on('data', data => {
this.emitOutput(data)
})
this.shell.on('end', () => {
this.logger.info('Shell session ended') this.logger.info('Shell session ended')
if (this.open) { if (this.open) {
this.destroy() this.destroy()
@@ -71,22 +79,19 @@ export class SSHShellSession extends BaseSession {
} }
resize (columns: number, rows: number): void { resize (columns: number, rows: number): void {
this.shell?.resizePTY({ if (this.shell) {
columns, this.shell.setWindow(rows, columns, rows, columns)
rows, }
pixHeight: 0,
pixWidth: 0,
})
} }
write (data: Buffer): void { write (data: Buffer): void {
if (this.shell) { if (this.shell) {
this.shell.write(new Uint8Array(data)) this.shell.write(data)
} }
} }
kill (_signal?: string): void { kill (signal?: string): void {
// this.shell?.signal(signal ?? 'TERM') this.shell?.signal(signal ?? 'TERM')
} }
async destroy (): Promise<void> { async destroy (): Promise<void> {

File diff suppressed because it is too large Load Diff

View File

@@ -7,4 +7,9 @@ import config from '../webpack.plugin.config.mjs'
export default () => config({ export default () => config({
name: 'ssh', name: 'ssh',
dirname: __dirname, dirname: __dirname,
alias: {
'cpu-features': false,
'./crypto/build/Release/sshcrypto.node': false,
'../build/Release/cpufeatures.node': false,
},
}) })

View File

@@ -9,11 +9,33 @@
dependencies: dependencies:
ipv6 "*" ipv6 "*"
"@types/node@*":
version "22.1.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b"
integrity sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==
dependencies:
undici-types "~6.13.0"
"@types/node@20.3.1": "@types/node@20.3.1":
version "20.3.1" version "20.3.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe"
integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==
"@types/ssh2-streams@*":
version "0.1.12"
resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz#e68795ba2bf01c76b93f9c9809e1f42f0eaaec5f"
integrity sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==
dependencies:
"@types/node" "*"
"@types/ssh2@^0.5.46":
version "0.5.52"
resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.52.tgz#9dbd8084e2a976e551d5e5e70b978ed8b5965741"
integrity sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==
dependencies:
"@types/node" "*"
"@types/ssh2-streams" "*"
ansi-colors@^4.1.1: ansi-colors@^4.1.1:
version "4.1.3" version "4.1.3"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
@@ -24,16 +46,40 @@ ansi-regex@^6.0.1:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
dependencies:
safer-buffer "~2.1.0"
assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
async@0.2.x: async@0.2.x:
version "0.2.10" version "0.2.10"
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
integrity sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ== integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E=
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
dependencies:
tweetnacl "^0.14.3"
bn.js@^4.0.0, bn.js@^4.1.0:
version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
brace-expansion@^1.1.7: brace-expansion@^1.1.7:
version "1.1.11" version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -42,17 +88,22 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0" balanced-match "^1.0.0"
concat-map "0.0.1" concat-map "0.0.1"
brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==
cli@0.4.x: cli@0.4.x:
version "0.4.5" version "0.4.5"
resolved "https://registry.yarnpkg.com/cli/-/cli-0.4.5.tgz#78f9485cd161b566e9a6c72d7170c4270e81db61" resolved "https://registry.yarnpkg.com/cli/-/cli-0.4.5.tgz#78f9485cd161b566e9a6c72d7170c4270e81db61"
integrity sha512-dbn5HyeJWSOU58RwOEiF1VWrl7HRvDsKLpu0uiI/vExH6iNoyUzjB5Mr3IJY5DVUfnbpe9793xw4DFJVzC9nWQ== integrity sha1-ePlIXNFhtWbppsctcXDEJw6B22E=
dependencies: dependencies:
glob ">= 3.1.4" glob ">= 3.1.4"
cliff@0.1.x: cliff@0.1.x:
version "0.1.10" version "0.1.10"
resolved "https://registry.yarnpkg.com/cliff/-/cliff-0.1.10.tgz#53be33ea9f59bec85609ee300ac4207603e52013" resolved "https://registry.yarnpkg.com/cliff/-/cliff-0.1.10.tgz#53be33ea9f59bec85609ee300ac4207603e52013"
integrity sha512-roZWcC2Cxo/kKjRXw7YUpVNtxJccbvcl7VzTjUYgLQk6Ot0R8bm2netbhSZYWWNrKlOO/7HD6GXHl8dtzE6SiQ== integrity sha1-U74z6p9ZvshWCe4wCsQgdgPlIBM=
dependencies: dependencies:
colors "~1.0.3" colors "~1.0.3"
eyes "~0.1.8" eyes "~0.1.8"
@@ -61,12 +112,12 @@ cliff@0.1.x:
colors@0.6.x: colors@0.6.x:
version "0.6.2" version "0.6.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc"
integrity sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw== integrity sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=
colors@~1.0.3: colors@~1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
integrity sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw== integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=
concat-map@0.0.1: concat-map@0.0.1:
version "0.0.1" version "0.0.1"
@@ -76,19 +127,62 @@ concat-map@0.0.1:
cycle@1.0.x: cycle@1.0.x:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
integrity sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA== integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI=
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
dependencies:
assert-plus "^1.0.0"
diffie-hellman@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
dependencies:
bn.js "^4.1.0"
miller-rabin "^4.0.0"
randombytes "^2.0.0"
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
eyes@0.1.x, eyes@~0.1.8: eyes@0.1.x, eyes@~0.1.8:
version "0.1.8" version "0.1.8"
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=
fs.realpath@^1.0.0: fs.realpath@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
glob@7.2.3, "glob@>= 3.1.4", glob@^7.1.3: getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
dependencies:
assert-plus "^1.0.0"
"glob@>= 3.1.4":
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.1.3:
version "7.2.3" version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -116,7 +210,7 @@ inherits@2:
ipv6@*: ipv6@*:
version "3.1.3" version "3.1.3"
resolved "https://registry.yarnpkg.com/ipv6/-/ipv6-3.1.3.tgz#4d9064f9c2dafa0dd10b8b7d76ffca4aad31b3b9" resolved "https://registry.yarnpkg.com/ipv6/-/ipv6-3.1.3.tgz#4d9064f9c2dafa0dd10b8b7d76ffca4aad31b3b9"
integrity sha512-TmLbUIURMAZ161GZDddTtAAb3aceRNLn7PRmP8fANp8xDRCW9oIQva8eenA48bRvw347jBqSREXMI38DybbUiQ== integrity sha1-TZBk+cLa+g3RC4t9dv/KSq0xs7k=
dependencies: dependencies:
cli "0.4.x" cli "0.4.x"
cliff "0.1.x" cliff "0.1.x"
@@ -125,7 +219,27 @@ ipv6@*:
isstream@0.1.x: isstream@0.1.x:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
dependencies:
bn.js "^4.0.0"
brorand "^1.0.1"
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
minimatch@^3.1.1: minimatch@^3.1.1:
version "3.1.2" version "3.1.2"
@@ -149,7 +263,14 @@ path-is-absolute@^1.0.0:
pkginfo@0.3.x: pkginfo@0.3.x:
version "0.3.1" version "0.3.1"
resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21" resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21"
integrity sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A== integrity sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=
randombytes@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
dependencies:
safe-buffer "^5.1.0"
rimraf@^3.0.0: rimraf@^3.0.0:
version "3.0.2" version "3.0.2"
@@ -163,15 +284,39 @@ run-script-os@^1.1.3:
resolved "https://registry.yarnpkg.com/run-script-os/-/run-script-os-1.1.6.tgz#8b0177fb1b54c99a670f95c7fdc54f18b9c72347" resolved "https://registry.yarnpkg.com/run-script-os/-/run-script-os-1.1.6.tgz#8b0177fb1b54c99a670f95c7fdc54f18b9c72347"
integrity sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw== integrity sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==
safe-buffer@^5.1.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.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sprintf@0.1.x: sprintf@0.1.x:
version "0.1.5" version "0.1.5"
resolved "https://registry.yarnpkg.com/sprintf/-/sprintf-0.1.5.tgz#8f83e39a9317c1a502cb7db8050e51c679f6edcf" resolved "https://registry.yarnpkg.com/sprintf/-/sprintf-0.1.5.tgz#8f83e39a9317c1a502cb7db8050e51c679f6edcf"
integrity sha512-4X5KsuXFQ7f+d7Y+bi4qSb6eI+YoifDTGr0MQJXRoYO7BO7evfRCjds6kk3z7l5CiJYxgDN1x5Er4WiyCt+zTQ== integrity sha1-j4PjmpMXwaUCy324BQ5Rxnn27c8=
sshpk@Eugeny/node-sshpk#c2b71d1243714d2daf0988f84c3323d180817136:
version "1.18.0"
resolved "https://codeload.github.com/Eugeny/node-sshpk/tar.gz/c2b71d1243714d2daf0988f84c3323d180817136"
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
bcrypt-pbkdf "^1.0.0"
dashdash "^1.12.0"
ecc-jsbn "~0.1.1"
getpass "^0.1.1"
jsbn "~0.1.0"
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
stack-trace@0.0.x: stack-trace@0.0.x:
version "0.0.10" version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=
strip-ansi@^7.0.0: strip-ansi@^7.0.0:
version "7.1.0" version "7.1.0"
@@ -194,10 +339,20 @@ tmp@^0.2.0:
dependencies: dependencies:
rimraf "^3.0.0" rimraf "^3.0.0"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
undici-types@~6.13.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5"
integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==
winston@0.8.x: winston@0.8.x:
version "0.8.3" version "0.8.3"
resolved "https://registry.yarnpkg.com/winston/-/winston-0.8.3.tgz#64b6abf4cd01adcaefd5009393b1d8e8bec19db0" resolved "https://registry.yarnpkg.com/winston/-/winston-0.8.3.tgz#64b6abf4cd01adcaefd5009393b1d8e8bec19db0"
integrity sha512-fPoamsHq8leJ62D1M9V/f15mjQ1UHe4+7j1wpAT3fqgA5JqhJkk4aIfPEjfMTI9x6ZTjaLOpMAjluLtmgO5b6g== integrity sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=
dependencies: dependencies:
async "0.2.x" async "0.2.x"
colors "0.6.x" colors "0.6.x"

View File

@@ -180,7 +180,9 @@ export class SaveAsProfileContextMenu extends TabContextMenuItemProvider {
return return
} }
const options = JSON.parse(JSON.stringify(tab.profile.options)) const options = {
...tab.profile.options,
}
const cwd = await tab.session?.getWorkingDirectory() ?? tab.profile.options.cwd const cwd = await tab.session?.getWorkingDirectory() ?? tab.profile.options.cwd
if (cwd) { if (cwd) {

Some files were not shown because too many files have changed in this diff Show More