mirror of
https://github.com/Eugeny/tabby.git
synced 2025-09-15 12:54:50 +00:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4efcf1bbd6 | ||
![]() |
abea964d94 | ||
![]() |
2e7b66ac60 | ||
![]() |
c4a514fc4a | ||
![]() |
d525374061 | ||
![]() |
6db08b765f | ||
![]() |
d8d346c507 | ||
![]() |
6ffeb61c9c | ||
![]() |
92c729dada | ||
![]() |
302f88058c | ||
![]() |
66c173b1b5 | ||
![]() |
f9dadf0816 |
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@@ -130,7 +130,7 @@ jobs:
|
|||||||
path: artifact-zip
|
path: artifact-zip
|
||||||
|
|
||||||
Linux-Build:
|
Linux-Build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ${{matrix.os}}
|
||||||
needs: Lint
|
needs: Lint
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -138,14 +138,17 @@ jobs:
|
|||||||
- build-arch: x64
|
- build-arch: x64
|
||||||
arch: amd64
|
arch: amd64
|
||||||
rust_triple: x86_64-unknown-linux-gnu
|
rust_triple: x86_64-unknown-linux-gnu
|
||||||
|
os: ubuntu-24.04
|
||||||
- build-arch: arm64
|
- build-arch: arm64
|
||||||
arch: arm64
|
arch: arm64
|
||||||
rust_triple: aarch64-unknown-linux-gnu
|
rust_triple: aarch64-unknown-linux-gnu
|
||||||
triplet: aarch64-linux-gnu-
|
triplet: aarch64-linux-gnu-
|
||||||
|
os: ubuntu-24.04-arm
|
||||||
- build-arch: arm
|
- build-arch: arm
|
||||||
arch: armhf
|
arch: armhf
|
||||||
rust_triple: arm-unknown-linux-gnueabihf
|
rust_triple: arm-unknown-linux-gnueabihf
|
||||||
triplet: arm-linux-gnueabihf-
|
triplet: arm-linux-gnueabihf-
|
||||||
|
os: ubuntu-24.04
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@@ -167,6 +170,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 22
|
||||||
|
|
||||||
|
- name: Install FPM
|
||||||
|
run: |
|
||||||
|
sudo gem install fpm
|
||||||
|
|
||||||
- run: rustup target add ${{matrix.rust_triple}}
|
- run: rustup target add ${{matrix.rust_triple}}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
@@ -176,12 +183,12 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup tar to run as root
|
- name: Setup tar to run as root
|
||||||
run: sudo chmod u+s "$(command -v tar)"
|
run: sudo chmod u+s "$(command -v tar)"
|
||||||
if: matrix.build-arch != 'x64'
|
if: matrix.build-arch == 'arm'
|
||||||
|
|
||||||
- name: Download cached sysroot
|
- name: Download cached sysroot
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
id: dl-cached-sysroot
|
id: dl-cached-sysroot
|
||||||
if: matrix.build-arch !='x64'
|
if: matrix.build-arch == 'arm'
|
||||||
with:
|
with:
|
||||||
key: sysroot-${{matrix.build-arch}}
|
key: sysroot-${{matrix.build-arch}}
|
||||||
path: /${{matrix.build-arch}}-sysroot
|
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 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 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' {} \; ;
|
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
|
- name: Setup env to use ${{matrix.build-arch}} sysroot
|
||||||
run: |
|
run: |
|
||||||
@@ -206,9 +213,9 @@ jobs:
|
|||||||
elif [[ ${{matrix.arch}} == 'arm64' ]]; then
|
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
|
echo "PKG_CONFIG_PATH=/${{matrix.build-arch}}-sysroot/usr/lib/pkgconfig/:/${{matrix.build-arch}}-sysroot/usr/lib/aarch64-linux-gnu/pkgconfig/" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
if: matrix.build-arch != 'x64'
|
if: matrix.build-arch == 'arm'
|
||||||
|
|
||||||
- name: Install npm_modules (amd64)
|
- name: Install npm_modules (native)
|
||||||
run: |
|
run: |
|
||||||
npm i -g yarn node-gyp
|
npm i -g yarn node-gyp
|
||||||
yarn --network-timeout 1000000 --arch=${{matrix.build-arch}} --target-arch=${{matrix.build-arch}}
|
yarn --network-timeout 1000000 --arch=${{matrix.build-arch}} --target-arch=${{matrix.build-arch}}
|
||||||
@@ -225,6 +232,7 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }}
|
KEYGEN_TOKEN: ${{ secrets.KEYGEN_TOKEN }}
|
||||||
USE_HARD_LINKS: false
|
USE_HARD_LINKS: false
|
||||||
|
USE_SYSTEM_FPM: true
|
||||||
# DEBUG: electron-builder,electron-builder:*
|
# DEBUG: electron-builder,electron-builder:*
|
||||||
|
|
||||||
- name: Build web resources (amd64 only)
|
- name: Build web resources (amd64 only)
|
||||||
|
@@ -144,7 +144,7 @@ Plugins and themes can be installed directly from the Settings view inside Tabby
|
|||||||
|
|
||||||
# Sponsors <!-- omit in toc -->
|
# 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
|
[**packagecloud**](https://packagecloud.io) has provided free Debian/RPM repository hosting
|
||||||
|
|
||||||
@@ -152,7 +152,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
|
[**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
|
[**IQ Hive**](https://iqhive.com) is providing financial support for the project development
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import * as glasstron from 'glasstron'
|
import * as glasstron from 'glasstron'
|
||||||
import { autoUpdater } from 'electron-updater'
|
import { autoUpdater } from 'electron-updater'
|
||||||
import { Subject, Observable, debounceTime } from 'rxjs'
|
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 ElectronConfig = require('electron-config')
|
||||||
import { enable as enableRemote } from '@electron/remote/main'
|
import { enable as enableRemote } from '@electron/remote/main'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
@@ -100,6 +100,10 @@ export class Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
bwOptions.visualEffectState = 'active'
|
||||||
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
this.window = new BrowserWindow(bwOptions) as GlasstronWindow
|
this.window = new BrowserWindow(bwOptions) as GlasstronWindow
|
||||||
} else {
|
} else {
|
||||||
@@ -115,6 +119,8 @@ export class Window {
|
|||||||
this.setVibrancy(true)
|
this.setVibrancy(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setDarkMode(this.configStore.appearance?.colorSchemeMode ?? 'dark')
|
||||||
|
|
||||||
if (!options.hidden) {
|
if (!options.hidden) {
|
||||||
if (maximized) {
|
if (maximized) {
|
||||||
this.window.maximize()
|
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 {
|
focus (): void {
|
||||||
this.window.focus()
|
this.window.focus()
|
||||||
}
|
}
|
||||||
@@ -373,6 +391,10 @@ export class Window {
|
|||||||
this.setVibrancy(enabled, type)
|
this.setVibrancy(enabled, type)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.on('window-set-dark-mode', (_, mode) => {
|
||||||
|
this.setDarkMode(mode)
|
||||||
|
})
|
||||||
|
|
||||||
this.on('window-set-window-controls-color', (_, theme) => {
|
this.on('window-set-window-controls-color', (_, theme) => {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
const symbolColor: string = theme.foreground
|
const symbolColor: string = theme.foreground
|
||||||
|
@@ -30,7 +30,7 @@
|
|||||||
"native-process-working-directory": "^1.0.2",
|
"native-process-working-directory": "^1.0.2",
|
||||||
"npm": "6",
|
"npm": "6",
|
||||||
"rxjs": "^7.5.7",
|
"rxjs": "^7.5.7",
|
||||||
"russh": "0.1.11",
|
"russh": "0.1.14",
|
||||||
"source-map-support": "^0.5.20",
|
"source-map-support": "^0.5.20",
|
||||||
"v8-compile-cache": "^2.3.0",
|
"v8-compile-cache": "^2.3.0",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
|
@@ -3628,10 +3628,10 @@ run-queue@^1.0.0, run-queue@^1.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
aproba "^1.1.1"
|
aproba "^1.1.1"
|
||||||
|
|
||||||
russh@0.1.11:
|
russh@0.1.14:
|
||||||
version "0.1.11"
|
version "0.1.14"
|
||||||
resolved "https://registry.yarnpkg.com/russh/-/russh-0.1.11.tgz#22e74f93ec1cb045930c85f8ba1daf2e5efcae4b"
|
resolved "https://registry.yarnpkg.com/russh/-/russh-0.1.14.tgz#d97a6435795f97693414c55c93b823bbbbe34465"
|
||||||
integrity sha512-3CuI+rMoGpnnFDJxsEmcHYRSHInf3bz3fbgeyPnX8n1wgsX6wdbyI1DKL188oQlsrFWEjO3/7ebbYliCi0Qz7w==
|
integrity sha512-x8n5P/QVm4yuRqRScxbjTt3RRLVLwUGC87OBXrZBOTEjPikGwyy/kcBGf2PjlV8iJ3M8JOro4b1xHj1JM8Khfg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@napi-rs/cli" "^2.18.3"
|
"@napi-rs/cli" "^2.18.3"
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@ export class ThemesService {
|
|||||||
) {
|
) {
|
||||||
this.rootElementStyleBackup = document.documentElement.style.cssText
|
this.rootElementStyleBackup = document.documentElement.style.cssText
|
||||||
this.applyTheme(standardTheme)
|
this.applyTheme(standardTheme)
|
||||||
|
this.applyThemeVariables()
|
||||||
config.ready$.toPromise().then(() => {
|
config.ready$.toPromise().then(() => {
|
||||||
this.applyCurrentTheme()
|
this.applyCurrentTheme()
|
||||||
this.applyThemeVariables()
|
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 () {
|
private applyThemeVariables () {
|
||||||
if (!this.findCurrentTheme().followsColorScheme) {
|
if (!this.findCurrentTheme().followsColorScheme) {
|
||||||
document.documentElement.style.cssText = this.rootElementStyleBackup
|
document.documentElement.style.cssText = this.rootElementStyleBackup
|
||||||
@@ -60,7 +66,7 @@ export class ThemesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let background = Color(theme.background)
|
let background = Color(theme.background)
|
||||||
if (this.config.store?.appearance.vibrancy) {
|
if (this.getConfigStoreOrDefaults().appearance.vibrancy) {
|
||||||
background = background.fade(0.6)
|
background = background.fade(0.6)
|
||||||
}
|
}
|
||||||
// const background = theme.background
|
// 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['--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) {
|
for (const [bg, fg] of contrastPairs) {
|
||||||
const colorBg = Color(vars[bg]).hsl()
|
const colorBg = Color(vars[bg]).hsl()
|
||||||
const colorFg = Color(vars[fg]).hsl()
|
const colorFg = Color(vars[fg]).hsl()
|
||||||
const bgContrast = colorBg.contrast(colorFg)
|
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()
|
vars[fg] = this.ensureContrast(colorFg, colorBg).string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +169,7 @@ export class ThemesService {
|
|||||||
document.documentElement.style.setProperty(key, value)
|
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 {
|
private ensureContrast (color: Color, against: Color): Color {
|
||||||
@@ -178,7 +184,7 @@ export class ThemesService {
|
|||||||
while (
|
while (
|
||||||
(step < 1 && color.color[2] > 1 ||
|
(step < 1 && color.color[2] > 1 ||
|
||||||
step > 1 && color.color[2] < 99) &&
|
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
|
color.color[2] *= step
|
||||||
}
|
}
|
||||||
return color
|
return color
|
||||||
@@ -189,22 +195,22 @@ export class ThemesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findCurrentTheme (): Theme {
|
findCurrentTheme (): Theme {
|
||||||
return this.findTheme(this.config.store.appearance.theme) ?? this.standardTheme
|
return this.findTheme(this.getConfigStoreOrDefaults().appearance.theme) ?? this.standardTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @hidden
|
/// @hidden
|
||||||
_getActiveColorScheme (): any {
|
_getActiveColorScheme (): any {
|
||||||
let theme: PlatformTheme = 'dark'
|
let theme: PlatformTheme = 'dark'
|
||||||
if (this.config.store.appearance.colorSchemeMode === 'light') {
|
if (this.getConfigStoreOrDefaults().appearance.colorSchemeMode === 'light') {
|
||||||
theme = 'light'
|
theme = 'light'
|
||||||
} else if (this.config.store.appearance.colorSchemeMode === 'auto') {
|
} else if (this.getConfigStoreOrDefaults().appearance.colorSchemeMode === 'auto') {
|
||||||
theme = this.platform.getTheme()
|
theme = this.platform.getTheme()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme === 'light') {
|
if (theme === 'light') {
|
||||||
return this.config.store.terminal.lightColorScheme
|
return this.getConfigStoreOrDefaults().terminal.lightColorScheme
|
||||||
} else {
|
} 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)
|
document.querySelector('head')!.appendChild(this.styleElement)
|
||||||
}
|
}
|
||||||
this.styleElement.textContent = theme.css
|
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)
|
this.themeChanged.next(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { NgModule } from '@angular/core'
|
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 { 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 { SFTPContextMenuItemProvider, SSHProfileImporter, AutoPrivateKeyLocator } from 'tabby-ssh'
|
||||||
import { PTYInterface, ShellProvider, UACService } from 'tabby-local'
|
import { PTYInterface, ShellProvider, UACService } from 'tabby-local'
|
||||||
import { auditTime } from 'rxjs'
|
import { auditTime } from 'rxjs'
|
||||||
@@ -23,6 +23,7 @@ import { ElectronConfigProvider } from './config'
|
|||||||
import { EditSFTPContextMenu } from './sftpContextMenu'
|
import { EditSFTPContextMenu } from './sftpContextMenu'
|
||||||
import { OpenSSHImporter, PrivateKeyLocator, StaticFileImporter } from './sshImporters'
|
import { OpenSSHImporter, PrivateKeyLocator, StaticFileImporter } from './sshImporters'
|
||||||
import { ElectronPTYInterface } from './pty'
|
import { ElectronPTYInterface } from './pty'
|
||||||
|
import { PathDropDecorator } from './pathDrop'
|
||||||
|
|
||||||
import { CmderShellProvider } from './shells/cmder'
|
import { CmderShellProvider } from './shells/cmder'
|
||||||
import { Cygwin32ShellProvider } from './shells/cygwin32'
|
import { Cygwin32ShellProvider } from './shells/cygwin32'
|
||||||
@@ -73,6 +74,8 @@ import { VSDevToolsProvider } from './shells/vs'
|
|||||||
|
|
||||||
{ provide: PTYInterface, useClass: ElectronPTYInterface },
|
{ provide: PTYInterface, useClass: ElectronPTYInterface },
|
||||||
|
|
||||||
|
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
|
||||||
|
|
||||||
// For WindowsDefaultShellProvider
|
// For WindowsDefaultShellProvider
|
||||||
PowerShellCoreShellProvider,
|
PowerShellCoreShellProvider,
|
||||||
WSLShellProvider,
|
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())
|
config.changed$.subscribe(() => this.updateWindowControlsColor())
|
||||||
|
|
||||||
@@ -173,6 +179,11 @@ export default class ElectronModule {
|
|||||||
this.hostWindow.setOpacity(this.config.store.appearance.opacity)
|
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 () {
|
private updateWindowControlsColor () {
|
||||||
// if windows and not using native frame, WCO does not exist, return.
|
// if windows and not using native frame, WCO does not exist, return.
|
||||||
if (this.hostApp.platform === Platform.Windows && this.config.store.appearance.frame === 'native') {
|
if (this.hostApp.platform === Platform.Windows && this.config.store.appearance.frame === 'native') {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { TerminalDecorator } from '../api/decorator'
|
import { TerminalDecorator, BaseTerminalTabComponent } from 'tabby-terminal'
|
||||||
import { BaseTerminalTabComponent } from '../api/baseTerminalTab.component'
|
import { webUtils } from 'electron'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -11,8 +11,8 @@ export class PathDropDecorator extends TerminalDecorator {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}))
|
}))
|
||||||
this.subscribeUntilDetached(terminal, terminal.frontend?.drop$.subscribe((event: DragEvent) => {
|
this.subscribeUntilDetached(terminal, terminal.frontend?.drop$.subscribe((event: DragEvent) => {
|
||||||
for (const file of event.dataTransfer!.files as any) {
|
for (const file of event.dataTransfer!.files as unknown as Iterable<File>) {
|
||||||
this.injectPath(terminal, file.path)
|
this.injectPath(terminal, webUtils.getPathForFile(file))
|
||||||
}
|
}
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}))
|
}))
|
@@ -50,6 +50,18 @@ type AuthMethod = {
|
|||||||
kind: 'pageant',
|
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 {
|
export class KeyboardInteractivePrompt {
|
||||||
readonly responses: string[] = []
|
readonly responses: string[] = []
|
||||||
|
|
||||||
@@ -181,6 +193,13 @@ export class SSHSession {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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.remainingAuthMethods.push({ type: 'keyboard-interactive' })
|
||||||
|
}
|
||||||
if (!this.profile.options.auth || this.profile.options.auth === 'password') {
|
if (!this.profile.options.auth || this.profile.options.auth === 'password') {
|
||||||
if (this.profile.options.password) {
|
if (this.profile.options.password) {
|
||||||
this.remainingAuthMethods.push({ type: 'saved-password', password: this.profile.options.password })
|
this.remainingAuthMethods.push({ type: 'saved-password', password: this.profile.options.password })
|
||||||
@@ -191,13 +210,6 @@ export class SSHSession {
|
|||||||
}
|
}
|
||||||
this.remainingAuthMethods.push({ type: 'prompt-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.remainingAuthMethods.push({ type: 'keyboard-interactive' })
|
|
||||||
}
|
|
||||||
this.remainingAuthMethods.push({ type: 'hostbased' })
|
this.remainingAuthMethods.push({ type: 'hostbased' })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,7 +507,7 @@ export class SSHSession {
|
|||||||
this.keyboardInteractivePrompt.next(prompt)
|
this.keyboardInteractivePrompt.next(prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleAuth (methodsLeft?: string[] | null): Promise<russh.AuthenticatedSSHClient|null> {
|
async handleAuth (): Promise<russh.AuthenticatedSSHClient|null> {
|
||||||
this.activePrivateKey = null
|
this.activePrivateKey = null
|
||||||
|
|
||||||
if (!(this.ssh instanceof russh.SSHClient)) {
|
if (!(this.ssh instanceof russh.SSHClient)) {
|
||||||
@@ -506,22 +518,36 @@ export class SSHSession {
|
|||||||
throw new Error('No username')
|
throw new Error('No username')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const noneResult = await this.ssh.authenticateNone(this.authUsername)
|
||||||
|
if (noneResult instanceof russh.AuthenticatedSSHClient) {
|
||||||
|
return noneResult
|
||||||
|
}
|
||||||
|
|
||||||
|
let methodsLeft = noneResult.remainingMethods
|
||||||
|
|
||||||
|
function maybeSetRemainingMethods (r: russh.AuthFailure) {
|
||||||
|
if (r.remainingMethods.length) {
|
||||||
|
methodsLeft = r.remainingMethods
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const method = this.remainingAuthMethods.shift()
|
const m = methodsLeft
|
||||||
|
const method = this.remainingAuthMethods.find(x => m.length === 0 || m.includes(sshAuthTypeForMethod(x)))
|
||||||
|
|
||||||
if (!method) {
|
if (!method) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
if (methodsLeft && !methodsLeft.includes(method.type) && method.type !== 'agent') {
|
|
||||||
// Agent can still be used even if not in methodsLeft
|
this.remainingAuthMethods = this.remainingAuthMethods.filter(x => x !== method)
|
||||||
this.logger.info('Server does not support auth method', method.type)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (method.type === 'saved-password') {
|
if (method.type === 'saved-password') {
|
||||||
this.emitServiceMessage(this.translate.instant('Using saved password'))
|
this.emitServiceMessage(this.translate.instant('Using saved password'))
|
||||||
const result = await this.ssh.authenticateWithPassword(this.authUsername, method.password)
|
const result = await this.ssh.authenticateWithPassword(this.authUsername, method.password)
|
||||||
if (result instanceof russh.AuthenticatedSSHClient) {
|
if (result instanceof russh.AuthenticatedSSHClient) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
maybeSetRemainingMethods(result)
|
||||||
}
|
}
|
||||||
if (method.type === 'prompt-password') {
|
if (method.type === 'prompt-password') {
|
||||||
const modal = this.ngbModal.open(PromptModalComponent)
|
const modal = this.ngbModal.open(PromptModalComponent)
|
||||||
@@ -539,6 +565,7 @@ export class SSHSession {
|
|||||||
if (result instanceof russh.AuthenticatedSSHClient) {
|
if (result instanceof russh.AuthenticatedSSHClient) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
maybeSetRemainingMethods(result)
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -556,6 +583,7 @@ export class SSHSession {
|
|||||||
if (result instanceof russh.AuthenticatedSSHClient) {
|
if (result instanceof russh.AuthenticatedSSHClient) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
maybeSetRemainingMethods(result)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to load private key ${method.name}: ${e}`)
|
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to load private key ${method.name}: ${e}`)
|
||||||
@@ -567,6 +595,7 @@ export class SSHSession {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (state.state === 'failure') {
|
if (state.state === 'failure') {
|
||||||
|
maybeSetRemainingMethods(state)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,7 +631,7 @@ export class SSHSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = await this.ssh .continueKeyboardInteractiveAuthentication(responses)
|
state = await this.ssh.continueKeyboardInteractiveAuthentication(responses)
|
||||||
|
|
||||||
if (state instanceof russh.AuthenticatedSSHClient) {
|
if (state instanceof russh.AuthenticatedSSHClient) {
|
||||||
return state
|
return state
|
||||||
@@ -615,6 +644,7 @@ export class SSHSession {
|
|||||||
if (result instanceof russh.AuthenticatedSSHClient) {
|
if (result instanceof russh.AuthenticatedSSHClient) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
maybeSetRemainingMethods(result)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to authenticate using agent: ${e}`)
|
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to authenticate using agent: ${e}`)
|
||||||
continue
|
continue
|
||||||
|
@@ -26,7 +26,6 @@ import { TerminalContextMenuItemProvider } from './api/contextMenuProvider'
|
|||||||
import { TerminalColorSchemeProvider } from './api/colorSchemeProvider'
|
import { TerminalColorSchemeProvider } from './api/colorSchemeProvider'
|
||||||
import { TerminalSettingsTabProvider, AppearanceSettingsTabProvider, ColorSchemeSettingsTabProvider } from './settings'
|
import { TerminalSettingsTabProvider, AppearanceSettingsTabProvider, ColorSchemeSettingsTabProvider } from './settings'
|
||||||
import { DebugDecorator } from './features/debug'
|
import { DebugDecorator } from './features/debug'
|
||||||
import { PathDropDecorator } from './features/pathDrop'
|
|
||||||
import { ZModemDecorator } from './features/zmodem'
|
import { ZModemDecorator } from './features/zmodem'
|
||||||
import { TerminalConfigProvider } from './config'
|
import { TerminalConfigProvider } from './config'
|
||||||
import { TerminalHotkeyProvider } from './hotkeys'
|
import { TerminalHotkeyProvider } from './hotkeys'
|
||||||
@@ -54,7 +53,6 @@ import { DefaultColorSchemes } from './colorSchemes'
|
|||||||
|
|
||||||
{ provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true },
|
{ provide: ConfigProvider, useClass: TerminalConfigProvider, multi: true },
|
||||||
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
|
{ provide: HotkeyProvider, useClass: TerminalHotkeyProvider, multi: true },
|
||||||
{ provide: TerminalDecorator, useClass: PathDropDecorator, multi: true },
|
|
||||||
{ provide: TerminalDecorator, useClass: ZModemDecorator, multi: true },
|
{ provide: TerminalDecorator, useClass: ZModemDecorator, multi: true },
|
||||||
{ provide: TerminalDecorator, useClass: DebugDecorator, multi: true },
|
{ provide: TerminalDecorator, useClass: DebugDecorator, multi: true },
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user