this repo has no description
0
fork

Configure Feed

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

the big merge

alice 9d747fbd 1c82b93f

+597 -45
+1 -1
.eslintrc.json
··· 1 1 { 2 - "extends": ["next/core-web-vitals", "next/typescript"] 2 + "extends": ["next/core-web-vitals", "next/typescript", "prettier"] 3 3 }
+2 -1
LICENSE
··· 1 1 MIT License Copyright (c) 2024 Samuel Newman 2 + MIT License Copyright (c) 2024 Alice (aliceisjustplaying@gmail.com) 2 3 3 4 Permission is hereby granted, free 4 5 of charge, to any person obtaining a copy of this software and associated ··· 18 19 EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 20 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 - THE SOFTWARE. 22 + THE SOFTWARE.
+4 -1
package.json
··· 1 1 { 2 - "name": "mozzius.dev", 2 + "name": "alice.bsky.sh", 3 3 "version": "0.1.0", 4 4 "private": true, 5 5 "license": "MIT", ··· 22 22 "react-markdown": "^9.0.1", 23 23 "reading-time": "^1.5.0", 24 24 "rehype-format": "^5.0.1", 25 + "rehype-raw": "^7.0.0", 25 26 "rehype-sanitize": "^6.0.0", 26 27 "rehype-stringify": "^10.0.1", 28 + "remark-gfm": "^4.0.0", 27 29 "remark-parse": "^11.0.0", 28 30 "remark-rehype": "^11.1.1", 29 31 "rss": "^1.2.2", ··· 40 42 "@types/rss": "^0.0.32", 41 43 "eslint": "^9.13.0", 42 44 "eslint-config-next": "15.0.1", 45 + "eslint-config-prettier": "^9.1.0", 43 46 "postcss": "^8", 44 47 "prettier": "^3.3.3", 45 48 "prettier-plugin-tailwindcss": "^0.6.8",
+316
pnpm-lock.yaml
··· 41 41 rehype-format: 42 42 specifier: ^5.0.1 43 43 version: 5.0.1 44 + rehype-raw: 45 + specifier: ^7.0.0 46 + version: 7.0.0 44 47 rehype-sanitize: 45 48 specifier: ^6.0.0 46 49 version: 6.0.0 47 50 rehype-stringify: 48 51 specifier: ^10.0.1 49 52 version: 10.0.1 53 + remark-gfm: 54 + specifier: ^4.0.0 55 + version: 4.0.0 50 56 remark-parse: 51 57 specifier: ^11.0.0 52 58 version: 11.0.0 ··· 90 96 eslint-config-next: 91 97 specifier: 15.0.1 92 98 version: 15.0.1(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) 99 + eslint-config-prettier: 100 + specifier: ^9.1.0 101 + version: 9.1.0(eslint@9.13.0(jiti@1.21.6)) 93 102 postcss: 94 103 specifier: ^8 95 104 version: 8.4.47 ··· 1032 1041 resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} 1033 1042 engines: {node: '>=10.13.0'} 1034 1043 1044 + entities@4.5.0: 1045 + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 1046 + engines: {node: '>=0.12'} 1047 + 1035 1048 es-abstract@1.23.3: 1036 1049 resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} 1037 1050 engines: {node: '>= 0.4'} ··· 1080 1093 resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 1081 1094 engines: {node: '>=10'} 1082 1095 1096 + escape-string-regexp@5.0.0: 1097 + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} 1098 + engines: {node: '>=12'} 1099 + 1083 1100 eslint-config-next@15.0.1: 1084 1101 resolution: {integrity: sha512-3cYCrgbH6GS/ufApza7XCKz92vtq4dAdYhx++rMFNlH2cAV+/GsAKkrr4+bohYOACmzG2nAOR+uWprKC1Uld6A==} 1085 1102 peerDependencies: ··· 1088 1105 peerDependenciesMeta: 1089 1106 typescript: 1090 1107 optional: true 1108 + 1109 + eslint-config-prettier@9.1.0: 1110 + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} 1111 + hasBin: true 1112 + peerDependencies: 1113 + eslint: '>=7.0.0' 1091 1114 1092 1115 eslint-import-resolver-node@0.3.9: 1093 1116 resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} ··· 1347 1370 hast-util-format@1.1.0: 1348 1371 resolution: {integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==} 1349 1372 1373 + hast-util-from-parse5@8.0.1: 1374 + resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} 1375 + 1350 1376 hast-util-has-property@3.0.0: 1351 1377 resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==} 1352 1378 ··· 1359 1385 hast-util-minify-whitespace@1.0.1: 1360 1386 resolution: {integrity: sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==} 1361 1387 1388 + hast-util-parse-selector@4.0.0: 1389 + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} 1390 + 1362 1391 hast-util-phrasing@3.0.1: 1363 1392 resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==} 1393 + 1394 + hast-util-raw@9.0.4: 1395 + resolution: {integrity: sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==} 1364 1396 1365 1397 hast-util-sanitize@5.0.1: 1366 1398 resolution: {integrity: sha512-IGrgWLuip4O2nq5CugXy4GI2V8kx4sFVy5Hd4vF7AR2gxS0N9s7nEAVUyeMtZKZvzrxVsHt73XdTsno1tClIkQ==} ··· 1371 1403 hast-util-to-jsx-runtime@2.3.2: 1372 1404 resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==} 1373 1405 1406 + hast-util-to-parse5@8.0.0: 1407 + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} 1408 + 1374 1409 hast-util-whitespace@3.0.0: 1375 1410 resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} 1411 + 1412 + hastscript@8.0.0: 1413 + resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} 1376 1414 1377 1415 html-url-attributes@3.0.1: 1378 1416 resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} ··· 1629 1667 resolution: {integrity: sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==} 1630 1668 peerDependencies: 1631 1669 react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc 1670 + 1671 + markdown-table@3.0.4: 1672 + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} 1673 + 1674 + mdast-util-find-and-replace@3.0.1: 1675 + resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} 1632 1676 1633 1677 mdast-util-from-markdown@2.0.1: 1634 1678 resolution: {integrity: sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==} 1635 1679 1680 + mdast-util-gfm-autolink-literal@2.0.1: 1681 + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} 1682 + 1683 + mdast-util-gfm-footnote@2.0.0: 1684 + resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} 1685 + 1686 + mdast-util-gfm-strikethrough@2.0.0: 1687 + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} 1688 + 1689 + mdast-util-gfm-table@2.0.0: 1690 + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} 1691 + 1692 + mdast-util-gfm-task-list-item@2.0.0: 1693 + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} 1694 + 1695 + mdast-util-gfm@3.0.0: 1696 + resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} 1697 + 1636 1698 mdast-util-mdx-expression@2.0.1: 1637 1699 resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} 1638 1700 ··· 1661 1723 micromark-core-commonmark@2.0.1: 1662 1724 resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==} 1663 1725 1726 + micromark-extension-gfm-autolink-literal@2.1.0: 1727 + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} 1728 + 1729 + micromark-extension-gfm-footnote@2.1.0: 1730 + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} 1731 + 1732 + micromark-extension-gfm-strikethrough@2.1.0: 1733 + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} 1734 + 1735 + micromark-extension-gfm-table@2.1.0: 1736 + resolution: {integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==} 1737 + 1738 + micromark-extension-gfm-tagfilter@2.0.0: 1739 + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} 1740 + 1741 + micromark-extension-gfm-task-list-item@2.1.0: 1742 + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} 1743 + 1744 + micromark-extension-gfm@3.0.0: 1745 + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} 1746 + 1664 1747 micromark-factory-destination@2.0.0: 1665 1748 resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} 1666 1749 ··· 1854 1937 parse-entities@4.0.1: 1855 1938 resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} 1856 1939 1940 + parse5@7.2.0: 1941 + resolution: {integrity: sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==} 1942 + 1857 1943 path-exists@4.0.0: 1858 1944 resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1859 1945 engines: {node: '>=8'} ··· 2048 2134 2049 2135 rehype-format@5.0.1: 2050 2136 resolution: {integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==} 2137 + 2138 + rehype-raw@7.0.0: 2139 + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} 2051 2140 2052 2141 rehype-sanitize@6.0.0: 2053 2142 resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} ··· 2055 2144 rehype-stringify@10.0.1: 2056 2145 resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} 2057 2146 2147 + remark-gfm@4.0.0: 2148 + resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} 2149 + 2058 2150 remark-parse@11.0.0: 2059 2151 resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} 2060 2152 2061 2153 remark-rehype@11.1.1: 2062 2154 resolution: {integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==} 2155 + 2156 + remark-stringify@11.0.0: 2157 + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} 2063 2158 2064 2159 resolve-from@4.0.0: 2065 2160 resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} ··· 2359 2454 util-deprecate@1.0.2: 2360 2455 resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 2361 2456 2457 + vfile-location@5.0.3: 2458 + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} 2459 + 2362 2460 vfile-message@4.0.2: 2363 2461 resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} 2364 2462 2365 2463 vfile@6.0.3: 2366 2464 resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} 2367 2465 2466 + web-namespaces@2.0.1: 2467 + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} 2468 + 2368 2469 which-boxed-primitive@1.0.2: 2369 2470 resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} 2370 2471 ··· 3286 3387 graceful-fs: 4.2.11 3287 3388 tapable: 2.2.1 3288 3389 3390 + entities@4.5.0: {} 3391 + 3289 3392 es-abstract@1.23.3: 3290 3393 dependencies: 3291 3394 array-buffer-byte-length: 1.0.1 ··· 3411 3514 3412 3515 escape-string-regexp@4.0.0: {} 3413 3516 3517 + escape-string-regexp@5.0.0: {} 3518 + 3414 3519 eslint-config-next@15.0.1(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3): 3415 3520 dependencies: 3416 3521 '@next/eslint-plugin-next': 15.0.1 ··· 3430 3535 - eslint-import-resolver-webpack 3431 3536 - eslint-plugin-import-x 3432 3537 - supports-color 3538 + 3539 + eslint-config-prettier@9.1.0(eslint@9.13.0(jiti@1.21.6)): 3540 + dependencies: 3541 + eslint: 9.13.0(jiti@1.21.6) 3433 3542 3434 3543 eslint-import-resolver-node@0.3.9: 3435 3544 dependencies: ··· 3777 3886 html-whitespace-sensitive-tag-names: 3.0.1 3778 3887 unist-util-visit-parents: 6.0.1 3779 3888 3889 + hast-util-from-parse5@8.0.1: 3890 + dependencies: 3891 + '@types/hast': 3.0.4 3892 + '@types/unist': 3.0.3 3893 + devlop: 1.1.0 3894 + hastscript: 8.0.0 3895 + property-information: 6.5.0 3896 + vfile: 6.0.3 3897 + vfile-location: 5.0.3 3898 + web-namespaces: 2.0.1 3899 + 3780 3900 hast-util-has-property@3.0.0: 3781 3901 dependencies: 3782 3902 '@types/hast': 3.0.4 ··· 3797 3917 hast-util-whitespace: 3.0.0 3798 3918 unist-util-is: 6.0.0 3799 3919 3920 + hast-util-parse-selector@4.0.0: 3921 + dependencies: 3922 + '@types/hast': 3.0.4 3923 + 3800 3924 hast-util-phrasing@3.0.1: 3801 3925 dependencies: 3802 3926 '@types/hast': 3.0.4 ··· 3805 3929 hast-util-is-body-ok-link: 3.0.1 3806 3930 hast-util-is-element: 3.0.0 3807 3931 3932 + hast-util-raw@9.0.4: 3933 + dependencies: 3934 + '@types/hast': 3.0.4 3935 + '@types/unist': 3.0.3 3936 + '@ungap/structured-clone': 1.2.0 3937 + hast-util-from-parse5: 8.0.1 3938 + hast-util-to-parse5: 8.0.0 3939 + html-void-elements: 3.0.0 3940 + mdast-util-to-hast: 13.2.0 3941 + parse5: 7.2.0 3942 + unist-util-position: 5.0.0 3943 + unist-util-visit: 5.0.0 3944 + vfile: 6.0.3 3945 + web-namespaces: 2.0.1 3946 + zwitch: 2.0.4 3947 + 3808 3948 hast-util-sanitize@5.0.1: 3809 3949 dependencies: 3810 3950 '@types/hast': 3.0.4 ··· 3845 3985 transitivePeerDependencies: 3846 3986 - supports-color 3847 3987 3988 + hast-util-to-parse5@8.0.0: 3989 + dependencies: 3990 + '@types/hast': 3.0.4 3991 + comma-separated-tokens: 2.0.3 3992 + devlop: 1.1.0 3993 + property-information: 6.5.0 3994 + space-separated-tokens: 2.0.2 3995 + web-namespaces: 2.0.1 3996 + zwitch: 2.0.4 3997 + 3848 3998 hast-util-whitespace@3.0.0: 3849 3999 dependencies: 3850 4000 '@types/hast': 3.0.4 4001 + 4002 + hastscript@8.0.0: 4003 + dependencies: 4004 + '@types/hast': 3.0.4 4005 + comma-separated-tokens: 2.0.3 4006 + hast-util-parse-selector: 4.0.0 4007 + property-information: 6.5.0 4008 + space-separated-tokens: 2.0.2 3851 4009 3852 4010 html-url-attributes@3.0.1: {} 3853 4011 ··· 4078 4236 dependencies: 4079 4237 react: 19.0.0-rc-1631855f-20241023 4080 4238 4239 + markdown-table@3.0.4: {} 4240 + 4241 + mdast-util-find-and-replace@3.0.1: 4242 + dependencies: 4243 + '@types/mdast': 4.0.4 4244 + escape-string-regexp: 5.0.0 4245 + unist-util-is: 6.0.0 4246 + unist-util-visit-parents: 6.0.1 4247 + 4081 4248 mdast-util-from-markdown@2.0.1: 4082 4249 dependencies: 4083 4250 '@types/mdast': 4.0.4 ··· 4092 4259 micromark-util-symbol: 2.0.0 4093 4260 micromark-util-types: 2.0.0 4094 4261 unist-util-stringify-position: 4.0.0 4262 + transitivePeerDependencies: 4263 + - supports-color 4264 + 4265 + mdast-util-gfm-autolink-literal@2.0.1: 4266 + dependencies: 4267 + '@types/mdast': 4.0.4 4268 + ccount: 2.0.1 4269 + devlop: 1.1.0 4270 + mdast-util-find-and-replace: 3.0.1 4271 + micromark-util-character: 2.1.0 4272 + 4273 + mdast-util-gfm-footnote@2.0.0: 4274 + dependencies: 4275 + '@types/mdast': 4.0.4 4276 + devlop: 1.1.0 4277 + mdast-util-from-markdown: 2.0.1 4278 + mdast-util-to-markdown: 2.1.0 4279 + micromark-util-normalize-identifier: 2.0.0 4280 + transitivePeerDependencies: 4281 + - supports-color 4282 + 4283 + mdast-util-gfm-strikethrough@2.0.0: 4284 + dependencies: 4285 + '@types/mdast': 4.0.4 4286 + mdast-util-from-markdown: 2.0.1 4287 + mdast-util-to-markdown: 2.1.0 4288 + transitivePeerDependencies: 4289 + - supports-color 4290 + 4291 + mdast-util-gfm-table@2.0.0: 4292 + dependencies: 4293 + '@types/mdast': 4.0.4 4294 + devlop: 1.1.0 4295 + markdown-table: 3.0.4 4296 + mdast-util-from-markdown: 2.0.1 4297 + mdast-util-to-markdown: 2.1.0 4298 + transitivePeerDependencies: 4299 + - supports-color 4300 + 4301 + mdast-util-gfm-task-list-item@2.0.0: 4302 + dependencies: 4303 + '@types/mdast': 4.0.4 4304 + devlop: 1.1.0 4305 + mdast-util-from-markdown: 2.0.1 4306 + mdast-util-to-markdown: 2.1.0 4307 + transitivePeerDependencies: 4308 + - supports-color 4309 + 4310 + mdast-util-gfm@3.0.0: 4311 + dependencies: 4312 + mdast-util-from-markdown: 2.0.1 4313 + mdast-util-gfm-autolink-literal: 2.0.1 4314 + mdast-util-gfm-footnote: 2.0.0 4315 + mdast-util-gfm-strikethrough: 2.0.0 4316 + mdast-util-gfm-table: 2.0.0 4317 + mdast-util-gfm-task-list-item: 2.0.0 4318 + mdast-util-to-markdown: 2.1.0 4095 4319 transitivePeerDependencies: 4096 4320 - supports-color 4097 4321 ··· 4187 4411 micromark-util-symbol: 2.0.0 4188 4412 micromark-util-types: 2.0.0 4189 4413 4414 + micromark-extension-gfm-autolink-literal@2.1.0: 4415 + dependencies: 4416 + micromark-util-character: 2.1.0 4417 + micromark-util-sanitize-uri: 2.0.0 4418 + micromark-util-symbol: 2.0.0 4419 + micromark-util-types: 2.0.0 4420 + 4421 + micromark-extension-gfm-footnote@2.1.0: 4422 + dependencies: 4423 + devlop: 1.1.0 4424 + micromark-core-commonmark: 2.0.1 4425 + micromark-factory-space: 2.0.0 4426 + micromark-util-character: 2.1.0 4427 + micromark-util-normalize-identifier: 2.0.0 4428 + micromark-util-sanitize-uri: 2.0.0 4429 + micromark-util-symbol: 2.0.0 4430 + micromark-util-types: 2.0.0 4431 + 4432 + micromark-extension-gfm-strikethrough@2.1.0: 4433 + dependencies: 4434 + devlop: 1.1.0 4435 + micromark-util-chunked: 2.0.0 4436 + micromark-util-classify-character: 2.0.0 4437 + micromark-util-resolve-all: 2.0.0 4438 + micromark-util-symbol: 2.0.0 4439 + micromark-util-types: 2.0.0 4440 + 4441 + micromark-extension-gfm-table@2.1.0: 4442 + dependencies: 4443 + devlop: 1.1.0 4444 + micromark-factory-space: 2.0.0 4445 + micromark-util-character: 2.1.0 4446 + micromark-util-symbol: 2.0.0 4447 + micromark-util-types: 2.0.0 4448 + 4449 + micromark-extension-gfm-tagfilter@2.0.0: 4450 + dependencies: 4451 + micromark-util-types: 2.0.0 4452 + 4453 + micromark-extension-gfm-task-list-item@2.1.0: 4454 + dependencies: 4455 + devlop: 1.1.0 4456 + micromark-factory-space: 2.0.0 4457 + micromark-util-character: 2.1.0 4458 + micromark-util-symbol: 2.0.0 4459 + micromark-util-types: 2.0.0 4460 + 4461 + micromark-extension-gfm@3.0.0: 4462 + dependencies: 4463 + micromark-extension-gfm-autolink-literal: 2.1.0 4464 + micromark-extension-gfm-footnote: 2.1.0 4465 + micromark-extension-gfm-strikethrough: 2.1.0 4466 + micromark-extension-gfm-table: 2.1.0 4467 + micromark-extension-gfm-tagfilter: 2.0.0 4468 + micromark-extension-gfm-task-list-item: 2.1.0 4469 + micromark-util-combine-extensions: 2.0.0 4470 + micromark-util-types: 2.0.0 4471 + 4190 4472 micromark-factory-destination@2.0.0: 4191 4473 dependencies: 4192 4474 micromark-util-character: 2.1.0 ··· 4445 4727 is-decimal: 2.0.1 4446 4728 is-hexadecimal: 2.0.1 4447 4729 4730 + parse5@7.2.0: 4731 + dependencies: 4732 + entities: 4.5.0 4733 + 4448 4734 path-exists@4.0.0: {} 4449 4735 4450 4736 path-key@3.1.1: {} ··· 4589 4875 '@types/hast': 3.0.4 4590 4876 hast-util-format: 1.1.0 4591 4877 4878 + rehype-raw@7.0.0: 4879 + dependencies: 4880 + '@types/hast': 3.0.4 4881 + hast-util-raw: 9.0.4 4882 + vfile: 6.0.3 4883 + 4592 4884 rehype-sanitize@6.0.0: 4593 4885 dependencies: 4594 4886 '@types/hast': 3.0.4 ··· 4600 4892 hast-util-to-html: 9.0.3 4601 4893 unified: 11.0.5 4602 4894 4895 + remark-gfm@4.0.0: 4896 + dependencies: 4897 + '@types/mdast': 4.0.4 4898 + mdast-util-gfm: 3.0.0 4899 + micromark-extension-gfm: 3.0.0 4900 + remark-parse: 11.0.0 4901 + remark-stringify: 11.0.0 4902 + unified: 11.0.5 4903 + transitivePeerDependencies: 4904 + - supports-color 4905 + 4603 4906 remark-parse@11.0.0: 4604 4907 dependencies: 4605 4908 '@types/mdast': 4.0.4 ··· 4616 4919 mdast-util-to-hast: 13.2.0 4617 4920 unified: 11.0.5 4618 4921 vfile: 6.0.3 4922 + 4923 + remark-stringify@11.0.0: 4924 + dependencies: 4925 + '@types/mdast': 4.0.4 4926 + mdast-util-to-markdown: 2.1.0 4927 + unified: 11.0.5 4619 4928 4620 4929 resolve-from@4.0.0: {} 4621 4930 ··· 5015 5324 5016 5325 util-deprecate@1.0.2: {} 5017 5326 5327 + vfile-location@5.0.3: 5328 + dependencies: 5329 + '@types/unist': 3.0.3 5330 + vfile: 6.0.3 5331 + 5018 5332 vfile-message@4.0.2: 5019 5333 dependencies: 5020 5334 '@types/unist': 3.0.3 ··· 5024 5338 dependencies: 5025 5339 '@types/unist': 3.0.3 5026 5340 vfile-message: 4.0.2 5341 + 5342 + web-namespaces@2.0.1: {} 5027 5343 5028 5344 which-boxed-primitive@1.0.2: 5029 5345 dependencies:
+21 -2
src/app/opengraph-image.tsx
··· 1 1 import { ImageResponse } from "next/og"; 2 2 import { DESCRIPTION, HOSTNAME } from "#/lib/config"; 3 + import { loadGoogleFont } from "#/lib/google-font"; 3 4 4 5 export const size = { 5 6 width: 1200, ··· 8 9 export const contentType = "image/png"; 9 10 10 11 export default async function OpenGraphImage() { 12 + const fontData = await loadGoogleFont( 13 + "Libre+Baskerville:ital@1", 14 + `${HOSTNAME} ${DESCRIPTION}`, 15 + ); 16 + 11 17 return new ImageResponse( 12 18 ( 13 19 <div tw="h-full w-full bg-white flex flex-col justify-center items-center"> ··· 15 21 style={{ 16 22 fontFamily: "sans", 17 23 fontSize: 80, 18 - textTransform: "uppercase", 19 24 fontWeight: 700, 20 25 fontStyle: "oblique", 21 26 }} 22 27 > 23 28 {HOSTNAME} 24 29 </h1> 25 - <h1 style={{ fontSize: 32 }}>{DESCRIPTION}</h1> 30 + <h1 31 + style={{ 32 + fontSize: 32, 33 + fontStyle: "italic", 34 + fontFamily: '"Libre Baskerville"', 35 + }} 36 + > 37 + {DESCRIPTION} 38 + </h1> 26 39 </div> 27 40 ), 28 41 { 29 42 ...size, 43 + fonts: [ 44 + { 45 + name: "Libre Baskerville", 46 + data: fontData, 47 + }, 48 + ], 30 49 }, 31 50 ); 32 51 }
+1 -1
src/app/page.tsx
··· 8 8 9 9 export default function Home() { 10 10 return ( 11 - <div className="xs:px-8 grid min-h-dvh grid-rows-[20px_1fr_20px] justify-items-center px-4 py-2 pb-20 sm:p-8"> 11 + <div className="xs:px-8 grid min-h-dvh grid-rows-[20px_1fr_20px] justify-items-center px-4 py-2 pb-20 sm:p-8 gap-16"> 12 12 <main className="row-start-2 flex w-full max-w-[400px] flex-col items-center gap-8 sm:items-start"> 13 13 <div> 14 14 <Title level="h1" className="m-0 flex flex-row justify-end">
+61
src/app/post/[rkey]/opengraph-image.tsx
··· 1 + import { ImageResponse } from "next/og"; 2 + import { getPost } from "#/lib/api"; 3 + import { loadGoogleFont } from "#/lib/google-font"; 4 + import { DESCRIPTION, HOSTNAME } from "#/lib/config"; 5 + 6 + export const size = { 7 + width: 1200, 8 + height: 630, 9 + }; 10 + export const contentType = "image/png"; 11 + 12 + export default async function OpenGraphImage({ 13 + params, 14 + }: { 15 + params: Promise<{ rkey: string }>; 16 + }) { 17 + const { rkey } = await params; 18 + 19 + const post = await getPost(rkey); 20 + 21 + const fontData = await loadGoogleFont( 22 + "Libre+Baskerville:ital@1", 23 + HOSTNAME + post.value.title?.toLocaleUpperCase(), 24 + ); 25 + 26 + return new ImageResponse( 27 + ( 28 + <div tw="h-full w-full bg-white flex flex-col justify-center items-center px-20"> 29 + <h1 30 + style={{ 31 + fontFamily: '"Libre Baskerville"', 32 + fontSize: 80, 33 + fontWeight: 700, 34 + fontStyle: "italic", 35 + textAlign: "center", 36 + }} 37 + > 38 + {post.value.title?.toLocaleUpperCase()} 39 + </h1> 40 + <h1 41 + style={{ 42 + fontSize: 32, 43 + fontStyle: "italic", 44 + fontFamily: '"Libre Baskerville"', 45 + }} 46 + > 47 + {HOSTNAME} 48 + </h1> 49 + </div> 50 + ), 51 + { 52 + ...size, 53 + fonts: [ 54 + { 55 + name: "Libre Baskerville", 56 + data: fontData, 57 + }, 58 + ], 59 + }, 60 + ); 61 + }
+43 -37
src/app/post/[rkey]/page.tsx
··· 1 - import { type ComWhtwndBlogEntry } from "@atcute/client/lexicons"; 2 1 import { Code as SyntaxHighlighter } from "bright"; 3 2 import { ArrowLeftIcon } from "lucide-react"; 4 3 import { type Metadata } from "next"; ··· 6 5 import Link from "next/link"; 7 6 import Markdown from "react-markdown"; 8 7 import readingTime from "reading-time"; 9 - import rehypeSanitize from "rehype-sanitize"; 8 + import rehypeRaw from "rehype-raw"; 9 + import rehypeSanitize, { defaultSchema } from "rehype-sanitize"; 10 + import remarkGfm from "remark-gfm"; 11 + import { BlueskyPostEmbed } from "#/components/bluesky-embed"; 10 12 import { Footer } from "#/components/footer"; 11 13 import { PostInfo } from "#/components/post-info"; 12 14 import { Code, Paragraph, Title } from "#/components/typography"; 13 - import { getPosts } from "#/lib/api"; 14 - import { bsky } from "#/lib/bsky"; 15 - import { AUTHOR_NAME, HOSTNAME, MY_DID } from "#/lib/config"; 15 + import { getPost, getPosts } from "#/lib/api"; 16 + import { MY_DID, HOSTNAME, DESCRIPTION, AUTHOR_NAME } from "#/lib/config"; 16 17 17 18 export const dynamic = "force-static"; 18 19 export const revalidate = 3600; // 1 hour ··· 24 25 }): Promise<Metadata> { 25 26 const { rkey } = await params; 26 27 27 - const post = await bsky.get("com.atproto.repo.getRecord", { 28 - params: { 29 - repo: MY_DID, 30 - rkey: rkey, 31 - collection: "com.whtwnd.blog.entry", 32 - }, 33 - }); 34 - 35 - const entry = post.data.value as ComWhtwndBlogEntry.Record; 28 + const post = await getPost(rkey); 36 29 37 30 return { 38 - title: entry.title + ` — ${HOSTNAME}`, 31 + title: post.value.title + ` — ${HOSTNAME}`, 39 32 authors: [{ name: AUTHOR_NAME, url: `https://bsky.app/profile/${MY_DID}` }], 40 - description: `by ${AUTHOR_NAME} · ${readingTime(entry.content).text}`, 33 + description: `by ${AUTHOR_NAME} · ${readingTime(post.value.content).text}`, 41 34 }; 42 35 } 43 36 ··· 48 41 }) { 49 42 const { rkey } = await params; 50 43 51 - const post = await bsky.get("com.atproto.repo.getRecord", { 52 - params: { 53 - repo: MY_DID, 54 - rkey: rkey, 55 - collection: "com.whtwnd.blog.entry", 56 - }, 57 - }); 58 - 59 - const entry = post.data.value as ComWhtwndBlogEntry.Record; 44 + const post = await getPost(rkey); 60 45 61 46 return ( 62 - <div className="xs:px-8 grid min-h-dvh grid-rows-[10px_1fr_20px] justify-items-center px-4 py-2 pb-20 sm:p-8"> 47 + <div className="xs:px-8 grid min-h-dvh grid-rows-[10px_1fr_20px] justify-items-center px-4 py-2 pb-20 sm:p-8 gap-16"> 48 + <link rel="alternate" href={post.uri} /> 63 49 <main className="row-start-2 flex w-full max-w-[600px] flex-col items-center gap-0 overflow-hidden sm:items-start"> 64 50 <article className="w-full space-y-4"> 65 51 <div className="w-full space-y-4"> ··· 70 56 <ArrowLeftIcon className="mb-px mr-1 inline size-4 align-middle" /> 71 57 Back 72 58 </Link> 73 - <Title>{entry.title}</Title> 59 + <Title>{post.value.title}</Title> 74 60 <PostInfo 75 - content={entry.content} 76 - createdAt={entry.createdAt} 61 + content={post.value.content} 62 + createdAt={post.value.createdAt} 77 63 includeAuthor 78 64 /> 79 65 <hr className="border-slate-800/10 dark:border-slate-100/10" /> 80 66 </div> 81 67 <Markdown 82 - rehypePlugins={[rehypeSanitize]} 68 + remarkPlugins={[remarkGfm]} 69 + remarkRehypeOptions={{ allowDangerousHtml: true }} 70 + rehypePlugins={[ 71 + rehypeRaw, 72 + [ 73 + rehypeSanitize, 74 + { 75 + ...defaultSchema, 76 + attributes: { 77 + ...defaultSchema.attributes, 78 + blockquote: [ 79 + ...(defaultSchema.attributes?.blockquote ?? []), 80 + "dataBlueskyUri", 81 + "dataBlueskyCid", 82 + ], 83 + }, 84 + } satisfies typeof defaultSchema, 85 + ], 86 + ]} 87 + className="[&>.bluesky-embed]:mb-0 [&>.bluesky-embed]:mt-8" 83 88 components={{ 84 89 h1: (props) => <Title level="h1" {...props} />, 85 90 h2: (props) => <Title level="h2" {...props} />, ··· 93 98 {...props} 94 99 /> 95 100 ), 96 - blockquote: (props) => ( 97 - <blockquote 98 - className="mt-6 border-l-2 border-slate-200 pl-4 italic text-slate-600 dark:border-slate-800 dark:text-slate-400" 99 - {...props} 100 - /> 101 - ), 101 + blockquote: (props) => 102 + "data-bluesky-uri" in props ? 103 + <BlueskyPostEmbed uri={props["data-bluesky-uri"] as string} /> 104 + : <blockquote 105 + className="mt-6 border-l-2 border-slate-200 pl-4 italic text-slate-600 dark:border-slate-800 dark:text-slate-400" 106 + {...props} 107 + />, 102 108 ul: (props) => ( 103 109 <ul 104 110 className="my-6 ml-6 list-disc [&>li]:mt-2 [&>ol]:my-2 [&>ul]:my-2" ··· 149 155 ), 150 156 }} 151 157 > 152 - {entry.content} 158 + {post.value.content} 153 159 </Markdown> 154 160 </article> 155 161 </main>
+16
src/app/post/[rkey]/purge/actions.ts
··· 1 + "use server"; 2 + 3 + import { revalidatePath } from "next/cache"; 4 + import { redirect } from "next/navigation"; 5 + 6 + export async function purgeCache(formData: FormData) { 7 + const rkey = formData.get("rkey")!; 8 + const password = formData.get("password"); 9 + if (password === process.env.PURGE_PASSWORD) { 10 + revalidatePath(`/post/${rkey}`, "page"); 11 + redirect(`/post/${rkey}`); 12 + } else { 13 + console.error(`Invalid password: ${password}`); 14 + redirect(`/post/${rkey}/purge?error=yeah`); 15 + } 16 + }
+31
src/app/post/[rkey]/purge/page.tsx
··· 1 + import { purgeCache } from "./actions"; 2 + 3 + export default async function Purge({ 4 + params, 5 + searchParams, 6 + }: { 7 + params: Promise<{ rkey: string }>; 8 + searchParams: Promise<{ error?: string }>; 9 + }) { 10 + const { rkey } = await params; 11 + const { error } = await searchParams; 12 + return ( 13 + <form 14 + action={purgeCache} 15 + method="post" 16 + className="mx-auto mt-24 flex max-w-96 flex-col gap-4 p-2" 17 + > 18 + <input type="hidden" name="rkey" value={rkey} /> 19 + <input 20 + type="password" 21 + name="password" 22 + placeholder="Password" 23 + className="rounded-xs border p-2" 24 + /> 25 + {error && <p className="text-red-500">wrong password mate</p>} 26 + <button type="submit" className="rounded-xs border p-2"> 27 + Purge 28 + </button> 29 + </form> 30 + ); 31 + }
+1 -1
src/app/purge/actions.ts
··· 11 11 redirect("/"); 12 12 } else { 13 13 console.error(`Invalid password: ${password}`); 14 - redirect("/?error=yeah"); 14 + redirect("/purge?error=yeah"); 15 15 } 16 16 }
+64
src/components/bluesky-embed.tsx
··· 1 + "use client"; 2 + 3 + import { usePathname } from "next/navigation"; 4 + import { useEffect, useId, useState } from "react"; 5 + 6 + const EMBED_URL = "https://embed.bsky.app"; 7 + 8 + export function BlueskyPostEmbed({ uri }: { uri: string }) { 9 + const id = useId(); 10 + const pathname = usePathname(); 11 + const [height, setHeight] = useState(0); 12 + 13 + useEffect(() => { 14 + const abortController = new AbortController(); 15 + const { signal } = abortController; 16 + window.addEventListener( 17 + "message", 18 + (event) => { 19 + if (event.origin !== EMBED_URL) { 20 + return; 21 + } 22 + 23 + const iframeId = (event.data as { id: string }).id; 24 + if (id !== iframeId) { 25 + return; 26 + } 27 + 28 + const internalHeight = (event.data as { height: number }).height; 29 + if (internalHeight && typeof internalHeight === "number") { 30 + setHeight(internalHeight); 31 + } 32 + }, 33 + { signal }, 34 + ); 35 + 36 + return () => { 37 + abortController.abort(); 38 + }; 39 + }, [id]); 40 + 41 + const ref_url = 42 + "https://" + process.env.VERCEL_PROJECT_PRODUCTION_URL + pathname; 43 + 44 + const searchParams = new URLSearchParams(); 45 + searchParams.set("id", id); 46 + searchParams.set("ref_url", encodeURIComponent(ref_url)); 47 + 48 + return ( 49 + <div 50 + className="bluesky-embed mt-6 flex w-full max-w-[600px]" 51 + data-uri={uri} 52 + > 53 + <iframe 54 + className="block w-full flex-grow border-none" 55 + style={{ height }} 56 + data-bluesky-uri={uri} 57 + src={`${EMBED_URL}/embed/${uri.slice("at://".length)}?${searchParams.toString()}`} 58 + width="100%" 59 + frameBorder="0" 60 + scrolling="no" 61 + /> 62 + </div> 63 + ); 64 + }
+14
src/lib/api.ts
··· 38 38 const post = record.value as ComWhtwndBlogEntry.Record; 39 39 return post.visibility === "public"; 40 40 } 41 + 42 + export async function getPost(rkey: string) { 43 + const post = await bsky.get("com.atproto.repo.getRecord", { 44 + params: { 45 + repo: MY_DID, 46 + rkey: rkey, 47 + collection: "com.whtwnd.blog.entry", 48 + }, 49 + }); 50 + 51 + return post.data as ComAtprotoRepoListRecords.Record & { 52 + value: ComWhtwndBlogEntry.Record; 53 + }; 54 + }
+1 -1
src/lib/config.ts
··· 1 1 export const MY_DID = "did:plc:by3jhwdqgbtrcc7q4tkkv3cf"; 2 2 export const MY_PDS = "oyster.us-east.host.bsky.network"; 3 - export const AUTHOR_NAME = "alice"; 3 + export const AUTHOR_NAME = "Alice"; 4 4 export const HOSTNAME = "alice.bsky.sh"; 5 5 export const TITLE = "alice.bsky.sh"; 6 6 export const DESCRIPTION =
+21
src/lib/google-font.ts
··· 1 + // from https://github.com/kosei28/vercel-og-google-fonts/blob/main/src/utils/font.ts 2 + export async function loadGoogleFont(font: string, text: string) { 3 + const url = `https://fonts.googleapis.com/css2?family=${font}&text=${encodeURIComponent( 4 + text, 5 + )}`; 6 + 7 + const css = await (await fetch(url)).text(); 8 + 9 + const resource = css.match( 10 + /src: url\((.+)\) format\('(opentype|truetype)'\)/, 11 + ); 12 + 13 + if (resource) { 14 + const res = await fetch(resource[1]); 15 + if (res.status == 200) { 16 + return await res.arrayBuffer(); 17 + } 18 + } 19 + 20 + throw new Error("failed to load font data"); 21 + }