forked from pdsls.dev/pdsls
atproto explorer

Compare changes

Choose any two refs to compare.

+1
index.html
··· 15 15 <link rel="stylesheet" href="https://rsms.me/inter/inter.css" /> 16 16 <link rel="preconnect" href="https://fonts.bunny.net" /> 17 17 <link href="https://fonts.bunny.net/css?family=roboto-mono:400" rel="stylesheet" /> 18 + <link href="https://fonts.cdnfonts.com/css/pecita" rel="stylesheet" /> 18 19 <script> 19 20 document.documentElement.classList.toggle( 20 21 "dark",
+11 -11
package.json
··· 9 9 "serve": "vite preview" 10 10 }, 11 11 "devDependencies": { 12 - "@iconify-json/lucide": "^1.2.69", 12 + "@iconify-json/lucide": "^1.2.70", 13 13 "@iconify/tailwind4": "^1.0.6", 14 14 "@tailwindcss/vite": "^4.1.14", 15 15 "prettier": "^3.6.2", 16 16 "prettier-plugin-organize-imports": "^4.3.0", 17 - "prettier-plugin-tailwindcss": "^0.6.14", 17 + "prettier-plugin-tailwindcss": "^0.7.1", 18 18 "tailwindcss": "^4.1.14", 19 19 "typescript": "^5.9.3", 20 - "vite": "^7.1.9", 20 + "vite": "^7.1.10", 21 21 "vite-plugin-solid": "^2.11.9" 22 22 }, 23 23 "dependencies": { 24 24 "@atcute/atproto": "^3.1.7", 25 - "@atcute/bluesky": "^3.2.6", 26 - "@atcute/car": "^3.1.2", 27 - "@atcute/cbor": "^2.2.6", 28 - "@atcute/cid": "^2.2.4", 29 - "@atcute/client": "^4.0.4", 25 + "@atcute/bluesky": "^3.2.7", 26 + "@atcute/car": "^3.1.3", 27 + "@atcute/cbor": "^2.2.7", 28 + "@atcute/cid": "^2.2.5", 29 + "@atcute/client": "^4.0.5", 30 30 "@atcute/crypto": "^2.2.5", 31 31 "@atcute/did-plc": "^0.1.7", 32 32 "@atcute/identity": "^1.1.1", 33 33 "@atcute/identity-resolver": "^1.1.4", 34 34 "@atcute/leaflet": "^1.0.10", 35 - "@atcute/lexicon-doc": "^1.1.2", 35 + "@atcute/lexicon-doc": "^1.1.3", 36 36 "@atcute/lexicon-resolver": "^0.1.2", 37 37 "@atcute/lexicons": "^1.2.2", 38 38 "@atcute/oauth-browser-client": "^1.0.27", 39 - "@atcute/tangled": "^1.0.8", 39 + "@atcute/tangled": "^1.0.9", 40 40 "@atcute/tid": "^1.0.3", 41 41 "@atcute/uint8array": "^1.0.5", 42 42 "@codemirror/commands": "^6.9.0", 43 43 "@codemirror/lang-json": "^6.0.2", 44 44 "@codemirror/lint": "^6.9.0", 45 45 "@codemirror/state": "^6.5.2", 46 - "@codemirror/view": "^6.38.5", 46 + "@codemirror/view": "^6.38.6", 47 47 "@fsegurai/codemirror-theme-basic-dark": "^6.2.2", 48 48 "@fsegurai/codemirror-theme-basic-light": "^6.2.2", 49 49 "@mary/exif-rm": "jsr:^0.2.2",
+330 -336
pnpm-lock.yaml
··· 12 12 specifier: ^3.1.7 13 13 version: 3.1.7 14 14 '@atcute/bluesky': 15 - specifier: ^3.2.6 16 - version: 3.2.6 15 + specifier: ^3.2.7 16 + version: 3.2.7 17 17 '@atcute/car': 18 - specifier: ^3.1.2 19 - version: 3.1.2 18 + specifier: ^3.1.3 19 + version: 3.1.3 20 20 '@atcute/cbor': 21 - specifier: ^2.2.6 22 - version: 2.2.6 21 + specifier: ^2.2.7 22 + version: 2.2.7 23 23 '@atcute/cid': 24 - specifier: ^2.2.4 25 - version: 2.2.4 24 + specifier: ^2.2.5 25 + version: 2.2.5 26 26 '@atcute/client': 27 - specifier: ^4.0.4 28 - version: 4.0.4 27 + specifier: ^4.0.5 28 + version: 4.0.5 29 29 '@atcute/crypto': 30 30 specifier: ^2.2.5 31 31 version: 2.2.5 ··· 42 42 specifier: ^1.0.10 43 43 version: 1.0.10 44 44 '@atcute/lexicon-doc': 45 - specifier: ^1.1.2 46 - version: 1.1.2 45 + specifier: ^1.1.3 46 + version: 1.1.3 47 47 '@atcute/lexicon-resolver': 48 48 specifier: ^0.1.2 49 49 version: 0.1.2(@atcute/identity-resolver@1.1.4(@atcute/identity@1.1.1))(@atcute/identity@1.1.1) ··· 54 54 specifier: ^1.0.27 55 55 version: 1.0.27 56 56 '@atcute/tangled': 57 - specifier: ^1.0.8 58 - version: 1.0.8 57 + specifier: ^1.0.9 58 + version: 1.0.9 59 59 '@atcute/tid': 60 60 specifier: ^1.0.3 61 61 version: 1.0.3 ··· 75 75 specifier: ^6.5.2 76 76 version: 6.5.2 77 77 '@codemirror/view': 78 - specifier: ^6.38.5 79 - version: 6.38.5 78 + specifier: ^6.38.6 79 + version: 6.38.6 80 80 '@fsegurai/codemirror-theme-basic-dark': 81 81 specifier: ^6.2.2 82 - version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) 82 + version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.2) 83 83 '@fsegurai/codemirror-theme-basic-light': 84 84 specifier: ^6.2.2 85 - version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1) 85 + version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.2) 86 86 '@mary/exif-rm': 87 87 specifier: jsr:^0.2.2 88 88 version: '@jsr/mary__exif-rm@0.2.2' ··· 103 103 version: 1.9.9 104 104 devDependencies: 105 105 '@iconify-json/lucide': 106 - specifier: ^1.2.69 107 - version: 1.2.69 106 + specifier: ^1.2.70 107 + version: 1.2.70 108 108 '@iconify/tailwind4': 109 109 specifier: ^1.0.6 110 110 version: 1.0.6(tailwindcss@4.1.14) 111 111 '@tailwindcss/vite': 112 112 specifier: ^4.1.14 113 - version: 4.1.14(vite@7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)) 113 + version: 4.1.14(vite@7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)) 114 114 prettier: 115 115 specifier: ^3.6.2 116 116 version: 3.6.2 ··· 118 118 specifier: ^4.3.0 119 119 version: 4.3.0(prettier@3.6.2)(typescript@5.9.3) 120 120 prettier-plugin-tailwindcss: 121 - specifier: ^0.6.14 122 - version: 0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3))(prettier@3.6.2) 121 + specifier: ^0.7.1 122 + version: 0.7.1(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3))(prettier@3.6.2) 123 123 tailwindcss: 124 124 specifier: ^4.1.14 125 125 version: 4.1.14 ··· 127 127 specifier: ^5.9.3 128 128 version: 5.9.3 129 129 vite: 130 - specifier: ^7.1.9 131 - version: 7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2) 130 + specifier: ^7.1.10 131 + version: 7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2) 132 132 vite-plugin-solid: 133 133 specifier: ^2.11.9 134 - version: 2.11.9(solid-js@1.9.9)(vite@7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)) 134 + version: 2.11.9(solid-js@1.9.9)(vite@7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)) 135 135 136 136 packages: 137 137 ··· 144 144 '@atcute/atproto@3.1.7': 145 145 resolution: {integrity: sha512-3Ym8qaVZg2vf8qw0KO1aue39z/5oik5J+UDoSes1vr8ddw40UVLA5sV4bXSKmLnhzQHiLLgoVZXe4zaKfozPoQ==} 146 146 147 - '@atcute/bluesky@3.2.6': 148 - resolution: {integrity: sha512-jUSSTW5Th1vy0bWBazVHuhGQ3Xz4cX648WvLNpYDv7WPzlFzIWm6cnQCbUToQ+uK3K4WyVuuqYtZqqI0f4wWUQ==} 147 + '@atcute/bluesky@3.2.7': 148 + resolution: {integrity: sha512-mofkZySIIp+Z+TbBD+cDWaPY6FVKNRZG8yhMFkh6uMCuiazDUAUjxr4yaFjYMVcgMN9FkwGllwQJevUH9aTSnQ==} 149 149 150 - '@atcute/car@3.1.2': 151 - resolution: {integrity: sha512-OZoi1C20Nj8aDRM/A5JeeQMLsQRm6/B7PqVI7T2tyoojiBsL+Vm42QRKxtTsJg+VFaTnWhOzQbf08GZpf2YW4Q==} 150 + '@atcute/car@3.1.3': 151 + resolution: {integrity: sha512-WJ13bAEt7TjDMVi09ubjLtvhdljbWInGm9Kfy7Y6NhrmiyC/aZYaA/zHX/bHI6xv1c/h3SQduWqxOr4ae49eqA==} 152 152 153 - '@atcute/cbor@2.2.6': 154 - resolution: {integrity: sha512-pDfsn/vPTmgeXZiZdyc5vCGCPSxWlfTUIGFMCd5SroAgoLk1v9xxF7R/8+gt1lj1OKAwCwhS0doVmtLjqqzdbA==} 153 + '@atcute/cbor@2.2.7': 154 + resolution: {integrity: sha512-/mwAF0gnokOphceZqFq3uzMGdd8sbw5y6bxF8CRutRkCCUcpjjpJc5fkLwhxyGgOveF3mZuHE6p7t/+IAqb7Aw==} 155 155 156 - '@atcute/cid@2.2.4': 157 - resolution: {integrity: sha512-6RUMyt7rp6KOSb4TWwifOZURnFrGgKqYyjVkYjiAcscZWgJpJxwoCUCdonxCfxhQtB0yJ+WlfqNXicGB+Pe94A==} 156 + '@atcute/cid@2.2.5': 157 + resolution: {integrity: sha512-7SId61nMyuxSwsDI02wEZn6/gVeha2TrAN4W0UPSdSEcwQD3R2W8VU7zvR4XGfU7A/KmBnVkwx5FTfzyizKj6g==} 158 158 159 - '@atcute/client@4.0.4': 160 - resolution: {integrity: sha512-0vkYe6HcGAef8FS4dlGMqCCPG4I4Lve1R8Amk8UEviUVofiqlv1WGoeez9CJFL8G/7vhcgVV9rPTHLJEjZ4RdQ==} 159 + '@atcute/client@4.0.5': 160 + resolution: {integrity: sha512-R8Qen8goGmEkynYGg2m6XFlVmz0GTDvQ+9w+4QqOob+XMk8/WDpF4aImev7WKEde/rV2gjcqW7zM8E6W9NShDA==} 161 161 162 162 '@atcute/crypto@2.2.5': 163 163 resolution: {integrity: sha512-9CbQ9cJ68XewsbLrgdmWQS2uDD9D0hizWFJ3OOZ16TCuARREmzKEpFgHlMxPswR3bDxjwfiXzmYUlHaTqsnxRQ==} ··· 176 176 '@atcute/leaflet@1.0.10': 177 177 resolution: {integrity: sha512-BTALobkbxNO6+IiGRcqfCmMxioqh88S0LEswHLf2rBVfNNL7/gZ0EuHhwN+sizHoY7Od6/MBUk4aOEjUPr16tw==} 178 178 179 - '@atcute/lexicon-doc@1.1.2': 180 - resolution: {integrity: sha512-Q3ONR2635MTVWT5Fi01FFcYTfciav0ATnX5ZBon7160hiDyk4n1a9dl8dQYgx+st2/IB0ZCNvOMHPCMZacdktg==} 179 + '@atcute/lexicon-doc@1.1.3': 180 + resolution: {integrity: sha512-HlQBmB4NCZPzREyVzr7lzjRxSiRHook2xfa7DgA3dk3oYZ+KnnPEtS6M1sAmAAddtUdrOrJ+0xJPQHkfElZmpQ==} 181 181 182 182 '@atcute/lexicon-resolver@0.1.2': 183 183 resolution: {integrity: sha512-qiBZspJQIcVL/9ZkWMIThsavbX4PL6F2IEEow5V/eCgKiknfq5G2X4ULb+iKvI2vwl9STCeUpctUT3/q4nXVqg==} ··· 194 194 '@atcute/oauth-browser-client@1.0.27': 195 195 resolution: {integrity: sha512-Ng1tCOTMLgFHHoIHXTtCZR1/ND62an1qxPX2kBoUzkxxd7iCP7IBYYqOiKyJYT5n1R4zS+s29hFS4t9mxXa5kQ==} 196 196 197 - '@atcute/tangled@1.0.8': 198 - resolution: {integrity: sha512-0E5GjyUa7rBN8qq/Z89ViH2FrInQqJCH/Ymhx4r75DzHHDQtAz9hVAM2J3iUx5Xp3/j9uRkAhYyPNGHmO6R/+A==} 197 + '@atcute/tangled@1.0.9': 198 + resolution: {integrity: sha512-Nr/LyEe4To5Yz+3IwL2pExKr+AJ7tPMR2+3YNMKeWHvVm+UfMTd4PkwwwAI9fKvBv2qEJ3egE1mXefGW15sqDQ==} 199 199 200 200 '@atcute/tid@1.0.3': 201 201 resolution: {integrity: sha512-wfMJx1IMdnu0CZgWl0uR4JO2s6PGT1YPhpytD4ZHzEYKKQVuqV6Eb/7vieaVo1eYNMp2FrY67FZObeR7utRl2w==} ··· 315 315 '@codemirror/state@6.5.2': 316 316 resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} 317 317 318 - '@codemirror/view@6.38.5': 319 - resolution: {integrity: sha512-SFVsNAgsAoou+BjRewMqN+m9jaztB9wCWN9RSRgePqUbq8UVlvJfku5zB2KVhLPgH/h0RLk38tvd4tGeAhygnw==} 318 + '@codemirror/view@6.38.6': 319 + resolution: {integrity: sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==} 320 320 321 321 '@esbuild/aix-ppc64@0.23.1': 322 322 resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} ··· 324 324 cpu: [ppc64] 325 325 os: [aix] 326 326 327 - '@esbuild/aix-ppc64@0.25.10': 328 - resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} 327 + '@esbuild/aix-ppc64@0.25.11': 328 + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} 329 329 engines: {node: '>=18'} 330 330 cpu: [ppc64] 331 331 os: [aix] ··· 336 336 cpu: [arm64] 337 337 os: [android] 338 338 339 - '@esbuild/android-arm64@0.25.10': 340 - resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} 339 + '@esbuild/android-arm64@0.25.11': 340 + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} 341 341 engines: {node: '>=18'} 342 342 cpu: [arm64] 343 343 os: [android] ··· 348 348 cpu: [arm] 349 349 os: [android] 350 350 351 - '@esbuild/android-arm@0.25.10': 352 - resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} 351 + '@esbuild/android-arm@0.25.11': 352 + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} 353 353 engines: {node: '>=18'} 354 354 cpu: [arm] 355 355 os: [android] ··· 360 360 cpu: [x64] 361 361 os: [android] 362 362 363 - '@esbuild/android-x64@0.25.10': 364 - resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} 363 + '@esbuild/android-x64@0.25.11': 364 + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} 365 365 engines: {node: '>=18'} 366 366 cpu: [x64] 367 367 os: [android] ··· 372 372 cpu: [arm64] 373 373 os: [darwin] 374 374 375 - '@esbuild/darwin-arm64@0.25.10': 376 - resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} 375 + '@esbuild/darwin-arm64@0.25.11': 376 + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} 377 377 engines: {node: '>=18'} 378 378 cpu: [arm64] 379 379 os: [darwin] ··· 384 384 cpu: [x64] 385 385 os: [darwin] 386 386 387 - '@esbuild/darwin-x64@0.25.10': 388 - resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} 387 + '@esbuild/darwin-x64@0.25.11': 388 + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} 389 389 engines: {node: '>=18'} 390 390 cpu: [x64] 391 391 os: [darwin] ··· 396 396 cpu: [arm64] 397 397 os: [freebsd] 398 398 399 - '@esbuild/freebsd-arm64@0.25.10': 400 - resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} 399 + '@esbuild/freebsd-arm64@0.25.11': 400 + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} 401 401 engines: {node: '>=18'} 402 402 cpu: [arm64] 403 403 os: [freebsd] ··· 408 408 cpu: [x64] 409 409 os: [freebsd] 410 410 411 - '@esbuild/freebsd-x64@0.25.10': 412 - resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} 411 + '@esbuild/freebsd-x64@0.25.11': 412 + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} 413 413 engines: {node: '>=18'} 414 414 cpu: [x64] 415 415 os: [freebsd] ··· 420 420 cpu: [arm64] 421 421 os: [linux] 422 422 423 - '@esbuild/linux-arm64@0.25.10': 424 - resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} 423 + '@esbuild/linux-arm64@0.25.11': 424 + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} 425 425 engines: {node: '>=18'} 426 426 cpu: [arm64] 427 427 os: [linux] ··· 432 432 cpu: [arm] 433 433 os: [linux] 434 434 435 - '@esbuild/linux-arm@0.25.10': 436 - resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} 435 + '@esbuild/linux-arm@0.25.11': 436 + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} 437 437 engines: {node: '>=18'} 438 438 cpu: [arm] 439 439 os: [linux] ··· 444 444 cpu: [ia32] 445 445 os: [linux] 446 446 447 - '@esbuild/linux-ia32@0.25.10': 448 - resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} 447 + '@esbuild/linux-ia32@0.25.11': 448 + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} 449 449 engines: {node: '>=18'} 450 450 cpu: [ia32] 451 451 os: [linux] ··· 456 456 cpu: [loong64] 457 457 os: [linux] 458 458 459 - '@esbuild/linux-loong64@0.25.10': 460 - resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} 459 + '@esbuild/linux-loong64@0.25.11': 460 + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} 461 461 engines: {node: '>=18'} 462 462 cpu: [loong64] 463 463 os: [linux] ··· 468 468 cpu: [mips64el] 469 469 os: [linux] 470 470 471 - '@esbuild/linux-mips64el@0.25.10': 472 - resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} 471 + '@esbuild/linux-mips64el@0.25.11': 472 + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} 473 473 engines: {node: '>=18'} 474 474 cpu: [mips64el] 475 475 os: [linux] ··· 480 480 cpu: [ppc64] 481 481 os: [linux] 482 482 483 - '@esbuild/linux-ppc64@0.25.10': 484 - resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} 483 + '@esbuild/linux-ppc64@0.25.11': 484 + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} 485 485 engines: {node: '>=18'} 486 486 cpu: [ppc64] 487 487 os: [linux] ··· 492 492 cpu: [riscv64] 493 493 os: [linux] 494 494 495 - '@esbuild/linux-riscv64@0.25.10': 496 - resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} 495 + '@esbuild/linux-riscv64@0.25.11': 496 + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} 497 497 engines: {node: '>=18'} 498 498 cpu: [riscv64] 499 499 os: [linux] ··· 504 504 cpu: [s390x] 505 505 os: [linux] 506 506 507 - '@esbuild/linux-s390x@0.25.10': 508 - resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} 507 + '@esbuild/linux-s390x@0.25.11': 508 + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} 509 509 engines: {node: '>=18'} 510 510 cpu: [s390x] 511 511 os: [linux] ··· 516 516 cpu: [x64] 517 517 os: [linux] 518 518 519 - '@esbuild/linux-x64@0.25.10': 520 - resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} 519 + '@esbuild/linux-x64@0.25.11': 520 + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} 521 521 engines: {node: '>=18'} 522 522 cpu: [x64] 523 523 os: [linux] 524 524 525 - '@esbuild/netbsd-arm64@0.25.10': 526 - resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} 525 + '@esbuild/netbsd-arm64@0.25.11': 526 + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} 527 527 engines: {node: '>=18'} 528 528 cpu: [arm64] 529 529 os: [netbsd] ··· 534 534 cpu: [x64] 535 535 os: [netbsd] 536 536 537 - '@esbuild/netbsd-x64@0.25.10': 538 - resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} 537 + '@esbuild/netbsd-x64@0.25.11': 538 + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} 539 539 engines: {node: '>=18'} 540 540 cpu: [x64] 541 541 os: [netbsd] ··· 546 546 cpu: [arm64] 547 547 os: [openbsd] 548 548 549 - '@esbuild/openbsd-arm64@0.25.10': 550 - resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} 549 + '@esbuild/openbsd-arm64@0.25.11': 550 + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} 551 551 engines: {node: '>=18'} 552 552 cpu: [arm64] 553 553 os: [openbsd] ··· 558 558 cpu: [x64] 559 559 os: [openbsd] 560 560 561 - '@esbuild/openbsd-x64@0.25.10': 562 - resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} 561 + '@esbuild/openbsd-x64@0.25.11': 562 + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} 563 563 engines: {node: '>=18'} 564 564 cpu: [x64] 565 565 os: [openbsd] 566 566 567 - '@esbuild/openharmony-arm64@0.25.10': 568 - resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} 567 + '@esbuild/openharmony-arm64@0.25.11': 568 + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} 569 569 engines: {node: '>=18'} 570 570 cpu: [arm64] 571 571 os: [openharmony] ··· 576 576 cpu: [x64] 577 577 os: [sunos] 578 578 579 - '@esbuild/sunos-x64@0.25.10': 580 - resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} 579 + '@esbuild/sunos-x64@0.25.11': 580 + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} 581 581 engines: {node: '>=18'} 582 582 cpu: [x64] 583 583 os: [sunos] ··· 588 588 cpu: [arm64] 589 589 os: [win32] 590 590 591 - '@esbuild/win32-arm64@0.25.10': 592 - resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} 591 + '@esbuild/win32-arm64@0.25.11': 592 + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} 593 593 engines: {node: '>=18'} 594 594 cpu: [arm64] 595 595 os: [win32] ··· 600 600 cpu: [ia32] 601 601 os: [win32] 602 602 603 - '@esbuild/win32-ia32@0.25.10': 604 - resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} 603 + '@esbuild/win32-ia32@0.25.11': 604 + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} 605 605 engines: {node: '>=18'} 606 606 cpu: [ia32] 607 607 os: [win32] ··· 612 612 cpu: [x64] 613 613 os: [win32] 614 614 615 - '@esbuild/win32-x64@0.25.10': 616 - resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} 615 + '@esbuild/win32-x64@0.25.11': 616 + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} 617 617 engines: {node: '>=18'} 618 618 cpu: [x64] 619 619 os: [win32] ··· 634 634 '@codemirror/view': ^6.0.0 635 635 '@lezer/highlight': ^1.0.0 636 636 637 - '@iconify-json/lucide@1.2.69': 638 - resolution: {integrity: sha512-xOhNf74m+C+nSCObfEqYi34dXk1GMfMUcOB+gfqKY/bn0RcsPLinGfgouOvrUFEreDEFbCti7sdheTf5HESLTA==} 637 + '@iconify-json/lucide@1.2.70': 638 + resolution: {integrity: sha512-56s9NdBKgshywVY1e4gOcxzAbU1J649e/jLHBJU1tyNqRs7mFLVEGwj2mmzHJ5YAZB5Tsngi4f/ocTBPlG06ZA==} 639 639 640 640 '@iconify/tailwind4@1.0.6': 641 641 resolution: {integrity: sha512-43ZXe+bC7CuE2LCgROdqbQeFYJi/J7L/k1UpSy8KDQlWVsWxPzLSWbWhlJx4uRYLOh1NRyw02YlDOgzBOFNd+A==} ··· 671 671 '@jsr/mary__exif-rm@0.2.2': 672 672 resolution: {integrity: sha512-+ZpLaC+1CyqWhH608Sqd6/yTG0pOlokn2tCXha7s1SMQ+GLKo4Nn/PskTeeP9Pt+6gNYSu6ednoSlRvXb2ZGxg==, tarball: https://npm.jsr.io/~/11/@jsr/mary__exif-rm/0.2.2.tgz} 673 673 674 - '@lezer/common@1.2.3': 675 - resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} 674 + '@lezer/common@1.3.0': 675 + resolution: {integrity: sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==} 676 676 677 - '@lezer/highlight@1.2.1': 678 - resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} 677 + '@lezer/highlight@1.2.2': 678 + resolution: {integrity: sha512-z8TQwaBXXQIvG6i2g3e9cgMwUUXu9Ib7jo2qRRggdhwKpM56Dw3PM3wmexn+EGaaOZ7az0K7sjc3/gcGW7sz7A==} 679 679 680 680 '@lezer/json@1.0.3': 681 681 resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} ··· 689 689 '@noble/secp256k1@2.3.0': 690 690 resolution: {integrity: sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw==} 691 691 692 - '@rollup/rollup-android-arm-eabi@4.52.4': 693 - resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} 692 + '@rollup/rollup-android-arm-eabi@4.52.5': 693 + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} 694 694 cpu: [arm] 695 695 os: [android] 696 696 697 - '@rollup/rollup-android-arm64@4.52.4': 698 - resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==} 697 + '@rollup/rollup-android-arm64@4.52.5': 698 + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} 699 699 cpu: [arm64] 700 700 os: [android] 701 701 702 - '@rollup/rollup-darwin-arm64@4.52.4': 703 - resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==} 702 + '@rollup/rollup-darwin-arm64@4.52.5': 703 + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} 704 704 cpu: [arm64] 705 705 os: [darwin] 706 706 707 - '@rollup/rollup-darwin-x64@4.52.4': 708 - resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==} 707 + '@rollup/rollup-darwin-x64@4.52.5': 708 + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} 709 709 cpu: [x64] 710 710 os: [darwin] 711 711 712 - '@rollup/rollup-freebsd-arm64@4.52.4': 713 - resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==} 712 + '@rollup/rollup-freebsd-arm64@4.52.5': 713 + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} 714 714 cpu: [arm64] 715 715 os: [freebsd] 716 716 717 - '@rollup/rollup-freebsd-x64@4.52.4': 718 - resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==} 717 + '@rollup/rollup-freebsd-x64@4.52.5': 718 + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} 719 719 cpu: [x64] 720 720 os: [freebsd] 721 721 722 - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': 723 - resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} 722 + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': 723 + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} 724 724 cpu: [arm] 725 725 os: [linux] 726 726 727 - '@rollup/rollup-linux-arm-musleabihf@4.52.4': 728 - resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} 727 + '@rollup/rollup-linux-arm-musleabihf@4.52.5': 728 + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} 729 729 cpu: [arm] 730 730 os: [linux] 731 731 732 - '@rollup/rollup-linux-arm64-gnu@4.52.4': 733 - resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} 732 + '@rollup/rollup-linux-arm64-gnu@4.52.5': 733 + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} 734 734 cpu: [arm64] 735 735 os: [linux] 736 736 737 - '@rollup/rollup-linux-arm64-musl@4.52.4': 738 - resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} 737 + '@rollup/rollup-linux-arm64-musl@4.52.5': 738 + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} 739 739 cpu: [arm64] 740 740 os: [linux] 741 741 742 - '@rollup/rollup-linux-loong64-gnu@4.52.4': 743 - resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} 742 + '@rollup/rollup-linux-loong64-gnu@4.52.5': 743 + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} 744 744 cpu: [loong64] 745 745 os: [linux] 746 746 747 - '@rollup/rollup-linux-ppc64-gnu@4.52.4': 748 - resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} 747 + '@rollup/rollup-linux-ppc64-gnu@4.52.5': 748 + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} 749 749 cpu: [ppc64] 750 750 os: [linux] 751 751 752 - '@rollup/rollup-linux-riscv64-gnu@4.52.4': 753 - resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} 752 + '@rollup/rollup-linux-riscv64-gnu@4.52.5': 753 + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} 754 754 cpu: [riscv64] 755 755 os: [linux] 756 756 757 - '@rollup/rollup-linux-riscv64-musl@4.52.4': 758 - resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} 757 + '@rollup/rollup-linux-riscv64-musl@4.52.5': 758 + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} 759 759 cpu: [riscv64] 760 760 os: [linux] 761 761 762 - '@rollup/rollup-linux-s390x-gnu@4.52.4': 763 - resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} 762 + '@rollup/rollup-linux-s390x-gnu@4.52.5': 763 + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} 764 764 cpu: [s390x] 765 765 os: [linux] 766 766 767 - '@rollup/rollup-linux-x64-gnu@4.52.4': 768 - resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} 767 + '@rollup/rollup-linux-x64-gnu@4.52.5': 768 + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} 769 769 cpu: [x64] 770 770 os: [linux] 771 771 772 - '@rollup/rollup-linux-x64-musl@4.52.4': 773 - resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} 772 + '@rollup/rollup-linux-x64-musl@4.52.5': 773 + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} 774 774 cpu: [x64] 775 775 os: [linux] 776 776 777 - '@rollup/rollup-openharmony-arm64@4.52.4': 778 - resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} 777 + '@rollup/rollup-openharmony-arm64@4.52.5': 778 + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} 779 779 cpu: [arm64] 780 780 os: [openharmony] 781 781 782 - '@rollup/rollup-win32-arm64-msvc@4.52.4': 783 - resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==} 782 + '@rollup/rollup-win32-arm64-msvc@4.52.5': 783 + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} 784 784 cpu: [arm64] 785 785 os: [win32] 786 786 787 - '@rollup/rollup-win32-ia32-msvc@4.52.4': 788 - resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==} 787 + '@rollup/rollup-win32-ia32-msvc@4.52.5': 788 + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} 789 789 cpu: [ia32] 790 790 os: [win32] 791 791 792 - '@rollup/rollup-win32-x64-gnu@4.52.4': 793 - resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==} 792 + '@rollup/rollup-win32-x64-gnu@4.52.5': 793 + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} 794 794 cpu: [x64] 795 795 os: [win32] 796 796 797 - '@rollup/rollup-win32-x64-msvc@4.52.4': 798 - resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==} 797 + '@rollup/rollup-win32-x64-msvc@4.52.5': 798 + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} 799 799 cpu: [x64] 800 800 os: [win32] 801 801 ··· 942 942 solid-js: 943 943 optional: true 944 944 945 - baseline-browser-mapping@2.8.15: 946 - resolution: {integrity: sha512-qsJ8/X+UypqxHXN75M7dF88jNK37dLBRW7LeUzCPz+TNs37G8cfWy9nWzS+LS//g600zrt2le9KuXt0rWfDz5Q==} 945 + baseline-browser-mapping@2.8.17: 946 + resolution: {integrity: sha512-j5zJcx6golJYTG6c05LUZ3Z8Gi+M62zRT/ycz4Xq4iCOdpcxwg7ngEYD4KA0eWZC7U17qh/Smq8bYbACJ0ipBA==} 947 947 hasBin: true 948 948 949 949 browserslist@4.26.3: ··· 951 951 engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 952 952 hasBin: true 953 953 954 - caniuse-lite@1.0.30001749: 955 - resolution: {integrity: sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==} 954 + caniuse-lite@1.0.30001751: 955 + resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} 956 956 957 957 chownr@3.0.0: 958 958 resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} ··· 989 989 resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} 990 990 engines: {node: '>=8'} 991 991 992 - electron-to-chromium@1.5.234: 993 - resolution: {integrity: sha512-RXfEp2x+VRYn8jbKfQlRImzoJU01kyDvVPBmG39eU2iuRVhuS6vQNocB8J0/8GrIMLnPzgz4eW6WiRnJkTuNWg==} 992 + electron-to-chromium@1.5.237: 993 + resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==} 994 994 995 995 enhanced-resolve@5.18.3: 996 996 resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} ··· 1005 1005 engines: {node: '>=18'} 1006 1006 hasBin: true 1007 1007 1008 - esbuild@0.25.10: 1009 - resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} 1008 + esbuild@0.25.11: 1009 + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} 1010 1010 engines: {node: '>=18'} 1011 1011 hasBin: true 1012 1012 ··· 1181 1181 engines: {node: ^18 || >=20} 1182 1182 hasBin: true 1183 1183 1184 - node-releases@2.0.23: 1185 - resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} 1184 + node-releases@2.0.25: 1185 + resolution: {integrity: sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==} 1186 1186 1187 - package-manager-detector@1.4.0: 1188 - resolution: {integrity: sha512-rRZ+pR1Usc+ND9M2NkmCvE/LYJS+8ORVV9X0KuNSY/gFsp7RBHJM/ADh9LYq4Vvfq6QkKrW6/weuh8SMEtN5gw==} 1187 + package-manager-detector@1.4.1: 1188 + resolution: {integrity: sha512-dSMiVLBEA4XaNJ0PRb4N5cV/SEP4BWrWZKBmfF+OUm2pQTiZ6DDkKeWaltwu3JRhLoy59ayIkJ00cx9K9CaYTg==} 1189 1189 1190 1190 parse5@7.3.0: 1191 1191 resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} ··· 1220 1220 vue-tsc: 1221 1221 optional: true 1222 1222 1223 - prettier-plugin-tailwindcss@0.6.14: 1224 - resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==} 1225 - engines: {node: '>=14.21.3'} 1223 + prettier-plugin-tailwindcss@0.7.1: 1224 + resolution: {integrity: sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==} 1225 + engines: {node: '>=20.19'} 1226 1226 peerDependencies: 1227 1227 '@ianvs/prettier-plugin-sort-imports': '*' 1228 1228 '@prettier/plugin-hermes': '*' ··· 1234 1234 prettier: ^3.0 1235 1235 prettier-plugin-astro: '*' 1236 1236 prettier-plugin-css-order: '*' 1237 - prettier-plugin-import-sort: '*' 1238 1237 prettier-plugin-jsdoc: '*' 1239 1238 prettier-plugin-marko: '*' 1240 1239 prettier-plugin-multiline-arrays: '*' 1241 1240 prettier-plugin-organize-attributes: '*' 1242 1241 prettier-plugin-organize-imports: '*' 1243 1242 prettier-plugin-sort-imports: '*' 1244 - prettier-plugin-style-order: '*' 1245 1243 prettier-plugin-svelte: '*' 1246 1244 peerDependenciesMeta: 1247 1245 '@ianvs/prettier-plugin-sort-imports': ··· 1262 1260 optional: true 1263 1261 prettier-plugin-css-order: 1264 1262 optional: true 1265 - prettier-plugin-import-sort: 1266 - optional: true 1267 1263 prettier-plugin-jsdoc: 1268 1264 optional: true 1269 1265 prettier-plugin-marko: ··· 1276 1272 optional: true 1277 1273 prettier-plugin-sort-imports: 1278 1274 optional: true 1279 - prettier-plugin-style-order: 1280 - optional: true 1281 1275 prettier-plugin-svelte: 1282 1276 optional: true 1283 1277 ··· 1292 1286 resolve-pkg-maps@1.0.0: 1293 1287 resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} 1294 1288 1295 - rollup@4.52.4: 1296 - resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} 1289 + rollup@4.52.5: 1290 + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} 1297 1291 engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1298 1292 hasBin: true 1299 1293 ··· 1323 1317 resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1324 1318 engines: {node: '>=0.10.0'} 1325 1319 1326 - style-mod@4.1.2: 1327 - resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} 1320 + style-mod@4.1.3: 1321 + resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} 1328 1322 1329 1323 tailwindcss@4.1.14: 1330 1324 resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==} ··· 1379 1373 '@testing-library/jest-dom': 1380 1374 optional: true 1381 1375 1382 - vite@7.1.9: 1383 - resolution: {integrity: sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==} 1376 + vite@7.1.10: 1377 + resolution: {integrity: sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==} 1384 1378 engines: {node: ^20.19.0 || >=22.12.0} 1385 1379 hasBin: true 1386 1380 peerDependencies: ··· 1445 1439 1446 1440 '@antfu/install-pkg@1.1.0': 1447 1441 dependencies: 1448 - package-manager-detector: 1.4.0 1442 + package-manager-detector: 1.4.1 1449 1443 tinyexec: 1.0.1 1450 1444 1451 1445 '@antfu/utils@8.1.1': {} ··· 1454 1448 dependencies: 1455 1449 '@atcute/lexicons': 1.2.2 1456 1450 1457 - '@atcute/bluesky@3.2.6': 1451 + '@atcute/bluesky@3.2.7': 1458 1452 dependencies: 1459 1453 '@atcute/atproto': 3.1.7 1460 1454 '@atcute/lexicons': 1.2.2 1461 1455 1462 - '@atcute/car@3.1.2': 1456 + '@atcute/car@3.1.3': 1463 1457 dependencies: 1464 - '@atcute/cbor': 2.2.6 1465 - '@atcute/cid': 2.2.4 1458 + '@atcute/cbor': 2.2.7 1459 + '@atcute/cid': 2.2.5 1466 1460 '@atcute/uint8array': 1.0.5 1467 1461 '@atcute/varint': 1.0.3 1468 1462 yocto-queue: 1.2.1 1469 1463 1470 - '@atcute/cbor@2.2.6': 1464 + '@atcute/cbor@2.2.7': 1471 1465 dependencies: 1472 - '@atcute/cid': 2.2.4 1466 + '@atcute/cid': 2.2.5 1473 1467 '@atcute/multibase': 1.1.6 1474 1468 '@atcute/uint8array': 1.0.5 1475 1469 1476 - '@atcute/cid@2.2.4': 1470 + '@atcute/cid@2.2.5': 1477 1471 dependencies: 1478 1472 '@atcute/multibase': 1.1.6 1479 1473 '@atcute/uint8array': 1.0.5 1480 1474 1481 - '@atcute/client@4.0.4': 1475 + '@atcute/client@4.0.5': 1482 1476 dependencies: 1483 1477 '@atcute/identity': 1.1.1 1484 1478 '@atcute/lexicons': 1.2.2 ··· 1491 1485 1492 1486 '@atcute/did-plc@0.1.7': 1493 1487 dependencies: 1494 - '@atcute/cbor': 2.2.6 1495 - '@atcute/cid': 2.2.4 1488 + '@atcute/cbor': 2.2.7 1489 + '@atcute/cid': 2.2.5 1496 1490 '@atcute/crypto': 2.2.5 1497 1491 '@atcute/identity': 1.1.1 1498 1492 '@atcute/lexicons': 1.2.2 ··· 1517 1511 '@atcute/atproto': 3.1.7 1518 1512 '@atcute/lexicons': 1.2.2 1519 1513 1520 - '@atcute/lexicon-doc@1.1.2': 1514 + '@atcute/lexicon-doc@1.1.3': 1521 1515 dependencies: 1522 1516 '@badrap/valita': 0.4.6 1523 1517 1524 1518 '@atcute/lexicon-resolver@0.1.2(@atcute/identity-resolver@1.1.4(@atcute/identity@1.1.1))(@atcute/identity@1.1.1)': 1525 1519 dependencies: 1526 - '@atcute/car': 3.1.2 1527 - '@atcute/cbor': 2.2.6 1528 - '@atcute/cid': 2.2.4 1520 + '@atcute/car': 3.1.3 1521 + '@atcute/cbor': 2.2.7 1522 + '@atcute/cid': 2.2.5 1529 1523 '@atcute/crypto': 2.2.5 1530 1524 '@atcute/identity': 1.1.1 1531 1525 '@atcute/identity-resolver': 1.1.4(@atcute/identity@1.1.1) 1532 - '@atcute/lexicon-doc': 1.1.2 1526 + '@atcute/lexicon-doc': 1.1.3 1533 1527 '@atcute/lexicons': 1.2.2 1534 1528 '@atcute/uint8array': 1.0.5 1535 1529 '@atcute/util-fetch': 1.0.3 ··· 1546 1540 1547 1541 '@atcute/oauth-browser-client@1.0.27': 1548 1542 dependencies: 1549 - '@atcute/client': 4.0.4 1543 + '@atcute/client': 4.0.5 1550 1544 '@atcute/identity': 1.1.1 1551 1545 '@atcute/lexicons': 1.2.2 1552 1546 '@atcute/multibase': 1.1.6 1553 1547 '@atcute/uint8array': 1.0.5 1554 1548 nanoid: 5.1.6 1555 1549 1556 - '@atcute/tangled@1.0.8': 1550 + '@atcute/tangled@1.0.9': 1557 1551 dependencies: 1558 1552 '@atcute/atproto': 3.1.7 1559 1553 '@atcute/lexicons': 1.2.2 ··· 1685 1679 dependencies: 1686 1680 '@codemirror/language': 6.11.3 1687 1681 '@codemirror/state': 6.5.2 1688 - '@codemirror/view': 6.38.5 1689 - '@lezer/common': 1.2.3 1682 + '@codemirror/view': 6.38.6 1683 + '@lezer/common': 1.3.0 1690 1684 1691 1685 '@codemirror/commands@6.9.0': 1692 1686 dependencies: 1693 1687 '@codemirror/language': 6.11.3 1694 1688 '@codemirror/state': 6.5.2 1695 - '@codemirror/view': 6.38.5 1696 - '@lezer/common': 1.2.3 1689 + '@codemirror/view': 6.38.6 1690 + '@lezer/common': 1.3.0 1697 1691 1698 1692 '@codemirror/lang-json@6.0.2': 1699 1693 dependencies: ··· 1703 1697 '@codemirror/language@6.11.3': 1704 1698 dependencies: 1705 1699 '@codemirror/state': 6.5.2 1706 - '@codemirror/view': 6.38.5 1707 - '@lezer/common': 1.2.3 1708 - '@lezer/highlight': 1.2.1 1700 + '@codemirror/view': 6.38.6 1701 + '@lezer/common': 1.3.0 1702 + '@lezer/highlight': 1.2.2 1709 1703 '@lezer/lr': 1.4.2 1710 - style-mod: 4.1.2 1704 + style-mod: 4.1.3 1711 1705 1712 1706 '@codemirror/lint@6.9.0': 1713 1707 dependencies: 1714 1708 '@codemirror/state': 6.5.2 1715 - '@codemirror/view': 6.38.5 1709 + '@codemirror/view': 6.38.6 1716 1710 crelt: 1.0.6 1717 1711 1718 1712 '@codemirror/search@6.5.11': 1719 1713 dependencies: 1720 1714 '@codemirror/state': 6.5.2 1721 - '@codemirror/view': 6.38.5 1715 + '@codemirror/view': 6.38.6 1722 1716 crelt: 1.0.6 1723 1717 1724 1718 '@codemirror/state@6.5.2': 1725 1719 dependencies: 1726 1720 '@marijn/find-cluster-break': 1.0.2 1727 1721 1728 - '@codemirror/view@6.38.5': 1722 + '@codemirror/view@6.38.6': 1729 1723 dependencies: 1730 1724 '@codemirror/state': 6.5.2 1731 1725 crelt: 1.0.6 1732 - style-mod: 4.1.2 1726 + style-mod: 4.1.3 1733 1727 w3c-keyname: 2.2.8 1734 1728 1735 1729 '@esbuild/aix-ppc64@0.23.1': 1736 1730 optional: true 1737 1731 1738 - '@esbuild/aix-ppc64@0.25.10': 1732 + '@esbuild/aix-ppc64@0.25.11': 1739 1733 optional: true 1740 1734 1741 1735 '@esbuild/android-arm64@0.23.1': 1742 1736 optional: true 1743 1737 1744 - '@esbuild/android-arm64@0.25.10': 1738 + '@esbuild/android-arm64@0.25.11': 1745 1739 optional: true 1746 1740 1747 1741 '@esbuild/android-arm@0.23.1': 1748 1742 optional: true 1749 1743 1750 - '@esbuild/android-arm@0.25.10': 1744 + '@esbuild/android-arm@0.25.11': 1751 1745 optional: true 1752 1746 1753 1747 '@esbuild/android-x64@0.23.1': 1754 1748 optional: true 1755 1749 1756 - '@esbuild/android-x64@0.25.10': 1750 + '@esbuild/android-x64@0.25.11': 1757 1751 optional: true 1758 1752 1759 1753 '@esbuild/darwin-arm64@0.23.1': 1760 1754 optional: true 1761 1755 1762 - '@esbuild/darwin-arm64@0.25.10': 1756 + '@esbuild/darwin-arm64@0.25.11': 1763 1757 optional: true 1764 1758 1765 1759 '@esbuild/darwin-x64@0.23.1': 1766 1760 optional: true 1767 1761 1768 - '@esbuild/darwin-x64@0.25.10': 1762 + '@esbuild/darwin-x64@0.25.11': 1769 1763 optional: true 1770 1764 1771 1765 '@esbuild/freebsd-arm64@0.23.1': 1772 1766 optional: true 1773 1767 1774 - '@esbuild/freebsd-arm64@0.25.10': 1768 + '@esbuild/freebsd-arm64@0.25.11': 1775 1769 optional: true 1776 1770 1777 1771 '@esbuild/freebsd-x64@0.23.1': 1778 1772 optional: true 1779 1773 1780 - '@esbuild/freebsd-x64@0.25.10': 1774 + '@esbuild/freebsd-x64@0.25.11': 1781 1775 optional: true 1782 1776 1783 1777 '@esbuild/linux-arm64@0.23.1': 1784 1778 optional: true 1785 1779 1786 - '@esbuild/linux-arm64@0.25.10': 1780 + '@esbuild/linux-arm64@0.25.11': 1787 1781 optional: true 1788 1782 1789 1783 '@esbuild/linux-arm@0.23.1': 1790 1784 optional: true 1791 1785 1792 - '@esbuild/linux-arm@0.25.10': 1786 + '@esbuild/linux-arm@0.25.11': 1793 1787 optional: true 1794 1788 1795 1789 '@esbuild/linux-ia32@0.23.1': 1796 1790 optional: true 1797 1791 1798 - '@esbuild/linux-ia32@0.25.10': 1792 + '@esbuild/linux-ia32@0.25.11': 1799 1793 optional: true 1800 1794 1801 1795 '@esbuild/linux-loong64@0.23.1': 1802 1796 optional: true 1803 1797 1804 - '@esbuild/linux-loong64@0.25.10': 1798 + '@esbuild/linux-loong64@0.25.11': 1805 1799 optional: true 1806 1800 1807 1801 '@esbuild/linux-mips64el@0.23.1': 1808 1802 optional: true 1809 1803 1810 - '@esbuild/linux-mips64el@0.25.10': 1804 + '@esbuild/linux-mips64el@0.25.11': 1811 1805 optional: true 1812 1806 1813 1807 '@esbuild/linux-ppc64@0.23.1': 1814 1808 optional: true 1815 1809 1816 - '@esbuild/linux-ppc64@0.25.10': 1810 + '@esbuild/linux-ppc64@0.25.11': 1817 1811 optional: true 1818 1812 1819 1813 '@esbuild/linux-riscv64@0.23.1': 1820 1814 optional: true 1821 1815 1822 - '@esbuild/linux-riscv64@0.25.10': 1816 + '@esbuild/linux-riscv64@0.25.11': 1823 1817 optional: true 1824 1818 1825 1819 '@esbuild/linux-s390x@0.23.1': 1826 1820 optional: true 1827 1821 1828 - '@esbuild/linux-s390x@0.25.10': 1822 + '@esbuild/linux-s390x@0.25.11': 1829 1823 optional: true 1830 1824 1831 1825 '@esbuild/linux-x64@0.23.1': 1832 1826 optional: true 1833 1827 1834 - '@esbuild/linux-x64@0.25.10': 1828 + '@esbuild/linux-x64@0.25.11': 1835 1829 optional: true 1836 1830 1837 - '@esbuild/netbsd-arm64@0.25.10': 1831 + '@esbuild/netbsd-arm64@0.25.11': 1838 1832 optional: true 1839 1833 1840 1834 '@esbuild/netbsd-x64@0.23.1': 1841 1835 optional: true 1842 1836 1843 - '@esbuild/netbsd-x64@0.25.10': 1837 + '@esbuild/netbsd-x64@0.25.11': 1844 1838 optional: true 1845 1839 1846 1840 '@esbuild/openbsd-arm64@0.23.1': 1847 1841 optional: true 1848 1842 1849 - '@esbuild/openbsd-arm64@0.25.10': 1843 + '@esbuild/openbsd-arm64@0.25.11': 1850 1844 optional: true 1851 1845 1852 1846 '@esbuild/openbsd-x64@0.23.1': 1853 1847 optional: true 1854 1848 1855 - '@esbuild/openbsd-x64@0.25.10': 1849 + '@esbuild/openbsd-x64@0.25.11': 1856 1850 optional: true 1857 1851 1858 - '@esbuild/openharmony-arm64@0.25.10': 1852 + '@esbuild/openharmony-arm64@0.25.11': 1859 1853 optional: true 1860 1854 1861 1855 '@esbuild/sunos-x64@0.23.1': 1862 1856 optional: true 1863 1857 1864 - '@esbuild/sunos-x64@0.25.10': 1858 + '@esbuild/sunos-x64@0.25.11': 1865 1859 optional: true 1866 1860 1867 1861 '@esbuild/win32-arm64@0.23.1': 1868 1862 optional: true 1869 1863 1870 - '@esbuild/win32-arm64@0.25.10': 1864 + '@esbuild/win32-arm64@0.25.11': 1871 1865 optional: true 1872 1866 1873 1867 '@esbuild/win32-ia32@0.23.1': 1874 1868 optional: true 1875 1869 1876 - '@esbuild/win32-ia32@0.25.10': 1870 + '@esbuild/win32-ia32@0.25.11': 1877 1871 optional: true 1878 1872 1879 1873 '@esbuild/win32-x64@0.23.1': 1880 1874 optional: true 1881 1875 1882 - '@esbuild/win32-x64@0.25.10': 1876 + '@esbuild/win32-x64@0.25.11': 1883 1877 optional: true 1884 1878 1885 - '@fsegurai/codemirror-theme-basic-dark@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': 1879 + '@fsegurai/codemirror-theme-basic-dark@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.2)': 1886 1880 dependencies: 1887 1881 '@codemirror/language': 6.11.3 1888 1882 '@codemirror/state': 6.5.2 1889 - '@codemirror/view': 6.38.5 1890 - '@lezer/highlight': 1.2.1 1883 + '@codemirror/view': 6.38.6 1884 + '@lezer/highlight': 1.2.2 1891 1885 1892 - '@fsegurai/codemirror-theme-basic-light@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.5)(@lezer/highlight@1.2.1)': 1886 + '@fsegurai/codemirror-theme-basic-light@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/highlight@1.2.2)': 1893 1887 dependencies: 1894 1888 '@codemirror/language': 6.11.3 1895 1889 '@codemirror/state': 6.5.2 1896 - '@codemirror/view': 6.38.5 1897 - '@lezer/highlight': 1.2.1 1890 + '@codemirror/view': 6.38.6 1891 + '@lezer/highlight': 1.2.2 1898 1892 1899 - '@iconify-json/lucide@1.2.69': 1893 + '@iconify-json/lucide@1.2.70': 1900 1894 dependencies: 1901 1895 '@iconify/types': 2.0.0 1902 1896 ··· 1948 1942 1949 1943 '@jsr/mary__exif-rm@0.2.2': {} 1950 1944 1951 - '@lezer/common@1.2.3': {} 1945 + '@lezer/common@1.3.0': {} 1952 1946 1953 - '@lezer/highlight@1.2.1': 1947 + '@lezer/highlight@1.2.2': 1954 1948 dependencies: 1955 - '@lezer/common': 1.2.3 1949 + '@lezer/common': 1.3.0 1956 1950 1957 1951 '@lezer/json@1.0.3': 1958 1952 dependencies: 1959 - '@lezer/common': 1.2.3 1960 - '@lezer/highlight': 1.2.1 1953 + '@lezer/common': 1.3.0 1954 + '@lezer/highlight': 1.2.2 1961 1955 '@lezer/lr': 1.4.2 1962 1956 1963 1957 '@lezer/lr@1.4.2': 1964 1958 dependencies: 1965 - '@lezer/common': 1.2.3 1959 + '@lezer/common': 1.3.0 1966 1960 1967 1961 '@marijn/find-cluster-break@1.0.2': {} 1968 1962 1969 1963 '@noble/secp256k1@2.3.0': {} 1970 1964 1971 - '@rollup/rollup-android-arm-eabi@4.52.4': 1965 + '@rollup/rollup-android-arm-eabi@4.52.5': 1972 1966 optional: true 1973 1967 1974 - '@rollup/rollup-android-arm64@4.52.4': 1968 + '@rollup/rollup-android-arm64@4.52.5': 1975 1969 optional: true 1976 1970 1977 - '@rollup/rollup-darwin-arm64@4.52.4': 1971 + '@rollup/rollup-darwin-arm64@4.52.5': 1978 1972 optional: true 1979 1973 1980 - '@rollup/rollup-darwin-x64@4.52.4': 1974 + '@rollup/rollup-darwin-x64@4.52.5': 1981 1975 optional: true 1982 1976 1983 - '@rollup/rollup-freebsd-arm64@4.52.4': 1977 + '@rollup/rollup-freebsd-arm64@4.52.5': 1984 1978 optional: true 1985 1979 1986 - '@rollup/rollup-freebsd-x64@4.52.4': 1980 + '@rollup/rollup-freebsd-x64@4.52.5': 1987 1981 optional: true 1988 1982 1989 - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': 1983 + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': 1990 1984 optional: true 1991 1985 1992 - '@rollup/rollup-linux-arm-musleabihf@4.52.4': 1986 + '@rollup/rollup-linux-arm-musleabihf@4.52.5': 1993 1987 optional: true 1994 1988 1995 - '@rollup/rollup-linux-arm64-gnu@4.52.4': 1989 + '@rollup/rollup-linux-arm64-gnu@4.52.5': 1996 1990 optional: true 1997 1991 1998 - '@rollup/rollup-linux-arm64-musl@4.52.4': 1992 + '@rollup/rollup-linux-arm64-musl@4.52.5': 1999 1993 optional: true 2000 1994 2001 - '@rollup/rollup-linux-loong64-gnu@4.52.4': 1995 + '@rollup/rollup-linux-loong64-gnu@4.52.5': 2002 1996 optional: true 2003 1997 2004 - '@rollup/rollup-linux-ppc64-gnu@4.52.4': 1998 + '@rollup/rollup-linux-ppc64-gnu@4.52.5': 2005 1999 optional: true 2006 2000 2007 - '@rollup/rollup-linux-riscv64-gnu@4.52.4': 2001 + '@rollup/rollup-linux-riscv64-gnu@4.52.5': 2008 2002 optional: true 2009 2003 2010 - '@rollup/rollup-linux-riscv64-musl@4.52.4': 2004 + '@rollup/rollup-linux-riscv64-musl@4.52.5': 2011 2005 optional: true 2012 2006 2013 - '@rollup/rollup-linux-s390x-gnu@4.52.4': 2007 + '@rollup/rollup-linux-s390x-gnu@4.52.5': 2014 2008 optional: true 2015 2009 2016 - '@rollup/rollup-linux-x64-gnu@4.52.4': 2010 + '@rollup/rollup-linux-x64-gnu@4.52.5': 2017 2011 optional: true 2018 2012 2019 - '@rollup/rollup-linux-x64-musl@4.52.4': 2013 + '@rollup/rollup-linux-x64-musl@4.52.5': 2020 2014 optional: true 2021 2015 2022 - '@rollup/rollup-openharmony-arm64@4.52.4': 2016 + '@rollup/rollup-openharmony-arm64@4.52.5': 2023 2017 optional: true 2024 2018 2025 - '@rollup/rollup-win32-arm64-msvc@4.52.4': 2019 + '@rollup/rollup-win32-arm64-msvc@4.52.5': 2026 2020 optional: true 2027 2021 2028 - '@rollup/rollup-win32-ia32-msvc@4.52.4': 2022 + '@rollup/rollup-win32-ia32-msvc@4.52.5': 2029 2023 optional: true 2030 2024 2031 - '@rollup/rollup-win32-x64-gnu@4.52.4': 2025 + '@rollup/rollup-win32-x64-gnu@4.52.5': 2032 2026 optional: true 2033 2027 2034 - '@rollup/rollup-win32-x64-msvc@4.52.4': 2028 + '@rollup/rollup-win32-x64-msvc@4.52.5': 2035 2029 optional: true 2036 2030 2037 2031 '@skyware/firehose@0.5.2': 2038 2032 dependencies: 2039 - '@atcute/car': 3.1.2 2040 - '@atcute/cbor': 2.2.6 2033 + '@atcute/car': 3.1.3 2034 + '@atcute/cbor': 2.2.7 2041 2035 nanoevents: 9.1.0 2042 2036 2043 2037 '@solidjs/meta@0.29.4(solid-js@1.9.9)': ··· 2114 2108 '@tailwindcss/oxide-win32-arm64-msvc': 4.1.14 2115 2109 '@tailwindcss/oxide-win32-x64-msvc': 4.1.14 2116 2110 2117 - '@tailwindcss/vite@4.1.14(vite@7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2))': 2111 + '@tailwindcss/vite@4.1.14(vite@7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2))': 2118 2112 dependencies: 2119 2113 '@tailwindcss/node': 4.1.14 2120 2114 '@tailwindcss/oxide': 4.1.14 2121 2115 tailwindcss: 4.1.14 2122 - vite: 7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2) 2116 + vite: 7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2) 2123 2117 2124 2118 '@types/babel__core@7.20.5': 2125 2119 dependencies: ··· 2168 2162 optionalDependencies: 2169 2163 solid-js: 1.9.9 2170 2164 2171 - baseline-browser-mapping@2.8.15: {} 2165 + baseline-browser-mapping@2.8.17: {} 2172 2166 2173 2167 browserslist@4.26.3: 2174 2168 dependencies: 2175 - baseline-browser-mapping: 2.8.15 2176 - caniuse-lite: 1.0.30001749 2177 - electron-to-chromium: 1.5.234 2178 - node-releases: 2.0.23 2169 + baseline-browser-mapping: 2.8.17 2170 + caniuse-lite: 1.0.30001751 2171 + electron-to-chromium: 1.5.237 2172 + node-releases: 2.0.25 2179 2173 update-browserslist-db: 1.1.3(browserslist@4.26.3) 2180 2174 2181 - caniuse-lite@1.0.30001749: {} 2175 + caniuse-lite@1.0.30001751: {} 2182 2176 2183 2177 chownr@3.0.0: {} 2184 2178 ··· 2190 2184 '@codemirror/lint': 6.9.0 2191 2185 '@codemirror/search': 6.5.11 2192 2186 '@codemirror/state': 6.5.2 2193 - '@codemirror/view': 6.38.5 2187 + '@codemirror/view': 6.38.6 2194 2188 2195 2189 confbox@0.1.8: {} 2196 2190 ··· 2208 2202 2209 2203 detect-libc@2.1.2: {} 2210 2204 2211 - electron-to-chromium@1.5.234: {} 2205 + electron-to-chromium@1.5.237: {} 2212 2206 2213 2207 enhanced-resolve@5.18.3: 2214 2208 dependencies: ··· 2245 2239 '@esbuild/win32-x64': 0.23.1 2246 2240 optional: true 2247 2241 2248 - esbuild@0.25.10: 2242 + esbuild@0.25.11: 2249 2243 optionalDependencies: 2250 - '@esbuild/aix-ppc64': 0.25.10 2251 - '@esbuild/android-arm': 0.25.10 2252 - '@esbuild/android-arm64': 0.25.10 2253 - '@esbuild/android-x64': 0.25.10 2254 - '@esbuild/darwin-arm64': 0.25.10 2255 - '@esbuild/darwin-x64': 0.25.10 2256 - '@esbuild/freebsd-arm64': 0.25.10 2257 - '@esbuild/freebsd-x64': 0.25.10 2258 - '@esbuild/linux-arm': 0.25.10 2259 - '@esbuild/linux-arm64': 0.25.10 2260 - '@esbuild/linux-ia32': 0.25.10 2261 - '@esbuild/linux-loong64': 0.25.10 2262 - '@esbuild/linux-mips64el': 0.25.10 2263 - '@esbuild/linux-ppc64': 0.25.10 2264 - '@esbuild/linux-riscv64': 0.25.10 2265 - '@esbuild/linux-s390x': 0.25.10 2266 - '@esbuild/linux-x64': 0.25.10 2267 - '@esbuild/netbsd-arm64': 0.25.10 2268 - '@esbuild/netbsd-x64': 0.25.10 2269 - '@esbuild/openbsd-arm64': 0.25.10 2270 - '@esbuild/openbsd-x64': 0.25.10 2271 - '@esbuild/openharmony-arm64': 0.25.10 2272 - '@esbuild/sunos-x64': 0.25.10 2273 - '@esbuild/win32-arm64': 0.25.10 2274 - '@esbuild/win32-ia32': 0.25.10 2275 - '@esbuild/win32-x64': 0.25.10 2244 + '@esbuild/aix-ppc64': 0.25.11 2245 + '@esbuild/android-arm': 0.25.11 2246 + '@esbuild/android-arm64': 0.25.11 2247 + '@esbuild/android-x64': 0.25.11 2248 + '@esbuild/darwin-arm64': 0.25.11 2249 + '@esbuild/darwin-x64': 0.25.11 2250 + '@esbuild/freebsd-arm64': 0.25.11 2251 + '@esbuild/freebsd-x64': 0.25.11 2252 + '@esbuild/linux-arm': 0.25.11 2253 + '@esbuild/linux-arm64': 0.25.11 2254 + '@esbuild/linux-ia32': 0.25.11 2255 + '@esbuild/linux-loong64': 0.25.11 2256 + '@esbuild/linux-mips64el': 0.25.11 2257 + '@esbuild/linux-ppc64': 0.25.11 2258 + '@esbuild/linux-riscv64': 0.25.11 2259 + '@esbuild/linux-s390x': 0.25.11 2260 + '@esbuild/linux-x64': 0.25.11 2261 + '@esbuild/netbsd-arm64': 0.25.11 2262 + '@esbuild/netbsd-x64': 0.25.11 2263 + '@esbuild/openbsd-arm64': 0.25.11 2264 + '@esbuild/openbsd-x64': 0.25.11 2265 + '@esbuild/openharmony-arm64': 0.25.11 2266 + '@esbuild/sunos-x64': 0.25.11 2267 + '@esbuild/win32-arm64': 0.25.11 2268 + '@esbuild/win32-ia32': 0.25.11 2269 + '@esbuild/win32-x64': 0.25.11 2276 2270 2277 2271 escalade@3.2.0: {} 2278 2272 ··· 2396 2390 2397 2391 nanoid@5.1.6: {} 2398 2392 2399 - node-releases@2.0.23: {} 2393 + node-releases@2.0.25: {} 2400 2394 2401 - package-manager-detector@1.4.0: {} 2395 + package-manager-detector@1.4.1: {} 2402 2396 2403 2397 parse5@7.3.0: 2404 2398 dependencies: ··· 2433 2427 prettier: 3.6.2 2434 2428 typescript: 5.9.3 2435 2429 2436 - prettier-plugin-tailwindcss@0.6.14(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3))(prettier@3.6.2): 2430 + prettier-plugin-tailwindcss@0.7.1(prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.9.3))(prettier@3.6.2): 2437 2431 dependencies: 2438 2432 prettier: 3.6.2 2439 2433 optionalDependencies: ··· 2446 2440 resolve-pkg-maps@1.0.0: 2447 2441 optional: true 2448 2442 2449 - rollup@4.52.4: 2443 + rollup@4.52.5: 2450 2444 dependencies: 2451 2445 '@types/estree': 1.0.8 2452 2446 optionalDependencies: 2453 - '@rollup/rollup-android-arm-eabi': 4.52.4 2454 - '@rollup/rollup-android-arm64': 4.52.4 2455 - '@rollup/rollup-darwin-arm64': 4.52.4 2456 - '@rollup/rollup-darwin-x64': 4.52.4 2457 - '@rollup/rollup-freebsd-arm64': 4.52.4 2458 - '@rollup/rollup-freebsd-x64': 4.52.4 2459 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.4 2460 - '@rollup/rollup-linux-arm-musleabihf': 4.52.4 2461 - '@rollup/rollup-linux-arm64-gnu': 4.52.4 2462 - '@rollup/rollup-linux-arm64-musl': 4.52.4 2463 - '@rollup/rollup-linux-loong64-gnu': 4.52.4 2464 - '@rollup/rollup-linux-ppc64-gnu': 4.52.4 2465 - '@rollup/rollup-linux-riscv64-gnu': 4.52.4 2466 - '@rollup/rollup-linux-riscv64-musl': 4.52.4 2467 - '@rollup/rollup-linux-s390x-gnu': 4.52.4 2468 - '@rollup/rollup-linux-x64-gnu': 4.52.4 2469 - '@rollup/rollup-linux-x64-musl': 4.52.4 2470 - '@rollup/rollup-openharmony-arm64': 4.52.4 2471 - '@rollup/rollup-win32-arm64-msvc': 4.52.4 2472 - '@rollup/rollup-win32-ia32-msvc': 4.52.4 2473 - '@rollup/rollup-win32-x64-gnu': 4.52.4 2474 - '@rollup/rollup-win32-x64-msvc': 4.52.4 2447 + '@rollup/rollup-android-arm-eabi': 4.52.5 2448 + '@rollup/rollup-android-arm64': 4.52.5 2449 + '@rollup/rollup-darwin-arm64': 4.52.5 2450 + '@rollup/rollup-darwin-x64': 4.52.5 2451 + '@rollup/rollup-freebsd-arm64': 4.52.5 2452 + '@rollup/rollup-freebsd-x64': 4.52.5 2453 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 2454 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 2455 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 2456 + '@rollup/rollup-linux-arm64-musl': 4.52.5 2457 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 2458 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 2459 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 2460 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 2461 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 2462 + '@rollup/rollup-linux-x64-gnu': 4.52.5 2463 + '@rollup/rollup-linux-x64-musl': 4.52.5 2464 + '@rollup/rollup-openharmony-arm64': 4.52.5 2465 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 2466 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 2467 + '@rollup/rollup-win32-x64-gnu': 4.52.5 2468 + '@rollup/rollup-win32-x64-msvc': 4.52.5 2475 2469 fsevents: 2.3.3 2476 2470 2477 2471 semver@6.3.1: {} ··· 2499 2493 2500 2494 source-map-js@1.2.1: {} 2501 2495 2502 - style-mod@4.1.2: {} 2496 + style-mod@4.1.3: {} 2503 2497 2504 2498 tailwindcss@4.1.14: {} 2505 2499 ··· 2543 2537 2544 2538 validate-html-nesting@1.2.3: {} 2545 2539 2546 - vite-plugin-solid@2.11.9(solid-js@1.9.9)(vite@7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)): 2540 + vite-plugin-solid@2.11.9(solid-js@1.9.9)(vite@7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)): 2547 2541 dependencies: 2548 2542 '@babel/core': 7.28.4 2549 2543 '@types/babel__core': 7.20.5 ··· 2551 2545 merge-anything: 5.1.7 2552 2546 solid-js: 1.9.9 2553 2547 solid-refresh: 0.6.3(solid-js@1.9.9) 2554 - vite: 7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2) 2555 - vitefu: 1.1.1(vite@7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)) 2548 + vite: 7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2) 2549 + vitefu: 1.1.1(vite@7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)) 2556 2550 transitivePeerDependencies: 2557 2551 - supports-color 2558 2552 2559 - vite@7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2): 2553 + vite@7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2): 2560 2554 dependencies: 2561 - esbuild: 0.25.10 2555 + esbuild: 0.25.11 2562 2556 fdir: 6.5.0(picomatch@4.0.3) 2563 2557 picomatch: 4.0.3 2564 2558 postcss: 8.5.6 2565 - rollup: 4.52.4 2559 + rollup: 4.52.5 2566 2560 tinyglobby: 0.2.15 2567 2561 optionalDependencies: 2568 2562 '@types/node': 22.13.1 ··· 2571 2565 lightningcss: 1.30.1 2572 2566 tsx: 4.19.2 2573 2567 2574 - vitefu@1.1.1(vite@7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)): 2568 + vitefu@1.1.1(vite@7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2)): 2575 2569 optionalDependencies: 2576 - vite: 7.1.9(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2) 2570 + vite: 7.1.10(@types/node@22.13.1)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.19.2) 2577 2571 2578 2572 w3c-keyname@2.2.8: {} 2579 2573
+62 -29
src/components/account.tsx
··· 1 1 import { Client, CredentialManager } from "@atcute/client"; 2 2 import { Did } from "@atcute/lexicons"; 3 - import { deleteStoredSession, getSession, OAuthUserAgent } from "@atcute/oauth-browser-client"; 3 + import { 4 + createAuthorizationUrl, 5 + deleteStoredSession, 6 + getSession, 7 + OAuthUserAgent, 8 + resolveFromIdentity, 9 + } from "@atcute/oauth-browser-client"; 4 10 import { A } from "@solidjs/router"; 5 11 import { createSignal, For, onMount, Show } from "solid-js"; 6 - import { createStore } from "solid-js/store"; 12 + import { createStore, produce } from "solid-js/store"; 7 13 import { resolveDidDoc } from "../utils/api.js"; 8 - import { agent, Login, retrieveSession, setAgent } from "./login.jsx"; 14 + import { agent, Login, retrieveSession, Sessions, setAgent } from "./login.jsx"; 9 15 import { Modal } from "./modal.jsx"; 10 16 11 - const AccountManager = () => { 17 + export const [sessions, setSessions] = createStore<Sessions>(); 18 + 19 + export const AccountManager = () => { 12 20 const [openManager, setOpenManager] = createSignal(false); 13 - const [sessions, setSessions] = createStore<Record<string, string | undefined>>(); 14 21 const [avatars, setAvatars] = createStore<Record<Did, string>>(); 15 22 16 23 onMount(async () => { 17 - await retrieveSession(); 24 + try { 25 + await retrieveSession(); 26 + } catch {} 18 27 19 - const storedSessions = localStorage.getItem("atcute-oauth:sessions"); 20 - if (storedSessions) { 21 - const sessionDids = Object.keys(JSON.parse(storedSessions)) as Did[]; 22 - sessionDids.forEach((did) => setSessions(did, "")); 28 + const localSessions = localStorage.getItem("sessions"); 29 + if (localSessions) { 30 + const storedSessions: Sessions = JSON.parse(localSessions); 31 + const sessionDids = Object.keys(storedSessions) as Did[]; 23 32 sessionDids.forEach(async (did) => { 24 33 const doc = await resolveDidDoc(did); 25 34 doc.alsoKnownAs?.forEach((alias) => { 26 35 if (alias.startsWith("at://")) { 27 - setSessions(did, alias.replace("at://", "")); 36 + setSessions(did, { 37 + signedIn: storedSessions[did].signedIn, 38 + handle: alias.replace("at://", ""), 39 + }); 28 40 return; 29 41 } 30 42 }); ··· 37 49 }); 38 50 39 51 const resumeSession = async (did: Did) => { 40 - localStorage.setItem("lastSignedIn", did); 41 - retrieveSession(); 52 + try { 53 + localStorage.setItem("lastSignedIn", did); 54 + await retrieveSession(); 55 + } catch { 56 + const resolved = await resolveFromIdentity(did); 57 + const authUrl = await createAuthorizationUrl({ 58 + scope: import.meta.env.VITE_OAUTH_SCOPE, 59 + ...resolved, 60 + }); 61 + 62 + await new Promise((resolve) => setTimeout(resolve, 250)); 63 + 64 + location.assign(authUrl); 65 + } 42 66 }; 43 67 44 68 const removeSession = async (did: Did) => { ··· 50 74 } catch { 51 75 deleteStoredSession(did); 52 76 } 53 - setSessions(did, undefined); 77 + setSessions( 78 + produce((accs) => { 79 + delete accs[did]; 80 + }), 81 + ); 82 + localStorage.setItem("sessions", JSON.stringify(sessions)); 54 83 if (currentSession === did) setAgent(undefined); 55 84 }; 56 85 ··· 68 97 return ( 69 98 <> 70 99 <Modal open={openManager()} onClose={() => setOpenManager(false)}> 71 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-16 left-[50%] w-[22rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 72 - <div class="mb-2 flex items-center gap-1 font-semibold"> 73 - <span class="iconify lucide--user-round"></span> 100 + <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-16 left-[50%] w-[22rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 101 + <div class="mb-2 px-1 font-semibold"> 74 102 <span>Manage accounts</span> 75 103 </div> 76 104 <div class="mb-3 max-h-[20rem] overflow-y-auto md:max-h-[25rem]"> ··· 81 109 class="flex w-full items-center justify-between gap-1 truncate rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 82 110 onclick={() => resumeSession(did as Did)} 83 111 > 84 - <span class="flex items-center gap-2"> 85 - <img 86 - src={avatars[did as Did].replace("img/avatar/", "img/avatar_thumbnail/")} 87 - class="size-5 rounded-full" 88 - /> 89 - <span class="truncate">{sessions[did]?.length ? sessions[did] : did}</span> 112 + <span class="flex items-center gap-2 truncate"> 113 + <Show when={avatars[did as Did]}> 114 + <img 115 + src={avatars[did as Did].replace("img/avatar/", "img/avatar_thumbnail/")} 116 + class="size-6 rounded-full" 117 + /> 118 + </Show> 119 + <span class="truncate"> 120 + {sessions[did]?.handle ? sessions[did].handle : did} 121 + </span> 90 122 </span> 91 - <Show when={did === agent()?.sub}> 92 - <span class="iconify lucide--check shrink-0"></span> 123 + <Show when={did === agent()?.sub && sessions[did].signedIn}> 124 + <span class="iconify lucide--check shrink-0 text-green-500 dark:text-green-400"></span> 125 + </Show> 126 + <Show when={!sessions[did].signedIn}> 127 + <span class="iconify lucide--circle-alert shrink-0 text-red-500 dark:text-red-400"></span> 93 128 </Show> 94 129 </button> 95 130 <A ··· 97 132 onClick={() => setOpenManager(false)} 98 133 class="flex items-center rounded-lg p-2 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 99 134 > 100 - <span class="iconify lucide--book-user"></span> 135 + <span class="iconify lucide--user-round"></span> 101 136 </A> 102 137 <button 103 138 onclick={() => removeSession(did as Did)} 104 139 class="flex items-center rounded-lg p-2 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 105 140 > 106 - <span class="iconify lucide--user-round-x"></span> 141 + <span class="iconify lucide--x"></span> 107 142 </button> 108 143 </div> 109 144 )} ··· 126 161 </> 127 162 ); 128 163 }; 129 - 130 - export { AccountManager };
+103 -77
src/components/backlinks.tsx
··· 1 1 import * as TID from "@atcute/tid"; 2 2 import { createResource, createSignal, For, onMount, Show } from "solid-js"; 3 - import { getAllBacklinks, getDidBacklinks, getRecordBacklinks } from "../utils/api.js"; 3 + import { 4 + getAllBacklinks, 5 + getDidBacklinks, 6 + getRecordBacklinks, 7 + LinksWithDids, 8 + LinksWithRecords, 9 + } from "../utils/api.js"; 4 10 import { localDateFromTimestamp } from "../utils/date.js"; 5 11 import { Button } from "./button.jsx"; 6 12 7 - // the actual backlink api will probably become closer to this 13 + type Backlink = { 14 + path: string; 15 + counts: { distinct_dids: number; records: number }; 16 + }; 17 + 8 18 const linksBySource = (links: Record<string, any>) => { 9 - let out: any[] = []; 19 + let out: Record<string, Backlink[]> = {}; 10 20 Object.keys(links) 11 21 .toSorted() 12 22 .forEach((collection) => { ··· 15 25 .toSorted() 16 26 .forEach((path) => { 17 27 if (paths[path].records === 0) return; 18 - out.push({ collection, path, counts: paths[path] }); 28 + if (out[collection]) out[collection].push({ path, counts: paths[path] }); 29 + else out[collection] = [{ path, counts: paths[path] }]; 19 30 }); 20 31 }); 21 32 return out; ··· 37 48 38 49 return ( 39 50 <div class="flex w-full flex-col gap-1 text-sm wrap-anywhere"> 40 - <Show when={response()?.length === 0}> 41 - <p>No backlinks found.</p> 42 - </Show> 43 - <For each={response()}> 44 - {({ collection, path, counts }) => ( 45 - <div> 51 + <Show 52 + when={response() && Object.keys(response()!).length} 53 + fallback={<p>No backlinks found.</p>} 54 + > 55 + <For each={Object.keys(response()!)}> 56 + {(collection) => ( 46 57 <div> 47 - <div title="Collection containing linking records" class="flex items-center gap-1"> 48 - <span class="iconify lucide--book-text shrink-0"></span> 58 + <div class="flex items-center gap-1"> 59 + <span 60 + title="Collection containing linking records" 61 + class="iconify lucide--book-text shrink-0" 62 + ></span> 49 63 {collection} 50 64 </div> 51 - <div title="Record path where the link is found" class="flex items-center gap-1"> 52 - <span class="iconify lucide--route shrink-0"></span> 53 - {path.slice(1)} 54 - </div> 55 - </div> 56 - <div class="ml-4.5"> 57 - <p> 58 - <button 59 - class="text-blue-400 hover:underline active:underline" 60 - title="Show linking records" 61 - onclick={() => 62 - ( 63 - show()?.collection === collection && 64 - show()?.path === path && 65 - !show()?.showDids 66 - ) ? 67 - setShow(null) 68 - : setShow({ collection, path, showDids: false }) 69 - } 70 - > 71 - {counts.records} record{counts.records < 2 ? "" : "s"} 72 - </button> 73 - {" from "} 74 - <button 75 - class="text-blue-400 hover:underline active:underline" 76 - title="Show linking DIDs" 77 - onclick={() => 78 - show()?.collection === collection && show()?.path === path && show()?.showDids ? 79 - setShow(null) 80 - : setShow({ collection, path, showDids: true }) 81 - } 82 - > 83 - {counts.distinct_dids} DID 84 - {counts.distinct_dids < 2 ? "" : "s"} 85 - </button> 86 - </p> 87 - <Show when={show()?.collection === collection && show()?.path === path}> 88 - <Show when={show()?.showDids}> 89 - {/* putting this in the `dids` prop directly failed to re-render. idk how to solidjs. */} 90 - <p class="w-full font-semibold">Distinct identities</p> 91 - <BacklinkItems 92 - target={props.target} 93 - collection={collection} 94 - path={path} 95 - dids={true} 96 - /> 97 - </Show> 98 - <Show when={!show()?.showDids}> 99 - <p class="w-full font-semibold">Records</p> 100 - <BacklinkItems 101 - target={props.target} 102 - collection={collection} 103 - path={path} 104 - dids={false} 105 - /> 106 - </Show> 107 - </Show> 65 + <For each={response()![collection]}> 66 + {({ path, counts }) => ( 67 + <div class="ml-4.5"> 68 + <div class="flex items-center gap-1"> 69 + <span 70 + title="Record path where the link is found" 71 + class="iconify lucide--route shrink-0" 72 + ></span> 73 + {path.slice(1)} 74 + </div> 75 + <div class="ml-4.5"> 76 + <p> 77 + <button 78 + class="text-blue-400 hover:underline active:underline" 79 + title="Show linking records" 80 + onclick={() => 81 + ( 82 + show()?.collection === collection && 83 + show()?.path === path && 84 + !show()?.showDids 85 + ) ? 86 + setShow(null) 87 + : setShow({ collection, path, showDids: false }) 88 + } 89 + > 90 + {counts.records} record{counts.records < 2 ? "" : "s"} 91 + </button> 92 + {" from "} 93 + <button 94 + class="text-blue-400 hover:underline active:underline" 95 + title="Show linking DIDs" 96 + onclick={() => 97 + ( 98 + show()?.collection === collection && 99 + show()?.path === path && 100 + show()?.showDids 101 + ) ? 102 + setShow(null) 103 + : setShow({ collection, path, showDids: true }) 104 + } 105 + > 106 + {counts.distinct_dids} DID 107 + {counts.distinct_dids < 2 ? "" : "s"} 108 + </button> 109 + </p> 110 + <Show when={show()?.collection === collection && show()?.path === path}> 111 + <Show when={show()?.showDids}> 112 + <p class="w-full font-semibold">Distinct identities</p> 113 + <BacklinkItems 114 + target={props.target} 115 + collection={collection} 116 + path={path} 117 + dids={true} 118 + /> 119 + </Show> 120 + <Show when={!show()?.showDids}> 121 + <p class="w-full font-semibold">Records</p> 122 + <BacklinkItems 123 + target={props.target} 124 + collection={collection} 125 + path={path} 126 + dids={false} 127 + /> 128 + </Show> 129 + </Show> 130 + </div> 131 + </div> 132 + )} 133 + </For> 108 134 </div> 109 - </div> 110 - )} 111 - </For> 135 + )} 136 + </For> 137 + </Show> 112 138 </div> 113 139 ); 114 140 }; ··· 128 154 dids: boolean; 129 155 cursor?: string; 130 156 }) => { 131 - const [links, setLinks] = createSignal<any>(); 157 + const [links, setLinks] = createSignal<LinksWithDids | LinksWithRecords>(); 132 158 const [more, setMore] = createSignal<boolean>(false); 133 159 134 160 onMount(async () => { ··· 147 173 return ( 148 174 <Show when={links()} fallback={<p>Loading&hellip;</p>}> 149 175 <Show when={dids}> 150 - <For each={links().linking_dids}> 176 + <For each={(links() as LinksWithDids).linking_dids}> 151 177 {(did) => ( 152 178 <a 153 179 href={`/at://${did}`} ··· 159 185 </For> 160 186 </Show> 161 187 <Show when={!dids}> 162 - <For each={links().linking_records}> 188 + <For each={(links() as LinksWithRecords).linking_records}> 163 189 {({ did, collection, rkey }) => ( 164 190 <p class="relative flex w-full items-center gap-1 font-mono"> 165 191 <a ··· 177 203 )} 178 204 </For> 179 205 </Show> 180 - <Show when={links().cursor}> 206 + <Show when={links()?.cursor}> 181 207 <Show when={more()} fallback={<Button onClick={() => setMore(true)}>Load More</Button>}> 182 208 <BacklinkItems 183 209 target={target} 184 210 collection={collection} 185 211 path={path} 186 212 dids={dids} 187 - cursor={links().cursor} 213 + cursor={links()!.cursor} 188 214 /> 189 215 </Show> 190 216 </Show>
+1 -1
src/components/button.tsx
··· 13 13 type="button" 14 14 class={ 15 15 props.class ?? 16 - "dark:hover:bg-dark-200 dark:shadow-dark-800 dark:active:bg-dark-100 box-border flex h-7 items-center gap-1 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-xs shadow-xs select-none hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800" 16 + "dark:hover:bg-dark-200 dark:shadow-dark-700 dark:active:bg-dark-100 box-border flex h-7 items-center gap-1 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-xs shadow-xs select-none hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800" 17 17 } 18 18 classList={props.classList} 19 19 onClick={props.onClick}
+173 -71
src/components/create.tsx
··· 1 1 import { Client } from "@atcute/client"; 2 + import { Did } from "@atcute/lexicons"; 3 + import { getSession, OAuthUserAgent } from "@atcute/oauth-browser-client"; 2 4 import { remove } from "@mary/exif-rm"; 3 5 import { useNavigate, useParams } from "@solidjs/router"; 4 - import { createSignal, onCleanup, Show } from "solid-js"; 6 + import { createEffect, createSignal, For, onCleanup, onMount, Show } from "solid-js"; 5 7 import { Editor, editorView } from "../components/editor.jsx"; 6 8 import { agent } from "../components/login.jsx"; 7 9 import { setNotif } from "../layout.jsx"; 10 + import { sessions } from "./account.jsx"; 8 11 import { Button } from "./button.jsx"; 9 12 import { Modal } from "./modal.jsx"; 10 13 import { TextInput } from "./text-input.jsx"; ··· 18 21 const [openDialog, setOpenDialog] = createSignal(false); 19 22 const [notice, setNotice] = createSignal(""); 20 23 const [openUpload, setOpenUpload] = createSignal(false); 24 + const [validate, setValidate] = createSignal<boolean | undefined>(undefined); 21 25 let blobInput!: HTMLInputElement; 22 26 let formRef!: HTMLFormElement; 23 27 ··· 38 42 }; 39 43 }; 40 44 45 + const getValidateIcon = () => { 46 + return ( 47 + validate() === true ? "lucide--circle-check" 48 + : validate() === false ? "lucide--circle-x" 49 + : "lucide--circle" 50 + ); 51 + }; 52 + 53 + const getValidateLabel = () => { 54 + return ( 55 + validate() === true ? "True" 56 + : validate() === false ? "False" 57 + : "Unset" 58 + ); 59 + }; 60 + 61 + createEffect(() => { 62 + if (openDialog()) setValidate(undefined); 63 + }); 64 + 41 65 const createRecord = async (formData: FormData) => { 42 - const rpc = new Client({ handler: agent()! }); 66 + const repo = formData.get("repo")?.toString(); 67 + if (!repo) return; 68 + const rpc = new Client({ handler: new OAuthUserAgent(await getSession(repo as Did)) }); 43 69 const collection = formData.get("collection"); 44 70 const rkey = formData.get("rkey"); 45 - const validate = formData.get("validate")?.toString(); 46 71 let record: any; 47 72 try { 48 73 record = JSON.parse(editorView.state.doc.toString()); ··· 52 77 } 53 78 const res = await rpc.post("com.atproto.repo.createRecord", { 54 79 input: { 55 - repo: agent()!.sub, 80 + repo: repo as Did, 56 81 collection: collection ? collection.toString() : record.$type, 57 82 rkey: rkey?.toString().length ? rkey?.toString() : undefined, 58 83 record: record, 59 - validate: 60 - validate === "true" ? true 61 - : validate === "false" ? false 62 - : undefined, 84 + validate: validate(), 63 85 }, 64 86 }); 65 87 if (!res.ok) { ··· 71 93 navigate(`/${res.data.uri}`); 72 94 }; 73 95 74 - const editRecord = async (formData: FormData) => { 96 + const editRecord = async (recreate?: boolean) => { 75 97 const record = editorView.state.doc.toString(); 76 - const validate = 77 - formData.get("validate")?.toString() === "true" ? true 78 - : formData.get("validate")?.toString() === "false" ? false 79 - : undefined; 80 98 if (!record) return; 81 99 const rpc = new Client({ handler: agent()! }); 82 100 try { 83 101 const editedRecord = JSON.parse(record); 84 - if (formData.get("recreate")) { 102 + if (recreate) { 85 103 const res = await rpc.post("com.atproto.repo.applyWrites", { 86 104 input: { 87 105 repo: agent()!.sub, 88 - validate: validate, 106 + validate: validate(), 89 107 writes: [ 90 108 { 91 109 collection: params.collection as `${string}.${string}.${string}`, ··· 112 130 collection: params.collection as `${string}.${string}.${string}`, 113 131 rkey: params.rkey, 114 132 record: editedRecord, 115 - validate: validate, 133 + validate: validate(), 116 134 }, 117 135 }); 118 136 if (!res.ok) { ··· 128 146 } 129 147 }; 130 148 149 + const dragBox = (box: HTMLDivElement) => { 150 + let currentBox: HTMLDivElement | null = null; 151 + let isDragging = false; 152 + let offsetX: number; 153 + let offsetY: number; 154 + 155 + const handleMouseDown = (e: MouseEvent) => { 156 + if (!(e.target instanceof HTMLElement)) return; 157 + 158 + const closestDraggable = e.target.closest("[data-draggable]") as HTMLElement; 159 + if (closestDraggable && closestDraggable !== box) return; 160 + 161 + if ( 162 + ["INPUT", "SELECT", "BUTTON", "LABEL"].includes(e.target.tagName) || 163 + e.target.closest("#editor, #close") 164 + ) 165 + return; 166 + 167 + e.preventDefault(); 168 + isDragging = true; 169 + box.classList.add("cursor-grabbing"); 170 + currentBox = box; 171 + 172 + const rect = box.getBoundingClientRect(); 173 + 174 + box.style.left = rect.left + "px"; 175 + box.style.top = rect.top + "px"; 176 + 177 + box.classList.remove("-translate-x-1/2"); 178 + 179 + offsetX = e.clientX - rect.left; 180 + offsetY = e.clientY - rect.top; 181 + }; 182 + 183 + const handleMouseMove = (e: MouseEvent) => { 184 + if (isDragging && box === currentBox) { 185 + let newLeft = e.clientX - offsetX; 186 + let newTop = e.clientY - offsetY; 187 + 188 + const boxWidth = box.offsetWidth; 189 + const boxHeight = box.offsetHeight; 190 + 191 + const viewportWidth = window.innerWidth; 192 + const viewportHeight = window.innerHeight; 193 + 194 + newLeft = Math.max(0, Math.min(newLeft, viewportWidth - boxWidth)); 195 + newTop = Math.max(0, Math.min(newTop, viewportHeight - boxHeight)); 196 + 197 + box.style.left = newLeft + "px"; 198 + box.style.top = newTop + "px"; 199 + } 200 + }; 201 + 202 + const handleMouseUp = () => { 203 + if (isDragging && box === currentBox) { 204 + isDragging = false; 205 + box.classList.remove("cursor-grabbing"); 206 + currentBox = null; 207 + } 208 + }; 209 + 210 + onMount(() => { 211 + box.addEventListener("mousedown", handleMouseDown); 212 + document.addEventListener("mousemove", handleMouseMove); 213 + document.addEventListener("mouseup", handleMouseUp); 214 + }); 215 + 216 + onCleanup(() => { 217 + box.removeEventListener("mousedown", handleMouseDown); 218 + document.removeEventListener("mousemove", handleMouseMove); 219 + document.removeEventListener("mouseup", handleMouseUp); 220 + }); 221 + }; 222 + 131 223 const FileUpload = (props: { file: File }) => { 132 224 const [uploading, setUploading] = createSignal(false); 133 225 const [error, setError] = createSignal(""); ··· 175 267 }; 176 268 177 269 return ( 178 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-70 left-[50%] w-[20rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 270 + <div 271 + data-draggable 272 + class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] w-[20rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0" 273 + ref={dragBox} 274 + > 179 275 <h2 class="mb-2 font-semibold">Upload blob</h2> 180 276 <div class="flex flex-col gap-2 text-sm"> 181 277 <div class="flex flex-col gap-1"> ··· 215 311 <Show when={!uploading()}> 216 312 <Button 217 313 onClick={uploadBlob} 218 - class="dark:shadow-dark-800 flex items-center gap-1 rounded-lg bg-blue-500 px-2 py-1.5 text-xs text-white shadow-xs select-none hover:bg-blue-600 active:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-500 dark:active:bg-blue-400" 314 + class="dark:shadow-dark-700 flex items-center gap-1 rounded-lg bg-blue-500 px-2 py-1.5 text-xs text-white shadow-xs select-none hover:bg-blue-600 active:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-500 dark:active:bg-blue-400" 219 315 > 220 316 Upload 221 317 </Button> ··· 229 325 return ( 230 326 <> 231 327 <Modal open={openDialog()} onClose={() => setOpenDialog(false)} closeOnClick={false}> 232 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-16 left-[50%] w-screen -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 sm:w-xl lg:w-[48rem] dark:border-neutral-700 starting:opacity-0"> 328 + <div 329 + data-draggable 330 + class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-16 left-[50%] w-screen -translate-x-1/2 cursor-grab rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 sm:w-xl lg:w-[48rem] dark:border-neutral-700 starting:opacity-0" 331 + ref={dragBox} 332 + > 233 333 <div class="mb-2 flex w-full justify-between"> 234 - <div class="flex items-center gap-1 font-semibold"> 235 - <span 236 - class={`iconify ${props.create ? "lucide--square-pen" : "lucide--pencil"}`} 237 - ></span> 238 - <span>{props.create ? "Creating" : "Editing"} record</span> 334 + <div class="font-semibold"> 335 + <span class="select-none">{props.create ? "Creating" : "Editing"} record</span> 239 336 </div> 240 337 <button 338 + id="close" 241 339 onclick={() => setOpenDialog(false)} 242 340 class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 243 341 > ··· 245 343 </button> 246 344 </div> 247 345 <form ref={formRef} class="flex flex-col gap-y-2"> 248 - <div class="flex w-fit flex-col gap-y-1 text-sm"> 249 - <Show when={props.create}> 250 - <div class="flex items-center gap-x-2"> 251 - <label for="collection" class="min-w-20 select-none"> 252 - Collection 253 - </label> 254 - <TextInput 255 - id="collection" 256 - name="collection" 257 - placeholder="Optional (default: $type)" 258 - class="w-[15rem]" 259 - /> 260 - </div> 261 - <div class="flex items-center gap-x-2"> 262 - <label for="rkey" class="min-w-20 select-none"> 263 - Record key 264 - </label> 265 - <TextInput 266 - id="rkey" 267 - name="rkey" 268 - placeholder="Optional (default: TID)" 269 - class="w-[15rem]" 270 - /> 271 - </div> 272 - </Show> 273 - <div class="flex items-center gap-x-2"> 274 - <label for="validate" class="min-w-20 select-none"> 275 - Validate 276 - </label> 346 + <Show when={props.create}> 347 + <div class="flex flex-wrap items-center gap-1 text-sm"> 348 + <span>at://</span> 277 349 <select 278 - name="validate" 279 - id="validate" 280 - class="dark:bg-dark-100 dark:shadow-dark-800 rounded-lg border-[0.5px] border-neutral-300 bg-white px-1 py-1 shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-700 dark:focus:outline-neutral-400" 350 + class="dark:bg-dark-100 dark:shadow-dark-700 max-w-[10rem] truncate rounded-lg border-[0.5px] border-neutral-300 bg-white px-1 py-1 shadow-xs select-none focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 351 + name="repo" 352 + id="repo" 281 353 > 282 - <option value="unset">Unset</option> 283 - <option value="true">True</option> 284 - <option value="false">False</option> 354 + <For each={Object.keys(sessions)}> 355 + {(session) => ( 356 + <option value={session} selected={session === agent()?.sub}> 357 + {sessions[session].handle ?? session} 358 + </option> 359 + )} 360 + </For> 285 361 </select> 362 + <span>/</span> 363 + <TextInput 364 + id="collection" 365 + name="collection" 366 + placeholder="Collection (default: $type)" 367 + class="w-[10rem] placeholder:text-xs lg:w-[13rem]" 368 + /> 369 + <span>/</span> 370 + <TextInput 371 + id="rkey" 372 + name="rkey" 373 + placeholder="Record key (default: TID)" 374 + class="w-[10rem] placeholder:text-xs lg:w-[13rem]" 375 + /> 286 376 </div> 287 - </div> 377 + </Show> 288 378 <Editor 289 379 content={JSON.stringify( 290 380 !props.create ? props.record ··· 299 389 <div class="text-sm text-red-500 dark:text-red-400">{notice()}</div> 300 390 </Show> 301 391 <div class="flex justify-between gap-2"> 302 - <div class="dark:hover:bg-dark-200 dark:shadow-dark-800 dark:active:bg-dark-100 flex w-fit rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 text-xs shadow-xs hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800"> 392 + <button 393 + type="button" 394 + class="dark:hover:bg-dark-200 dark:shadow-dark-700 dark:active:bg-dark-100 flex w-fit rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 text-xs shadow-xs hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700 dark:bg-neutral-800" 395 + > 303 396 <input 304 397 type="file" 305 398 id="blob" ··· 313 406 <span class="iconify lucide--upload"></span> 314 407 Upload 315 408 </label> 316 - </div> 409 + </button> 317 410 <Modal 318 411 open={openUpload()} 319 412 onClose={() => setOpenUpload(false)} ··· 322 415 <FileUpload file={blobInput.files![0]} /> 323 416 </Modal> 324 417 <div class="flex items-center justify-end gap-2"> 418 + <button 419 + type="button" 420 + class="flex items-center gap-1 rounded-sm p-1 text-sm hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 421 + onClick={() => 422 + setValidate( 423 + validate() === true ? false 424 + : validate() === false ? undefined 425 + : true, 426 + ) 427 + } 428 + > 429 + <Tooltip text={getValidateLabel()}> 430 + <span class={`iconify ${getValidateIcon()}`}></span> 431 + </Tooltip> 432 + <span>Validate</span> 433 + </button> 325 434 <Show when={!props.create}> 326 - <div class="flex items-center gap-1"> 327 - <input id="recreate" name="recreate" type="checkbox" /> 328 - <label for="recreate" class="text-sm select-none"> 329 - Recreate record 330 - </label> 331 - </div> 435 + <Button onClick={() => editRecord(true)}>Recreate</Button> 332 436 </Show> 333 437 <Button 334 438 onClick={() => 335 - props.create ? 336 - createRecord(new FormData(formRef)) 337 - : editRecord(new FormData(formRef)) 439 + props.create ? createRecord(new FormData(formRef)) : editRecord() 338 440 } 339 441 > 340 442 {props.create ? "Create" : "Edit"}
+10 -3
src/components/dropdown.tsx
··· 43 43 ); 44 44 }; 45 45 46 - export const NavMenu = (props: { href: string; label: string; icon: string; newTab?: boolean }) => { 46 + export const NavMenu = (props: { 47 + href: string; 48 + label: string; 49 + icon?: string; 50 + newTab?: boolean; 51 + }) => { 47 52 const ctx = useContext(MenuContext); 48 53 49 54 return ( ··· 53 58 class="flex items-center gap-1.5 rounded-lg p-1 hover:bg-neutral-200/50 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 54 59 target={props.newTab ? "_blank" : undefined} 55 60 > 56 - <span class={"iconify shrink-0 " + props.icon}></span> 61 + <Show when={props.icon}> 62 + <span class={"iconify shrink-0 " + props.icon}></span> 63 + </Show> 57 64 <span class="whitespace-nowrap">{props.label}</span> 58 65 </A> 59 66 ); ··· 111 118 <div 112 119 ref={setMenu} 113 120 class={ 114 - "dark:bg-dark-300 dark:shadow-dark-800 absolute right-0 z-40 flex flex-col rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 shadow-md dark:border-neutral-700 " + 121 + "dark:bg-dark-300 dark:shadow-dark-700 absolute right-0 z-40 flex flex-col rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 shadow-md dark:border-neutral-700 " + 115 122 props.menuClass 116 123 } 117 124 >
+2 -1
src/components/editor.tsx
··· 57 57 return ( 58 58 <div 59 59 ref={editorDiv} 60 - class="dark:shadow-dark-800 border-[0.5px] border-neutral-300 shadow-xs dark:border-neutral-700" 60 + id="editor" 61 + class="dark:shadow-dark-700 cursor-auto border-[0.5px] border-neutral-300 shadow-xs dark:border-neutral-700" 61 62 ></div> 62 63 ); 63 64 };
+2 -2
src/components/json.tsx
··· 47 47 ["http:", "https:", "web+at:"].includes(new URL(part).protocol) && 48 48 part.split("\n").length === 1 49 49 ) ? 50 - <a class="underline" href={part} target="_blank" rel="noopener noreferrer"> 50 + <a class="underline" href={part} target="_blank" rel="noopener"> 51 51 {part} 52 52 </a> 53 53 : part} ··· 134 134 <span class="flex gap-x-1"> 135 135 <Show when={blob.mimeType.startsWith("image/") && !hide()}> 136 136 <img 137 - class="max-h-[16rem] w-fit max-w-[16rem]" 137 + class="size-fit max-h-[16rem] max-w-[16rem]" 138 138 src={`https://${pds()}/xrpc/com.atproto.sync.getBlob?did=${repo}&cid=${blob.ref.$link}`} 139 139 /> 140 140 </Show>
+44 -18
src/components/login.tsx
··· 1 + import { Client } from "@atcute/client"; 1 2 import { Did } from "@atcute/lexicons"; 2 3 import { isHandle } from "@atcute/lexicons/syntax"; 3 4 import { 4 5 configureOAuth, 5 6 createAuthorizationUrl, 6 - deleteStoredSession, 7 7 finalizeAuthorization, 8 8 getSession, 9 9 OAuthUserAgent, ··· 11 11 resolveFromService, 12 12 type Session, 13 13 } from "@atcute/oauth-browser-client"; 14 - import { createSignal } from "solid-js"; 15 - import { TextInput } from "./text-input"; 14 + import { createSignal, Show } from "solid-js"; 16 15 17 16 configureOAuth({ 18 17 metadata: { ··· 23 22 24 23 export const [agent, setAgent] = createSignal<OAuthUserAgent | undefined>(); 25 24 25 + type Account = { 26 + signedIn: boolean; 27 + handle?: string; 28 + }; 29 + 30 + export type Sessions = Record<string, Account>; 31 + 26 32 const Login = () => { 27 33 const [notice, setNotice] = createSignal(""); 28 34 const [loginInput, setLoginInput] = createSignal(""); 29 35 30 36 const login = async (handle: string) => { 31 37 try { 38 + setNotice(""); 32 39 if (!handle) return; 33 40 let resolved; 34 41 if (!isHandle(handle)) { ··· 56 63 }; 57 64 58 65 return ( 59 - <form class="flex flex-col gap-y-2" onsubmit={(e) => e.preventDefault()}> 60 - <div class="flex items-center gap-1"> 61 - <label for="handle" class="mr-1 flex items-center"> 62 - <span class="iconify lucide--user-round-plus text-lg"></span> 63 - </label> 64 - <TextInput 65 - id="handle" 66 + <form class="flex flex-col gap-y-2 px-1" onsubmit={(e) => e.preventDefault()}> 67 + <label for="handle" class="hidden"> 68 + Add account 69 + </label> 70 + <div class="dark:bg-dark-100 dark:shadow-dark-700 flex grow items-center gap-2 rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 shadow-xs focus-within:outline-[1px] focus-within:outline-neutral-600 dark:border-neutral-600 dark:focus-within:outline-neutral-400"> 71 + <label 72 + for="handle" 73 + class="iconify lucide--user-round-plus text-neutral-500 dark:text-neutral-400" 74 + ></label> 75 + <input 76 + type="text" 77 + spellcheck={false} 66 78 placeholder="user.bsky.social" 79 + id="handle" 80 + class="grow py-1 select-none placeholder:text-sm focus:outline-none" 67 81 onInput={(e) => setLoginInput(e.currentTarget.value)} 68 - class="grow" 69 82 /> 70 83 <button 71 84 onclick={() => login(loginInput())} 72 - class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 85 + class="flex items-center rounded-lg p-1 hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-600 dark:active:bg-neutral-500" 73 86 > 74 - <span class="iconify lucide--log-in text-lg"></span> 87 + <span class="iconify lucide--log-in"></span> 75 88 </button> 76 89 </div> 77 - <div>{notice()}</div> 90 + <Show when={notice()}> 91 + <div class="text-sm">{notice()}</div> 92 + </Show> 78 93 </form> 79 94 ); 80 95 }; ··· 90 105 const did = session.info.sub; 91 106 92 107 localStorage.setItem("lastSignedIn", did); 108 + 109 + const sessions = localStorage.getItem("sessions"); 110 + const newSessions: Sessions = sessions ? JSON.parse(sessions) : { [did]: {} }; 111 + newSessions[did] = { signedIn: true }; 112 + localStorage.setItem("sessions", JSON.stringify(newSessions)); 93 113 return session; 94 114 } else { 95 115 const lastSignedIn = localStorage.getItem("lastSignedIn"); 96 116 97 117 if (lastSignedIn) { 98 118 try { 99 - return await getSession(lastSignedIn as Did); 119 + const session = await getSession(lastSignedIn as Did); 120 + const rpc = new Client({ handler: new OAuthUserAgent(session) }); 121 + const res = await rpc.get("com.atproto.server.getSession"); 122 + if (!res.ok) throw res.data.error; 123 + return session; 100 124 } catch (err) { 101 - deleteStoredSession(lastSignedIn as Did); 102 - localStorage.removeItem("lastSignedIn"); 125 + const sessions = localStorage.getItem("sessions"); 126 + const newSessions: Sessions = sessions ? JSON.parse(sessions) : {}; 127 + newSessions[lastSignedIn].signedIn = false; 128 + localStorage.setItem("sessions", JSON.stringify(newSessions)); 103 129 throw err; 104 130 } 105 131 } 106 132 } 107 133 }; 108 134 109 - const session = await init().catch(() => {}); 135 + const session = await init(); 110 136 111 137 if (session) setAgent(new OAuthUserAgent(session)); 112 138 };
+18 -25
src/components/navbar.tsx
··· 5 5 import Tooltip from "./tooltip"; 6 6 7 7 export const [pds, setPDS] = createSignal<string>(); 8 - export const [isLabeler, setIsLabeler] = createSignal(false); 9 8 10 9 export const NavBar = (props: { params: Params }) => { 11 10 const location = useLocation(); 12 11 const [handle, setHandle] = createSignal(props.params.repo); 13 12 const [showHandle, setShowHandle] = createSignal(localStorage.showHandle === "true"); 14 13 15 - createEffect(async () => { 14 + createEffect(() => { 16 15 if (pds() !== undefined && props.params.repo) { 17 16 const hdl = 18 17 didDocCache[props.params.repo]?.alsoKnownAs ··· 23 22 }); 24 23 25 24 return ( 26 - <nav class="flex w-full flex-col px-2 text-sm wrap-anywhere"> 25 + <nav class="flex w-full flex-col px-2 wrap-anywhere"> 27 26 <div class="relative flex items-center justify-between gap-1"> 28 - <div class="flex min-h-[1.25rem] basis-full items-center gap-2"> 27 + <div class="flex min-h-[1.5rem] basis-full items-center gap-2"> 29 28 <Tooltip text="PDS"> 30 - <span class="iconify lucide--hard-drive shrink-0 text-base"></span> 29 + <span class="iconify lucide--hard-drive shrink-0"></span> 31 30 </Tooltip> 32 31 <Show when={pds()}> 33 - <Show when={props.params.repo}> 32 + <Show when={props.params.repo} fallback={<span>{pds()}</span>}> 34 33 <A 35 34 end 36 35 href={pds()!} ··· 39 38 {pds()} 40 39 </A> 41 40 </Show> 42 - <Show when={!props.params.repo}> 43 - <span>{pds()}</span> 44 - </Show> 45 41 </Show> 46 42 </div> 47 43 <Show when={props.params.repo}> 48 44 <MenuProvider> 49 45 <DropdownMenu 50 - icon="lucide--copy text-base" 51 - buttonClass="rounded p-0.5" 46 + icon="lucide--copy" 47 + buttonClass="rounded p-1" 52 48 menuClass="top-6 p-2 text-xs" 53 49 > 54 50 <Show when={pds()}> ··· 65 61 </div> 66 62 <div class="flex flex-col flex-wrap"> 67 63 <Show when={props.params.repo}> 68 - <div class="relative mt-1 flex items-center justify-between gap-1"> 64 + <div class="relative flex items-center justify-between gap-1"> 69 65 <div class="flex basis-full items-center gap-2"> 70 66 <Tooltip text="Repository"> 71 - <span class="iconify lucide--book-user text-base"></span> 67 + <span class="iconify lucide--book-user"></span> 72 68 </Tooltip> 73 69 {props.params.collection || location.pathname.includes("/labels") ? 74 70 <A ··· 82 78 </div> 83 79 <Tooltip text={showHandle() ? "Show DID" : "Show handle"}> 84 80 <button 85 - class="flex items-center rounded p-0.5 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 81 + class="flex items-center rounded p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 86 82 onclick={() => { 87 83 localStorage.showHandle = !showHandle(); 88 84 setShowHandle(!showHandle()); 89 85 }} 90 86 > 91 87 <span 92 - class={`iconify shrink-0 text-base transition-transform duration-400 ${showHandle() ? "rotate-y-180" : ""} lucide--arrow-left-right`} 88 + class={`iconify shrink-0 transition-transform duration-400 ${showHandle() ? "rotate-y-180" : ""} lucide--arrow-left-right`} 93 89 ></span> 94 90 </button> 95 91 </Tooltip> ··· 101 97 (props.params.repo in labelerCache || location.pathname.endsWith("/labels")) 102 98 } 103 99 > 104 - <div class="mt-1 flex items-center gap-2"> 105 - <span class="iconify lucide--tag text-base"></span> 100 + <div class="flex items-center gap-2"> 101 + <span class="iconify lucide--tag"></span> 106 102 <A 107 103 end 108 104 href={`/at://${props.params.repo}/labels`} ··· 113 109 </div> 114 110 </Show> 115 111 <Show when={props.params.collection}> 116 - <div class="mt-1 flex items-center gap-2"> 112 + <div class="flex items-center gap-2"> 117 113 <Tooltip text="Collection"> 118 - <span class="iconify lucide--folder-open text-base"></span> 114 + <span class="iconify lucide--folder-open"></span> 119 115 </Tooltip> 120 - <Show when={props.params.rkey}> 116 + <Show when={props.params.rkey} fallback={<span>{props.params.collection}</span>}> 121 117 <A 122 118 end 123 119 href={`/at://${props.params.repo}/${props.params.collection}`} ··· 126 122 {props.params.collection} 127 123 </A> 128 124 </Show> 129 - <Show when={!props.params.rkey}> 130 - <span>{props.params.collection}</span> 131 - </Show> 132 125 </div> 133 126 </Show> 134 127 <Show when={props.params.rkey}> 135 - <div class="mt-1 flex items-center gap-2"> 128 + <div class="flex items-center gap-2"> 136 129 <Tooltip text="Record"> 137 - <span class="iconify lucide--file-json text-base"></span> 130 + <span class="iconify lucide--file-json"></span> 138 131 </Tooltip> 139 132 <span>{props.params.rkey}</span> 140 133 </div>
+17 -11
src/components/search.tsx
··· 1 1 import { Client, CredentialManager } from "@atcute/client"; 2 + import { Nsid } from "@atcute/lexicons"; 2 3 import { A, useLocation, useNavigate } from "@solidjs/router"; 3 4 import { createResource, createSignal, For, onCleanup, onMount, Show } from "solid-js"; 4 5 import { isTouchDevice } from "../layout"; 6 + import { resolveLexiconAuthority } from "../utils/api"; 5 7 import { appHandleLink, appList, appName, AppUrl } from "../utils/app-urls"; 6 8 import { createDebouncedValue } from "../utils/hooks/debounced"; 7 9 import { Modal } from "./modal"; ··· 47 49 }); 48 50 49 51 onMount(() => { 50 - if (useLocation().pathname !== "/") searchInput.focus(); 52 + if (!isTouchDevice || useLocation().pathname !== "/") searchInput.focus(); 51 53 }); 52 54 53 55 const fetchTypeahead = async (input: string) => { ··· 64 66 const [input, setInput] = createSignal<string>(); 65 67 const [search] = createResource(createDebouncedValue(input, 250), fetchTypeahead); 66 68 67 - const processInput = (input: string) => { 69 + const processInput = async (input: string) => { 68 70 input = input.trim().replace(/^@/, ""); 69 71 if (!input.length) return; 70 72 setShowSearch(false); ··· 83 85 const uri = appHandleLink[app](path); 84 86 navigate(`/${uri}`); 85 87 } 88 + } else if (input.startsWith("lex:")) { 89 + const nsid = input.replace("lex:", "") as Nsid; 90 + const res = await resolveLexiconAuthority(nsid); 91 + navigate(`/at://${res}/com.atproto.lexicon.schema/${nsid}`); 86 92 } else { 87 93 navigate(`/at://${input.replace("at://", "")}`); 88 94 } ··· 97 103 }} 98 104 > 99 105 <label for="input" class="hidden"> 100 - PDS URL, AT URI, or handle 106 + PDS URL, AT URI, NSID, DID, or handle 101 107 </label> 102 - <div class="dark:bg-dark-100 dark:shadow-dark-800 flex items-center gap-2 rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 shadow-xs focus-within:outline-[1px] focus-within:outline-neutral-600 dark:border-neutral-700 dark:focus-within:outline-neutral-400"> 103 - <span 108 + <div class="dark:bg-dark-100 dark:shadow-dark-700 flex items-center gap-2 rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 shadow-xs focus-within:outline-[1px] focus-within:outline-neutral-600 dark:border-neutral-600 dark:focus-within:outline-neutral-400"> 109 + <label 110 + for="input" 104 111 class="iconify lucide--search text-neutral-500 dark:text-neutral-400" 105 - onClick={() => searchInput.focus()} 106 - ></span> 112 + ></label> 107 113 <input 108 114 type="text" 109 115 spellcheck={false} 110 - placeholder="PDS URL, AT URI, DID, or handle" 116 + placeholder="PDS, AT URI, NSID, DID, or handle" 111 117 ref={searchInput} 112 118 id="input" 113 - class="grow select-none placeholder:text-sm focus:outline-none" 119 + class="grow py-1 select-none placeholder:text-sm focus:outline-none" 114 120 value={input() ?? ""} 115 121 onInput={(e) => setInput(e.currentTarget.value)} 116 122 /> ··· 125 131 </Show> 126 132 </div> 127 133 <Show when={search()?.length && input()}> 128 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute z-30 mt-1 flex w-full flex-col rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-2 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 134 + <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute z-30 mt-1 flex w-full flex-col rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-2 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 129 135 <For each={search()}> 130 136 {(actor) => ( 131 137 <A ··· 159 165 return ( 160 166 <> 161 167 <Modal open={openList()} onClose={() => setOpenList(false)}> 162 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-16 left-[50%] w-[22rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 sm:w-[26rem] dark:border-neutral-700 starting:opacity-0"> 168 + <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-16 left-[50%] w-[22rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 sm:w-[26rem] dark:border-neutral-700 starting:opacity-0"> 163 169 <div class="mb-2 flex items-center gap-1 font-semibold"> 164 170 <span class="iconify lucide--link"></span> 165 171 <span>Supported URLs</span>
+2 -2
src/components/text-input.tsx
··· 1 1 export interface TextInputProps { 2 - ref?: HTMLInputElement; 2 + ref?: HTMLInputElement | ((el: HTMLInputElement) => void); 3 3 class?: string; 4 4 id?: string; 5 5 type?: "text" | "email" | "password" | "search" | "tel" | "url"; ··· 25 25 disabled={props.disabled} 26 26 required={props.required} 27 27 class={ 28 - "dark:bg-dark-100 dark:shadow-dark-800 rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 shadow-xs select-none placeholder:text-sm focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-700 dark:focus:outline-neutral-400 " + 28 + "dark:bg-dark-100 dark:shadow-dark-700 rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 shadow-xs select-none placeholder:text-sm focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400 " + 29 29 props.class 30 30 } 31 31 onInput={props.onInput}
+1 -1
src/components/tooltip.tsx
··· 7 7 <Show when={!isTouchDevice}> 8 8 <span 9 9 style={`transform: translate(-50%, 28px)`} 10 - class={`dark:shadow-dark-800 pointer-events-none absolute left-[50%] z-20 hidden min-w-fit rounded border-[0.5px] border-neutral-300 bg-neutral-50 p-1 text-center font-sans text-xs whitespace-nowrap text-neutral-900 shadow-md select-none group-hover/tooltip:inline first-letter:capitalize dark:border-neutral-600 dark:bg-neutral-800 dark:text-neutral-200`} 10 + class={`dark:shadow-dark-700 pointer-events-none absolute left-[50%] z-20 hidden min-w-fit rounded border-[0.5px] border-neutral-300 bg-neutral-50 p-1 text-center font-sans text-xs whitespace-nowrap text-neutral-900 shadow-md select-none group-hover/tooltip:inline first-letter:capitalize dark:border-neutral-600 dark:bg-neutral-800 dark:text-neutral-200`} 11 11 > 12 12 {props.text} 13 13 </span>
+5 -5
src/layout.tsx
··· 66 66 </Show> 67 67 </MetaProvider> 68 68 <header 69 - class={`dark:shadow-dark-800 dark:bg-dark-300 mb-4 flex w-full items-center justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 bg-size-[95%] bg-right bg-no-repeat p-2 shadow-xs [--header-bg:#fafafa] dark:border-neutral-700 dark:[--header-bg:#2d2d2d] ${localStorage.getItem("hrt") === "true" ? "bg-[linear-gradient(to_left,transparent_10%,var(--header-bg)_85%),linear-gradient(to_bottom,#5BCEFA90_0%,#5BCEFA90_20%,#F5A9B890_20%,#F5A9B890_40%,#FFFFFF90_40%,#FFFFFF90_60%,#F5A9B890_60%,#F5A9B890_80%,#5BCEFA90_80%,#5BCEFA90_100%)]" : ""}`} 69 + class={`dark:shadow-dark-700 dark:bg-dark-300 mb-4 flex w-full items-center justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 bg-size-[95%] bg-right bg-no-repeat p-2 shadow-xs [--header-bg:#fafafa] dark:border-neutral-700 dark:[--header-bg:#2d2d2d] ${localStorage.getItem("hrt") === "true" ? "bg-[linear-gradient(to_left,transparent_10%,var(--header-bg)_85%),linear-gradient(to_bottom,#5BCEFA90_0%,#5BCEFA90_20%,#F5A9B890_20%,#F5A9B890_40%,#FFFFFF90_40%,#FFFFFF90_60%,#F5A9B890_60%,#F5A9B890_80%,#5BCEFA90_80%,#5BCEFA90_100%)]" : ""}`} 70 70 style={{ 71 71 "background-image": 72 72 props.params.repo in headers ? ··· 96 96 buttonClass="rounded-lg p-1" 97 97 menuClass="top-10 p-3" 98 98 > 99 - <NavMenu href="/jetstream" label="Jetstream" icon="lucide--radio-tower" /> 100 - <NavMenu href="/firehose" label="Firehose" icon="lucide--waves" /> 101 - <NavMenu href="/settings" label="Settings" icon="lucide--settings" /> 99 + <NavMenu href="/jetstream" label="Jetstream" /> 100 + <NavMenu href="/firehose" label="Firehose" /> 101 + <NavMenu href="/settings" label="Settings" /> 102 102 <ThemeSelection /> 103 103 </DropdownMenu> 104 104 </MenuProvider> ··· 127 127 </div> 128 128 <Show when={notif().show}> 129 129 <button 130 - class="dark:shadow-dark-800 dark:bg-dark-100 fixed bottom-10 z-50 flex items-center rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-2 shadow-md dark:border-neutral-700" 130 + class="dark:shadow-dark-700 dark:bg-dark-100 fixed bottom-10 z-50 flex items-center rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-2 shadow-md dark:border-neutral-700" 131 131 onClick={() => setNotif({ show: false })} 132 132 > 133 133 <span class={`iconify ${notif().icon} mr-1`}></span>
+1
src/styles/index.css
··· 9 9 @theme { 10 10 --font-sans: "Inter", sans-serif; 11 11 --font-mono: "Roboto Mono", monospace; 12 + --font-pecita: "Pecita", serif; 12 13 13 14 --color-dark-50: oklch(40.91% 0 0); 14 15 --color-dark-100: oklch(35.62% 0 0);
+18 -2
src/utils/api.ts
··· 119 119 }; 120 120 } 121 121 122 + type LinksWithRecords = { 123 + cursor: string; 124 + total: number; 125 + linking_records: Array<{ did: string; collection: string; rkey: string }>; 126 + }; 127 + 128 + type LinksWithDids = { 129 + cursor: string; 130 + total: number; 131 + linking_dids: Array<string>; 132 + }; 133 + 122 134 const getConstellation = async ( 123 135 endpoint: string, 124 136 target: string, ··· 152 164 path: string, 153 165 cursor?: string, 154 166 limit?: number, 155 - ) => getConstellation("/links", target, collection, path, cursor, limit || 100); 167 + ): Promise<LinksWithRecords> => 168 + getConstellation("/links", target, collection, path, cursor, limit || 100); 156 169 157 170 const getDidBacklinks = ( 158 171 target: string, ··· 160 173 path: string, 161 174 cursor?: string, 162 175 limit?: number, 163 - ) => getConstellation("/links/distinct-dids", target, collection, path, cursor, limit || 100); 176 + ): Promise<LinksWithDids> => 177 + getConstellation("/links/distinct-dids", target, collection, path, cursor, limit || 100); 164 178 165 179 export { 166 180 didDocCache, ··· 175 189 resolvePDS, 176 190 validateHandle, 177 191 type LinkData, 192 + type LinksWithDids, 193 + type LinksWithRecords, 178 194 };
+1 -1
src/utils/app-urls.ts
··· 47 47 48 48 if (type === "post") { 49 49 return `at://${user}/app.bsky.feed.post/${rkey}`; 50 - } else if (type === "list") { 50 + } else if (type === "lists") { 51 51 return `at://${user}/app.bsky.graph.list/${rkey}`; 52 52 } else if (type === "feed") { 53 53 return `at://${user}/app.bsky.feed.generator/${rkey}`;
+3 -3
src/views/collection.tsx
··· 60 60 <Show when={hover()}> 61 61 <span 62 62 ref={previewRef} 63 - class={`dark:bg-dark-300 dark:shadow-dark-800 pointer-events-none absolute left-[50%] z-25 block max-h-[20rem] w-max max-w-sm -translate-x-1/2 overflow-hidden rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-2 text-xs whitespace-pre-wrap shadow-md sm:max-h-[28rem] lg:max-w-lg dark:border-neutral-700 ${isOverflowing(previewHeight()) ? "bottom-7" : "top-7"}`} 63 + class={`dark:bg-dark-300 dark:shadow-dark-700 pointer-events-none absolute left-[50%] z-25 block max-h-[20rem] w-max max-w-sm -translate-x-1/2 overflow-hidden rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-2 text-xs whitespace-pre-wrap shadow-md sm:max-h-[28rem] lg:max-w-lg dark:border-neutral-700 ${isOverflowing(previewHeight()) ? "bottom-7" : "top-7"}`} 64 64 > 65 65 <JSONValue 66 66 data={props.record.record.value as JSONType} ··· 258 258 </Show> 259 259 </div> 260 260 <Modal open={openDelete()} onClose={() => setOpenDelete(false)}> 261 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 261 + <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 262 262 <h2 class="mb-2 font-semibold"> 263 263 {recreate() ? "Recreate" : "Delete"}{" "} 264 264 {records.filter((r) => r.toDelete).length} records? ··· 267 267 <Button onClick={() => setOpenDelete(false)}>Cancel</Button> 268 268 <Button 269 269 onClick={deleteRecords} 270 - class={`dark:shadow-dark-800 rounded-lg px-2 py-1.5 text-xs text-white shadow-xs select-none ${recreate() ? "bg-green-500 hover:bg-green-400 dark:bg-green-600 dark:hover:bg-green-500" : "bg-red-500 hover:bg-red-400 active:bg-red-400"}`} 270 + class={`dark:shadow-dark-700 rounded-lg px-2 py-1.5 text-xs text-white shadow-xs select-none ${recreate() ? "bg-green-500 hover:bg-green-400 dark:bg-green-600 dark:hover:bg-green-500" : "bg-red-500 hover:bg-red-400 active:bg-red-400"}`} 271 271 > 272 272 {recreate() ? "Recreate" : "Delete"} 273 273 </Button>
+10
src/views/home.tsx
··· 54 54 <span class="iconify ri--bluesky"></span> 55 55 </a> 56 56 </div> 57 + <div class="text-center text-sm italic"> 58 + Made by{" "} 59 + <a 60 + href="https://juli.ee" 61 + class="font-pecita relative after:absolute after:bottom-0 after:left-0 after:h-[1px] after:w-0 after:bg-current after:transition-[width] after:duration-300 after:ease-out hover:after:w-full" 62 + > 63 + Juliet 64 + </a>{" "} 65 + with love 66 + </div> 57 67 </div> 58 68 ); 59 69 };
+3 -3
src/views/labels.tsx
··· 76 76 id="patterns" 77 77 name="patterns" 78 78 spellcheck={false} 79 - rows={3} 79 + rows={2} 80 80 value={searchParams.uriPatterns ?? "*"} 81 - class="dark:bg-dark-100 dark:shadow-dark-800 grow rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-700 dark:focus:outline-neutral-400" 81 + class="dark:bg-dark-100 dark:shadow-dark-700 grow rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 text-sm shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 82 82 /> 83 83 <div class="flex justify-center"> 84 84 <Show when={!response.loading}> ··· 102 102 placeholder="Filter by label" 103 103 name="filter" 104 104 onInput={(e) => setFilter(e.currentTarget.value)} 105 - class="w-full" 105 + class="w-full text-sm" 106 106 /> 107 107 <div class="flex items-center gap-x-2"> 108 108 <Show when={labelCount() && labels().length}>
+7 -19
src/views/logs.tsx
··· 5 5 processIndexedEntryLog, 6 6 } from "@atcute/did-plc"; 7 7 import { createResource, createSignal, For, Show } from "solid-js"; 8 - import Tooltip from "../components/tooltip.jsx"; 9 8 import { localDateFromTimestamp } from "../utils/date.js"; 10 9 import { createOperationHistory, DiffEntry, groupBy } from "../utils/plc-logs.js"; 11 10 ··· 111 110 112 111 return ( 113 112 <div class="flex w-full flex-col gap-2 wrap-anywhere"> 114 - <div class="flex items-center justify-between"> 115 - <div class="flex items-center gap-1"> 116 - <div class="iconify lucide--filter" /> 117 - <div class="dark:shadow-dark-800 dark:bg-dark-300 flex w-fit items-center rounded-full border-[0.5px] border-neutral-300 bg-neutral-50 shadow-xs dark:border-neutral-700"> 118 - <FilterButton icon="iconify lucide--at-sign" event="handle" /> 119 - <FilterButton icon="iconify lucide--key-round" event="rotation_key" /> 120 - <FilterButton icon="iconify lucide--hard-drive" event="service" /> 121 - <FilterButton icon="iconify lucide--shield-check" event="verification_method" /> 122 - </div> 113 + <div class="flex items-center gap-1"> 114 + <div class="iconify lucide--filter" /> 115 + <div class="dark:shadow-dark-700 dark:bg-dark-300 flex w-fit items-center rounded-full border-[0.5px] border-neutral-300 bg-neutral-50 shadow-xs dark:border-neutral-700"> 116 + <FilterButton icon="iconify lucide--at-sign" event="handle" /> 117 + <FilterButton icon="iconify lucide--key-round" event="rotation_key" /> 118 + <FilterButton icon="iconify lucide--hard-drive" event="service" /> 119 + <FilterButton icon="iconify lucide--shield-check" event="verification_method" /> 123 120 </div> 124 - <Tooltip text="Audit log"> 125 - <a 126 - href={`${localStorage.plcDirectory ?? "https://plc.directory"}/${props.did}/log/audit`} 127 - target="_blank" 128 - class="-mr-1 flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 129 - > 130 - <span class="iconify lucide--external-link"></span> 131 - </a> 132 - </Tooltip> 133 121 </div> 134 122 <div class="flex flex-col gap-1 text-sm"> 135 123 <For each={plcOps()}>
+18 -20
src/views/pds.tsx
··· 5 5 import { A, useLocation, useParams } from "@solidjs/router"; 6 6 import { createResource, createSignal, For, Show } from "solid-js"; 7 7 import { Button } from "../components/button"; 8 + import { CopyMenu, DropdownMenu, MenuProvider, NavMenu } from "../components/dropdown"; 8 9 import { Modal } from "../components/modal"; 9 10 import { setPDS } from "../components/navbar"; 10 11 import Tooltip from "../components/tooltip"; 11 - import { addToClipboard } from "../utils/copy"; 12 12 import { localDateFromTimestamp } from "../utils/date"; 13 13 14 14 const LIMIT = 1000; ··· 74 74 <span class="iconify lucide--info"></span> 75 75 </button> 76 76 <Modal open={openInfo()} onClose={() => setOpenInfo(false)}> 77 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-70 left-[50%] w-max max-w-full -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-3 break-words shadow-md transition-opacity duration-200 sm:max-w-[32rem] dark:border-neutral-700 starting:opacity-0"> 77 + <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] w-max max-w-full -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-3 break-words shadow-md transition-opacity duration-200 sm:max-w-[32rem] dark:border-neutral-700 starting:opacity-0"> 78 78 <div class="mb-1 flex justify-between gap-2"> 79 79 <div class="flex items-center gap-1"> 80 80 <span class="iconify lucide--info"></span> ··· 128 128 return ( 129 129 <Show when={repos() || response()}> 130 130 <div class="flex w-full flex-col"> 131 - <div class="dark:shadow-dark-800 dark:bg-dark-300 mb-2 flex w-full justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm shadow-xs dark:border-neutral-700"> 131 + <div class="dark:shadow-dark-700 dark:bg-dark-300 mb-2 flex w-full justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm shadow-xs dark:border-neutral-700"> 132 132 <div class="flex gap-3"> 133 133 <Tab tab="repos" label="Repositories" /> 134 134 <Tab tab="info" label="Info" /> 135 135 </div> 136 - <div class="flex gap-1"> 137 - <Tooltip text="Copy PDS"> 138 - <button 139 - onClick={() => addToClipboard(params.pds)} 140 - class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 141 - > 142 - <span class="iconify lucide--copy"></span> 143 - </button> 144 - </Tooltip> 145 - <Tooltip text="Firehose"> 146 - <A 136 + <MenuProvider> 137 + <DropdownMenu 138 + icon="lucide--ellipsis-vertical" 139 + buttonClass="rounded-sm p-1" 140 + menuClass="top-8 p-2 text-sm" 141 + > 142 + <CopyMenu copyContent={params.pds} label="Copy PDS" icon="lucide--copy" /> 143 + <NavMenu 147 144 href={`/firehose?instance=wss://${params.pds}`} 148 - class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 149 - > 150 - <span class="iconify lucide--radio-tower"></span> 151 - </A> 152 - </Tooltip> 153 - </div> 145 + label="Firehose" 146 + icon="lucide--radio-tower" 147 + /> 148 + </DropdownMenu> 149 + </MenuProvider> 154 150 </div> 155 151 <div class="flex flex-col gap-1 px-2"> 156 152 <Show when={!location.hash || location.hash === "#repos"}> ··· 195 191 href={server().links?.privacyPolicy} 196 192 class="text-sm hover:underline" 197 193 target="_blank" 194 + rel="noopener" 198 195 > 199 196 {server().links?.privacyPolicy} 200 197 </a> ··· 207 204 href={server().links?.termsOfService} 208 205 class="text-sm hover:underline" 209 206 target="_blank" 207 + rel="noopener" 210 208 > 211 209 {server().links?.termsOfService} 212 210 </a>
+4 -4
src/views/record.tsx
··· 154 154 return ( 155 155 <Show when={record()} keyed> 156 156 <div class="flex w-full flex-col items-center"> 157 - <div class="dark:shadow-dark-800 dark:bg-dark-300 mb-3 flex w-full justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm shadow-xs dark:border-neutral-700"> 157 + <div class="dark:shadow-dark-700 dark:bg-dark-300 mb-3 flex w-full justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm shadow-xs dark:border-neutral-700"> 158 158 <div class="flex gap-3"> 159 159 <RecordTab tab="record" label="Record" /> 160 160 <RecordTab tab="backlinks" label="Backlinks" /> ··· 172 172 </button> 173 173 </Tooltip> 174 174 <Modal open={openDelete()} onClose={() => setOpenDelete(false)}> 175 - <div class="dark:bg-dark-300 dark:shadow-dark-800 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 175 + <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 176 176 <h2 class="mb-2 font-semibold">Delete this record?</h2> 177 177 <div class="flex justify-end gap-2"> 178 178 <Button onClick={() => setOpenDelete(false)}>Cancel</Button> 179 179 <Button 180 180 onClick={deleteRecord} 181 - class="dark:shadow-dark-800 rounded-lg bg-red-500 px-2 py-1.5 text-xs text-white shadow-xs select-none hover:bg-red-400 active:bg-red-400" 181 + class="dark:shadow-dark-700 rounded-lg bg-red-500 px-2 py-1.5 text-xs text-white shadow-xs select-none hover:bg-red-400 active:bg-red-400" 182 182 > 183 183 Delete 184 184 </Button> ··· 188 188 </Show> 189 189 <MenuProvider> 190 190 <DropdownMenu 191 - icon="lucide--ellipsis-vertical " 191 + icon="lucide--ellipsis-vertical" 192 192 buttonClass="rounded-sm p-1" 193 193 menuClass="top-8 p-2 text-sm" 194 194 >
+72 -32
src/views/repo.tsx
··· 1 1 import { Client, CredentialManager } from "@atcute/client"; 2 2 import { parsePublicMultikey } from "@atcute/crypto"; 3 3 import { DidDocument } from "@atcute/identity"; 4 - import { ActorIdentifier, Did, Handle } from "@atcute/lexicons"; 4 + import { ActorIdentifier, Did, Handle, Nsid } from "@atcute/lexicons"; 5 5 import { A, useLocation, useNavigate, useParams } from "@solidjs/router"; 6 - import { createResource, createSignal, ErrorBoundary, For, Show, Suspense } from "solid-js"; 6 + import { 7 + createResource, 8 + createSignal, 9 + ErrorBoundary, 10 + For, 11 + onMount, 12 + Show, 13 + Suspense, 14 + } from "solid-js"; 7 15 import { createStore } from "solid-js/store"; 8 16 import { Backlinks } from "../components/backlinks.jsx"; 9 17 import { ActionMenu, DropdownMenu, MenuProvider, NavMenu } from "../components/dropdown.jsx"; 10 18 import { TextInput } from "../components/text-input.jsx"; 11 19 import Tooltip from "../components/tooltip.jsx"; 12 - import { didDocCache, resolveHandle, resolvePDS, validateHandle } from "../utils/api.js"; 20 + import { 21 + didDocCache, 22 + resolveHandle, 23 + resolveLexiconAuthority, 24 + resolvePDS, 25 + validateHandle, 26 + } from "../utils/api.js"; 13 27 import { BlobView } from "./blob.jsx"; 14 28 import { PlcLogView } from "./logs.jsx"; 15 29 ··· 22 36 const [didDoc, setDidDoc] = createSignal<DidDocument>(); 23 37 const [nsids, setNsids] = createSignal<Record<string, { hidden: boolean; nsids: string[] }>>(); 24 38 const [filter, setFilter] = createSignal<string>(); 39 + const [showFilter, setShowFilter] = createSignal(false); 25 40 const [validHandles, setValidHandles] = createStore<Record<string, boolean>>({}); 26 41 let rpc: Client; 27 42 let pds: string; ··· 53 68 const did = await resolveHandle(params.repo as Handle); 54 69 navigate(location.pathname.replace(params.repo, did)); 55 70 } catch { 56 - navigate(`/${did}`); 71 + try { 72 + const nsid = params.repo as Nsid; 73 + const res = await resolveLexiconAuthority(nsid); 74 + navigate(`/at://${res}/com.atproto.lexicon.schema/${nsid}`); 75 + } catch { 76 + navigate(`/${did}`); 77 + } 57 78 } 58 79 } 59 80 setDidDoc(didDocCache[did] as DidDocument); ··· 134 155 <Show when={repo()}> 135 156 <div class="flex w-full flex-col gap-2 break-words"> 136 157 <div 137 - class={`dark:shadow-dark-800 dark:bg-dark-300 flex justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm shadow-xs dark:border-neutral-700`} 158 + class={`dark:shadow-dark-700 dark:bg-dark-300 flex justify-between rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 px-2 py-1.5 text-sm shadow-xs dark:border-neutral-700`} 138 159 > 139 - <div class="flex gap-2 sm:gap-4"> 160 + <div class="flex gap-2 text-xs sm:gap-4 sm:text-sm"> 140 161 <Show when={!error()}> 141 162 <RepoTab tab="collections" label="Collections" /> 142 163 </Show> ··· 156 177 <span>{error()}</span> 157 178 </div> 158 179 </Show> 180 + <Show when={!error() && (!location.hash || location.hash === "#collections")}> 181 + <Tooltip text="Filter collections"> 182 + <button 183 + class="flex items-center rounded-sm p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 184 + onClick={() => setShowFilter(!showFilter())} 185 + > 186 + <span class="iconify lucide--filter"></span> 187 + </button> 188 + </Tooltip> 189 + </Show> 159 190 <MenuProvider> 160 191 <DropdownMenu 161 192 icon="lucide--ellipsis-vertical" ··· 167 198 label="Jetstream" 168 199 icon="lucide--radio-tower" 169 200 /> 201 + <NavMenu 202 + href={ 203 + did.startsWith("did:plc") ? 204 + `${localStorage.plcDirectory ?? "https://plc.directory"}/${did}` 205 + : `https://${did.split("did:web:")[1]}/.well-known/did.json` 206 + } 207 + newTab 208 + label="DID Document" 209 + icon="lucide--external-link" 210 + /> 211 + <Show when={did.startsWith("did:plc")}> 212 + <NavMenu 213 + href={`${localStorage.plcDirectory ?? "https://plc.directory"}/${did}/log/audit`} 214 + newTab 215 + label="Audit Log" 216 + icon="lucide--external-link" 217 + /> 218 + </Show> 170 219 <Show when={error()?.length === 0 || error() === undefined}> 171 220 <ActionMenu 172 221 label="Export Repo" ··· 213 262 </ErrorBoundary> 214 263 </Show> 215 264 <Show when={nsids() && (!location.hash || location.hash === "#collections")}> 216 - <TextInput 217 - name="filter" 218 - placeholder="Filter collections" 219 - onInput={(e) => setFilter(e.currentTarget.value.toLowerCase())} 220 - class="grow" 221 - /> 265 + <Show when={showFilter()}> 266 + <TextInput 267 + name="filter" 268 + placeholder="Filter collections" 269 + onInput={(e) => setFilter(e.currentTarget.value.toLowerCase())} 270 + class="grow" 271 + ref={(node) => { 272 + onMount(() => node.focus()); 273 + }} 274 + /> 275 + </Show> 222 276 <div class="flex flex-col overflow-hidden text-sm"> 223 277 <For 224 278 each={Object.keys(nsids() ?? {}).filter((authority) => ··· 253 307 <Show when={didDoc()}> 254 308 {(didDocument) => ( 255 309 <div class="flex flex-col gap-y-1 wrap-anywhere"> 256 - <div class="flex items-baseline justify-between gap-2"> 257 - <div> 258 - <div class="flex items-center gap-1"> 259 - <div class="iconify lucide--id-card" /> 260 - <p class="font-semibold">ID</p> 261 - </div> 262 - <div class="text-sm">{didDocument().id}</div> 310 + <div> 311 + <div class="flex items-center gap-1"> 312 + <div class="iconify lucide--id-card" /> 313 + <p class="font-semibold">ID</p> 263 314 </div> 264 - <Tooltip text="DID document"> 265 - <a 266 - href={ 267 - did.startsWith("did:plc") ? 268 - `${localStorage.plcDirectory ?? "https://plc.directory"}/${did}` 269 - : `https://${did.split("did:web:")[1]}/.well-known/did.json` 270 - } 271 - target="_blank" 272 - class="-mr-1 flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 273 - > 274 - <span class="iconify lucide--external-link"></span> 275 - </a> 276 - </Tooltip> 315 + <div class="text-sm">{didDocument().id}</div> 277 316 </div> 278 317 <div> 279 318 <div class="flex items-center gap-1"> ··· 324 363 class="w-fit underline" 325 364 href={service.serviceEndpoint.toString()} 326 365 target="_blank" 366 + rel="noopener" 327 367 > 328 368 {service.serviceEndpoint.toString()} 329 369 </a>
+4 -4
src/views/stream.tsx
··· 37 37 url = url.concat("?"); 38 38 } else { 39 39 url = formData.get("instance")?.toString() ?? "wss://bsky.network"; 40 + url = url.replace("/xrpc/com.atproto.sync.subscribeRepos", ""); 41 + if (!(url.startsWith("wss://") || url.startsWith("ws://"))) url = "wss://" + url; 40 42 } 41 43 42 44 const collections = formData.get("collections")?.toString().split(","); ··· 147 149 inactiveClass="border-transparent hover:border-neutral-400 dark:hover:border-neutral-600" 148 150 href="/jetstream" 149 151 > 150 - <span class="iconify lucide--radio-tower"></span> 151 152 Jetstream 152 153 </A> 153 154 <A ··· 155 156 inactiveClass="border-transparent hover:border-neutral-400 dark:hover:border-neutral-600" 156 157 href="/firehose" 157 158 > 158 - <span class="iconify lucide--waves"></span> 159 159 Firehose 160 160 </A> 161 161 </div> ··· 183 183 spellcheck={false} 184 184 placeholder="Comma-separated list of collections" 185 185 value={searchParams.collections ?? ""} 186 - class="dark:bg-dark-100 dark:shadow-dark-800 grow rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-700 dark:focus:outline-neutral-400" 186 + class="dark:bg-dark-100 dark:shadow-dark-700 grow rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 187 187 /> 188 188 </label> 189 189 </Show> ··· 195 195 spellcheck={false} 196 196 placeholder="Comma-separated list of DIDs" 197 197 value={searchParams.dids ?? ""} 198 - class="dark:bg-dark-100 dark:shadow-dark-800 grow rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-700 dark:focus:outline-neutral-400" 198 + class="dark:bg-dark-100 dark:shadow-dark-700 grow rounded-lg border-[0.5px] border-neutral-300 bg-white px-2 py-1 shadow-xs focus:outline-[1px] focus:outline-neutral-600 dark:border-neutral-600 dark:focus:outline-neutral-400" 199 199 /> 200 200 </label> 201 201 </Show>