A demon compendium, deterministically derived from DIDs + PDS records.
0

Configure Feed

Select the types of activity you want to include in your feed.

metadata

+460 -29
+70
bun.lock
··· 10 10 "react-dom": "^19.2.8", 11 11 }, 12 12 "devDependencies": { 13 + "@resvg/resvg-js": "^2.6.2", 13 14 "@tailwindcss/postcss": "^4.3.3", 14 15 "@types/node": "^20.19.43", 15 16 "@types/react": "^19.2.17", ··· 19 20 "postcss": "^8.5.22", 20 21 "prettier": "^3.9.6", 21 22 "prettier-plugin-tailwindcss": "^0.6.14", 23 + "satori": "^0.28.2", 22 24 "tailwindcss": "^4.3.3", 23 25 "typescript": "^5.9.3", 24 26 "typescript-eslint": "^8.65.0", ··· 79 81 80 82 "@oxc-project/types": ["@oxc-project/types@0.139.0", "", {}, "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw=="], 81 83 84 + "@resvg/resvg-js": ["@resvg/resvg-js@2.6.2", "", { "optionalDependencies": { "@resvg/resvg-js-android-arm-eabi": "2.6.2", "@resvg/resvg-js-android-arm64": "2.6.2", "@resvg/resvg-js-darwin-arm64": "2.6.2", "@resvg/resvg-js-darwin-x64": "2.6.2", "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", "@resvg/resvg-js-linux-arm64-musl": "2.6.2", "@resvg/resvg-js-linux-x64-gnu": "2.6.2", "@resvg/resvg-js-linux-x64-musl": "2.6.2", "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", "@resvg/resvg-js-win32-x64-msvc": "2.6.2" } }, "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q=="], 85 + 86 + "@resvg/resvg-js-android-arm-eabi": ["@resvg/resvg-js-android-arm-eabi@2.6.2", "", { "os": "android", "cpu": "arm" }, "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA=="], 87 + 88 + "@resvg/resvg-js-android-arm64": ["@resvg/resvg-js-android-arm64@2.6.2", "", { "os": "android", "cpu": "arm64" }, "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ=="], 89 + 90 + "@resvg/resvg-js-darwin-arm64": ["@resvg/resvg-js-darwin-arm64@2.6.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A=="], 91 + 92 + "@resvg/resvg-js-darwin-x64": ["@resvg/resvg-js-darwin-x64@2.6.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw=="], 93 + 94 + "@resvg/resvg-js-linux-arm-gnueabihf": ["@resvg/resvg-js-linux-arm-gnueabihf@2.6.2", "", { "os": "linux", "cpu": "arm" }, "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw=="], 95 + 96 + "@resvg/resvg-js-linux-arm64-gnu": ["@resvg/resvg-js-linux-arm64-gnu@2.6.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg=="], 97 + 98 + "@resvg/resvg-js-linux-arm64-musl": ["@resvg/resvg-js-linux-arm64-musl@2.6.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg=="], 99 + 100 + "@resvg/resvg-js-linux-x64-gnu": ["@resvg/resvg-js-linux-x64-gnu@2.6.2", "", { "os": "linux", "cpu": "x64" }, "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw=="], 101 + 102 + "@resvg/resvg-js-linux-x64-musl": ["@resvg/resvg-js-linux-x64-musl@2.6.2", "", { "os": "linux", "cpu": "x64" }, "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ=="], 103 + 104 + "@resvg/resvg-js-win32-arm64-msvc": ["@resvg/resvg-js-win32-arm64-msvc@2.6.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ=="], 105 + 106 + "@resvg/resvg-js-win32-ia32-msvc": ["@resvg/resvg-js-win32-ia32-msvc@2.6.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w=="], 107 + 108 + "@resvg/resvg-js-win32-x64-msvc": ["@resvg/resvg-js-win32-x64-msvc@2.6.2", "", { "os": "win32", "cpu": "x64" }, "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ=="], 109 + 82 110 "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.5", "", { "os": "android", "cpu": "arm64" }, "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ=="], 83 111 84 112 "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw=="], ··· 110 138 "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA=="], 111 139 112 140 "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], 141 + 142 + "@shuding/opentype.js": ["@shuding/opentype.js@1.4.0-beta.0", "", { "dependencies": { "fflate": "^0.7.3", "string.prototype.codepointat": "^0.2.1" }, "bin": { "ot": "bin/ot" } }, "sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA=="], 113 143 114 144 "@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="], 115 145 ··· 187 217 188 218 "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], 189 219 220 + "base64-js": ["base64-js@0.0.8", "", {}, "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="], 221 + 190 222 "brace-expansion": ["brace-expansion@1.1.16", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw=="], 191 223 192 224 "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], 225 + 226 + "camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="], 193 227 194 228 "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], 195 229 ··· 201 235 202 236 "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], 203 237 238 + "css-background-parser": ["css-background-parser@0.1.0", "", {}, "sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA=="], 239 + 240 + "css-box-shadow": ["css-box-shadow@1.0.0-3", "", {}, "sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg=="], 241 + 242 + "css-color-keywords": ["css-color-keywords@1.0.0", "", {}, "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg=="], 243 + 244 + "css-gradient-parser": ["css-gradient-parser@0.0.17", "", {}, "sha512-w2Xy9UMMwlKtou0vlRnXvWglPAceXCTtcmVSo8ZBUvqCV5aXEFP/PC6d+I464810I9FT++UACwTD5511bmGPUg=="], 245 + 246 + "css-to-react-native": ["css-to-react-native@3.2.0", "", { "dependencies": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", "postcss-value-parser": "^4.0.2" } }, "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ=="], 247 + 204 248 "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], 205 249 206 250 "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], ··· 209 253 210 254 "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 211 255 256 + "emoji-regex-xs": ["emoji-regex-xs@2.0.1", "", {}, "sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g=="], 257 + 212 258 "enhanced-resolve": ["enhanced-resolve@5.24.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ=="], 259 + 260 + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], 213 261 214 262 "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], 215 263 ··· 237 285 238 286 "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 239 287 288 + "fflate": ["fflate@0.7.5", "", {}, "sha512-QieYf//cis6ywHNi5qW1+PXPQ4bC+XVJAtS4AXIML8P76GroEiOxm/oQtn1f02UkJY1+KsXMJcC+R2v/Eg4G3g=="], 289 + 240 290 "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], 241 291 242 292 "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], ··· 254 304 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 255 305 256 306 "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], 307 + 308 + "hex-rgb": ["hex-rgb@4.3.0", "", {}, "sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw=="], 257 309 258 310 "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], 259 311 ··· 305 357 306 358 "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], 307 359 360 + "linebreak": ["linebreak@1.1.0", "", { "dependencies": { "base64-js": "0.0.8", "unicode-trie": "^2.0.0" } }, "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ=="], 361 + 308 362 "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], 309 363 310 364 "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], ··· 325 379 326 380 "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], 327 381 382 + "pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="], 383 + 328 384 "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], 385 + 386 + "parse-css-color": ["parse-css-color@0.2.1", "", { "dependencies": { "color-name": "^1.1.4", "hex-rgb": "^4.1.0" } }, "sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg=="], 329 387 330 388 "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], 331 389 ··· 337 395 338 396 "postcss": ["postcss@8.5.22", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ=="], 339 397 398 + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], 399 + 340 400 "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], 341 401 342 402 "prettier": ["prettier@3.9.6", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g=="], ··· 352 412 "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], 353 413 354 414 "rolldown": ["rolldown@1.1.5", "", { "dependencies": { "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.5", "@rolldown/binding-darwin-arm64": "1.1.5", "@rolldown/binding-darwin-x64": "1.1.5", "@rolldown/binding-freebsd-x64": "1.1.5", "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", "@rolldown/binding-linux-arm64-gnu": "1.1.5", "@rolldown/binding-linux-arm64-musl": "1.1.5", "@rolldown/binding-linux-ppc64-gnu": "1.1.5", "@rolldown/binding-linux-s390x-gnu": "1.1.5", "@rolldown/binding-linux-x64-gnu": "1.1.5", "@rolldown/binding-linux-x64-musl": "1.1.5", "@rolldown/binding-openharmony-arm64": "1.1.5", "@rolldown/binding-wasm32-wasi": "1.1.5", "@rolldown/binding-win32-arm64-msvc": "1.1.5", "@rolldown/binding-win32-x64-msvc": "1.1.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA=="], 415 + 416 + "satori": ["satori@0.28.2", "", { "dependencies": { "@shuding/opentype.js": "1.4.0-beta.0", "css-background-parser": "^0.1.0", "css-box-shadow": "1.0.0-3", "css-gradient-parser": "^0.0.17", "css-to-react-native": "^3.0.0", "emoji-regex-xs": "^2.0.1", "escape-html": "^1.0.3", "linebreak": "^1.1.0", "parse-css-color": "^0.2.1", "postcss-value-parser": "^4.2.0", "yoga-layout": "^3.2.1" } }, "sha512-AZBuptlBvlMf+EC/WI6Ss5MJwSEstrC6Pf1o5b2keg6h1QbvjStmrnJXUDGWOrfC1KSU0FApyGhF2xnBwmJRgA=="], 355 417 356 418 "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], 357 419 ··· 363 425 364 426 "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 365 427 428 + "string.prototype.codepointat": ["string.prototype.codepointat@0.2.1", "", {}, "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="], 429 + 366 430 "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], 367 431 368 432 "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], ··· 370 434 "tailwindcss": ["tailwindcss@4.3.3", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="], 371 435 372 436 "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], 437 + 438 + "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], 373 439 374 440 "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], 375 441 ··· 385 451 386 452 "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 387 453 454 + "unicode-trie": ["unicode-trie@2.0.0", "", { "dependencies": { "pako": "^0.2.5", "tiny-inflate": "^1.0.0" } }, "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ=="], 455 + 388 456 "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], 389 457 390 458 "vite": ["vite@8.1.5", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5", "postcss": "^8.5.17", "rolldown": "~1.1.5", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw=="], ··· 394 462 "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], 395 463 396 464 "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 465 + 466 + "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], 397 467 398 468 "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], 399 469
+16 -2
index.html
··· 4 4 <head> 5 5 <meta charset="UTF-8" /> 6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 - <meta name="description" content="Your haunt on the Atmosphere" /> 8 - <link rel="icon" href="/favicon.ico" /> 7 + <meta name="description" content="A compendium of presences on the AT Protocol." /> 8 + <meta name="theme-color" content="#10090d" /> 9 + <link rel="canonical" href="https://haunt.at/" /> 10 + <link rel="icon" type="image/svg+xml" href="/haunt-sigil.svg" /> 11 + <meta property="og:type" content="website" /> 12 + <meta property="og:url" content="https://haunt.at/" /> 13 + <meta property="og:site_name" content="haunt.at" /> 14 + <meta property="og:title" content="haunt.at — a compendium of the atmosphere" /> 15 + <meta property="og:description" content="Visit a presence on the AT Protocol." /> 16 + <meta property="og:image" content="https://haunt.at/og.png" /> 17 + <meta property="og:image:alt" content="haunt.at — a compendium of the atmosphere" /> 18 + <meta name="twitter:card" content="summary" /> 19 + <meta name="twitter:title" content="haunt.at — a compendium of the atmosphere" /> 20 + <meta name="twitter:description" content="Visit a presence on the AT Protocol." /> 21 + <meta name="twitter:image" content="https://haunt.at/og.png" /> 22 + <meta name="twitter:image:alt" content="haunt.at — a compendium of the atmosphere" /> 9 23 <title>haunt</title> 10 24 </head> 11 25
+4 -1
package.json
··· 4 4 "private": true, 5 5 "type": "module", 6 6 "scripts": { 7 - "build": "tsc --noEmit && vite build", 7 + "build": "bun run generate:og && tsc --noEmit && vite build", 8 8 "check": "eslint . && tsc --noEmit", 9 9 "dev": "vite", 10 + "generate:og": "bun scripts/generate-og-image.tsx", 10 11 "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,mdx}\" --cache", 11 12 "format:write": "prettier --write \"**/*.{ts,tsx,js,jsx,mdx}\" --cache", 12 13 "lint": "eslint .", ··· 20 21 "react-dom": "^19.2.8" 21 22 }, 22 23 "devDependencies": { 24 + "@resvg/resvg-js": "^2.6.2", 23 25 "@tailwindcss/postcss": "^4.3.3", 24 26 "@types/node": "^20.19.43", 25 27 "@types/react": "^19.2.17", ··· 29 31 "postcss": "^8.5.22", 30 32 "prettier": "^3.9.6", 31 33 "prettier-plugin-tailwindcss": "^0.6.14", 34 + "satori": "^0.28.2", 32 35 "tailwindcss": "^4.3.3", 33 36 "typescript": "^5.9.3", 34 37 "typescript-eslint": "^8.65.0",
+13
public/haunt-sigil.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"> 2 + <rect width="64" height="64" rx="14" fill="#10090d"/> 3 + <g fill="none" stroke="#9cc6d1" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"> 4 + <circle cx="32" cy="32" r="24" stroke-dasharray="7 5"/> 5 + <circle cx="32" cy="32" r="18"/> 6 + <path d="M32 14 47 23v18L32 50 17 41V23Z"/> 7 + <path d="m32 20 11 24M21 39l22-14M21 25l11 24"/> 8 + </g> 9 + <circle cx="32" cy="32" r="5" fill="#9cc6d1"/> 10 + <circle cx="32" cy="14" r="3" fill="#9cc6d1"/> 11 + <circle cx="47" cy="41" r="3" fill="#9cc6d1"/> 12 + <circle cx="17" cy="41" r="3" fill="#9cc6d1"/> 13 + </svg>
public/og.png

This is a binary file and will not be displayed.

+93
scripts/OFL-SpaceMono.txt
··· 1 + Copyright 2016 The Space Mono Project Authors (https://github.com/googlefonts/spacemono) 2 + 3 + This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 + This license is copied below, and is also available with a FAQ at: 5 + http://scripts.sil.org/OFL 6 + 7 + 8 + ----------------------------------------------------------- 9 + SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 + ----------------------------------------------------------- 11 + 12 + PREAMBLE 13 + The goals of the Open Font License (OFL) are to stimulate worldwide 14 + development of collaborative font projects, to support the font creation 15 + efforts of academic and linguistic communities, and to provide a free and 16 + open framework in which fonts may be shared and improved in partnership 17 + with others. 18 + 19 + The OFL allows the licensed fonts to be used, studied, modified and 20 + redistributed freely as long as they are not sold by themselves. The 21 + fonts, including any derivative works, can be bundled, embedded, 22 + redistributed and/or sold with any software provided that any reserved 23 + names are not used by derivative works. The fonts and derivatives, 24 + however, cannot be released under any other type of license. The 25 + requirement for fonts to remain under this license does not apply 26 + to any document created using the fonts or their derivatives. 27 + 28 + DEFINITIONS 29 + "Font Software" refers to the set of files released by the Copyright 30 + Holder(s) under this license and clearly marked as such. This may 31 + include source files, build scripts and documentation. 32 + 33 + "Reserved Font Name" refers to any names specified as such after the 34 + copyright statement(s). 35 + 36 + "Original Version" refers to the collection of Font Software components as 37 + distributed by the Copyright Holder(s). 38 + 39 + "Modified Version" refers to any derivative made by adding to, deleting, 40 + or substituting -- in part or in whole -- any of the components of the 41 + Original Version, by changing formats or by porting the Font Software to a 42 + new environment. 43 + 44 + "Author" refers to any designer, engineer, programmer, technical 45 + writer or other person who contributed to the Font Software. 46 + 47 + PERMISSION & CONDITIONS 48 + Permission is hereby granted, free of charge, to any person obtaining 49 + a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 + redistribute, and sell modified and unmodified copies of the Font 51 + Software, subject to the following conditions: 52 + 53 + 1) Neither the Font Software nor any of its individual components, 54 + in Original or Modified Versions, may be sold by itself. 55 + 56 + 2) Original or Modified Versions of the Font Software may be bundled, 57 + redistributed and/or sold with any software, provided that each copy 58 + contains the above copyright notice and this license. These can be 59 + included either as stand-alone text files, human-readable headers or 60 + in the appropriate machine-readable metadata fields within text or 61 + binary files as long as those fields can be easily viewed by the user. 62 + 63 + 3) No Modified Version of the Font Software may use the Reserved Font 64 + Name(s) unless explicit written permission is granted by the corresponding 65 + Copyright Holder. This restriction only applies to the primary font name as 66 + presented to the users. 67 + 68 + 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 + Software shall not be used to promote, endorse or advertise any 70 + Modified Version, except to acknowledge the contribution(s) of the 71 + Copyright Holder(s) and the Author(s) or with their explicit written 72 + permission. 73 + 74 + 5) The Font Software, modified or unmodified, in part or in whole, 75 + must be distributed entirely under this license, and must not be 76 + distributed under any other license. The requirement for fonts to 77 + remain under this license does not apply to any document created 78 + using the Font Software. 79 + 80 + TERMINATION 81 + This license becomes null and void if any of the above conditions are 82 + not met. 83 + 84 + DISCLAIMER 85 + THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 + OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 + COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 + INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 + DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 + FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 + OTHER DEALINGS IN THE FONT SOFTWARE.
+109
scripts/generate-og-image.tsx
··· 1 + import { readFile, writeFile } from "node:fs/promises"; 2 + 3 + import { Resvg } from "@resvg/resvg-js"; 4 + import satori from "satori"; 5 + 6 + const IMAGE_WIDTH = 1200; 7 + const IMAGE_HEIGHT = 630; 8 + const font = await readFile("scripts/space-mono.ttf"); 9 + 10 + const svg = await satori( 11 + <div 12 + style={{ 13 + alignItems: "center", 14 + background: "#10090d", 15 + color: "#e8ddcc", 16 + display: "flex", 17 + height: "100%", 18 + overflow: "hidden", 19 + padding: "76px 92px", 20 + position: "relative", 21 + width: "100%", 22 + }} 23 + > 24 + <div 25 + style={{ 26 + background: "#35101f", 27 + borderRadius: "999px", 28 + height: "720px", 29 + opacity: 0.68, 30 + position: "absolute", 31 + right: "-180px", 32 + top: "-290px", 33 + width: "720px", 34 + }} 35 + /> 36 + <div style={{ display: "flex", flexDirection: "column", width: "620px" }}> 37 + <div 38 + style={{ 39 + color: "#a98e8e", 40 + display: "flex", 41 + fontSize: "20px", 42 + letterSpacing: "4px", 43 + textTransform: "uppercase", 44 + }} 45 + > 46 + An atmosphere compendium 47 + </div> 48 + <div 49 + style={{ 50 + display: "flex", 51 + fontSize: "86px", 52 + fontWeight: 650, 53 + letterSpacing: "-5px", 54 + lineHeight: 1, 55 + marginTop: "24px", 56 + }} 57 + > 58 + haunt.at 59 + </div> 60 + {/* <div 61 + style={{ 62 + color: "#c4afa8", 63 + display: "flex", 64 + fontSize: "28px", 65 + lineHeight: 1.35, 66 + marginTop: "34px", 67 + }} 68 + > 69 + Visit a presence on the AT Protocol. 70 + </div> */} 71 + </div> 72 + <Sigil /> 73 + </div>, 74 + { 75 + width: IMAGE_WIDTH, 76 + height: IMAGE_HEIGHT, 77 + fonts: [{ name: "Space Mono", data: font, weight: 400, style: "normal" }], 78 + }, 79 + ); 80 + 81 + const png = new Resvg(svg, { 82 + fitTo: { mode: "width", value: IMAGE_WIDTH }, 83 + }).render(); 84 + 85 + await writeFile("public/og.png", png.asPng()); 86 + 87 + function Sigil() { 88 + return ( 89 + <svg 90 + width="286" 91 + height="286" 92 + viewBox="0 0 120 120" 93 + fill="none" 94 + stroke="#9cc6d1" 95 + strokeLinecap="round" 96 + strokeLinejoin="round" 97 + strokeWidth="1.5" 98 + > 99 + <circle cx="60" cy="60" r="51" strokeDasharray="10 7" /> 100 + <circle cx="60" cy="60" r="39" /> 101 + <path d="M60 19 91 37v36L60 101 29 73V37Z" /> 102 + <path d="M60 31v58M35 45l50 30M85 45 35 75" /> 103 + <circle cx="60" cy="60" r="8" fill="#9cc6d1" /> 104 + <circle cx="60" cy="19" r="3" fill="#9cc6d1" /> 105 + <circle cx="91" cy="73" r="3" fill="#9cc6d1" /> 106 + <circle cx="29" cy="73" r="3" fill="#9cc6d1" /> 107 + </svg> 108 + ); 109 + }
scripts/space-mono.ttf

This is a binary file and will not be displayed.

+93 -24
src/components/actor-app.tsx
··· 1 - import type { CSSProperties, FormEvent } from "react"; 1 + import type { CSSProperties, FormEvent, ReactNode } from "react"; 2 2 import { useEffect, useRef, useState } from "react"; 3 3 4 4 import { BlueskyProfile } from "~/components/bluesky-profile"; 5 5 import { IdentityCard } from "~/components/identity-card"; 6 6 import { MasonryGrid } from "~/components/masonry-grid"; 7 - import { ProfileSection } from "~/components/profile-section"; 8 7 import { ProfileSections } from "~/components/profile-sections"; 9 8 import { resolveMiniDoc } from "~/lib/atproto/mini-doc"; 10 9 import { hashString } from "~/lib/did-random"; ··· 17 16 import type { BlueskyActorSearchResult } from "~/lib/providers/bluesky-search"; 18 17 import type { AtprotoMiniIdentity } from "~/lib/atproto/mini-doc"; 19 18 19 + type AppRoute = 20 + | { type: "home" } 21 + | { identifier: string; type: "profile" } 22 + | { type: "not-found" }; 23 + 20 24 export function ActorApp() { 21 25 const pathname = useBrowserPathname(); 22 - const identifier = getPathIdentifier(pathname); 26 + const route = getAppRoute(pathname); 27 + const identifier = route.type === "profile" ? route.identifier : null; 23 28 const [identity, setIdentity] = useState<AtprotoMiniIdentity | null>(null); 24 29 const [profile, setProfile] = useState<BlueskyProfileData | null>(null); 25 30 const [isLoading, setIsLoading] = useState(true); ··· 74 79 }, [identifier]); 75 80 76 81 if (pathname === null) { 77 - return <LoadingPage />; 82 + return <LoadingPage seed="haunt.at" />; 78 83 } 79 84 80 - if (!identifier) { 85 + if (route.type === "home") { 81 86 return <Home />; 87 + } 88 + 89 + if (route.type === "not-found") { 90 + return <NotFoundPage />; 82 91 } 83 92 84 93 if (isLoading) { 85 - return <LoadingPage />; 94 + return <LoadingPage seed={route.identifier} />; 86 95 } 87 96 88 97 if (error || !identity) { 89 - return <ErrorPage message={error ?? "Could not resolve this profile."} />; 98 + return ( 99 + <ErrorPage 100 + message={error ?? "Could not resolve this profile."} 101 + seed={route.identifier} 102 + /> 103 + ); 90 104 } 91 105 92 106 const theme = getDidTheme(identity.did).css; ··· 138 152 return pathname; 139 153 } 140 154 141 - function getPathIdentifier(pathname: string | null): string | null { 155 + function getAppRoute(pathname: string | null): AppRoute { 142 156 if (!pathname || pathname === "/") { 143 - return null; 157 + return { type: "home" }; 144 158 } 145 159 146 160 const path = pathname.replace(/^\/+|\/+$/g, ""); 147 - return path ? decodeURIComponent(path) : null; 161 + if (!path || path.includes("/")) { 162 + return { type: "not-found" }; 163 + } 164 + 165 + return { identifier: decodeURIComponent(path), type: "profile" }; 148 166 } 149 167 150 168 function Home() { ··· 402 420 } 403 421 } 404 422 405 - function LoadingPage() { 423 + type StatePageProps = { 424 + children?: ReactNode; 425 + description: string; 426 + label: string; 427 + seed: string; 428 + title: string; 429 + }; 430 + 431 + function StatePage({ 432 + label, 433 + title, 434 + description, 435 + seed, 436 + children, 437 + }: StatePageProps) { 406 438 return ( 407 - <main className="min-h-screen p-4 sm:p-8"> 408 - <div className="mx-auto max-w-2xl"> 409 - <ProfileSection title="Resolving identity"> 410 - <p className="text-sm">Scanning the Atmosphere…</p> 411 - </ProfileSection> 439 + <main className="state-page min-h-screen p-4 sm:p-8"> 440 + <div className="state-content mx-auto max-w-2xl"> 441 + <a className="state-masthead" href="/"> 442 + haunt.at 443 + </a> 444 + 445 + <section className="state-reading" aria-labelledby="state-title"> 446 + <HauntSigil seed={seed} /> 447 + <p className="state-label">{label}</p> 448 + <h1 id="state-title">{title}</h1> 449 + <p className="state-description">{description}</p> 450 + {children} 451 + </section> 412 452 </div> 413 453 </main> 414 454 ); 415 455 } 416 456 417 - function ErrorPage({ message }: { message: string }) { 457 + function LoadingPage({ seed }: { seed: string }) { 458 + return ( 459 + <StatePage 460 + label="resolving" 461 + title="Finding a presence" 462 + description="Following the public record to its source." 463 + seed={seed} 464 + /> 465 + ); 466 + } 467 + 468 + function ErrorPage({ message, seed }: { message: string; seed: string }) { 469 + return ( 470 + <StatePage 471 + label="unresolved" 472 + title="No presence found" 473 + description={message} 474 + seed={seed} 475 + > 476 + <a className="state-action" href="/"> 477 + return to haunt 478 + </a> 479 + </StatePage> 480 + ); 481 + } 482 + 483 + function NotFoundPage() { 418 484 return ( 419 - <main className="min-h-screen p-4 sm:p-8"> 420 - <div className="mx-auto max-w-2xl"> 421 - <ProfileSection title="Could not resolve profile"> 422 - <p className="text-sm">{message}</p> 423 - </ProfileSection> 424 - </div> 425 - </main> 485 + <StatePage 486 + label="lost path" 487 + title="This path leads nowhere" 488 + description="Haunt pages begin with a handle or DID." 489 + seed="lost-path" 490 + > 491 + <a className="state-action" href="/"> 492 + return to haunt 493 + </a> 494 + </StatePage> 426 495 ); 427 496 }
+61 -1
src/styles/globals.css
··· 16 16 background-size: 108% 108%; 17 17 } 18 18 19 - .home-page { 19 + .home-page, 20 + .state-page { 20 21 background-color: #10090d; 21 22 background-image: 22 23 radial-gradient(circle at 16% 12%, #2b111d 0, transparent 36rem), ··· 26 27 27 28 .home-content { 28 29 padding-top: clamp(1.5rem, 4vw, 3rem); 30 + } 31 + 32 + .state-content { 33 + padding-top: clamp(1.5rem, 4vw, 3rem); 34 + } 35 + 36 + .state-masthead { 37 + color: color-mix(in srgb, currentColor 50%, transparent); 38 + font-size: 0.75rem; 39 + letter-spacing: 0.12em; 40 + text-transform: uppercase; 41 + } 42 + 43 + .state-masthead:focus-visible, 44 + .state-action:focus-visible { 45 + outline: 2px solid #d4ae6b; 46 + outline-offset: 3px; 47 + } 48 + 49 + .state-reading { 50 + display: grid; 51 + justify-items: center; 52 + margin: clamp(7rem, 20vh, 13rem) auto 0; 53 + max-width: 25rem; 54 + text-align: center; 55 + } 56 + 57 + .state-reading .home-sigil { 58 + height: clamp(8rem, 18vw, 10rem); 59 + width: clamp(8rem, 18vw, 10rem); 60 + } 61 + 62 + .state-label { 63 + color: color-mix(in srgb, currentColor 48%, transparent); 64 + font-size: 0.6875rem; 65 + letter-spacing: 0.08em; 66 + margin-top: 0.875rem; 67 + text-transform: uppercase; 68 + } 69 + 70 + .state-reading h1 { 71 + font-size: clamp(1.5rem, 4vw, 2rem); 72 + font-weight: 650; 73 + letter-spacing: -0.035em; 74 + line-height: 1.1; 75 + margin-top: 1.5rem; 76 + } 77 + 78 + .state-description { 79 + color: color-mix(in srgb, currentColor 60%, transparent); 80 + margin-top: 0.75rem; 81 + } 82 + 83 + .state-action { 84 + border-bottom: 1px solid color-mix(in srgb, #d4ae6b 70%, transparent); 85 + color: #e8ddcc; 86 + font-size: 0.875rem; 87 + margin-top: 1.75rem; 88 + padding-bottom: 0.15rem; 29 89 } 30 90 31 91 .home-index-label {
+1 -1
tsconfig.json
··· 24 24 "~/*": ["./src/*"] 25 25 } 26 26 }, 27 - "include": ["src", "vite.config.ts"], 27 + "include": ["src", "scripts", "vite.config.ts"], 28 28 "exclude": ["node_modules", "dist"] 29 29 }