Fork of atp.tools as a universal profile for people on the ATmosphere

commit code :)

+1
env.d.ts
··· 1 + /// <reference types="@atcute/bluesky/lexicons" />
+20
index.html
··· 10 10 <div id="app"></div> 11 11 <script type="module" src="/src/main.tsx"></script> 12 12 </body> 13 + <script> 14 + (function () { 15 + function getInitialColorMode() { 16 + const persistedColorPreference = window.localStorage.getItem("theme"); 17 + const hasPersistedPreference = 18 + typeof persistedColorPreference === "string"; 19 + if (hasPersistedPreference) { 20 + return persistedColorPreference; 21 + } 22 + const mql = window.matchMedia("(prefers-color-scheme: dark)"); 23 + const hasMediaQueryPreference = typeof mql.matches === "boolean"; 24 + if (hasMediaQueryPreference) { 25 + return mql.matches ? "dark" : "light"; 26 + } 27 + return "light"; 28 + } 29 + const colorMode = getInitialColorMode(); 30 + document.documentElement.className = colorMode; 31 + })(); 32 + </script> 13 33 </html>
+12
package.json
··· 9 9 "preview": "vite preview" 10 10 }, 11 11 "dependencies": { 12 + "@atcute/bluesky": "^1.0.11", 13 + "@atcute/client": "^2.0.6", 14 + "@atcute/oauth-browser-client": "^1.0.7", 15 + "@radix-ui/react-dialog": "^1.1.4", 16 + "@radix-ui/react-separator": "^1.1.1", 17 + "@radix-ui/react-slot": "^1.1.1", 18 + "@radix-ui/react-tooltip": "^1.1.6", 19 + "@tanstack/react-router": "^1.91.3", 12 20 "class-variance-authority": "^0.7.1", 13 21 "clsx": "^2.1.1", 22 + "lexicons": "link:@atcute/bluesky/lexicons", 14 23 "lucide-react": "^0.469.0", 15 24 "preact": "^10.25.2", 25 + "simple-icons": "^13.21.0", 16 26 "tailwind-merge": "^2.5.5", 17 27 "tailwindcss-animate": "^1.0.7" 18 28 }, 19 29 "devDependencies": { 20 30 "@preact/preset-vite": "^2.9.3", 31 + "@tanstack/router-devtools": "^1.91.3", 32 + "@tanstack/router-plugin": "^1.91.1", 21 33 "@types/node": "^22.10.2", 22 34 "autoprefixer": "^10.4.20", 23 35 "postcss": "^8.4.49",
+1158 -8
pnpm-lock.yaml
··· 8 8 9 9 .: 10 10 dependencies: 11 + '@atcute/bluesky': 12 + specifier: ^1.0.11 13 + version: 1.0.11(@atcute/client@2.0.6) 14 + '@atcute/client': 15 + specifier: ^2.0.6 16 + version: 2.0.6 17 + '@atcute/oauth-browser-client': 18 + specifier: ^1.0.7 19 + version: 1.0.7 20 + '@radix-ui/react-dialog': 21 + specifier: ^1.1.4 22 + version: 1.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 23 + '@radix-ui/react-separator': 24 + specifier: ^1.1.1 25 + version: 1.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 26 + '@radix-ui/react-slot': 27 + specifier: ^1.1.1 28 + version: 1.1.1(react@19.0.0) 29 + '@radix-ui/react-tooltip': 30 + specifier: ^1.1.6 31 + version: 1.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 32 + '@tanstack/react-router': 33 + specifier: ^1.91.3 34 + version: 1.91.3(@tanstack/router-generator@1.87.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 11 35 class-variance-authority: 12 36 specifier: ^0.7.1 13 37 version: 0.7.1 14 38 clsx: 15 39 specifier: ^2.1.1 16 40 version: 2.1.1 41 + lexicons: 42 + specifier: link:@atcute/bluesky/lexicons 43 + version: link:@atcute/bluesky/lexicons 17 44 lucide-react: 18 45 specifier: ^0.469.0 19 46 version: 0.469.0(react@19.0.0) 20 47 preact: 21 48 specifier: ^10.25.2 22 49 version: 10.25.3 50 + simple-icons: 51 + specifier: ^13.21.0 52 + version: 13.21.0 23 53 tailwind-merge: 24 54 specifier: ^2.5.5 25 55 version: 2.5.5 ··· 29 59 devDependencies: 30 60 '@preact/preset-vite': 31 61 specifier: ^2.9.3 32 - version: 2.9.3(@babel/core@7.26.0)(preact@10.25.3)(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) 62 + version: 2.9.3(@babel/core@7.26.0)(preact@10.25.3)(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) 63 + '@tanstack/router-devtools': 64 + specifier: ^1.91.3 65 + version: 1.91.3(@tanstack/react-router@1.91.3(@tanstack/router-generator@1.87.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 66 + '@tanstack/router-plugin': 67 + specifier: ^1.91.1 68 + version: 1.91.1(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) 33 69 '@types/node': 34 70 specifier: ^22.10.2 35 71 version: 22.10.2 ··· 47 83 version: 5.6.3 48 84 vite: 49 85 specifier: ^6.0.3 50 - version: 6.0.5(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) 86 + version: 6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) 51 87 52 88 packages: 53 89 ··· 58 94 '@ampproject/remapping@2.3.0': 59 95 resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} 60 96 engines: {node: '>=6.0.0'} 97 + 98 + '@atcute/bluesky@1.0.11': 99 + resolution: {integrity: sha512-j4wLJIzKWh0gmQZ7s5svhEVRThlzqyfstbM0qM/H1Y+ZIQESx5HtVu+390zAWzZoEKZYSyTM9+ctY39OiEGDQw==} 100 + peerDependencies: 101 + '@atcute/client': ^1.0.0 || ^2.0.0 102 + 103 + '@atcute/client@2.0.6': 104 + resolution: {integrity: sha512-mhdqEicGUx0s5HTFOLpz91rcLS9j/g63de0nmAqv7blhU3j+xBf4le54qr2YIXNfnReZI7EwLYLX/YIBez4LGA==} 105 + 106 + '@atcute/oauth-browser-client@1.0.7': 107 + resolution: {integrity: sha512-ikf3FscGZXYU+S0K4n9eDUMg6pS//g/Zr159+bznxO3Wn2JYBohEIxzy29OIEExXD/qAYMq9kfqvo2d0gs4JWQ==} 61 108 62 109 '@babel/code-frame@7.26.2': 63 110 resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} ··· 124 171 peerDependencies: 125 172 '@babel/core': ^7.0.0-0 126 173 174 + '@babel/plugin-syntax-typescript@7.25.9': 175 + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} 176 + engines: {node: '>=6.9.0'} 177 + peerDependencies: 178 + '@babel/core': ^7.0.0-0 179 + 127 180 '@babel/plugin-transform-react-jsx-development@7.25.9': 128 181 resolution: {integrity: sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==} 129 182 engines: {node: '>=6.9.0'} ··· 148 201 resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==} 149 202 engines: {node: '>=6.9.0'} 150 203 204 + '@esbuild/aix-ppc64@0.23.1': 205 + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} 206 + engines: {node: '>=18'} 207 + cpu: [ppc64] 208 + os: [aix] 209 + 151 210 '@esbuild/aix-ppc64@0.24.0': 152 211 resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==} 153 212 engines: {node: '>=18'} 154 213 cpu: [ppc64] 155 214 os: [aix] 156 215 216 + '@esbuild/android-arm64@0.23.1': 217 + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} 218 + engines: {node: '>=18'} 219 + cpu: [arm64] 220 + os: [android] 221 + 157 222 '@esbuild/android-arm64@0.24.0': 158 223 resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==} 159 224 engines: {node: '>=18'} 160 225 cpu: [arm64] 161 226 os: [android] 162 227 228 + '@esbuild/android-arm@0.23.1': 229 + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} 230 + engines: {node: '>=18'} 231 + cpu: [arm] 232 + os: [android] 233 + 163 234 '@esbuild/android-arm@0.24.0': 164 235 resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==} 165 236 engines: {node: '>=18'} 166 237 cpu: [arm] 167 238 os: [android] 168 239 240 + '@esbuild/android-x64@0.23.1': 241 + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} 242 + engines: {node: '>=18'} 243 + cpu: [x64] 244 + os: [android] 245 + 169 246 '@esbuild/android-x64@0.24.0': 170 247 resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==} 171 248 engines: {node: '>=18'} 172 249 cpu: [x64] 173 250 os: [android] 174 251 252 + '@esbuild/darwin-arm64@0.23.1': 253 + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} 254 + engines: {node: '>=18'} 255 + cpu: [arm64] 256 + os: [darwin] 257 + 175 258 '@esbuild/darwin-arm64@0.24.0': 176 259 resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==} 177 260 engines: {node: '>=18'} 178 261 cpu: [arm64] 179 262 os: [darwin] 180 263 264 + '@esbuild/darwin-x64@0.23.1': 265 + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} 266 + engines: {node: '>=18'} 267 + cpu: [x64] 268 + os: [darwin] 269 + 181 270 '@esbuild/darwin-x64@0.24.0': 182 271 resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==} 183 272 engines: {node: '>=18'} 184 273 cpu: [x64] 185 274 os: [darwin] 186 275 276 + '@esbuild/freebsd-arm64@0.23.1': 277 + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} 278 + engines: {node: '>=18'} 279 + cpu: [arm64] 280 + os: [freebsd] 281 + 187 282 '@esbuild/freebsd-arm64@0.24.0': 188 283 resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==} 189 284 engines: {node: '>=18'} 190 285 cpu: [arm64] 191 286 os: [freebsd] 192 287 288 + '@esbuild/freebsd-x64@0.23.1': 289 + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} 290 + engines: {node: '>=18'} 291 + cpu: [x64] 292 + os: [freebsd] 293 + 193 294 '@esbuild/freebsd-x64@0.24.0': 194 295 resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==} 195 296 engines: {node: '>=18'} 196 297 cpu: [x64] 197 298 os: [freebsd] 198 299 300 + '@esbuild/linux-arm64@0.23.1': 301 + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} 302 + engines: {node: '>=18'} 303 + cpu: [arm64] 304 + os: [linux] 305 + 199 306 '@esbuild/linux-arm64@0.24.0': 200 307 resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==} 201 308 engines: {node: '>=18'} 202 309 cpu: [arm64] 203 310 os: [linux] 204 311 312 + '@esbuild/linux-arm@0.23.1': 313 + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} 314 + engines: {node: '>=18'} 315 + cpu: [arm] 316 + os: [linux] 317 + 205 318 '@esbuild/linux-arm@0.24.0': 206 319 resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==} 207 320 engines: {node: '>=18'} 208 321 cpu: [arm] 209 322 os: [linux] 210 323 324 + '@esbuild/linux-ia32@0.23.1': 325 + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} 326 + engines: {node: '>=18'} 327 + cpu: [ia32] 328 + os: [linux] 329 + 211 330 '@esbuild/linux-ia32@0.24.0': 212 331 resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==} 213 332 engines: {node: '>=18'} 214 333 cpu: [ia32] 215 334 os: [linux] 216 335 336 + '@esbuild/linux-loong64@0.23.1': 337 + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} 338 + engines: {node: '>=18'} 339 + cpu: [loong64] 340 + os: [linux] 341 + 217 342 '@esbuild/linux-loong64@0.24.0': 218 343 resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==} 219 344 engines: {node: '>=18'} 220 345 cpu: [loong64] 221 346 os: [linux] 222 347 348 + '@esbuild/linux-mips64el@0.23.1': 349 + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} 350 + engines: {node: '>=18'} 351 + cpu: [mips64el] 352 + os: [linux] 353 + 223 354 '@esbuild/linux-mips64el@0.24.0': 224 355 resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==} 225 356 engines: {node: '>=18'} 226 357 cpu: [mips64el] 227 358 os: [linux] 228 359 360 + '@esbuild/linux-ppc64@0.23.1': 361 + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} 362 + engines: {node: '>=18'} 363 + cpu: [ppc64] 364 + os: [linux] 365 + 229 366 '@esbuild/linux-ppc64@0.24.0': 230 367 resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==} 231 368 engines: {node: '>=18'} 232 369 cpu: [ppc64] 233 370 os: [linux] 234 371 372 + '@esbuild/linux-riscv64@0.23.1': 373 + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} 374 + engines: {node: '>=18'} 375 + cpu: [riscv64] 376 + os: [linux] 377 + 235 378 '@esbuild/linux-riscv64@0.24.0': 236 379 resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==} 237 380 engines: {node: '>=18'} 238 381 cpu: [riscv64] 239 382 os: [linux] 240 383 384 + '@esbuild/linux-s390x@0.23.1': 385 + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} 386 + engines: {node: '>=18'} 387 + cpu: [s390x] 388 + os: [linux] 389 + 241 390 '@esbuild/linux-s390x@0.24.0': 242 391 resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==} 243 392 engines: {node: '>=18'} 244 393 cpu: [s390x] 245 394 os: [linux] 246 395 396 + '@esbuild/linux-x64@0.23.1': 397 + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} 398 + engines: {node: '>=18'} 399 + cpu: [x64] 400 + os: [linux] 401 + 247 402 '@esbuild/linux-x64@0.24.0': 248 403 resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==} 249 404 engines: {node: '>=18'} 250 405 cpu: [x64] 251 406 os: [linux] 407 + 408 + '@esbuild/netbsd-x64@0.23.1': 409 + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} 410 + engines: {node: '>=18'} 411 + cpu: [x64] 412 + os: [netbsd] 252 413 253 414 '@esbuild/netbsd-x64@0.24.0': 254 415 resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==} ··· 256 417 cpu: [x64] 257 418 os: [netbsd] 258 419 420 + '@esbuild/openbsd-arm64@0.23.1': 421 + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} 422 + engines: {node: '>=18'} 423 + cpu: [arm64] 424 + os: [openbsd] 425 + 259 426 '@esbuild/openbsd-arm64@0.24.0': 260 427 resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==} 261 428 engines: {node: '>=18'} 262 429 cpu: [arm64] 263 430 os: [openbsd] 264 431 432 + '@esbuild/openbsd-x64@0.23.1': 433 + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} 434 + engines: {node: '>=18'} 435 + cpu: [x64] 436 + os: [openbsd] 437 + 265 438 '@esbuild/openbsd-x64@0.24.0': 266 439 resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==} 267 440 engines: {node: '>=18'} 268 441 cpu: [x64] 269 442 os: [openbsd] 443 + 444 + '@esbuild/sunos-x64@0.23.1': 445 + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} 446 + engines: {node: '>=18'} 447 + cpu: [x64] 448 + os: [sunos] 270 449 271 450 '@esbuild/sunos-x64@0.24.0': 272 451 resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==} ··· 274 453 cpu: [x64] 275 454 os: [sunos] 276 455 456 + '@esbuild/win32-arm64@0.23.1': 457 + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} 458 + engines: {node: '>=18'} 459 + cpu: [arm64] 460 + os: [win32] 461 + 277 462 '@esbuild/win32-arm64@0.24.0': 278 463 resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==} 279 464 engines: {node: '>=18'} 280 465 cpu: [arm64] 281 466 os: [win32] 282 467 468 + '@esbuild/win32-ia32@0.23.1': 469 + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} 470 + engines: {node: '>=18'} 471 + cpu: [ia32] 472 + os: [win32] 473 + 283 474 '@esbuild/win32-ia32@0.24.0': 284 475 resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==} 285 476 engines: {node: '>=18'} 286 477 cpu: [ia32] 287 478 os: [win32] 288 479 480 + '@esbuild/win32-x64@0.23.1': 481 + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} 482 + engines: {node: '>=18'} 483 + cpu: [x64] 484 + os: [win32] 485 + 289 486 '@esbuild/win32-x64@0.24.0': 290 487 resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==} 291 488 engines: {node: '>=18'} 292 489 cpu: [x64] 293 490 os: [win32] 491 + 492 + '@floating-ui/core@1.6.8': 493 + resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} 494 + 495 + '@floating-ui/dom@1.6.12': 496 + resolution: {integrity: sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==} 497 + 498 + '@floating-ui/react-dom@2.1.2': 499 + resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} 500 + peerDependencies: 501 + react: '>=16.8.0' 502 + react-dom: '>=16.8.0' 503 + 504 + '@floating-ui/utils@0.2.8': 505 + resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} 294 506 295 507 '@isaacs/cliui@8.0.2': 296 508 resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} ··· 353 565 preact: ^10.4.0 354 566 vite: '>=2.0.0' 355 567 568 + '@radix-ui/primitive@1.1.1': 569 + resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} 570 + 571 + '@radix-ui/react-arrow@1.1.1': 572 + resolution: {integrity: sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==} 573 + peerDependencies: 574 + '@types/react': '*' 575 + '@types/react-dom': '*' 576 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 577 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 578 + peerDependenciesMeta: 579 + '@types/react': 580 + optional: true 581 + '@types/react-dom': 582 + optional: true 583 + 584 + '@radix-ui/react-compose-refs@1.1.1': 585 + resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} 586 + peerDependencies: 587 + '@types/react': '*' 588 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 589 + peerDependenciesMeta: 590 + '@types/react': 591 + optional: true 592 + 593 + '@radix-ui/react-context@1.1.1': 594 + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} 595 + peerDependencies: 596 + '@types/react': '*' 597 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 598 + peerDependenciesMeta: 599 + '@types/react': 600 + optional: true 601 + 602 + '@radix-ui/react-dialog@1.1.4': 603 + resolution: {integrity: sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==} 604 + peerDependencies: 605 + '@types/react': '*' 606 + '@types/react-dom': '*' 607 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 608 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 609 + peerDependenciesMeta: 610 + '@types/react': 611 + optional: true 612 + '@types/react-dom': 613 + optional: true 614 + 615 + '@radix-ui/react-dismissable-layer@1.1.3': 616 + resolution: {integrity: sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==} 617 + peerDependencies: 618 + '@types/react': '*' 619 + '@types/react-dom': '*' 620 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 621 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 622 + peerDependenciesMeta: 623 + '@types/react': 624 + optional: true 625 + '@types/react-dom': 626 + optional: true 627 + 628 + '@radix-ui/react-focus-guards@1.1.1': 629 + resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} 630 + peerDependencies: 631 + '@types/react': '*' 632 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 633 + peerDependenciesMeta: 634 + '@types/react': 635 + optional: true 636 + 637 + '@radix-ui/react-focus-scope@1.1.1': 638 + resolution: {integrity: sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==} 639 + peerDependencies: 640 + '@types/react': '*' 641 + '@types/react-dom': '*' 642 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 643 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 644 + peerDependenciesMeta: 645 + '@types/react': 646 + optional: true 647 + '@types/react-dom': 648 + optional: true 649 + 650 + '@radix-ui/react-id@1.1.0': 651 + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} 652 + peerDependencies: 653 + '@types/react': '*' 654 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 655 + peerDependenciesMeta: 656 + '@types/react': 657 + optional: true 658 + 659 + '@radix-ui/react-popper@1.2.1': 660 + resolution: {integrity: sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==} 661 + peerDependencies: 662 + '@types/react': '*' 663 + '@types/react-dom': '*' 664 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 665 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 666 + peerDependenciesMeta: 667 + '@types/react': 668 + optional: true 669 + '@types/react-dom': 670 + optional: true 671 + 672 + '@radix-ui/react-portal@1.1.3': 673 + resolution: {integrity: sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==} 674 + peerDependencies: 675 + '@types/react': '*' 676 + '@types/react-dom': '*' 677 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 678 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 679 + peerDependenciesMeta: 680 + '@types/react': 681 + optional: true 682 + '@types/react-dom': 683 + optional: true 684 + 685 + '@radix-ui/react-presence@1.1.2': 686 + resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} 687 + peerDependencies: 688 + '@types/react': '*' 689 + '@types/react-dom': '*' 690 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 691 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 692 + peerDependenciesMeta: 693 + '@types/react': 694 + optional: true 695 + '@types/react-dom': 696 + optional: true 697 + 698 + '@radix-ui/react-primitive@2.0.1': 699 + resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} 700 + peerDependencies: 701 + '@types/react': '*' 702 + '@types/react-dom': '*' 703 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 704 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 705 + peerDependenciesMeta: 706 + '@types/react': 707 + optional: true 708 + '@types/react-dom': 709 + optional: true 710 + 711 + '@radix-ui/react-separator@1.1.1': 712 + resolution: {integrity: sha512-RRiNRSrD8iUiXriq/Y5n4/3iE8HzqgLHsusUSg5jVpU2+3tqcUFPJXHDymwEypunc2sWxDUS3UC+rkZRlHedsw==} 713 + peerDependencies: 714 + '@types/react': '*' 715 + '@types/react-dom': '*' 716 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 717 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 718 + peerDependenciesMeta: 719 + '@types/react': 720 + optional: true 721 + '@types/react-dom': 722 + optional: true 723 + 724 + '@radix-ui/react-slot@1.1.1': 725 + resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} 726 + peerDependencies: 727 + '@types/react': '*' 728 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 729 + peerDependenciesMeta: 730 + '@types/react': 731 + optional: true 732 + 733 + '@radix-ui/react-tooltip@1.1.6': 734 + resolution: {integrity: sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA==} 735 + peerDependencies: 736 + '@types/react': '*' 737 + '@types/react-dom': '*' 738 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 739 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 740 + peerDependenciesMeta: 741 + '@types/react': 742 + optional: true 743 + '@types/react-dom': 744 + optional: true 745 + 746 + '@radix-ui/react-use-callback-ref@1.1.0': 747 + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} 748 + peerDependencies: 749 + '@types/react': '*' 750 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 751 + peerDependenciesMeta: 752 + '@types/react': 753 + optional: true 754 + 755 + '@radix-ui/react-use-controllable-state@1.1.0': 756 + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} 757 + peerDependencies: 758 + '@types/react': '*' 759 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 760 + peerDependenciesMeta: 761 + '@types/react': 762 + optional: true 763 + 764 + '@radix-ui/react-use-escape-keydown@1.1.0': 765 + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} 766 + peerDependencies: 767 + '@types/react': '*' 768 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 769 + peerDependenciesMeta: 770 + '@types/react': 771 + optional: true 772 + 773 + '@radix-ui/react-use-layout-effect@1.1.0': 774 + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} 775 + peerDependencies: 776 + '@types/react': '*' 777 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 778 + peerDependenciesMeta: 779 + '@types/react': 780 + optional: true 781 + 782 + '@radix-ui/react-use-rect@1.1.0': 783 + resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} 784 + peerDependencies: 785 + '@types/react': '*' 786 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 787 + peerDependenciesMeta: 788 + '@types/react': 789 + optional: true 790 + 791 + '@radix-ui/react-use-size@1.1.0': 792 + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} 793 + peerDependencies: 794 + '@types/react': '*' 795 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 796 + peerDependenciesMeta: 797 + '@types/react': 798 + optional: true 799 + 800 + '@radix-ui/react-visually-hidden@1.1.1': 801 + resolution: {integrity: sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==} 802 + peerDependencies: 803 + '@types/react': '*' 804 + '@types/react-dom': '*' 805 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 806 + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 807 + peerDependenciesMeta: 808 + '@types/react': 809 + optional: true 810 + '@types/react-dom': 811 + optional: true 812 + 813 + '@radix-ui/rect@1.1.0': 814 + resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} 815 + 356 816 '@rollup/pluginutils@4.2.1': 357 817 resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} 358 818 engines: {node: '>= 8.0.0'} ··· 452 912 cpu: [x64] 453 913 os: [win32] 454 914 915 + '@tanstack/history@1.90.0': 916 + resolution: {integrity: sha512-riNhDGm+fAwxgZRJ0J/36IZis1UDHsDCNIxfEodbw6BgTWJr0ah+G20V4HT91uBXiCqYFvX3somlfTLhS5yHDA==} 917 + engines: {node: '>=12'} 918 + 919 + '@tanstack/react-router@1.91.3': 920 + resolution: {integrity: sha512-T6k50ApwcWKYjJB4VSK2WhXu/p40luynNJg5QC3oIqk24p0tLlgXIblXoTJzy7lVvDmQ4lwHCP9dBTvLy5NhVA==} 921 + engines: {node: '>=12'} 922 + peerDependencies: 923 + '@tanstack/router-generator': ^1.87.7 924 + react: '>=18' 925 + react-dom: '>=18' 926 + peerDependenciesMeta: 927 + '@tanstack/router-generator': 928 + optional: true 929 + 930 + '@tanstack/react-store@0.6.1': 931 + resolution: {integrity: sha512-6gOopOpPp1cAXkEyTEv6tMbAywwFunvIdCKN/SpEiButUayjXU+Q5Sp5Y3hREN3VMR4OA5+RI5SPhhJoqP9e4w==} 932 + peerDependencies: 933 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 934 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 935 + 936 + '@tanstack/router-devtools@1.91.3': 937 + resolution: {integrity: sha512-b/WOhWEC7+Znh0+OrSGxl7RMCwHT5vX6fygDN1wh1yiUOjs32EJquu4c9deWzRDlF3jV6ji7XYWs1XgKXcsrww==} 938 + engines: {node: '>=12'} 939 + peerDependencies: 940 + '@tanstack/react-router': ^1.91.3 941 + react: '>=18' 942 + react-dom: '>=18' 943 + 944 + '@tanstack/router-generator@1.87.7': 945 + resolution: {integrity: sha512-w9Px1C6DM0YNVXvu1VjUuZ5el0ykOeofEmEZBW83VUTzvCXFpcjPCHncU9FO9uXup8NFIxNfGz+xpwf93GoFnQ==} 946 + engines: {node: '>=12'} 947 + 948 + '@tanstack/router-plugin@1.91.1': 949 + resolution: {integrity: sha512-+htKBNRKwdZjpgT0ee32oBb7gpH3o0cJUKvx74oTfZ9N5oth255pns1ka4Sa6lhC/gyvC3NLgk/lMqD7eVJejA==} 950 + engines: {node: '>=12'} 951 + peerDependencies: 952 + '@rsbuild/core': '>=1.0.2' 953 + vite: '>=5.0.0 || >=6.0.0' 954 + webpack: '>=5.92.0' 955 + peerDependenciesMeta: 956 + '@rsbuild/core': 957 + optional: true 958 + vite: 959 + optional: true 960 + webpack: 961 + optional: true 962 + 963 + '@tanstack/store@0.6.0': 964 + resolution: {integrity: sha512-+m2OBglsjXcLmmKOX6/9v8BDOCtyxhMmZLsRUDswOOSdIIR9mvv6i0XNKsmTh3AlYU8c1mRcodC8/Vyf+69VlQ==} 965 + 966 + '@tanstack/virtual-file-routes@1.87.6': 967 + resolution: {integrity: sha512-PTpeM8SHL7AJM0pJOacFvHribbUODS51qe9NsMqku4mogh6BWObY1EeVmeGnp9o3VngAEsf+rJMs2zqIVz3WFA==} 968 + engines: {node: '>=12'} 969 + 970 + '@types/babel__core@7.20.5': 971 + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} 972 + 973 + '@types/babel__generator@7.6.8': 974 + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} 975 + 976 + '@types/babel__template@7.4.4': 977 + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} 978 + 979 + '@types/babel__traverse@7.20.6': 980 + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} 981 + 455 982 '@types/estree@1.0.6': 456 983 resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 457 984 458 985 '@types/node@22.10.2': 459 986 resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} 987 + 988 + acorn@8.14.0: 989 + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} 990 + engines: {node: '>=0.4.0'} 991 + hasBin: true 460 992 461 993 ansi-regex@5.0.1: 462 994 resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} ··· 484 1016 arg@5.0.2: 485 1017 resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} 486 1018 1019 + aria-hidden@1.2.4: 1020 + resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} 1021 + engines: {node: '>=10'} 1022 + 487 1023 autoprefixer@10.4.20: 488 1024 resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} 489 1025 engines: {node: ^10 || ^12 || >=14} 490 1026 hasBin: true 491 1027 peerDependencies: 492 1028 postcss: ^8.1.0 1029 + 1030 + babel-dead-code-elimination@1.0.8: 1031 + resolution: {integrity: sha512-og6HQERk0Cmm+nTT4Od2wbPtgABXFMPaHACjbKLulZIFMkYyXZLkUGuAxdgpMJBrxyt/XFpSz++lNzjbcMnPkQ==} 493 1032 494 1033 babel-plugin-transform-hook-names@1.0.2: 495 1034 resolution: {integrity: sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==} ··· 566 1105 engines: {node: '>=4'} 567 1106 hasBin: true 568 1107 1108 + csstype@3.1.3: 1109 + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 1110 + 569 1111 debug@4.4.0: 570 1112 resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 571 1113 engines: {node: '>=6.0'} ··· 575 1117 supports-color: 576 1118 optional: true 577 1119 1120 + detect-node-es@1.1.0: 1121 + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} 1122 + 578 1123 didyoumean@1.2.2: 579 1124 resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} 580 1125 ··· 609 1154 entities@4.5.0: 610 1155 resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 611 1156 engines: {node: '>=0.12'} 1157 + 1158 + esbuild@0.23.1: 1159 + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} 1160 + engines: {node: '>=18'} 1161 + hasBin: true 612 1162 613 1163 esbuild@0.24.0: 614 1164 resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==} ··· 652 1202 resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} 653 1203 engines: {node: '>=6.9.0'} 654 1204 1205 + get-nonce@1.0.1: 1206 + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} 1207 + engines: {node: '>=6'} 1208 + 1209 + get-tsconfig@4.8.1: 1210 + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} 1211 + 655 1212 glob-parent@5.1.2: 656 1213 resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 657 1214 engines: {node: '>= 6'} ··· 668 1225 resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} 669 1226 engines: {node: '>=4'} 670 1227 1228 + goober@2.1.16: 1229 + resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==} 1230 + peerDependencies: 1231 + csstype: ^3.0.10 1232 + 671 1233 hasown@2.0.2: 672 1234 resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 673 1235 engines: {node: '>= 0.4'} ··· 775 1337 engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 776 1338 hasBin: true 777 1339 1340 + nanoid@5.0.9: 1341 + resolution: {integrity: sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==} 1342 + engines: {node: ^18 || >=20} 1343 + hasBin: true 1344 + 778 1345 node-html-parser@6.1.13: 779 1346 resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==} 780 1347 ··· 873 1440 preact@10.25.3: 874 1441 resolution: {integrity: sha512-dzQmIFtM970z+fP9ziQ3yG4e3ULIbwZzJ734vaMVUTaKQ2+Ru1Ou/gjshOYVHCcd1rpAelC6ngjvjDXph98unQ==} 875 1442 1443 + prettier@3.4.2: 1444 + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} 1445 + engines: {node: '>=14'} 1446 + hasBin: true 1447 + 876 1448 queue-microtask@1.2.3: 877 1449 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 878 1450 1451 + react-dom@19.0.0: 1452 + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} 1453 + peerDependencies: 1454 + react: ^19.0.0 1455 + 1456 + react-remove-scroll-bar@2.3.8: 1457 + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} 1458 + engines: {node: '>=10'} 1459 + peerDependencies: 1460 + '@types/react': '*' 1461 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 1462 + peerDependenciesMeta: 1463 + '@types/react': 1464 + optional: true 1465 + 1466 + react-remove-scroll@2.6.2: 1467 + resolution: {integrity: sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==} 1468 + engines: {node: '>=10'} 1469 + peerDependencies: 1470 + '@types/react': '*' 1471 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 1472 + peerDependenciesMeta: 1473 + '@types/react': 1474 + optional: true 1475 + 1476 + react-style-singleton@2.2.3: 1477 + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} 1478 + engines: {node: '>=10'} 1479 + peerDependencies: 1480 + '@types/react': '*' 1481 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 1482 + peerDependenciesMeta: 1483 + '@types/react': 1484 + optional: true 1485 + 879 1486 react@19.0.0: 880 1487 resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} 881 1488 engines: {node: '>=0.10.0'} ··· 887 1494 resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 888 1495 engines: {node: '>=8.10.0'} 889 1496 1497 + resolve-pkg-maps@1.0.0: 1498 + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} 1499 + 890 1500 resolve@1.22.10: 891 1501 resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} 892 1502 engines: {node: '>= 0.4'} ··· 904 1514 run-parallel@1.2.0: 905 1515 resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 906 1516 1517 + scheduler@0.25.0: 1518 + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} 1519 + 907 1520 semver@6.3.1: 908 1521 resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 909 1522 hasBin: true ··· 920 1533 resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 921 1534 engines: {node: '>=14'} 922 1535 1536 + simple-icons@13.21.0: 1537 + resolution: {integrity: sha512-LI5pVJPBv6oc79OMsffwb6kEqnmB8P1Cjg1crNUlhsxPETQ5UzbCKQdxU+7MW6+DD1qfPkla/vSKlLD4IfyXpQ==} 1538 + engines: {node: '>=0.12.18'} 1539 + 923 1540 source-map-js@1.2.1: 924 1541 resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 925 1542 engines: {node: '>=0.10.0'} ··· 977 1594 thenify@3.3.1: 978 1595 resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 979 1596 1597 + tiny-invariant@1.3.3: 1598 + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} 1599 + 1600 + tiny-warning@1.0.3: 1601 + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} 1602 + 980 1603 to-regex-range@5.0.1: 981 1604 resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 982 1605 engines: {node: '>=8.0'} ··· 984 1607 ts-interface-checker@0.1.13: 985 1608 resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 986 1609 1610 + tslib@2.8.1: 1611 + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1612 + 1613 + tsx@4.19.2: 1614 + resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} 1615 + engines: {node: '>=18.0.0'} 1616 + hasBin: true 1617 + 987 1618 typescript@5.6.3: 988 1619 resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} 989 1620 engines: {node: '>=14.17'} ··· 991 1622 992 1623 undici-types@6.20.0: 993 1624 resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 1625 + 1626 + unplugin@1.16.0: 1627 + resolution: {integrity: sha512-5liCNPuJW8dqh3+DM6uNM2EI3MLLpCKp/KY+9pB5M2S2SR2qvvDHhKgBOaTWEbZTAws3CXfB0rKTIolWKL05VQ==} 1628 + engines: {node: '>=14.0.0'} 994 1629 995 1630 update-browserslist-db@1.1.1: 996 1631 resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} ··· 998 1633 peerDependencies: 999 1634 browserslist: '>= 4.21.0' 1000 1635 1636 + use-callback-ref@1.3.3: 1637 + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} 1638 + engines: {node: '>=10'} 1639 + peerDependencies: 1640 + '@types/react': '*' 1641 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 1642 + peerDependenciesMeta: 1643 + '@types/react': 1644 + optional: true 1645 + 1646 + use-sidecar@1.1.3: 1647 + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} 1648 + engines: {node: '>=10'} 1649 + peerDependencies: 1650 + '@types/react': '*' 1651 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc 1652 + peerDependenciesMeta: 1653 + '@types/react': 1654 + optional: true 1655 + 1656 + use-sync-external-store@1.4.0: 1657 + resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} 1658 + peerDependencies: 1659 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 1660 + 1001 1661 util-deprecate@1.0.2: 1002 1662 resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 1003 1663 ··· 1041 1701 yaml: 1042 1702 optional: true 1043 1703 1704 + webpack-virtual-modules@0.6.2: 1705 + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} 1706 + 1044 1707 which@2.0.2: 1045 1708 resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1046 1709 engines: {node: '>= 8'} ··· 1062 1725 engines: {node: '>= 14'} 1063 1726 hasBin: true 1064 1727 1728 + zod@3.24.1: 1729 + resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} 1730 + 1065 1731 snapshots: 1066 1732 1067 1733 '@alloc/quick-lru@5.2.0': {} ··· 1071 1737 '@jridgewell/gen-mapping': 0.3.8 1072 1738 '@jridgewell/trace-mapping': 0.3.25 1073 1739 1740 + '@atcute/bluesky@1.0.11(@atcute/client@2.0.6)': 1741 + dependencies: 1742 + '@atcute/client': 2.0.6 1743 + 1744 + '@atcute/client@2.0.6': {} 1745 + 1746 + '@atcute/oauth-browser-client@1.0.7': 1747 + dependencies: 1748 + '@atcute/client': 2.0.6 1749 + nanoid: 5.0.9 1750 + 1074 1751 '@babel/code-frame@7.26.2': 1075 1752 dependencies: 1076 1753 '@babel/helper-validator-identifier': 7.25.9 ··· 1157 1834 '@babel/core': 7.26.0 1158 1835 '@babel/helper-plugin-utils': 7.25.9 1159 1836 1837 + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': 1838 + dependencies: 1839 + '@babel/core': 7.26.0 1840 + '@babel/helper-plugin-utils': 7.25.9 1841 + 1160 1842 '@babel/plugin-transform-react-jsx-development@7.25.9(@babel/core@7.26.0)': 1161 1843 dependencies: 1162 1844 '@babel/core': 7.26.0 ··· 1198 1880 '@babel/helper-string-parser': 7.25.9 1199 1881 '@babel/helper-validator-identifier': 7.25.9 1200 1882 1883 + '@esbuild/aix-ppc64@0.23.1': 1884 + optional: true 1885 + 1201 1886 '@esbuild/aix-ppc64@0.24.0': 1202 1887 optional: true 1203 1888 1889 + '@esbuild/android-arm64@0.23.1': 1890 + optional: true 1891 + 1204 1892 '@esbuild/android-arm64@0.24.0': 1205 1893 optional: true 1206 1894 1895 + '@esbuild/android-arm@0.23.1': 1896 + optional: true 1897 + 1207 1898 '@esbuild/android-arm@0.24.0': 1899 + optional: true 1900 + 1901 + '@esbuild/android-x64@0.23.1': 1208 1902 optional: true 1209 1903 1210 1904 '@esbuild/android-x64@0.24.0': 1211 1905 optional: true 1212 1906 1907 + '@esbuild/darwin-arm64@0.23.1': 1908 + optional: true 1909 + 1213 1910 '@esbuild/darwin-arm64@0.24.0': 1214 1911 optional: true 1215 1912 1913 + '@esbuild/darwin-x64@0.23.1': 1914 + optional: true 1915 + 1216 1916 '@esbuild/darwin-x64@0.24.0': 1917 + optional: true 1918 + 1919 + '@esbuild/freebsd-arm64@0.23.1': 1217 1920 optional: true 1218 1921 1219 1922 '@esbuild/freebsd-arm64@0.24.0': 1220 1923 optional: true 1221 1924 1925 + '@esbuild/freebsd-x64@0.23.1': 1926 + optional: true 1927 + 1222 1928 '@esbuild/freebsd-x64@0.24.0': 1223 1929 optional: true 1224 1930 1931 + '@esbuild/linux-arm64@0.23.1': 1932 + optional: true 1933 + 1225 1934 '@esbuild/linux-arm64@0.24.0': 1935 + optional: true 1936 + 1937 + '@esbuild/linux-arm@0.23.1': 1226 1938 optional: true 1227 1939 1228 1940 '@esbuild/linux-arm@0.24.0': 1229 1941 optional: true 1230 1942 1943 + '@esbuild/linux-ia32@0.23.1': 1944 + optional: true 1945 + 1231 1946 '@esbuild/linux-ia32@0.24.0': 1232 1947 optional: true 1233 1948 1949 + '@esbuild/linux-loong64@0.23.1': 1950 + optional: true 1951 + 1234 1952 '@esbuild/linux-loong64@0.24.0': 1953 + optional: true 1954 + 1955 + '@esbuild/linux-mips64el@0.23.1': 1235 1956 optional: true 1236 1957 1237 1958 '@esbuild/linux-mips64el@0.24.0': 1238 1959 optional: true 1239 1960 1961 + '@esbuild/linux-ppc64@0.23.1': 1962 + optional: true 1963 + 1240 1964 '@esbuild/linux-ppc64@0.24.0': 1241 1965 optional: true 1242 1966 1967 + '@esbuild/linux-riscv64@0.23.1': 1968 + optional: true 1969 + 1243 1970 '@esbuild/linux-riscv64@0.24.0': 1971 + optional: true 1972 + 1973 + '@esbuild/linux-s390x@0.23.1': 1244 1974 optional: true 1245 1975 1246 1976 '@esbuild/linux-s390x@0.24.0': 1247 1977 optional: true 1248 1978 1979 + '@esbuild/linux-x64@0.23.1': 1980 + optional: true 1981 + 1249 1982 '@esbuild/linux-x64@0.24.0': 1250 1983 optional: true 1251 1984 1985 + '@esbuild/netbsd-x64@0.23.1': 1986 + optional: true 1987 + 1252 1988 '@esbuild/netbsd-x64@0.24.0': 1253 1989 optional: true 1254 1990 1991 + '@esbuild/openbsd-arm64@0.23.1': 1992 + optional: true 1993 + 1255 1994 '@esbuild/openbsd-arm64@0.24.0': 1256 1995 optional: true 1257 1996 1997 + '@esbuild/openbsd-x64@0.23.1': 1998 + optional: true 1999 + 1258 2000 '@esbuild/openbsd-x64@0.24.0': 1259 2001 optional: true 1260 2002 2003 + '@esbuild/sunos-x64@0.23.1': 2004 + optional: true 2005 + 1261 2006 '@esbuild/sunos-x64@0.24.0': 1262 2007 optional: true 1263 2008 2009 + '@esbuild/win32-arm64@0.23.1': 2010 + optional: true 2011 + 1264 2012 '@esbuild/win32-arm64@0.24.0': 1265 2013 optional: true 1266 2014 2015 + '@esbuild/win32-ia32@0.23.1': 2016 + optional: true 2017 + 1267 2018 '@esbuild/win32-ia32@0.24.0': 1268 2019 optional: true 1269 2020 2021 + '@esbuild/win32-x64@0.23.1': 2022 + optional: true 2023 + 1270 2024 '@esbuild/win32-x64@0.24.0': 1271 2025 optional: true 1272 2026 2027 + '@floating-ui/core@1.6.8': 2028 + dependencies: 2029 + '@floating-ui/utils': 0.2.8 2030 + 2031 + '@floating-ui/dom@1.6.12': 2032 + dependencies: 2033 + '@floating-ui/core': 1.6.8 2034 + '@floating-ui/utils': 0.2.8 2035 + 2036 + '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2037 + dependencies: 2038 + '@floating-ui/dom': 1.6.12 2039 + react: 19.0.0 2040 + react-dom: 19.0.0(react@19.0.0) 2041 + 2042 + '@floating-ui/utils@0.2.8': {} 2043 + 1273 2044 '@isaacs/cliui@8.0.2': 1274 2045 dependencies: 1275 2046 string-width: 5.1.2 ··· 1311 2082 '@pkgjs/parseargs@0.11.0': 1312 2083 optional: true 1313 2084 1314 - '@preact/preset-vite@2.9.3(@babel/core@7.26.0)(preact@10.25.3)(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': 2085 + '@preact/preset-vite@2.9.3(@babel/core@7.26.0)(preact@10.25.3)(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': 1315 2086 dependencies: 1316 2087 '@babel/code-frame': 7.26.2 1317 2088 '@babel/core': 7.26.0 1318 2089 '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) 1319 2090 '@babel/plugin-transform-react-jsx-development': 7.25.9(@babel/core@7.26.0) 1320 - '@prefresh/vite': 2.4.6(preact@10.25.3)(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1)) 2091 + '@prefresh/vite': 2.4.6(preact@10.25.3)(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1)) 1321 2092 '@rollup/pluginutils': 4.2.1 1322 2093 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.26.0) 1323 2094 debug: 4.4.0 ··· 1326 2097 node-html-parser: 6.1.13 1327 2098 source-map: 0.7.4 1328 2099 stack-trace: 1.0.0-pre2 1329 - vite: 6.0.5(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) 2100 + vite: 6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) 1330 2101 transitivePeerDependencies: 1331 2102 - preact 1332 2103 - supports-color ··· 1339 2110 1340 2111 '@prefresh/utils@1.2.0': {} 1341 2112 1342 - '@prefresh/vite@2.4.6(preact@10.25.3)(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': 2113 + '@prefresh/vite@2.4.6(preact@10.25.3)(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': 1343 2114 dependencies: 1344 2115 '@babel/core': 7.26.0 1345 2116 '@prefresh/babel-plugin': 0.5.1 ··· 1347 2118 '@prefresh/utils': 1.2.0 1348 2119 '@rollup/pluginutils': 4.2.1 1349 2120 preact: 10.25.3 1350 - vite: 6.0.5(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1) 2121 + vite: 6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) 1351 2122 transitivePeerDependencies: 1352 2123 - supports-color 1353 2124 2125 + '@radix-ui/primitive@1.1.1': {} 2126 + 2127 + '@radix-ui/react-arrow@1.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2128 + dependencies: 2129 + '@radix-ui/react-primitive': 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2130 + react: 19.0.0 2131 + react-dom: 19.0.0(react@19.0.0) 2132 + 2133 + '@radix-ui/react-compose-refs@1.1.1(react@19.0.0)': 2134 + dependencies: 2135 + react: 19.0.0 2136 + 2137 + '@radix-ui/react-context@1.1.1(react@19.0.0)': 2138 + dependencies: 2139 + react: 19.0.0 2140 + 2141 + '@radix-ui/react-dialog@1.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2142 + dependencies: 2143 + '@radix-ui/primitive': 1.1.1 2144 + '@radix-ui/react-compose-refs': 1.1.1(react@19.0.0) 2145 + '@radix-ui/react-context': 1.1.1(react@19.0.0) 2146 + '@radix-ui/react-dismissable-layer': 1.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2147 + '@radix-ui/react-focus-guards': 1.1.1(react@19.0.0) 2148 + '@radix-ui/react-focus-scope': 1.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2149 + '@radix-ui/react-id': 1.1.0(react@19.0.0) 2150 + '@radix-ui/react-portal': 1.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2151 + '@radix-ui/react-presence': 1.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2152 + '@radix-ui/react-primitive': 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2153 + '@radix-ui/react-slot': 1.1.1(react@19.0.0) 2154 + '@radix-ui/react-use-controllable-state': 1.1.0(react@19.0.0) 2155 + aria-hidden: 1.2.4 2156 + react: 19.0.0 2157 + react-dom: 19.0.0(react@19.0.0) 2158 + react-remove-scroll: 2.6.2(react@19.0.0) 2159 + 2160 + '@radix-ui/react-dismissable-layer@1.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2161 + dependencies: 2162 + '@radix-ui/primitive': 1.1.1 2163 + '@radix-ui/react-compose-refs': 1.1.1(react@19.0.0) 2164 + '@radix-ui/react-primitive': 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2165 + '@radix-ui/react-use-callback-ref': 1.1.0(react@19.0.0) 2166 + '@radix-ui/react-use-escape-keydown': 1.1.0(react@19.0.0) 2167 + react: 19.0.0 2168 + react-dom: 19.0.0(react@19.0.0) 2169 + 2170 + '@radix-ui/react-focus-guards@1.1.1(react@19.0.0)': 2171 + dependencies: 2172 + react: 19.0.0 2173 + 2174 + '@radix-ui/react-focus-scope@1.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2175 + dependencies: 2176 + '@radix-ui/react-compose-refs': 1.1.1(react@19.0.0) 2177 + '@radix-ui/react-primitive': 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2178 + '@radix-ui/react-use-callback-ref': 1.1.0(react@19.0.0) 2179 + react: 19.0.0 2180 + react-dom: 19.0.0(react@19.0.0) 2181 + 2182 + '@radix-ui/react-id@1.1.0(react@19.0.0)': 2183 + dependencies: 2184 + '@radix-ui/react-use-layout-effect': 1.1.0(react@19.0.0) 2185 + react: 19.0.0 2186 + 2187 + '@radix-ui/react-popper@1.2.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2188 + dependencies: 2189 + '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2190 + '@radix-ui/react-arrow': 1.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2191 + '@radix-ui/react-compose-refs': 1.1.1(react@19.0.0) 2192 + '@radix-ui/react-context': 1.1.1(react@19.0.0) 2193 + '@radix-ui/react-primitive': 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2194 + '@radix-ui/react-use-callback-ref': 1.1.0(react@19.0.0) 2195 + '@radix-ui/react-use-layout-effect': 1.1.0(react@19.0.0) 2196 + '@radix-ui/react-use-rect': 1.1.0(react@19.0.0) 2197 + '@radix-ui/react-use-size': 1.1.0(react@19.0.0) 2198 + '@radix-ui/rect': 1.1.0 2199 + react: 19.0.0 2200 + react-dom: 19.0.0(react@19.0.0) 2201 + 2202 + '@radix-ui/react-portal@1.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2203 + dependencies: 2204 + '@radix-ui/react-primitive': 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2205 + '@radix-ui/react-use-layout-effect': 1.1.0(react@19.0.0) 2206 + react: 19.0.0 2207 + react-dom: 19.0.0(react@19.0.0) 2208 + 2209 + '@radix-ui/react-presence@1.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2210 + dependencies: 2211 + '@radix-ui/react-compose-refs': 1.1.1(react@19.0.0) 2212 + '@radix-ui/react-use-layout-effect': 1.1.0(react@19.0.0) 2213 + react: 19.0.0 2214 + react-dom: 19.0.0(react@19.0.0) 2215 + 2216 + '@radix-ui/react-primitive@2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2217 + dependencies: 2218 + '@radix-ui/react-slot': 1.1.1(react@19.0.0) 2219 + react: 19.0.0 2220 + react-dom: 19.0.0(react@19.0.0) 2221 + 2222 + '@radix-ui/react-separator@1.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2223 + dependencies: 2224 + '@radix-ui/react-primitive': 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2225 + react: 19.0.0 2226 + react-dom: 19.0.0(react@19.0.0) 2227 + 2228 + '@radix-ui/react-slot@1.1.1(react@19.0.0)': 2229 + dependencies: 2230 + '@radix-ui/react-compose-refs': 1.1.1(react@19.0.0) 2231 + react: 19.0.0 2232 + 2233 + '@radix-ui/react-tooltip@1.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2234 + dependencies: 2235 + '@radix-ui/primitive': 1.1.1 2236 + '@radix-ui/react-compose-refs': 1.1.1(react@19.0.0) 2237 + '@radix-ui/react-context': 1.1.1(react@19.0.0) 2238 + '@radix-ui/react-dismissable-layer': 1.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2239 + '@radix-ui/react-id': 1.1.0(react@19.0.0) 2240 + '@radix-ui/react-popper': 1.2.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2241 + '@radix-ui/react-portal': 1.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2242 + '@radix-ui/react-presence': 1.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2243 + '@radix-ui/react-primitive': 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2244 + '@radix-ui/react-slot': 1.1.1(react@19.0.0) 2245 + '@radix-ui/react-use-controllable-state': 1.1.0(react@19.0.0) 2246 + '@radix-ui/react-visually-hidden': 1.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2247 + react: 19.0.0 2248 + react-dom: 19.0.0(react@19.0.0) 2249 + 2250 + '@radix-ui/react-use-callback-ref@1.1.0(react@19.0.0)': 2251 + dependencies: 2252 + react: 19.0.0 2253 + 2254 + '@radix-ui/react-use-controllable-state@1.1.0(react@19.0.0)': 2255 + dependencies: 2256 + '@radix-ui/react-use-callback-ref': 1.1.0(react@19.0.0) 2257 + react: 19.0.0 2258 + 2259 + '@radix-ui/react-use-escape-keydown@1.1.0(react@19.0.0)': 2260 + dependencies: 2261 + '@radix-ui/react-use-callback-ref': 1.1.0(react@19.0.0) 2262 + react: 19.0.0 2263 + 2264 + '@radix-ui/react-use-layout-effect@1.1.0(react@19.0.0)': 2265 + dependencies: 2266 + react: 19.0.0 2267 + 2268 + '@radix-ui/react-use-rect@1.1.0(react@19.0.0)': 2269 + dependencies: 2270 + '@radix-ui/rect': 1.1.0 2271 + react: 19.0.0 2272 + 2273 + '@radix-ui/react-use-size@1.1.0(react@19.0.0)': 2274 + dependencies: 2275 + '@radix-ui/react-use-layout-effect': 1.1.0(react@19.0.0) 2276 + react: 19.0.0 2277 + 2278 + '@radix-ui/react-visually-hidden@1.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2279 + dependencies: 2280 + '@radix-ui/react-primitive': 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2281 + react: 19.0.0 2282 + react-dom: 19.0.0(react@19.0.0) 2283 + 2284 + '@radix-ui/rect@1.1.0': {} 2285 + 1354 2286 '@rollup/pluginutils@4.2.1': 1355 2287 dependencies: 1356 2288 estree-walker: 2.0.2 ··· 1413 2345 '@rollup/rollup-win32-x64-msvc@4.29.0': 1414 2346 optional: true 1415 2347 2348 + '@tanstack/history@1.90.0': {} 2349 + 2350 + '@tanstack/react-router@1.91.3(@tanstack/router-generator@1.87.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2351 + dependencies: 2352 + '@tanstack/history': 1.90.0 2353 + '@tanstack/react-store': 0.6.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2354 + jsesc: 3.1.0 2355 + react: 19.0.0 2356 + react-dom: 19.0.0(react@19.0.0) 2357 + tiny-invariant: 1.3.3 2358 + tiny-warning: 1.0.3 2359 + optionalDependencies: 2360 + '@tanstack/router-generator': 1.87.7 2361 + 2362 + '@tanstack/react-store@0.6.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2363 + dependencies: 2364 + '@tanstack/store': 0.6.0 2365 + react: 19.0.0 2366 + react-dom: 19.0.0(react@19.0.0) 2367 + use-sync-external-store: 1.4.0(react@19.0.0) 2368 + 2369 + '@tanstack/router-devtools@1.91.3(@tanstack/react-router@1.91.3(@tanstack/router-generator@1.87.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': 2370 + dependencies: 2371 + '@tanstack/react-router': 1.91.3(@tanstack/router-generator@1.87.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 2372 + clsx: 2.1.1 2373 + goober: 2.1.16(csstype@3.1.3) 2374 + react: 19.0.0 2375 + react-dom: 19.0.0(react@19.0.0) 2376 + transitivePeerDependencies: 2377 + - csstype 2378 + 2379 + '@tanstack/router-generator@1.87.7': 2380 + dependencies: 2381 + '@tanstack/virtual-file-routes': 1.87.6 2382 + prettier: 3.4.2 2383 + tsx: 4.19.2 2384 + zod: 3.24.1 2385 + 2386 + '@tanstack/router-plugin@1.91.1(vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1))': 2387 + dependencies: 2388 + '@babel/core': 7.26.0 2389 + '@babel/generator': 7.26.3 2390 + '@babel/parser': 7.26.3 2391 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) 2392 + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) 2393 + '@babel/template': 7.25.9 2394 + '@babel/traverse': 7.26.4 2395 + '@babel/types': 7.26.3 2396 + '@tanstack/router-generator': 1.87.7 2397 + '@tanstack/virtual-file-routes': 1.87.6 2398 + '@types/babel__core': 7.20.5 2399 + '@types/babel__generator': 7.6.8 2400 + '@types/babel__template': 7.4.4 2401 + '@types/babel__traverse': 7.20.6 2402 + babel-dead-code-elimination: 1.0.8 2403 + chokidar: 3.6.0 2404 + unplugin: 1.16.0 2405 + zod: 3.24.1 2406 + optionalDependencies: 2407 + vite: 6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1) 2408 + transitivePeerDependencies: 2409 + - supports-color 2410 + 2411 + '@tanstack/store@0.6.0': {} 2412 + 2413 + '@tanstack/virtual-file-routes@1.87.6': {} 2414 + 2415 + '@types/babel__core@7.20.5': 2416 + dependencies: 2417 + '@babel/parser': 7.26.3 2418 + '@babel/types': 7.26.3 2419 + '@types/babel__generator': 7.6.8 2420 + '@types/babel__template': 7.4.4 2421 + '@types/babel__traverse': 7.20.6 2422 + 2423 + '@types/babel__generator@7.6.8': 2424 + dependencies: 2425 + '@babel/types': 7.26.3 2426 + 2427 + '@types/babel__template@7.4.4': 2428 + dependencies: 2429 + '@babel/parser': 7.26.3 2430 + '@babel/types': 7.26.3 2431 + 2432 + '@types/babel__traverse@7.20.6': 2433 + dependencies: 2434 + '@babel/types': 7.26.3 2435 + 1416 2436 '@types/estree@1.0.6': {} 1417 2437 1418 2438 '@types/node@22.10.2': 1419 2439 dependencies: 1420 2440 undici-types: 6.20.0 2441 + 2442 + acorn@8.14.0: {} 1421 2443 1422 2444 ansi-regex@5.0.1: {} 1423 2445 ··· 1437 2459 picomatch: 2.3.1 1438 2460 1439 2461 arg@5.0.2: {} 2462 + 2463 + aria-hidden@1.2.4: 2464 + dependencies: 2465 + tslib: 2.8.1 1440 2466 1441 2467 autoprefixer@10.4.20(postcss@8.4.49): 1442 2468 dependencies: ··· 1448 2474 postcss: 8.4.49 1449 2475 postcss-value-parser: 4.2.0 1450 2476 2477 + babel-dead-code-elimination@1.0.8: 2478 + dependencies: 2479 + '@babel/core': 7.26.0 2480 + '@babel/parser': 7.26.3 2481 + '@babel/traverse': 7.26.4 2482 + '@babel/types': 7.26.3 2483 + transitivePeerDependencies: 2484 + - supports-color 2485 + 1451 2486 babel-plugin-transform-hook-names@1.0.2(@babel/core@7.26.0): 1452 2487 dependencies: 1453 2488 '@babel/core': 7.26.0 ··· 1523 2558 1524 2559 cssesc@3.0.0: {} 1525 2560 2561 + csstype@3.1.3: {} 2562 + 1526 2563 debug@4.4.0: 1527 2564 dependencies: 1528 2565 ms: 2.1.3 2566 + 2567 + detect-node-es@1.1.0: {} 1529 2568 1530 2569 didyoumean@1.2.2: {} 1531 2570 ··· 1559 2598 1560 2599 entities@4.5.0: {} 1561 2600 2601 + esbuild@0.23.1: 2602 + optionalDependencies: 2603 + '@esbuild/aix-ppc64': 0.23.1 2604 + '@esbuild/android-arm': 0.23.1 2605 + '@esbuild/android-arm64': 0.23.1 2606 + '@esbuild/android-x64': 0.23.1 2607 + '@esbuild/darwin-arm64': 0.23.1 2608 + '@esbuild/darwin-x64': 0.23.1 2609 + '@esbuild/freebsd-arm64': 0.23.1 2610 + '@esbuild/freebsd-x64': 0.23.1 2611 + '@esbuild/linux-arm': 0.23.1 2612 + '@esbuild/linux-arm64': 0.23.1 2613 + '@esbuild/linux-ia32': 0.23.1 2614 + '@esbuild/linux-loong64': 0.23.1 2615 + '@esbuild/linux-mips64el': 0.23.1 2616 + '@esbuild/linux-ppc64': 0.23.1 2617 + '@esbuild/linux-riscv64': 0.23.1 2618 + '@esbuild/linux-s390x': 0.23.1 2619 + '@esbuild/linux-x64': 0.23.1 2620 + '@esbuild/netbsd-x64': 0.23.1 2621 + '@esbuild/openbsd-arm64': 0.23.1 2622 + '@esbuild/openbsd-x64': 0.23.1 2623 + '@esbuild/sunos-x64': 0.23.1 2624 + '@esbuild/win32-arm64': 0.23.1 2625 + '@esbuild/win32-ia32': 0.23.1 2626 + '@esbuild/win32-x64': 0.23.1 2627 + 1562 2628 esbuild@0.24.0: 1563 2629 optionalDependencies: 1564 2630 '@esbuild/aix-ppc64': 0.24.0 ··· 1620 2686 1621 2687 gensync@1.0.0-beta.2: {} 1622 2688 2689 + get-nonce@1.0.1: {} 2690 + 2691 + get-tsconfig@4.8.1: 2692 + dependencies: 2693 + resolve-pkg-maps: 1.0.0 2694 + 1623 2695 glob-parent@5.1.2: 1624 2696 dependencies: 1625 2697 is-glob: 4.0.3 ··· 1638 2710 path-scurry: 1.11.1 1639 2711 1640 2712 globals@11.12.0: {} 2713 + 2714 + goober@2.1.16(csstype@3.1.3): 2715 + dependencies: 2716 + csstype: 3.1.3 1641 2717 1642 2718 hasown@2.0.2: 1643 2719 dependencies: ··· 1722 2798 1723 2799 nanoid@3.3.8: {} 1724 2800 2801 + nanoid@5.0.9: {} 2802 + 1725 2803 node-html-parser@6.1.13: 1726 2804 dependencies: 1727 2805 css-select: 5.1.0 ··· 1798 2876 source-map-js: 1.2.1 1799 2877 1800 2878 preact@10.25.3: {} 2879 + 2880 + prettier@3.4.2: {} 1801 2881 1802 2882 queue-microtask@1.2.3: {} 1803 2883 2884 + react-dom@19.0.0(react@19.0.0): 2885 + dependencies: 2886 + react: 19.0.0 2887 + scheduler: 0.25.0 2888 + 2889 + react-remove-scroll-bar@2.3.8(react@19.0.0): 2890 + dependencies: 2891 + react: 19.0.0 2892 + react-style-singleton: 2.2.3(react@19.0.0) 2893 + tslib: 2.8.1 2894 + 2895 + react-remove-scroll@2.6.2(react@19.0.0): 2896 + dependencies: 2897 + react: 19.0.0 2898 + react-remove-scroll-bar: 2.3.8(react@19.0.0) 2899 + react-style-singleton: 2.2.3(react@19.0.0) 2900 + tslib: 2.8.1 2901 + use-callback-ref: 1.3.3(react@19.0.0) 2902 + use-sidecar: 1.1.3(react@19.0.0) 2903 + 2904 + react-style-singleton@2.2.3(react@19.0.0): 2905 + dependencies: 2906 + get-nonce: 1.0.1 2907 + react: 19.0.0 2908 + tslib: 2.8.1 2909 + 1804 2910 react@19.0.0: {} 1805 2911 1806 2912 read-cache@1.0.0: ··· 1810 2916 readdirp@3.6.0: 1811 2917 dependencies: 1812 2918 picomatch: 2.3.1 2919 + 2920 + resolve-pkg-maps@1.0.0: {} 1813 2921 1814 2922 resolve@1.22.10: 1815 2923 dependencies: ··· 1848 2956 dependencies: 1849 2957 queue-microtask: 1.2.3 1850 2958 2959 + scheduler@0.25.0: {} 2960 + 1851 2961 semver@6.3.1: {} 1852 2962 1853 2963 shebang-command@2.0.0: ··· 1857 2967 shebang-regex@3.0.0: {} 1858 2968 1859 2969 signal-exit@4.1.0: {} 2970 + 2971 + simple-icons@13.21.0: {} 1860 2972 1861 2973 source-map-js@1.2.1: {} 1862 2974 ··· 1936 3048 thenify@3.3.1: 1937 3049 dependencies: 1938 3050 any-promise: 1.3.0 3051 + 3052 + tiny-invariant@1.3.3: {} 3053 + 3054 + tiny-warning@1.0.3: {} 1939 3055 1940 3056 to-regex-range@5.0.1: 1941 3057 dependencies: ··· 1943 3059 1944 3060 ts-interface-checker@0.1.13: {} 1945 3061 3062 + tslib@2.8.1: {} 3063 + 3064 + tsx@4.19.2: 3065 + dependencies: 3066 + esbuild: 0.23.1 3067 + get-tsconfig: 4.8.1 3068 + optionalDependencies: 3069 + fsevents: 2.3.3 3070 + 1946 3071 typescript@5.6.3: {} 1947 3072 1948 3073 undici-types@6.20.0: {} 1949 3074 3075 + unplugin@1.16.0: 3076 + dependencies: 3077 + acorn: 8.14.0 3078 + webpack-virtual-modules: 0.6.2 3079 + 1950 3080 update-browserslist-db@1.1.1(browserslist@4.24.3): 1951 3081 dependencies: 1952 3082 browserslist: 4.24.3 1953 3083 escalade: 3.2.0 1954 3084 picocolors: 1.1.1 1955 3085 3086 + use-callback-ref@1.3.3(react@19.0.0): 3087 + dependencies: 3088 + react: 19.0.0 3089 + tslib: 2.8.1 3090 + 3091 + use-sidecar@1.1.3(react@19.0.0): 3092 + dependencies: 3093 + detect-node-es: 1.1.0 3094 + react: 19.0.0 3095 + tslib: 2.8.1 3096 + 3097 + use-sync-external-store@1.4.0(react@19.0.0): 3098 + dependencies: 3099 + react: 19.0.0 3100 + 1956 3101 util-deprecate@1.0.2: {} 1957 3102 1958 - vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1): 3103 + vite@6.0.5(@types/node@22.10.2)(jiti@1.21.7)(tsx@4.19.2)(yaml@2.6.1): 1959 3104 dependencies: 1960 3105 esbuild: 0.24.0 1961 3106 postcss: 8.4.49 ··· 1964 3109 '@types/node': 22.10.2 1965 3110 fsevents: 2.3.3 1966 3111 jiti: 1.21.7 3112 + tsx: 4.19.2 1967 3113 yaml: 2.6.1 3114 + 3115 + webpack-virtual-modules@0.6.2: {} 1968 3116 1969 3117 which@2.0.2: 1970 3118 dependencies: ··· 1985 3133 yallist@3.1.1: {} 1986 3134 1987 3135 yaml@2.6.1: {} 3136 + 3137 + zod@3.24.1: {}
-25
src/app.css
··· 1 - #app { 2 - max-width: 1280px; 3 - margin: 0 auto; 4 - padding: 2rem; 5 - text-align: center; 6 - } 7 - 8 - .logo { 9 - height: 6em; 10 - padding: 1.5em; 11 - } 12 - .logo:hover { 13 - filter: drop-shadow(0 0 2em #646cffaa); 14 - } 15 - .logo.preact:hover { 16 - filter: drop-shadow(0 0 2em #673ab8aa); 17 - } 18 - 19 - .card { 20 - padding: 2em; 21 - } 22 - 23 - .read-the-docs { 24 - color: #888; 25 - }
+15 -40
src/app.tsx
··· 1 - import { useState } from 'preact/hooks' 2 - import preactLogo from './assets/preact.svg' 3 - import viteLogo from '/vite.svg' 4 - import './app.css' 1 + import { RouterProvider, createRouter } from "@tanstack/react-router"; 5 2 6 - export function App() { 7 - const [count, setCount] = useState(0) 3 + // Import the generated route tree 4 + import { routeTree } from "./routeTree.gen"; 8 5 9 - return ( 10 - <> 11 - <div> 12 - <a href="https://vite.dev" target="_blank"> 13 - <img src={viteLogo} class="logo" alt="Vite logo" /> 14 - </a> 15 - <a href="https://preactjs.com" target="_blank"> 16 - <img src={preactLogo} class="logo preact" alt="Preact logo" /> 17 - </a> 18 - </div> 19 - <h1>Vite + Preact</h1> 20 - <div class="card"> 21 - <button onClick={() => setCount((count) => count + 1)}> 22 - count is {count} 23 - </button> 24 - <p> 25 - Edit <code>src/app.tsx</code> and save to test HMR 26 - </p> 27 - </div> 28 - <p> 29 - Check out{' '} 30 - <a 31 - href="https://preactjs.com/guide/v10/getting-started#create-a-vite-powered-preact-app" 32 - target="_blank" 33 - > 34 - create-preact 35 - </a> 36 - , the official Preact + Vite starter 37 - </p> 38 - <p class="read-the-docs"> 39 - Click on the Vite and Preact logos to learn more 40 - </p> 41 - </> 42 - ) 6 + // Create a new router instance 7 + const router = createRouter({ routeTree }); 8 + 9 + // Register the router instance for type safety 10 + declare module "@tanstack/react-router" { 11 + interface Register { 12 + router: typeof router; 13 + } 14 + } 15 + 16 + export function App() { 17 + return <RouterProvider router={router} />; 43 18 }
+103
src/components/repoIcons.tsx
··· 1 + import { 2 + ClipboardPaste, 3 + File, 4 + MessageSquare, 5 + Pen, 6 + ShieldQuestionIcon, 7 + Star, 8 + ThumbsUp, 9 + Waves, 10 + } from "lucide-react"; 11 + 12 + import { 13 + siBluesky, 14 + siLinkfire, 15 + siMediafire, 16 + SimpleIcon, 17 + siReddit, 18 + } from "simple-icons"; 19 + 20 + interface IconMapping { 21 + // The icon to display, a url to an image or a component (Lucide icon) 22 + icon: string | React.ReactNode; 23 + // The label to display on icon hover 24 + label: string; 25 + } 26 + 27 + function svgB64ify(svg: string) { 28 + return "data:image/svg+xml;base64," + btoa(svg); 29 + } 30 + 31 + const iconMappings: Record<string, IconMapping> = { 32 + "app.bsky": { icon: svgB64ify(siBluesky.svg), label: "Bluesky" }, 33 + "blue.zio.atfile": { 34 + icon: <File />, 35 + label: "Atfile", 36 + }, 37 + "com.shinolabs.pinksea": { 38 + icon: <Waves />, 39 + label: "Pinksea", 40 + }, 41 + "com.whtwnd.blog.entry": { icon: <Pen />, label: "Blog" }, 42 + "fyi.unravel.frontpage": { 43 + icon: svgB64ify(siReddit.svg), 44 + label: "Frontpage", 45 + }, 46 + "events.smokesignal": { 47 + icon: svgB64ify(siMediafire.svg), 48 + label: "Smokesignal", 49 + }, 50 + "link.pastesphere": { icon: <ClipboardPaste />, label: "Pastesphere" }, 51 + "my.skylights": { icon: <Star />, label: "Skylights" }, 52 + "social.psky": { icon: <MessageSquare />, label: "Psky" }, 53 + "xyz.statusphere": { icon: <ThumbsUp />, label: "Statusphere example app" }, 54 + }; 55 + 56 + function getIconForCollection(collection: string) { 57 + // Find matching key in iconMappings 58 + const matchingKey = Object.keys(iconMappings).find((key) => 59 + collection.includes(key), 60 + ); 61 + return matchingKey ? iconMappings[matchingKey] : null; 62 + } 63 + 64 + function RepoIcons({ collections }: { collections: string[] }) { 65 + let uniqueTypes = Array.from( 66 + collections 67 + .map((collection) => ({ 68 + id: collection, 69 + Icon: getIconForCollection(collection)?.icon ?? <ShieldQuestionIcon />, 70 + displayName: getIconForCollection(collection)?.label ?? "Unknown", 71 + })) 72 + .reduce((acc, current) => { 73 + if ( 74 + !Array.from(acc.values()).some( 75 + (item) => item.displayName === current.displayName, 76 + ) 77 + ) { 78 + acc.set(current.displayName, current); 79 + } 80 + return acc; 81 + }, new Map()) 82 + .values(), 83 + ); 84 + // remove all unknowns 85 + uniqueTypes = uniqueTypes.filter( 86 + ({ displayName }) => displayName !== "Unknown", 87 + ); 88 + 89 + console.log(uniqueTypes); 90 + return uniqueTypes.map(({ id, Icon, displayName }) => ( 91 + <div key={id}> 92 + <div className="w-8 h-8 p-1 mr-2 rounded-full bg-neutral-500 text-white"> 93 + {typeof Icon === "string" ? ( 94 + <img src={Icon} alt={displayName} /> 95 + ) : ( 96 + <>{Icon}</> 97 + )} 98 + </div> 99 + </div> 100 + )); 101 + } 102 + 103 + export default RepoIcons;
+78
src/components/sidebar.tsx
··· 1 + import { AtSign, FireExtinguisher, Home } from "lucide-react"; 2 + 3 + import { 4 + Sidebar, 5 + SidebarContent, 6 + SidebarFooter, 7 + SidebarGroup, 8 + SidebarGroupContent, 9 + SidebarGroupLabel, 10 + SidebarHeader, 11 + SidebarMenu, 12 + SidebarMenuButton, 13 + SidebarMenuItem, 14 + SidebarRail, 15 + } from "@/components/ui/sidebar"; 16 + import { SmartSearchBar } from "./smartSearchBar"; 17 + import { ColorToggle } from "./themeSwitcher"; 18 + 19 + // Menu items. 20 + const items = [ 21 + { 22 + title: "Home", 23 + url: "#", 24 + icon: Home, 25 + }, 26 + { 27 + title: "Firehose", 28 + url: "#", 29 + icon: FireExtinguisher, 30 + }, 31 + ]; 32 + 33 + export function AppSidebar() { 34 + return ( 35 + <Sidebar collapsible="icon" variant="sidebar"> 36 + <SidebarContent> 37 + <SidebarHeader className="-my-4"> 38 + <div className="flex items-center text-3xl px-2 pt-4"> 39 + <AtSign className="text-blue-500 mr-1 mt-1" height={36} /> 40 + tools 41 + </div> 42 + </SidebarHeader> 43 + <SidebarGroup> 44 + <SidebarGroupLabel> 45 + Search{" "} 46 + <div className="text-xs ml-1 text-center bg-muted-foreground text-muted rounded-full aspect-square h-4 w-4"> 47 + ? 48 + </div> 49 + </SidebarGroupLabel> 50 + <SidebarGroupContent> 51 + <SmartSearchBar /> 52 + </SidebarGroupContent> 53 + </SidebarGroup> 54 + <SidebarGroup className="-my-4"> 55 + <SidebarGroupLabel>Application</SidebarGroupLabel> 56 + <SidebarGroupContent> 57 + <SidebarMenu> 58 + {items.map((item) => ( 59 + <SidebarMenuItem key={item.title}> 60 + <SidebarMenuButton asChild> 61 + <a href={item.url}> 62 + <item.icon /> 63 + <span>{item.title}</span> 64 + </a> 65 + </SidebarMenuButton> 66 + </SidebarMenuItem> 67 + ))} 68 + </SidebarMenu> 69 + </SidebarGroupContent> 70 + </SidebarGroup> 71 + </SidebarContent> 72 + <SidebarFooter> 73 + <ColorToggle /> 74 + </SidebarFooter> 75 + <SidebarRail /> 76 + </Sidebar> 77 + ); 78 + }
+35
src/components/smartSearchBar.tsx
··· 1 + import { Input } from "@/components/ui/input"; 2 + import { useState } from "react"; 3 + import { ArrowRight, Search } from "lucide-react"; 4 + import { useNavigate } from "@tanstack/react-router"; 5 + 6 + export function SmartSearchBar() { 7 + const navigate = useNavigate(); 8 + const [input, setInput] = useState(""); 9 + 10 + const handleSubmit = (e: React.FormEvent) => { 11 + e.preventDefault(); 12 + if (input.trim()) { 13 + navigate({ to: `/at/${input}` }); 14 + // Or if you have a route defined with params: 15 + // navigate({ to: '/at/$id', params: { id: input } }); 16 + } 17 + }; 18 + 19 + return ( 20 + <form onSubmit={handleSubmit} className="relative w-full max-w-sm"> 21 + <Search className="absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" /> 22 + <Input 23 + type="text" 24 + placeholder="Search..." 25 + value={input} 26 + onChange={(e) => setInput(e.currentTarget.value)} 27 + className="pl-8" 28 + /> 29 + <ArrowRight 30 + className="absolute right-2 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" 31 + onClick={handleSubmit} 32 + /> 33 + </form> 34 + ); 35 + }
+41
src/components/themeSwitcher.tsx
··· 1 + "use client"; 2 + import { useTheme } from "@/providers/themeProvider"; 3 + import { useEffect, useState } from "react"; 4 + import { IconButton } from "./ui/iconButton"; 5 + import { Circle, Moon, Sun } from "lucide-react"; 6 + 7 + const other = (theme: string) => { 8 + if (theme === "dark") { 9 + return "light"; 10 + } else { 11 + if (theme === "system") { 12 + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)"); 13 + return systemTheme.matches ? "light" : "dark"; 14 + } else { 15 + return "dark"; 16 + } 17 + } 18 + }; 19 + 20 + export const ColorToggle = () => { 21 + const { theme, toggleTheme } = useTheme(); 22 + const [mounted, setMounted] = useState(false); 23 + useEffect(() => { 24 + setMounted(true); 25 + }, []); 26 + const DarkLightIcon = () => { 27 + if (!mounted) return <Circle />; 28 + return theme === "dark" ? <Sun /> : <Moon />; 29 + }; 30 + return ( 31 + // tailwindcss button 32 + <IconButton 33 + className="flex items-center justify-center w-10 h-10 p-3 rounded-full bg-gray-200 dark:bg-gray-800 cursor-pointer hover:bg-gray-300 dark:hover:bg-gray-700" 34 + aria-label="button" 35 + Icon={DarkLightIcon} 36 + onClick={() => { 37 + toggleTheme(); 38 + }} 39 + /> 40 + ); 41 + };
+56
src/components/ui/button.tsx
··· 1 + import * as React from "react" 2 + import { Slot } from "@radix-ui/react-slot" 3 + import { cva, type VariantProps } from "class-variance-authority" 4 + 5 + import { cn } from "@/lib/utils" 6 + 7 + const buttonVariants = cva( 8 + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", 9 + { 10 + variants: { 11 + variant: { 12 + default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 + destructive: 14 + "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 + outline: 16 + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 + secondary: 18 + "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 + ghost: "hover:bg-accent hover:text-accent-foreground", 20 + link: "text-primary underline-offset-4 hover:underline", 21 + }, 22 + size: { 23 + default: "h-10 px-4 py-2", 24 + sm: "h-9 rounded-md px-3", 25 + lg: "h-11 rounded-md px-8", 26 + icon: "h-10 w-10", 27 + }, 28 + }, 29 + defaultVariants: { 30 + variant: "default", 31 + size: "default", 32 + }, 33 + } 34 + ) 35 + 36 + export interface ButtonProps 37 + extends React.ButtonHTMLAttributes<HTMLButtonElement>, 38 + VariantProps<typeof buttonVariants> { 39 + asChild?: boolean 40 + } 41 + 42 + const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( 43 + ({ className, variant, size, asChild = false, ...props }, ref) => { 44 + const Comp = asChild ? Slot : "button" 45 + return ( 46 + <Comp 47 + className={cn(buttonVariants({ variant, size, className }))} 48 + ref={ref} 49 + {...props} 50 + /> 51 + ) 52 + } 53 + ) 54 + Button.displayName = "Button" 55 + 56 + export { Button, buttonVariants }
+27
src/components/ui/iconButton.tsx
··· 1 + import clsx from "clsx"; 2 + import { IconType } from "react-icons/lib"; 3 + interface IconButtonProps extends React.HTMLAttributes<HTMLButtonElement> { 4 + className?: string; 5 + Icon: IconType; 6 + onClick?: () => void; 7 + ariaLabel?: string; 8 + disabled?: boolean; 9 + } 10 + 11 + export const IconButton = (props: IconButtonProps) => { 12 + return ( 13 + // tailwindcss button 14 + <button 15 + className={clsx( 16 + props.className, 17 + "flex items-center justify-center w-10 h-10 p-3 rounded-full transition-colors duration-150 bg-gray-200 dark:bg-gray-800 cursor-pointer hover:bg-gray-300 dark:hover:bg-gray-700", 18 + "disabled:cursor-default disabled:bg-gray-200 disabled:dark:bg-gray-800", 19 + )} 20 + disabled={props.disabled} 21 + onClick={props.onClick} 22 + aria-label={props.ariaLabel} 23 + > 24 + <props.Icon /> 25 + </button> 26 + ); 27 + };
+22
src/components/ui/input.tsx
··· 1 + import * as React from "react" 2 + 3 + import { cn } from "@/lib/utils" 4 + 5 + const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>( 6 + ({ className, type, ...props }, ref) => { 7 + return ( 8 + <input 9 + type={type} 10 + className={cn( 11 + "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", 12 + className 13 + )} 14 + ref={ref} 15 + {...props} 16 + /> 17 + ) 18 + } 19 + ) 20 + Input.displayName = "Input" 21 + 22 + export { Input }
+29
src/components/ui/separator.tsx
··· 1 + import * as React from "react" 2 + import * as SeparatorPrimitive from "@radix-ui/react-separator" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const Separator = React.forwardRef< 7 + React.ElementRef<typeof SeparatorPrimitive.Root>, 8 + React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root> 9 + >( 10 + ( 11 + { className, orientation = "horizontal", decorative = true, ...props }, 12 + ref 13 + ) => ( 14 + <SeparatorPrimitive.Root 15 + ref={ref} 16 + decorative={decorative} 17 + orientation={orientation} 18 + className={cn( 19 + "shrink-0 bg-border", 20 + orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", 21 + className 22 + )} 23 + {...props} 24 + /> 25 + ) 26 + ) 27 + Separator.displayName = SeparatorPrimitive.Root.displayName 28 + 29 + export { Separator }
+140
src/components/ui/sheet.tsx
··· 1 + "use client" 2 + 3 + import * as React from "react" 4 + import * as SheetPrimitive from "@radix-ui/react-dialog" 5 + import { cva, type VariantProps } from "class-variance-authority" 6 + import { X } from "lucide-react" 7 + 8 + import { cn } from "@/lib/utils" 9 + 10 + const Sheet = SheetPrimitive.Root 11 + 12 + const SheetTrigger = SheetPrimitive.Trigger 13 + 14 + const SheetClose = SheetPrimitive.Close 15 + 16 + const SheetPortal = SheetPrimitive.Portal 17 + 18 + const SheetOverlay = React.forwardRef< 19 + React.ElementRef<typeof SheetPrimitive.Overlay>, 20 + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay> 21 + >(({ className, ...props }, ref) => ( 22 + <SheetPrimitive.Overlay 23 + className={cn( 24 + "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", 25 + className 26 + )} 27 + {...props} 28 + ref={ref} 29 + /> 30 + )) 31 + SheetOverlay.displayName = SheetPrimitive.Overlay.displayName 32 + 33 + const sheetVariants = cva( 34 + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", 35 + { 36 + variants: { 37 + side: { 38 + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", 39 + bottom: 40 + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", 41 + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", 42 + right: 43 + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", 44 + }, 45 + }, 46 + defaultVariants: { 47 + side: "right", 48 + }, 49 + } 50 + ) 51 + 52 + interface SheetContentProps 53 + extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, 54 + VariantProps<typeof sheetVariants> {} 55 + 56 + const SheetContent = React.forwardRef< 57 + React.ElementRef<typeof SheetPrimitive.Content>, 58 + SheetContentProps 59 + >(({ side = "right", className, children, ...props }, ref) => ( 60 + <SheetPortal> 61 + <SheetOverlay /> 62 + <SheetPrimitive.Content 63 + ref={ref} 64 + className={cn(sheetVariants({ side }), className)} 65 + {...props} 66 + > 67 + {children} 68 + <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> 69 + <X className="h-4 w-4" /> 70 + <span className="sr-only">Close</span> 71 + </SheetPrimitive.Close> 72 + </SheetPrimitive.Content> 73 + </SheetPortal> 74 + )) 75 + SheetContent.displayName = SheetPrimitive.Content.displayName 76 + 77 + const SheetHeader = ({ 78 + className, 79 + ...props 80 + }: React.HTMLAttributes<HTMLDivElement>) => ( 81 + <div 82 + className={cn( 83 + "flex flex-col space-y-2 text-center sm:text-left", 84 + className 85 + )} 86 + {...props} 87 + /> 88 + ) 89 + SheetHeader.displayName = "SheetHeader" 90 + 91 + const SheetFooter = ({ 92 + className, 93 + ...props 94 + }: React.HTMLAttributes<HTMLDivElement>) => ( 95 + <div 96 + className={cn( 97 + "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", 98 + className 99 + )} 100 + {...props} 101 + /> 102 + ) 103 + SheetFooter.displayName = "SheetFooter" 104 + 105 + const SheetTitle = React.forwardRef< 106 + React.ElementRef<typeof SheetPrimitive.Title>, 107 + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title> 108 + >(({ className, ...props }, ref) => ( 109 + <SheetPrimitive.Title 110 + ref={ref} 111 + className={cn("text-lg font-semibold text-foreground", className)} 112 + {...props} 113 + /> 114 + )) 115 + SheetTitle.displayName = SheetPrimitive.Title.displayName 116 + 117 + const SheetDescription = React.forwardRef< 118 + React.ElementRef<typeof SheetPrimitive.Description>, 119 + React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description> 120 + >(({ className, ...props }, ref) => ( 121 + <SheetPrimitive.Description 122 + ref={ref} 123 + className={cn("text-sm text-muted-foreground", className)} 124 + {...props} 125 + /> 126 + )) 127 + SheetDescription.displayName = SheetPrimitive.Description.displayName 128 + 129 + export { 130 + Sheet, 131 + SheetPortal, 132 + SheetOverlay, 133 + SheetTrigger, 134 + SheetClose, 135 + SheetContent, 136 + SheetHeader, 137 + SheetFooter, 138 + SheetTitle, 139 + SheetDescription, 140 + }
+761
src/components/ui/sidebar.tsx
··· 1 + import * as React from "react" 2 + import { Slot } from "@radix-ui/react-slot" 3 + import { VariantProps, cva } from "class-variance-authority" 4 + import { PanelLeft } from "lucide-react" 5 + 6 + import { useIsMobile } from "@/hooks/use-mobile" 7 + import { cn } from "@/lib/utils" 8 + import { Button } from "@/components/ui/button" 9 + import { Input } from "@/components/ui/input" 10 + import { Separator } from "@/components/ui/separator" 11 + import { Sheet, SheetContent } from "@/components/ui/sheet" 12 + import { Skeleton } from "@/components/ui/skeleton" 13 + import { 14 + Tooltip, 15 + TooltipContent, 16 + TooltipProvider, 17 + TooltipTrigger, 18 + } from "@/components/ui/tooltip" 19 + 20 + const SIDEBAR_COOKIE_NAME = "sidebar:state" 21 + const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 22 + const SIDEBAR_WIDTH = "16rem" 23 + const SIDEBAR_WIDTH_MOBILE = "18rem" 24 + const SIDEBAR_WIDTH_ICON = "3rem" 25 + const SIDEBAR_KEYBOARD_SHORTCUT = "b" 26 + 27 + type SidebarContext = { 28 + state: "expanded" | "collapsed" 29 + open: boolean 30 + setOpen: (open: boolean) => void 31 + openMobile: boolean 32 + setOpenMobile: (open: boolean) => void 33 + isMobile: boolean 34 + toggleSidebar: () => void 35 + } 36 + 37 + const SidebarContext = React.createContext<SidebarContext | null>(null) 38 + 39 + function useSidebar() { 40 + const context = React.useContext(SidebarContext) 41 + if (!context) { 42 + throw new Error("useSidebar must be used within a SidebarProvider.") 43 + } 44 + 45 + return context 46 + } 47 + 48 + const SidebarProvider = React.forwardRef< 49 + HTMLDivElement, 50 + React.ComponentProps<"div"> & { 51 + defaultOpen?: boolean 52 + open?: boolean 53 + onOpenChange?: (open: boolean) => void 54 + } 55 + >( 56 + ( 57 + { 58 + defaultOpen = true, 59 + open: openProp, 60 + onOpenChange: setOpenProp, 61 + className, 62 + style, 63 + children, 64 + ...props 65 + }, 66 + ref 67 + ) => { 68 + const isMobile = useIsMobile() 69 + const [openMobile, setOpenMobile] = React.useState(false) 70 + 71 + // This is the internal state of the sidebar. 72 + // We use openProp and setOpenProp for control from outside the component. 73 + const [_open, _setOpen] = React.useState(defaultOpen) 74 + const open = openProp ?? _open 75 + const setOpen = React.useCallback( 76 + (value: boolean | ((value: boolean) => boolean)) => { 77 + const openState = typeof value === "function" ? value(open) : value 78 + if (setOpenProp) { 79 + setOpenProp(openState) 80 + } else { 81 + _setOpen(openState) 82 + } 83 + 84 + // This sets the cookie to keep the sidebar state. 85 + document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}` 86 + }, 87 + [setOpenProp, open] 88 + ) 89 + 90 + // Helper to toggle the sidebar. 91 + const toggleSidebar = React.useCallback(() => { 92 + return isMobile 93 + ? setOpenMobile((open) => !open) 94 + : setOpen((open) => !open) 95 + }, [isMobile, setOpen, setOpenMobile]) 96 + 97 + // Adds a keyboard shortcut to toggle the sidebar. 98 + React.useEffect(() => { 99 + const handleKeyDown = (event: KeyboardEvent) => { 100 + if ( 101 + event.key === SIDEBAR_KEYBOARD_SHORTCUT && 102 + (event.metaKey || event.ctrlKey) 103 + ) { 104 + event.preventDefault() 105 + toggleSidebar() 106 + } 107 + } 108 + 109 + window.addEventListener("keydown", handleKeyDown) 110 + return () => window.removeEventListener("keydown", handleKeyDown) 111 + }, [toggleSidebar]) 112 + 113 + // We add a state so that we can do data-state="expanded" or "collapsed". 114 + // This makes it easier to style the sidebar with Tailwind classes. 115 + const state = open ? "expanded" : "collapsed" 116 + 117 + const contextValue = React.useMemo<SidebarContext>( 118 + () => ({ 119 + state, 120 + open, 121 + setOpen, 122 + isMobile, 123 + openMobile, 124 + setOpenMobile, 125 + toggleSidebar, 126 + }), 127 + [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar] 128 + ) 129 + 130 + return ( 131 + <SidebarContext.Provider value={contextValue}> 132 + <TooltipProvider delayDuration={0}> 133 + <div 134 + style={ 135 + { 136 + "--sidebar-width": SIDEBAR_WIDTH, 137 + "--sidebar-width-icon": SIDEBAR_WIDTH_ICON, 138 + ...style, 139 + } as React.CSSProperties 140 + } 141 + className={cn( 142 + "group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar", 143 + className 144 + )} 145 + ref={ref} 146 + {...props} 147 + > 148 + {children} 149 + </div> 150 + </TooltipProvider> 151 + </SidebarContext.Provider> 152 + ) 153 + } 154 + ) 155 + SidebarProvider.displayName = "SidebarProvider" 156 + 157 + const Sidebar = React.forwardRef< 158 + HTMLDivElement, 159 + React.ComponentProps<"div"> & { 160 + side?: "left" | "right" 161 + variant?: "sidebar" | "floating" | "inset" 162 + collapsible?: "offcanvas" | "icon" | "none" 163 + } 164 + >( 165 + ( 166 + { 167 + side = "left", 168 + variant = "sidebar", 169 + collapsible = "offcanvas", 170 + className, 171 + children, 172 + ...props 173 + }, 174 + ref 175 + ) => { 176 + const { isMobile, state, openMobile, setOpenMobile } = useSidebar() 177 + 178 + if (collapsible === "none") { 179 + return ( 180 + <div 181 + className={cn( 182 + "flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground", 183 + className 184 + )} 185 + ref={ref} 186 + {...props} 187 + > 188 + {children} 189 + </div> 190 + ) 191 + } 192 + 193 + if (isMobile) { 194 + return ( 195 + <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}> 196 + <SheetContent 197 + data-sidebar="sidebar" 198 + data-mobile="true" 199 + className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden" 200 + style={ 201 + { 202 + "--sidebar-width": SIDEBAR_WIDTH_MOBILE, 203 + } as React.CSSProperties 204 + } 205 + side={side} 206 + > 207 + <div className="flex h-full w-full flex-col">{children}</div> 208 + </SheetContent> 209 + </Sheet> 210 + ) 211 + } 212 + 213 + return ( 214 + <div 215 + ref={ref} 216 + className="group peer hidden md:block text-sidebar-foreground" 217 + data-state={state} 218 + data-collapsible={state === "collapsed" ? collapsible : ""} 219 + data-variant={variant} 220 + data-side={side} 221 + > 222 + {/* This is what handles the sidebar gap on desktop */} 223 + <div 224 + className={cn( 225 + "duration-200 relative h-svh w-[--sidebar-width] bg-transparent transition-[width] ease-linear", 226 + "group-data-[collapsible=offcanvas]:w-0", 227 + "group-data-[side=right]:rotate-180", 228 + variant === "floating" || variant === "inset" 229 + ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]" 230 + : "group-data-[collapsible=icon]:w-[--sidebar-width-icon]" 231 + )} 232 + /> 233 + <div 234 + className={cn( 235 + "duration-200 fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] ease-linear md:flex", 236 + side === "left" 237 + ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" 238 + : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", 239 + // Adjust the padding for floating and inset variants. 240 + variant === "floating" || variant === "inset" 241 + ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]" 242 + : "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l", 243 + className 244 + )} 245 + {...props} 246 + > 247 + <div 248 + data-sidebar="sidebar" 249 + className="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow" 250 + > 251 + {children} 252 + </div> 253 + </div> 254 + </div> 255 + ) 256 + } 257 + ) 258 + Sidebar.displayName = "Sidebar" 259 + 260 + const SidebarTrigger = React.forwardRef< 261 + React.ElementRef<typeof Button>, 262 + React.ComponentProps<typeof Button> 263 + >(({ className, onClick, ...props }, ref) => { 264 + const { toggleSidebar } = useSidebar() 265 + 266 + return ( 267 + <Button 268 + ref={ref} 269 + data-sidebar="trigger" 270 + variant="ghost" 271 + size="icon" 272 + className={cn("h-7 w-7", className)} 273 + onClick={(event) => { 274 + onClick?.(event) 275 + toggleSidebar() 276 + }} 277 + {...props} 278 + > 279 + <PanelLeft /> 280 + <span className="sr-only">Toggle Sidebar</span> 281 + </Button> 282 + ) 283 + }) 284 + SidebarTrigger.displayName = "SidebarTrigger" 285 + 286 + const SidebarRail = React.forwardRef< 287 + HTMLButtonElement, 288 + React.ComponentProps<"button"> 289 + >(({ className, ...props }, ref) => { 290 + const { toggleSidebar } = useSidebar() 291 + 292 + return ( 293 + <button 294 + ref={ref} 295 + data-sidebar="rail" 296 + aria-label="Toggle Sidebar" 297 + tabIndex={-1} 298 + onClick={toggleSidebar} 299 + title="Toggle Sidebar" 300 + className={cn( 301 + "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex", 302 + "[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize", 303 + "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", 304 + "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar", 305 + "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2", 306 + "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2", 307 + className 308 + )} 309 + {...props} 310 + /> 311 + ) 312 + }) 313 + SidebarRail.displayName = "SidebarRail" 314 + 315 + const SidebarInset = React.forwardRef< 316 + HTMLDivElement, 317 + React.ComponentProps<"main"> 318 + >(({ className, ...props }, ref) => { 319 + return ( 320 + <main 321 + ref={ref} 322 + className={cn( 323 + "relative flex min-h-svh flex-1 flex-col bg-background", 324 + "peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow", 325 + className 326 + )} 327 + {...props} 328 + /> 329 + ) 330 + }) 331 + SidebarInset.displayName = "SidebarInset" 332 + 333 + const SidebarInput = React.forwardRef< 334 + React.ElementRef<typeof Input>, 335 + React.ComponentProps<typeof Input> 336 + >(({ className, ...props }, ref) => { 337 + return ( 338 + <Input 339 + ref={ref} 340 + data-sidebar="input" 341 + className={cn( 342 + "h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring", 343 + className 344 + )} 345 + {...props} 346 + /> 347 + ) 348 + }) 349 + SidebarInput.displayName = "SidebarInput" 350 + 351 + const SidebarHeader = React.forwardRef< 352 + HTMLDivElement, 353 + React.ComponentProps<"div"> 354 + >(({ className, ...props }, ref) => { 355 + return ( 356 + <div 357 + ref={ref} 358 + data-sidebar="header" 359 + className={cn("flex flex-col gap-2 p-2", className)} 360 + {...props} 361 + /> 362 + ) 363 + }) 364 + SidebarHeader.displayName = "SidebarHeader" 365 + 366 + const SidebarFooter = React.forwardRef< 367 + HTMLDivElement, 368 + React.ComponentProps<"div"> 369 + >(({ className, ...props }, ref) => { 370 + return ( 371 + <div 372 + ref={ref} 373 + data-sidebar="footer" 374 + className={cn("flex flex-col gap-2 p-2", className)} 375 + {...props} 376 + /> 377 + ) 378 + }) 379 + SidebarFooter.displayName = "SidebarFooter" 380 + 381 + const SidebarSeparator = React.forwardRef< 382 + React.ElementRef<typeof Separator>, 383 + React.ComponentProps<typeof Separator> 384 + >(({ className, ...props }, ref) => { 385 + return ( 386 + <Separator 387 + ref={ref} 388 + data-sidebar="separator" 389 + className={cn("mx-2 w-auto bg-sidebar-border", className)} 390 + {...props} 391 + /> 392 + ) 393 + }) 394 + SidebarSeparator.displayName = "SidebarSeparator" 395 + 396 + const SidebarContent = React.forwardRef< 397 + HTMLDivElement, 398 + React.ComponentProps<"div"> 399 + >(({ className, ...props }, ref) => { 400 + return ( 401 + <div 402 + ref={ref} 403 + data-sidebar="content" 404 + className={cn( 405 + "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden", 406 + className 407 + )} 408 + {...props} 409 + /> 410 + ) 411 + }) 412 + SidebarContent.displayName = "SidebarContent" 413 + 414 + const SidebarGroup = React.forwardRef< 415 + HTMLDivElement, 416 + React.ComponentProps<"div"> 417 + >(({ className, ...props }, ref) => { 418 + return ( 419 + <div 420 + ref={ref} 421 + data-sidebar="group" 422 + className={cn("relative flex w-full min-w-0 flex-col p-2", className)} 423 + {...props} 424 + /> 425 + ) 426 + }) 427 + SidebarGroup.displayName = "SidebarGroup" 428 + 429 + const SidebarGroupLabel = React.forwardRef< 430 + HTMLDivElement, 431 + React.ComponentProps<"div"> & { asChild?: boolean } 432 + >(({ className, asChild = false, ...props }, ref) => { 433 + const Comp = asChild ? Slot : "div" 434 + 435 + return ( 436 + <Comp 437 + ref={ref} 438 + data-sidebar="group-label" 439 + className={cn( 440 + "duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", 441 + "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", 442 + className 443 + )} 444 + {...props} 445 + /> 446 + ) 447 + }) 448 + SidebarGroupLabel.displayName = "SidebarGroupLabel" 449 + 450 + const SidebarGroupAction = React.forwardRef< 451 + HTMLButtonElement, 452 + React.ComponentProps<"button"> & { asChild?: boolean } 453 + >(({ className, asChild = false, ...props }, ref) => { 454 + const Comp = asChild ? Slot : "button" 455 + 456 + return ( 457 + <Comp 458 + ref={ref} 459 + data-sidebar="group-action" 460 + className={cn( 461 + "absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", 462 + // Increases the hit area of the button on mobile. 463 + "after:absolute after:-inset-2 after:md:hidden", 464 + "group-data-[collapsible=icon]:hidden", 465 + className 466 + )} 467 + {...props} 468 + /> 469 + ) 470 + }) 471 + SidebarGroupAction.displayName = "SidebarGroupAction" 472 + 473 + const SidebarGroupContent = React.forwardRef< 474 + HTMLDivElement, 475 + React.ComponentProps<"div"> 476 + >(({ className, ...props }, ref) => ( 477 + <div 478 + ref={ref} 479 + data-sidebar="group-content" 480 + className={cn("w-full text-sm", className)} 481 + {...props} 482 + /> 483 + )) 484 + SidebarGroupContent.displayName = "SidebarGroupContent" 485 + 486 + const SidebarMenu = React.forwardRef< 487 + HTMLUListElement, 488 + React.ComponentProps<"ul"> 489 + >(({ className, ...props }, ref) => ( 490 + <ul 491 + ref={ref} 492 + data-sidebar="menu" 493 + className={cn("flex w-full min-w-0 flex-col gap-1", className)} 494 + {...props} 495 + /> 496 + )) 497 + SidebarMenu.displayName = "SidebarMenu" 498 + 499 + const SidebarMenuItem = React.forwardRef< 500 + HTMLLIElement, 501 + React.ComponentProps<"li"> 502 + >(({ className, ...props }, ref) => ( 503 + <li 504 + ref={ref} 505 + data-sidebar="menu-item" 506 + className={cn("group/menu-item relative", className)} 507 + {...props} 508 + /> 509 + )) 510 + SidebarMenuItem.displayName = "SidebarMenuItem" 511 + 512 + const sidebarMenuButtonVariants = cva( 513 + "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", 514 + { 515 + variants: { 516 + variant: { 517 + default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", 518 + outline: 519 + "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", 520 + }, 521 + size: { 522 + default: "h-8 text-sm", 523 + sm: "h-7 text-xs", 524 + lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0", 525 + }, 526 + }, 527 + defaultVariants: { 528 + variant: "default", 529 + size: "default", 530 + }, 531 + } 532 + ) 533 + 534 + const SidebarMenuButton = React.forwardRef< 535 + HTMLButtonElement, 536 + React.ComponentProps<"button"> & { 537 + asChild?: boolean 538 + isActive?: boolean 539 + tooltip?: string | React.ComponentProps<typeof TooltipContent> 540 + } & VariantProps<typeof sidebarMenuButtonVariants> 541 + >( 542 + ( 543 + { 544 + asChild = false, 545 + isActive = false, 546 + variant = "default", 547 + size = "default", 548 + tooltip, 549 + className, 550 + ...props 551 + }, 552 + ref 553 + ) => { 554 + const Comp = asChild ? Slot : "button" 555 + const { isMobile, state } = useSidebar() 556 + 557 + const button = ( 558 + <Comp 559 + ref={ref} 560 + data-sidebar="menu-button" 561 + data-size={size} 562 + data-active={isActive} 563 + className={cn(sidebarMenuButtonVariants({ variant, size }), className)} 564 + {...props} 565 + /> 566 + ) 567 + 568 + if (!tooltip) { 569 + return button 570 + } 571 + 572 + if (typeof tooltip === "string") { 573 + tooltip = { 574 + children: tooltip, 575 + } 576 + } 577 + 578 + return ( 579 + <Tooltip> 580 + <TooltipTrigger asChild>{button}</TooltipTrigger> 581 + <TooltipContent 582 + side="right" 583 + align="center" 584 + hidden={state !== "collapsed" || isMobile} 585 + {...tooltip} 586 + /> 587 + </Tooltip> 588 + ) 589 + } 590 + ) 591 + SidebarMenuButton.displayName = "SidebarMenuButton" 592 + 593 + const SidebarMenuAction = React.forwardRef< 594 + HTMLButtonElement, 595 + React.ComponentProps<"button"> & { 596 + asChild?: boolean 597 + showOnHover?: boolean 598 + } 599 + >(({ className, asChild = false, showOnHover = false, ...props }, ref) => { 600 + const Comp = asChild ? Slot : "button" 601 + 602 + return ( 603 + <Comp 604 + ref={ref} 605 + data-sidebar="menu-action" 606 + className={cn( 607 + "absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0", 608 + // Increases the hit area of the button on mobile. 609 + "after:absolute after:-inset-2 after:md:hidden", 610 + "peer-data-[size=sm]/menu-button:top-1", 611 + "peer-data-[size=default]/menu-button:top-1.5", 612 + "peer-data-[size=lg]/menu-button:top-2.5", 613 + "group-data-[collapsible=icon]:hidden", 614 + showOnHover && 615 + "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0", 616 + className 617 + )} 618 + {...props} 619 + /> 620 + ) 621 + }) 622 + SidebarMenuAction.displayName = "SidebarMenuAction" 623 + 624 + const SidebarMenuBadge = React.forwardRef< 625 + HTMLDivElement, 626 + React.ComponentProps<"div"> 627 + >(({ className, ...props }, ref) => ( 628 + <div 629 + ref={ref} 630 + data-sidebar="menu-badge" 631 + className={cn( 632 + "absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-none", 633 + "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground", 634 + "peer-data-[size=sm]/menu-button:top-1", 635 + "peer-data-[size=default]/menu-button:top-1.5", 636 + "peer-data-[size=lg]/menu-button:top-2.5", 637 + "group-data-[collapsible=icon]:hidden", 638 + className 639 + )} 640 + {...props} 641 + /> 642 + )) 643 + SidebarMenuBadge.displayName = "SidebarMenuBadge" 644 + 645 + const SidebarMenuSkeleton = React.forwardRef< 646 + HTMLDivElement, 647 + React.ComponentProps<"div"> & { 648 + showIcon?: boolean 649 + } 650 + >(({ className, showIcon = false, ...props }, ref) => { 651 + // Random width between 50 to 90%. 652 + const width = React.useMemo(() => { 653 + return `${Math.floor(Math.random() * 40) + 50}%` 654 + }, []) 655 + 656 + return ( 657 + <div 658 + ref={ref} 659 + data-sidebar="menu-skeleton" 660 + className={cn("rounded-md h-8 flex gap-2 px-2 items-center", className)} 661 + {...props} 662 + > 663 + {showIcon && ( 664 + <Skeleton 665 + className="size-4 rounded-md" 666 + data-sidebar="menu-skeleton-icon" 667 + /> 668 + )} 669 + <Skeleton 670 + className="h-4 flex-1 max-w-[--skeleton-width]" 671 + data-sidebar="menu-skeleton-text" 672 + style={ 673 + { 674 + "--skeleton-width": width, 675 + } as React.CSSProperties 676 + } 677 + /> 678 + </div> 679 + ) 680 + }) 681 + SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton" 682 + 683 + const SidebarMenuSub = React.forwardRef< 684 + HTMLUListElement, 685 + React.ComponentProps<"ul"> 686 + >(({ className, ...props }, ref) => ( 687 + <ul 688 + ref={ref} 689 + data-sidebar="menu-sub" 690 + className={cn( 691 + "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5", 692 + "group-data-[collapsible=icon]:hidden", 693 + className 694 + )} 695 + {...props} 696 + /> 697 + )) 698 + SidebarMenuSub.displayName = "SidebarMenuSub" 699 + 700 + const SidebarMenuSubItem = React.forwardRef< 701 + HTMLLIElement, 702 + React.ComponentProps<"li"> 703 + >(({ ...props }, ref) => <li ref={ref} {...props} />) 704 + SidebarMenuSubItem.displayName = "SidebarMenuSubItem" 705 + 706 + const SidebarMenuSubButton = React.forwardRef< 707 + HTMLAnchorElement, 708 + React.ComponentProps<"a"> & { 709 + asChild?: boolean 710 + size?: "sm" | "md" 711 + isActive?: boolean 712 + } 713 + >(({ asChild = false, size = "md", isActive, className, ...props }, ref) => { 714 + const Comp = asChild ? Slot : "a" 715 + 716 + return ( 717 + <Comp 718 + ref={ref} 719 + data-sidebar="menu-sub-button" 720 + data-size={size} 721 + data-active={isActive} 722 + className={cn( 723 + "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", 724 + "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", 725 + size === "sm" && "text-xs", 726 + size === "md" && "text-sm", 727 + "group-data-[collapsible=icon]:hidden", 728 + className 729 + )} 730 + {...props} 731 + /> 732 + ) 733 + }) 734 + SidebarMenuSubButton.displayName = "SidebarMenuSubButton" 735 + 736 + export { 737 + Sidebar, 738 + SidebarContent, 739 + SidebarFooter, 740 + SidebarGroup, 741 + SidebarGroupAction, 742 + SidebarGroupContent, 743 + SidebarGroupLabel, 744 + SidebarHeader, 745 + SidebarInput, 746 + SidebarInset, 747 + SidebarMenu, 748 + SidebarMenuAction, 749 + SidebarMenuBadge, 750 + SidebarMenuButton, 751 + SidebarMenuItem, 752 + SidebarMenuSkeleton, 753 + SidebarMenuSub, 754 + SidebarMenuSubButton, 755 + SidebarMenuSubItem, 756 + SidebarProvider, 757 + SidebarRail, 758 + SidebarSeparator, 759 + SidebarTrigger, 760 + useSidebar, 761 + }
+15
src/components/ui/skeleton.tsx
··· 1 + import { cn } from "@/lib/utils" 2 + 3 + function Skeleton({ 4 + className, 5 + ...props 6 + }: React.HTMLAttributes<HTMLDivElement>) { 7 + return ( 8 + <div 9 + className={cn("animate-pulse rounded-md bg-muted", className)} 10 + {...props} 11 + /> 12 + ) 13 + } 14 + 15 + export { Skeleton }
+28
src/components/ui/tooltip.tsx
··· 1 + import * as React from "react" 2 + import * as TooltipPrimitive from "@radix-ui/react-tooltip" 3 + 4 + import { cn } from "@/lib/utils" 5 + 6 + const TooltipProvider = TooltipPrimitive.Provider 7 + 8 + const Tooltip = TooltipPrimitive.Root 9 + 10 + const TooltipTrigger = TooltipPrimitive.Trigger 11 + 12 + const TooltipContent = React.forwardRef< 13 + React.ElementRef<typeof TooltipPrimitive.Content>, 14 + React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> 15 + >(({ className, sideOffset = 4, ...props }, ref) => ( 16 + <TooltipPrimitive.Content 17 + ref={ref} 18 + sideOffset={sideOffset} 19 + className={cn( 20 + "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", 21 + className 22 + )} 23 + {...props} 24 + /> 25 + )) 26 + TooltipContent.displayName = TooltipPrimitive.Content.displayName 27 + 28 + export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
+19
src/hooks/use-mobile.tsx
··· 1 + import * as React from "react" 2 + 3 + const MOBILE_BREAKPOINT = 768 4 + 5 + export function useIsMobile() { 6 + const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined) 7 + 8 + React.useEffect(() => { 9 + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) 10 + const onChange = () => { 11 + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 12 + } 13 + mql.addEventListener("change", onChange) 14 + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) 15 + return () => mql.removeEventListener("change", onChange) 16 + }, []) 17 + 18 + return !!isMobile 19 + }
+19 -2
src/index.css
··· 1 1 @tailwind base; 2 2 @tailwind components; 3 3 @tailwind utilities; 4 + 4 5 @layer base { 5 6 :root { 6 7 --background: 0 0% 100%; ··· 27 28 --chart-3: 197 37% 24%; 28 29 --chart-4: 43 74% 66%; 29 30 --chart-5: 27 87% 67%; 30 - --radius: 0.5rem 31 + --radius: 0.5rem; 32 + --sidebar-background: 0 0% 98%; 33 + --sidebar-foreground: 240 5.3% 26.1%; 34 + --sidebar-primary: 240 5.9% 10%; 35 + --sidebar-primary-foreground: 0 0% 98%; 36 + --sidebar-accent: 240 4.8% 95.9%; 37 + --sidebar-accent-foreground: 240 5.9% 10%; 38 + --sidebar-border: 220 13% 91%; 39 + --sidebar-ring: 217.2 91.2% 59.8%; 31 40 } 32 41 .dark { 33 42 --background: 0 0% 3.9%; ··· 53 62 --chart-2: 160 60% 45%; 54 63 --chart-3: 30 80% 55%; 55 64 --chart-4: 280 65% 60%; 56 - --chart-5: 340 75% 55% 65 + --chart-5: 340 75% 55%; 66 + --sidebar-background: 240 5.9% 10%; 67 + --sidebar-foreground: 240 4.8% 95.9%; 68 + --sidebar-primary: 224.3 76.3% 48%; 69 + --sidebar-primary-foreground: 0 0% 100%; 70 + --sidebar-accent: 240 3.7% 15.9%; 71 + --sidebar-accent-foreground: 240 4.8% 95.9%; 72 + --sidebar-border: 240 3.7% 15.9%; 73 + --sidebar-ring: 217.2 91.2% 59.8%; 57 74 } 58 75 } 59 76 @layer base {
+4 -4
src/main.tsx
··· 1 - import { render } from 'preact' 2 - import './index.css' 3 - import { App } from './app.tsx' 1 + import { render } from "preact"; 2 + import { App } from "./app.tsx"; 3 + import "./index.css"; 4 4 5 - render(<App />, document.getElementById('app')!) 5 + render(<App />, document.getElementById("app")!);
+24
src/providers/qtprovider.tsx
··· 1 + import { XRPC, CredentialManager } from "@atcute/client"; 2 + import React from "preact/compat"; 3 + 4 + export const QtContext = React.createContext<QtClient | null>(null); 5 + 6 + export function useXrpc(): XRPC { 7 + const client = React.useContext(QtContext); 8 + if (!client) { 9 + throw new Error("useXrpc must be used within a QtProvider"); 10 + } 11 + return client.rpc; 12 + } 13 + 14 + export class QtClient { 15 + manager: CredentialManager; 16 + rpc: XRPC; 17 + constructor(service: URL = new URL("https://bsky.social")) { 18 + this.manager = new CredentialManager({ service: service.toString() }); 19 + this.rpc = new XRPC({ handler: this.manager }); 20 + } 21 + getXrpcClient() { 22 + return this.rpc; 23 + } 24 + }
+52
src/providers/themeProvider.tsx
··· 1 + import React, { createContext, useContext, useEffect, useState } from "react"; 2 + 3 + type Theme = "dark" | "light"; 4 + 5 + type ThemeContextType = { 6 + theme: Theme; 7 + toggleTheme: () => void; 8 + }; 9 + 10 + const ThemeContext = createContext<ThemeContextType | undefined>(undefined); 11 + 12 + export function ThemeProvider({ children }: { children: React.ReactNode }) { 13 + // Initialize with undefined to prevent hydration mismatch 14 + const [theme, setTheme] = useState<Theme | undefined>(undefined); 15 + 16 + useEffect(() => { 17 + // Get initial theme on mount 18 + const root = window.document.documentElement; 19 + const initialColorValue = root.className as Theme; 20 + setTheme(initialColorValue); 21 + }, []); 22 + 23 + useEffect(() => { 24 + if (theme) { 25 + // Update class and localStorage when theme changes 26 + const root = window.document.documentElement; 27 + root.className = theme; 28 + localStorage.setItem("theme", theme); 29 + } 30 + }, [theme]); 31 + 32 + const toggleTheme = () => { 33 + setTheme((prev) => (prev === "light" ? "dark" : "light")); 34 + }; 35 + 36 + // Only render children once theme is set 37 + if (theme === undefined) return null; 38 + 39 + return ( 40 + <ThemeContext.Provider value={{ theme, toggleTheme }}> 41 + {children} 42 + </ThemeContext.Provider> 43 + ); 44 + } 45 + 46 + export function useTheme() { 47 + const context = useContext(ThemeContext); 48 + if (context === undefined) { 49 + throw new Error("useTheme must be used within a ThemeProvider"); 50 + } 51 + return context; 52 + }
+239
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 { createFileRoute } from '@tanstack/react-router' 12 + 13 + // Import Routes 14 + 15 + import { Route as rootRoute } from './routes/__root' 16 + 17 + // Create Virtual Routes 18 + 19 + const AboutLazyImport = createFileRoute('/about')() 20 + const IndexLazyImport = createFileRoute('/')() 21 + const AtHandleLazyImport = createFileRoute('/at/$handle')() 22 + const AtHandleCollectionLazyImport = createFileRoute( 23 + '/at/$handle/$collection', 24 + )() 25 + const AtHandleCollectionRkeyLazyImport = createFileRoute( 26 + '/at/$handle/$collection/$rkey', 27 + )() 28 + 29 + // Create/Update Routes 30 + 31 + const AboutLazyRoute = AboutLazyImport.update({ 32 + id: '/about', 33 + path: '/about', 34 + getParentRoute: () => rootRoute, 35 + } as any).lazy(() => import('./routes/about.lazy').then((d) => d.Route)) 36 + 37 + const IndexLazyRoute = IndexLazyImport.update({ 38 + id: '/', 39 + path: '/', 40 + getParentRoute: () => rootRoute, 41 + } as any).lazy(() => import('./routes/index.lazy').then((d) => d.Route)) 42 + 43 + const AtHandleLazyRoute = AtHandleLazyImport.update({ 44 + id: '/at/$handle', 45 + path: '/at/$handle', 46 + getParentRoute: () => rootRoute, 47 + } as any).lazy(() => import('./routes/at/$handle.lazy').then((d) => d.Route)) 48 + 49 + const AtHandleCollectionLazyRoute = AtHandleCollectionLazyImport.update({ 50 + id: '/$collection', 51 + path: '/$collection', 52 + getParentRoute: () => AtHandleLazyRoute, 53 + } as any).lazy(() => 54 + import('./routes/at/$handle.$collection.lazy').then((d) => d.Route), 55 + ) 56 + 57 + const AtHandleCollectionRkeyLazyRoute = AtHandleCollectionRkeyLazyImport.update( 58 + { 59 + id: '/$rkey', 60 + path: '/$rkey', 61 + getParentRoute: () => AtHandleCollectionLazyRoute, 62 + } as any, 63 + ).lazy(() => 64 + import('./routes/at/$handle.$collection.$rkey.lazy').then((d) => d.Route), 65 + ) 66 + 67 + // Populate the FileRoutesByPath interface 68 + 69 + declare module '@tanstack/react-router' { 70 + interface FileRoutesByPath { 71 + '/': { 72 + id: '/' 73 + path: '/' 74 + fullPath: '/' 75 + preLoaderRoute: typeof IndexLazyImport 76 + parentRoute: typeof rootRoute 77 + } 78 + '/about': { 79 + id: '/about' 80 + path: '/about' 81 + fullPath: '/about' 82 + preLoaderRoute: typeof AboutLazyImport 83 + parentRoute: typeof rootRoute 84 + } 85 + '/at/$handle': { 86 + id: '/at/$handle' 87 + path: '/at/$handle' 88 + fullPath: '/at/$handle' 89 + preLoaderRoute: typeof AtHandleLazyImport 90 + parentRoute: typeof rootRoute 91 + } 92 + '/at/$handle/$collection': { 93 + id: '/at/$handle/$collection' 94 + path: '/$collection' 95 + fullPath: '/at/$handle/$collection' 96 + preLoaderRoute: typeof AtHandleCollectionLazyImport 97 + parentRoute: typeof AtHandleLazyImport 98 + } 99 + '/at/$handle/$collection/$rkey': { 100 + id: '/at/$handle/$collection/$rkey' 101 + path: '/$rkey' 102 + fullPath: '/at/$handle/$collection/$rkey' 103 + preLoaderRoute: typeof AtHandleCollectionRkeyLazyImport 104 + parentRoute: typeof AtHandleCollectionLazyImport 105 + } 106 + } 107 + } 108 + 109 + // Create and export the route tree 110 + 111 + interface AtHandleCollectionLazyRouteChildren { 112 + AtHandleCollectionRkeyLazyRoute: typeof AtHandleCollectionRkeyLazyRoute 113 + } 114 + 115 + const AtHandleCollectionLazyRouteChildren: AtHandleCollectionLazyRouteChildren = 116 + { 117 + AtHandleCollectionRkeyLazyRoute: AtHandleCollectionRkeyLazyRoute, 118 + } 119 + 120 + const AtHandleCollectionLazyRouteWithChildren = 121 + AtHandleCollectionLazyRoute._addFileChildren( 122 + AtHandleCollectionLazyRouteChildren, 123 + ) 124 + 125 + interface AtHandleLazyRouteChildren { 126 + AtHandleCollectionLazyRoute: typeof AtHandleCollectionLazyRouteWithChildren 127 + } 128 + 129 + const AtHandleLazyRouteChildren: AtHandleLazyRouteChildren = { 130 + AtHandleCollectionLazyRoute: AtHandleCollectionLazyRouteWithChildren, 131 + } 132 + 133 + const AtHandleLazyRouteWithChildren = AtHandleLazyRoute._addFileChildren( 134 + AtHandleLazyRouteChildren, 135 + ) 136 + 137 + export interface FileRoutesByFullPath { 138 + '/': typeof IndexLazyRoute 139 + '/about': typeof AboutLazyRoute 140 + '/at/$handle': typeof AtHandleLazyRouteWithChildren 141 + '/at/$handle/$collection': typeof AtHandleCollectionLazyRouteWithChildren 142 + '/at/$handle/$collection/$rkey': typeof AtHandleCollectionRkeyLazyRoute 143 + } 144 + 145 + export interface FileRoutesByTo { 146 + '/': typeof IndexLazyRoute 147 + '/about': typeof AboutLazyRoute 148 + '/at/$handle': typeof AtHandleLazyRouteWithChildren 149 + '/at/$handle/$collection': typeof AtHandleCollectionLazyRouteWithChildren 150 + '/at/$handle/$collection/$rkey': typeof AtHandleCollectionRkeyLazyRoute 151 + } 152 + 153 + export interface FileRoutesById { 154 + __root__: typeof rootRoute 155 + '/': typeof IndexLazyRoute 156 + '/about': typeof AboutLazyRoute 157 + '/at/$handle': typeof AtHandleLazyRouteWithChildren 158 + '/at/$handle/$collection': typeof AtHandleCollectionLazyRouteWithChildren 159 + '/at/$handle/$collection/$rkey': typeof AtHandleCollectionRkeyLazyRoute 160 + } 161 + 162 + export interface FileRouteTypes { 163 + fileRoutesByFullPath: FileRoutesByFullPath 164 + fullPaths: 165 + | '/' 166 + | '/about' 167 + | '/at/$handle' 168 + | '/at/$handle/$collection' 169 + | '/at/$handle/$collection/$rkey' 170 + fileRoutesByTo: FileRoutesByTo 171 + to: 172 + | '/' 173 + | '/about' 174 + | '/at/$handle' 175 + | '/at/$handle/$collection' 176 + | '/at/$handle/$collection/$rkey' 177 + id: 178 + | '__root__' 179 + | '/' 180 + | '/about' 181 + | '/at/$handle' 182 + | '/at/$handle/$collection' 183 + | '/at/$handle/$collection/$rkey' 184 + fileRoutesById: FileRoutesById 185 + } 186 + 187 + export interface RootRouteChildren { 188 + IndexLazyRoute: typeof IndexLazyRoute 189 + AboutLazyRoute: typeof AboutLazyRoute 190 + AtHandleLazyRoute: typeof AtHandleLazyRouteWithChildren 191 + } 192 + 193 + const rootRouteChildren: RootRouteChildren = { 194 + IndexLazyRoute: IndexLazyRoute, 195 + AboutLazyRoute: AboutLazyRoute, 196 + AtHandleLazyRoute: AtHandleLazyRouteWithChildren, 197 + } 198 + 199 + export const routeTree = rootRoute 200 + ._addFileChildren(rootRouteChildren) 201 + ._addFileTypes<FileRouteTypes>() 202 + 203 + /* ROUTE_MANIFEST_START 204 + { 205 + "routes": { 206 + "__root__": { 207 + "filePath": "__root.tsx", 208 + "children": [ 209 + "/", 210 + "/about", 211 + "/at/$handle" 212 + ] 213 + }, 214 + "/": { 215 + "filePath": "index.lazy.tsx" 216 + }, 217 + "/about": { 218 + "filePath": "about.lazy.tsx" 219 + }, 220 + "/at/$handle": { 221 + "filePath": "at/$handle.lazy.tsx", 222 + "children": [ 223 + "/at/$handle/$collection" 224 + ] 225 + }, 226 + "/at/$handle/$collection": { 227 + "filePath": "at/$handle.$collection.lazy.tsx", 228 + "parent": "/at/$handle", 229 + "children": [ 230 + "/at/$handle/$collection/$rkey" 231 + ] 232 + }, 233 + "/at/$handle/$collection/$rkey": { 234 + "filePath": "at/$handle.$collection.$rkey.lazy.tsx", 235 + "parent": "/at/$handle/$collection" 236 + } 237 + } 238 + } 239 + ROUTE_MANIFEST_END */
+23
src/routes/__root.tsx
··· 1 + import { AppSidebar } from "@/components/sidebar"; 2 + import { SidebarProvider } from "@/components/ui/sidebar"; 3 + import { QtClient, QtContext } from "@/providers/qtprovider"; 4 + import { ThemeProvider } from "@/providers/themeProvider"; 5 + import { createRootRoute, Link, Outlet } from "@tanstack/react-router"; 6 + import { TanStackRouterDevtools } from "@tanstack/router-devtools"; 7 + 8 + // It's the layout component 9 + export const Route = createRootRoute({ 10 + component: () => ( 11 + <> 12 + <ThemeProvider> 13 + <QtContext.Provider value={new QtClient()}> 14 + <SidebarProvider> 15 + <AppSidebar /> 16 + <Outlet /> 17 + </SidebarProvider> 18 + </QtContext.Provider> 19 + </ThemeProvider> 20 + <TanStackRouterDevtools /> 21 + </> 22 + ), 23 + });
+9
src/routes/about.lazy.tsx
··· 1 + import { createLazyFileRoute } from "@tanstack/react-router"; 2 + 3 + export const Route = createLazyFileRoute("/about")({ 4 + component: About, 5 + }); 6 + 7 + function About() { 8 + return <div className="p-2">Hello from About!</div>; 9 + }
+9
src/routes/at/$handle.$collection.$rkey.lazy.tsx
··· 1 + import { createLazyFileRoute } from '@tanstack/react-router' 2 + 3 + export const Route = createLazyFileRoute('/at/$handle/$collection/$rkey')({ 4 + component: RouteComponent, 5 + }) 6 + 7 + function RouteComponent() { 8 + return <div>Hello "/at/$handle/$collection/$rkey"!</div> 9 + }
+9
src/routes/at/$handle.$collection.lazy.tsx
··· 1 + import { createLazyFileRoute } from '@tanstack/react-router' 2 + 3 + export const Route = createLazyFileRoute('/at/$handle/$collection')({ 4 + component: RouteComponent, 5 + }) 6 + 7 + function RouteComponent() { 8 + return <div>Hello "/at/$handle/$collection"!</div> 9 + }
+172
src/routes/at/$handle.lazy.tsx
··· 1 + import RepoIcons from "@/components/repoIcons"; 2 + import { QtClient, useXrpc } from "@/providers/qtprovider"; 3 + import "@atcute/bluesky/lexicons"; 4 + import { 5 + AppBskyActorGetProfile, 6 + ComAtprotoRepoDescribeRepo, 7 + } from "@atcute/client/lexicons"; 8 + import { 9 + IdentityMetadata, 10 + resolveFromIdentity, 11 + resolveHandle, 12 + } from "@atcute/oauth-browser-client"; 13 + import { createLazyFileRoute } from "@tanstack/react-router"; 14 + import { AtSign } from "lucide-react"; 15 + import { useState, useEffect } from "preact/compat"; 16 + 17 + interface RepoData { 18 + data?: ComAtprotoRepoDescribeRepo.Output; 19 + blueSkyData?: AppBskyActorGetProfile.Output | null; 20 + identity?: IdentityMetadata; 21 + isLoading: boolean; 22 + error: Error | null; 23 + } 24 + 25 + function useRepoData(handle: string): RepoData { 26 + const xrpc = useXrpc(); 27 + const [state, setState] = useState<RepoData>({ 28 + data: undefined, 29 + isLoading: true, 30 + error: null, 31 + }); 32 + 33 + useEffect(() => { 34 + const abortController = new AbortController(); 35 + 36 + async function fetchRepoData() { 37 + try { 38 + setState((prev) => ({ ...prev, isLoading: true })); 39 + 40 + const id = await resolveFromIdentity(handle); 41 + // we dont use the main authenticated client here 42 + const rpc = new QtClient(id.identity.pds); 43 + // get the PDS 44 + const response = await rpc 45 + .getXrpcClient() 46 + .get("com.atproto.repo.describeRepo", { 47 + params: { repo: id.identity.id }, 48 + signal: abortController.signal, 49 + }); 50 + // can we get bsky data? 51 + if (response.data.collections.includes("app.bsky.actor.profile")) { 52 + // reuse client dumbass 53 + const bskyData = await new QtClient( 54 + new URL("https://public.api.bsky.app"), 55 + ) 56 + .getXrpcClient() 57 + .get("app.bsky.actor.getProfile", { 58 + params: { actor: id.identity.id }, 59 + }); 60 + 61 + setState({ 62 + blueSkyData: bskyData.data, 63 + data: response.data, 64 + identity: id.identity, 65 + isLoading: false, 66 + error: null, 67 + }); 68 + } else { 69 + setState({ 70 + blueSkyData: null, 71 + data: response.data, 72 + identity: id.identity, 73 + isLoading: false, 74 + error: null, 75 + }); 76 + } 77 + // todo: actual errors 78 + } catch (err: any) { 79 + if (err.name === "AbortError") return; 80 + 81 + setState({ 82 + data: undefined, 83 + isLoading: false, 84 + error: err instanceof Error ? err : new Error("An error occurred"), 85 + }); 86 + } 87 + } 88 + 89 + fetchRepoData(); 90 + 91 + return () => { 92 + abortController.abort(); 93 + }; 94 + }, [handle, xrpc]); 95 + 96 + return state; 97 + } 98 + 99 + export const Route = createLazyFileRoute("/at/$handle")({ 100 + component: RouteComponent, 101 + }); 102 + 103 + function RouteComponent() { 104 + const { handle } = Route.useParams(); 105 + const { blueSkyData, data, identity, isLoading, error } = useRepoData(handle); 106 + 107 + if (isLoading) { 108 + return <div>Loading...</div>; 109 + } 110 + 111 + if (error) { 112 + return <div>Error: {error.message}</div>; 113 + } 114 + 115 + return ( 116 + <div className="flex flex-row justify-center w-full min-h-screen"> 117 + <div className="max-w-2xl w-screen p-4 md:mt-16 space-y-2"> 118 + {blueSkyData ? ( 119 + blueSkyData?.banner ? ( 120 + <div className="relative mb-12 md:mb-16"> 121 + <img 122 + src={blueSkyData?.banner} 123 + className="w-full rounded-lg scale-[108%] lg:scale-125 -z-10 border" 124 + /> 125 + <img 126 + src={blueSkyData?.avatar} 127 + className="absolute -bottom-12 md:-bottom-16 w-24 lg:w-32 aspect-square rounded-full border" 128 + /> 129 + </div> 130 + ) : ( 131 + <img src={blueSkyData?.avatar} className="w-32 h-32 rounded-full" /> 132 + ) 133 + ) : ( 134 + <div className="w-32 h-32 bg-neutral-500 rounded-full grid place-items-center"> 135 + <AtSign className="w-16 h-16" /> 136 + </div> 137 + )} 138 + <h1 className="text-2xl md:text-3xl font-bold"> 139 + {blueSkyData?.displayName}{" "} 140 + <span className="text-muted-foreground font-normal"> 141 + @{data?.handle} 142 + {data?.handleIsCorrect ? "" : " (invalid handle)"} 143 + </span> 144 + </h1> 145 + 146 + {data?.collections && ( 147 + <div className="flex flex-row pb-2"> 148 + <RepoIcons collections={data?.collections} /> 149 + </div> 150 + )} 151 + <code>{data?.did}</code> 152 + <br /> 153 + 154 + <div> 155 + PDS: {identity?.pds.hostname.includes("bsky.network") && "🍄"}{" "} 156 + {identity?.pds.hostname} 157 + </div> 158 + 159 + <div> 160 + <h2 className="text-xl font-bold">Collections</h2> 161 + <ul> 162 + {data?.collections.map((c) => ( 163 + <li key={c} className="text-blue-500"> 164 + <a href={`/at/${handle}/${c}`}>{c}</a> 165 + </li> 166 + ))} 167 + </ul> 168 + </div> 169 + </div> 170 + </div> 171 + ); 172 + }
+166
src/routes/index.lazy.tsx
··· 1 + import RepoIcons from "@/components/repoIcons"; 2 + import { QtClient, useXrpc } from "@/providers/qtprovider"; 3 + import "@atcute/bluesky/lexicons"; 4 + import { 5 + AppBskyActorGetProfile, 6 + ComAtprotoRepoDescribeRepo, 7 + } from "@atcute/client/lexicons"; 8 + import { 9 + IdentityMetadata, 10 + resolveFromIdentity, 11 + resolveHandle, 12 + } from "@atcute/oauth-browser-client"; 13 + import { createLazyFileRoute } from "@tanstack/react-router"; 14 + import { AtSign } from "lucide-react"; 15 + import { useState, useEffect } from "preact/compat"; 16 + 17 + interface RepoData { 18 + data?: ComAtprotoRepoDescribeRepo.Output; 19 + blueSkyData?: AppBskyActorGetProfile.Output | null; 20 + identity?: IdentityMetadata; 21 + isLoading: boolean; 22 + error: Error | null; 23 + } 24 + 25 + function useRepoData(handle: string): RepoData { 26 + const xrpc = useXrpc(); 27 + const [state, setState] = useState<RepoData>({ 28 + data: undefined, 29 + isLoading: true, 30 + error: null, 31 + }); 32 + 33 + useEffect(() => { 34 + const abortController = new AbortController(); 35 + 36 + async function fetchRepoData() { 37 + try { 38 + setState((prev) => ({ ...prev, isLoading: true })); 39 + 40 + const id = await resolveFromIdentity(handle); 41 + // we dont use the main authenticated client here 42 + const rpc = new QtClient(id.identity.pds); 43 + // get the PDS 44 + const response = await rpc 45 + .getXrpcClient() 46 + .get("com.atproto.repo.describeRepo", { 47 + params: { repo: id.identity.id }, 48 + signal: abortController.signal, 49 + }); 50 + // can we get bsky data? 51 + if (response.data.collections.includes("app.bsky.actor.profile")) { 52 + // reuse client dumbass 53 + const bskyData = await new QtClient( 54 + new URL("https://public.api.bsky.app"), 55 + ) 56 + .getXrpcClient() 57 + .get("app.bsky.actor.getProfile", { 58 + params: { actor: id.identity.id }, 59 + }); 60 + 61 + setState({ 62 + blueSkyData: bskyData.data, 63 + data: response.data, 64 + identity: id.identity, 65 + isLoading: false, 66 + error: null, 67 + }); 68 + } else { 69 + setState({ 70 + blueSkyData: null, 71 + data: response.data, 72 + identity: id.identity, 73 + isLoading: false, 74 + error: null, 75 + }); 76 + } 77 + // todo: actual errors 78 + } catch (err: any) { 79 + if (err.name === "AbortError") return; 80 + 81 + setState({ 82 + data: undefined, 83 + isLoading: false, 84 + error: err instanceof Error ? err : new Error("An error occurred"), 85 + }); 86 + } 87 + } 88 + 89 + fetchRepoData(); 90 + 91 + return () => { 92 + abortController.abort(); 93 + }; 94 + }, [handle, xrpc]); 95 + 96 + return state; 97 + } 98 + 99 + export const Route = createLazyFileRoute("/")({ 100 + component: Index, 101 + }); 102 + 103 + function Index() { 104 + const { blueSkyData, data, identity, isLoading, error } = 105 + useRepoData("danabra.mov"); 106 + 107 + if (isLoading) { 108 + return <div>Loading...</div>; 109 + } 110 + 111 + if (error) { 112 + return <div>Error: {error.message}</div>; 113 + } 114 + 115 + return ( 116 + <div className="flex flex-row justify-center w-full min-h-screen"> 117 + <div className="max-w-2xl w-screen p-4 md:mt-16 space-y-2"> 118 + {blueSkyData ? ( 119 + blueSkyData?.banner ? ( 120 + <div className="relative mb-12 md:mb-16"> 121 + <img 122 + src={blueSkyData?.banner} 123 + className="w-full rounded-lg scale-[108%] lg:scale-125 -z-10 border" 124 + /> 125 + <img 126 + src={blueSkyData?.avatar} 127 + className="absolute -bottom-12 md:-bottom-16 w-24 lg:w-32 aspect-square rounded-full border" 128 + /> 129 + </div> 130 + ) : ( 131 + <img src={blueSkyData?.avatar} className="w-32 h-32 rounded-full" /> 132 + ) 133 + ) : ( 134 + <div className="w-32 h-32 bg-neutral-500 rounded-full grid place-items-center"> 135 + <AtSign className="w-16 h-16" /> 136 + </div> 137 + )} 138 + <h1 className="text-2xl md:text-3xl font-bold"> 139 + {blueSkyData?.displayName}{" "} 140 + <span className="text-muted-foreground font-normal"> 141 + @{data?.handle} 142 + {data?.handleIsCorrect ? "" : " (invalid handle)"} 143 + </span> 144 + </h1> 145 + 146 + {data?.collections && ( 147 + <div className="flex flex-row pb-2"> 148 + <RepoIcons collections={data?.collections} /> 149 + </div> 150 + )} 151 + <code>{data?.did}</code> 152 + <br /> 153 + 154 + <div> 155 + PDS: {identity?.pds.hostname.includes("bsky.network") && "🍄"}{" "} 156 + {identity?.pds.hostname} 157 + </div> 158 + 159 + <div> 160 + <h2 className="text-xl font-bold">Collections</h2> 161 + <ul>{data?.collections.map((c) => <li key={c}>{c}</li>)}</ul> 162 + </div> 163 + </div> 164 + </div> 165 + ); 166 + }
+10
tailwind.config.js
··· 49 49 '3': 'hsl(var(--chart-3))', 50 50 '4': 'hsl(var(--chart-4))', 51 51 '5': 'hsl(var(--chart-5))' 52 + }, 53 + sidebar: { 54 + DEFAULT: 'hsl(var(--sidebar-background))', 55 + foreground: 'hsl(var(--sidebar-foreground))', 56 + primary: 'hsl(var(--sidebar-primary))', 57 + 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))', 58 + accent: 'hsl(var(--sidebar-accent))', 59 + 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))', 60 + border: 'hsl(var(--sidebar-border))', 61 + ring: 'hsl(var(--sidebar-ring))' 52 62 } 53 63 } 54 64 }
+1
tsconfig.json
··· 10 10 ], 11 11 "compilerOptions": { 12 12 "baseUrl": ".", 13 + "types": ["@atcute/bluesky/lexicons"], 13 14 "paths": { 14 15 "@/*": ["./src/*"] 15 16 }
+2 -1
vite.config.ts
··· 1 1 import path from "path"; 2 2 import { defineConfig } from "vite"; 3 3 import preact from "@preact/preset-vite"; 4 + import { TanStackRouterVite } from "@tanstack/router-plugin/vite"; 4 5 5 6 // https://vite.dev/config/ 6 7 export default defineConfig({ 7 - plugins: [preact()], 8 + plugins: [TanStackRouterVite(), preact()], 8 9 resolve: { 9 10 alias: { 10 11 "@": path.resolve(__dirname, "./src"),