mirror of
https://github.com/Eugeny/tabby.git
synced 2025-06-08 13:30:02 +00:00
Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ef59394b79 | ||
![]() |
020372c902 | ||
![]() |
912e0aa426 | ||
![]() |
4a5087afc1 | ||
![]() |
e0c34ef7bc | ||
![]() |
406e9e1c42 | ||
![]() |
7ac85a329e | ||
![]() |
af5d3b729b | ||
![]() |
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 |
38
.github/workflows/build.yml
vendored
38
.github/workflows/build.yml
vendored
@ -6,12 +6,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v3.7.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
@ -49,12 +49,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v3.7.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
@ -130,7 +130,7 @@ jobs:
|
||||
path: artifact-zip
|
||||
|
||||
Linux-Build:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ${{matrix.os}}
|
||||
needs: Lint
|
||||
strategy:
|
||||
matrix:
|
||||
@ -138,14 +138,17 @@ jobs:
|
||||
- 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:
|
||||
@ -158,15 +161,19 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v3.7.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Install FPM
|
||||
run: |
|
||||
sudo gem install fpm
|
||||
|
||||
- run: rustup target add ${{matrix.rust_triple}}
|
||||
|
||||
- name: Install dependencies
|
||||
@ -176,12 +183,12 @@ jobs:
|
||||
|
||||
- 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
|
||||
@ -191,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: |
|
||||
@ -206,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}}
|
||||
@ -225,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)
|
||||
@ -250,7 +258,7 @@ jobs:
|
||||
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 ubuntu/oracular 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}})
|
||||
@ -307,7 +315,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@ -316,7 +324,7 @@ jobs:
|
||||
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags'))
|
||||
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v3.7.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
6
.github/workflows/docs.yml
vendored
6
.github/workflows/docs.yml
vendored
@ -7,14 +7,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Installing Node
|
||||
uses: actions/setup-node@v3.7.0
|
||||
uses: actions/setup-node@v4.4.0
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
||||
# テーマ
|
||||
|
@ -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>
|
||||
# 테마
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -31,7 +31,7 @@
|
||||
* Встроенный SSH- и Telnet-клиент и менеджер подключений;
|
||||
* Встроенный последовательный терминал;
|
||||
* Темы и цветовые схемы;
|
||||
* Полностью настраеваемые сочетания клавиш;
|
||||
* Полностью настраиваемые сочетания клавиш;
|
||||
* Панели;
|
||||
* Запоминание вкладок;
|
||||
* Поддержка PowerShell (and PS Core), WSL, Git-Bash, Cygwin, MSYS2, Cmder и CMD;
|
||||
@ -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>
|
||||
# Темы
|
||||
|
||||
|
@ -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>
|
||||
# 主题
|
||||
|
||||
|
@ -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 {
|
||||
@ -115,6 +119,8 @@ export class Window {
|
||||
this.setVibrancy(true)
|
||||
}
|
||||
|
||||
this.setDarkMode(this.configStore.appearance?.colorSchemeMode ?? 'dark')
|
||||
|
||||
if (!options.hidden) {
|
||||
if (maximized) {
|
||||
this.window.maximize()
|
||||
@ -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
|
||||
|
@ -30,7 +30,7 @@
|
||||
"native-process-working-directory": "^1.0.2",
|
||||
"npm": "6",
|
||||
"rxjs": "^7.5.7",
|
||||
"russh": "0.1.9",
|
||||
"russh": "0.1.24",
|
||||
"source-map-support": "^0.5.20",
|
||||
"v8-compile-cache": "^2.3.0",
|
||||
"yargs": "^17.7.2"
|
||||
@ -64,9 +64,9 @@
|
||||
"tabby-terminal": "*"
|
||||
},
|
||||
"resolutions": {
|
||||
"*/node-abi": "^3",
|
||||
"node-abi": "4.9.0",
|
||||
"node-gyp": "^10.0.0",
|
||||
"nan": "2.22.0",
|
||||
"nan": "2.22.2",
|
||||
"node-addon-api": "^8.3.0"
|
||||
}
|
||||
}
|
||||
|
@ -2607,10 +2607,10 @@ mz@^2.7.0:
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
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==
|
||||
nan@2.22.2, nan@^2.17.0:
|
||||
version "2.22.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb"
|
||||
integrity sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==
|
||||
|
||||
napi-build-utils@^1.0.1:
|
||||
version "1.0.2"
|
||||
@ -2641,19 +2641,12 @@ nice-try@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-abi@^3:
|
||||
version "3.65.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.65.0.tgz#ca92d559388e1e9cab1680a18c1a18757cdac9d3"
|
||||
integrity sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==
|
||||
node-abi@4.9.0, node-abi@^3.3.0:
|
||||
version "4.9.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-4.9.0.tgz#ca6dabf7991e54bf3ba6d8d32641e1b84f305263"
|
||||
integrity sha512-0isb3h+AXUblx5Iv0mnYy2WsErH+dk2e9iXJXdKAtS076Q5hP+scQhp6P4tvDeVlOBlG3ROKvkpQHtbORllq2A==
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
node-abi@^3.3.0:
|
||||
version "3.8.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.8.0.tgz#679957dc8e7aa47b0a02589dbfde4f77b29ccb32"
|
||||
integrity sha512-tzua9qWWi7iW4I42vUPKM+SfaF0vQSLAm4yO5J83mSwB7GeoWrDKC/K+8YCnYNwqP5duwazbw2X9l4m8SC2cUw==
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
semver "^7.6.3"
|
||||
|
||||
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"
|
||||
@ -3628,10 +3621,10 @@ run-queue@^1.0.0, run-queue@^1.0.3:
|
||||
dependencies:
|
||||
aproba "^1.1.1"
|
||||
|
||||
russh@0.1.9:
|
||||
version "0.1.9"
|
||||
resolved "https://registry.yarnpkg.com/russh/-/russh-0.1.9.tgz#9b9623062cce4533a26355acb15203849069c789"
|
||||
integrity sha512-jGeFjV5G6NIS3jI2MskIUrOt3sO8y3CXTDP3/lWXkmpvIkJtNQexH6pXU+XdhfuNcHzhXOoh3+fooF+aseoOcw==
|
||||
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"
|
||||
|
||||
@ -3681,6 +3674,11 @@ semver@^7.3.5:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^7.6.3:
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
|
||||
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
|
||||
|
||||
serialize-error@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac"
|
||||
|
@ -8,7 +8,7 @@
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.microphone</key>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
|
@ -37,7 +37,8 @@ asarUnpack:
|
||||
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}
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Language: id_ID\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\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-12-24 22:58\n"
|
||||
"PO-Revision-Date: 2025-01-22 22:02\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
@ -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"
|
||||
|
@ -10,7 +10,7 @@ msgstr ""
|
||||
"Project-Id-Version: tabby\n"
|
||||
"Language-Team: Turkish\n"
|
||||
"Language: tr_TR\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\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: Chinese Simplified\n"
|
||||
"Language: zh_CN\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
"PO-Revision-Date: 2025-01-22 22:02\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: Chinese Traditional\n"
|
||||
"Language: zh_TW\n"
|
||||
"PO-Revision-Date: 2024-12-24 22:58\n"
|
||||
"PO-Revision-Date: 2025-01-22 22:02\n"
|
||||
|
||||
#: tabby-local/src/components/terminalTab.component.ts:113
|
||||
msgid "\"{command}\" is still running. Close?"
|
||||
|
11
package.json
11
package.json
@ -12,6 +12,7 @@
|
||||
"@angular/platform-browser-dynamic": "^15.2.6",
|
||||
"@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
|
||||
"@electron/notarize": "^1.2.3",
|
||||
"@electron/rebuild": "^4",
|
||||
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^14.1.0",
|
||||
"@ngtools/webpack": "^15.2.5",
|
||||
@ -39,11 +40,10 @@
|
||||
"cross-env": "7.0.3",
|
||||
"css-loader": "^6.7.3",
|
||||
"deep-equal": "2.0.5",
|
||||
"electron": "^32.2.7",
|
||||
"electron-builder": "^26.0.0-alpha.8",
|
||||
"electron": "^36.3",
|
||||
"electron-builder": "^26.0",
|
||||
"electron-download": "^4.1.1",
|
||||
"electron-installer-snap": "^5.1.0",
|
||||
"@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.71.0",
|
||||
"node-abi": "^4",
|
||||
"npmlog": "6.0.2",
|
||||
"npx": "^10.2.2",
|
||||
"patch-package": "^6.4.7",
|
||||
@ -96,7 +96,8 @@
|
||||
"*/pug": "^3",
|
||||
"lzma-native": "^8.0.6",
|
||||
"**/graceful-fs": "^4.2.4",
|
||||
"nan": "2.22.0",
|
||||
"nan": "2.22.2",
|
||||
"node-abi": "4.9.0",
|
||||
"node-gyp": "^10.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -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 d159c17..eb48466 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() {
|
@ -28,9 +28,7 @@ builder({
|
||||
},
|
||||
mac: {
|
||||
identity: !process.env.CI || process.env.CSC_LINK ? undefined : null,
|
||||
notarize: process.env.APPLE_TEAM_ID ? {
|
||||
teamId: process.env.APPLE_TEAM_ID,
|
||||
} : false,
|
||||
notarize: !!process.env.APPLE_TEAM_ID,
|
||||
},
|
||||
npmRebuild: process.env.ARCH !== 'arm64',
|
||||
publish: process.env.KEYGEN_TOKEN ? [
|
||||
|
@ -28,27 +28,29 @@ builder({
|
||||
] : undefined,
|
||||
forceCodeSigning: !!keypair,
|
||||
win: {
|
||||
certificateSha1: process.env.SM_CODE_SIGNING_CERT_SHA1_HASH,
|
||||
publisherName: process.env.SM_PUBLISHER_NAME,
|
||||
signingHashAlgorithms: ['sha256'],
|
||||
sign: keypair ? async function (configuration) {
|
||||
console.log('Signing', configuration)
|
||||
if (configuration.path) {
|
||||
try {
|
||||
const out = execSync(
|
||||
`smctl sign --keypair-alias=${keypair} --input "${String(configuration.path)}"`
|
||||
)
|
||||
if (out.toString().includes('FAILED')) {
|
||||
throw new Error(out.toString())
|
||||
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)
|
||||
}
|
||||
console.log(out.toString())
|
||||
} catch (e) {
|
||||
console.error(`Failed to sign ${configuration.path}`)
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
} : undefined,
|
||||
} : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
|
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:
|
@ -36,7 +36,7 @@ export { TabsService, NewTabParameters, TabComponentType } from '../services/tab
|
||||
export { UpdaterService } from '../services/updater.service'
|
||||
export { VaultService, Vault, VaultSecret, VaultFileSecret, VAULT_SECRET_TYPE_FILE, StoredVault, VaultSecretKey } from '../services/vault.service'
|
||||
export { FileProvidersService } from '../services/fileProviders.service'
|
||||
export { LocaleService } from '../services/locale.service'
|
||||
export { LocaleService, TabbyFormatedDatePipe } from '../services/locale.service'
|
||||
export { TranslateService } from '@ngx-translate/core'
|
||||
export * from '../utils'
|
||||
export { UTF8Splitter } from '../utfSplitter'
|
||||
|
@ -43,7 +43,7 @@ import { AppService } from './services/app.service'
|
||||
import { ConfigService } from './services/config.service'
|
||||
import { VaultFileProvider } from './services/vault.service'
|
||||
import { HotkeysService } from './services/hotkeys.service'
|
||||
import { CustomMissingTranslationHandler, LocaleService } from './services/locale.service'
|
||||
import { CustomMissingTranslationHandler, LocaleService, TabbyFormatedDatePipe } from './services/locale.service'
|
||||
import { CommandService } from './services/commands.service'
|
||||
|
||||
import { NewTheme } from './theme'
|
||||
@ -130,6 +130,7 @@ const PROVIDERS = [
|
||||
DropZoneDirective,
|
||||
CdkAutoDropGroup,
|
||||
ProfileIconComponent,
|
||||
TabbyFormatedDatePipe,
|
||||
],
|
||||
exports: [
|
||||
AppRootComponent,
|
||||
@ -144,6 +145,7 @@ const PROVIDERS = [
|
||||
TranslateModule,
|
||||
CdkAutoDropGroup,
|
||||
ProfileIconComponent,
|
||||
TabbyFormatedDatePipe,
|
||||
],
|
||||
})
|
||||
export default class AppModule { // eslint-disable-line @typescript-eslint/no-extraneous-class
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { registerLocaleData } from '@angular/common'
|
||||
import { Injectable, Pipe, PipeTransform } from '@angular/core'
|
||||
import { formatDate, registerLocaleData } from '@angular/common'
|
||||
import { TranslateService, MissingTranslationHandler } from '@ngx-translate/core'
|
||||
import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'
|
||||
|
||||
@ -257,3 +257,15 @@ export class LocaleService {
|
||||
return this.locale
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({
|
||||
name: 'tabbyDate',
|
||||
})
|
||||
export class TabbyFormatedDatePipe implements PipeTransform {
|
||||
|
||||
constructor (private locale: LocaleService) {}
|
||||
|
||||
transform (date: string): string {
|
||||
return formatDate(date, 'medium', this.locale.getLocale())
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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()
|
||||
}))
|
@ -50,7 +50,7 @@ export class DockMenuService {
|
||||
])
|
||||
}
|
||||
if (this.hostApp.platform === Platform.macOS) {
|
||||
this.electron.app.dock.setMenu(this.electron.Menu.buildFromTemplate(
|
||||
this.electron.app.dock?.setMenu(this.electron.Menu.buildFromTemplate(
|
||||
[
|
||||
...[...this.profilesService.getRecentProfiles(), ...profiles].map(profile => ({
|
||||
label: profile.name,
|
||||
|
@ -13,4 +13,12 @@ export abstract class LinkHandler {
|
||||
}
|
||||
|
||||
abstract handle (uri: string, tab?: BaseTerminalTabComponent<any>): void
|
||||
|
||||
private _fullMatchRegex: RegExp | null = null
|
||||
get fullMatchRegex (): RegExp {
|
||||
if (!this._fullMatchRegex) {
|
||||
this._fullMatchRegex = new RegExp(`^${this.regex.source}$`)
|
||||
}
|
||||
return this._fullMatchRegex
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ export class LinkHighlighterDecorator extends TerminalDecorator {
|
||||
|
||||
const openLink = async uri => {
|
||||
for (const handler of this.handlers) {
|
||||
if (!handler.regex.test(uri)) {
|
||||
if (!handler.fullMatchRegex.test(uri)) {
|
||||
continue
|
||||
}
|
||||
if (!await handler.verify(await handler.convert(uri, tab), tab)) {
|
||||
|
@ -67,7 +67,7 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
div {{cfg.name}}
|
||||
small.text-muted(
|
||||
translate='Modified on {date}',
|
||||
[translateParams]='{date: cfg.modified_at|date:"medium"}'
|
||||
[translateParams]='{date: cfg.modified_at|tabbyDate}'
|
||||
)
|
||||
.me-auto
|
||||
button.btn.btn-link.ms-1(
|
||||
|
@ -145,4 +145,5 @@ export class ConfigSyncSettingsTabComponent extends BaseComponent {
|
||||
openTabbyWebInfo () {
|
||||
this.platform.openExternal('https://github.com/Eugeny/tabby-web')
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,5 +8,5 @@
|
||||
)
|
||||
div(*ngFor='let release of releases')
|
||||
h1 {{release.name}}
|
||||
.text-muted {{release.version}} / {{release.date|date:'mediumDate'}}
|
||||
.text-muted {{release.version}} / {{release.date|tabbyDate}}
|
||||
section([fastHtmlBind]='release.content')
|
||||
|
@ -45,4 +45,5 @@ export class ReleaseNotesComponent extends BaseTabComponent {
|
||||
onScrolled () {
|
||||
this.loadReleases(this.lastPage + 1)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ export const supportedAlgorithms = {
|
||||
[SSHAlgorithmType.HOSTKEY]: russh.getSupportedKeyTypes().filter(x => x !== 'none'),
|
||||
[SSHAlgorithmType.CIPHER]: russh.getSupportedCiphers().filter(x => x !== 'clear'),
|
||||
[SSHAlgorithmType.HMAC]: russh.getSupportedMACs().filter(x => x !== 'none'),
|
||||
[SSHAlgorithmType.COMPRESSION]: russh.getSupportedCompressionAlgorithms().reverse(),
|
||||
}
|
||||
|
||||
export const defaultAlgorithms = {
|
||||
@ -42,4 +43,9 @@ export const defaultAlgorithms = {
|
||||
'hmac-sha1-etm@openssh.com',
|
||||
'hmac-sha1',
|
||||
],
|
||||
[SSHAlgorithmType.COMPRESSION]: [
|
||||
'zlib@openssh.com',
|
||||
'zlib',
|
||||
'none',
|
||||
],
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ export enum SSHAlgorithmType {
|
||||
KEX = 'kex',
|
||||
CIPHER = 'cipher',
|
||||
HOSTKEY = 'serverHostKey',
|
||||
COMPRESSION = 'compression',
|
||||
|
||||
}
|
||||
|
||||
export interface SSHProfile extends ConnectableTerminalProfile {
|
||||
|
@ -61,5 +61,5 @@
|
||||
div {{item.name}}
|
||||
.me-auto
|
||||
.size(*ngIf='!item.isDirectory') {{item.size|filesize}}
|
||||
.date {{item.modified|date:'medium'}}
|
||||
.date {{item.modified|tabbyDate}}
|
||||
.mode {{getModeString(item)}}
|
||||
|
@ -273,4 +273,5 @@ export class SFTPPanelComponent {
|
||||
close (): void {
|
||||
this.closed.emit()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -286,6 +286,12 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
|
||||
div(*ngFor='let alg of supportedAlgorithms.serverHostKey')
|
||||
checkbox([text]='alg', [(ngModel)]='algorithms.serverHostKey[alg]')
|
||||
|
||||
.form-line.align-items-start
|
||||
.header
|
||||
.title Compression
|
||||
.w-75
|
||||
div(*ngFor='let alg of supportedAlgorithms.compression')
|
||||
checkbox([text]='alg', [(ngModel)]='algorithms.compression[alg]')
|
||||
li(ngbNavItem)
|
||||
a(ngbNavLink, translate) Colors
|
||||
ng-template(ngbNavContent)
|
||||
|
@ -107,7 +107,7 @@ export class SSHProfileSettingsComponent {
|
||||
this.profile.options.algorithms![k] = Object.entries(this.algorithms[k])
|
||||
.filter(([_, v]) => !!v)
|
||||
.map(([key, _]) => key)
|
||||
this.profile.options.algorithms![k].sort()
|
||||
if(k !== SSHAlgorithmType.COMPRESSION) { this.profile.options.algorithms![k].sort() }
|
||||
}
|
||||
|
||||
if (this.connectionMode !== 'jumpHost') {
|
||||
|
@ -55,7 +55,7 @@ export class SSHProfilesService extends QuickConnectProfileProvider<SSHProfile>
|
||||
super()
|
||||
for (const k of Object.values(SSHAlgorithmType)) {
|
||||
this.configDefaults.options.algorithms[k] = [...defaultAlgorithms[k]]
|
||||
this.configDefaults.options.algorithms[k].sort()
|
||||
if (k !== SSHAlgorithmType.COMPRESSION) { this.configDefaults.options.algorithms[k].sort() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
// import * as fs from 'fs/promises'
|
||||
import * as fs from 'fs/promises'
|
||||
import * as crypto from 'crypto'
|
||||
import * as tmp from 'tmp-promise'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ConfigService, HostAppService, Platform, PlatformService } from 'tabby-core'
|
||||
import { ConfigService, FileProvidersService, HostAppService, Platform, PlatformService } from 'tabby-core'
|
||||
import { SSHSession } from '../session/ssh'
|
||||
import { SSHProfile } from '../api'
|
||||
import { PasswordStorageService } from './passwordStorage.service'
|
||||
@ -15,6 +16,7 @@ export class SSHService {
|
||||
private config: ConfigService,
|
||||
hostApp: HostAppService,
|
||||
private platform: PlatformService,
|
||||
private fileProviders: FileProvidersService,
|
||||
) {
|
||||
if (hostApp.platform === Platform.Windows) {
|
||||
this.detectedWinSCPPath = platform.getWinSCPPath()
|
||||
@ -47,14 +49,35 @@ export class SSHService {
|
||||
const args = [await this.getWinSCPURI(session.profile, undefined, session.authUsername ?? undefined)]
|
||||
|
||||
let tmpFile: tmp.FileResult|null = null
|
||||
if (session.activePrivateKey) {
|
||||
tmpFile = await tmp.file()
|
||||
// await fs.writeFile(tmpFile.path, session.activePrivateKey)
|
||||
const winSCPcom = path.slice(0, -3) + 'com'
|
||||
await this.platform.exec(winSCPcom, ['/keygen', tmpFile.path, `/output=${tmpFile.path}`])
|
||||
args.push(`/privatekey=${tmpFile.path}`)
|
||||
try {
|
||||
if (session.activePrivateKey && session.profile.options.privateKeys && session.profile.options.privateKeys.length > 0) {
|
||||
tmpFile = await tmp.file()
|
||||
let passphrase: string|null = null
|
||||
for (const pk of session.profile.options.privateKeys) {
|
||||
let privateKeyContent: string|null = null
|
||||
const buffer = await this.fileProviders.retrieveFile(pk)
|
||||
privateKeyContent = buffer.toString()
|
||||
await fs.writeFile(tmpFile.path, privateKeyContent)
|
||||
const keyHash = crypto.createHash('sha512').update(privateKeyContent).digest('hex')
|
||||
// need to pass an default passphrase, otherwise it might get stuck at the passphrase input
|
||||
passphrase = await this.passwordStorage.loadPrivateKeyPassword(keyHash) ?? 'tabby'
|
||||
const winSCPcom = path.slice(0, -3) + 'com'
|
||||
try {
|
||||
await this.platform.exec(winSCPcom, ['/keygen', tmpFile.path, '-o', tmpFile.path, '--old-passphrase', passphrase])
|
||||
} catch (error) {
|
||||
console.warn('Could not convert private key ', error)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
args.push(`/privatekey=${tmpFile.path}`)
|
||||
if (passphrase != null) {
|
||||
args.push(`/passphrase=${passphrase}`)
|
||||
}
|
||||
}
|
||||
await this.platform.exec(path, args)
|
||||
} finally {
|
||||
tmpFile?.cleanup()
|
||||
}
|
||||
await this.platform.exec(path, args)
|
||||
tmpFile?.cleanup()
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ type AuthMethod = {
|
||||
type: 'publickey'
|
||||
name: string
|
||||
contents: Buffer
|
||||
hashAlg: 'sha256'|'sha512'|'sha1'|null
|
||||
} | {
|
||||
type: 'agent',
|
||||
kind: 'unix-socket',
|
||||
@ -51,6 +50,18 @@ type AuthMethod = {
|
||||
kind: 'pageant',
|
||||
}
|
||||
|
||||
function sshAuthTypeForMethod (m: AuthMethod): string {
|
||||
switch (m.type) {
|
||||
case 'none': return 'none'
|
||||
case 'hostbased': return 'hostbased'
|
||||
case 'prompt-password': return 'password'
|
||||
case 'saved-password': return 'password'
|
||||
case 'keyboard-interactive': return 'keyboard-interactive'
|
||||
case 'publickey': return 'publickey'
|
||||
case 'agent': return 'publickey'
|
||||
}
|
||||
}
|
||||
|
||||
export class KeyboardInteractivePrompt {
|
||||
readonly responses: string[] = []
|
||||
|
||||
@ -87,7 +98,7 @@ export class SSHSession {
|
||||
ssh: russh.SSHClient|russh.AuthenticatedSSHClient
|
||||
sftp?: russh.SFTP
|
||||
forwardedPorts: ForwardedPort[] = []
|
||||
jumpChannel: russh.Channel|null = null
|
||||
jumpChannel: russh.NewChannel|null = null
|
||||
savedPassword?: string
|
||||
get serviceMessage$ (): Observable<string> { return this.serviceMessage }
|
||||
get keyboardInteractivePrompt$ (): Observable<KeyboardInteractivePrompt> { return this.keyboardInteractivePrompt }
|
||||
@ -100,7 +111,7 @@ export class SSHSession {
|
||||
|
||||
private logger: Logger
|
||||
private refCount = 0
|
||||
private remainingAuthMethods: AuthMethod[] = []
|
||||
private allAuthMethods: AuthMethod[] = []
|
||||
private serviceMessage = new Subject<string>()
|
||||
private keyboardInteractivePrompt = new Subject<KeyboardInteractivePrompt>()
|
||||
private willDestroy = new Subject<void>()
|
||||
@ -114,6 +125,7 @@ export class SSHSession {
|
||||
private translate: TranslateService
|
||||
private knownHosts: SSHKnownHostsService
|
||||
private privateKeyImporters: AutoPrivateKeyLocator[]
|
||||
private previouslyDisconnected = false
|
||||
|
||||
constructor (
|
||||
private injector: Injector,
|
||||
@ -139,23 +151,22 @@ export class SSHSession {
|
||||
}
|
||||
|
||||
private addPublicKeyAuthMethod (name: string, contents: Buffer) {
|
||||
for (const hashAlg of ['sha512', 'sha256', 'sha1', null] as const) {
|
||||
this.remainingAuthMethods.push({
|
||||
type: 'publickey',
|
||||
name,
|
||||
contents,
|
||||
hashAlg,
|
||||
})
|
||||
}
|
||||
this.allAuthMethods.push({
|
||||
type: 'publickey',
|
||||
name,
|
||||
contents,
|
||||
})
|
||||
}
|
||||
|
||||
async init (): Promise<void> {
|
||||
this.remainingAuthMethods = [{ type: 'none' }]
|
||||
this.allAuthMethods = [{ type: 'none' }]
|
||||
if (!this.profile.options.auth || this.profile.options.auth === 'publicKey') {
|
||||
if (this.profile.options.privateKeys?.length) {
|
||||
for (const pk of this.profile.options.privateKeys) {
|
||||
for (let pk of this.profile.options.privateKeys) {
|
||||
// eslint-disable-next-line @typescript-eslint/init-declarations
|
||||
let contents: Buffer
|
||||
pk = pk.replace('%h', this.profile.options.host)
|
||||
pk = pk.replace('%r', this.profile.options.user)
|
||||
try {
|
||||
contents = await this.fileProviders.retrieveFile(pk)
|
||||
} catch (error) {
|
||||
@ -179,7 +190,7 @@ export class SSHSession {
|
||||
if (!spec) {
|
||||
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Agent auth selected, but no running Agent process is found`)
|
||||
} else {
|
||||
this.remainingAuthMethods.push({
|
||||
this.allAuthMethods.push({
|
||||
type: 'agent',
|
||||
...spec,
|
||||
})
|
||||
@ -187,22 +198,24 @@ export class SSHSession {
|
||||
}
|
||||
if (!this.profile.options.auth || this.profile.options.auth === 'password') {
|
||||
if (this.profile.options.password) {
|
||||
this.remainingAuthMethods.push({ type: 'saved-password', password: this.profile.options.password })
|
||||
this.allAuthMethods.push({ type: 'saved-password', password: this.profile.options.password })
|
||||
}
|
||||
const password = await this.passwordStorage.loadPassword(this.profile)
|
||||
if (password) {
|
||||
this.remainingAuthMethods.push({ type: 'saved-password', password })
|
||||
this.allAuthMethods.push({ type: 'saved-password', password })
|
||||
}
|
||||
this.remainingAuthMethods.push({ type: 'prompt-password' })
|
||||
}
|
||||
if (!this.profile.options.auth || this.profile.options.auth === 'keyboardInteractive') {
|
||||
const savedPassword = this.profile.options.password ?? await this.passwordStorage.loadPassword(this.profile)
|
||||
if (savedPassword) {
|
||||
this.remainingAuthMethods.push({ type: 'keyboard-interactive', savedPassword })
|
||||
this.allAuthMethods.push({ type: 'keyboard-interactive', savedPassword })
|
||||
}
|
||||
this.remainingAuthMethods.push({ type: 'keyboard-interactive' })
|
||||
this.allAuthMethods.push({ type: 'keyboard-interactive' })
|
||||
}
|
||||
this.remainingAuthMethods.push({ type: 'hostbased' })
|
||||
if (!this.profile.options.auth || this.profile.options.auth === 'password') {
|
||||
this.allAuthMethods.push({ type: 'prompt-password' })
|
||||
}
|
||||
this.allAuthMethods.push({ type: 'hostbased' })
|
||||
}
|
||||
|
||||
private async getAgentConnectionSpec (): Promise<russh.AgentConnectionSpec|null> {
|
||||
@ -244,7 +257,7 @@ export class SSHSession {
|
||||
throw new Error('Cannot open SFTP session before auth')
|
||||
}
|
||||
if (!this.sftp) {
|
||||
this.sftp = await this.ssh.openSFTPChannel()
|
||||
this.sftp = await this.ssh.activateSFTP(await this.ssh.openSessionChannel())
|
||||
}
|
||||
return new SFTPSession(this.sftp, this.injector)
|
||||
}
|
||||
@ -265,7 +278,7 @@ export class SSHSession {
|
||||
const argv = shellQuote.parse(this.profile.options.proxyCommand)
|
||||
transport = await russh.SshTransport.newCommand(argv[0], argv.slice(1))
|
||||
} else if (this.jumpChannel) {
|
||||
transport = await russh.SshTransport.newSshChannel(await this.jumpChannel.take())
|
||||
transport = await russh.SshTransport.newSshChannel(this.jumpChannel.take())
|
||||
this.jumpChannel = null
|
||||
} else if (this.profile.options.socksProxyHost) {
|
||||
this.emitServiceMessage(colors.bgBlue.black(' Proxy ') + ` Using ${this.profile.options.socksProxyHost}:${this.profile.options.socksProxyPort}`)
|
||||
@ -302,6 +315,7 @@ export class SSHSession {
|
||||
kex: this.profile.options.algorithms?.[SSHAlgorithmType.KEX]?.filter(x => supportedAlgorithms[SSHAlgorithmType.KEX].includes(x)),
|
||||
mac: this.profile.options.algorithms?.[SSHAlgorithmType.HMAC]?.filter(x => supportedAlgorithms[SSHAlgorithmType.HMAC].includes(x)),
|
||||
key: this.profile.options.algorithms?.[SSHAlgorithmType.HOSTKEY]?.filter(x => supportedAlgorithms[SSHAlgorithmType.HOSTKEY].includes(x)),
|
||||
compression: this.profile.options.algorithms?.[SSHAlgorithmType.COMPRESSION]?.filter(x => supportedAlgorithms[SSHAlgorithmType.COMPRESSION].includes(x)),
|
||||
},
|
||||
keepaliveIntervalSeconds: Math.round((this.profile.options.keepaliveInterval ?? 15000) / 1000),
|
||||
keepaliveCountMax: this.profile.options.keepaliveCountMax,
|
||||
@ -315,9 +329,14 @@ export class SSHSession {
|
||||
}
|
||||
})
|
||||
|
||||
this.previouslyDisconnected = false
|
||||
this.ssh.disconnect$.subscribe(() => {
|
||||
if (this.open) {
|
||||
this.destroy()
|
||||
if (!this.previouslyDisconnected) {
|
||||
this.previouslyDisconnected = true
|
||||
// Let service messages drain
|
||||
setTimeout(() => {
|
||||
this.destroy()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@ -368,22 +387,31 @@ export class SSHSession {
|
||||
|
||||
this.ssh.tcpChannelOpen$.subscribe(async event => {
|
||||
this.logger.info(`Incoming forwarded connection: ${event.clientAddress}:${event.clientPort} -> ${event.targetAddress}:${event.targetPort}`)
|
||||
|
||||
if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) {
|
||||
throw new Error('Cannot open agent channel before auth')
|
||||
}
|
||||
|
||||
const channel = await this.ssh.activateChannel(event.channel)
|
||||
|
||||
const forward = this.forwardedPorts.find(x => x.port === event.targetPort && x.host === event.targetAddress)
|
||||
if (!forward) {
|
||||
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Rejected incoming forwarded connection for unrecognized port ${event.targetAddress}:${event.targetPort}`)
|
||||
channel.close()
|
||||
return
|
||||
}
|
||||
|
||||
const socket = new Socket()
|
||||
socket.connect(forward.targetPort, forward.targetAddress)
|
||||
socket.on('error', e => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Could not forward the remote connection to ${forward.targetAddress}:${forward.targetPort}: ${e}`)
|
||||
event.channel.close()
|
||||
channel.close()
|
||||
})
|
||||
event.channel.data$.subscribe(data => socket.write(data))
|
||||
socket.on('data', data => event.channel.write(Uint8Array.from(data)))
|
||||
event.channel.closed$.subscribe(() => socket.destroy())
|
||||
socket.on('close', () => event.channel.close())
|
||||
channel.data$.subscribe(data => socket.write(data))
|
||||
socket.on('data', data => channel.write(Uint8Array.from(data)))
|
||||
channel.closed$.subscribe(() => socket.destroy())
|
||||
socket.on('close', () => channel.close())
|
||||
socket.on('connect', () => {
|
||||
this.logger.info('Connection forwarded')
|
||||
})
|
||||
@ -394,22 +422,28 @@ export class SSHSession {
|
||||
const displaySpec = (this.config.store.ssh.x11Display || process.env.DISPLAY) ?? 'localhost:0'
|
||||
this.logger.debug(`Trying display ${displaySpec}`)
|
||||
|
||||
if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) {
|
||||
throw new Error('Cannot open agent channel before auth')
|
||||
}
|
||||
|
||||
const channel = await this.ssh.activateChannel(event.channel)
|
||||
|
||||
const socket = new X11Socket()
|
||||
try {
|
||||
const x11Stream = await socket.connect(displaySpec)
|
||||
this.logger.info('Connection forwarded')
|
||||
|
||||
event.channel.data$.subscribe(data => {
|
||||
channel.data$.subscribe(data => {
|
||||
x11Stream.write(data)
|
||||
})
|
||||
x11Stream.on('data', data => {
|
||||
event.channel.write(Uint8Array.from(data))
|
||||
channel.write(Uint8Array.from(data))
|
||||
})
|
||||
event.channel.closed$.subscribe(() => {
|
||||
channel.closed$.subscribe(() => {
|
||||
socket.destroy()
|
||||
})
|
||||
x11Stream.on('close', () => {
|
||||
event.channel.close()
|
||||
channel.close()
|
||||
})
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||
@ -420,11 +454,17 @@ export class SSHSession {
|
||||
this.emitServiceMessage(' * VcXsrv: https://sourceforge.net/projects/vcxsrv/')
|
||||
this.emitServiceMessage(' * Xming: https://sourceforge.net/projects/xming/')
|
||||
}
|
||||
event.channel.close()
|
||||
channel.close()
|
||||
}
|
||||
})
|
||||
|
||||
this.ssh.agentChannelOpen$.subscribe(async channel => {
|
||||
this.ssh.agentChannelOpen$.subscribe(async newChannel => {
|
||||
if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) {
|
||||
throw new Error('Cannot open agent channel before auth')
|
||||
}
|
||||
|
||||
const channel = await this.ssh.activateChannel(newChannel)
|
||||
|
||||
const spec = await this.getAgentConnectionSpec()
|
||||
if (!spec) {
|
||||
await channel.close()
|
||||
@ -478,7 +518,23 @@ export class SSHSession {
|
||||
this.keyboardInteractivePrompt.next(prompt)
|
||||
}
|
||||
|
||||
async handleAuth (methodsLeft?: string[] | null): Promise<russh.AuthenticatedSSHClient|null> {
|
||||
async handleAuth (): Promise<russh.AuthenticatedSSHClient|null> {
|
||||
const subscription = this.ssh.disconnect$.subscribe(() => {
|
||||
// Auto auth and >=3 keys found
|
||||
if (!this.profile.options.auth && this.allAuthMethods.filter(x => x.type === 'publickey').length >= 3) {
|
||||
this.emitServiceMessage('The server has disconnected during authentication.')
|
||||
this.emitServiceMessage('This may happen if too many private key authentication attemps are made.')
|
||||
this.emitServiceMessage('You can set the specific private key for authentication in the profile settings.')
|
||||
}
|
||||
})
|
||||
try {
|
||||
return await this._handleAuth()
|
||||
} finally {
|
||||
subscription.unsubscribe()
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleAuth (): Promise<russh.AuthenticatedSSHClient|null> {
|
||||
this.activePrivateKey = null
|
||||
|
||||
if (!(this.ssh instanceof russh.SSHClient)) {
|
||||
@ -489,22 +545,37 @@ export class SSHSession {
|
||||
throw new Error('No username')
|
||||
}
|
||||
|
||||
const noneResult = await this.ssh.authenticateNone(this.authUsername)
|
||||
if (noneResult instanceof russh.AuthenticatedSSHClient) {
|
||||
return noneResult
|
||||
}
|
||||
|
||||
let remainingMethods = [...this.allAuthMethods]
|
||||
let methodsLeft = noneResult.remainingMethods
|
||||
|
||||
function maybeSetRemainingMethods (r: russh.AuthFailure) {
|
||||
if (r.remainingMethods.length) {
|
||||
methodsLeft = r.remainingMethods
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const method = this.remainingAuthMethods.shift()
|
||||
if (!method) {
|
||||
const m = methodsLeft
|
||||
const method = remainingMethods.find(x => m.length === 0 || m.includes(sshAuthTypeForMethod(x)))
|
||||
|
||||
if (this.previouslyDisconnected || !method) {
|
||||
return null
|
||||
}
|
||||
if (methodsLeft && !methodsLeft.includes(method.type) && method.type !== 'agent') {
|
||||
// Agent can still be used even if not in methodsLeft
|
||||
this.logger.info('Server does not support auth method', method.type)
|
||||
continue
|
||||
}
|
||||
|
||||
remainingMethods = remainingMethods.filter(x => x !== method)
|
||||
|
||||
if (method.type === 'saved-password') {
|
||||
this.emitServiceMessage(this.translate.instant('Using saved password'))
|
||||
const result = await this.ssh.authenticateWithPassword(this.authUsername, method.password)
|
||||
if (result instanceof russh.AuthenticatedSSHClient) {
|
||||
return result
|
||||
}
|
||||
maybeSetRemainingMethods(result)
|
||||
}
|
||||
if (method.type === 'prompt-password') {
|
||||
const modal = this.ngbModal.open(PromptModalComponent)
|
||||
@ -522,6 +593,7 @@ export class SSHSession {
|
||||
if (result instanceof russh.AuthenticatedSSHClient) {
|
||||
return result
|
||||
}
|
||||
maybeSetRemainingMethods(result)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
@ -532,20 +604,12 @@ export class SSHSession {
|
||||
if (method.type === 'publickey') {
|
||||
try {
|
||||
const key = await this.loadPrivateKey(method.name, method.contents)
|
||||
const possibleHashAlgs = (['ssh-rsa', 'rsa-sha2-256', 'rsa-sha2-512'].includes(key.algorithm) ? ['sha256', 'sha512', 'sha1'] : [null]) as (string|null)[]
|
||||
if (!possibleHashAlgs.includes(method.hashAlg)) {
|
||||
// skip incompatible hash algs
|
||||
continue
|
||||
}
|
||||
let msg = `Using private key: ${method.name}`
|
||||
if (method.hashAlg) {
|
||||
msg += ` (${method.hashAlg})`
|
||||
}
|
||||
this.emitServiceMessage(msg)
|
||||
const result = await this.ssh.authenticateWithKeyPair(this.authUsername, key, method.hashAlg)
|
||||
this.emitServiceMessage(`Trying private key: ${method.name}`)
|
||||
const result = await this.ssh.authenticateWithKeyPair(this.authUsername, key, null)
|
||||
if (result instanceof russh.AuthenticatedSSHClient) {
|
||||
return result
|
||||
}
|
||||
maybeSetRemainingMethods(result)
|
||||
} catch (e) {
|
||||
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to load private key ${method.name}: ${e}`)
|
||||
continue
|
||||
@ -556,6 +620,7 @@ export class SSHSession {
|
||||
|
||||
while (true) {
|
||||
if (state.state === 'failure') {
|
||||
maybeSetRemainingMethods(state)
|
||||
break
|
||||
}
|
||||
|
||||
@ -591,7 +656,7 @@ export class SSHSession {
|
||||
}
|
||||
}
|
||||
|
||||
state = await this.ssh .continueKeyboardInteractiveAuthentication(responses)
|
||||
state = await this.ssh.continueKeyboardInteractiveAuthentication(responses)
|
||||
|
||||
if (state instanceof russh.AuthenticatedSSHClient) {
|
||||
return state
|
||||
@ -604,6 +669,7 @@ export class SSHSession {
|
||||
if (result instanceof russh.AuthenticatedSSHClient) {
|
||||
return result
|
||||
}
|
||||
maybeSetRemainingMethods(result)
|
||||
} catch (e) {
|
||||
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to authenticate using agent: ${e}`)
|
||||
continue
|
||||
@ -622,7 +688,7 @@ export class SSHSession {
|
||||
reject()
|
||||
return
|
||||
}
|
||||
const channel = await this.ssh.openTCPForwardChannel({
|
||||
const channel = await this.ssh.activateChannel(await this.ssh.openTCPForwardChannel({
|
||||
addressToConnectTo: targetAddress,
|
||||
portToConnectTo: targetPort,
|
||||
originatorAddress: sourceAddress ?? '127.0.0.1',
|
||||
@ -631,7 +697,7 @@ export class SSHSession {
|
||||
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote has rejected the forwarded connection to ${targetAddress}:${targetPort} via ${fw}: ${err}`)
|
||||
reject()
|
||||
throw err
|
||||
})
|
||||
}))
|
||||
const socket = accept()
|
||||
channel.data$.subscribe(data => socket.write(data))
|
||||
socket.on('data', data => channel.write(Uint8Array.from(data)))
|
||||
@ -688,7 +754,7 @@ export class SSHSession {
|
||||
if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) {
|
||||
throw new Error('Cannot open shell channel before auth')
|
||||
}
|
||||
const ch = await this.ssh.openSessionChannel()
|
||||
const ch = await this.ssh.activateChannel(await this.ssh.openSessionChannel())
|
||||
await ch.requestPTY('xterm-256color', {
|
||||
columns: 80,
|
||||
rows: 24,
|
||||
@ -731,7 +797,12 @@ export class SSHSession {
|
||||
triedSavedPassphrase = true
|
||||
continue
|
||||
}
|
||||
if (e.toString() === 'Error: Keys(KeyIsEncrypted)' || e.toString() === 'Error: Keys(SshKey(Crypto))') {
|
||||
if ([
|
||||
'Error: Keys(KeyIsEncrypted)',
|
||||
'Error: Keys(SshKey(Ppk(Encrypted)))',
|
||||
'Error: Keys(SshKey(Ppk(IncorrectMac)))',
|
||||
'Error: Keys(SshKey(Crypto))',
|
||||
].includes(e.toString())) {
|
||||
await this.passwordStorage.deletePrivateKeyPassword(keyHash)
|
||||
|
||||
const modal = this.ngbModal.open(PromptModalComponent)
|
||||
|
@ -309,10 +309,16 @@ export class BaseTerminalTabComponent<P extends BaseTerminalProfile> extends Bas
|
||||
case 'scroll-to-top':
|
||||
this.frontend?.scrollToTop()
|
||||
break
|
||||
case 'scroll-up':
|
||||
case 'scroll-page-up':
|
||||
this.frontend?.scrollPages(-1)
|
||||
break
|
||||
case 'scroll-up':
|
||||
this.frontend?.scrollLines(-1)
|
||||
break
|
||||
case 'scroll-down':
|
||||
this.frontend?.scrollLines(1)
|
||||
break
|
||||
case 'scroll-page-down':
|
||||
this.frontend?.scrollPages(1)
|
||||
break
|
||||
case 'scroll-to-bottom':
|
||||
@ -536,7 +542,7 @@ export class BaseTerminalTabComponent<P extends BaseTerminalProfile> extends Bas
|
||||
}
|
||||
|
||||
if (!this.alternateScreenActive) {
|
||||
if (data.includes('\r') && this.config.store.terminal.warnOnMultilinePaste) {
|
||||
if ((data.includes('\r') || data.includes('\n')) && this.config.store.terminal.warnOnMultilinePaste) {
|
||||
const buttons = [
|
||||
this.translate.instant('Paste'),
|
||||
this.translate.instant('Cancel'),
|
||||
|
@ -6,6 +6,8 @@
|
||||
[class.text-danger]='state.resultCount == 0',
|
||||
(click)='$event.stopPropagation()',
|
||||
(keyup.enter)='findPrevious()',
|
||||
(keyup.up)='findPrevious()',
|
||||
(keyup.down)='findNext()',
|
||||
(keyup.esc)='close.emit()',
|
||||
[placeholder]='"Search"|translate'
|
||||
)
|
||||
|
@ -101,8 +101,10 @@ export class TerminalConfigProvider extends ConfigProvider {
|
||||
'⌘-⌥-Shift-I',
|
||||
],
|
||||
'scroll-to-top': ['Shift-PageUp'],
|
||||
'scroll-up': ['⌥-PageUp'],
|
||||
'scroll-down': ['⌥-PageDown'],
|
||||
'scroll-page-up': ['⌥-PageUp'],
|
||||
'scroll-up': ['Ctrl-Shift-Up'],
|
||||
'scroll-down': ['Ctrl-Shift-Down'],
|
||||
'scroll-page-down': ['⌥-PageDown'],
|
||||
'scroll-to-bottom': ['Shift-PageDown'],
|
||||
},
|
||||
},
|
||||
@ -152,8 +154,10 @@ export class TerminalConfigProvider extends ConfigProvider {
|
||||
'Ctrl-Alt-Shift-I',
|
||||
],
|
||||
'scroll-to-top': ['Ctrl-PageUp'],
|
||||
'scroll-up': ['Alt-PageUp'],
|
||||
'scroll-down': ['Alt-PageDown'],
|
||||
'scroll-page-up': ['Alt-PageUp'],
|
||||
'scroll-up': ['Ctrl-Shift-Up'],
|
||||
'scroll-down': ['Ctrl-Shift-Down'],
|
||||
'scroll-page-down': ['Alt-PageDown'],
|
||||
'scroll-to-bottom': ['Ctrl-PageDown'],
|
||||
},
|
||||
},
|
||||
@ -201,8 +205,10 @@ export class TerminalConfigProvider extends ConfigProvider {
|
||||
'Ctrl-Alt-Shift-I',
|
||||
],
|
||||
'scroll-to-top': ['Ctrl-PageUp'],
|
||||
'scroll-up': ['Alt-PageUp'],
|
||||
'scroll-down': ['Alt-PageDown'],
|
||||
'scroll-page-up': ['Alt-PageUp'],
|
||||
'scroll-up': ['Ctrl-Shift-Up'],
|
||||
'scroll-down': ['Ctrl-Shift-Down'],
|
||||
'scroll-page-down': ['Alt-PageDown'],
|
||||
'scroll-to-bottom': ['Ctrl-PageDown'],
|
||||
},
|
||||
},
|
||||
|
@ -77,6 +77,7 @@ export abstract class Frontend {
|
||||
abstract visualBell (): void
|
||||
|
||||
abstract scrollToTop (): void
|
||||
abstract scrollLines (amount: number): void
|
||||
abstract scrollPages (pages: number): void
|
||||
abstract scrollToBottom (): void
|
||||
|
||||
|
@ -357,6 +357,10 @@ export class XTermFrontend extends Frontend {
|
||||
this.xterm.scrollPages(pages)
|
||||
}
|
||||
|
||||
scrollLines (amount: number): void {
|
||||
this.xterm.scrollLines(amount)
|
||||
}
|
||||
|
||||
scrollToBottom (): void {
|
||||
this.xtermCore._scrollToBottom()
|
||||
}
|
||||
|
@ -86,11 +86,19 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
|
||||
name: this.translate.instant('Scroll terminal to top'),
|
||||
},
|
||||
{
|
||||
id: 'scroll-up',
|
||||
id: 'scroll-page-up',
|
||||
name: this.translate.instant('Scroll terminal one page up'),
|
||||
},
|
||||
{
|
||||
id: 'scroll-up',
|
||||
name: this.translate.instant('Scroll terminal one line up'),
|
||||
},
|
||||
{
|
||||
id: 'scroll-down',
|
||||
name: this.translate.instant('Scroll terminal one line down'),
|
||||
},
|
||||
{
|
||||
id: 'scroll-page-down',
|
||||
name: this.translate.instant('Scroll terminal one page down'),
|
||||
},
|
||||
{
|
||||
@ -113,3 +121,4 @@ export class TerminalHotkeyProvider extends HotkeyProvider {
|
||||
return this.hotkeys
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ import { TerminalContextMenuItemProvider } from './api/contextMenuProvider'
|
||||
import { TerminalColorSchemeProvider } from './api/colorSchemeProvider'
|
||||
import { TerminalSettingsTabProvider, AppearanceSettingsTabProvider, ColorSchemeSettingsTabProvider } from './settings'
|
||||
import { DebugDecorator } from './features/debug'
|
||||
import { PathDropDecorator } from './features/pathDrop'
|
||||
import { ZModemDecorator } from './features/zmodem'
|
||||
import { TerminalConfigProvider } from './config'
|
||||
import { TerminalHotkeyProvider } from './hotkeys'
|
||||
@ -54,7 +53,6 @@ import { DefaultColorSchemes } from './colorSchemes'
|
||||
|
||||
{ provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true },
|
||||
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
|
||||
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
|
||||
{ provide: TerminalDecorator, useClass: ZModemDecorator, multi: true },
|
||||
{ provide: TerminalDecorator, useClass: DebugDecorator, multi: true },
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { Subject, Observable } from 'rxjs'
|
||||
import { SessionMiddleware } from '../api/middleware'
|
||||
|
||||
const OSCPrefix = Buffer.from('\x1b]')
|
||||
const OSCSuffix = Buffer.from('\x07')
|
||||
const OSCSuffixes = [Buffer.from('\x07'), Buffer.from('\x1b\\')]
|
||||
|
||||
export class OSCProcessor extends SessionMiddleware {
|
||||
get cwdReported$ (): Observable<string> { return this.cwdReported }
|
||||
@ -14,11 +14,22 @@ export class OSCProcessor extends SessionMiddleware {
|
||||
|
||||
feedFromSession (data: Buffer): void {
|
||||
let startIndex = 0
|
||||
while (data.includes(OSCPrefix, startIndex) && data.includes(OSCSuffix, startIndex)) {
|
||||
const params = data.subarray(data.indexOf(OSCPrefix, startIndex) + OSCPrefix.length)
|
||||
const oscString = params.subarray(0, params.indexOf(OSCSuffix)).toString()
|
||||
while (data.includes(OSCPrefix, startIndex)) {
|
||||
const si = startIndex
|
||||
if (!OSCSuffixes.some(s => data.includes(s, si))) {
|
||||
break
|
||||
}
|
||||
|
||||
startIndex = data.indexOf(OSCSuffix, startIndex) + OSCSuffix.length
|
||||
const params = data.subarray(data.indexOf(OSCPrefix, startIndex) + OSCPrefix.length)
|
||||
|
||||
const [closesSuffix, closestSuffixIndex] = OSCSuffixes
|
||||
.map((suffix): [Buffer, number] => [suffix, params.indexOf(suffix)])
|
||||
.filter(([_, index]) => index !== -1)
|
||||
.sort(([_, a], [__, b]) => a - b)[0]
|
||||
|
||||
const oscString = params.subarray(0, closestSuffixIndex).toString()
|
||||
|
||||
startIndex = data.indexOf(closesSuffix, startIndex) + closesSuffix.length
|
||||
|
||||
const [oscCodeString, ...oscParams] = oscString.split(';')
|
||||
const oscCode = parseInt(oscCodeString)
|
||||
|
209
yarn.lock
209
yarn.lock
@ -321,16 +321,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
||||
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
|
||||
|
||||
"@electron/asar@3.2.17":
|
||||
version "3.2.17"
|
||||
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.17.tgz#91d28087aad80d1a1c8cc4e667c6476edf50f949"
|
||||
integrity sha512-OcWImUI686w8LkghQj9R2ynZ2ME693Ek6L1SiaAgqGKzBaTIZw3fHDqN82Rcl+EU1Gm9EgkJ5KLIY/q5DCRbbA==
|
||||
dependencies:
|
||||
commander "^5.0.0"
|
||||
glob "^7.1.6"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
"@electron/asar@^3.2.7":
|
||||
"@electron/asar@3.2.18", "@electron/asar@^3.2.7":
|
||||
version "3.2.18"
|
||||
resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.18.tgz#fa607f829209bab8b9e0ce6658d3fe81b2cba517"
|
||||
integrity sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==
|
||||
@ -363,21 +354,6 @@
|
||||
optionalDependencies:
|
||||
global-agent "^3.0.0"
|
||||
|
||||
"@electron/node-gyp@git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2":
|
||||
version "10.2.0-electron.1"
|
||||
resolved "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2"
|
||||
dependencies:
|
||||
env-paths "^2.2.0"
|
||||
exponential-backoff "^3.1.1"
|
||||
glob "^8.1.0"
|
||||
graceful-fs "^4.2.6"
|
||||
make-fetch-happen "^10.2.1"
|
||||
nopt "^6.0.0"
|
||||
proc-log "^2.0.1"
|
||||
semver "^7.3.5"
|
||||
tar "^6.2.1"
|
||||
which "^2.0.2"
|
||||
|
||||
"@electron/node-gyp@https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2":
|
||||
version "10.2.0-electron.1"
|
||||
resolved "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2"
|
||||
@ -442,20 +418,20 @@
|
||||
tar "^6.0.5"
|
||||
yargs "^17.0.1"
|
||||
|
||||
"@electron/rebuild@^3.7.1":
|
||||
version "3.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/rebuild/-/rebuild-3.7.1.tgz#27ed124f7f1dbed92b222aabe68c0e4a3e6c5cea"
|
||||
integrity sha512-sKGD+xav4Gh25+LcLY0rjIwcCFTw+f/HU1pB48UVbwxXXRGaXEqIH0AaYKN46dgd/7+6kuiDXzoyAEvx1zCsdw==
|
||||
"@electron/rebuild@^4":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@electron/rebuild/-/rebuild-4.0.1.tgz#0620d5bb71a0b8b09a86fb9fa979244e1fcc10bf"
|
||||
integrity sha512-iMGXb6Ib7H/Q3v+BKZJoETgF9g6KMNZVbsO4b7Dmpgb5qTFqyFTzqW9F3TOSHdybv2vKYKzSS9OiZL+dcJb+1Q==
|
||||
dependencies:
|
||||
"@electron/node-gyp" "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2"
|
||||
"@malept/cross-spawn-promise" "^2.0.0"
|
||||
chalk "^4.0.0"
|
||||
debug "^4.1.1"
|
||||
detect-libc "^2.0.1"
|
||||
fs-extra "^10.0.0"
|
||||
got "^11.7.0"
|
||||
node-abi "^3.45.0"
|
||||
node-api-version "^0.2.0"
|
||||
graceful-fs "^4.2.11"
|
||||
node-abi "^4.2.0"
|
||||
node-api-version "^0.2.1"
|
||||
node-gyp "^11.2.0"
|
||||
ora "^5.1.0"
|
||||
read-binary-file-arch "^1.0.6"
|
||||
semver "^7.3.5"
|
||||
@ -985,12 +961,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe"
|
||||
integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==
|
||||
|
||||
"@types/node@^20.9.0":
|
||||
version "20.14.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.14.tgz#6b655d4a88623b0edb98300bb9dd2107225f885e"
|
||||
integrity sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==
|
||||
"@types/node@^22.7.7":
|
||||
version "22.15.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.21.tgz#196ef14fe20d87f7caf1e7b39832767f9a995b77"
|
||||
integrity sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
undici-types "~6.21.0"
|
||||
|
||||
"@types/parse5@^5":
|
||||
version "5.0.3"
|
||||
@ -1500,13 +1476,13 @@ app-builder-bin@5.0.0-alpha.12:
|
||||
resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz#2daf82f8badc698e0adcc95ba36af4ff0650dc80"
|
||||
integrity sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==
|
||||
|
||||
app-builder-lib@26.0.0-alpha.8:
|
||||
version "26.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-26.0.0-alpha.8.tgz#2ecc525692fa300414e4906dc001adec0ecb2567"
|
||||
integrity sha512-IvvGAa/RXwuNPiSckIBPjBxI4et8PWb+TsJnhKa/XtxOH64ncs6hDtV7bSxIeUmbvUj3R8dm32dej7UO0Cgtng==
|
||||
app-builder-lib@26.0.12:
|
||||
version "26.0.12"
|
||||
resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-26.0.12.tgz#2e33df936e0f78d4266b058ece90308ea981eefb"
|
||||
integrity sha512-+/CEPH1fVKf6HowBUs6LcAIoRcjeqgvAeoSE+cl7Y7LndyQ9ViGPYibNk7wmhMHzNgHIuIbw4nWADPO+4mjgWw==
|
||||
dependencies:
|
||||
"@develar/schema-utils" "~2.6.5"
|
||||
"@electron/asar" "3.2.17"
|
||||
"@electron/asar" "3.2.18"
|
||||
"@electron/fuses" "^1.8.0"
|
||||
"@electron/notarize" "2.5.0"
|
||||
"@electron/osx-sign" "1.3.1"
|
||||
@ -1515,16 +1491,15 @@ app-builder-lib@26.0.0-alpha.8:
|
||||
"@malept/flatpak-bundler" "^0.4.0"
|
||||
"@types/fs-extra" "9.0.13"
|
||||
async-exit-hook "^2.0.1"
|
||||
bluebird-lst "^1.0.9"
|
||||
builder-util "26.0.0-alpha.8"
|
||||
builder-util-runtime "9.3.0-alpha.0"
|
||||
builder-util "26.0.11"
|
||||
builder-util-runtime "9.3.1"
|
||||
chromium-pickle-js "^0.2.0"
|
||||
config-file-ts "0.2.8-rc1"
|
||||
debug "^4.3.4"
|
||||
dotenv "^16.4.5"
|
||||
dotenv-expand "^11.0.6"
|
||||
ejs "^3.1.8"
|
||||
electron-publish "26.0.0-alpha.8"
|
||||
electron-publish "26.0.11"
|
||||
fs-extra "^10.1.0"
|
||||
hosted-git-info "^4.1.0"
|
||||
is-ci "^3.0.0"
|
||||
@ -1533,10 +1508,12 @@ app-builder-lib@26.0.0-alpha.8:
|
||||
json5 "^2.2.3"
|
||||
lazy-val "^1.0.5"
|
||||
minimatch "^10.0.0"
|
||||
plist "3.1.0"
|
||||
resedit "^1.7.0"
|
||||
semver "^7.3.8"
|
||||
tar "^6.1.12"
|
||||
temp-file "^3.4.0"
|
||||
tiny-async-pool "1.3.0"
|
||||
|
||||
apply-loader@2.0.0:
|
||||
version "2.0.0"
|
||||
@ -1868,14 +1845,7 @@ block-stream@*:
|
||||
dependencies:
|
||||
inherits "~2.0.0"
|
||||
|
||||
bluebird-lst@^1.0.9:
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz#a64a0e4365658b9ab5fe875eb9dfb694189bb41c"
|
||||
integrity sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==
|
||||
dependencies:
|
||||
bluebird "^3.5.5"
|
||||
|
||||
bluebird@^3.5.0, bluebird@^3.5.5:
|
||||
bluebird@^3.5.0:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
||||
@ -2046,26 +2016,25 @@ buffer@^5.1.0:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
|
||||
builder-util-runtime@9.3.0-alpha.0:
|
||||
version "9.3.0-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.3.0-alpha.0.tgz#c4639ae24a74d2e0f4eb324100af3040300bae62"
|
||||
integrity sha512-EriE6Uf15niqdkyjBOS09OrXlhEV0HKhnATlI9n63vCoisnvvRTQNgoR2MV9vnBmNGhavBPZXPWPItv4QMDVfw==
|
||||
builder-util-runtime@9.3.1:
|
||||
version "9.3.1"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.3.1.tgz#0daedde0f6d381f2a00a50a407b166fe7dca1a67"
|
||||
integrity sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==
|
||||
dependencies:
|
||||
debug "^4.3.4"
|
||||
sax "^1.2.4"
|
||||
|
||||
builder-util@26.0.0-alpha.8:
|
||||
version "26.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-26.0.0-alpha.8.tgz#83369a6b66d8659281d72ae41102caee7dd6f0f9"
|
||||
integrity sha512-qQLArPCYUvlx1Ess7Bwsdbx7F4lnPRZBMOeoVuofcdBWIg1HbGdgYp9I0VNcD2O9D2+lVUHI1gSkCj03oRXRnQ==
|
||||
builder-util@26.0.11:
|
||||
version "26.0.11"
|
||||
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-26.0.11.tgz#ad85b92c93f2b976b973e1d87337e0c6813fcb8f"
|
||||
integrity sha512-xNjXfsldUEe153h1DraD0XvDOpqGR0L5eKFkdReB7eFW5HqysDZFfly4rckda6y9dF39N3pkPlOblcfHKGw+uA==
|
||||
dependencies:
|
||||
"7zip-bin" "~5.2.0"
|
||||
"@types/debug" "^4.1.6"
|
||||
app-builder-bin "5.0.0-alpha.12"
|
||||
bluebird-lst "^1.0.9"
|
||||
builder-util-runtime "9.3.0-alpha.0"
|
||||
builder-util-runtime "9.3.1"
|
||||
chalk "^4.1.2"
|
||||
cross-spawn "^7.0.3"
|
||||
cross-spawn "^7.0.6"
|
||||
debug "^4.3.4"
|
||||
fs-extra "^10.1.0"
|
||||
http-proxy-agent "^7.0.0"
|
||||
@ -2076,6 +2045,7 @@ builder-util@26.0.0-alpha.8:
|
||||
source-map-support "^0.5.19"
|
||||
stat-mode "^1.0.0"
|
||||
temp-file "^3.4.0"
|
||||
tiny-async-pool "1.3.0"
|
||||
|
||||
builtin-modules@^1.0.0:
|
||||
version "1.1.1"
|
||||
@ -2725,7 +2695,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-spawn@^7.0.0:
|
||||
cross-spawn@^7.0.0, cross-spawn@^7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||
@ -2962,14 +2932,14 @@ dir-glob@^3.0.1:
|
||||
dependencies:
|
||||
path-type "^4.0.0"
|
||||
|
||||
dmg-builder@26.0.0-alpha.8:
|
||||
version "26.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-26.0.0-alpha.8.tgz#d9766adef7124fbaa21300256b53c4bf6c89f62b"
|
||||
integrity sha512-H53RkHuUBIgiZtTTdjGigD5BVKYoH6t7Y+ZNmjdzMuptL6rCni7K0mrqvVycCkYRvdeM8BWZeUvw4iOwRQIhmQ==
|
||||
dmg-builder@26.0.12:
|
||||
version "26.0.12"
|
||||
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-26.0.12.tgz#6996ad0bab80a861c9a7b33ee9734d4f60566b46"
|
||||
integrity sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==
|
||||
dependencies:
|
||||
app-builder-lib "26.0.0-alpha.8"
|
||||
builder-util "26.0.0-alpha.8"
|
||||
builder-util-runtime "9.3.0-alpha.0"
|
||||
app-builder-lib "26.0.12"
|
||||
builder-util "26.0.11"
|
||||
builder-util-runtime "9.3.1"
|
||||
fs-extra "^10.1.0"
|
||||
iconv-lite "^0.6.2"
|
||||
js-yaml "^4.1.0"
|
||||
@ -3091,16 +3061,16 @@ ejs@^3.1.8:
|
||||
dependencies:
|
||||
jake "^10.8.5"
|
||||
|
||||
electron-builder@^26.0.0-alpha.8:
|
||||
version "26.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-26.0.0-alpha.8.tgz#61529b4e977deedf2612a5ca152ee14f69a2aba2"
|
||||
integrity sha512-sx9ObBOEPiHdmDkTRehZWZG2Z26dL6v+Ue3PMji6bj6q5EwY+3h8Q0qZk5JEvUYO2LRuGFbkYpnzdOZrbxRd7A==
|
||||
electron-builder@^26.0:
|
||||
version "26.0.12"
|
||||
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-26.0.12.tgz#797af2e70efdd96c9ea5d8a8164b8728c90d65ff"
|
||||
integrity sha512-cD1kz5g2sgPTMFHjLxfMjUK5JABq3//J4jPswi93tOPFz6btzXYtK5NrDt717NRbukCUDOrrvmYVOWERlqoiXA==
|
||||
dependencies:
|
||||
app-builder-lib "26.0.0-alpha.8"
|
||||
builder-util "26.0.0-alpha.8"
|
||||
builder-util-runtime "9.3.0-alpha.0"
|
||||
app-builder-lib "26.0.12"
|
||||
builder-util "26.0.11"
|
||||
builder-util-runtime "9.3.1"
|
||||
chalk "^4.1.2"
|
||||
dmg-builder "26.0.0-alpha.8"
|
||||
dmg-builder "26.0.12"
|
||||
fs-extra "^10.1.0"
|
||||
is-ci "^3.0.0"
|
||||
lazy-val "^1.0.5"
|
||||
@ -3190,14 +3160,14 @@ electron-localshortcut@^3.1.0:
|
||||
keyboardevent-from-electron-accelerator "^2.0.0"
|
||||
keyboardevents-areequal "^0.2.1"
|
||||
|
||||
electron-publish@26.0.0-alpha.8:
|
||||
version "26.0.0-alpha.8"
|
||||
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-26.0.0-alpha.8.tgz#8e2234905e3a1a1909170543b159283600919562"
|
||||
integrity sha512-IGHPQkfSL+LYAIiqJ2E1mVTxNPFh4XRvQ+OPmBFrgpZrR32NrMlxssUyx1B0N1bGLjevjBMMitlwKFNM5WPnXg==
|
||||
electron-publish@26.0.11:
|
||||
version "26.0.11"
|
||||
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-26.0.11.tgz#92c9329a101af2836d9d228c82966eca1eee9a7b"
|
||||
integrity sha512-a8QRH0rAPIWH9WyyS5LbNvW9Ark6qe63/LqDB7vu2JXYpi0Gma5Q60Dh4tmTqhOBQt0xsrzD8qE7C+D7j+B24A==
|
||||
dependencies:
|
||||
"@types/fs-extra" "^9.0.11"
|
||||
builder-util "26.0.0-alpha.8"
|
||||
builder-util-runtime "9.3.0-alpha.0"
|
||||
builder-util "26.0.11"
|
||||
builder-util-runtime "9.3.1"
|
||||
chalk "^4.1.2"
|
||||
form-data "^4.0.0"
|
||||
fs-extra "^10.1.0"
|
||||
@ -3209,13 +3179,13 @@ electron-to-chromium@^1.4.284:
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.286.tgz#0e039de59135f44ab9a8ec9025e53a9135eba11f"
|
||||
integrity sha512-Vp3CVhmYpgf4iXNKAucoQUDcCrBQX3XLBtwgFqP9BUXuucgvAV9zWp1kYU7LL9j4++s9O+12cb3wMtN4SJy6UQ==
|
||||
|
||||
electron@^32.2.7:
|
||||
version "32.2.7"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-32.2.7.tgz#4eaf78534c99ba060da5bb002c737206be33f225"
|
||||
integrity sha512-y8jbQRG3xogF70XPlk5c+dWe5iRfUBo28o2NMpKd/CcW7ENIaWtBlGima8/8nmRdAaYTy1+yIt6KB0Lon9H8cA==
|
||||
electron@^36.3:
|
||||
version "36.3.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-36.3.1.tgz#12a8c1b1cd9163a4bd0cb60f89816243b26ab788"
|
||||
integrity sha512-LeOZ+tVahmctHaAssLCGRRUa2SAO09GXua3pKdG+WzkbSDMh+3iOPONNVPTqGp8HlWnzGj4r6mhsIbM2RgH+eQ==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^20.9.0"
|
||||
"@types/node" "^22.7.7"
|
||||
extract-zip "^2.0.1"
|
||||
|
||||
elliptic@^6.5.3:
|
||||
@ -4463,7 +4433,7 @@ got@^6.7.1:
|
||||
unzip-response "^2.0.1"
|
||||
url-parse-lax "^1.0.0"
|
||||
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9, graceful-fs@~4.1.11:
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9, graceful-fs@~4.1.11:
|
||||
version "4.2.10"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||
@ -6246,10 +6216,10 @@ mute-stream@~0.0.4:
|
||||
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz"
|
||||
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
|
||||
|
||||
nan@2.22.0:
|
||||
version "2.22.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3"
|
||||
integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==
|
||||
nan@2.22.2:
|
||||
version "2.22.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb"
|
||||
integrity sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==
|
||||
|
||||
nanoid@^3.3.4:
|
||||
version "3.3.4"
|
||||
@ -6291,12 +6261,12 @@ no-case@^3.0.4:
|
||||
lower-case "^2.0.2"
|
||||
tslib "^2.0.3"
|
||||
|
||||
node-abi@^3.45.0, node-abi@^3.71.0:
|
||||
version "3.71.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.71.0.tgz#52d84bbcd8575efb71468fbaa1f9a49b2c242038"
|
||||
integrity sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==
|
||||
node-abi@4.9.0, node-abi@^3.45.0, node-abi@^4, node-abi@^4.2.0:
|
||||
version "4.9.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-4.9.0.tgz#ca6dabf7991e54bf3ba6d8d32641e1b84f305263"
|
||||
integrity sha512-0isb3h+AXUblx5Iv0mnYy2WsErH+dk2e9iXJXdKAtS076Q5hP+scQhp6P4tvDeVlOBlG3ROKvkpQHtbORllq2A==
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
semver "^7.6.3"
|
||||
|
||||
node-addon-api@^1.6.3:
|
||||
version "1.7.2"
|
||||
@ -6315,6 +6285,13 @@ node-api-version@^0.2.0:
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
node-api-version@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.2.1.tgz#19bad54f6d65628cbee4e607a325e4488ace2de9"
|
||||
integrity sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
node-fetch-npm@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.1.tgz"
|
||||
@ -6336,7 +6313,7 @@ node-gyp-build@^4.2.1:
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
|
||||
integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==
|
||||
|
||||
node-gyp@^10.0.0, node-gyp@~3.6.2:
|
||||
node-gyp@^10.0.0, node-gyp@^11.2.0, node-gyp@~3.6.2:
|
||||
version "10.3.1"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.3.1.tgz#1dd1a1a1c6c5c59da1a76aea06a062786b2c8a1a"
|
||||
integrity sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==
|
||||
@ -7177,7 +7154,7 @@ pkg-up@^2.0.0:
|
||||
dependencies:
|
||||
find-up "^2.1.0"
|
||||
|
||||
plist@^3.0.4, plist@^3.0.5, plist@^3.1.0:
|
||||
plist@3.1.0, plist@^3.0.4, plist@^3.0.5, plist@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9"
|
||||
integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==
|
||||
@ -8167,7 +8144,7 @@ semver@^6.2.0, semver@^6.3.1:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.0.0, semver@^7.1.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.8:
|
||||
semver@^7.0.0, semver@^7.1.1, semver@^7.3.4, semver@^7.3.8:
|
||||
version "7.3.8"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
|
||||
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
|
||||
@ -8181,6 +8158,11 @@ semver@^7.3.2, semver@^7.5.3, semver@^7.5.4:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^7.3.5, semver@^7.6.3:
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
|
||||
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
|
||||
|
||||
semver@~5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
|
||||
@ -8965,6 +8947,13 @@ timed-out@^4.0.0:
|
||||
resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz"
|
||||
integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=
|
||||
|
||||
tiny-async-pool@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz#c013e1b369095e7005db5595f95e646cca6ef8a5"
|
||||
integrity sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==
|
||||
dependencies:
|
||||
semver "^5.5.0"
|
||||
|
||||
tinyqueue@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08"
|
||||
@ -9250,10 +9239,10 @@ unbox-primitive@^1.0.2:
|
||||
has-symbols "^1.0.3"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
|
||||
undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
undici-types@~6.21.0:
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
|
||||
integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
|
||||
|
||||
unique-filename@^1.1.0, unique-filename@~1.1.0:
|
||||
version "1.1.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user