diff --git a/CHANGELOG.md b/CHANGELOG.md index 57da83f..f88cdb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## [2.1.0] - 2026-04-08 + +### Features +- **Request Payment**: Users can now request Bitcoin payments from other identities via the Payments page + - New "Request Payment" tab with recipient search, amount, description, and configurable expiry + - New "Incoming Requests" tab showing payment request cards with Pay/Decline actions + - Editable payment amount — payer can modify the amount before fulfilling + - Optional notes on payments and declines + - Outgoing request tracker with live status updates (Pending/Paid/Declined/Expired/Cancelled) + - Cancel pending outgoing requests +- **Request Security Settings** (in Incoming Requests tab): + - Identity whitelist with on/off toggle — only whitelisted identities can send requests when enabled + - Identity search integration for adding to whitelist + - Configurable min/max amount limits to filter dust and oversized requests +- Payments page reorganized into 4 full-width tabs: Send Payment, Request Payment, Incoming Requests, Pending Payments + +### Dependencies +- `@bsv/message-box-client` ^2.0.6 (requires >=2.0.7 with payment request methods once published) ## [2.0.9] - 2026-03-18 ### Features diff --git a/package-lock.json b/package-lock.json index 74bfd6d..e11a031 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bsv-desktop-electron", - "version": "2.0.9", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bsv-desktop-electron", - "version": "2.0.9", + "version": "2.1.0", "license": "Apache-2.0", "dependencies": { "@bsv/amountinator": "^2.0.1", @@ -14,16 +14,17 @@ "@bsv/btms-permission-module": "^1.0.1", "@bsv/btms-permission-module-ui": "^1.0.0", "@bsv/identity-react": "^1.1.14", - "@bsv/message-box-client": "^2.0.4", - "@bsv/sdk": "^2.0.7", + "@bsv/message-box-client": "^2.0.7", + "@bsv/sdk": "^2.0.13", "@bsv/uhrp-react": "^1.0.6", - "@bsv/wallet-toolbox": "^2.1.5", - "@bsv/wallet-toolbox-client": "^2.1.5", + "@bsv/wallet-toolbox": "^2.1.19", + "@bsv/wallet-toolbox-client": "^2.1.19", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.4.8", "@mui/material": "^6.4.8", "@mui/styles": "^6.4.8", + "better-sqlite3": "^12.8.0", "cors": "^2.8.6", "date-fns": "^4.1.0", "dotenv": "^17.2.4", @@ -56,7 +57,7 @@ "@types/react-router-dom": "^5.3.3", "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", - "electron": "^38.8.0", + "electron": "^41.2.0", "electron-builder": "^26.0.12", "typescript": "~5.6.2", "vite": "^6.3.6", @@ -750,13 +751,13 @@ } }, "node_modules/@bsv/message-box-client": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@bsv/message-box-client/-/message-box-client-2.0.4.tgz", - "integrity": "sha512-XvnNrchW6gZB1U3pskIld8rie8ds2ifxrOhfO4Ot+/kHaMm8osYky7KK6cxCnIdPtOLc8G1yiV41M13p+BzY/A==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@bsv/message-box-client/-/message-box-client-2.0.7.tgz", + "integrity": "sha512-POhAoG0daA/+E0jUJLZLysvNDjDpMtwsD3GBaY/SyqR26euqIQMSJjrnCDnjhPWO7Et34rcaJNa46mllXgxENw==", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@bsv/authsocket-client": "^2.0.2", - "@bsv/sdk": "^2.0.4" + "@bsv/sdk": "^2.0.11" } }, "node_modules/@bsv/payment-express-middleware": { @@ -1076,9 +1077,9 @@ } }, "node_modules/@bsv/sdk": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@bsv/sdk/-/sdk-2.0.7.tgz", - "integrity": "sha512-zh3DtbGIAeojf/TYlhVTIOIFeUWboq7jyWSEmiPmKxp8MnprZM+jino1Lm0KbjEnwn56pAPCZt8eb+5tTWtHaA==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@bsv/sdk/-/sdk-2.0.13.tgz", + "integrity": "sha512-VV25sV6oB1Piu7tTEMta+B29IRcq7Xpj1ohuk8RZRT656onop1YbA3oTnYf6stleWnbvsZeOciwH7Xppvs1QWw==", "license": "SEE LICENSE IN LICENSE.txt" }, "node_modules/@bsv/uhrp-react": { @@ -1095,16 +1096,17 @@ } }, "node_modules/@bsv/wallet-toolbox": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@bsv/wallet-toolbox/-/wallet-toolbox-2.1.5.tgz", - "integrity": "sha512-0VAgT1O16iqwQzyps5cmYzZf7rmVc7bhn1KF4yf+1crUu+30VHAqAjms7o5IGPsHDWSOfU8WwPrDXgMqFVYQUQ==", + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/@bsv/wallet-toolbox/-/wallet-toolbox-2.1.19.tgz", + "integrity": "sha512-KTKh5GHHIByPJ/HerVnQMkMeX+an5naIH0YYk+0D1NFF+dYzxl9iZzMdArpVVR7/t3Z/4k0Ey1F9Yy3qDCiycA==", "license": "SEE LICENSE IN license.md", "dependencies": { "@bsv/auth-express-middleware": "^2.0.4", "@bsv/payment-express-middleware": "^2.0.1", - "@bsv/sdk": "^2.0.6", + "@bsv/sdk": "^2.0.13", "better-sqlite3": "^12.6.2", "express": "^4.21.2", + "hash-wasm": "^4.12.0", "idb": "^8.0.2", "knex": "^3.1.0", "mysql2": "^3.12.0", @@ -1112,12 +1114,13 @@ } }, "node_modules/@bsv/wallet-toolbox-client": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@bsv/wallet-toolbox-client/-/wallet-toolbox-client-2.1.5.tgz", - "integrity": "sha512-LZQpyQ2tCQF0ZCr8X5HkcxMTMdc/SU+IiEQ7xGBKajuDOXX27VPm6qe/cV9lNCgkV2fhArJqt/65LIMI3B6uZw==", + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/@bsv/wallet-toolbox-client/-/wallet-toolbox-client-2.1.19.tgz", + "integrity": "sha512-uGHRUqRZsB/N3BwW7AFEXPAuap24JzPFG4hOUbtKmsLfUNnpjwMaLmAIYKJqmBPwkp7/f9UmNURxb1YgvJuQKQ==", "license": "SEE LICENSE IN license.md", "dependencies": { - "@bsv/sdk": "^2.0.0", + "@bsv/sdk": "^2.0.13", + "hash-wasm": "^4.12.0", "idb": "^8.0.2" } }, @@ -1158,9 +1161,9 @@ } }, "node_modules/@electron/asar/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -1415,9 +1418,9 @@ } }, "node_modules/@electron/rebuild/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -1448,16 +1451,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@electron/rebuild/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/@electron/rebuild/node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", @@ -1534,13 +1527,13 @@ } }, "node_modules/@electron/rebuild/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -1590,19 +1583,6 @@ "encoding": "^0.1.13" } }, - "node_modules/@electron/rebuild/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/@electron/rebuild/node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -1690,23 +1670,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@electron/rebuild/node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@electron/rebuild/node_modules/unique-filename": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", @@ -1749,16 +1712,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@electron/rebuild/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/@electron/universal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", @@ -1779,9 +1732,9 @@ } }, "node_modules/@electron/universal/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -1817,13 +1770,13 @@ } }, "node_modules/@electron/universal/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -2496,29 +2449,6 @@ "node": ">=18" } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", - "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3149,9 +3079,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", - "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", "cpu": [ "arm" ], @@ -3163,9 +3093,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", - "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", "cpu": [ "arm64" ], @@ -3177,9 +3107,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", - "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", "cpu": [ "arm64" ], @@ -3191,9 +3121,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", - "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", "cpu": [ "x64" ], @@ -3205,9 +3135,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", - "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", "cpu": [ "arm64" ], @@ -3219,9 +3149,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", - "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", "cpu": [ "x64" ], @@ -3233,9 +3163,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", - "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", "cpu": [ "arm" ], @@ -3247,9 +3177,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", - "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", "cpu": [ "arm" ], @@ -3261,9 +3191,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", - "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", "cpu": [ "arm64" ], @@ -3275,9 +3205,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", - "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", "cpu": [ "arm64" ], @@ -3289,9 +3219,23 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", - "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", "cpu": [ "loong64" ], @@ -3303,9 +3247,23 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", - "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", "cpu": [ "ppc64" ], @@ -3317,9 +3275,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", - "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", "cpu": [ "riscv64" ], @@ -3331,9 +3289,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", - "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", "cpu": [ "riscv64" ], @@ -3345,9 +3303,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", - "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", "cpu": [ "s390x" ], @@ -3359,9 +3317,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", - "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", "cpu": [ "x64" ], @@ -3373,9 +3331,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", - "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", "cpu": [ "x64" ], @@ -3386,10 +3344,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", - "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", "cpu": [ "arm64" ], @@ -3401,9 +3373,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", - "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", "cpu": [ "arm64" ], @@ -3415,9 +3387,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", - "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", "cpu": [ "ia32" ], @@ -3429,9 +3401,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", - "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", "cpu": [ "x64" ], @@ -3443,9 +3415,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", - "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", "cpu": [ "x64" ], @@ -4061,9 +4033,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", - "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz", + "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==", "dev": true, "license": "MIT", "engines": { @@ -4101,9 +4073,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -4258,16 +4230,6 @@ "semver": "bin/semver.js" } }, - "node_modules/app-builder-lib/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/app-builder-lib/node_modules/ci-info": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", @@ -4345,29 +4307,6 @@ "node": ">=16" } }, - "node_modules/app-builder-lib/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/app-builder-lib/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/app-builder-lib/node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", @@ -4381,23 +4320,6 @@ "node": ">=10" } }, - "node_modules/app-builder-lib/node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/app-builder-lib/node_modules/which": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", @@ -4414,16 +4336,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/app-builder-lib/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4564,9 +4476,9 @@ } }, "node_modules/better-sqlite3": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", - "integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==", + "version": "12.8.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.8.0.tgz", + "integrity": "sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -4687,9 +4599,9 @@ "optional": true }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -5323,9 +5235,9 @@ } }, "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", "license": "ISC", "engines": { "node": ">= 6" @@ -5601,9 +5513,9 @@ } }, "node_modules/dir-compare/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -5790,15 +5702,15 @@ } }, "node_modules/electron": { - "version": "38.8.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-38.8.2.tgz", - "integrity": "sha512-EvXHgoy7NJ/O1mg6ZIEq5jgYnm1LHy4dNDMh/jhiJz6rAOH9VKDfXhx7Y65LYaTyntukNQhS90KBY3zk4t4+ow==", + "version": "41.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-41.2.0.tgz", + "integrity": "sha512-0OKLiymqfV0WK68RBXqAm3Myad2TpI5wwxLCBEUcH5Nugo3YfSk7p1Js/AL9266qTz5xZioUnxt9hG8FFwax0g==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^22.7.7", + "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { @@ -6073,6 +5985,23 @@ "node": ">=6 <7 || >=8" } }, + "node_modules/electron/node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/electron/node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -6532,9 +6461,9 @@ } }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -6542,9 +6471,9 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, "license": "ISC", "dependencies": { @@ -6869,9 +6798,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -7027,6 +6956,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash-wasm": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.12.0.tgz", + "integrity": "sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ==", + "license": "MIT" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -7546,9 +7481,9 @@ } }, "node_modules/jest-util/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -7859,9 +7794,9 @@ "license": "MIT" }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.escaperegexp": { @@ -8317,9 +8252,9 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -8383,21 +8318,44 @@ } }, "node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimatch/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -8466,6 +8424,29 @@ "dev": true, "license": "ISC" }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -8663,9 +8644,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -8943,9 +8924,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, "node_modules/path-type": { @@ -9009,9 +8990,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -9260,9 +9241,9 @@ } }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -9665,9 +9646,9 @@ } }, "node_modules/rollup": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", - "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "dev": true, "license": "MIT", "dependencies": { @@ -9681,28 +9662,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.3", - "@rollup/rollup-android-arm64": "4.52.3", - "@rollup/rollup-darwin-arm64": "4.52.3", - "@rollup/rollup-darwin-x64": "4.52.3", - "@rollup/rollup-freebsd-arm64": "4.52.3", - "@rollup/rollup-freebsd-x64": "4.52.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", - "@rollup/rollup-linux-arm-musleabihf": "4.52.3", - "@rollup/rollup-linux-arm64-gnu": "4.52.3", - "@rollup/rollup-linux-arm64-musl": "4.52.3", - "@rollup/rollup-linux-loong64-gnu": "4.52.3", - "@rollup/rollup-linux-ppc64-gnu": "4.52.3", - "@rollup/rollup-linux-riscv64-gnu": "4.52.3", - "@rollup/rollup-linux-riscv64-musl": "4.52.3", - "@rollup/rollup-linux-s390x-gnu": "4.52.3", - "@rollup/rollup-linux-x64-gnu": "4.52.3", - "@rollup/rollup-linux-x64-musl": "4.52.3", - "@rollup/rollup-openharmony-arm64": "4.52.3", - "@rollup/rollup-win32-arm64-msvc": "4.52.3", - "@rollup/rollup-win32-ia32-msvc": "4.52.3", - "@rollup/rollup-win32-x64-gnu": "4.52.3", - "@rollup/rollup-win32-x64-msvc": "4.52.3", + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" } }, @@ -9723,9 +9707,9 @@ } }, "node_modules/router/node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", @@ -10159,9 +10143,9 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz", - "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", + "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", @@ -10440,6 +10424,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", + "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/tar-fs": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", @@ -10468,6 +10469,36 @@ "node": ">=6" } }, + "node_modules/tar/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/tarn": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", @@ -10937,9 +10968,9 @@ } }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 6adc0fc..983dd5f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bsv-desktop-electron", - "version": "2.0.9", + "version": "2.1.0", "description": "BSV Desktop Wallet - Electron Edition", "main": "dist-electron/main.js", "type": "module", @@ -28,16 +28,17 @@ "@bsv/btms-permission-module": "^1.0.1", "@bsv/btms-permission-module-ui": "^1.0.0", "@bsv/identity-react": "^1.1.14", - "@bsv/message-box-client": "^2.0.4", - "@bsv/sdk": "^2.0.7", + "@bsv/message-box-client": "^2.0.7", + "@bsv/sdk": "^2.0.13", "@bsv/uhrp-react": "^1.0.6", - "@bsv/wallet-toolbox": "^2.1.5", - "@bsv/wallet-toolbox-client": "^2.1.5", + "@bsv/wallet-toolbox": "^2.1.19", + "@bsv/wallet-toolbox-client": "^2.1.19", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^6.4.8", "@mui/material": "^6.4.8", "@mui/styles": "^6.4.8", + "better-sqlite3": "^12.8.0", "cors": "^2.8.6", "date-fns": "^4.1.0", "dotenv": "^17.2.4", @@ -70,7 +71,7 @@ "@types/react-router-dom": "^5.3.3", "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.2.1", - "electron": "^38.8.0", + "electron": "^41.2.0", "electron-builder": "^26.0.12", "typescript": "~5.6.2", "vite": "^6.3.6", diff --git a/src/lib/WalletContext.tsx b/src/lib/WalletContext.tsx index a7ab668..dc357fe 100644 --- a/src/lib/WalletContext.tsx +++ b/src/lib/WalletContext.tsx @@ -2209,12 +2209,26 @@ export const WalletContextProvider: React.FC = ({ }, [peerPayClient, checkAnointmentStatus]); const logout = useCallback(() => { + // Preserve payment request data across logout (keyed by identity). + const preservedKeys: Record = {} + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i) + if (key && key.startsWith('payReq_')) { + preservedKeys[key] = localStorage.getItem(key) ?? '' + } + } + // Clear localStorage to prevent auto-login localStorage.clear(); if (localStorage.snap) { localStorage.removeItem('snap'); } + // Restore preserved payment request data + for (const [key, value] of Object.entries(preservedKeys)) { + localStorage.setItem(key, value) + } + // Reset manager state setManagers({}); diff --git a/src/lib/pages/Dashboard/Payments/IncomingRequestList.tsx b/src/lib/pages/Dashboard/Payments/IncomingRequestList.tsx new file mode 100644 index 0000000..17f755a --- /dev/null +++ b/src/lib/pages/Dashboard/Payments/IncomingRequestList.tsx @@ -0,0 +1,561 @@ +// src/lib/pages/Dashboard/Payments/IncomingRequestList.tsx +import React, { useCallback, useContext, useEffect, useState } from 'react' +import { + Alert, + Autocomplete, + Avatar, + Box, + Button, + Chip, + CircularProgress, + Collapse, + Divider, + IconButton, + List, + ListItem, + ListItemText, + Paper, + Stack, + Switch, + TextField, + Typography, +} from '@mui/material' +import DeleteIcon from '@mui/icons-material/Delete' +import { PublicKey, WalletInterface } from '@bsv/sdk' +import { WalletContext } from '../../../WalletContext' +import { IncomingPaymentRequest } from '@bsv/message-box-client' +import { toast } from 'react-toastify' +import { useIdentitySearch } from '@bsv/identity-react' + +/* ------------------------------------------------------------------ */ +/* Helpers */ +/* ------------------------------------------------------------------ */ + +function getInitials(identityKey: string): string { + return identityKey.slice(0, 2).toUpperCase() +} + +function truncateKey(key: string, chars = 14): string { + if (key.length <= chars + 3) return key + return `${key.slice(0, chars)}…` +} + +function formatTimeRemaining(expiresAt: number): string { + const diff = expiresAt - Date.now() + if (diff <= 0) return 'Expired' + const hours = Math.floor(diff / (1000 * 60 * 60)) + const days = Math.floor(hours / 24) + if (days > 0) return `${days}d ${hours % 24}h remaining` + const mins = Math.floor(diff / (1000 * 60)) + if (hours > 0) return `${hours}h ${mins % 60}m remaining` + return `${mins}m remaining` +} + +/* ------------------------------------------------------------------ */ +/* Types */ +/* ------------------------------------------------------------------ */ + +type Props = { + requests: IncomingPaymentRequest[] + onRefresh: () => void + wallet: WalletInterface +} + +type LoadingMap = Record + +/* ------------------------------------------------------------------ */ +/* Component */ +/* ------------------------------------------------------------------ */ + +export default function IncomingRequestList({ requests, onRefresh, wallet }: Props) { + const { peerPayClient, messageBoxUrl, adminOriginator, activeProfile } = useContext(WalletContext) + + // Storage keys scoped to the current user's identity to prevent cross-account overwrites. + const idSuffix = activeProfile?.identityKey ? `_${activeProfile.identityKey}` : '' + const whitelistKey = `payReq_whitelist${idSuffix}` + const minAmountKey = `payReq_minAmount${idSuffix}` + const maxAmountKey = `payReq_maxAmount${idSuffix}` + const whitelistEnabledKey = `payReq_whitelistEnabled${idSuffix}` + + // Per-card state + const [notes, setNotes] = useState>({}) + const [paying, setPaying] = useState({}) + const [declining, setDeclining] = useState({}) + + // Settings panel + const [settingsOpen, setSettingsOpen] = useState(false) + const [permissions, setPermissions] = useState(() => { + try { + return JSON.parse(localStorage.getItem(whitelistKey) ?? '[]') + } catch { return [] } + }) + const [whitelistKeyInput, setWhitelistKeyInput] = useState('') + const [allowError, setAllowError] = useState('') + const [minAmount, setMinAmount] = useState(() => + localStorage.getItem(minAmountKey) ?? '1000' + ) + const [maxAmount, setMaxAmount] = useState(() => + localStorage.getItem(maxAmountKey) ?? '10000000' + ) + const [limitsSaved, setLimitsSaved] = useState(false) + + // Whitelist toggle (on/off) + const [whitelistEnabled, setWhitelistEnabled] = useState(() => + localStorage.getItem(whitelistEnabledKey) !== 'false' + ) + + // Identity search for whitelist + const whitelistIdentitySearch = useIdentitySearch({ + originator: adminOriginator, + wallet, + onIdentitySelected: (identity) => { + if (identity) { + setWhitelistKeyInput(identity.identityKey) + } + } + }) + + /* ---------------------------------------------------------------- */ + /* Settings helpers */ + /* ---------------------------------------------------------------- */ + + /** Persist the whitelist to localStorage. */ + const saveWhitelist = useCallback((list: string[]) => { + setPermissions(list) + localStorage.setItem(whitelistKey, JSON.stringify(list)) + }, [whitelistKey]) + + const handleToggleSettings = () => { + setSettingsOpen(prev => !prev) + } + + const handleToggleWhitelist = (enabled: boolean) => { + setWhitelistEnabled(enabled) + localStorage.setItem(whitelistEnabledKey, String(enabled)) + } + + const handleAllow = async () => { + const key = whitelistKeyInput.trim() + if (!key) { setAllowError('Identity key is required'); return } + if (permissions.includes(key)) { setAllowError('Identity already whitelisted'); return } + setAllowError('') + + // Best-effort server-side permission set (may not persist on all servers). + if (peerPayClient) { + try { + await peerPayClient.allowPaymentRequestsFrom({ identityKey: key }) + } catch (e) { + console.warn('[IncomingRequestList] Server-side allow failed (non-blocking):', e) + } + } + + // Authoritative: persist in localStorage. + saveWhitelist([...permissions, key]) + setWhitelistKeyInput('') + whitelistIdentitySearch.handleSelect(null, null) + toast.success('Identity whitelisted') + } + + const handleBlock = async (identityKey: string) => { + // Best-effort server-side block. + if (peerPayClient) { + try { + await peerPayClient.blockPaymentRequestsFrom({ identityKey }) + } catch (e) { + console.warn('[IncomingRequestList] Server-side block failed (non-blocking):', e) + } + } + + // Authoritative: remove from localStorage. + saveWhitelist(permissions.filter(k => k !== identityKey)) + toast.success('Identity removed from whitelist') + } + + const saveLimits = () => { + localStorage.setItem(minAmountKey, minAmount) + localStorage.setItem(maxAmountKey, maxAmount) + setLimitsSaved(true) + setTimeout(() => setLimitsSaved(false), 2000) + toast.success('Amount limits saved') + onRefresh() + } + + /* ---------------------------------------------------------------- */ + /* Pay / Decline */ + /* ---------------------------------------------------------------- */ + + const handlePay = async (req: IncomingPaymentRequest) => { + if (!peerPayClient) return + const id = req.messageId + setPaying(prev => ({ ...prev, [id]: true })) + try { + const note = notes[id] || undefined + await peerPayClient.fulfillPaymentRequest( + { request: req, note }, + messageBoxUrl || undefined + ) + window.dispatchEvent(new CustomEvent('balance-changed')) + toast.success('Payment sent!') + onRefresh() + } catch (e) { + toast.error((e as Error)?.message ?? 'Failed to send payment') + } finally { + setPaying(prev => { const n = { ...prev }; delete n[id]; return n }) + } + } + + const handleDecline = async (req: IncomingPaymentRequest) => { + if (!peerPayClient) return + const id = req.messageId + setDeclining(prev => ({ ...prev, [id]: true })) + try { + const note = notes[id] || undefined + await peerPayClient.declinePaymentRequest( + { request: req, note }, + messageBoxUrl || undefined + ) + toast.info('Request declined') + onRefresh() + } catch (e) { + toast.error((e as Error)?.message ?? 'Failed to decline request') + } finally { + setDeclining(prev => { const n = { ...prev }; delete n[id]; return n }) + } + } + + /* ---------------------------------------------------------------- */ + /* Render */ + /* ---------------------------------------------------------------- */ + + return ( + + {/* ---- Settings panel ---- */} + + + Incoming Requests + + + + + + + + + + + {/* Whitelist toggle */} + + + + Identity Whitelist + + + When enabled, only whitelisted identities can send you requests + + + + + handleToggleWhitelist(e.target.checked)} + /> + + + + {/* Whitelist management — only shown when enabled */} + + + {/* Identity search for adding */} + { + whitelistIdentitySearch.handleSelect(event, value as any) + if (value && typeof value !== 'string') { + setWhitelistKeyInput(value.identityKey) + } else { + setWhitelistKeyInput('') + } + }} + getOptionLabel={(option) => + typeof option === 'string' ? option : option.name || option.identityKey.slice(0, 16) + } + isOptionEqualToValue={(option, value) => { + if (typeof option === 'string' || typeof value === 'string') return false + return option.identityKey === value.identityKey + }} + renderInput={(params) => ( + + )} + renderOption={(props, option) => { + if (typeof option === 'string') return null + const { key, ...otherProps } = props + return ( +
  • + + + {getInitials(option.identityKey)} + + + {option.name || 'Unknown'} + + {option.identityKey.slice(0, 20)}... + + + +
  • + ) + }} + noOptionsText={whitelistIdentitySearch.inputValue ? 'No identities found' : 'Start typing to search'} + fullWidth + size="small" + /> + + {/* Direct key input fallback */} + + { + const val = e.target.value.trim() + setWhitelistKeyInput(val) + setAllowError('') + if (!val) return + try { + PublicKey.fromString(val) + whitelistIdentitySearch.handleSelect(null, null) + } catch { + // Not a valid key yet — allow typing + } + }} + disabled={!!whitelistIdentitySearch.selectedIdentity} + error={!!allowError} + helperText={allowError} + /> + + + + {/* Whitelisted identities list */} + {permissions.length === 0 ? ( + + No whitelisted identities yet. Add identities above to receive payment requests. + + ) : ( + + {permissions.map(key => ( + handleBlock(key)} + color="error" + > + + + } + > + + {truncateKey(key, 24)} + + } + secondary={ + + {key} + + } + /> + + ))} + + )} +
    +
    + + + + {/* Amount limits */} + + Amount Limits (satoshis) + + + setMinAmount(e.target.value)} + fullWidth + /> + setMaxAmount(e.target.value)} + fullWidth + /> + + +
    +
    +
    + + {/* ---- Request cards ---- */} + {requests.length === 0 ? ( + + No incoming payment requests. + + ) : ( + requests.map(req => { + const id = req.messageId + const isExpired = req.expiresAt < Date.now() + const isPaying = !!paying[id] + const isDeclining = !!declining[id] + const isBusy = isPaying || isDeclining + + return ( + + + {/* Header row */} + + + {getInitials(req.sender)} + + + + {truncateKey(req.sender)} + + + {formatTimeRemaining(req.expiresAt)} + + + + + + {/* Amount — prominent */} + + {req.amount.toLocaleString()} sats + + + {/* Description quote block */} + + + "{req.description}" + + + + {!isExpired && ( + <> + {/* Optional note */} + setNotes(prev => ({ ...prev, [id]: e.target.value }))} + disabled={isBusy} + /> + + {/* Actions */} + + + + + + )} + + {isExpired && ( + + This request has expired and can no longer be fulfilled. + + )} + + + ) + }) + )} +
    + ) +} diff --git a/src/lib/pages/Dashboard/Payments/RequestPaymentForm.tsx b/src/lib/pages/Dashboard/Payments/RequestPaymentForm.tsx new file mode 100644 index 0000000..9c6a547 --- /dev/null +++ b/src/lib/pages/Dashboard/Payments/RequestPaymentForm.tsx @@ -0,0 +1,585 @@ +// src/lib/pages/Dashboard/Payments/RequestPaymentForm.tsx +import React, { useCallback, useContext, useEffect, useRef, useState } from 'react' +import { + Autocomplete, + Avatar, + Box, + Button, + Chip, + CircularProgress, + FormControl, + InputAdornment, + InputLabel, + MenuItem, + Paper, + Select, + Stack, + TextField, + Typography, +} from '@mui/material' +import { PublicKey, WalletInterface } from '@bsv/sdk' +import { WalletContext } from '../../../WalletContext' +import { CurrencyConverter } from '@bsv/amountinator' +import { toast } from 'react-toastify' +import useAsyncEffect from 'use-async-effect' +import { useIdentitySearch } from '@bsv/identity-react' +import { PaymentRequestResponse, IncomingPayment } from '@bsv/message-box-client' + +/* ------------------------------------------------------------------ */ +/* Types */ +/* ------------------------------------------------------------------ */ + +export type OutgoingRequest = { + requestId: string + requestProof: string + recipient: string + recipientLabel: string + amount: number + description: string + expiresAt: number + status: 'pending' | 'paid' | 'declined' | 'expired' | 'cancelled' | 'received' + amountPaid?: number + note?: string + /** The incoming payment associated with a fulfilled request, used for accepting funds. */ + incomingPayment?: IncomingPayment +} + +type Props = { + wallet: WalletInterface + onRequestSent?: () => void +} + +type IdentityOption = { + identityKey: string + name?: string + avatarURL?: string + badgeLabel?: string +} + +/* ------------------------------------------------------------------ */ +/* Expiry helpers */ +/* ------------------------------------------------------------------ */ + +const EXPIRY_OPTIONS = [ + { label: '1 hour', ms: 60 * 60 * 1000 }, + { label: '1 day', ms: 24 * 60 * 60 * 1000 }, + { label: '3 days', ms: 3 * 24 * 60 * 60 * 1000 }, + { label: '7 days', ms: 7 * 24 * 60 * 60 * 1000 }, + { label: '30 days', ms: 30 * 24 * 60 * 60 * 1000 }, +] as const + +function getInitials(name: string, identityKey: string): string { + if (!name || name.trim() === '') return identityKey.slice(0, 2).toUpperCase() + const words = name.trim().split(/\s+/) + if (words.length >= 2) return (words[0][0] + words[words.length - 1][0]).toUpperCase() + return name.slice(0, 2).toUpperCase() +} + +function statusColor(status: OutgoingRequest['status']): 'success' | 'info' | 'error' | 'default' { + switch (status) { + case 'pending': return 'success' + case 'paid': return 'info' + case 'received': return 'success' + case 'declined': return 'error' + default: return 'default' + } +} + +/* ------------------------------------------------------------------ */ +/* Component */ +/* ------------------------------------------------------------------ */ + +export default function RequestPaymentForm({ wallet, onRequestSent }: Props) { + const { managers, adminOriginator, peerPayClient, messageBoxUrl, activeProfile } = useContext(WalletContext) + + // Storage key scoped to the current user's identity to prevent cross-account overwrites. + // Null until identity is available — prevents saving/loading with the wrong key. + const identityKey = activeProfile?.identityKey + const storageKey = identityKey ? `payReq_outgoing_${identityKey}` : null + + // Form state + const [recipient, setRecipient] = useState('') + const [publicKeyInput, setPublicKeyInput] = useState('') + const [amount, setAmount] = useState(0) + const [amountInput, setAmountInput] = useState('') + const [description, setDescription] = useState('') + const [expiryMs, setExpiryMs] = useState(EXPIRY_OPTIONS[2].ms) // 3 days default + const [sending, setSending] = useState(false) + const [currencySymbol, setCurrencySymbol] = useState('$') + + // Outgoing tracker — loaded from localStorage once identity is known. + const [outgoing, setOutgoing] = useState([]) + const [cancellingId, setCancellingId] = useState(null) + const [receivingId, setReceivingId] = useState(null) + + // Load outgoing requests from localStorage when identity becomes available or changes. + useEffect(() => { + if (!storageKey) return + try { + const raw = localStorage.getItem(storageKey) + setOutgoing(raw ? JSON.parse(raw) : []) + } catch { + setOutgoing([]) + } + }, [storageKey]) + + /** + * Persist-aware setter for outgoing requests. + * Writes to localStorage immediately when called, avoiding the + * race condition where a useEffect-based save overwrites data + * before the load effect's setOutgoing takes effect. + */ + const updateOutgoing = useCallback((updater: OutgoingRequest[] | ((prev: OutgoingRequest[]) => OutgoingRequest[])) => { + setOutgoing(prev => { + const next = typeof updater === 'function' ? updater(prev) : updater + if (storageKey) { + const serializable = next.map(({ incomingPayment, ...rest }) => rest) + localStorage.setItem(storageKey, JSON.stringify(serializable)) + } + return next + }) + }, [storageKey]) + + const currencyConverter = new CurrencyConverter(undefined, managers?.settingsManager as any) + + const identitySearch = useIdentitySearch({ + originator: adminOriginator, + wallet, + onIdentitySelected: (identity) => { + if (identity) setRecipient(identity.identityKey) + } + }) + + useAsyncEffect(async () => { + await currencyConverter.initialize() + setCurrencySymbol(currencyConverter.getCurrencySymbol()) + }, []) + + // Keep a ref to outgoing so pollResponses doesn't depend on it (avoids re-render loops). + const outgoingRef = useRef(outgoing) + useEffect(() => { outgoingRef.current = outgoing }, [outgoing]) + + // Poll for responses when there are pending or paid (unreceived) requests. + const pollResponses = useCallback(async () => { + if (!peerPayClient || !messageBoxUrl) return + const current = outgoingRef.current + const hasPending = current.some(r => r.status === 'pending' && r.expiresAt > Date.now()) + const hasPaidUnreceived = current.some(r => r.status === 'paid' && !r.incomingPayment) + if (!hasPending && !hasPaidUnreceived) return + try { + const responses: PaymentRequestResponse[] = await peerPayClient.listPaymentRequestResponses(messageBoxUrl) + + // Also fetch incoming payments to match with fulfilled requests + let incomingPayments: IncomingPayment[] = [] + if (responses.some(r => r.status === 'paid') || hasPaidUnreceived) { + incomingPayments = await peerPayClient.listIncomingPayments(messageBoxUrl) + } + + updateOutgoing(prev => prev.map(req => { + // Don't update already received requests + if (req.status === 'received') return req + + const match = responses.find(r => r.requestId === req.requestId) + if (!match) { + // Try to find incoming payment for already-paid requests missing their payment ref + if (req.status === 'paid' && !req.incomingPayment) { + const payment = incomingPayments.find(p => p.sender === req.recipient) + if (payment) return { ...req, incomingPayment: payment } + } + return req + } + + if (match.status === 'paid') { + // Find the matching incoming payment from this sender + const payment = incomingPayments.find(p => p.sender === req.recipient) + return { + ...req, + status: 'paid' as const, + amountPaid: match.amountPaid, + note: match.note, + incomingPayment: payment, + } + } + return { + ...req, + status: 'declined' as const, + note: match.note, + } + })) + } catch { + // Silently ignore poll errors + } + }, [peerPayClient, messageBoxUrl]) + + // Mark expired requests + useEffect(() => { + const tick = setInterval(() => { + updateOutgoing(prev => + prev.map(r => + r.status === 'pending' && r.expiresAt < Date.now() + ? { ...r, status: 'expired' } + : r + ) + ) + }, 30_000) + return () => clearInterval(tick) + }, []) + + // Immediate poll once on mount if there are actionable requests. + const hasMountPolled = useRef(false) + useEffect(() => { + if (hasMountPolled.current) return + const current = outgoingRef.current + const needsPoll = current.some(r => + r.status === 'pending' || (r.status === 'paid' && !r.incomingPayment) + ) + if (needsPoll && peerPayClient && messageBoxUrl) { + hasMountPolled.current = true + pollResponses() + } + }, [peerPayClient, messageBoxUrl, pollResponses]) + + // Set up 15-second polling interval (stable — doesn't re-create on outgoing changes). + useEffect(() => { + const id = setInterval(pollResponses, 15_000) + return () => clearInterval(id) + }, [pollResponses]) + + const handleAmountChange = useCallback(async (e: React.ChangeEvent) => { + const val = e.target.value.replace(/[^0-9.]/g, '') + setAmountInput(val) + const sats = await currencyConverter.convertToSatoshis(parseFloat(val) || 0) + setAmount(sats ?? 0) + }, []) + + const canRequest = + recipient.trim().length > 0 && + amount > 0 && + description.trim().length > 0 && + !sending + + const handleSubmit = async () => { + if (!canRequest || !peerPayClient) return + try { + setSending(true) + const expiresAt = Date.now() + expiryMs + const { requestId, requestProof } = await peerPayClient.requestPayment( + { recipient: recipient.trim(), amount, description: description.trim(), expiresAt }, + messageBoxUrl || undefined + ) + const label = + identitySearch.selectedIdentity + ? (identitySearch.selectedIdentity as IdentityOption).name || + (identitySearch.selectedIdentity as IdentityOption).identityKey.slice(0, 14) + : recipient.slice(0, 14) + updateOutgoing(prev => [ + { + requestId, + requestProof, + recipient: recipient.trim(), + recipientLabel: label, + amount, + description: description.trim(), + expiresAt, + status: 'pending', + }, + ...prev, + ]) + toast.success('Payment request sent!') + setAmountInput('') + setAmount(0) + setDescription('') + setRecipient('') + setPublicKeyInput('') + identitySearch.handleSelect(null, null) + onRequestSent?.() + } catch (e) { + const msg = (e as Error)?.message ?? 'Failed to send payment request' + toast.error(msg) + } finally { + setSending(false) + } + } + + const handleCancel = async (req: OutgoingRequest) => { + if (!peerPayClient) return + try { + setCancellingId(req.requestId) + await peerPayClient.cancelPaymentRequest( + { recipient: req.recipient, requestId: req.requestId, requestProof: req.requestProof }, + messageBoxUrl || undefined + ) + updateOutgoing(prev => + prev.map(r => r.requestId === req.requestId ? { ...r, status: 'cancelled' } : r) + ) + toast.success('Request cancelled') + } catch (e) { + toast.error((e as Error)?.message ?? 'Failed to cancel request') + } finally { + setCancellingId(null) + } + } + + /** Accept/internalize the payment associated with a fulfilled request. */ + const handleReceive = async (req: OutgoingRequest) => { + if (!peerPayClient || !req.incomingPayment) return + try { + setReceivingId(req.requestId) + await peerPayClient.acceptPayment(req.incomingPayment) + updateOutgoing(prev => + prev.map(r => r.requestId === req.requestId ? { ...r, status: 'received' as const } : r) + ) + window.dispatchEvent(new CustomEvent('balance-changed')) + toast.success('Payment received!') + } catch (e) { + toast.error((e as Error)?.message ?? 'Failed to receive payment') + } finally { + setReceivingId(null) + } + } + + return ( + + {/* ---- Form ---- */} + + + Request Payment + + + {/* Recipient autocomplete */} + { + identitySearch.handleSelect(event, value as any) + if (value && typeof value !== 'string') { + setRecipient((value as IdentityOption).identityKey) + setPublicKeyInput((value as IdentityOption).identityKey) + } else { + setRecipient('') + setPublicKeyInput('') + } + }} + filterOptions={(options: IdentityOption[]) => + options.filter( + (identity, index, array) => + array.findIndex(i => i.identityKey === identity.identityKey) === index + ) + } + getOptionLabel={(option) => { + if (typeof option === 'string') return option + return (option as IdentityOption).name || (option as IdentityOption).identityKey.slice(0, 16) + }} + isOptionEqualToValue={(option, value) => { + if (typeof option === 'string' || typeof value === 'string') return false + return (option as IdentityOption).identityKey === (value as IdentityOption).identityKey + }} + renderInput={(params) => ( + + {identitySearch.isLoading ? : null} + {params.InputProps.endAdornment} + + ) + }} + /> + )} + renderOption={(props, option) => { + if (typeof option === 'string') return null + const opt = option as IdentityOption + const { key, ...otherProps } = props as any + return ( +
  • + + {opt.avatarURL ? ( + + ) : ( + + {getInitials(opt.name || '', opt.identityKey)} + + )} + + {opt.name || 'Unknown'} + + {opt.identityKey.slice(0, 20)}... + + + {opt.badgeLabel && } + +
  • + ) + }} + noOptionsText={identitySearch.inputValue ? 'No identities found' : 'Start typing to search'} + fullWidth + /> + + {/* Direct public key fallback */} + { + const val = e.target.value.trim() + setPublicKeyInput(val) + if (val) { + try { + PublicKey.fromString(val) + setRecipient(val) + identitySearch.handleSelect(null, null) + } catch { + setRecipient('') + } + } else { + setRecipient('') + } + }} + disabled={!!identitySearch.selectedIdentity} + error={Boolean(publicKeyInput && !recipient && !identitySearch.selectedIdentity)} + helperText={publicKeyInput && !recipient && !identitySearch.selectedIdentity ? 'Invalid public key' : ''} + /> + + {/* Amount */} + {currencySymbol} + }} + /> + + {/* Description */} + setDescription(e.target.value)} + required + /> + + {/* Expiry */} + + Expires In + + + + + + +
    +
    + + {/* ---- Outgoing tracker ---- */} + {outgoing.length > 0 && ( + + + Sent Requests + + + + {outgoing.map(req => ( + + + + + To: {req.recipientLabel}… + + + {req.amount.toLocaleString()} sats + + + {req.description} + + {req.status === 'paid' && req.amountPaid != null && ( + + Paid: {req.amountPaid.toLocaleString()} sats + + )} + {req.note && ( + + + "{req.note}" + + + )} + + + + {req.status === 'pending' && ( + + )} + {req.status === 'paid' && req.incomingPayment && ( + + )} + {req.status === 'paid' && !req.incomingPayment && ( + + Waiting for payment… + + )} + + + + ))} + + + )} +
    + ) +} diff --git a/src/lib/pages/Dashboard/Payments/index.tsx b/src/lib/pages/Dashboard/Payments/index.tsx index c870dd3..26ca6a6 100644 --- a/src/lib/pages/Dashboard/Payments/index.tsx +++ b/src/lib/pages/Dashboard/Payments/index.tsx @@ -27,7 +27,9 @@ import { Link } from '@mui/material' import InputAdornment from '@mui/material/InputAdornment' -import { IncomingPayment } from '@bsv/message-box-client' +import { IncomingPayment, IncomingPaymentRequest } from '@bsv/message-box-client' +import RequestPaymentForm from './RequestPaymentForm' +import IncomingRequestList from './IncomingRequestList' import { Utils, Script, PublicKey, WalletInterface } from '@bsv/sdk' import { WalletContext } from '../../../WalletContext' import { toast } from 'react-toastify' @@ -475,11 +477,15 @@ export default function PeerPayRoute() { isHostAnointed, anointCurrentHost, anointmentLoading, - adminOriginator + adminOriginator, + activeProfile } = useContext(WalletContext) const wallet = managers?.permissionsManager || null + const idSuffix = activeProfile?.identityKey ? `_${activeProfile.identityKey}` : '' const [payments, setPayments] = useState([]) + const [incomingRequests, setIncomingRequests] = useState([]) + const [activeTab, setActiveTab] = useState(0) const [loading, setLoading] = useState(false) const [transactions, setTransactions] = useState([]) const [snack, setSnack] = useState<{ open: boolean; msg: string; severity: 'success' | 'info' | 'warning' | 'error' }>({ @@ -501,6 +507,21 @@ export default function PeerPayRoute() { } }, [peerPayClient, messageBoxUrl]) + const fetchRequests = useCallback(async () => { + try { + if (!peerPayClient || !messageBoxUrl) return + const min = parseInt(localStorage.getItem(`payReq_minAmount${idSuffix}`) ?? '1000', 10) + const max = parseInt(localStorage.getItem(`payReq_maxAmount${idSuffix}`) ?? '10000000', 10) + const list = await peerPayClient.listIncomingPaymentRequests(messageBoxUrl, { + minAmount: isNaN(min) ? 1000 : min, + maxAmount: isNaN(max) ? 10000000 : max + }) + setIncomingRequests(list) + } catch (e) { + setSnack({ open: true, msg: (e as Error)?.message ?? 'Failed to load requests', severity: 'error' }) + } + }, [peerPayClient, messageBoxUrl, idSuffix]) + const getPastTransactions = async () => { if (!wallet) return @@ -567,65 +588,107 @@ export default function PeerPayRoute() { Payments - - {!isHostAnointed && ( - - Your message box host is not yet anointed. You can send payments, but others cannot find you to send payments to you until you anoint the host in Settings. - - )} + {!isHostAnointed && ( + + Your message box host is not yet anointed. You can send payments, but others cannot find you to send payments to you until you anoint the host in Settings. + + )} - + { + setActiveTab(v) + if (v === 2) fetchRequests() + if (v === 3) fetchPayments() + }} + variant="fullWidth" + sx={{ mb: 2, borderBottom: 1, borderColor: 'divider' }} + > + + + + + + + {/* Tab 0: Send Payment */} + {activeTab === 0 && ( + + - {loading && } + {loading && } - + {/* Transaction History Section */} + + + Transaction History + + + + + + {transactions.length === 0 ? ( + + No transactions yet... + + ) : ( + + {transactions.map((tx, index) => ( + + + + TXID:{' '} + + {tx.txid} + + + + To: {tx.to || 'N/A'} + + + Amount: {tx.amount || 'N/A'} BSV + + + + ))} + + )} + + + )} - {/* Transaction History Section */} - - - Transaction History - - + {/* Tab 1: Request Payment */} + {activeTab === 1 && ( + + )} - + {/* Tab 2: Incoming Requests */} + {activeTab === 2 && ( + + )} - {transactions.length === 0 ? ( - - No transactions yet... - - ) : ( - - {transactions.map((tx, index) => ( - - - - TXID:{' '} - - {tx.txid} - - - - To: {tx.to || 'N/A'} - - - Amount: {tx.amount || 'N/A'} BSV - - - - ))} - - )} - - + {/* Tab 3: Pending Payments */} + {activeTab === 3 && ( + + {loading && } + + + )}