diff --git a/README.md b/README.md
index 1015c34..2a57f8d 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,12 @@
# Why another terminal?
-Tess was mainly built in order to offer you a new, intuitive, fully customizable and blazing fast terminal app, by using the power of web technologies.
+Tess was mainly built in order to offer you a new, intuitive, fully customizable, and blazing fast terminal app by using the power of web technologies.
-Why should you not test it? We work for more than 2 years on this, and we've been relied on by thousands of users.
+Why should you not test it? We've worked on this for more than 2 years, and we've been relied on by thousands of users.
-Tess is currently officially tested on Windows & Linux, but it should work also on other platform.
-If you cannot have access to Tess yet, fill an issue, we'll try to provide you the best cross-platform experience by making new packages or helping you manually installing Tess.
+Tess is currently officially tested on Windows and Linux, but it should also work on other platforms.
+If you do not have access to Tess yet, fill out an issue, and we'll try to provide you with the best cross-platform experience by making new packages or helping you manually install Tess.
@@ -21,13 +21,15 @@ If you cannot have access to Tess yet, fill an issue, we'll try to provide you t
-## Installation from archive
+## Installation from the archive
-We provide package for most major distribution, simply select the one that match your distro and install it.
+We provide packages for most major distributions; simply select the one that matches your distro and install it.
+
+
## Installation with PPA
-If you prefer using apt over downloading and installing the `deb` archive. You could set up the PPA and download Tess with these
+If you prefer using apt over downloading and installing the `deb` archive, you could set up the PPA and download Tess with these
```bash
apt install curl apt-transport-https gnupg2
@@ -38,14 +40,20 @@ echo 'deb [signed-by=/usr/share/keyrings/tess.gpg] https://apt.tessapp.dev stabl
apt update && apt install tess
```
+
+
## Installation with AUR
-On Arch Linux, the recommended way to install Tess is using an AUR package manager like [yay](https://github.com/Jguer/yay)
-`yay -S tess-git`
+On Arch Linux, the recommended way to install Tess is to use an AUR package manager like [yay](https://github.com/Jguer/yay)
+```sh
+yay -S tess-git
+```
+
+
## Installation with RPM repository
-If you are on RHEL derivate distros, you could also want to add our RPM repository to install Tess and receive update automatically with your package manager
+If you are on RHEL-derived distros, you may also want to add our RPM repository to install Tess and receive updates automatically with your package manager.
```bash
yum install curl
@@ -66,27 +74,33 @@ yum check-update && yum install tess
## Installation from Installer
-Simply download and execute the installer, available in the [releases](https://github.com/SquitchYT/Tess/releases) page.
+Simply download and execute the installer, available on the [releases](https://github.com/SquitchYT/Tess/releases) page.
+
+
## Installation using Winget
-If you are running on `Windows 10 1709 (build 16299)` or superior, you could download [Winget](https://github.com/microsoft/winget-cli). It may also be already installed on your system.
-Next, execute the following command
+
+
+If you are running on `Windows 10 1709 (build 16299)` or superior, you could download [Winget](https://github.com/microsoft/winget-cli). It may also already be installed on your system.
+Next, execute the following command.
```sh
winget install Squitch.Tess
```
+
+
## Installation using Chocolatey
Firstly, download [Chocolatey](https://chocolatey.org/install).
-Next, you will be allowed to run this
+Next, you will be allowed to run this.
```sh
choco install tess
```
-You can also find the package [here](https://community.chocolatey.org/packages/tess)
+You can also find the package [here](https://community.chocolatey.org/packages/tess).
@@ -95,11 +109,11 @@ You can also find the package [here](https://community.chocolatey.org/packages/t
## Getting started
-You want to contribute in Tess, find a simple task to help us with this project.
+If you want to contribute to Tess, find a simple task to help us with this project.
-* You've found a mistake in documentations, code or in the wiki, let us know by opening an [issue](https://github.com/SquitchYT/Tess/issues).
-* You've an amazing feature idea, simply post your suggestion by creating an issue too.
-* You want to help us close an issue, implementing a feature or something else related to code, follow the guide below.
+* If you've found a mistake in documentation, sources, or the wiki, let us know by opening an [issue](https://github.com/SquitchYT/Tess/issues).
+* You've got an amazing feature idea; simply post your suggestion by creating an issue too.
+* You want to help us close an issue, implement a feature, or do something else related to code, follow the guide below.
*Please, search for a similar issue before creating a new one.*
@@ -107,20 +121,47 @@ You want to contribute in Tess, find a simple task to help us with this project.
## Developing with Tess
-You want to contribute to Tess, it's simply, fork the repository and start developing on it.
+First and foremost, you need to ensure that you have installed the necessary tools:
+
+* [Rust & Cargo](https://rustup.rs/)
+* [Node.js](https://nodejs.org/en)
+* Tauri CLI `cargo install tauri-cli`
+
+
+
+Next, start by downloading the source code.
+```sh
+git clone -b dev https://github.com/SquitchYT/Tess
+```
+
+Next, set up the project.
+```sh
+npm i
+```
+
+To ensure that everything is set up properly, run Tess with this command; it should launch Tess.
+```sh
+cargo tauri dev
+```
+
+You are now ready!
+
+
+
+Important notice:
-* As Tess is cross-platform, when you implement a new feature, try to make it available everywhere. If despite all your efforts, you are unable to make it cross-platform, let us know the supported platforms when submitting your changes.
-* If you update code, explain why you think this change is important and what you've done.
+* As Tess is cross-platform, when you implement a new feature, try to make it available everywhere. If, despite all your efforts, you are unable to make it cross-platform, let us know the supported platforms when submitting your changes.
+* If you update the code, explain why you think this change is important and what you've done.
-After making your changes, simply open a [pull request](https://github.com/SquitchYT/Tess/pulls).
+Simply open a [pull request](https://github.com/SquitchYT/Tess/pulls) to submit your changes.
# Roadmap
-As long as Tess is in beta, many bugs may occur and some of them have not yet been fixed.
-More, many features are not yet available, here's a quick recap of major features that will want to integrate in Tess.
+As long as Tess is in beta, many bugs may occur, and some of them have not yet been fixed.
+Many features are not yet available; here's a quick recap of the major features that we'll integrate in Tess.
|Features |Electron|Tauri|
@@ -131,15 +172,15 @@ More, many features are not yet available, here's a quick recap of major feature
|Administrator tabs |❌ |⌛ |
|Tabs split |❌ |⌛ |
|Command line interface |✔️ |⌛ |
-|Notifications |❌ |⌛ |
+|Notifications |❌ |🟠 |
|Macros |❌ |⌛ |
|Plugins |🟠 |⌛ |
-|Themes |🟠 |⌛ |
+|Themes |🟠 |🟠 |
|Config page |✔️ |⌛ |
|Config watching |🟠 |⌛ |
|Image display |❌ |⌛ |
|Font ligature |🟠 |⌛ |
-|Animated background |❌ |⌛ |
+|Animated background |❌ |✔️ |
|URI scheme API |❌ |⌛ |
|Search in a shell |❌ |⌛ |
|Marketplace |❌ |⌛ |
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 2dee7c2..88b6fa9 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -2,6 +2,20 @@
# It is not intended for manual editing.
version = 3
+[[package]]
+name = "addr2line"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
+dependencies = [
+ "cpp_demangle",
+ "fallible-iterator",
+ "gimli",
+ "object",
+ "rustc-demangle",
+ "smallvec",
+]
+
[[package]]
name = "adler"
version = "1.0.2"
@@ -87,6 +101,17 @@ dependencies = [
"url",
]
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -99,6 +124,48 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+[[package]]
+name = "bindgen"
+version = "0.59.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "lazy_static",
+ "lazycell",
+ "peeking_take_while",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+]
+
+[[package]]
+name = "bindgen"
+version = "0.60.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "clap",
+ "env_logger",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "peeking_take_while",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "which",
+]
+
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -220,6 +287,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
[[package]]
name = "cfb"
version = "0.6.1"
@@ -265,6 +341,41 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "clang-sys"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
+name = "clap"
+version = "3.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_lex",
+ "indexmap",
+ "strsim",
+ "termcolor",
+ "textwrap",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
+]
+
[[package]]
name = "cocoa"
version = "0.24.1"
@@ -359,6 +470,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "cpp_demangle"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c76f98bdfc7f66172e6c7065f981ebb576ffc903fe4c0561d9f0c2509226dc6"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "cpufeatures"
version = "0.2.5"
@@ -545,6 +665,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+[[package]]
+name = "downcast-rs"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
+
[[package]]
name = "dtoa"
version = "0.4.8"
@@ -566,6 +692,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c"
+[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
[[package]]
name = "embed_plist"
version = "1.2.2"
@@ -581,6 +713,46 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "env_logger"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fallible-iterator"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+
[[package]]
name = "fastrand"
version = "1.8.0"
@@ -871,6 +1043,16 @@ dependencies = [
"wasi 0.11.0+wasi-snapshot-preview1",
]
+[[package]]
+name = "gimli"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
+dependencies = [
+ "fallible-iterator",
+ "stable_deref_trait",
+]
+
[[package]]
name = "gio"
version = "0.15.12"
@@ -976,6 +1158,17 @@ dependencies = [
"system-deps 6.0.3",
]
+[[package]]
+name = "goblin"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d6b4de4a8eb6c46a8c77e1d3be942cb9a8bf073c22374578e5ba4b08ed0ff68"
+dependencies = [
+ "log",
+ "plain",
+ "scroll",
+]
+
[[package]]
name = "gtk"
version = "0.15.5"
@@ -1092,6 +1285,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
[[package]]
name = "ico"
version = "0.2.0"
@@ -1328,11 +1527,17 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
[[package]]
name = "libc"
-version = "0.2.138"
+version = "0.2.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
+checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
[[package]]
name = "libdbus-sys"
@@ -1343,6 +1548,27 @@ dependencies = [
"pkg-config",
]
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "libproc"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b799ad155d75ce914c467ee5627b62247c20d4aedbd446f821484cebf3cded7"
+dependencies = [
+ "bindgen 0.59.2",
+ "errno",
+ "libc",
+]
+
[[package]]
name = "line-wrap"
version = "0.1.1"
@@ -1405,6 +1631,30 @@ dependencies = [
"time",
]
+[[package]]
+name = "mach"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "mach2"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "mach_o_sys"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e854583a83f20cf329bb9283366335387f7db59d640d1412167e05fedb98826"
+
[[package]]
name = "malloc_buf"
version = "0.0.6"
@@ -1449,6 +1699,16 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+[[package]]
+name = "memmap"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
[[package]]
name = "memoffset"
version = "0.6.5"
@@ -1458,6 +1718,12 @@ dependencies = [
"autocfg",
]
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
[[package]]
name = "miniz_oxide"
version = "0.6.2"
@@ -1531,12 +1797,36 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
+[[package]]
+name = "nix"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
+dependencies = [
+ "autocfg",
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "memoffset",
+ "pin-utils",
+]
+
[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
[[package]]
name = "notify"
version = "5.1.0"
@@ -1676,6 +1966,16 @@ dependencies = [
"objc",
]
+[[package]]
+name = "object"
+version = "0.30.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
+dependencies = [
+ "flate2",
+ "memchr",
+]
+
[[package]]
name = "once_cell"
version = "1.16.0"
@@ -1758,6 +2058,12 @@ dependencies = [
"windows-sys 0.42.0",
]
+[[package]]
+name = "os_str_bytes"
+version = "6.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
+
[[package]]
name = "overload"
version = "0.1.1"
@@ -1824,6 +2130,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
+[[package]]
+name = "peeking_take_while"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+
[[package]]
name = "percent-encoding"
version = "2.2.0"
@@ -1956,6 +2268,12 @@ version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+
[[package]]
name = "plist"
version = "1.3.1"
@@ -1984,20 +2302,23 @@ dependencies = [
[[package]]
name = "portable-pty"
-version = "0.7.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4d17ec050a6b7ea4b15c430183772bce8384072d3f328e0967e72b7eec46b5"
+checksum = "806ee80c2a03dbe1a9fb9534f8d19e4c0546b790cde8fd1fea9d6390644cb0be"
dependencies = [
"anyhow",
"bitflags",
+ "downcast-rs",
"filedescriptor",
"lazy_static",
"libc",
"log",
+ "nix",
"serial",
"shared_library",
"shell-words",
"winapi",
+ "winreg",
]
[[package]]
@@ -2062,6 +2383,20 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "proc-maps"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d17946c951c8e8c4233375fdbfc212b215bd14ea1b18388eae8c95bb03a0174"
+dependencies = [
+ "anyhow",
+ "bindgen 0.60.1",
+ "libc",
+ "libproc",
+ "mach2",
+ "winapi",
+]
+
[[package]]
name = "quick-xml"
version = "0.23.1"
@@ -2170,6 +2505,18 @@ dependencies = [
"cty",
]
+[[package]]
+name = "read-process-memory"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8497683b2f0b6887786f1928c118f26ecc6bb3d78bbb6ed23e8e7ba110af3bb0"
+dependencies = [
+ "libc",
+ "log",
+ "mach",
+ "winapi",
+]
+
[[package]]
name = "redox_syscall"
version = "0.2.16"
@@ -2222,6 +2569,29 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+[[package]]
+name = "remoteprocess"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3ed67d3fade907d519c2edd07329a8f16296a64f91b6f3c35570769cf6b25d"
+dependencies = [
+ "addr2line",
+ "goblin",
+ "lazy_static",
+ "libc",
+ "libproc",
+ "log",
+ "mach",
+ "mach_o_sys",
+ "memmap",
+ "nix",
+ "object",
+ "proc-maps",
+ "read-process-memory",
+ "regex",
+ "winapi",
+]
+
[[package]]
name = "remove_dir_all"
version = "0.5.3"
@@ -2255,6 +2625,18 @@ dependencies = [
"windows 0.37.0",
]
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
[[package]]
name = "rustc_version"
version = "0.3.3"
@@ -2322,6 +2704,26 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+[[package]]
+name = "scroll"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
+dependencies = [
+ "scroll_derive",
+]
+
+[[package]]
+name = "scroll_derive"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "security-framework"
version = "2.7.0"
@@ -2588,6 +2990,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
+[[package]]
+name = "shlex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
+
[[package]]
name = "siphasher"
version = "0.3.10"
@@ -3012,6 +3420,15 @@ dependencies = [
"utf-8",
]
+[[package]]
+name = "termcolor"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
+dependencies = [
+ "winapi-util",
+]
+
[[package]]
name = "termios"
version = "0.2.2"
@@ -3023,7 +3440,7 @@ dependencies = [
[[package]]
name = "tess"
-version = "0.7.0-alpha.4"
+version = "0.7.0-alpha.5"
dependencies = [
"dirs-next",
"infer 0.12.0",
@@ -3031,14 +3448,22 @@ dependencies = [
"notify",
"portable-pty",
"regex",
+ "remoteprocess",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tokio",
"uuid 1.3.0",
+ "window-vibrancy",
]
+[[package]]
+name = "textwrap"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+
[[package]]
name = "thin-slice"
version = "0.1.1"
@@ -3496,6 +3921,17 @@ dependencies = [
"windows-metadata",
]
+[[package]]
+name = "which"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
+dependencies = [
+ "either",
+ "libc",
+ "once_cell",
+]
+
[[package]]
name = "winapi"
version = "0.3.9"
@@ -3527,6 +3963,18 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+[[package]]
+name = "window-vibrancy"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f762d9cc392fb85e6b1b5eed1ef13d73fed5149a5cbb017a7137497d14ef612"
+dependencies = [
+ "cocoa",
+ "objc",
+ "raw-window-handle",
+ "windows-sys 0.42.0",
+]
+
[[package]]
name = "windows"
version = "0.37.0"
@@ -3746,6 +4194,15 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+[[package]]
+name = "winreg"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "winres"
version = "0.1.12"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 95bb881..63cb31a 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tess"
-version = "0.7.0-alpha.4"
+version = "0.7.0-alpha.5"
description = "Fast. Lightweight. Famous. Terminal for the new era."
authors = ["Squitch"]
repository = "https://github.com/SquitchYT/Tess"
@@ -17,16 +17,18 @@ tauri-build = { version = "1.1.1", features = [] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.1.1", features = ["api-all"] }
-portable-pty = "0.7.0"
+portable-pty = "0.8.1"
infer = "0.12.0"
notify = "5.1.0"
tokio = "1.25.0"
dirs-next = "2.0.0"
uuid = "1.3.0"
+remoteprocess = "0.4.11"
[target.'cfg(windows)'.dependencies]
lazy_static = "1.4.0"
regex = "1.8.4"
+window-vibrancy = "0.3.2"
[features]
default = ["custom-protocol"]
diff --git a/src-tauri/src/command/option.rs b/src-tauri/src/command/option.rs
index 2870b96..0704e1a 100644
--- a/src-tauri/src/command/option.rs
+++ b/src-tauri/src/command/option.rs
@@ -1,7 +1,6 @@
use crate::configuration::deserialized::Option;
use std::sync::{Arc, Mutex};
-
#[tauri::command]
pub async fn get_configuration(option: tauri::State<'_, Arc>>) -> Result {
Ok(option.lock().unwrap().clone())
diff --git a/src-tauri/src/command/term.rs b/src-tauri/src/command/term.rs
index 73b23f2..9959ee9 100644
--- a/src-tauri/src/command/term.rs
+++ b/src-tauri/src/command/term.rs
@@ -1,3 +1,4 @@
+use crate::configuration::deserialized::Option;
use crate::state::pty_manager::PtyManager;
use std::sync::{Arc, Mutex};
@@ -10,7 +11,7 @@ pub async fn terminal_input(
content: String,
) -> Result<(), PtyError> {
if let Ok(mut pty_manager) = pty_manager.lock() {
- pty_manager.write(id, content)?;
+ pty_manager.write(&id, content)?;
Ok(())
} else {
Err(PtyError::ManagerUnresponding)
@@ -24,17 +25,28 @@ pub async fn create_terminal(
cols: u16,
rows: u16,
id: String,
- command: String,
+ profile_uuid: String,
+ option: tauri::State<'_, Arc>>,
) -> Result<(), PtyError> {
- if let Ok(mut pty_manager) = pty_manager.lock() {
- if let None = pty_manager.app {
- pty_manager.app = Some(Arc::new(app));
- }
+ if let Some(profile) = option
+ .lock()
+ .unwrap()
+ .profiles
+ .iter()
+ .find(|profile| profile.uuid == profile_uuid)
+ {
+ if let Ok(mut pty_manager) = pty_manager.lock() {
+ if pty_manager.app.is_none() {
+ pty_manager.app = Some(Arc::new(app));
+ }
- pty_manager.create_pty(cols, rows, id, &command)?;
- Ok(())
+ pty_manager.create_pty(cols, rows, id, profile.clone())?;
+ Ok(())
+ } else {
+ Err(PtyError::ManagerUnresponding)
+ }
} else {
- Err(PtyError::ManagerUnresponding)
+ todo!()
}
}
@@ -46,7 +58,7 @@ pub async fn resize_terminal(
rows: u16,
) -> Result<(), PtyError> {
if let Ok(mut pty_manager) = pty_manager.lock() {
- pty_manager.resize(id, cols, rows)?;
+ pty_manager.resize(&id, cols, rows)?;
Ok(())
} else {
Err(PtyError::ManagerUnresponding)
@@ -59,7 +71,7 @@ pub async fn close_terminal(
id: String,
) -> Result<(), PtyError> {
if let Ok(mut pty_manager) = pty_manager.lock() {
- pty_manager.close(id)?;
+ pty_manager.close(&id)?;
Ok(())
} else {
Err(PtyError::ManagerUnresponding)
diff --git a/src-tauri/src/common/payloads.rs b/src-tauri/src/common/payloads.rs
index 1db6a1c..f451f11 100644
--- a/src-tauri/src/common/payloads.rs
+++ b/src-tauri/src/common/payloads.rs
@@ -3,3 +3,9 @@ pub struct PtySendData {
pub id: String,
pub data: String,
}
+
+#[derive(serde::Serialize, Clone)]
+pub struct PtyTitleChanged {
+ pub id: String,
+ pub title: String,
+}
diff --git a/src-tauri/src/common/utils.rs b/src-tauri/src/common/utils.rs
index 8782f2a..befbec4 100644
--- a/src-tauri/src/common/utils.rs
+++ b/src-tauri/src/common/utils.rs
@@ -1,5 +1,8 @@
use crate::configuration::deserialized::TerminalTheme;
+#[cfg(target_os = "windows")]
+use std::ffi::OsString;
+
pub fn parse_theme(location: String) -> (Option, Option) {
let theme_path = if std::path::Path::new(&format!(
"{}/tess/themes/{}",
@@ -20,11 +23,65 @@ pub fn parse_theme(location: String) -> (Option, Option)
};
if let Some(theme_path) = theme_path {
- let app_theme = if std::path::Path::new(&format!("{}/style.css", theme_path)).exists() { Some(format!("{}/style.css", theme_path)) } else { None };
- let terminal_theme = if std::path::Path::new(&format!("{}/terminal.json", theme_path)).exists() { Some(serde_json::from_str(&std::fs::read_to_string(std::path::Path::new(&format!("{}/terminal.json", theme_path))).unwrap_or_default()).unwrap_or_default()) } else { None };
+ let app_theme = if std::path::Path::new(&format!("{}/style.css", theme_path)).exists() {
+ Some(format!("{}/style.css", theme_path))
+ } else {
+ None
+ };
+ let terminal_theme =
+ if std::path::Path::new(&format!("{}/terminal.json", theme_path)).exists() {
+ Some(
+ serde_json::from_str(
+ &std::fs::read_to_string(std::path::Path::new(&format!(
+ "{}/terminal.json",
+ theme_path
+ )))
+ .unwrap_or_default(),
+ )
+ .unwrap_or_default(),
+ )
+ } else {
+ None
+ };
(app_theme, terminal_theme)
} else {
(None, None)
}
-}
\ No newline at end of file
+}
+
+#[cfg(target_os = "windows")]
+pub fn get_leader_process_name(process: remoteprocess::Process) -> Option {
+ if let Ok(childs) = process.child_processes() {
+ let mut childs = childs
+ .iter()
+ .filter(|tmp| {
+ tmp.1 == process.pid
+ && remoteprocess::Process::new(tmp.0)
+ .is_ok_and(|sub_process| sub_process.exe().is_ok())
+ })
+ .collect::>();
+
+ if childs.is_empty() {
+ std::path::Path::new(&process.exe().ok().unwrap_or_default())
+ .file_name()
+ .map(|x| x.to_owned())
+ } else {
+ childs.sort();
+
+ if let Ok(child_process) =
+ remoteprocess::Process::new(childs.as_slice().last().unwrap().0)
+ {
+ get_leader_process_name(child_process)
+ } else {
+ std::path::Path::new(&process.exe().ok().unwrap_or_default())
+ .file_name()
+ .map(|x| x.to_owned())
+ }
+ }
+ } else {
+ std::path::Path::new(&process.exe().ok().unwrap_or_default())
+ .file_name()
+ .map(|x| x.to_owned())
+ }
+}
diff --git a/src-tauri/src/configuration/deserialized.rs b/src-tauri/src/configuration/deserialized.rs
index 9c8fa05..3549720 100644
--- a/src-tauri/src/configuration/deserialized.rs
+++ b/src-tauri/src/configuration/deserialized.rs
@@ -1,14 +1,12 @@
-use serde::{Serialize, Serializer, ser::SerializeSeq};
-use serde::Deserialize;
-use crate::configuration::types::RangedInt;
-use crate::configuration::types::CursorType;
-use crate::configuration::types::BackgroundType;
use crate::configuration::partial::PartialOption;
+use crate::configuration::types::CursorType;
+use crate::configuration::types::RangedInt;
+use crate::configuration::types::{BackgroundMedia, BackgroundType};
+use serde::Deserialize;
+use serde::{ser::SerializeSeq, Serialize, Serializer};
use crate::common::utils::parse_theme;
-
-
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Option {
@@ -25,7 +23,7 @@ pub struct Option {
pub default_profile: Profile,
#[serde(skip_serializing)]
- theme: String
+ theme: String,
}
impl Default for Option {
@@ -45,7 +43,7 @@ impl Default for Option {
macros: Vec::default(),
default_profile: default_profile(uuid),
- theme: String::default()
+ theme: String::default(),
}
}
}
@@ -58,23 +56,23 @@ impl<'de> serde::Deserialize<'de> for Option {
let app_theme = app_theme.unwrap_or_default();
let terminal_theme = terminal_theme.unwrap_or_default();
-
let mut profiles = vec![];
if partial_option.profiles.is_empty() {
- profiles.push(
- Profile {
- name: String::from("Default profile"),
- terminal_options: partial_option.terminal.clone(),
- theme: terminal_theme.clone(),
- background_transparency: RangedInt::default(),
- uuid: uuid::Uuid::new_v4().to_string(),
- #[cfg(target_family = "unix")]
- command: String::from("sh -c $SHELL"),
- #[cfg(target_os = "windows")]
- command: String::from("%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"),
- }
- )
+ profiles.push(Profile {
+ name: String::from("Default profile"),
+ terminal_options: partial_option.terminal.clone(),
+ theme: terminal_theme.clone(),
+ background_transparency: RangedInt::default(),
+ uuid: uuid::Uuid::new_v4().to_string(),
+ #[cfg(target_family = "unix")]
+ command: String::from("sh -c $SHELL"),
+ #[cfg(target_os = "windows")]
+ command: String::from(
+ "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
+ ),
+ background: None,
+ })
} else {
for partial_profile in partial_option.profiles {
let profile_option = TerminalOption {
@@ -120,24 +118,30 @@ impl<'de> serde::Deserialize<'de> for Option {
.unwrap_or(partial_option.terminal.title_is_running_process),
};
- let profile_theme = if let Some(partial_profile_theme) = partial_profile.theme { parse_theme(partial_profile_theme).1.unwrap_or(terminal_theme.clone()) } else { terminal_theme.clone() };
-
+ let profile_theme = if let Some(partial_profile_theme) = partial_profile.theme {
+ parse_theme(partial_profile_theme)
+ .1
+ .unwrap_or(terminal_theme.clone())
+ } else {
+ terminal_theme.clone()
+ };
+
profiles.push(Profile {
name: partial_profile.name,
terminal_options: profile_option,
theme: profile_theme,
background_transparency: partial_profile
.background_transparency
- .unwrap_or(RangedInt::default()),
+ .unwrap_or(partial_option.background_transparency),
uuid: uuid::Uuid::parse_str(partial_profile.uuid.unwrap_or_default().as_str())
.unwrap_or(uuid::Uuid::new_v4())
.to_string(),
- command: partial_profile.command
+ command: partial_profile.command,
+ background: partial_profile.background,
})
}
}
-
let mut macros = vec![];
if let Some(partial_macros) = partial_option.macros {
@@ -151,7 +155,6 @@ impl<'de> serde::Deserialize<'de> for Option {
}
}
-
let shortcuts = if let Some(partial_option_shortcuts) = partial_option.shortcuts {
let mut shortcuts = vec![];
@@ -177,12 +180,13 @@ impl<'de> serde::Deserialize<'de> for Option {
action: ShortcutAction::ExecuteMacro(macro_command.uuid.clone()),
})
}
- },
- _ => {
- shortcuts.push(Shortcut { shortcut: partial_shortcut.shortcut, action: partial_shortcut.action })
}
+ _ => shortcuts.push(Shortcut {
+ shortcut: partial_shortcut.shortcut,
+ action: partial_shortcut.action,
+ }),
};
- };
+ }
shortcuts
} else {
@@ -192,23 +196,25 @@ impl<'de> serde::Deserialize<'de> for Option {
Ok(Option {
theme: partial_option.theme.clone(),
- terminal_theme: terminal_theme,
- app_theme: app_theme,
+ terminal_theme,
+ app_theme,
background: partial_option.background,
custom_titlebar: partial_option.custom_titlebar,
terminal: partial_option.terminal,
profiles: profiles.clone(),
close_confirmation: partial_option.close_confirmation,
background_transparency: partial_option.background_transparency,
- shortcuts: shortcuts,
- macros: macros,
- default_profile: profiles.iter().find(|&profile| {profile.uuid == partial_option.default_profile}).unwrap_or(&profiles[0]).clone()
+ shortcuts,
+ macros,
+ default_profile: profiles
+ .iter()
+ .find(|&profile| profile.uuid == partial_option.default_profile)
+ .unwrap_or(&profiles[0])
+ .clone(),
})
}
}
-
-
#[derive(Deserialize, Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TerminalOption {
@@ -239,7 +245,7 @@ pub struct TerminalOption {
#[serde(default)]
font_weight_bold: RangedInt<1, 9, 6>,
#[serde(default = "default_to_true")]
- title_is_running_process: bool,
+ pub title_is_running_process: bool,
}
impl Default for TerminalOption {
@@ -263,24 +269,18 @@ impl Default for TerminalOption {
}
}
-
-
#[derive(Deserialize, Debug, Serialize, Clone)]
pub struct Macro {
pub content: String,
pub uuid: String,
}
-
-
#[derive(Debug, Serialize, Clone)]
pub struct Shortcut {
pub shortcut: String,
pub action: ShortcutAction,
}
-
-
#[derive(Debug, Clone, Deserialize)]
pub enum ShortcutAction {
CloseFocusedTab,
@@ -334,23 +334,20 @@ impl Serialize for ShortcutAction {
}
}
-
-
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Profile {
// TODO: Add Icon
- // TODO: Add background_media
name: String,
- terminal_options: TerminalOption,
+ pub terminal_options: TerminalOption,
theme: TerminalTheme,
- background_transparency: RangedInt<0, 100, 0>,
- uuid: String,
- command: String
+ background_transparency: RangedInt<0, 100, 100>,
+ background: std::option::Option,
+ pub uuid: String,
+ pub command: String,
}
-
#[derive(Debug, Clone, Serialize)]
pub struct TerminalTheme {
foreground: String,
@@ -372,10 +369,9 @@ pub struct TerminalTheme {
bright_cyan: String,
bright_white: String,
cursor: String,
- cursor_accent: String
+ cursor_accent: String,
}
-
impl Default for TerminalTheme {
fn default() -> Self {
Self {
@@ -398,7 +394,7 @@ impl Default for TerminalTheme {
bright_cyan: String::from("#4DE8FE"),
bright_white: String::from("#ABB4D6"),
cursor: String::from("#DEEAF8"),
- cursor_accent: String::from("#141A29")
+ cursor_accent: String::from("#141A29"),
}
}
}
@@ -446,54 +442,94 @@ impl<'de> Deserialize<'de> for TerminalTheme {
#[serde(default)]
pub cursor: std::option::Option,
#[serde(default)]
- pub cursor_accent: std::option::Option
+ pub cursor_accent: std::option::Option,
}
- let partial_terminal_theme = PartialTerminalTheme::deserialize(deserializer).unwrap_or_default();
+ let partial_terminal_theme =
+ PartialTerminalTheme::deserialize(deserializer).unwrap_or_default();
let default_terminal_theme = TerminalTheme::default();
-
+
Ok(TerminalTheme {
- foreground: partial_terminal_theme.foreground.unwrap_or(default_terminal_theme.foreground),
- background: partial_terminal_theme.background.unwrap_or(default_terminal_theme.background),
- black: partial_terminal_theme.black.unwrap_or(default_terminal_theme.black),
- red: partial_terminal_theme.red.unwrap_or(default_terminal_theme.red),
- green: partial_terminal_theme.green.unwrap_or(default_terminal_theme.green),
- yellow: partial_terminal_theme.yellow.unwrap_or(default_terminal_theme.yellow),
- blue: partial_terminal_theme.blue.unwrap_or(default_terminal_theme.blue),
- magenta: partial_terminal_theme.magenta.unwrap_or(default_terminal_theme.magenta),
- cyan: partial_terminal_theme.cyan.unwrap_or(default_terminal_theme.cyan),
- white: partial_terminal_theme.white.unwrap_or(default_terminal_theme.white),
- bright_black: partial_terminal_theme.bright_black.unwrap_or(default_terminal_theme.bright_black),
- bright_red: partial_terminal_theme.bright_red.unwrap_or(default_terminal_theme.bright_red),
- bright_green: partial_terminal_theme.bright_green.unwrap_or(default_terminal_theme.bright_green),
- bright_yellow: partial_terminal_theme.bright_yellow.unwrap_or(default_terminal_theme.bright_yellow),
- bright_blue: partial_terminal_theme.bright_blue.unwrap_or(default_terminal_theme.bright_blue),
- bright_magenta: partial_terminal_theme.bright_magenta.unwrap_or(default_terminal_theme.bright_magenta),
- bright_cyan: partial_terminal_theme.bright_cyan.unwrap_or(default_terminal_theme.bright_cyan),
- bright_white: partial_terminal_theme.bright_white.unwrap_or(default_terminal_theme.bright_white),
- cursor: partial_terminal_theme.cursor.unwrap_or(default_terminal_theme.cursor),
- cursor_accent: partial_terminal_theme.cursor_accent.unwrap_or(default_terminal_theme.cursor_accent),
+ foreground: partial_terminal_theme
+ .foreground
+ .unwrap_or(default_terminal_theme.foreground),
+ background: partial_terminal_theme
+ .background
+ .unwrap_or(default_terminal_theme.background),
+ black: partial_terminal_theme
+ .black
+ .unwrap_or(default_terminal_theme.black),
+ red: partial_terminal_theme
+ .red
+ .unwrap_or(default_terminal_theme.red),
+ green: partial_terminal_theme
+ .green
+ .unwrap_or(default_terminal_theme.green),
+ yellow: partial_terminal_theme
+ .yellow
+ .unwrap_or(default_terminal_theme.yellow),
+ blue: partial_terminal_theme
+ .blue
+ .unwrap_or(default_terminal_theme.blue),
+ magenta: partial_terminal_theme
+ .magenta
+ .unwrap_or(default_terminal_theme.magenta),
+ cyan: partial_terminal_theme
+ .cyan
+ .unwrap_or(default_terminal_theme.cyan),
+ white: partial_terminal_theme
+ .white
+ .unwrap_or(default_terminal_theme.white),
+ bright_black: partial_terminal_theme
+ .bright_black
+ .unwrap_or(default_terminal_theme.bright_black),
+ bright_red: partial_terminal_theme
+ .bright_red
+ .unwrap_or(default_terminal_theme.bright_red),
+ bright_green: partial_terminal_theme
+ .bright_green
+ .unwrap_or(default_terminal_theme.bright_green),
+ bright_yellow: partial_terminal_theme
+ .bright_yellow
+ .unwrap_or(default_terminal_theme.bright_yellow),
+ bright_blue: partial_terminal_theme
+ .bright_blue
+ .unwrap_or(default_terminal_theme.bright_blue),
+ bright_magenta: partial_terminal_theme
+ .bright_magenta
+ .unwrap_or(default_terminal_theme.bright_magenta),
+ bright_cyan: partial_terminal_theme
+ .bright_cyan
+ .unwrap_or(default_terminal_theme.bright_cyan),
+ bright_white: partial_terminal_theme
+ .bright_white
+ .unwrap_or(default_terminal_theme.bright_white),
+ cursor: partial_terminal_theme
+ .cursor
+ .unwrap_or(default_terminal_theme.cursor),
+ cursor_accent: partial_terminal_theme
+ .cursor_accent
+ .unwrap_or(default_terminal_theme.cursor_accent),
})
}
}
-
-
fn default_to_true() -> bool {
true
}
fn default_profile(uuid: String) -> Profile {
Profile {
- name: String::from("Default profile"),
- terminal_options: TerminalOption::default(),
- theme: TerminalTheme::default(),
- background_transparency: RangedInt::default(),
- uuid,
+ name: String::from("Default profile"),
+ terminal_options: TerminalOption::default(),
+ theme: TerminalTheme::default(),
+ background_transparency: RangedInt::default(),
+ uuid,
#[cfg(target_family = "unix")]
command: String::from("sh -c $SHELL"),
#[cfg(target_os = "windows")]
command: String::from("%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"),
+ background: None,
}
}
@@ -528,4 +564,4 @@ fn default_shortcuts() -> Vec {
action: ShortcutAction::FocusPrevTab,
},
]
-}
\ No newline at end of file
+}
diff --git a/src-tauri/src/configuration/mod.rs b/src-tauri/src/configuration/mod.rs
index b6d6941..fe6466c 100644
--- a/src-tauri/src/configuration/mod.rs
+++ b/src-tauri/src/configuration/mod.rs
@@ -1,3 +1,3 @@
-pub mod partial;
pub mod deserialized;
-pub mod types;
\ No newline at end of file
+pub mod partial;
+pub mod types;
diff --git a/src-tauri/src/configuration/partial.rs b/src-tauri/src/configuration/partial.rs
index 6f55efa..c662026 100644
--- a/src-tauri/src/configuration/partial.rs
+++ b/src-tauri/src/configuration/partial.rs
@@ -1,10 +1,10 @@
-use crate::configuration::types::RangedInt;
use crate::configuration::deserialized::ShortcutAction;
use crate::configuration::deserialized::TerminalOption;
use crate::configuration::types::CursorType;
-use crate::configuration::types::BackgroundType;
+use crate::configuration::types::RangedInt;
+use crate::configuration::types::{BackgroundMedia, BackgroundType};
use serde::Deserialize;
-
+use serde::Deserializer;
#[derive(Deserialize, Debug)]
pub struct PartialOption {
@@ -31,7 +31,7 @@ pub struct PartialOption {
pub close_confirmation: bool,
#[serde(default)]
- pub default_profile: String
+ pub default_profile: String,
}
impl Default for PartialOption {
@@ -46,13 +46,11 @@ impl Default for PartialOption {
background_transparency: RangedInt::default(),
shortcuts: Some(Vec::default()),
macros: Some(Vec::default()),
- default_profile: String::new()
+ default_profile: String::new(),
}
}
}
-
-
#[derive(Deserialize, Debug)]
pub struct PartialProfile {
pub name: String,
@@ -65,7 +63,7 @@ pub struct PartialProfile {
pub show_picture: std::option::Option,
pub bell: std::option::Option,
pub theme: std::option::Option,
- pub background_transparency: std::option::Option>,
+ pub background_transparency: std::option::Option>,
pub cursor_blink: std::option::Option,
pub draw_bold_in_bright: std::option::Option,
pub show_unread_data_indicator: std::option::Option,
@@ -74,31 +72,48 @@ pub struct PartialProfile {
pub font_weight: std::option::Option>,
pub font_weight_bold: std::option::Option>,
pub title_is_running_process: std::option::Option,
+ #[serde(deserialize_with = "deserialize_profile_background")]
+ #[serde(default)]
+ pub background: std::option::Option,
}
-
-
#[derive(Deserialize, Debug)]
pub struct PartialMacro {
pub content: String,
pub uuid: Option,
}
-
-
#[derive(Deserialize, Debug)]
pub struct PartialShortcut {
pub shortcut: String,
pub action: ShortcutAction,
}
+fn deserialize_profile_background<'de, D>(
+ data: D,
+) -> Result, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize, Debug)]
+ #[serde(untagged)]
+ enum Representation {
+ Simple(String),
+ Complex(BackgroundMedia),
+ }
-
-
-
-
-
+ Ok(
+ if let Ok(name_to_find) = Representation::deserialize(data) {
+ match name_to_find {
+ Representation::Simple(path) => BackgroundMedia::deserialize_from_string(path),
+ Representation::Complex(background) => Some(background),
+ }
+ } else {
+ None
+ },
+ )
+}
fn default_to_true() -> bool {
true
-}
\ No newline at end of file
+}
diff --git a/src-tauri/src/configuration/types.rs b/src-tauri/src/configuration/types.rs
index 5c9b89c..f0e76b6 100644
--- a/src-tauri/src/configuration/types.rs
+++ b/src-tauri/src/configuration/types.rs
@@ -1,6 +1,6 @@
-use serde::{Serializer, Serialize};
use serde::de::Error;
use serde::Deserialize;
+use serde::{Serialize, Serializer};
#[derive(Debug, Clone, Copy)]
pub struct RangedInt {
@@ -46,13 +46,13 @@ impl<'de, const MIN: u32, const MAX: u32, const DEF: u32> serde::Serialize
}
}
-
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub enum BackgroundType {
Opaque,
Media(BackgroundMedia),
Transparent,
+ #[cfg(target_family = "unix")]
Blurred,
#[cfg(target_os = "windows")]
Acrylic,
@@ -84,6 +84,7 @@ impl<'de> serde::Deserialize<'de> for BackgroundType {
match option_value.to_lowercase().as_str() {
"opaque" => BackgroundType::Opaque,
"transparent" => BackgroundType::Transparent,
+ #[cfg(target_family = "unix")]
"blurred" => BackgroundType::Blurred,
#[cfg(target_os = "windows")]
"acrylic" => BackgroundType::Acrylic,
@@ -127,13 +128,13 @@ impl Default for CursorType {
#[derive(Debug, Serialize, Clone)]
pub struct BackgroundMedia {
blur: RangedInt<0, 20, 0>,
- location: String,
+ pub location: String,
}
impl BackgroundMedia {
- fn deserialize_from_string(value: String) -> Option {
+ pub fn deserialize_from_string(value: String) -> Option {
if let Ok(file) = std::fs::read(&value) {
- if infer::is_image(&file) || infer::is_video(&file) {
+ if infer::is_image(&file) {
Some(Self {
location: value,
blur: RangedInt::default(),
@@ -156,18 +157,15 @@ impl<'de> serde::Deserialize<'de> for BackgroundMedia {
}
let partial_background_media = PartialBackgroundMedia::deserialize(deserializer)?;
-
- if let Ok(file) = std::fs::read(&partial_background_media.location) {
- if infer::is_image(&file) || infer::is_video(&file) {
- Ok(Self {
- blur: partial_background_media.blur.unwrap_or_default(),
- location: partial_background_media.location,
- })
- } else {
- Err(D::Error::custom("Format not supported"))
- }
+ if std::fs::read(&partial_background_media.location)
+ .is_ok_and(|file| infer::is_image(&file))
+ {
+ Ok(Self {
+ blur: partial_background_media.blur.unwrap_or_default(),
+ location: partial_background_media.location,
+ })
} else {
- Err(D::Error::custom("Cannot read file"))
+ Err(D::Error::custom("File not found or format not supported"))
}
}
-}
\ No newline at end of file
+}
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 51d4b15..3fae476 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -1,6 +1,6 @@
pub mod command;
-pub mod configuration;
pub mod common;
+pub mod configuration;
pub mod logger;
pub mod pty;
pub mod state;
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index c974264..2027321 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -7,13 +7,14 @@ use notify::Watcher;
use tauri::Manager;
use tess::command::{option::*, term::*, window::*};
use tess::configuration::deserialized::Option;
+use tess::configuration::types::BackgroundType;
use tess::logger::Logger;
use std::sync::{Arc, Mutex};
fn main() {
let start = std::time::Instant::now();
- let logger = Arc::from(Logger {});
+ let logger = Logger{};
#[cfg(target_family = "unix")]
let config_file = std::fs::read_to_string(format!(
@@ -26,7 +27,6 @@ fn main() {
dirs_next::config_dir().unwrap_or_default().display()
));
-
let option = Arc::from(Mutex::from(if let Ok(config_file) = config_file {
// TODO: Log error
serde_json::from_str(&config_file).unwrap_or_default()
@@ -51,11 +51,40 @@ fn main() {
let app_handle = app.as_ref().unwrap().app_handle();
+ match &option.lock().unwrap().background {
+ BackgroundType::Media(media) => {
+ app_handle.fs_scope().allow_file(&media.location);
+ }
+ #[cfg(target_family = "unix")]
+ BackgroundType::Blurred => {
+ todo!()
+ }
+ #[cfg(target_os = "windows")]
+ BackgroundType::Mica => {
+ if let Err(_) = window_vibrancy::apply_mica(app_handle.get_window("main").unwrap()) {
+ logger.warn("Cannot apply mica background effect. Switching back to transparent background");
+ }
+ }
+ #[cfg(target_os = "windows")]
+ BackgroundType::Acrylic => {
+ if let Err(_) = window_vibrancy::apply_acrylic(app_handle.get_window("main").unwrap(), None) {
+ logger.warn("Cannot apply acrylic background effect. Switching back to transparent background");
+ }
+ }
+ #[cfg(target_os = "macos")]
+ BackgroundType::Vibrancy => {
+ todo!()
+ }
+ _ => {}
+ }
+
+ app_handle.get_window("main").unwrap().set_decorations(true);
+
+
app_handle
.fs_scope()
.allow_file(&option.lock().unwrap().app_theme);
- let logger_clone = logger.clone();
let mut watcher =
notify::recommended_watcher(move |res: Result| {
@@ -77,7 +106,7 @@ fn main() {
*reff = option;
- logger_clone.info("Refreshing config...");
+ logger.info("Refreshing config...");
app_handle.emit_all("global_config_updated", format!("{:?}", reff));
}
diff --git a/src-tauri/src/pty.rs b/src-tauri/src/pty.rs
index 97e7570..aaf4da8 100644
--- a/src-tauri/src/pty.rs
+++ b/src-tauri/src/pty.rs
@@ -1,23 +1,35 @@
-use portable_pty::{native_pty_system, CommandBuilder, PtySize};
use portable_pty::Child;
-use std::sync::{Arc, Mutex};
+use portable_pty::{native_pty_system, CommandBuilder, PtySize};
+use std::sync::{Arc, Mutex, RwLock};
use crate::common::errors::PtyError;
-use std::sync::mpsc::{Receiver, Sender, TryRecvError};
+use crate::configuration::deserialized::Profile;
use tauri::{AppHandle, Manager};
-use crate::common::payloads::PtySendData;
+use crate::common::payloads::{PtySendData, PtyTitleChanged};
+#[cfg(target_os = "windows")]
+use crate::common::utils::get_leader_process_name;
#[cfg(target_os = "windows")]
use lazy_static::lazy_static;
#[cfg(target_os = "windows")]
-use regex::{Regex, Captures};
+use regex::{Captures, Regex};
+#[cfg(target_os = "windows")]
+use remoteprocess::Process;
+#[cfg(target_os = "windows")]
+use std::ffi::OsString;
pub struct Pty {
pub app: Arc,
- pair: Arc,
- close_channel_sender: Sender<()>,
+ pair: Arc>,
child: Option>>>,
+ #[cfg(target_family = "unix")]
+ current_leader: Arc>,
+ #[cfg(target_os = "windows")]
+ leader_programm_name: Arc>,
+ writer: Option>,
+ is_running: Arc>,
+ title_is_running_process: Arc>,
}
unsafe impl Send for Pty {}
@@ -26,31 +38,35 @@ unsafe impl Sync for Pty {}
impl Pty {
pub fn new(
app: Arc,
- cmd: &str,
+ profile: Profile,
cols: u16,
rows: u16,
id: String,
) -> Result {
let pty_system = native_pty_system();
- let pair = pty_system.openpty(PtySize {
- rows: rows,
- cols: cols,
+ if let Ok(pair) = pty_system.openpty(PtySize {
+ rows,
+ cols,
pixel_width: 0,
pixel_height: 0,
- });
-
- let (sender, receiver) = std::sync::mpsc::channel::<()>();
-
- if let Ok(pair) = pair {
+ }) {
let mut pty = Pty {
- app: app,
- pair: Arc::new(pair),
+ app,
+ pair: Arc::new(Mutex::from(pair)),
child: None,
- close_channel_sender: sender,
+ #[cfg(target_family = "unix")]
+ current_leader: Arc::new(RwLock::new(0)),
+ #[cfg(target_os = "windows")]
+ leader_programm_name: Arc::new(Mutex::from(OsString::new())),
+ writer: None,
+ is_running: Arc::new(RwLock::new(true)),
+ title_is_running_process: Arc::new(RwLock::new(
+ profile.terminal_options.title_is_running_process,
+ )),
};
- pty.run(cmd, id, receiver)?;
+ pty.run(profile, id)?;
Ok(pty)
} else {
@@ -60,77 +76,161 @@ impl Pty {
}
}
- pub fn run(
- &mut self,
- cmd: &str,
- id: String,
- close_channel_receiver: Receiver<()>,
- ) -> Result<(), PtyError> {
+ pub fn run(&mut self, profile: Profile, id: String) -> Result<(), PtyError> {
#[cfg(target_os = "windows")]
lazy_static! {
static ref PROGRAMM_PARSING_REGEX: Regex = Regex::new("%([[:word:]]*)%").unwrap();
}
#[cfg(target_os = "windows")]
- let cmd: String = PROGRAMM_PARSING_REGEX.replace_all(&cmd, |env_variable: &Captures| {
- std::env::var(&env_variable[1]).unwrap_or_default()
- }).into();
+ let cmd: String = PROGRAMM_PARSING_REGEX
+ .replace_all(&profile.command, |env_variable: &Captures| {
+ std::env::var(&env_variable[1]).unwrap_or_default()
+ })
+ .into();
+ #[cfg(target_family = "unix")]
+ let cmd = profile.command;
+
+ #[allow(unused_mut)]
let mut command_builder = CommandBuilder::from_argv(Vec::from_iter(
- cmd.split(' ').map(|s| std::ffi::OsString::from(s)),
+ cmd.split(' ')
+ .map(std::ffi::OsString::from),
));
#[cfg(target_family = "unix")]
command_builder.env("TERM", "xterm-256color");
- if let Ok(child) = self.pair.slave.spawn_command(command_builder) {
+ let locked_pair = self.pair.lock().unwrap();
+
+ if let Ok(child) = locked_pair.slave.spawn_command(command_builder) {
+ #[cfg(target_os = "windows")]
+ let tmp_pty_pid = child.process_id().unwrap();
+
let child = Arc::from(Mutex::from(child));
let child_clone = child.clone();
- if let Ok(mut reader) = self.pair.clone().master.try_clone_reader() {
+ if let Ok(mut reader) = locked_pair.master.try_clone_reader() {
let app = self.app.as_ref().clone();
let cloned_app = app.clone();
let id_cloned = id.clone();
- std::thread::spawn(move || loop {
- match close_channel_receiver.try_recv() {
- Err(TryRecvError::Empty) => {
- let mut buffer = [0; 4096];
-
- if reader.read(&mut buffer).is_ok() {
- app.emit_all(
- "terminalData",
- PtySendData {
- data: String::from_utf8(buffer.to_vec())
- .unwrap_or_default(),
- id: id.clone(),
- },
- )
- .ok();
- }
- }
- _ => {
- break;
+ self.child = Some(child);
+ self.writer = Some(locked_pair.master.take_writer().unwrap());
+
+ let is_running = self.is_running.clone();
+ let is_running_cloned = is_running.clone();
+
+ let title_is_running_process = self.title_is_running_process.clone();
+
+ #[cfg(target_os = "windows")]
+ let leader_programm_name = self.leader_programm_name.clone();
+
+ #[cfg(target_family = "unix")]
+ let cloned_pair = self.pair.clone();
+ #[cfg(target_family = "unix")]
+ let current_process_leader_pid = self.current_leader.clone();
+
+ std::thread::spawn(move || {
+ while *is_running.read().unwrap() {
+ let mut buffer = [0; 4096];
+
+ if reader.read(&mut buffer).is_ok() {
+ app.emit_all(
+ "terminalData",
+ PtySendData {
+ data: String::from_utf8(buffer.to_vec()).unwrap_or_default(),
+ id: id.clone(),
+ },
+ )
+ .ok();
}
}
});
- std::thread::spawn(move || loop {
- if let Ok(Some(_)) = child_clone.as_ref().lock().unwrap().try_wait() {
- cloned_app
- .emit_all("terminal_closed", id_cloned.clone())
- .ok();
+ std::thread::spawn(move || {
+ while *is_running_cloned.read().unwrap() {
+ if let Ok(Some(_)) = child_clone.as_ref().lock().unwrap().try_wait() {
+ *is_running_cloned.write().unwrap() = false;
- break;
- }
+ cloned_app
+ .emit_all("terminal_closed", id_cloned.clone())
+ .ok();
+
+ break;
+ } else if *title_is_running_process.read().unwrap() {
+ #[cfg(target_os = "windows")]
+ {
+ let leader_process_name =
+ get_leader_process_name(Process::new(tmp_pty_pid).unwrap());
+
+ if let Some(new_leader_process_name) = leader_process_name {
+ let mut leader_programm_name_locked =
+ leader_programm_name.lock().unwrap();
+
+ if new_leader_process_name != *leader_programm_name_locked {
+ *leader_programm_name_locked = new_leader_process_name;
+
+ cloned_app
+ .emit_all(
+ "terminalTitleChanged",
+ PtyTitleChanged {
+ id: id_cloned.clone(),
+ title: leader_programm_name_locked
+ .clone()
+ .into_string()
+ .unwrap(),
+ },
+ )
+ .ok();
+ }
+ }
+ }
+
+ #[cfg(target_family = "unix")]
+ {
+ let cloned_locked_pair = cloned_pair.lock().unwrap();
+
+ if let Some(process_leader_pid) =
+ cloned_locked_pair.master.process_group_leader()
+ {
+ if process_leader_pid
+ != *current_process_leader_pid.read().unwrap()
+ {
+ std::thread::sleep(std::time::Duration::from_millis(5));
+
+ *current_process_leader_pid.write().unwrap() =
+ process_leader_pid;
+
+ if let Ok(mut process_leader_title) =
+ std::fs::read_to_string(format!(
+ "/proc/{}/comm",
+ process_leader_pid
+ ))
+ {
+ process_leader_title.pop();
+
+ cloned_app
+ .emit_all(
+ "terminalTitleChanged",
+ PtyTitleChanged {
+ id: id_cloned.clone(),
+ title: process_leader_title,
+ },
+ )
+ .ok();
+ }
+ }
+ }
+ }
+ }
- std::thread::sleep(std::time::Duration::from_millis(5));
+ std::thread::sleep(std::time::Duration::from_millis(10));
+ }
});
};
- self.child = Some(child);
-
Ok(())
} else {
Err(PtyError::Create(String::from(
@@ -140,11 +240,7 @@ impl Pty {
}
pub fn write(&mut self, content: String) -> Result<(), PtyError> {
- if let Ok(()) = write!(
- self.pair.clone().master.try_clone_writer().unwrap(),
- "{}",
- content
- ) {
+ if let Ok(()) = write!(self.writer.as_mut().unwrap(), "{}", content) {
Ok(())
} else {
Err(PtyError::Write(String::from(
@@ -154,12 +250,18 @@ impl Pty {
}
pub fn resize(&self, cols: u16, rows: u16) -> Result<(), PtyError> {
- if let Ok(()) = self.pair.master.resize(portable_pty::PtySize {
- cols,
- rows,
- pixel_height: 0,
- pixel_width: 0,
- }) {
+ if let Ok(()) = self
+ .pair
+ .lock()
+ .unwrap()
+ .master
+ .resize(portable_pty::PtySize {
+ cols,
+ rows,
+ pixel_height: 0,
+ pixel_width: 0,
+ })
+ {
Ok(())
} else {
Err(PtyError::Resize(String::from(
@@ -170,37 +272,21 @@ impl Pty {
pub fn close(&mut self) -> Result<(), PtyError> {
if let Some(child) = self.child.as_deref() {
+ *self.is_running.write().unwrap() = false;
+
if let Ok(mut child) = child.lock() {
- if let Ok(()) = child.kill() {
- if let Ok(()) = self.close_channel_sender.send(()) {
- Ok(())
- } else {
- Err(PtyError::Kill(String::from(
- "process didn't terminate correctly.",
- )))
- }
- } else if let Ok(_) = child.try_wait() {
- if let Ok(()) = self.close_channel_sender.send(()) {
- Ok(())
- } else {
- Err(PtyError::Kill(String::from(
- "process didn't terminate correctly.",
- )))
- }
+ if child.try_wait().is_ok_and(|x| x.is_some()) {
+ Ok(())
+ } else if let Ok(()) = child.kill() {
+ Ok(())
} else {
- Err(PtyError::Kill(String::from(
- "process didn't terminate correctly.",
- )))
+ todo!()
}
} else {
- Err(PtyError::Kill(String::from(
- "doesn't have access to the pty.",
- )))
+ todo!()
}
} else {
- Err(PtyError::Kill(String::from(
- "doesn't have access to the pty.",
- )))
+ todo!()
}
}
}
diff --git a/src-tauri/src/state/pty_manager.rs b/src-tauri/src/state/pty_manager.rs
index 846ccaf..61139f5 100644
--- a/src-tauri/src/state/pty_manager.rs
+++ b/src-tauri/src/state/pty_manager.rs
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::sync::Arc;
+use crate::configuration::deserialized::Profile;
use crate::pty::Pty;
use crate::common::errors::PtyError;
@@ -23,10 +24,10 @@ impl PtyManager {
cols: u16,
rows: u16,
id: String,
- cmd: &str,
+ profile: Profile,
) -> Result<(), PtyError> {
if let Some(app_ref) = self.app.as_ref() {
- let pty = Pty::new(app_ref.clone(), cmd, cols, rows, id.clone())?;
+ let pty = Pty::new(app_ref.clone(), profile, cols, rows, id.clone())?;
self.ptys.insert(id, pty);
@@ -38,33 +39,32 @@ impl PtyManager {
}
}
- pub fn write(&mut self, id: String, content: String) -> Result<(), PtyError> {
+ pub fn write(&mut self, id: &str, content: String) -> Result<(), PtyError> {
self.ptys
- .get_mut(&id)
- .ok_or(PtyError::Write(String::from(
+ .get_mut(id)
+ .ok_or_else(|| PtyError::Write(String::from(
"Unable to access to the terminal.",
)))?
.write(content)?;
Ok(())
}
- pub fn resize(&mut self, id: String, cols: u16, rows: u16) -> Result<(), PtyError> {
+ pub fn resize(&mut self, id: &str, cols: u16, rows: u16) -> Result<(), PtyError> {
self.ptys
- .get_mut(&id)
- .ok_or(PtyError::Resize(String::from(
+ .get_mut(id)
+ .ok_or_else(|| PtyError::Resize(String::from(
"Unable to access to the terminal.",
)))?
.resize(cols, rows)?;
Ok(())
}
- pub fn close(&mut self, id: String) -> Result<(), PtyError> {
+ pub fn close(&mut self, id: &str) -> Result<(), PtyError> {
self.ptys
- .get_mut(&id)
- .ok_or(PtyError::Kill(String::from(
+ .remove(id)
+ .ok_or_else(|| PtyError::Kill(String::from(
"Unable to access to the terminal.",
)))?
- .close()?;
- Ok(())
+ .close()
}
}
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index ac7157f..1fc66d4 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -62,7 +62,9 @@
"height": 720,
"resizable": true,
"title": "Tess",
- "width": 1280
+ "width": 1280,
+ "transparent": true,
+ "decorations": false
}
]
}
diff --git a/src/style/style.scss b/src/style/style.scss
index 4c3c7cf..2867b6a 100644
--- a/src/style/style.scss
+++ b/src/style/style.scss
@@ -46,6 +46,7 @@ body {
background: var(--app-background);
}
+
::-webkit-scrollbar {
height: 4px;
width: 4px;
@@ -54,4 +55,16 @@ body {
::-webkit-scrollbar-thumb {
background:rgba(212, 212, 212, 0.14);
border-radius: 6px;
+}
+
+
+.background-image {
+ height: 100%;
+ width: 100%;
+ z-index: -4;
+ object-fit: cover;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ position: absolute;
}
\ No newline at end of file
diff --git a/src/style/tab.scss b/src/style/tab.scss
index 99f3e67..35a379d 100644
--- a/src/style/tab.scss
+++ b/src/style/tab.scss
@@ -48,6 +48,7 @@
width: calc(100% - 64px);
text-align: center;
font-weight: 600;
+ line-height: 12px;
}
.close {
diff --git a/src/style/target/linux.scss b/src/style/target/linux.scss
index 729e375..15481f1 100644
--- a/src/style/target/linux.scss
+++ b/src/style/target/linux.scss
@@ -1,3 +1,7 @@
.views .view .pane .internal-term canvas {
will-change: transform;
+}
+
+.background-image {
+ will-change: transform; // Require to force GPU acceleration to improve blur performance.
}
\ No newline at end of file
diff --git a/src/style/term.scss b/src/style/term.scss
index c5adc2f..56a215b 100644
--- a/src/style/term.scss
+++ b/src/style/term.scss
@@ -18,10 +18,24 @@
width: 100%;
height: 100%;
display: flex;
+ position: relative;
+
+ .background-image {
+ z-index: -2;
+ }
.internal-term {
height: calc(100% - 30px);
width: 100%;
+
+ &::before {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ content: "";
+ background: var(--profile-background);
+ opacity: var(--profile-background-transparency);
+ }
}
}
}
diff --git a/src/ts/class/panes.ts b/src/ts/class/panes.ts
index 7d8bdeb..9a58ade 100644
--- a/src/ts/class/panes.ts
+++ b/src/ts/class/panes.ts
@@ -1,16 +1,21 @@
-import { invoke } from '@tauri-apps/api/tauri';
+import { invoke, convertFileSrc } from '@tauri-apps/api/tauri';
import { Terminal } from "./terminal";
import { Profile } from 'ts/schema/option';
export class TerminalPane {
element: Element;
id: string;
+ profile: Profile;
term: Terminal | null = null;
- constructor(id: string) {
+ title: String
+
+ constructor(id: string, profile: Profile) {
+ this.profile = profile
this.element = this.generateComponents();
this.id = id;
+ this.title = profile.name;
}
generateComponents() : Element {
@@ -18,16 +23,29 @@ export class TerminalPane {
element.classList.add("pane");
let internalTerm = document.createElement("div");
- internalTerm.classList.add("internal-term")
+ internalTerm.classList.add("internal-term");
+
+ if (this.profile.background) {
+ let background = document.createElement("img");
+ background.src = convertFileSrc(this.profile.background.location);
+ background.classList.add("background-image");
+ element.appendChild(background);
+
+ internalTerm.style.setProperty("-webkit-backdrop-filter", `blur(${this.profile.background.blur}px)`)
+ }
+
+ internalTerm.style.setProperty("--profile-background", this.profile.theme.background);
+ internalTerm.style.setProperty("--profile-background-transparency", `${this.profile.backgroundTransparency}%`);
+
element.appendChild(internalTerm);
return element
}
- async initializeTerm(profile: Profile) {
- let terminal = new Terminal(this.id, profile.terminalOptions, profile.theme);
+ async initializeTerm() {
+ let terminal = new Terminal(this.id, this.profile.terminalOptions, this.profile.theme);
- await terminal.launch(this.element.querySelector(".internal-term")!, profile.command);
+ await terminal.launch(this.element.querySelector(".internal-term")!, this.profile.uuid);
this.term = terminal;
}
@@ -61,10 +79,13 @@ export class PagePane {
id: string
element: Element | null = null;
+ title: String
+
constructor(id: string) {
// TODO: Implement
this.id = id;
+ this.title = "";
}
async close() {
diff --git a/src/ts/class/tab.ts b/src/ts/class/tab.ts
index f65cb11..ee333eb 100644
--- a/src/ts/class/tab.ts
+++ b/src/ts/class/tab.ts
@@ -48,8 +48,6 @@ export class Tab {
}
private generateComponents() : HTMLElement {
- // TODO: Finish implementing this
-
let tab = document.createElement("div");
let title = document.createElement("span");
diff --git a/src/ts/class/terminal.ts b/src/ts/class/terminal.ts
index b9a86cb..baf1403 100644
--- a/src/ts/class/terminal.ts
+++ b/src/ts/class/terminal.ts
@@ -8,17 +8,21 @@ import { TerminalOptions, TerminalTheme } from "ts/schema/option";
export class Terminal {
id: string;
term: Xterm;
- fitAddon: FitAddon
+ fitAddon: FitAddon;
+
constructor(id: string, options: TerminalOptions, theme: TerminalTheme) {
// TODO: Finish
// TODO: Load all addons
+ theme = Object.assign({}, theme);
+ theme.background = "transparent";
this.id = id;
this.term = new Xterm({
allowProposedApi: true,
fontFamily: "Fira Code, monospace",
+ allowTransparency: true,
fontSize: options.fontSize,
drawBoldTextInBrightColors: options.drawBoldInBright,
cursorBlink: options.cursorBlink,
@@ -33,16 +37,6 @@ export class Terminal {
this.fitAddon = new FitAddon();
- addEventListener("resize", () => {
- this.fitAddon.fit();
- invoke("resize_terminal", {cols: this.term.cols, rows: this.term.rows, id: this.id}).catch((error) => {
- // TODO: Send notification with error message
-
- console.error(error);
- });
- })
-
-
this.term.attachCustomKeyEventHandler((e) => {
if (e.key == "F10") {
invoke("terminal_input", {content: "\x1b[21~", id: id});
@@ -54,8 +48,17 @@ export class Terminal {
})
}
- async launch(target: HTMLElement, command: string) {
- await invoke("create_terminal", {cols: this.term.cols, rows: this.term.rows, id: this.id, command: command})
+ async launch(target: HTMLElement, profile_id: string) {
+ target.addEventListener("resize", () => {
+ this.fitAddon.fit();
+ invoke("resize_terminal", {cols: this.term.cols, rows: this.term.rows, id: this.id}).catch((error) => {
+ // TODO: Send notification with error message
+
+ console.error(error);
+ });
+ })
+
+ await invoke("create_terminal", {cols: this.term.cols, rows: this.term.rows, id: this.id, profileUuid: profile_id})
this.term.open(target);
diff --git a/src/ts/class/views.ts b/src/ts/class/views.ts
index 4508c08..56e2842 100644
--- a/src/ts/class/views.ts
+++ b/src/ts/class/views.ts
@@ -11,18 +11,17 @@ export class View {
panes: (TerminalPane|PagePane)[] = []
closedEvent: ((id: string) => void) | undefined;
+ focusedPaneTitleChangedEvent: ((title: string) => void) | undefined;
- async buildNew(viewId: string, paneId: string, closedEvent: ((id: string) => void), profile: Profile) {
+ async buildNew(viewId: string, paneId: string, closedEvent: ((id: string) => void), profile: Profile, focusedPaneTitleChangedEvent: ((title: string) => void)) {
this.id = viewId;
this.element = this.generateComponents();
this.closedEvent = closedEvent;
+ this.focusedPaneTitleChangedEvent = focusedPaneTitleChangedEvent;
-
-
- let pane = new TerminalPane(paneId);
-
- await pane.initializeTerm(profile);
+ let pane = new TerminalPane(paneId, profile);
+ await pane.initializeTerm();
this.panes.push(pane)
this.element.appendChild(pane.element);
@@ -59,8 +58,6 @@ export class View {
if (this.panes.length == 0) {
this.closedEvent!(this.id!);
}
-
- // TODO: Implement
}
writeToTerm(termId: string, data: string) {
@@ -97,6 +94,14 @@ export class View {
})
}
+ updatePaneTitle(id: string, title: string) {
+ let pane = this.panes.find((pane) => pane.id == id);
+ if (pane) {
+ pane.title = title;
+ this.focusedPaneTitleChangedEvent!(title)
+ }
+ }
+
/*openPane(split: "horizontaly" | "vertically", paneId: string, subviewToSplit: string) {
// TODO: Implement
}*/
diff --git a/src/ts/main.ts b/src/ts/main.ts
index 858da0b..ad0da92 100644
--- a/src/ts/main.ts
+++ b/src/ts/main.ts
@@ -9,21 +9,32 @@ window.addEventListener("contextmenu", (e) => {
})
invoke("get_configuration").then((option) => {
+ if (option.background != "opaque" && !(option.background instanceof Object)) {
+ document.body.style.background = "transparent";
+ } else if (option.background instanceof Object) {
+ let background = document.createElement("img");
+ background.src = convertFileSrc(option.background.media.location);
+ background.classList.add("background-image");
+ background.style.setProperty("-webkit-filter", `blur(${option.background.media.blur}px)`);
+ document.body.appendChild(background)
+ }
+
+
let stylesheet = document.createElement("link");
stylesheet.type = "text/css";
stylesheet.rel = "stylesheet";
stylesheet.href = convertFileSrc(option.appTheme);
document.head.appendChild(stylesheet);
- let viewsManager = new ViewsManager(document.querySelector(".views")!, document.querySelector(".tabs")!, document.querySelector(".toasts")!, option);
+ let viewsManager = new ViewsManager(document.querySelector(".views")!, document.querySelector(".tabs")!, document.querySelector(".toasts")!, option);
viewsManager.openProfile(option.defaultProfile.uuid, true)
-
document.querySelector(".open")!.addEventListener("click", () => {
viewsManager.openProfile(option.defaultProfile.uuid, true);
})
+
listen("global_config_updated", (e) => {
console.log("Updated config:", e.payload);
});
diff --git a/src/ts/manager/view.ts b/src/ts/manager/view.ts
index 294c102..05d702b 100644
--- a/src/ts/manager/view.ts
+++ b/src/ts/manager/view.ts
@@ -3,7 +3,7 @@ import { listen, Event } from '@tauri-apps/api/event'
import { v4 as uuid } from 'uuid';
import { invoke } from '@tauri-apps/api/tauri'
-import { terminalDataPayload } from "../schema/term";
+import { terminalDataPayload, terminalTitleChangedPayload } from "../schema/term";
import { View } from "../class/views";
import { Toaster } from "./toast";
import { Option } from "ts/schema/option";
@@ -28,6 +28,7 @@ export class ViewsManager {
this.tabsManager.addEventListener("tabFocused", (id) => { this.onTabFocused(id); });
listen("terminalData", (e) => { this.onTerminalReceiveData(e); });
+ listen("terminalTitleChanged", (e) => { this.onTerminalTitleChanged(e); })
listen("terminal_closed", (e) => { this.onTerminalProcessExited(e); });
this.toaster = new Toaster(toastTarget);
@@ -80,6 +81,14 @@ export class ViewsManager {
})
}
+ private onTerminalTitleChanged(e: Event) {
+ this.views.forEach((view) => {
+ if (view.getTerm(e.payload.id)) {
+ view.updatePaneTitle(e.payload.id, e.payload.title)
+ }
+ })
+ }
+
private onTerminalPaneInput(id: string, data: string) {
invoke("terminal_input", {content: data, id: id});
}
@@ -103,7 +112,7 @@ export class ViewsManager {
let profile = this.option.profiles.find(profile => profile.uuid == profileId);
if (profile) {
- view.buildNew(viewId, paneId, (id) => {this.tabsManager.closeTab(id)}, profile).then(() => {
+ view.buildNew(viewId, paneId, (id) => {this.tabsManager.closeTab(id)}, profile, (title) => {this.tabsManager.setTitle(viewId, title)}).then(() => {
this.views.push(view);
this.target.appendChild(view.element!);
diff --git a/src/ts/schema/option.ts b/src/ts/schema/option.ts
index f5cdfa5..a45ab33 100644
--- a/src/ts/schema/option.ts
+++ b/src/ts/schema/option.ts
@@ -2,11 +2,11 @@ export type Option = {
appTheme : string,
closeConfirmation: boolean,
customTitlebar: boolean,
- backgroundTransparency: Number,
profiles: Profile[],
macros: Macro[],
shortcuts: Shortcut[],
- defaultProfile: Profile
+ defaultProfile: Profile,
+ background: "transparent" | "opaque" | "blurred" | "mica" | "acrylic" | "vibrancy" | {media: BackgroundMedia}
}
type Shortcut = {
@@ -42,6 +42,7 @@ export type Profile = {
theme: TerminalTheme,
uuid: string,
backgroundTransparency: number,
+ background: BackgroundMedia | null,
command: string
}
@@ -66,4 +67,9 @@ export type TerminalTheme = {
brightWhite: string,
cursor: string,
cursorAccent: string
+}
+
+export type BackgroundMedia = {
+ blur: number,
+ location: string
}
\ No newline at end of file
diff --git a/src/ts/schema/term.ts b/src/ts/schema/term.ts
index c4702c3..3951d21 100644
--- a/src/ts/schema/term.ts
+++ b/src/ts/schema/term.ts
@@ -1,4 +1,9 @@
export type terminalDataPayload = {
data: string,
id: string
+}
+
+export type terminalTitleChangedPayload = {
+ title: string,
+ id: string
}
\ No newline at end of file