mirror of
https://github.com/Eugeny/tabby.git
synced 2025-04-24 16:30:55 +08:00
Compare commits
93 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c3dfb351fc | ||
|
7b942c4e28 | ||
|
c5759b29ca | ||
|
66257c87af | ||
|
424b062d5b | ||
|
5deb725758 | ||
|
4069c22891 | ||
|
4a515d9432 | ||
|
b83b2e5acc | ||
|
e407ee8bf1 | ||
|
c7b39bdca7 | ||
|
934cdff0f8 | ||
|
ab87099b8b | ||
|
47b4b54557 | ||
|
15f4182e0e | ||
|
4be1e12559 | ||
|
5d2d179677 | ||
|
4197cefdfd | ||
|
7c1421ffcf | ||
|
380c306d89 | ||
|
cf0da75224 | ||
|
d1c1b48502 | ||
|
a3c5b41bb1 | ||
|
2fa7678bec | ||
|
c6939b114d | ||
|
d3e8e2a6af | ||
|
d1b161364b | ||
|
c4a514fc4a | ||
|
d525374061 | ||
|
6db08b765f | ||
|
d8d346c507 | ||
|
6ffeb61c9c | ||
|
92c729dada | ||
|
302f88058c | ||
|
66c173b1b5 | ||
|
f9dadf0816 | ||
|
0a475daa9c | ||
|
ba7c31d940 | ||
|
52a85e4f36 | ||
|
ddbb2feb9c | ||
|
c43ee28a0c | ||
|
9f3c7a95cf | ||
|
02205410b6 | ||
|
9bcbde265d | ||
|
fee857a0f6 | ||
|
73f978781c | ||
|
87e99654f6 | ||
|
93513541f7 | ||
|
60a6c148d8 | ||
|
070343aebe | ||
|
fe376a48d8 | ||
|
2008f30cf5 | ||
|
53ca484ca6 | ||
|
fe46ed7847 | ||
|
e90731f427 | ||
|
025cc68ad8 | ||
|
6edb1e9cd4 | ||
|
c6981a8216 | ||
|
5d318478ec | ||
|
39e3ba35c4 | ||
|
d0dd09ad88 | ||
|
1e44d8c525 | ||
|
a10e9c05b6 | ||
|
6b0b1563c5 | ||
|
04b34b0edd | ||
|
2346e10b70 | ||
|
55c13edace | ||
|
6a91565da1 | ||
|
90d0bbce23 | ||
|
daa5687cfc | ||
|
9590ab8bf6 | ||
|
d7971294aa | ||
|
67230c1601 | ||
|
09556ae6a1 | ||
|
eae946d0f0 | ||
|
33eb5bd800 | ||
|
6196f3b85d | ||
|
e1e6e1cdab | ||
|
ac6f60f1ae | ||
|
aa67130e37 | ||
|
21aedb6045 | ||
|
2bc95bb0e0 | ||
|
b4d3667b9a | ||
|
e84cbd82d8 | ||
|
2ecccad2db | ||
|
aab7e285a9 | ||
|
3eaa600544 | ||
|
9beab8994b | ||
|
d9363b5de1 | ||
|
ed22309e93 | ||
|
5932b8664b | ||
|
0ca097fa23 | ||
|
e8fdb8b8f9 |
@ -1328,6 +1328,33 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "SelfHosted-Club",
|
||||
"name": "SelfHosted",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/128927132?v=4",
|
||||
"profile": "https://www.selfhosted.sg/",
|
||||
"contributions": [
|
||||
"financial"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "xhiroga",
|
||||
"name": "Hiroaki Ogasawara",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13391129?v=4",
|
||||
"profile": "http://hiroga.hatenablog.com/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "geodic",
|
||||
"name": "geodic",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/64704703?v=4",
|
||||
"profile": "https://github.com/geodic",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
128
.github/workflows/build.yml
vendored
128
.github/workflows/build.yml
vendored
@ -2,7 +2,7 @@ name: Package-Build
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
Lint:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -13,10 +13,11 @@ jobs:
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v3.7.0
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 22
|
||||
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y libfontconfig1-dev
|
||||
npm i -g yarn
|
||||
cd app
|
||||
yarn
|
||||
@ -31,15 +32,21 @@ jobs:
|
||||
run: yarn run lint
|
||||
|
||||
macOS-Build:
|
||||
runs-on: macos-12
|
||||
runs-on: macos-15
|
||||
needs: Lint
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
rust_triple: x86_64-apple-darwin
|
||||
- arch: arm64
|
||||
rust_triple: aarch64-apple-darwin
|
||||
fail-fast: false
|
||||
|
||||
env:
|
||||
ARCH: ${{matrix.arch}}
|
||||
RUST_TARGET_TRIPLE: ${{matrix.rust_triple}}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@ -49,22 +56,16 @@ jobs:
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v3.7.0
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 22
|
||||
|
||||
- run: rustup target add ${{matrix.rust_triple}}
|
||||
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo -H pip3 install setuptools
|
||||
sudo npm i -g yarn
|
||||
yarn --network-timeout 1000000
|
||||
env:
|
||||
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
|
||||
run: yarn run build
|
||||
|
||||
@ -80,7 +81,7 @@ jobs:
|
||||
|
||||
- name: Build and sign packages
|
||||
run: scripts/build-macos.mjs
|
||||
if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
|
||||
if: github.event_name == 'push' && (github.ref_protected || startsWith(github.ref, 'refs/tags'))
|
||||
env:
|
||||
ARCH: ${{matrix.arch}}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@ -95,7 +96,7 @@ jobs:
|
||||
|
||||
- name: Build packages without signing
|
||||
run: scripts/build-macos.mjs
|
||||
if: "! (github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
|
||||
if: "! (github.event_name == 'push' && (github.ref_protected || startsWith(github.ref, 'refs/tags')))"
|
||||
env:
|
||||
ARCH: ${{matrix.arch}}
|
||||
# DEBUG: electron-builder,electron-builder:*
|
||||
@ -129,25 +130,34 @@ jobs:
|
||||
path: artifact-zip
|
||||
|
||||
Linux-Build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ${{matrix.os}}
|
||||
needs: Lint
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- build-arch: x64
|
||||
arch: amd64
|
||||
rust_triple: x86_64-unknown-linux-gnu
|
||||
os: ubuntu-24.04
|
||||
- build-arch: arm64
|
||||
arch: arm64
|
||||
rust_triple: aarch64-unknown-linux-gnu
|
||||
triplet: aarch64-linux-gnu-
|
||||
os: ubuntu-24.04-arm
|
||||
- build-arch: arm
|
||||
arch: armhf
|
||||
rust_triple: arm-unknown-linux-gnueabihf
|
||||
triplet: arm-linux-gnueabihf-
|
||||
os: ubuntu-24.04
|
||||
fail-fast: false
|
||||
|
||||
env:
|
||||
CC: ${{matrix.triplet}}gcc
|
||||
CXX: ${{matrix.triplet}}g++
|
||||
ARCH: ${{matrix.build-arch}}
|
||||
npm_config_arch: ${{matrix.build-arch}}
|
||||
npm_config_target_arch: ${{matrix.build-arch}}
|
||||
RUST_TARGET_TRIPLE: ${{matrix.rust_triple}}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -158,21 +168,27 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v3.7.0
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 22
|
||||
|
||||
- name: Install FPM
|
||||
run: |
|
||||
sudo gem install fpm
|
||||
|
||||
- run: rustup target add ${{matrix.rust_triple}}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libarchive-tools zsh crossbuild-essential-${{matrix.arch}}
|
||||
sudo apt-get install libfontconfig1-dev libarchive-tools zsh crossbuild-essential-${{matrix.arch}}
|
||||
|
||||
- name: Setup tar to run as root
|
||||
run: sudo chmod u+s "$(command -v tar)"
|
||||
if: matrix.build-arch != 'x64'
|
||||
if: matrix.build-arch == 'arm'
|
||||
|
||||
- name: Download cached sysroot
|
||||
uses: actions/cache@v3
|
||||
id: dl-cached-sysroot
|
||||
if: matrix.build-arch !='x64'
|
||||
if: matrix.build-arch == 'arm'
|
||||
with:
|
||||
key: sysroot-${{matrix.build-arch}}
|
||||
path: /${{matrix.build-arch}}-sysroot
|
||||
@ -182,7 +198,7 @@ jobs:
|
||||
sudo apt-get update -y && sudo apt-get install debootstrap qemu-user-static binfmt-support -y
|
||||
sudo qemu-debootstrap --include=libfontconfig1-dev,libsecret-1-dev,libnss3,libatk1.0-0,libatk-bridge2.0-0,libgdk-pixbuf2.0-0,libgtk-3-0,libgbm1 --variant=buildd --exclude=snapd --components=main,restricted,universe,multiverse --extractor=dpkg-deb --arch ${{matrix.arch}} bionic /${{matrix.build-arch}}-sysroot/ http://ports.ubuntu.com/ubuntu-ports/
|
||||
sudo find /${{matrix.build-arch}}-sysroot -type l -lname '/*' -exec sh -c 'file="$0"; dir=$(dirname "$file"); target=$(readlink "$0"); prefix=$(dirname "$dir" | sed 's@[^/]*@\.\.@g'); newtarget="$prefix$target"; ln -snf $newtarget $file' {} \; ;
|
||||
if: matrix.build-arch != 'x64' && steps.dl-cached-sysroot.outputs.cache-hit != 'true'
|
||||
if: matrix.build-arch == 'arm' && steps.dl-cached-sysroot.outputs.cache-hit != 'true'
|
||||
|
||||
- name: Setup env to use ${{matrix.build-arch}} sysroot
|
||||
run: |
|
||||
@ -197,9 +213,9 @@ jobs:
|
||||
elif [[ ${{matrix.arch}} == 'arm64' ]]; then
|
||||
echo "PKG_CONFIG_PATH=/${{matrix.build-arch}}-sysroot/usr/lib/pkgconfig/:/${{matrix.build-arch}}-sysroot/usr/lib/aarch64-linux-gnu/pkgconfig/" >> $GITHUB_ENV
|
||||
fi
|
||||
if: matrix.build-arch != 'x64'
|
||||
if: matrix.build-arch == 'arm'
|
||||
|
||||
- name: Install npm_modules (amd64)
|
||||
- name: Install npm_modules (native)
|
||||
run: |
|
||||
npm i -g yarn node-gyp
|
||||
yarn --network-timeout 1000000 --arch=${{matrix.build-arch}} --target-arch=${{matrix.build-arch}}
|
||||
@ -216,6 +232,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }}
|
||||
USE_HARD_LINKS: false
|
||||
USE_SYSTEM_FPM: true
|
||||
# DEBUG: electron-builder,electron-builder:*
|
||||
|
||||
- name: Build web resources (amd64 only)
|
||||
@ -234,14 +251,14 @@ jobs:
|
||||
|
||||
- name: Upload packages to packagecloud.io
|
||||
uses: TykTechnologies/packagecloud-action@main
|
||||
if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
env:
|
||||
PACKAGECLOUD_TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }}
|
||||
with:
|
||||
repo: 'eugeny/tabby'
|
||||
dir: 'dist'
|
||||
rpmvers: 'el/9 el/8 ol/6 ol/7'
|
||||
debvers: 'ubuntu/bionic ubuntu/focal ubuntu/hirsute ubuntu/impish ubuntu/jammy ubuntu/kinetic ubuntu/noble debian/jessie debian/stretch debian/buster'
|
||||
debvers: 'ubuntu/bionic ubuntu/focal ubuntu/hirsute ubuntu/impish ubuntu/jammy ubuntu/kinetic ubuntu/noble ubuntu/oracular debian/jessie debian/stretch debian/buster debian/bullseye debian/bookworm debian/trixie debian/testing debian/unstable'
|
||||
|
||||
- uses: actions/upload-artifact@master
|
||||
name: Upload AppImage (${{matrix.arch}})
|
||||
@ -280,27 +297,44 @@ jobs:
|
||||
path: tabby-web.tar.gz
|
||||
if: matrix.build-arch == 'x64'
|
||||
|
||||
|
||||
Windows-Build:
|
||||
runs-on: windows-2022
|
||||
runs-on: windows-latest
|
||||
needs: Lint
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x64
|
||||
rust_triple: x86_64-pc-windows-msvc
|
||||
- arch: arm64
|
||||
rust_triple: aarch64-pc-windows-msvc
|
||||
fail-fast: false
|
||||
|
||||
env:
|
||||
RUST_TARGET_TRIPLE: ${{matrix.rust_triple}}
|
||||
ARCH: ${{matrix.arch}}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
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
|
||||
uses: actions/setup-node@v3.7.0
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 22
|
||||
|
||||
- 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
|
||||
shell: powershell
|
||||
@ -312,20 +346,48 @@ jobs:
|
||||
env:
|
||||
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
|
||||
run: node scripts/build-windows.mjs
|
||||
if: github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags'))
|
||||
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags'))
|
||||
shell: powershell
|
||||
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:
|
||||
ARCH: ${{matrix.arch}}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }}
|
||||
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
|
||||
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
|
||||
DEBUG: electron-builder,electron-builder:*
|
||||
SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }}
|
||||
SM_PUBLISHER_NAME: ${{ secrets.SM_PUBLISHER_NAME }}
|
||||
SM_API_KEY: ${{ secrets.SM_API_KEY }}
|
||||
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
|
||||
run: node scripts/build-windows.mjs
|
||||
if: "! (github.repository == 'Eugeny/tabby' && github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')))"
|
||||
if: "! (github.event_name == 'push' && (startsWith(github.ref, 'refs/tags')))"
|
||||
env:
|
||||
ARCH: ${{matrix.arch}}
|
||||
|
||||
|
1
.github/workflows/docs.yml
vendored
1
.github/workflows/docs.yml
vendored
@ -18,6 +18,7 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
sudo apt update && sudo apt install libfontconfig1-dev
|
||||
yarn cache clean
|
||||
cd app
|
||||
yarn
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -33,7 +33,6 @@ docs/api
|
||||
sentry.properties
|
||||
sentry-symbols.js
|
||||
|
||||
tabby-ssh/util/pagent.exe
|
||||
*.psd
|
||||
|
||||
crowdin.yml
|
||||
|
1
.well-known/funding-manifest-urls
Normal file
1
.well-known/funding-manifest-urls
Normal file
@ -0,0 +1 @@
|
||||
https://null.page/funding.json
|
@ -66,7 +66,7 @@ Diese README ist auch verfügbar in: <a href="./README.md">:gb: English</a> ·
|
||||
|
||||

|
||||
|
||||
* Ein V220-Terminal + verschiedene Erweiterungen
|
||||
* Ein VT220-Terminal + verschiedene Erweiterungen
|
||||
* Mehrere verschachtelte, geteilte Fenster
|
||||
* Tabs auf jeder Seite des Fensters
|
||||
* Optional andockbares Fenster mit einem globalen Spawn-Hotkey ("Quake-Konsole")
|
||||
@ -119,7 +119,9 @@ Plugins und Themen können direkt aus der Ansicht "Einstellungen" in Tabby insta
|
||||
* [clippy](https://github.com/Eugeny/tabby-clippy) - ein Beispiel-Plugin, das einen die ganze Zeit nervt
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - ermöglicht das Erstellen eigener Workspace-Profile auf Basis der angegebenen Konfiguration
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - öffnet den Standard-Systembrowser mit einem Text, der aus dem Tabby Tab ausgewählt wurde
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - Öffnet ein SFTP-Tab für SSH-Verbindungen ähnlich wie SecureCRT
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - In-App-Web-Authentifizierungspopups (Hauptsächlich für die in-Browser-Authentifizierung von Warpgate entwickelt)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - Leistungsstarke Model Context Protocol Server-Integration für Tabby, die sich nahtlos mit KI-Assistenten über MCP-Clients wie Cursor und Windsurf verbindet und Ihren Terminal-Workflow mit intelligenten KI-Funktionen verbessert.
|
||||
|
||||
<a name="themes"></a>
|
||||
|
||||
@ -342,6 +344,11 @@ 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/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://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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -67,7 +67,7 @@ Este fichero README está disponible en: <a href="./README.md">:gb: English</a>
|
||||
|
||||

|
||||
|
||||
* Un terminal V220 + varias extensiones
|
||||
* Un terminal VT220 + varias extensiones
|
||||
* Múltiples paneles divididos anidados
|
||||
* Pestañas en cualquier lado de la ventana
|
||||
* Ventana acoplable opcional con una tecla de acceso directo global ("consola de Quake")
|
||||
@ -120,7 +120,9 @@ Los plugins y los temas se pueden instalar directamente desde la vista de Config
|
||||
* [clippy](https://github.com/Eugeny/tabby-clippy) - un ejemplo de plugin que te molesta todo el tiempo
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - permite crear perfiles de espacio de trabajo personalizados basados en la configuración dada
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - abre el navegador del sistema por defecto con un texto seleccionado en la pestaña de Tabby's
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - abre una pestaña sftp para la conexión ssh como SecureCRT
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - Ventanas emergentes de autenticación web dentro de la app (Construidas principalmente para la autenticación en el navegador de warpgate)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - Potente integración del servidor de Protocolo de Contexto de Modelo para Tabby que se conecta sin problemas con asistentes de IA a través de clientes MCP como Cursor y Windsurf, mejorando tu flujo de trabajo en terminal con capacidades inteligentes de IA.
|
||||
<a name="themes"></a>
|
||||
|
||||
# Temas
|
||||
@ -344,6 +346,11 @@ 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/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://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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -67,7 +67,7 @@ This README is also available in: <a href="./README.md">:gb: English</a> · <a
|
||||
|
||||

|
||||
|
||||
* Terminal V220 + berbagai macam ekstensi
|
||||
* Terminal VT220 + berbagai macam ekstensi
|
||||
* Beberapa pembagian panel
|
||||
* Tab di sisi mana pun dari jendela
|
||||
* Jendela dockable opsional dengan hotkey spawn global ("Quake console")
|
||||
@ -120,8 +120,9 @@ Tema dan Plugin bisa langsung di install dari Pengaturan di dalam Tabby.
|
||||
* [clippy](https://github.com/Eugeny/tabby-clippy) - suatu contoh plugin yang akan mengganggu anda setiap saat
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - memperbolehkan membuat kustom profil workspace dari konfigurasi yang diberikan
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - membuka browser default dengan text yang dipilih dari Tab Tabby
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
|
||||
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - membuka tab sftp untuk koneksi ssh seperti SecureCRT
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - Pop-up otentikasi web di dalam aplikasi (Dibangun terutama untuk otentikasi in-browser warpgate)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - Integrasi server Model Context Protocol yang kuat untuk Tabby yang terhubung secara mulus dengan asisten AI melalui klien MCP seperti Cursor dan Windsurf, meningkatkan alur kerja terminal Anda dengan kemampuan AI yang cerdas.
|
||||
<a name="themes"></a>
|
||||
|
||||
# Tema
|
||||
@ -341,6 +342,11 @@ 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/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://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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -68,7 +68,7 @@ Questo README è disponibile anche in: <a href="./README.md">:gb: English</a>
|
||||
|
||||

|
||||
|
||||
* Un terminale V220 + vari estensioni
|
||||
* Un terminale VT220 + vari estensioni
|
||||
* Suddivisione in pannelli
|
||||
* Schede su qualsiasi lato della finestra
|
||||
* Finestra agganciabile opzionale con un tasto di scelta rapida ("Quake console")
|
||||
@ -107,18 +107,19 @@ Tabby può essere eseguito come app portatile su Windows, se crei una cartella `
|
||||
<a name="plugins"></a>
|
||||
# Plugin
|
||||
|
||||
Plugins and themes can be installed directly from the Settings view inside Tabby.
|
||||
|
||||
* [docker](https://github.com/Eugeny/tabby-docker) - connect to Docker containers
|
||||
* [title-control](https://github.com/kbjr/terminus-title-control) - allows modifying the title of the terminal tabs by providing a prefix, suffix, and/or strings to be removed
|
||||
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - quickly send commands to one or all terminal tabs
|
||||
* [save-output](https://github.com/Eugeny/tabby-save-output) - record terminal output into a file
|
||||
* [sync-config](https://github.com/starxg/terminus-sync-config) - sync the config to Gist or Gitee
|
||||
* [clippy](https://github.com/Eugeny/tabby-clippy) - an example plugin which annoys you all the time
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - allows creating custom workspace profiles based on the given config
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - opens default system browser with a text selected from the Tabby's tab
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
|
||||
I plugin e i temi possono essere installati direttamente dalla vista Impostazioni all'interno di Tabby.
|
||||
|
||||
* [docker](https://github.com/Eugeny/tabby-docker) - connessione ai container Docker
|
||||
* [title-control](https://github.com/kbjr/terminus-title-control) - consente di modificare il titolo delle schede del terminale fornendo un prefisso, un suffisso e/o stringhe da rimuovere
|
||||
* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - invia rapidamente comandi a una o tutte le schede del terminale
|
||||
* [save-output](https://github.com/Eugeny/tabby-save-output) - registra l'output del terminale in un file
|
||||
* [sync-config](https://github.com/starxg/terminus-sync-config) - sincronizza la configurazione con Gist o Gitee
|
||||
* [clippy](https://github.com/Eugeny/tabby-clippy) - un esempio di plugin che ti infastidisce tutto il tempo
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - consente di creare profili di spazio di lavoro personalizzati basati sulla configurazione fornita
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - apre il browser di sistema predefinito con un testo selezionato dalla scheda di Tabby
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - apre una scheda SFTP per la connessione SSH come SecureCRT
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - Popup di autenticazione web in-app (costruito principalmente per l'autenticazione in-browser di Warpgate)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - Potente integrazione del server Model Context Protocol per Tabby che si connette perfettamente con assistenti AI attraverso client MCP come Cursor e Windsurf, migliorando il tuo flusso di lavoro del terminale con capacità AI intelligenti.
|
||||
<a name="themes"></a>
|
||||
# Temi
|
||||
|
||||
@ -337,6 +338,11 @@ 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/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://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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -74,7 +74,7 @@
|
||||
|
||||

|
||||
|
||||
* V220ターミナル+各種拡張機能
|
||||
* VT220ターミナル+各種拡張機能
|
||||
* 複数ネストされたペイン分割に対応
|
||||
* ウィンドウ内に自由に配置可能なタブ
|
||||
* グローバルホットキーで呼び出せるドックウィンドウ機能("Quakeコンソール")
|
||||
@ -127,8 +127,9 @@ Windows上では、`Tabby.exe`がある場所と同じ場所に`data`フォル
|
||||
* [clippy](https://github.com/Eugeny/tabby-clippy) - プラグインの作例として、いつも厄介なあいつが出てくるプラグイン
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - 指定された設定からカスタマイズされたワークスペースを作成することができます
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - Tabby内の端末で選択したテキストを既定ブラウザで開くことができます。
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
|
||||
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - SecureCRTのようにSSH接続のSFTPタブを開く
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - アプリ内ウェブ認証ポップアップ(主にwarpgateのブラウザ認証用に構築)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - TabbyのためのパワフルなModel Context Protocolサーバー統合機能で、CursorやWindsurfなどのMCPクライアントを通じてAIアシスタントとシームレスに接続し、インテリジェントなAI機能によってターミナルワークフローを強化します。
|
||||
<a name="themes"></a>
|
||||
|
||||
# テーマ
|
||||
@ -352,6 +353,11 @@ 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/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://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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -66,7 +66,7 @@ This README is also available in: <a href="./README.md">:gb: English</a> · <a
|
||||
|
||||

|
||||
|
||||
* A V220 터미널 + 다양한 확장
|
||||
* A VT220 터미널 + 다양한 확장
|
||||
* 여러 개의 분할 창 중첩
|
||||
* 모든 측면에 탭이 위치함
|
||||
* 전역 스폰 단축키가 있는 도킹 가능한 윈도우 ("Quake console")
|
||||
@ -116,8 +116,9 @@ This README is also available in: <a href="./README.md">:gb: English</a> · <a
|
||||
* [clippy](https://github.com/Eugeny/tabby-clippy) - 항상 당신을 귀찮게 하는 예제 플러그인
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - 주어진 구성을 기반으로 사용자 정의 작업 공간 프로필을 생성할 수 있습니다
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - Tabby의 탭에서 선택한 텍스트로 기본 시스템 브라우저를 엽니다
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
|
||||
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - SecureCRT와 유사하게 SSH 연결에 대한 SFTP 탭을 엽니다.
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - 앱 내 웹 인증 팝업 (주로 warpgate 브라우저 인증을 위해 구축)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - Cursor 및 Windsurf와 같은 MCP 클라이언트를 통해 AI 어시스턴트와 원활하게 연결되는 Tabby용 강력한 모델 컨텍스트 프로토콜 서버 통합으로, 지능형 AI 기능으로 터미널 워크플로우를 향상시킵니다.
|
||||
<a name="themes"></a>
|
||||
# 테마
|
||||
|
||||
@ -336,6 +337,11 @@ 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/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://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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
14
README.md
14
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
<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> <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> <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>   <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://twitter.com/eugeeeeny"><img alt="Twitter" src="https://shields.io/badge/Subscribe-News-blue?logo=twitter&style=for-the-badge&color=blue"></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> <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> <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>   <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>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@ -72,7 +72,7 @@ This README is also available in: <a href="./README.es-ES.md">:es: Spanish</a>
|
||||
|
||||

|
||||
|
||||
* A V220 terminal + various extensions
|
||||
* A VT220 terminal + various extensions
|
||||
* Multiple nested split panes
|
||||
* Tabs on any side of the window
|
||||
* Optional dockable window with a global spawn hotkey ("Quake console")
|
||||
@ -129,6 +129,7 @@ Plugins and themes can be installed directly from the Settings view inside Tabby
|
||||
* [background](https://github.com/moemoechu/tabby-background) - change Tabby background image and more...
|
||||
* [highlight](https://github.com/moemoechu/tabby-highlight) - Tabby terminal keyword highlight plugin
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - In-app web authentication popups (Built primarily for warpgate in-browser auth)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - Powerful Model Context Protocol server integration for Tabby that seamlessly connects with AI assistants through MCP clients like Cursor and Windsurf, enhancing your terminal workflow with intelligent AI capabilities.
|
||||
|
||||
<a name="themes"></a>
|
||||
|
||||
@ -144,7 +145,7 @@ Plugins and themes can be installed directly from the Settings view inside Tabby
|
||||
|
||||
# Sponsors <!-- omit in toc -->
|
||||
|
||||
[](https://packagecloud.io)
|
||||
<a href="https://packagecloud.io"><img src="https://assets-production.packagecloud.io/assets/logo_v1-d5895e7b89b2dee19030e85515fd0f91d8f3b37c82d218a6531fc89c2b1b613c.png" width="200"></a>
|
||||
|
||||
[**packagecloud**](https://packagecloud.io) has provided free Debian/RPM repository hosting
|
||||
|
||||
@ -152,7 +153,7 @@ Plugins and themes can be installed directly from the Settings view inside Tabby
|
||||
|
||||
[**keygen**](https://keygen.sh/?via=eugene) has provided free release & auto-update hosting
|
||||
|
||||
<a href="https://iqhive.com/"><img src="https://private-user-images.githubusercontent.com/161476/361584584-ed292436-1d50-46bc-b479-78222c83ed22.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjQ3MDg3NjgsIm5iZiI6MTcyNDcwODQ2OCwicGF0aCI6Ii8xNjE0NzYvMzYxNTg0NTg0LWVkMjkyNDM2LTFkNTAtNDZiYy1iNDc5LTc4MjIyYzgzZWQyMi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwODI2JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDgyNlQyMTQxMDhaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1iYzNlZjIxN2JiYzBkYTU5NGE4YmZlZDJiNmIxZWE1ZTAyOTNhYjJlZTRhOGZjYTk4N2E4MzMzZjg0ZTNkZWQ0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.pQzR2d71YV4TIxOH3Lg20HpNKrm_r2D-xfkEM_F2DTs" width="100"></a>
|
||||
<a href="https://iqhive.com/"><img src="https://iqhive.com/img/icons/logo.svg" width="200"></a>
|
||||
|
||||
[**IQ Hive**](https://iqhive.com) is providing financial support for the project development
|
||||
|
||||
@ -360,6 +361,11 @@ 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/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://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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -75,7 +75,7 @@ Ten plik README jest również dostępny w językach: <a href="./README.md">:gb
|
||||
|
||||

|
||||
|
||||
* Konsola V220 + wiele rozszerzeń
|
||||
* Konsola VT220 + wiele rozszerzeń
|
||||
* Wiele nakładających się podzielonych okien
|
||||
* Okna na każdej stronie ekranu
|
||||
* Opcjonalne dokowanie okna za pomocą skrótu ("Quake console")
|
||||
@ -132,6 +132,7 @@ Wtyczki (jak i motywy) mogą być instalowane bezpośrednio z widoku ustawień w
|
||||
* [background](https://github.com/moemoechu/tabby-background) - zmień tło Tabby oraz wiele więcej
|
||||
* [highlight](https://github.com/moemoechu/tabby-highlight) - Tabby terminal keyword highlight plugin
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - okienka autoryzacji wewnątrz konsoli (Zbudowany głównie pod autoryzację projektu Warpgate w przeglądarce)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - Potężna integracja serwera Model Context Protocol dla Tabby, która bezproblemowo łączy się z asystentami AI poprzez klientów MCP, takich jak Cursor i Windsurf, usprawniając przepływ pracy w terminalu dzięki inteligentnym możliwościom AI.
|
||||
|
||||
<a name="themes"></a>
|
||||
|
||||
|
@ -67,7 +67,7 @@ Esse README também está disponível em: <a href="./README.md">:gb: English</a
|
||||
|
||||

|
||||
|
||||
* Um terminal V220 + várias extensões
|
||||
* Um terminal VT220 + várias extensões
|
||||
* Múltiplos painéis divididos aninhados
|
||||
* Guias em qualquer lado da janela
|
||||
* Opção de minimizar para a barra de tarefas com uma tecla de atalho global ("Quake console")
|
||||
@ -120,8 +120,9 @@ Plugins e temas podem ser instalados durante a execução na pagina de configura
|
||||
* [clippy](https://github.com/Eugeny/tabby-clippy) - um plugin de exemplo que te incomoda o tempo todo
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - permite criar perfis de espaço de trabalho personalizados com base na configuração fornecida
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - abre o navegador padrão do sistema com um texto selecionado na guia do Tabby
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - open sftp tab for ssh connection like SecureCRT
|
||||
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - abre uma guia SFTP para conexão SSH como o SecureCRT
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - pop-ups de autenticação web dentro do aplicativo (Construído principalmente para autenticação in-browser do warpgate)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - Integração poderosa do servidor Model Context Protocol para Tabby que se conecta perfeitamente com assistentes de IA através de clientes MCP como Cursor e Windsurf, aprimorando seu fluxo de trabalho no terminal com recursos inteligentes de IA.
|
||||
<a name="themes"></a>
|
||||
|
||||
# Temas
|
||||
@ -345,6 +346,11 @@ 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/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://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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -31,7 +31,7 @@
|
||||
* Встроенный SSH- и Telnet-клиент и менеджер подключений;
|
||||
* Встроенный последовательный терминал;
|
||||
* Темы и цветовые схемы;
|
||||
* Полностью настраеваемые сочетания клавиш;
|
||||
* Полностью настраиваемые сочетания клавиш;
|
||||
* Панели;
|
||||
* Запоминание вкладок;
|
||||
* Поддержка PowerShell (and PS Core), WSL, Git-Bash, Cygwin, MSYS2, Cmder и CMD;
|
||||
@ -67,7 +67,7 @@
|
||||
|
||||

|
||||
|
||||
* Терминал V220 + различные дополнения;
|
||||
* Терминал VT220 + различные дополнения;
|
||||
* Деление окна на несколько панелей;
|
||||
* Вкладки на любой стороне окна;
|
||||
* Опционально закрепляемое окно с глобальной горячей клавишей для вызова («Quake console»);
|
||||
@ -118,7 +118,8 @@
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) — позволяет создавать пользовательские профили рабочего окружения на основе конфига;
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) — открывает браузер по умолчанию с текстом, выделенном во вкладке Tabby.
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - открывает sftp вкладку для ssh соединения, похож на SecureCRT
|
||||
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - Встроенные всплывающие окна веб-аутентификации (Основано в основном для аутентификации в браузере warpgate)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - Мощная интеграция сервера протокола Model Context Protocol для Tabby, которая беспрепятственно соединяется с ИИ-ассистентами через MCP-клиенты, такие как Cursor и Windsurf, улучшая рабочий процесс в терминале с помощью интеллектуальных возможностей искусственного интеллекта.
|
||||
<a name="themes"></a>
|
||||
# Темы
|
||||
|
||||
@ -337,6 +338,11 @@ 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/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://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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -66,7 +66,7 @@
|
||||
|
||||

|
||||
|
||||
* 一个 V220 终端 + 各种插件
|
||||
* 一个 VT220 终端 + 各种插件
|
||||
* 多个嵌套的拆分窗格
|
||||
* 可以将标签页设置在窗口的任意一侧
|
||||
* 带有全局生成热键的可选可停靠窗口(“Quake console”)
|
||||
@ -117,7 +117,8 @@
|
||||
* [workspace-manager](https://github.com/composer404/tabby-workspace-manager) - 允许根据给定的配置创建自定义工作区配置文件
|
||||
* [search-in-browser](https://github.com/composer404/tabby-search-in-browser) - 从 Tabby 标签页带有选中的文本来打开系统默认浏览器
|
||||
* [sftp-tab](https://github.com/wljince007/tabby-sftp-tab) - 为ssh连接打开类似SecureCRT的sftp标签页
|
||||
|
||||
* [web-auth-handler](https://github.com/Jazzmoon/tabby-web-auth-handler) - 应用内网页认证弹出窗口(主要为warpgate浏览器认证而建)
|
||||
* [mcp-server](https://github.com/thuanpham582002/tabby-mcp-server) - 为 Tabby 提供强大的模型上下文协议服务器集成,可通过 Cursor 和 Windsurf 等 MCP 客户端无缝连接 AI 助手,利用智能 AI 功能增强您的终端工作流程。
|
||||
<a name="themes"></a>
|
||||
# 主题
|
||||
|
||||
@ -336,6 +337,11 @@
|
||||
<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/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="http://hiroga.hatenablog.com/"><img src="https://avatars.githubusercontent.com/u/13391129?v=4?s=100" width="100px;" alt="Hiroaki Ogasawara"/><br /><sub><b>Hiroaki Ogasawara</b></sub></a><br /><a href="https://github.com/Eugeny/tabby/commits?author=xhiroga" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as glasstron from 'glasstron'
|
||||
import { autoUpdater } from 'electron-updater'
|
||||
import { Subject, Observable, debounceTime } from 'rxjs'
|
||||
import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen, BrowserWindowConstructorOptions, TouchBar, nativeImage, WebContents } from 'electron'
|
||||
import { BrowserWindow, app, ipcMain, Rectangle, Menu, screen, BrowserWindowConstructorOptions, TouchBar, nativeImage, WebContents, nativeTheme } from 'electron'
|
||||
import ElectronConfig = require('electron-config')
|
||||
import { enable as enableRemote } from '@electron/remote/main'
|
||||
import * as os from 'os'
|
||||
@ -100,6 +100,10 @@ export class Window {
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
bwOptions.visualEffectState = 'active'
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
this.window = new BrowserWindow(bwOptions) as GlasstronWindow
|
||||
} else {
|
||||
@ -108,13 +112,15 @@ export class Window {
|
||||
|
||||
this.webContents = this.window.webContents
|
||||
|
||||
this.window.once('ready-to-show', () => {
|
||||
this.window.webContents.once('did-finish-load', () => {
|
||||
if (process.platform === 'darwin') {
|
||||
this.window.setVibrancy(macOSVibrancyType)
|
||||
} else if (process.platform === 'win32' && this.configStore.appearance?.vibrancy) {
|
||||
this.setVibrancy(true)
|
||||
}
|
||||
|
||||
this.setDarkMode(this.configStore.appearance?.colorSchemeMode ?? 'dark')
|
||||
|
||||
if (!options.hidden) {
|
||||
if (maximized) {
|
||||
this.window.maximize()
|
||||
@ -139,7 +145,7 @@ export class Window {
|
||||
|
||||
enableRemote(this.window.webContents)
|
||||
|
||||
this.window.loadURL(`file://${app.getAppPath()}/dist/index.html`, { extraHeaders: 'pragma: no-cache\n' })
|
||||
this.window.loadFile(path.join(app.getAppPath(), 'dist', 'index.html'))
|
||||
|
||||
this.window.webContents.setVisualZoomLevelLimits(1, 1)
|
||||
this.window.webContents.setZoomFactor(1)
|
||||
@ -201,6 +207,18 @@ export class Window {
|
||||
}
|
||||
}
|
||||
|
||||
setDarkMode (mode: string): void {
|
||||
if (process.platform === 'darwin') {
|
||||
if ('light' === mode ) {
|
||||
nativeTheme.themeSource = 'light'
|
||||
} else if ('auto' === mode) {
|
||||
nativeTheme.themeSource = 'system'
|
||||
} else {
|
||||
nativeTheme.themeSource = 'dark'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
focus (): void {
|
||||
this.window.focus()
|
||||
}
|
||||
@ -373,6 +391,10 @@ export class Window {
|
||||
this.setVibrancy(enabled, type)
|
||||
})
|
||||
|
||||
this.on('window-set-dark-mode', (_, mode) => {
|
||||
this.setDarkMode(mode)
|
||||
})
|
||||
|
||||
this.on('window-set-window-controls-color', (_, theme) => {
|
||||
if (process.platform === 'win32') {
|
||||
const symbolColor: string = theme.foreground
|
||||
|
@ -16,7 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2",
|
||||
"node-pty": "^1.0",
|
||||
"node-pty": "^1.0.0",
|
||||
"any-promise": "^1.3.0",
|
||||
"electron-config": "2.0.0",
|
||||
"electron-debug": "^3.2.0",
|
||||
@ -30,18 +30,19 @@
|
||||
"native-process-working-directory": "^1.0.2",
|
||||
"npm": "6",
|
||||
"rxjs": "^7.5.7",
|
||||
"russh": "0.1.24",
|
||||
"source-map-support": "^0.5.20",
|
||||
"v8-compile-cache": "^2.3.0",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tabby-gang/windows-blurbehind": "^3.0.0",
|
||||
"@tabby-gang/windows-blurbehind": "^3.1.0",
|
||||
"macos-native-processlist": "^2.1.0",
|
||||
"patch-package": "^6.5.0",
|
||||
"serialport": "11.0.1",
|
||||
"serialport-binding-webserialapi": "^1.0.3",
|
||||
"windows-native-registry": "^3.2.1",
|
||||
"windows-process-tree": "^0.4.0"
|
||||
"@tabby-gang/windows-process-tree": "^0.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
@ -64,6 +65,8 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"*/node-abi": "^3",
|
||||
"node-gyp": "^10.0.0"
|
||||
"node-gyp": "^10.0.0",
|
||||
"nan": "2.22.0",
|
||||
"node-addon-api": "^8.3.0"
|
||||
}
|
||||
}
|
||||
|
53
app/patches/node-pty+1.0.0.patch
Normal file
53
app/patches/node-pty+1.0.0.patch
Normal file
@ -0,0 +1,53 @@
|
||||
diff --git a/node_modules/node-pty/binding.gyp b/node_modules/node-pty/binding.gyp
|
||||
index 79a93e7..efb0a3f 100644
|
||||
--- a/node_modules/node-pty/binding.gyp
|
||||
+++ b/node_modules/node-pty/binding.gyp
|
||||
@@ -18,6 +18,9 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
+ 'defines': [
|
||||
+ 'NOMINMAX'
|
||||
+ ]
|
||||
}],
|
||||
],
|
||||
},
|
||||
diff --git a/node_modules/node-pty/src/win/winpty.cc b/node_modules/node-pty/src/win/winpty.cc
|
||||
index b054dee..a094b1c 100644
|
||||
--- a/node_modules/node-pty/src/win/winpty.cc
|
||||
+++ b/node_modules/node-pty/src/win/winpty.cc
|
||||
@@ -164,7 +164,7 @@ static NAN_METHOD(PtyStartProcess) {
|
||||
Nan::ThrowError(why.str().c_str());
|
||||
goto cleanup;
|
||||
}
|
||||
-
|
||||
+ {
|
||||
int cols = info[4]->Int32Value(Nan::GetCurrentContext()).FromJust();
|
||||
int rows = info[5]->Int32Value(Nan::GetCurrentContext()).FromJust();
|
||||
bool debug = Nan::To<bool>(info[6]).FromJust();
|
||||
@@ -179,6 +179,7 @@ static NAN_METHOD(PtyStartProcess) {
|
||||
throw_winpty_error("Error creating WinPTY config", error_ptr);
|
||||
goto cleanup;
|
||||
}
|
||||
+ {
|
||||
winpty_error_free(error_ptr);
|
||||
|
||||
// Set pty size on config
|
||||
@@ -215,7 +216,7 @@ static NAN_METHOD(PtyStartProcess) {
|
||||
winpty_error_free(error_ptr);
|
||||
|
||||
// Set return values
|
||||
- v8::Local<v8::Object> marshal = Nan::New<v8::Object>();
|
||||
+ {v8::Local<v8::Object> marshal = Nan::New<v8::Object>();
|
||||
Nan::Set(marshal, Nan::New<v8::String>("innerPid").ToLocalChecked(), Nan::New<v8::Number>((int)GetProcessId(handle)));
|
||||
Nan::Set(marshal, Nan::New<v8::String>("innerPidHandle").ToLocalChecked(), Nan::New<v8::Number>((int)handle));
|
||||
Nan::Set(marshal, Nan::New<v8::String>("pid").ToLocalChecked(), Nan::New<v8::Number>((int)winpty_agent_process(pc)));
|
||||
@@ -232,7 +233,7 @@ static NAN_METHOD(PtyStartProcess) {
|
||||
Nan::Set(marshal, Nan::New<v8::String>("conout").ToLocalChecked(), Nan::New<v8::String>(conoutPipeNameStr).ToLocalChecked());
|
||||
}
|
||||
info.GetReturnValue().Set(marshal);
|
||||
-
|
||||
+ }}}
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
135
app/yarn.lock
135
app/yarn.lock
@ -28,6 +28,11 @@
|
||||
wrap-ansi "^8.1.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":
|
||||
version "14.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-14.0.0.tgz#af421d0e1a28376843f0fed375cd2fae7630a5ff"
|
||||
@ -173,13 +178,20 @@
|
||||
dependencies:
|
||||
debug "^4.3.2"
|
||||
|
||||
"@tabby-gang/windows-blurbehind@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tabby-gang/windows-blurbehind/-/windows-blurbehind-3.0.0.tgz#48d409c2eb14a12c867b70de5ee4d6769ef45e8f"
|
||||
integrity sha512-ah6eJcoQZWOZfu9sd2pWlOJmfl1v+2EZQMeIp7MWvg+/16WS16UFNdnOtlV6AUiABHfZo2QKfCNUEuorCM+Q2A==
|
||||
"@tabby-gang/windows-blurbehind@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@tabby-gang/windows-blurbehind/-/windows-blurbehind-3.1.0.tgz#bd1462f2a970d195e277d48cceb8b2c0a20f09bd"
|
||||
integrity sha512-wTvyNrBDNxD4yq1bXv7lvXRQujJYRQ2Ke3LJyE9yzY9e/L9/fHVIuprIgAMiLsnW+BKPERq0k27iC38WnxUBZQ==
|
||||
dependencies:
|
||||
"@types/node" "^10.12.18"
|
||||
|
||||
"@tabby-gang/windows-process-tree@^0.6.1":
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@tabby-gang/windows-process-tree/-/windows-process-tree-0.6.1.tgz#2a5a5cbbee16f611fc61d53cbadf930c8d3d20a8"
|
||||
integrity sha512-I7AwncTXTmo1+WPfV+O+jYRJzjCMJznIjC/ycl4dP/n2HAocuXCIjTZfoMmL+rgjN2nRFpTyn6P+EhuIPMACbQ==
|
||||
dependencies:
|
||||
node-addon-api "7.1.0"
|
||||
|
||||
"@types/mz@2.7.4":
|
||||
version "2.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/mz/-/mz-2.7.4.tgz#f9d1535cb5171199b28ae6abd6ec29e856551401"
|
||||
@ -1490,25 +1502,26 @@ glasstron@0.1.1:
|
||||
x11 "^2.3.0"
|
||||
|
||||
glob@^10.2.2, glob@^10.3.10:
|
||||
version "10.3.10"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b"
|
||||
integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==
|
||||
version "10.4.5"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
|
||||
integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
|
||||
dependencies:
|
||||
foreground-child "^3.1.0"
|
||||
jackspeak "^2.3.5"
|
||||
minimatch "^9.0.1"
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
path-scurry "^1.10.1"
|
||||
jackspeak "^3.1.2"
|
||||
minimatch "^9.0.4"
|
||||
minipass "^7.1.2"
|
||||
package-json-from-dist "^1.0.0"
|
||||
path-scurry "^1.11.1"
|
||||
|
||||
glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
minimatch "^3.1.1"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
@ -1931,10 +1944,10 @@ isstream@~0.1.2:
|
||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz"
|
||||
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
|
||||
|
||||
jackspeak@^2.3.5:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8"
|
||||
integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==
|
||||
jackspeak@^3.1.2:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
|
||||
integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==
|
||||
dependencies:
|
||||
"@isaacs/cliui" "^8.0.2"
|
||||
optionalDependencies:
|
||||
@ -2286,13 +2299,18 @@ lowercase-keys@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||
integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
|
||||
|
||||
lru-cache@^10.0.1, "lru-cache@^9.1.1 || ^10.0.0":
|
||||
lru-cache@^10.0.1:
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.2.tgz#34504678cc3266b09b8dfd6fab4e1515258271b7"
|
||||
integrity sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==
|
||||
dependencies:
|
||||
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:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
|
||||
@ -2412,10 +2430,17 @@ minimatch@^3.0.4:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^9.0.1:
|
||||
version "9.0.3"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
|
||||
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
|
||||
minimatch@^3.1.1:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
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:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
@ -2488,6 +2513,11 @@ minipass@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
|
||||
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:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
|
||||
@ -2577,10 +2607,10 @@ mz@^2.7.0:
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
nan@^2.17.0:
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3"
|
||||
integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==
|
||||
nan@2.22.0, nan@^2.17.0:
|
||||
version "2.22.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3"
|
||||
integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==
|
||||
|
||||
napi-build-utils@^1.0.1:
|
||||
version "1.0.2"
|
||||
@ -2625,20 +2655,10 @@ node-abi@^3.3.0:
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
node-addon-api@3.1.0, node-addon-api@^3.0.2, node-addon-api@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz"
|
||||
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-addon-api@3.1.0, node-addon-api@6.1.0, node-addon-api@7.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@^8.3.0:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-8.3.0.tgz#ec3763f18befc1cdf66d11e157ce44d5eddc0603"
|
||||
integrity sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==
|
||||
|
||||
node-fetch-npm@^2.0.2:
|
||||
version "2.0.4"
|
||||
@ -2670,7 +2690,7 @@ node-gyp@^10.0.0, node-gyp@^5.0.2, node-gyp@^5.1.0:
|
||||
tar "^6.1.2"
|
||||
which "^4.0.0"
|
||||
|
||||
node-pty@^1.0:
|
||||
node-pty@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.0.0.tgz#7daafc0aca1c4ca3de15c61330373af4af5861fd"
|
||||
integrity sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==
|
||||
@ -3097,6 +3117,11 @@ p-try@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz"
|
||||
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:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed"
|
||||
@ -3209,12 +3234,12 @@ path-parse@^1.0.6:
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
path-scurry@^1.10.1:
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698"
|
||||
integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==
|
||||
path-scurry@^1.11.1:
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
|
||||
integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
|
||||
dependencies:
|
||||
lru-cache "^9.1.1 || ^10.0.0"
|
||||
lru-cache "^10.2.0"
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
|
||||
path-type@^2.0.0:
|
||||
@ -3603,6 +3628,13 @@ run-queue@^1.0.0, run-queue@^1.0.3:
|
||||
dependencies:
|
||||
aproba "^1.1.1"
|
||||
|
||||
russh@0.1.24:
|
||||
version "0.1.24"
|
||||
resolved "https://registry.yarnpkg.com/russh/-/russh-0.1.24.tgz#dce27a3bc63eb78024db60e6767bc80cbf523b9a"
|
||||
integrity sha512-lLMtXHJKL5uwRxwoFNDx71T7+qCXiL80qyGCRgQjYMV10gaW2AlI6mqcz3FVH8dXvdgK2ZE8DuSwlhCBK7schA==
|
||||
dependencies:
|
||||
"@napi-rs/cli" "^2.18.3"
|
||||
|
||||
rxjs@^7.5.2, rxjs@^7.5.7:
|
||||
version "7.5.7"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39"
|
||||
@ -4420,13 +4452,6 @@ windows-native-registry@^3.2.1:
|
||||
dependencies:
|
||||
node-addon-api "^3.1.0"
|
||||
|
||||
windows-process-tree@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/windows-process-tree/-/windows-process-tree-0.4.0.tgz#31ac49c5da557e628ce7e37a5800972173d3349a"
|
||||
integrity sha512-9LunDnc1WwuhyLeTAXMFX8wbActGJtDCBaiapQXFYk/nO4W4X9YxOKV5g/lQL3XX69QYxveDbjVVrdnTt1qqCQ==
|
||||
dependencies:
|
||||
nan "^2.17.0"
|
||||
|
||||
worker-farm@^1.6.0, worker-farm@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"
|
||||
|
@ -8,10 +8,6 @@
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<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>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
|
@ -32,11 +32,13 @@ extraResources:
|
||||
- extras
|
||||
asarUnpack:
|
||||
- 'dist/*.map'
|
||||
|
||||
- node_modules/keytar/build/Release/*.node
|
||||
- node_modules/**/*.node
|
||||
win:
|
||||
icon: "./build/windows/icon.ico"
|
||||
artifactName: tabby-${version}-portable-${env.ARCH}.${ext}
|
||||
rfc3161TimeStampServer: http://timestamp.sectigo.com
|
||||
signtoolOptions:
|
||||
rfc3161TimeStampServer: http://timestamp.sectigo.com
|
||||
nsis:
|
||||
oneClick: false
|
||||
artifactName: tabby-${version}-setup-${env.ARCH}.${ext}
|
||||
@ -70,7 +72,8 @@ linux:
|
||||
executableArgs:
|
||||
- "--no-sandbox"
|
||||
desktop:
|
||||
StartupWMClass: tabby
|
||||
entry:
|
||||
StartupWMClass: tabby
|
||||
snap:
|
||||
plugs:
|
||||
- default
|
||||
@ -99,3 +102,7 @@ rpm:
|
||||
- '_build_id_links none'
|
||||
- '--replaces'
|
||||
- 'terminus-terminal'
|
||||
electronFuses:
|
||||
runAsNode: false
|
||||
enableNodeOptionsEnvironmentVariable: false
|
||||
enableNodeCliInspectArguments: false
|
||||
|
1371
extras/clink/CHANGES
1371
extras/clink/CHANGES
File diff suppressed because it is too large
Load Diff
@ -1,63 +1,64 @@
|
||||
:: Copyright (c) 2012 Martin Ridgers
|
||||
:: License: http://opensource.org/licenses/MIT
|
||||
|
||||
@echo off
|
||||
rem -- Copyright (c) 2012 Martin Ridgers
|
||||
rem -- Portions Copyright (c) 2020-2024 Christopher Antos
|
||||
rem -- License: http://opensource.org/licenses/MIT
|
||||
|
||||
setlocal enableextensions
|
||||
set clink_profile_arg=
|
||||
set clink_quiet_arg=
|
||||
|
||||
:: Mimic cmd.exe's behaviour when starting from the start menu.
|
||||
if /i "%1"=="startmenu" (
|
||||
rem -- Mimic cmd.exe's behaviour when starting from the start menu.
|
||||
if /i "%~1"=="startmenu" (
|
||||
cd /d "%userprofile%"
|
||||
shift
|
||||
)
|
||||
|
||||
:: Check for the --profile option.
|
||||
if /i "%1"=="--profile" (
|
||||
rem -- Check for the --profile option.
|
||||
if /i "%~1"=="--profile" (
|
||||
set clink_profile_arg=--profile "%~2"
|
||||
shift
|
||||
shift
|
||||
)
|
||||
|
||||
:: Check for the --quiet option.
|
||||
if /i "%1"=="--quiet" (
|
||||
rem -- Check for the --quiet option.
|
||||
if /i "%~1"=="--quiet" (
|
||||
set clink_quiet_arg= --quiet
|
||||
shift
|
||||
)
|
||||
|
||||
:: If the .bat is run without any arguments, then start a cmd.exe instance.
|
||||
if "%1"=="" (
|
||||
rem -- If the .bat is run without any arguments, then start a cmd.exe instance.
|
||||
if _%1==_ (
|
||||
call :launch
|
||||
goto :end
|
||||
)
|
||||
|
||||
:: Test for autorun.
|
||||
if defined CLINK_NOAUTORUN if /i "%1"=="inject" if /i "%2"=="--autorun" goto :end
|
||||
rem -- Test for autorun.
|
||||
if defined CLINK_NOAUTORUN if /i "%~1"=="inject" if /i "%~2"=="--autorun" goto :end
|
||||
|
||||
:: Endlocal before inject tags the prompt.
|
||||
endlocal
|
||||
|
||||
:: Pass through to appropriate loader.
|
||||
rem -- Forward to appropriate loader, and endlocal before inject tags the prompt.
|
||||
if /i "%processor_architecture%"=="x86" (
|
||||
endlocal
|
||||
"%~dp0\clink_x86.exe" %*
|
||||
) else if /i "%processor_architecture%"=="arm64" (
|
||||
endlocal
|
||||
"%~dp0\clink_arm64.exe" %*
|
||||
) else if /i "%processor_architecture%"=="amd64" (
|
||||
if defined processor_architew6432 (
|
||||
endlocal
|
||||
"%~dp0\clink_x86.exe" %*
|
||||
) else (
|
||||
endlocal
|
||||
"%~dp0\clink_x64.exe" %*
|
||||
)
|
||||
)
|
||||
|
||||
:end
|
||||
goto :eof
|
||||
goto :end
|
||||
|
||||
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
:launch
|
||||
setlocal
|
||||
setlocal enableextensions
|
||||
set WT_PROFILE_ID=
|
||||
set WT_SESSION=
|
||||
start "Clink" cmd.exe /s /k ""%~dpnx0" inject %clink_profile_arg%%clink_quiet_arg%"
|
||||
endlocal
|
||||
exit /b 0
|
||||
|
||||
:end
|
||||
|
BIN
extras/clink/clink_arm64.exe
Normal file
BIN
extras/clink/clink_arm64.exe
Normal file
Binary file not shown.
BIN
extras/clink/clink_dll_arm64.dll
Normal file
BIN
extras/clink/clink_dll_arm64.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -4,10 +4,10 @@
|
||||
# Override the built-in Readline defaults with ones that provide a more
|
||||
# enhanced Clink experience.
|
||||
|
||||
colored-completion-prefix on
|
||||
colored-stats on
|
||||
mark-symlinked-directories on
|
||||
completion-auto-query-items on
|
||||
history-point-at-end-of-anchored-search on
|
||||
search-ignore-case on
|
||||
set colored-completion-prefix on
|
||||
set colored-stats on
|
||||
set mark-symlinked-directories on
|
||||
set completion-auto-query-items on
|
||||
set history-point-at-end-of-anchored-search on
|
||||
set search-ignore-case on
|
||||
|
||||
|
@ -4,31 +4,29 @@
|
||||
# Override built-in default settings with ones that provide a more
|
||||
# enhanced Clink experience.
|
||||
|
||||
autosuggest.enable = True
|
||||
clink.default_bindings = windows
|
||||
clink.autoupdate = off
|
||||
cmd.ctrld_exits = False
|
||||
color.arginfo = sgr 38;5;172
|
||||
color.argmatcher = sgr 1;38;5;40
|
||||
color.cmd = sgr 1;38;5;231
|
||||
color.cmd = bold
|
||||
color.cmdredir = sgr 38;5;172
|
||||
color.cmdsep = sgr 38;5;214
|
||||
color.cmdsep = sgr 38;5;135
|
||||
color.comment_row = sgr 38;5;87;48;5;18
|
||||
color.description = sgr 38;5;39
|
||||
color.doskey = sgr 1;38;5;75
|
||||
color.executable = sgr 1;38;5;33
|
||||
color.filtered = sgr 38;5;231
|
||||
color.filtered = bold
|
||||
color.flag = sgr 38;5;117
|
||||
color.hidden = sgr 38;5;160
|
||||
color.histexpand = sgr 97;48;5;55
|
||||
color.horizscroll = sgr 38;5;16;48;5;30
|
||||
color.input = sgr 38;5;222
|
||||
color.input = sgr 38;5;214
|
||||
color.readonly = sgr 38;5;28
|
||||
color.selected_completion = sgr 38;5;16;48;5;254
|
||||
color.selected_completion = sgr 7
|
||||
color.selection = sgr 38;5;16;48;5;179
|
||||
color.suggestion = sgr 38;5;239
|
||||
color.unrecognized = sgr 38;5;203
|
||||
history.max_lines = 25000
|
||||
history.time_stamp = show
|
||||
match.expand_envvars = True
|
||||
match.substring = True
|
||||
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Afrikaans\n"
|
||||
"Language: af_ZA\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Bulgarian\n"
|
||||
"Language: bg_BG\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Czech\n"
|
||||
"Language: cs_CZ\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Danish\n"
|
||||
"Language: da_DK\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
@ -211,7 +211,7 @@ msgstr "Sløring"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:28
|
||||
msgid "Bold font weight"
|
||||
msgstr ""
|
||||
msgstr "Fed skriftvægt"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:132
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:80
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de_DE\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: English, United Kingdom\n"
|
||||
"Language: en_GB\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Language: es_ES\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr_FR\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Croatian\n"
|
||||
"Language: hr_HR\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Language: id_ID\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2025-01-22 22:02\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
@ -23,7 +23,7 @@ msgstr "{name} salin"
|
||||
|
||||
#: 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"
|
||||
msgstr "Keluarga font kedua digunakan untuk menampilkan karakter yang hilang di font utama"
|
||||
msgstr "Keluarga huruf kedua digunakan untuk menampilkan karakter yang hilang di huruf utama"
|
||||
|
||||
#: tabby-core/src/components/transfersMenu.component.ts:49
|
||||
msgid "Abort all"
|
||||
@ -100,12 +100,12 @@ msgstr "Izinkan buka dengan cepat terminal di direktori terpilih"
|
||||
#: locale/tmp-html/tabby-core/src/components/welcomeTab.component.html:25
|
||||
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsTab.component.html:11
|
||||
msgid "Always dark"
|
||||
msgstr ""
|
||||
msgstr "Selalu gelap"
|
||||
|
||||
#: locale/tmp-html/tabby-core/src/components/welcomeTab.component.html:27
|
||||
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsTab.component.html:13
|
||||
msgid "Always light"
|
||||
msgstr ""
|
||||
msgstr "Selalu terang"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:2
|
||||
#: tabby-terminal/src/settings.ts:14
|
||||
@ -255,7 +255,7 @@ msgstr "Ubah baud rate"
|
||||
|
||||
#: tabby-core/src/tabContextMenu.ts:133
|
||||
msgid "Change tab color"
|
||||
msgstr ""
|
||||
msgstr "Rubah warna label"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/vaultSettingsTab.component.html:12
|
||||
msgid "Change the master passphrase"
|
||||
@ -337,7 +337,7 @@ msgstr "Skema warna"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsTab.component.html:2
|
||||
msgid "Color schemes"
|
||||
msgstr ""
|
||||
msgstr "Warna Skema"
|
||||
|
||||
#: locale/tmp-html/tabby-serial/src/components/serialProfileSettings.component.html:81
|
||||
#: locale/tmp-html/tabby-ssh/src/components/sshProfileSettings.component.html:216
|
||||
@ -362,7 +362,7 @@ msgstr "Perintah-perintah"
|
||||
|
||||
#: tabby-core/src/theme.ts:16
|
||||
msgid "Compact (legacy)"
|
||||
msgstr ""
|
||||
msgstr "Padat (tua)"
|
||||
|
||||
#: tabby-settings/src/components/configSyncSettingsTab.component.ts:126
|
||||
msgid "Config deleted"
|
||||
@ -449,7 +449,7 @@ msgstr "Salin jalur saat ini"
|
||||
|
||||
#: tabby-electron/src/sftpContextMenu.ts:29
|
||||
msgid "Copy full path"
|
||||
msgstr ""
|
||||
msgstr "Salin alamat lengkap"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:97
|
||||
msgid "Copy on select"
|
||||
@ -1732,7 +1732,7 @@ msgstr "Ganti Nama"
|
||||
#: tabby-core/src/hotkeys.ts:24
|
||||
#: tabby-core/src/tabContextMenu.ts:121
|
||||
msgid "Rename tab"
|
||||
msgstr ""
|
||||
msgstr "Ganti nama label"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:3
|
||||
msgid "Rendering"
|
||||
@ -1770,7 +1770,7 @@ msgstr "Mulai ulang sesi SSH saat ini"
|
||||
|
||||
#: tabby-telnet/src/hotkeys.ts:10
|
||||
msgid "Restart current Telnet session"
|
||||
msgstr ""
|
||||
msgstr "Mulai ulang sesi Telnet saat ini"
|
||||
|
||||
#: tabby-core/src/hotkeys.ts:64
|
||||
msgid "Restart tab"
|
||||
@ -1782,7 +1782,7 @@ msgstr "Mulai ulang aplikasi untuk menerapkan perubahan"
|
||||
|
||||
#: tabby-settings/src/components/profilesSettingsTab.component.ts:316
|
||||
msgid "Restore settings to defaults ?"
|
||||
msgstr ""
|
||||
msgstr "Kembali ke settingan sebelumnya ?"
|
||||
|
||||
#: tabby-settings/src/components/editProfileGroupModal.component.ts:36
|
||||
msgid "Restore settings to inherited defaults ?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Italian\n"
|
||||
"Language: it_IT\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2025-01-22 22:02\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
@ -23,7 +23,7 @@ msgstr "{name} copia"
|
||||
|
||||
#: 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"
|
||||
msgstr "Una famiglia di font secondaria usata per visualizzare quelle mancanti nella font principale"
|
||||
msgstr "Un set di caratteri secondario usato per mostrare quelli mancanti del font principale"
|
||||
|
||||
#: tabby-core/src/components/transfersMenu.component.ts:49
|
||||
msgid "Abort all"
|
||||
@ -192,7 +192,7 @@ msgstr "Modalità tasto backspace"
|
||||
#: tabby-serial/src/components/serialTab.component.ts:93
|
||||
#: tabby-serial/src/profiles.ts:88
|
||||
msgid "Baud rate"
|
||||
msgstr "Velocità di trasmissione"
|
||||
msgstr "Velocità trasmissione"
|
||||
|
||||
#: tabby-terminal/src/hotkeys.ts:22
|
||||
msgid "Beginning of the line"
|
||||
@ -479,7 +479,7 @@ msgstr "Crea cartella"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:90
|
||||
msgid "Current"
|
||||
msgstr "Corrente"
|
||||
msgstr "Attuale"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsForMode.component.html:3
|
||||
msgid "Current color scheme"
|
||||
@ -487,7 +487,7 @@ msgstr "Tema in uso"
|
||||
|
||||
#: locale/tmp-html/tabby-ssh/src/components/hostKeyPromptModal.component.html:17
|
||||
msgid "Current host key fingerprint"
|
||||
msgstr "Firma della chiave host attuale"
|
||||
msgstr "Firma chiave host attuale"
|
||||
|
||||
#: tabby-core/src/tabContextMenu.ts:184
|
||||
msgid "Current process: {name}"
|
||||
@ -495,7 +495,7 @@ msgstr "Processo attuale: {name}"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:51
|
||||
msgid "Cursor shape"
|
||||
msgstr "Forma del cursore"
|
||||
msgstr "Forma cursore"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsForMode.component.html:46
|
||||
msgid "Custom"
|
||||
@ -531,7 +531,7 @@ msgstr "\"Connetti a\" predefinito"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:93
|
||||
msgid "Default connection type used by quick connect feature (ex. SSH, Telnet)"
|
||||
msgstr "Tipo di connessione predefinito usato dalla funzione di connessione rapida (es. SSH, Telnet)"
|
||||
msgstr "Tipo di connessione predefinita usato dalla funzione di connessione rapida (es. SSH, Telnet)"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:8
|
||||
msgid "Default profile for new tabs"
|
||||
@ -791,7 +791,7 @@ msgstr "Cancella configurazione"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/vaultSettingsTab.component.html:12
|
||||
msgid "Erase the Vault"
|
||||
msgstr "Cancella la cassaforte"
|
||||
msgstr "Cancella cassaforte"
|
||||
|
||||
#: locale/tmp-html/tabby-plugin-manager/src/components/pluginsSettingsTab.component.html:6
|
||||
msgid "Error in {plugin}:"
|
||||
@ -1189,7 +1189,7 @@ msgstr "Lingua"
|
||||
|
||||
#: locale/tmp-html/tabby-ssh/src/components/hostKeyPromptModal.component.html:11
|
||||
msgid "Last known host key fingerprint"
|
||||
msgstr "Ultima firma nota della chiave host"
|
||||
msgstr "Ultima firma nota chiave host"
|
||||
|
||||
#: tabby-ssh/src/tabContextMenu.ts:32
|
||||
msgid "Launch WinSCP"
|
||||
@ -1439,7 +1439,7 @@ msgstr "Nelle discussioni di GitHub"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/editProfileModal.component.html:47
|
||||
msgid "Only close the tab when session is explicitly terminated"
|
||||
msgstr "Chiude la scheda solo quando la sessione è esplicitamente terminata"
|
||||
msgstr "Chiudi la scheda solo quando la sessione è esplicitamente terminata"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:46
|
||||
msgid "Opacity"
|
||||
@ -1476,11 +1476,11 @@ msgstr "Arancione"
|
||||
|
||||
#: tabby-electron/src/shells/macDefault.ts:25
|
||||
msgid "OS default"
|
||||
msgstr "Predefinito del OS"
|
||||
msgstr "Predefinito S.O."
|
||||
|
||||
#: tabby-electron/src/shells/winDefault.ts:43
|
||||
msgid "OS default ({name})"
|
||||
msgstr "Predefinito del OS ({name})"
|
||||
msgstr "Predefinito S.O. ({name})"
|
||||
|
||||
#: tabby-terminal/src/components/streamProcessingSettings.component.ts:46
|
||||
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
|
||||
msgid "Ready Timeout (Milliseconds)"
|
||||
msgstr "Tempo di preparazione (in millisecondi)"
|
||||
msgstr "Tempo stato pronto (millisecondi)"
|
||||
|
||||
#: tabby-core/src/services/profiles.service.ts:235
|
||||
#: 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:119
|
||||
msgid "Reconnect"
|
||||
msgstr "Riconetti"
|
||||
msgstr "Riconnetti"
|
||||
|
||||
#: tabby-terminal/src/hotkeys.ts:102
|
||||
msgid "Reconnect current tab (Serial/Telnet/SSH)"
|
||||
@ -1723,7 +1723,7 @@ msgstr "Remoto"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:124
|
||||
msgid "Remove whitespace and newlines around the copied text"
|
||||
msgstr "Rimuovi spazi bianchi e a riorni a capo dal testo copiato"
|
||||
msgstr "Rimuovi spazi bianchi e a ritorni a capo dal testo copiato"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/vaultSettingsTab.component.html:26
|
||||
#: tabby-core/src/tabContextMenu.ts:120
|
||||
@ -2011,7 +2011,7 @@ msgstr "Visualizza predefiniti"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:146
|
||||
msgid "Show Mixer"
|
||||
msgstr "Visualizza Mixer"
|
||||
msgstr "Visualizza mixer"
|
||||
|
||||
#: tabby-core/src/hotkeys.ts:56
|
||||
msgid "Show pane labels (for rearranging)"
|
||||
@ -2177,7 +2177,7 @@ msgstr "Cambia l'implementazione del frontend del terminale (sperimentale)"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/configSyncSettingsTab.component.html:4
|
||||
msgid "Sync"
|
||||
msgstr "Sincronizazione"
|
||||
msgstr "Sincronizzazione"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/configSyncSettingsTab.component.html:53
|
||||
msgid "Sync automatically"
|
||||
@ -2382,7 +2382,7 @@ msgstr "Carica come nuova configurazione"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:39
|
||||
msgid "Use {altKeyName} as the Meta key"
|
||||
msgstr "Usa {altKeyName} come chiave Meta"
|
||||
msgstr "Come chiave meta usa {altKeyName} "
|
||||
|
||||
#: locale/tmp-html/tabby-local/src/components/shellSettingsTab.component.html:5
|
||||
msgid "Use ConPTY"
|
||||
@ -2554,7 +2554,7 @@ msgstr "Giallo"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/setVaultPassphraseModal.component.html:4
|
||||
msgid "You can change it later, but it's unrecoverable if forgotten."
|
||||
msgstr "Si può cambiare più tardi, ma è irrecuperabile se dimenticata."
|
||||
msgstr "Si può modificare più tardi, ma è irrecuperabile se dimenticata."
|
||||
|
||||
#: 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."
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja_JP\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
@ -185,7 +185,7 @@ msgstr "透過の種類"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/inputProcessingSettings.component.html:4
|
||||
msgid "Backspace key mode"
|
||||
msgstr "BackSpaceキー"
|
||||
msgstr "Backspaceキー"
|
||||
|
||||
#: locale/tmp-html/tabby-serial/src/components/serialProfileSettings.component.html:14
|
||||
#: tabby-serial/src/components/serialTab.component.ts:93
|
||||
@ -526,7 +526,7 @@ msgstr "垂直方向の分割サイズを縮小"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:92
|
||||
msgid "Default \"Connect to\" type"
|
||||
msgstr "既定の接続"
|
||||
msgstr "既定の接続方式"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:93
|
||||
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
|
||||
msgid "Disable fluent background while dragging"
|
||||
msgstr "ドラッグ中はFluentを無効にする"
|
||||
msgstr "ドラッグ中はFluent背景を無効化する"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:204
|
||||
msgid "Disable GPU acceleration"
|
||||
@ -757,7 +757,7 @@ msgstr "アップデートが利用可能になったら自動的にインスト
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:211
|
||||
msgid "Enable fluent background option"
|
||||
msgstr "Fluent背景設定を有効にする"
|
||||
msgstr "Fluent背景オプションを有効にする"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:16
|
||||
msgid "Enable font ligatures"
|
||||
@ -806,7 +806,7 @@ msgstr "例:"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:212
|
||||
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
|
||||
msgid "Export"
|
||||
@ -834,7 +834,7 @@ msgstr "固定"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:219
|
||||
msgid "Fluent background sometimes causes drag lag"
|
||||
msgstr "Fluentはドラッグ中にラグを発生させる場合があります"
|
||||
msgstr "Fluent背景はドラッグ中にラグを発生させることがあります"
|
||||
|
||||
#: tabby-terminal/src/tabContextMenu.ts:82
|
||||
msgid "Focus all panes"
|
||||
@ -1052,7 +1052,7 @@ msgstr "ホットキー"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:83
|
||||
msgid "How Tabby presents itself through environment vars"
|
||||
msgstr "環境変数を利用してTabbyを他の端末のように扱わせます"
|
||||
msgstr "環境変数を利用して、Tabbyを他の端末として認識させます"
|
||||
|
||||
#: locale/tmp-html/tabby-ssh/src/components/sshProfileSettings.component.html:24
|
||||
msgid "HTTP proxy"
|
||||
@ -1299,7 +1299,7 @@ msgstr "\"未分類\" に移動"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:197
|
||||
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/editProfileModal.component.html:12
|
||||
@ -1366,7 +1366,7 @@ msgstr "新しいタブ: {profile}"
|
||||
#: tabby-local/src/buttonProvider.ts:20
|
||||
#: tabby-local/src/tabContextMenu.ts:27
|
||||
msgid "New terminal"
|
||||
msgstr "新しいターミナル"
|
||||
msgstr "新しい端末"
|
||||
|
||||
#: tabby-electron/src/hotkeys.ts:10
|
||||
msgid "New window"
|
||||
@ -1956,7 +1956,7 @@ msgstr "Tabbyを %COMSPEC% に設定"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/profilesSettingsTab.component.html:69
|
||||
msgid "Set to 0 to disable recent profiles"
|
||||
msgstr "最近使用したプロファイルをセレクターに表示しない場合は0に設定してください"
|
||||
msgstr "セレクターに最近使用したプロファイルを表示したくない場合は、0に設定してください"
|
||||
|
||||
#: locale/tmp-html/tabby-ssh/src/components/sshSettingsTab.component.html:36
|
||||
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
|
||||
msgid "Show built-in profiles in selector"
|
||||
msgstr "セレクターに標準プロファイルを表示する"
|
||||
msgstr "標準プロファイルをセレクターに表示"
|
||||
|
||||
#: tabby-core/src/hotkeys.ts:12
|
||||
msgid "Show command selector"
|
||||
@ -2089,7 +2089,7 @@ msgstr "ソースコード"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:13
|
||||
msgid "Spaciness"
|
||||
msgstr "UIの間隔"
|
||||
msgstr "UI間隔"
|
||||
|
||||
#: tabby-core/src/tabContextMenu.ts:75
|
||||
msgid "Split"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Korean\n"
|
||||
"Language: ko_KR\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Polish\n"
|
||||
"Language: pl_PL\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Portuguese, Brazilian\n"
|
||||
"Language: pt_BR\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Portuguese\n"
|
||||
"Language: pt_PT\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Russian\n"
|
||||
"Language: ru_RU\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
@ -660,7 +660,7 @@ msgstr "Отключиться от {host}?"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:30
|
||||
msgid "Display images via Sixel escape sequences"
|
||||
msgstr "Отображать изображения при помощи сиксельной управляющей последовательности"
|
||||
msgstr "Отображать изображения при помощи управляющей последовательности Sixel"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:85
|
||||
msgid "Display on"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Serbian (Cyrillic)\n"
|
||||
"Language: sr_SP\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
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-ssh/src/components/sshProfileSettings.component.html:216
|
||||
msgid "Colors"
|
||||
msgstr ""
|
||||
msgstr "Боје"
|
||||
|
||||
#: tabby-core/src/hotkeys.ts:72
|
||||
msgid "Combine all tabs into the current tab"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Swedish\n"
|
||||
"Language: sv_SE\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Turkish\n"
|
||||
"Language: tr_TR\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2025-01-22 22:02\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
@ -966,7 +966,7 @@ msgstr "Hazır bir GitHub sorunu oluşturun"
|
||||
|
||||
#: locale/tmp-html/tabby-plugin-manager/src/components/pluginsSettingsTab.component.html:25
|
||||
msgid "Get"
|
||||
msgstr "Getir"
|
||||
msgstr "Yükle"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/configSyncSettingsTab.component.html:18
|
||||
msgid "Get it from the Tabby Web settings window"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Ukrainian\n"
|
||||
"Language: uk_UA\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
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-terminal/src/components/colorSchemeSettingsTab.component.html:11
|
||||
msgid "Always dark"
|
||||
msgstr "Завжди темно"
|
||||
msgstr "Завжди темна"
|
||||
|
||||
#: locale/tmp-html/tabby-core/src/components/welcomeTab.component.html:27
|
||||
#: locale/tmp-html/tabby-terminal/src/components/colorSchemeSettingsTab.component.html:13
|
||||
msgid "Always light"
|
||||
msgstr "Завжди світити"
|
||||
msgstr "Завжди світла"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/appearanceSettingsTab.component.html:2
|
||||
#: tabby-terminal/src/settings.ts:14
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Chinese Simplified\n"
|
||||
"Language: zh_CN\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2025-01-22 22:02\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
@ -43,7 +43,7 @@ msgstr "辅助功能"
|
||||
|
||||
#: locale/tmp-html/tabby-settings/src/components/windowSettingsTab.component.html:27
|
||||
msgid "Acrylic background"
|
||||
msgstr "亚克力背景"
|
||||
msgstr "毛玻璃背景"
|
||||
|
||||
#: locale/tmp-html/tabby-local/src/components/commandLineEditor.component.html:24
|
||||
#: locale/tmp-html/tabby-local/src/components/environmentEditor.component.html:11
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Chinese Traditional\n"
|
||||
"Language: zh_TW\n"
|
||||
"PO-Revision-Date: 2024-07-10 09:04\n"
|
||||
"PO-Revision-Date: 2025-01-22 22:02\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
@ -1390,7 +1390,7 @@ msgstr "無色彩"
|
||||
|
||||
#: locale/tmp-html/tabby-terminal/src/components/terminalSettingsTab.component.html:85
|
||||
msgid "No modifier"
|
||||
msgstr "不可修改"
|
||||
msgstr "無修飾鍵"
|
||||
|
||||
#: locale/tmp-html/tabby-serial/src/components/serialProfileSettings.component.html:41
|
||||
msgid "None"
|
||||
|
12
package.json
12
package.json
@ -39,11 +39,11 @@
|
||||
"cross-env": "7.0.3",
|
||||
"css-loader": "^6.7.3",
|
||||
"deep-equal": "2.0.5",
|
||||
"electron": "^29",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron": "^32.2.7",
|
||||
"electron-builder": "^26.0.0-alpha.10",
|
||||
"electron-download": "^4.1.1",
|
||||
"electron-installer-snap": "^5.1.0",
|
||||
"electron-rebuild": "^3.2.9",
|
||||
"@electron/rebuild": "^3.7.1",
|
||||
"eslint": "^8.48.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
@ -55,7 +55,7 @@
|
||||
"lru-cache": "^6.0.0",
|
||||
"macos-release": "^3.3.0",
|
||||
"ngx-toastr": "^16.0.2",
|
||||
"node-abi": "^3.65.0",
|
||||
"node-abi": "^3.71.0",
|
||||
"npmlog": "6.0.2",
|
||||
"npx": "^10.2.2",
|
||||
"patch-package": "^6.4.7",
|
||||
@ -76,7 +76,6 @@
|
||||
"source-code-pro": "^2.38.0",
|
||||
"source-map-loader": "^4.0.1",
|
||||
"source-sans-pro": "3.6.0",
|
||||
"ssh2": "^1.14.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"svg-inline-loader": "^0.8.2",
|
||||
"thenby": "^1.3.4",
|
||||
@ -97,7 +96,8 @@
|
||||
"*/pug": "^3",
|
||||
"lzma-native": "^8.0.6",
|
||||
"**/graceful-fs": "^4.2.4",
|
||||
"nan": "2.17.0"
|
||||
"nan": "2.22.0",
|
||||
"node-gyp": "^10.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:typings && node scripts/build-modules.mjs",
|
||||
|
@ -1,8 +1,8 @@
|
||||
diff --git a/node_modules/app-builder-lib/out/appInfo.js b/node_modules/app-builder-lib/out/appInfo.js
|
||||
index 49f6dca..0ea11f2 100644
|
||||
index 7fbbef7..0821807 100644
|
||||
--- a/node_modules/app-builder-lib/out/appInfo.js
|
||||
+++ b/node_modules/app-builder-lib/out/appInfo.js
|
||||
@@ -112,9 +112,7 @@ class AppInfo {
|
||||
@@ -116,9 +116,7 @@ class AppInfo {
|
||||
return this.info.metadata.name;
|
||||
}
|
||||
get linuxPackageName() {
|
@ -1,39 +0,0 @@
|
||||
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': {
|
@ -25,7 +25,7 @@ builder({
|
||||
},
|
||||
] : undefined,
|
||||
},
|
||||
publish: process.env.KEYGEN_TOKEN ? isTag ? 'always' : 'onTagOrDraft' : 'never',
|
||||
publish: (process.env.KEYGEN_TOKEN && isTag) ? 'always' : 'never',
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
|
@ -28,10 +28,7 @@ builder({
|
||||
},
|
||||
mac: {
|
||||
identity: !process.env.CI || process.env.CSC_LINK ? undefined : null,
|
||||
notarize: process.env.APPLE_TEAM_ID ? {
|
||||
appBundleId: 'org.tabby',
|
||||
teamId: process.env.APPLE_TEAM_ID,
|
||||
} : false,
|
||||
notarize: !!process.env.APPLE_TEAM_ID,
|
||||
},
|
||||
npmRebuild: process.env.ARCH !== 'arm64',
|
||||
publish: process.env.KEYGEN_TOKEN ? [
|
||||
@ -42,7 +39,7 @@ builder({
|
||||
},
|
||||
] : undefined,
|
||||
},
|
||||
publish: process.env.KEYGEN_TOKEN ? isTag ? 'always' : 'onTagOrDraft' : 'never',
|
||||
publish: (process.env.KEYGEN_TOKEN && isTag) ? 'always' : 'never',
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { rebuild } from 'electron-rebuild'
|
||||
import { rebuild } from '@electron/rebuild'
|
||||
import * as path from 'path'
|
||||
import * as vars from './vars.mjs'
|
||||
|
||||
|
@ -2,11 +2,15 @@
|
||||
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
||||
import { build as builder } from 'electron-builder'
|
||||
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 keypair = process.env.SM_KEYPAIR_ALIAS
|
||||
|
||||
process.env.ARCH = process.env.ARCH || process.arch
|
||||
|
||||
console.log('Signing enabled:', !!keypair)
|
||||
|
||||
builder({
|
||||
dir: true,
|
||||
win: ['nsis', 'zip'],
|
||||
@ -22,8 +26,35 @@ builder({
|
||||
channel: `latest-${process.env.ARCH}`,
|
||||
},
|
||||
] : undefined,
|
||||
forceCodeSigning: !!keypair,
|
||||
win: {
|
||||
signtoolOptions: {
|
||||
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 => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
|
@ -10,6 +10,8 @@ log.info('deps', 'app')
|
||||
|
||||
sh.cd('app')
|
||||
sh.exec(`yarn install --force --network-timeout 1000000`, { fatal: true })
|
||||
// Some native packages might fail to build before patch-package gets a chance to run via postinstall
|
||||
sh.exec(`yarn postinstall`, { fatal: false })
|
||||
sh.cd('..')
|
||||
|
||||
sh.cd('web')
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { rebuild } from 'electron-rebuild'
|
||||
import { rebuild } from '@electron/rebuild'
|
||||
import sh from 'shelljs'
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs'
|
||||
|
44
tabby-community-color-schemes/schemes/TokyoNight
Normal file
44
tabby-community-color-schemes/schemes/TokyoNight
Normal file
@ -0,0 +1,44 @@
|
||||
!
|
||||
! Generated with :
|
||||
! XRDB2Xreources.py
|
||||
!
|
||||
*.foreground: #c0caf5
|
||||
*.background: #1a1b26
|
||||
*.cursorColor: #c0caf5
|
||||
!
|
||||
! Black
|
||||
*.color0: #15161e
|
||||
*.color8: #414868
|
||||
!
|
||||
! Red
|
||||
*.color1: #f7768e
|
||||
*.color9: #f7768e
|
||||
!
|
||||
! Green
|
||||
*.color2: #9ece6a
|
||||
*.color10: #9ece6a
|
||||
!
|
||||
! Yellow
|
||||
*.color3: #e0af68
|
||||
*.color11: #e0af68
|
||||
!
|
||||
! Blue
|
||||
*.color4: #7aa2f7
|
||||
*.color12: #7aa2f7
|
||||
!
|
||||
! Magenta
|
||||
*.color5: #bb9af7
|
||||
*.color13: #bb9af7
|
||||
!
|
||||
! Cyan
|
||||
*.color6: #7dcfff
|
||||
*.color14: #7dcfff
|
||||
!
|
||||
! White
|
||||
*.color7: #a9b1d6
|
||||
*.color15: #c0caf5
|
||||
!
|
||||
! Bold, Italic, Underline
|
||||
*.colorBD: #eeeeee
|
||||
!*.colorIT:
|
||||
!*.colorUL:
|
44
tabby-community-color-schemes/schemes/TokyoNight Day
Normal file
44
tabby-community-color-schemes/schemes/TokyoNight Day
Normal file
@ -0,0 +1,44 @@
|
||||
!
|
||||
! Generated with :
|
||||
! XRDB2Xreources.py
|
||||
!
|
||||
*.foreground: #3760bf
|
||||
*.background: #e1e2e7
|
||||
*.cursorColor: #3760bf
|
||||
!
|
||||
! Black
|
||||
*.color0: #e9e9ed
|
||||
*.color8: #a1a6c5
|
||||
!
|
||||
! Red
|
||||
*.color1: #f52a65
|
||||
*.color9: #f52a65
|
||||
!
|
||||
! Green
|
||||
*.color2: #587539
|
||||
*.color10: #587539
|
||||
!
|
||||
! Yellow
|
||||
*.color3: #8c6c3e
|
||||
*.color11: #8c6c3e
|
||||
!
|
||||
! Blue
|
||||
*.color4: #2e7de9
|
||||
*.color12: #2e7de9
|
||||
!
|
||||
! Magenta
|
||||
*.color5: #9854f1
|
||||
*.color13: #9854f1
|
||||
!
|
||||
! Cyan
|
||||
*.color6: #007197
|
||||
*.color14: #007197
|
||||
!
|
||||
! White
|
||||
*.color7: #6172b0
|
||||
*.color15: #3760bf
|
||||
!
|
||||
! Bold, Italic, Underline
|
||||
*.colorBD: #eeeeee
|
||||
!*.colorIT:
|
||||
!*.colorUL:
|
44
tabby-community-color-schemes/schemes/TokyoNight Storm
Normal file
44
tabby-community-color-schemes/schemes/TokyoNight Storm
Normal file
@ -0,0 +1,44 @@
|
||||
!
|
||||
! Generated with :
|
||||
! XRDB2Xreources.py
|
||||
!
|
||||
*.foreground: #c0caf5
|
||||
*.background: #24283b
|
||||
*.cursorColor: #c0caf5
|
||||
!
|
||||
! Black
|
||||
*.color0: #1d202f
|
||||
*.color8: #414868
|
||||
!
|
||||
! Red
|
||||
*.color1: #f7768e
|
||||
*.color9: #f7768e
|
||||
!
|
||||
! Green
|
||||
*.color2: #9ece6a
|
||||
*.color10: #9ece6a
|
||||
!
|
||||
! Yellow
|
||||
*.color3: #e0af68
|
||||
*.color11: #e0af68
|
||||
!
|
||||
! Blue
|
||||
*.color4: #7aa2f7
|
||||
*.color12: #7aa2f7
|
||||
!
|
||||
! Magenta
|
||||
*.color5: #bb9af7
|
||||
*.color13: #bb9af7
|
||||
!
|
||||
! Cyan
|
||||
*.color6: #7dcfff
|
||||
*.color14: #7dcfff
|
||||
!
|
||||
! White
|
||||
*.color7: #a9b1d6
|
||||
*.color15: #c0caf5
|
||||
!
|
||||
! Bold, Italic, Underline
|
||||
*.colorBD: #eeeeee
|
||||
!*.colorIT:
|
||||
!*.colorUL:
|
@ -63,22 +63,24 @@ export abstract class FileTransfer {
|
||||
}
|
||||
|
||||
export abstract class FileDownload extends FileTransfer {
|
||||
abstract write (buffer: Buffer): Promise<void>
|
||||
abstract write (buffer: Uint8Array): Promise<void>
|
||||
}
|
||||
|
||||
export abstract class FileUpload extends FileTransfer {
|
||||
abstract read (): Promise<Buffer>
|
||||
abstract read (): Promise<Uint8Array>
|
||||
|
||||
async readAll (): Promise<Buffer> {
|
||||
const buffers: Buffer[] = []
|
||||
async readAll (): Promise<Uint8Array> {
|
||||
const result = new Uint8Array(this.getSize())
|
||||
let pos = 0
|
||||
while (true) {
|
||||
const buf = await this.read()
|
||||
if (!buf.length) {
|
||||
break
|
||||
}
|
||||
buffers.push(Buffer.from(buf))
|
||||
result.set(buf, pos)
|
||||
pos += buf.length
|
||||
}
|
||||
return Buffer.concat(buffers)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,12 +263,12 @@ export class HTMLFileUpload extends FileUpload {
|
||||
return this.file.size
|
||||
}
|
||||
|
||||
async read (): Promise<Buffer> {
|
||||
async read (): Promise<Uint8Array> {
|
||||
const result: any = await this.reader.read()
|
||||
if (result.done || !result.value) {
|
||||
return Buffer.from('')
|
||||
return new Uint8Array(0)
|
||||
}
|
||||
const chunk = Buffer.from(result.value)
|
||||
const chunk = new Uint8Array(result.value)
|
||||
this.increaseProgress(chunk.length)
|
||||
return chunk
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ hotkeys:
|
||||
__nonStructural: true
|
||||
profile-selectors:
|
||||
__nonStructural: true
|
||||
group-selectors:
|
||||
__nonStructural: true
|
||||
profiles: []
|
||||
groups: []
|
||||
profileDefaults:
|
||||
|
@ -264,6 +264,7 @@ export class AppHotkeyProvider extends HotkeyProvider {
|
||||
|
||||
async provide (): Promise<HotkeyDescription[]> {
|
||||
const profiles = await this.profilesService.getProfiles()
|
||||
const groups = await this.profilesService.getProfileGroups()
|
||||
return [
|
||||
...this.hotkeys,
|
||||
...profiles.map(profile => ({
|
||||
@ -274,6 +275,10 @@ export class AppHotkeyProvider extends HotkeyProvider {
|
||||
id: `profile-selectors.${provider.id}`,
|
||||
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 }),
|
||||
})),
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
|
||||
import { DropZoneDirective } from './directives/dropZone.directive'
|
||||
import { CdkAutoDropGroup } from './directives/cdkAutoDropGroup.directive'
|
||||
|
||||
import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ProfilesService, ProfileProvider, QuickConnectProfileProvider, SelectorOption, Profile, SelectorService, CommandProvider } from './api'
|
||||
import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ProfilesService, ProfileProvider, QuickConnectProfileProvider, SelectorOption, Profile, SelectorService, CommandProvider, PartialProfileGroup, ProfileGroup } from './api'
|
||||
|
||||
import { AppService } from './services/app.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 { CommandService } from './services/commands.service'
|
||||
|
||||
import { StandardTheme, StandardCompactTheme, PaperTheme, NewTheme } from './theme'
|
||||
import { NewTheme } from './theme'
|
||||
import { CoreConfigProvider } from './config'
|
||||
import { AppHotkeyProvider } from './hotkeys'
|
||||
import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu, ProfilesContextMenu } from './tabContextMenu'
|
||||
@ -60,9 +60,6 @@ export function TranslateMessageFormatCompilerFactory (): TranslateMessageFormat
|
||||
|
||||
const PROVIDERS = [
|
||||
{ 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: ConfigProvider, useClass: CoreConfigProvider, multi: true },
|
||||
{ provide: TabContextMenuItemProvider, useClass: CommonOptionsContextMenu, multi: true },
|
||||
@ -181,20 +178,24 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
|
||||
if (profile) {
|
||||
profilesService.openNewTabForProfile(profile)
|
||||
}
|
||||
}
|
||||
if (hotkey.startsWith('profile-selectors.')) {
|
||||
} else if (hotkey.startsWith('profile-selectors.')) {
|
||||
const id = hotkey.substring(hotkey.indexOf('.') + 1)
|
||||
const provider = profilesService.getProviders().find(x => x.id === id)
|
||||
if (!provider) {
|
||||
return
|
||||
}
|
||||
this.showSelector(provider).catch(() => null)
|
||||
}
|
||||
if (hotkey === 'command-selector') {
|
||||
} else if (hotkey.startsWith('group-selectors.')) {
|
||||
const id = hotkey.substring(hotkey.indexOf('.') + 1)
|
||||
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)
|
||||
}
|
||||
|
||||
if (hotkey === 'profile-selector') {
|
||||
} else if (hotkey === 'profile-selector') {
|
||||
commands.run('core:profile-selector', {})
|
||||
}
|
||||
})
|
||||
@ -232,6 +233,21 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
|
||||
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> {
|
||||
return {
|
||||
ngModule: AppModule,
|
||||
|
@ -28,7 +28,7 @@ export class HomeBaseService {
|
||||
}
|
||||
|
||||
openDiscord (): void {
|
||||
this.platform.openExternal('https://discord.gg/4c5EVTBhtp')
|
||||
this.platform.openExternal('https://discord.gg/Vn7BjmzhtF')
|
||||
}
|
||||
|
||||
openTranslations (): void {
|
||||
|
@ -487,6 +487,12 @@ export class ProfilesService {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,6 +23,7 @@ export class ThemesService {
|
||||
) {
|
||||
this.rootElementStyleBackup = document.documentElement.style.cssText
|
||||
this.applyTheme(standardTheme)
|
||||
this.applyThemeVariables()
|
||||
config.ready$.toPromise().then(() => {
|
||||
this.applyCurrentTheme()
|
||||
this.applyThemeVariables()
|
||||
@ -37,6 +38,11 @@ export class ThemesService {
|
||||
})
|
||||
}
|
||||
|
||||
private getConfigStoreOrDefaults (): any {
|
||||
/// Theme service is active before the vault is unlocked and config is available
|
||||
return this.config.store ?? this.config.getDefaults()
|
||||
}
|
||||
|
||||
private applyThemeVariables () {
|
||||
if (!this.findCurrentTheme().followsColorScheme) {
|
||||
document.documentElement.style.cssText = this.rootElementStyleBackup
|
||||
@ -60,7 +66,7 @@ export class ThemesService {
|
||||
}
|
||||
|
||||
let background = Color(theme.background)
|
||||
if (this.config.store?.appearance.vibrancy) {
|
||||
if (this.getConfigStoreOrDefaults().appearance.vibrancy) {
|
||||
background = background.fade(0.6)
|
||||
}
|
||||
// const background = theme.background
|
||||
@ -148,13 +154,13 @@ export class ThemesService {
|
||||
vars['--bs-form-switch-bg'] = `url("data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%27-4 -4 8 8%27%3e%3ccircle r=%273%27 fill=%27${switchBackground}%27/%3e%3c/svg%3e")`
|
||||
}
|
||||
|
||||
vars['--spaciness'] = this.config.store.appearance.spaciness
|
||||
vars['--spaciness'] = this.getConfigStoreOrDefaults().appearance.spaciness
|
||||
|
||||
for (const [bg, fg] of contrastPairs) {
|
||||
const colorBg = Color(vars[bg]).hsl()
|
||||
const colorFg = Color(vars[fg]).hsl()
|
||||
const bgContrast = colorBg.contrast(colorFg)
|
||||
if (bgContrast < this.config.store.terminal.minimumContrastRatio) {
|
||||
if (bgContrast < this.getConfigStoreOrDefaults().terminal.minimumContrastRatio) {
|
||||
vars[fg] = this.ensureContrast(colorFg, colorBg).string()
|
||||
}
|
||||
}
|
||||
@ -163,7 +169,7 @@ export class ThemesService {
|
||||
document.documentElement.style.setProperty(key, value)
|
||||
}
|
||||
|
||||
document.body.classList.toggle('no-animations', !this.config.store.accessibility.animations)
|
||||
document.body.classList.toggle('no-animations', !this.getConfigStoreOrDefaults().accessibility.animations)
|
||||
}
|
||||
|
||||
private ensureContrast (color: Color, against: Color): Color {
|
||||
@ -178,7 +184,7 @@ export class ThemesService {
|
||||
while (
|
||||
(step < 1 && color.color[2] > 1 ||
|
||||
step > 1 && color.color[2] < 99) &&
|
||||
color.contrast(against) < this.config.store.terminal.minimumContrastRatio) {
|
||||
color.contrast(against) < this.getConfigStoreOrDefaults().terminal.minimumContrastRatio) {
|
||||
color.color[2] *= step
|
||||
}
|
||||
return color
|
||||
@ -189,22 +195,22 @@ export class ThemesService {
|
||||
}
|
||||
|
||||
findCurrentTheme (): Theme {
|
||||
return this.findTheme(this.config.store.appearance.theme) ?? this.standardTheme
|
||||
return this.findTheme(this.getConfigStoreOrDefaults().appearance.theme) ?? this.standardTheme
|
||||
}
|
||||
|
||||
/// @hidden
|
||||
_getActiveColorScheme (): any {
|
||||
let theme: PlatformTheme = 'dark'
|
||||
if (this.config.store.appearance.colorSchemeMode === 'light') {
|
||||
if (this.getConfigStoreOrDefaults().appearance.colorSchemeMode === 'light') {
|
||||
theme = 'light'
|
||||
} else if (this.config.store.appearance.colorSchemeMode === 'auto') {
|
||||
} else if (this.getConfigStoreOrDefaults().appearance.colorSchemeMode === 'auto') {
|
||||
theme = this.platform.getTheme()
|
||||
}
|
||||
|
||||
if (theme === 'light') {
|
||||
return this.config.store.terminal.lightColorScheme
|
||||
return this.getConfigStoreOrDefaults().terminal.lightColorScheme
|
||||
} else {
|
||||
return this.config.store.terminal.colorScheme
|
||||
return this.getConfigStoreOrDefaults().terminal.colorScheme
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,7 +221,7 @@ export class ThemesService {
|
||||
document.querySelector('head')!.appendChild(this.styleElement)
|
||||
}
|
||||
this.styleElement.textContent = theme.css
|
||||
document.querySelector('style#custom-css')!.innerHTML = this.config.store?.appearance?.css
|
||||
document.querySelector('style#custom-css')!.innerHTML = this.getConfigStoreOrDefaults().appearance.css
|
||||
this.themeChanged.next(theme)
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,13 @@ export class VaultService {
|
||||
if (!vault) {
|
||||
return null
|
||||
}
|
||||
return vault.secrets.find(s => s.type === type && this.keyMatches(key, s)) ?? null
|
||||
let vaultSecret = vault.secrets.find(s => s.type === type && this.keyMatches(key, s))
|
||||
if (!vaultSecret) {
|
||||
// search for secret without host in vault (like a default user/password used in multiple servers)
|
||||
key['host'] = null
|
||||
vaultSecret = vault.secrets.find(s => s.type === type && this.keyMatches(key, s))
|
||||
}
|
||||
return vaultSecret ?? null
|
||||
}
|
||||
|
||||
async addSecret (secret: VaultSecret): Promise<void> {
|
||||
@ -306,7 +312,7 @@ export class VaultFileProvider extends FileProvider {
|
||||
id,
|
||||
description: `${description} (${transfer.getName()})`,
|
||||
},
|
||||
value: (await transfer.readAll()).toString('base64'),
|
||||
value: Buffer.from(await transfer.readAll()).toString('base64'),
|
||||
})
|
||||
return `${this.prefix}${id}`
|
||||
}
|
||||
|
@ -1,36 +0,0 @@
|
||||
@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;
|
||||
}
|
||||
}
|
@ -1,407 +0,0 @@
|
||||
$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);
|
||||
}
|
||||
}
|
@ -1,428 +0,0 @@
|
||||
@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);
|
||||
}
|
||||
}
|
@ -2,32 +2,6 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
|
||||
import { Injectable } from '@angular/core'
|
||||
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 */
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class NewTheme extends Theme {
|
||||
|
@ -24,6 +24,7 @@
|
||||
"devDependencies": {
|
||||
"electron-promise-ipc": "^2.2.4",
|
||||
"ps-node": "^0.1.6",
|
||||
"ssh-config": "^5.0.0",
|
||||
"tmp-promise": "^3.0.2",
|
||||
"which": "^3.0.0",
|
||||
"winston": "^3.3.3"
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { PlatformService, LogService, UpdaterService, DockingService, HostAppService, ThemesService, Platform, AppService, ConfigService, WIN_BUILD_FLUENT_BG_SUPPORTED, isWindowsBuild, HostWindowService, HotkeyProvider, ConfigProvider, FileProvider } from 'tabby-core'
|
||||
import { TerminalColorSchemeProvider } from 'tabby-terminal'
|
||||
import { TerminalColorSchemeProvider, TerminalDecorator } from 'tabby-terminal'
|
||||
import { SFTPContextMenuItemProvider, SSHProfileImporter, AutoPrivateKeyLocator } from 'tabby-ssh'
|
||||
import { PTYInterface, ShellProvider, UACService } from 'tabby-local'
|
||||
import { auditTime } from 'rxjs'
|
||||
@ -23,6 +23,7 @@ import { ElectronConfigProvider } from './config'
|
||||
import { EditSFTPContextMenu } from './sftpContextMenu'
|
||||
import { OpenSSHImporter, PrivateKeyLocator, StaticFileImporter } from './sshImporters'
|
||||
import { ElectronPTYInterface } from './pty'
|
||||
import { PathDropDecorator } from './pathDrop'
|
||||
|
||||
import { CmderShellProvider } from './shells/cmder'
|
||||
import { Cygwin32ShellProvider } from './shells/cygwin32'
|
||||
@ -73,6 +74,8 @@ import { VSDevToolsProvider } from './shells/vs'
|
||||
|
||||
{ provide: PTYInterface, useClass: ElectronPTYInterface },
|
||||
|
||||
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
|
||||
|
||||
// For WindowsDefaultShellProvider
|
||||
PowerShellCoreShellProvider,
|
||||
WSLShellProvider,
|
||||
@ -130,7 +133,10 @@ export default class ElectronModule {
|
||||
})
|
||||
})
|
||||
|
||||
config.changed$.subscribe(() => this.updateVibrancy())
|
||||
config.changed$.subscribe(() => {
|
||||
this.updateVibrancy()
|
||||
this.updateDarkMode()
|
||||
})
|
||||
|
||||
config.changed$.subscribe(() => this.updateWindowControlsColor())
|
||||
|
||||
@ -173,6 +179,11 @@ export default class ElectronModule {
|
||||
this.hostWindow.setOpacity(this.config.store.appearance.opacity)
|
||||
}
|
||||
|
||||
private updateDarkMode () {
|
||||
const colorSchemeMode = this.config.store.appearance.colorSchemeMode
|
||||
this.electron.ipcRenderer.send('window-set-dark-mode', colorSchemeMode)
|
||||
}
|
||||
|
||||
private updateWindowControlsColor () {
|
||||
// if windows and not using native frame, WCO does not exist, return.
|
||||
if (this.hostApp.platform === Platform.Windows && this.config.store.appearance.frame === 'native') {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { TerminalDecorator } from '../api/decorator'
|
||||
import { BaseTerminalTabComponent } from '../api/baseTerminalTab.component'
|
||||
import { TerminalDecorator, BaseTerminalTabComponent } from 'tabby-terminal'
|
||||
import { webUtils } from 'electron'
|
||||
|
||||
/** @hidden */
|
||||
@Injectable()
|
||||
@ -11,8 +11,8 @@ export class PathDropDecorator extends TerminalDecorator {
|
||||
event.preventDefault()
|
||||
}))
|
||||
this.subscribeUntilDetached(terminal, terminal.frontend?.drop$.subscribe((event: DragEvent) => {
|
||||
for (const file of event.dataTransfer!.files as any) {
|
||||
this.injectPath(terminal, file.path)
|
||||
for (const file of event.dataTransfer!.files as unknown as Iterable<File>) {
|
||||
this.injectPath(terminal, webUtils.getPathForFile(file))
|
||||
}
|
||||
event.preventDefault()
|
||||
}))
|
@ -10,7 +10,7 @@ try {
|
||||
} catch { }
|
||||
|
||||
try {
|
||||
var windowsProcessTree = require('windows-process-tree') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
|
||||
var windowsProcessTree = require('@tabby-gang/windows-process-tree') // eslint-disable-line @typescript-eslint/no-var-requires, no-var
|
||||
} catch { }
|
||||
|
||||
export class ElectronPTYInterface extends PTYInterface {
|
||||
|
@ -18,7 +18,7 @@ const fontManager = require('fontmanager-redux') // eslint-disable-line
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-var
|
||||
var windowsProcessTreeNative = require('windows-process-tree/build/Release/windows_process_tree.node')
|
||||
var windowsProcessTreeNative = require('@tabby-gang/windows-process-tree/build/Release/windows_process_tree.node')
|
||||
// eslint-disable-next-line no-var
|
||||
var wnr = require('windows-native-registry')
|
||||
} catch { }
|
||||
@ -300,12 +300,12 @@ class ElectronFileUpload extends FileUpload {
|
||||
private size: number
|
||||
private mode: number
|
||||
private file: fs.FileHandle
|
||||
private buffer: Buffer
|
||||
private buffer: Uint8Array
|
||||
private powerSaveBlocker = 0
|
||||
|
||||
constructor (private filePath: string, private electron: ElectronService) {
|
||||
super()
|
||||
this.buffer = Buffer.alloc(256 * 1024)
|
||||
this.buffer = new Uint8Array(256 * 1024)
|
||||
this.powerSaveBlocker = electron.powerSaveBlocker.start('prevent-app-suspension')
|
||||
}
|
||||
|
||||
@ -328,7 +328,7 @@ class ElectronFileUpload extends FileUpload {
|
||||
return this.size
|
||||
}
|
||||
|
||||
async read (): Promise<Buffer> {
|
||||
async read (): Promise<Uint8Array> {
|
||||
const result = await this.file.read(this.buffer, 0, this.buffer.length, null)
|
||||
this.increaseProgress(result.bytesRead)
|
||||
return this.buffer.slice(0, result.bytesRead)
|
||||
@ -370,7 +370,7 @@ class ElectronFileDownload extends FileDownload {
|
||||
return this.size
|
||||
}
|
||||
|
||||
async write (buffer: Buffer): Promise<void> {
|
||||
async write (buffer: Uint8Array): Promise<void> {
|
||||
let pos = 0
|
||||
while (pos < buffer.length) {
|
||||
const result = await this.file.write(buffer, pos, buffer.length - pos, null)
|
||||
|
@ -49,19 +49,24 @@ export class EditSFTPContextMenu extends SFTPContextMenuItemProvider {
|
||||
this.platform.openPath(tempPath)
|
||||
|
||||
const events = new Subject<string>()
|
||||
const watcher = fs.watch(tempPath, event => events.next(event))
|
||||
events.pipe(debounceTime(1000), debounce(async event => {
|
||||
if (event === 'rename') {
|
||||
watcher.close()
|
||||
}
|
||||
const upload = await this.platform.startUpload({ multiple: false }, [tempPath])
|
||||
if (!upload.length) {
|
||||
return
|
||||
}
|
||||
await sftp.upload(item.fullPath, upload[0])
|
||||
await sftp.chmod(item.fullPath, item.mode)
|
||||
})).subscribe()
|
||||
watcher.on('close', () => events.complete())
|
||||
sftp.closed$.subscribe(() => watcher.close())
|
||||
fs.chmodSync(tempPath, 0o700)
|
||||
|
||||
// skip the first burst of events
|
||||
setTimeout(() => {
|
||||
const watcher = fs.watch(tempPath, event => events.next(event))
|
||||
events.pipe(debounceTime(1000), debounce(async event => {
|
||||
if (event === 'rename') {
|
||||
watcher.close()
|
||||
}
|
||||
const upload = await this.platform.startUpload({ multiple: false }, [tempPath])
|
||||
if (!upload.length) {
|
||||
return
|
||||
}
|
||||
await sftp.upload(item.fullPath, upload[0])
|
||||
await sftp.chmod(item.fullPath, item.mode)
|
||||
})).subscribe()
|
||||
watcher.on('close', () => events.complete())
|
||||
sftp.closed$.subscribe(() => watcher.close())
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
@ -1,130 +1,346 @@
|
||||
import at from 'core-js-pure/actual/array/at'
|
||||
import * as fs from 'fs/promises'
|
||||
import * as fsSync from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as glob from 'glob'
|
||||
import slugify from 'slugify'
|
||||
import * as yaml from 'js-yaml'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { PartialProfile } from 'tabby-core'
|
||||
import { SSHProfileImporter, PortForwardType, SSHProfile, SSHProfileOptions, AutoPrivateKeyLocator } from 'tabby-ssh'
|
||||
import {
|
||||
SSHProfileImporter,
|
||||
PortForwardType,
|
||||
SSHProfile,
|
||||
AutoPrivateKeyLocator,
|
||||
ForwardedPortConfig,
|
||||
} from 'tabby-ssh'
|
||||
|
||||
import { ElectronService } from './services/electron.service'
|
||||
import SSHConfig, { Directive, 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 recursively and parse any Include directives
|
||||
async function parseSSHConfigFile (
|
||||
filePath: string,
|
||||
visited = new Set<string>(),
|
||||
): Promise<SSHConfig> {
|
||||
// If we've already processed this file, return an empty config to avoid infinite recursion
|
||||
if (visited.has(filePath)) {
|
||||
return SSHConfig.parse('')
|
||||
}
|
||||
visited.add(filePath)
|
||||
|
||||
let raw = ''
|
||||
try {
|
||||
raw = await fs.readFile(filePath, 'utf8')
|
||||
} catch (err) {
|
||||
console.error(`Error reading SSH config file: ${filePath}`, err)
|
||||
return SSHConfig.parse('')
|
||||
}
|
||||
|
||||
const parsed = SSHConfig.parse(raw)
|
||||
const merged = SSHConfig.parse('')
|
||||
for (const entry of parsed) {
|
||||
if (entry.type === LineType.DIRECTIVE && entry.param.toLowerCase() === 'include') {
|
||||
const directive = entry as Directive
|
||||
if (typeof directive.value !== 'string') {
|
||||
continue
|
||||
}
|
||||
|
||||
// ssh_config(5) says "Files without absolute paths are assumed to be in ~/.ssh if included in a user configuration file or /etc/ssh if included from the system configuration file."
|
||||
let incPath = ''
|
||||
if (path.isAbsolute(directive.value)) {
|
||||
incPath = directive.value
|
||||
} else if (directive.value.startsWith('~')) {
|
||||
incPath = path.join(process.env.HOME ?? '~', directive.value.slice(1))
|
||||
} else {
|
||||
incPath = path.join(process.env.HOME ?? '~', '.ssh', directive.value)
|
||||
}
|
||||
|
||||
const matches = glob.sync(incPath)
|
||||
for (const match of matches) {
|
||||
const stat = await fs.stat(match)
|
||||
if (stat.isDirectory()) {
|
||||
continue
|
||||
}
|
||||
const matchedConfig = await parseSSHConfigFile(match, visited)
|
||||
merged.push(...matchedConfig)
|
||||
}
|
||||
} else {
|
||||
merged.push(entry)
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// 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 (Object.keys(configuration).map(key => key.toLowerCase()).includes('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' })
|
||||
export class OpenSSHImporter extends SSHProfileImporter {
|
||||
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')
|
||||
try {
|
||||
const lines = (await fs.readFile(configPath, 'utf8')).split('\n')
|
||||
const globalOptions: Partial<SSHProfileOptions> = {}
|
||||
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
|
||||
try {
|
||||
const config: SSHConfig = await parseSSHConfigFile(configPath)
|
||||
return convertToSSHProfiles(config)
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
return []
|
||||
|
@ -413,6 +413,11 @@ simple-swizzle@^0.2.2:
|
||||
dependencies:
|
||||
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:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
|
||||
|
@ -1,27 +1,38 @@
|
||||
command-line-editor([model]='profile.options')
|
||||
ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
li(ngbNavItem)
|
||||
a(ngbNavLink, translate) General
|
||||
ng-template(ngbNavContent)
|
||||
command-line-editor([model]='profile.options')
|
||||
|
||||
.form-line(*ngIf='uac?.isAvailable')
|
||||
.header
|
||||
.title(translate) Run as administrator
|
||||
toggle(
|
||||
[(ngModel)]='profile.options.runAsAdministrator',
|
||||
)
|
||||
.form-line(*ngIf='uac?.isAvailable')
|
||||
.header
|
||||
.title(translate) Run as administrator
|
||||
toggle(
|
||||
[(ngModel)]='profile.options.runAsAdministrator',
|
||||
)
|
||||
|
||||
.mb-3
|
||||
label(translate) Working directory
|
||||
.mb-3
|
||||
label(translate) Working directory
|
||||
|
||||
.input-group
|
||||
input.form-control(
|
||||
type='text',
|
||||
placeholder='Home directory',
|
||||
[(ngModel)]='profile.options.cwd'
|
||||
)
|
||||
button.btn.btn-secondary((click)='pickWorkingDirectory()')
|
||||
i.fas.fa-folder-open
|
||||
.input-group
|
||||
input.form-control(
|
||||
type='text',
|
||||
placeholder='Home directory',
|
||||
[(ngModel)]='profile.options.cwd'
|
||||
)
|
||||
button.btn.btn-secondary((click)='pickWorkingDirectory()')
|
||||
i.fas.fa-folder-open
|
||||
|
||||
.mb-3
|
||||
label(translate) Environment
|
||||
environment-editor(
|
||||
type='text',
|
||||
[(model)]='profile.options.env',
|
||||
)
|
||||
.mb-3
|
||||
label(translate) Environment
|
||||
environment-editor(
|
||||
type='text',
|
||||
[(model)]='profile.options.env',
|
||||
)
|
||||
|
||||
li(ngbNavItem)
|
||||
a(ngbNavLink, translate) Colors
|
||||
ng-template(ngbNavContent)
|
||||
color-scheme-selector([(model)]='profile.terminalColorScheme')
|
||||
|
||||
div([ngbNavOutlet]='nav')
|
||||
|
@ -123,7 +123,7 @@ export class VaultSettingsTabComponent extends BaseComponent {
|
||||
}
|
||||
await this.vault.updateSecret(secret, {
|
||||
...secret,
|
||||
value: (await transfers[0].readAll()).toString('base64'),
|
||||
value: Buffer.from(await transfers[0].readAll()).toString('base64'),
|
||||
})
|
||||
this.loadVault()
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
h3.mb-3(translate) Window
|
||||
|
||||
.form-line
|
||||
.form-line(*ngIf='themes.length > 1')
|
||||
.header
|
||||
.title(translate) Theme
|
||||
select.form-control(
|
||||
|
@ -11,22 +11,17 @@
|
||||
"build": "webpack --progress --color",
|
||||
"watch": "webpack --progress --color --watch",
|
||||
"postinstall": "run-script-os",
|
||||
"postinstall:darwin:linux": "exit",
|
||||
"postinstall:win32": "xcopy /i /y ..\\node_modules\\ssh2\\util\\pagent.exe util\\"
|
||||
"postinstall:darwin:linux": "exit"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"util/pagent.exe",
|
||||
"typings"
|
||||
],
|
||||
"author": "Eugene Pankov",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "20.3.1",
|
||||
"@types/ssh2": "^0.5.46",
|
||||
"ansi-colors": "^4.1.1",
|
||||
"diffie-hellman": "^5.0.3",
|
||||
"sshpk": "Eugeny/node-sshpk#c2b71d1243714d2daf0988f84c3323d180817136",
|
||||
"strip-ansi": "^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -45,5 +40,8 @@
|
||||
"tabby-core": "*",
|
||||
"tabby-settings": "*",
|
||||
"tabby-terminal": "*"
|
||||
},
|
||||
"resolutions": {
|
||||
"glob": "7.2.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,45 @@
|
||||
import * as ALGORITHMS from 'ssh2/lib/protocol/constants'
|
||||
import { ALGORITHM_BLACKLIST, SSHAlgorithmType } from './api'
|
||||
import * as russh from 'russh'
|
||||
import { SSHAlgorithmType } from './api'
|
||||
|
||||
// Counteracts https://github.com/mscdex/ssh2/commit/f1b5ac3c81734c194740016eab79a699efae83d8
|
||||
ALGORITHMS.DEFAULT_CIPHER.push('aes128-gcm')
|
||||
ALGORITHMS.DEFAULT_CIPHER.push('aes256-gcm')
|
||||
ALGORITHMS.SUPPORTED_CIPHER.push('aes128-gcm')
|
||||
ALGORITHMS.SUPPORTED_CIPHER.push('aes256-gcm')
|
||||
|
||||
export const supportedAlgorithms: Record<string, string> = {}
|
||||
|
||||
for (const k of Object.values(SSHAlgorithmType)) {
|
||||
const supportedAlg = {
|
||||
[SSHAlgorithmType.KEX]: 'SUPPORTED_KEX',
|
||||
[SSHAlgorithmType.HOSTKEY]: 'SUPPORTED_SERVER_HOST_KEY',
|
||||
[SSHAlgorithmType.CIPHER]: 'SUPPORTED_CIPHER',
|
||||
[SSHAlgorithmType.HMAC]: 'SUPPORTED_MAC',
|
||||
}[k]
|
||||
supportedAlgorithms[k] = ALGORITHMS[supportedAlg].filter(x => !ALGORITHM_BLACKLIST.includes(x)).sort()
|
||||
export const supportedAlgorithms = {
|
||||
[SSHAlgorithmType.KEX]: russh.getSupportedKexAlgorithms().filter(x => x !== 'none'),
|
||||
[SSHAlgorithmType.HOSTKEY]: russh.getSupportedKeyTypes().filter(x => x !== 'none'),
|
||||
[SSHAlgorithmType.CIPHER]: russh.getSupportedCiphers().filter(x => x !== 'clear'),
|
||||
[SSHAlgorithmType.HMAC]: russh.getSupportedMACs().filter(x => x !== 'none'),
|
||||
}
|
||||
|
||||
export const defaultAlgorithms = {
|
||||
[SSHAlgorithmType.KEX]: [
|
||||
'curve25519-sha256',
|
||||
'curve25519-sha256@libssh.org',
|
||||
'diffie-hellman-group16-sha512',
|
||||
'diffie-hellman-group14-sha256',
|
||||
'ext-info-c',
|
||||
'ext-info-s',
|
||||
'kex-strict-c-v00@openssh.com',
|
||||
'kex-strict-s-v00@openssh.com',
|
||||
],
|
||||
[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',
|
||||
],
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
export * from './contextMenu'
|
||||
export * from './interfaces'
|
||||
export * from './importer'
|
||||
export * from './proxyStream'
|
||||
export { SSHMultiplexerService } from '../services/sshMultiplexer.service'
|
||||
|
@ -51,13 +51,3 @@ export interface ForwardedPortConfig {
|
||||
targetPort: number
|
||||
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 = []
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -14,11 +14,19 @@ input.form-control.mt-2(
|
||||
)
|
||||
|
||||
.d-flex.mt-3
|
||||
button.btn.btn-secondary(
|
||||
checkbox(
|
||||
*ngIf='isPassword()',
|
||||
[(ngModel)]='remember',
|
||||
[text]='"Save password"|translate'
|
||||
)
|
||||
|
||||
.ms-auto
|
||||
|
||||
button.btn.btn-secondary.me-3(
|
||||
*ngIf='step > 0',
|
||||
(click)='previous()'
|
||||
)
|
||||
.ms-auto
|
||||
|
||||
button.btn.btn-primary(
|
||||
(click)='next()'
|
||||
)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core'
|
||||
import { KeyboardInteractivePrompt } from '../session/ssh'
|
||||
|
||||
import { SSHProfile } from '../api'
|
||||
import { PasswordStorageService } from '../services/passwordStorage.service'
|
||||
|
||||
@Component({
|
||||
selector: 'keyboard-interactive-auth-panel',
|
||||
@ -9,13 +10,17 @@ import { KeyboardInteractivePrompt } from '../session/ssh'
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class KeyboardInteractiveAuthComponent {
|
||||
@Input() profile: SSHProfile
|
||||
@Input() prompt: KeyboardInteractivePrompt
|
||||
@Input() step = 0
|
||||
@Output() done = new EventEmitter()
|
||||
@ViewChild('input') input: ElementRef
|
||||
remember = false
|
||||
|
||||
constructor (private passwordStorage: PasswordStorageService) {}
|
||||
|
||||
isPassword (): boolean {
|
||||
return this.prompt.prompts[this.step].prompt.toLowerCase().includes('password') || !this.prompt.prompts[this.step].echo
|
||||
return this.prompt.isAPasswordPrompt(this.step)
|
||||
}
|
||||
|
||||
previous (): void {
|
||||
@ -26,6 +31,10 @@ export class KeyboardInteractiveAuthComponent {
|
||||
}
|
||||
|
||||
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) {
|
||||
this.prompt.respond()
|
||||
this.done.emit()
|
||||
|
@ -51,6 +51,7 @@ sftp-panel.bg-dark(
|
||||
keyboard-interactive-auth-panel.bg-dark(
|
||||
*ngIf='activeKIPrompt',
|
||||
[prompt]='activeKIPrompt',
|
||||
[profile]='profile',
|
||||
(click)='$event.stopPropagation()',
|
||||
(done)='activeKIPrompt = null; frontend?.focus()'
|
||||
)
|
||||
|
@ -1,3 +1,4 @@
|
||||
import * as russh from 'russh'
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
|
||||
import colors from 'ansi-colors'
|
||||
import { Component, Injector, HostListener } from '@angular/core'
|
||||
@ -94,17 +95,21 @@ export class SSHTabComponent extends ConnectableTerminalTabComponent<SSHProfile>
|
||||
}
|
||||
})
|
||||
|
||||
session.jumpStream = await new Promise((resolve, reject) => jumpSession.ssh.forwardOut(
|
||||
'127.0.0.1', 0, profile.options.host, profile.options.port ?? 22,
|
||||
(err, stream) => {
|
||||
if (err) {
|
||||
jumpSession.emitServiceMessage(colors.bgRed.black(' X ') + ` Could not set up port forward on ${jumpConnection.name}`)
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
resolve(stream)
|
||||
},
|
||||
))
|
||||
if (!(jumpSession.ssh instanceof russh.AuthenticatedSSHClient)) {
|
||||
throw new Error('Jump session is not authenticated yet somehow')
|
||||
}
|
||||
|
||||
try {
|
||||
session.jumpChannel = await jumpSession.ssh.openTCPForwardChannel({
|
||||
addressToConnectTo: profile.options.host,
|
||||
portToConnectTo: profile.options.port ?? 22,
|
||||
originatorAddress: '127.0.0.1',
|
||||
originatorPort: 0,
|
||||
})
|
||||
} catch (err) {
|
||||
jumpSession.emitServiceMessage(colors.bgRed.black(' X ') + ` Could not set up port forward on ${jumpConnection.name}`)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +130,7 @@ export class SSHTabComponent extends ConnectableTerminalTabComponent<SSHProfile>
|
||||
})
|
||||
|
||||
if (!session.open) {
|
||||
this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` Connecting to ${session.profile.options.host}\r\n`)
|
||||
this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` Connecting to ${session.profile.name}\r\n`)
|
||||
|
||||
this.startSpinner(this.translate.instant(_('Connecting')))
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
import './polyfills'
|
||||
|
||||
import { NgModule } from '@angular/core'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user