diff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml index b27a8641e..f5f276da8 100644 --- a/.github/workflows/meta.yml +++ b/.github/workflows/meta.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: permissions: - contents: write + contents: write jobs: update: @@ -26,17 +26,13 @@ jobs: run: pnpm i - name: Update metainfo - run: pnpm updateMeta + run: pnpm generateMeta - name: Commit and merge in changes run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - gh release upload "${{ github.event.release.tag_name }}" meta/dev.vencord.Vesktop.metainfo.xml - - git add meta/dev.vencord.Vesktop.metainfo.xml - git commit -m "metainfo: add entry for ${{ github.event.release.tag_name }}" - git push origin HEAD:main + gh release upload "${{ github.event.release.tag_name }}" dist/dev.vencord.Vesktop.metainfo.xml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 967d1c201..5da8894c3 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Vesktop is a custom Discord desktop app ## Installing -Visit https://vesktop.vencord.dev/install +Visit https://vesktop.dev/install ## Building from Source @@ -47,3 +47,14 @@ pnpm package --linux pacman # Or package to a directory only pnpm package:dir ``` + +## Building LibVesktop from Source + +This is a small C++ helper library Vesktop uses on Linux to emit D-Bus events. By default, prebuilt binaries for x64 and arm64 are used. + +If you want to build it from source: +1. Install build dependencies: + - Debian/Ubuntu: `apt install build-essential python3 curl pkg-config libglib2.0-dev` + - Fedora: `dnf install @c-development @development-tools python3 curl pkgconf-pkg-config glib2-devel` +2. Run `pnpm buildLibVesktop` +3. From now on, building Vesktop will use your own build \ No newline at end of file diff --git a/build/Assets.car b/build/Assets.car new file mode 100644 index 000000000..60c0fe0ae Binary files /dev/null and b/build/Assets.car differ diff --git a/build/background.tiff b/build/background.tiff index a0e3f7f10..1306d0756 100644 Binary files a/build/background.tiff and b/build/background.tiff differ diff --git a/build/icon.icns b/build/icon.icns index 34d837112..6b3e94f74 100644 Binary files a/build/icon.icns and b/build/icon.icns differ diff --git a/build/icon.ico b/build/icon.ico new file mode 100644 index 000000000..9e06b4314 Binary files /dev/null and b/build/icon.ico differ diff --git a/build/icon.png b/build/icon.png deleted file mode 100644 index 027ab0b6b..000000000 Binary files a/build/icon.png and /dev/null differ diff --git a/build/icon.svg b/build/icon.svg new file mode 100644 index 000000000..3bcbd110a --- /dev/null +++ b/build/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/package.json b/package.json index 4be176026..97ed640f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vesktop", - "version": "1.5.8", + "version": "1.6.1", "private": true, "description": "Vesktop is a custom Discord desktop app", "keywords": [], @@ -11,6 +11,7 @@ "scripts": { "build": "tsx scripts/build/build.mts", "build:dev": "pnpm build --dev", + "buildLibVesktop": "pnpm -C packages/libvesktop install && pnpm -C packages/libvesktop run build", "package": "pnpm build && electron-builder", "package:dir": "pnpm build && electron-builder --dir", "lint": "eslint", @@ -21,7 +22,7 @@ "test": "pnpm lint && pnpm testTypes", "testTypes": "tsc --noEmit", "watch": "pnpm build --watch", - "updateMeta": "tsx scripts/utils/updateMeta.mts", + "generateMeta": "tsx scripts/utils/generateMeta.mts", "updateArrpcDB": "node ./node_modules/arrpc/update_db.js", "postinstall": "pnpm updateArrpcDB" }, @@ -35,28 +36,29 @@ }, "devDependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", - "@stylistic/eslint-plugin": "^5.1.0", - "@types/node": "^24.0.10", - "@types/react": "18.3.1", - "@vencord/types": "^1.11.5", - "dotenv": "^16.5.0", - "electron": "^37.2.0", + "@stylistic/eslint-plugin": "^5.5.0", + "@types/node": "^24.9.1", + "@types/react": "19.2.1", + "@vencord/types": "^1.13.2", + "dotenv": "^17.2.3", + "electron": "^39.0.0", "electron-builder": "^26.0.12", - "esbuild": "^0.25.5", - "eslint": "^9.30.1", + "esbuild": "^0.25.11", + "eslint": "^9.38.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-path-alias": "^2.1.0", - "eslint-plugin-prettier": "^5.5.1", + "eslint-plugin-prettier": "^5.5.4", "eslint-plugin-simple-header": "^1.2.2", "eslint-plugin-simple-import-sort": "^12.1.1", - "eslint-plugin-unused-imports": "^4.1.4", + "eslint-plugin-unused-imports": "^4.3.0", + "libvesktop": "link:packages/libvesktop", "prettier": "^3.6.2", "source-map-support": "^0.5.21", - "tsx": "^4.20.3", - "type-fest": "^4.41.0", - "typescript": "^5.8.3", - "typescript-eslint": "^8.35.1", - "xml-formatter": "^3.6.6" + "tsx": "^4.20.6", + "type-fest": "^5.1.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.46.2", + "xml-formatter": "^3.6.7" }, "packageManager": "pnpm@10.7.1", "engines": { @@ -66,6 +68,7 @@ "build": { "appId": "dev.vencord.vesktop", "productName": "Vesktop", + "executableName": "vesktop", "files": [ "!*", "!node_modules", @@ -80,9 +83,10 @@ "discord" ] }, - "beforePack": "scripts/build/sandboxFix.js", + "beforePack": "scripts/build/beforePack.mjs", + "afterPack": "scripts/build/afterPack.mjs", "linux": { - "icon": "build/icon.icns", + "icon": "build/icon.svg", "category": "Network", "maintainer": "vendicated+vesktop@riseup.net", "target": [ @@ -142,7 +146,8 @@ "NSMicrophoneUsageDescription": "This app needs access to the microphone", "NSCameraUsageDescription": "This app needs access to the camera", "com.apple.security.device.audio-input": true, - "com.apple.security.device.camera": true + "com.apple.security.device.camera": true, + "CFBundleIconName": "Icon" }, "notarize": true }, @@ -172,6 +177,7 @@ "oneClick": false }, "win": { + "icon": "build/icon.ico", "target": [ { "target": "nsis", diff --git a/packages/libvesktop/.gitignore b/packages/libvesktop/.gitignore new file mode 100644 index 000000000..c795b054e --- /dev/null +++ b/packages/libvesktop/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/packages/libvesktop/Dockerfile b/packages/libvesktop/Dockerfile new file mode 100644 index 000000000..864ff2619 --- /dev/null +++ b/packages/libvesktop/Dockerfile @@ -0,0 +1,19 @@ +# Dockerfile for building both x64 and arm64 on an old distro for maximum compatibility. + +# ubuntu20 is dead but debian11 is still supported for now +FROM debian:11 +ENV DEBIAN_FRONTEND=noninteractive + +RUN dpkg --add-architecture arm64 + +RUN apt-get update && apt-get install -y \ + build-essential python3 curl pkg-config \ + g++-aarch64-linux-gnu libglib2.0-dev:amd64 libglib2.0-dev:arm64 \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get update && apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /src diff --git a/packages/libvesktop/binding.gyp b/packages/libvesktop/binding.gyp new file mode 100644 index 000000000..685aadfaf --- /dev/null +++ b/packages/libvesktop/binding.gyp @@ -0,0 +1,19 @@ +{ + "targets": [ + { + "target_name": "libvesktop", + "sources": [ "src/libvesktop.cc" ], + "include_dirs": [ + "=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@npmcli/agent@3.0.0': + resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/fs@4.0.0': + resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + cacache@19.0.1: + resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + exponential-backoff@3.1.2: + resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + make-fetch-happen@14.0.3: + resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@4.0.1: + resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-addon-api@8.5.0: + resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} + engines: {node: ^18 || ^20 || >= 21} + + node-gyp@11.4.2: + resolution: {integrity: sha512-3gD+6zsrLQH7DyYOUIutaauuXrcyxeTPyQuZQCQoNPZMHMMS5m4y0xclNpvYzoK3VNzuyxT6eF4mkIL4WSZ1eQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + p-map@7.0.3: + resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} + engines: {node: '>=18'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + ssri@12.0.0: + resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + tar@7.5.1: + resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + unique-filename@4.0.0: + resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + unique-slug@5.0.0: + resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==} + engines: {node: ^18.17.0 || >=20.5.0} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + +snapshots: + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@npmcli/agent@3.0.0': + dependencies: + agent-base: 7.1.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 10.4.3 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/fs@4.0.0': + dependencies: + semver: 7.7.2 + + '@pkgjs/parseargs@0.11.0': + optional: true + + abbrev@3.0.1: {} + + agent-base@7.1.4: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + balanced-match@1.0.2: {} + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + cacache@19.0.1: + dependencies: + '@npmcli/fs': 4.0.0 + fs-minipass: 3.0.3 + glob: 10.4.5 + lru-cache: 10.4.3 + minipass: 7.1.2 + minipass-collect: 2.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 7.0.3 + ssri: 12.0.0 + tar: 7.5.1 + unique-filename: 4.0.0 + + chownr@3.0.0: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + eastasianwidth@0.2.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + env-paths@2.2.1: {} + + err-code@2.0.3: {} + + exponential-backoff@3.1.2: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.2 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + graceful-fs@4.2.11: {} + + http-cache-semantics@4.2.0: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + + imurmurhash@0.1.4: {} + + ip-address@10.0.1: {} + + is-fullwidth-code-point@3.0.0: {} + + isexe@2.0.0: {} + + isexe@3.1.1: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + lru-cache@10.4.3: {} + + make-fetch-happen@14.0.3: + dependencies: + '@npmcli/agent': 3.0.0 + cacache: 19.0.1 + http-cache-semantics: 4.2.0 + minipass: 7.1.2 + minipass-fetch: 4.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 5.0.0 + promise-retry: 2.0.1 + ssri: 12.0.0 + transitivePeerDependencies: + - supports-color + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.2 + + minipass-fetch@4.0.1: + dependencies: + minipass: 7.1.2 + minipass-sized: 1.0.3 + minizlib: 3.1.0 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@7.1.2: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.2 + + ms@2.1.3: {} + + negotiator@1.0.0: {} + + node-addon-api@8.5.0: {} + + node-gyp@11.4.2: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.2 + graceful-fs: 4.2.11 + make-fetch-happen: 14.0.3 + nopt: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.2 + tar: 7.5.1 + tinyglobby: 0.2.15 + which: 5.0.0 + transitivePeerDependencies: + - supports-color + + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + + p-map@7.0.3: {} + + package-json-from-dist@1.0.1: {} + + path-key@3.1.1: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + picomatch@4.0.3: {} + + proc-log@5.0.0: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + + retry@0.12.0: {} + + safer-buffer@2.1.2: + optional: true + + semver@7.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + + socks@2.8.7: + dependencies: + ip-address: 10.0.1 + smart-buffer: 4.2.0 + + ssri@12.0.0: + dependencies: + minipass: 7.1.2 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + tar@7.5.1: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + unique-filename@4.0.0: + dependencies: + unique-slug: 5.0.0 + + unique-slug@5.0.0: + dependencies: + imurmurhash: 0.1.4 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@5.0.0: + dependencies: + isexe: 3.1.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + yallist@4.0.0: {} + + yallist@5.0.0: {} diff --git a/packages/libvesktop/prebuilds/vesktop-arm64.node b/packages/libvesktop/prebuilds/vesktop-arm64.node new file mode 100755 index 000000000..ba301872c Binary files /dev/null and b/packages/libvesktop/prebuilds/vesktop-arm64.node differ diff --git a/packages/libvesktop/prebuilds/vesktop-x64.node b/packages/libvesktop/prebuilds/vesktop-x64.node new file mode 100755 index 000000000..843d4998d Binary files /dev/null and b/packages/libvesktop/prebuilds/vesktop-x64.node differ diff --git a/packages/libvesktop/src/libvesktop.cc b/packages/libvesktop/src/libvesktop.cc new file mode 100644 index 000000000..8222bb30b --- /dev/null +++ b/packages/libvesktop/src/libvesktop.cc @@ -0,0 +1,269 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +template +struct GObjectDeleter +{ + void operator()(T *obj) const + { + if (obj) + g_object_unref(obj); + } +}; + +template +using GObjectPtr = std::unique_ptr>; + +struct GVariantDeleter +{ + void operator()(GVariant *variant) const + { + if (variant) + g_variant_unref(variant); + } +}; + +using GVariantPtr = std::unique_ptr; + +struct GErrorDeleter +{ + void operator()(GError *error) const + { + if (error) + g_error_free(error); + } +}; + +using GErrorPtr = std::unique_ptr; + +bool update_launcher_count(int count) +{ + GError *error = nullptr; + + const char *chromeDesktop = std::getenv("CHROME_DESKTOP"); + std::string desktop_id = std::string("application://") + (chromeDesktop ? chromeDesktop : "vesktop.desktop"); + + GObjectPtr bus(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error)); + if (!bus) + { + GErrorPtr error_ptr(error); + std::cerr << "[libvesktop::update_launcher_count] Failed to connect to session bus: " + << (error_ptr ? error_ptr->message : "unknown error") << std::endl; + return false; + } + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&builder, "{sv}", "count", g_variant_new_int64(count)); + g_variant_builder_add(&builder, "{sv}", "count-visible", g_variant_new_boolean(count != 0)); + + gboolean result = g_dbus_connection_emit_signal( + bus.get(), + nullptr, + "/", + "com.canonical.Unity.LauncherEntry", + "Update", + g_variant_new("(sa{sv})", desktop_id.c_str(), &builder), + &error); + + if (!result || error) + { + GErrorPtr error_ptr(error); + std::cerr << "[libvesktop::update_launcher_count] Failed to emit Update signal: " + << (error_ptr ? error_ptr->message : "unknown error") << std::endl; + return false; + } + + return true; +} + +std::optional get_accent_color() +{ + GError *error = nullptr; + + GObjectPtr bus(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error)); + if (!bus) + { + GErrorPtr error_ptr(error); + std::cerr << "[libvesktop::get_accent_color] Failed to connect to session bus: " + << (error_ptr ? error_ptr->message : "unknown error") << std::endl; + return std::nullopt; + } + + GVariantPtr reply(g_dbus_connection_call_sync( + bus.get(), + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", + "Read", + g_variant_new("(ss)", "org.freedesktop.appearance", "accent-color"), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + 5000, + nullptr, + &error)); + + if (!reply) + { + GErrorPtr error_ptr(error); + std::cerr << "[libvesktop::get_accent_color] Failed to call Read: " + << (error_ptr ? error_ptr->message : "unknown error") << std::endl; + return std::nullopt; + } + + GVariant *inner_raw = nullptr; + g_variant_get(reply.get(), "(v)", &inner_raw); + if (!inner_raw) + { + std::cerr << "[libvesktop::get_accent_color] Inner variant is null" << std::endl; + return std::nullopt; + } + + GVariantPtr inner(inner_raw); + + // Unwrap nested variants + while (g_variant_is_of_type(inner.get(), G_VARIANT_TYPE_VARIANT)) + { + GVariant *next = g_variant_get_variant(inner.get()); + inner.reset(next); + } + + if (!g_variant_is_of_type(inner.get(), G_VARIANT_TYPE_TUPLE) || + g_variant_n_children(inner.get()) < 3) + { + std::cerr << "[libvesktop::get_accent_color] Inner variant is not a tuple of 3 doubles" << std::endl; + return std::nullopt; + } + + double r = 0.0, g = 0.0, b = 0.0; + g_variant_get(inner.get(), "(ddd)", &r, &g, &b); + + bool discard = false; + auto toInt = [&discard](double v) -> int + { + if (!std::isfinite(v) || v < 0.0 || v > 1.0) + { + discard = true; + return 0; + } + + return static_cast(std::round(v * 255.0)); + }; + + int32_t rgb = (toInt(r) << 16) | (toInt(g) << 8) | toInt(b); + if (discard) + return std::nullopt; + + return rgb; +} + +bool request_background(bool autostart, const std::vector &commandline) +{ + GError *error = nullptr; + + GObjectPtr bus(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error)); + if (!bus) + { + GErrorPtr error_ptr(error); + std::cerr << "[libvesktop::request_background] Failed to connect to session bus: " + << (error_ptr ? error_ptr->message : "unknown error") << std::endl; + return false; + } + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&builder, "{sv}", "autostart", g_variant_new_boolean(autostart)); + + if (!commandline.empty()) + { + GVariantBuilder cmd_builder; + g_variant_builder_init(&cmd_builder, G_VARIANT_TYPE("as")); + for (const auto &s : commandline) + g_variant_builder_add(&cmd_builder, "s", s.c_str()); + g_variant_builder_add(&builder, "{sv}", "commandline", g_variant_builder_end(&cmd_builder)); + } + + GVariantPtr reply(g_dbus_connection_call_sync( + bus.get(), + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Background", + "RequestBackground", + g_variant_new("(sa{sv})", "", &builder), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + 5000, + nullptr, + &error)); + + if (!reply) + { + GErrorPtr error_ptr(error); + std::cerr << "[libvesktop::request_background] Failed to call RequestBackground: " + << (error_ptr ? error_ptr->message : "unknown error") << std::endl; + return false; + } + + return true; +} + +Napi::Value updateUnityLauncherCount(Napi::CallbackInfo const &info) +{ + if (info.Length() < 1 || !info[0].IsNumber()) + { + Napi::TypeError::New(info.Env(), "Expected (number)").ThrowAsJavaScriptException(); + return info.Env().Undefined(); + } + + int count = info[0].As().Int32Value(); + bool success = update_launcher_count(count); + return Napi::Boolean::New(info.Env(), success); +} + +Napi::Value getAccentColor(const Napi::CallbackInfo &info) +{ + auto color = get_accent_color(); + if (color) + return Napi::Number::New(info.Env(), *color); + return info.Env().Null(); +} + +Napi::Value RequestBackground(const Napi::CallbackInfo &info) +{ + Napi::Env env = info.Env(); + + if (info.Length() < 2 || !info[0].IsBoolean() || !info[1].IsArray()) + { + Napi::TypeError::New(env, "Expected (boolean, string[])").ThrowAsJavaScriptException(); + return env.Null(); + } + + bool autostart = info[0].As(); + Napi::Array arr = info[1].As(); + std::vector commandline; + for (uint32_t i = 0; i < arr.Length(); i++) + { + Napi::Value v = arr.Get(i); + if (v.IsString()) + commandline.push_back(v.As().Utf8Value()); + } + + bool ok = request_background(autostart, commandline); + return Napi::Boolean::New(env, ok); +} + +Napi::Object Init(Napi::Env env, Napi::Object exports) +{ + exports.Set("updateUnityLauncherCount", Napi::Function::New(env, updateUnityLauncherCount)); + exports.Set("getAccentColor", Napi::Function::New(env, getAccentColor)); + exports.Set("requestBackground", Napi::Function::New(env, RequestBackground)); + return exports; +} + +NODE_API_MODULE(libvesktop, Init) diff --git a/packages/libvesktop/test.js b/packages/libvesktop/test.js new file mode 100644 index 000000000..c2c12e6ec --- /dev/null +++ b/packages/libvesktop/test.js @@ -0,0 +1,22 @@ +/** + * @type {typeof import(".")} + */ +const libVesktop = require("."); +const test = require("node:test"); +const assert = require("node:assert/strict"); + +test("getAccentColor should return a number", () => { + const color = libVesktop.getAccentColor(); + assert.strictEqual(typeof color, "number"); +}); + +test("updateUnityLauncherCount should return true (success)", () => { + assert.strictEqual(libVesktop.updateUnityLauncherCount(5), true); + assert.strictEqual(libVesktop.updateUnityLauncherCount(0), true); + assert.strictEqual(libVesktop.updateUnityLauncherCount(10), true); +}); + +test("requestBackground should return true (success)", () => { + assert.strictEqual(libVesktop.requestBackground(true, ["bash"]), true); + assert.strictEqual(libVesktop.requestBackground(false, []), true); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff37bc548..b1a953af1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,50 +30,53 @@ importers: specifier: ^2.1.2 version: 2.1.2 '@stylistic/eslint-plugin': - specifier: ^5.1.0 - version: 5.1.0(eslint@9.30.1) + specifier: ^5.5.0 + version: 5.5.0(eslint@9.38.0) '@types/node': - specifier: ^24.0.10 - version: 24.0.10 + specifier: ^24.9.1 + version: 24.9.1 '@types/react': - specifier: 18.3.1 - version: 18.3.1 + specifier: 19.2.1 + version: 19.2.1 '@vencord/types': - specifier: ^1.11.5 - version: 1.11.5 + specifier: ^1.13.2 + version: 1.13.2(@types/react-dom@18.3.1)(@types/react@19.2.1) dotenv: - specifier: ^16.5.0 - version: 16.6.1 + specifier: ^17.2.3 + version: 17.2.3 electron: - specifier: ^37.2.0 - version: 37.2.0 + specifier: ^39.0.0 + version: 39.0.0 electron-builder: specifier: ^26.0.12 version: 26.0.12(electron-builder-squirrel-windows@25.1.8) esbuild: - specifier: ^0.25.5 - version: 0.25.5 + specifier: ^0.25.11 + version: 0.25.11 eslint: - specifier: ^9.30.1 - version: 9.30.1 + specifier: ^9.38.0 + version: 9.38.0 eslint-import-resolver-alias: specifier: ^1.1.2 - version: 1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)) + version: 1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0)) eslint-plugin-path-alias: specifier: ^2.1.0 - version: 2.1.0(eslint@9.30.1) + version: 2.1.0(eslint@9.38.0) eslint-plugin-prettier: - specifier: ^5.5.1 - version: 5.5.1(eslint@9.30.1)(prettier@3.6.2) + specifier: ^5.5.4 + version: 5.5.4(eslint@9.38.0)(prettier@3.6.2) eslint-plugin-simple-header: specifier: ^1.2.2 - version: 1.2.2(eslint@9.30.1) + version: 1.2.2(eslint@9.38.0) eslint-plugin-simple-import-sort: specifier: ^12.1.1 - version: 12.1.1(eslint@9.30.1) + version: 12.1.1(eslint@9.38.0) eslint-plugin-unused-imports: - specifier: ^4.1.4 - version: 4.1.4(@typescript-eslint/eslint-plugin@8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1) + specifier: ^4.3.0 + version: 4.3.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0) + libvesktop: + specifier: link:packages/libvesktop + version: link:packages/libvesktop prettier: specifier: ^3.6.2 version: 3.6.2 @@ -81,20 +84,20 @@ importers: specifier: ^0.5.21 version: 0.5.21 tsx: - specifier: ^4.20.3 - version: 4.20.3 + specifier: ^4.20.6 + version: 4.20.6 type-fest: - specifier: ^4.41.0 - version: 4.41.0 + specifier: ^5.1.0 + version: 5.1.0 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 typescript-eslint: - specifier: ^8.35.1 - version: 8.35.1(eslint@9.30.1)(typescript@5.8.3) + specifier: ^8.46.2 + version: 8.46.2(eslint@9.38.0)(typescript@5.9.3) xml-formatter: - specifier: ^3.6.6 - version: 3.6.6 + specifier: ^3.6.7 + version: 3.6.7 optionalDependencies: '@vencord/venmic': specifier: ^6.1.0 @@ -151,196 +154,198 @@ packages: resolution: {integrity: sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==} engines: {node: '>=16.4'} - '@esbuild/aix-ppc64@0.25.5': - resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.5': - resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.5': - resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.5': - resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.5': - resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.5': - resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.5': - resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.5': - resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.5': - resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.5': - resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.5': - resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.5': - resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.5': - resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.5': - resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.5': - resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.5': - resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.5': - resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.5': - resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.5': - resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.5': - resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.5': - resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.25.5': - resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.5': - resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.5': - resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.5': - resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.3.0': - resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.14.0': - resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + '@eslint/config-helpers@0.4.1': + resolution: {integrity: sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.15.1': - resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.30.1': - resolution: {integrity: sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==} + '@eslint/js@9.38.0': + resolution: {integrity: sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.3': - resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} + '@eslint/plugin-kit@0.4.0': + resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@fal-works/esbuild-plugin-global-externals@2.1.2': @@ -353,18 +358,14 @@ packages: resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} @@ -414,8 +415,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.2.7': - resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} '@rtsao/scc@1.1.0': @@ -425,8 +426,8 @@ packages: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} - '@stylistic/eslint-plugin@5.1.0': - resolution: {integrity: sha512-TJRJul4u/lmry5N/kyCU+7RWWOk0wyXN+BncRlDYBqpLFnzXkd7QGVfN7KewarFIXv0IX0jSF/Ksu7aHWEDeuw==} + '@stylistic/eslint-plugin@5.5.0': + resolution: {integrity: sha512-IeZF+8H0ns6prg4VrkhgL+yrvDXWDH2cKchrbh80ejG9dQgZWp10epHMbgRuQvgchLII/lfh6Xn3lu6+6L86Hw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=9.0.0' @@ -469,26 +470,20 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@22.16.0': - resolution: {integrity: sha512-B2egV9wALML1JCpv3VQoQ+yesQKAmNMBIAY7OteVrikcOcAkWm+dGL6qpeCktPjAv6N1JLnhbNiqS35UpFyBsQ==} + '@types/node@22.18.12': + resolution: {integrity: sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog==} - '@types/node@24.0.10': - resolution: {integrity: sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==} + '@types/node@24.9.1': + resolution: {integrity: sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==} '@types/plist@3.0.5': resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} - '@types/prop-types@15.7.14': - resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - '@types/react-dom@18.3.1': resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==} - '@types/react@17.0.2': - resolution: {integrity: sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA==} - - '@types/react@18.3.1': - resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} + '@types/react@19.2.1': + resolution: {integrity: sha512-1U5NQWh/GylZQ50ZMnnPjkYHEaGhg6t5i/KI0LDDh3t4E3h3T3vzm+GLY2BRzMfIjSBwzm6tginoZl5z0O/qsA==} '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} @@ -499,75 +494,83 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.35.1': - resolution: {integrity: sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==} + '@typescript-eslint/eslint-plugin@8.46.2': + resolution: {integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.35.1 + '@typescript-eslint/parser': ^8.46.2 eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.35.1': - resolution: {integrity: sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==} + '@typescript-eslint/parser@8.46.2': + resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.35.1': - resolution: {integrity: sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==} + '@typescript-eslint/project-service@8.46.2': + resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.35.1': - resolution: {integrity: sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==} + '@typescript-eslint/scope-manager@8.46.2': + resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.35.1': - resolution: {integrity: sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==} + '@typescript-eslint/tsconfig-utils@8.46.2': + resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.35.1': - resolution: {integrity: sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==} + '@typescript-eslint/type-utils@8.46.2': + resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.35.1': - resolution: {integrity: sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==} + '@typescript-eslint/types@8.46.2': + resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.35.1': - resolution: {integrity: sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==} + '@typescript-eslint/typescript-estree@8.46.2': + resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.35.1': - resolution: {integrity: sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==} + '@typescript-eslint/utils@8.46.2': + resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.35.1': - resolution: {integrity: sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==} + '@typescript-eslint/visitor-keys@8.46.2': + resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vencord/types@1.11.5': - resolution: {integrity: sha512-1cLt48MMAi2d53PhxdmKiZnTL0JdxSkZpmhpjoC+cHN6K2kKQiVntD6JExaFclucLlzYG0pOXVu63AaeQCVXmg==} + '@vencord/discord-types@1.0.0': + resolution: {integrity: sha512-idtijKdbjiqo8k+4mo5Aq9zrQYwr2u11VmiRasaf+8+cGC5bYsMTIZl7zKLF3VdYx2a0ICjxgZtcfa1U3mv1Yg==} + peerDependencies: + '@types/react': ^19.0.10 + + '@vencord/types@1.13.2': + resolution: {integrity: sha512-5RifSCC1X4xzoNaDTMANRLVZreEsHWWsGs6iBooqnBkLeigfP7FqAQfn6onogk/2+YQKNMTQT8N+MD+HGes5EA==} + peerDependencies: + '@types/react': 18.3.1 + '@types/react-dom': 18.3.1 '@vencord/venmic@6.1.0': resolution: {integrity: sha512-YiCtzml/W8tYbGhu3jm5jfbbEnl2slKKARNK0jO+8qV979k9eFnfIRTxvhMN/SWq1h8ZNJdXVwvXpffQwq0RuA==} engines: {node: '>=14.15'} os: [linux] - '@xmldom/xmldom@0.8.10': - resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + '@xmldom/xmldom@0.8.11': + resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} engines: {node: '>=10.0.0'} abbrev@1.1.1: @@ -587,8 +590,8 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - agent-base@7.1.3: - resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} agentkeepalive@4.6.0: @@ -611,16 +614,16 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} app-builder-bin@5.0.0-alpha.10: @@ -997,8 +1000,8 @@ packages: supports-color: optional: true - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1055,12 +1058,8 @@ packages: delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} - engines: {node: '>=8'} - - detect-libc@2.0.4: - resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} detect-node@2.1.0: @@ -1069,9 +1068,6 @@ packages: dir-compare@4.2.0: resolution: {integrity: sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==} - discord-types@1.3.26: - resolution: {integrity: sha512-ToG51AOCH+JTQf7b+8vuYQe5Iqwz7nZ7StpECAZ/VZcI1ZhQk13pvt9KkRTfRv1xNvwJ2qib4e3+RifQlo8VPQ==} - dmg-builder@26.0.12: resolution: {integrity: sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==} @@ -1093,6 +1089,10 @@ packages: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} + engines: {node: '>=12'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -1122,8 +1122,8 @@ packages: electron-updater@6.6.2: resolution: {integrity: sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw==} - electron@37.2.0: - resolution: {integrity: sha512-dE6+qeg6SBUVd5d8CD2+GH82kh+gF1v40+hs+U+UOno681NMSGmBtgqwldQRpbvtnQDD7V2M9Cpfr3+Abw7aBg==} + electron@39.0.0: + resolution: {integrity: sha512-UejnuOK4jpRZUq7MkEAnR/szsRWLKBJAdvn6j3xdQLT57fVv13VSNdaUHHjSheaqGzNhCGIdkPsPJnGJVh5kiA==} engines: {node: '>= 12.20.55'} hasBin: true @@ -1177,8 +1177,8 @@ packages: es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - esbuild@0.25.5: - resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} engines: {node: '>=18'} hasBin: true @@ -1235,8 +1235,8 @@ packages: peerDependencies: eslint: ^8.0.0 - eslint-plugin-prettier@5.5.1: - resolution: {integrity: sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==} + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -1259,8 +1259,8 @@ packages: peerDependencies: eslint: '>=5.0.0' - eslint-plugin-unused-imports@4.1.4: - resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==} + eslint-plugin-unused-imports@4.3.0: + resolution: {integrity: sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==} peerDependencies: '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 eslint: ^9.0.0 || ^8.0.0 @@ -1280,8 +1280,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.30.1: - resolution: {integrity: sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==} + eslint@9.38.0: + resolution: {integrity: sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1314,8 +1314,8 @@ packages: resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} engines: {node: '>=0.10.0'} - exponential-backoff@3.1.2: - resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} @@ -1411,8 +1411,8 @@ packages: resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} engines: {node: '>= 6'} - form-data@4.0.3: - resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} fragment-cache@0.2.1: @@ -1430,6 +1430,10 @@ packages: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + engines: {node: '>=14.14'} + fs-extra@8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} @@ -1465,6 +1469,10 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This package is no longer supported. + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1596,6 +1604,10 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + homedir-polyfill@1.0.3: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} engines: {node: '>=0.10.0'} @@ -1679,8 +1691,8 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} engines: {node: '>= 12'} is-accessor-descriptor@1.0.1: @@ -1758,8 +1770,8 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} is-glob@4.0.3: @@ -1851,8 +1863,8 @@ packages: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} engines: {node: '>= 8.0.0'} - isbinaryfile@5.0.4: - resolution: {integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==} + isbinaryfile@5.0.6: + resolution: {integrity: sha512-I+NmIfBHUl+r2wcDd6JwE9yWje/PIVY/R5/CmV8dXLZd5K+L9X2klAOwfAHNnondLXkbHyTAleQAWonpTJBTtw==} engines: {node: '>= 18.0.0'} isexe@2.0.0: @@ -1869,8 +1881,8 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jake@10.9.2: - resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} engines: {node: '>=10'} hasBin: true @@ -1878,9 +1890,6 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1905,8 +1914,8 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -2041,10 +2050,6 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - minimatch@10.0.1: - resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} - engines: {node: 20 || >=22} - minimatch@10.0.3: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} @@ -2128,12 +2133,8 @@ packages: resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} engines: {node: '>= 0.6'} - node-abi@3.74.0: - resolution: {integrity: sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==} - engines: {node: '>=10'} - - node-abi@3.75.0: - resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} + node-abi@3.78.0: + resolution: {integrity: sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==} engines: {node: '>=10'} node-addon-api@1.7.2: @@ -2284,12 +2285,15 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} pkg-prebuilds@0.2.1: @@ -2414,8 +2418,8 @@ packages: resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} deprecated: https://github.com/lydell/resolve-url#deprecated - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true @@ -2501,6 +2505,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} @@ -2575,8 +2584,8 @@ packages: resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} engines: {node: '>= 10'} - socks@2.8.5: - resolution: {integrity: sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==} + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} source-map-resolve@0.5.3: @@ -2609,9 +2618,6 @@ packages: resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - standalone-electron-types@34.2.0: - resolution: {integrity: sha512-+BIrNe0TdZBBBRS3G/F7cPbuBipSZylSjSrJu9+sjw84+vz36a5oZ7l9NeH7aXgMWmPzPZKsTm+K0nOIvhENJQ==} - stat-mode@1.0.0: resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} engines: {node: '>= 6'} @@ -2654,8 +2660,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-bom@3.0.0: @@ -2682,10 +2688,14 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - synckit@0.11.8: - resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==} + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} engines: {node: ^14.18.0 || >=16.0.0} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -2706,8 +2716,8 @@ packages: tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} - tmp@0.2.3: - resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} to-object-path@0.3.0: @@ -2731,11 +2741,14 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-pattern@5.8.0: + resolution: {integrity: sha512-kIjN2qmWiHnhgr5DAkAafF9fwb0T5OhMVSWrm8XEdTFnX6+wfXwYOFjeF86UZ54vduqiR7BfqScFmXSzSaH8oA==} + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tsx@4.20.3: - resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + tsx@4.20.6: + resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} engines: {node: '>=18.0.0'} hasBin: true @@ -2751,6 +2764,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.1.0: + resolution: {integrity: sha512-wQ531tuWvB6oK+pchHIu5lHe5f5wpSCqB8Kf4dWQRbOYc9HTge7JL0G4Qd44bh6QuJCccIzL3bugb8GI0MwHrg==} + engines: {node: '>=20'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -2767,15 +2784,15 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.35.1: - resolution: {integrity: sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==} + typescript-eslint@8.46.2: + resolution: {integrity: sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true @@ -2786,8 +2803,8 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.8.0: - resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} union-value@1.0.1: resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} @@ -2900,12 +2917,12 @@ packages: utf-8-validate: optional: true - xml-formatter@3.6.6: - resolution: {integrity: sha512-yfofQht42x2sN1YThT6Er6GFXiQinfDAsMTNvMPi2uZw5/Vtc2PYHfvALR8U+b2oN2ekBxLd2tGWV06rAM8nQA==} + xml-formatter@3.6.7: + resolution: {integrity: sha512-IsfFYJQuoDqtUlKhm4EzeoBOb+fQwzQVeyxxAQ0sThn/nFnQmyLPTplqq4yRhaOENH/tAyujD2TBfIYzUKB6hg==} engines: {node: '>= 16'} - xml-parser-xo@4.1.4: - resolution: {integrity: sha512-wo+yWDNeMwd1ctzH4CsiGXaAappDsxuR+VnmPewOzHk/zvefksT2ZlcWpAePl11THOWgnIZM4GjvumevurNWZw==} + xml-parser-xo@4.1.5: + resolution: {integrity: sha512-TxyRxk9sTOUg3glxSIY6f0nfuqRll2OEF8TspLgh5mZkLuBgheCn3zClcDSGJ58TvNmiwyCCuat4UajPud/5Og==} engines: {node: '>= 16'} xmlbuilder@15.1.1: @@ -2961,7 +2978,7 @@ snapshots: '@electron/get@2.0.3': dependencies: - debug: 4.4.1 + debug: 4.4.3 env-paths: 2.2.1 fs-extra: 8.1.0 got: 11.8.6 @@ -2976,7 +2993,7 @@ snapshots: '@electron/node-gyp@https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2': dependencies: env-paths: 2.2.1 - exponential-backoff: 3.1.2 + exponential-backoff: 3.1.3 glob: 8.1.0 graceful-fs: 4.2.11 make-fetch-happen: 10.2.1 @@ -2991,7 +3008,7 @@ snapshots: '@electron/notarize@2.5.0': dependencies: - debug: 4.4.0 + debug: 4.4.3 fs-extra: 9.1.0 promise-retry: 2.0.1 transitivePeerDependencies: @@ -3000,7 +3017,7 @@ snapshots: '@electron/osx-sign@1.3.1': dependencies: compare-version: 0.1.2 - debug: 4.4.0 + debug: 4.4.3 fs-extra: 10.1.0 isbinaryfile: 4.0.10 minimist: 1.2.8 @@ -3012,16 +3029,16 @@ snapshots: dependencies: '@malept/cross-spawn-promise': 2.0.0 chalk: 4.1.2 - debug: 4.4.1 - detect-libc: 2.0.4 + debug: 4.4.3 + detect-libc: 2.1.2 fs-extra: 10.1.0 got: 11.8.6 - node-abi: 3.75.0 + node-abi: 3.78.0 node-api-version: 0.2.1 node-gyp: 9.4.1 ora: 5.4.1 read-binary-file-arch: 1.0.6 - semver: 7.7.2 + semver: 7.7.3 tar: 6.2.1 yargs: 17.7.2 transitivePeerDependencies: @@ -3033,15 +3050,15 @@ snapshots: '@electron/node-gyp': https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2 '@malept/cross-spawn-promise': 2.0.0 chalk: 4.1.2 - debug: 4.4.0 - detect-libc: 2.0.3 + debug: 4.4.3 + detect-libc: 2.1.2 fs-extra: 10.1.0 got: 11.8.6 - node-abi: 3.74.0 + node-abi: 3.78.0 node-api-version: 0.2.1 ora: 5.4.1 read-binary-file-arch: 1.0.6 - semver: 7.7.1 + semver: 7.7.2 tar: 6.2.1 yargs: 17.7.2 transitivePeerDependencies: @@ -3052,118 +3069,119 @@ snapshots: dependencies: '@electron/asar': 3.2.18 '@malept/cross-spawn-promise': 2.0.0 - debug: 4.4.0 + debug: 4.4.3 dir-compare: 4.2.0 - fs-extra: 11.3.0 + fs-extra: 11.3.2 minimatch: 9.0.5 plist: 3.1.0 transitivePeerDependencies: - supports-color - '@esbuild/aix-ppc64@0.25.5': + '@esbuild/aix-ppc64@0.25.11': + optional: true + + '@esbuild/android-arm64@0.25.11': optional: true - '@esbuild/android-arm64@0.25.5': + '@esbuild/android-arm@0.25.11': optional: true - '@esbuild/android-arm@0.25.5': + '@esbuild/android-x64@0.25.11': optional: true - '@esbuild/android-x64@0.25.5': + '@esbuild/darwin-arm64@0.25.11': optional: true - '@esbuild/darwin-arm64@0.25.5': + '@esbuild/darwin-x64@0.25.11': optional: true - '@esbuild/darwin-x64@0.25.5': + '@esbuild/freebsd-arm64@0.25.11': optional: true - '@esbuild/freebsd-arm64@0.25.5': + '@esbuild/freebsd-x64@0.25.11': optional: true - '@esbuild/freebsd-x64@0.25.5': + '@esbuild/linux-arm64@0.25.11': optional: true - '@esbuild/linux-arm64@0.25.5': + '@esbuild/linux-arm@0.25.11': optional: true - '@esbuild/linux-arm@0.25.5': + '@esbuild/linux-ia32@0.25.11': optional: true - '@esbuild/linux-ia32@0.25.5': + '@esbuild/linux-loong64@0.25.11': optional: true - '@esbuild/linux-loong64@0.25.5': + '@esbuild/linux-mips64el@0.25.11': optional: true - '@esbuild/linux-mips64el@0.25.5': + '@esbuild/linux-ppc64@0.25.11': optional: true - '@esbuild/linux-ppc64@0.25.5': + '@esbuild/linux-riscv64@0.25.11': optional: true - '@esbuild/linux-riscv64@0.25.5': + '@esbuild/linux-s390x@0.25.11': optional: true - '@esbuild/linux-s390x@0.25.5': + '@esbuild/linux-x64@0.25.11': optional: true - '@esbuild/linux-x64@0.25.5': + '@esbuild/netbsd-arm64@0.25.11': optional: true - '@esbuild/netbsd-arm64@0.25.5': + '@esbuild/netbsd-x64@0.25.11': optional: true - '@esbuild/netbsd-x64@0.25.5': + '@esbuild/openbsd-arm64@0.25.11': optional: true - '@esbuild/openbsd-arm64@0.25.5': + '@esbuild/openbsd-x64@0.25.11': optional: true - '@esbuild/openbsd-x64@0.25.5': + '@esbuild/openharmony-arm64@0.25.11': optional: true - '@esbuild/sunos-x64@0.25.5': + '@esbuild/sunos-x64@0.25.11': optional: true - '@esbuild/win32-arm64@0.25.5': + '@esbuild/win32-arm64@0.25.11': optional: true - '@esbuild/win32-ia32@0.25.5': + '@esbuild/win32-ia32@0.25.11': optional: true - '@esbuild/win32-x64@0.25.5': + '@esbuild/win32-x64@0.25.11': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.30.1)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.38.0)': dependencies: - eslint: 9.30.1 + eslint: 9.38.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.0': + '@eslint/config-array@0.21.1': dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.1 + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.0': {} - - '@eslint/core@0.14.0': + '@eslint/config-helpers@0.4.1': dependencies: - '@types/json-schema': 7.0.15 + '@eslint/core': 0.16.0 - '@eslint/core@0.15.1': + '@eslint/core@0.16.0': dependencies: '@types/json-schema': 7.0.15 '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.1 + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -3174,13 +3192,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.30.1': {} + '@eslint/js@9.38.0': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.3.3': + '@eslint/plugin-kit@0.4.0': dependencies: - '@eslint/core': 0.15.1 + '@eslint/core': 0.16.0 levn: 0.4.1 '@fal-works/esbuild-plugin-global-externals@2.1.2': {} @@ -3189,15 +3207,13 @@ snapshots: '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.6': + '@humanfs/node@0.16.7': dependencies: '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.3': {} '@isaacs/balanced-match@4.0.1': {} @@ -3210,7 +3226,7 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -3221,7 +3237,7 @@ snapshots: '@malept/flatpak-bundler@0.4.0': dependencies: - debug: 4.4.0 + debug: 4.4.3 fs-extra: 9.1.0 lodash: 4.17.21 tmp-promise: 3.0.3 @@ -3253,21 +3269,21 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.2.7': {} + '@pkgr/core@0.2.9': {} '@rtsao/scc@1.1.0': {} '@sindresorhus/is@4.6.0': {} - '@stylistic/eslint-plugin@5.1.0(eslint@9.30.1)': + '@stylistic/eslint-plugin@5.5.0(eslint@9.38.0)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1) - '@typescript-eslint/types': 8.35.1 - eslint: 9.30.1 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0) + '@typescript-eslint/types': 8.46.2 + eslint: 9.38.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 - picomatch: 4.0.2 + picomatch: 4.0.3 '@szmarczak/http-timer@4.0.6': dependencies: @@ -3279,7 +3295,7 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 24.0.10 + '@types/node': 24.9.1 '@types/responselike': 1.0.3 '@types/debug@4.1.12': @@ -3290,7 +3306,7 @@ snapshots: '@types/fs-extra@9.0.13': dependencies: - '@types/node': 24.0.10 + '@types/node': 24.9.1 '@types/http-cache-semantics@4.0.4': {} @@ -3300,154 +3316,155 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 24.0.10 + '@types/node': 24.9.1 '@types/lodash@4.17.15': {} '@types/ms@2.1.0': {} - '@types/node@22.16.0': + '@types/node@22.18.12': dependencies: undici-types: 6.21.0 - '@types/node@24.0.10': + '@types/node@24.9.1': dependencies: - undici-types: 7.8.0 + undici-types: 7.16.0 '@types/plist@3.0.5': dependencies: - '@types/node': 24.0.10 + '@types/node': 24.9.1 xmlbuilder: 15.1.1 optional: true - '@types/prop-types@15.7.14': {} - '@types/react-dom@18.3.1': dependencies: - '@types/react': 18.3.1 - - '@types/react@17.0.2': - dependencies: - '@types/prop-types': 15.7.14 - csstype: 3.1.3 + '@types/react': 19.2.1 - '@types/react@18.3.1': + '@types/react@19.2.1': dependencies: - '@types/prop-types': 15.7.14 csstype: 3.1.3 '@types/responselike@1.0.3': dependencies: - '@types/node': 24.0.10 + '@types/node': 24.9.1 '@types/verror@1.10.11': optional: true '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.0.10 + '@types/node': 24.9.1 optional: true - '@typescript-eslint/eslint-plugin@8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0)(typescript@5.9.3)': dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.35.1(eslint@9.30.1)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.35.1 - '@typescript-eslint/type-utils': 8.35.1(eslint@9.30.1)(typescript@5.8.3) - '@typescript-eslint/utils': 8.35.1(eslint@9.30.1)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.35.1 - eslint: 9.30.1 + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.46.2(eslint@9.38.0)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/type-utils': 8.46.2(eslint@9.38.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.38.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.2 + eslint: 9.38.0 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3)': + '@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.35.1 - '@typescript-eslint/types': 8.35.1 - '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.35.1 - debug: 4.4.1 - eslint: 9.30.1 - typescript: 5.8.3 + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.2 + debug: 4.4.3 + eslint: 9.38.0 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.35.1(typescript@5.8.3)': + '@typescript-eslint/project-service@8.46.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) - '@typescript-eslint/types': 8.35.1 - debug: 4.4.1 - typescript: 5.8.3 + '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) + '@typescript-eslint/types': 8.46.2 + debug: 4.4.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.35.1': + '@typescript-eslint/scope-manager@8.46.2': dependencies: - '@typescript-eslint/types': 8.35.1 - '@typescript-eslint/visitor-keys': 8.35.1 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/visitor-keys': 8.46.2 - '@typescript-eslint/tsconfig-utils@8.35.1(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)': dependencies: - typescript: 5.8.3 + typescript: 5.9.3 - '@typescript-eslint/type-utils@8.35.1(eslint@9.30.1)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.46.2(eslint@9.38.0)(typescript@5.9.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.35.1(eslint@9.30.1)(typescript@5.8.3) - debug: 4.4.1 - eslint: 9.30.1 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.38.0)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.38.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.35.1': {} + '@typescript-eslint/types@8.46.2': {} - '@typescript-eslint/typescript-estree@8.35.1(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.35.1(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) - '@typescript-eslint/types': 8.35.1 - '@typescript-eslint/visitor-keys': 8.35.1 - debug: 4.4.1 + '@typescript-eslint/project-service': 8.46.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/visitor-keys': 8.46.2 + debug: 4.4.3 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + semver: 7.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.35.1(eslint@9.30.1)(typescript@5.8.3)': + '@typescript-eslint/utils@8.46.2(eslint@9.38.0)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1) - '@typescript-eslint/scope-manager': 8.35.1 - '@typescript-eslint/types': 8.35.1 - '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) - eslint: 9.30.1 - typescript: 5.8.3 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0) + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + eslint: 9.38.0 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.35.1': + '@typescript-eslint/visitor-keys@8.46.2': dependencies: - '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/types': 8.46.2 eslint-visitor-keys: 4.2.1 - '@vencord/types@1.11.5': + '@vencord/discord-types@1.0.0(@types/react@19.2.1)': + dependencies: + '@types/react': 19.2.1 + moment: 2.30.1 + type-fest: 4.41.0 + + '@vencord/types@1.13.2(@types/react-dom@18.3.1)(@types/react@19.2.1)': dependencies: '@types/lodash': 4.17.15 - '@types/node': 22.16.0 - '@types/react': 18.3.1 + '@types/node': 22.18.12 + '@types/react': 19.2.1 '@types/react-dom': 18.3.1 - discord-types: 1.3.26 - standalone-electron-types: 34.2.0 + '@vencord/discord-types': 1.0.0(@types/react@19.2.1) + highlight.js: 11.11.1 + moment: 2.30.1 + ts-pattern: 5.8.0 type-fest: 4.41.0 '@vencord/venmic@6.1.0': @@ -3459,7 +3476,7 @@ snapshots: - supports-color optional: true - '@xmldom/xmldom@0.8.10': {} + '@xmldom/xmldom@0.8.11': {} abbrev@1.1.1: {} @@ -3471,11 +3488,11 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color - agent-base@7.1.3: {} + agent-base@7.1.4: {} agentkeepalive@4.6.0: dependencies: @@ -3499,13 +3516,13 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} app-builder-bin@5.0.0-alpha.10: {} @@ -3526,25 +3543,25 @@ snapshots: builder-util-runtime: 9.2.10 chromium-pickle-js: 0.2.0 config-file-ts: 0.2.8-rc1 - debug: 4.4.1 + debug: 4.4.3 dmg-builder: 26.0.12(electron-builder-squirrel-windows@25.1.8) dotenv: 16.6.1 dotenv-expand: 11.0.7 ejs: 3.1.10 electron-builder-squirrel-windows: 25.1.8(dmg-builder@26.0.12) electron-publish: 25.1.7 - form-data: 4.0.3 + form-data: 4.0.4 fs-extra: 10.1.0 hosted-git-info: 4.1.0 is-ci: 3.0.1 - isbinaryfile: 5.0.4 + isbinaryfile: 5.0.6 js-yaml: 4.1.0 json5: 2.2.3 lazy-val: 1.0.5 minimatch: 10.0.3 resedit: 1.7.2 sanitize-filename: 1.6.3 - semver: 7.7.2 + semver: 7.7.3 tar: 6.2.1 temp-file: 3.4.0 transitivePeerDependencies: @@ -3567,7 +3584,7 @@ snapshots: builder-util-runtime: 9.3.1 chromium-pickle-js: 0.2.0 config-file-ts: 0.2.8-rc1 - debug: 4.4.0 + debug: 4.4.3 dmg-builder: 26.0.12(electron-builder-squirrel-windows@25.1.8) dotenv: 16.6.1 dotenv-expand: 11.0.7 @@ -3577,14 +3594,14 @@ snapshots: fs-extra: 10.1.0 hosted-git-info: 4.1.0 is-ci: 3.0.1 - isbinaryfile: 5.0.4 + isbinaryfile: 5.0.6 js-yaml: 4.1.0 json5: 2.2.3 lazy-val: 1.0.5 - minimatch: 10.0.1 + minimatch: 10.0.3 plist: 3.1.0 resedit: 1.7.2 - semver: 7.7.1 + semver: 7.7.2 tar: 6.2.1 temp-file: 3.4.0 tiny-async-pool: 1.3.0 @@ -3786,14 +3803,14 @@ snapshots: builder-util-runtime@9.2.10: dependencies: - debug: 4.4.1 + debug: 4.4.3 sax: 1.4.1 transitivePeerDependencies: - supports-color builder-util-runtime@9.3.1: dependencies: - debug: 4.4.0 + debug: 4.4.3 sax: 1.4.1 transitivePeerDependencies: - supports-color @@ -3807,7 +3824,7 @@ snapshots: builder-util-runtime: 9.2.10 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.3 fs-extra: 10.1.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -3827,7 +3844,7 @@ snapshots: builder-util-runtime: 9.3.1 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.0 + debug: 4.4.3 fs-extra: 10.1.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -4005,7 +4022,7 @@ snapshots: config-file-ts@0.2.8-rc1: dependencies: glob: 10.4.5 - typescript: 5.8.3 + typescript: 5.9.3 console-control-strings@1.1.0: {} @@ -4065,8 +4082,9 @@ snapshots: debug@4.4.0: dependencies: ms: 2.1.3 + optional: true - debug@4.4.1: + debug@4.4.3: dependencies: ms: 2.1.3 @@ -4116,9 +4134,7 @@ snapshots: delegates@1.0.0: {} - detect-libc@2.0.3: {} - - detect-libc@2.0.4: {} + detect-libc@2.1.2: {} detect-node@2.1.0: optional: true @@ -4128,11 +4144,6 @@ snapshots: minimatch: 3.1.2 p-limit: 3.1.0 - discord-types@1.3.26: - dependencies: - '@types/react': 17.0.2 - moment: 2.30.1 - dmg-builder@26.0.12(electron-builder-squirrel-windows@25.1.8): dependencies: app-builder-lib: 26.0.12(dmg-builder@26.0.12)(electron-builder-squirrel-windows@25.1.8) @@ -4170,6 +4181,8 @@ snapshots: dotenv@16.6.1: {} + dotenv@17.2.3: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4180,7 +4193,7 @@ snapshots: ejs@3.1.10: dependencies: - jake: 10.9.2 + jake: 10.9.4 electron-builder-squirrel-windows@25.1.8(dmg-builder@26.0.12): dependencies: @@ -4228,7 +4241,7 @@ snapshots: builder-util: 26.0.11 builder-util-runtime: 9.3.1 chalk: 4.1.2 - form-data: 4.0.2 + form-data: 4.0.4 fs-extra: 10.1.0 lazy-val: 1.0.5 mime: 2.6.0 @@ -4248,10 +4261,10 @@ snapshots: transitivePeerDependencies: - supports-color - electron@37.2.0: + electron@39.0.0: dependencies: '@electron/get': 2.0.3 - '@types/node': 22.16.0 + '@types/node': 22.18.12 extract-zip: 2.0.1 transitivePeerDependencies: - supports-color @@ -4358,61 +4371,62 @@ snapshots: es6-error@4.1.1: optional: true - esbuild@0.25.5: + esbuild@0.25.11: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.5 - '@esbuild/android-arm': 0.25.5 - '@esbuild/android-arm64': 0.25.5 - '@esbuild/android-x64': 0.25.5 - '@esbuild/darwin-arm64': 0.25.5 - '@esbuild/darwin-x64': 0.25.5 - '@esbuild/freebsd-arm64': 0.25.5 - '@esbuild/freebsd-x64': 0.25.5 - '@esbuild/linux-arm': 0.25.5 - '@esbuild/linux-arm64': 0.25.5 - '@esbuild/linux-ia32': 0.25.5 - '@esbuild/linux-loong64': 0.25.5 - '@esbuild/linux-mips64el': 0.25.5 - '@esbuild/linux-ppc64': 0.25.5 - '@esbuild/linux-riscv64': 0.25.5 - '@esbuild/linux-s390x': 0.25.5 - '@esbuild/linux-x64': 0.25.5 - '@esbuild/netbsd-arm64': 0.25.5 - '@esbuild/netbsd-x64': 0.25.5 - '@esbuild/openbsd-arm64': 0.25.5 - '@esbuild/openbsd-x64': 0.25.5 - '@esbuild/sunos-x64': 0.25.5 - '@esbuild/win32-arm64': 0.25.5 - '@esbuild/win32-ia32': 0.25.5 - '@esbuild/win32-x64': 0.25.5 + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)): + eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0)): dependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0) eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 is-core-module: 2.16.1 - resolve: 1.22.10 + resolve: 1.22.11 transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.30.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.38.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.35.1(eslint@9.30.1)(typescript@5.8.3) - eslint: 9.30.1 + '@typescript-eslint/parser': 8.46.2(eslint@9.38.0)(typescript@5.9.3) + eslint: 9.38.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -4421,9 +4435,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.30.1 + eslint: 9.38.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.30.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.38.0) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -4435,41 +4449,41 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.35.1(eslint@9.30.1)(typescript@5.8.3) + '@typescript-eslint/parser': 8.46.2(eslint@9.38.0)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-path-alias@2.1.0(eslint@9.30.1): + eslint-plugin-path-alias@2.1.0(eslint@9.38.0): dependencies: - eslint: 9.30.1 + eslint: 9.38.0 find-pkg: 2.0.0 get-tsconfig: 4.10.0 nanomatch: 1.2.13 transitivePeerDependencies: - supports-color - eslint-plugin-prettier@5.5.1(eslint@9.30.1)(prettier@3.6.2): + eslint-plugin-prettier@5.5.4(eslint@9.38.0)(prettier@3.6.2): dependencies: - eslint: 9.30.1 + eslint: 9.38.0 prettier: 3.6.2 prettier-linter-helpers: 1.0.0 - synckit: 0.11.8 + synckit: 0.11.11 - eslint-plugin-simple-header@1.2.2(eslint@9.30.1): + eslint-plugin-simple-header@1.2.2(eslint@9.38.0): dependencies: - eslint: 9.30.1 + eslint: 9.38.0 - eslint-plugin-simple-import-sort@12.1.1(eslint@9.30.1): + eslint-plugin-simple-import-sort@12.1.1(eslint@9.38.0): dependencies: - eslint: 9.30.1 + eslint: 9.38.0 - eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1): + eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0): dependencies: - eslint: 9.30.1 + eslint: 9.38.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0)(typescript@5.9.3) eslint-scope@8.4.0: dependencies: @@ -4480,25 +4494,24 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.30.1: + eslint@9.38.0: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.0 - '@eslint/core': 0.14.0 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.1 + '@eslint/core': 0.16.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.30.1 - '@eslint/plugin-kit': 0.3.3 - '@humanfs/node': 0.16.6 + '@eslint/js': 9.38.0 + '@eslint/plugin-kit': 0.4.0 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -4542,7 +4555,7 @@ snapshots: dependencies: homedir-polyfill: 1.0.3 - exponential-backoff@3.1.2: {} + exponential-backoff@3.1.3: {} extend-shallow@2.0.1: dependencies: @@ -4555,7 +4568,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.1 + debug: 4.4.3 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -4644,8 +4657,9 @@ snapshots: combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 mime-types: 2.1.35 + optional: true - form-data@4.0.3: + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -4662,13 +4676,20 @@ snapshots: fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.1.0 + jsonfile: 6.2.0 universalify: 2.0.1 fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.1.0 + jsonfile: 6.2.0 + universalify: 2.0.1 + optional: true + + fs-extra@11.3.2: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 universalify: 2.0.1 fs-extra@8.1.0: @@ -4681,7 +4702,7 @@ snapshots: dependencies: at-least-node: 1.0.0 graceful-fs: 4.2.11 - jsonfile: 6.1.0 + jsonfile: 6.2.0 universalify: 2.0.1 fs-minipass@2.1.0: @@ -4717,6 +4738,8 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 + generator-function@2.0.1: {} + get-caller-file@2.0.5: {} get-intrinsic@1.3.0: @@ -4797,7 +4820,7 @@ snapshots: es6-error: 4.1.1 matcher: 3.0.0 roarr: 2.15.4 - semver: 7.7.2 + semver: 7.7.3 serialize-error: 7.0.1 optional: true @@ -4885,6 +4908,8 @@ snapshots: dependencies: function-bind: 1.1.2 + highlight.js@11.11.1: {} + homedir-polyfill@1.0.3: dependencies: parse-passwd: 1.0.0 @@ -4899,14 +4924,14 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.3 - debug: 4.4.0 + agent-base: 7.1.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -4918,14 +4943,14 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.3 - debug: 4.4.0 + agent-base: 7.1.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -4975,10 +5000,7 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - ip-address@9.0.5: - dependencies: - jsbn: 1.1.0 - sprintf-js: 1.1.3 + ip-address@10.0.1: {} is-accessor-descriptor@1.0.1: dependencies: @@ -5058,9 +5080,10 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.0: + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 + generator-function: 2.0.1 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -5141,7 +5164,7 @@ snapshots: isbinaryfile@4.0.10: {} - isbinaryfile@5.0.4: {} + isbinaryfile@5.0.6: {} isexe@2.0.0: {} @@ -5157,19 +5180,16 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jake@10.9.2: + jake@10.9.4: dependencies: async: 3.2.6 - chalk: 4.1.2 filelist: 1.0.4 - minimatch: 3.1.2 + picocolors: 1.1.1 js-yaml@4.1.0: dependencies: argparse: 2.0.1 - jsbn@1.1.0: {} - json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -5189,7 +5209,7 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - jsonfile@6.1.0: + jsonfile@6.2.0: dependencies: universalify: 2.0.1 optionalDependencies: @@ -5318,10 +5338,6 @@ snapshots: mimic-response@3.1.0: {} - minimatch@10.0.1: - dependencies: - brace-expansion: 2.0.2 - minimatch@10.0.3: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -5410,11 +5426,7 @@ snapshots: negotiator@0.6.4: {} - node-abi@3.74.0: - dependencies: - semver: 7.7.1 - - node-abi@3.75.0: + node-abi@3.78.0: dependencies: semver: 7.7.2 @@ -5429,19 +5441,19 @@ snapshots: node-api-version@0.2.1: dependencies: - semver: 7.7.1 + semver: 7.7.2 node-gyp@9.4.1: dependencies: env-paths: 2.2.1 - exponential-backoff: 3.1.2 + exponential-backoff: 3.1.3 glob: 7.2.3 graceful-fs: 4.2.11 make-fetch-happen: 10.2.1 nopt: 6.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.7.2 + semver: 7.7.3 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: @@ -5586,9 +5598,11 @@ snapshots: pend@1.2.0: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} - picomatch@4.0.2: {} + picomatch@4.0.3: {} pkg-prebuilds@0.2.1: dependencies: @@ -5597,7 +5611,7 @@ snapshots: plist@3.1.0: dependencies: - '@xmldom/xmldom': 0.8.10 + '@xmldom/xmldom': 0.8.11 base64-js: 1.5.1 xmlbuilder: 15.1.1 @@ -5648,7 +5662,7 @@ snapshots: read-binary-file-arch@1.0.6: dependencies: - debug: 4.4.0 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -5716,7 +5730,7 @@ snapshots: resolve-url@0.2.1: {} - resolve@1.22.10: + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 @@ -5801,6 +5815,8 @@ snapshots: semver@7.7.2: {} + semver@7.7.3: {} + serialize-error@7.0.1: dependencies: type-fest: 0.13.1 @@ -5877,7 +5893,7 @@ snapshots: simple-update-notifier@2.0.0: dependencies: - semver: 7.7.1 + semver: 7.7.2 slice-ansi@3.0.0: dependencies: @@ -5904,14 +5920,14 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.4.1 - socks: 2.8.5 + debug: 4.4.3 + socks: 2.8.7 transitivePeerDependencies: - supports-color - socks@2.8.5: + socks@2.8.7: dependencies: - ip-address: 9.0.5 + ip-address: 10.0.1 smart-buffer: 4.2.0 source-map-resolve@0.5.3: @@ -5937,16 +5953,13 @@ snapshots: dependencies: extend-shallow: 3.0.2 - sprintf-js@1.1.3: {} + sprintf-js@1.1.3: + optional: true ssri@9.0.1: dependencies: minipass: 3.3.6 - standalone-electron-types@34.2.0: - dependencies: - '@types/node': 22.16.0 - stat-mode@1.0.0: {} static-extend@0.1.2: @@ -5969,7 +5982,7 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 string.prototype.trim@1.2.10: dependencies: @@ -6006,9 +6019,9 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 strip-bom@3.0.0: {} @@ -6019,7 +6032,7 @@ snapshots: sumchecker@3.0.1: dependencies: - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -6029,9 +6042,11 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - synckit@0.11.8: + synckit@0.11.11: dependencies: - '@pkgr/core': 0.2.7 + '@pkgr/core': 0.2.9 + + tagged-tag@1.0.0: {} tar-stream@2.2.0: dependencies: @@ -6063,9 +6078,9 @@ snapshots: tmp-promise@3.0.3: dependencies: - tmp: 0.2.3 + tmp: 0.2.5 - tmp@0.2.3: {} + tmp@0.2.5: {} to-object-path@0.3.0: dependencies: @@ -6086,9 +6101,11 @@ snapshots: dependencies: utf8-byte-length: 1.0.5 - ts-api-utils@2.1.0(typescript@5.8.3): + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: - typescript: 5.8.3 + typescript: 5.9.3 + + ts-pattern@5.8.0: {} tsconfig-paths@3.15.0: dependencies: @@ -6097,9 +6114,9 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tsx@4.20.3: + tsx@4.20.6: dependencies: - esbuild: 0.25.5 + esbuild: 0.25.11 get-tsconfig: 4.10.1 optionalDependencies: fsevents: 2.3.3 @@ -6113,6 +6130,10 @@ snapshots: type-fest@4.41.0: {} + type-fest@5.1.0: + dependencies: + tagged-tag: 1.0.0 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -6146,17 +6167,18 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.35.1(eslint@9.30.1)(typescript@5.8.3): + typescript-eslint@8.46.2(eslint@9.38.0)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3) - '@typescript-eslint/parser': 8.35.1(eslint@9.30.1)(typescript@5.8.3) - '@typescript-eslint/utils': 8.35.1(eslint@9.30.1)(typescript@5.8.3) - eslint: 9.30.1 - typescript: 5.8.3 + '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0)(typescript@5.9.3))(eslint@9.38.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.2(eslint@9.38.0)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.38.0)(typescript@5.9.3) + eslint: 9.38.0 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - typescript@5.8.3: {} + typescript@5.9.3: {} unbox-primitive@1.1.0: dependencies: @@ -6167,7 +6189,7 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.8.0: {} + undici-types@7.16.0: {} union-value@1.0.1: dependencies: @@ -6237,7 +6259,7 @@ snapshots: is-async-function: 2.1.1 is-date-object: 1.1.0 is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.0 + is-generator-function: 1.1.2 is-regex: 1.2.1 is-weakref: 1.1.1 isarray: 2.0.5 @@ -6284,19 +6306,19 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 wrappy@1.0.2: {} ws@8.18.0: {} - xml-formatter@3.6.6: + xml-formatter@3.6.7: dependencies: - xml-parser-xo: 4.1.4 + xml-parser-xo: 4.1.5 - xml-parser-xo@4.1.4: {} + xml-parser-xo@4.1.5: {} xmlbuilder@15.1.1: {} diff --git a/scripts/build/addAssetsCar.mjs b/scripts/build/addAssetsCar.mjs new file mode 100644 index 000000000..123b61bcd --- /dev/null +++ b/scripts/build/addAssetsCar.mjs @@ -0,0 +1,24 @@ +import { copyFile, readdir } from "fs/promises"; + +/** + * @param {{ + * readonly appOutDir: string; + * readonly arch: Arch; + * readonly electronPlatformName: string; + * readonly outDir: string; + * readonly packager: PlatformPackager; + * readonly targets: Target[]; + * }} context + */ +export async function addAssetsCar({ appOutDir }) { + if (process.platform !== "darwin") return; + + const appName = (await readdir(appOutDir)).find(item => item.endsWith(".app")); + + if (!appName) { + console.warn(`Could not find .app directory in ${appOutDir}. Skipping adding assets.car`); + return; + } + + await copyFile("build/Assets.car", `${appOutDir}/${appName}/Contents/Resources/Assets.car`); +} diff --git a/scripts/build/afterPack.mjs b/scripts/build/afterPack.mjs new file mode 100644 index 000000000..6a446d67b --- /dev/null +++ b/scripts/build/afterPack.mjs @@ -0,0 +1,5 @@ +import { addAssetsCar } from "./addAssetsCar.mjs"; + +export default async function afterPack(context) { + await addAssetsCar(context); +} diff --git a/scripts/build/beforePack.mjs b/scripts/build/beforePack.mjs new file mode 100644 index 000000000..5b1fa38cb --- /dev/null +++ b/scripts/build/beforePack.mjs @@ -0,0 +1,5 @@ +import { applyAppImageSandboxFix } from "./sandboxFix.mjs"; + +export default async function beforePack() { + await applyAppImageSandboxFix(); +} diff --git a/scripts/build/build.mts b/scripts/build/build.mts index 3ab958000..90465d60b 100644 --- a/scripts/build/build.mts +++ b/scripts/build/build.mts @@ -25,6 +25,9 @@ const NodeCommonOpts: BuildOptions = { platform: "node", external: ["electron"], target: ["esnext"], + loader: { + ".node": "file" + }, define: { IS_DEV: JSON.stringify(isDev) } @@ -78,34 +81,62 @@ async function copyVenbind() { return Promise.reject().catch(() => console.warn("Venbind doesn't support this platform. Building without venbind support") ); + } +} + +async function copyLibVesktop() { + if (process.platform !== "linux") return; + + try { + await copyFile( + "./packages/libvesktop/build/Release/vesktop.node", + `./static/dist/libvesktop-${process.arch}.node` + ); + console.log("Using local libvesktop build"); + } catch { + console.log( + "Using prebuilt libvesktop binaries. Run `pnpm buildLibVesktop` and build again to build from source - see README.md for more details" + ); + return Promise.all([ + copyFile("./packages/libvesktop/prebuilds/vesktop-x64.node", "./static/dist/libvesktop-x64.node"), + copyFile("./packages/libvesktop/prebuilds/vesktop-arm64.node", "./static/dist/libvesktop-arm64.node") + ]).catch(() => console.warn("Failed to copy libvesktop. Building without libvesktop support")); } } await Promise.all([ copyVenmic(), copyVenbind(), + copyLibVesktop(), createContext({ ...NodeCommonOpts, entryPoints: ["src/main/index.ts"], outfile: "dist/js/main.js", - footer: { js: "//# sourceURL=VCDMain" } + footer: { js: "//# sourceURL=VesktopMain" } }), createContext({ ...NodeCommonOpts, entryPoints: ["src/main/arrpc/worker.ts"], outfile: "dist/js/arRpcWorker.js", - footer: { js: "//# sourceURL=VCDArRpcWorker" } + footer: { js: "//# sourceURL=VesktopArRpcWorker" } }), createContext({ ...NodeCommonOpts, entryPoints: ["src/preload/index.ts"], outfile: "dist/js/preload.js", - footer: { js: "//# sourceURL=VCDPreload" } + footer: { js: "//# sourceURL=VesktopPreload" } }), createContext({ ...NodeCommonOpts, entryPoints: ["src/preload/splash.ts"], - outfile: "dist/js/splashPreload.js" + outfile: "dist/js/splashPreload.js", + footer: { js: "//# sourceURL=VesktopSplashPreload" } + }), + createContext({ + ...NodeCommonOpts, + entryPoints: ["src/preload/updater.ts"], + outfile: "dist/js/updaterPreload.js", + footer: { js: "//# sourceURL=VesktopUpdaterPreload" } }), createContext({ ...CommonOpts, @@ -118,7 +149,7 @@ await Promise.all([ jsxFragment: "VencordFragment", external: ["@vencord/types/*", "/assets/*"], plugins: [vencordDep, includeDirPlugin("patches", "src/renderer/patches")], - footer: { js: "//# sourceURL=VCDRenderer" } + footer: { js: "//# sourceURL=VesktopRenderer" } }) ]); diff --git a/scripts/build/sandboxFix.js b/scripts/build/sandboxFix.mjs similarity index 91% rename from scripts/build/sandboxFix.js rename to scripts/build/sandboxFix.mjs index ff0473252..03f92e49a 100644 --- a/scripts/build/sandboxFix.js +++ b/scripts/build/sandboxFix.mjs @@ -6,18 +6,21 @@ // Based on https://github.com/gergof/electron-builder-sandbox-fix/blob/master/lib/index.js -const fs = require("fs/promises"); -const path = require("path"); +import fs from "fs/promises"; +import path from "path"; +import AppImageTarget from "app-builder-lib/out/targets/AppImageTarget.js"; + let isApplied = false; -const hook = async () => { - if (isApplied) return; - isApplied = true; +export async function applyAppImageSandboxFix() { if (process.platform !== "linux") { // this fix is only required on linux return; } - const AppImageTarget = require("app-builder-lib/out/targets/AppImageTarget"); + + if (isApplied) return; + isApplied = true; + const oldBuildMethod = AppImageTarget.default.prototype.build; AppImageTarget.default.prototype.build = async function (...args) { console.log("Running AppImage builder hook", args); @@ -69,6 +72,4 @@ exec "$SCRIPT_DIR/${this.packager.executableName}.bin" "$([ "$IS_STEAMOS" == 1 ] return ret; }; -}; - -module.exports = hook; +} diff --git a/scripts/utils/updateMeta.mts b/scripts/utils/generateMeta.mts similarity index 79% rename from scripts/utils/updateMeta.mts rename to scripts/utils/generateMeta.mts index 2771f92f9..486f65709 100644 --- a/scripts/utils/updateMeta.mts +++ b/scripts/utils/generateMeta.mts @@ -5,7 +5,7 @@ */ import { promises as fs } from "node:fs"; - +import { mkdir } from "node:fs/promises"; import { DOMParser, XMLSerializer } from "@xmldom/xmldom"; import xmlFormat from "xml-formatter"; @@ -43,14 +43,25 @@ function generateDescription(description: string, descriptionNode: Element) { } } -const latestReleaseInformation = await fetch("https://api.github.com/repos/Vencord/Vesktop/releases/latest", { +const releases = await fetch("https://api.github.com/repos/Vencord/Vesktop/releases", { headers: { Accept: "application/vnd.github+json", "X-Github-Api-Version": "2022-11-28" } }).then(res => res.json()); -const metaInfo = await fs.readFile("./meta/dev.vencord.Vesktop.metainfo.xml", "utf-8"); +const latestReleaseInformation = releases[0]; + +const metaInfo = await (async () => { + for (const release of releases) { + const metaAsset = release.assets.find((a: any) => a.name === "dev.vencord.Vesktop.metainfo.xml"); + if (metaAsset) return fetch(metaAsset.browser_download_url).then(res => res.text()); + } +})(); + +if (!metaInfo) { + throw new Error("Could not find existing meta information from any release"); +} const parser = new DOMParser().parseFromString(metaInfo, "text/xml"); @@ -90,4 +101,7 @@ const output = xmlFormat(new XMLSerializer().serializeToString(parser), { indentation: " " }); -await fs.writeFile("./meta/dev.vencord.Vesktop.metainfo.xml", output, "utf-8"); +await mkdir("./dist", { recursive: true }); +await fs.writeFile("./dist/dev.vencord.Vesktop.metainfo.xml", output, "utf-8"); + +console.log("Updated meta information written to ./dist/dev.vencord.Vesktop.metainfo.xml"); diff --git a/src/main/about.ts b/src/main/about.ts index bed2f347b..e0f7e2dfe 100644 --- a/src/main/about.ts +++ b/src/main/about.ts @@ -5,10 +5,9 @@ */ import { app, BrowserWindow } from "electron"; -import { join } from "path"; -import { ICON_PATH, VIEW_DIR } from "shared/paths"; import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; +import { loadView } from "./vesktopStatic"; export async function createAboutWindow() { const height = 750; @@ -17,7 +16,6 @@ export async function createAboutWindow() { const about = new BrowserWindow({ center: true, autoHideMenuBar: true, - icon: ICON_PATH, height, width }); @@ -28,9 +26,7 @@ export async function createAboutWindow() { APP_VERSION: app.getVersion() }); - about.loadFile(join(VIEW_DIR, "about.html"), { - search: data.toString() - }); + loadView(about, "about.html", data); return about; } diff --git a/src/main/appBadge.ts b/src/main/appBadge.ts index 66ad70bcb..a6230f7c7 100644 --- a/src/main/appBadge.ts +++ b/src/main/appBadge.ts @@ -8,6 +8,10 @@ import { app, NativeImage, nativeImage } from "electron"; import { join } from "path"; import { BADGE_DIR } from "shared/paths"; +import { updateUnityLauncherCount } from "./dbus"; +import { AppEvents } from "./events"; +import { mainWin } from "./mainWindow"; + const imgCache = new Map(); function loadBadge(index: number) { const cached = imgCache.get(index); @@ -21,11 +25,17 @@ function loadBadge(index: number) { let lastIndex: null | number = -1; +/** + * -1 = show unread indicator + * 0 = clear + */ export function setBadgeCount(count: number) { + AppEvents.emit("setTrayVariant", count !== 0 ? "trayUnread" : "tray"); + switch (process.platform) { case "linux": if (count === -1) count = 0; - app.setBadgeCount(count); + updateUnityLauncherCount(count); break; case "darwin": if (count === 0) { @@ -40,8 +50,6 @@ export function setBadgeCount(count: number) { lastIndex = index; - // circular import shenanigans - const { mainWin } = require("./mainWindow") as typeof import("./mainWindow"); mainWin.setOverlayIcon(index === null ? null : loadBadge(index), description); break; } diff --git a/src/main/autoStart.ts b/src/main/autoStart.ts index 656947341..cfb43e82a 100644 --- a/src/main/autoStart.ts +++ b/src/main/autoStart.ts @@ -5,30 +5,32 @@ */ import { app } from "electron"; -import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs"; +import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs"; import { join } from "path"; import { stripIndent } from "shared/utils/text"; +import { IS_FLATPAK } from "./constants"; +import { requestBackground } from "./dbus"; +import { Settings, State } from "./settings"; +import { escapeDesktopFileArgument } from "./utils/desktopFileEscape"; + interface AutoStart { isEnabled(): boolean; enable(): void; disable(): void; } -function makeAutoStartLinux(): AutoStart { +function getEscapedCommandLine() { + const args = process.argv.map(escapeDesktopFileArgument); + if (Settings.store.autoStartMinimized) args.push("--start-minimized"); + return args; +} + +function makeAutoStartLinuxDesktop(): AutoStart { const configDir = process.env.XDG_CONFIG_HOME || join(process.env.HOME!, ".config"); const dir = join(configDir, "autostart"); const file = join(dir, "vesktop.desktop"); - // IM STUPID - const legacyName = join(dir, "vencord.desktop"); - if (existsSync(legacyName)) renameSync(legacyName, file); - - // "Quoting must be done by enclosing the argument between double quotes and escaping the double quote character, - // backtick character ("`"), dollar sign ("$") and backslash character ("\") by preceding it with an additional backslash character" - // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables - const commandLine = process.argv.map(arg => '"' + arg.replace(/["$`\\]/g, "\\$&") + '"').join(" "); - return { isEnabled: () => existsSync(file), enable() { @@ -37,9 +39,10 @@ function makeAutoStartLinux(): AutoStart { Type=Application Name=Vesktop Comment=Vesktop autostart script - Exec=${commandLine} + Exec=${getEscapedCommandLine().join(" ")} StartupNotify=false Terminal=false + Icon=vesktop `; mkdirSync(dir, { recursive: true }); @@ -49,10 +52,49 @@ function makeAutoStartLinux(): AutoStart { }; } +function makeAutoStartLinuxPortal() { + return { + isEnabled: () => State.store.linuxAutoStartEnabled === true, + enable() { + const success = requestBackground(true, getEscapedCommandLine()); + if (success) { + State.store.linuxAutoStartEnabled = true; + } + return success; + }, + disable() { + const success = requestBackground(false, []); + if (success) { + State.store.linuxAutoStartEnabled = false; + } + return success; + } + }; +} + const autoStartWindowsMac: AutoStart = { isEnabled: () => app.getLoginItemSettings().openAtLogin, - enable: () => app.setLoginItemSettings({ openAtLogin: true }), + enable: () => + app.setLoginItemSettings({ + openAtLogin: true, + args: Settings.store.autoStartMinimized ? ["--start-minimized"] : [] + }), disable: () => app.setLoginItemSettings({ openAtLogin: false }) }; -export const autoStart = process.platform === "linux" ? makeAutoStartLinux() : autoStartWindowsMac; +// The portal call uses the app id by default, which is org.chromium.Chromium, even in packaged Vesktop. +// This leads to an autostart entry named "Chromium" instead of "Vesktop". +// Thus, only use the portal inside Flatpak, where the app is actually correct. +// Maybe there is a way to fix it outside of flatpak, but I couldn't figure it out. +export const autoStart = + process.platform !== "linux" + ? autoStartWindowsMac + : IS_FLATPAK + ? makeAutoStartLinuxPortal() + : makeAutoStartLinuxDesktop(); + +Settings.addChangeListener("autoStartMinimized", () => { + if (!autoStart.isEnabled()) return; + + autoStart.enable(); +}); diff --git a/src/main/cli.ts b/src/main/cli.ts new file mode 100644 index 000000000..7d789fdb2 --- /dev/null +++ b/src/main/cli.ts @@ -0,0 +1,143 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { app } from "electron"; +import { basename } from "path"; +import { stripIndent } from "shared/utils/text"; +import { parseArgs, ParseArgsOptionDescriptor } from "util"; + +type Option = ParseArgsOptionDescriptor & { + description: string; + hidden?: boolean; + options?: string[]; + argumentName?: string; +}; + +const options = { + "start-minimized": { + default: false, + type: "boolean", + short: "m", + description: "Start the application minimized to the system tray" + }, + version: { + type: "boolean", + short: "v", + description: "Print the application version and exit" + }, + help: { + type: "boolean", + short: "h", + description: "Print help information and exit" + }, + "user-agent": { + type: "string", + argumentName: "ua", + description: "Set a custom User-Agent. May trigger anti-spam or break voice chat" + }, + "user-agent-os": { + type: "string", + description: "Set User-Agent to a specific operating system. May trigger anti-spam or break voice chat", + options: ["windows", "linux", "darwin"] + } +} satisfies Record; + +// only for help display +const extraOptions = { + "enable-features": { + type: "string", + description: "Enable specific Chromium features", + argumentName: "feature1,feature2,…" + }, + "disable-features": { + type: "string", + description: "Disable specific Chromium features", + argumentName: "feature1,feature2,…" + }, + "ozone-platform": { + hidden: process.platform !== "linux", + type: "string", + description: "Whether to run Vesktop in Wayland or X11 (XWayland)", + options: ["x11", "wayland"] + } +} satisfies Record; + +const args = basename(process.argv[0]) === "electron" ? process.argv.slice(2) : process.argv.slice(1); + +export const CommandLine = parseArgs({ + args, + options, + strict: false as true, // we manually check later, so cast to true to get better types + allowPositionals: true +}); + +export function checkCommandLineForHelpOrVersion() { + const { help, version } = CommandLine.values; + + if (version) { + console.log(`Vesktop v${app.getVersion()}`); + app.exit(0); + } + + if (help) { + const base = stripIndent` + Vesktop v${app.getVersion()} + + Usage: ${basename(process.execPath)} [options] [url] + + Electron Options: + See + + Chromium Options: + See - only some of them work + + Vesktop Options: + `; + + const optionLines = Object.entries(options) + .sort(([a], [b]) => a.localeCompare(b)) + .concat(Object.entries(extraOptions)) + .filter(([, opt]) => !("hidden" in opt && opt.hidden)) + .map(([name, opt]) => { + const flags = [ + "short" in opt && `-${opt.short}`, + `--${name}`, + opt.type !== "boolean" && + ("options" in opt ? `<${opt.options.join(" | ")}>` : `<${opt.argumentName ?? opt.type}>`) + ] + .filter(Boolean) + .join(" "); + + return [flags, opt.description]; + }); + + const padding = optionLines.reduce((max, [flags]) => Math.max(max, flags.length), 0) + 4; + + const optionsHelp = optionLines + .map(([flags, description]) => ` ${flags.padEnd(padding, " ")}${description}`) + .join("\n"); + + console.log(base + "\n" + optionsHelp); + app.exit(0); + } + + for (const [name, def] of Object.entries(options)) { + const value = CommandLine.values[name]; + if (value == null) continue; + + if (typeof value !== def.type) { + console.error(`Invalid options. Expected ${def.type === "boolean" ? "no" : "an"} argument for --${name}`); + app.exit(1); + } + + if ("options" in def && !def.options?.includes(value as string)) { + console.error(`Invalid value for --${name}: ${value}\nExpected one of: ${def.options.join(", ")}`); + app.exit(1); + } + } +} + +checkCommandLineForHelpOrVersion(); diff --git a/src/main/constants.ts b/src/main/constants.ts index dcfc8fc31..458e11b4b 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -8,6 +8,8 @@ import { app } from "electron"; import { existsSync, mkdirSync } from "fs"; import { dirname, join } from "path"; +import { CommandLine } from "./cli"; + const vesktopDir = dirname(process.execPath); export const PORTABLE = @@ -20,20 +22,15 @@ export const DATA_DIR = mkdirSync(DATA_DIR, { recursive: true }); -const SESSION_DATA_DIR = join(DATA_DIR, "sessionData"); +export const SESSION_DATA_DIR = join(DATA_DIR, "sessionData"); app.setPath("sessionData", SESSION_DATA_DIR); export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings"); +mkdirSync(VENCORD_SETTINGS_DIR, { recursive: true }); export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css"); export const VENCORD_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json"); export const VENCORD_THEMES_DIR = join(DATA_DIR, "themes"); -// needs to be inline require because of circular dependency -// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised -export const VENCORD_FILES_DIR = - (require("./settings") as typeof import("./settings")).State.store.vencordDir || - join(SESSION_DATA_DIR, "vencordFiles"); - export const USER_AGENT = `Vesktop/${app.getVersion()} (https://github.com/Vencord/Vesktop)`; // dimensions shamelessly stolen from Discord Desktop :3 @@ -51,9 +48,14 @@ const BrowserUserAgents = { windows: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) ${VersionString}` }; -export const BrowserUserAgent = BrowserUserAgents[process.platform] || BrowserUserAgents.windows; +export const BrowserUserAgent = + CommandLine.values["user-agent"] || + BrowserUserAgents[CommandLine.values["user-agent-os"] || process.platform] || + BrowserUserAgents.windows; export const enum MessageBoxChoice { Default, Cancel } + +export const IS_FLATPAK = process.env.FLATPAK_ID !== undefined; diff --git a/src/main/dbus.ts b/src/main/dbus.ts new file mode 100644 index 000000000..05b257892 --- /dev/null +++ b/src/main/dbus.ts @@ -0,0 +1,40 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { app } from "electron"; +import { join } from "path"; +import { STATIC_DIR } from "shared/paths"; + +let libVesktop: typeof import("libvesktop") | null = null; + +function loadLibVesktop() { + try { + if (!libVesktop) { + libVesktop = require(join(STATIC_DIR, `dist/libvesktop-${process.arch}.node`)); + } + } catch (e) { + console.error("Failed to load libvesktop:", e); + } + + return libVesktop; +} + +export function getAccentColor() { + return loadLibVesktop()?.getAccentColor() ?? null; +} + +export function updateUnityLauncherCount(count: number) { + const libVesktop = loadLibVesktop(); + if (!libVesktop) { + return app.setBadgeCount(count); + } + + return libVesktop.updateUnityLauncherCount(count); +} + +export function requestBackground(autoStart: boolean, commandLine: string[]) { + return loadLibVesktop()?.requestBackground(autoStart, commandLine) ?? false; +} diff --git a/src/main/events.ts b/src/main/events.ts new file mode 100644 index 000000000..f331a535c --- /dev/null +++ b/src/main/events.ts @@ -0,0 +1,15 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { EventEmitter } from "events"; + +import { UserAssetType } from "./userAssets"; + +export const AppEvents = new EventEmitter<{ + appLoaded: []; + userAssetChanged: [UserAssetType]; + setTrayVariant: ["tray" | "trayUnread"]; +}>(); diff --git a/src/main/firstLaunch.ts b/src/main/firstLaunch.ts index 69e01a0da..689f9bc41 100644 --- a/src/main/firstLaunch.ts +++ b/src/main/firstLaunch.ts @@ -9,13 +9,13 @@ import { BrowserWindow } from "electron/main"; import { copyFileSync, mkdirSync, readdirSync } from "fs"; import { join } from "path"; import { SplashProps } from "shared/browserWinProperties"; -import { ICON_PATH, VIEW_DIR } from "shared/paths"; import { autoStart } from "./autoStart"; import { DATA_DIR } from "./constants"; import { createWindows } from "./mainWindow"; import { Settings, State } from "./settings"; import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; +import { loadView } from "./vesktopStatic"; interface Data { discordBranch: "stable" | "canary" | "ptb"; @@ -31,21 +31,19 @@ export function createFirstLaunchTour() { transparent: false, frame: true, autoHideMenuBar: true, - height: 470, - width: 550, - icon: ICON_PATH + height: 550, + width: 600 }); makeLinksOpenExternally(win); - win.loadFile(join(VIEW_DIR, "first-launch.html")); + loadView(win, "first-launch.html"); win.webContents.addListener("console-message", (_e, _l, msg) => { if (msg === "cancel") return app.exit(); if (!msg.startsWith("form:")) return; const data = JSON.parse(msg.slice(5)) as Data; - console.log(data); State.store.firstLaunch = false; Settings.store.discordBranch = data.discordBranch; Settings.store.minimizeToTray = !!data.minimizeToTray; @@ -64,7 +62,11 @@ export function createFirstLaunchTour() { copyFileSync(join(from, file), join(to, file)); } } catch (e) { - console.error("Failed to import settings:", e); + if (e instanceof Error && "code" in e && e.code === "ENOENT") { + console.log("No Vencord settings found to import."); + } else { + console.error("Failed to import Vencord settings:", e); + } } } diff --git a/src/main/index.ts b/src/main/index.ts index 5deed1eb4..e9a2f3c48 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -4,10 +4,13 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import "./cli"; +import "./updater"; import "./ipc"; +import "./userAssets"; +import "./vesktopProtocol"; import { app, BrowserWindow, nativeTheme } from "electron"; -import { autoUpdater } from "electron-updater"; import { DATA_DIR } from "./constants"; import { createFirstLaunchTour } from "./firstLaunch"; @@ -15,13 +18,10 @@ import { createWindows, mainWin } from "./mainWindow"; import { registerMediaPermissionsHandler } from "./mediaPermissions"; import { registerScreenShareHandler } from "./screenShare"; import { Settings, State } from "./settings"; +import { setAsDefaultProtocolClient } from "./utils/setAsDefaultProtocolClient"; import { isDeckGameMode } from "./utils/steamOS"; import { startVenbind } from "./venbind"; -if (!IS_DEV) { - autoUpdater.checkForUpdatesAndNotify(); -} - console.log("Vesktop v" + app.getVersion()); // Make the Vencord files use our DATA_DIR @@ -32,7 +32,7 @@ const isLinux = process.platform === "linux"; export let enableHardwareAcceleration = true; function init() { - app.setAsDefaultProtocolClient("discord"); + setAsDefaultProtocolClient("discord"); const { disableSmoothScroll, hardwareAcceleration, hardwareVideoAcceleration } = Settings.store; diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 94eb67158..a17bc9cdb 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -28,13 +28,14 @@ import { debounce } from "shared/utils/debounce"; import { IpcEvents } from "../shared/IpcEvents"; import { setBadgeCount } from "./appBadge"; import { autoStart } from "./autoStart"; -import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants"; +import { VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants"; import { mainWin } from "./mainWindow"; import { Settings, State } from "./settings"; import { handle, handleSync } from "./utils/ipcWrappers"; import { PopoutWindows } from "./utils/popout"; import { isDeckGameMode, showGamePage } from "./utils/steamOS"; import { isValidVencordInstall } from "./utils/vencordLoader"; +import { VENCORD_FILES_DIR } from "./vencordFilesDir"; handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js")); handleSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, () => diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index 762d39be9..71edd52ae 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -8,45 +8,36 @@ import { app, BrowserWindow, BrowserWindowConstructorOptions, - dialog, Menu, MenuItemConstructorOptions, nativeTheme, + Rectangle, screen, - session, - Tray + session } from "electron"; -import { EventEmitter } from "events"; -import { rm } from "fs/promises"; import { join } from "path"; import { IpcCommands, IpcEvents } from "shared/IpcEvents"; import { isTruthy } from "shared/utils/guards"; import { once } from "shared/utils/once"; import type { SettingsStore } from "shared/utils/SettingsStore"; -import { ICON_PATH } from "../shared/paths"; import { createAboutWindow } from "./about"; import { initArRPC } from "./arrpc"; -import { - BrowserUserAgent, - DATA_DIR, - DEFAULT_HEIGHT, - DEFAULT_WIDTH, - MessageBoxChoice, - MIN_HEIGHT, - MIN_WIDTH, - VENCORD_FILES_DIR -} from "./constants"; +import { CommandLine } from "./cli"; +import { BrowserUserAgent, DEFAULT_HEIGHT, DEFAULT_WIDTH, MIN_HEIGHT, MIN_WIDTH } from "./constants"; +import { AppEvents } from "./events"; import { darwinURL } from "./index"; import { sendRendererCommand } from "./ipcCommands"; import { Settings, State, VencordSettings } from "./settings"; import { createSplashWindow, updateSplashMessage } from "./splash"; +import { destroyTray, initTray } from "./tray"; +import { clearData } from "./utils/clearData"; import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS"; import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader"; +import { VENCORD_FILES_DIR } from "./vencordFilesDir"; let isQuitting = false; -let tray: Tray; applyDeckKeyboardFix(); @@ -77,84 +68,6 @@ function makeSettingsListenerHelpers(o: SettingsStore) { const [addSettingsListener, removeSettingsListeners] = makeSettingsListenerHelpers(Settings); const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings); -function initTray(win: BrowserWindow) { - const onTrayClick = () => { - if (Settings.store.clickTrayToShowHide && win.isVisible()) win.hide(); - else win.show(); - }; - const trayMenu = Menu.buildFromTemplate([ - { - label: "Open", - click() { - win.show(); - } - }, - { - label: "About", - click: createAboutWindow - }, - { - label: "Repair Vencord", - async click() { - await downloadVencordFiles(); - app.relaunch(); - app.quit(); - } - }, - { - label: "Reset Vesktop", - async click() { - await clearData(win); - } - }, - { - type: "separator" - }, - { - label: "Restart", - click() { - app.relaunch(); - app.quit(); - } - }, - { - label: "Quit", - click() { - isQuitting = true; - app.quit(); - } - } - ]); - - tray = new Tray(ICON_PATH); - tray.setToolTip("Vesktop"); - tray.setContextMenu(trayMenu); - tray.on("click", onTrayClick); -} - -async function clearData(win: BrowserWindow) { - const { response } = await dialog.showMessageBox(win, { - message: "Are you sure you want to reset Vesktop?", - detail: "This will log you out, clear caches and reset all your settings!\n\nVesktop will automatically restart after this operation.", - buttons: ["Yes", "No"], - cancelId: MessageBoxChoice.Cancel, - defaultId: MessageBoxChoice.Default, - type: "warning" - }); - - if (response === MessageBoxChoice.Cancel) return; - - win.close(); - - await win.webContents.session.clearStorageData(); - await win.webContents.session.clearCache(); - await win.webContents.session.clearCodeCaches({}); - await rm(DATA_DIR, { force: true, recursive: true }); - - app.relaunch(); - app.quit(); -} - type MenuItemList = Array; function initMenuBar(win: BrowserWindow) { @@ -263,55 +176,6 @@ function initMenuBar(win: BrowserWindow) { Menu.setApplicationMenu(menu); } -function getWindowBoundsOptions(): BrowserWindowConstructorOptions { - // We want the default window behaivour to apply in game mode since it expects everything to be fullscreen and maximized. - if (isDeckGameMode) return {}; - - const { x, y, width, height } = State.store.windowBounds ?? {}; - - const options = { - width: width ?? DEFAULT_WIDTH, - height: height ?? DEFAULT_HEIGHT - } as BrowserWindowConstructorOptions; - - const storedDisplay = screen.getAllDisplays().find(display => display.id === State.store.displayId); - - if (x != null && y != null && storedDisplay) { - options.x = x; - options.y = y; - } - - if (!Settings.store.disableMinSize) { - options.minWidth = MIN_WIDTH; - options.minHeight = MIN_HEIGHT; - } - - return options; -} - -function getDarwinOptions(): BrowserWindowConstructorOptions { - const options = { - titleBarStyle: "hidden", - trafficLightPosition: { x: 10, y: 10 } - } as BrowserWindowConstructorOptions; - - const { splashTheming, splashBackground } = Settings.store; - const { macosTranslucency } = VencordSettings.store; - - if (macosTranslucency) { - options.vibrancy = "sidebar"; - options.backgroundColor = "#ffffff00"; - } else { - if (splashTheming !== false) { - options.backgroundColor = splashBackground; - } else { - options.backgroundColor = nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff"; - } - } - - return options; -} - function initWindowBoundsListeners(win: BrowserWindow) { const saveState = () => { State.store.maximized = win.isMaximized(); @@ -324,7 +188,6 @@ function initWindowBoundsListeners(win: BrowserWindow) { const saveBounds = () => { State.store.windowBounds = win.getBounds(); - State.store.displayId = screen.getDisplayMatching(State.store.windowBounds).id; }; win.on("resize", saveBounds); @@ -333,8 +196,8 @@ function initWindowBoundsListeners(win: BrowserWindow) { function initSettingsListeners(win: BrowserWindow) { addSettingsListener("tray", enable => { - if (enable) initTray(win); - else tray?.destroy(); + if (enable) initTray(win, q => (isQuitting = q)); + else destroyTray(); }); addSettingsListener("disableMinSize", disable => { @@ -412,26 +275,55 @@ function initStaticTitle(win: BrowserWindow) { }); } -function createMainWindow() { - // Clear up previous settings listeners - removeSettingsListeners(); - removeVencordSettingsListeners(); +function getWindowBoundsOptions(): BrowserWindowConstructorOptions { + // We want the default window behaviour to apply in game mode since it expects everything to be fullscreen and maximized. + if (isDeckGameMode) return {}; + + const { x, y, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = State.store.windowBounds ?? {}; + + const options = { width, height } as BrowserWindowConstructorOptions; + + if (x != null && y != null) { + function isInBounds(rect: Rectangle, display: Rectangle) { + return !( + rect.x + rect.width < display.x || + rect.x > display.x + display.width || + rect.y + rect.height < display.y || + rect.y > display.y + display.height + ); + } + + const inBounds = screen.getAllDisplays().some(d => isInBounds({ x, y, width, height }, d.bounds)); + if (inBounds) { + options.x = x; + options.y = y; + } + } + + if (!Settings.store.disableMinSize) { + options.minWidth = MIN_WIDTH; + options.minHeight = MIN_HEIGHT; + } + return options; +} + +function buildBrowserWindowOptions(): BrowserWindowConstructorOptions { const { staticTitle, transparencyOption, enableMenu, customTitleBar, splashTheming, splashBackground } = Settings.store; - const { frameless, transparent } = VencordSettings.store; + const { frameless, transparent, macosTranslucency } = VencordSettings.store; const noFrame = frameless === true || customTitleBar === true; const backgroundColor = splashTheming !== false ? splashBackground : nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff"; - const win = (mainWin = new BrowserWindow({ + const options: BrowserWindowConstructorOptions = { show: Settings.store.enableSplashScreen === false, backgroundColor, webPreferences: { nodeIntegration: false, - sandbox: false, + sandbox: false, // TODO contextIsolation: true, devTools: true, preload: join(__dirname, "preload.js"), @@ -439,30 +331,51 @@ function createMainWindow() { // disable renderer backgrounding to prevent the app from unloading when in the background backgroundThrottling: false }, - icon: ICON_PATH, frame: !noFrame, - ...(transparent && { - transparent: true, - backgroundColor: "#00000000" - }), - ...(transparencyOption && - transparencyOption !== "none" && { - backgroundColor: "#00000000", - backgroundMaterial: transparencyOption - }), - // Fix transparencyOption for custom discord titlebar - ...(customTitleBar && - transparencyOption && - transparencyOption !== "none" && { - transparent: true - }), - ...(staticTitle && { title: "Vesktop" }), - ...(process.platform === "darwin" && getDarwinOptions()), - ...getWindowBoundsOptions(), - autoHideMenuBar: enableMenu - })); + autoHideMenuBar: enableMenu, + ...getWindowBoundsOptions() + }; + + if (transparent) { + options.transparent = true; + options.backgroundColor = "#00000000"; + } + + if (transparencyOption && transparencyOption !== "none") { + options.backgroundColor = "#00000000"; + options.backgroundMaterial = transparencyOption; + + if (customTitleBar) { + options.transparent = true; + } + } + + if (staticTitle) { + options.title = "Vesktop"; + } + + if (process.platform === "darwin") { + options.titleBarStyle = "hidden"; + options.trafficLightPosition = { x: 10, y: 10 }; + + if (macosTranslucency) { + options.vibrancy = "sidebar"; + options.backgroundColor = "#ffffff00"; + } + } + + return options; +} + +function createMainWindow() { + // Clear up previous settings listeners + removeSettingsListeners(); + removeVencordSettingsListeners(); + + const win = (mainWin = new BrowserWindow(buildBrowserWindowOptions())); + win.setMenuBarVisibility(false); - if (process.platform === "darwin" && customTitleBar) win.setWindowButtonVisibility(false); + if (process.platform === "darwin" && Settings.store.customTitleBar) win.setWindowButtonVisibility(false); win.on("close", e => { const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false; @@ -477,7 +390,9 @@ function createMainWindow() { }); initWindowBoundsListeners(win); - if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win); + if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin") + initTray(win, q => (isQuitting = q)); + initMenuBar(win); makeLinksOpenExternally(win); initSettingsListeners(win); @@ -497,8 +412,6 @@ function createMainWindow() { const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDesktopMain.js"))); -const loadEvents = new EventEmitter(); - export function loadUrl(uri: string | undefined) { const branch = Settings.store.discordBranch; const subdomain = branch === "canary" || branch === "ptb" ? `${branch}.` : ""; @@ -506,7 +419,7 @@ export function loadUrl(uri: string | undefined) { // we do not rely on 'did-finish-load' because it fires even if loadURL fails which triggers early detruction of the splash mainWin .loadURL(`https://${subdomain}discord.com/${uri ? new URL(uri).pathname.slice(1) || "app" : "app"}`) - .then(() => loadEvents.emit("app-loaded")) + .then(() => AppEvents.emit("appLoaded")) .catch(error => retryUrl(error.url, error.code)); } @@ -518,7 +431,7 @@ function retryUrl(url: string, description: string) { } export async function createWindows() { - const startMinimized = process.argv.includes("--start-minimized"); + const startMinimized = CommandLine.values["start-minimized"]; let splash: BrowserWindow | undefined; if (Settings.store.enableSplashScreen !== false) { @@ -533,7 +446,7 @@ export async function createWindows() { mainWin = createMainWindow(); - loadEvents.on("app-loaded", () => { + AppEvents.on("appLoaded", () => { splash?.destroy(); if (!startMinimized) { diff --git a/src/main/splash.ts b/src/main/splash.ts index d9f39bd7a..cfc77460a 100644 --- a/src/main/splash.ts +++ b/src/main/splash.ts @@ -7,25 +7,24 @@ import { BrowserWindow } from "electron"; import { join } from "path"; import { SplashProps } from "shared/browserWinProperties"; -import { ICON_PATH, VIEW_DIR } from "shared/paths"; import { Settings } from "./settings"; +import { loadView } from "./vesktopStatic"; let splash: BrowserWindow | undefined; export function createSplashWindow(startMinimized = false) { splash = new BrowserWindow({ ...SplashProps, - icon: ICON_PATH, show: !startMinimized, webPreferences: { preload: join(__dirname, "splashPreload.js") } }); - splash.loadFile(join(VIEW_DIR, "splash.html")); + loadView(splash, "splash.html"); - const { splashBackground, splashColor, splashTheming } = Settings.store; + const { splashBackground, splashColor, splashTheming, splashPixelated } = Settings.store; if (splashTheming !== false) { if (splashColor) { @@ -40,6 +39,10 @@ export function createSplashWindow(startMinimized = false) { } } + if (splashPixelated) { + splash.webContents.insertCSS(`img { image-rendering: pixelated; }`); + } + return splash; } diff --git a/src/main/tray.ts b/src/main/tray.ts new file mode 100644 index 000000000..f58a71960 --- /dev/null +++ b/src/main/tray.ts @@ -0,0 +1,92 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { app, BrowserWindow, Menu, Tray } from "electron"; + +import { createAboutWindow } from "./about"; +import { AppEvents } from "./events"; +import { Settings } from "./settings"; +import { resolveAssetPath } from "./userAssets"; +import { clearData } from "./utils/clearData"; +import { downloadVencordFiles } from "./utils/vencordLoader"; + +let tray: Tray; +let trayVariant: "tray" | "trayUnread" = "tray"; + +AppEvents.on("userAssetChanged", async asset => { + if (tray && (asset === "tray" || asset === "trayUnread")) { + tray.setImage(await resolveAssetPath(trayVariant)); + } +}); + +AppEvents.on("setTrayVariant", async variant => { + if (trayVariant === variant) return; + + trayVariant = variant; + if (!tray) return; + + tray.setImage(await resolveAssetPath(trayVariant)); +}); + +export function destroyTray() { + tray?.destroy(); +} + +export async function initTray(win: BrowserWindow, setIsQuitting: (val: boolean) => void) { + const onTrayClick = () => { + if (Settings.store.clickTrayToShowHide && win.isVisible()) win.hide(); + else win.show(); + }; + + const trayMenu = Menu.buildFromTemplate([ + { + label: "Open", + click() { + win.show(); + } + }, + { + label: "About", + click: createAboutWindow + }, + { + label: "Repair Vencord", + async click() { + await downloadVencordFiles(); + app.relaunch(); + app.quit(); + } + }, + { + label: "Reset Vesktop", + async click() { + await clearData(win); + } + }, + { + type: "separator" + }, + { + label: "Restart", + click() { + app.relaunch(); + app.quit(); + } + }, + { + label: "Quit", + click() { + setIsQuitting(true); + app.quit(); + } + } + ]); + + tray = new Tray(await resolveAssetPath(trayVariant)); + tray.setToolTip("Vesktop"); + tray.setContextMenu(trayMenu); + tray.on("click", onTrayClick); +} diff --git a/src/main/updater.ts b/src/main/updater.ts new file mode 100644 index 000000000..0b80391cd --- /dev/null +++ b/src/main/updater.ts @@ -0,0 +1,81 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { app, BrowserWindow, ipcMain } from "electron"; +import { autoUpdater, UpdateInfo } from "electron-updater"; +import { join } from "path"; +import { IpcEvents, UpdaterIpcEvents } from "shared/IpcEvents"; +import { Millis } from "shared/utils/millis"; + +import { State } from "./settings"; +import { handle } from "./utils/ipcWrappers"; +import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; +import { loadView } from "./vesktopStatic"; + +let updaterWindow: BrowserWindow | null = null; + +autoUpdater.on("update-available", update => { + if (State.store.updater?.ignoredVersion === update.version) return; + if ((State.store.updater?.snoozeUntil ?? 0) > Date.now()) return; + + openUpdater(update); +}); + +autoUpdater.on("update-downloaded", () => setTimeout(() => autoUpdater.quitAndInstall(), 100)); +autoUpdater.on("download-progress", p => + updaterWindow?.webContents.send(UpdaterIpcEvents.DOWNLOAD_PROGRESS, p.percent) +); +autoUpdater.on("error", err => updaterWindow?.webContents.send(UpdaterIpcEvents.ERROR, err.message)); + +autoUpdater.autoDownload = false; +autoUpdater.autoInstallOnAppQuit = false; +autoUpdater.fullChangelog = true; + +const isOutdated = autoUpdater.checkForUpdates().then(res => Boolean(res?.isUpdateAvailable)); + +handle(IpcEvents.UPDATER_IS_OUTDATED, () => isOutdated); +handle(IpcEvents.UPDATER_OPEN, async () => { + const res = await autoUpdater.checkForUpdates(); + if (res?.isUpdateAvailable && res.updateInfo) openUpdater(res.updateInfo); +}); + +function openUpdater(update: UpdateInfo) { + updaterWindow = new BrowserWindow({ + title: "Vesktop Updater", + autoHideMenuBar: true, + webPreferences: { + preload: join(__dirname, "updaterPreload.js") + }, + minHeight: 400, + minWidth: 750 + }); + makeLinksOpenExternally(updaterWindow); + + handle(UpdaterIpcEvents.GET_DATA, () => ({ update, version: app.getVersion() })); + handle(UpdaterIpcEvents.INSTALL, async () => { + await autoUpdater.downloadUpdate(); + }); + handle(UpdaterIpcEvents.SNOOZE_UPDATE, () => { + State.store.updater ??= {}; + State.store.updater.snoozeUntil = Date.now() + 1 * Millis.DAY; + updaterWindow?.close(); + }); + handle(UpdaterIpcEvents.IGNORE_UPDATE, () => { + State.store.updater ??= {}; + State.store.updater.ignoredVersion = update.version; + updaterWindow?.close(); + }); + + updaterWindow.on("closed", () => { + ipcMain.removeHandler(UpdaterIpcEvents.GET_DATA); + ipcMain.removeHandler(UpdaterIpcEvents.INSTALL); + ipcMain.removeHandler(UpdaterIpcEvents.SNOOZE_UPDATE); + ipcMain.removeHandler(UpdaterIpcEvents.IGNORE_UPDATE); + updaterWindow = null; + }); + + loadView(updaterWindow, "updater/index.html"); +} diff --git a/src/main/userAssets.ts b/src/main/userAssets.ts new file mode 100644 index 000000000..5fb0d4b60 --- /dev/null +++ b/src/main/userAssets.ts @@ -0,0 +1,101 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { app, dialog, net } from "electron"; +import { copyFile, mkdir, rm } from "fs/promises"; +import { join } from "path"; +import { IpcEvents } from "shared/IpcEvents"; +import { STATIC_DIR } from "shared/paths"; +import { pathToFileURL } from "url"; + +import { DATA_DIR } from "./constants"; +import { AppEvents } from "./events"; +import { mainWin } from "./mainWindow"; +import { fileExistsAsync } from "./utils/fileExists"; +import { handle } from "./utils/ipcWrappers"; + +const CUSTOMIZABLE_ASSETS = ["splash", "tray", "trayUnread"] as const; +export type UserAssetType = (typeof CUSTOMIZABLE_ASSETS)[number]; + +const DEFAULT_ASSETS: Record = { + splash: "splash.webp", + tray: `tray/${process.platform === "darwin" ? "trayTemplate" : "tray"}.png`, + trayUnread: "tray/trayUnread.png" +}; + +const UserAssetFolder = join(DATA_DIR, "userAssets"); + +export async function resolveAssetPath(asset: UserAssetType) { + if (!CUSTOMIZABLE_ASSETS.includes(asset)) { + throw new Error(`Invalid asset: ${asset}`); + } + + const assetPath = join(UserAssetFolder, asset); + if (await fileExistsAsync(assetPath)) { + return assetPath; + } + + return join(STATIC_DIR, DEFAULT_ASSETS[asset]); +} + +export async function handleVesktopAssetsProtocol(path: string, req: Request) { + const asset = path.slice(1); + + // @ts-expect-error dumb types + if (!CUSTOMIZABLE_ASSETS.includes(asset)) { + return new Response(null, { status: 404 }); + } + + try { + const res = await net.fetch(pathToFileURL(join(UserAssetFolder, asset)).href); + if (res.ok) return res; + } catch {} + + return net.fetch(pathToFileURL(join(STATIC_DIR, DEFAULT_ASSETS[asset])).href); +} + +handle(IpcEvents.CHOOSE_USER_ASSET, async (_event, asset: UserAssetType, value?: null) => { + if (!CUSTOMIZABLE_ASSETS.includes(asset)) { + throw `Invalid asset: ${asset}`; + } + + const assetPath = join(UserAssetFolder, asset); + + if (value === null) { + try { + await rm(assetPath, { force: true }); + AppEvents.emit("userAssetChanged", asset); + return "ok"; + } catch (e) { + console.error(`Failed to remove user asset ${asset}:`, e); + return "failed"; + } + } + + const res = await dialog.showOpenDialog(mainWin, { + properties: ["openFile"], + title: `Select an image to use as ${asset}`, + defaultPath: app.getPath("pictures"), + filters: [ + { + name: "Images", + extensions: ["png", "jpg", "jpeg", "webp", "gif", "avif", "svg"] + } + ] + }); + + if (res.canceled || !res.filePaths.length) return "cancelled"; + + try { + await mkdir(UserAssetFolder, { recursive: true }); + await copyFile(res.filePaths[0], assetPath); + AppEvents.emit("userAssetChanged", asset); + return "ok"; + } catch (e) { + console.error(`Failed to copy user asset ${asset}:`, e); + return "failed"; + } +}); diff --git a/src/main/utils/clearData.ts b/src/main/utils/clearData.ts new file mode 100644 index 000000000..6ce5c8979 --- /dev/null +++ b/src/main/utils/clearData.ts @@ -0,0 +1,32 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { app, BrowserWindow, dialog } from "electron"; +import { rm } from "fs/promises"; +import { DATA_DIR, MessageBoxChoice } from "main/constants"; + +export async function clearData(win: BrowserWindow) { + const { response } = await dialog.showMessageBox(win, { + message: "Are you sure you want to reset Vesktop?", + detail: "This will log you out, clear caches and reset all your settings!\n\nVesktop will automatically restart after this operation.", + buttons: ["Yes", "No"], + cancelId: MessageBoxChoice.Cancel, + defaultId: MessageBoxChoice.Default, + type: "warning" + }); + + if (response === MessageBoxChoice.Cancel) return; + + win.close(); + + await win.webContents.session.clearStorageData(); + await win.webContents.session.clearCache(); + await win.webContents.session.clearCodeCaches({}); + await rm(DATA_DIR, { force: true, recursive: true }); + + app.relaunch(); + app.quit(); +} diff --git a/src/main/utils/desktopFileEscape.ts b/src/main/utils/desktopFileEscape.ts new file mode 100644 index 000000000..d41265baa --- /dev/null +++ b/src/main/utils/desktopFileEscape.ts @@ -0,0 +1,56 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +// https://specifications.freedesktop.org/desktop-entry-spec/latest/exec-variables.html + +// "If an argument contains a reserved character the argument must be quoted." +const desktopFileReservedChars = new Set([ + " ", + "\t", + "\n", + '"', + "'", + "\\", + ">", + "<", + "~", + "|", + "&", + ";", + "$", + "*", + "?", + "#", + "(", + ")", + "`" +]); + +export function escapeDesktopFileArgument(arg: string) { + let needsQuoting = false; + let out = ""; + + for (const c of arg) { + if (desktopFileReservedChars.has(c)) { + // "Quoting must be done by enclosing the argument between double quotes" + needsQuoting = true; + // "and escaping the double quote character, backtick character ("`"), dollar sign ("$") + // and backslash character ("\") by preceding it with an additional backslash character" + if (c === '"' || c === "`" || c === "$" || c === "\\") { + out += "\\"; + } + } + + // "Literal percentage characters must be escaped as %%" + if (c === "%") { + out += "%%"; + } else { + out += c; + } + } + + return needsQuoting ? `"${out}"` : out; +} diff --git a/src/main/utils/fileExists.ts b/src/main/utils/fileExists.ts new file mode 100644 index 000000000..ce73efb7b --- /dev/null +++ b/src/main/utils/fileExists.ts @@ -0,0 +1,13 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { access, constants } from "fs/promises"; + +export async function fileExistsAsync(path: string) { + return await access(path, constants.F_OK) + .then(() => true) + .catch(() => false); +} diff --git a/src/main/utils/ipcWrappers.ts b/src/main/utils/ipcWrappers.ts index 27a35fda3..bd59538b8 100644 --- a/src/main/utils/ipcWrappers.ts +++ b/src/main/utils/ipcWrappers.ts @@ -6,7 +6,7 @@ import { ipcMain, IpcMainEvent, IpcMainInvokeEvent, WebFrameMain } from "electron"; import { DISCORD_HOSTNAMES } from "main/constants"; -import { IpcEvents } from "shared/IpcEvents"; +import { IpcEvents, UpdaterIpcEvents } from "shared/IpcEvents"; export function validateSender(frame: WebFrameMain | null, event: string) { if (!frame) throw new Error(`ipc[${event}]: No sender frame`); @@ -18,21 +18,21 @@ export function validateSender(frame: WebFrameMain | null, event: string) { throw new Error(`ipc[${event}]: Invalid URL ${frame.url}`); } - if (protocol === "file:") return; + if (protocol === "file:" || protocol === "vesktop:") return; if (!DISCORD_HOSTNAMES.includes(hostname)) { throw new Error(`ipc[${event}]: Disallowed hostname ${hostname}`); } } -export function handleSync(event: IpcEvents, cb: (e: IpcMainEvent, ...args: any[]) => any) { +export function handleSync(event: IpcEvents | UpdaterIpcEvents, cb: (e: IpcMainEvent, ...args: any[]) => any) { ipcMain.on(event, (e, ...args) => { validateSender(e.senderFrame, event); e.returnValue = cb(e, ...args); }); } -export function handle(event: IpcEvents, cb: (e: IpcMainInvokeEvent, ...args: any[]) => any) { +export function handle(event: IpcEvents | UpdaterIpcEvents, cb: (e: IpcMainInvokeEvent, ...args: any[]) => any) { ipcMain.handle(event, (e, ...args) => { validateSender(e.senderFrame, event); return cb(e, ...args); diff --git a/src/main/utils/isPathInDirectory.ts b/src/main/utils/isPathInDirectory.ts new file mode 100644 index 000000000..b153b5756 --- /dev/null +++ b/src/main/utils/isPathInDirectory.ts @@ -0,0 +1,16 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { resolve, sep } from "path"; + +export function isPathInDirectory(filePath: string, directory: string) { + const resolvedPath = resolve(filePath); + const resolvedDirectory = resolve(directory); + + const normalizedDirectory = resolvedDirectory.endsWith(sep) ? resolvedDirectory : resolvedDirectory + sep; + + return resolvedPath.startsWith(normalizedDirectory) || resolvedPath === resolvedDirectory; +} diff --git a/src/main/utils/setAsDefaultProtocolClient.ts b/src/main/utils/setAsDefaultProtocolClient.ts new file mode 100644 index 000000000..c4b358052 --- /dev/null +++ b/src/main/utils/setAsDefaultProtocolClient.ts @@ -0,0 +1,29 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { execFile } from "child_process"; +import { app } from "electron"; + +export async function setAsDefaultProtocolClient(protocol: string) { + if (process.platform !== "linux") { + return app.setAsDefaultProtocolClient(protocol); + } + + // electron setAsDefaultProtocolClient uses xdg-settings instead of xdg-mime. + // xdg-settings had a bug where it would also register the app as a handler for text/html, + // aka become your browser. This bug was fixed years ago (xdg-utils 1.2.0) but Ubuntu ships + // 7 (YES, SEVEN) years out of date xdg-utils which STILL has the bug. + // FIXME: remove this workaround when Ubuntu updates their xdg-utils or electron switches to xdg-mime. + + const { CHROME_DESKTOP } = process.env; + if (!CHROME_DESKTOP) return false; + + return new Promise(resolve => { + execFile("xdg-mime", ["default", CHROME_DESKTOP, `x-scheme-handler/${protocol}`], err => { + resolve(err == null); + }); + }); +} diff --git a/src/main/utils/vencordLoader.ts b/src/main/utils/vencordLoader.ts index 85b611210..25748b370 100644 --- a/src/main/utils/vencordLoader.ts +++ b/src/main/utils/vencordLoader.ts @@ -6,9 +6,10 @@ import { mkdirSync } from "fs"; import { access, constants as FsConstants, writeFile } from "fs/promises"; +import { VENCORD_FILES_DIR } from "main/vencordFilesDir"; import { join } from "path"; -import { USER_AGENT, VENCORD_FILES_DIR } from "../constants"; +import { USER_AGENT } from "../constants"; import { downloadFile, fetchie } from "./http"; const API_BASE = "https://api.github.com"; diff --git a/src/main/vencordFilesDir.ts b/src/main/vencordFilesDir.ts new file mode 100644 index 000000000..8ef9ab8a2 --- /dev/null +++ b/src/main/vencordFilesDir.ts @@ -0,0 +1,13 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { join } from "path"; + +import { SESSION_DATA_DIR } from "./constants"; +import { State } from "./settings"; + +// this is in a separate file to avoid circular dependencies +export const VENCORD_FILES_DIR = State.store.vencordDir || join(SESSION_DATA_DIR, "vencordFiles"); diff --git a/src/main/vesktopProtocol.ts b/src/main/vesktopProtocol.ts new file mode 100644 index 000000000..c05f5febb --- /dev/null +++ b/src/main/vesktopProtocol.ts @@ -0,0 +1,25 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { app, protocol } from "electron"; + +import { handleVesktopAssetsProtocol } from "./userAssets"; +import { handleVesktopStaticProtocol } from "./vesktopStatic"; + +app.whenReady().then(() => { + protocol.handle("vesktop", async req => { + const url = new URL(req.url); + + switch (url.hostname) { + case "assets": + return handleVesktopAssetsProtocol(url.pathname, req); + case "static": + return handleVesktopStaticProtocol(url.pathname, req); + default: + return new Response(null, { status: 404 }); + } + }); +}); diff --git a/src/main/vesktopStatic.ts b/src/main/vesktopStatic.ts new file mode 100644 index 000000000..d5b4c47f5 --- /dev/null +++ b/src/main/vesktopStatic.ts @@ -0,0 +1,31 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { BrowserWindow, net } from "electron"; +import { join } from "path"; +import { pathToFileURL } from "url"; + +import { isPathInDirectory } from "./utils/isPathInDirectory"; + +const STATIC_DIR = join(__dirname, "..", "..", "static"); + +export async function handleVesktopStaticProtocol(path: string, req: Request) { + const fullPath = join(STATIC_DIR, path); + if (!isPathInDirectory(fullPath, STATIC_DIR)) { + return new Response(null, { status: 404 }); + } + + return net.fetch(pathToFileURL(fullPath).href); +} + +export function loadView(browserWindow: BrowserWindow, view: string, params?: URLSearchParams) { + const url = new URL(`vesktop://static/views/${view}`); + if (params) { + url.search = params.toString(); + } + + return browserWindow.loadURL(url.toString()); +} diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index 45d3ae0a8..fa0e3cf14 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -32,7 +32,9 @@ export const VesktopNative = { getVersion: () => sendSync(IpcEvents.GET_VERSION), setBadgeCount: (count: number) => invoke(IpcEvents.SET_BADGE_COUNT, count), supportsWindowsTransparency: () => sendSync(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY), - getEnableHardwareAcceleration: () => sendSync(IpcEvents.GET_ENABLE_HARDWARE_ACCELERATION) + getEnableHardwareAcceleration: () => sendSync(IpcEvents.GET_ENABLE_HARDWARE_ACCELERATION), + isOutdated: () => invoke(IpcEvents.UPDATER_IS_OUTDATED), + openUpdater: () => invoke(IpcEvents.UPDATER_OPEN) }, autostart: { isEnabled: () => sendSync(IpcEvents.AUTOSTART_ENABLED), @@ -42,7 +44,9 @@ export const VesktopNative = { fileManager: { showItemInFolder: (path: string) => invoke(IpcEvents.SHOW_ITEM_IN_FOLDER, path), getVencordDir: () => sendSync(IpcEvents.GET_VENCORD_DIR), - selectVencordDir: (value?: null) => invoke<"cancelled" | "invalid" | "ok">(IpcEvents.SELECT_VENCORD_DIR, value) + selectVencordDir: (value?: null) => invoke<"cancelled" | "invalid" | "ok">(IpcEvents.SELECT_VENCORD_DIR, value), + chooseUserAsset: (asset: string, value?: null) => + invoke<"cancelled" | "invalid" | "ok" | "failed">(IpcEvents.CHOOSE_USER_ASSET, asset, value) }, settings: { get: () => sendSync(IpcEvents.GET_SETTINGS), diff --git a/src/preload/typedIpc.ts b/src/preload/typedIpc.ts index 91129e0cf..ebb8975c4 100644 --- a/src/preload/typedIpc.ts +++ b/src/preload/typedIpc.ts @@ -5,12 +5,12 @@ */ import { ipcRenderer } from "electron"; -import { IpcEvents } from "shared/IpcEvents"; +import { IpcEvents, UpdaterIpcEvents } from "shared/IpcEvents"; -export function invoke(event: IpcEvents, ...args: any[]) { +export function invoke(event: IpcEvents | UpdaterIpcEvents, ...args: any[]) { return ipcRenderer.invoke(event, ...args) as Promise; } -export function sendSync(event: IpcEvents, ...args: any[]) { +export function sendSync(event: IpcEvents | UpdaterIpcEvents, ...args: any[]) { return ipcRenderer.sendSync(event, ...args) as T; } diff --git a/src/preload/updater.ts b/src/preload/updater.ts new file mode 100644 index 000000000..1881f653a --- /dev/null +++ b/src/preload/updater.ts @@ -0,0 +1,24 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { contextBridge, ipcRenderer } from "electron"; +import type { UpdateInfo } from "electron-updater"; +import { UpdaterIpcEvents } from "shared/IpcEvents"; + +import { invoke } from "./typedIpc"; + +contextBridge.exposeInMainWorld("VesktopUpdaterNative", { + getData: () => invoke(UpdaterIpcEvents.GET_DATA), + installUpdate: () => invoke(UpdaterIpcEvents.INSTALL), + onProgress: (cb: (percent: number) => void) => { + ipcRenderer.on(UpdaterIpcEvents.DOWNLOAD_PROGRESS, (_, percent: number) => cb(percent)); + }, + onError: (cb: (message: string) => void) => { + ipcRenderer.on(UpdaterIpcEvents.ERROR, (_, message: string) => cb(message)); + }, + snoozeUpdate: () => invoke(UpdaterIpcEvents.SNOOZE_UPDATE), + ignoreUpdate: () => invoke(UpdaterIpcEvents.IGNORE_UPDATE) +}); diff --git a/src/renderer/components/ScreenSharePicker.tsx b/src/renderer/components/ScreenSharePicker.tsx index 009b78cde..ea9a4a3d4 100644 --- a/src/renderer/components/ScreenSharePicker.tsx +++ b/src/renderer/components/ScreenSharePicker.tsx @@ -6,25 +6,17 @@ import "./screenSharePicker.css"; +import { classNameFactory } from "@vencord/types/api/Styles"; +import { FormSwitch } from "@vencord/types/components"; import { closeModal, Logger, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils"; import { onceReady } from "@vencord/types/webpack"; -import { - Button, - Card, - FluxDispatcher, - Forms, - Select, - Switch, - Text, - UserStore, - useState -} from "@vencord/types/webpack/common"; +import { Button, Card, FluxDispatcher, Forms, Select, Text, UserStore, useState } from "@vencord/types/webpack/common"; import { Node } from "@vencord/venmic"; import type { Dispatch, SetStateAction } from "react"; import { MediaEngineStore } from "renderer/common"; import { addPatch } from "renderer/patches/shared"; import { State, useSettings, useVesktopState } from "renderer/settings"; -import { classNameFactory, isLinux, isWindows } from "renderer/utils"; +import { isLinux, isWindows } from "renderer/utils"; const StreamResolutions = ["480", "720", "1080", "1440", "2160"] as const; const StreamFps = ["15", "30", "60"] as const; @@ -202,67 +194,64 @@ function AudioSettingsModal({ - (Settings.audio = { ...Settings.audio, workaround: v })} - value={Settings.audio?.workaround ?? false} - note={ + Work around an issue that causes the microphone to be shared instead of the correct audio. Only enable if you're experiencing this issue. } - > - Microphone Workaround - - (Settings.audio = { ...Settings.audio, onlySpeakers: v })} - value={Settings.audio?.onlySpeakers ?? true} - note={ + onChange={v => (Settings.audio = { ...Settings.audio, workaround: v })} + value={Settings.audio?.workaround ?? false} + /> + When sharing entire desktop audio, only share apps that play to a speaker. You may want to disable this when using "mix bussing". } - > - Only Speakers - - (Settings.audio = { ...Settings.audio, onlyDefaultSpeakers: v })} - value={Settings.audio?.onlyDefaultSpeakers ?? true} - note={ + onChange={v => (Settings.audio = { ...Settings.audio, onlySpeakers: v })} + value={Settings.audio?.onlySpeakers ?? true} + /> + When sharing entire desktop audio, only share apps that play to the default speakers. You may want to disable this when using "mix bussing". } - > - Only Default Speakers - - (Settings.audio = { ...Settings.audio, onlyDefaultSpeakers: v })} + value={Settings.audio?.onlyDefaultSpeakers ?? true} + /> + Exclude nodes that are intended to capture audio.} hideBorder onChange={v => (Settings.audio = { ...Settings.audio, ignoreInputMedia: v })} value={Settings.audio?.ignoreInputMedia ?? true} - note={<>Exclude nodes that are intended to capture audio.} - > - Ignore Inputs - - (Settings.audio = { ...Settings.audio, ignoreVirtual: v })} - value={Settings.audio?.ignoreVirtual ?? false} - note={ + /> + Exclude virtual nodes, such as nodes belonging to loopbacks. This might be useful when using "mix bussing". } - > - Ignore Virtual - - (Settings.audio = { ...Settings.audio, ignoreVirtual: v })} + value={Settings.audio?.ignoreVirtual ?? false} + /> + Exclude device nodes, such as nodes belonging to microphones or speakers.} hideBorder onChange={v => (Settings.audio = { @@ -272,22 +261,25 @@ function AudioSettingsModal({ }) } value={Settings.audio?.ignoreDevices ?? true} - note={<>Exclude device nodes, such as nodes belonging to microphones or speakers.} - > - Ignore Devices - - + Allow to select applications more granularly.} hideBorder onChange={value => { Settings.audio = { ...Settings.audio, granularSelect: value }; setAudioSources("None"); }} value={Settings.audio?.granularSelect ?? false} - note={<>Allow to select applications more granularly.} - > - Granular Selection - - + + Allow to select devices such as microphones. Requires Ignore Devices to be turned + off. + + } hideBorder onChange={value => { Settings.audio = { ...Settings.audio, deviceSelect: value }; @@ -295,15 +287,7 @@ function AudioSettingsModal({ }} value={Settings.audio?.deviceSelect ?? false} disabled={Settings.audio?.ignoreDevices} - note={ - <> - Allow to select devices such as microphones. Requires Ignore Devices to be turned - off. - - } - > - Device Selection - + /> ; @@ -41,7 +41,7 @@ function openDeveloperOptionsModal(settings: Settings) { Debugging -
+
+
+ ); +} diff --git a/src/renderer/components/settings/UserAssets.css b/src/renderer/components/settings/UserAssets.css new file mode 100644 index 000000000..622107a22 --- /dev/null +++ b/src/renderer/components/settings/UserAssets.css @@ -0,0 +1,31 @@ +.vcd-user-assets { + display: flex; + margin-block: 1em 2em; + flex-direction: column; + gap: 1em; +} + +.vcd-user-assets-asset { + display: flex; + margin-top: 0.5em; + align-items: center; + gap: 1em; +} + +.vcd-user-assets-actions { + display: grid; + width: 100%; + gap: 0.5em; + margin-bottom: auto; +} + +.vcd-user-assets-buttons { + display: flex; + gap: 0.5em; +} + +.vcd-user-assets-image { + height: 7.5em; + width: 7.5em; + object-fit: contain; +} \ No newline at end of file diff --git a/src/renderer/components/settings/UserAssets.tsx b/src/renderer/components/settings/UserAssets.tsx new file mode 100644 index 000000000..08c30c73f --- /dev/null +++ b/src/renderer/components/settings/UserAssets.tsx @@ -0,0 +1,106 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vencord contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./UserAssets.css"; + +import { FormSwitch } from "@vencord/types/components"; +import { + Margins, + ModalCloseButton, + ModalContent, + ModalHeader, + ModalRoot, + ModalSize, + openModal, + wordsFromCamel, + wordsToTitle +} from "@vencord/types/utils"; +import { Button, showToast, Text, useState } from "@vencord/types/webpack/common"; +import { UserAssetType } from "main/userAssets"; +import { useSettings } from "renderer/settings"; + +import { SettingsComponent } from "./Settings"; + +const CUSTOMIZABLE_ASSETS: UserAssetType[] = ["splash", "tray", "trayUnread"]; + +export const UserAssetsButton: SettingsComponent = () => { + return ; +}; + +function openAssetsModal() { + openModal(props => ( + + + + User Assets + + + + + +
+ {CUSTOMIZABLE_ASSETS.map(asset => ( + + ))} +
+
+
+ )); +} + +function Asset({ asset }: { asset: UserAssetType }) { + // cache busting + const [version, setVersion] = useState(Date.now()); + const settings = useSettings(); + + const isSplash = asset === "splash"; + const imageRendering = isSplash && settings.splashPixelated ? "pixelated" : "auto"; + + const onChooseAsset = (value?: null) => async () => { + const res = await VesktopNative.fileManager.chooseUserAsset(asset, value); + if (res === "ok") { + setVersion(Date.now()); + if (isSplash && value === null) { + settings.splashPixelated = false; + } + } else if (res === "failed") { + showToast("Something went wrong. Please try again"); + } + }; + + return ( +
+ + {wordsToTitle(wordsFromCamel(asset))} + +
+ +
+
+ + +
+ {isSplash && ( + (settings.splashPixelated = val)} + className={Margins.top16} + hideBorder + /> + )} +
+
+
+ ); +} diff --git a/src/renderer/components/settings/VesktopSettingsSwitch.tsx b/src/renderer/components/settings/VesktopSettingsSwitch.tsx index bd1c2fb07..98f9b716a 100644 --- a/src/renderer/components/settings/VesktopSettingsSwitch.tsx +++ b/src/renderer/components/settings/VesktopSettingsSwitch.tsx @@ -4,13 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { Switch } from "@vencord/types/webpack/common"; +import { FormSwitch } from "@vencord/types/components"; import { ComponentProps } from "react"; -export function VesktopSettingsSwitch(props: ComponentProps) { - return ( - - {props.children} - - ); +import { cl } from "./Settings"; + +export function VesktopSettingsSwitch(props: ComponentProps) { + return ; } diff --git a/src/renderer/components/settings/WindowsTransparencyControls.tsx b/src/renderer/components/settings/WindowsTransparencyControls.tsx index 3e864a7eb..9147ded75 100644 --- a/src/renderer/components/settings/WindowsTransparencyControls.tsx +++ b/src/renderer/components/settings/WindowsTransparencyControls.tsx @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { ErrorBoundary } from "@vencord/types/components"; import { Margins } from "@vencord/types/utils"; import { Forms, Select } from "@vencord/types/webpack/common"; @@ -13,35 +14,37 @@ export const WindowsTransparencyControls: SettingsComponent = ({ settings }) => if (!VesktopNative.app.supportsWindowsTransparency()) return null; return ( -
- Transparency Options - - Requires a full restart. You will need a theme that supports transparency for this to work. - + +
+ Transparency Options + + Requires a full restart. You will need a theme that supports transparency for this to work. + - (settings.transparencyOption = v)} + isSelected={v => v === settings.transparencyOption} + serialize={s => s} + /> +
+
); }; diff --git a/src/renderer/components/settings/settings.css b/src/renderer/components/settings/settings.css index 5ae9887b1..36e136ca2 100644 --- a/src/renderer/components/settings/settings.css +++ b/src/renderer/components/settings/settings.css @@ -31,4 +31,17 @@ .vcd-settings-switch { margin-bottom: 0; +} + +.vcd-settings-updater-card { + padding: 1em; + margin-bottom: 1em; + display: grid; + gap: 0.5em; + + border-radius: 8px; + background-color: var(--bg-secondary); + background: var(--background-feedback-warning); + border: 1px solid var(--info-warning-foreground); + color: var(--text-feedback-warning); } \ No newline at end of file diff --git a/src/renderer/utils.ts b/src/renderer/utils.ts index e72775cad..041fdeadd 100644 --- a/src/renderer/utils.ts +++ b/src/renderer/utils.ts @@ -19,26 +19,3 @@ const { platform } = navigator; export const isWindows = platform.startsWith("Win"); export const isMac = platform.startsWith("Mac"); export const isLinux = platform.startsWith("Linux"); - -type ClassNameFactoryArg = string | string[] | Record | false | null | undefined | 0 | ""; -/** - * @param prefix The prefix to add to each class, defaults to `""` - * @returns A classname generator function - * @example - * const cl = classNameFactory("plugin-"); - * - * cl("base", ["item", "editable"], { selected: null, disabled: true }) - * // => "plugin-base plugin-item plugin-editable plugin-disabled" - */ -export const classNameFactory = - (prefix: string = "") => - (...args: ClassNameFactoryArg[]) => { - const classNames = new Set(); - for (const arg of args) { - if (arg && typeof arg === "string") classNames.add(arg); - else if (Array.isArray(arg)) arg.forEach(name => classNames.add(name)); - else if (arg && typeof arg === "object") - Object.entries(arg).forEach(([name, value]) => value && classNames.add(name)); - } - return Array.from(classNames, name => prefix + name).join(" "); - }; diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index df083e266..65fa86776 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -27,9 +27,8 @@ export const enum IpcEvents { GET_VENCORD_DIR = "VCD_GET_VENCORD_DIR", SELECT_VENCORD_DIR = "VCD_SELECT_VENCORD_DIR", - UPDATER_GET_DATA = "VCD_UPDATER_GET_DATA", - UPDATER_DOWNLOAD = "VCD_UPDATER_DOWNLOAD", - UPDATE_IGNORE = "VCD_UPDATE_IGNORE", + UPDATER_IS_OUTDATED = "VCD_UPDATER_IS_OUTDATED", + UPDATER_OPEN = "VCD_UPDATER_OPEN", SPELLCHECK_GET_AVAILABLE_LANGUAGES = "VCD_SPELLCHECK_GET_AVAILABLE_LANGUAGES", SPELLCHECK_RESULT = "VCD_SPELLCHECK_RESULT", @@ -61,7 +60,17 @@ export const enum IpcEvents { KEYBIND_SET_KEYBINDS = "VCD_KEYBIND_SET_KEYBINDS", KEYBIND_GET_CURRENT_SHORTCUT = "VCD_KEYBIND_GET_CURRENT_SHORTCUT", - KEYBIND_NEEDS_XDP = "VCD_KEYBIND_NEEDS_XDP" + KEYBIND_NEEDS_XDP = "VCD_KEYBIND_NEEDS_XDP", + CHOOSE_USER_ASSET = "VCD_CHOOSE_USER_ASSET", +} + +export const enum UpdaterIpcEvents { + GET_DATA = "VCD_UPDATER_GET_DATA", + INSTALL = "VCD_UPDATER_INSTALL", + DOWNLOAD_PROGRESS = "VCD_UPDATER_DOWNLOAD_PROGRESS", + ERROR = "VCD_UPDATER_ERROR", + SNOOZE_UPDATE = "VCD_UPDATER_SNOOZE_UPDATE", + IGNORE_UPDATE = "VCD_UPDATER_IGNORE_UPDATE" } export const enum IpcCommands { diff --git a/src/shared/paths.ts b/src/shared/paths.ts index a935d825a..db5033a17 100644 --- a/src/shared/paths.ts +++ b/src/shared/paths.ts @@ -7,6 +7,4 @@ import { join } from "path"; export const STATIC_DIR = /* @__PURE__ */ join(__dirname, "..", "..", "static"); -export const VIEW_DIR = /* @__PURE__ */ join(STATIC_DIR, "views"); export const BADGE_DIR = /* @__PURE__ */ join(STATIC_DIR, "badges"); -export const ICON_PATH = /* @__PURE__ */ join(STATIC_DIR, "icon.png"); diff --git a/src/shared/settings.d.ts b/src/shared/settings.d.ts index 01c26e593..4b4a7e5cf 100644 --- a/src/shared/settings.d.ts +++ b/src/shared/settings.d.ts @@ -11,6 +11,7 @@ export interface Settings { transparencyOption?: "none" | "mica" | "tabbed" | "acrylic"; tray?: boolean; minimizeToTray?: boolean; + autoStartMinimized?: boolean; openLinksWithElectron?: boolean; staticTitle?: boolean; enableMenu?: boolean; @@ -27,6 +28,7 @@ export interface Settings { splashTheming?: boolean; splashColor?: string; splashBackground?: string; + splashPixelated?: boolean; spellCheckLanguages?: string[]; @@ -49,11 +51,16 @@ export interface State { maximized?: boolean; minimized?: boolean; windowBounds?: Rectangle; - displayId: int; firstLaunch?: boolean; steamOSLayoutVersion?: number; + linuxAutoStartEnabled?: boolean; vencordDir?: string; + + updater?: { + ignoredVersion?: string; + snoozeUntil?: number; + }; } diff --git a/src/shared/utils/millis.ts b/src/shared/utils/millis.ts new file mode 100644 index 000000000..056776858 --- /dev/null +++ b/src/shared/utils/millis.ts @@ -0,0 +1,12 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2025 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export const enum Millis { + SECOND = 1000, + MINUTE = 60 * SECOND, + HOUR = 60 * MINUTE, + DAY = 24 * HOUR +} diff --git a/static/icon.ico b/static/icon.ico deleted file mode 100644 index 1dc9894da..000000000 Binary files a/static/icon.ico and /dev/null differ diff --git a/static/icon.png b/static/icon.png deleted file mode 100644 index 027ab0b6b..000000000 Binary files a/static/icon.png and /dev/null differ diff --git a/static/shiggy.gif b/static/shiggy.gif deleted file mode 100644 index fc564900a..000000000 Binary files a/static/shiggy.gif and /dev/null differ diff --git a/static/splash.webp b/static/splash.webp new file mode 100644 index 000000000..c693eb595 Binary files /dev/null and b/static/splash.webp differ diff --git a/static/tray.png b/static/tray.png new file mode 100644 index 000000000..df7877513 Binary files /dev/null and b/static/tray.png differ diff --git a/static/tray/tray.png b/static/tray/tray.png new file mode 100644 index 000000000..f4b80d625 Binary files /dev/null and b/static/tray/tray.png differ diff --git a/static/tray/trayTemplate.png b/static/tray/trayTemplate.png new file mode 100644 index 000000000..6a34cf669 Binary files /dev/null and b/static/tray/trayTemplate.png differ diff --git a/static/tray/trayUnread.png b/static/tray/trayUnread.png new file mode 100644 index 000000000..4af928ce6 Binary files /dev/null and b/static/tray/trayUnread.png differ diff --git a/static/views/about.html b/static/views/about.html index 38ea463af..02688df58 100644 --- a/static/views/about.html +++ b/static/views/about.html @@ -1,7 +1,10 @@ + + + About Vesktop - +
- shiggy +

Loading Vesktop...

@@ -61,4 +66,4 @@ messageElement.textContent = message; }) }); - + \ No newline at end of file diff --git a/static/views/style.css b/static/views/style.css deleted file mode 100644 index 872cee750..000000000 --- a/static/views/style.css +++ /dev/null @@ -1,37 +0,0 @@ -:root { - --bg: white; - --fg: black; - --fg-secondary: #313338; - --fg-semi-trans: rgb(0 0 0 / 0.2); - --link: #006ce7; - --link-hover: #005bb5; -} - -@media (prefers-color-scheme: dark) { - :root { - --bg: hsl(223 6.7% 20.6%); - --fg: white; - --fg-secondary: #b5bac1; - --fg-semi-trans: rgb(255 255 255 / 0.2); - --link: #00a8fc; - --link-hover: #0086c3; - } -} - -body { - font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, - "Open Sans", "Helvetica Neue", sans-serif; - margin: 0; - padding: 0; - background: var(--bg); - color: var(--fg); -} - -a { - color: var(--link); - transition: color 0.2s linear; -} - -a:hover { - color: var(--link-hover); -} \ No newline at end of file diff --git a/static/views/updater.html b/static/views/updater.html deleted file mode 100644 index 7eaba43d2..000000000 --- a/static/views/updater.html +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - -
-
-

Update Available

-

There's a new update for Vesktop! Update now to get new fixes and features!

-

- Current: -
- Latest: -

- -

Changelog

-

Loading...

-
- -
- - -
- - -
-
-
- - - - - diff --git a/static/views/updater/index.html b/static/views/updater/index.html new file mode 100644 index 000000000..e9111001a --- /dev/null +++ b/static/views/updater/index.html @@ -0,0 +1,64 @@ + + + + + Vesktop Updater + + + + + + + +
+

An update is available!

+
+ +
+

+ Current version: + New version: +

+

Release Notes

+ +
+
+ +
+
+ + + +
+
+ + +

Downloading Update

+

+ Please wait while the update is being downloaded. Once the update finished downloading, it will + automatically install and Vesktop will restart. +

+ + + + + +

+
+ + +

Installing Update

+

Please wait while the update is being installed. Vesktop will restart shortly.

+ +
+
+
+
+ + + \ No newline at end of file diff --git a/static/views/updater/script.js b/static/views/updater/script.js new file mode 100644 index 000000000..2af79bce4 --- /dev/null +++ b/static/views/updater/script.js @@ -0,0 +1,69 @@ +const { update, version: currentVersion } = await VesktopUpdaterNative.getData(); + +document.getElementById("current-version").textContent = currentVersion; +document.getElementById("new-version").textContent = update.version; +document.getElementById("release-notes").innerHTML = update.releaseNotes + .map( + ({ version, note: html }) => ` +
+

Version ${version}

+
${html.replace(/<\/?h([1-3])/g, (m, level) => m.replace(level, Number(level) + 3))}
+
+ ` + ) + .join("\n"); + +document.querySelectorAll("a").forEach(a => { + a.target = "_blank"; +}); + +// remove useless headings +document.querySelectorAll("h3, h4, h5, h6").forEach(h => { + if (h.textContent.trim().toLowerCase() === "what's changed") { + h.remove(); + } +}); + +/** @type {HTMLDialogElement} */ +const updateDialog = document.getElementById("update-dialog"); +/** @type {HTMLDialogElement} */ +const installingDialog = document.getElementById("installing-dialog"); +/** @type {HTMLProgressElement} */ +const downloadProgress = document.getElementById("download-progress"); +/** @type {HTMLElement} */ +const errorText = document.getElementById("error"); + +document.getElementById("update-button").addEventListener("click", () => { + downloadProgress.value = 0; + errorText.textContent = ""; + + if (navigator.platform.startsWith("Linux")) { + document.getElementById("linux-note").classList.remove("hidden"); + } + + updateDialog.showModal(); + + VesktopUpdaterNative.installUpdate().then(() => { + downloadProgress.value = 100; + updateDialog.closedBy = "any"; + + installingDialog.showModal(); + updateDialog.classList.add("hidden"); + }); +}); + +document.getElementById("later-button").addEventListener("click", () => VesktopUpdaterNative.snoozeUpdate()); +document.getElementById("ignore-button").addEventListener("click", () => { + const confirmed = confirm( + "Are you sure you want to ignore this update? You will not be notified about this update again. Updates are important for security and stability." + ); + if (confirmed) VesktopUpdaterNative.ignoreUpdate(); +}); + +VesktopUpdaterNative.onProgress(percent => (downloadProgress.value = percent)); +VesktopUpdaterNative.onError(message => { + updateDialog.closedBy = "any"; + errorText.textContent = `An error occurred while downloading the update: ${message}`; + installingDialog.close(); + updateDialog.classList.remove("hidden"); +}); diff --git a/static/views/updater/style.css b/static/views/updater/style.css new file mode 100644 index 000000000..97db89f96 --- /dev/null +++ b/static/views/updater/style.css @@ -0,0 +1,147 @@ +html, +body { + height: 100%; + margin: 0; + box-sizing: border-box; +} + +body { + padding: 2em; + display: flex; + flex-direction: column; + height: 100vh; + + header, + footer { + flex: 0 0 auto; + } + + main { + flex: 1 1 0%; + min-height: 0; + overflow: auto; + } + + footer { + margin-top: 1em; + } +} + + +#versions { + display: inline-grid; + grid-template-columns: auto 1fr; + column-gap: 0.5em; + + .label { + white-space: nowrap; + } + + .value { + text-align: left; + } + + #current-version { + color: #fc8f8a; + } + + #new-version { + color: #71c07f; + } +} + +#release-notes { + display: grid; + gap: 1em; +} + +#buttons { + display: flex; + gap: 0.5em; + justify-content: end; +} + +button { + cursor: pointer; + padding: 0.5rem 1rem; + font-size: 1.2em; + color: var(--fg); + border: none; + border-radius: 3px; + font-weight: bold; + transition: filter 0.2 ease-in-out; +} + +button:hover, +button:active { + filter: brightness(0.9); +} + +.green { + background-color: #248046; +} + +.grey { + background-color: rgba(151, 151, 159, 0.12); +} + +.hidden { + display: none; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0 0 0.5em 0; +} + +h1 { + text-align: center; +} + +h2 { + margin-bottom: 1em; +} + +dialog { + width: 80%; + padding: 2em; +} + +progress { + width: 100%; + height: 1.5em; + margin-top: 1em; +} + +#error { + color: red; + font-weight: bold; +} + +.spinner-wrapper { + display: flex; + justify-content: center; + align-items: center; + margin-top: 2em; +} + +.spinner { + width: 48px; + height: 48px; + border: 5px solid var(--fg); + border-bottom-color: transparent; + border-radius: 50%; + display: inline-block; + box-sizing: border-box; + animation: rotation 1s linear infinite; +} + +@keyframes rotation { + to { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 4e23ba29a..22551bd2a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "target": "ESNEXT", "jsx": "preserve", - // we have duplicate electron types but it's w/e + // @vencord/types has some errors for now "skipLibCheck": true, "baseUrl": "./src/",