From 7204302ed472260b485a181c1b1ebab397d2a5c5 Mon Sep 17 00:00:00 2001 From: Maarten van Heusden <50550545+mmvanheusden@users.noreply.github.com> Date: Sat, 31 Jan 2026 20:59:02 +0100 Subject: [PATCH] feat: integrated terminal --- package.json | 6 +- pnpm-lock.yaml | 460 +++++++++++++++++++++++++++++++++++--- src-tauri/Cargo.lock | 99 +++++++- src-tauri/Cargo.toml | 1 + src-tauri/src/main.rs | 113 ++++++---- src-tauri/src/steam.rs | 1 - src-tauri/src/terminal.rs | 358 +++++------------------------ src-tauri/tauri.conf.json | 2 +- src/css/style.css | 10 +- src/index.html | 365 ++++++++++++++---------------- src/ts/main.ts | 108 ++++----- vite.config.ts | 6 +- 12 files changed, 911 insertions(+), 618 deletions(-) diff --git a/package.json b/package.json index 33f14096..8ade8c96 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 934f66fa..349fce96 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 236b8eaa..dd5b1e9f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -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" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3ed0d296..ca229ef1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -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" diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ce512f48..55adddbe 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -6,46 +6,43 @@ 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::{BufRead, 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 std::{env, thread}; +use tauri::async_runtime::Mutex; +use tauri::{AppHandle, Manager, State}; use tauri_plugin_shell::ShellExt; +struct AppState { + pty_pair: Arc>, + writer: Arc>>, + reader: Arc>>>, +} + /// The first terminal found. Used as default terminal. -static TERMINAL: OnceLock> = 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 = 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(); +async fn start_download(steam_download: steam::SteamDownload, app: AppHandle, state: State<'_, AppState>) -> Result<(), String> { 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() }; // 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 +52,32 @@ 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}"); + // exit(status.exit_code() as i32) + }); + Ok(()) } /// Downloads the DepotDownloader zip file from the internet based on the OS. @@ -71,7 +87,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 +101,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 +113,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 +125,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 +137,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"); } diff --git a/src-tauri/src/steam.rs b/src-tauri/src/steam.rs index a43d40b2..c5dbcec8 100644 --- a/src-tauri/src/steam.rs +++ b/src-tauri/src/steam.rs @@ -16,7 +16,6 @@ pub struct SteamDownload { #[derive(Debug, Deserialize, Getters)] pub struct VectumOptions { - terminal: Option, output_directory: Option, directory_name: Option } diff --git a/src-tauri/src/terminal.rs b/src-tauri/src/terminal.rs index 104cfb26..a60fe3bf 100644 --- a/src-tauri/src/terminal.rs +++ b/src-tauri/src/terminal.rs @@ -1,309 +1,75 @@ -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, ()> { + 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 { - 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::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 { - 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) -> 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, 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) -> Vec { - match get_os() { - "windows" => { return vec!(Terminal::CMD); } - "macos" => { return vec!(Terminal::Terminal); } - _ => {} - } - - - let mut available_terminals: Vec = 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 { - 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 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 mut command: CommandBuilder; 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)] - } + command = CommandBuilder::new("./DepotDownloader"); } 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)] - } + command = CommandBuilder::new(".\\DepotDownloader"); } + 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 } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 0d547ef0..5924b1f4 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -13,7 +13,7 @@ "windows": [ { "title": "SteamDepotDownloaderGUI", - "width": 445, + "width": 890, "height": 650, "resizable": false } diff --git a/src/css/style.css b/src/css/style.css index cd38e520..773019d3 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -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); } diff --git a/src/index.html b/src/index.html index fd6a4159..015549ef 100644 --- a/src/index.html +++ b/src/index.html @@ -10,200 +10,210 @@ + + - -
-
-
Steam Depot Downloader
-
-
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
- -
-
- Set location + +
Steam Depot Downloader
+
+
+
+ +
+
+ +
+
-
- Open location +
+
+ +
+
- +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ Set location +
+ +
+ Open location +
+ + Busy -
- +
+ -
-
-
- - -
-
- -
- - - - - Discord -
- -
- - SteamDB -
- -
- - - - Donate -
- -
- - - - - Tutorial -
-
- -
- + + +
+
+
+ + +
+
-
-
+
+
+
+ Download output +
+
+
+
+ + + + Discord +
+ +
+ + SteamDB +
+ +
+ + + + Donate +
+ +
+ + + + + Tutorial +
+
+
+
-
+

Settings

-

Appearance

@@ -223,7 +233,6 @@
-

Output

@@ -246,38 +255,6 @@ type="text">
-
-

Debugging

-
-
- -
-
-
- -
- found: none -
default: none -
-
-
-
diff --git a/src/ts/main.ts b/src/ts/main.ts index 9dc8ecce..35301f85 100644 --- a/src/ts/main.ts +++ b/src/ts/main.ts @@ -2,7 +2,9 @@ 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 "@xterm/xterm/css/xterm.css"; +import {Terminal} from "@xterm/xterm"; +import { FitAddon } from '@xterm/addon-fit'; function setLoader(state: boolean) { $("#busy").prop("hidden", !state); @@ -46,34 +48,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("async_resize_pty", { + rows: term.rows, + cols: term.cols, + }); + } + + // Write data from pty into the terminal + function writeToTerminal(data: string) { + return new Promise((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("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 +167,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, }; @@ -165,7 +198,7 @@ $(async () => { }); $("#settings-button").on("click", async () => { - if (terminalsCollected) $("#settings-surrounding").css("display", "block"); + $("#settings-surrounding").css("display", "block"); }); $("#settings-surrounding").on("click", (event) => { @@ -176,7 +209,7 @@ $(async () => { }); $("#opium-btn").on("click", () => { - openUrl("https://l.aphex.cc/index.html"); + openUrl("https://aphex.cc/index.html"); }); @@ -191,33 +224,4 @@ $(async () => { $("#downloadbtn").prop("ariaDisabled", false); } }); -}); - - -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