an appview-less Bluesky client using Constellation and PDS Queries - https://reddwarf.app/

Compare changes

Choose any two refs to compare.

Changed files
+603 -607
src
components
placeholders
routes
+41 -72
package-lock.json
··· 376 "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", 377 "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", 378 "license": "MIT", 379 "dependencies": { 380 "@ampproject/remapping": "^2.2.0", 381 "@babel/code-frame": "^7.27.1", ··· 883 } 884 ], 885 "license": "MIT", 886 "engines": { 887 "node": ">=18" 888 }, ··· 906 } 907 ], 908 "license": "MIT", 909 "engines": { 910 "node": ">=18" 911 } ··· 1494 "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", 1495 "dev": true, 1496 "license": "Apache-2.0", 1497 - "peer": true, 1498 "dependencies": { 1499 "@eslint/object-schema": "^2.1.6", 1500 "debug": "^4.3.1", ··· 1510 "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", 1511 "dev": true, 1512 "license": "Apache-2.0", 1513 - "peer": true, 1514 "dependencies": { 1515 "@eslint/core": "^0.16.0" 1516 }, ··· 1524 "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", 1525 "dev": true, 1526 "license": "Apache-2.0", 1527 - "peer": true, 1528 "dependencies": { 1529 "@types/json-schema": "^7.0.15" 1530 }, ··· 1538 "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", 1539 "dev": true, 1540 "license": "MIT", 1541 - "peer": true, 1542 "dependencies": { 1543 "ajv": "^6.12.4", 1544 "debug": "^4.3.2", ··· 1563 "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", 1564 "dev": true, 1565 "license": "MIT", 1566 - "peer": true, 1567 "engines": { 1568 "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1569 }, ··· 1577 "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", 1578 "dev": true, 1579 "license": "Apache-2.0", 1580 - "peer": true, 1581 "engines": { 1582 "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1583 } ··· 1588 "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", 1589 "dev": true, 1590 "license": "Apache-2.0", 1591 - "peer": true, 1592 "dependencies": { 1593 "@eslint/core": "^0.16.0", 1594 "levn": "^0.4.1" ··· 1637 "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", 1638 "dev": true, 1639 "license": "Apache-2.0", 1640 - "peer": true, 1641 "engines": { 1642 "node": ">=18.18.0" 1643 } ··· 1648 "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", 1649 "dev": true, 1650 "license": "Apache-2.0", 1651 - "peer": true, 1652 "dependencies": { 1653 "@humanfs/core": "^0.19.1", 1654 "@humanwhocodes/retry": "^0.4.0" ··· 1663 "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 1664 "dev": true, 1665 "license": "Apache-2.0", 1666 - "peer": true, 1667 "engines": { 1668 "node": ">=12.22" 1669 }, ··· 1678 "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", 1679 "dev": true, 1680 "license": "Apache-2.0", 1681 - "peer": true, 1682 "engines": { 1683 "node": ">=18.18" 1684 }, ··· 3856 "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", 3857 "dev": true, 3858 "license": "MIT", 3859 "dependencies": { 3860 "@babel/core": "^7.21.3", 3861 "@svgr/babel-preset": "8.1.0", ··· 4330 "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.85.6.tgz", 4331 "integrity": "sha512-VUAag4ERjh+qlmg0wNivQIVCZUrYndqYu3/wPCVZd4r0E+1IqotbeyGTc+ICroL/PqbpSaGZg02zSWYfcvxbdA==", 4332 "license": "MIT", 4333 "dependencies": { 4334 "@tanstack/query-core": "5.85.6" 4335 }, ··· 4363 "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.131.28.tgz", 4364 "integrity": "sha512-vWExhrqHJuT9v+6/2DCQ4pVvPaYoLazMNw8WXiLNuzBXh1FuEoIGaW3jw3DEP0OJCmMiWtTi34NzQnakkQZlQg==", 4365 "license": "MIT", 4366 "dependencies": { 4367 "@tanstack/history": "1.131.2", 4368 "@tanstack/react-store": "^0.7.0", ··· 4427 "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.131.28.tgz", 4428 "integrity": "sha512-f+vdfr3WKSS/BcqgI5s4vZg9xYb7NkvIolkaMELrbz3l+khkw1aTjx8wqCHRY4dqwIAxq+iZBZtMWXA7pztGJg==", 4429 "license": "MIT", 4430 "dependencies": { 4431 "@tanstack/history": "1.131.2", 4432 "@tanstack/store": "^0.7.0", ··· 4599 "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", 4600 "dev": true, 4601 "license": "MIT", 4602 "dependencies": { 4603 "@babel/code-frame": "^7.10.4", 4604 "@babel/runtime": "^7.12.5", ··· 4721 "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 4722 "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 4723 "dev": true, 4724 - "license": "MIT", 4725 - "peer": true 4726 }, 4727 "node_modules/@types/node": { 4728 "version": "24.3.0", ··· 4730 "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", 4731 "devOptional": true, 4732 "license": "MIT", 4733 "dependencies": { 4734 "undici-types": "~7.10.0" 4735 } ··· 4739 "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", 4740 "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", 4741 "license": "MIT", 4742 "dependencies": { 4743 "csstype": "^3.0.2" 4744 } ··· 4748 "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", 4749 "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", 4750 "license": "MIT", 4751 "peerDependencies": { 4752 "@types/react": "^19.0.0" 4753 } ··· 4765 "integrity": "sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==", 4766 "dev": true, 4767 "license": "MIT", 4768 "dependencies": { 4769 "@eslint-community/regexpp": "^4.10.0", 4770 "@typescript-eslint/scope-manager": "8.46.1", ··· 4805 "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", 4806 "dev": true, 4807 "license": "MIT", 4808 "dependencies": { 4809 "@typescript-eslint/scope-manager": "8.46.1", 4810 "@typescript-eslint/types": "8.46.1", ··· 5187 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", 5188 "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 5189 "license": "MIT", 5190 "bin": { 5191 "acorn": "bin/acorn" 5192 }, ··· 5200 "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 5201 "dev": true, 5202 "license": "MIT", 5203 - "peer": true, 5204 "peerDependencies": { 5205 "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 5206 } ··· 5221 "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 5222 "dev": true, 5223 "license": "MIT", 5224 - "peer": true, 5225 "dependencies": { 5226 "fast-deep-equal": "^3.1.1", 5227 "fast-json-stable-stringify": "^2.0.0", ··· 5627 } 5628 ], 5629 "license": "MIT", 5630 "dependencies": { 5631 "caniuse-lite": "^1.0.30001737", 5632 "electron-to-chromium": "^1.5.211", ··· 5784 "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 5785 "dev": true, 5786 "license": "MIT", 5787 - "peer": true, 5788 "dependencies": { 5789 "ansi-styles": "^4.1.0", 5790 "supports-color": "^7.1.0" ··· 5802 "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 5803 "dev": true, 5804 "license": "MIT", 5805 - "peer": true, 5806 "dependencies": { 5807 "color-convert": "^2.0.1" 5808 }, ··· 5883 "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 5884 "dev": true, 5885 "license": "MIT", 5886 - "peer": true, 5887 "dependencies": { 5888 "color-name": "~1.1.4" 5889 }, ··· 5896 "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 5897 "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 5898 "dev": true, 5899 - "license": "MIT", 5900 - "peer": true 5901 }, 5902 "node_modules/compare-versions": { 5903 "version": "6.1.1", ··· 5976 "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 5977 "dev": true, 5978 "license": "MIT", 5979 - "peer": true, 5980 "dependencies": { 5981 "path-key": "^3.1.0", 5982 "shebang-command": "^2.0.0", ··· 6004 "version": "3.1.3", 6005 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", 6006 "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", 6007 - "license": "MIT" 6008 }, 6009 "node_modules/custom-media-element": { 6010 "version": "1.4.5", ··· 6147 "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 6148 "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 6149 "dev": true, 6150 - "license": "MIT", 6151 - "peer": true 6152 }, 6153 "node_modules/define-data-property": { 6154 "version": "1.1.4", ··· 6556 "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 6557 "dev": true, 6558 "license": "MIT", 6559 - "peer": true, 6560 "engines": { 6561 "node": ">=10" 6562 }, ··· 6848 "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", 6849 "dev": true, 6850 "license": "BSD-2-Clause", 6851 - "peer": true, 6852 "dependencies": { 6853 "esrecurse": "^4.3.0", 6854 "estraverse": "^5.2.0" ··· 6879 "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 6880 "dev": true, 6881 "license": "ISC", 6882 - "peer": true, 6883 "dependencies": { 6884 "is-glob": "^4.0.3" 6885 }, ··· 6893 "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", 6894 "dev": true, 6895 "license": "BSD-2-Clause", 6896 - "peer": true, 6897 "dependencies": { 6898 "acorn": "^8.15.0", 6899 "acorn-jsx": "^5.3.2", ··· 6925 "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", 6926 "dev": true, 6927 "license": "BSD-3-Clause", 6928 - "peer": true, 6929 "dependencies": { 6930 "estraverse": "^5.1.0" 6931 }, ··· 6939 "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 6940 "dev": true, 6941 "license": "BSD-2-Clause", 6942 - "peer": true, 6943 "dependencies": { 6944 "estraverse": "^5.2.0" 6945 }, ··· 7028 "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 7029 "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 7030 "dev": true, 7031 - "license": "MIT", 7032 - "peer": true 7033 }, 7034 "node_modules/fast-levenshtein": { 7035 "version": "2.0.6", 7036 "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 7037 "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 7038 "dev": true, 7039 - "license": "MIT", 7040 - "peer": true 7041 }, 7042 "node_modules/fastq": { 7043 "version": "1.19.1", ··· 7055 "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 7056 "dev": true, 7057 "license": "MIT", 7058 - "peer": true, 7059 "dependencies": { 7060 "flat-cache": "^4.0.0" 7061 }, ··· 7081 "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 7082 "dev": true, 7083 "license": "MIT", 7084 - "peer": true, 7085 "dependencies": { 7086 "locate-path": "^6.0.0", 7087 "path-exists": "^4.0.0" ··· 7099 "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 7100 "dev": true, 7101 "license": "MIT", 7102 - "peer": true, 7103 "dependencies": { 7104 "flatted": "^3.2.9", 7105 "keyv": "^4.5.4" ··· 7113 "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", 7114 "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", 7115 "dev": true, 7116 - "license": "ISC", 7117 - "peer": true 7118 }, 7119 "node_modules/for-each": { 7120 "version": "0.3.5", ··· 7301 "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", 7302 "dev": true, 7303 "license": "MIT", 7304 - "peer": true, 7305 "engines": { 7306 "node": ">=18" 7307 }, ··· 7379 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 7380 "dev": true, 7381 "license": "MIT", 7382 - "peer": true, 7383 "engines": { 7384 "node": ">=8" 7385 } ··· 7592 "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", 7593 "dev": true, 7594 "license": "MIT", 7595 - "peer": true, 7596 "engines": { 7597 "node": ">= 4" 7598 } ··· 7635 "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 7636 "dev": true, 7637 "license": "MIT", 7638 - "peer": true, 7639 "engines": { 7640 "node": ">=0.8.19" 7641 } ··· 8141 "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 8142 "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 8143 "dev": true, 8144 - "license": "ISC", 8145 - "peer": true 8146 }, 8147 "node_modules/iso-datestring-validator": { 8148 "version": "2.2.2", ··· 8240 "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", 8241 "dev": true, 8242 "license": "MIT", 8243 "dependencies": { 8244 "cssstyle": "^4.2.1", 8245 "data-urls": "^5.0.0", ··· 8291 "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 8292 "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 8293 "dev": true, 8294 - "license": "MIT", 8295 - "peer": true 8296 }, 8297 "node_modules/json-parse-even-better-errors": { 8298 "version": "2.3.1", ··· 8306 "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 8307 "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 8308 "dev": true, 8309 - "license": "MIT", 8310 - "peer": true 8311 }, 8312 "node_modules/json-stable-stringify-without-jsonify": { 8313 "version": "1.0.1", 8314 "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 8315 "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 8316 "dev": true, 8317 - "license": "MIT", 8318 - "peer": true 8319 }, 8320 "node_modules/json5": { 8321 "version": "2.2.3", ··· 8351 "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 8352 "dev": true, 8353 "license": "MIT", 8354 - "peer": true, 8355 "dependencies": { 8356 "json-buffer": "3.0.1" 8357 } ··· 8369 "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 8370 "dev": true, 8371 "license": "MIT", 8372 - "peer": true, 8373 "dependencies": { 8374 "prelude-ls": "^1.2.1", 8375 "type-check": "~0.4.0" ··· 8655 "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 8656 "dev": true, 8657 "license": "MIT", 8658 - "peer": true, 8659 "dependencies": { 8660 "p-locate": "^5.0.0" 8661 }, ··· 8677 "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 8678 "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 8679 "dev": true, 8680 - "license": "MIT", 8681 - "peer": true 8682 }, 8683 "node_modules/loose-envify": { 8684 "version": "1.4.0", ··· 11138 "version": "4.0.3", 11139 "inBundle": true, 11140 "license": "MIT", 11141 "engines": { 11142 "node": ">=12" 11143 }, ··· 11471 "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 11472 "dev": true, 11473 "license": "MIT", 11474 - "peer": true, 11475 "dependencies": { 11476 "deep-is": "^0.1.3", 11477 "fast-levenshtein": "^2.0.6", ··· 11508 "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 11509 "dev": true, 11510 "license": "MIT", 11511 - "peer": true, 11512 "dependencies": { 11513 "yocto-queue": "^0.1.0" 11514 }, ··· 11525 "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 11526 "dev": true, 11527 "license": "MIT", 11528 - "peer": true, 11529 "dependencies": { 11530 "p-limit": "^3.0.2" 11531 }, ··· 11600 "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 11601 "dev": true, 11602 "license": "MIT", 11603 - "peer": true, 11604 "engines": { 11605 "node": ">=8" 11606 } ··· 11611 "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 11612 "dev": true, 11613 "license": "MIT", 11614 - "peer": true, 11615 "engines": { 11616 "node": ">=8" 11617 } ··· 11740 "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 11741 "dev": true, 11742 "license": "MIT", 11743 - "peer": true, 11744 "engines": { 11745 "node": ">= 0.8.0" 11746 } ··· 11921 "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", 11922 "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", 11923 "license": "MIT", 11924 "engines": { 11925 "node": ">=0.10.0" 11926 } ··· 11930 "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", 11931 "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", 11932 "license": "MIT", 11933 "dependencies": { 11934 "scheduler": "^0.26.0" 11935 }, ··· 12350 "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", 12351 "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", 12352 "license": "MIT", 12353 "engines": { 12354 "node": ">=10" 12355 } ··· 12421 "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 12422 "dev": true, 12423 "license": "MIT", 12424 - "peer": true, 12425 "dependencies": { 12426 "shebang-regex": "^3.0.0" 12427 }, ··· 12435 "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 12436 "dev": true, 12437 "license": "MIT", 12438 - "peer": true, 12439 "engines": { 12440 "node": ">=8" 12441 } ··· 12539 "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.9.tgz", 12540 "integrity": "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==", 12541 "license": "MIT", 12542 "dependencies": { 12543 "csstype": "^3.1.0", 12544 "seroval": "~1.3.0", ··· 12708 "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 12709 "dev": true, 12710 "license": "MIT", 12711 - "peer": true, 12712 "engines": { 12713 "node": ">=8" 12714 }, ··· 12748 "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 12749 "dev": true, 12750 "license": "MIT", 12751 - "peer": true, 12752 "dependencies": { 12753 "has-flag": "^4.0.0" 12754 }, ··· 12848 "version": "1.3.3", 12849 "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", 12850 "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", 12851 - "license": "MIT" 12852 }, 12853 "node_modules/tiny-warning": { 12854 "version": "1.0.3", ··· 12908 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 12909 "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 12910 "license": "MIT", 12911 "engines": { 12912 "node": ">=12" 12913 }, ··· 13105 "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 13106 "dev": true, 13107 "license": "MIT", 13108 - "peer": true, 13109 "dependencies": { 13110 "prelude-ls": "^1.2.1" 13111 }, ··· 13197 "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 13198 "dev": true, 13199 "license": "Apache-2.0", 13200 "bin": { 13201 "tsc": "bin/tsc", 13202 "tsserver": "bin/tsserver" ··· 13533 "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 13534 "dev": true, 13535 "license": "BSD-2-Clause", 13536 - "peer": true, 13537 "dependencies": { 13538 "punycode": "^2.1.0" 13539 } ··· 13602 "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", 13603 "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", 13604 "license": "MIT", 13605 "dependencies": { 13606 "esbuild": "^0.25.0", 13607 "fdir": "^6.4.4", ··· 13716 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 13717 "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 13718 "license": "MIT", 13719 "engines": { 13720 "node": ">=12" 13721 }, ··· 13897 "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 13898 "dev": true, 13899 "license": "ISC", 13900 - "peer": true, 13901 "dependencies": { 13902 "isexe": "^2.0.0" 13903 }, ··· 14029 "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 14030 "dev": true, 14031 "license": "MIT", 14032 - "peer": true, 14033 "engines": { 14034 "node": ">=0.10.0" 14035 } ··· 14084 "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 14085 "dev": true, 14086 "license": "MIT", 14087 - "peer": true, 14088 "engines": { 14089 "node": ">=10" 14090 }, ··· 14103 "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 14104 "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 14105 "license": "MIT", 14106 "funding": { 14107 "url": "https://github.com/sponsors/colinhacks" 14108 }
··· 376 "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", 377 "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", 378 "license": "MIT", 379 + "peer": true, 380 "dependencies": { 381 "@ampproject/remapping": "^2.2.0", 382 "@babel/code-frame": "^7.27.1", ··· 884 } 885 ], 886 "license": "MIT", 887 + "peer": true, 888 "engines": { 889 "node": ">=18" 890 }, ··· 908 } 909 ], 910 "license": "MIT", 911 + "peer": true, 912 "engines": { 913 "node": ">=18" 914 } ··· 1497 "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", 1498 "dev": true, 1499 "license": "Apache-2.0", 1500 "dependencies": { 1501 "@eslint/object-schema": "^2.1.6", 1502 "debug": "^4.3.1", ··· 1512 "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", 1513 "dev": true, 1514 "license": "Apache-2.0", 1515 "dependencies": { 1516 "@eslint/core": "^0.16.0" 1517 }, ··· 1525 "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", 1526 "dev": true, 1527 "license": "Apache-2.0", 1528 "dependencies": { 1529 "@types/json-schema": "^7.0.15" 1530 }, ··· 1538 "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", 1539 "dev": true, 1540 "license": "MIT", 1541 "dependencies": { 1542 "ajv": "^6.12.4", 1543 "debug": "^4.3.2", ··· 1562 "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", 1563 "dev": true, 1564 "license": "MIT", 1565 "engines": { 1566 "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1567 }, ··· 1575 "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", 1576 "dev": true, 1577 "license": "Apache-2.0", 1578 "engines": { 1579 "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1580 } ··· 1585 "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", 1586 "dev": true, 1587 "license": "Apache-2.0", 1588 "dependencies": { 1589 "@eslint/core": "^0.16.0", 1590 "levn": "^0.4.1" ··· 1633 "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", 1634 "dev": true, 1635 "license": "Apache-2.0", 1636 "engines": { 1637 "node": ">=18.18.0" 1638 } ··· 1643 "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", 1644 "dev": true, 1645 "license": "Apache-2.0", 1646 "dependencies": { 1647 "@humanfs/core": "^0.19.1", 1648 "@humanwhocodes/retry": "^0.4.0" ··· 1657 "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 1658 "dev": true, 1659 "license": "Apache-2.0", 1660 "engines": { 1661 "node": ">=12.22" 1662 }, ··· 1671 "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", 1672 "dev": true, 1673 "license": "Apache-2.0", 1674 "engines": { 1675 "node": ">=18.18" 1676 }, ··· 3848 "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", 3849 "dev": true, 3850 "license": "MIT", 3851 + "peer": true, 3852 "dependencies": { 3853 "@babel/core": "^7.21.3", 3854 "@svgr/babel-preset": "8.1.0", ··· 4323 "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.85.6.tgz", 4324 "integrity": "sha512-VUAag4ERjh+qlmg0wNivQIVCZUrYndqYu3/wPCVZd4r0E+1IqotbeyGTc+ICroL/PqbpSaGZg02zSWYfcvxbdA==", 4325 "license": "MIT", 4326 + "peer": true, 4327 "dependencies": { 4328 "@tanstack/query-core": "5.85.6" 4329 }, ··· 4357 "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.131.28.tgz", 4358 "integrity": "sha512-vWExhrqHJuT9v+6/2DCQ4pVvPaYoLazMNw8WXiLNuzBXh1FuEoIGaW3jw3DEP0OJCmMiWtTi34NzQnakkQZlQg==", 4359 "license": "MIT", 4360 + "peer": true, 4361 "dependencies": { 4362 "@tanstack/history": "1.131.2", 4363 "@tanstack/react-store": "^0.7.0", ··· 4422 "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.131.28.tgz", 4423 "integrity": "sha512-f+vdfr3WKSS/BcqgI5s4vZg9xYb7NkvIolkaMELrbz3l+khkw1aTjx8wqCHRY4dqwIAxq+iZBZtMWXA7pztGJg==", 4424 "license": "MIT", 4425 + "peer": true, 4426 "dependencies": { 4427 "@tanstack/history": "1.131.2", 4428 "@tanstack/store": "^0.7.0", ··· 4595 "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", 4596 "dev": true, 4597 "license": "MIT", 4598 + "peer": true, 4599 "dependencies": { 4600 "@babel/code-frame": "^7.10.4", 4601 "@babel/runtime": "^7.12.5", ··· 4718 "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 4719 "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 4720 "dev": true, 4721 + "license": "MIT" 4722 }, 4723 "node_modules/@types/node": { 4724 "version": "24.3.0", ··· 4726 "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", 4727 "devOptional": true, 4728 "license": "MIT", 4729 + "peer": true, 4730 "dependencies": { 4731 "undici-types": "~7.10.0" 4732 } ··· 4736 "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", 4737 "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", 4738 "license": "MIT", 4739 + "peer": true, 4740 "dependencies": { 4741 "csstype": "^3.0.2" 4742 } ··· 4746 "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", 4747 "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", 4748 "license": "MIT", 4749 + "peer": true, 4750 "peerDependencies": { 4751 "@types/react": "^19.0.0" 4752 } ··· 4764 "integrity": "sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==", 4765 "dev": true, 4766 "license": "MIT", 4767 + "peer": true, 4768 "dependencies": { 4769 "@eslint-community/regexpp": "^4.10.0", 4770 "@typescript-eslint/scope-manager": "8.46.1", ··· 4805 "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", 4806 "dev": true, 4807 "license": "MIT", 4808 + "peer": true, 4809 "dependencies": { 4810 "@typescript-eslint/scope-manager": "8.46.1", 4811 "@typescript-eslint/types": "8.46.1", ··· 5188 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", 5189 "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 5190 "license": "MIT", 5191 + "peer": true, 5192 "bin": { 5193 "acorn": "bin/acorn" 5194 }, ··· 5202 "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 5203 "dev": true, 5204 "license": "MIT", 5205 "peerDependencies": { 5206 "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 5207 } ··· 5222 "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 5223 "dev": true, 5224 "license": "MIT", 5225 "dependencies": { 5226 "fast-deep-equal": "^3.1.1", 5227 "fast-json-stable-stringify": "^2.0.0", ··· 5627 } 5628 ], 5629 "license": "MIT", 5630 + "peer": true, 5631 "dependencies": { 5632 "caniuse-lite": "^1.0.30001737", 5633 "electron-to-chromium": "^1.5.211", ··· 5785 "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 5786 "dev": true, 5787 "license": "MIT", 5788 "dependencies": { 5789 "ansi-styles": "^4.1.0", 5790 "supports-color": "^7.1.0" ··· 5802 "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 5803 "dev": true, 5804 "license": "MIT", 5805 "dependencies": { 5806 "color-convert": "^2.0.1" 5807 }, ··· 5882 "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 5883 "dev": true, 5884 "license": "MIT", 5885 "dependencies": { 5886 "color-name": "~1.1.4" 5887 }, ··· 5894 "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 5895 "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 5896 "dev": true, 5897 + "license": "MIT" 5898 }, 5899 "node_modules/compare-versions": { 5900 "version": "6.1.1", ··· 5973 "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 5974 "dev": true, 5975 "license": "MIT", 5976 "dependencies": { 5977 "path-key": "^3.1.0", 5978 "shebang-command": "^2.0.0", ··· 6000 "version": "3.1.3", 6001 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", 6002 "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", 6003 + "license": "MIT", 6004 + "peer": true 6005 }, 6006 "node_modules/custom-media-element": { 6007 "version": "1.4.5", ··· 6144 "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 6145 "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 6146 "dev": true, 6147 + "license": "MIT" 6148 }, 6149 "node_modules/define-data-property": { 6150 "version": "1.1.4", ··· 6552 "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 6553 "dev": true, 6554 "license": "MIT", 6555 "engines": { 6556 "node": ">=10" 6557 }, ··· 6843 "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", 6844 "dev": true, 6845 "license": "BSD-2-Clause", 6846 "dependencies": { 6847 "esrecurse": "^4.3.0", 6848 "estraverse": "^5.2.0" ··· 6873 "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 6874 "dev": true, 6875 "license": "ISC", 6876 "dependencies": { 6877 "is-glob": "^4.0.3" 6878 }, ··· 6886 "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", 6887 "dev": true, 6888 "license": "BSD-2-Clause", 6889 "dependencies": { 6890 "acorn": "^8.15.0", 6891 "acorn-jsx": "^5.3.2", ··· 6917 "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", 6918 "dev": true, 6919 "license": "BSD-3-Clause", 6920 "dependencies": { 6921 "estraverse": "^5.1.0" 6922 }, ··· 6930 "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 6931 "dev": true, 6932 "license": "BSD-2-Clause", 6933 "dependencies": { 6934 "estraverse": "^5.2.0" 6935 }, ··· 7018 "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 7019 "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 7020 "dev": true, 7021 + "license": "MIT" 7022 }, 7023 "node_modules/fast-levenshtein": { 7024 "version": "2.0.6", 7025 "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 7026 "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 7027 "dev": true, 7028 + "license": "MIT" 7029 }, 7030 "node_modules/fastq": { 7031 "version": "1.19.1", ··· 7043 "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 7044 "dev": true, 7045 "license": "MIT", 7046 "dependencies": { 7047 "flat-cache": "^4.0.0" 7048 }, ··· 7068 "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 7069 "dev": true, 7070 "license": "MIT", 7071 "dependencies": { 7072 "locate-path": "^6.0.0", 7073 "path-exists": "^4.0.0" ··· 7085 "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 7086 "dev": true, 7087 "license": "MIT", 7088 "dependencies": { 7089 "flatted": "^3.2.9", 7090 "keyv": "^4.5.4" ··· 7098 "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", 7099 "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", 7100 "dev": true, 7101 + "license": "ISC" 7102 }, 7103 "node_modules/for-each": { 7104 "version": "0.3.5", ··· 7285 "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", 7286 "dev": true, 7287 "license": "MIT", 7288 "engines": { 7289 "node": ">=18" 7290 }, ··· 7362 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 7363 "dev": true, 7364 "license": "MIT", 7365 "engines": { 7366 "node": ">=8" 7367 } ··· 7574 "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", 7575 "dev": true, 7576 "license": "MIT", 7577 "engines": { 7578 "node": ">= 4" 7579 } ··· 7616 "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 7617 "dev": true, 7618 "license": "MIT", 7619 "engines": { 7620 "node": ">=0.8.19" 7621 } ··· 8121 "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 8122 "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 8123 "dev": true, 8124 + "license": "ISC" 8125 }, 8126 "node_modules/iso-datestring-validator": { 8127 "version": "2.2.2", ··· 8219 "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", 8220 "dev": true, 8221 "license": "MIT", 8222 + "peer": true, 8223 "dependencies": { 8224 "cssstyle": "^4.2.1", 8225 "data-urls": "^5.0.0", ··· 8271 "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 8272 "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 8273 "dev": true, 8274 + "license": "MIT" 8275 }, 8276 "node_modules/json-parse-even-better-errors": { 8277 "version": "2.3.1", ··· 8285 "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 8286 "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 8287 "dev": true, 8288 + "license": "MIT" 8289 }, 8290 "node_modules/json-stable-stringify-without-jsonify": { 8291 "version": "1.0.1", 8292 "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 8293 "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 8294 "dev": true, 8295 + "license": "MIT" 8296 }, 8297 "node_modules/json5": { 8298 "version": "2.2.3", ··· 8328 "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 8329 "dev": true, 8330 "license": "MIT", 8331 "dependencies": { 8332 "json-buffer": "3.0.1" 8333 } ··· 8345 "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 8346 "dev": true, 8347 "license": "MIT", 8348 "dependencies": { 8349 "prelude-ls": "^1.2.1", 8350 "type-check": "~0.4.0" ··· 8630 "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 8631 "dev": true, 8632 "license": "MIT", 8633 "dependencies": { 8634 "p-locate": "^5.0.0" 8635 }, ··· 8651 "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 8652 "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 8653 "dev": true, 8654 + "license": "MIT" 8655 }, 8656 "node_modules/loose-envify": { 8657 "version": "1.4.0", ··· 11111 "version": "4.0.3", 11112 "inBundle": true, 11113 "license": "MIT", 11114 + "peer": true, 11115 "engines": { 11116 "node": ">=12" 11117 }, ··· 11445 "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 11446 "dev": true, 11447 "license": "MIT", 11448 "dependencies": { 11449 "deep-is": "^0.1.3", 11450 "fast-levenshtein": "^2.0.6", ··· 11481 "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 11482 "dev": true, 11483 "license": "MIT", 11484 "dependencies": { 11485 "yocto-queue": "^0.1.0" 11486 }, ··· 11497 "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 11498 "dev": true, 11499 "license": "MIT", 11500 "dependencies": { 11501 "p-limit": "^3.0.2" 11502 }, ··· 11571 "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 11572 "dev": true, 11573 "license": "MIT", 11574 "engines": { 11575 "node": ">=8" 11576 } ··· 11581 "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 11582 "dev": true, 11583 "license": "MIT", 11584 "engines": { 11585 "node": ">=8" 11586 } ··· 11709 "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 11710 "dev": true, 11711 "license": "MIT", 11712 "engines": { 11713 "node": ">= 0.8.0" 11714 } ··· 11889 "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", 11890 "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", 11891 "license": "MIT", 11892 + "peer": true, 11893 "engines": { 11894 "node": ">=0.10.0" 11895 } ··· 11899 "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", 11900 "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", 11901 "license": "MIT", 11902 + "peer": true, 11903 "dependencies": { 11904 "scheduler": "^0.26.0" 11905 }, ··· 12320 "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", 12321 "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", 12322 "license": "MIT", 12323 + "peer": true, 12324 "engines": { 12325 "node": ">=10" 12326 } ··· 12392 "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 12393 "dev": true, 12394 "license": "MIT", 12395 "dependencies": { 12396 "shebang-regex": "^3.0.0" 12397 }, ··· 12405 "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 12406 "dev": true, 12407 "license": "MIT", 12408 "engines": { 12409 "node": ">=8" 12410 } ··· 12508 "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.9.tgz", 12509 "integrity": "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==", 12510 "license": "MIT", 12511 + "peer": true, 12512 "dependencies": { 12513 "csstype": "^3.1.0", 12514 "seroval": "~1.3.0", ··· 12678 "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 12679 "dev": true, 12680 "license": "MIT", 12681 "engines": { 12682 "node": ">=8" 12683 }, ··· 12717 "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 12718 "dev": true, 12719 "license": "MIT", 12720 "dependencies": { 12721 "has-flag": "^4.0.0" 12722 }, ··· 12816 "version": "1.3.3", 12817 "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", 12818 "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", 12819 + "license": "MIT", 12820 + "peer": true 12821 }, 12822 "node_modules/tiny-warning": { 12823 "version": "1.0.3", ··· 12877 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 12878 "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 12879 "license": "MIT", 12880 + "peer": true, 12881 "engines": { 12882 "node": ">=12" 12883 }, ··· 13075 "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 13076 "dev": true, 13077 "license": "MIT", 13078 "dependencies": { 13079 "prelude-ls": "^1.2.1" 13080 }, ··· 13166 "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 13167 "dev": true, 13168 "license": "Apache-2.0", 13169 + "peer": true, 13170 "bin": { 13171 "tsc": "bin/tsc", 13172 "tsserver": "bin/tsserver" ··· 13503 "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 13504 "dev": true, 13505 "license": "BSD-2-Clause", 13506 "dependencies": { 13507 "punycode": "^2.1.0" 13508 } ··· 13571 "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", 13572 "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", 13573 "license": "MIT", 13574 + "peer": true, 13575 "dependencies": { 13576 "esbuild": "^0.25.0", 13577 "fdir": "^6.4.4", ··· 13686 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 13687 "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 13688 "license": "MIT", 13689 + "peer": true, 13690 "engines": { 13691 "node": ">=12" 13692 }, ··· 13868 "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 13869 "dev": true, 13870 "license": "ISC", 13871 "dependencies": { 13872 "isexe": "^2.0.0" 13873 }, ··· 13999 "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 14000 "dev": true, 14001 "license": "MIT", 14002 "engines": { 14003 "node": ">=0.10.0" 14004 } ··· 14053 "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 14054 "dev": true, 14055 "license": "MIT", 14056 "engines": { 14057 "node": ">=10" 14058 }, ··· 14071 "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 14072 "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 14073 "license": "MIT", 14074 + "peer": true, 14075 "funding": { 14076 "url": "https://github.com/sponsors/colinhacks" 14077 }
+31
src/components/placeholders/TextPlaceholder.tsx
···
··· 1 + import type { ReactNode } from "react"; 2 + 3 + export function TextPlaceholder({ 4 + className, 5 + pulse = false, 6 + }: { 7 + className?: string; 8 + pulse?: boolean; 9 + }) { 10 + return ( 11 + <span 12 + className={`bg-gray-100 dark:bg-gray-800 rounded h-4 ${pulse ? "animate-pulse" : ""} ${className ?? ""}`} 13 + ></span> 14 + ); 15 + } 16 + 17 + export function ConditionalTextPlaceholder({ 18 + show, 19 + placeholderClassName, 20 + children, 21 + }: { 22 + show: boolean; 23 + placeholderClassName?: string; 24 + children?: ReactNode; 25 + }) { 26 + return ( 27 + <> 28 + {show ? children : <TextPlaceholder className={placeholderClassName} />} 29 + </> 30 + ); 31 + }
+531 -535
src/routes/notifications.tsx
··· 7 import defaultpfp from "~/../public/favicon.png"; 8 import { Header } from "~/components/Header"; 9 import { 10 - ReusableTabRoute, 11 - useReusableTabScrollRestore, 12 } from "~/components/ReusableTabRoute"; 13 import { 14 - MdiCardsHeartOutline, 15 - MdiCommentOutline, 16 - MdiRepeat, 17 - UniversalPostRendererATURILoader, 18 } from "~/components/UniversalPostRenderer"; 19 import { useAuth } from "~/providers/UnifiedAuthProvider"; 20 import { 21 - constellationURLAtom, 22 - enableBitesAtom, 23 - imgCDNAtom, 24 - postInteractionsFiltersAtom, 25 } from "~/utils/atoms"; 26 import { 27 - useInfiniteQueryAuthorFeed, 28 - useQueryConstellation, 29 - useQueryIdentity, 30 - useQueryProfile, 31 - yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks, 32 } from "~/utils/useQuery"; 33 34 import { FollowButton, Mutual } from "./profile.$did"; 35 36 export function NotificationsComponent() { 37 - return ( 38 - <div className=""> 39 - <Header 40 - title={`Notifications`} 41 - backButtonCallback={() => { 42 - if (window.history.length > 1) { 43 - window.history.back(); 44 - } else { 45 - window.location.assign("/"); 46 - } 47 - }} 48 - bottomBorderDisabled={true} 49 - /> 50 - <NotificationsTabs /> 51 - </div> 52 - ); 53 } 54 55 export const Route = createFileRoute("/notifications")({ 56 - component: NotificationsComponent, 57 }); 58 59 export default function NotificationsTabs() { 60 - const [bitesEnabled] = useAtom(enableBitesAtom); 61 - return ( 62 - <ReusableTabRoute 63 - route={`Notifications`} 64 - tabs={{ 65 - Mentions: <MentionsTab />, 66 - Follows: <FollowsTab />, 67 - "Post Interactions": <PostInteractionsTab />, 68 - ...bitesEnabled ? { 69 - Bites: <BitesTab />, 70 - } : {} 71 - }} 72 - /> 73 - ); 74 } 75 76 function MentionsTab() { 77 - const { agent } = useAuth(); 78 - const [constellationurl] = useAtom(constellationURLAtom); 79 - const infinitequeryresults = useInfiniteQuery({ 80 - ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( 81 - { 82 - constellation: constellationurl, 83 - method: "/links", 84 - target: agent?.did, 85 - collection: "app.bsky.feed.post", 86 - path: ".facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet#mention].did", 87 - } 88 - ), 89 - enabled: !!agent?.did, 90 - }); 91 92 - const { 93 - data: infiniteMentionsData, 94 - fetchNextPage, 95 - hasNextPage, 96 - isFetchingNextPage, 97 - isLoading, 98 - isError, 99 - error, 100 - } = infinitequeryresults; 101 102 - const mentionsAturis = React.useMemo(() => { 103 - // Get all replies from the standard infinite query 104 - return ( 105 - infiniteMentionsData?.pages.flatMap( 106 - (page) => 107 - page?.linking_records.map( 108 - (r) => `at://${r.did}/${r.collection}/${r.rkey}` 109 - ) ?? [] 110 - ) ?? [] 111 - ); 112 - }, [infiniteMentionsData]); 113 114 - useReusableTabScrollRestore("Notifications"); 115 116 - if (isLoading) return <LoadingState text="Loading mentions..." />; 117 - if (isError) return <ErrorState error={error} />; 118 119 - if (!mentionsAturis?.length) return <EmptyState text="No mentions yet." />; 120 121 - return ( 122 - <> 123 - {mentionsAturis.map((m) => ( 124 - <UniversalPostRendererATURILoader key={m} atUri={m} /> 125 - ))} 126 127 - {hasNextPage && ( 128 - <button 129 - onClick={() => fetchNextPage()} 130 - disabled={isFetchingNextPage} 131 - className="w-[calc(100%-2rem)] mx-4 my-4 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 font-semibold disabled:opacity-50" 132 - > 133 - {isFetchingNextPage ? "Loading..." : "Load More"} 134 - </button> 135 - )} 136 - </> 137 - ); 138 } 139 140 - export function FollowsTab({did}:{did?:string}) { 141 - const { agent } = useAuth(); 142 - const userdidunsafe = did ?? agent?.did; 143 - const { data: identity} = useQueryIdentity(userdidunsafe); 144 - const userdid = identity?.did; 145 - 146 - const [constellationurl] = useAtom(constellationURLAtom); 147 - const infinitequeryresults = useInfiniteQuery({ 148 - ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( 149 - { 150 - constellation: constellationurl, 151 - method: "/links", 152 - target: userdid, 153 - collection: "app.bsky.graph.follow", 154 - path: ".subject", 155 - } 156 - ), 157 - enabled: !!userdid, 158 - }); 159 160 - const { 161 - data: infiniteFollowsData, 162 - fetchNextPage, 163 - hasNextPage, 164 - isFetchingNextPage, 165 - isLoading, 166 - isError, 167 - error, 168 - } = infinitequeryresults; 169 170 - const followsAturis = React.useMemo(() => { 171 - // Get all replies from the standard infinite query 172 - return ( 173 - infiniteFollowsData?.pages.flatMap( 174 - (page) => 175 - page?.linking_records.map( 176 - (r) => `at://${r.did}/${r.collection}/${r.rkey}` 177 - ) ?? [] 178 - ) ?? [] 179 - ); 180 - }, [infiniteFollowsData]); 181 182 - useReusableTabScrollRestore("Notifications"); 183 184 - if (isLoading) return <LoadingState text="Loading follows..." />; 185 - if (isError) return <ErrorState error={error} />; 186 187 - if (!followsAturis?.length) return <EmptyState text="No follows yet." />; 188 189 - return ( 190 - <> 191 - {followsAturis.map((m) => ( 192 - <NotificationItem key={m} notification={m} /> 193 - ))} 194 195 - {hasNextPage && ( 196 - <button 197 - onClick={() => fetchNextPage()} 198 - disabled={isFetchingNextPage} 199 - className="w-[calc(100%-2rem)] mx-4 my-4 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 font-semibold disabled:opacity-50" 200 - > 201 - {isFetchingNextPage ? "Loading..." : "Load More"} 202 - </button> 203 - )} 204 - </> 205 - ); 206 } 207 208 209 - export function BitesTab({did}:{did?:string}) { 210 - const { agent } = useAuth(); 211 - const userdidunsafe = did ?? agent?.did; 212 - const { data: identity} = useQueryIdentity(userdidunsafe); 213 - const userdid = identity?.did; 214 - 215 - const [constellationurl] = useAtom(constellationURLAtom); 216 - const infinitequeryresults = useInfiniteQuery({ 217 - ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( 218 - { 219 - constellation: constellationurl, 220 - method: "/links", 221 - target: "at://"+userdid, 222 - collection: "net.wafrn.feed.bite", 223 - path: ".subject", 224 - staleMult: 0 // safe fun 225 - } 226 - ), 227 - enabled: !!userdid, 228 - }); 229 230 - const { 231 - data: infiniteFollowsData, 232 - fetchNextPage, 233 - hasNextPage, 234 - isFetchingNextPage, 235 - isLoading, 236 - isError, 237 - error, 238 - } = infinitequeryresults; 239 240 - const followsAturis = React.useMemo(() => { 241 - // Get all replies from the standard infinite query 242 - return ( 243 - infiniteFollowsData?.pages.flatMap( 244 - (page) => 245 - page?.linking_records.map( 246 - (r) => `at://${r.did}/${r.collection}/${r.rkey}` 247 - ) ?? [] 248 - ) ?? [] 249 - ); 250 - }, [infiniteFollowsData]); 251 252 - useReusableTabScrollRestore("Notifications"); 253 254 - if (isLoading) return <LoadingState text="Loading bites..." />; 255 - if (isError) return <ErrorState error={error} />; 256 257 - if (!followsAturis?.length) return <EmptyState text="No bites yet." />; 258 259 - return ( 260 - <> 261 - {followsAturis.map((m) => ( 262 - <NotificationItem key={m} notification={m} /> 263 - ))} 264 265 - {hasNextPage && ( 266 - <button 267 - onClick={() => fetchNextPage()} 268 - disabled={isFetchingNextPage} 269 - className="w-[calc(100%-2rem)] mx-4 my-4 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 font-semibold disabled:opacity-50" 270 - > 271 - {isFetchingNextPage ? "Loading..." : "Load More"} 272 - </button> 273 - )} 274 - </> 275 - ); 276 } 277 278 function PostInteractionsTab() { 279 - const { agent } = useAuth(); 280 - const { data: identity } = useQueryIdentity(agent?.did); 281 - const queryClient = useQueryClient(); 282 - const { 283 - data: postsData, 284 - fetchNextPage, 285 - hasNextPage, 286 - isFetchingNextPage, 287 - isLoading: arePostsLoading, 288 - } = useInfiniteQueryAuthorFeed(agent?.did, identity?.pds); 289 290 - React.useEffect(() => { 291 - if (postsData) { 292 - postsData.pages.forEach((page) => { 293 - page.records.forEach((record) => { 294 - if (!queryClient.getQueryData(["post", record.uri])) { 295 - queryClient.setQueryData(["post", record.uri], record); 296 - } 297 - }); 298 - }); 299 - } 300 - }, [postsData, queryClient]); 301 302 - const posts = React.useMemo( 303 - () => postsData?.pages.flatMap((page) => page.records) ?? [], 304 - [postsData] 305 - ); 306 307 - useReusableTabScrollRestore("Notifications"); 308 309 - const [filters] = useAtom(postInteractionsFiltersAtom); 310 - const empty = (!filters.likes && !filters.quotes && !filters.replies && !filters.reposts); 311 312 - return ( 313 - <> 314 - <PostInteractionsFilterChipBar /> 315 - {!empty && posts.map((m) => ( 316 - <PostInteractionsItem key={m.uri} uri={m.uri} /> 317 - ))} 318 319 - {hasNextPage && ( 320 - <button 321 - onClick={() => fetchNextPage()} 322 - disabled={isFetchingNextPage} 323 - className="w-[calc(100%-2rem)] mx-4 my-4 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 font-semibold disabled:opacity-50" 324 - > 325 - {isFetchingNextPage ? "Loading..." : "Load More"} 326 - </button> 327 - )} 328 - </> 329 - ); 330 } 331 332 function PostInteractionsFilterChipBar() { 333 - const [filters, setFilters] = useAtom(postInteractionsFiltersAtom); 334 - // const empty = (!filters.likes && !filters.quotes && !filters.replies && !filters.reposts); 335 336 - // useEffect(() => { 337 - // if (empty) { 338 - // setFilters((prev) => ({ 339 - // ...prev, 340 - // likes: true, 341 - // })); 342 - // } 343 - // }, [ 344 - // empty, 345 - // setFilters, 346 - // ]); 347 348 - const toggle = (key: keyof typeof filters) => { 349 - setFilters((prev) => ({ 350 - ...prev, 351 - [key]: !prev[key], 352 - })); 353 - }; 354 355 - return ( 356 - <div className="flex flex-row flex-wrap gap-2 px-4 pt-4"> 357 - <Chip 358 - state={filters.likes} 359 - text="Likes" 360 - onClick={() => toggle("likes")} 361 - /> 362 - <Chip 363 - state={filters.reposts} 364 - text="Reposts" 365 - onClick={() => toggle("reposts")} 366 - /> 367 - <Chip 368 - state={filters.replies} 369 - text="Replies" 370 - onClick={() => toggle("replies")} 371 - /> 372 - <Chip 373 - state={filters.quotes} 374 - text="Quotes" 375 - onClick={() => toggle("quotes")} 376 - /> 377 - <Chip 378 - state={filters.showAll} 379 - text="Show All Metrics" 380 - onClick={() => toggle("showAll")} 381 - /> 382 - </div> 383 - ); 384 } 385 386 export function Chip({ 387 - state, 388 - text, 389 - onClick, 390 }: { 391 - state: boolean; 392 - text: string; 393 - onClick: React.MouseEventHandler<HTMLButtonElement>; 394 }) { 395 - return ( 396 - <button 397 - onClick={onClick} 398 - className={`relative inline-flex items-center px-3 py-1.5 rounded-lg text-sm font-medium transition-all 399 ${ 400 - state 401 - ? "bg-primary/20 text-primary bg-gray-200 dark:bg-gray-800 border border-transparent" 402 - : "bg-surface-container-low text-on-surface-variant border border-outline" 403 - } 404 hover:bg-primary/30 active:scale-[0.97] 405 dark:border-outline-variant 406 `} 407 - > 408 - {state && ( 409 - <IconMdiCheck 410 - className="mr-1.5 inline-block w-4 h-4 rounded-full bg-primary" 411 - aria-hidden 412 - /> 413 - )} 414 - {text} 415 - </button> 416 - ); 417 } 418 419 function PostInteractionsItem({ uri }: { uri: string }) { 420 - const [filters] = useAtom(postInteractionsFiltersAtom); 421 - const { data: links } = useQueryConstellation({ 422 - method: "/links/all", 423 - target: uri, 424 - }); 425 426 - const likes = 427 - links?.links?.["app.bsky.feed.like"]?.[".subject.uri"]?.records || 0; 428 - const replies = 429 - links?.links?.["app.bsky.feed.post"]?.[".reply.parent.uri"]?.records || 0; 430 - const reposts = 431 - links?.links?.["app.bsky.feed.repost"]?.[".subject.uri"]?.records || 0; 432 - const quotes1 = 433 - links?.links?.["app.bsky.feed.post"]?.[".embed.record.uri"]?.records || 0; 434 - const quotes2 = 435 - links?.links?.["app.bsky.feed.post"]?.[".embed.record.record.uri"] 436 - ?.records || 0; 437 - const quotes = quotes1 + quotes2; 438 439 - const all = likes + replies + reposts + quotes; 440 441 - //const failLikes = filters.likes && likes < 1; 442 - //const failReposts = filters.reposts && reposts < 1; 443 - //const failReplies = filters.replies && replies < 1; 444 - //const failQuotes = filters.quotes && quotes < 1; 445 446 - const showLikes = filters.showAll || filters.likes 447 - const showReposts = filters.showAll || filters.reposts 448 - const showReplies = filters.showAll || filters.replies 449 - const showQuotes = filters.showAll || filters.quotes 450 451 - //const showNone = !showLikes && !showReposts && !showReplies && !showQuotes; 452 453 - //const fail = failLikes || failReposts || failReplies || failQuotes || showNone; 454 455 - const matchesLikes = filters.likes && likes > 0; 456 - const matchesReposts = filters.reposts && reposts > 0; 457 - const matchesReplies = filters.replies && replies > 0; 458 - const matchesQuotes = filters.quotes && quotes > 0; 459 460 - const matchesAnything = 461 - // filters.showAll || 462 - matchesLikes || 463 - matchesReposts || 464 - matchesReplies || 465 - matchesQuotes; 466 467 - if (!matchesAnything) return null; 468 469 - //if (fail) return; 470 471 - return ( 472 - <div className="flex flex-col"> 473 - {/* <span>fail likes {failLikes ? "true" : "false"}</span> 474 <span>fail repost {failReposts ? "true" : "false"}</span> 475 <span>fail reply {failReplies ? "true" : "false"}</span> 476 <span>fail qupte {failQuotes ? "true" : "false"}</span> */} 477 - <div className="border rounded-xl mx-4 mt-4 overflow-hidden"> 478 - <UniversalPostRendererATURILoader 479 - isQuote 480 - key={uri} 481 - atUri={uri} 482 - nopics={true} 483 - concise={true} 484 - /> 485 - <div className="flex flex-col divide-x"> 486 - {showLikes &&(<InteractionsButton 487 - type={"like"} 488 - uri={uri} 489 - count={likes} 490 - />)} 491 - {showReposts && (<InteractionsButton 492 - type={"repost"} 493 - uri={uri} 494 - count={reposts} 495 - />)} 496 - {showReplies && (<InteractionsButton 497 - type={"reply"} 498 - uri={uri} 499 - count={replies} 500 - />)} 501 - {showQuotes && (<InteractionsButton 502 - type={"quote"} 503 - uri={uri} 504 - count={quotes} 505 - />)} 506 - {!all && ( 507 - <div className="text-center text-gray-500 dark:text-gray-400 pb-3 pt-2 border-t"> 508 - No interactions yet. 509 - </div> 510 - )} 511 - </div> 512 - </div> 513 - </div> 514 - ); 515 } 516 517 function InteractionsButton({ 518 - type, 519 - uri, 520 - count, 521 }: { 522 - type: "reply" | "repost" | "like" | "quote"; 523 - uri: string; 524 - count: number; 525 }) { 526 - if (!count) return <></>; 527 - const aturi = new AtUri(uri); 528 - return ( 529 - <Link 530 - to={ 531 - `/profile/$did/post/$rkey` + 532 - (type === "like" 533 - ? "/liked-by" 534 - : type === "repost" 535 - ? "/reposted-by" 536 - : type === "quote" 537 - ? "/quotes" 538 - : "") 539 - } 540 - params={{ 541 - did: aturi.host, 542 - rkey: aturi.rkey, 543 - }} 544 - className="flex-1 border-t py-2 px-4 flex flex-row items-center gap-2 transition-colors hover:bg-gray-100 hover:dark:bg-gray-800" 545 - > 546 - {type === "like" ? ( 547 - <MdiCardsHeartOutline height={22} width={22} /> 548 - ) : type === "repost" ? ( 549 - <MdiRepeat height={22} width={22} /> 550 - ) : type === "reply" ? ( 551 - <MdiCommentOutline height={22} width={22} /> 552 - ) : type === "quote" ? ( 553 - <IconMdiMessageReplyTextOutline 554 - height={22} 555 - width={22} 556 - className=" text-gray-400" 557 - /> 558 - ) : ( 559 - <></> 560 - )} 561 - {type === "like" 562 - ? "likes" 563 - : type === "reply" 564 - ? "replies" 565 - : type === "quote" 566 - ? "quotes" 567 - : type === "repost" 568 - ? "reposts" 569 - : ""} 570 - <div className="flex-1" /> {count} 571 - </Link> 572 - ); 573 } 574 575 export function NotificationItem({ notification }: { notification: string }) { 576 - const aturi = new AtUri(notification); 577 - const bite = aturi.collection === "net.wafrn.feed.bite"; 578 - const navigate = useNavigate(); 579 - const { data: identity } = useQueryIdentity(aturi.host); 580 - const resolvedDid = identity?.did; 581 - const profileUri = resolvedDid 582 - ? `at://${resolvedDid}/app.bsky.actor.profile/self` 583 - : undefined; 584 - const { data: profileRecord } = useQueryProfile(profileUri); 585 - const profile = profileRecord?.value; 586 587 - const [imgcdn] = useAtom(imgCDNAtom); 588 589 - function getAvatarUrl(p: typeof profile) { 590 - const link = p?.avatar?.ref?.["$link"]; 591 - if (!link || !resolvedDid) return null; 592 - return `https://${imgcdn}/img/avatar/plain/${resolvedDid}/${link}@jpeg`; 593 - } 594 595 - const avatar = getAvatarUrl(profile); 596 597 - return ( 598 - <div 599 - className="flex items-center p-4 cursor-pointer gap-3 justify-around border-b flex-row" 600 - onClick={() => 601 - aturi && 602 - navigate({ 603 - to: "/profile/$did", 604 - params: { did: aturi.host }, 605 - }) 606 - } 607 - > 608 - {/* <div> 609 {aturi.collection === "app.bsky.graph.follow" ? ( 610 <IconMdiAccountPlus /> 611 ) : aturi.collection === "app.bsky.feed.like" ? ( ··· 614 <></> 615 )} 616 </div> */} 617 - {profile ? ( 618 - <img 619 - src={avatar || defaultpfp} 620 - alt={identity?.handle} 621 - className="w-10 h-10 rounded-full" 622 - /> 623 - ) : ( 624 - <div className="w-10 h-10 rounded-full bg-gray-300 dark:bg-gray-700" /> 625 - )} 626 - <div className="flex flex-col min-w-0"> 627 - <div className="flex flex-row gap-2 overflow-hidden text-ellipsis whitespace-nowrap min-w-0"> 628 - <span className="font-medium text-gray-900 dark:text-gray-100 truncate"> 629 - {profile?.displayName || identity?.handle || "Someone"} 630 - </span> 631 - <span className="text-gray-700 dark:text-gray-400 truncate"> 632 - @{identity?.handle} 633 - </span> 634 - </div> 635 - <div className="flex flex-row gap-2"> 636 - {identity?.did && <Mutual targetdidorhandle={identity?.did} />} 637 - {/* <span className="text-sm text-gray-600 dark:text-gray-400"> 638 followed you 639 </span> */} 640 - </div> 641 - </div> 642 - <div className="flex-1" /> 643 - {identity?.did && <FollowButton targetdidorhandle={identity?.did} />} 644 - </div> 645 - ); 646 } 647 648 export const EmptyState = ({ text }: { text: string }) => ( 649 - <div className="py-10 text-center text-gray-500 dark:text-gray-400"> 650 - {text} 651 - </div> 652 ); 653 654 export const LoadingState = ({ text }: { text: string }) => ( 655 - <div className="py-10 text-center text-gray-500 dark:text-gray-400 italic"> 656 - {text} 657 - </div> 658 ); 659 660 export const ErrorState = ({ error }: { error: unknown }) => ( 661 - <div className="py-10 text-center text-red-600 dark:text-red-400"> 662 - Error: {(error as Error)?.message || "Something went wrong."} 663 - </div> 664 );
··· 7 import defaultpfp from "~/../public/favicon.png"; 8 import { Header } from "~/components/Header"; 9 import { 10 + ReusableTabRoute, 11 + useReusableTabScrollRestore, 12 } from "~/components/ReusableTabRoute"; 13 import { 14 + MdiCardsHeartOutline, 15 + MdiCommentOutline, 16 + MdiRepeat, 17 + UniversalPostRendererATURILoader, 18 } from "~/components/UniversalPostRenderer"; 19 import { useAuth } from "~/providers/UnifiedAuthProvider"; 20 import { 21 + constellationURLAtom, 22 + enableBitesAtom, 23 + imgCDNAtom, 24 + postInteractionsFiltersAtom, 25 } from "~/utils/atoms"; 26 import { 27 + useInfiniteQueryAuthorFeed, 28 + useQueryConstellation, 29 + useQueryIdentity, 30 + useQueryProfile, 31 + yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks, 32 } from "~/utils/useQuery"; 33 34 import { FollowButton, Mutual } from "./profile.$did"; 35 + import { ConditionalTextPlaceholder } from "~/components/placeholders/TextPlaceholder"; 36 37 export function NotificationsComponent() { 38 + return ( 39 + <div className=""> 40 + <Header 41 + title={`Notifications`} 42 + backButtonCallback={() => { 43 + if (window.history.length > 1) { 44 + window.history.back(); 45 + } else { 46 + window.location.assign("/"); 47 + } 48 + }} 49 + bottomBorderDisabled={true} 50 + /> 51 + <NotificationsTabs /> 52 + </div> 53 + ); 54 } 55 56 export const Route = createFileRoute("/notifications")({ 57 + component: NotificationsComponent, 58 }); 59 60 export default function NotificationsTabs() { 61 + const [bitesEnabled] = useAtom(enableBitesAtom); 62 + return ( 63 + <ReusableTabRoute 64 + route={`Notifications`} 65 + tabs={{ 66 + Mentions: <MentionsTab />, 67 + Follows: <FollowsTab />, 68 + "Post Interactions": <PostInteractionsTab />, 69 + ...(bitesEnabled 70 + ? { 71 + Bites: <BitesTab />, 72 + } 73 + : {}), 74 + }} 75 + /> 76 + ); 77 } 78 79 function MentionsTab() { 80 + const { agent } = useAuth(); 81 + const [constellationurl] = useAtom(constellationURLAtom); 82 + const infinitequeryresults = useInfiniteQuery({ 83 + ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( 84 + { 85 + constellation: constellationurl, 86 + method: "/links", 87 + target: agent?.did, 88 + collection: "app.bsky.feed.post", 89 + path: ".facets[app.bsky.richtext.facet].features[app.bsky.richtext.facet#mention].did", 90 + } 91 + ), 92 + enabled: !!agent?.did, 93 + }); 94 95 + const { 96 + data: infiniteMentionsData, 97 + fetchNextPage, 98 + hasNextPage, 99 + isFetchingNextPage, 100 + isLoading, 101 + isError, 102 + error, 103 + } = infinitequeryresults; 104 105 + const mentionsAturis = React.useMemo(() => { 106 + // Get all replies from the standard infinite query 107 + return ( 108 + infiniteMentionsData?.pages.flatMap( 109 + (page) => 110 + page?.linking_records.map( 111 + (r) => `at://${r.did}/${r.collection}/${r.rkey}` 112 + ) ?? [] 113 + ) ?? [] 114 + ); 115 + }, [infiniteMentionsData]); 116 117 + useReusableTabScrollRestore("Notifications"); 118 119 + if (isLoading) return <LoadingState text="Loading mentions..." />; 120 + if (isError) return <ErrorState error={error} />; 121 122 + if (!mentionsAturis?.length) return <EmptyState text="No mentions yet." />; 123 124 + return ( 125 + <> 126 + {mentionsAturis.map((m) => ( 127 + <UniversalPostRendererATURILoader key={m} atUri={m} /> 128 + ))} 129 130 + {hasNextPage && ( 131 + <button 132 + onClick={() => fetchNextPage()} 133 + disabled={isFetchingNextPage} 134 + className="w-[calc(100%-2rem)] mx-4 my-4 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 font-semibold disabled:opacity-50" 135 + > 136 + {isFetchingNextPage ? "Loading..." : "Load More"} 137 + </button> 138 + )} 139 + </> 140 + ); 141 } 142 143 + export function FollowsTab({ did }: { did?: string }) { 144 + const { agent } = useAuth(); 145 + const userdidunsafe = did ?? agent?.did; 146 + const { data: identity } = useQueryIdentity(userdidunsafe); 147 + const userdid = identity?.did; 148 149 + const [constellationurl] = useAtom(constellationURLAtom); 150 + const infinitequeryresults = useInfiniteQuery({ 151 + ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( 152 + { 153 + constellation: constellationurl, 154 + method: "/links", 155 + target: userdid, 156 + collection: "app.bsky.graph.follow", 157 + path: ".subject", 158 + } 159 + ), 160 + enabled: !!userdid, 161 + }); 162 + 163 + const { 164 + data: infiniteFollowsData, 165 + fetchNextPage, 166 + hasNextPage, 167 + isFetchingNextPage, 168 + isLoading, 169 + isError, 170 + error, 171 + } = infinitequeryresults; 172 173 + const followsAturis = React.useMemo(() => { 174 + // Get all replies from the standard infinite query 175 + return ( 176 + infiniteFollowsData?.pages.flatMap( 177 + (page) => 178 + page?.linking_records.map( 179 + (r) => `at://${r.did}/${r.collection}/${r.rkey}` 180 + ) ?? [] 181 + ) ?? [] 182 + ); 183 + }, [infiniteFollowsData]); 184 185 + useReusableTabScrollRestore("Notifications"); 186 187 + if (isLoading) return <LoadingState text="Loading follows..." />; 188 + if (isError) return <ErrorState error={error} />; 189 190 + if (!followsAturis?.length) return <EmptyState text="No follows yet." />; 191 192 + return ( 193 + <> 194 + {followsAturis.map((m) => ( 195 + <NotificationItem key={m} notification={m} /> 196 + ))} 197 198 + {hasNextPage && ( 199 + <button 200 + onClick={() => fetchNextPage()} 201 + disabled={isFetchingNextPage} 202 + className="w-[calc(100%-2rem)] mx-4 my-4 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 font-semibold disabled:opacity-50" 203 + > 204 + {isFetchingNextPage ? "Loading..." : "Load More"} 205 + </button> 206 + )} 207 + </> 208 + ); 209 } 210 211 + export function BitesTab({ did }: { did?: string }) { 212 + const { agent } = useAuth(); 213 + const userdidunsafe = did ?? agent?.did; 214 + const { data: identity } = useQueryIdentity(userdidunsafe); 215 + const userdid = identity?.did; 216 217 + const [constellationurl] = useAtom(constellationURLAtom); 218 + const infinitequeryresults = useInfiniteQuery({ 219 + ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( 220 + { 221 + constellation: constellationurl, 222 + method: "/links", 223 + target: "at://" + userdid, 224 + collection: "net.wafrn.feed.bite", 225 + path: ".subject", 226 + staleMult: 0, // safe fun 227 + } 228 + ), 229 + enabled: !!userdid, 230 + }); 231 232 + const { 233 + data: infiniteFollowsData, 234 + fetchNextPage, 235 + hasNextPage, 236 + isFetchingNextPage, 237 + isLoading, 238 + isError, 239 + error, 240 + } = infinitequeryresults; 241 242 + const followsAturis = React.useMemo(() => { 243 + // Get all replies from the standard infinite query 244 + return ( 245 + infiniteFollowsData?.pages.flatMap( 246 + (page) => 247 + page?.linking_records.map( 248 + (r) => `at://${r.did}/${r.collection}/${r.rkey}` 249 + ) ?? [] 250 + ) ?? [] 251 + ); 252 + }, [infiniteFollowsData]); 253 254 + useReusableTabScrollRestore("Notifications"); 255 256 + if (isLoading) return <LoadingState text="Loading bites..." />; 257 + if (isError) return <ErrorState error={error} />; 258 259 + if (!followsAturis?.length) return <EmptyState text="No bites yet." />; 260 261 + return ( 262 + <> 263 + {followsAturis.map((m) => ( 264 + <NotificationItem key={m} notification={m} /> 265 + ))} 266 267 + {hasNextPage && ( 268 + <button 269 + onClick={() => fetchNextPage()} 270 + disabled={isFetchingNextPage} 271 + className="w-[calc(100%-2rem)] mx-4 my-4 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 font-semibold disabled:opacity-50" 272 + > 273 + {isFetchingNextPage ? "Loading..." : "Load More"} 274 + </button> 275 + )} 276 + </> 277 + ); 278 } 279 280 function PostInteractionsTab() { 281 + const { agent } = useAuth(); 282 + const { data: identity } = useQueryIdentity(agent?.did); 283 + const queryClient = useQueryClient(); 284 + const { 285 + data: postsData, 286 + fetchNextPage, 287 + hasNextPage, 288 + isFetchingNextPage, 289 + isLoading: arePostsLoading, 290 + } = useInfiniteQueryAuthorFeed(agent?.did, identity?.pds); 291 292 + React.useEffect(() => { 293 + if (postsData) { 294 + postsData.pages.forEach((page) => { 295 + page.records.forEach((record) => { 296 + if (!queryClient.getQueryData(["post", record.uri])) { 297 + queryClient.setQueryData(["post", record.uri], record); 298 + } 299 + }); 300 + }); 301 + } 302 + }, [postsData, queryClient]); 303 304 + const posts = React.useMemo( 305 + () => postsData?.pages.flatMap((page) => page.records) ?? [], 306 + [postsData] 307 + ); 308 309 + useReusableTabScrollRestore("Notifications"); 310 311 + const [filters] = useAtom(postInteractionsFiltersAtom); 312 + const empty = 313 + !filters.likes && !filters.quotes && !filters.replies && !filters.reposts; 314 315 + return ( 316 + <> 317 + <PostInteractionsFilterChipBar /> 318 + {!empty && 319 + posts.map((m) => <PostInteractionsItem key={m.uri} uri={m.uri} />)} 320 321 + {hasNextPage && ( 322 + <button 323 + onClick={() => fetchNextPage()} 324 + disabled={isFetchingNextPage} 325 + className="w-[calc(100%-2rem)] mx-4 my-4 px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 font-semibold disabled:opacity-50" 326 + > 327 + {isFetchingNextPage ? "Loading..." : "Load More"} 328 + </button> 329 + )} 330 + </> 331 + ); 332 } 333 334 function PostInteractionsFilterChipBar() { 335 + const [filters, setFilters] = useAtom(postInteractionsFiltersAtom); 336 + // const empty = (!filters.likes && !filters.quotes && !filters.replies && !filters.reposts); 337 338 + // useEffect(() => { 339 + // if (empty) { 340 + // setFilters((prev) => ({ 341 + // ...prev, 342 + // likes: true, 343 + // })); 344 + // } 345 + // }, [ 346 + // empty, 347 + // setFilters, 348 + // ]); 349 350 + const toggle = (key: keyof typeof filters) => { 351 + setFilters((prev) => ({ 352 + ...prev, 353 + [key]: !prev[key], 354 + })); 355 + }; 356 357 + return ( 358 + <div className="flex flex-row flex-wrap gap-2 px-4 pt-4"> 359 + <Chip 360 + state={filters.likes} 361 + text="Likes" 362 + onClick={() => toggle("likes")} 363 + /> 364 + <Chip 365 + state={filters.reposts} 366 + text="Reposts" 367 + onClick={() => toggle("reposts")} 368 + /> 369 + <Chip 370 + state={filters.replies} 371 + text="Replies" 372 + onClick={() => toggle("replies")} 373 + /> 374 + <Chip 375 + state={filters.quotes} 376 + text="Quotes" 377 + onClick={() => toggle("quotes")} 378 + /> 379 + <Chip 380 + state={filters.showAll} 381 + text="Show All Metrics" 382 + onClick={() => toggle("showAll")} 383 + /> 384 + </div> 385 + ); 386 } 387 388 export function Chip({ 389 + state, 390 + text, 391 + onClick, 392 }: { 393 + state: boolean; 394 + text: string; 395 + onClick: React.MouseEventHandler<HTMLButtonElement>; 396 }) { 397 + return ( 398 + <button 399 + onClick={onClick} 400 + className={`relative inline-flex items-center px-3 py-1.5 rounded-lg text-sm font-medium transition-all 401 ${ 402 + state 403 + ? "bg-primary/20 text-primary bg-gray-200 dark:bg-gray-800 border border-transparent" 404 + : "bg-surface-container-low text-on-surface-variant border border-outline" 405 + } 406 hover:bg-primary/30 active:scale-[0.97] 407 dark:border-outline-variant 408 `} 409 + > 410 + {state && ( 411 + <IconMdiCheck 412 + className="mr-1.5 inline-block w-4 h-4 rounded-full bg-primary" 413 + aria-hidden 414 + /> 415 + )} 416 + {text} 417 + </button> 418 + ); 419 } 420 421 function PostInteractionsItem({ uri }: { uri: string }) { 422 + const [filters] = useAtom(postInteractionsFiltersAtom); 423 + const { data: links } = useQueryConstellation({ 424 + method: "/links/all", 425 + target: uri, 426 + }); 427 428 + const likes = 429 + links?.links?.["app.bsky.feed.like"]?.[".subject.uri"]?.records || 0; 430 + const replies = 431 + links?.links?.["app.bsky.feed.post"]?.[".reply.parent.uri"]?.records || 0; 432 + const reposts = 433 + links?.links?.["app.bsky.feed.repost"]?.[".subject.uri"]?.records || 0; 434 + const quotes1 = 435 + links?.links?.["app.bsky.feed.post"]?.[".embed.record.uri"]?.records || 0; 436 + const quotes2 = 437 + links?.links?.["app.bsky.feed.post"]?.[".embed.record.record.uri"] 438 + ?.records || 0; 439 + const quotes = quotes1 + quotes2; 440 441 + const all = likes + replies + reposts + quotes; 442 443 + //const failLikes = filters.likes && likes < 1; 444 + //const failReposts = filters.reposts && reposts < 1; 445 + //const failReplies = filters.replies && replies < 1; 446 + //const failQuotes = filters.quotes && quotes < 1; 447 448 + const showLikes = filters.showAll || filters.likes; 449 + const showReposts = filters.showAll || filters.reposts; 450 + const showReplies = filters.showAll || filters.replies; 451 + const showQuotes = filters.showAll || filters.quotes; 452 453 + //const showNone = !showLikes && !showReposts && !showReplies && !showQuotes; 454 455 + //const fail = failLikes || failReposts || failReplies || failQuotes || showNone; 456 457 + const matchesLikes = filters.likes && likes > 0; 458 + const matchesReposts = filters.reposts && reposts > 0; 459 + const matchesReplies = filters.replies && replies > 0; 460 + const matchesQuotes = filters.quotes && quotes > 0; 461 462 + const matchesAnything = 463 + // filters.showAll || 464 + matchesLikes || matchesReposts || matchesReplies || matchesQuotes; 465 466 + if (!matchesAnything) return null; 467 468 + //if (fail) return; 469 470 + return ( 471 + <div className="flex flex-col"> 472 + {/* <span>fail likes {failLikes ? "true" : "false"}</span> 473 <span>fail repost {failReposts ? "true" : "false"}</span> 474 <span>fail reply {failReplies ? "true" : "false"}</span> 475 <span>fail qupte {failQuotes ? "true" : "false"}</span> */} 476 + <div className="border rounded-xl mx-4 mt-4 overflow-hidden"> 477 + <UniversalPostRendererATURILoader 478 + isQuote 479 + key={uri} 480 + atUri={uri} 481 + nopics={true} 482 + concise={true} 483 + /> 484 + <div className="flex flex-col divide-x"> 485 + {showLikes && ( 486 + <InteractionsButton type={"like"} uri={uri} count={likes} /> 487 + )} 488 + {showReposts && ( 489 + <InteractionsButton type={"repost"} uri={uri} count={reposts} /> 490 + )} 491 + {showReplies && ( 492 + <InteractionsButton type={"reply"} uri={uri} count={replies} /> 493 + )} 494 + {showQuotes && ( 495 + <InteractionsButton type={"quote"} uri={uri} count={quotes} /> 496 + )} 497 + {!all && ( 498 + <div className="text-center text-gray-500 dark:text-gray-400 pb-3 pt-2 border-t"> 499 + No interactions yet. 500 + </div> 501 + )} 502 + </div> 503 + </div> 504 + </div> 505 + ); 506 } 507 508 function InteractionsButton({ 509 + type, 510 + uri, 511 + count, 512 }: { 513 + type: "reply" | "repost" | "like" | "quote"; 514 + uri: string; 515 + count: number; 516 }) { 517 + if (!count) return <></>; 518 + const aturi = new AtUri(uri); 519 + return ( 520 + <Link 521 + to={ 522 + `/profile/$did/post/$rkey` + 523 + (type === "like" 524 + ? "/liked-by" 525 + : type === "repost" 526 + ? "/reposted-by" 527 + : type === "quote" 528 + ? "/quotes" 529 + : "") 530 + } 531 + params={{ 532 + did: aturi.host, 533 + rkey: aturi.rkey, 534 + }} 535 + className="flex-1 border-t py-2 px-4 flex flex-row items-center gap-2 transition-colors hover:bg-gray-100 hover:dark:bg-gray-800" 536 + > 537 + {type === "like" ? ( 538 + <MdiCardsHeartOutline height={22} width={22} /> 539 + ) : type === "repost" ? ( 540 + <MdiRepeat height={22} width={22} /> 541 + ) : type === "reply" ? ( 542 + <MdiCommentOutline height={22} width={22} /> 543 + ) : type === "quote" ? ( 544 + <IconMdiMessageReplyTextOutline 545 + height={22} 546 + width={22} 547 + className=" text-gray-400" 548 + /> 549 + ) : ( 550 + <></> 551 + )} 552 + {type === "like" 553 + ? "likes" 554 + : type === "reply" 555 + ? "replies" 556 + : type === "quote" 557 + ? "quotes" 558 + : type === "repost" 559 + ? "reposts" 560 + : ""} 561 + <div className="flex-1" /> {count} 562 + </Link> 563 + ); 564 } 565 566 export function NotificationItem({ notification }: { notification: string }) { 567 + const aturi = new AtUri(notification); 568 + const bite = aturi.collection === "net.wafrn.feed.bite"; 569 + const navigate = useNavigate(); 570 + const { data: identity } = useQueryIdentity(aturi.host); 571 + const resolvedDid = identity?.did; 572 + const profileUri = resolvedDid 573 + ? `at://${resolvedDid}/app.bsky.actor.profile/self` 574 + : undefined; 575 + const { data: profileRecord } = useQueryProfile(profileUri); 576 + const profile = profileRecord?.value; 577 578 + const [imgcdn] = useAtom(imgCDNAtom); 579 580 + function getAvatarUrl(p: typeof profile) { 581 + const link = p?.avatar?.ref?.["$link"]; 582 + if (!link || !resolvedDid) return null; 583 + return `https://${imgcdn}/img/avatar/plain/${resolvedDid}/${link}@jpeg`; 584 + } 585 586 + const avatar = getAvatarUrl(profile); 587 588 + return ( 589 + <div 590 + className="flex items-center p-4 cursor-pointer gap-3 justify-around border-b flex-row" 591 + onClick={() => 592 + aturi && 593 + navigate({ 594 + to: "/profile/$did", 595 + params: { did: aturi.host }, 596 + }) 597 + } 598 + > 599 + {/* <div> 600 {aturi.collection === "app.bsky.graph.follow" ? ( 601 <IconMdiAccountPlus /> 602 ) : aturi.collection === "app.bsky.feed.like" ? ( ··· 605 <></> 606 )} 607 </div> */} 608 + {profile ? ( 609 + <img 610 + src={avatar || defaultpfp} 611 + alt={identity?.handle} 612 + className="w-10 h-10 rounded-full" 613 + /> 614 + ) : ( 615 + <div className="w-10 h-10 rounded-full bg-gray-300 dark:bg-gray-700" /> 616 + )} 617 + <div className="flex flex-col min-w-0 flex-grow"> 618 + <div className="flex flex-row gap-2 items-center overflow-hidden text-ellipsis whitespace-nowrap min-w-0"> 619 + <ConditionalTextPlaceholder 620 + show={profile != undefined} 621 + placeholderClassName="flex-grow" 622 + > 623 + <span className="font-medium text-gray-900 dark:text-gray-100 truncate"> 624 + {profile?.displayName || identity?.handle} 625 + </span> 626 + </ConditionalTextPlaceholder> 627 + <span className="text-gray-700 dark:text-gray-400 truncate"> 628 + @{identity?.handle} 629 + </span> 630 + </div> 631 + <div className="flex flex-row gap-2"> 632 + {identity?.did && <Mutual targetdidorhandle={identity?.did} />} 633 + {/* <span className="text-sm text-gray-600 dark:text-gray-400"> 634 followed you 635 </span> */} 636 + </div> 637 + </div> 638 + <div className="flex-1" /> 639 + {identity?.did && <FollowButton targetdidorhandle={identity?.did} />} 640 + </div> 641 + ); 642 } 643 644 export const EmptyState = ({ text }: { text: string }) => ( 645 + <div className="py-10 text-center text-gray-500 dark:text-gray-400"> 646 + {text} 647 + </div> 648 ); 649 650 export const LoadingState = ({ text }: { text: string }) => ( 651 + <div className="py-10 text-center text-gray-500 dark:text-gray-400 italic"> 652 + {text} 653 + </div> 654 ); 655 656 export const ErrorState = ({ error }: { error: unknown }) => ( 657 + <div className="py-10 text-center text-red-600 dark:text-red-400"> 658 + Error: {(error as Error)?.message || "Something went wrong."} 659 + </div> 660 );