ATProto forum built with ESAV

init

rimar1337 46243fdd

+11
.cta.json
··· 1 + { 2 + "projectName": "forumtest", 3 + "mode": "file-router", 4 + "typescript": true, 5 + "tailwind": false, 6 + "packageManager": "npm", 7 + "git": true, 8 + "version": 1, 9 + "framework": "react-cra", 10 + "chosenAddOns": [] 11 + }
+9
.gitignore
··· 1 + node_modules 2 + .DS_Store 3 + dist 4 + dist-ssr 5 + *.local 6 + count.txt 7 + .env 8 + .nitro 9 + .tanstack
+11
.vscode/settings.json
··· 1 + { 2 + "files.watcherExclude": { 3 + "**/routeTree.gen.ts": true 4 + }, 5 + "search.exclude": { 6 + "**/routeTree.gen.ts": true 7 + }, 8 + "files.readonlyInclude": { 9 + "**/routeTree.gen.ts": true 10 + } 11 + }
+73
README.md
··· 1 + # ForumTest - ATProto forum built with ESAV 2 + its an atproto forum built with [ESAV](https://tangled.sh/@whey.party/esav), an elasticsearch-based configurable generic appview. 3 + 4 + this project explores how far you can push ESAV as a backend with a example product (this forum atproto forum test thing). everything goes through ESAV, except for identity resolution, i have a deno deploy app that i use and share across multiple projects (is used in red dwarf too) 5 + 6 + i havent enabled a CDN yet so currently all images load using getBlob 7 + 8 + Live at: [https://forumtest.whey.party](https://forumtest.whey.party) 9 + Discuss at: [https://forumtest.whey.party/f/@forumtest.whey.party](https://forumtest.whey.party/f/@forumtest.whey.party) 10 + 11 + ## ESAV config 12 + custom record types: 13 + ```json 14 + "record_types": [ 15 + "com.example.ft.topic.post", 16 + "com.example.ft.topic.reaction", 17 + "com.example.ft.topic.moderation", 18 + "com.example.ft.forum.definition", 19 + "com.example.ft.forum.layout", 20 + "com.example.ft.forum.request", 21 + "com.example.ft.forum.accept", 22 + "com.example.ft.forum.category" 23 + ], 24 + ``` 25 + 26 + custom indexes: 27 + ```json 28 + "index_fields": { 29 + "com.example.ft.topic.reaction": { 30 + "subject": { 31 + "id": "reactionSubject", 32 + "type": "keyword" 33 + }, 34 + "reactionEmoji": { 35 + "id": "reactionEmoji", 36 + "type": "keyword" 37 + } 38 + }, 39 + "com.example.ft.topic.post": { 40 + "text": { 41 + "id": "text", 42 + "type": "text" 43 + }, 44 + "title": { 45 + "id": "title", 46 + "type": "text" 47 + }, 48 + "reply.root.uri": { 49 + "id": "root", 50 + "type": "keyword" 51 + }, 52 + "reply.parent.uri": { 53 + "id": "parent", 54 + "type": "keyword" 55 + }, 56 + "forum": { 57 + "id": "forum", 58 + "type": "keyword" 59 + } 60 + }, 61 + "com.example.ft.forum.definition": { 62 + "description": { 63 + "id": "description", 64 + "type": "text" 65 + }, 66 + "displayName": { 67 + "id": "displayName", 68 + "type": "text" 69 + } 70 + } 71 + } 72 + 73 + ```
+20
index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 + <link rel="icon" href="/favicon.ico" /> 7 + <meta name="theme-color" content="#000000" /> 8 + <meta 9 + name="description" 10 + content="atproto forum test" 11 + /> 12 + <link rel="apple-touch-icon" href="/logo192.png" /> 13 + <link rel="manifest" href="/manifest.json" /> 14 + <title>ForumTest</title> 15 + </head> 16 + <body class="bg-gray-900"> 17 + <div id="app" class="overflow-auto h-dvh max-h-dvh"></div> 18 + <script type="module" src="/src/main.tsx"></script> 19 + </body> 20 + </html>
+5209
package-lock.json
··· 1 + { 2 + "name": "forumtest", 3 + "lockfileVersion": 3, 4 + "requires": true, 5 + "packages": { 6 + "": { 7 + "name": "forumtest", 8 + "dependencies": { 9 + "@atproto/api": "^0.16.0", 10 + "@radix-ui/react-dialog": "^1.1.14", 11 + "@radix-ui/react-icons": "^1.3.2", 12 + "@radix-ui/react-popover": "^1.1.14", 13 + "@radix-ui/react-select": "^2.2.5", 14 + "@tailwindcss/vite": "^4.1.11", 15 + "@tanstack/react-query": "^5.84.1", 16 + "@tanstack/react-router": "^1.130.2", 17 + "@tanstack/react-router-devtools": "^1.130.2", 18 + "@tanstack/router-plugin": "^1.121.2", 19 + "idb-keyval": "^6.2.2", 20 + "react": "^19.0.0", 21 + "react-dom": "^19.0.0", 22 + "tailwindcss": "^4.1.11" 23 + }, 24 + "devDependencies": { 25 + "@testing-library/dom": "^10.4.0", 26 + "@testing-library/react": "^16.2.0", 27 + "@types/react": "^19.0.8", 28 + "@types/react-dom": "^19.0.3", 29 + "@vitejs/plugin-react": "^4.3.4", 30 + "jsdom": "^26.0.0", 31 + "typescript": "^5.7.2", 32 + "vite": "^6.3.5", 33 + "vitest": "^3.0.5", 34 + "web-vitals": "^4.2.4" 35 + } 36 + }, 37 + "node_modules/@ampproject/remapping": { 38 + "version": "2.3.0", 39 + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", 40 + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", 41 + "license": "Apache-2.0", 42 + "dependencies": { 43 + "@jridgewell/gen-mapping": "^0.3.5", 44 + "@jridgewell/trace-mapping": "^0.3.24" 45 + }, 46 + "engines": { 47 + "node": ">=6.0.0" 48 + } 49 + }, 50 + "node_modules/@asamuzakjp/css-color": { 51 + "version": "3.2.0", 52 + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", 53 + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", 54 + "dev": true, 55 + "license": "MIT", 56 + "dependencies": { 57 + "@csstools/css-calc": "^2.1.3", 58 + "@csstools/css-color-parser": "^3.0.9", 59 + "@csstools/css-parser-algorithms": "^3.0.4", 60 + "@csstools/css-tokenizer": "^3.0.3", 61 + "lru-cache": "^10.4.3" 62 + } 63 + }, 64 + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { 65 + "version": "10.4.3", 66 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 67 + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 68 + "dev": true, 69 + "license": "ISC" 70 + }, 71 + "node_modules/@atproto/api": { 72 + "version": "0.16.0", 73 + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.16.0.tgz", 74 + "integrity": "sha512-PQHeae6mz/L1YirUslfci7bknfg3RrSZjXpYwzLICxIOvqGKIkOi0+qukC2Py238RhXRo8YZ9dCuole9HQBXDw==", 75 + "license": "MIT", 76 + "dependencies": { 77 + "@atproto/common-web": "^0.4.2", 78 + "@atproto/lexicon": "^0.4.12", 79 + "@atproto/syntax": "^0.4.0", 80 + "@atproto/xrpc": "^0.7.1", 81 + "await-lock": "^2.2.2", 82 + "multiformats": "^9.9.0", 83 + "tlds": "^1.234.0", 84 + "zod": "^3.23.8" 85 + } 86 + }, 87 + "node_modules/@atproto/common-web": { 88 + "version": "0.4.2", 89 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.2.tgz", 90 + "integrity": "sha512-vrXwGNoFGogodjQvJDxAeP3QbGtawgZute2ed1XdRO0wMixLk3qewtikZm06H259QDJVu6voKC5mubml+WgQUw==", 91 + "license": "MIT", 92 + "dependencies": { 93 + "graphemer": "^1.4.0", 94 + "multiformats": "^9.9.0", 95 + "uint8arrays": "3.0.0", 96 + "zod": "^3.23.8" 97 + } 98 + }, 99 + "node_modules/@atproto/lexicon": { 100 + "version": "0.4.12", 101 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.4.12.tgz", 102 + "integrity": "sha512-fcEvEQ1GpQYF5igZ4IZjPWEoWVpsEF22L9RexxLS3ptfySXLflEyH384e7HITzO/73McDeaJx3lqHIuqn9ulnw==", 103 + "license": "MIT", 104 + "dependencies": { 105 + "@atproto/common-web": "^0.4.2", 106 + "@atproto/syntax": "^0.4.0", 107 + "iso-datestring-validator": "^2.2.2", 108 + "multiformats": "^9.9.0", 109 + "zod": "^3.23.8" 110 + } 111 + }, 112 + "node_modules/@atproto/syntax": { 113 + "version": "0.4.0", 114 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.0.tgz", 115 + "integrity": "sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA==", 116 + "license": "MIT" 117 + }, 118 + "node_modules/@atproto/xrpc": { 119 + "version": "0.7.1", 120 + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.1.tgz", 121 + "integrity": "sha512-ANHEzlskYlMEdH18m+Itp3a8d0pEJao2qoDybDoMupTnoeNkya4VKIaOgAi6ERQnqatBBZyn9asW+7rJmSt/8g==", 122 + "license": "MIT", 123 + "dependencies": { 124 + "@atproto/lexicon": "^0.4.12", 125 + "zod": "^3.23.8" 126 + } 127 + }, 128 + "node_modules/@babel/code-frame": { 129 + "version": "7.27.1", 130 + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", 131 + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", 132 + "license": "MIT", 133 + "dependencies": { 134 + "@babel/helper-validator-identifier": "^7.27.1", 135 + "js-tokens": "^4.0.0", 136 + "picocolors": "^1.1.1" 137 + }, 138 + "engines": { 139 + "node": ">=6.9.0" 140 + } 141 + }, 142 + "node_modules/@babel/compat-data": { 143 + "version": "7.28.0", 144 + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", 145 + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", 146 + "license": "MIT", 147 + "engines": { 148 + "node": ">=6.9.0" 149 + } 150 + }, 151 + "node_modules/@babel/core": { 152 + "version": "7.28.0", 153 + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", 154 + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", 155 + "license": "MIT", 156 + "dependencies": { 157 + "@ampproject/remapping": "^2.2.0", 158 + "@babel/code-frame": "^7.27.1", 159 + "@babel/generator": "^7.28.0", 160 + "@babel/helper-compilation-targets": "^7.27.2", 161 + "@babel/helper-module-transforms": "^7.27.3", 162 + "@babel/helpers": "^7.27.6", 163 + "@babel/parser": "^7.28.0", 164 + "@babel/template": "^7.27.2", 165 + "@babel/traverse": "^7.28.0", 166 + "@babel/types": "^7.28.0", 167 + "convert-source-map": "^2.0.0", 168 + "debug": "^4.1.0", 169 + "gensync": "^1.0.0-beta.2", 170 + "json5": "^2.2.3", 171 + "semver": "^6.3.1" 172 + }, 173 + "engines": { 174 + "node": ">=6.9.0" 175 + }, 176 + "funding": { 177 + "type": "opencollective", 178 + "url": "https://opencollective.com/babel" 179 + } 180 + }, 181 + "node_modules/@babel/generator": { 182 + "version": "7.28.0", 183 + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", 184 + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", 185 + "license": "MIT", 186 + "dependencies": { 187 + "@babel/parser": "^7.28.0", 188 + "@babel/types": "^7.28.0", 189 + "@jridgewell/gen-mapping": "^0.3.12", 190 + "@jridgewell/trace-mapping": "^0.3.28", 191 + "jsesc": "^3.0.2" 192 + }, 193 + "engines": { 194 + "node": ">=6.9.0" 195 + } 196 + }, 197 + "node_modules/@babel/helper-annotate-as-pure": { 198 + "version": "7.27.3", 199 + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", 200 + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", 201 + "license": "MIT", 202 + "dependencies": { 203 + "@babel/types": "^7.27.3" 204 + }, 205 + "engines": { 206 + "node": ">=6.9.0" 207 + } 208 + }, 209 + "node_modules/@babel/helper-compilation-targets": { 210 + "version": "7.27.2", 211 + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", 212 + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", 213 + "license": "MIT", 214 + "dependencies": { 215 + "@babel/compat-data": "^7.27.2", 216 + "@babel/helper-validator-option": "^7.27.1", 217 + "browserslist": "^4.24.0", 218 + "lru-cache": "^5.1.1", 219 + "semver": "^6.3.1" 220 + }, 221 + "engines": { 222 + "node": ">=6.9.0" 223 + } 224 + }, 225 + "node_modules/@babel/helper-create-class-features-plugin": { 226 + "version": "7.27.1", 227 + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", 228 + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", 229 + "license": "MIT", 230 + "dependencies": { 231 + "@babel/helper-annotate-as-pure": "^7.27.1", 232 + "@babel/helper-member-expression-to-functions": "^7.27.1", 233 + "@babel/helper-optimise-call-expression": "^7.27.1", 234 + "@babel/helper-replace-supers": "^7.27.1", 235 + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", 236 + "@babel/traverse": "^7.27.1", 237 + "semver": "^6.3.1" 238 + }, 239 + "engines": { 240 + "node": ">=6.9.0" 241 + }, 242 + "peerDependencies": { 243 + "@babel/core": "^7.0.0" 244 + } 245 + }, 246 + "node_modules/@babel/helper-globals": { 247 + "version": "7.28.0", 248 + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", 249 + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", 250 + "license": "MIT", 251 + "engines": { 252 + "node": ">=6.9.0" 253 + } 254 + }, 255 + "node_modules/@babel/helper-member-expression-to-functions": { 256 + "version": "7.27.1", 257 + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", 258 + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", 259 + "license": "MIT", 260 + "dependencies": { 261 + "@babel/traverse": "^7.27.1", 262 + "@babel/types": "^7.27.1" 263 + }, 264 + "engines": { 265 + "node": ">=6.9.0" 266 + } 267 + }, 268 + "node_modules/@babel/helper-module-imports": { 269 + "version": "7.27.1", 270 + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", 271 + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", 272 + "license": "MIT", 273 + "dependencies": { 274 + "@babel/traverse": "^7.27.1", 275 + "@babel/types": "^7.27.1" 276 + }, 277 + "engines": { 278 + "node": ">=6.9.0" 279 + } 280 + }, 281 + "node_modules/@babel/helper-module-transforms": { 282 + "version": "7.27.3", 283 + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", 284 + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", 285 + "license": "MIT", 286 + "dependencies": { 287 + "@babel/helper-module-imports": "^7.27.1", 288 + "@babel/helper-validator-identifier": "^7.27.1", 289 + "@babel/traverse": "^7.27.3" 290 + }, 291 + "engines": { 292 + "node": ">=6.9.0" 293 + }, 294 + "peerDependencies": { 295 + "@babel/core": "^7.0.0" 296 + } 297 + }, 298 + "node_modules/@babel/helper-optimise-call-expression": { 299 + "version": "7.27.1", 300 + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", 301 + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", 302 + "license": "MIT", 303 + "dependencies": { 304 + "@babel/types": "^7.27.1" 305 + }, 306 + "engines": { 307 + "node": ">=6.9.0" 308 + } 309 + }, 310 + "node_modules/@babel/helper-plugin-utils": { 311 + "version": "7.27.1", 312 + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", 313 + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", 314 + "license": "MIT", 315 + "engines": { 316 + "node": ">=6.9.0" 317 + } 318 + }, 319 + "node_modules/@babel/helper-replace-supers": { 320 + "version": "7.27.1", 321 + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", 322 + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", 323 + "license": "MIT", 324 + "dependencies": { 325 + "@babel/helper-member-expression-to-functions": "^7.27.1", 326 + "@babel/helper-optimise-call-expression": "^7.27.1", 327 + "@babel/traverse": "^7.27.1" 328 + }, 329 + "engines": { 330 + "node": ">=6.9.0" 331 + }, 332 + "peerDependencies": { 333 + "@babel/core": "^7.0.0" 334 + } 335 + }, 336 + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { 337 + "version": "7.27.1", 338 + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", 339 + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", 340 + "license": "MIT", 341 + "dependencies": { 342 + "@babel/traverse": "^7.27.1", 343 + "@babel/types": "^7.27.1" 344 + }, 345 + "engines": { 346 + "node": ">=6.9.0" 347 + } 348 + }, 349 + "node_modules/@babel/helper-string-parser": { 350 + "version": "7.27.1", 351 + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", 352 + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", 353 + "license": "MIT", 354 + "engines": { 355 + "node": ">=6.9.0" 356 + } 357 + }, 358 + "node_modules/@babel/helper-validator-identifier": { 359 + "version": "7.27.1", 360 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", 361 + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", 362 + "license": "MIT", 363 + "engines": { 364 + "node": ">=6.9.0" 365 + } 366 + }, 367 + "node_modules/@babel/helper-validator-option": { 368 + "version": "7.27.1", 369 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", 370 + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", 371 + "license": "MIT", 372 + "engines": { 373 + "node": ">=6.9.0" 374 + } 375 + }, 376 + "node_modules/@babel/helpers": { 377 + "version": "7.28.2", 378 + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", 379 + "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", 380 + "license": "MIT", 381 + "dependencies": { 382 + "@babel/template": "^7.27.2", 383 + "@babel/types": "^7.28.2" 384 + }, 385 + "engines": { 386 + "node": ">=6.9.0" 387 + } 388 + }, 389 + "node_modules/@babel/parser": { 390 + "version": "7.28.0", 391 + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", 392 + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", 393 + "license": "MIT", 394 + "dependencies": { 395 + "@babel/types": "^7.28.0" 396 + }, 397 + "bin": { 398 + "parser": "bin/babel-parser.js" 399 + }, 400 + "engines": { 401 + "node": ">=6.0.0" 402 + } 403 + }, 404 + "node_modules/@babel/plugin-syntax-jsx": { 405 + "version": "7.27.1", 406 + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", 407 + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", 408 + "license": "MIT", 409 + "dependencies": { 410 + "@babel/helper-plugin-utils": "^7.27.1" 411 + }, 412 + "engines": { 413 + "node": ">=6.9.0" 414 + }, 415 + "peerDependencies": { 416 + "@babel/core": "^7.0.0-0" 417 + } 418 + }, 419 + "node_modules/@babel/plugin-syntax-typescript": { 420 + "version": "7.27.1", 421 + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", 422 + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", 423 + "license": "MIT", 424 + "dependencies": { 425 + "@babel/helper-plugin-utils": "^7.27.1" 426 + }, 427 + "engines": { 428 + "node": ">=6.9.0" 429 + }, 430 + "peerDependencies": { 431 + "@babel/core": "^7.0.0-0" 432 + } 433 + }, 434 + "node_modules/@babel/plugin-transform-modules-commonjs": { 435 + "version": "7.27.1", 436 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", 437 + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", 438 + "license": "MIT", 439 + "dependencies": { 440 + "@babel/helper-module-transforms": "^7.27.1", 441 + "@babel/helper-plugin-utils": "^7.27.1" 442 + }, 443 + "engines": { 444 + "node": ">=6.9.0" 445 + }, 446 + "peerDependencies": { 447 + "@babel/core": "^7.0.0-0" 448 + } 449 + }, 450 + "node_modules/@babel/plugin-transform-react-jsx-self": { 451 + "version": "7.27.1", 452 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", 453 + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", 454 + "dev": true, 455 + "license": "MIT", 456 + "dependencies": { 457 + "@babel/helper-plugin-utils": "^7.27.1" 458 + }, 459 + "engines": { 460 + "node": ">=6.9.0" 461 + }, 462 + "peerDependencies": { 463 + "@babel/core": "^7.0.0-0" 464 + } 465 + }, 466 + "node_modules/@babel/plugin-transform-react-jsx-source": { 467 + "version": "7.27.1", 468 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", 469 + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", 470 + "dev": true, 471 + "license": "MIT", 472 + "dependencies": { 473 + "@babel/helper-plugin-utils": "^7.27.1" 474 + }, 475 + "engines": { 476 + "node": ">=6.9.0" 477 + }, 478 + "peerDependencies": { 479 + "@babel/core": "^7.0.0-0" 480 + } 481 + }, 482 + "node_modules/@babel/plugin-transform-typescript": { 483 + "version": "7.28.0", 484 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", 485 + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", 486 + "license": "MIT", 487 + "dependencies": { 488 + "@babel/helper-annotate-as-pure": "^7.27.3", 489 + "@babel/helper-create-class-features-plugin": "^7.27.1", 490 + "@babel/helper-plugin-utils": "^7.27.1", 491 + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", 492 + "@babel/plugin-syntax-typescript": "^7.27.1" 493 + }, 494 + "engines": { 495 + "node": ">=6.9.0" 496 + }, 497 + "peerDependencies": { 498 + "@babel/core": "^7.0.0-0" 499 + } 500 + }, 501 + "node_modules/@babel/preset-typescript": { 502 + "version": "7.27.1", 503 + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", 504 + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", 505 + "license": "MIT", 506 + "dependencies": { 507 + "@babel/helper-plugin-utils": "^7.27.1", 508 + "@babel/helper-validator-option": "^7.27.1", 509 + "@babel/plugin-syntax-jsx": "^7.27.1", 510 + "@babel/plugin-transform-modules-commonjs": "^7.27.1", 511 + "@babel/plugin-transform-typescript": "^7.27.1" 512 + }, 513 + "engines": { 514 + "node": ">=6.9.0" 515 + }, 516 + "peerDependencies": { 517 + "@babel/core": "^7.0.0-0" 518 + } 519 + }, 520 + "node_modules/@babel/runtime": { 521 + "version": "7.28.2", 522 + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", 523 + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", 524 + "dev": true, 525 + "license": "MIT", 526 + "engines": { 527 + "node": ">=6.9.0" 528 + } 529 + }, 530 + "node_modules/@babel/template": { 531 + "version": "7.27.2", 532 + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", 533 + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", 534 + "license": "MIT", 535 + "dependencies": { 536 + "@babel/code-frame": "^7.27.1", 537 + "@babel/parser": "^7.27.2", 538 + "@babel/types": "^7.27.1" 539 + }, 540 + "engines": { 541 + "node": ">=6.9.0" 542 + } 543 + }, 544 + "node_modules/@babel/traverse": { 545 + "version": "7.28.0", 546 + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", 547 + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", 548 + "license": "MIT", 549 + "dependencies": { 550 + "@babel/code-frame": "^7.27.1", 551 + "@babel/generator": "^7.28.0", 552 + "@babel/helper-globals": "^7.28.0", 553 + "@babel/parser": "^7.28.0", 554 + "@babel/template": "^7.27.2", 555 + "@babel/types": "^7.28.0", 556 + "debug": "^4.3.1" 557 + }, 558 + "engines": { 559 + "node": ">=6.9.0" 560 + } 561 + }, 562 + "node_modules/@babel/types": { 563 + "version": "7.28.2", 564 + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", 565 + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", 566 + "license": "MIT", 567 + "dependencies": { 568 + "@babel/helper-string-parser": "^7.27.1", 569 + "@babel/helper-validator-identifier": "^7.27.1" 570 + }, 571 + "engines": { 572 + "node": ">=6.9.0" 573 + } 574 + }, 575 + "node_modules/@csstools/color-helpers": { 576 + "version": "5.0.2", 577 + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", 578 + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", 579 + "dev": true, 580 + "funding": [ 581 + { 582 + "type": "github", 583 + "url": "https://github.com/sponsors/csstools" 584 + }, 585 + { 586 + "type": "opencollective", 587 + "url": "https://opencollective.com/csstools" 588 + } 589 + ], 590 + "license": "MIT-0", 591 + "engines": { 592 + "node": ">=18" 593 + } 594 + }, 595 + "node_modules/@csstools/css-calc": { 596 + "version": "2.1.4", 597 + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", 598 + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", 599 + "dev": true, 600 + "funding": [ 601 + { 602 + "type": "github", 603 + "url": "https://github.com/sponsors/csstools" 604 + }, 605 + { 606 + "type": "opencollective", 607 + "url": "https://opencollective.com/csstools" 608 + } 609 + ], 610 + "license": "MIT", 611 + "engines": { 612 + "node": ">=18" 613 + }, 614 + "peerDependencies": { 615 + "@csstools/css-parser-algorithms": "^3.0.5", 616 + "@csstools/css-tokenizer": "^3.0.4" 617 + } 618 + }, 619 + "node_modules/@csstools/css-color-parser": { 620 + "version": "3.0.10", 621 + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", 622 + "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", 623 + "dev": true, 624 + "funding": [ 625 + { 626 + "type": "github", 627 + "url": "https://github.com/sponsors/csstools" 628 + }, 629 + { 630 + "type": "opencollective", 631 + "url": "https://opencollective.com/csstools" 632 + } 633 + ], 634 + "license": "MIT", 635 + "dependencies": { 636 + "@csstools/color-helpers": "^5.0.2", 637 + "@csstools/css-calc": "^2.1.4" 638 + }, 639 + "engines": { 640 + "node": ">=18" 641 + }, 642 + "peerDependencies": { 643 + "@csstools/css-parser-algorithms": "^3.0.5", 644 + "@csstools/css-tokenizer": "^3.0.4" 645 + } 646 + }, 647 + "node_modules/@csstools/css-parser-algorithms": { 648 + "version": "3.0.5", 649 + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", 650 + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", 651 + "dev": true, 652 + "funding": [ 653 + { 654 + "type": "github", 655 + "url": "https://github.com/sponsors/csstools" 656 + }, 657 + { 658 + "type": "opencollective", 659 + "url": "https://opencollective.com/csstools" 660 + } 661 + ], 662 + "license": "MIT", 663 + "engines": { 664 + "node": ">=18" 665 + }, 666 + "peerDependencies": { 667 + "@csstools/css-tokenizer": "^3.0.4" 668 + } 669 + }, 670 + "node_modules/@csstools/css-tokenizer": { 671 + "version": "3.0.4", 672 + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", 673 + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", 674 + "dev": true, 675 + "funding": [ 676 + { 677 + "type": "github", 678 + "url": "https://github.com/sponsors/csstools" 679 + }, 680 + { 681 + "type": "opencollective", 682 + "url": "https://opencollective.com/csstools" 683 + } 684 + ], 685 + "license": "MIT", 686 + "engines": { 687 + "node": ">=18" 688 + } 689 + }, 690 + "node_modules/@esbuild/aix-ppc64": { 691 + "version": "0.25.8", 692 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", 693 + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", 694 + "cpu": [ 695 + "ppc64" 696 + ], 697 + "license": "MIT", 698 + "optional": true, 699 + "os": [ 700 + "aix" 701 + ], 702 + "engines": { 703 + "node": ">=18" 704 + } 705 + }, 706 + "node_modules/@esbuild/android-arm": { 707 + "version": "0.25.8", 708 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", 709 + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", 710 + "cpu": [ 711 + "arm" 712 + ], 713 + "license": "MIT", 714 + "optional": true, 715 + "os": [ 716 + "android" 717 + ], 718 + "engines": { 719 + "node": ">=18" 720 + } 721 + }, 722 + "node_modules/@esbuild/android-arm64": { 723 + "version": "0.25.8", 724 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", 725 + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", 726 + "cpu": [ 727 + "arm64" 728 + ], 729 + "license": "MIT", 730 + "optional": true, 731 + "os": [ 732 + "android" 733 + ], 734 + "engines": { 735 + "node": ">=18" 736 + } 737 + }, 738 + "node_modules/@esbuild/android-x64": { 739 + "version": "0.25.8", 740 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", 741 + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", 742 + "cpu": [ 743 + "x64" 744 + ], 745 + "license": "MIT", 746 + "optional": true, 747 + "os": [ 748 + "android" 749 + ], 750 + "engines": { 751 + "node": ">=18" 752 + } 753 + }, 754 + "node_modules/@esbuild/darwin-arm64": { 755 + "version": "0.25.8", 756 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", 757 + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", 758 + "cpu": [ 759 + "arm64" 760 + ], 761 + "license": "MIT", 762 + "optional": true, 763 + "os": [ 764 + "darwin" 765 + ], 766 + "engines": { 767 + "node": ">=18" 768 + } 769 + }, 770 + "node_modules/@esbuild/darwin-x64": { 771 + "version": "0.25.8", 772 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", 773 + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", 774 + "cpu": [ 775 + "x64" 776 + ], 777 + "license": "MIT", 778 + "optional": true, 779 + "os": [ 780 + "darwin" 781 + ], 782 + "engines": { 783 + "node": ">=18" 784 + } 785 + }, 786 + "node_modules/@esbuild/freebsd-arm64": { 787 + "version": "0.25.8", 788 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", 789 + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", 790 + "cpu": [ 791 + "arm64" 792 + ], 793 + "license": "MIT", 794 + "optional": true, 795 + "os": [ 796 + "freebsd" 797 + ], 798 + "engines": { 799 + "node": ">=18" 800 + } 801 + }, 802 + "node_modules/@esbuild/freebsd-x64": { 803 + "version": "0.25.8", 804 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", 805 + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", 806 + "cpu": [ 807 + "x64" 808 + ], 809 + "license": "MIT", 810 + "optional": true, 811 + "os": [ 812 + "freebsd" 813 + ], 814 + "engines": { 815 + "node": ">=18" 816 + } 817 + }, 818 + "node_modules/@esbuild/linux-arm": { 819 + "version": "0.25.8", 820 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", 821 + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", 822 + "cpu": [ 823 + "arm" 824 + ], 825 + "license": "MIT", 826 + "optional": true, 827 + "os": [ 828 + "linux" 829 + ], 830 + "engines": { 831 + "node": ">=18" 832 + } 833 + }, 834 + "node_modules/@esbuild/linux-arm64": { 835 + "version": "0.25.8", 836 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", 837 + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", 838 + "cpu": [ 839 + "arm64" 840 + ], 841 + "license": "MIT", 842 + "optional": true, 843 + "os": [ 844 + "linux" 845 + ], 846 + "engines": { 847 + "node": ">=18" 848 + } 849 + }, 850 + "node_modules/@esbuild/linux-ia32": { 851 + "version": "0.25.8", 852 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", 853 + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", 854 + "cpu": [ 855 + "ia32" 856 + ], 857 + "license": "MIT", 858 + "optional": true, 859 + "os": [ 860 + "linux" 861 + ], 862 + "engines": { 863 + "node": ">=18" 864 + } 865 + }, 866 + "node_modules/@esbuild/linux-loong64": { 867 + "version": "0.25.8", 868 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", 869 + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", 870 + "cpu": [ 871 + "loong64" 872 + ], 873 + "license": "MIT", 874 + "optional": true, 875 + "os": [ 876 + "linux" 877 + ], 878 + "engines": { 879 + "node": ">=18" 880 + } 881 + }, 882 + "node_modules/@esbuild/linux-mips64el": { 883 + "version": "0.25.8", 884 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", 885 + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", 886 + "cpu": [ 887 + "mips64el" 888 + ], 889 + "license": "MIT", 890 + "optional": true, 891 + "os": [ 892 + "linux" 893 + ], 894 + "engines": { 895 + "node": ">=18" 896 + } 897 + }, 898 + "node_modules/@esbuild/linux-ppc64": { 899 + "version": "0.25.8", 900 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", 901 + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", 902 + "cpu": [ 903 + "ppc64" 904 + ], 905 + "license": "MIT", 906 + "optional": true, 907 + "os": [ 908 + "linux" 909 + ], 910 + "engines": { 911 + "node": ">=18" 912 + } 913 + }, 914 + "node_modules/@esbuild/linux-riscv64": { 915 + "version": "0.25.8", 916 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", 917 + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", 918 + "cpu": [ 919 + "riscv64" 920 + ], 921 + "license": "MIT", 922 + "optional": true, 923 + "os": [ 924 + "linux" 925 + ], 926 + "engines": { 927 + "node": ">=18" 928 + } 929 + }, 930 + "node_modules/@esbuild/linux-s390x": { 931 + "version": "0.25.8", 932 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", 933 + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", 934 + "cpu": [ 935 + "s390x" 936 + ], 937 + "license": "MIT", 938 + "optional": true, 939 + "os": [ 940 + "linux" 941 + ], 942 + "engines": { 943 + "node": ">=18" 944 + } 945 + }, 946 + "node_modules/@esbuild/linux-x64": { 947 + "version": "0.25.8", 948 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", 949 + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", 950 + "cpu": [ 951 + "x64" 952 + ], 953 + "license": "MIT", 954 + "optional": true, 955 + "os": [ 956 + "linux" 957 + ], 958 + "engines": { 959 + "node": ">=18" 960 + } 961 + }, 962 + "node_modules/@esbuild/netbsd-arm64": { 963 + "version": "0.25.8", 964 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", 965 + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", 966 + "cpu": [ 967 + "arm64" 968 + ], 969 + "license": "MIT", 970 + "optional": true, 971 + "os": [ 972 + "netbsd" 973 + ], 974 + "engines": { 975 + "node": ">=18" 976 + } 977 + }, 978 + "node_modules/@esbuild/netbsd-x64": { 979 + "version": "0.25.8", 980 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", 981 + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", 982 + "cpu": [ 983 + "x64" 984 + ], 985 + "license": "MIT", 986 + "optional": true, 987 + "os": [ 988 + "netbsd" 989 + ], 990 + "engines": { 991 + "node": ">=18" 992 + } 993 + }, 994 + "node_modules/@esbuild/openbsd-arm64": { 995 + "version": "0.25.8", 996 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", 997 + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", 998 + "cpu": [ 999 + "arm64" 1000 + ], 1001 + "license": "MIT", 1002 + "optional": true, 1003 + "os": [ 1004 + "openbsd" 1005 + ], 1006 + "engines": { 1007 + "node": ">=18" 1008 + } 1009 + }, 1010 + "node_modules/@esbuild/openbsd-x64": { 1011 + "version": "0.25.8", 1012 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", 1013 + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", 1014 + "cpu": [ 1015 + "x64" 1016 + ], 1017 + "license": "MIT", 1018 + "optional": true, 1019 + "os": [ 1020 + "openbsd" 1021 + ], 1022 + "engines": { 1023 + "node": ">=18" 1024 + } 1025 + }, 1026 + "node_modules/@esbuild/openharmony-arm64": { 1027 + "version": "0.25.8", 1028 + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", 1029 + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", 1030 + "cpu": [ 1031 + "arm64" 1032 + ], 1033 + "license": "MIT", 1034 + "optional": true, 1035 + "os": [ 1036 + "openharmony" 1037 + ], 1038 + "engines": { 1039 + "node": ">=18" 1040 + } 1041 + }, 1042 + "node_modules/@esbuild/sunos-x64": { 1043 + "version": "0.25.8", 1044 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", 1045 + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", 1046 + "cpu": [ 1047 + "x64" 1048 + ], 1049 + "license": "MIT", 1050 + "optional": true, 1051 + "os": [ 1052 + "sunos" 1053 + ], 1054 + "engines": { 1055 + "node": ">=18" 1056 + } 1057 + }, 1058 + "node_modules/@esbuild/win32-arm64": { 1059 + "version": "0.25.8", 1060 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", 1061 + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", 1062 + "cpu": [ 1063 + "arm64" 1064 + ], 1065 + "license": "MIT", 1066 + "optional": true, 1067 + "os": [ 1068 + "win32" 1069 + ], 1070 + "engines": { 1071 + "node": ">=18" 1072 + } 1073 + }, 1074 + "node_modules/@esbuild/win32-ia32": { 1075 + "version": "0.25.8", 1076 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", 1077 + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", 1078 + "cpu": [ 1079 + "ia32" 1080 + ], 1081 + "license": "MIT", 1082 + "optional": true, 1083 + "os": [ 1084 + "win32" 1085 + ], 1086 + "engines": { 1087 + "node": ">=18" 1088 + } 1089 + }, 1090 + "node_modules/@esbuild/win32-x64": { 1091 + "version": "0.25.8", 1092 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", 1093 + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", 1094 + "cpu": [ 1095 + "x64" 1096 + ], 1097 + "license": "MIT", 1098 + "optional": true, 1099 + "os": [ 1100 + "win32" 1101 + ], 1102 + "engines": { 1103 + "node": ">=18" 1104 + } 1105 + }, 1106 + "node_modules/@floating-ui/core": { 1107 + "version": "1.7.3", 1108 + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", 1109 + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", 1110 + "license": "MIT", 1111 + "dependencies": { 1112 + "@floating-ui/utils": "^0.2.10" 1113 + } 1114 + }, 1115 + "node_modules/@floating-ui/dom": { 1116 + "version": "1.7.3", 1117 + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz", 1118 + "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", 1119 + "license": "MIT", 1120 + "dependencies": { 1121 + "@floating-ui/core": "^1.7.3", 1122 + "@floating-ui/utils": "^0.2.10" 1123 + } 1124 + }, 1125 + "node_modules/@floating-ui/react-dom": { 1126 + "version": "2.1.5", 1127 + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.5.tgz", 1128 + "integrity": "sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==", 1129 + "license": "MIT", 1130 + "dependencies": { 1131 + "@floating-ui/dom": "^1.7.3" 1132 + }, 1133 + "peerDependencies": { 1134 + "react": ">=16.8.0", 1135 + "react-dom": ">=16.8.0" 1136 + } 1137 + }, 1138 + "node_modules/@floating-ui/utils": { 1139 + "version": "0.2.10", 1140 + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", 1141 + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", 1142 + "license": "MIT" 1143 + }, 1144 + "node_modules/@isaacs/fs-minipass": { 1145 + "version": "4.0.1", 1146 + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", 1147 + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", 1148 + "license": "ISC", 1149 + "dependencies": { 1150 + "minipass": "^7.0.4" 1151 + }, 1152 + "engines": { 1153 + "node": ">=18.0.0" 1154 + } 1155 + }, 1156 + "node_modules/@jridgewell/gen-mapping": { 1157 + "version": "0.3.12", 1158 + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", 1159 + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", 1160 + "license": "MIT", 1161 + "dependencies": { 1162 + "@jridgewell/sourcemap-codec": "^1.5.0", 1163 + "@jridgewell/trace-mapping": "^0.3.24" 1164 + } 1165 + }, 1166 + "node_modules/@jridgewell/resolve-uri": { 1167 + "version": "3.1.2", 1168 + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 1169 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 1170 + "license": "MIT", 1171 + "engines": { 1172 + "node": ">=6.0.0" 1173 + } 1174 + }, 1175 + "node_modules/@jridgewell/sourcemap-codec": { 1176 + "version": "1.5.4", 1177 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", 1178 + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", 1179 + "license": "MIT" 1180 + }, 1181 + "node_modules/@jridgewell/trace-mapping": { 1182 + "version": "0.3.29", 1183 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", 1184 + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", 1185 + "license": "MIT", 1186 + "dependencies": { 1187 + "@jridgewell/resolve-uri": "^3.1.0", 1188 + "@jridgewell/sourcemap-codec": "^1.4.14" 1189 + } 1190 + }, 1191 + "node_modules/@radix-ui/number": { 1192 + "version": "1.1.1", 1193 + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", 1194 + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", 1195 + "license": "MIT" 1196 + }, 1197 + "node_modules/@radix-ui/primitive": { 1198 + "version": "1.1.2", 1199 + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", 1200 + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", 1201 + "license": "MIT" 1202 + }, 1203 + "node_modules/@radix-ui/react-arrow": { 1204 + "version": "1.1.7", 1205 + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", 1206 + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", 1207 + "license": "MIT", 1208 + "dependencies": { 1209 + "@radix-ui/react-primitive": "2.1.3" 1210 + }, 1211 + "peerDependencies": { 1212 + "@types/react": "*", 1213 + "@types/react-dom": "*", 1214 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1215 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1216 + }, 1217 + "peerDependenciesMeta": { 1218 + "@types/react": { 1219 + "optional": true 1220 + }, 1221 + "@types/react-dom": { 1222 + "optional": true 1223 + } 1224 + } 1225 + }, 1226 + "node_modules/@radix-ui/react-collection": { 1227 + "version": "1.1.7", 1228 + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", 1229 + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", 1230 + "license": "MIT", 1231 + "dependencies": { 1232 + "@radix-ui/react-compose-refs": "1.1.2", 1233 + "@radix-ui/react-context": "1.1.2", 1234 + "@radix-ui/react-primitive": "2.1.3", 1235 + "@radix-ui/react-slot": "1.2.3" 1236 + }, 1237 + "peerDependencies": { 1238 + "@types/react": "*", 1239 + "@types/react-dom": "*", 1240 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1241 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1242 + }, 1243 + "peerDependenciesMeta": { 1244 + "@types/react": { 1245 + "optional": true 1246 + }, 1247 + "@types/react-dom": { 1248 + "optional": true 1249 + } 1250 + } 1251 + }, 1252 + "node_modules/@radix-ui/react-compose-refs": { 1253 + "version": "1.1.2", 1254 + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", 1255 + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", 1256 + "license": "MIT", 1257 + "peerDependencies": { 1258 + "@types/react": "*", 1259 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1260 + }, 1261 + "peerDependenciesMeta": { 1262 + "@types/react": { 1263 + "optional": true 1264 + } 1265 + } 1266 + }, 1267 + "node_modules/@radix-ui/react-context": { 1268 + "version": "1.1.2", 1269 + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", 1270 + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", 1271 + "license": "MIT", 1272 + "peerDependencies": { 1273 + "@types/react": "*", 1274 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1275 + }, 1276 + "peerDependenciesMeta": { 1277 + "@types/react": { 1278 + "optional": true 1279 + } 1280 + } 1281 + }, 1282 + "node_modules/@radix-ui/react-dialog": { 1283 + "version": "1.1.14", 1284 + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", 1285 + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", 1286 + "license": "MIT", 1287 + "dependencies": { 1288 + "@radix-ui/primitive": "1.1.2", 1289 + "@radix-ui/react-compose-refs": "1.1.2", 1290 + "@radix-ui/react-context": "1.1.2", 1291 + "@radix-ui/react-dismissable-layer": "1.1.10", 1292 + "@radix-ui/react-focus-guards": "1.1.2", 1293 + "@radix-ui/react-focus-scope": "1.1.7", 1294 + "@radix-ui/react-id": "1.1.1", 1295 + "@radix-ui/react-portal": "1.1.9", 1296 + "@radix-ui/react-presence": "1.1.4", 1297 + "@radix-ui/react-primitive": "2.1.3", 1298 + "@radix-ui/react-slot": "1.2.3", 1299 + "@radix-ui/react-use-controllable-state": "1.2.2", 1300 + "aria-hidden": "^1.2.4", 1301 + "react-remove-scroll": "^2.6.3" 1302 + }, 1303 + "peerDependencies": { 1304 + "@types/react": "*", 1305 + "@types/react-dom": "*", 1306 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1307 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1308 + }, 1309 + "peerDependenciesMeta": { 1310 + "@types/react": { 1311 + "optional": true 1312 + }, 1313 + "@types/react-dom": { 1314 + "optional": true 1315 + } 1316 + } 1317 + }, 1318 + "node_modules/@radix-ui/react-direction": { 1319 + "version": "1.1.1", 1320 + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", 1321 + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", 1322 + "license": "MIT", 1323 + "peerDependencies": { 1324 + "@types/react": "*", 1325 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1326 + }, 1327 + "peerDependenciesMeta": { 1328 + "@types/react": { 1329 + "optional": true 1330 + } 1331 + } 1332 + }, 1333 + "node_modules/@radix-ui/react-dismissable-layer": { 1334 + "version": "1.1.10", 1335 + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", 1336 + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", 1337 + "license": "MIT", 1338 + "dependencies": { 1339 + "@radix-ui/primitive": "1.1.2", 1340 + "@radix-ui/react-compose-refs": "1.1.2", 1341 + "@radix-ui/react-primitive": "2.1.3", 1342 + "@radix-ui/react-use-callback-ref": "1.1.1", 1343 + "@radix-ui/react-use-escape-keydown": "1.1.1" 1344 + }, 1345 + "peerDependencies": { 1346 + "@types/react": "*", 1347 + "@types/react-dom": "*", 1348 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1349 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1350 + }, 1351 + "peerDependenciesMeta": { 1352 + "@types/react": { 1353 + "optional": true 1354 + }, 1355 + "@types/react-dom": { 1356 + "optional": true 1357 + } 1358 + } 1359 + }, 1360 + "node_modules/@radix-ui/react-focus-guards": { 1361 + "version": "1.1.2", 1362 + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", 1363 + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", 1364 + "license": "MIT", 1365 + "peerDependencies": { 1366 + "@types/react": "*", 1367 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1368 + }, 1369 + "peerDependenciesMeta": { 1370 + "@types/react": { 1371 + "optional": true 1372 + } 1373 + } 1374 + }, 1375 + "node_modules/@radix-ui/react-focus-scope": { 1376 + "version": "1.1.7", 1377 + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", 1378 + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", 1379 + "license": "MIT", 1380 + "dependencies": { 1381 + "@radix-ui/react-compose-refs": "1.1.2", 1382 + "@radix-ui/react-primitive": "2.1.3", 1383 + "@radix-ui/react-use-callback-ref": "1.1.1" 1384 + }, 1385 + "peerDependencies": { 1386 + "@types/react": "*", 1387 + "@types/react-dom": "*", 1388 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1389 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1390 + }, 1391 + "peerDependenciesMeta": { 1392 + "@types/react": { 1393 + "optional": true 1394 + }, 1395 + "@types/react-dom": { 1396 + "optional": true 1397 + } 1398 + } 1399 + }, 1400 + "node_modules/@radix-ui/react-icons": { 1401 + "version": "1.3.2", 1402 + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz", 1403 + "integrity": "sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==", 1404 + "license": "MIT", 1405 + "peerDependencies": { 1406 + "react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc" 1407 + } 1408 + }, 1409 + "node_modules/@radix-ui/react-id": { 1410 + "version": "1.1.1", 1411 + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", 1412 + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", 1413 + "license": "MIT", 1414 + "dependencies": { 1415 + "@radix-ui/react-use-layout-effect": "1.1.1" 1416 + }, 1417 + "peerDependencies": { 1418 + "@types/react": "*", 1419 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1420 + }, 1421 + "peerDependenciesMeta": { 1422 + "@types/react": { 1423 + "optional": true 1424 + } 1425 + } 1426 + }, 1427 + "node_modules/@radix-ui/react-popover": { 1428 + "version": "1.1.14", 1429 + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", 1430 + "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", 1431 + "license": "MIT", 1432 + "dependencies": { 1433 + "@radix-ui/primitive": "1.1.2", 1434 + "@radix-ui/react-compose-refs": "1.1.2", 1435 + "@radix-ui/react-context": "1.1.2", 1436 + "@radix-ui/react-dismissable-layer": "1.1.10", 1437 + "@radix-ui/react-focus-guards": "1.1.2", 1438 + "@radix-ui/react-focus-scope": "1.1.7", 1439 + "@radix-ui/react-id": "1.1.1", 1440 + "@radix-ui/react-popper": "1.2.7", 1441 + "@radix-ui/react-portal": "1.1.9", 1442 + "@radix-ui/react-presence": "1.1.4", 1443 + "@radix-ui/react-primitive": "2.1.3", 1444 + "@radix-ui/react-slot": "1.2.3", 1445 + "@radix-ui/react-use-controllable-state": "1.2.2", 1446 + "aria-hidden": "^1.2.4", 1447 + "react-remove-scroll": "^2.6.3" 1448 + }, 1449 + "peerDependencies": { 1450 + "@types/react": "*", 1451 + "@types/react-dom": "*", 1452 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1453 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1454 + }, 1455 + "peerDependenciesMeta": { 1456 + "@types/react": { 1457 + "optional": true 1458 + }, 1459 + "@types/react-dom": { 1460 + "optional": true 1461 + } 1462 + } 1463 + }, 1464 + "node_modules/@radix-ui/react-popper": { 1465 + "version": "1.2.7", 1466 + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", 1467 + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", 1468 + "license": "MIT", 1469 + "dependencies": { 1470 + "@floating-ui/react-dom": "^2.0.0", 1471 + "@radix-ui/react-arrow": "1.1.7", 1472 + "@radix-ui/react-compose-refs": "1.1.2", 1473 + "@radix-ui/react-context": "1.1.2", 1474 + "@radix-ui/react-primitive": "2.1.3", 1475 + "@radix-ui/react-use-callback-ref": "1.1.1", 1476 + "@radix-ui/react-use-layout-effect": "1.1.1", 1477 + "@radix-ui/react-use-rect": "1.1.1", 1478 + "@radix-ui/react-use-size": "1.1.1", 1479 + "@radix-ui/rect": "1.1.1" 1480 + }, 1481 + "peerDependencies": { 1482 + "@types/react": "*", 1483 + "@types/react-dom": "*", 1484 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1485 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1486 + }, 1487 + "peerDependenciesMeta": { 1488 + "@types/react": { 1489 + "optional": true 1490 + }, 1491 + "@types/react-dom": { 1492 + "optional": true 1493 + } 1494 + } 1495 + }, 1496 + "node_modules/@radix-ui/react-portal": { 1497 + "version": "1.1.9", 1498 + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", 1499 + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", 1500 + "license": "MIT", 1501 + "dependencies": { 1502 + "@radix-ui/react-primitive": "2.1.3", 1503 + "@radix-ui/react-use-layout-effect": "1.1.1" 1504 + }, 1505 + "peerDependencies": { 1506 + "@types/react": "*", 1507 + "@types/react-dom": "*", 1508 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1509 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1510 + }, 1511 + "peerDependenciesMeta": { 1512 + "@types/react": { 1513 + "optional": true 1514 + }, 1515 + "@types/react-dom": { 1516 + "optional": true 1517 + } 1518 + } 1519 + }, 1520 + "node_modules/@radix-ui/react-presence": { 1521 + "version": "1.1.4", 1522 + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", 1523 + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", 1524 + "license": "MIT", 1525 + "dependencies": { 1526 + "@radix-ui/react-compose-refs": "1.1.2", 1527 + "@radix-ui/react-use-layout-effect": "1.1.1" 1528 + }, 1529 + "peerDependencies": { 1530 + "@types/react": "*", 1531 + "@types/react-dom": "*", 1532 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1533 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1534 + }, 1535 + "peerDependenciesMeta": { 1536 + "@types/react": { 1537 + "optional": true 1538 + }, 1539 + "@types/react-dom": { 1540 + "optional": true 1541 + } 1542 + } 1543 + }, 1544 + "node_modules/@radix-ui/react-primitive": { 1545 + "version": "2.1.3", 1546 + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", 1547 + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", 1548 + "license": "MIT", 1549 + "dependencies": { 1550 + "@radix-ui/react-slot": "1.2.3" 1551 + }, 1552 + "peerDependencies": { 1553 + "@types/react": "*", 1554 + "@types/react-dom": "*", 1555 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1556 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1557 + }, 1558 + "peerDependenciesMeta": { 1559 + "@types/react": { 1560 + "optional": true 1561 + }, 1562 + "@types/react-dom": { 1563 + "optional": true 1564 + } 1565 + } 1566 + }, 1567 + "node_modules/@radix-ui/react-select": { 1568 + "version": "2.2.5", 1569 + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz", 1570 + "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", 1571 + "license": "MIT", 1572 + "dependencies": { 1573 + "@radix-ui/number": "1.1.1", 1574 + "@radix-ui/primitive": "1.1.2", 1575 + "@radix-ui/react-collection": "1.1.7", 1576 + "@radix-ui/react-compose-refs": "1.1.2", 1577 + "@radix-ui/react-context": "1.1.2", 1578 + "@radix-ui/react-direction": "1.1.1", 1579 + "@radix-ui/react-dismissable-layer": "1.1.10", 1580 + "@radix-ui/react-focus-guards": "1.1.2", 1581 + "@radix-ui/react-focus-scope": "1.1.7", 1582 + "@radix-ui/react-id": "1.1.1", 1583 + "@radix-ui/react-popper": "1.2.7", 1584 + "@radix-ui/react-portal": "1.1.9", 1585 + "@radix-ui/react-primitive": "2.1.3", 1586 + "@radix-ui/react-slot": "1.2.3", 1587 + "@radix-ui/react-use-callback-ref": "1.1.1", 1588 + "@radix-ui/react-use-controllable-state": "1.2.2", 1589 + "@radix-ui/react-use-layout-effect": "1.1.1", 1590 + "@radix-ui/react-use-previous": "1.1.1", 1591 + "@radix-ui/react-visually-hidden": "1.2.3", 1592 + "aria-hidden": "^1.2.4", 1593 + "react-remove-scroll": "^2.6.3" 1594 + }, 1595 + "peerDependencies": { 1596 + "@types/react": "*", 1597 + "@types/react-dom": "*", 1598 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1599 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1600 + }, 1601 + "peerDependenciesMeta": { 1602 + "@types/react": { 1603 + "optional": true 1604 + }, 1605 + "@types/react-dom": { 1606 + "optional": true 1607 + } 1608 + } 1609 + }, 1610 + "node_modules/@radix-ui/react-slot": { 1611 + "version": "1.2.3", 1612 + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", 1613 + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", 1614 + "license": "MIT", 1615 + "dependencies": { 1616 + "@radix-ui/react-compose-refs": "1.1.2" 1617 + }, 1618 + "peerDependencies": { 1619 + "@types/react": "*", 1620 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1621 + }, 1622 + "peerDependenciesMeta": { 1623 + "@types/react": { 1624 + "optional": true 1625 + } 1626 + } 1627 + }, 1628 + "node_modules/@radix-ui/react-use-callback-ref": { 1629 + "version": "1.1.1", 1630 + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", 1631 + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", 1632 + "license": "MIT", 1633 + "peerDependencies": { 1634 + "@types/react": "*", 1635 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1636 + }, 1637 + "peerDependenciesMeta": { 1638 + "@types/react": { 1639 + "optional": true 1640 + } 1641 + } 1642 + }, 1643 + "node_modules/@radix-ui/react-use-controllable-state": { 1644 + "version": "1.2.2", 1645 + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", 1646 + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", 1647 + "license": "MIT", 1648 + "dependencies": { 1649 + "@radix-ui/react-use-effect-event": "0.0.2", 1650 + "@radix-ui/react-use-layout-effect": "1.1.1" 1651 + }, 1652 + "peerDependencies": { 1653 + "@types/react": "*", 1654 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1655 + }, 1656 + "peerDependenciesMeta": { 1657 + "@types/react": { 1658 + "optional": true 1659 + } 1660 + } 1661 + }, 1662 + "node_modules/@radix-ui/react-use-effect-event": { 1663 + "version": "0.0.2", 1664 + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", 1665 + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", 1666 + "license": "MIT", 1667 + "dependencies": { 1668 + "@radix-ui/react-use-layout-effect": "1.1.1" 1669 + }, 1670 + "peerDependencies": { 1671 + "@types/react": "*", 1672 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1673 + }, 1674 + "peerDependenciesMeta": { 1675 + "@types/react": { 1676 + "optional": true 1677 + } 1678 + } 1679 + }, 1680 + "node_modules/@radix-ui/react-use-escape-keydown": { 1681 + "version": "1.1.1", 1682 + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", 1683 + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", 1684 + "license": "MIT", 1685 + "dependencies": { 1686 + "@radix-ui/react-use-callback-ref": "1.1.1" 1687 + }, 1688 + "peerDependencies": { 1689 + "@types/react": "*", 1690 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1691 + }, 1692 + "peerDependenciesMeta": { 1693 + "@types/react": { 1694 + "optional": true 1695 + } 1696 + } 1697 + }, 1698 + "node_modules/@radix-ui/react-use-layout-effect": { 1699 + "version": "1.1.1", 1700 + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", 1701 + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", 1702 + "license": "MIT", 1703 + "peerDependencies": { 1704 + "@types/react": "*", 1705 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1706 + }, 1707 + "peerDependenciesMeta": { 1708 + "@types/react": { 1709 + "optional": true 1710 + } 1711 + } 1712 + }, 1713 + "node_modules/@radix-ui/react-use-previous": { 1714 + "version": "1.1.1", 1715 + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", 1716 + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", 1717 + "license": "MIT", 1718 + "peerDependencies": { 1719 + "@types/react": "*", 1720 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1721 + }, 1722 + "peerDependenciesMeta": { 1723 + "@types/react": { 1724 + "optional": true 1725 + } 1726 + } 1727 + }, 1728 + "node_modules/@radix-ui/react-use-rect": { 1729 + "version": "1.1.1", 1730 + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", 1731 + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", 1732 + "license": "MIT", 1733 + "dependencies": { 1734 + "@radix-ui/rect": "1.1.1" 1735 + }, 1736 + "peerDependencies": { 1737 + "@types/react": "*", 1738 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1739 + }, 1740 + "peerDependenciesMeta": { 1741 + "@types/react": { 1742 + "optional": true 1743 + } 1744 + } 1745 + }, 1746 + "node_modules/@radix-ui/react-use-size": { 1747 + "version": "1.1.1", 1748 + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", 1749 + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", 1750 + "license": "MIT", 1751 + "dependencies": { 1752 + "@radix-ui/react-use-layout-effect": "1.1.1" 1753 + }, 1754 + "peerDependencies": { 1755 + "@types/react": "*", 1756 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1757 + }, 1758 + "peerDependenciesMeta": { 1759 + "@types/react": { 1760 + "optional": true 1761 + } 1762 + } 1763 + }, 1764 + "node_modules/@radix-ui/react-visually-hidden": { 1765 + "version": "1.2.3", 1766 + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", 1767 + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", 1768 + "license": "MIT", 1769 + "dependencies": { 1770 + "@radix-ui/react-primitive": "2.1.3" 1771 + }, 1772 + "peerDependencies": { 1773 + "@types/react": "*", 1774 + "@types/react-dom": "*", 1775 + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", 1776 + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" 1777 + }, 1778 + "peerDependenciesMeta": { 1779 + "@types/react": { 1780 + "optional": true 1781 + }, 1782 + "@types/react-dom": { 1783 + "optional": true 1784 + } 1785 + } 1786 + }, 1787 + "node_modules/@radix-ui/rect": { 1788 + "version": "1.1.1", 1789 + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", 1790 + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", 1791 + "license": "MIT" 1792 + }, 1793 + "node_modules/@rolldown/pluginutils": { 1794 + "version": "1.0.0-beta.27", 1795 + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", 1796 + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", 1797 + "dev": true, 1798 + "license": "MIT" 1799 + }, 1800 + "node_modules/@rollup/rollup-android-arm-eabi": { 1801 + "version": "4.46.2", 1802 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", 1803 + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", 1804 + "cpu": [ 1805 + "arm" 1806 + ], 1807 + "license": "MIT", 1808 + "optional": true, 1809 + "os": [ 1810 + "android" 1811 + ] 1812 + }, 1813 + "node_modules/@rollup/rollup-android-arm64": { 1814 + "version": "4.46.2", 1815 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", 1816 + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", 1817 + "cpu": [ 1818 + "arm64" 1819 + ], 1820 + "license": "MIT", 1821 + "optional": true, 1822 + "os": [ 1823 + "android" 1824 + ] 1825 + }, 1826 + "node_modules/@rollup/rollup-darwin-arm64": { 1827 + "version": "4.46.2", 1828 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", 1829 + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", 1830 + "cpu": [ 1831 + "arm64" 1832 + ], 1833 + "license": "MIT", 1834 + "optional": true, 1835 + "os": [ 1836 + "darwin" 1837 + ] 1838 + }, 1839 + "node_modules/@rollup/rollup-darwin-x64": { 1840 + "version": "4.46.2", 1841 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", 1842 + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", 1843 + "cpu": [ 1844 + "x64" 1845 + ], 1846 + "license": "MIT", 1847 + "optional": true, 1848 + "os": [ 1849 + "darwin" 1850 + ] 1851 + }, 1852 + "node_modules/@rollup/rollup-freebsd-arm64": { 1853 + "version": "4.46.2", 1854 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", 1855 + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", 1856 + "cpu": [ 1857 + "arm64" 1858 + ], 1859 + "license": "MIT", 1860 + "optional": true, 1861 + "os": [ 1862 + "freebsd" 1863 + ] 1864 + }, 1865 + "node_modules/@rollup/rollup-freebsd-x64": { 1866 + "version": "4.46.2", 1867 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", 1868 + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", 1869 + "cpu": [ 1870 + "x64" 1871 + ], 1872 + "license": "MIT", 1873 + "optional": true, 1874 + "os": [ 1875 + "freebsd" 1876 + ] 1877 + }, 1878 + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 1879 + "version": "4.46.2", 1880 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", 1881 + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", 1882 + "cpu": [ 1883 + "arm" 1884 + ], 1885 + "license": "MIT", 1886 + "optional": true, 1887 + "os": [ 1888 + "linux" 1889 + ] 1890 + }, 1891 + "node_modules/@rollup/rollup-linux-arm-musleabihf": { 1892 + "version": "4.46.2", 1893 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", 1894 + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", 1895 + "cpu": [ 1896 + "arm" 1897 + ], 1898 + "license": "MIT", 1899 + "optional": true, 1900 + "os": [ 1901 + "linux" 1902 + ] 1903 + }, 1904 + "node_modules/@rollup/rollup-linux-arm64-gnu": { 1905 + "version": "4.46.2", 1906 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", 1907 + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", 1908 + "cpu": [ 1909 + "arm64" 1910 + ], 1911 + "license": "MIT", 1912 + "optional": true, 1913 + "os": [ 1914 + "linux" 1915 + ] 1916 + }, 1917 + "node_modules/@rollup/rollup-linux-arm64-musl": { 1918 + "version": "4.46.2", 1919 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", 1920 + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", 1921 + "cpu": [ 1922 + "arm64" 1923 + ], 1924 + "license": "MIT", 1925 + "optional": true, 1926 + "os": [ 1927 + "linux" 1928 + ] 1929 + }, 1930 + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { 1931 + "version": "4.46.2", 1932 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", 1933 + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", 1934 + "cpu": [ 1935 + "loong64" 1936 + ], 1937 + "license": "MIT", 1938 + "optional": true, 1939 + "os": [ 1940 + "linux" 1941 + ] 1942 + }, 1943 + "node_modules/@rollup/rollup-linux-ppc64-gnu": { 1944 + "version": "4.46.2", 1945 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", 1946 + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", 1947 + "cpu": [ 1948 + "ppc64" 1949 + ], 1950 + "license": "MIT", 1951 + "optional": true, 1952 + "os": [ 1953 + "linux" 1954 + ] 1955 + }, 1956 + "node_modules/@rollup/rollup-linux-riscv64-gnu": { 1957 + "version": "4.46.2", 1958 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", 1959 + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", 1960 + "cpu": [ 1961 + "riscv64" 1962 + ], 1963 + "license": "MIT", 1964 + "optional": true, 1965 + "os": [ 1966 + "linux" 1967 + ] 1968 + }, 1969 + "node_modules/@rollup/rollup-linux-riscv64-musl": { 1970 + "version": "4.46.2", 1971 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", 1972 + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", 1973 + "cpu": [ 1974 + "riscv64" 1975 + ], 1976 + "license": "MIT", 1977 + "optional": true, 1978 + "os": [ 1979 + "linux" 1980 + ] 1981 + }, 1982 + "node_modules/@rollup/rollup-linux-s390x-gnu": { 1983 + "version": "4.46.2", 1984 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", 1985 + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", 1986 + "cpu": [ 1987 + "s390x" 1988 + ], 1989 + "license": "MIT", 1990 + "optional": true, 1991 + "os": [ 1992 + "linux" 1993 + ] 1994 + }, 1995 + "node_modules/@rollup/rollup-linux-x64-gnu": { 1996 + "version": "4.46.2", 1997 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", 1998 + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", 1999 + "cpu": [ 2000 + "x64" 2001 + ], 2002 + "license": "MIT", 2003 + "optional": true, 2004 + "os": [ 2005 + "linux" 2006 + ] 2007 + }, 2008 + "node_modules/@rollup/rollup-linux-x64-musl": { 2009 + "version": "4.46.2", 2010 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", 2011 + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", 2012 + "cpu": [ 2013 + "x64" 2014 + ], 2015 + "license": "MIT", 2016 + "optional": true, 2017 + "os": [ 2018 + "linux" 2019 + ] 2020 + }, 2021 + "node_modules/@rollup/rollup-win32-arm64-msvc": { 2022 + "version": "4.46.2", 2023 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", 2024 + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", 2025 + "cpu": [ 2026 + "arm64" 2027 + ], 2028 + "license": "MIT", 2029 + "optional": true, 2030 + "os": [ 2031 + "win32" 2032 + ] 2033 + }, 2034 + "node_modules/@rollup/rollup-win32-ia32-msvc": { 2035 + "version": "4.46.2", 2036 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", 2037 + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", 2038 + "cpu": [ 2039 + "ia32" 2040 + ], 2041 + "license": "MIT", 2042 + "optional": true, 2043 + "os": [ 2044 + "win32" 2045 + ] 2046 + }, 2047 + "node_modules/@rollup/rollup-win32-x64-msvc": { 2048 + "version": "4.46.2", 2049 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", 2050 + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", 2051 + "cpu": [ 2052 + "x64" 2053 + ], 2054 + "license": "MIT", 2055 + "optional": true, 2056 + "os": [ 2057 + "win32" 2058 + ] 2059 + }, 2060 + "node_modules/@tailwindcss/node": { 2061 + "version": "4.1.11", 2062 + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", 2063 + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", 2064 + "license": "MIT", 2065 + "dependencies": { 2066 + "@ampproject/remapping": "^2.3.0", 2067 + "enhanced-resolve": "^5.18.1", 2068 + "jiti": "^2.4.2", 2069 + "lightningcss": "1.30.1", 2070 + "magic-string": "^0.30.17", 2071 + "source-map-js": "^1.2.1", 2072 + "tailwindcss": "4.1.11" 2073 + } 2074 + }, 2075 + "node_modules/@tailwindcss/oxide": { 2076 + "version": "4.1.11", 2077 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", 2078 + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", 2079 + "hasInstallScript": true, 2080 + "license": "MIT", 2081 + "dependencies": { 2082 + "detect-libc": "^2.0.4", 2083 + "tar": "^7.4.3" 2084 + }, 2085 + "engines": { 2086 + "node": ">= 10" 2087 + }, 2088 + "optionalDependencies": { 2089 + "@tailwindcss/oxide-android-arm64": "4.1.11", 2090 + "@tailwindcss/oxide-darwin-arm64": "4.1.11", 2091 + "@tailwindcss/oxide-darwin-x64": "4.1.11", 2092 + "@tailwindcss/oxide-freebsd-x64": "4.1.11", 2093 + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", 2094 + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", 2095 + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", 2096 + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", 2097 + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", 2098 + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", 2099 + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", 2100 + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" 2101 + } 2102 + }, 2103 + "node_modules/@tailwindcss/oxide-android-arm64": { 2104 + "version": "4.1.11", 2105 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", 2106 + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", 2107 + "cpu": [ 2108 + "arm64" 2109 + ], 2110 + "license": "MIT", 2111 + "optional": true, 2112 + "os": [ 2113 + "android" 2114 + ], 2115 + "engines": { 2116 + "node": ">= 10" 2117 + } 2118 + }, 2119 + "node_modules/@tailwindcss/oxide-darwin-arm64": { 2120 + "version": "4.1.11", 2121 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", 2122 + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", 2123 + "cpu": [ 2124 + "arm64" 2125 + ], 2126 + "license": "MIT", 2127 + "optional": true, 2128 + "os": [ 2129 + "darwin" 2130 + ], 2131 + "engines": { 2132 + "node": ">= 10" 2133 + } 2134 + }, 2135 + "node_modules/@tailwindcss/oxide-darwin-x64": { 2136 + "version": "4.1.11", 2137 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", 2138 + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", 2139 + "cpu": [ 2140 + "x64" 2141 + ], 2142 + "license": "MIT", 2143 + "optional": true, 2144 + "os": [ 2145 + "darwin" 2146 + ], 2147 + "engines": { 2148 + "node": ">= 10" 2149 + } 2150 + }, 2151 + "node_modules/@tailwindcss/oxide-freebsd-x64": { 2152 + "version": "4.1.11", 2153 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", 2154 + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", 2155 + "cpu": [ 2156 + "x64" 2157 + ], 2158 + "license": "MIT", 2159 + "optional": true, 2160 + "os": [ 2161 + "freebsd" 2162 + ], 2163 + "engines": { 2164 + "node": ">= 10" 2165 + } 2166 + }, 2167 + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { 2168 + "version": "4.1.11", 2169 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", 2170 + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", 2171 + "cpu": [ 2172 + "arm" 2173 + ], 2174 + "license": "MIT", 2175 + "optional": true, 2176 + "os": [ 2177 + "linux" 2178 + ], 2179 + "engines": { 2180 + "node": ">= 10" 2181 + } 2182 + }, 2183 + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { 2184 + "version": "4.1.11", 2185 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", 2186 + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", 2187 + "cpu": [ 2188 + "arm64" 2189 + ], 2190 + "license": "MIT", 2191 + "optional": true, 2192 + "os": [ 2193 + "linux" 2194 + ], 2195 + "engines": { 2196 + "node": ">= 10" 2197 + } 2198 + }, 2199 + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { 2200 + "version": "4.1.11", 2201 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", 2202 + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", 2203 + "cpu": [ 2204 + "arm64" 2205 + ], 2206 + "license": "MIT", 2207 + "optional": true, 2208 + "os": [ 2209 + "linux" 2210 + ], 2211 + "engines": { 2212 + "node": ">= 10" 2213 + } 2214 + }, 2215 + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { 2216 + "version": "4.1.11", 2217 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", 2218 + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", 2219 + "cpu": [ 2220 + "x64" 2221 + ], 2222 + "license": "MIT", 2223 + "optional": true, 2224 + "os": [ 2225 + "linux" 2226 + ], 2227 + "engines": { 2228 + "node": ">= 10" 2229 + } 2230 + }, 2231 + "node_modules/@tailwindcss/oxide-linux-x64-musl": { 2232 + "version": "4.1.11", 2233 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", 2234 + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", 2235 + "cpu": [ 2236 + "x64" 2237 + ], 2238 + "license": "MIT", 2239 + "optional": true, 2240 + "os": [ 2241 + "linux" 2242 + ], 2243 + "engines": { 2244 + "node": ">= 10" 2245 + } 2246 + }, 2247 + "node_modules/@tailwindcss/oxide-wasm32-wasi": { 2248 + "version": "4.1.11", 2249 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", 2250 + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", 2251 + "bundleDependencies": [ 2252 + "@napi-rs/wasm-runtime", 2253 + "@emnapi/core", 2254 + "@emnapi/runtime", 2255 + "@tybys/wasm-util", 2256 + "@emnapi/wasi-threads", 2257 + "tslib" 2258 + ], 2259 + "cpu": [ 2260 + "wasm32" 2261 + ], 2262 + "license": "MIT", 2263 + "optional": true, 2264 + "dependencies": { 2265 + "@emnapi/core": "^1.4.3", 2266 + "@emnapi/runtime": "^1.4.3", 2267 + "@emnapi/wasi-threads": "^1.0.2", 2268 + "@napi-rs/wasm-runtime": "^0.2.11", 2269 + "@tybys/wasm-util": "^0.9.0", 2270 + "tslib": "^2.8.0" 2271 + }, 2272 + "engines": { 2273 + "node": ">=14.0.0" 2274 + } 2275 + }, 2276 + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { 2277 + "version": "4.1.11", 2278 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", 2279 + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", 2280 + "cpu": [ 2281 + "arm64" 2282 + ], 2283 + "license": "MIT", 2284 + "optional": true, 2285 + "os": [ 2286 + "win32" 2287 + ], 2288 + "engines": { 2289 + "node": ">= 10" 2290 + } 2291 + }, 2292 + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { 2293 + "version": "4.1.11", 2294 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", 2295 + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", 2296 + "cpu": [ 2297 + "x64" 2298 + ], 2299 + "license": "MIT", 2300 + "optional": true, 2301 + "os": [ 2302 + "win32" 2303 + ], 2304 + "engines": { 2305 + "node": ">= 10" 2306 + } 2307 + }, 2308 + "node_modules/@tailwindcss/vite": { 2309 + "version": "4.1.11", 2310 + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", 2311 + "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", 2312 + "license": "MIT", 2313 + "dependencies": { 2314 + "@tailwindcss/node": "4.1.11", 2315 + "@tailwindcss/oxide": "4.1.11", 2316 + "tailwindcss": "4.1.11" 2317 + }, 2318 + "peerDependencies": { 2319 + "vite": "^5.2.0 || ^6 || ^7" 2320 + } 2321 + }, 2322 + "node_modules/@tanstack/history": { 2323 + "version": "1.130.12", 2324 + "resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.130.12.tgz", 2325 + "integrity": "sha512-2VO1nNFDWojgZ7Uqv/OJfH6LphZQ1kE6l8sI3YBgSPtj3qN6I/rsoTHW9rGjwiDO8sQoDRXod2hpH6HMs5NDsw==", 2326 + "license": "MIT", 2327 + "engines": { 2328 + "node": ">=12" 2329 + }, 2330 + "funding": { 2331 + "type": "github", 2332 + "url": "https://github.com/sponsors/tannerlinsley" 2333 + } 2334 + }, 2335 + "node_modules/@tanstack/query-core": { 2336 + "version": "5.83.1", 2337 + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.1.tgz", 2338 + "integrity": "sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==", 2339 + "license": "MIT", 2340 + "funding": { 2341 + "type": "github", 2342 + "url": "https://github.com/sponsors/tannerlinsley" 2343 + } 2344 + }, 2345 + "node_modules/@tanstack/react-query": { 2346 + "version": "5.84.1", 2347 + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.84.1.tgz", 2348 + "integrity": "sha512-zo7EUygcWJMQfFNWDSG7CBhy8irje/XY0RDVKKV4IQJAysb+ZJkkJPcnQi+KboyGUgT+SQebRFoTqLuTtfoDLw==", 2349 + "license": "MIT", 2350 + "dependencies": { 2351 + "@tanstack/query-core": "5.83.1" 2352 + }, 2353 + "funding": { 2354 + "type": "github", 2355 + "url": "https://github.com/sponsors/tannerlinsley" 2356 + }, 2357 + "peerDependencies": { 2358 + "react": "^18 || ^19" 2359 + } 2360 + }, 2361 + "node_modules/@tanstack/react-router": { 2362 + "version": "1.130.12", 2363 + "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.130.12.tgz", 2364 + "integrity": "sha512-7BYgOpGc1vK8MH1LIFLLBudGpH46GQy+hewnP7dNQJ4KHmkwPHv958L1IMA9jU/rs5g1ZH5n1f33BAMOBXUMYQ==", 2365 + "license": "MIT", 2366 + "dependencies": { 2367 + "@tanstack/history": "1.130.12", 2368 + "@tanstack/react-store": "^0.7.0", 2369 + "@tanstack/router-core": "1.130.12", 2370 + "isbot": "^5.1.22", 2371 + "tiny-invariant": "^1.3.3", 2372 + "tiny-warning": "^1.0.3" 2373 + }, 2374 + "engines": { 2375 + "node": ">=12" 2376 + }, 2377 + "funding": { 2378 + "type": "github", 2379 + "url": "https://github.com/sponsors/tannerlinsley" 2380 + }, 2381 + "peerDependencies": { 2382 + "react": ">=18.0.0 || >=19.0.0", 2383 + "react-dom": ">=18.0.0 || >=19.0.0" 2384 + } 2385 + }, 2386 + "node_modules/@tanstack/react-router-devtools": { 2387 + "version": "1.130.13", 2388 + "resolved": "https://registry.npmjs.org/@tanstack/react-router-devtools/-/react-router-devtools-1.130.13.tgz", 2389 + "integrity": "sha512-cY+jYxEP4/WNDgFFlI5/1u2U9zY9zHmJDoNxCF3NiaSgtAIVHdCKRGvfG6oRl6EposNGtn+JJhQkMkfAyoN9lQ==", 2390 + "license": "MIT", 2391 + "dependencies": { 2392 + "@tanstack/router-devtools-core": "1.130.13" 2393 + }, 2394 + "engines": { 2395 + "node": ">=12" 2396 + }, 2397 + "funding": { 2398 + "type": "github", 2399 + "url": "https://github.com/sponsors/tannerlinsley" 2400 + }, 2401 + "peerDependencies": { 2402 + "@tanstack/react-router": "^1.130.12", 2403 + "react": ">=18.0.0 || >=19.0.0", 2404 + "react-dom": ">=18.0.0 || >=19.0.0" 2405 + } 2406 + }, 2407 + "node_modules/@tanstack/react-store": { 2408 + "version": "0.7.3", 2409 + "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.7.3.tgz", 2410 + "integrity": "sha512-3Dnqtbw9P2P0gw8uUM8WP2fFfg8XMDSZCTsywRPZe/XqqYW8PGkXKZTvP0AHkE4mpqP9Y43GpOg9vwO44azu6Q==", 2411 + "license": "MIT", 2412 + "dependencies": { 2413 + "@tanstack/store": "0.7.2", 2414 + "use-sync-external-store": "^1.5.0" 2415 + }, 2416 + "funding": { 2417 + "type": "github", 2418 + "url": "https://github.com/sponsors/tannerlinsley" 2419 + }, 2420 + "peerDependencies": { 2421 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", 2422 + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 2423 + } 2424 + }, 2425 + "node_modules/@tanstack/router-core": { 2426 + "version": "1.130.12", 2427 + "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.130.12.tgz", 2428 + "integrity": "sha512-emq3cRU9Na1hnIToojzkfJcOZm/MG2bv9M+Kr/elUxEf83enGEwQXC1EKezTuwNgeJrOv8vPJdEhWM7IQodnHQ==", 2429 + "license": "MIT", 2430 + "dependencies": { 2431 + "@tanstack/history": "1.130.12", 2432 + "@tanstack/store": "^0.7.0", 2433 + "cookie-es": "^1.2.2", 2434 + "seroval": "^1.3.2", 2435 + "seroval-plugins": "^1.3.2", 2436 + "tiny-invariant": "^1.3.3", 2437 + "tiny-warning": "^1.0.3" 2438 + }, 2439 + "engines": { 2440 + "node": ">=12" 2441 + }, 2442 + "funding": { 2443 + "type": "github", 2444 + "url": "https://github.com/sponsors/tannerlinsley" 2445 + } 2446 + }, 2447 + "node_modules/@tanstack/router-devtools-core": { 2448 + "version": "1.130.13", 2449 + "resolved": "https://registry.npmjs.org/@tanstack/router-devtools-core/-/router-devtools-core-1.130.13.tgz", 2450 + "integrity": "sha512-Fn8lwnc5zvyllaDQNY6qTSTtKZsEY4mlZlJVTmC2/vvY1susXUA0NQPmpBquJYQAHJGzqPX83h/yKb7hzBSH3g==", 2451 + "license": "MIT", 2452 + "dependencies": { 2453 + "clsx": "^2.1.1", 2454 + "goober": "^2.1.16", 2455 + "solid-js": "^1.9.5" 2456 + }, 2457 + "engines": { 2458 + "node": ">=12" 2459 + }, 2460 + "funding": { 2461 + "type": "github", 2462 + "url": "https://github.com/sponsors/tannerlinsley" 2463 + }, 2464 + "peerDependencies": { 2465 + "@tanstack/router-core": "^1.130.12", 2466 + "csstype": "^3.0.10", 2467 + "solid-js": ">=1.9.5", 2468 + "tiny-invariant": "^1.3.3" 2469 + }, 2470 + "peerDependenciesMeta": { 2471 + "csstype": { 2472 + "optional": true 2473 + } 2474 + } 2475 + }, 2476 + "node_modules/@tanstack/router-generator": { 2477 + "version": "1.130.15", 2478 + "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.130.15.tgz", 2479 + "integrity": "sha512-2TICfuSN8oYydTHd+nATkKV4B37XRrAWrwK9+g5dPVUP9lhLy7FQy3IVcb1HRXFXvW0zr5zPNxXErTrOmrceyA==", 2480 + "license": "MIT", 2481 + "dependencies": { 2482 + "@tanstack/router-core": "1.130.12", 2483 + "@tanstack/router-utils": "1.130.12", 2484 + "@tanstack/virtual-file-routes": "1.129.7", 2485 + "prettier": "^3.5.0", 2486 + "recast": "^0.23.11", 2487 + "source-map": "^0.7.4", 2488 + "tsx": "^4.19.2", 2489 + "zod": "^3.24.2" 2490 + }, 2491 + "engines": { 2492 + "node": ">=12" 2493 + }, 2494 + "funding": { 2495 + "type": "github", 2496 + "url": "https://github.com/sponsors/tannerlinsley" 2497 + } 2498 + }, 2499 + "node_modules/@tanstack/router-plugin": { 2500 + "version": "1.130.15", 2501 + "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.130.15.tgz", 2502 + "integrity": "sha512-ovYGN0a5CxIPkVdbJPLAqwlE0eUYhHm0PkPCH0TxR24XpEGaCxAOw92DriLRZj9R4xTg5oeJqM+3wiZJfujx/A==", 2503 + "license": "MIT", 2504 + "dependencies": { 2505 + "@babel/core": "^7.27.7", 2506 + "@babel/plugin-syntax-jsx": "^7.27.1", 2507 + "@babel/plugin-syntax-typescript": "^7.27.1", 2508 + "@babel/template": "^7.27.2", 2509 + "@babel/traverse": "^7.27.7", 2510 + "@babel/types": "^7.27.7", 2511 + "@tanstack/router-core": "1.130.12", 2512 + "@tanstack/router-generator": "1.130.15", 2513 + "@tanstack/router-utils": "1.130.12", 2514 + "@tanstack/virtual-file-routes": "1.129.7", 2515 + "babel-dead-code-elimination": "^1.0.10", 2516 + "chokidar": "^3.6.0", 2517 + "unplugin": "^2.1.2", 2518 + "zod": "^3.24.2" 2519 + }, 2520 + "engines": { 2521 + "node": ">=12" 2522 + }, 2523 + "funding": { 2524 + "type": "github", 2525 + "url": "https://github.com/sponsors/tannerlinsley" 2526 + }, 2527 + "peerDependencies": { 2528 + "@rsbuild/core": ">=1.0.2", 2529 + "@tanstack/react-router": "^1.130.12", 2530 + "vite": ">=5.0.0 || >=6.0.0", 2531 + "vite-plugin-solid": "^2.11.2", 2532 + "webpack": ">=5.92.0" 2533 + }, 2534 + "peerDependenciesMeta": { 2535 + "@rsbuild/core": { 2536 + "optional": true 2537 + }, 2538 + "@tanstack/react-router": { 2539 + "optional": true 2540 + }, 2541 + "vite": { 2542 + "optional": true 2543 + }, 2544 + "vite-plugin-solid": { 2545 + "optional": true 2546 + }, 2547 + "webpack": { 2548 + "optional": true 2549 + } 2550 + } 2551 + }, 2552 + "node_modules/@tanstack/router-utils": { 2553 + "version": "1.130.12", 2554 + "resolved": "https://registry.npmjs.org/@tanstack/router-utils/-/router-utils-1.130.12.tgz", 2555 + "integrity": "sha512-vyk7qduNrVrJWgUXHqRyZrFLOL9YJ/4ycN5PbJ2cLRBln01NkG/abKTHi32V31yMehxkxAO0EoicicvalnV0FA==", 2556 + "license": "MIT", 2557 + "dependencies": { 2558 + "@babel/core": "^7.27.4", 2559 + "@babel/generator": "^7.27.5", 2560 + "@babel/parser": "^7.27.5", 2561 + "@babel/preset-typescript": "^7.27.1", 2562 + "ansis": "^4.1.0", 2563 + "diff": "^8.0.2" 2564 + }, 2565 + "engines": { 2566 + "node": ">=12" 2567 + }, 2568 + "funding": { 2569 + "type": "github", 2570 + "url": "https://github.com/sponsors/tannerlinsley" 2571 + } 2572 + }, 2573 + "node_modules/@tanstack/store": { 2574 + "version": "0.7.2", 2575 + "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.7.2.tgz", 2576 + "integrity": "sha512-RP80Z30BYiPX2Pyo0Nyw4s1SJFH2jyM6f9i3HfX4pA+gm5jsnYryscdq2aIQLnL4TaGuQMO+zXmN9nh1Qck+Pg==", 2577 + "license": "MIT", 2578 + "funding": { 2579 + "type": "github", 2580 + "url": "https://github.com/sponsors/tannerlinsley" 2581 + } 2582 + }, 2583 + "node_modules/@tanstack/virtual-file-routes": { 2584 + "version": "1.129.7", 2585 + "resolved": "https://registry.npmjs.org/@tanstack/virtual-file-routes/-/virtual-file-routes-1.129.7.tgz", 2586 + "integrity": "sha512-a+MxoAXG+Sq94Jp67OtveKOp2vQq75AWdVI8DRt6w19B0NEqpfm784FTLbVp/qdR1wmxCOmKAvElGSIiBOx5OQ==", 2587 + "license": "MIT", 2588 + "engines": { 2589 + "node": ">=12" 2590 + }, 2591 + "funding": { 2592 + "type": "github", 2593 + "url": "https://github.com/sponsors/tannerlinsley" 2594 + } 2595 + }, 2596 + "node_modules/@testing-library/dom": { 2597 + "version": "10.4.1", 2598 + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", 2599 + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", 2600 + "dev": true, 2601 + "license": "MIT", 2602 + "dependencies": { 2603 + "@babel/code-frame": "^7.10.4", 2604 + "@babel/runtime": "^7.12.5", 2605 + "@types/aria-query": "^5.0.1", 2606 + "aria-query": "5.3.0", 2607 + "dom-accessibility-api": "^0.5.9", 2608 + "lz-string": "^1.5.0", 2609 + "picocolors": "1.1.1", 2610 + "pretty-format": "^27.0.2" 2611 + }, 2612 + "engines": { 2613 + "node": ">=18" 2614 + } 2615 + }, 2616 + "node_modules/@testing-library/react": { 2617 + "version": "16.3.0", 2618 + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", 2619 + "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", 2620 + "dev": true, 2621 + "license": "MIT", 2622 + "dependencies": { 2623 + "@babel/runtime": "^7.12.5" 2624 + }, 2625 + "engines": { 2626 + "node": ">=18" 2627 + }, 2628 + "peerDependencies": { 2629 + "@testing-library/dom": "^10.0.0", 2630 + "@types/react": "^18.0.0 || ^19.0.0", 2631 + "@types/react-dom": "^18.0.0 || ^19.0.0", 2632 + "react": "^18.0.0 || ^19.0.0", 2633 + "react-dom": "^18.0.0 || ^19.0.0" 2634 + }, 2635 + "peerDependenciesMeta": { 2636 + "@types/react": { 2637 + "optional": true 2638 + }, 2639 + "@types/react-dom": { 2640 + "optional": true 2641 + } 2642 + } 2643 + }, 2644 + "node_modules/@types/aria-query": { 2645 + "version": "5.0.4", 2646 + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", 2647 + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", 2648 + "dev": true, 2649 + "license": "MIT" 2650 + }, 2651 + "node_modules/@types/babel__core": { 2652 + "version": "7.20.5", 2653 + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", 2654 + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", 2655 + "dev": true, 2656 + "license": "MIT", 2657 + "dependencies": { 2658 + "@babel/parser": "^7.20.7", 2659 + "@babel/types": "^7.20.7", 2660 + "@types/babel__generator": "*", 2661 + "@types/babel__template": "*", 2662 + "@types/babel__traverse": "*" 2663 + } 2664 + }, 2665 + "node_modules/@types/babel__generator": { 2666 + "version": "7.27.0", 2667 + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", 2668 + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", 2669 + "dev": true, 2670 + "license": "MIT", 2671 + "dependencies": { 2672 + "@babel/types": "^7.0.0" 2673 + } 2674 + }, 2675 + "node_modules/@types/babel__template": { 2676 + "version": "7.4.4", 2677 + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", 2678 + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", 2679 + "dev": true, 2680 + "license": "MIT", 2681 + "dependencies": { 2682 + "@babel/parser": "^7.1.0", 2683 + "@babel/types": "^7.0.0" 2684 + } 2685 + }, 2686 + "node_modules/@types/babel__traverse": { 2687 + "version": "7.28.0", 2688 + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", 2689 + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", 2690 + "dev": true, 2691 + "license": "MIT", 2692 + "dependencies": { 2693 + "@babel/types": "^7.28.2" 2694 + } 2695 + }, 2696 + "node_modules/@types/chai": { 2697 + "version": "5.2.2", 2698 + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", 2699 + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", 2700 + "dev": true, 2701 + "license": "MIT", 2702 + "dependencies": { 2703 + "@types/deep-eql": "*" 2704 + } 2705 + }, 2706 + "node_modules/@types/deep-eql": { 2707 + "version": "4.0.2", 2708 + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", 2709 + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", 2710 + "dev": true, 2711 + "license": "MIT" 2712 + }, 2713 + "node_modules/@types/estree": { 2714 + "version": "1.0.8", 2715 + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", 2716 + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", 2717 + "license": "MIT" 2718 + }, 2719 + "node_modules/@types/react": { 2720 + "version": "19.1.9", 2721 + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz", 2722 + "integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==", 2723 + "devOptional": true, 2724 + "license": "MIT", 2725 + "dependencies": { 2726 + "csstype": "^3.0.2" 2727 + } 2728 + }, 2729 + "node_modules/@types/react-dom": { 2730 + "version": "19.1.7", 2731 + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz", 2732 + "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==", 2733 + "devOptional": true, 2734 + "license": "MIT", 2735 + "peerDependencies": { 2736 + "@types/react": "^19.0.0" 2737 + } 2738 + }, 2739 + "node_modules/@vitejs/plugin-react": { 2740 + "version": "4.7.0", 2741 + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", 2742 + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", 2743 + "dev": true, 2744 + "license": "MIT", 2745 + "dependencies": { 2746 + "@babel/core": "^7.28.0", 2747 + "@babel/plugin-transform-react-jsx-self": "^7.27.1", 2748 + "@babel/plugin-transform-react-jsx-source": "^7.27.1", 2749 + "@rolldown/pluginutils": "1.0.0-beta.27", 2750 + "@types/babel__core": "^7.20.5", 2751 + "react-refresh": "^0.17.0" 2752 + }, 2753 + "engines": { 2754 + "node": "^14.18.0 || >=16.0.0" 2755 + }, 2756 + "peerDependencies": { 2757 + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" 2758 + } 2759 + }, 2760 + "node_modules/@vitest/expect": { 2761 + "version": "3.2.4", 2762 + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", 2763 + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", 2764 + "dev": true, 2765 + "license": "MIT", 2766 + "dependencies": { 2767 + "@types/chai": "^5.2.2", 2768 + "@vitest/spy": "3.2.4", 2769 + "@vitest/utils": "3.2.4", 2770 + "chai": "^5.2.0", 2771 + "tinyrainbow": "^2.0.0" 2772 + }, 2773 + "funding": { 2774 + "url": "https://opencollective.com/vitest" 2775 + } 2776 + }, 2777 + "node_modules/@vitest/mocker": { 2778 + "version": "3.2.4", 2779 + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", 2780 + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", 2781 + "dev": true, 2782 + "license": "MIT", 2783 + "dependencies": { 2784 + "@vitest/spy": "3.2.4", 2785 + "estree-walker": "^3.0.3", 2786 + "magic-string": "^0.30.17" 2787 + }, 2788 + "funding": { 2789 + "url": "https://opencollective.com/vitest" 2790 + }, 2791 + "peerDependencies": { 2792 + "msw": "^2.4.9", 2793 + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" 2794 + }, 2795 + "peerDependenciesMeta": { 2796 + "msw": { 2797 + "optional": true 2798 + }, 2799 + "vite": { 2800 + "optional": true 2801 + } 2802 + } 2803 + }, 2804 + "node_modules/@vitest/pretty-format": { 2805 + "version": "3.2.4", 2806 + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", 2807 + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", 2808 + "dev": true, 2809 + "license": "MIT", 2810 + "dependencies": { 2811 + "tinyrainbow": "^2.0.0" 2812 + }, 2813 + "funding": { 2814 + "url": "https://opencollective.com/vitest" 2815 + } 2816 + }, 2817 + "node_modules/@vitest/runner": { 2818 + "version": "3.2.4", 2819 + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", 2820 + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", 2821 + "dev": true, 2822 + "license": "MIT", 2823 + "dependencies": { 2824 + "@vitest/utils": "3.2.4", 2825 + "pathe": "^2.0.3", 2826 + "strip-literal": "^3.0.0" 2827 + }, 2828 + "funding": { 2829 + "url": "https://opencollective.com/vitest" 2830 + } 2831 + }, 2832 + "node_modules/@vitest/snapshot": { 2833 + "version": "3.2.4", 2834 + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", 2835 + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", 2836 + "dev": true, 2837 + "license": "MIT", 2838 + "dependencies": { 2839 + "@vitest/pretty-format": "3.2.4", 2840 + "magic-string": "^0.30.17", 2841 + "pathe": "^2.0.3" 2842 + }, 2843 + "funding": { 2844 + "url": "https://opencollective.com/vitest" 2845 + } 2846 + }, 2847 + "node_modules/@vitest/spy": { 2848 + "version": "3.2.4", 2849 + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", 2850 + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", 2851 + "dev": true, 2852 + "license": "MIT", 2853 + "dependencies": { 2854 + "tinyspy": "^4.0.3" 2855 + }, 2856 + "funding": { 2857 + "url": "https://opencollective.com/vitest" 2858 + } 2859 + }, 2860 + "node_modules/@vitest/utils": { 2861 + "version": "3.2.4", 2862 + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", 2863 + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", 2864 + "dev": true, 2865 + "license": "MIT", 2866 + "dependencies": { 2867 + "@vitest/pretty-format": "3.2.4", 2868 + "loupe": "^3.1.4", 2869 + "tinyrainbow": "^2.0.0" 2870 + }, 2871 + "funding": { 2872 + "url": "https://opencollective.com/vitest" 2873 + } 2874 + }, 2875 + "node_modules/acorn": { 2876 + "version": "8.15.0", 2877 + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", 2878 + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 2879 + "license": "MIT", 2880 + "bin": { 2881 + "acorn": "bin/acorn" 2882 + }, 2883 + "engines": { 2884 + "node": ">=0.4.0" 2885 + } 2886 + }, 2887 + "node_modules/agent-base": { 2888 + "version": "7.1.4", 2889 + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", 2890 + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", 2891 + "dev": true, 2892 + "license": "MIT", 2893 + "engines": { 2894 + "node": ">= 14" 2895 + } 2896 + }, 2897 + "node_modules/ansi-regex": { 2898 + "version": "5.0.1", 2899 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 2900 + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 2901 + "dev": true, 2902 + "license": "MIT", 2903 + "engines": { 2904 + "node": ">=8" 2905 + } 2906 + }, 2907 + "node_modules/ansi-styles": { 2908 + "version": "5.2.0", 2909 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", 2910 + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", 2911 + "dev": true, 2912 + "license": "MIT", 2913 + "engines": { 2914 + "node": ">=10" 2915 + }, 2916 + "funding": { 2917 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 2918 + } 2919 + }, 2920 + "node_modules/ansis": { 2921 + "version": "4.1.0", 2922 + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz", 2923 + "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==", 2924 + "license": "ISC", 2925 + "engines": { 2926 + "node": ">=14" 2927 + } 2928 + }, 2929 + "node_modules/anymatch": { 2930 + "version": "3.1.3", 2931 + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 2932 + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 2933 + "license": "ISC", 2934 + "dependencies": { 2935 + "normalize-path": "^3.0.0", 2936 + "picomatch": "^2.0.4" 2937 + }, 2938 + "engines": { 2939 + "node": ">= 8" 2940 + } 2941 + }, 2942 + "node_modules/aria-hidden": { 2943 + "version": "1.2.6", 2944 + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", 2945 + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", 2946 + "license": "MIT", 2947 + "dependencies": { 2948 + "tslib": "^2.0.0" 2949 + }, 2950 + "engines": { 2951 + "node": ">=10" 2952 + } 2953 + }, 2954 + "node_modules/aria-query": { 2955 + "version": "5.3.0", 2956 + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", 2957 + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", 2958 + "dev": true, 2959 + "license": "Apache-2.0", 2960 + "dependencies": { 2961 + "dequal": "^2.0.3" 2962 + } 2963 + }, 2964 + "node_modules/assertion-error": { 2965 + "version": "2.0.1", 2966 + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", 2967 + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", 2968 + "dev": true, 2969 + "license": "MIT", 2970 + "engines": { 2971 + "node": ">=12" 2972 + } 2973 + }, 2974 + "node_modules/ast-types": { 2975 + "version": "0.16.1", 2976 + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", 2977 + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", 2978 + "license": "MIT", 2979 + "dependencies": { 2980 + "tslib": "^2.0.1" 2981 + }, 2982 + "engines": { 2983 + "node": ">=4" 2984 + } 2985 + }, 2986 + "node_modules/await-lock": { 2987 + "version": "2.2.2", 2988 + "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", 2989 + "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==", 2990 + "license": "MIT" 2991 + }, 2992 + "node_modules/babel-dead-code-elimination": { 2993 + "version": "1.0.10", 2994 + "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.10.tgz", 2995 + "integrity": "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==", 2996 + "license": "MIT", 2997 + "dependencies": { 2998 + "@babel/core": "^7.23.7", 2999 + "@babel/parser": "^7.23.6", 3000 + "@babel/traverse": "^7.23.7", 3001 + "@babel/types": "^7.23.6" 3002 + } 3003 + }, 3004 + "node_modules/binary-extensions": { 3005 + "version": "2.3.0", 3006 + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 3007 + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 3008 + "license": "MIT", 3009 + "engines": { 3010 + "node": ">=8" 3011 + }, 3012 + "funding": { 3013 + "url": "https://github.com/sponsors/sindresorhus" 3014 + } 3015 + }, 3016 + "node_modules/braces": { 3017 + "version": "3.0.3", 3018 + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 3019 + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 3020 + "license": "MIT", 3021 + "dependencies": { 3022 + "fill-range": "^7.1.1" 3023 + }, 3024 + "engines": { 3025 + "node": ">=8" 3026 + } 3027 + }, 3028 + "node_modules/browserslist": { 3029 + "version": "4.25.1", 3030 + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", 3031 + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", 3032 + "funding": [ 3033 + { 3034 + "type": "opencollective", 3035 + "url": "https://opencollective.com/browserslist" 3036 + }, 3037 + { 3038 + "type": "tidelift", 3039 + "url": "https://tidelift.com/funding/github/npm/browserslist" 3040 + }, 3041 + { 3042 + "type": "github", 3043 + "url": "https://github.com/sponsors/ai" 3044 + } 3045 + ], 3046 + "license": "MIT", 3047 + "dependencies": { 3048 + "caniuse-lite": "^1.0.30001726", 3049 + "electron-to-chromium": "^1.5.173", 3050 + "node-releases": "^2.0.19", 3051 + "update-browserslist-db": "^1.1.3" 3052 + }, 3053 + "bin": { 3054 + "browserslist": "cli.js" 3055 + }, 3056 + "engines": { 3057 + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 3058 + } 3059 + }, 3060 + "node_modules/cac": { 3061 + "version": "6.7.14", 3062 + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", 3063 + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", 3064 + "dev": true, 3065 + "license": "MIT", 3066 + "engines": { 3067 + "node": ">=8" 3068 + } 3069 + }, 3070 + "node_modules/caniuse-lite": { 3071 + "version": "1.0.30001731", 3072 + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", 3073 + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", 3074 + "funding": [ 3075 + { 3076 + "type": "opencollective", 3077 + "url": "https://opencollective.com/browserslist" 3078 + }, 3079 + { 3080 + "type": "tidelift", 3081 + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 3082 + }, 3083 + { 3084 + "type": "github", 3085 + "url": "https://github.com/sponsors/ai" 3086 + } 3087 + ], 3088 + "license": "CC-BY-4.0" 3089 + }, 3090 + "node_modules/chai": { 3091 + "version": "5.2.1", 3092 + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", 3093 + "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", 3094 + "dev": true, 3095 + "license": "MIT", 3096 + "dependencies": { 3097 + "assertion-error": "^2.0.1", 3098 + "check-error": "^2.1.1", 3099 + "deep-eql": "^5.0.1", 3100 + "loupe": "^3.1.0", 3101 + "pathval": "^2.0.0" 3102 + }, 3103 + "engines": { 3104 + "node": ">=18" 3105 + } 3106 + }, 3107 + "node_modules/check-error": { 3108 + "version": "2.1.1", 3109 + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", 3110 + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", 3111 + "dev": true, 3112 + "license": "MIT", 3113 + "engines": { 3114 + "node": ">= 16" 3115 + } 3116 + }, 3117 + "node_modules/chokidar": { 3118 + "version": "3.6.0", 3119 + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 3120 + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 3121 + "license": "MIT", 3122 + "dependencies": { 3123 + "anymatch": "~3.1.2", 3124 + "braces": "~3.0.2", 3125 + "glob-parent": "~5.1.2", 3126 + "is-binary-path": "~2.1.0", 3127 + "is-glob": "~4.0.1", 3128 + "normalize-path": "~3.0.0", 3129 + "readdirp": "~3.6.0" 3130 + }, 3131 + "engines": { 3132 + "node": ">= 8.10.0" 3133 + }, 3134 + "funding": { 3135 + "url": "https://paulmillr.com/funding/" 3136 + }, 3137 + "optionalDependencies": { 3138 + "fsevents": "~2.3.2" 3139 + } 3140 + }, 3141 + "node_modules/chownr": { 3142 + "version": "3.0.0", 3143 + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", 3144 + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", 3145 + "license": "BlueOak-1.0.0", 3146 + "engines": { 3147 + "node": ">=18" 3148 + } 3149 + }, 3150 + "node_modules/clsx": { 3151 + "version": "2.1.1", 3152 + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", 3153 + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", 3154 + "license": "MIT", 3155 + "engines": { 3156 + "node": ">=6" 3157 + } 3158 + }, 3159 + "node_modules/convert-source-map": { 3160 + "version": "2.0.0", 3161 + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", 3162 + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", 3163 + "license": "MIT" 3164 + }, 3165 + "node_modules/cookie-es": { 3166 + "version": "1.2.2", 3167 + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", 3168 + "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", 3169 + "license": "MIT" 3170 + }, 3171 + "node_modules/cssstyle": { 3172 + "version": "4.6.0", 3173 + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", 3174 + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", 3175 + "dev": true, 3176 + "license": "MIT", 3177 + "dependencies": { 3178 + "@asamuzakjp/css-color": "^3.2.0", 3179 + "rrweb-cssom": "^0.8.0" 3180 + }, 3181 + "engines": { 3182 + "node": ">=18" 3183 + } 3184 + }, 3185 + "node_modules/csstype": { 3186 + "version": "3.1.3", 3187 + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", 3188 + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", 3189 + "license": "MIT" 3190 + }, 3191 + "node_modules/data-urls": { 3192 + "version": "5.0.0", 3193 + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", 3194 + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", 3195 + "dev": true, 3196 + "license": "MIT", 3197 + "dependencies": { 3198 + "whatwg-mimetype": "^4.0.0", 3199 + "whatwg-url": "^14.0.0" 3200 + }, 3201 + "engines": { 3202 + "node": ">=18" 3203 + } 3204 + }, 3205 + "node_modules/debug": { 3206 + "version": "4.4.1", 3207 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", 3208 + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", 3209 + "license": "MIT", 3210 + "dependencies": { 3211 + "ms": "^2.1.3" 3212 + }, 3213 + "engines": { 3214 + "node": ">=6.0" 3215 + }, 3216 + "peerDependenciesMeta": { 3217 + "supports-color": { 3218 + "optional": true 3219 + } 3220 + } 3221 + }, 3222 + "node_modules/decimal.js": { 3223 + "version": "10.6.0", 3224 + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", 3225 + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", 3226 + "dev": true, 3227 + "license": "MIT" 3228 + }, 3229 + "node_modules/deep-eql": { 3230 + "version": "5.0.2", 3231 + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", 3232 + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", 3233 + "dev": true, 3234 + "license": "MIT", 3235 + "engines": { 3236 + "node": ">=6" 3237 + } 3238 + }, 3239 + "node_modules/dequal": { 3240 + "version": "2.0.3", 3241 + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", 3242 + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", 3243 + "dev": true, 3244 + "license": "MIT", 3245 + "engines": { 3246 + "node": ">=6" 3247 + } 3248 + }, 3249 + "node_modules/detect-libc": { 3250 + "version": "2.0.4", 3251 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", 3252 + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", 3253 + "license": "Apache-2.0", 3254 + "engines": { 3255 + "node": ">=8" 3256 + } 3257 + }, 3258 + "node_modules/detect-node-es": { 3259 + "version": "1.1.0", 3260 + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", 3261 + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", 3262 + "license": "MIT" 3263 + }, 3264 + "node_modules/diff": { 3265 + "version": "8.0.2", 3266 + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", 3267 + "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", 3268 + "license": "BSD-3-Clause", 3269 + "engines": { 3270 + "node": ">=0.3.1" 3271 + } 3272 + }, 3273 + "node_modules/dom-accessibility-api": { 3274 + "version": "0.5.16", 3275 + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", 3276 + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", 3277 + "dev": true, 3278 + "license": "MIT" 3279 + }, 3280 + "node_modules/electron-to-chromium": { 3281 + "version": "1.5.194", 3282 + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.194.tgz", 3283 + "integrity": "sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==", 3284 + "license": "ISC" 3285 + }, 3286 + "node_modules/enhanced-resolve": { 3287 + "version": "5.18.2", 3288 + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", 3289 + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", 3290 + "license": "MIT", 3291 + "dependencies": { 3292 + "graceful-fs": "^4.2.4", 3293 + "tapable": "^2.2.0" 3294 + }, 3295 + "engines": { 3296 + "node": ">=10.13.0" 3297 + } 3298 + }, 3299 + "node_modules/entities": { 3300 + "version": "6.0.1", 3301 + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", 3302 + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", 3303 + "dev": true, 3304 + "license": "BSD-2-Clause", 3305 + "engines": { 3306 + "node": ">=0.12" 3307 + }, 3308 + "funding": { 3309 + "url": "https://github.com/fb55/entities?sponsor=1" 3310 + } 3311 + }, 3312 + "node_modules/es-module-lexer": { 3313 + "version": "1.7.0", 3314 + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", 3315 + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", 3316 + "dev": true, 3317 + "license": "MIT" 3318 + }, 3319 + "node_modules/esbuild": { 3320 + "version": "0.25.8", 3321 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", 3322 + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", 3323 + "hasInstallScript": true, 3324 + "license": "MIT", 3325 + "bin": { 3326 + "esbuild": "bin/esbuild" 3327 + }, 3328 + "engines": { 3329 + "node": ">=18" 3330 + }, 3331 + "optionalDependencies": { 3332 + "@esbuild/aix-ppc64": "0.25.8", 3333 + "@esbuild/android-arm": "0.25.8", 3334 + "@esbuild/android-arm64": "0.25.8", 3335 + "@esbuild/android-x64": "0.25.8", 3336 + "@esbuild/darwin-arm64": "0.25.8", 3337 + "@esbuild/darwin-x64": "0.25.8", 3338 + "@esbuild/freebsd-arm64": "0.25.8", 3339 + "@esbuild/freebsd-x64": "0.25.8", 3340 + "@esbuild/linux-arm": "0.25.8", 3341 + "@esbuild/linux-arm64": "0.25.8", 3342 + "@esbuild/linux-ia32": "0.25.8", 3343 + "@esbuild/linux-loong64": "0.25.8", 3344 + "@esbuild/linux-mips64el": "0.25.8", 3345 + "@esbuild/linux-ppc64": "0.25.8", 3346 + "@esbuild/linux-riscv64": "0.25.8", 3347 + "@esbuild/linux-s390x": "0.25.8", 3348 + "@esbuild/linux-x64": "0.25.8", 3349 + "@esbuild/netbsd-arm64": "0.25.8", 3350 + "@esbuild/netbsd-x64": "0.25.8", 3351 + "@esbuild/openbsd-arm64": "0.25.8", 3352 + "@esbuild/openbsd-x64": "0.25.8", 3353 + "@esbuild/openharmony-arm64": "0.25.8", 3354 + "@esbuild/sunos-x64": "0.25.8", 3355 + "@esbuild/win32-arm64": "0.25.8", 3356 + "@esbuild/win32-ia32": "0.25.8", 3357 + "@esbuild/win32-x64": "0.25.8" 3358 + } 3359 + }, 3360 + "node_modules/escalade": { 3361 + "version": "3.2.0", 3362 + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 3363 + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 3364 + "license": "MIT", 3365 + "engines": { 3366 + "node": ">=6" 3367 + } 3368 + }, 3369 + "node_modules/esprima": { 3370 + "version": "4.0.1", 3371 + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 3372 + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 3373 + "license": "BSD-2-Clause", 3374 + "bin": { 3375 + "esparse": "bin/esparse.js", 3376 + "esvalidate": "bin/esvalidate.js" 3377 + }, 3378 + "engines": { 3379 + "node": ">=4" 3380 + } 3381 + }, 3382 + "node_modules/estree-walker": { 3383 + "version": "3.0.3", 3384 + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", 3385 + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", 3386 + "dev": true, 3387 + "license": "MIT", 3388 + "dependencies": { 3389 + "@types/estree": "^1.0.0" 3390 + } 3391 + }, 3392 + "node_modules/expect-type": { 3393 + "version": "1.2.2", 3394 + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", 3395 + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", 3396 + "dev": true, 3397 + "license": "Apache-2.0", 3398 + "engines": { 3399 + "node": ">=12.0.0" 3400 + } 3401 + }, 3402 + "node_modules/fill-range": { 3403 + "version": "7.1.1", 3404 + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 3405 + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 3406 + "license": "MIT", 3407 + "dependencies": { 3408 + "to-regex-range": "^5.0.1" 3409 + }, 3410 + "engines": { 3411 + "node": ">=8" 3412 + } 3413 + }, 3414 + "node_modules/fsevents": { 3415 + "version": "2.3.3", 3416 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 3417 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 3418 + "hasInstallScript": true, 3419 + "license": "MIT", 3420 + "optional": true, 3421 + "os": [ 3422 + "darwin" 3423 + ], 3424 + "engines": { 3425 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 3426 + } 3427 + }, 3428 + "node_modules/gensync": { 3429 + "version": "1.0.0-beta.2", 3430 + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 3431 + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 3432 + "license": "MIT", 3433 + "engines": { 3434 + "node": ">=6.9.0" 3435 + } 3436 + }, 3437 + "node_modules/get-nonce": { 3438 + "version": "1.0.1", 3439 + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", 3440 + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", 3441 + "license": "MIT", 3442 + "engines": { 3443 + "node": ">=6" 3444 + } 3445 + }, 3446 + "node_modules/get-tsconfig": { 3447 + "version": "4.10.1", 3448 + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", 3449 + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", 3450 + "license": "MIT", 3451 + "dependencies": { 3452 + "resolve-pkg-maps": "^1.0.0" 3453 + }, 3454 + "funding": { 3455 + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" 3456 + } 3457 + }, 3458 + "node_modules/glob-parent": { 3459 + "version": "5.1.2", 3460 + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 3461 + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 3462 + "license": "ISC", 3463 + "dependencies": { 3464 + "is-glob": "^4.0.1" 3465 + }, 3466 + "engines": { 3467 + "node": ">= 6" 3468 + } 3469 + }, 3470 + "node_modules/goober": { 3471 + "version": "2.1.16", 3472 + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", 3473 + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", 3474 + "license": "MIT", 3475 + "peerDependencies": { 3476 + "csstype": "^3.0.10" 3477 + } 3478 + }, 3479 + "node_modules/graceful-fs": { 3480 + "version": "4.2.11", 3481 + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 3482 + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 3483 + "license": "ISC" 3484 + }, 3485 + "node_modules/graphemer": { 3486 + "version": "1.4.0", 3487 + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 3488 + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 3489 + "license": "MIT" 3490 + }, 3491 + "node_modules/html-encoding-sniffer": { 3492 + "version": "4.0.0", 3493 + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", 3494 + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", 3495 + "dev": true, 3496 + "license": "MIT", 3497 + "dependencies": { 3498 + "whatwg-encoding": "^3.1.1" 3499 + }, 3500 + "engines": { 3501 + "node": ">=18" 3502 + } 3503 + }, 3504 + "node_modules/http-proxy-agent": { 3505 + "version": "7.0.2", 3506 + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", 3507 + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", 3508 + "dev": true, 3509 + "license": "MIT", 3510 + "dependencies": { 3511 + "agent-base": "^7.1.0", 3512 + "debug": "^4.3.4" 3513 + }, 3514 + "engines": { 3515 + "node": ">= 14" 3516 + } 3517 + }, 3518 + "node_modules/https-proxy-agent": { 3519 + "version": "7.0.6", 3520 + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", 3521 + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", 3522 + "dev": true, 3523 + "license": "MIT", 3524 + "dependencies": { 3525 + "agent-base": "^7.1.2", 3526 + "debug": "4" 3527 + }, 3528 + "engines": { 3529 + "node": ">= 14" 3530 + } 3531 + }, 3532 + "node_modules/iconv-lite": { 3533 + "version": "0.6.3", 3534 + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 3535 + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 3536 + "dev": true, 3537 + "license": "MIT", 3538 + "dependencies": { 3539 + "safer-buffer": ">= 2.1.2 < 3.0.0" 3540 + }, 3541 + "engines": { 3542 + "node": ">=0.10.0" 3543 + } 3544 + }, 3545 + "node_modules/idb-keyval": { 3546 + "version": "6.2.2", 3547 + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", 3548 + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", 3549 + "license": "Apache-2.0" 3550 + }, 3551 + "node_modules/is-binary-path": { 3552 + "version": "2.1.0", 3553 + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 3554 + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 3555 + "license": "MIT", 3556 + "dependencies": { 3557 + "binary-extensions": "^2.0.0" 3558 + }, 3559 + "engines": { 3560 + "node": ">=8" 3561 + } 3562 + }, 3563 + "node_modules/is-extglob": { 3564 + "version": "2.1.1", 3565 + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 3566 + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 3567 + "license": "MIT", 3568 + "engines": { 3569 + "node": ">=0.10.0" 3570 + } 3571 + }, 3572 + "node_modules/is-glob": { 3573 + "version": "4.0.3", 3574 + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 3575 + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 3576 + "license": "MIT", 3577 + "dependencies": { 3578 + "is-extglob": "^2.1.1" 3579 + }, 3580 + "engines": { 3581 + "node": ">=0.10.0" 3582 + } 3583 + }, 3584 + "node_modules/is-number": { 3585 + "version": "7.0.0", 3586 + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 3587 + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 3588 + "license": "MIT", 3589 + "engines": { 3590 + "node": ">=0.12.0" 3591 + } 3592 + }, 3593 + "node_modules/is-potential-custom-element-name": { 3594 + "version": "1.0.1", 3595 + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", 3596 + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", 3597 + "dev": true, 3598 + "license": "MIT" 3599 + }, 3600 + "node_modules/isbot": { 3601 + "version": "5.1.29", 3602 + "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.29.tgz", 3603 + "integrity": "sha512-DelDWWoa3mBoyWTq3wjp+GIWx/yZdN7zLUE7NFhKjAiJ+uJVRkbLlwykdduCE4sPUUy8mlTYTmdhBUYu91F+sw==", 3604 + "license": "Unlicense", 3605 + "engines": { 3606 + "node": ">=18" 3607 + } 3608 + }, 3609 + "node_modules/iso-datestring-validator": { 3610 + "version": "2.2.2", 3611 + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 3612 + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 3613 + "license": "MIT" 3614 + }, 3615 + "node_modules/jiti": { 3616 + "version": "2.5.1", 3617 + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", 3618 + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", 3619 + "license": "MIT", 3620 + "bin": { 3621 + "jiti": "lib/jiti-cli.mjs" 3622 + } 3623 + }, 3624 + "node_modules/js-tokens": { 3625 + "version": "4.0.0", 3626 + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 3627 + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 3628 + "license": "MIT" 3629 + }, 3630 + "node_modules/jsdom": { 3631 + "version": "26.1.0", 3632 + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", 3633 + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", 3634 + "dev": true, 3635 + "license": "MIT", 3636 + "dependencies": { 3637 + "cssstyle": "^4.2.1", 3638 + "data-urls": "^5.0.0", 3639 + "decimal.js": "^10.5.0", 3640 + "html-encoding-sniffer": "^4.0.0", 3641 + "http-proxy-agent": "^7.0.2", 3642 + "https-proxy-agent": "^7.0.6", 3643 + "is-potential-custom-element-name": "^1.0.1", 3644 + "nwsapi": "^2.2.16", 3645 + "parse5": "^7.2.1", 3646 + "rrweb-cssom": "^0.8.0", 3647 + "saxes": "^6.0.0", 3648 + "symbol-tree": "^3.2.4", 3649 + "tough-cookie": "^5.1.1", 3650 + "w3c-xmlserializer": "^5.0.0", 3651 + "webidl-conversions": "^7.0.0", 3652 + "whatwg-encoding": "^3.1.1", 3653 + "whatwg-mimetype": "^4.0.0", 3654 + "whatwg-url": "^14.1.1", 3655 + "ws": "^8.18.0", 3656 + "xml-name-validator": "^5.0.0" 3657 + }, 3658 + "engines": { 3659 + "node": ">=18" 3660 + }, 3661 + "peerDependencies": { 3662 + "canvas": "^3.0.0" 3663 + }, 3664 + "peerDependenciesMeta": { 3665 + "canvas": { 3666 + "optional": true 3667 + } 3668 + } 3669 + }, 3670 + "node_modules/jsesc": { 3671 + "version": "3.1.0", 3672 + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", 3673 + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", 3674 + "license": "MIT", 3675 + "bin": { 3676 + "jsesc": "bin/jsesc" 3677 + }, 3678 + "engines": { 3679 + "node": ">=6" 3680 + } 3681 + }, 3682 + "node_modules/json5": { 3683 + "version": "2.2.3", 3684 + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", 3685 + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", 3686 + "license": "MIT", 3687 + "bin": { 3688 + "json5": "lib/cli.js" 3689 + }, 3690 + "engines": { 3691 + "node": ">=6" 3692 + } 3693 + }, 3694 + "node_modules/lightningcss": { 3695 + "version": "1.30.1", 3696 + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", 3697 + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", 3698 + "license": "MPL-2.0", 3699 + "dependencies": { 3700 + "detect-libc": "^2.0.3" 3701 + }, 3702 + "engines": { 3703 + "node": ">= 12.0.0" 3704 + }, 3705 + "funding": { 3706 + "type": "opencollective", 3707 + "url": "https://opencollective.com/parcel" 3708 + }, 3709 + "optionalDependencies": { 3710 + "lightningcss-darwin-arm64": "1.30.1", 3711 + "lightningcss-darwin-x64": "1.30.1", 3712 + "lightningcss-freebsd-x64": "1.30.1", 3713 + "lightningcss-linux-arm-gnueabihf": "1.30.1", 3714 + "lightningcss-linux-arm64-gnu": "1.30.1", 3715 + "lightningcss-linux-arm64-musl": "1.30.1", 3716 + "lightningcss-linux-x64-gnu": "1.30.1", 3717 + "lightningcss-linux-x64-musl": "1.30.1", 3718 + "lightningcss-win32-arm64-msvc": "1.30.1", 3719 + "lightningcss-win32-x64-msvc": "1.30.1" 3720 + } 3721 + }, 3722 + "node_modules/lightningcss-darwin-arm64": { 3723 + "version": "1.30.1", 3724 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", 3725 + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", 3726 + "cpu": [ 3727 + "arm64" 3728 + ], 3729 + "license": "MPL-2.0", 3730 + "optional": true, 3731 + "os": [ 3732 + "darwin" 3733 + ], 3734 + "engines": { 3735 + "node": ">= 12.0.0" 3736 + }, 3737 + "funding": { 3738 + "type": "opencollective", 3739 + "url": "https://opencollective.com/parcel" 3740 + } 3741 + }, 3742 + "node_modules/lightningcss-darwin-x64": { 3743 + "version": "1.30.1", 3744 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", 3745 + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", 3746 + "cpu": [ 3747 + "x64" 3748 + ], 3749 + "license": "MPL-2.0", 3750 + "optional": true, 3751 + "os": [ 3752 + "darwin" 3753 + ], 3754 + "engines": { 3755 + "node": ">= 12.0.0" 3756 + }, 3757 + "funding": { 3758 + "type": "opencollective", 3759 + "url": "https://opencollective.com/parcel" 3760 + } 3761 + }, 3762 + "node_modules/lightningcss-freebsd-x64": { 3763 + "version": "1.30.1", 3764 + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", 3765 + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", 3766 + "cpu": [ 3767 + "x64" 3768 + ], 3769 + "license": "MPL-2.0", 3770 + "optional": true, 3771 + "os": [ 3772 + "freebsd" 3773 + ], 3774 + "engines": { 3775 + "node": ">= 12.0.0" 3776 + }, 3777 + "funding": { 3778 + "type": "opencollective", 3779 + "url": "https://opencollective.com/parcel" 3780 + } 3781 + }, 3782 + "node_modules/lightningcss-linux-arm-gnueabihf": { 3783 + "version": "1.30.1", 3784 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", 3785 + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", 3786 + "cpu": [ 3787 + "arm" 3788 + ], 3789 + "license": "MPL-2.0", 3790 + "optional": true, 3791 + "os": [ 3792 + "linux" 3793 + ], 3794 + "engines": { 3795 + "node": ">= 12.0.0" 3796 + }, 3797 + "funding": { 3798 + "type": "opencollective", 3799 + "url": "https://opencollective.com/parcel" 3800 + } 3801 + }, 3802 + "node_modules/lightningcss-linux-arm64-gnu": { 3803 + "version": "1.30.1", 3804 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", 3805 + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", 3806 + "cpu": [ 3807 + "arm64" 3808 + ], 3809 + "license": "MPL-2.0", 3810 + "optional": true, 3811 + "os": [ 3812 + "linux" 3813 + ], 3814 + "engines": { 3815 + "node": ">= 12.0.0" 3816 + }, 3817 + "funding": { 3818 + "type": "opencollective", 3819 + "url": "https://opencollective.com/parcel" 3820 + } 3821 + }, 3822 + "node_modules/lightningcss-linux-arm64-musl": { 3823 + "version": "1.30.1", 3824 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", 3825 + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", 3826 + "cpu": [ 3827 + "arm64" 3828 + ], 3829 + "license": "MPL-2.0", 3830 + "optional": true, 3831 + "os": [ 3832 + "linux" 3833 + ], 3834 + "engines": { 3835 + "node": ">= 12.0.0" 3836 + }, 3837 + "funding": { 3838 + "type": "opencollective", 3839 + "url": "https://opencollective.com/parcel" 3840 + } 3841 + }, 3842 + "node_modules/lightningcss-linux-x64-gnu": { 3843 + "version": "1.30.1", 3844 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", 3845 + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", 3846 + "cpu": [ 3847 + "x64" 3848 + ], 3849 + "license": "MPL-2.0", 3850 + "optional": true, 3851 + "os": [ 3852 + "linux" 3853 + ], 3854 + "engines": { 3855 + "node": ">= 12.0.0" 3856 + }, 3857 + "funding": { 3858 + "type": "opencollective", 3859 + "url": "https://opencollective.com/parcel" 3860 + } 3861 + }, 3862 + "node_modules/lightningcss-linux-x64-musl": { 3863 + "version": "1.30.1", 3864 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", 3865 + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", 3866 + "cpu": [ 3867 + "x64" 3868 + ], 3869 + "license": "MPL-2.0", 3870 + "optional": true, 3871 + "os": [ 3872 + "linux" 3873 + ], 3874 + "engines": { 3875 + "node": ">= 12.0.0" 3876 + }, 3877 + "funding": { 3878 + "type": "opencollective", 3879 + "url": "https://opencollective.com/parcel" 3880 + } 3881 + }, 3882 + "node_modules/lightningcss-win32-arm64-msvc": { 3883 + "version": "1.30.1", 3884 + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", 3885 + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", 3886 + "cpu": [ 3887 + "arm64" 3888 + ], 3889 + "license": "MPL-2.0", 3890 + "optional": true, 3891 + "os": [ 3892 + "win32" 3893 + ], 3894 + "engines": { 3895 + "node": ">= 12.0.0" 3896 + }, 3897 + "funding": { 3898 + "type": "opencollective", 3899 + "url": "https://opencollective.com/parcel" 3900 + } 3901 + }, 3902 + "node_modules/lightningcss-win32-x64-msvc": { 3903 + "version": "1.30.1", 3904 + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", 3905 + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", 3906 + "cpu": [ 3907 + "x64" 3908 + ], 3909 + "license": "MPL-2.0", 3910 + "optional": true, 3911 + "os": [ 3912 + "win32" 3913 + ], 3914 + "engines": { 3915 + "node": ">= 12.0.0" 3916 + }, 3917 + "funding": { 3918 + "type": "opencollective", 3919 + "url": "https://opencollective.com/parcel" 3920 + } 3921 + }, 3922 + "node_modules/loupe": { 3923 + "version": "3.2.0", 3924 + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", 3925 + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", 3926 + "dev": true, 3927 + "license": "MIT" 3928 + }, 3929 + "node_modules/lru-cache": { 3930 + "version": "5.1.1", 3931 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 3932 + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 3933 + "license": "ISC", 3934 + "dependencies": { 3935 + "yallist": "^3.0.2" 3936 + } 3937 + }, 3938 + "node_modules/lz-string": { 3939 + "version": "1.5.0", 3940 + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", 3941 + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", 3942 + "dev": true, 3943 + "license": "MIT", 3944 + "bin": { 3945 + "lz-string": "bin/bin.js" 3946 + } 3947 + }, 3948 + "node_modules/magic-string": { 3949 + "version": "0.30.17", 3950 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", 3951 + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", 3952 + "license": "MIT", 3953 + "dependencies": { 3954 + "@jridgewell/sourcemap-codec": "^1.5.0" 3955 + } 3956 + }, 3957 + "node_modules/minipass": { 3958 + "version": "7.1.2", 3959 + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", 3960 + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", 3961 + "license": "ISC", 3962 + "engines": { 3963 + "node": ">=16 || 14 >=14.17" 3964 + } 3965 + }, 3966 + "node_modules/minizlib": { 3967 + "version": "3.0.2", 3968 + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", 3969 + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", 3970 + "license": "MIT", 3971 + "dependencies": { 3972 + "minipass": "^7.1.2" 3973 + }, 3974 + "engines": { 3975 + "node": ">= 18" 3976 + } 3977 + }, 3978 + "node_modules/mkdirp": { 3979 + "version": "3.0.1", 3980 + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", 3981 + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", 3982 + "license": "MIT", 3983 + "bin": { 3984 + "mkdirp": "dist/cjs/src/bin.js" 3985 + }, 3986 + "engines": { 3987 + "node": ">=10" 3988 + }, 3989 + "funding": { 3990 + "url": "https://github.com/sponsors/isaacs" 3991 + } 3992 + }, 3993 + "node_modules/ms": { 3994 + "version": "2.1.3", 3995 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 3996 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 3997 + "license": "MIT" 3998 + }, 3999 + "node_modules/multiformats": { 4000 + "version": "9.9.0", 4001 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 4002 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 4003 + "license": "(Apache-2.0 AND MIT)" 4004 + }, 4005 + "node_modules/nanoid": { 4006 + "version": "3.3.11", 4007 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 4008 + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 4009 + "funding": [ 4010 + { 4011 + "type": "github", 4012 + "url": "https://github.com/sponsors/ai" 4013 + } 4014 + ], 4015 + "license": "MIT", 4016 + "bin": { 4017 + "nanoid": "bin/nanoid.cjs" 4018 + }, 4019 + "engines": { 4020 + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 4021 + } 4022 + }, 4023 + "node_modules/node-releases": { 4024 + "version": "2.0.19", 4025 + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", 4026 + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", 4027 + "license": "MIT" 4028 + }, 4029 + "node_modules/normalize-path": { 4030 + "version": "3.0.0", 4031 + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 4032 + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 4033 + "license": "MIT", 4034 + "engines": { 4035 + "node": ">=0.10.0" 4036 + } 4037 + }, 4038 + "node_modules/nwsapi": { 4039 + "version": "2.2.21", 4040 + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz", 4041 + "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==", 4042 + "dev": true, 4043 + "license": "MIT" 4044 + }, 4045 + "node_modules/parse5": { 4046 + "version": "7.3.0", 4047 + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", 4048 + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", 4049 + "dev": true, 4050 + "license": "MIT", 4051 + "dependencies": { 4052 + "entities": "^6.0.0" 4053 + }, 4054 + "funding": { 4055 + "url": "https://github.com/inikulin/parse5?sponsor=1" 4056 + } 4057 + }, 4058 + "node_modules/pathe": { 4059 + "version": "2.0.3", 4060 + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", 4061 + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", 4062 + "dev": true, 4063 + "license": "MIT" 4064 + }, 4065 + "node_modules/pathval": { 4066 + "version": "2.0.1", 4067 + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", 4068 + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", 4069 + "dev": true, 4070 + "license": "MIT", 4071 + "engines": { 4072 + "node": ">= 14.16" 4073 + } 4074 + }, 4075 + "node_modules/picocolors": { 4076 + "version": "1.1.1", 4077 + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 4078 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 4079 + "license": "ISC" 4080 + }, 4081 + "node_modules/picomatch": { 4082 + "version": "2.3.1", 4083 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 4084 + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 4085 + "license": "MIT", 4086 + "engines": { 4087 + "node": ">=8.6" 4088 + }, 4089 + "funding": { 4090 + "url": "https://github.com/sponsors/jonschlinkert" 4091 + } 4092 + }, 4093 + "node_modules/postcss": { 4094 + "version": "8.5.6", 4095 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", 4096 + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", 4097 + "funding": [ 4098 + { 4099 + "type": "opencollective", 4100 + "url": "https://opencollective.com/postcss/" 4101 + }, 4102 + { 4103 + "type": "tidelift", 4104 + "url": "https://tidelift.com/funding/github/npm/postcss" 4105 + }, 4106 + { 4107 + "type": "github", 4108 + "url": "https://github.com/sponsors/ai" 4109 + } 4110 + ], 4111 + "license": "MIT", 4112 + "dependencies": { 4113 + "nanoid": "^3.3.11", 4114 + "picocolors": "^1.1.1", 4115 + "source-map-js": "^1.2.1" 4116 + }, 4117 + "engines": { 4118 + "node": "^10 || ^12 || >=14" 4119 + } 4120 + }, 4121 + "node_modules/prettier": { 4122 + "version": "3.6.2", 4123 + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", 4124 + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", 4125 + "license": "MIT", 4126 + "bin": { 4127 + "prettier": "bin/prettier.cjs" 4128 + }, 4129 + "engines": { 4130 + "node": ">=14" 4131 + }, 4132 + "funding": { 4133 + "url": "https://github.com/prettier/prettier?sponsor=1" 4134 + } 4135 + }, 4136 + "node_modules/pretty-format": { 4137 + "version": "27.5.1", 4138 + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", 4139 + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", 4140 + "dev": true, 4141 + "license": "MIT", 4142 + "dependencies": { 4143 + "ansi-regex": "^5.0.1", 4144 + "ansi-styles": "^5.0.0", 4145 + "react-is": "^17.0.1" 4146 + }, 4147 + "engines": { 4148 + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" 4149 + } 4150 + }, 4151 + "node_modules/punycode": { 4152 + "version": "2.3.1", 4153 + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 4154 + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 4155 + "dev": true, 4156 + "license": "MIT", 4157 + "engines": { 4158 + "node": ">=6" 4159 + } 4160 + }, 4161 + "node_modules/react": { 4162 + "version": "19.1.1", 4163 + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", 4164 + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", 4165 + "license": "MIT", 4166 + "engines": { 4167 + "node": ">=0.10.0" 4168 + } 4169 + }, 4170 + "node_modules/react-dom": { 4171 + "version": "19.1.1", 4172 + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", 4173 + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", 4174 + "license": "MIT", 4175 + "dependencies": { 4176 + "scheduler": "^0.26.0" 4177 + }, 4178 + "peerDependencies": { 4179 + "react": "^19.1.1" 4180 + } 4181 + }, 4182 + "node_modules/react-is": { 4183 + "version": "17.0.2", 4184 + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", 4185 + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", 4186 + "dev": true, 4187 + "license": "MIT" 4188 + }, 4189 + "node_modules/react-refresh": { 4190 + "version": "0.17.0", 4191 + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", 4192 + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", 4193 + "dev": true, 4194 + "license": "MIT", 4195 + "engines": { 4196 + "node": ">=0.10.0" 4197 + } 4198 + }, 4199 + "node_modules/react-remove-scroll": { 4200 + "version": "2.7.1", 4201 + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", 4202 + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", 4203 + "license": "MIT", 4204 + "dependencies": { 4205 + "react-remove-scroll-bar": "^2.3.7", 4206 + "react-style-singleton": "^2.2.3", 4207 + "tslib": "^2.1.0", 4208 + "use-callback-ref": "^1.3.3", 4209 + "use-sidecar": "^1.1.3" 4210 + }, 4211 + "engines": { 4212 + "node": ">=10" 4213 + }, 4214 + "peerDependencies": { 4215 + "@types/react": "*", 4216 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" 4217 + }, 4218 + "peerDependenciesMeta": { 4219 + "@types/react": { 4220 + "optional": true 4221 + } 4222 + } 4223 + }, 4224 + "node_modules/react-remove-scroll-bar": { 4225 + "version": "2.3.8", 4226 + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", 4227 + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", 4228 + "license": "MIT", 4229 + "dependencies": { 4230 + "react-style-singleton": "^2.2.2", 4231 + "tslib": "^2.0.0" 4232 + }, 4233 + "engines": { 4234 + "node": ">=10" 4235 + }, 4236 + "peerDependencies": { 4237 + "@types/react": "*", 4238 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 4239 + }, 4240 + "peerDependenciesMeta": { 4241 + "@types/react": { 4242 + "optional": true 4243 + } 4244 + } 4245 + }, 4246 + "node_modules/react-style-singleton": { 4247 + "version": "2.2.3", 4248 + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", 4249 + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", 4250 + "license": "MIT", 4251 + "dependencies": { 4252 + "get-nonce": "^1.0.0", 4253 + "tslib": "^2.0.0" 4254 + }, 4255 + "engines": { 4256 + "node": ">=10" 4257 + }, 4258 + "peerDependencies": { 4259 + "@types/react": "*", 4260 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" 4261 + }, 4262 + "peerDependenciesMeta": { 4263 + "@types/react": { 4264 + "optional": true 4265 + } 4266 + } 4267 + }, 4268 + "node_modules/readdirp": { 4269 + "version": "3.6.0", 4270 + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 4271 + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 4272 + "license": "MIT", 4273 + "dependencies": { 4274 + "picomatch": "^2.2.1" 4275 + }, 4276 + "engines": { 4277 + "node": ">=8.10.0" 4278 + } 4279 + }, 4280 + "node_modules/recast": { 4281 + "version": "0.23.11", 4282 + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", 4283 + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", 4284 + "license": "MIT", 4285 + "dependencies": { 4286 + "ast-types": "^0.16.1", 4287 + "esprima": "~4.0.0", 4288 + "source-map": "~0.6.1", 4289 + "tiny-invariant": "^1.3.3", 4290 + "tslib": "^2.0.1" 4291 + }, 4292 + "engines": { 4293 + "node": ">= 4" 4294 + } 4295 + }, 4296 + "node_modules/recast/node_modules/source-map": { 4297 + "version": "0.6.1", 4298 + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 4299 + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 4300 + "license": "BSD-3-Clause", 4301 + "engines": { 4302 + "node": ">=0.10.0" 4303 + } 4304 + }, 4305 + "node_modules/resolve-pkg-maps": { 4306 + "version": "1.0.0", 4307 + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", 4308 + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", 4309 + "license": "MIT", 4310 + "funding": { 4311 + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" 4312 + } 4313 + }, 4314 + "node_modules/rollup": { 4315 + "version": "4.46.2", 4316 + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", 4317 + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", 4318 + "license": "MIT", 4319 + "dependencies": { 4320 + "@types/estree": "1.0.8" 4321 + }, 4322 + "bin": { 4323 + "rollup": "dist/bin/rollup" 4324 + }, 4325 + "engines": { 4326 + "node": ">=18.0.0", 4327 + "npm": ">=8.0.0" 4328 + }, 4329 + "optionalDependencies": { 4330 + "@rollup/rollup-android-arm-eabi": "4.46.2", 4331 + "@rollup/rollup-android-arm64": "4.46.2", 4332 + "@rollup/rollup-darwin-arm64": "4.46.2", 4333 + "@rollup/rollup-darwin-x64": "4.46.2", 4334 + "@rollup/rollup-freebsd-arm64": "4.46.2", 4335 + "@rollup/rollup-freebsd-x64": "4.46.2", 4336 + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", 4337 + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", 4338 + "@rollup/rollup-linux-arm64-gnu": "4.46.2", 4339 + "@rollup/rollup-linux-arm64-musl": "4.46.2", 4340 + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", 4341 + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", 4342 + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", 4343 + "@rollup/rollup-linux-riscv64-musl": "4.46.2", 4344 + "@rollup/rollup-linux-s390x-gnu": "4.46.2", 4345 + "@rollup/rollup-linux-x64-gnu": "4.46.2", 4346 + "@rollup/rollup-linux-x64-musl": "4.46.2", 4347 + "@rollup/rollup-win32-arm64-msvc": "4.46.2", 4348 + "@rollup/rollup-win32-ia32-msvc": "4.46.2", 4349 + "@rollup/rollup-win32-x64-msvc": "4.46.2", 4350 + "fsevents": "~2.3.2" 4351 + } 4352 + }, 4353 + "node_modules/rrweb-cssom": { 4354 + "version": "0.8.0", 4355 + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", 4356 + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", 4357 + "dev": true, 4358 + "license": "MIT" 4359 + }, 4360 + "node_modules/safer-buffer": { 4361 + "version": "2.1.2", 4362 + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 4363 + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 4364 + "dev": true, 4365 + "license": "MIT" 4366 + }, 4367 + "node_modules/saxes": { 4368 + "version": "6.0.0", 4369 + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", 4370 + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", 4371 + "dev": true, 4372 + "license": "ISC", 4373 + "dependencies": { 4374 + "xmlchars": "^2.2.0" 4375 + }, 4376 + "engines": { 4377 + "node": ">=v12.22.7" 4378 + } 4379 + }, 4380 + "node_modules/scheduler": { 4381 + "version": "0.26.0", 4382 + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", 4383 + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", 4384 + "license": "MIT" 4385 + }, 4386 + "node_modules/semver": { 4387 + "version": "6.3.1", 4388 + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", 4389 + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", 4390 + "license": "ISC", 4391 + "bin": { 4392 + "semver": "bin/semver.js" 4393 + } 4394 + }, 4395 + "node_modules/seroval": { 4396 + "version": "1.3.2", 4397 + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", 4398 + "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", 4399 + "license": "MIT", 4400 + "engines": { 4401 + "node": ">=10" 4402 + } 4403 + }, 4404 + "node_modules/seroval-plugins": { 4405 + "version": "1.3.2", 4406 + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.2.tgz", 4407 + "integrity": "sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==", 4408 + "license": "MIT", 4409 + "engines": { 4410 + "node": ">=10" 4411 + }, 4412 + "peerDependencies": { 4413 + "seroval": "^1.0" 4414 + } 4415 + }, 4416 + "node_modules/siginfo": { 4417 + "version": "2.0.0", 4418 + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", 4419 + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", 4420 + "dev": true, 4421 + "license": "ISC" 4422 + }, 4423 + "node_modules/solid-js": { 4424 + "version": "1.9.7", 4425 + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.7.tgz", 4426 + "integrity": "sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==", 4427 + "license": "MIT", 4428 + "dependencies": { 4429 + "csstype": "^3.1.0", 4430 + "seroval": "~1.3.0", 4431 + "seroval-plugins": "~1.3.0" 4432 + } 4433 + }, 4434 + "node_modules/source-map": { 4435 + "version": "0.7.6", 4436 + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", 4437 + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", 4438 + "license": "BSD-3-Clause", 4439 + "engines": { 4440 + "node": ">= 12" 4441 + } 4442 + }, 4443 + "node_modules/source-map-js": { 4444 + "version": "1.2.1", 4445 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 4446 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 4447 + "license": "BSD-3-Clause", 4448 + "engines": { 4449 + "node": ">=0.10.0" 4450 + } 4451 + }, 4452 + "node_modules/stackback": { 4453 + "version": "0.0.2", 4454 + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", 4455 + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", 4456 + "dev": true, 4457 + "license": "MIT" 4458 + }, 4459 + "node_modules/std-env": { 4460 + "version": "3.9.0", 4461 + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", 4462 + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", 4463 + "dev": true, 4464 + "license": "MIT" 4465 + }, 4466 + "node_modules/strip-literal": { 4467 + "version": "3.0.0", 4468 + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", 4469 + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", 4470 + "dev": true, 4471 + "license": "MIT", 4472 + "dependencies": { 4473 + "js-tokens": "^9.0.1" 4474 + }, 4475 + "funding": { 4476 + "url": "https://github.com/sponsors/antfu" 4477 + } 4478 + }, 4479 + "node_modules/strip-literal/node_modules/js-tokens": { 4480 + "version": "9.0.1", 4481 + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", 4482 + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", 4483 + "dev": true, 4484 + "license": "MIT" 4485 + }, 4486 + "node_modules/symbol-tree": { 4487 + "version": "3.2.4", 4488 + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", 4489 + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", 4490 + "dev": true, 4491 + "license": "MIT" 4492 + }, 4493 + "node_modules/tailwindcss": { 4494 + "version": "4.1.11", 4495 + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", 4496 + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", 4497 + "license": "MIT" 4498 + }, 4499 + "node_modules/tapable": { 4500 + "version": "2.2.2", 4501 + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", 4502 + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", 4503 + "license": "MIT", 4504 + "engines": { 4505 + "node": ">=6" 4506 + } 4507 + }, 4508 + "node_modules/tar": { 4509 + "version": "7.4.3", 4510 + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", 4511 + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", 4512 + "license": "ISC", 4513 + "dependencies": { 4514 + "@isaacs/fs-minipass": "^4.0.0", 4515 + "chownr": "^3.0.0", 4516 + "minipass": "^7.1.2", 4517 + "minizlib": "^3.0.1", 4518 + "mkdirp": "^3.0.1", 4519 + "yallist": "^5.0.0" 4520 + }, 4521 + "engines": { 4522 + "node": ">=18" 4523 + } 4524 + }, 4525 + "node_modules/tar/node_modules/yallist": { 4526 + "version": "5.0.0", 4527 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", 4528 + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", 4529 + "license": "BlueOak-1.0.0", 4530 + "engines": { 4531 + "node": ">=18" 4532 + } 4533 + }, 4534 + "node_modules/tiny-invariant": { 4535 + "version": "1.3.3", 4536 + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", 4537 + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", 4538 + "license": "MIT" 4539 + }, 4540 + "node_modules/tiny-warning": { 4541 + "version": "1.0.3", 4542 + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", 4543 + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", 4544 + "license": "MIT" 4545 + }, 4546 + "node_modules/tinybench": { 4547 + "version": "2.9.0", 4548 + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", 4549 + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", 4550 + "dev": true, 4551 + "license": "MIT" 4552 + }, 4553 + "node_modules/tinyexec": { 4554 + "version": "0.3.2", 4555 + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", 4556 + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", 4557 + "dev": true, 4558 + "license": "MIT" 4559 + }, 4560 + "node_modules/tinyglobby": { 4561 + "version": "0.2.14", 4562 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", 4563 + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", 4564 + "license": "MIT", 4565 + "dependencies": { 4566 + "fdir": "^6.4.4", 4567 + "picomatch": "^4.0.2" 4568 + }, 4569 + "engines": { 4570 + "node": ">=12.0.0" 4571 + }, 4572 + "funding": { 4573 + "url": "https://github.com/sponsors/SuperchupuDev" 4574 + } 4575 + }, 4576 + "node_modules/tinyglobby/node_modules/fdir": { 4577 + "version": "6.4.6", 4578 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", 4579 + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", 4580 + "license": "MIT", 4581 + "peerDependencies": { 4582 + "picomatch": "^3 || ^4" 4583 + }, 4584 + "peerDependenciesMeta": { 4585 + "picomatch": { 4586 + "optional": true 4587 + } 4588 + } 4589 + }, 4590 + "node_modules/tinyglobby/node_modules/picomatch": { 4591 + "version": "4.0.3", 4592 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 4593 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 4594 + "license": "MIT", 4595 + "engines": { 4596 + "node": ">=12" 4597 + }, 4598 + "funding": { 4599 + "url": "https://github.com/sponsors/jonschlinkert" 4600 + } 4601 + }, 4602 + "node_modules/tinypool": { 4603 + "version": "1.1.1", 4604 + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", 4605 + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", 4606 + "dev": true, 4607 + "license": "MIT", 4608 + "engines": { 4609 + "node": "^18.0.0 || >=20.0.0" 4610 + } 4611 + }, 4612 + "node_modules/tinyrainbow": { 4613 + "version": "2.0.0", 4614 + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", 4615 + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", 4616 + "dev": true, 4617 + "license": "MIT", 4618 + "engines": { 4619 + "node": ">=14.0.0" 4620 + } 4621 + }, 4622 + "node_modules/tinyspy": { 4623 + "version": "4.0.3", 4624 + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", 4625 + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", 4626 + "dev": true, 4627 + "license": "MIT", 4628 + "engines": { 4629 + "node": ">=14.0.0" 4630 + } 4631 + }, 4632 + "node_modules/tlds": { 4633 + "version": "1.259.0", 4634 + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.259.0.tgz", 4635 + "integrity": "sha512-AldGGlDP0PNgwppe2quAvuBl18UcjuNtOnDuUkqhd6ipPqrYYBt3aTxK1QTsBVknk97lS2JcafWMghjGWFtunw==", 4636 + "license": "MIT", 4637 + "bin": { 4638 + "tlds": "bin.js" 4639 + } 4640 + }, 4641 + "node_modules/tldts": { 4642 + "version": "6.1.86", 4643 + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", 4644 + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", 4645 + "dev": true, 4646 + "license": "MIT", 4647 + "dependencies": { 4648 + "tldts-core": "^6.1.86" 4649 + }, 4650 + "bin": { 4651 + "tldts": "bin/cli.js" 4652 + } 4653 + }, 4654 + "node_modules/tldts-core": { 4655 + "version": "6.1.86", 4656 + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", 4657 + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", 4658 + "dev": true, 4659 + "license": "MIT" 4660 + }, 4661 + "node_modules/to-regex-range": { 4662 + "version": "5.0.1", 4663 + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 4664 + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 4665 + "license": "MIT", 4666 + "dependencies": { 4667 + "is-number": "^7.0.0" 4668 + }, 4669 + "engines": { 4670 + "node": ">=8.0" 4671 + } 4672 + }, 4673 + "node_modules/tough-cookie": { 4674 + "version": "5.1.2", 4675 + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", 4676 + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", 4677 + "dev": true, 4678 + "license": "BSD-3-Clause", 4679 + "dependencies": { 4680 + "tldts": "^6.1.32" 4681 + }, 4682 + "engines": { 4683 + "node": ">=16" 4684 + } 4685 + }, 4686 + "node_modules/tr46": { 4687 + "version": "5.1.1", 4688 + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", 4689 + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", 4690 + "dev": true, 4691 + "license": "MIT", 4692 + "dependencies": { 4693 + "punycode": "^2.3.1" 4694 + }, 4695 + "engines": { 4696 + "node": ">=18" 4697 + } 4698 + }, 4699 + "node_modules/tslib": { 4700 + "version": "2.8.1", 4701 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 4702 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 4703 + "license": "0BSD" 4704 + }, 4705 + "node_modules/tsx": { 4706 + "version": "4.20.3", 4707 + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", 4708 + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", 4709 + "license": "MIT", 4710 + "dependencies": { 4711 + "esbuild": "~0.25.0", 4712 + "get-tsconfig": "^4.7.5" 4713 + }, 4714 + "bin": { 4715 + "tsx": "dist/cli.mjs" 4716 + }, 4717 + "engines": { 4718 + "node": ">=18.0.0" 4719 + }, 4720 + "optionalDependencies": { 4721 + "fsevents": "~2.3.3" 4722 + } 4723 + }, 4724 + "node_modules/typescript": { 4725 + "version": "5.9.2", 4726 + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", 4727 + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", 4728 + "dev": true, 4729 + "license": "Apache-2.0", 4730 + "bin": { 4731 + "tsc": "bin/tsc", 4732 + "tsserver": "bin/tsserver" 4733 + }, 4734 + "engines": { 4735 + "node": ">=14.17" 4736 + } 4737 + }, 4738 + "node_modules/uint8arrays": { 4739 + "version": "3.0.0", 4740 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 4741 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 4742 + "license": "MIT", 4743 + "dependencies": { 4744 + "multiformats": "^9.4.2" 4745 + } 4746 + }, 4747 + "node_modules/unplugin": { 4748 + "version": "2.3.5", 4749 + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.5.tgz", 4750 + "integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==", 4751 + "license": "MIT", 4752 + "dependencies": { 4753 + "acorn": "^8.14.1", 4754 + "picomatch": "^4.0.2", 4755 + "webpack-virtual-modules": "^0.6.2" 4756 + }, 4757 + "engines": { 4758 + "node": ">=18.12.0" 4759 + } 4760 + }, 4761 + "node_modules/unplugin/node_modules/picomatch": { 4762 + "version": "4.0.3", 4763 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 4764 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 4765 + "license": "MIT", 4766 + "engines": { 4767 + "node": ">=12" 4768 + }, 4769 + "funding": { 4770 + "url": "https://github.com/sponsors/jonschlinkert" 4771 + } 4772 + }, 4773 + "node_modules/update-browserslist-db": { 4774 + "version": "1.1.3", 4775 + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", 4776 + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", 4777 + "funding": [ 4778 + { 4779 + "type": "opencollective", 4780 + "url": "https://opencollective.com/browserslist" 4781 + }, 4782 + { 4783 + "type": "tidelift", 4784 + "url": "https://tidelift.com/funding/github/npm/browserslist" 4785 + }, 4786 + { 4787 + "type": "github", 4788 + "url": "https://github.com/sponsors/ai" 4789 + } 4790 + ], 4791 + "license": "MIT", 4792 + "dependencies": { 4793 + "escalade": "^3.2.0", 4794 + "picocolors": "^1.1.1" 4795 + }, 4796 + "bin": { 4797 + "update-browserslist-db": "cli.js" 4798 + }, 4799 + "peerDependencies": { 4800 + "browserslist": ">= 4.21.0" 4801 + } 4802 + }, 4803 + "node_modules/use-callback-ref": { 4804 + "version": "1.3.3", 4805 + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", 4806 + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", 4807 + "license": "MIT", 4808 + "dependencies": { 4809 + "tslib": "^2.0.0" 4810 + }, 4811 + "engines": { 4812 + "node": ">=10" 4813 + }, 4814 + "peerDependencies": { 4815 + "@types/react": "*", 4816 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" 4817 + }, 4818 + "peerDependenciesMeta": { 4819 + "@types/react": { 4820 + "optional": true 4821 + } 4822 + } 4823 + }, 4824 + "node_modules/use-sidecar": { 4825 + "version": "1.1.3", 4826 + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", 4827 + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", 4828 + "license": "MIT", 4829 + "dependencies": { 4830 + "detect-node-es": "^1.1.0", 4831 + "tslib": "^2.0.0" 4832 + }, 4833 + "engines": { 4834 + "node": ">=10" 4835 + }, 4836 + "peerDependencies": { 4837 + "@types/react": "*", 4838 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" 4839 + }, 4840 + "peerDependenciesMeta": { 4841 + "@types/react": { 4842 + "optional": true 4843 + } 4844 + } 4845 + }, 4846 + "node_modules/use-sync-external-store": { 4847 + "version": "1.5.0", 4848 + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", 4849 + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", 4850 + "license": "MIT", 4851 + "peerDependencies": { 4852 + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 4853 + } 4854 + }, 4855 + "node_modules/vite": { 4856 + "version": "6.3.5", 4857 + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", 4858 + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", 4859 + "license": "MIT", 4860 + "dependencies": { 4861 + "esbuild": "^0.25.0", 4862 + "fdir": "^6.4.4", 4863 + "picomatch": "^4.0.2", 4864 + "postcss": "^8.5.3", 4865 + "rollup": "^4.34.9", 4866 + "tinyglobby": "^0.2.13" 4867 + }, 4868 + "bin": { 4869 + "vite": "bin/vite.js" 4870 + }, 4871 + "engines": { 4872 + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 4873 + }, 4874 + "funding": { 4875 + "url": "https://github.com/vitejs/vite?sponsor=1" 4876 + }, 4877 + "optionalDependencies": { 4878 + "fsevents": "~2.3.3" 4879 + }, 4880 + "peerDependencies": { 4881 + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 4882 + "jiti": ">=1.21.0", 4883 + "less": "*", 4884 + "lightningcss": "^1.21.0", 4885 + "sass": "*", 4886 + "sass-embedded": "*", 4887 + "stylus": "*", 4888 + "sugarss": "*", 4889 + "terser": "^5.16.0", 4890 + "tsx": "^4.8.1", 4891 + "yaml": "^2.4.2" 4892 + }, 4893 + "peerDependenciesMeta": { 4894 + "@types/node": { 4895 + "optional": true 4896 + }, 4897 + "jiti": { 4898 + "optional": true 4899 + }, 4900 + "less": { 4901 + "optional": true 4902 + }, 4903 + "lightningcss": { 4904 + "optional": true 4905 + }, 4906 + "sass": { 4907 + "optional": true 4908 + }, 4909 + "sass-embedded": { 4910 + "optional": true 4911 + }, 4912 + "stylus": { 4913 + "optional": true 4914 + }, 4915 + "sugarss": { 4916 + "optional": true 4917 + }, 4918 + "terser": { 4919 + "optional": true 4920 + }, 4921 + "tsx": { 4922 + "optional": true 4923 + }, 4924 + "yaml": { 4925 + "optional": true 4926 + } 4927 + } 4928 + }, 4929 + "node_modules/vite-node": { 4930 + "version": "3.2.4", 4931 + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", 4932 + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", 4933 + "dev": true, 4934 + "license": "MIT", 4935 + "dependencies": { 4936 + "cac": "^6.7.14", 4937 + "debug": "^4.4.1", 4938 + "es-module-lexer": "^1.7.0", 4939 + "pathe": "^2.0.3", 4940 + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" 4941 + }, 4942 + "bin": { 4943 + "vite-node": "vite-node.mjs" 4944 + }, 4945 + "engines": { 4946 + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 4947 + }, 4948 + "funding": { 4949 + "url": "https://opencollective.com/vitest" 4950 + } 4951 + }, 4952 + "node_modules/vite/node_modules/fdir": { 4953 + "version": "6.4.6", 4954 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", 4955 + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", 4956 + "license": "MIT", 4957 + "peerDependencies": { 4958 + "picomatch": "^3 || ^4" 4959 + }, 4960 + "peerDependenciesMeta": { 4961 + "picomatch": { 4962 + "optional": true 4963 + } 4964 + } 4965 + }, 4966 + "node_modules/vite/node_modules/picomatch": { 4967 + "version": "4.0.3", 4968 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 4969 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 4970 + "license": "MIT", 4971 + "engines": { 4972 + "node": ">=12" 4973 + }, 4974 + "funding": { 4975 + "url": "https://github.com/sponsors/jonschlinkert" 4976 + } 4977 + }, 4978 + "node_modules/vitest": { 4979 + "version": "3.2.4", 4980 + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", 4981 + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", 4982 + "dev": true, 4983 + "license": "MIT", 4984 + "dependencies": { 4985 + "@types/chai": "^5.2.2", 4986 + "@vitest/expect": "3.2.4", 4987 + "@vitest/mocker": "3.2.4", 4988 + "@vitest/pretty-format": "^3.2.4", 4989 + "@vitest/runner": "3.2.4", 4990 + "@vitest/snapshot": "3.2.4", 4991 + "@vitest/spy": "3.2.4", 4992 + "@vitest/utils": "3.2.4", 4993 + "chai": "^5.2.0", 4994 + "debug": "^4.4.1", 4995 + "expect-type": "^1.2.1", 4996 + "magic-string": "^0.30.17", 4997 + "pathe": "^2.0.3", 4998 + "picomatch": "^4.0.2", 4999 + "std-env": "^3.9.0", 5000 + "tinybench": "^2.9.0", 5001 + "tinyexec": "^0.3.2", 5002 + "tinyglobby": "^0.2.14", 5003 + "tinypool": "^1.1.1", 5004 + "tinyrainbow": "^2.0.0", 5005 + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", 5006 + "vite-node": "3.2.4", 5007 + "why-is-node-running": "^2.3.0" 5008 + }, 5009 + "bin": { 5010 + "vitest": "vitest.mjs" 5011 + }, 5012 + "engines": { 5013 + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 5014 + }, 5015 + "funding": { 5016 + "url": "https://opencollective.com/vitest" 5017 + }, 5018 + "peerDependencies": { 5019 + "@edge-runtime/vm": "*", 5020 + "@types/debug": "^4.1.12", 5021 + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 5022 + "@vitest/browser": "3.2.4", 5023 + "@vitest/ui": "3.2.4", 5024 + "happy-dom": "*", 5025 + "jsdom": "*" 5026 + }, 5027 + "peerDependenciesMeta": { 5028 + "@edge-runtime/vm": { 5029 + "optional": true 5030 + }, 5031 + "@types/debug": { 5032 + "optional": true 5033 + }, 5034 + "@types/node": { 5035 + "optional": true 5036 + }, 5037 + "@vitest/browser": { 5038 + "optional": true 5039 + }, 5040 + "@vitest/ui": { 5041 + "optional": true 5042 + }, 5043 + "happy-dom": { 5044 + "optional": true 5045 + }, 5046 + "jsdom": { 5047 + "optional": true 5048 + } 5049 + } 5050 + }, 5051 + "node_modules/vitest/node_modules/picomatch": { 5052 + "version": "4.0.3", 5053 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 5054 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 5055 + "dev": true, 5056 + "license": "MIT", 5057 + "engines": { 5058 + "node": ">=12" 5059 + }, 5060 + "funding": { 5061 + "url": "https://github.com/sponsors/jonschlinkert" 5062 + } 5063 + }, 5064 + "node_modules/w3c-xmlserializer": { 5065 + "version": "5.0.0", 5066 + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", 5067 + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", 5068 + "dev": true, 5069 + "license": "MIT", 5070 + "dependencies": { 5071 + "xml-name-validator": "^5.0.0" 5072 + }, 5073 + "engines": { 5074 + "node": ">=18" 5075 + } 5076 + }, 5077 + "node_modules/web-vitals": { 5078 + "version": "4.2.4", 5079 + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", 5080 + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", 5081 + "dev": true, 5082 + "license": "Apache-2.0" 5083 + }, 5084 + "node_modules/webidl-conversions": { 5085 + "version": "7.0.0", 5086 + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 5087 + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", 5088 + "dev": true, 5089 + "license": "BSD-2-Clause", 5090 + "engines": { 5091 + "node": ">=12" 5092 + } 5093 + }, 5094 + "node_modules/webpack-virtual-modules": { 5095 + "version": "0.6.2", 5096 + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", 5097 + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", 5098 + "license": "MIT" 5099 + }, 5100 + "node_modules/whatwg-encoding": { 5101 + "version": "3.1.1", 5102 + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", 5103 + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", 5104 + "dev": true, 5105 + "license": "MIT", 5106 + "dependencies": { 5107 + "iconv-lite": "0.6.3" 5108 + }, 5109 + "engines": { 5110 + "node": ">=18" 5111 + } 5112 + }, 5113 + "node_modules/whatwg-mimetype": { 5114 + "version": "4.0.0", 5115 + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", 5116 + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", 5117 + "dev": true, 5118 + "license": "MIT", 5119 + "engines": { 5120 + "node": ">=18" 5121 + } 5122 + }, 5123 + "node_modules/whatwg-url": { 5124 + "version": "14.2.0", 5125 + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", 5126 + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", 5127 + "dev": true, 5128 + "license": "MIT", 5129 + "dependencies": { 5130 + "tr46": "^5.1.0", 5131 + "webidl-conversions": "^7.0.0" 5132 + }, 5133 + "engines": { 5134 + "node": ">=18" 5135 + } 5136 + }, 5137 + "node_modules/why-is-node-running": { 5138 + "version": "2.3.0", 5139 + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", 5140 + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", 5141 + "dev": true, 5142 + "license": "MIT", 5143 + "dependencies": { 5144 + "siginfo": "^2.0.0", 5145 + "stackback": "0.0.2" 5146 + }, 5147 + "bin": { 5148 + "why-is-node-running": "cli.js" 5149 + }, 5150 + "engines": { 5151 + "node": ">=8" 5152 + } 5153 + }, 5154 + "node_modules/ws": { 5155 + "version": "8.18.3", 5156 + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", 5157 + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", 5158 + "dev": true, 5159 + "license": "MIT", 5160 + "engines": { 5161 + "node": ">=10.0.0" 5162 + }, 5163 + "peerDependencies": { 5164 + "bufferutil": "^4.0.1", 5165 + "utf-8-validate": ">=5.0.2" 5166 + }, 5167 + "peerDependenciesMeta": { 5168 + "bufferutil": { 5169 + "optional": true 5170 + }, 5171 + "utf-8-validate": { 5172 + "optional": true 5173 + } 5174 + } 5175 + }, 5176 + "node_modules/xml-name-validator": { 5177 + "version": "5.0.0", 5178 + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", 5179 + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", 5180 + "dev": true, 5181 + "license": "Apache-2.0", 5182 + "engines": { 5183 + "node": ">=18" 5184 + } 5185 + }, 5186 + "node_modules/xmlchars": { 5187 + "version": "2.2.0", 5188 + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", 5189 + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", 5190 + "dev": true, 5191 + "license": "MIT" 5192 + }, 5193 + "node_modules/yallist": { 5194 + "version": "3.1.1", 5195 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 5196 + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", 5197 + "license": "ISC" 5198 + }, 5199 + "node_modules/zod": { 5200 + "version": "3.25.76", 5201 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 5202 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 5203 + "license": "MIT", 5204 + "funding": { 5205 + "url": "https://github.com/sponsors/colinhacks" 5206 + } 5207 + } 5208 + } 5209 + }
+40
package.json
··· 1 + { 2 + "name": "forumtest", 3 + "private": true, 4 + "type": "module", 5 + "scripts": { 6 + "dev": "vite --port 3000", 7 + "start": "vite --port 3000", 8 + "build": "vite build && tsc", 9 + "serve": "vite preview", 10 + "test": "vitest run" 11 + }, 12 + "dependencies": { 13 + "@atproto/api": "^0.16.0", 14 + "@radix-ui/react-dialog": "^1.1.14", 15 + "@radix-ui/react-icons": "^1.3.2", 16 + "@radix-ui/react-popover": "^1.1.14", 17 + "@radix-ui/react-select": "^2.2.5", 18 + "@tailwindcss/vite": "^4.1.11", 19 + "@tanstack/react-query": "^5.84.1", 20 + "@tanstack/react-router": "^1.130.2", 21 + "@tanstack/react-router-devtools": "^1.130.2", 22 + "@tanstack/router-plugin": "^1.121.2", 23 + "idb-keyval": "^6.2.2", 24 + "react": "^19.0.0", 25 + "react-dom": "^19.0.0", 26 + "tailwindcss": "^4.1.11" 27 + }, 28 + "devDependencies": { 29 + "@testing-library/dom": "^10.4.0", 30 + "@testing-library/react": "^16.2.0", 31 + "@types/react": "^19.0.8", 32 + "@types/react-dom": "^19.0.3", 33 + "@vitejs/plugin-react": "^4.3.4", 34 + "jsdom": "^26.0.0", 35 + "typescript": "^5.7.2", 36 + "vite": "^6.3.5", 37 + "vitest": "^3.0.5", 38 + "web-vitals": "^4.2.4" 39 + } 40 + }
+1
public/.well-known/atproto-did
··· 1 + did:plc:cjfima2v3vnyfuzieu7bvjx7
public/favicon.ico

This is a binary file and will not be displayed.

public/logo192.png

This is a binary file and will not be displayed.

public/logo512.png

This is a binary file and will not be displayed.

+25
public/manifest.json
··· 1 + { 2 + "short_name": "ForumTest", 3 + "name": "ForumTest", 4 + "icons": [ 5 + { 6 + "src": "favicon.ico", 7 + "sizes": "64x64 32x32 24x24 16x16", 8 + "type": "image/x-icon" 9 + }, 10 + { 11 + "src": "logo192.png", 12 + "type": "image/png", 13 + "sizes": "192x192" 14 + }, 15 + { 16 + "src": "logo512.png", 17 + "type": "image/png", 18 + "sizes": "512x512" 19 + } 20 + ], 21 + "start_url": ".", 22 + "display": "standalone", 23 + "theme_color": "#000000", 24 + "background_color": "#ffffff" 25 + }
+3
public/robots.txt
··· 1 + # https://www.robotstxt.org/robotstxt.html 2 + User-agent: * 3 + Disallow:
+53
src/App.css
··· 1 + @import "tailwindcss"; 2 + .stretched-link::after { 3 + position: absolute; 4 + top: 0; 5 + right: 0; 6 + bottom: 0; 7 + left: 0; 8 + z-index: 1; /* This makes the link's clickable area cover the content */ 9 + content: ""; 10 + } 11 + .forum-grid { 12 + display: grid; 13 + grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); 14 + gap: 1.5rem; 15 + } 16 + .App { 17 + text-align: center; 18 + } 19 + 20 + .App-logo { 21 + height: 40vmin; 22 + pointer-events: none; 23 + } 24 + 25 + @media (prefers-reduced-motion: no-preference) { 26 + .App-logo { 27 + animation: App-logo-spin infinite 20s linear; 28 + } 29 + } 30 + 31 + .App-header { 32 + background-color: #282c34; 33 + min-height: 100vh; 34 + display: flex; 35 + flex-direction: column; 36 + align-items: center; 37 + justify-content: center; 38 + font-size: calc(10px + 2vmin); 39 + color: white; 40 + } 41 + 42 + .App-link { 43 + color: #61dafb; 44 + } 45 + 46 + @keyframes App-logo-spin { 47 + from { 48 + transform: rotate(0deg); 49 + } 50 + to { 51 + transform: rotate(360deg); 52 + } 53 + }
+53
src/components/DefaultCatchBoundary.tsx
··· 1 + import { 2 + ErrorComponent, 3 + Link, 4 + rootRouteId, 5 + useMatch, 6 + useRouter, 7 + } from '@tanstack/react-router' 8 + import type { ErrorComponentProps } from '@tanstack/react-router' 9 + 10 + export function DefaultCatchBoundary({ error }: ErrorComponentProps) { 11 + const router = useRouter() 12 + const isRoot = useMatch({ 13 + strict: false, 14 + select: (state) => state.id === rootRouteId, 15 + }) 16 + 17 + console.error('DefaultCatchBoundary Error:', error) 18 + 19 + return ( 20 + <div className="min-w-0 flex-1 p-4 flex flex-col items-center justify-center gap-6"> 21 + <ErrorComponent error={error} /> 22 + <div className="flex gap-2 items-center flex-wrap"> 23 + <button 24 + onClick={() => { 25 + router.invalidate() 26 + }} 27 + className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`} 28 + > 29 + Try Again 30 + </button> 31 + {isRoot ? ( 32 + <Link 33 + to="/" 34 + className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`} 35 + > 36 + Home 37 + </Link> 38 + ) : ( 39 + <Link 40 + to="/" 41 + className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`} 42 + onClick={(e) => { 43 + e.preventDefault() 44 + window.history.back() 45 + }} 46 + > 47 + Go Back 48 + </Link> 49 + )} 50 + </div> 51 + </div> 52 + ) 53 + }
+16
src/components/Header.tsx
··· 1 + import { Link } from "@tanstack/react-router"; 2 + import Login from "./Login"; 3 + import { SearchBox } from "./Search"; 4 + 5 + 6 + 7 + export default function Header(){ 8 + 9 + 10 + return <div className="flex flex-row h-10 items-center px-2 sticky top-0 bg-gray-700 z-50"> 11 + <Link to="/"><span className=" text-gray-50 font-bold">ForumTest</span></Link> 12 + {/* <div className="spacer flex-1" /> */} 13 + <SearchBox /> 14 + <Login compact /> 15 + </div> 16 + }
+214
src/components/Login.tsx
··· 1 + import React, { useEffect, useState, useRef } from 'react'; 2 + import { useAuth } from '@/providers/PassAuthProvider'; 3 + 4 + interface LoginProps { 5 + compact?: boolean; 6 + } 7 + 8 + export default function Login({ compact = false }: LoginProps) { 9 + const { loginStatus, login, logout, loading, authed } = useAuth(); 10 + const [user, setUser] = useState(''); 11 + const [password, setPassword] = useState(''); 12 + const [serviceURL, setServiceURL] = useState('bsky.social'); 13 + const [showLoginForm, setShowLoginForm] = useState(false); 14 + const formRef = useRef<HTMLDivElement>(null); 15 + 16 + useEffect(() => { 17 + function handleClickOutside(event: MouseEvent) { 18 + if (formRef.current && !formRef.current.contains(event.target as Node)) { 19 + setShowLoginForm(false); 20 + } 21 + } 22 + 23 + if (showLoginForm) { 24 + document.addEventListener('mousedown', handleClickOutside); 25 + } 26 + 27 + return () => { 28 + document.removeEventListener('mousedown', handleClickOutside); 29 + }; 30 + }, [showLoginForm]); 31 + 32 + if (loading) { 33 + return ( 34 + <div className="flex items-center justify-center p-6 text-gray-500 dark:text-gray-400"> 35 + Loading... 36 + </div> 37 + ); 38 + } 39 + 40 + if (compact) { 41 + if (authed) { 42 + return ( 43 + <button 44 + onClick={logout} 45 + className="text-sm bg-gray-600 hover:bg-gray-700 text-white rounded px-3 py-1 font-medium transition-colors" 46 + > 47 + Log out 48 + </button> 49 + ); 50 + } else { 51 + return ( 52 + <div className="relative" ref={formRef}> 53 + <button 54 + onClick={() => setShowLoginForm(!showLoginForm)} 55 + className="text-sm bg-gray-600 hover:bg-gray-700 text-white rounded px-3 py-1 font-medium transition-colors" 56 + > 57 + Log in 58 + </button> 59 + {showLoginForm && ( 60 + <div className="absolute top-full right-0 mt-2 w-80 bg-white dark:bg-gray-900 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 p-4 z-50"> 61 + <form 62 + onSubmit={e => { 63 + e.preventDefault(); 64 + login(user, password, `https://${serviceURL}`); 65 + setShowLoginForm(false); 66 + }} 67 + className="flex flex-col gap-3" 68 + > 69 + <p className="text-xs text-gray-500 dark:text-gray-400">sorry for the temporary login,<br />oauth will come soon enough i swear</p> 70 + <input 71 + type="text" 72 + placeholder="Username" 73 + value={user} 74 + onChange={e => setUser(e.target.value)} 75 + className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" 76 + autoComplete="username" 77 + /> 78 + <input 79 + type="password" 80 + placeholder="Password" 81 + value={password} 82 + onChange={e => setPassword(e.target.value)} 83 + className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" 84 + autoComplete="current-password" 85 + /> 86 + <input 87 + type="text" 88 + placeholder="bsky.social" 89 + value={serviceURL} 90 + onChange={e => setServiceURL(e.target.value)} 91 + className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" 92 + /> 93 + <button 94 + type="submit" 95 + className="bg-gray-600 hover:bg-gray-700 text-white rounded px-4 py-2 font-medium text-sm transition-colors" 96 + > 97 + Log in 98 + </button> 99 + </form> 100 + </div> 101 + )} 102 + </div> 103 + ); 104 + } 105 + } 106 + 107 + return ( 108 + <div className="p-6 bg-gray-100 dark:bg-gray-900 rounded-xl shadow border border-gray-200 dark:border-gray-800 mt-6 mx-4"> 109 + {authed ? ( 110 + <div className="flex flex-col items-center justify-center text-center"> 111 + <p className="text-lg font-semibold mb-6 text-gray-800 dark:text-gray-100">You are logged in!</p> 112 + <button 113 + onClick={logout} 114 + className="bg-gray-600 hover:bg-gray-700 text-white rounded px-6 py-2 font-semibold text-base transition-colors" 115 + > 116 + Log out 117 + </button> 118 + </div> 119 + ) : ( 120 + <form 121 + onSubmit={e => { 122 + e.preventDefault(); 123 + login(user, password, `https://${serviceURL}`); 124 + }} 125 + className="flex flex-col gap-4" 126 + > 127 + <p className="text-sm text-gray-500 dark:text-gray-400 mb-2">sorry for the temporary login,<br />oauth will come soon enough i swear</p> 128 + <input 129 + type="text" 130 + placeholder="Username" 131 + value={user} 132 + onChange={e => setUser(e.target.value)} 133 + className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-base focus:outline-none focus:ring-2 focus:ring-blue-500" 134 + autoComplete="username" 135 + /> 136 + <input 137 + type="password" 138 + placeholder="Password" 139 + value={password} 140 + onChange={e => setPassword(e.target.value)} 141 + className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-base focus:outline-none focus:ring-2 focus:ring-blue-500" 142 + autoComplete="current-password" 143 + /> 144 + <input 145 + type="text" 146 + placeholder="bsky.social" 147 + value={serviceURL} 148 + onChange={e => setServiceURL(e.target.value)} 149 + className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-base focus:outline-none focus:ring-2 focus:ring-blue-500" 150 + /> 151 + <button 152 + type="submit" 153 + className="bg-gray-600 hover:bg-gray-700 text-white rounded px-6 py-2 font-semibold text-base transition-colors mt-2" 154 + > 155 + Log in 156 + </button> 157 + </form> 158 + )} 159 + </div> 160 + ); 161 + } 162 + 163 + export const ProfileThing = () => { 164 + const { agent, loading, loginStatus, authed } = useAuth(); 165 + const [response, setResponse] = useState<any>(null); 166 + 167 + useEffect(() => { 168 + if (loginStatus && agent && !loading && authed) { 169 + fetchUser(); 170 + } 171 + }, [loginStatus, agent, loading, authed]); 172 + 173 + const fetchUser = async () => { 174 + if (!agent) { 175 + console.error("Agent is null or undefined"); 176 + return; 177 + } 178 + const res = await agent.app.bsky.actor.getProfile({ 179 + actor: agent.assertDid, 180 + }); 181 + setResponse(res.data); 182 + }; 183 + 184 + if (!authed) { 185 + return ( 186 + <div className="inline-block"> 187 + <span className="text-gray-100 text-base font-medium px-1.5">Login</span> 188 + </div> 189 + ); 190 + } 191 + 192 + if (!response) { 193 + return ( 194 + <div className="flex flex-col items-start gap-1.5"> 195 + <span className="w-5 h-5 border-2 border-gray-200 dark:border-gray-600 border-t-transparent rounded-full animate-spin inline-block" /> 196 + <span className="text-gray-100">Loading... </span> 197 + </div> 198 + ); 199 + } 200 + 201 + return ( 202 + <div className="flex flex-row items-start gap-1.5"> 203 + <img 204 + src={response?.avatar} 205 + alt="avatar" 206 + className="w-[30px] h-[30px] rounded-full object-cover" 207 + /> 208 + <div> 209 + <div className="text-gray-100 text-xs">{response?.displayName}</div> 210 + <div className="text-gray-100 text-xs">@{response?.handle}</div> 211 + </div> 212 + </div> 213 + ); 214 + };
+25
src/components/NotFound.tsx
··· 1 + import { Link } from '@tanstack/react-router' 2 + 3 + export function NotFound({ children }: { children?: any }) { 4 + return ( 5 + <div className="space-y-2 p-2"> 6 + <div className="text-gray-600 dark:text-gray-400"> 7 + {children || <p>The page you are looking for does not exist.</p>} 8 + </div> 9 + <p className="flex items-center gap-2 flex-wrap"> 10 + <button 11 + onClick={() => window.history.back()} 12 + className="bg-emerald-500 text-white px-2 py-1 rounded uppercase font-black text-sm" 13 + > 14 + Go back 15 + </button> 16 + <Link 17 + to="/" 18 + className="bg-cyan-600 text-white px-2 py-1 rounded uppercase font-black text-sm" 19 + > 20 + Start Over 21 + </Link> 22 + </p> 23 + </div> 24 + ) 25 + }
+5
src/components/PostError.tsx
··· 1 + import { ErrorComponent, ErrorComponentProps } from '@tanstack/react-router' 2 + 3 + export function PostErrorComponent({ error }: ErrorComponentProps) { 4 + return <ErrorComponent error={error} /> 5 + }
+26
src/components/Search.tsx
··· 1 + import { useNavigate } from '@tanstack/react-router' 2 + import { useState } from 'react' 3 + 4 + export function SearchBox() { 5 + const navigate = useNavigate() 6 + const [query, setQuery] = useState('') 7 + 8 + function handleSubmit(e: React.FormEvent) { 9 + e.preventDefault() 10 + if (query.trim()) { 11 + navigate({ to: '/search', search: { q: query.trim() } }) 12 + } 13 + } 14 + 15 + return ( 16 + <form onSubmit={handleSubmit} className="w-full max-w-md mx-auto"> 17 + <input 18 + type="text" 19 + value={query} 20 + onChange={e => setQuery(e.target.value)} 21 + placeholder="Search..." 22 + className=" text-sm my-2 w-full border border-gray-500 rounded-lg px-2 py-1 focus:outline-none focus:ring text-gray-50 bg-gray-800" 23 + /> 24 + </form> 25 + ) 26 + }
+5
src/components/UserError.tsx
··· 1 + import { ErrorComponent, ErrorComponentProps } from '@tanstack/react-router' 2 + 3 + export function UserErrorComponent({ error }: ErrorComponentProps) { 4 + return <ErrorComponent error={error} /> 5 + }
+49
src/helpers/cachedidentityresolver.ts
··· 1 + export type ResolvedIdentity = 2 + | { 3 + handle: string 4 + did: string 5 + pdsUrl: string 6 + bskyPds: boolean 7 + } 8 + | undefined 9 + const HANDLE_DID_CACHE_TIMEOUT = 60 * 60 * 1000; // 1 hour 10 + export async function cachedResolveIdentity({ 11 + didOrHandle, 12 + cacheTimeout = HANDLE_DID_CACHE_TIMEOUT, 13 + get, 14 + set, 15 + }: { 16 + didOrHandle: string; 17 + cacheTimeout?: number; 18 + get: (key: string) => any; 19 + set: (key: string, value: string) => void; 20 + }): Promise<ResolvedIdentity|undefined> { 21 + const isDidInput = didOrHandle.startsWith("did:"); 22 + const cacheKey = `handleDid:${didOrHandle}`; 23 + const now = Date.now(); 24 + const cached = get(cacheKey); 25 + if ( 26 + cached && 27 + cached.value && 28 + cached.time && 29 + now - cached.time < cacheTimeout 30 + ) { 31 + try { 32 + return JSON.parse(cached.value); 33 + } catch {} 34 + } 35 + const url = `https://free-fly-24.deno.dev/?${ 36 + isDidInput 37 + ? `did=${encodeURIComponent(didOrHandle)}` 38 + : `handle=${encodeURIComponent(didOrHandle)}` 39 + }`; 40 + const res = await fetch(url); 41 + if (!res.ok) throw new Error("Failed to resolve handle/did"); 42 + const data = await res.json(); 43 + set(cacheKey, JSON.stringify(data)); 44 + // also cache by did if input was handle 45 + if (!isDidInput && data.did) { 46 + set(`handleDid:${data.did}`, JSON.stringify(data)); 47 + } 48 + return data; 49 + }
+23
src/helpers/esquery.ts
··· 1 + export async function esavQuery<T = unknown>( 2 + esQuery: object, 3 + options?: { 4 + endpoint?: string; 5 + signal?: AbortSignal; 6 + } 7 + ): Promise<T> { 8 + const endpoint = options?.endpoint ?? "https://esav.whey.party/xrpc/com.example.prototypeESQuery"; 9 + const q = encodeURIComponent(JSON.stringify(esQuery)); 10 + const url = `${endpoint}?q=${q}`; 11 + 12 + const res = await fetch(url, { 13 + method: "GET", 14 + signal: options?.signal, 15 + }); 16 + 17 + if (!res.ok) { 18 + const errText = await res.text(); 19 + throw new Error(`ESAV query failed: ${res.status} ${res.statusText} - ${errText}`); 20 + } 21 + 22 + return res.json(); 23 + }
+44
src/logo.svg
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <svg id="Layer_1" 3 + xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 841.9 595.3"> 4 + <!-- Generator: Adobe Illustrator 29.3.0, SVG Export Plug-In . SVG Version: 2.1.0 Build 146) --> 5 + <defs> 6 + <style> 7 + .st0 { 8 + fill: #9ae7fc; 9 + } 10 + 11 + .st1 { 12 + fill: #61dafb; 13 + } 14 + </style> 15 + </defs> 16 + <g> 17 + <path class="st1" d="M666.3,296.5c0-32.5-40.7-63.3-103.1-82.4,14.4-63.6,8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6,0,8.3.9,11.4,2.6,13.6,7.8,19.5,37.5,14.9,75.7-1.1,9.4-2.9,19.3-5.1,29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50,32.6-30.3,63.2-46.9,84-46.9v-22.3c-27.5,0-63.5,19.6-99.9,53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7,0,51.4,16.5,84,46.6-14,14.7-28,31.4-41.3,49.9-22.6,2.4-44,6.1-63.6,11-2.3-10-4-19.7-5.2-29-4.7-38.2,1.1-67.9,14.6-75.8,3-1.8,6.9-2.6,11.5-2.6v-22.3c-8.4,0-16,1.8-22.6,5.6-28.1,16.2-34.4,66.7-19.9,130.1-62.2,19.2-102.7,49.9-102.7,82.3s40.7,63.3,103.1,82.4c-14.4,63.6-8,114.2,20.2,130.4,6.5,3.8,14.1,5.6,22.5,5.6,27.5,0,63.5-19.6,99.9-53.6,36.4,33.8,72.4,53.2,99.9,53.2,8.4,0,16-1.8,22.6-5.6,28.1-16.2,34.4-66.7,19.9-130.1,62-19.1,102.5-49.9,102.5-82.3zm-130.2-66.7c-3.7,12.9-8.3,26.2-13.5,39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4,14.2,2.1,27.9,4.7,41,7.9zm-45.8,106.5c-7.8,13.5-15.8,26.3-24.1,38.2-14.9,1.3-30,2-45.2,2s-30.2-.7-45-1.9c-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8,6.2-13.4,13.2-26.8,20.7-39.9,7.8-13.5,15.8-26.3,24.1-38.2,14.9-1.3,30-2,45.2-2s30.2.7,45,1.9c8.3,11.9,16.4,24.6,24.2,38,7.6,13.1,14.5,26.4,20.8,39.8-6.3,13.4-13.2,26.8-20.7,39.9zm32.3-13c5.4,13.4,10,26.8,13.8,39.8-13.1,3.2-26.9,5.9-41.2,8,4.9-7.7,9.8-15.6,14.4-23.7,4.6-8,8.9-16.1,13-24.1zm-101.4,106.7c-9.3-9.6-18.6-20.3-27.8-32,9,.4,18.2.7,27.5.7s18.7-.2,27.8-.7c-9,11.7-18.3,22.4-27.5,32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9,3.7-12.9,8.3-26.2,13.5-39.5,4.1,8,8.4,16,13.1,24s9.5,15.8,14.4,23.4zm73.9-208.1c9.3,9.6,18.6,20.3,27.8,32-9-.4-18.2-.7-27.5-.7s-18.7.2-27.8.7c9-11.7,18.3-22.4,27.5-32zm-74,58.9c-4.9,7.7-9.8,15.6-14.4,23.7-4.6,8-8.9,16-13,24-5.4-13.4-10-26.8-13.8-39.8,13.1-3.1,26.9-5.8,41.2-7.9zm-90.5,125.2c-35.4-15.1-58.3-34.9-58.3-50.6s22.9-35.6,58.3-50.6c8.6-3.7,18-7,27.7-10.1,5.7,19.6,13.2,40,22.5,60.9-9.2,20.8-16.6,41.1-22.2,60.6-9.9-3.1-19.3-6.5-28-10.2zm53.8,142.9c-13.6-7.8-19.5-37.5-14.9-75.7,1.1-9.4,2.9-19.3,5.1-29.4,19.6,4.8,41,8.5,63.5,10.9,13.5,18.5,27.5,35.3,41.6,50-32.6,30.3-63.2,46.9-84,46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7,38.2-1.1,67.9-14.6,75.8-3,1.8-6.9,2.6-11.5,2.6-20.7,0-51.4-16.5-84-46.6,14-14.7,28-31.4,41.3-49.9,22.6-2.4,44-6.1,63.6-11,2.3,10.1,4.1,19.8,5.2,29.1zm38.5-66.7c-8.6,3.7-18,7-27.7,10.1-5.7-19.6-13.2-40-22.5-60.9,9.2-20.8,16.6-41.1,22.2-60.6,9.9,3.1,19.3,6.5,28.1,10.2,35.4,15.1,58.3,34.9,58.3,50.6,0,15.7-23,35.6-58.4,50.6zm-264.9-268.7z"/> 18 + <circle class="st1" cx="420.9" cy="296.5" r="45.7"/> 19 + <path class="st1" d="M520.5,78.1"/> 20 + </g> 21 + <circle class="st0" cx="420.8" cy="296.6" r="43"/> 22 + <path class="st1" d="M466.1,296.6c0,25-20.2,45.2-45.2,45.2s-45.2-20.2-45.2-45.2,20.2-45.2,45.2-45.2,45.2,20.2,45.2,45.2ZM386,295.6v-6.3c0-1.1,1.2-5.1,1.8-6.2,1-1.9,2.9-3.5,4.6-4.7l-3.4-3.4c4-3.6,9.4-3.7,13.7-.7,1.9-4.7,6.6-7.1,11.6-6.7l-.8,4.2c5.9.2,13.1,4.1,13.1,10.8s0,.5-.7.7c-1.7.3-3.4-.4-5-.6s-1.2-.4-1.2.3,2.5,4.1,3,5.5,1,3.5.8,5.3c-5.6-.8-10.5-3.2-14.8-6.7.3,2.6,4.1,21.7,5.3,21.9s.8-.6,1-1.1,1.3-6.3,1.3-6.7c0-1-1.7-1.8-2.2-2.8-1.2-2.7,1.3-4.7,3.7-3.3s5.2,6.2,7.5,7.3,13,1.4,14.8,3.3-2.9,4.6-1.5,7.6c6.7-2.6,13.5-3.3,20.6-2.5,3.1-9.7,3.1-20.3-.9-29.8-7.3,0-14.7-3.6-17.2-10.8-2.5-7.2-.7-8.6-1.3-9.3-.8-1-6.3.6-7.4-1.5s.3-1.1-.2-1.4-1.9-.6-2.6-.8c-26-6.4-51.3,15.7-49.7,42.1,0,1.6,1.6,10.3,2.4,11.1s4.8,0,6.3,0,3.7.3,5,.5c2.9.4,7.2,2.4,9.4,2.5s2.4-.8,2.7-2.4c.4-2.6.5-7.4.5-10.1s-1-7.8-1.3-11.6c-.9-.2-.7,0-.9.5-.7,1.3-1.1,3.2-1.9,4.8s-5.2,8.7-5.7,9-.7-.5-.8-.8c-1.6-3.5-2-7.9-1.9-11.8-.9-1-5.4,4.9-6.7,5.3l-.8-.4v-.3h-.2ZM455.6,276.4c1.1-1.2-6-8.9-7.2-10-3-2.7-5.4-4.5-3.5,1.4s5.7,7.8,10.6,8.5h.1ZM410.9,270.1c-.4-.5-6.1,2.9-5.5,4.6,1.9-1.3,5.9-1.7,5.5-4.6ZM400.4,276.4c-.3-2.4-6.3-2.7-7.2-1s1.6,1.4,1.9,1.4c1.8.3,3.5-.6,5.2-.4h.1ZM411.3,276.8c3.8,1.3,6.6,3.6,10.9,3.7s0-3-1.2-3.9c-2.2-1.7-5.1-2.4-7.8-2.4s-1.6-.3-1.4.4c2.8.6,7.3.7,8.4,3.8-2.3-.3-3.9-1.6-6.2-2s-2.5-.5-2.6.3h0ZM420.6,290.3c-.8-5.1-5.7-10.8-10.9-11.6s-1.3-.4-.8.5,4.7,3.2,5.7,4,4.5,4.2,2.1,3.8-8.4-7.8-9.4-6.7c.2.9,1.1,1.9,1.7,2.7,3,3.8,6.9,6.8,11.8,7.4h-.2ZM395.3,279.8c-5,1.1-6.9,6.3-6.7,11,.7.8,5-3.8,5.4-4.5s2.7-4.6,1.1-4-2.9,4.4-4.2,4.6.2-2.1.4-2.5c1.1-1.6,2.9-3.1,4-4.6h0ZM400.4,281.5c-.4-.5-2,1.3-2.3,1.7-2.9,3.9-2.6,10.2-1.5,14.8.8.2.8-.3,1.2-.7,3-3.8,5.5-10.5,4.5-15.4-2.1,3.1-3.1,7.3-3.6,11h-1.3c0-4,1.9-7.7,3-11.4h0ZM426.9,305.9c0-1.7-1.7-1.4-2.5-1.9s-1.3-1.9-3-1.4c1.3,2.1,3,3.2,5.5,3.4h0ZM417.2,308.5c7.6.7,5.5-1.9,1.4-5.5-1.3-.3-1.5,4.5-1.4,5.5ZM437,309.7c-3.5-.3-7.8-2-11.2-2.1s-1.3,0-1.9.7c4,1.3,8.4,1.7,12.1,4l1-2.5h0ZM420.5,312.8c-7.3,0-15.1,3.7-20.4,8.8s-4.8,5.3-4.8,6.2c0,1.8,8.6,6.2,10.5,6.8,12.1,4.8,27.5,3.5,38.2-4.2s3.1-2.7,0-6.2c-5.7-6.6-14.7-11.4-23.4-11.3h-.1ZM398.7,316.9c-1.4-1.4-5-1.9-7-2.1s-5.3-.3-6.9.6l13.9,1.4h0ZM456.9,314.8h-7.4c-.9,0-4.9,1.1-6,1.6s-.8.6,0,.5c2.4,0,5.1-1,7.6-1.3s3.5.2,5.1,0,1.3-.3.6-.8h0Z"/> 23 + <path class="st0" d="M386,295.6l.8.4c1.3-.3,5.8-6.2,6.7-5.3,0,3.9.3,8.3,1.9,11.8s0,1.2.8.8,5.1-7.8,5.7-9,1.3-3.5,1.9-4.8,0-.7.9-.5c.3,3.8,1.2,7.8,1.3,11.6s0,7.5-.5,10.1-1.1,2.4-2.7,2.4-6.5-2.1-9.4-2.5-3.7-.5-5-.5-5.4,1.1-6.3,0-2.2-9.5-2.4-11.1c-1.5-26.4,23.7-48.5,49.7-42.1s2.2.4,2.6.8,0,1,.2,1.4c1.1,2,6.5.5,7.4,1.5s.4,6.9,1.3,9.3c2.5,7.2,10,10.9,17.2,10.8,4,9.4,4,20.1.9,29.8-7.2-.7-13.9,0-20.6,2.5-1.3-3.1,4.1-5.1,1.5-7.6s-11.8-1.9-14.8-3.3-5.4-6.1-7.5-7.3-4.9.6-3.7,3.3,2.1,1.8,2.2,2.8-1,6.2-1.3,6.7-.3,1.3-1,1.1c-1.1-.3-5-19.3-5.3-21.9,4.3,3.5,9.2,5.9,14.8,6.7.2-1.9-.3-3.5-.8-5.3s-3-5.1-3-5.5c0-.8.9-.3,1.2-.3,1.6,0,3.3.8,5,.6s.7.3.7-.7c0-6.6-7.2-10.6-13.1-10.8l.8-4.2c-5.1-.3-9.6,2-11.6,6.7-4.3-3-9.8-3-13.7.7l3.4,3.4c-1.8,1.3-3.5,2.8-4.6,4.7s-1.8,5.1-1.8,6.2v6.6h.2ZM431.6,265c7.8,2.1,8.7-3.5.2-1.3l-.2,1.3ZM432.4,270.9c.3.6,6.4-.4,5.8-2.3s-4.6.6-5.7.6l-.2,1.7h.1ZM434.5,276c.8,1.2,5.7-1.8,5.5-2.7-.4-1.9-6.6,1.2-5.5,2.7ZM442.9,276.4c-.9-.9-5,2.8-4.6,4,.6,2.4,5.7-3,4.6-4ZM445.1,279.9c-.3.2-3.1,4.6-1.5,5s3.5-3.4,3.5-4-1.3-1.3-2-.9h0ZM448.9,287.4c2.1.8,3.8-5.1,2.3-5.5-1.9-.6-2.6,5.1-2.3,5.5ZM457.3,288.6c.5-1.7,1.1-4.7-1-5.5-1,.3-.6,3.9-.6,4.8l.3.5,1.3.2h0Z"/> 24 + <path class="st0" d="M455.6,276.4c-5-.8-9.1-3.6-10.6-8.5s.5-4,3.5-1.4,8.3,8.7,7.2,10h-.1Z"/> 25 + <path class="st0" d="M420.6,290.3c-4.9-.6-8.9-3.6-11.8-7.4s-1.5-1.8-1.7-2.7c1-1,8.5,6.6,9.4,6.7,2.4.4-1.8-3.5-2.1-3.8-1-.8-5.4-3.5-5.7-4-.4-.8.5-.5.8-.5,5.2.8,10.1,6.6,10.9,11.6h.2Z"/> 26 + <path class="st0" d="M400.4,281.5c-1.1,3.7-3,7.3-3,11.4h1.3c.5-3.7,1.5-7.8,3.6-11,1,4.8-1.5,11.6-4.5,15.4s-.4.8-1.2.7c-1.1-4.5-1.3-10.8,1.5-14.8s1.9-2.2,2.3-1.7h0Z"/> 27 + <path class="st0" d="M411.3,276.8c0-.8,2.1-.4,2.6-.3,2.4.4,4,1.7,6.2,2-1.2-3.1-5.7-3.2-8.4-3.8,0-.8.9-.4,1.4-.4,2.8,0,5.6.7,7.8,2.4,2.2,1.7,4,4,1.2,3.9-4.3,0-7.1-2.4-10.9-3.7h0Z"/> 28 + <path class="st0" d="M395.3,279.8c-1.1,1.6-3,3-4,4.6s-1.9,2.8-.4,2.5,2.8-4,4.2-4.6-.9,3.6-1.1,4c-.4.7-4.7,5.2-5.4,4.5-.2-4.6,1.8-9.9,6.7-11h0Z"/> 29 + <path class="st0" d="M437,309.7l-1,2.5c-3.6-2.3-8-2.8-12.1-4,.5-.7,1.1-.7,1.9-.7,3.4,0,7.8,1.8,11.2,2.1h0Z"/> 30 + <path class="st0" d="M417.2,308.5c0-1,0-5.8,1.4-5.5,4,3.5,6.1,6.2-1.4,5.5Z"/> 31 + <path class="st0" d="M400.4,276.4c-1.8-.3-3.5.7-5.2.4s-2.3-.8-1.9-1.4c.8-1.6,6.9-1.4,7.2,1h-.1Z"/> 32 + <path class="st0" d="M410.9,270.1c.4,3-3.6,3.3-5.5,4.6-.6-1.8,5-5.1,5.5-4.6Z"/> 33 + <path class="st0" d="M426.9,305.9c-2.5-.2-4.1-1.3-5.5-3.4,1.7-.4,2,.8,3,1.4s2.6.3,2.5,1.9h0Z"/> 34 + <path class="st1" d="M432.4,270.9l.2-1.7c1.1,0,5.1-2.2,5.7-.6s-5.5,2.9-5.8,2.3h-.1Z"/> 35 + <path class="st1" d="M431.6,265l.2-1.3c8.4-2.1,7.7,3.4-.2,1.3Z"/> 36 + <path class="st1" d="M434.5,276c-1.1-1.5,5.1-4.6,5.5-2.7s-4.6,4-5.5,2.7Z"/> 37 + <path class="st1" d="M442.9,276.4c1.1,1.1-4,6.4-4.6,4s3.7-4.9,4.6-4Z"/> 38 + <path class="st1" d="M445.1,279.9c.7-.4,2.1,0,2,.9s-2.4,4.4-3.5,4,1.3-4.8,1.5-5h0Z"/> 39 + <path class="st1" d="M448.9,287.4c-.3-.3.4-6.1,2.3-5.5,1.4.4-.2,6.2-2.3,5.5Z"/> 40 + <path class="st1" d="M457.3,288.6l-1.3-.2-.3-.5c0-.9-.4-4.6.6-4.8,2.1.8,1.5,3.8,1,5.5h0Z"/> 41 + <path class="st0" d="M420.5,312.8c8.9,0,17.9,4.7,23.4,11.3,5.6,6.6,3.8,3.5,0,6.2-10.7,7.7-26.1,9-38.2,4.2-1.9-.8-10.5-5.1-10.5-6.8s4-5.3,4.8-6.2c5.3-5,13.1-8.6,20.4-8.8h.1Z"/> 42 + <path class="st0" d="M398.7,316.9l-13.9-1.4c1.7-1,5-.8,6.9-.6s5.6.7,7,2.1h0Z"/> 43 + <path class="st0" d="M456.9,314.8c.7.5,0,.8-.6.8-1.6.2-3.5-.2-5.1,0-2.4.3-5.2,1.2-7.6,1.3s-1.1,0,0-.5,5.1-1.6,6-1.6h7.4,0Z"/> 44 + </svg>
+48
src/main.tsx
··· 1 + import { StrictMode } from "react"; 2 + import ReactDOM from "react-dom/client"; 3 + import { RouterProvider, createRouter } from "@tanstack/react-router"; 4 + 5 + // Import the generated route tree 6 + import { routeTree } from "./routeTree.gen"; 7 + 8 + import "./styles.css"; 9 + import reportWebVitals from "./reportWebVitals.ts"; 10 + import { AuthProvider } from "./providers/PassAuthProvider.tsx"; 11 + import { PersistentStoreProvider } from "./providers/PersistentStoreProvider.tsx"; 12 + 13 + // Create a new router instance 14 + const router = createRouter({ 15 + routeTree, 16 + context: {}, 17 + defaultPreload: "intent", 18 + scrollRestoration: true, 19 + defaultStructuralSharing: true, 20 + defaultPreloadStaleTime: 0, 21 + }); 22 + 23 + // Register the router instance for type safety 24 + declare module "@tanstack/react-router" { 25 + interface Register { 26 + router: typeof router; 27 + } 28 + } 29 + 30 + // Render the app 31 + const rootElement = document.getElementById("app"); 32 + if (rootElement && !rootElement.innerHTML) { 33 + const root = ReactDOM.createRoot(rootElement); 34 + root.render( 35 + <StrictMode> 36 + <PersistentStoreProvider> 37 + <AuthProvider> 38 + <RouterProvider router={router} /> 39 + </AuthProvider> 40 + </PersistentStoreProvider> 41 + </StrictMode> 42 + ); 43 + } 44 + 45 + // If you want to start measuring performance in your app, pass a function 46 + // to log results (for example: reportWebVitals(console.log)) 47 + // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 48 + reportWebVitals();
+144
src/providers/PassAuthProvider.tsx
··· 1 + import React, { createContext, useState, useEffect, useContext } from 'react'; 2 + import { AtpAgent, type AtpSessionData } from '@atproto/api'; 3 + 4 + interface AuthContextValue { 5 + agent: AtpAgent | null; 6 + loginStatus: boolean; 7 + login: (user: string, password: string, service?: string) => Promise<void>; 8 + logout: () => Promise<void>; 9 + loading: boolean; 10 + authed: boolean | undefined; 11 + } 12 + 13 + const AuthContext = createContext<AuthContextValue>({} as AuthContextValue); 14 + 15 + export const AuthProvider = ({ children } : { children: React.ReactNode }) => { 16 + const [agent, setAgent] = useState<AtpAgent | null>(null); 17 + const [loginStatus, setLoginStatus] = useState(false); 18 + const [loading, setLoading] = useState(true); 19 + const [increment, setIncrement] = useState(0); 20 + const [authed, setAuthed] = useState<boolean | undefined>(undefined); 21 + 22 + useEffect(() => { 23 + const initialize = async () => { 24 + try { 25 + const service = localStorage.getItem('service'); 26 + // const user = await AsyncStorage.getItem('user'); 27 + // const password = await AsyncStorage.getItem('password'); 28 + const session = localStorage.getItem("sess"); 29 + 30 + if (service && session) { 31 + console.log("Auto-login service is:", service); 32 + const apiAgent = new AtpAgent({ service }); 33 + try { 34 + if (!apiAgent) { 35 + console.log("Agent is null or undefined"); 36 + return; 37 + } 38 + let sess: AtpSessionData = JSON.parse(session); 39 + console.log("resuming session is:", sess); 40 + const { data } = await apiAgent.resumeSession(sess); 41 + console.log("!!!8!!! agent resume session") 42 + setAgent(apiAgent); 43 + setLoginStatus(true); 44 + setLoading(false); 45 + setAuthed(true); 46 + } catch (e) { 47 + console.log("Failed to resume session" + e); 48 + setLoginStatus(true); 49 + localStorage.removeItem("sess"); 50 + localStorage.removeItem('service'); 51 + const apiAgent = new AtpAgent({ service: 'https://api.bsky.app' }); 52 + setAgent(apiAgent); 53 + setLoginStatus(true); 54 + setLoading(false); 55 + setAuthed(false); 56 + return; 57 + } 58 + } 59 + else { 60 + const apiAgent = new AtpAgent({ service: 'https://api.bsky.app' }); 61 + setAgent(apiAgent); 62 + setLoginStatus(true); 63 + setLoading(false); 64 + setAuthed(false); 65 + } 66 + } catch (e) { 67 + console.log('Failed to auto-login:', e); 68 + } finally { 69 + setLoading(false); 70 + } 71 + }; 72 + 73 + initialize(); 74 + }, [increment]); 75 + 76 + const login = async (user: string, password: string, service: string = 'https://bsky.social') => { 77 + try { 78 + let sessionthing 79 + const apiAgent = new AtpAgent({ 80 + service: service, 81 + persistSession: (evt, sess) => { 82 + sessionthing = sess; 83 + }, 84 + }); 85 + await apiAgent.login({ identifier: user, password }); 86 + console.log("!!!8!!! agent logged on") 87 + 88 + localStorage.setItem('service', service); 89 + // await AsyncStorage.setItem('user', user); 90 + // await AsyncStorage.setItem('password', password); 91 + if (sessionthing) { 92 + localStorage.setItem('sess', JSON.stringify(sessionthing)); 93 + } else { 94 + localStorage.setItem('sess', '{}'); 95 + } 96 + 97 + setAgent(apiAgent); 98 + setLoginStatus(true); 99 + setAuthed(true); 100 + } catch (e) { 101 + console.error('Login failed:', e); 102 + } 103 + }; 104 + 105 + const logout = async () => { 106 + if (!agent) { 107 + console.error("Agent is null or undefined"); 108 + return; 109 + } 110 + setLoading(true); 111 + try { 112 + // check if its even in async storage before removing 113 + if (localStorage.getItem('service') && localStorage.getItem('sess')) { 114 + localStorage.removeItem('service'); 115 + localStorage.removeItem('sess'); 116 + } 117 + await agent.logout(); 118 + console.log("!!!8!!! agent logout") 119 + setLoginStatus(false); 120 + setAuthed(undefined); 121 + await agent.com.atproto.server.deleteSession(); 122 + console.log("!!!8!!! agent deltesession") 123 + //setAgent(null); 124 + setIncrement(increment + 1); 125 + } catch (e) { 126 + console.error("Logout failed:", e); 127 + } finally { 128 + setLoading(false); 129 + } 130 + }; 131 + 132 + // why the hell are we doing this 133 + /*if (loading) { 134 + return <div><span>Laoding...ae</span></div>; 135 + }*/ 136 + 137 + return ( 138 + <AuthContext.Provider value={{ agent, loginStatus, login, logout, loading, authed }}> 139 + {children} 140 + </AuthContext.Provider> 141 + ); 142 + }; 143 + 144 + export const useAuth = () => useContext(AuthContext);
+51
src/providers/PersistentStoreProvider.tsx
··· 1 + import React, { createContext, useContext, useCallback } from 'react'; 2 + import { get as idbGet, set as idbSet, del as idbDel } from 'idb-keyval'; 3 + 4 + type PersistentValue = { 5 + value: string; 6 + time: number; 7 + }; 8 + 9 + type PersistentStoreContextType = { 10 + get: (key: string) => Promise<PersistentValue | null>; 11 + set: (key: string, value: string) => Promise<void>; 12 + remove: (key: string) => Promise<void>; 13 + }; 14 + 15 + const PersistentStoreContext = createContext<PersistentStoreContextType | null>(null); 16 + 17 + export const PersistentStoreProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { 18 + const get = useCallback(async (key: string): Promise<PersistentValue | null> => { 19 + if (typeof window === 'undefined') return null; 20 + const raw = await idbGet(key); 21 + if (!raw) return null; 22 + try { 23 + return JSON.parse(raw) as PersistentValue; 24 + } catch { 25 + return null; 26 + } 27 + }, []); 28 + 29 + const set = useCallback(async (key: string, value: string) => { 30 + if (typeof window === 'undefined') return; 31 + const entry: PersistentValue = { value, time: Date.now() }; 32 + await idbSet(key, JSON.stringify(entry)); 33 + }, []); 34 + 35 + const remove = useCallback(async (key: string) => { 36 + if (typeof window === 'undefined') return; 37 + await idbDel(key); 38 + }, []); 39 + 40 + return ( 41 + <PersistentStoreContext.Provider value={{ get, set, remove }}> 42 + {children} 43 + </PersistentStoreContext.Provider> 44 + ); 45 + }; 46 + 47 + export const usePersistentStore = (): PersistentStoreContextType => { 48 + const context = useContext(PersistentStoreContext); 49 + if (!context) throw new Error('usePersistentStore must be used within a PersistentStoreProvider'); 50 + return context; 51 + };
+24
src/providers/StringStoreProvider.tsx
··· 1 + import React, { createContext, useContext, useState } from 'react'; 2 + 3 + type StringStoreContextType = { 4 + value: string; 5 + setString: (newVal: string) => void; 6 + }; 7 + 8 + const StringStoreContext = createContext<StringStoreContextType | null>(null); 9 + 10 + export const StringStoreProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { 11 + const [value, setValue] = useState(''); 12 + 13 + return ( 14 + <StringStoreContext.Provider value={{ value, setString: setValue }}> 15 + {children} 16 + </StringStoreContext.Provider> 17 + ); 18 + }; 19 + 20 + export const useStringStore = (): StringStoreContextType => { 21 + const context = useContext(StringStoreContext); 22 + if (!context) throw new Error('useStringStore must be used within a StringStoreProvider'); 23 + return context; 24 + };
+13
src/reportWebVitals.ts
··· 1 + const reportWebVitals = (onPerfEntry?: () => void) => { 2 + if (onPerfEntry && onPerfEntry instanceof Function) { 3 + import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => { 4 + onCLS(onPerfEntry) 5 + onINP(onPerfEntry) 6 + onFCP(onPerfEntry) 7 + onLCP(onPerfEntry) 8 + onTTFB(onPerfEntry) 9 + }) 10 + } 11 + } 12 + 13 + export default reportWebVitals
+156
src/routeTree.gen.ts
··· 1 + /* eslint-disable */ 2 + 3 + // @ts-nocheck 4 + 5 + // noinspection JSUnusedGlobalSymbols 6 + 7 + // This file was automatically generated by TanStack Router. 8 + // You should NOT make any changes in this file as it will be overwritten. 9 + // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. 10 + 11 + import { Route as rootRouteImport } from './routes/__root' 12 + import { Route as SearchRouteImport } from './routes/search' 13 + import { Route as IndexRouteImport } from './routes/index' 14 + import { Route as FForumHandleRouteImport } from './routes/f/$forumHandle' 15 + import { Route as FForumHandleIndexRouteImport } from './routes/f/$forumHandle/index' 16 + import { Route as FForumHandleTUserHandleTopicRKeyRouteImport } from './routes/f/$forumHandle/t/$userHandle/$topicRKey' 17 + 18 + const SearchRoute = SearchRouteImport.update({ 19 + id: '/search', 20 + path: '/search', 21 + getParentRoute: () => rootRouteImport, 22 + } as any) 23 + const IndexRoute = IndexRouteImport.update({ 24 + id: '/', 25 + path: '/', 26 + getParentRoute: () => rootRouteImport, 27 + } as any) 28 + const FForumHandleRoute = FForumHandleRouteImport.update({ 29 + id: '/f/$forumHandle', 30 + path: '/f/$forumHandle', 31 + getParentRoute: () => rootRouteImport, 32 + } as any) 33 + const FForumHandleIndexRoute = FForumHandleIndexRouteImport.update({ 34 + id: '/', 35 + path: '/', 36 + getParentRoute: () => FForumHandleRoute, 37 + } as any) 38 + const FForumHandleTUserHandleTopicRKeyRoute = 39 + FForumHandleTUserHandleTopicRKeyRouteImport.update({ 40 + id: '/t/$userHandle/$topicRKey', 41 + path: '/t/$userHandle/$topicRKey', 42 + getParentRoute: () => FForumHandleRoute, 43 + } as any) 44 + 45 + export interface FileRoutesByFullPath { 46 + '/': typeof IndexRoute 47 + '/search': typeof SearchRoute 48 + '/f/$forumHandle': typeof FForumHandleRouteWithChildren 49 + '/f/$forumHandle/': typeof FForumHandleIndexRoute 50 + '/f/$forumHandle/t/$userHandle/$topicRKey': typeof FForumHandleTUserHandleTopicRKeyRoute 51 + } 52 + export interface FileRoutesByTo { 53 + '/': typeof IndexRoute 54 + '/search': typeof SearchRoute 55 + '/f/$forumHandle': typeof FForumHandleIndexRoute 56 + '/f/$forumHandle/t/$userHandle/$topicRKey': typeof FForumHandleTUserHandleTopicRKeyRoute 57 + } 58 + export interface FileRoutesById { 59 + __root__: typeof rootRouteImport 60 + '/': typeof IndexRoute 61 + '/search': typeof SearchRoute 62 + '/f/$forumHandle': typeof FForumHandleRouteWithChildren 63 + '/f/$forumHandle/': typeof FForumHandleIndexRoute 64 + '/f/$forumHandle/t/$userHandle/$topicRKey': typeof FForumHandleTUserHandleTopicRKeyRoute 65 + } 66 + export interface FileRouteTypes { 67 + fileRoutesByFullPath: FileRoutesByFullPath 68 + fullPaths: 69 + | '/' 70 + | '/search' 71 + | '/f/$forumHandle' 72 + | '/f/$forumHandle/' 73 + | '/f/$forumHandle/t/$userHandle/$topicRKey' 74 + fileRoutesByTo: FileRoutesByTo 75 + to: 76 + | '/' 77 + | '/search' 78 + | '/f/$forumHandle' 79 + | '/f/$forumHandle/t/$userHandle/$topicRKey' 80 + id: 81 + | '__root__' 82 + | '/' 83 + | '/search' 84 + | '/f/$forumHandle' 85 + | '/f/$forumHandle/' 86 + | '/f/$forumHandle/t/$userHandle/$topicRKey' 87 + fileRoutesById: FileRoutesById 88 + } 89 + export interface RootRouteChildren { 90 + IndexRoute: typeof IndexRoute 91 + SearchRoute: typeof SearchRoute 92 + FForumHandleRoute: typeof FForumHandleRouteWithChildren 93 + } 94 + 95 + declare module '@tanstack/react-router' { 96 + interface FileRoutesByPath { 97 + '/search': { 98 + id: '/search' 99 + path: '/search' 100 + fullPath: '/search' 101 + preLoaderRoute: typeof SearchRouteImport 102 + parentRoute: typeof rootRouteImport 103 + } 104 + '/': { 105 + id: '/' 106 + path: '/' 107 + fullPath: '/' 108 + preLoaderRoute: typeof IndexRouteImport 109 + parentRoute: typeof rootRouteImport 110 + } 111 + '/f/$forumHandle': { 112 + id: '/f/$forumHandle' 113 + path: '/f/$forumHandle' 114 + fullPath: '/f/$forumHandle' 115 + preLoaderRoute: typeof FForumHandleRouteImport 116 + parentRoute: typeof rootRouteImport 117 + } 118 + '/f/$forumHandle/': { 119 + id: '/f/$forumHandle/' 120 + path: '/' 121 + fullPath: '/f/$forumHandle/' 122 + preLoaderRoute: typeof FForumHandleIndexRouteImport 123 + parentRoute: typeof FForumHandleRoute 124 + } 125 + '/f/$forumHandle/t/$userHandle/$topicRKey': { 126 + id: '/f/$forumHandle/t/$userHandle/$topicRKey' 127 + path: '/t/$userHandle/$topicRKey' 128 + fullPath: '/f/$forumHandle/t/$userHandle/$topicRKey' 129 + preLoaderRoute: typeof FForumHandleTUserHandleTopicRKeyRouteImport 130 + parentRoute: typeof FForumHandleRoute 131 + } 132 + } 133 + } 134 + 135 + interface FForumHandleRouteChildren { 136 + FForumHandleIndexRoute: typeof FForumHandleIndexRoute 137 + FForumHandleTUserHandleTopicRKeyRoute: typeof FForumHandleTUserHandleTopicRKeyRoute 138 + } 139 + 140 + const FForumHandleRouteChildren: FForumHandleRouteChildren = { 141 + FForumHandleIndexRoute: FForumHandleIndexRoute, 142 + FForumHandleTUserHandleTopicRKeyRoute: FForumHandleTUserHandleTopicRKeyRoute, 143 + } 144 + 145 + const FForumHandleRouteWithChildren = FForumHandleRoute._addFileChildren( 146 + FForumHandleRouteChildren, 147 + ) 148 + 149 + const rootRouteChildren: RootRouteChildren = { 150 + IndexRoute: IndexRoute, 151 + SearchRoute: SearchRoute, 152 + FForumHandleRoute: FForumHandleRouteWithChildren, 153 + } 154 + export const routeTree = rootRouteImport 155 + ._addFileChildren(rootRouteChildren) 156 + ._addFileTypes<FileRouteTypes>()
+13
src/routes/__root.tsx
··· 1 + import Header from '@/components/Header' 2 + import { Outlet, createRootRoute } from '@tanstack/react-router' 3 + import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' 4 + 5 + export const Route = createRootRoute({ 6 + component: () => ( 7 + <> 8 + <Header /> 9 + <Outlet /> 10 + <TanStackRouterDevtools /> 11 + </> 12 + ), 13 + })
+271
src/routes/f/$forumHandle.tsx
··· 1 + import { 2 + cachedResolveIdentity, 3 + type ResolvedIdentity, 4 + } from "@/helpers/cachedidentityresolver"; 5 + import { esavQuery } from "@/helpers/esquery"; 6 + import { usePersistentStore } from "@/providers/PersistentStoreProvider"; 7 + import { createFileRoute, Link, useLoaderData, useNavigate } from "@tanstack/react-router"; 8 + import { Outlet } from "@tanstack/react-router"; 9 + import { useEffect, useState } from "react"; 10 + 11 + export const Route = createFileRoute("/f/$forumHandle")({ 12 + loader: ({ params }) => { 13 + console.log("[loader] params.forumHandle:", params.forumHandle); 14 + return { forumHandle: params.forumHandle }; 15 + }, 16 + component: ForumHeader, 17 + }); 18 + 19 + type ForumDoc = { 20 + $type: "com.example.ft.forum.definition"; 21 + $metadata: { 22 + uri: string; 23 + did: string; 24 + }; 25 + displayName?: string; 26 + description?: string; 27 + $raw?: { 28 + avatar?: { ref?: { $link: string } }; 29 + banner?: { ref?: { $link: string } }; 30 + }; 31 + }; 32 + 33 + function ForumHeaderContentSkeleton() { 34 + return ( 35 + <> 36 + <div className="w-full flex flex-col items-center pt-6"> 37 + <div className="w-full max-w-5xl rounded-2xl bg-gray-800 border border-t-0 shadow-2xl overflow-hidden"> 38 + <div className="relative w-full h-32 bg-gray-700/50"> 39 + <div className="absolute inset-0 bg-black/60" /> 40 + <div className="relative z-10 flex items-center p-6 h-full"> 41 + <div className="flex items-center gap-4 max-w-1/2"> 42 + <div className="w-16 h-16 rounded-full bg-gray-700 animate-pulse" /> 43 + <div> 44 + <div className="h-8 w-48 bg-gray-700 rounded-md animate-pulse" /> 45 + <div className="mt-2 h-4 w-32 bg-gray-700 rounded-md animate-pulse" /> 46 + </div> 47 + </div> 48 + <div className="ml-auto text-end max-w-1/2 space-y-2"> 49 + <div className="h-4 w-full bg-gray-700 rounded-md animate-pulse" /> 50 + <div className="h-4 w-10/12 ml-auto bg-gray-700 rounded-md animate-pulse" /> 51 + </div> 52 + </div> 53 + </div> 54 + <div className="flex items-center justify-between pl-3 pr-[6px] py-1.5"> 55 + <div className="flex flex-wrap items-center gap-3 text-sm"> 56 + {[...Array(6)].map((_, i) => ( 57 + <div key={i} className="h-5 w-20 bg-gray-700 rounded animate-pulse" /> 58 + ))} 59 + </div> 60 + <div className="relative w-48"> 61 + <div className="h-[34px] w-full bg-gray-700 rounded-[11px] animate-pulse" /> 62 + </div> 63 + </div> 64 + </div> 65 + </div> 66 + </> 67 + ); 68 + } 69 + function ForumHeaderSearch() { 70 + const navigate = useNavigate(); 71 + const [query, setQuery] = useState(""); 72 + 73 + function handleSubmit(e: React.FormEvent) { 74 + e.preventDefault(); 75 + if (query.trim()) { 76 + navigate({ to: "/search", search: { q: query.trim() } }); 77 + } 78 + } 79 + 80 + return ( 81 + <form onSubmit={handleSubmit} className="relative w-48"> 82 + <input 83 + type="text" 84 + value={query} 85 + onChange={(e) => setQuery(e.target.value)} 86 + placeholder="Search…" 87 + className="w-full rounded-[11px] border border-gray-700 bg-gray-900 px-2.5 py-1.5 pr-9 text-sm text-gray-100 placeholder-gray-400 focus:outline-none focus:ring-1 focus:ring-blue-500" 88 + /> 89 + <svg 90 + className="absolute right-2 top-2.5 h-4 w-4 text-gray-400 pointer-events-none" 91 + xmlns="http://www.w3.org/2000/svg" 92 + fill="none" 93 + viewBox="0 0 24 24" 94 + stroke="currentColor" 95 + > 96 + <path 97 + strokeLinecap="round" 98 + strokeLinejoin="round" 99 + strokeWidth={2} 100 + d="M21 21l-4.35-4.35M11 19a8 8 0 100-16 8 8 0 000 16z" 101 + /> 102 + </svg> 103 + </form> 104 + ); 105 + } 106 + function ForumHeaderContent({ forumDoc, identity, forumHandle }:{ forumDoc:ForumDoc, identity: ResolvedIdentity, forumHandle: string }) { 107 + const did = identity?.did; 108 + const bannerCid = forumDoc?.$raw?.banner?.ref?.$link; 109 + const avatarCid = forumDoc?.$raw?.avatar?.ref?.$link; 110 + const bannerUrl = 111 + did && bannerCid 112 + ? `${identity?.pdsUrl}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${bannerCid}` 113 + : null; 114 + const avatarUrl = 115 + did && avatarCid 116 + ? `${identity?.pdsUrl}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${avatarCid}` 117 + : null; 118 + 119 + return ( 120 + <div className="w-full flex flex-col items-center pt-6"> 121 + <div className="w-full max-w-5xl rounded-2xl bg-gray-800 border border-t-0 shadow-2xl overflow-hidden"> 122 + <div className="relative w-full h-32"> 123 + {bannerUrl ? ( 124 + <div 125 + className="absolute inset-0 bg-cover bg-center" 126 + style={{ backgroundImage: `url(${bannerUrl})` }} 127 + /> 128 + ) : ( 129 + <div className="absolute inset-0 bg-gray-700/50" /> 130 + )} 131 + <div className="absolute inset-0 bg-black/60" /> 132 + <div className="relative z-10 flex items-center p-6 h-full"> 133 + <div className="flex items-center gap-4 max-w-1/2"> 134 + {/*//@ts-ignore */} 135 + <Link to={`/f/${forumHandle}`} className="flex items-center gap-4 no-underline"> 136 + {avatarUrl ? ( 137 + <img 138 + src={avatarUrl} 139 + alt="Forum avatar" 140 + className="w-16 h-16 rounded-full border border-gray-700 object-cover" 141 + /> 142 + ) : ( 143 + <div className="w-16 h-16 rounded-full bg-gray-700 flex items-center justify-center text-gray-400"> 144 + ? 145 + </div> 146 + )} 147 + <div> 148 + <div className="text-white text-3xl font-bold"> 149 + {forumDoc.displayName || "Unnamed Forum"} 150 + </div> 151 + <div className="text-blue-300 font-mono"> 152 + /f/{decodeURIComponent(forumHandle || "")} 153 + </div> 154 + </div> 155 + </Link> 156 + </div> 157 + <div className="ml-auto text-gray-300 text-base text-end max-w-1/2"> 158 + {forumDoc.description || "No description provided."} 159 + </div> 160 + </div> 161 + </div> 162 + 163 + <div className="flex items-center justify-between pl-3 pr-[6px] py-1.5"> 164 + <div className="flex flex-wrap items-center gap-3 text-sm text-gray-300 font-medium"> 165 + {[ 166 + "All Topics", 167 + "Announcements", 168 + "General", 169 + "Support", 170 + "Off-topic", 171 + "Introductions", 172 + "Guides", 173 + "Feedback", 174 + ].map((label) => ( 175 + <button 176 + key={label} 177 + className="hover:underline hover:text-white transition" 178 + onClick={() => console.log(`Clicked ${label}`)} 179 + > 180 + {label} 181 + </button> 182 + ))} 183 + </div> 184 + 185 + <ForumHeaderSearch /> 186 + </div> 187 + </div> 188 + </div> 189 + ); 190 + } 191 + 192 + function ForumHeader() { 193 + const { forumHandle } = useLoaderData({ 194 + from: "/f/$forumHandle", 195 + }); 196 + const { get, set } = usePersistentStore(); 197 + const [forumDoc, setForumDoc] = useState<ForumDoc | null>(null); 198 + const [error, setError] = useState<string | null>(null); 199 + const [identity, setIdentity] = useState<ResolvedIdentity | null>(null); 200 + 201 + useEffect(() => { 202 + setForumDoc(null); 203 + setError(null); 204 + setIdentity(null); 205 + 206 + async function loadForum() { 207 + if (!forumHandle) return; 208 + 209 + try { 210 + const normalizedHandle = decodeURIComponent(forumHandle).replace( 211 + /^@/, 212 + "" 213 + ); 214 + const identity = await cachedResolveIdentity({ 215 + didOrHandle: normalizedHandle, 216 + get, 217 + set, 218 + }); 219 + setIdentity(identity); 220 + 221 + if (!identity) throw new Error("Could not resolve forum handle"); 222 + const resolvedDid = identity.did; 223 + //setDid(resolvedDid); 224 + 225 + const forumRes = await esavQuery<{ 226 + hits: { hits: { _source: ForumDoc }[] }; 227 + }>({ 228 + query: { 229 + bool: { 230 + must: [ 231 + { term: { "$metadata.did": resolvedDid } }, 232 + { 233 + term: { 234 + "$metadata.collection": "com.example.ft.forum.definition", 235 + }, 236 + }, 237 + { term: { "$metadata.rkey": "self" } }, 238 + ], 239 + }, 240 + }, 241 + }); 242 + 243 + const doc = forumRes.hits.hits[0]?._source; 244 + if (!doc) throw new Error("Forum definition not found."); 245 + 246 + setForumDoc(doc); 247 + } catch (e) { 248 + setError((e as Error).message); 249 + } 250 + } 251 + 252 + loadForum(); 253 + }, [forumHandle, get, set]); 254 + 255 + if (error) return <div className="text-red-500 text-center pt-10">Error: {error}</div>; 256 + 257 + return ( 258 + <> 259 + {!forumDoc || !identity ? ( 260 + <ForumHeaderContentSkeleton /> 261 + ) : ( 262 + <ForumHeaderContent 263 + forumDoc={forumDoc} 264 + identity={identity} 265 + forumHandle={forumHandle} 266 + /> 267 + )} 268 + <Outlet /> 269 + </> 270 + ); 271 + }
+652
src/routes/f/$forumHandle/index.tsx
··· 1 + import { 2 + createFileRoute, 3 + useLoaderData, 4 + useNavigate, 5 + Link, 6 + } from "@tanstack/react-router"; 7 + import { useEffect, useState } from "react"; 8 + import { 9 + cachedResolveIdentity, 10 + type ResolvedIdentity, 11 + } from "@/helpers/cachedidentityresolver"; 12 + import { usePersistentStore } from "@/providers/PersistentStoreProvider"; 13 + import { esavQuery } from "@/helpers/esquery"; 14 + import * as Select from "@radix-ui/react-select"; 15 + import * as Dialog from "@radix-ui/react-dialog"; 16 + import { 17 + ChevronDownIcon, 18 + CheckIcon, 19 + Cross2Icon, 20 + } from "@radix-ui/react-icons"; 21 + import { useAuth } from "@/providers/PassAuthProvider"; 22 + import { AtUri } from "@atproto/api"; 23 + 24 + type PostDoc = { 25 + $type: "com.example.ft.topic.post"; 26 + $metadata: { 27 + uri: string; 28 + did: string; 29 + rkey: string; 30 + indexedAt: string; 31 + }; 32 + forum: string; 33 + text: string; 34 + title: string; 35 + reply?: any; 36 + participants?: string[]; 37 + replyCount?: number; 38 + [key: string]: any; 39 + }; 40 + 41 + export const Route = createFileRoute("/f/$forumHandle/")({ 42 + loader: ({ params }) => ({ forumHandle: params.forumHandle }), 43 + component: Forum, 44 + }); 45 + 46 + function getRelativeTimeString(input: string | Date): string { 47 + const date = typeof input === "string" ? new Date(input) : input; 48 + const now = new Date(); 49 + if (isNaN(date.getTime())) return "invalid date"; 50 + const diff = (date.getTime() - now.getTime()) / 1000; 51 + const units: [Intl.RelativeTimeFormatUnit, number][] = [ 52 + ["year", 31536000],["month", 2592000],["week", 604800],["day", 86400],["hour", 3600],["minute", 60],["second", 1], 53 + ]; 54 + const formatter = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); 55 + for (const [unit, secondsInUnit] of units) { 56 + const value = Math.round(diff / secondsInUnit); 57 + if (Math.abs(value) >= 1) return formatter.format(value, unit); 58 + } 59 + return "just now"; 60 + } 61 + 62 + function ForumHeaderSkeleton() { 63 + return ( 64 + <div className="flex flex-wrap items-center justify-between mb-4 gap-4 animate-pulse"> 65 + <div className="flex items-center gap-4"> 66 + <div className="flex items-center gap-2"> 67 + <div className="h-5 w-16 bg-gray-700 rounded-md" /> 68 + <div className="h-9 w-[150px] bg-gray-900 border border-gray-700 rounded-md" /> 69 + </div> 70 + <div className="flex items-center gap-2"> 71 + <div className="h-5 w-16 bg-gray-700 rounded-md" /> 72 + <div className="h-9 w-[150px] bg-gray-900 border border-gray-700 rounded-md" /> 73 + </div> 74 + </div> 75 + <div className="ml-auto h-9 w-28 bg-gray-700 rounded-md" /> 76 + </div> 77 + ); 78 + } 79 + function TopicRowSkeleton() { 80 + return ( 81 + <tr className="bg-gray-800 animate-pulse"> 82 + <td className="px-4 py-3 rounded-l-lg min-w-52"> 83 + <div className="space-y-2"> 84 + <div className="h-5 w-3/4 bg-gray-700 rounded-md" /> 85 + <div className="h-4 w-full bg-gray-600 rounded-md" /> 86 + </div> 87 + </td> 88 + 89 + <td className="px-4 py-3"> 90 + <div className="flex -space-x-2 justify-center"> 91 + <div className="w-6 h-6 rounded-full bg-gray-700 border-2 border-gray-800" /> 92 + <div className="w-6 h-6 rounded-full bg-gray-700 border-2 border-gray-800" /> 93 + <div className="w-6 h-6 rounded-full bg-gray-700 border-2 border-gray-800" /> 94 + </div> 95 + </td> 96 + 97 + <td className="px-4 py-3 text-center"> 98 + <div className="w-8 h-5 bg-gray-700 rounded-md mx-auto" /> 99 + </td> 100 + 101 + <td className="px-4 py-3 text-center"> 102 + <div className="w-10 h-5 bg-gray-700 rounded-md mx-auto" /> 103 + </td> 104 + 105 + <td className="px-4 py-3 text-right rounded-r-lg"> 106 + <div className="flex flex-col items-end space-y-1.5"> 107 + <div className="h-4 w-24 bg-gray-700 rounded-md" /> 108 + <div className="h-3 w-20 bg-gray-600 rounded-md" /> 109 + </div> 110 + </td> 111 + </tr> 112 + ); 113 + } 114 + 115 + export function Forum({ forumHandle: propHandle }: { forumHandle?: string }) { 116 + const navigate = useNavigate(); 117 + const { agent, loading: authLoading } = useAuth(); 118 + const { forumHandle: routeHandle } = useLoaderData({ from: "/f/$forumHandle/" }); 119 + const forumHandle = propHandle ?? routeHandle; 120 + 121 + const { get, set } = usePersistentStore(); 122 + const [posts, setPosts] = useState<PostDoc[]>([]); 123 + const [error, setError] = useState<string | null>(null); 124 + const [identity, setIdentity] = useState<ResolvedIdentity | null>(null); 125 + const [participantAvatars, setParticipantAvatars] = useState<Record<string, { avatarCid?: string; pdsUrl: string; handle?: string }>>({}); 126 + const [postAuthors, setPostAuthors] = useState<Record<string, string>>({}); 127 + const [isLoading, setIsLoading] = useState(true); 128 + 129 + const [selectedCategory, setSelectedCategory] = useState("uncategorized"); 130 + const [sortOrder, setSortOrder] = useState("latest"); 131 + const [isModalOpen, setIsModalOpen] = useState(false); 132 + const [newTopicTitle, setNewTopicTitle] = useState(""); 133 + const [newTopicText, setNewTopicText] = useState(""); 134 + const [isSubmitting, setIsSubmitting] = useState(false); 135 + const [formError, setFormError] = useState<string | null>(null); 136 + 137 + useEffect(() => { 138 + async function loadForum() { 139 + if (!forumHandle) return; 140 + 141 + setIsLoading(true); 142 + setPosts([]); 143 + setError(null); 144 + 145 + try { 146 + const normalizedHandle = decodeURIComponent(forumHandle).replace( 147 + /^@/, 148 + "" 149 + ); 150 + const identity = await cachedResolveIdentity({ 151 + didOrHandle: normalizedHandle, 152 + get, 153 + set, 154 + }); 155 + setIdentity(identity); 156 + 157 + if (!identity) throw new Error("Could not resolve forum handle"); 158 + const resolvedDid = identity.did; 159 + 160 + const postRes = await esavQuery<{ 161 + hits: { hits: { _source: PostDoc }[] }; 162 + }>({ 163 + query: { 164 + bool: { 165 + must: [ 166 + { term: { forum: resolvedDid } }, 167 + { 168 + term: { "$metadata.collection": "com.example.ft.topic.post" }, 169 + }, 170 + { bool: { must_not: [{ exists: { field: "root" } }] } }, 171 + ], 172 + }, 173 + }, 174 + sort: [ 175 + { 176 + "$metadata.indexedAt": { 177 + order: "desc", 178 + }, 179 + }, 180 + ], 181 + size: 100, 182 + }); 183 + 184 + const initialPosts = postRes.hits.hits.map((h) => h._source); 185 + 186 + const postsWithReplies = await Promise.all( 187 + initialPosts.map(async (post) => { 188 + const topicUri = post["$metadata.uri"]; 189 + 190 + const repliesRes = await esavQuery<{ 191 + hits: { total: { value: number } }; 192 + aggregations: { 193 + unique_dids: { buckets: { key: string }[] }; 194 + }; 195 + }>({ 196 + size: 0, 197 + track_total_hits: true, 198 + query: { 199 + bool: { 200 + must: [{ term: { root: topicUri } }], 201 + }, 202 + }, 203 + aggs: { 204 + unique_dids: { 205 + terms: { 206 + field: "$metadata.did", 207 + size: 10000, 208 + }, 209 + }, 210 + }, 211 + }); 212 + 213 + const replyCount = repliesRes.hits.total.value; 214 + const replyDids = repliesRes.aggregations.unique_dids.buckets.map( 215 + (bucket) => bucket.key 216 + ); 217 + 218 + const allParticipants = Array.from( 219 + new Set([post["$metadata.did"], ...replyDids]) 220 + ); 221 + 222 + return { 223 + ...post, 224 + replyCount: replyCount, 225 + participants: allParticipants, 226 + }; 227 + }) 228 + ); 229 + 230 + setPosts(postsWithReplies); 231 + 232 + const authorsToResolve = new Set( 233 + // @ts-ignore 234 + postsWithReplies.map((post) => post["$metadata.did"]) 235 + ); 236 + 237 + const participantsToResolve = new Set<string>(); 238 + postsWithReplies.forEach((post) => { 239 + post.participants?.forEach((did) => { 240 + if (did) participantsToResolve.add(did); 241 + }); 242 + }); 243 + 244 + const peopleToResolve = new Set<string>([ 245 + ...authorsToResolve, 246 + ...participantsToResolve, 247 + ]); 248 + 249 + const resolvedAuthors: Record<string, string> = {}; 250 + await Promise.all( 251 + Array.from(peopleToResolve).map(async (did) => { 252 + try { 253 + const identity = await cachedResolveIdentity({ 254 + didOrHandle: did, 255 + get, 256 + set, 257 + }); 258 + if (identity?.handle) resolvedAuthors[did] = identity.handle; 259 + } catch {} 260 + }) 261 + ); 262 + 263 + setPostAuthors(resolvedAuthors); 264 + } catch (e) { 265 + setError((e as Error).message); 266 + } finally { 267 + setIsLoading(false); 268 + } 269 + } 270 + 271 + loadForum(); 272 + }, [forumHandle, get, set]); 273 + 274 + useEffect(() => { 275 + if (!agent || authLoading || posts.length === 0) return; 276 + 277 + const fetchAvatars = async () => { 278 + const participantsToResolve = new Set<string>(); 279 + posts.forEach((post) => { 280 + post.participants?.forEach((did) => { 281 + if (did) participantsToResolve.add(did); 282 + }); 283 + }); 284 + 285 + const avatarMap: Record< 286 + string, 287 + { avatarCid?: string; pdsUrl: string; handle?: string } 288 + > = {}; 289 + 290 + await Promise.all( 291 + Array.from(participantsToResolve).map(async (did) => { 292 + try { 293 + const identity = await cachedResolveIdentity({ 294 + didOrHandle: did, 295 + get, 296 + set, 297 + }); 298 + if (!identity) return; 299 + 300 + let avatarCid: string | undefined; 301 + try { 302 + const profile = await agent.com.atproto.repo.getRecord({ 303 + repo: did, 304 + collection: "app.bsky.actor.profile", 305 + rkey: "self", 306 + }); 307 + const rejason = JSON.parse(JSON.stringify(profile, null, 2)); 308 + avatarCid = rejason.data?.value?.avatar?.ref?.["$link"]; 309 + } catch {} 310 + 311 + avatarMap[did] = { 312 + avatarCid, 313 + pdsUrl: identity.pdsUrl, 314 + handle: identity.handle, 315 + }; 316 + } catch {} 317 + }) 318 + ); 319 + 320 + setParticipantAvatars(avatarMap); 321 + }; 322 + 323 + fetchAvatars(); 324 + }, [agent, authLoading, posts, get, set]); 325 + 326 + const handleCreateTopic = async () => { 327 + if (!agent || !agent.did || !identity) { 328 + setFormError("You must be logged in to create a topic."); 329 + return; 330 + } 331 + if (!newTopicTitle.trim() || !newTopicText.trim()) { 332 + setFormError("Title and text cannot be empty."); 333 + return; 334 + } 335 + 336 + setIsSubmitting(true); 337 + setFormError(null); 338 + 339 + try { 340 + const topicRecord = { 341 + $type: "com.example.ft.topic.post", 342 + title: newTopicTitle, 343 + text: newTopicText, 344 + createdAt: new Date().toISOString(), 345 + forum: identity.did, 346 + }; 347 + 348 + const response = await agent.com.atproto.repo.createRecord({ 349 + repo: agent?.did, 350 + collection: "com.example.ft.topic.post", 351 + record: topicRecord, 352 + }); 353 + 354 + const postUri = new AtUri(response.data.uri); 355 + 356 + setIsModalOpen(false); 357 + setNewTopicTitle(""); 358 + setNewTopicText(""); 359 + navigate({ 360 + to: `/f/${forumHandle}/t/${agent?.did}/${postUri.rkey}`, 361 + }); 362 + } catch (e) { 363 + console.error("Failed to create topic:", e); 364 + setFormError((e as Error).message); 365 + } finally { 366 + setIsSubmitting(false); 367 + } 368 + }; 369 + 370 + if (error) return <div className="text-red-500 p-8">Error: {error}</div>; 371 + 372 + return ( 373 + <div className="w-full flex flex-col items-center pt-6 px-4"> 374 + <div className="w-full max-w-5xl"> 375 + {isLoading ? ( 376 + <ForumHeaderSkeleton /> 377 + ) : ( 378 + <div className="flex flex-wrap items-center justify-between mb-4 gap-4"> 379 + <div className="flex items-center gap-2"> 380 + <span className="text-gray-100 text-sm">Category:</span> 381 + <Select.Root 382 + value={selectedCategory} 383 + onValueChange={setSelectedCategory} 384 + > 385 + <Select.Trigger 386 + className="inline-flex items-center justify-between rounded-md bg-gray-900 px-3 py-2 text-sm text-gray-100 border border-gray-700 w-[150px] focus:outline-none" 387 + aria-label="Category" 388 + > 389 + <Select.Value placeholder="Select category" /> 390 + <Select.Icon className="text-gray-400"> 391 + <ChevronDownIcon /> 392 + </Select.Icon> 393 + </Select.Trigger> 394 + <Select.Portal> 395 + <Select.Content className="z-50 overflow-hidden rounded-md bg-gray-800 text-gray-100 shadow-lg"> 396 + <Select.Viewport className="p-1"> 397 + {["uncategorized", "general", "meta", "support"].map( 398 + (category) => ( 399 + <Select.Item 400 + key={category} 401 + value={category} 402 + className="flex items-center px-3 py-2 text-sm hover:bg-gray-700 rounded-md cursor-pointer select-none" 403 + > 404 + <Select.ItemIndicator className="mr-2"> 405 + <CheckIcon className="h-3 w-3 text-gray-100" /> 406 + </Select.ItemIndicator> 407 + <Select.ItemText>{category}</Select.ItemText> 408 + </Select.Item> 409 + ) 410 + )} 411 + </Select.Viewport> 412 + </Select.Content> 413 + </Select.Portal> 414 + </Select.Root> 415 + </div> 416 + 417 + <div className="flex items-center gap-2"> 418 + <span className="text-gray-100 text-sm">Sort by:</span> 419 + <Select.Root value={sortOrder} onValueChange={setSortOrder}> 420 + <Select.Trigger 421 + className="inline-flex items-center justify-between rounded-md bg-gray-900 px-3 py-2 text-sm text-gray-100 border border-gray-700 w-[150px] focus:outline-none" 422 + aria-label="Sort" 423 + > 424 + <Select.Value placeholder="Sort by" /> 425 + <Select.Icon className="text-gray-400"> 426 + <ChevronDownIcon /> 427 + </Select.Icon> 428 + </Select.Trigger> 429 + <Select.Portal> 430 + <Select.Content className="z-50 overflow-hidden rounded-md bg-gray-800 text-gray-100 shadow-lg"> 431 + <Select.Viewport className="p-1"> 432 + {["latest", "top", "active", "views"].map((sort) => ( 433 + <Select.Item 434 + key={sort} 435 + value={sort} 436 + className="flex items-center px-3 py-2 text-sm hover:bg-gray-700 rounded-md cursor-pointer select-none" 437 + > 438 + <Select.ItemIndicator className="mr-2"> 439 + <CheckIcon className="h-3 w-3 text-gray-100" /> 440 + </Select.ItemIndicator> 441 + <Select.ItemText>{sort}</Select.ItemText> 442 + </Select.Item> 443 + ))} 444 + </Select.Viewport> 445 + </Select.Content> 446 + </Select.Portal> 447 + </Select.Root> 448 + </div> 449 + 450 + <Dialog.Root open={isModalOpen} onOpenChange={setIsModalOpen}> 451 + <Dialog.Trigger asChild> 452 + <button 453 + className="ml-auto bg-blue-600 hover:bg-blue-500 text-white px-4 py-2 rounded-md text-sm font-semibold transition disabled:bg-gray-500" 454 + disabled={!identity} 455 + title={ 456 + !identity ? "Loading forum..." : "Create a new topic" 457 + } 458 + > 459 + + New Topic 460 + </button> 461 + </Dialog.Trigger> 462 + <Dialog.Portal> 463 + <Dialog.Overlay className="bg-black/60 data-[state=open]:animate-overlayShow fixed inset-0 z-50" /> 464 + <Dialog.Content className="data-[state=open]:animate-contentShow fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50 w-[90vw] max-w-lg p-6 bg-gray-800 text-gray-100 rounded-lg shadow-xl focus:outline-none"> 465 + <Dialog.Title className="text-xl font-bold mb-4"> 466 + Create New Topic in @{forumHandle} 467 + </Dialog.Title> 468 + <Dialog.Close asChild> 469 + <button 470 + className="absolute top-4 right-4 text-gray-400 hover:text-white" 471 + aria-label="Close" 472 + > 473 + <Cross2Icon /> 474 + </button> 475 + </Dialog.Close> 476 + 477 + {!agent || !agent.did ? ( 478 + <div className="text-center py-4"> 479 + <p className="text-gray-300"> 480 + You must be logged in to create a new topic. 481 + </p> 482 + <span className="inline-block mt-4 bg-blue-600 hover:bg-blue-500 text-white px-4 py-2 rounded-md font-semibold"> 483 + Log In 484 + </span> 485 + </div> 486 + ) : ( 487 + <form 488 + onSubmit={(e) => { 489 + e.preventDefault(); 490 + handleCreateTopic(); 491 + }} 492 + > 493 + <fieldset disabled={isSubmitting} className="space-y-4"> 494 + <div> 495 + <label 496 + htmlFor="topic-title" 497 + className="text-sm font-medium text-gray-300 block mb-1" 498 + > 499 + Topic Title 500 + </label> 501 + <input 502 + id="topic-title" 503 + value={newTopicTitle} 504 + onChange={(e) => setNewTopicTitle(e.target.value)} 505 + className="w-full p-2 bg-gray-900 border border-gray-700 rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none" 506 + placeholder="A short, descriptive title" 507 + required 508 + /> 509 + </div> 510 + <div> 511 + <label 512 + htmlFor="topic-text" 513 + className="text-sm font-medium text-gray-300 block mb-1" 514 + > 515 + Topic Content 516 + </label> 517 + <textarea 518 + id="topic-text" 519 + value={newTopicText} 520 + onChange={(e) => setNewTopicText(e.target.value)} 521 + className="w-full p-2 bg-gray-900 border border-gray-700 rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none" 522 + rows={8} 523 + placeholder="Write the main content of your topic here..." 524 + required 525 + /> 526 + </div> 527 + </fieldset> 528 + {formError && ( 529 + <p className="text-red-400 text-sm mt-2"> 530 + {formError} 531 + </p> 532 + )} 533 + <div className="flex justify-end gap-4 mt-6"> 534 + <Dialog.Close asChild> 535 + <button 536 + type="button" 537 + className="px-4 py-2 rounded-md bg-gray-600 hover:bg-gray-500 font-semibold" 538 + disabled={isSubmitting} 539 + > 540 + Cancel 541 + </button> 542 + </Dialog.Close> 543 + <button 544 + type="submit" 545 + className="px-4 py-2 rounded-md bg-blue-600 hover:bg-blue-500 font-semibold disabled:bg-gray-500 disabled:cursor-not-allowed" 546 + disabled={ 547 + isSubmitting || 548 + !newTopicTitle.trim() || 549 + !newTopicText.trim() 550 + } 551 + > 552 + {isSubmitting ? "Creating..." : "Create Topic"} 553 + </button> 554 + </div> 555 + </form> 556 + )} 557 + </Dialog.Content> 558 + </Dialog.Portal> 559 + </Dialog.Root> 560 + </div> 561 + )} 562 + 563 + <table className="w-full table-auto border-separate border-spacing-y-2"> 564 + <thead> 565 + <tr className="text-left text-sm text-gray-400"> 566 + <th className="px-4 py-2">Topic</th> 567 + <th className="px-4 py-2 text-center">Participants</th> 568 + <th className="px-4 py-2 text-center">Replies</th> 569 + <th className="px-4 py-2 text-center">Views</th> 570 + <th className="px-4 py-2 text-right">Last Post</th> 571 + </tr> 572 + </thead> 573 + <tbody> 574 + {isLoading ? ( 575 + Array.from({ length: 10 }).map((_, i) => <TopicRowSkeleton key={i} />) 576 + ) : posts.length > 0 ? ( 577 + posts.map((post) => ( 578 + <tr 579 + onClick={() => 580 + navigate({ 581 + to: `/f/${forumHandle}/t/${post?.["$metadata.did"]}/${post?.["$metadata.rkey"]}`, 582 + }) 583 + } 584 + key={post?.["$metadata.uri"]} 585 + className="bg-gray-800 hover:bg-gray-700/50 rounded-lg cursor-pointer transition-colors duration-150 group relative" 586 + > 587 + <td className="px-4 py-3 text-white rounded-l-lg"> 588 + <Link 589 + // @ts-ignore 590 + to={`/f/${forumHandle}/t/${post?.["$metadata.did"]}/${post?.["$metadata.rkey"]}`} 591 + className="stretched-link" 592 + > 593 + <span className="sr-only">View topic:</span> 594 + </Link> 595 + <div className="font-semibold text-gray-50 line-clamp-1">{post.title}</div> 596 + <div className="text-sm text-gray-400">#general • #meta</div> 597 + </td> 598 + <td className="px-4 py-3"> 599 + <div className="flex -space-x-2 justify-center"> 600 + {post.participants?.slice(0, 5).map((did) => { 601 + const participant = participantAvatars[did]; 602 + const avatarUrl = 603 + participant?.avatarCid && participant?.pdsUrl 604 + ? `${participant.pdsUrl}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${participant.avatarCid}` 605 + : undefined; 606 + return ( 607 + avatarUrl ? 608 + <img 609 + key={did} 610 + src={avatarUrl} 611 + alt={`@${participant?.handle || did.slice(0, 8)}`} 612 + className="w-6 h-6 rounded-full border-2 border-gray-800 object-cover bg-gray-700" 613 + title={`@${participant?.handle || did.slice(0, 8)}`} 614 + /> : <div className="w-6 h-6 rounded-full border-2 border-gray-800 bg-gray-700" /> 615 + ); 616 + })} 617 + </div> 618 + </td> 619 + <td className="px-4 py-3 text-center text-gray-100 font-medium"> 620 + {post.replyCount ?? 0} 621 + </td> 622 + <td className="px-4 py-3 text-center text-gray-300 font-medium"> 623 + idk 624 + </td> 625 + <td className="px-4 py-3 text-gray-400 text-right rounded-r-lg"> 626 + <div className="text-sm"> 627 + by{" "} 628 + <span className="text-blue-400 hover:underline"> 629 + {postAuthors[post?.["$metadata.did"]] 630 + ? `@${postAuthors[post?.["$metadata.did"]]}` 631 + : post?.["$metadata.did"].slice(4,12)} 632 + </span> 633 + </div> 634 + <div className="text-xs"> 635 + {getRelativeTimeString(post?.["$metadata.indexedAt"])} 636 + </div> 637 + </td> 638 + </tr> 639 + )) 640 + ) : ( 641 + <tr> 642 + <td colSpan={5} className="text-center text-gray-500 py-10"> 643 + No topics have been posted yet. Be the first! 644 + </td> 645 + </tr> 646 + )} 647 + </tbody> 648 + </table> 649 + </div> 650 + </div> 651 + ); 652 + }
+606
src/routes/f/$forumHandle/t/$userHandle/$topicRKey.tsx
··· 1 + import { 2 + createFileRoute, 3 + useLoaderData, 4 + Link, 5 + } from "@tanstack/react-router"; 6 + import { useEffect, useMemo, useState, useCallback } from "react"; 7 + import { useAuth } from "@/providers/PassAuthProvider"; 8 + import { usePersistentStore } from "@/providers/PersistentStoreProvider"; 9 + import { esavQuery } from "@/helpers/esquery"; 10 + import { 11 + cachedResolveIdentity, 12 + type ResolvedIdentity, 13 + } from "@/helpers/cachedidentityresolver"; 14 + import AtpAgent from "@atproto/api"; 15 + import { 16 + ArrowLeftIcon, 17 + ChatBubbleIcon, 18 + FaceIcon, 19 + PlusIcon, 20 + Cross2Icon, 21 + } from "@radix-ui/react-icons"; 22 + import * as Popover from "@radix-ui/react-popover"; 23 + 24 + type PostDoc = { 25 + $type: "com.example.ft.topic.post"; 26 + $metadata: { 27 + uri: string; 28 + cid: string; 29 + did: string; 30 + rkey: string; 31 + indexedAt: string; 32 + }; 33 + forum: string; 34 + text: string; 35 + title?: string; 36 + reply?: { 37 + root: { uri: string; cid: string }; 38 + parent: { uri: string; cid: string }; 39 + }; 40 + [key: string]: any; 41 + }; 42 + 43 + type ReactionDoc = { 44 + $type: "com.example.ft.topic.reaction"; 45 + reactionEmoji: string; 46 + reactionSubject: string; 47 + }; 48 + 49 + type AuthorInfo = ResolvedIdentity & { 50 + avatarCid?: string; 51 + displayName?: string; 52 + footer?: string; 53 + }; 54 + 55 + const EMOJI_SELECTION = ["👍", "❤️", "😂", "🔥", "🤔", "🎉", "🙏", "🤯"]; 56 + 57 + export const Route = createFileRoute( 58 + "/f/$forumHandle/t/$userHandle/$topicRKey" 59 + )({ 60 + loader: ({ params }) => { 61 + return { 62 + forumHandle: decodeURIComponent(params.forumHandle), 63 + userHandle: decodeURIComponent(params.userHandle), 64 + topicRKey: params.topicRKey, 65 + }; 66 + }, 67 + component: ForumTopic, 68 + }); 69 + 70 + export function PostCardSkeleton() { 71 + return ( 72 + <div className="flex w-full gap-4 bg-gray-800 border border-gray-700/50 rounded-xl p-4 animate-pulse"> 73 + <div className="w-32 flex-shrink-0 space-y-3"> 74 + <div className="w-12 h-12 rounded-full bg-gray-700 mx-auto" /> 75 + <div className="h-4 w-3/4 bg-gray-700 rounded-md mx-auto" /> 76 + <div className="h-3 w-1/2 bg-gray-700 rounded-md mx-auto" /> 77 + <div className="border-t border-gray-700 pt-3 mt-3 space-y-2"> 78 + <div className="h-3 w-full bg-gray-600 rounded" /> 79 + <div className="h-3 w-10/12 bg-gray-600 rounded" /> 80 + </div> 81 + </div> 82 + <div className="flex-grow space-y-4"> 83 + <div className="h-4 w-28 bg-gray-700 rounded-md" /> 84 + <div className="space-y-2.5"> 85 + <div className="h-4 w-full bg-gray-600 rounded" /> 86 + <div className="h-4 w-11/12 bg-gray-600 rounded" /> 87 + <div className="h-4 w-4/5 bg-gray-600 rounded" /> 88 + </div> 89 + <div className="flex justify-between items-center pt-2"> 90 + <div className="h-6 w-16 bg-gray-700 rounded-md" /> 91 + <div className="h-8 w-20 bg-gray-700 rounded-md" />{" "} 92 + </div> 93 + </div> 94 + </div> 95 + ); 96 + } 97 + 98 + function TopicPageSkeleton() { 99 + return ( 100 + <div className="w-full flex flex-col items-center pt-6 px-4"> 101 + <div className="w-full max-w-5xl space-y-4"> 102 + <div className="animate-pulse"> 103 + <div className="h-5 w-48 bg-gray-700 rounded-md" /> 104 + <div className="h-8 w-3/4 bg-gray-700 rounded-md mt-4" /> 105 + </div> 106 + <PostCardSkeleton /> 107 + <PostCardSkeleton /> 108 + <div className="p-4 bg-gray-800 rounded-xl border border-gray-700/50 animate-pulse"> 109 + <div className="h-6 w-32 bg-gray-700 rounded-md mb-3" /> 110 + <div className="h-28 w-full bg-gray-900 border border-gray-700 rounded-md" /> 111 + <div className="flex justify-end mt-3"> 112 + <div className="h-10 w-32 bg-gray-700 rounded-md" /> 113 + </div> 114 + </div> 115 + </div> 116 + </div> 117 + ); 118 + } 119 + 120 + function UserInfoColumn({ author }: { author: AuthorInfo | null }) { 121 + const avatarUrl = 122 + author?.avatarCid && author?.pdsUrl 123 + ? `${author.pdsUrl}/xrpc/com.atproto.sync.getBlob?did=${author.did}&cid=${author.avatarCid}` 124 + : undefined; 125 + 126 + const authorDisplayName = author?.displayName || author?.handle || "Unknown"; 127 + const authorHandle = author?.handle ? `@${author.handle}` : "did:..."; 128 + 129 + return ( 130 + <div className="w-32 flex-shrink-0 text-center text-sm text-gray-400"> 131 + {avatarUrl ? ( 132 + <img 133 + src={avatarUrl} 134 + alt={authorDisplayName} 135 + className="w-12 h-12 rounded-full object-cover bg-gray-700 mx-auto" 136 + /> 137 + ) : ( 138 + <div className="w-12 h-12 rounded-full bg-gray-700 flex items-center justify-center text-gray-400 font-bold text-2xl mx-auto"> 139 + {authorDisplayName.charAt(0)} 140 + </div> 141 + )} 142 + <div className="font-bold text-[15px] text-white mt-1 break-words whitespace-normal"> 143 + {authorDisplayName} 144 + </div> 145 + <div className="break-words whitespace-normal">{authorHandle}</div> 146 + {author?.footer && ( 147 + <div className="border-t border-gray-700/80 mt-4 pt-3 text-xs text-gray-500 text-left whitespace-pre-wrap break-words"> 148 + {author.footer} 149 + </div> 150 + )} 151 + </div> 152 + ); 153 + } 154 + 155 + function Reactions({ reactions }: { reactions: ReactionDoc[] }) { 156 + const groupedReactions = useMemo(() => { 157 + return reactions.reduce( 158 + (acc, reaction) => { 159 + acc[reaction.reactionEmoji] = (acc[reaction.reactionEmoji] || 0) + 1; 160 + return acc; 161 + }, 162 + {} as Record<string, number> 163 + ); 164 + }, [reactions]); 165 + 166 + if (!reactions || reactions.length === 0) return null; 167 + 168 + return ( 169 + <div className="flex items-center gap-2"> 170 + {Object.entries(groupedReactions).map(([emoji, count]) => ( 171 + <button 172 + key={emoji} 173 + className="flex items-center gap-1.5 bg-gray-700/50 hover:bg-gray-700/80 px-2 py-1 rounded-full text-sm text-gray-300 transition-colors" 174 + > 175 + <span>{emoji}</span> 176 + <span className="font-semibold">{count}</span> 177 + </button> 178 + ))} 179 + </div> 180 + ); 181 + } 182 + 183 + export function PostCard({ 184 + agent, 185 + post, 186 + author, 187 + reactions, 188 + index, 189 + onSetReplyParent, 190 + onNewReaction, 191 + isCreatingReaction, 192 + }: { 193 + agent: AtpAgent | null; 194 + post: PostDoc; 195 + author: AuthorInfo | null; 196 + reactions: ReactionDoc[]; 197 + index: number; 198 + onSetReplyParent: (post: PostDoc) => void; 199 + onNewReaction: (post: PostDoc, emoji: string) => Promise<void>; 200 + isCreatingReaction: boolean; 201 + }) { 202 + const postUri = post["$metadata.uri"]; 203 + const postDate = new Date(post["$metadata.indexedAt"]); 204 + 205 + return ( 206 + <div 207 + id={postUri} 208 + className="w-full bg-gray-800 border border-gray-700/50 rounded-xl flex gap-x-4 p-1" 209 + > 210 + <div className="bg-gray-800/50 p-3 rounded-l-lg"> 211 + <UserInfoColumn author={author} /> 212 + </div> 213 + 214 + <div className="flex-grow flex flex-col py-3 pr-4"> 215 + <div className="flex justify-between items-center text-xs text-gray-500 mb-4 border-b border-gray-700/50 pb-2"> 216 + <span>{postDate.toLocaleString()}</span> 217 + <a 218 + href={`#${postUri}`} 219 + className="font-mono hover:underline hover:text-gray-300" 220 + > 221 + #{index + 1} 222 + </a> 223 + </div> 224 + 225 + <div className="flex-grow"> 226 + <p className="whitespace-pre-wrap text-gray-200 text-[15px] leading-relaxed"> 227 + {post.text} 228 + </p> 229 + </div> 230 + 231 + <div className="flex justify-between items-center mt-4 pt-3 border-t border-gray-700/50"> 232 + <div className="flex items-center gap-1"> 233 + <Reactions reactions={reactions} /> 234 + <Popover.Root> 235 + <Popover.Trigger asChild> 236 + <button 237 + disabled={isCreatingReaction || !agent || !agent.did} 238 + className="flex items-center justify-center w-8 h-8 bg-gray-700/50 hover:bg-gray-700/80 rounded-full text-gray-400 hover:text-white transition-colors disabled:opacity-50" 239 + aria-label="Add reaction" 240 + > 241 + <FaceIcon className="w-4 h-4" /> 242 + <PlusIcon className="w-3 h-3 -ml-1.5 mt-2" /> 243 + </button> 244 + </Popover.Trigger> 245 + <Popover.Portal> 246 + <Popover.Content 247 + sideOffset={5} 248 + className="bg-gray-900 border border-gray-700 rounded-lg p-2 z-10 shadow-lg" 249 + > 250 + <div className="grid grid-cols-4 gap-1"> 251 + {EMOJI_SELECTION.map((emoji) => ( 252 + <Popover.Close key={emoji} asChild> 253 + <button 254 + onClick={() => onNewReaction(post, emoji)} 255 + className="text-2xl p-1.5 rounded-md hover:bg-gray-700/80 transition-colors" 256 + aria-label={`React with ${emoji}`} 257 + > 258 + {emoji} 259 + </button> 260 + </Popover.Close> 261 + ))} 262 + </div> 263 + </Popover.Content> 264 + </Popover.Portal> 265 + </Popover.Root> 266 + </div> 267 + <div className="spacer flex-1" /> 268 + <button 269 + onClick={() => onSetReplyParent(post)} 270 + className="flex items-center gap-2 bg-blue-600/20 hover:bg-blue-600/40 text-blue-300 px-3 py-1.5 rounded-md font-semibold text-sm transition" 271 + > 272 + <ChatBubbleIcon /> 273 + <span>Reply</span> 274 + </button> 275 + </div> 276 + </div> 277 + </div> 278 + ); 279 + } 280 + 281 + export function ForumTopic() { 282 + const { forumHandle, userHandle, topicRKey } = useLoaderData({ 283 + from: "/f/$forumHandle/t/$userHandle/$topicRKey", 284 + }); 285 + 286 + const { agent, loading: authLoading } = useAuth(); 287 + const { get, set } = usePersistentStore(); 288 + 289 + const [posts, setPosts] = useState<PostDoc[]>([]); 290 + const [authors, setAuthors] = useState<Record<string, AuthorInfo>>({}); 291 + const [reactions, setReactions] = useState<Record<string, ReactionDoc[]>>({}); 292 + const [error, setError] = useState<string | null>(null); 293 + const [isLoading, setIsLoading] = useState(true); 294 + 295 + const [replyText, setReplyText] = useState(""); 296 + const [isSubmitting, setIsSubmitting] = useState(false); 297 + const [replyingTo, setReplyingTo] = useState<PostDoc | null>(null); 298 + const [isCreatingReaction, setIsCreatingReaction] = useState(false); 299 + 300 + const loadTopic = useCallback(async () => { 301 + setError(null); 302 + try { 303 + const authorIdentity = await cachedResolveIdentity({ 304 + didOrHandle: userHandle, 305 + get, 306 + set, 307 + }); 308 + if (!authorIdentity) throw new Error("Could not find topic author."); 309 + const topicUri = `at://${authorIdentity.did}/com.example.ft.topic.post/${topicRKey}`; 310 + 311 + const [postRes, repliesRes] = await Promise.all([ 312 + esavQuery<{ hits: { hits: { _source: PostDoc }[] } }>({ 313 + query: { term: { "$metadata.uri": topicUri } }, 314 + size: 1, 315 + }), 316 + esavQuery<{ hits: { hits: { _source: PostDoc }[] } }>({ 317 + query: { term: { root: topicUri } }, 318 + sort: [{ "$metadata.indexedAt": "asc" }], 319 + size: 100, 320 + }), 321 + ]); 322 + 323 + if (postRes.hits.hits.length === 0) throw new Error("Topic not found."); 324 + const mainPost = postRes.hits.hits[0]._source; 325 + const fetchedReplies = repliesRes.hits.hits.map((h) => h._source); 326 + const allPosts = [mainPost, ...fetchedReplies]; 327 + setPosts(allPosts); 328 + 329 + const postUris = allPosts.map((p) => p["$metadata.uri"]); 330 + const authorDids = [...new Set(allPosts.map((p) => p["$metadata.did"]))]; 331 + 332 + const [reactionsRes, footersRes, pdsProfiles] = await Promise.all([ 333 + esavQuery<{ hits: { hits: { _source: ReactionDoc }[] } }>({ 334 + query: { 335 + bool: { 336 + must: [ 337 + { 338 + term: { 339 + "$metadata.collection": "com.example.ft.topic.reaction", 340 + }, 341 + }, 342 + { terms: { reactionSubject: postUris } }, 343 + ], 344 + }, 345 + }, 346 + _source: ["reactionSubject", "reactionEmoji"], 347 + size: 1000, 348 + }), 349 + 350 + esavQuery<{ 351 + hits: { 352 + hits: { _source: { "$metadata.did": string; footer: string } }[]; 353 + }; 354 + }>({ 355 + query: { 356 + bool: { 357 + must: [ 358 + { term: { $type: "com.example.ft.user.profile" } }, 359 + { terms: { "$metadata.did": authorDids } }, 360 + ], 361 + }, 362 + }, 363 + _source: ["$metadata.did", "footer"], 364 + size: authorDids.length, 365 + }), 366 + 367 + Promise.all( 368 + authorDids.map(async (did) => { 369 + if (!agent) return { did, profile: null }; 370 + try { 371 + const res = await agent.com.atproto.repo.getRecord({ 372 + repo: did, 373 + collection: "app.bsky.actor.profile", 374 + rkey: "self", 375 + }); 376 + return { 377 + did, 378 + profile: JSON.parse(JSON.stringify(res.data.value)), 379 + }; 380 + } catch (e) { 381 + return { did, profile: null }; 382 + } 383 + }) 384 + ), 385 + ]); 386 + 387 + const reactionsByPostUri = reactionsRes.hits.hits.reduce( 388 + (acc, hit) => { 389 + const reaction = hit._source; 390 + (acc[reaction.reactionSubject] = acc[reaction.reactionSubject] || []).push(reaction); 391 + return acc; 392 + }, 393 + {} as Record<string, ReactionDoc[]> 394 + ); 395 + setReactions(reactionsByPostUri); 396 + 397 + const footersByDid = footersRes.hits.hits.reduce( 398 + (acc, hit) => { 399 + acc[hit._source["$metadata.did"]] = hit._source.footer; 400 + return acc; 401 + }, 402 + {} as Record<string, string> 403 + ); 404 + 405 + const newAuthors: Record<string, AuthorInfo> = {}; 406 + await Promise.all( 407 + authorDids.map(async (did) => { 408 + const identity = await cachedResolveIdentity({ 409 + didOrHandle: did, 410 + get, 411 + set, 412 + }); 413 + if (!identity) return; 414 + const pdsProfile = pdsProfiles.find((p) => p.did === did)?.profile; 415 + newAuthors[did] = { 416 + ...identity, 417 + displayName: pdsProfile?.displayName, 418 + avatarCid: pdsProfile?.avatar?.ref?.["$link"], 419 + footer: footersByDid[did], 420 + }; 421 + }) 422 + ); 423 + setAuthors(newAuthors); 424 + } catch (e) { 425 + setError((e as Error).message); 426 + } 427 + }, [topicRKey, userHandle, get, set, agent]); 428 + 429 + useEffect(() => { 430 + if (!authLoading) { 431 + setIsLoading(true); 432 + loadTopic().finally(() => setIsLoading(false)); 433 + } 434 + }, [authLoading, loadTopic]); 435 + 436 + const handleSetReplyParent = (post: PostDoc) => { 437 + setReplyingTo(post); 438 + document.getElementById("reply-box")?.focus(); 439 + }; 440 + 441 + const handleCreateReaction = async (post: PostDoc, emoji: string) => { 442 + if (!agent?.did || isCreatingReaction) return; 443 + setIsCreatingReaction(true); 444 + const postUri = post["$metadata.uri"]; 445 + try { 446 + await agent.com.atproto.repo.createRecord({ 447 + repo: agent.did, 448 + collection: "com.example.ft.topic.reaction", 449 + record: { 450 + $type: "com.example.ft.topic.reaction", 451 + reactionEmoji: emoji, 452 + subject: postUri, 453 + createdAt: new Date().toISOString(), 454 + }, 455 + }); 456 + const newReaction: ReactionDoc = { 457 + $type: "com.example.ft.topic.reaction", 458 + reactionEmoji: emoji, 459 + reactionSubject: postUri, 460 + }; 461 + setReactions((prev) => ({ 462 + ...prev, 463 + [postUri]: [...(prev[postUri] || []), newReaction], 464 + })); 465 + } catch (e) { 466 + console.error("Failed to create reaction", e); 467 + setError("Failed to post reaction. Please try again."); 468 + } finally { 469 + setIsCreatingReaction(false); 470 + } 471 + }; 472 + 473 + const handleReply = async () => { 474 + if (!agent?.did || isSubmitting || !replyText.trim() || posts.length === 0) 475 + return; 476 + setIsSubmitting(true); 477 + setError(null); 478 + try { 479 + const rootPost = posts[0]; 480 + const rootRef = { 481 + uri: rootPost["$metadata.uri"], 482 + cid: rootPost["$metadata.cid"], 483 + }; 484 + const parentPost = replyingTo || rootPost; 485 + const parentRef = { 486 + uri: parentPost["$metadata.uri"], 487 + cid: parentPost["$metadata.cid"], 488 + }; 489 + await agent.com.atproto.repo.createRecord({ 490 + repo: agent.did, 491 + collection: "com.example.ft.topic.post", 492 + record: { 493 + $type: "com.example.ft.topic.post", 494 + text: replyText, 495 + forum: forumHandle, 496 + reply: { root: rootRef, parent: parentRef }, 497 + createdAt: new Date().toISOString(), 498 + }, 499 + }); 500 + setReplyText(""); 501 + setReplyingTo(null); 502 + await loadTopic(); 503 + } catch (e) { 504 + setError(`Failed to post reply: ${(e as Error).message}`); 505 + } finally { 506 + setIsSubmitting(false); 507 + } 508 + }; 509 + 510 + if (isLoading) return <TopicPageSkeleton />; 511 + if (error) 512 + return ( 513 + <div className="text-center text-red-500 pt-20 text-lg"> 514 + Error: {error} 515 + </div> 516 + ); 517 + if (posts.length === 0) 518 + return ( 519 + <div className="text-center text-gray-400 pt-20 text-lg"> 520 + Topic not found. 521 + </div> 522 + ); 523 + 524 + const topicPost = posts[0]; 525 + const postIndexBeingRepliedTo = replyingTo 526 + ? posts.findIndex((p) => p["$metadata.uri"] === replyingTo["$metadata.uri"]) 527 + : -1; 528 + 529 + return ( 530 + <div className="w-full flex flex-col items-center pt-6 px-4 pb-12"> 531 + <div className="w-full max-w-5xl space-y-4"> 532 + <div> 533 + <Link 534 + to="/f/$forumHandle" 535 + params={{ forumHandle }} 536 + className="inline-flex items-center gap-2 text-sm text-blue-300 hover:text-white hover:underline transition-colors" 537 + > 538 + <ArrowLeftIcon /> 539 + Back to /f/{forumHandle} 540 + </Link> 541 + {topicPost.title && ( 542 + <h1 className="text-3xl font-bold text-gray-50 mt-4 break-words"> 543 + {topicPost.title} 544 + </h1> 545 + )} 546 + </div> 547 + 548 + {posts.map((post, index) => ( 549 + <PostCard 550 + agent={agent} 551 + key={post["$metadata.uri"]} 552 + post={post} 553 + author={authors[post["$metadata.did"]]} 554 + reactions={reactions[post["$metadata.uri"]] || []} 555 + index={index} 556 + onSetReplyParent={handleSetReplyParent} 557 + onNewReaction={handleCreateReaction} 558 + isCreatingReaction={isCreatingReaction} 559 + /> 560 + ))} 561 + 562 + {agent && !authLoading && ( 563 + <div className="p-4 bg-gray-800 rounded-xl border border-gray-700/50"> 564 + <div className="flex justify-between items-center mb-2"> 565 + <h3 className="text-lg font-semibold text-gray-200"> 566 + {replyingTo ? "Write a Reply" : "Reply to Topic"} 567 + </h3> 568 + {replyingTo && postIndexBeingRepliedTo !== -1 && ( 569 + <div className="flex items-center gap-2 text-sm bg-gray-700/60 px-3 py-1 rounded-full"> 570 + <span className="text-gray-400"> 571 + Replying to #{postIndexBeingRepliedTo + 1} 572 + </span> 573 + <button 574 + onClick={() => setReplyingTo(null)} 575 + className="text-gray-500 hover:text-white" 576 + aria-label="Cancel reply" 577 + > 578 + <Cross2Icon /> 579 + </button> 580 + </div> 581 + )} 582 + </div> 583 + <textarea 584 + id="reply-box" 585 + value={replyText} 586 + onChange={(e) => setReplyText(e.target.value)} 587 + className="w-full p-3 bg-gray-900 border border-gray-700 rounded-md text-gray-200 focus:ring-2 focus:ring-blue-500 focus:outline-none transition" 588 + rows={5} 589 + placeholder="Share your thoughts..." 590 + disabled={isSubmitting} 591 + /> 592 + <div className="flex justify-end mt-3"> 593 + <button 594 + onClick={handleReply} 595 + disabled={isSubmitting || !replyText.trim() || !agent?.did} 596 + className="bg-blue-600 hover:bg-blue-500 text-white px-5 py-2 rounded-md font-semibold transition disabled:bg-gray-600 disabled:cursor-not-allowed" 597 + > 598 + {isSubmitting ? "Posting..." : "Submit Reply"} 599 + </button> 600 + </div> 601 + </div> 602 + )} 603 + </div> 604 + </div> 605 + ); 606 + }
+204
src/routes/index.tsx
··· 1 + import { createFileRoute, Link } from "@tanstack/react-router"; 2 + import { useEffect, useState } from "react"; 3 + import "../App.css"; 4 + import { esavQuery } from "@/helpers/esquery"; 5 + import { usePersistentStore } from "@/providers/PersistentStoreProvider"; 6 + import { cachedResolveIdentity } from "@/helpers/cachedidentityresolver"; 7 + 8 + type ForumDoc = { 9 + $type: "com.example.ft.forum.definition"; 10 + $metadata: { 11 + collection: string; 12 + uri: string; 13 + did: string; 14 + }; 15 + displayName?: string; 16 + description?: string; 17 + $raw?: { 18 + avatar?: { 19 + ref?: { $link: string }; 20 + }; 21 + banner?: { 22 + ref?: { $link: string }; 23 + }; 24 + }; 25 + [key: string]: any; 26 + }; 27 + 28 + type ResolvedForum = ForumDoc & { 29 + resolvedIdentity?: { 30 + handle: string; 31 + pdsUrl: string; 32 + }; 33 + }; 34 + 35 + export const Route = createFileRoute("/")({ 36 + component: Home, 37 + }); 38 + 39 + function ForumCardSkeleton() { 40 + return ( 41 + <div className="relative bg-zinc-900 rounded-2xl overflow-hidden border border-zinc-800 shadow-sm aspect-video animate-pulse"> 42 + <div className="absolute inset-0 bg-black/60" /> 43 + 44 + <div className="relative z-10 flex flex-col justify-between h-full p-5"> 45 + <div className="flex justify-between items-start gap-4"> 46 + <div className="flex flex-col"> 47 + <div className="h-5 w-40 bg-gray-700 rounded-md mb-2" /> 48 + <div className="h-7 w-56 bg-gray-700 rounded-md" /> 49 + </div> 50 + <div className="w-12 h-12 rounded-full bg-gray-700 flex-shrink-0" /> 51 + </div> 52 + 53 + <div className="flex flex-col gap-2 mt-4"> 54 + <div className="h-4 w-full bg-gray-600 rounded-md" /> 55 + <div className="h-4 w-3/4 bg-gray-600 rounded-md" /> 56 + <div className="h-3 w-1/2 bg-gray-700 rounded-md mt-2" /> 57 + </div> 58 + </div> 59 + </div> 60 + ); 61 + } 62 + 63 + function Home() { 64 + const [forums, setForums] = useState<ResolvedForum[]>([]); 65 + const [loading, setLoading] = useState(true); 66 + const [error, setError] = useState<string | null>(null); 67 + const { get, set } = usePersistentStore(); 68 + 69 + useEffect(() => { 70 + async function fetchForums() { 71 + try { 72 + const res = await esavQuery<{ 73 + hits: { hits: { _source: ForumDoc }[] }; 74 + }>({ 75 + query: { 76 + bool: { 77 + must: [ 78 + { term: { "$metadata.collection": "com.example.ft.forum.definition" } }, 79 + { term: { "$metadata.rkey": "self" } }, 80 + ], 81 + }, 82 + }, 83 + size: 50, 84 + }); 85 + 86 + const rawForums = res.hits.hits.map((hit) => hit._source); 87 + 88 + const resolvedForums = ( 89 + await Promise.all( 90 + rawForums.map(async (forum) => { 91 + const did = forum?.["$metadata.did"]; 92 + if (!did) return null; 93 + try { 94 + const identity = await cachedResolveIdentity({ didOrHandle: did, get, set }); 95 + return identity ? { ...forum, resolvedIdentity: { handle: identity.handle, pdsUrl: identity.pdsUrl } } : null; 96 + } catch (e) { 97 + console.warn(`Failed to resolve identity for ${did}`, e); 98 + return null; 99 + } 100 + }) 101 + ) 102 + ).filter(Boolean) as ResolvedForum[]; 103 + 104 + setForums(resolvedForums); 105 + } catch (err) { 106 + setError((err as Error).message); 107 + } finally { 108 + setLoading(false); 109 + } 110 + } 111 + 112 + fetchForums(); 113 + }, [get, set]); 114 + 115 + return ( 116 + <div className="w-full flex flex-col items-center"> 117 + <div className="w-full max-w-7xl flex items-center flex-col"> 118 + <div className="w-full max-w-5xl mt-4 px-4 sm:px-0"> 119 + <div> 120 + <span className="text-gray-50 font-bold text-2xl">Forums</span> 121 + </div> 122 + 123 + <div className="mt-4 w-full forum-grid"> 124 + {loading ? ( 125 + Array.from({ length: 6 }).map((_, i) => <ForumCardSkeleton key={i} />) 126 + ) : error ? ( 127 + <div className="text-red-500 col-span-full text-center py-10">Error: {error}</div> 128 + ) : ( 129 + forums.map((forum) => { 130 + const did = forum?.["$metadata.did"]; 131 + const { resolvedIdentity } = forum; 132 + if (!resolvedIdentity) return null; 133 + 134 + const cidBanner = forum?.$raw?.banner?.ref?.$link; 135 + const cidAvatar = forum?.$raw?.avatar?.ref?.$link; 136 + 137 + const bannerUrl = 138 + cidBanner && resolvedIdentity 139 + ? `${resolvedIdentity.pdsUrl}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cidBanner}` 140 + : null; 141 + 142 + const avatarUrl = 143 + cidAvatar && resolvedIdentity 144 + ? `${resolvedIdentity.pdsUrl}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cidAvatar}` 145 + : null; 146 + 147 + return ( 148 + <Link 149 + // @ts-ignore 150 + to={`/f/@${resolvedIdentity.handle}`} 151 + className="block" 152 + key={forum?.$metadata?.uri} 153 + > 154 + <div 155 + key={forum?.$metadata?.uri} 156 + className="relative bg-zinc-900 rounded-2xl overflow-hidden border border-zinc-800 shadow-sm aspect-video hover:border-blue-500/50 transition-all duration-200" 157 + > 158 + {bannerUrl && ( 159 + <div 160 + className="absolute inset-0 bg-cover bg-center" 161 + style={{ backgroundImage: `url(${bannerUrl})` }} 162 + /> 163 + )} 164 + <div className="absolute inset-0 bg-black/60" /> 165 + <div className="relative z-10 flex flex-col justify-between h-full p-5"> 166 + <div className="flex justify-between items-start gap-4"> 167 + <div className="flex flex-col"> 168 + {resolvedIdentity?.handle && ( 169 + <div className="text-blue-300 text-base font-mono mb-1"> 170 + /f/@{resolvedIdentity.handle} 171 + </div> 172 + )} 173 + <div className="text-white text-2xl font-bold leading-tight"> 174 + {forum.displayName || 'Unnamed Forum'} 175 + </div> 176 + </div> 177 + {avatarUrl && ( 178 + <img 179 + src={avatarUrl} 180 + alt="Avatar" 181 + className="w-12 h-12 rounded-full object-cover border border-zinc-700 flex-shrink-0" 182 + /> 183 + )} 184 + </div> 185 + <div className="flex flex-col gap-2 mt-4"> 186 + <div className="text-sm text-gray-200 line-clamp-2"> 187 + {forum.description || 'No description available.'} 188 + </div> 189 + <div className="text-xs text-gray-400 font-medium"> 190 + 0 members · ~0 topics · Active a while ago 191 + </div> 192 + </div> 193 + </div> 194 + </div> 195 + </Link> 196 + ); 197 + }) 198 + )} 199 + </div> 200 + </div> 201 + </div> 202 + </div> 203 + ); 204 + }
+253
src/routes/search.tsx
··· 1 + import { 2 + createFileRoute, 3 + useSearch, 4 + useNavigate, 5 + Link, 6 + } from "@tanstack/react-router"; 7 + import { useEffect, useState, useCallback, useMemo } from "react"; 8 + import { useAuth } from "@/providers/PassAuthProvider"; 9 + import { usePersistentStore } from "@/providers/PersistentStoreProvider"; 10 + import { esavQuery } from "@/helpers/esquery"; 11 + import { 12 + cachedResolveIdentity, 13 + type ResolvedIdentity, 14 + } from "@/helpers/cachedidentityresolver"; 15 + import AtpAgent, { AtUri } from "@atproto/api"; 16 + import { ArrowRightIcon } from "@radix-ui/react-icons"; 17 + import { PostCard, PostCardSkeleton } from "@/routes/f/$forumHandle/t/$userHandle/$topicRKey"; // Adjust path as needed 18 + 19 + type PostDoc = { 20 + $type: "com.example.ft.topic.post"; 21 + $metadata: { uri: string; cid: string; did: string; rkey: string; indexedAt: string; }; 22 + forum: string; 23 + text: string; 24 + title?: string; 25 + reply?: { root: { uri:string; cid:string; }; parent: { uri:string; cid:string; }; }; 26 + [key: string]: any; 27 + }; 28 + type ReactionDoc = { 29 + $type: "com.example.ft.topic.reaction"; 30 + reactionEmoji: string; 31 + reactionSubject: string; 32 + }; 33 + type AuthorInfo = ResolvedIdentity & { 34 + avatarCid?: string; 35 + displayName?: string; 36 + footer?: string; 37 + }; 38 + 39 + export const Route = createFileRoute("/search")({ 40 + validateSearch: (search: Record<string, unknown>) => ({ q: typeof search.q === "string" ? search.q : "" }), 41 + component: SearchPage, 42 + }); 43 + 44 + interface SearchResultCardProps { 45 + agent: AtpAgent | null; 46 + post: PostDoc; 47 + author: AuthorInfo | null; 48 + reactions: ReactionDoc[]; 49 + index: number; 50 + onNewReaction: (post: PostDoc, emoji: string) => Promise<void>; 51 + isCreatingReaction: boolean; 52 + } 53 + 54 + function SearchResultCard({ post, ...rest }: SearchResultCardProps) { 55 + const navigate = useNavigate(); 56 + const [forumHandle, setForumHandle] = useState<string | undefined>(undefined) 57 + const { get, set } = usePersistentStore(); 58 + 59 + const rootUri = useMemo(() => post.root || post["$metadata.uri"], [post]); 60 + const postUri = post["$metadata.uri"]; 61 + 62 + const [threadLink, setThreadLink] = useState<{ to: string, hash: string } | null>(null); 63 + 64 + useEffect(() => { 65 + let isCancelled = false; 66 + const buildLink = async () => { 67 + try { 68 + const rootAtUri = new AtUri(rootUri); 69 + const authorIdentity = await cachedResolveIdentity({ didOrHandle: rootAtUri.hostname, get, set }); 70 + setForumHandle("@"+authorIdentity?.handle) 71 + if (!isCancelled && authorIdentity?.handle) { 72 + setThreadLink({ 73 + to: '/f/$forumHandle/t/$userHandle/$topicRKey', 74 + hash: postUri, 75 + }); 76 + } 77 + } catch(e) { 78 + console.error("Failed to build thread link for search result", e) 79 + } 80 + }; 81 + buildLink(); 82 + return () => { isCancelled = true; }; 83 + }, [rootUri, postUri, get, set]); 84 + 85 + const handleNavigateToPost = () => { 86 + if (!threadLink) return; 87 + const rootAtUri = new AtUri(rootUri); 88 + const authorIdentity = authors[rootAtUri.hostname]; 89 + if (!authorIdentity?.handle) return; 90 + 91 + navigate({ 92 + to: threadLink.to, 93 + params: { 94 + forumHandle: post.forum, 95 + userHandle: authorIdentity.handle, 96 + topicRKey: rootAtUri.rkey, 97 + }, 98 + hash: threadLink.hash 99 + }); 100 + }; 101 + 102 + return ( 103 + <div className="bg-gray-800/50 border border-gray-700/50 rounded-xl"> 104 + <div className="flex justify-between items-center px-4 py-2.5 border-b border-gray-700/50"> 105 + <span className="text-sm text-gray-400"> 106 + From forum:{" "} 107 + <Link 108 + to="/f/$forumHandle" 109 + params={{ forumHandle: post.forum }} 110 + className="font-semibold text-blue-300 hover:underline" 111 + > 112 + /f/{forumHandle || post.root} 113 + </Link> 114 + </span> 115 + {threadLink ? ( 116 + <Link 117 + to={threadLink.to} 118 + params={{ 119 + forumHandle: post.forum, 120 + userHandle: authors[new AtUri(rootUri).hostname]?.handle || '', // Needs pre-fetched author handle 121 + topicRKey: new AtUri(rootUri).rkey 122 + }} 123 + hash={threadLink.hash} 124 + className="flex items-center gap-2 text-sm font-semibold text-blue-300 hover:text-white transition-colors" 125 + > 126 + View Full Thread <ArrowRightIcon /> 127 + </Link> 128 + ) : ( 129 + <span className="flex items-center gap-2 text-sm font-semibold text-gray-500"> 130 + View Full Thread <ArrowRightIcon /> 131 + </span> 132 + )} 133 + </div> 134 + 135 + <PostCard 136 + {...rest} 137 + post={post} 138 + onSetReplyParent={handleNavigateToPost} 139 + /> 140 + </div> 141 + ); 142 + } 143 + 144 + let authors: Record<string, AuthorInfo> = {}; 145 + 146 + export function SearchPage() { 147 + const { q } = useSearch({ from: "/search" }); 148 + 149 + const { agent, loading: authLoading } = useAuth(); 150 + const { get, set } = usePersistentStore(); 151 + 152 + const [results, setResults] = useState<PostDoc[]>([]); 153 + const [reactions, setReactions] = useState<Record<string, ReactionDoc[]>>({}); 154 + const [_authors, setAuthors] = useState<Record<string, AuthorInfo>>({}); 155 + const [error, setError] = useState<string | null>(null); 156 + const [isLoading, setIsLoading] = useState(false); 157 + const [isCreatingReaction, setIsCreatingReaction] = useState(false); 158 + 159 + useEffect(() => { 160 + authors = _authors; 161 + }, [_authors]); 162 + 163 + const performSearch = useCallback(async (query: string) => { 164 + if (!query.trim()) { setResults([]); return; } 165 + setIsLoading(true); 166 + setError(null); 167 + setResults([]); 168 + setAuthors({}); 169 + setReactions({}); 170 + try { 171 + const searchRes = await esavQuery<{ hits: { hits: { _source: PostDoc }[] } }>({ 172 + query: { bool: { must: { multi_match: { query: query, fields: ["text", "title^2"], }, }, filter: [ { term: { "$metadata.collection": "com.example.ft.topic.post" } }, ], }, }, 173 + sort: [ { _score: "desc" }, { "$metadata.indexedAt": "desc" } ], 174 + size: 25, 175 + }); 176 + const foundPosts = searchRes.hits.hits.map((hit) => hit._source); 177 + if (foundPosts.length === 0) { setIsLoading(false); return; } 178 + setResults(foundPosts); 179 + 180 + const allUris = foundPosts.flatMap(p => [p["$metadata.uri"], p.reply?.root.uri]).filter(Boolean) as string[]; 181 + const uniqueUris = [...new Set(allUris)]; 182 + const allDids = [...new Set(uniqueUris.map(uri => new AtUri(uri).hostname))]; 183 + 184 + const [reactionsRes, footersRes, pdsProfiles] = await Promise.all([ 185 + esavQuery<{ hits: { hits: { _source: ReactionDoc }[] } }>({ query: { bool: { must: [{ term: { "$metadata.collection": "com.example.ft.topic.reaction" } }], filter: [{ terms: { reactionSubject: allUris.filter(u => u.includes('post')) } }], }, }, _source: ["reactionSubject", "reactionEmoji"], size: 1000, }), 186 + esavQuery<{ hits: { hits: { _source: { "$metadata.did": string; footer: string } }[] } }>({ query: { bool: { must: [{ term: { $type: "com.example.ft.user.profile" } }], filter: [{ terms: { "$metadata.did": allDids } }], }, }, _source: ["$metadata.did", "footer"], size: allDids.length, }), 187 + Promise.all(allDids.map(async (did) => { if (!agent) return { did, profile: null }; try { const res = await agent.com.atproto.repo.getRecord({ repo: did, collection: "app.bsky.actor.profile", rkey: "self", }); return { did, profile: JSON.parse(JSON.stringify(res.data.value)) }; } catch (e) { return { did, profile: null }; } })), 188 + ]); 189 + 190 + const reactionsByPostUri = reactionsRes.hits.hits.reduce((acc, hit) => { const r = hit._source; (acc[r.reactionSubject] = acc[r.reactionSubject] || []).push(r); return acc; }, {} as Record<string, ReactionDoc[]>); 191 + setReactions(reactionsByPostUri); 192 + 193 + const footersByDid = footersRes.hits.hits.reduce((acc, hit) => { acc[hit._source["$metadata.did"]] = hit._source.footer; return acc; }, {} as Record<string, string>); 194 + 195 + const newAuthors: Record<string, AuthorInfo> = {}; 196 + await Promise.all(allDids.map(async (did) => { 197 + const identity = await cachedResolveIdentity({ didOrHandle: did, get, set }); 198 + if (!identity) return; 199 + const pdsProfile = pdsProfiles.find((p) => p.did === did)?.profile; 200 + newAuthors[did] = { ...identity, displayName: pdsProfile?.displayName, avatarCid: pdsProfile?.avatar?.ref?.["$link"], footer: footersByDid[did], }; 201 + })); 202 + setAuthors(newAuthors); 203 + } catch (e) { console.error("Search failed:", e); setError("An error occurred during the search."); } finally { setIsLoading(false); } 204 + }, [agent, get, set]); 205 + 206 + useEffect(() => { 207 + if (!authLoading) performSearch(q); 208 + }, [q, authLoading, performSearch]); 209 + 210 + const handleCreateReaction = async (post: PostDoc, emoji: string) => { 211 + if (!agent?.did || isCreatingReaction) return; 212 + setIsCreatingReaction(true); 213 + const postUri = post["$metadata.uri"]; 214 + try { 215 + await agent.com.atproto.repo.createRecord({ repo: agent.did, collection: "com.example.ft.topic.reaction", record: { $type: "com.example.ft.topic.reaction", reactionEmoji: emoji, subject: postUri, createdAt: new Date().toISOString(), }, }); 216 + const newReaction: ReactionDoc = { $type: "com.example.ft.topic.reaction", reactionEmoji: emoji, reactionSubject: postUri }; 217 + setReactions((prev) => ({ ...prev, [postUri]: [...(prev[postUri] || []), newReaction] })); 218 + } catch (e) { console.error("Failed to create reaction", e); setError("Failed to post reaction."); } finally { setIsCreatingReaction(false); } 219 + }; 220 + 221 + const renderContent = () => { 222 + if (isLoading) return <div className="space-y-4">{Array.from({ length: 3 }).map((_, i) => <PostCardSkeleton key={i} />)}</div>; 223 + if (error) return <div className="text-center text-red-400 p-8">{error}</div>; 224 + if (!q.trim()) return <div className="text-center text-gray-400 p-8">Enter a search term to begin.</div>; 225 + if (results.length === 0) return <div className="text-center text-gray-400 p-8">No results found for "{q}".</div>; 226 + return ( 227 + <div className="space-y-4"> 228 + {results.map((post, index) => ( 229 + <SearchResultCard 230 + agent={agent} 231 + key={post["$metadata.uri"]} 232 + post={post} 233 + author={_authors[post["$metadata.did"]] || null} 234 + reactions={reactions[post["$metadata.uri"]] || []} 235 + index={index} 236 + onNewReaction={handleCreateReaction} 237 + isCreatingReaction={isCreatingReaction} 238 + /> 239 + ))} 240 + </div> 241 + ); 242 + }; 243 + 244 + return ( 245 + <div className="w-full flex flex-col items-center pt-6 px-4 pb-12"> 246 + <div className="w-full max-w-5xl space-y-4"> 247 + <h1 className="text-2xl font-bold text-gray-100 mb-2">Search Results</h1> 248 + {q && <p className="text-gray-400">Showing results for: <span className="font-semibold text-gray-200">"{q}"</span></p>} 249 + <div className="mt-6">{renderContent()}</div> 250 + </div> 251 + </div> 252 + ); 253 + }
+14
src/styles.css
··· 1 + 2 + body { 3 + margin: 0; 4 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 + sans-serif; 7 + -webkit-font-smoothing: antialiased; 8 + -moz-osx-font-smoothing: grayscale; 9 + } 10 + 11 + code { 12 + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 + monospace; 14 + }
+28
tsconfig.json
··· 1 + { 2 + "include": ["**/*.ts", "**/*.tsx"], 3 + "compilerOptions": { 4 + "target": "ES2022", 5 + "jsx": "react-jsx", 6 + "module": "ESNext", 7 + "lib": ["ES2022", "DOM", "DOM.Iterable"], 8 + "types": ["vite/client"], 9 + 10 + /* Bundler mode */ 11 + "moduleResolution": "bundler", 12 + "allowImportingTsExtensions": true, 13 + "verbatimModuleSyntax": true, 14 + "noEmit": true, 15 + 16 + /* Linting */ 17 + "skipLibCheck": true, 18 + "strict": true, 19 + "noUnusedLocals": true, 20 + "noUnusedParameters": true, 21 + "noFallthroughCasesInSwitch": true, 22 + "noUncheckedSideEffectImports": true, 23 + "baseUrl": ".", 24 + "paths": { 25 + "@/*": ["./src/*"], 26 + } 27 + } 28 + }
+19
vite.config.ts
··· 1 + import { defineConfig } from 'vite' 2 + import viteReact from '@vitejs/plugin-react' 3 + import { TanStackRouterVite } from '@tanstack/router-plugin/vite' 4 + import { resolve } from 'node:path' 5 + import tailwindcss from "@tailwindcss/vite"; 6 + 7 + // https://vitejs.dev/config/ 8 + export default defineConfig({ 9 + plugins: [tailwindcss(),TanStackRouterVite({ autoCodeSplitting: true }), viteReact()], 10 + test: { 11 + globals: true, 12 + environment: 'jsdom', 13 + }, 14 + resolve: { 15 + alias: { 16 + '@': resolve(__dirname, './src'), 17 + }, 18 + }, 19 + })