feat: integrated terminal (#303)

* feat: integrated terminal

* fix: settings modal

* chore: make compatible for windows

* feat: block app when downloading

* docs: update README with new interface

* refactor: cleanup main.ts styling
This commit is contained in:
Maarten
2026-02-01 17:42:22 +01:00
committed by GitHub
parent 3192f1bdc9
commit f12000df52
14 changed files with 939 additions and 659 deletions

View File

@@ -12,54 +12,34 @@
<a href="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest"><img src="https://img.shields.io/badge/Download -ffbd03?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAABd0lEQVRoge2XwUrDQBCGPz3Ee++2UBSvdsGbeLHeFX0lPQhei+8g2AfwSVREK+LJ1oqHUGjRQ7Z0laRJNtldKPPBwoTM7D9/sks2IAiCsKrsAK/AC7DtSmTd1cTAKbAJtIAzVyIuDURGvOFKxKUBL4iB0IiB0IiB0IiB0IiB0IiB0IiB0IgBoA08Ak+AKpCvdO6AGn416zBwDGyRGLljuQmlc9pAEzipKl6HgVtgpOMGSYOdlLyOvtfQ1yPgpgb9WlDAEPjRYwj0jet+yv0iy80ru8AHiyazxiewF6jHXPJMeG3+CoiBy5J1/5dTlWVzAXwD5yXrAJho4YlFbdqesFnzsa6PLWr/PD0bFPAA3GO/YXN7WMspLpLnktweVvooMTPiKDPLHabmLCtpmYF3I96v3E55Doz4zWaCaxYb6Bno4udNRMCR1pzr92wmagFfZH+UfI0xycHPim5gE2Pg0Lb5OU2SVzgAph6anmqtHhWevCAIgh9+AdLMtu/CZhHJAAAAAElFTkSuQmCC" alt="Download latest release badge"></a>
<a href="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest"><img src="https://img.shields.io/github/downloads/mmvanheusden/SteamDepotDownloaderGUI/total?color=orange&label=downloads" alt="Download count badge"></a>
<img src="https://github.com/user-attachments/assets/2e1b1b8e-9560-4dde-86c0-b70384a54fbb" alt="Steam downgrader interface" style="max-width: 40%;"/>
<img src="https://github.com/user-attachments/assets/0a2debcc-617a-449d-971f-9e1cf1d5cb0b" alt="Steam downgrader interface" />
</div>
## Features
- **Cross-platform support**
| OS | Supported |
|---------|-----------|
| Windows | ✅ |
| Linux | ✅ |
| macOS | ✅ |
> ↓ *The downloader in action:*<br>
> <img src="https://github.com/user-attachments/assets/8739f06e-a258-48b2-a684-ca8fcf84dd7e" alt="Steam downgrader process" style="width: 25svw;"/>
- **Support for every major Linux terminal emulator**
<details><summary>List of supported terminals</summary>
### Cross-platform support
| OS | Supported |
|---------|-----------|
| Windows | ✅ |
| Linux | ✅ |
| macOS | ✅ |
* GNOME Terminal
* GNOME Console
* Konsole
* Xfce-terminal
* Alacritty
* XTerm
* Terminator
* cool-retro-term
* Kitty
* LXTerminal
* Deepin Terminal
* Terminology
* Tilix
</details>
- **Automatic download and extraction of DepotDownloader**
## How to download
## Installation
> [!CAUTION]
> This GitHub repository is the only official place to download this software.
> If you have paid for this software, or downloaded this from an untrusted place, **you are at risk** <sub><sup><sub><sup>and an idiot.<sub><sup><sub><sup>
> If you have paid for this software, or downloaded this from a different place than here, **you are an idiot and at risk**.
### Windows:
### Windows
Download the [latest Windows release](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest). There are multiple variants to choose from, but you are probably looking for the file that ends with **`.exe`**.
### Linux:
### Linux
You'll need at least one of the supported terminal emulators. You most likely already have one of these.
Download the [latest Linux release](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest). There are multiple options to choose from.
Download the [latest Linux release](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest). There are multiple formats to choose from.
## Tutorials
* https://www.youtube.com/watch?v=H2COwT5OUOo How to download older versions of Steam games tutorial
@@ -96,4 +76,3 @@ $ pnpm eslint --fix src/
<img src="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/assets/50550545/83f5f3b2-2bf9-41aa-ab87-880466f785fe" height="40px">
</a>
</p>

View File

@@ -11,11 +11,15 @@
"tauri": "tauri"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.18",
"@tauri-apps/api": "2.9.1",
"@tauri-apps/plugin-dialog": "2.6.0",
"@tauri-apps/plugin-opener": "~2.5.3",
"@tauri-apps/plugin-shell": "2.3.4",
"jquery": "^4.0.0"
"@xterm/addon-fit": "^0.11.0",
"@xterm/xterm": "^6.0.0",
"jquery": "^4.0.0",
"tailwindcss": "^4.1.18"
},
"devDependencies": {
"@eslint/js": "^9.39.2",

460
pnpm-lock.yaml generated
View File

@@ -8,6 +8,9 @@ importers:
.:
dependencies:
'@tailwindcss/vite':
specifier: ^4.1.18
version: 4.1.18(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))
'@tauri-apps/api':
specifier: 2.9.1
version: 2.9.1
@@ -20,9 +23,18 @@ importers:
'@tauri-apps/plugin-shell':
specifier: 2.3.4
version: 2.3.4
'@xterm/addon-fit':
specifier: ^0.11.0
version: 0.11.0
'@xterm/xterm':
specifier: ^6.0.0
version: 6.0.0
jquery:
specifier: ^4.0.0
version: 4.0.0
tailwindcss:
specifier: ^4.1.18
version: 4.1.18
devDependencies:
'@eslint/js':
specifier: ^9.39.2
@@ -35,16 +47,16 @@ importers:
version: 3.5.33
eslint:
specifier: ^9.39.2
version: 9.39.2
version: 9.39.2(jiti@2.6.1)
typescript:
specifier: ^5.9.3
version: 5.9.3
typescript-eslint:
specifier: ^8.54.0
version: 8.54.0(eslint@9.39.2)(typescript@5.9.3)
version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
vite:
specifier: ^7.3.1
version: 7.3.1
version: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2)
packages:
@@ -258,6 +270,22 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
'@jridgewell/remapping@2.3.5':
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@rollup/rollup-android-arm-eabi@4.57.1':
resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==}
cpu: [arm]
@@ -292,66 +320,79 @@ packages:
resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.57.1':
resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.57.1':
resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.57.1':
resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.57.1':
resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.57.1':
resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.57.1':
resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.57.1':
resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.57.1':
resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.57.1':
resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.57.1':
resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.57.1':
resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.57.1':
resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.57.1':
resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==}
@@ -383,6 +424,100 @@ packages:
cpu: [x64]
os: [win32]
'@tailwindcss/node@4.1.18':
resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==}
'@tailwindcss/oxide-android-arm64@4.1.18':
resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
'@tailwindcss/oxide-darwin-arm64@4.1.18':
resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@tailwindcss/oxide-darwin-x64@4.1.18':
resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@tailwindcss/oxide-freebsd-x64@4.1.18':
resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
bundledDependencies:
- '@napi-rs/wasm-runtime'
- '@emnapi/core'
- '@emnapi/runtime'
- '@tybys/wasm-util'
- '@emnapi/wasi-threads'
- tslib
'@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@tailwindcss/oxide-win32-x64-msvc@4.1.18':
resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@tailwindcss/oxide@4.1.18':
resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==}
engines: {node: '>= 10'}
'@tailwindcss/vite@4.1.18':
resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==}
peerDependencies:
vite: ^5.2.0 || ^6 || ^7
'@tauri-apps/api@2.9.1':
resolution: {integrity: sha512-IGlhP6EivjXHepbBic618GOmiWe4URJiIeZFlB7x3czM0yDHHYviH1Xvoiv4FefdkQtn6v7TuwWCRfOGdnVUGw==}
@@ -409,30 +544,35 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-arm64-musl@2.9.6':
resolution: {integrity: sha512-02TKUndpodXBCR0oP//6dZWGYcc22Upf2eP27NvC6z0DIqvkBBFziQUcvi2n6SrwTRL0yGgQjkm9K5NIn8s6jw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tauri-apps/cli-linux-riscv64-gnu@2.9.6':
resolution: {integrity: sha512-fmp1hnulbqzl1GkXl4aTX9fV+ubHw2LqlLH1PE3BxZ11EQk+l/TmiEongjnxF0ie4kV8DQfDNJ1KGiIdWe1GvQ==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-x64-gnu@2.9.6':
resolution: {integrity: sha512-vY0le8ad2KaV1PJr+jCd8fUF9VOjwwQP/uBuTJvhvKTloEwxYA/kAjKK9OpIslGA9m/zcnSo74czI6bBrm2sYA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-x64-musl@2.9.6':
resolution: {integrity: sha512-TOEuB8YCFZTWVDzsO2yW0+zGcoMiPPwcUgdnW1ODnmgfwccpnihDRoks+ABT1e3fHb1ol8QQWsHSCovb3o2ENQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tauri-apps/cli-win32-arm64-msvc@2.9.6':
resolution: {integrity: sha512-ujmDGMRc4qRLAnj8nNG26Rlz9klJ0I0jmZs2BPpmNNf0gM/rcVHhqbEkAaHPTBVIrtUdf7bGvQAD2pyIiUrBHQ==}
@@ -537,6 +677,12 @@ packages:
resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@xterm/addon-fit@0.11.0':
resolution: {integrity: sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==}
'@xterm/xterm@6.0.0':
resolution: {integrity: sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==}
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -600,6 +746,14 @@ packages:
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
enhanced-resolve@5.18.4:
resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==}
engines: {node: '>=10.13.0'}
esbuild@0.27.2:
resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==}
engines: {node: '>=18'}
@@ -697,6 +851,9 @@ packages:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'}
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -728,6 +885,10 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
jiti@2.6.1:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true
jquery@4.0.0:
resolution: {integrity: sha512-TXCHVR3Lb6TZdtw1l3RTLf8RBWVGexdxL6AC8/e0xZKEpBflBsjh9/8LXw+dkNFuOyW9B7iB3O1sP7hS0Kiacg==}
@@ -751,6 +912,80 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
lightningcss-android-arm64@1.30.2:
resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [android]
lightningcss-darwin-arm64@1.30.2:
resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
lightningcss-darwin-x64@1.30.2:
resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
lightningcss-freebsd-x64@1.30.2:
resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
lightningcss-linux-arm-gnueabihf@1.30.2:
resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
lightningcss-linux-arm64-gnu@1.30.2:
resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.2:
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
lightningcss-win32-x64-msvc@1.30.2:
resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
lightningcss@1.30.2:
resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
engines: {node: '>= 12.0.0'}
locate-path@6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
@@ -758,6 +993,9 @@ packages:
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@@ -853,6 +1091,13 @@ packages:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
tailwindcss@4.1.18:
resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
tapable@2.3.0:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
engines: {node: '>=6'}
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
@@ -1015,9 +1260,9 @@ snapshots:
'@esbuild/win32-x64@0.27.2':
optional: true
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)':
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))':
dependencies:
eslint: 9.39.2
eslint: 9.39.2(jiti@2.6.1)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
@@ -1072,6 +1317,25 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
'@jridgewell/trace-mapping': 0.3.31
'@jridgewell/remapping@2.3.5':
dependencies:
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/sourcemap-codec@1.5.5': {}
'@jridgewell/trace-mapping@0.3.31':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@rollup/rollup-android-arm-eabi@4.57.1':
optional: true
@@ -1147,6 +1411,74 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.57.1':
optional: true
'@tailwindcss/node@4.1.18':
dependencies:
'@jridgewell/remapping': 2.3.5
enhanced-resolve: 5.18.4
jiti: 2.6.1
lightningcss: 1.30.2
magic-string: 0.30.21
source-map-js: 1.2.1
tailwindcss: 4.1.18
'@tailwindcss/oxide-android-arm64@4.1.18':
optional: true
'@tailwindcss/oxide-darwin-arm64@4.1.18':
optional: true
'@tailwindcss/oxide-darwin-x64@4.1.18':
optional: true
'@tailwindcss/oxide-freebsd-x64@4.1.18':
optional: true
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
optional: true
'@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
optional: true
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
optional: true
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
optional: true
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
optional: true
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
optional: true
'@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
optional: true
'@tailwindcss/oxide-win32-x64-msvc@4.1.18':
optional: true
'@tailwindcss/oxide@4.1.18':
optionalDependencies:
'@tailwindcss/oxide-android-arm64': 4.1.18
'@tailwindcss/oxide-darwin-arm64': 4.1.18
'@tailwindcss/oxide-darwin-x64': 4.1.18
'@tailwindcss/oxide-freebsd-x64': 4.1.18
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18
'@tailwindcss/oxide-linux-arm64-gnu': 4.1.18
'@tailwindcss/oxide-linux-arm64-musl': 4.1.18
'@tailwindcss/oxide-linux-x64-gnu': 4.1.18
'@tailwindcss/oxide-linux-x64-musl': 4.1.18
'@tailwindcss/oxide-wasm32-wasi': 4.1.18
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.18
'@tailwindcss/oxide-win32-x64-msvc': 4.1.18
'@tailwindcss/vite@4.1.18(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))':
dependencies:
'@tailwindcss/node': 4.1.18
'@tailwindcss/oxide': 4.1.18
tailwindcss: 4.1.18
vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2)
'@tauri-apps/api@2.9.1': {}
'@tauri-apps/cli-darwin-arm64@2.9.6':
@@ -1218,15 +1550,15 @@ snapshots:
'@types/sizzle@2.3.10': {}
'@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.54.0
'@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.54.0
eslint: 9.39.2
eslint: 9.39.2(jiti@2.6.1)
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.4.0(typescript@5.9.3)
@@ -1234,14 +1566,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.54.0
'@typescript-eslint/types': 8.54.0
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.54.0
debug: 4.4.3
eslint: 9.39.2
eslint: 9.39.2(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -1264,13 +1596,13 @@ snapshots:
dependencies:
typescript: 5.9.3
'@typescript-eslint/type-utils@8.54.0(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/type-utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.54.0
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
debug: 4.4.3
eslint: 9.39.2
eslint: 9.39.2(jiti@2.6.1)
ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
@@ -1293,13 +1625,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.54.0(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2)
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
'@typescript-eslint/scope-manager': 8.54.0
'@typescript-eslint/types': 8.54.0
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
eslint: 9.39.2
eslint: 9.39.2(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -1309,6 +1641,10 @@ snapshots:
'@typescript-eslint/types': 8.54.0
eslint-visitor-keys: 4.2.1
'@xterm/addon-fit@0.11.0': {}
'@xterm/xterm@6.0.0': {}
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
@@ -1366,6 +1702,13 @@ snapshots:
deep-is@0.1.4: {}
detect-libc@2.1.2: {}
enhanced-resolve@5.18.4:
dependencies:
graceful-fs: 4.2.11
tapable: 2.3.0
esbuild@0.27.2:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.2
@@ -1406,9 +1749,9 @@ snapshots:
eslint-visitor-keys@4.2.1: {}
eslint@9.39.2:
eslint@9.39.2(jiti@2.6.1):
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2)
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.1
'@eslint/config-helpers': 0.4.2
@@ -1442,6 +1785,8 @@ snapshots:
minimatch: 3.1.2
natural-compare: 1.4.0
optionator: 0.9.4
optionalDependencies:
jiti: 2.6.1
transitivePeerDependencies:
- supports-color
@@ -1498,6 +1843,8 @@ snapshots:
globals@14.0.0: {}
graceful-fs@4.2.11: {}
has-flag@4.0.0: {}
ignore@5.3.2: {}
@@ -1519,6 +1866,8 @@ snapshots:
isexe@2.0.0: {}
jiti@2.6.1: {}
jquery@4.0.0: {}
js-yaml@4.1.1:
@@ -1540,12 +1889,65 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
lightningcss-android-arm64@1.30.2:
optional: true
lightningcss-darwin-arm64@1.30.2:
optional: true
lightningcss-darwin-x64@1.30.2:
optional: true
lightningcss-freebsd-x64@1.30.2:
optional: true
lightningcss-linux-arm-gnueabihf@1.30.2:
optional: true
lightningcss-linux-arm64-gnu@1.30.2:
optional: true
lightningcss-linux-arm64-musl@1.30.2:
optional: true
lightningcss-linux-x64-gnu@1.30.2:
optional: true
lightningcss-linux-x64-musl@1.30.2:
optional: true
lightningcss-win32-arm64-msvc@1.30.2:
optional: true
lightningcss-win32-x64-msvc@1.30.2:
optional: true
lightningcss@1.30.2:
dependencies:
detect-libc: 2.1.2
optionalDependencies:
lightningcss-android-arm64: 1.30.2
lightningcss-darwin-arm64: 1.30.2
lightningcss-darwin-x64: 1.30.2
lightningcss-freebsd-x64: 1.30.2
lightningcss-linux-arm-gnueabihf: 1.30.2
lightningcss-linux-arm64-gnu: 1.30.2
lightningcss-linux-arm64-musl: 1.30.2
lightningcss-linux-x64-gnu: 1.30.2
lightningcss-linux-x64-musl: 1.30.2
lightningcss-win32-arm64-msvc: 1.30.2
lightningcss-win32-x64-msvc: 1.30.2
locate-path@6.0.0:
dependencies:
p-locate: 5.0.0
lodash.merge@4.6.2: {}
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.12
@@ -1648,6 +2050,10 @@ snapshots:
dependencies:
has-flag: 4.0.0
tailwindcss@4.1.18: {}
tapable@2.3.0: {}
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
@@ -1661,13 +2067,13 @@ snapshots:
dependencies:
prelude-ls: 1.2.1
typescript-eslint@8.54.0(eslint@9.39.2)(typescript@5.9.3):
typescript-eslint@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
dependencies:
'@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3)
eslint: 9.39.2
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -1678,7 +2084,7 @@ snapshots:
dependencies:
punycode: 2.3.1
vite@7.3.1:
vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2):
dependencies:
esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.3)
@@ -1688,6 +2094,8 @@ snapshots:
tinyglobby: 0.2.15
optionalDependencies:
fsevents: 2.3.3
jiti: 2.6.1
lightningcss: 1.30.2
which@2.0.2:
dependencies:

99
src-tauri/Cargo.lock generated
View File

@@ -473,6 +473,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
@@ -855,6 +861,12 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "downcast-rs"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "dpi"
version = "0.1.2"
@@ -902,7 +914,7 @@ dependencies = [
"rustc_version",
"toml 0.9.11+spec-1.1.0",
"vswhom",
"winreg",
"winreg 0.55.0",
]
[[package]]
@@ -1020,6 +1032,17 @@ dependencies = [
"rustc_version",
]
[[package]]
name = "filedescriptor"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d"
dependencies = [
"libc",
"thiserror 1.0.69",
"winapi",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@@ -2258,6 +2281,18 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nix"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.10.0",
"cfg-if",
"cfg_aliases 0.1.1",
"libc",
]
[[package]]
name = "nodrop"
version = "0.1.14"
@@ -2842,6 +2877,27 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "portable-pty"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4a596a2b3d2752d94f51fac2d4a96737b8705dddd311a32b9af47211f08671e"
dependencies = [
"anyhow",
"bitflags 1.3.2",
"downcast-rs",
"filedescriptor",
"lazy_static",
"libc",
"log",
"nix",
"serial2",
"shared_library",
"shell-words",
"winapi",
"winreg 0.10.1",
]
[[package]]
name = "potential_utf"
version = "0.1.4"
@@ -2962,7 +3018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases",
"cfg_aliases 0.2.1",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
@@ -3003,7 +3059,7 @@ version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"cfg_aliases 0.2.1",
"libc",
"once_cell",
"socket2",
@@ -3703,6 +3759,17 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "serial2"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cc76fa68e25e771492ca1e3c53d447ef0be3093e05cd3b47f4b712ba10c6f3c"
dependencies = [
"cfg-if",
"libc",
"winapi",
]
[[package]]
name = "serialize-to-javascript"
version = "0.1.2"
@@ -3768,6 +3835,22 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "shared_library"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
dependencies = [
"lazy_static",
"libc",
]
[[package]]
name = "shell-words"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
[[package]]
name = "shlex"
version = "1.3.0"
@@ -4905,6 +4988,7 @@ version = "3.0.1"
dependencies = [
"derive-getters",
"fix-path-env",
"portable-pty",
"reqwest 0.13.1",
"serde",
"serde_json",
@@ -5641,6 +5725,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]
[[package]]
name = "winreg"
version = "0.55.0"

View File

@@ -22,6 +22,7 @@ derive-getters = "0.5.0"
reqwest = { version = "0.13.1",features = ["blocking"] }
zip = "7.2.0"
tauri-plugin-opener = "2"
portable-pty = "0.9.0"

View File

@@ -6,46 +6,41 @@ mod steam;
mod terminal;
use crate::depotdownloader::{get_depotdownloader_url, DEPOTDOWNLOADER_VERSION};
use crate::terminal::Terminal;
use std::env;
use crate::terminal::{async_read_from_pty, async_resize_pty, async_write_to_pty};
use portable_pty::{native_pty_system, PtyPair, PtySize};
use std::io::ErrorKind::AlreadyExists;
use std::io::{BufReader, Read, Write};
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use std::sync::{Arc, OnceLock};
use std::time::Duration;
use tauri::{AppHandle, Emitter, Manager};
use tauri_plugin_shell::ShellExt;
use std::{env, thread};
use tauri::async_runtime::Mutex;
use tauri::{AppHandle, Emitter, Manager, State};
struct AppState {
pty_pair: Arc<Mutex<PtyPair>>,
writer: Arc<Mutex<Box<dyn Write + Send>>>,
reader: Arc<Mutex<BufReader<Box<dyn Read + Send>>>>,
}
/// The first terminal found. Used as default terminal.
static TERMINAL: OnceLock<Vec<Terminal>> = OnceLock::new(); // We create this variable now, and quickly populate it in preload_vectum(). we then later access the data in start_download()
static WORKING_DIR: OnceLock<PathBuf> = OnceLock::new();
/// This function is called every time the app is reloaded/started. It quickly populates the [`TERMINAL`] variable with a working terminal.
#[tauri::command]
async fn preload_vectum(app: AppHandle) {
// Only fill these variables once.
if TERMINAL.get().is_none() {
TERMINAL.set(terminal::get_installed_terminals(true, app.shell()).await).expect("Failed to set available terminals")
}
if WORKING_DIR.get().is_none() {
WORKING_DIR.set(Path::join(&app.path().local_data_dir().unwrap(), "SteamDepotDownloaderGUI")).expect("Failed to configure working directory")
}
// Send the default terminal name to the frontend.
app.emit(
"default-terminal",
Terminal::pretty_name(&TERMINAL.get().unwrap()[0]),
).unwrap();
}
#[tauri::command]
async fn start_download(steam_download: steam::SteamDownload, app: AppHandle) {
let default_terminal = TERMINAL.get().unwrap();
let shell = app.shell();
let terminal_to_use = if steam_download.options().terminal().is_none() { default_terminal.first().unwrap() } else { &Terminal::from_index(&steam_download.options().terminal().unwrap()).unwrap() };
async fn start_download(steam_download: steam::SteamDownload, app: AppHandle, state: State<'_, AppState>) -> Result<(), String> {
// Also change working directory
std::env::set_current_dir(&WORKING_DIR.get().unwrap()).unwrap();
// std::env::set_current_dir(&WORKING_DIR.get().unwrap()).unwrap();
println!("\n-------------------------DEBUG INFO------------------------");
println!("received these values from frontend:");
@@ -55,13 +50,33 @@ async fn start_download(steam_download: steam::SteamDownload, app: AppHandle) {
println!("\t- Depot ID: {}", steam_download.depot_id());
println!("\t- Manifest ID: {}", steam_download.manifest_id());
println!("\t- Output Path: {}", steam_download.output_path());
println!("\t- Default terminal: {}", Terminal::pretty_name(&default_terminal[0]));
println!("\t- Working directory: {}", &WORKING_DIR.get().unwrap().display());
println!("\t- Terminal command: \n\t {:?}", terminal_to_use.create_command(&steam_download, shell, &WORKING_DIR.get().unwrap()));
println!("----------------------------------------------------------\n");
/* Build the command and spawn it in our terminal */
let mut cmd = terminal::create_depotdownloader_command(&steam_download, WORKING_DIR.get().unwrap());
terminal_to_use.create_command(&steam_download, shell, &WORKING_DIR.get().unwrap()).spawn().ok();
// add the $TERM env variable so we can use clear and other commands
#[cfg(target_os = "windows")]
cmd.env("TERM", "cygwin");
#[cfg(not(target_os = "windows"))]
cmd.env("TERM", "xterm-256color");
let mut child = state
.pty_pair
.lock()
.await
.slave
.spawn_command(cmd)
.map_err(|err| err.to_string())?;
thread::spawn(move || {
let status = child.wait().unwrap();
println!("Command exited with status: {status}");
app.emit("command-exited", {}).unwrap();
// exit(status.exit_code() as i32)
});
Ok(())
}
/// Downloads the DepotDownloader zip file from the internet based on the OS.
@@ -71,7 +86,7 @@ async fn download_depotdownloader() {
// Where we store the DepotDownloader zip.
let zip_filename = format!("DepotDownloader-v{}-{}.zip", DEPOTDOWNLOADER_VERSION, env::consts::OS);
let depotdownloader_zip = Path::join(&WORKING_DIR.get().unwrap(), Path::new(&zip_filename));
let depotdownloader_zip = Path::join(WORKING_DIR.get().unwrap(), Path::new(&zip_filename));
if let Err(e) = depotdownloader::download_file(url.as_str(), depotdownloader_zip.as_path()).await {
@@ -85,7 +100,7 @@ async fn download_depotdownloader() {
println!("Downloaded DepotDownloader for {} to {}", env::consts::OS, depotdownloader_zip.display());
}
depotdownloader::unzip(depotdownloader_zip.as_path(), &WORKING_DIR.get().unwrap()).unwrap();
depotdownloader::unzip(depotdownloader_zip.as_path(), WORKING_DIR.get().unwrap()).unwrap();
println!("Succesfully extracted DepotDownloader zip.");
}
@@ -97,17 +112,6 @@ async fn internet_connection() -> bool {
client.get("https://connectivitycheck.android.com/generate_204").send().await.is_ok()
}
#[tauri::command]
async fn get_all_terminals(app: AppHandle) {
let terminals = terminal::get_installed_terminals(false, app.shell()).await;
terminals.iter().for_each(|terminal| {
println!("Terminal #{} ({}) is installed!", terminal.index().unwrap(), terminal.pretty_name());
// Sends: (terminal index aligned with dropdown; total terminals)
app.emit("working-terminal", (terminal.index(), Terminal::total())).unwrap();
});
}
pub fn get_os() -> &'static str {
match env::consts::OS {
@@ -120,8 +124,9 @@ pub fn get_os() -> &'static str {
fn main() {
// macOS: change dir to documents because upon opening, our current dir by default is "/".
if get_os() == "macos" {
let _ = fix_path_env::fix(); // todo: does this actually do something useful
// todo: Is this still needed ??
/* if get_os() == "macos" {
let _ = fix_path_env::fix();
// let documents_dir = format!(
// "{}/Documents/SteamDepotDownloaderGUI",
// std::env::var_os("HOME").unwrap().to_str().unwrap()
@@ -131,14 +136,41 @@ fn main() {
// std::fs::create_dir_all(documents_dir).unwrap();
// env::set_current_dir(documents_dir).unwrap();
}
}*/
/* Initialize the pty system */
let pty_system = native_pty_system();
let pty_pair = pty_system
.openpty(PtySize {
rows: 24,
cols: 80,
pixel_width: 0,
pixel_height: 0,
})
.unwrap();
let reader = pty_pair.master.try_clone_reader().unwrap();
let writer = pty_pair.master.take_writer().unwrap();
println!();
tauri::Builder::default().plugin(tauri_plugin_opener::init()).plugin(tauri_plugin_dialog::init()).plugin(tauri_plugin_shell::init()).invoke_handler(tauri::generate_handler![
tauri::Builder::default()
.manage(AppState {
pty_pair: Arc::new(Mutex::new(pty_pair)),
writer: Arc::new(Mutex::new(writer)),
reader: Arc::new(Mutex::new(BufReader::new(reader))),
})
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![
start_download,
download_depotdownloader,
internet_connection,
preload_vectum,
get_all_terminals
]).run(tauri::generate_context!()).expect("error while running tauri application");
async_write_to_pty,
async_read_from_pty,
async_resize_pty,
]).run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -16,7 +16,6 @@ pub struct SteamDownload {
#[derive(Debug, Deserialize, Getters)]
pub struct VectumOptions {
terminal: Option<u8>,
output_directory: Option<PathBuf>,
directory_name: Option<String>
}

View File

@@ -1,309 +1,77 @@
use crate::get_os;
use crate::steam::SteamDownload;
use std::fs;
use std::io::BufRead;
use std::path::PathBuf;
use tauri::Wry;
use tauri_plugin_shell::process::Command;
use tauri_plugin_shell::Shell;
use portable_pty::{CommandBuilder, PtySize};
use tauri::State;
use crate::AppState;
use crate::steam::SteamDownload;
/// Represents a terminal that can be used to run commands.
/// **Should be in sync with the terminal dropdown in the frontend.**
#[derive(Debug, PartialEq)]
pub enum Terminal {
GNOMETerminal,
Alacritty,
Konsole,
GNOMEConsole,
Xfce4Terminal,
DeepinTerminal,
Terminator,
Kitty,
LXTerminal,
Tilix,
XTerm,
CMD,
Terminal
#[tauri::command]
pub async fn async_write_to_pty(data: &str, state: State<'_, AppState>) -> Result<(), ()> {
write!(state.writer.lock().await, "{}", data).map_err(|_| ())
}
#[tauri::command]
pub async fn async_read_from_pty(state: State<'_, AppState>) -> Result<Option<String>, ()> {
let mut reader = state.reader.lock().await;
let data = {
// Read all available text
let data = reader.fill_buf().map_err(|_| ())?;
impl Terminal {
/// Iterates through each terminal
pub fn iter() -> impl Iterator<Item=Terminal> {
use self::Terminal::*;
vec![
GNOMETerminal, Alacritty, Konsole, GNOMEConsole, Xfce4Terminal, DeepinTerminal, Terminator, Kitty, LXTerminal, Tilix, XTerm, CMD, Terminal
].into_iter()
}
/// Get terminal from index in order of the [`Terminal`] enum
pub fn from_index(index: &u8) -> Option<Terminal> {
Terminal::iter().nth(*index as usize)
}
/// Get the index of a terminal in the order of the [`Terminal`] enum
/// Returns `None` if the terminal is not found.
pub fn index(&self) -> Option<u8> {
Terminal::iter().position(|x| x == *self).map(|x| x as u8)
}
/// Get total number of terminals **possible** depending on the OS
pub fn total() -> u8 {
if get_os() == "windows" || get_os() == "macos" {
return 1;
// Send the data to the webview if necessary
if data.len() > 0 {
std::str::from_utf8(data)
.map(|v| Some(v.to_string()))
.map_err(|_| ())?
} else {
None
}
Terminal::iter().count() as u8 - 1 // -1 because cmd is not available on linux
}
/// Get the pretty name of a terminal
pub fn pretty_name(&self) -> &str {
match self {
Terminal::GNOMETerminal => "GNOME Terminal",
Terminal::GNOMEConsole => "GNOME Console",
Terminal::Konsole => "Konsole",
Terminal::Xfce4Terminal => "Xfce Terminal",
Terminal::Terminator => "Terminator",
Terminal::XTerm => "XTerm",
Terminal::Kitty => "Kitty",
Terminal::LXTerminal => "LXTerminal",
Terminal::Tilix => "Tilix",
Terminal::DeepinTerminal => "Deepin Terminal",
Terminal::Alacritty => "Alacritty",
Terminal::CMD => "cmd",
Terminal::Terminal => "Terminal"
}
}
//region Probing a terminal
/// Checks if a [`Terminal`] is installed.
/// **See:** [`get_installed_terminals`]
pub async fn installed(&self, shell: &Shell<Wry>) -> bool {
match self {
Terminal::CMD => get_os() == "windows",
Terminal::GNOMETerminal => shell.command("gnome-terminal").arg("--version").status().await.is_ok(),
Terminal::GNOMEConsole => shell.command("kgx").arg("--version").status().await.is_ok(),
Terminal::Konsole => shell.command("konsole").arg("--version").status().await.is_ok(),
Terminal::Xfce4Terminal => shell.command("xfce4-terminal").arg("--version").status().await.is_ok(),
Terminal::Terminator => shell.command("terminator").arg("--version").status().await.is_ok(),
Terminal::XTerm => shell.command("xterm").arg("-v").status().await.is_ok(),
Terminal::Kitty => shell.command("kitty").arg("--version").status().await.is_ok(),
Terminal::LXTerminal => shell.command("lxterminal").arg("--version").status().await.is_ok(),
Terminal::Tilix => shell.command("tilix").arg("--version").status().await.is_ok(),
Terminal::DeepinTerminal => shell.command("deepin-terminal").arg("--version").status().await.is_ok(),
Terminal::Alacritty => shell.command("alacritty").arg("--version").status().await.is_ok(),
Terminal::Terminal => get_os() == "macos",
}
}
//endregion
//region Running a command in the terminal
/**
Returns a [`Command`] that, when executed should open the terminal and run the command.
## Commands
`{command}` = `{command};echo Command finished with code $?;sleep infinity`
| Terminal | Command to open terminal |
|------------------|--------------------------------------------------------------------------|
| cmd | `start cmd.exe /k {command}` |
| GNOMETerminal | `gnome-terminal -- /usr/bin/env sh -c {command}` |
| GNOMEConsole | `kgx -e /usr/bin/env sh -c {command}` |
| Konsole | `konsole -e /usr/bin/env sh -c {command}` |
| Xfce4Terminal | `xfce4-terminal -x /usr/bin/env sh -c {command}` |
| Terminator | `terminator -T "Downloading depot..." -e {command}` |
| XTerm | `xterm -hold -T "Downloading depot..." -e /usr/bin/env sh -c {command}` |
| Kitty | `kitty /usr/bin/env sh -c {command}` |
| LXTerminal | `lxterminal -e /usr/bin/env sh -c {command}` |
| Tilix | `tilix -e /usr/bin/env sh -c {command}` |
| DeepinTerminal | `deepin-terminal -e /usr/bin/env sh -c {command}` |
| Alacritty | `alacritty -e /usr/bin/env sh -c {command}` |
| Terminal (macOS) | We create a bash script and run that using `open`. |
*/
pub fn create_command(&self, steam_download: &SteamDownload, shell: &Shell<Wry>, working_dir: &PathBuf) -> Command {
let command = create_depotdownloader_command(steam_download);
match self {
Terminal::CMD => {
return shell.command("cmd.exe").args(&["/c", "start", "PowerShell.exe", "-NoExit", "-Command"]).args(command);
/* let mut cmd = std::process::Command::new("cmd.exe");
cmd.args(&["/c", "start", "PowerShell.exe", "-NoExit", "-Command"]).args(command);
return cmd*/
}
Terminal::GNOMETerminal => {
shell.command("gnome-terminal")
.args(&["--", "/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::GNOMEConsole => {
shell.command("kgx")
.args(&["-e", "/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::Konsole => {
shell.command("konsole")
.args(&["-e", "/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::Xfce4Terminal => {
shell.command("xfce4-terminal")
.args(&["-x", "/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::Terminator => {
shell.command("terminator")
.args(&["-T", "Downloading depot...", "-e"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::XTerm => {
shell.command("xterm")
.args(&["-hold", "-T", "Downloading depot...", "-e", "/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::Kitty => {
shell.command("kitty")
.args(&["/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::LXTerminal => {
shell.command("lxterminal")
.args(&["-e", "/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::Tilix => {
shell.command("tilix")
.args(&["-e", "/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::DeepinTerminal => {
shell.command("deepin-terminal")
.args(&["-e", "/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::Alacritty => {
shell.command("alacritty")
.args(&["-e", "/usr/bin/env", "sh", "-c"])
.args(command)
.current_dir(working_dir.as_path())
}
Terminal::Terminal => {
// Create a bash script and run that. Not very secure but it makes this easier.
let download_script = format!("#!/bin/bash\ncd {}\n{}",working_dir.to_str().unwrap().replace(" ", "\\ "), command[0]);
fs::write("./script.sh", download_script).unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions("./script.sh", fs::Permissions::from_mode(0o755)).unwrap(); // Won't run without executable permission
}
shell.command("/usr/bin/open")
.args(&["-a", "Terminal", "./script.sh"])
.current_dir(working_dir.as_path())
}
}
}
//endregion
}
/**
Checks if terminals are installed by checking if they respond to commands.
## How it works
Probes a list of popular terminals and checks if they return an error when calling their `--version` or similar command line flag.
## Options
* `return_immediately`: [`bool`]: Return as soon as one terminal is found.
## Returns
A vector containing a list of terminals that should work.
## Commands
| Terminal | Command to check if installed |
|----------------|-------------------------------|
| cmd | `cmd /?` |
| GNOMETerminal | `gnome-terminal --version` |
| GNOMEConsole | `kgx --version` |
| Konsole | `konsole --version` |
| Xfce4Terminal | `xfce4-terminal --version` |
| Terminator | `terminator --version` |
| XTerm | `xterm -v` |
| Kitty | `kitty --version` |
| LXTerminal | `lxterminal --version` |
| Tilix | `tilix --version` |
| DeepinTerminal | `deepin-terminal --version` |
| Alacritty | `alacritty --version` |
*/
pub async fn get_installed_terminals(return_immediately: bool, shell: &Shell<Wry>) -> Vec<Terminal> {
match get_os() {
"windows" => { return vec!(Terminal::CMD); }
"macos" => { return vec!(Terminal::Terminal); }
_ => {}
}
let mut available_terminals: Vec<Terminal> = Vec::new();
for terminal in Terminal::iter() {
// Probe terminal. If it doesn't raise an error, it is probably installed.
if terminal.installed(shell).await {
if return_immediately {
return vec![terminal];
}
available_terminals.push(terminal);
}
}
if available_terminals.is_empty() {
eprintln!("No terminals were detected. Try installing one.");
}
available_terminals
}
/// Creates the DepotDownloader command necessary to download the requested manifest.
fn create_depotdownloader_command(steam_download: &SteamDownload) -> Vec<String> {
let output_dir = if get_os() == "windows" {
// In PowerShell, spaces can be escaped with a backtick.
steam_download.output_path().replace(" ", "` ")
} else {
// In bash, spaces can be escaped with a backslash.
steam_download.output_path().replace(" ", "\\ ")
};
if cfg!(not(windows)) {
if steam_download.is_anonymous() {
vec![format!(r#"./DepotDownloader -app {} -depot {} -manifest {} -dir {};echo Done!;sleep infinity"#, steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
} else {
vec![format!(r#"./DepotDownloader -username {} -password {} -app {} -depot {} -manifest {} -dir {};echo Done!;sleep infinity"#, steam_download.username().clone().unwrap(), steam_download.password().clone().unwrap(), steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
}
} else {
if steam_download.is_anonymous() {
vec![format!(r#".\DepotDownloader.exe -app {} -depot {} -manifest {} -dir {}"#, steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
} else {
vec![format!(r#".\DepotDownloader.exe -username {} -password {} -app {} -depot {} -manifest {} -dir {}"#, steam_download.username().clone().unwrap(), steam_download.password().clone().unwrap(), steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
}
if let Some(data) = &data {
reader.consume(data.len());
}
Ok(data)
}
#[tauri::command]
pub async fn async_resize_pty(rows: u16, cols: u16, state: State<'_, AppState>) -> Result<(), ()> {
state
.pty_pair
.lock()
.await
.master
.resize(PtySize {
rows,
cols,
..Default::default()
})
.map_err(|_| ())
}
/// Creates the DepotDownloader command necessary to download the requested manifest.
pub fn create_depotdownloader_command(steam_download: &SteamDownload, cwd: &PathBuf) -> CommandBuilder {
let depotdownloader_binary = if cfg!(windows) {
"DepotDownloader.exe"
} else {
"DepotDownloader"
};
let program = cwd.join(depotdownloader_binary);
let mut command = CommandBuilder::new(program);
command.cwd(cwd);
if !steam_download.is_anonymous() {
command.args(["-username", &*steam_download.username().clone().unwrap()]);
command.args(["-password", &*steam_download.password().clone().unwrap()]);
}
command.args(["-app", &*steam_download.app_id()]);
command.args(["-depot", &*steam_download.depot_id()]);
command.args(["-manifest", &*steam_download.manifest_id()]);
command.args(["-dir", &*steam_download.output_path()]);
command
}

View File

@@ -13,7 +13,7 @@
"windows": [
{
"title": "SteamDepotDownloaderGUI",
"width": 445,
"width": 890,
"height": 650,
"resizable": false
}

View File

@@ -1,3 +1,5 @@
@import "tailwindcss";
@font-face {
font-family: 'Hubot Sans';
src: url('../assets/Hubot-Sans.woff2') format('woff2 supports variations'),
@@ -20,6 +22,10 @@
white-space: nowrap;
}
.f2-light {
font-family: 'Hubot Sans', sans-serif;
}
/* The grey part */
.settings-surrounding {
display: none;
@@ -39,11 +45,9 @@
overflow: auto;
/*noinspection CssUnresolvedCustomProperty*/
background-color: var(--bgColor-default, var(--color-canvas-default));
margin: 5%;
margin: 1%;
padding: 25px;
border: 1.5px solid white;
width: 90vw; /* 90vw -> 90% */
height: 90vh; /* 90vh -> 90% */
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 6px 20px rgba(0, 0, 0, 0.1);
}

View File

@@ -10,200 +10,210 @@
<script defer src="./ts/preload.ts" type="module"></script>
<script src="./ts/main.ts" type="module"></script>
<script src="./ts/settings.ts" type="module"></script>
<script src="../node_modules/@xterm/xterm/lib/xterm.js"></script>
<link rel="stylesheet" href="../node_modules/@xterm/xterm/css/xterm.css" />
</head>
<body>
<div class="mx-auto">
<div>
<div class="f1-light text-center">Steam Depot Downloader</div>
<form id="theform">
<div class="form-group mx-3 mt-1">
<div class="form-group-header">
<label for="username">Username</label>
</div>
<input spellcheck="false" class="form-control input-block" id="username" placeholder="Leave empty for anonymous download"
type="text"/>
</div>
<div class="form-group mx-3 mt-1">
<div class="form-group-header">
<label for="password">Password</label>
</div>
<input class="form-control input-block" id="password" placeholder="Leave empty for anonymous download"
type="password"/>
</div>
<div class="form-group mx-3 mt-1 required">
<div class="form-group-header">
<label for="appid">App ID</label>
</div>
<input class="form-control input-block" id="appid" type="number"/>
</div>
<div class="form-group mx-3 mt-1 required">
<div class="form-group-header">
<label for="depotid">Depot ID</label>
</div>
<input class="form-control input-block" id="depotid" type="number"/>
</div>
<div class="form-group mx-3 mt-1 required">
<div class="form-group-header">
<label for="manifestid">Manifest ID</label>
</div>
<input class="form-control input-block" id="manifestid" type="number"/>
</div>
<div class="mx-3 mt-1 required">
<div class="form-group-header">
<label>Download Location</label>
</div>
<div aria-label="Pick the path/location where the game will be downloaded to."
class="form-control btn btn-sm tooltipped tooltipped-ne" id="pickpath">
Set location
<body class="select-none">
<div class="f1-light text-center mb-1">Steam Depot Downloader</div>
<div class="flex justify-between gap-2 w-svw flex-row">
<div class="w-1/2 h-full">
<div class="mx-auto">
<form id="theform">
<div class="form-group mx-3 mt-1">
<div class="form-group-header">
<label for="username">Username</label>
</div>
<input spellcheck="false" class="form-control input-block" id="username" placeholder="Leave empty for anonymous download"
type="text"/>
</div>
<div aria-disabled="true" aria-label="Check the location that has been selected."
class="form-control btn btn-sm ml-2 tooltipped tooltipped-ne" id="checkpath">
Open location
<div class="form-group mx-3 mt-1">
<div class="form-group-header">
<label for="password">Password</label>
</div>
<input class="form-control input-block" id="password" placeholder="Leave empty for anonymous download"
type="password"/>
</div>
<span class="Label mt-1 ml-3 Label--warning" id="busy">
<div class="form-group mx-3 mt-1 required">
<div class="form-group-header">
<label for="appid">App ID</label>
</div>
<input class="form-control input-block" id="appid" type="number"/>
</div>
<div class="form-group mx-3 mt-1 required">
<div class="form-group-header">
<label for="depotid">Depot ID</label>
</div>
<input class="form-control input-block" id="depotid" type="number"/>
</div>
<div class="form-group mx-3 mt-1 required">
<div class="form-group-header">
<label for="manifestid">Manifest ID</label>
</div>
<input class="form-control input-block" id="manifestid" type="number"/>
</div>
<div class="mx-3 mt-1 required">
<div class="form-group-header">
<label>Download Location</label>
</div>
<div aria-label="Pick the path/location where the game will be downloaded to."
class="form-control btn btn-sm tooltipped tooltipped-ne" id="pickpath">
Set location
</div>
<div aria-disabled="true" aria-label="Check the location that has been selected."
class="form-control btn btn-sm ml-2 tooltipped tooltipped-ne" id="checkpath">
Open location
</div>
<span class="Label mt-1 ml-3 Label--warning" id="busy">
<span aria-label="Application is executing a task. Please be patient."
class="tooltipped tooltipped-n">Busy<span class="AnimatedEllipsis"></span>
</span>
</span>
</div>
</form>
</div>
</form>
<div id="internet-btns">
<div class="form-group mt-3 ml-3 mr-3">
<div class="BtnGroup d-flex">
<button class="BtnGroup-item btn btn-block btn-primary flex-1" id="downloadbtn">
<svg class="octicon filter-red" height="16"
style="display: inline-block; user-select: none; vertical-align: text-bottom;"
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<div class="mt-2" id="warning-banners">
<div hidden id="dotnetwarning">
<div class="flash flash-error mx-2 mt-2 color-shadow-medium" id="dotnetalert">
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5 0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
fill-rule="evenodd"></path>
</svg>
Download
</button>
<button aria-disabled="true" class="BtnGroup-item btn flex-0" id="settings-button">
<svg fill="#8B949E" height="16"
style="display: inline-block; user-select: none; vertical-align: text-bottom;"
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.066.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.038.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.158-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.066-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.158.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.53.01 7.764 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8Z">
</path>
</svg>
</button>
</div>
</div>
<div aria-label="Join the Discord server for rapid support." class="btn btn-sm ml-3 tooltipped tooltipped-ne mb-1"
id="smbtn1">
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;" viewBox="0 0 16 16">
<path d="M13.545 2.907a13.2 13.2 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.2 12.2 0 0 0-3.658 0 8 8 0 0 0-.412-.833.05.05 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.04.04 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032q.003.022.021.037a13.3 13.3 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019q.463-.63.818-1.329a.05.05 0 0 0-.01-.059l-.018-.011a9 9 0 0 1-1.248-.595.05.05 0 0 1-.02-.066l.015-.019q.127-.095.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.05.05 0 0 1 .053.007q.121.1.248.195a.05.05 0 0 1-.004.085 8 8 0 0 1-1.249.594.05.05 0 0 0-.03.03.05.05 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.2 13.2 0 0 0 4.001-2.02.05.05 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.03.03 0 0 0-.02-.019m-8.198 7.307c-.789 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612"/>
</svg>
Discord
</div>
<div aria-label="Visit the SteamDB instant search website."
class="btn btn-sm ml-2 tooltipped tooltipped-n mb-1" id="smbtn2">
<svg aria-hidden="true" class="octicon" fill="#8B949E" height="16" viewBox="0 0 128 128" width="16"
xmlns="http://www.w3.org/2000/svg">
<path
d="M63.9 0C30.5 0 3.1 11.9.1 27.1l35.6 6.7c2.9-.9 6.2-1.3 9.6-1.3l16.7-10c-.2-2.5 1.3-5.1 4.7-7.2 4.8-3.1 12.3-4.8 19.9-4.8 5.2-.1 10.5.7 15 2.2 11.2 3.8 13.7 11.1 5.7 16.3-5.1 3.3-13.3 5-21.4 4.8l-22 7.9c-.2 1.6-1.3 3.1-3.4 4.5-5.9 3.8-17.4 4.7-25.6 1.9-3.6-1.2-6-3-7-4.8L2.5 38.4c2.3 3.6 6 6.9 10.8 9.8C5 53 0 59 0 65.5c0 6.4 4.8 12.3 12.9 17.1C4.8 87.3 0 93.2 0 99.6 0 115.3 28.6 128 64 128c35.3 0 64-12.7 64-28.4 0-6.4-4.8-12.3-12.9-17 8.1-4.8 12.9-10.7 12.9-17.1 0-6.5-5-12.6-13.4-17.4 8.3-5.1 13.3-11.4 13.3-18.2 0-16.5-28.7-29.9-64-29.9zm22.8 14.2c-5.2.1-10.2 1.2-13.4 3.3-5.5 3.6-3.8 8.5 3.8 11.1 7.6 2.6 18.1 1.8 23.6-1.8s3.8-8.5-3.8-11c-3.1-1-6.7-1.5-10.2-1.5zm.3 1.7c7.4 0 13.3 2.8 13.3 6.2 0 3.4-5.9 6.2-13.3 6.2s-13.3-2.8-13.3-6.2c0-3.4 5.9-6.2 13.3-6.2zM45.3 34.4c-1.6.1-3.1.2-4.6.4l9.1 1.7a10.8 5 0 1 1-8.1 9.3l-8.9-1.7c1 .9 2.4 1.7 4.3 2.4 6.4 2.2 15.4 1.5 20-1.5s3.2-7.2-3.2-9.3c-2.6-.9-5.7-1.3-8.6-1.3zM109 51v9.3c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-9.2c11.5 5.3 27.4 8.6 44.9 8.6 17.6 0 33.6-3.3 45.2-8.7zm0 34.6v8.8c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-8.8c11.6 5.1 27.4 8.2 45 8.2s33.5-3.1 45-8.2z"
fill-rule="evenodd"></path>
</svg>
SteamDB
</div>
<div aria-label="Donate to the author of SteamDepotDownloaderGUI."
class="btn btn-sm ml-2 tooltipped tooltipped-n mb-1" id="smbtn3">
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;"
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M2 2.75A2.75 2.75 0 0 1 4.75 0c.983 0 1.873.42 2.57 1.232.268.318.497.668.68 1.042.183-.375.411-.725.68-1.044C9.376.42 10.266 0 11.25 0a2.75 2.75 0 0 1 2.45 4h.55c.966 0 1.75.784 1.75 1.75v2c0 .698-.409 1.301-1 1.582v4.918A1.75 1.75 0 0 1 13.25 16H2.75A1.75 1.75 0 0 1 1 14.25V9.332C.409 9.05 0 8.448 0 7.75v-2C0 4.784.784 4 1.75 4h.55c-.192-.375-.3-.8-.3-1.25ZM7.25 9.5H2.5v4.75c0 .138.112.25.25.25h4.5Zm1.5 0v5h4.5a.25.25 0 0 0 .25-.25V9.5Zm0-4V8h5.5a.25.25 0 0 0 .25-.25v-2a.25.25 0 0 0-.25-.25Zm-7 0a.25.25 0 0 0-.25.25v2c0 .138.112.25.25.25h5.5V5.5h-5.5Zm3-4a1.25 1.25 0 0 0 0 2.5h2.309c-.233-.818-.542-1.401-.878-1.793-.43-.502-.915-.707-1.431-.707ZM8.941 4h2.309a1.25 1.25 0 0 0 0-2.5c-.516 0-1 .205-1.43.707-.337.392-.646.975-.879 1.793Z"
fill-rule="evenodd"></path>
</svg>
Donate
</div>
<div aria-label="View the official SteamDepotDownloaderGUI tutorials."
class="btn btn-sm ml-2 tooltipped tooltipped-nw mb-1" id="smbtn4">
<svg fill="#8B949E" style="display: inline-block; vertical-align: text-bottom;" height="16" width="16">
<path d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25Z"></path>
<path d="M6 10.559V5.442a.25.25 0 0 1 .379-.215l4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559Z"></path>
</svg>
Tutorial
</div>
</div>
<div class="mt-2" id="warning-banners">
<div hidden id="dotnetwarning">
<div class="flash flash-error mx-2 mt-2 color-shadow-medium" id="dotnetalert">
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
fill-rule="evenodd"></path>
</svg>
<code><span class="text-italic">dotnet</span></code> was not found.
<button class="btn btn-sm flash-action" id="dotnetalertbtn">
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16"
xmlns="http://www.w3.org/2000/svg">
<path d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5
<code><span class="text-italic">dotnet</span></code> was not found.
<button class="btn btn-sm flash-action" id="dotnetalertbtn">
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16"
xmlns="http://www.w3.org/2000/svg">
<path d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5
0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"
fill-rule="evenodd"></path>
fill-rule="evenodd"></path>
</svg>
<span class="text-bold">Download</span>
</button>
</div>
</div>
<div hidden id="emptywarning">
<div class="flash flash-warn mx-2 mt-2 color-shadow-medium" id="emptyalert">
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
fill-rule="evenodd"></path>
</svg>
<span class="text-bold">Download</span>
</button>
Please fill in all required fields.
</div>
</div>
<div id="internet-btns">
<div class="form-group mt-3 ml-3 mr-3">
<div class="BtnGroup d-flex">
<button class="BtnGroup-item btn btn-block btn-primary flex-1" id="downloadbtn">
<svg class="octicon filter-red" height="16"
style="display: inline-block; user-select: none; vertical-align: text-bottom;"
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5 0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"
fill-rule="evenodd"></path>
</svg>
Download
</button>
<button class="BtnGroup-item btn flex-0" id="settings-button">
<svg fill="#8B949E" height="16"
style="display: inline-block; user-select: none; vertical-align: text-bottom;"
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.066.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.038.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.158-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.066-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.158.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.53.01 7.764 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8Z">
</path>
</svg>
</button>
</div>
</div>
</div>
</div>
<div hidden id="emptywarning">
<div class="flash flash-warn mx-2 mt-2 color-shadow-medium" id="emptyalert">
<div hidden id="nopathwarning">
<div class="flash flash-warn mx-2 mt-2 color-shadow-medium" id="emptyalert">
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
fill-rule="evenodd"></path>
</svg>
Please choose a download location.
</div>
</div>
<div class="flash mx-2 mt-2 color-shadow-medium" hidden id="downloadingnotice">
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
fill-rule="evenodd"></path>
</svg>
Please fill in all required fields.
Downloading and extracting DepotDownloader<span class="AnimatedEllipsis"></span>
</div>
</div>
<div hidden id="nopathwarning">
<div class="flash flash-warn mx-2 mt-2 color-shadow-medium" id="emptyalert">
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
fill-rule="evenodd"></path>
</svg>
Please choose a download location.
</div>
</div>
<div class="flash mx-2 mt-2 color-shadow-medium" hidden id="downloadingnotice">
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
fill-rule="evenodd"></path>
</svg>
Downloading and extracting DepotDownloader<span class="AnimatedEllipsis"></span>
</div>
</div>
</div>
<div class="w-1/2 h-full px-2">
<div class="mt-2 h-full w-full mx-auto">
<div class="border border-gray-300 rounded-md bg-gray-900 text-white shadow shadow-blue-200">
<span class="text-md font-semibold block text-center">Download output</span>
<div class="max-h-[70vh]" id="xtermjs"></div>
</div>
<div class="mt-3 justify-between flex flex-row gap-3">
<div aria-label="Join the Discord server for rapid support." class="btn btn-sm tooltipped tooltipped-ne mb-1 w-full text-center items-center"
id="smbtn1">
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;" viewBox="0 0 16 16">
<path d="M13.545 2.907a13.2 13.2 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.2 12.2 0 0 0-3.658 0 8 8 0 0 0-.412-.833.05.05 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.04.04 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032q.003.022.021.037a13.3 13.3 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019q.463-.63.818-1.329a.05.05 0 0 0-.01-.059l-.018-.011a9 9 0 0 1-1.248-.595.05.05 0 0 1-.02-.066l.015-.019q.127-.095.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.05.05 0 0 1 .053.007q.121.1.248.195a.05.05 0 0 1-.004.085 8 8 0 0 1-1.249.594.05.05 0 0 0-.03.03.05.05 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.2 13.2 0 0 0 4.001-2.02.05.05 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.03.03 0 0 0-.02-.019m-8.198 7.307c-.789 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612"/>
</svg>
Discord
</div>
<div aria-label="Visit the SteamDB instant search website."
class="btn btn-sm tooltipped tooltipped-n mb-1 w-full text-center items-center" id="smbtn2">
<svg aria-hidden="true" class="octicon" fill="#8B949E" height="16" viewBox="0 0 128 128" width="16"
xmlns="http://www.w3.org/2000/svg">
<path
d="M63.9 0C30.5 0 3.1 11.9.1 27.1l35.6 6.7c2.9-.9 6.2-1.3 9.6-1.3l16.7-10c-.2-2.5 1.3-5.1 4.7-7.2 4.8-3.1 12.3-4.8 19.9-4.8 5.2-.1 10.5.7 15 2.2 11.2 3.8 13.7 11.1 5.7 16.3-5.1 3.3-13.3 5-21.4 4.8l-22 7.9c-.2 1.6-1.3 3.1-3.4 4.5-5.9 3.8-17.4 4.7-25.6 1.9-3.6-1.2-6-3-7-4.8L2.5 38.4c2.3 3.6 6 6.9 10.8 9.8C5 53 0 59 0 65.5c0 6.4 4.8 12.3 12.9 17.1C4.8 87.3 0 93.2 0 99.6 0 115.3 28.6 128 64 128c35.3 0 64-12.7 64-28.4 0-6.4-4.8-12.3-12.9-17 8.1-4.8 12.9-10.7 12.9-17.1 0-6.5-5-12.6-13.4-17.4 8.3-5.1 13.3-11.4 13.3-18.2 0-16.5-28.7-29.9-64-29.9zm22.8 14.2c-5.2.1-10.2 1.2-13.4 3.3-5.5 3.6-3.8 8.5 3.8 11.1 7.6 2.6 18.1 1.8 23.6-1.8s3.8-8.5-3.8-11c-3.1-1-6.7-1.5-10.2-1.5zm.3 1.7c7.4 0 13.3 2.8 13.3 6.2 0 3.4-5.9 6.2-13.3 6.2s-13.3-2.8-13.3-6.2c0-3.4 5.9-6.2 13.3-6.2zM45.3 34.4c-1.6.1-3.1.2-4.6.4l9.1 1.7a10.8 5 0 1 1-8.1 9.3l-8.9-1.7c1 .9 2.4 1.7 4.3 2.4 6.4 2.2 15.4 1.5 20-1.5s3.2-7.2-3.2-9.3c-2.6-.9-5.7-1.3-8.6-1.3zM109 51v9.3c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-9.2c11.5 5.3 27.4 8.6 44.9 8.6 17.6 0 33.6-3.3 45.2-8.7zm0 34.6v8.8c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-8.8c11.6 5.1 27.4 8.2 45 8.2s33.5-3.1 45-8.2z"
fill-rule="evenodd"></path>
</svg>
SteamDB
</div>
<div aria-label="Donate to the author of SteamDepotDownloaderGUI."
class="btn btn-sm tooltipped tooltipped-n mb-1 w-full text-center items-center" id="smbtn3">
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;"
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="M2 2.75A2.75 2.75 0 0 1 4.75 0c.983 0 1.873.42 2.57 1.232.268.318.497.668.68 1.042.183-.375.411-.725.68-1.044C9.376.42 10.266 0 11.25 0a2.75 2.75 0 0 1 2.45 4h.55c.966 0 1.75.784 1.75 1.75v2c0 .698-.409 1.301-1 1.582v4.918A1.75 1.75 0 0 1 13.25 16H2.75A1.75 1.75 0 0 1 1 14.25V9.332C.409 9.05 0 8.448 0 7.75v-2C0 4.784.784 4 1.75 4h.55c-.192-.375-.3-.8-.3-1.25ZM7.25 9.5H2.5v4.75c0 .138.112.25.25.25h4.5Zm1.5 0v5h4.5a.25.25 0 0 0 .25-.25V9.5Zm0-4V8h5.5a.25.25 0 0 0 .25-.25v-2a.25.25 0 0 0-.25-.25Zm-7 0a.25.25 0 0 0-.25.25v2c0 .138.112.25.25.25h5.5V5.5h-5.5Zm3-4a1.25 1.25 0 0 0 0 2.5h2.309c-.233-.818-.542-1.401-.878-1.793-.43-.502-.915-.707-1.431-.707ZM8.941 4h2.309a1.25 1.25 0 0 0 0-2.5c-.516 0-1 .205-1.43.707-.337.392-.646.975-.879 1.793Z"
fill-rule="evenodd"></path>
</svg>
Donate
</div>
<div aria-label="View the official SteamDepotDownloaderGUI tutorials."
class="btn btn-sm tooltipped tooltipped-nw mb-1 w-full text-center items-center" id="smbtn4">
<svg fill="#8B949E" style="display: inline-block; vertical-align: text-bottom;" height="16" width="16">
<path d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25Z"></path>
<path d="M6 10.559V5.442a.25.25 0 0 1 .379-.215l4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559Z"></path>
</svg>
Tutorial
</div>
</div>
</div>
</div>
</div>
<div class="settings-surrounding" id="settings-surrounding">
<div class="settings-content mx-auto" id="settings-content">
<div class="settings-content mx-auto max-w-2/3 h-[85vh] mt-4" id="settings-content">
<label class="version-info" id="version-info">UNKNOWN</label>
<button class="opium-button" id="opium-btn">aphex</button>
<h2><b>Settings</b></h2>
<hr>
<h4><b>Appearance</b></h4>
<div class="form-group">
<div class="form-group-header">
@@ -223,7 +233,6 @@
</div>
</div>
</div>
<hr>
<h4><b>Output</b></h4>
<form>
<div class="form-group">
@@ -246,38 +255,6 @@
type="text">
</div>
</div>
<hr>
<h4><b>Debugging</b></h4>
<div class="form-group">
<div class="form-group-header">
<label for="terminal-dropdown">Linux only: Force a terminal</label>
</div>
<div class="form-group-body">
<div class="mb-2">
<select class="form-select" id="terminal-dropdown">
<!-- "(not installed)" part is sliced later. -->
<option disabled>GNOME Terminal (not installed)</option>
<option disabled>Alacritty (not installed)</option>
<option disabled>Konsole (not installed)</option>
<option disabled>GNOME Console (not installed)</option>
<option disabled>Xfce Terminal (not installed)</option>
<option disabled>Deepin Terminal (not installed)</option>
<option disabled>Terminator (not installed)</option>
<option disabled>Kitty (not installed)</option>
<option disabled>LXTerminal (not installed)</option>
<option disabled>Tilix (not installed)</option>
<option disabled>XTerm (not installed)</option>
<option disabled>CMD (not installed)</option>
<option disabled>macOS Terminal (not installed)</option>
<option selected="selected">Auto</option>
</select>
<br>
found: <span class="Counter"><code id="terminals-found">none</code></span>
<br>default: <span class="Counter"><code id="default-terminal">none</code></span>
</div>
</div>
</div>
<hr>
</form>
</div>
</div>

View File

@@ -1,8 +1,11 @@
import $ from "jquery";
import {invoke} from "@tauri-apps/api/core";
import {open as openDialog} from "@tauri-apps/plugin-dialog";
import {openPath, openUrl} from '@tauri-apps/plugin-opener';
import {listen} from "@tauri-apps/api/event";
import {openPath, openUrl} from "@tauri-apps/plugin-opener";
import "@xterm/xterm/css/xterm.css";
import {Terminal} from "@xterm/xterm";
import { FitAddon } from "@xterm/addon-fit";
import { listen } from "@tauri-apps/api/event";
function setLoader(state: boolean) {
$("#busy").prop("hidden", !state);
@@ -46,34 +49,67 @@ const invalidFields = () => {
return invalidFields;
};
const registerTerminal = async (terminalElement: HTMLElement) => {
const fitAddon = new FitAddon();
const term = new Terminal({
fontSize: 10,
cursorBlink: true,
rows: 100,
cols: 100,
theme: {
background: "rgb(47, 47, 47)",
},
});
term.loadAddon(fitAddon);
term.open(terminalElement);
async function fitTerminal() {
fitAddon.fit();
void invoke<string>("async_resize_pty", {
rows: term.rows,
cols: term.cols,
});
}
// Write data from pty into the terminal
function writeToTerminal(data: string) {
return new Promise<void>((r) => {
term.write(data, () => r());
});
}
// Write data from the terminal to the pty
function writeToPty(data: string) {
void invoke("async_write_to_pty", {
data,
});
}
term.onData(writeToPty);
addEventListener("resize", fitTerminal);
await fitTerminal();
async function readFromPty() {
const data = await invoke<string>("async_read_from_pty");
if (data) {
await writeToTerminal(data);
}
window.requestAnimationFrame(readFromPty);
}
window.requestAnimationFrame(readFromPty);
};
$(async () => {
let terminalsCollected = false;
await registerTerminal($("#xtermjs")[0]);
let downloadDirectory: string | null;
// Startup logic
setLoadingState(true);
await invoke("preload_vectum");
setLoadingState(false);
// Collect the rest of the terminals in the background.
if (!terminalsCollected) {
setLoader(true);
// @ts-ignore
const terminals = await invoke("get_all_terminals") as string[];
for (const terminal in terminals) {
console.log(terminal);
}
// Allow opening settings now that it is ready to be shown.
$("#settings-button").prop("ariaDisabled", false);
terminalsCollected = true;
setLoader(false);
}
$("#pickpath").on("click", async () => {
// Open a dialog
downloadDirectory = await openDialog({
@@ -132,12 +168,10 @@ $(async () => {
$("#downloadingnotice").prop("hidden", false);
$("#busy").prop("hidden", true); // Don't show the loader this time.
const terminalChoice = (document.getElementById("terminal-dropdown") as HTMLSelectElement).selectedIndex;
const directoryNameChoice = $("#folder-name-custom-input").val();
// Output path w/ directories chosen is: {downloadDirectory}/{directoryNameChoice}
const vectumOptions = {
terminal: terminalChoice == 13 ? null : terminalChoice,
output_directory: downloadDirectory || null, // if not specified let backend choose a path.
directory_name: directoryNameChoice || null,
};
@@ -160,23 +194,23 @@ $(async () => {
console.debug("DepotDownloader download process completed. Starting game download...");
setLoadingState(true);
await invoke("start_download", {steamDownload: steamDownload});
console.log("Send frontend data over to backend. Ready for next download.");
});
$("#settings-button").on("click", async () => {
if (terminalsCollected) $("#settings-surrounding").css("display", "block");
$("#settings-surrounding").toggle();
});
$("#settings-surrounding").on("click", (event) => {
if (event.target === document.getElementById("settings-surrounding")) {
$("#settings-surrounding").css("display", "none");
$("#settings-surrounding").toggle();
}
});
$("#opium-btn").on("click", () => {
openUrl("https://l.aphex.cc/index.html");
openUrl("https://aphex.cc/index.html");
});
@@ -194,30 +228,6 @@ $(async () => {
});
let a = 0;
// Each terminal that is installed gets received from rust with this event.
listen<[number, number]>("working-terminal", (event) => {
a++;
console.log(
`Terminal #${event.payload[0]} is installed. a = ${a}`
);
const terminalSelection = (document.getElementById("terminal-dropdown") as HTMLSelectElement);
// Enable the <option> of the terminal because we know it is available. Ignore null check because we know it is valid.
// @ts-ignore
terminalSelection.options.item(event.payload[0]).disabled = false;
// @ts-ignore 16
terminalSelection.options.item(event.payload[0]).text = terminalSelection.options.item(event.payload[0]).text.slice(0,-16);
$("#terminals-found").text(`${a}/${event.payload[1]}`);
});
listen<string>("default-terminal", (event) => {
console.log(
`Default terminal is ${event.payload}.`
);
$("#default-terminal").text(event.payload);
listen<string>("command-exited", () => {
setLoadingState(false);
});

View File

@@ -43,4 +43,5 @@ function setTheme(theme: string) {
$("#theme-light").attr("aria-selected", String(theme === "light"));
$("#theme-dark").attr("aria-selected", String(theme === "dark"));
$("#theme").attr("data-color-mode", theme);
localStorage.theme = theme;
}

View File

@@ -1,4 +1,5 @@
import {defineConfig} from "vite";
import tailwindcss from '@tailwindcss/vite'
// @ts-expect-error process is a nodejs global
const host = process.env.TAURI_DEV_HOST;
@@ -31,5 +32,8 @@ export default defineConfig(async () => ({
build: {
outDir: '../dist',
emptyOutDir: true,
}
},
plugins: [
tailwindcss(),
]
}));