Compare commits
10 Commits
3.0.4
...
cdc3c78ffe
Author | SHA1 | Date | |
---|---|---|---|
cdc3c78ffe | |||
509aa36379 | |||
20e5961b7c | |||
9b816e1e5c | |||
ada83b32dc | |||
cbf85753a8 | |||
eb1ef06c07 | |||
a64655ec5d | |||
45b902cfb6 | |||
f53aba9a7b |
24
bun.lock
24
bun.lock
@@ -7,18 +7,17 @@
|
||||
"@better-fetch/fetch": "^1.1.18",
|
||||
"@fontsource/sora": "^5.2.6",
|
||||
"@fontsource/space-mono": "^5.2.8",
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@number-flow/svelte": "^0.3.9",
|
||||
"@tailwindcss/typography": "0.5.16",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tauri-apps/api": "2.6.0",
|
||||
"@tauri-apps/plugin-dialog": "2.3.1",
|
||||
"@tauri-apps/plugin-dialog": "2.3.2",
|
||||
"@tauri-apps/plugin-fs": "2.4.1",
|
||||
"@tauri-apps/plugin-shell": "2.3.0",
|
||||
"animejs": "^4.0.2",
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"lucide-svelte": "0.525.0",
|
||||
"lucide-svelte": "^0.536.0",
|
||||
"semver": "^7.7.2",
|
||||
"svelte-confetti": "^2.0.0",
|
||||
"tw-animate-css": "^1.3.0",
|
||||
@@ -26,7 +25,8 @@
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.2.5",
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@lucide/svelte": "^0.525.0",
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@lucide/svelte": "^0.536.0",
|
||||
"@sveltejs/adapter-static": "3.0.8",
|
||||
"@sveltejs/kit": "2.22.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.1.0",
|
||||
@@ -49,14 +49,14 @@
|
||||
"svelte-check": "4.2.2",
|
||||
"svelte-sonner": "^1.0.5",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwind-variants": "^1.0.0",
|
||||
"tailwind-variants": "^2.1.0",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tslib": "2.8.1",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "7.0.0",
|
||||
"vite-plugin-devtools-json": "^0.3.0",
|
||||
"vite-plugin-devtools-json": "^0.4.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -171,7 +171,7 @@
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"@lucide/svelte": ["@lucide/svelte@0.525.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-dyUxkXzepagLUzL8jHQNdeH286nC66ClLACsg+Neu/bjkRJWPWMzkT+H0DKlE70QdkicGCfs1ZGmXCc351hmZA=="],
|
||||
"@lucide/svelte": ["@lucide/svelte@0.536.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-YAeoWU+0B/RriFZZ3wHno1FMkbrVrFdityuo2B0YuphD0vtJWXStzZkWLGVhT3jMb7zhugmhayIg+gI4+AZu1g=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
|
||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.6.1", "", { "os": "win32", "cpu": "x64" }, "sha512-fBsjPqIIHaaQt7tnjIGmPHu5p/BNBVD4JfOhO3QqIVBzAb+W2bDyIQPdoDMI943soLr/+N10xeTiPu+3L74+dQ=="],
|
||||
|
||||
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.3.1", "", { "dependencies": { "@tauri-apps/api": "^2.6.0" } }, "sha512-B7jvyhycV8SI/WHzPjciwtYfdFM6/9EXuMjRgYWZwn8GPDmHxpT80aJdb/eDVN+NgoAFDh9bu4QPonYahoYnZQ=="],
|
||||
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.6.0" } }, "sha512-cNLo9YeQSC0MF4IgXnotHsqEgJk72MBZLXmQPrLA95qTaaWiiaFQ38hIMdZ6YbGUNkr3oni3EhU+AD5jLHcdUA=="],
|
||||
|
||||
"@tauri-apps/plugin-fs": ["@tauri-apps/plugin-fs@2.4.1", "", { "dependencies": { "@tauri-apps/api": "^2.6.0" } }, "sha512-vJlKZVGF3UAFGoIEVT6Oq5L4HGDCD78WmA4uhzitToqYiBKWAvZR61M6zAyQzHqLs0ADemkE4RSy/5sCmZm6ZQ=="],
|
||||
|
||||
@@ -555,7 +555,7 @@
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
|
||||
"lucide-svelte": ["lucide-svelte@0.525.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" } }, "sha512-kfuN6JcCqTfCz2B76aXnyGLAzEBRSYw5GaUspM5RNHQZS5aI5yaKu06fbaofOk8cDvUtY0AUm/zAix7aUX6Q3A=="],
|
||||
"lucide-svelte": ["lucide-svelte@0.536.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" } }, "sha512-UIm/R2IYZMs4HW134ik7rDqEDXdvXb109Fznb8mG7OFQShnXkRpAaoyytJ8FzXR5W6GbfQ+r5jHBnIUs6uQp9w=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
||||
|
||||
@@ -715,7 +715,7 @@
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
|
||||
|
||||
"tailwind-variants": ["tailwind-variants@1.0.0", "", { "dependencies": { "tailwind-merge": "3.0.2" }, "peerDependencies": { "tailwindcss": "*" } }, "sha512-2WSbv4ulEEyuBKomOunut65D8UZwxrHoRfYnxGcQNnHqlSCp2+B7Yz2W+yrNDrxRodOXtGD/1oCcKGNBnUqMqA=="],
|
||||
"tailwind-variants": ["tailwind-variants@2.1.0", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge"] }, "sha512-82m0eRex0z6A3GpvfoTCpHr+wWJmbecfVZfP3mqLoDxeya5tN4mYJQZwa5Aw1hRZTedwpu1D2JizYenoEdyD8w=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
|
||||
|
||||
@@ -757,7 +757,7 @@
|
||||
|
||||
"vite": ["vite@7.0.0", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g=="],
|
||||
|
||||
"vite-plugin-devtools-json": ["vite-plugin-devtools-json@0.3.0", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "vite": "^2.7.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-y8QdN/uZNV0Jj96H9R3s7G9jtcSuyssk8EwSfWaY+NUAdy7976d7rjtXJzqmeCcgp4CZywcTyUc+k6zpPFHdeg=="],
|
||||
"vite-plugin-devtools-json": ["vite-plugin-devtools-json@0.4.1", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "vite": "^2.7.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-pN+QJL+NwZUV+Via8w/Sh6X2pDrVClIMDAXdl7+EteXKB6mcHhsFGGclmxrPx6ZPGKSK5ez5ns64oRpjE5wFCg=="],
|
||||
|
||||
"vitefu": ["vitefu@1.0.7", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-eRWXLBbJjW3X5z5P5IHcSm2yYbYRPb2kQuc+oqsbAl99WB5kVsPbiiox+cymo8twTzifA6itvhr2CmjnaZZp0Q=="],
|
||||
|
||||
@@ -817,8 +817,6 @@
|
||||
|
||||
"svelte-sonner/runed": ["runed@0.28.0", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ=="],
|
||||
|
||||
"tailwind-variants/tailwind-merge": ["tailwind-merge@3.0.2", "", {}, "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||
|
||||
"mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "^1.0.0" }, "peerDependencies": { "svelte": "^5.7.0" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="],
|
||||
|
10
package.json
10
package.json
@@ -25,13 +25,13 @@
|
||||
"@tailwindcss/typography": "0.5.16",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tauri-apps/api": "2.6.0",
|
||||
"@tauri-apps/plugin-dialog": "2.3.1",
|
||||
"@tauri-apps/plugin-dialog": "2.3.2",
|
||||
"@tauri-apps/plugin-fs": "2.4.1",
|
||||
"@tauri-apps/plugin-shell": "2.3.0",
|
||||
"animejs": "^4.0.2",
|
||||
"buffer": "^6.0.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"lucide-svelte": "0.525.0",
|
||||
"lucide-svelte": "^0.536.0",
|
||||
"semver": "^7.7.2",
|
||||
"tw-animate-css": "^1.3.0",
|
||||
"svelte-confetti": "^2.0.0"
|
||||
@@ -40,7 +40,7 @@
|
||||
"@eslint/compat": "^1.2.5",
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@iarna/toml": "2.2.5",
|
||||
"@lucide/svelte": "^0.525.0",
|
||||
"@lucide/svelte": "^0.536.0",
|
||||
"@sveltejs/adapter-static": "3.0.8",
|
||||
"@sveltejs/kit": "2.22.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.1.0",
|
||||
@@ -63,13 +63,13 @@
|
||||
"svelte-check": "4.2.2",
|
||||
"svelte-sonner": "^1.0.5",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwind-variants": "^1.0.0",
|
||||
"tailwind-variants": "^2.1.0",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tslib": "2.8.1",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"vite": "7.0.0",
|
||||
"vite-plugin-devtools-json": "^0.3.0"
|
||||
"vite-plugin-devtools-json": "^0.4.1"
|
||||
}
|
||||
}
|
||||
|
@@ -23,14 +23,14 @@ tauri-plugin-shell = "2.3.0"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.141"
|
||||
serde_repr = "0.1.20"
|
||||
tauri-plugin-dialog = "2.3.1"
|
||||
tauri-plugin-dialog = "2.3.2"
|
||||
tauri-plugin-fs = "2.4.1"
|
||||
hardware-id = "0.3.0"
|
||||
tauri-plugin-cors-fetch = "4.1.0"
|
||||
sysinfo = "0.36.1"
|
||||
reqwest = { version = "0.12.22", features = ["json", "stream"] }
|
||||
md5 = "0.8.0"
|
||||
tokio = { version = "1.46.1", features = ["full"] }
|
||||
tokio = { version = "1.47.0", features = ["full"] }
|
||||
open = "5.3.2"
|
||||
windows-sys = "0.60.2"
|
||||
discord-rich-presence = "0.2.5"
|
||||
|
343
src-tauri/nsis/dotnet.nsh
Normal file
343
src-tauri/nsis/dotnet.nsh
Normal file
@@ -0,0 +1,343 @@
|
||||
; A set of NSIS macros to check whether a dotnet core runtime is installed and, if not, offer to
|
||||
; download and install it. Supports dotnet versions 3.1 and newer - latest tested version is 7.0.
|
||||
;
|
||||
; Inspired by & initially based on NsisDotNetChecker, which does the same thing for .NET framework
|
||||
; https://github.com/alex-sitnikov/NsisDotNetChecker
|
||||
|
||||
!include "WordFunc.nsh"
|
||||
!include "TextFunc.nsh"
|
||||
!include "X64.nsh"
|
||||
|
||||
!ifndef DOTNETCORE_INCLUDED
|
||||
!define DOTNETCORE_INCLUDED
|
||||
|
||||
; Check that a specific version of the dotnet core runtime is installed and, if not, attempts to
|
||||
; install it
|
||||
;
|
||||
; \param Version The desired dotnet core runtime version as a 2 digit version. e.g. 3.1, 6.0, 7.0
|
||||
!macro CheckDotNetCore Version
|
||||
|
||||
; Save registers
|
||||
Push $R0
|
||||
Push $R1
|
||||
Push $R2
|
||||
|
||||
; Push and pop parameters so we don't have conflicts if parameters are $R#
|
||||
Push ${Version}
|
||||
Pop $R0 ; Version
|
||||
|
||||
!define ID ${__LINE__}
|
||||
|
||||
; Check current installed version
|
||||
!insertmacro DotNetCoreGetInstalledVersion $R0 $R1
|
||||
|
||||
; If $R1 is blank then there is no version installed, otherwise it is installed
|
||||
; todo in future we might want to support "must be at least 6.0.7", for now we only deal with "yes/no" for a major version (e.g. 6.0)
|
||||
StrCmp $R1 "" notinstalled_${ID}
|
||||
DetailPrint "dotnet version $R1 already installed"
|
||||
Goto end_${ID}
|
||||
|
||||
notinstalled_${ID}:
|
||||
DetailPrint "dotnet $R0 is not installed"
|
||||
|
||||
!insertmacro DotNetCoreGetLatestVersion $R0 $R1
|
||||
DetailPrint "Latest Version of $R0 is $R1"
|
||||
|
||||
|
||||
; Get number of input digits
|
||||
; ${WordFind} $R1 "." "#" $R2
|
||||
; DetailPrint "version parts count is $R2"
|
||||
|
||||
; ${WordFind} $R1 "." "+1" $R2
|
||||
; DetailPrint "version part 1 is $R2"
|
||||
|
||||
; ${WordFind} $R1 "." "+2" $R2
|
||||
; DetailPrint "version part 2 is $R2"
|
||||
|
||||
; ${WordFind} $R1 "." "+3" $R2
|
||||
; DetailPrint "version part 3 is $R2"
|
||||
|
||||
!insertmacro DotNetCoreInstallVersion $R1
|
||||
|
||||
end_${ID}:
|
||||
!undef ID
|
||||
|
||||
; Restore registers
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Pop $R0
|
||||
|
||||
!macroend
|
||||
|
||||
|
||||
|
||||
; Gets the latest version of the runtime for a specified dotnet version. This uses the same endpoint
|
||||
; as the dotnet-install scripts to determine the latest full version of a dotnet version
|
||||
;
|
||||
; \param[in] Version The desired dotnet core runtime version as a 2 digit version. e.g. 3.1, 6.0, 7.0
|
||||
; \param[out] Result The full version number of the latest version - e.g. 6.0.7
|
||||
!macro DotNetCoreGetLatestVersion Version Result
|
||||
|
||||
; Save registers
|
||||
Push $R0
|
||||
Push $R1
|
||||
Push $R2
|
||||
|
||||
; Push and pop parameters so we don't have conflicts if parameters are $R#
|
||||
Push ${Version}
|
||||
Pop $R0 ; Version
|
||||
|
||||
StrCpy $R1 https://dotnetcli.azureedge.net/dotnet/WindowsDesktop/$R0/latest.version
|
||||
DetailPrint "Querying latest version of dotnet $R0 from $R1"
|
||||
|
||||
; Fetch latest version of the desired dotnet version
|
||||
; todo error handling in the PS script? so we can check for errors here
|
||||
StrCpy $R2 "Write-Host (Invoke-WebRequest -UseBasicParsing -URI $\"$R1$\").Content;"
|
||||
!insertmacro DotNetCorePSExec $R2 $R2
|
||||
; $R2 contains latest version, e.g. 6.0.7
|
||||
|
||||
; todo error handling here
|
||||
|
||||
; Push the result onto the stack
|
||||
${TrimNewLines} $R2 $R2
|
||||
Push $R2
|
||||
|
||||
; Restore registers
|
||||
Exch
|
||||
Pop $R2
|
||||
Exch
|
||||
Pop $R1
|
||||
Exch
|
||||
Pop $R0
|
||||
|
||||
; Set result
|
||||
Pop ${Result}
|
||||
|
||||
!macroend
|
||||
|
||||
!macro DotNetCoreGetInstalledVersion Version Result
|
||||
!define DNC_INS_ID ${__LINE__}
|
||||
|
||||
; Save registers
|
||||
Push $R0
|
||||
Push $R1
|
||||
Push $R2
|
||||
|
||||
; Push and pop parameters so we don't have conflicts if parameters are $R#
|
||||
Push ${Version}
|
||||
Pop $R0 ; Version
|
||||
|
||||
DetailPrint "Checking installed version of dotnet $R0"
|
||||
|
||||
StrCpy $R1 "dotnet --list-runtimes | % { if($$_ -match $\".*WindowsDesktop.*($R0.\d+).*$\") { $$matches[1] } } | Sort-Object {[int]($$_ -replace '\d.\d.(\d+)', '$$1')} -Descending | Select-Object -first 1"
|
||||
!insertmacro DotNetCorePSExec $R1 $R1
|
||||
; $R1 contains highest installed version, e.g. 6.0.7
|
||||
|
||||
${TrimNewLines} $R1 $R1
|
||||
|
||||
; If there is an installed version it should start with the same two "words" as the input version,
|
||||
; otherwise assume we got an error response
|
||||
|
||||
; todo improve this simple test which checks there are at least 3 "words" separated by periods
|
||||
${WordFind} $R1 "." "E#" $R2
|
||||
IfErrors error_${DNC_INS_ID}
|
||||
; $R2 contains number of version parts in R1 (dot separated words = version parts)
|
||||
|
||||
; If less than 3 parts, or more than 4 parts, error
|
||||
IntCmp $R2 3 0 error_${DNC_INS_ID}
|
||||
IntCmp $R2 4 0 0 error_${DNC_INS_ID}
|
||||
|
||||
; todo more error handling here / validation
|
||||
|
||||
; Seems to be OK, skip the "set to blank string" error handler
|
||||
Goto end_${DNC_INS_ID}
|
||||
|
||||
error_${DNC_INS_ID}:
|
||||
StrCpy $R1 "" ; Set result to blank string if any error occurs (means not installed)
|
||||
|
||||
end_${DNC_INS_ID}:
|
||||
!undef DNC_INS_ID
|
||||
|
||||
; Push the result onto the stack
|
||||
Push $R1
|
||||
|
||||
; Restore registers
|
||||
Exch
|
||||
Pop $R2
|
||||
Exch
|
||||
Pop $R1
|
||||
Exch
|
||||
Pop $R0
|
||||
|
||||
; Set result
|
||||
Pop ${Result}
|
||||
|
||||
!macroend
|
||||
|
||||
!macro DotNetCoreInstallVersion Version
|
||||
|
||||
; Save registers
|
||||
Push $R0
|
||||
Push $R1
|
||||
Push $R2
|
||||
Push $R3
|
||||
|
||||
; Push and pop parameters so we don't have conflicts if parameters are $R#
|
||||
Push ${Version}
|
||||
Pop $R0 ; Version
|
||||
|
||||
${If} ${IsNativeAMD64}
|
||||
StrCpy $R3 "x64"
|
||||
${ElseIf} ${IsNativeARM64}
|
||||
StrCpy $R3 "arm64"
|
||||
${ElseIf} ${IsNativeIA32}
|
||||
StrCpy $R3 "x86"
|
||||
${Else}
|
||||
StrCpy $R3 "unknown"
|
||||
${EndIf}
|
||||
|
||||
; todo can download as a .zip, which is smaller, then we'd need to unzip it before running it...
|
||||
StrCpy $R1 https://dotnetcli.azureedge.net/dotnet/WindowsDesktop/$R0/windowsdesktop-runtime-$R0-win-$R3.exe
|
||||
|
||||
; For dotnet versions less than 5 the WindowsDesktop runtime has a different path
|
||||
${WordFind} $R0 "." "+1" $R2
|
||||
IntCmp $R2 5 +2 0 +2
|
||||
StrCpy $R1 https://dotnetcli.azureedge.net/dotnet/Runtime/$R0/windowsdesktop-runtime-$R0-win-$R3.exe
|
||||
|
||||
DetailPrint "Downloading dotnet $R0 from $R1"
|
||||
|
||||
; Create destination file
|
||||
GetTempFileName $R2
|
||||
nsExec::Exec 'cmd.exe /c rename "$R2" "$R2.exe"' ; Not using Rename to avoid spam in details log
|
||||
Pop $R3 ; Pop exit code
|
||||
StrCpy $R2 "$R2.exe"
|
||||
|
||||
; Fetch runtime installer
|
||||
; todo error handling in the PS script? so we can check for errors here
|
||||
StrCpy $R1 "Invoke-WebRequest -UseBasicParsing -URI $\"$R1$\" -OutFile $\"$R2$\""
|
||||
!insertmacro DotNetCorePSExec $R1 $R1
|
||||
; $R1 contains powershell script result
|
||||
|
||||
${WordFind} $R1 "BlobNotFound" "E+1{" $R3
|
||||
ifErrors +3 0
|
||||
DetailPrint "Dotnet installer $R0 not found."
|
||||
Goto +10
|
||||
|
||||
; todo error handling for PS result, verify download result
|
||||
|
||||
|
||||
IfFileExists $R2 +3, 0
|
||||
DetailPrint "Dotnet installer did not download."
|
||||
Goto +7
|
||||
|
||||
DetailPrint "Download complete"
|
||||
|
||||
DetailPrint "Installing dotnet $R0"
|
||||
ExecWait "$\"$R2$\" /install /quiet /norestart" $R1
|
||||
DetailPrint "Installer completed (Result: $R1)"
|
||||
|
||||
nsExec::Exec 'cmd.exe /c del "$R2"' ; Not using Delete to avoid spam in details log
|
||||
Pop $R3 ; Pop exit code
|
||||
|
||||
; Error checking? Verify install result?
|
||||
|
||||
; Restore registers
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Pop $R0
|
||||
|
||||
!macroend
|
||||
|
||||
; below is adapted from https://nsis.sourceforge.io/PowerShell_support but avoids using the plugin
|
||||
; directory in favour of a temp file and providing a return variable rather than returning on the
|
||||
; stack. Methods renamed to avoid conflicting with use of the original macros
|
||||
|
||||
; DotNetCorePSExec
|
||||
; Executes a powershell script
|
||||
;
|
||||
; \param[in] PSCommand The powershell command or script to execute
|
||||
; \param[out] Result The output from the powershell script
|
||||
!macro DotNetCorePSExec PSCommand Result
|
||||
|
||||
Push ${PSCommand}
|
||||
Call DotNetCorePSExecFn
|
||||
Pop ${Result}
|
||||
|
||||
!macroend
|
||||
|
||||
; DotNetCorePSExecFile
|
||||
; Executes a powershell file
|
||||
;
|
||||
; \param[in] FilePath The path to the powershell script file to execute
|
||||
; \param[out] Result The output from the powershell script
|
||||
!macro DotNetCorePSExecFile FilePath Result
|
||||
|
||||
Push ${FilePath}
|
||||
Call DotNetCorePSExecFileFn
|
||||
Pop ${Result}
|
||||
|
||||
!macroend
|
||||
|
||||
Function DotNetCorePSExecFn
|
||||
|
||||
; Read parameters and save registers
|
||||
Exch $R0 ; Script
|
||||
Push $R1
|
||||
Push $R2
|
||||
|
||||
; Write the command into a temp file
|
||||
; Note: Using GetTempFileName to get a temp file name, but since we need to have a .ps1 extension
|
||||
; on the end we rename it with an extra file extension
|
||||
GetTempFileName $R1
|
||||
nsExec::Exec 'cmd.exe /c rename "$R1" "$R1.ps1"' ; Not using Rename to avoid spam in details log
|
||||
Pop $R2 ; Pop exit code
|
||||
StrCpy $R1 "$R1.ps1"
|
||||
|
||||
FileOpen $R2 $R1 w
|
||||
FileWrite $R2 $R0
|
||||
FileClose $R2
|
||||
|
||||
; Execute the powershell script and delete the temp file
|
||||
Push $R1
|
||||
Call DotNetCorePSExecFileFn
|
||||
nsExec::Exec 'cmd.exe /c del "$R1"' ; Not using Delete to avoid spam in details log
|
||||
Pop $R0 ; Pop exit code
|
||||
|
||||
; Restore registers
|
||||
Exch
|
||||
Pop $R2
|
||||
Exch
|
||||
Pop $R1
|
||||
Exch
|
||||
Pop $R0
|
||||
|
||||
; Stack contains script output only, which we leave as the function result
|
||||
|
||||
FunctionEnd
|
||||
|
||||
Function DotNetCorePSExecFileFn
|
||||
|
||||
; Read parameters and save registers
|
||||
Exch $R0 ; FilePath
|
||||
Push $R1
|
||||
|
||||
nsExec::ExecToStack 'powershell -inputformat none -ExecutionPolicy RemoteSigned -File "$R0" '
|
||||
; Stack contain exitCode, scriptOutput, registers
|
||||
|
||||
; Pop exit code & validate
|
||||
Pop $R1
|
||||
IntCmp $R1 0 +2
|
||||
SetErrorLevel 2
|
||||
|
||||
; Restore registers
|
||||
Exch
|
||||
Pop $R1
|
||||
Exch
|
||||
Pop $R0
|
||||
|
||||
; Stack contains script output only, which we leave as the function result
|
||||
|
||||
FunctionEnd
|
||||
|
||||
!endif
|
@@ -1,3 +1,9 @@
|
||||
!incluide "dotnet.nsh"
|
||||
|
||||
!macro NSIS_HOOK_PREINSTALL
|
||||
!insertmacro CheckDotNetCore 8.0
|
||||
!macroend
|
||||
|
||||
!macro NSIS_HOOK_POSTINSTALL
|
||||
${If} $PassiveMode = 1
|
||||
${OrIf} ${Silent}
|
@@ -286,8 +286,18 @@ pub fn get_window_title_by_pid(pid: Pid) -> String {
|
||||
}
|
||||
|
||||
pub async fn is_net8_installed() -> bool {
|
||||
use std::process::Command;
|
||||
let output_result = Command::new("dotnet").arg("--list-runtimes").output();
|
||||
use tokio::process::Command;
|
||||
|
||||
let mut command = Command::new("dotnet");
|
||||
command.arg("--list-runtimes");
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
command.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
|
||||
let output_result = command.output().await;
|
||||
match output_result {
|
||||
Ok(output) => {
|
||||
if !output.status.success() {
|
||||
@@ -300,9 +310,7 @@ pub async fn is_net8_installed() -> bool {
|
||||
}
|
||||
|
||||
let stdout_str = String::from_utf8_lossy(&output.stdout);
|
||||
stdout_str
|
||||
.lines()
|
||||
.any(|line| line.starts_with("Microsoft.WindowsDesktop.App 8."))
|
||||
stdout_str.lines().any(|line| line.starts_with("Microsoft.WindowsDesktop.App 8."))
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@
|
||||
"nsis": {
|
||||
"installMode": "currentUser",
|
||||
"compression": "lzma",
|
||||
"installerHooks": "./nsis-hooks.nsh"
|
||||
"installerHooks": "./nsis/nsis-hooks.nsh"
|
||||
}
|
||||
},
|
||||
"icon": [
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
|
@@ -53,7 +53,7 @@
|
||||
releaseStreamToReadable,
|
||||
urlIsValidImage,
|
||||
} from '@/utils';
|
||||
import { fade, scale } from 'svelte/transition';
|
||||
import { crossfade, fade, scale } from 'svelte/transition';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import Label from '@/components/ui/label/label.svelte';
|
||||
import {
|
||||
@@ -110,8 +110,20 @@
|
||||
import Hearts from '@/components/ui/effects/Hearts.svelte';
|
||||
import { EZPPActionStatus } from '@/types';
|
||||
import * as presence from '@/presence';
|
||||
import { expoOut } from 'svelte/easing';
|
||||
|
||||
const tabs = [
|
||||
{ name: 'Home', key: 'home' },
|
||||
{
|
||||
name: 'Settings',
|
||||
key: 'settings',
|
||||
},
|
||||
];
|
||||
let selectedTab = $state('home');
|
||||
let [tab_send, tab_receive] = crossfade({
|
||||
duration: $reduceAnimations ? 0 : 400,
|
||||
easing: expoOut,
|
||||
});
|
||||
let progress = $state(-1);
|
||||
let launchInfo = $state('');
|
||||
let launchError = $state<Error | undefined>(undefined);
|
||||
@@ -889,24 +901,25 @@
|
||||
<div
|
||||
class="flex flex-row flex-nowrap h-11 gap-1 w-full bg-theme-800/50 border border-theme-800/90 rounded-lg p-[4px]"
|
||||
>
|
||||
<button
|
||||
class="w-full flex justify-center items-center font-semibold text-sm rounded-lg {selectedTab ===
|
||||
'home'
|
||||
? 'bg-white/70 border border-white/60 text-theme-950'
|
||||
: ''} transition-all"
|
||||
onclick={() => (selectedTab = 'home')}
|
||||
>
|
||||
Home
|
||||
</button>
|
||||
<button
|
||||
class="w-full flex justify-center items-center font-semibold text-sm rounded-lg {selectedTab ===
|
||||
'settings'
|
||||
? 'bg-white/70 border border-white/60 text-theme-950'
|
||||
: ''} transition-all"
|
||||
onclick={() => (selectedTab = 'settings')}
|
||||
>
|
||||
Settings
|
||||
</button>
|
||||
{#each tabs as tab (tab.key)}
|
||||
<button
|
||||
class="inline-block relative py-2 px-2 disabled:opacity-25 transition-opacity w-full text-center"
|
||||
onclick={() => (selectedTab = tab.key)}
|
||||
>
|
||||
<div
|
||||
class="flex flex-row items-center justify-center gap-1 text-xs md:text-sm font-semibold w-full text-center"
|
||||
>
|
||||
<div>{tab.name}</div>
|
||||
</div>
|
||||
{#if selectedTab === tab.key}
|
||||
<div
|
||||
in:tab_receive={{ key: 'tab' }}
|
||||
out:tab_send={{ key: 'tab' }}
|
||||
class="absolute left-0 bottom-0 w-full h-full rounded-lg bg-white/10 border border-white/10"
|
||||
></div>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{#if selectedTab === 'home'}
|
||||
<div
|
||||
@@ -950,14 +963,14 @@
|
||||
</div>
|
||||
<div class="relative font-bold text-xl text-yellow-400">
|
||||
<div
|
||||
class="absolute top-1 left-1/2 -translate-x-1/2 {$skins === null
|
||||
class="absolute top-1 left-1/2 -translate-x-1/2 {!$skins && $skins !== 0
|
||||
? 'opacity-100'
|
||||
: 'opacity-0'} transition-opacity duration-1000"
|
||||
>
|
||||
<LoaderCircle class="animate-spin" />
|
||||
</div>
|
||||
<div
|
||||
class="{$skins === null
|
||||
class="{!$skins && $skins !== 0
|
||||
? 'opacity-0'
|
||||
: 'opacity-100'} transition-opacity duration-1000"
|
||||
>
|
||||
|
Reference in New Issue
Block a user