diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e590fd9..0fee3ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,35 @@ env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true jobs: + installer-defaults: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Verify installer default extension ID consistency + run: | + set -euo pipefail + + sh_id=$(sed -n 's/^DEFAULT_EXTENSION_ID="\([a-z0-9]\+\)"$/\1/p' install/install.sh) + ps_id=$(sed -n 's/^\$DefaultExtensionId = "\([a-z0-9]\+\)"$/\1/p' install/install.ps1) + rs_id=$(sed -n 's/^ const DEFAULT_EXTENSION_ID: &str = "\([a-z0-9]\+\)";$/\1/p' installer/src/main.rs) + + if [[ -z "$sh_id" || -z "$ps_id" || -z "$rs_id" ]]; then + echo "Failed to parse one or more default extension IDs" + exit 1 + fi + + if [[ "$sh_id" != "$ps_id" || "$sh_id" != "$rs_id" ]]; then + echo "Installer default extension IDs are inconsistent" + echo "install.sh: $sh_id" + echo "install.ps1: $ps_id" + echo "installer/src/main.rs: $rs_id" + exit 1 + fi + + echo "Default extension ID is consistent: $sh_id" + extension: runs-on: ubuntu-latest defaults: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c0341df..d31d08f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -97,11 +97,21 @@ jobs: run: copy target\release\freemid.exe ${{ matrix.artifact }} if: runner.os == 'Windows' + - name: Collect setup binary (Windows) + run: copy target\release\freemid-setup.exe freemid-setup.exe + if: runner.os == 'Windows' + - uses: actions/upload-artifact@v7 with: name: ${{ matrix.artifact }} path: ${{ matrix.artifact }} + - uses: actions/upload-artifact@v7 + if: runner.os == 'Windows' + with: + name: freemid-setup.exe + path: freemid-setup.exe + # Create the GitHub release with all artifacts release: needs: [audit-rust, build-extension, build-native] @@ -120,6 +130,7 @@ jobs: cp artifacts/freemid-macos-arm64/freemid-macos-arm64 . cp artifacts/freemid-macos-x86_64/freemid-macos-x86_64 . cp artifacts/freemid-windows-x86_64.exe/freemid-windows-x86_64.exe . + cp artifacts/freemid-setup.exe/freemid-setup.exe . chmod +x freemid-linux-x86_64 freemid-macos-arm64 freemid-macos-x86_64 - name: Generate checksums @@ -129,9 +140,27 @@ jobs: freemid-macos-arm64 \ freemid-macos-x86_64 \ freemid-windows-x86_64.exe \ + freemid-setup.exe \ freemid-extension.zip \ > checksums.sha256 + - name: Validate checksum coverage + run: | + required=( + freemid-linux-x86_64 + freemid-macos-arm64 + freemid-macos-x86_64 + freemid-windows-x86_64.exe + freemid-setup.exe + freemid-extension.zip + ) + for f in "${required[@]}"; do + grep -q "[[:space:]]$f$" checksums.sha256 || { + echo "Missing checksum entry for $f" + exit 1 + } + done + - uses: softprops/action-gh-release@v2 with: prerelease: ${{ contains(github.ref_name, '-') }} @@ -141,6 +170,7 @@ jobs: freemid-macos-arm64 freemid-macos-x86_64 freemid-windows-x86_64.exe + freemid-setup.exe checksums.sha256 install/install.sh install/install.ps1 diff --git a/.github/workflows/windows-artifacts.yml b/.github/workflows/windows-artifacts.yml new file mode 100644 index 0000000..b7653a1 --- /dev/null +++ b/.github/workflows/windows-artifacts.yml @@ -0,0 +1,61 @@ +name: Windows Artifacts + +on: + workflow_dispatch: + push: + branches: + - dev/ux-improvements-auto-update + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + build-windows-artifacts: + runs-on: windows-latest + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Cargo + uses: actions/cache@v5 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: windows-artifacts-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Ensure DISCORD_CLIENT_ID fallback + shell: pwsh + env: + DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }} + run: | + if ([string]::IsNullOrWhiteSpace($env:DISCORD_CLIENT_ID)) { + "DISCORD_CLIENT_ID=placeholder-ci-build" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + } else { + "DISCORD_CLIENT_ID=$env:DISCORD_CLIENT_ID" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + } + + - name: Build Windows binaries + run: cargo build --release -p freemid -p freemid-installer + + - name: Collect artifacts + shell: pwsh + run: | + New-Item -ItemType Directory -Path artifacts -Force | Out-Null + Copy-Item target/release/freemid.exe artifacts/freemid-windows-x86_64.exe + Copy-Item target/release/freemid-setup.exe artifacts/freemid-setup.exe + Copy-Item install/install.ps1 artifacts/install.ps1 + Copy-Item install/uninstall.ps1 artifacts/uninstall.ps1 + + - name: Upload Windows artifacts + uses: actions/upload-artifact@v7 + with: + name: freemid-windows-artifacts-${{ github.sha }} + path: artifacts/* diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..18d10c7 --- /dev/null +++ b/.yamllint @@ -0,0 +1,9 @@ +extends: default + +rules: + document-start: disable + line-length: + max: 140 + level: warning + truthy: + check-keys: false \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 4718ba4..ab02d77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,155 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "embed-resource" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d506610004cfc74a6f5ee7e8c632b355de5eca1f03ee5e5e0ec11b77d4eb3d61" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml", + "vswhom", + "winreg", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "freemid" version = "0.3.11" @@ -13,6 +162,176 @@ dependencies = [ [[package]] name = "freemid-installer" version = "0.3.11" +dependencies = [ + "embed-resource", + "native-windows-gui", + "sha2", + "ureq", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] [[package]] name = "itoa" @@ -20,12 +339,143 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "js-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "log" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f" + [[package]] name = "memchr" version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "native-windows-gui" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7003a669f68deb6b7c57d74fff4f8e533c44a3f0b297492440ef4ff5a28454" +dependencies = [ + "bitflags", + "lazy_static", + "newline-converter", + "plotters", + "plotters-backend", + "stretch", + "winapi", + "winapi-build", +] + +[[package]] +name = "newline-converter" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -44,6 +494,76 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.228" @@ -88,21 +608,581 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.117" +name = "serde_spanned" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "serde", ] [[package]] -name = "unicode-ident" -version = "1.0.24" +name = "sha2" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stretch" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0dc6d20ce137f302edf90f9cd3d278866fd7fb139efca6f246161222ad6d87" +dependencies = [ + "lazy_static", + "libm", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zmij" diff --git a/README.md b/README.md index b4454b4..a24914a 100644 --- a/README.md +++ b/README.md @@ -83,24 +83,15 @@ No admin rights required. irm https://github.com/ClickSentinel/FreeMiD/releases/latest/download/install.ps1 | iex ``` -To use a custom extension ID: - -```powershell -$env:FREEMID_EXTENSION_ID = "" -.\freemid-setup.exe -# or -$env:FREEMID_EXTENSION_ID = ""; irm .../install.ps1 | iex -``` - | Platform | Binary location | Manifest registration | | --- | --- | --- | | Linux | `~/.local/bin/freemid` | `~/.config//NativeMessagingHosts/` | | macOS | `~/.local/bin/freemid` | `~/Library/Application Support//NativeMessagingHosts/` | | Windows | `%LOCALAPPDATA%\FreeMiD\freemid.exe` | `HKCU\Software\\NativeMessagingHosts\` | -### Step 3 — Restart your browser +### Step 3 — Reload extension (restart only if needed) -Chrome caches native messaging manifests at startup. A full restart (not just extension reload) is required after installation. +After installing the native host, reload the FreeMiD extension page once. If the host is not detected right away, restart your browser and check again. ### Step 4 — Verify @@ -118,7 +109,11 @@ Click the FreeMiD icon in your toolbar. The dot should turn **green** within a f curl -sSL https://github.com/ClickSentinel/FreeMiD/releases/latest/download/uninstall.sh | bash ``` -**Windows (PowerShell):** +**Windows (recommended):** + +Use Apps and Features uninstall for FreeMiD. This runs local Setup uninstall mode. + +**Windows (PowerShell fallback):** ```powershell irm https://github.com/ClickSentinel/FreeMiD/releases/latest/download/uninstall.ps1 | iex diff --git a/docs/WINDOWS-INSTALLER-PLAN.md b/docs/WINDOWS-INSTALLER-PLAN.md new file mode 100644 index 0000000..402c039 --- /dev/null +++ b/docs/WINDOWS-INSTALLER-PLAN.md @@ -0,0 +1,136 @@ +# Windows Installer and Uninstaller Plan + +## Goals + +1. Ship a dead-simple Windows GUI installer experience. +2. Provide a matching dead-simple uninstall experience. +3. Keep installation non-admin (HKCU + LOCALAPPDATA). +4. Ensure release artifacts and checksums are always in sync. +5. Ensure all installers use the production extension ID by default. + +## Current Gaps + +1. `freemid-setup.exe` is documented but not published by the release workflow. +2. Windows install can fail if `checksums.sha256` does not contain the expected Windows artifact line. +3. Uninstall is script-only (`uninstall.ps1`) and not exposed as a GUI workflow. +4. Extension ID consistency must be validated across all installer entrypoints on every release. + +## Phase 1: Make Release Artifacts Reliable (First) + +1. Update `.github/workflows/release.yml` to build and upload `freemid-setup.exe` from the `installer` crate. +2. Include `freemid-setup.exe` in release assets uploaded by `softprops/action-gh-release`. +3. Generate `checksums.sha256` from the final release file set and include all expected artifacts: + - `freemid-windows-x86_64.exe` + - `freemid-setup.exe` + - existing Linux/macOS binaries + - `freemid-extension.zip` +4. Add a release validation step that fails if `checksums.sha256` is missing any required filenames. +5. Add a post-release smoke test job that downloads `install.ps1` + `checksums.sha256` and verifies the artifact lookup succeeds for Windows. + +## Phase 2: Unify and Harden Installer Defaults + +1. Keep production extension ID default in all installer paths: + - `install/install.ps1` + - `install/install.sh` + - `installer/src/main.rs` +2. Add a CI check script that asserts these defaults are identical. +3. Add an install script runtime warning when non-default extension ID is used (for clarity, not failure). +4. Improve checksum parser robustness in PowerShell installer: + - normalize line endings + - tolerate extra whitespace + - fail with explicit diagnostics showing artifact name and first lines of checksum file +5. Keep checksum mismatch behavior strict (delete downloaded binary and exit non-zero). + +## Phase 3: GUI Installer UX (Dead Simple) + +1. Convert `freemid-setup.exe` to true GUI mode (`windows_subsystem = "windows"`). +2. Implement one lightweight window with these actions: + - Install or Update + - Uninstall + - Open troubleshooting docs +3. Display simple progress states: + - Stopping existing process + - Downloading + - Verifying checksum + - Registering host + - Complete or failed +4. Show actionable error dialogs with copyable details. +5. Keep advanced options hidden by default: + - release tag override + +## Phase 4: GUI Uninstaller Support + +1. Add uninstall execution path to Rust installer binary (same logic as `uninstall.ps1`). +2. Ensure uninstall removes: + - browser host registry entries (HKCU) + - manifest + - `freemid.exe` (unless user chooses keep-binary) +3. Add confirmation dialog and completion status. +4. Preserve script uninstall for automation and fallback. + +## Phase 5: Windows App Lifecycle Integration + +1. Register in Apps and Features (ARP) under HKCU: + - DisplayName + - DisplayVersion + - Publisher + - UninstallString (pointing to local setup uninstaller mode, no remote script fetch) +2. Add Start Menu shortcuts: + - FreeMiD Setup + - Uninstall FreeMiD +3. Optional: add file version metadata and code signing preparation notes. + +## Safety Hardening Steps (Current Priority) + +1. Make Apps and Features uninstall local and deterministic. + - Set `UninstallString` to local `freemid-setup.exe --uninstall`. + - Do not use remote `irm ... | iex` in ARP uninstall path. +2. Add explicit installer CLI uninstall mode. + - Ensure `freemid-setup.exe --uninstall` runs the same uninstall logic as GUI uninstall. + - Use `--silent` for ARP-triggered uninstall flows. +3. Remove installer runtime dependency on PowerShell for download and hashing. + - Replace PowerShell `Invoke-WebRequest` and `Get-FileHash` with native Rust HTTP + SHA256. +4. Fail install when browser host registration did not succeed for any target browser. + - Keep per-browser warnings for diagnostics but return a user-visible install failure on zero successful registrations. + +Status in this branch: + +- Step 1 implemented: ARP uninstall now targets local setup uninstall mode. +- Step 2 implemented: `--uninstall` mode and `--silent` mode added to setup executable. +- Step 3 implemented: setup now uses native Rust HTTP and SHA256 for download/checksum verification. +- Step 4 implemented: setup fails install when browser host registration succeeds for zero target browsers. + +## Phase 6: Docs and Extension UX Alignment + +1. Update README to match actual published files and exact install paths. +2. Update popup Update and Uninstall behavior on Windows: + - prefer GUI setup executable when available + - fallback to script URLs when not available +3. Add troubleshooting section for checksum/artifact mismatch. +4. Update installer completion copy to avoid browser restart requirement and instead guide users to install or enable the extension. + +## Validation Checklist (Release Gate) + +1. `install.ps1` succeeds on clean Windows profile. +2. `freemid-setup.exe` install succeeds on clean Windows profile. +3. GUI uninstall removes all expected artifacts and registry keys. +4. `checksums.sha256` contains all required release files. +5. Default extension ID is identical across all installer entrypoints. +6. Reinstall over existing version works without admin rights. +7. Browser restart guidance appears after successful install. + +## Delivery Sequence + +1. PR 1: release artifact and checksum reliability (Phase 1). +2. PR 2: default consistency checks + script hardening (Phase 2). +3. PR 3: GUI installer shell + install flow (Phase 3). +4. PR 4: GUI uninstall + ARP integration (Phases 4 and 5). +5. PR 5: docs and popup polish (Phase 6). + +## Owner Notes + +1. Keep all Windows paths under `%LOCALAPPDATA%\\FreeMiD`. +2. Keep registry writes under `HKCU` only. +3. Keep `uninstall.ps1` as permanent fallback even after GUI uninstall is shipped. +4. Keep checksum verification mandatory for downloaded binaries. +5. Do not require browser restart in installer messaging; guide extension install/enable actions instead. diff --git a/extension/public/popup/index.html b/extension/public/popup/index.html index e3cd7c4..578f729 100644 --- a/extension/public/popup/index.html +++ b/extension/public/popup/index.html @@ -14,6 +14,7 @@ --text-1: #c4c8d4; --text-2: #7d8395; --accent: #5865f2; + --accent-strong: #7983f5; --accent-soft: rgba(88, 101, 242, 0.18); --success: #2dd17a; --warning: #f0b232; @@ -47,7 +48,7 @@ width: 36px; height: 36px; border-radius: 10px; - background: linear-gradient(135deg, var(--accent) 0%, #7983f5 100%); + background: linear-gradient(135deg, var(--accent) 0%, var(--accent-strong) 100%); display: grid; place-items: center; color: #fff; @@ -106,12 +107,12 @@ @keyframes pulse { 0%, 100% { opacity: 1; } - 50% { opacity: 0.35; } + 50% { opacity: 0.35; } } .status-label { flex: 1; - font-weight: 500; + font-weight: 600; color: var(--text-0); font-size: 13px; } @@ -124,33 +125,24 @@ .status-text { min-width: 0; flex: 1; } - .panel { - padding: 14px 18px; + .panel, + .control-row, + .help, + .now-playing, + .update-banner, + footer { + padding: 12px 18px; border-bottom: 1px solid var(--border); } - .panel-label { - font-size: 10px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.08em; - color: var(--text-2); - margin-bottom: 6px; - } - - .panel-value { - font-size: 13px; - color: var(--text-0); - font-weight: 500; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + .panel, + .control-row, + footer { + background: var(--bg-1); } .help { - padding: 14px 18px; background: var(--bg-2); - border-bottom: 1px solid var(--border); font-size: 12px; color: var(--text-1); } @@ -172,16 +164,14 @@ } .help p { margin-top: 2px; } - .help.hidden { display: none; } footer { - padding: 10px 18px; display: flex; justify-content: space-between; align-items: center; - background: var(--bg-1); font-size: 11px; + border-bottom: 0; } footer a { @@ -202,7 +192,7 @@ gap: 5px; padding: 4px 10px; background: var(--accent-soft); - border: 1px solid rgba(88,101,242,0.35); + border: 1px solid rgba(88, 101, 242, 0.35); border-radius: 6px; color: var(--accent); font-size: 11px; @@ -211,13 +201,12 @@ transition: background 0.15s, opacity 0.15s; flex-shrink: 0; } - .btn-reconnect:hover { background: rgba(88,101,242,0.28); } + .btn-reconnect:hover { background: rgba(88, 101, 242, 0.28); } .btn-reconnect:disabled { opacity: 0.45; cursor: not-allowed; } .btn-reconnect svg { flex-shrink: 0; } @keyframes spin { to { transform: rotate(360deg); } } .btn-reconnect.spinning svg { animation: spin 0.7s linear infinite; } - /* Toggle switch */ .toggle-btn { position: relative; width: 36px; @@ -243,100 +232,27 @@ } .toggle-btn[aria-checked="true"] .toggle-thumb { transform: translateX(16px); background: #fff; } - /* Pause control row */ .control-row { display: flex; align-items: center; justify-content: space-between; - padding: 11px 18px; - border-bottom: 1px solid var(--border); - background: var(--bg-1); } .control-label { display: flex; flex-direction: column; gap: 2px; } - .control-label > span:first-child { font-size: 13px; font-weight: 500; color: var(--text-0); } + .control-label > span:first-child { font-size: 13px; font-weight: 600; color: var(--text-0); } .control-sub { font-size: 11px; color: var(--text-2); } .control-row[data-paused="true"] .control-sub { color: var(--warning); } - /* Site toggles */ .site-toggles { display: flex; flex-direction: column; gap: 10px; } .site-row { display: flex; align-items: center; justify-content: space-between; } .site-row span { font-size: 12px; color: var(--text-1); } - /* Activity sub-label */ - .panel-sub { - font-size: 11px; - color: var(--text-2); - margin-top: 1px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .preview-meta { - margin-top: 5px; - display: flex; - align-items: center; - gap: 6px; - font-size: 11px; - color: var(--text-2); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .preview-meta-icon { - flex-shrink: 0; - opacity: 0.9; - } - - .preview-meta-text { - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .preview-extra { - margin-top: 6px; - display: flex; - align-items: center; - gap: 6px; - } - - .preview-art { - width: 18px; - height: 18px; - border-radius: 4px; - object-fit: cover; - border: 1px solid var(--border); - background: var(--bg-1); - opacity: 0.9; - } - - .preview-button { - display: inline-flex; - align-items: center; - min-width: 0; - max-width: 100%; - padding: 2px 7px; - border-radius: 999px; - background: rgba(255, 255, 255, 0.05); - border: 1px solid var(--border); - font-size: 10px; - color: var(--text-1); - line-height: 1.25; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - /* Open Discord button */ - .btn-discord { + .btn-discord, + .btn-install { display: inline-flex; align-items: center; margin-top: 8px; padding: 5px 12px; - background: var(--accent); + background: linear-gradient(135deg, var(--accent) 0%, var(--accent-strong) 100%); border: none; border-radius: 6px; color: #fff; @@ -345,32 +261,32 @@ cursor: pointer; transition: opacity 0.15s; } - .btn-discord:hover { opacity: 0.85; } + .btn-discord:hover, + .btn-install:hover { opacity: 0.85; } - .btn-install { - display: inline-flex; - align-items: center; - margin-top: 8px; - padding: 5px 12px; - background: var(--accent); - border: none; - border-radius: 6px; - color: #fff; - font-size: 12px; + .panel-label { + font-size: 10px; font-weight: 600; - cursor: pointer; - transition: opacity 0.15s; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--text-2); + margin-bottom: 6px; + } + + .panel-value { + font-size: 13px; + color: var(--text-0); + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } - .btn-install:hover { opacity: 0.85; } - /* Update banner */ .update-banner { display: flex; align-items: center; justify-content: space-between; - padding: 8px 18px; background: rgba(240, 178, 50, 0.1); - border-top: 1px solid rgba(240, 178, 50, 0.25); font-size: 12px; color: var(--warning); gap: 8px; @@ -390,9 +306,9 @@ } .btn-update:hover { opacity: 0.82; } - /* host-version is now inside .version-group — see .version-host */ - - .now-playing { padding: 12px 18px; border-bottom: 1px solid var(--border); background: var(--bg-0); } + .now-playing { + background: var(--bg-0); + } .now-playing.hidden { display: none; } .now-top { display: flex; @@ -478,7 +394,6 @@ font-variant-numeric: tabular-nums; } - /* GitHub icon link */ .gh-link { color: var(--text-2); display: flex; align-items: center; transition: color 0.15s; } .gh-link:hover { color: var(--text-0); } @@ -536,7 +451,7 @@

FreeMiD

@@ -601,7 +516,7 @@

FreeMiD