this repo has no description
0
fork

Configure Feed

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

prettier, eslint

alice a3dac852 ccdcda4e

+409 -61
+12
.editorconfig
··· 1 + # EditorConfig is awesome: https://EditorConfig.org 2 + 3 + # top-most EditorConfig file 4 + root = true 5 + 6 + [*] 7 + indent_style = space 8 + indent_size = 2 9 + end_of_line = lf 10 + charset = utf-8 11 + trim_trailing_whitespace = false 12 + insert_final_newline = true
+1
.prettierignore
··· 1 + pnpm-lock.yaml
+10
.prettierrc
··· 1 + { 2 + "trailingComma": "all", 3 + "tabWidth": 2, 4 + "semi": true, 5 + "singleQuote": false, 6 + "printWidth": 80, 7 + "experimentalTernaries": true, 8 + "plugins": ["@ianvs/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"], 9 + "importOrder": ["<BUILTIN_MODULES>", "<THIRD_PARTY_MODULES>", "#/(.*)$", "", "^[./]"] 10 + }
+4 -1
package.json
··· 33 33 }, 34 34 "devDependencies": { 35 35 "@ianvs/prettier-plugin-sort-imports": "^4.3.1", 36 + "@next/eslint-plugin-next": "^15.0.1", 36 37 "@types/node": "^22.7.9", 37 38 "@types/react": "^18", 38 39 "@types/react-dom": "^18", ··· 43 44 "prettier": "^3.3.3", 44 45 "prettier-plugin-tailwindcss": "^0.6.8", 45 46 "tailwindcss": "^3.4.1", 46 - "typescript": "^5" 47 + "tsx": "^4", 48 + "typescript": "^5", 49 + "typescript-eslint": "^8.11.0" 47 50 }, 48 51 "packageManager": "pnpm@9.12.2" 49 52 }
+289
pnpm-lock.yaml
··· 69 69 '@ianvs/prettier-plugin-sort-imports': 70 70 specifier: ^4.3.1 71 71 version: 4.3.1(prettier@3.3.3) 72 + '@next/eslint-plugin-next': 73 + specifier: ^15.0.1 74 + version: 15.0.1 72 75 '@types/node': 73 76 specifier: ^22.7.9 74 77 version: 22.7.9 ··· 99 102 tailwindcss: 100 103 specifier: ^3.4.1 101 104 version: 3.4.14 105 + tsx: 106 + specifier: ^4 107 + version: 4.19.1 102 108 typescript: 103 109 specifier: ^5 104 110 version: 5.6.3 111 + typescript-eslint: 112 + specifier: ^8.11.0 113 + version: 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) 105 114 106 115 packages: 107 116 ··· 198 207 '@emnapi/runtime@1.3.1': 199 208 resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} 200 209 210 + '@esbuild/aix-ppc64@0.23.1': 211 + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} 212 + engines: {node: '>=18'} 213 + cpu: [ppc64] 214 + os: [aix] 215 + 216 + '@esbuild/android-arm64@0.23.1': 217 + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} 218 + engines: {node: '>=18'} 219 + cpu: [arm64] 220 + os: [android] 221 + 222 + '@esbuild/android-arm@0.23.1': 223 + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} 224 + engines: {node: '>=18'} 225 + cpu: [arm] 226 + os: [android] 227 + 228 + '@esbuild/android-x64@0.23.1': 229 + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} 230 + engines: {node: '>=18'} 231 + cpu: [x64] 232 + os: [android] 233 + 234 + '@esbuild/darwin-arm64@0.23.1': 235 + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} 236 + engines: {node: '>=18'} 237 + cpu: [arm64] 238 + os: [darwin] 239 + 240 + '@esbuild/darwin-x64@0.23.1': 241 + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} 242 + engines: {node: '>=18'} 243 + cpu: [x64] 244 + os: [darwin] 245 + 246 + '@esbuild/freebsd-arm64@0.23.1': 247 + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} 248 + engines: {node: '>=18'} 249 + cpu: [arm64] 250 + os: [freebsd] 251 + 252 + '@esbuild/freebsd-x64@0.23.1': 253 + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} 254 + engines: {node: '>=18'} 255 + cpu: [x64] 256 + os: [freebsd] 257 + 258 + '@esbuild/linux-arm64@0.23.1': 259 + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} 260 + engines: {node: '>=18'} 261 + cpu: [arm64] 262 + os: [linux] 263 + 264 + '@esbuild/linux-arm@0.23.1': 265 + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} 266 + engines: {node: '>=18'} 267 + cpu: [arm] 268 + os: [linux] 269 + 270 + '@esbuild/linux-ia32@0.23.1': 271 + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} 272 + engines: {node: '>=18'} 273 + cpu: [ia32] 274 + os: [linux] 275 + 276 + '@esbuild/linux-loong64@0.23.1': 277 + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} 278 + engines: {node: '>=18'} 279 + cpu: [loong64] 280 + os: [linux] 281 + 282 + '@esbuild/linux-mips64el@0.23.1': 283 + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} 284 + engines: {node: '>=18'} 285 + cpu: [mips64el] 286 + os: [linux] 287 + 288 + '@esbuild/linux-ppc64@0.23.1': 289 + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} 290 + engines: {node: '>=18'} 291 + cpu: [ppc64] 292 + os: [linux] 293 + 294 + '@esbuild/linux-riscv64@0.23.1': 295 + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} 296 + engines: {node: '>=18'} 297 + cpu: [riscv64] 298 + os: [linux] 299 + 300 + '@esbuild/linux-s390x@0.23.1': 301 + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} 302 + engines: {node: '>=18'} 303 + cpu: [s390x] 304 + os: [linux] 305 + 306 + '@esbuild/linux-x64@0.23.1': 307 + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} 308 + engines: {node: '>=18'} 309 + cpu: [x64] 310 + os: [linux] 311 + 312 + '@esbuild/netbsd-x64@0.23.1': 313 + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} 314 + engines: {node: '>=18'} 315 + cpu: [x64] 316 + os: [netbsd] 317 + 318 + '@esbuild/openbsd-arm64@0.23.1': 319 + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} 320 + engines: {node: '>=18'} 321 + cpu: [arm64] 322 + os: [openbsd] 323 + 324 + '@esbuild/openbsd-x64@0.23.1': 325 + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} 326 + engines: {node: '>=18'} 327 + cpu: [x64] 328 + os: [openbsd] 329 + 330 + '@esbuild/sunos-x64@0.23.1': 331 + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} 332 + engines: {node: '>=18'} 333 + cpu: [x64] 334 + os: [sunos] 335 + 336 + '@esbuild/win32-arm64@0.23.1': 337 + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} 338 + engines: {node: '>=18'} 339 + cpu: [arm64] 340 + os: [win32] 341 + 342 + '@esbuild/win32-ia32@0.23.1': 343 + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} 344 + engines: {node: '>=18'} 345 + cpu: [ia32] 346 + os: [win32] 347 + 348 + '@esbuild/win32-x64@0.23.1': 349 + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} 350 + engines: {node: '>=18'} 351 + cpu: [x64] 352 + os: [win32] 353 + 201 354 '@eslint-community/eslint-utils@4.4.0': 202 355 resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} 203 356 engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} ··· 909 1062 es-to-primitive@1.2.1: 910 1063 resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} 911 1064 engines: {node: '>= 0.4'} 1065 + 1066 + esbuild@0.23.1: 1067 + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} 1068 + engines: {node: '>=18'} 1069 + hasBin: true 912 1070 913 1071 escalade@3.2.0: 914 1072 resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} ··· 2126 2284 tslib@2.8.0: 2127 2285 resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} 2128 2286 2287 + tsx@4.19.1: 2288 + resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} 2289 + engines: {node: '>=18.0.0'} 2290 + hasBin: true 2291 + 2129 2292 type-check@0.4.0: 2130 2293 resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 2131 2294 engines: {node: '>= 0.8.0'} ··· 2145 2308 typed-array-length@1.0.6: 2146 2309 resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} 2147 2310 engines: {node: '>= 0.4'} 2311 + 2312 + typescript-eslint@8.11.0: 2313 + resolution: {integrity: sha512-cBRGnW3FSlxaYwU8KfAewxFK5uzeOAp0l2KebIlPDOT5olVi65KDG/yjBooPBG0kGW/HLkoz1c/iuBFehcS3IA==} 2314 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 2315 + peerDependencies: 2316 + typescript: '*' 2317 + peerDependenciesMeta: 2318 + typescript: 2319 + optional: true 2148 2320 2149 2321 typescript@5.6.3: 2150 2322 resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} ··· 2376 2548 tslib: 2.8.0 2377 2549 optional: true 2378 2550 2551 + '@esbuild/aix-ppc64@0.23.1': 2552 + optional: true 2553 + 2554 + '@esbuild/android-arm64@0.23.1': 2555 + optional: true 2556 + 2557 + '@esbuild/android-arm@0.23.1': 2558 + optional: true 2559 + 2560 + '@esbuild/android-x64@0.23.1': 2561 + optional: true 2562 + 2563 + '@esbuild/darwin-arm64@0.23.1': 2564 + optional: true 2565 + 2566 + '@esbuild/darwin-x64@0.23.1': 2567 + optional: true 2568 + 2569 + '@esbuild/freebsd-arm64@0.23.1': 2570 + optional: true 2571 + 2572 + '@esbuild/freebsd-x64@0.23.1': 2573 + optional: true 2574 + 2575 + '@esbuild/linux-arm64@0.23.1': 2576 + optional: true 2577 + 2578 + '@esbuild/linux-arm@0.23.1': 2579 + optional: true 2580 + 2581 + '@esbuild/linux-ia32@0.23.1': 2582 + optional: true 2583 + 2584 + '@esbuild/linux-loong64@0.23.1': 2585 + optional: true 2586 + 2587 + '@esbuild/linux-mips64el@0.23.1': 2588 + optional: true 2589 + 2590 + '@esbuild/linux-ppc64@0.23.1': 2591 + optional: true 2592 + 2593 + '@esbuild/linux-riscv64@0.23.1': 2594 + optional: true 2595 + 2596 + '@esbuild/linux-s390x@0.23.1': 2597 + optional: true 2598 + 2599 + '@esbuild/linux-x64@0.23.1': 2600 + optional: true 2601 + 2602 + '@esbuild/netbsd-x64@0.23.1': 2603 + optional: true 2604 + 2605 + '@esbuild/openbsd-arm64@0.23.1': 2606 + optional: true 2607 + 2608 + '@esbuild/openbsd-x64@0.23.1': 2609 + optional: true 2610 + 2611 + '@esbuild/sunos-x64@0.23.1': 2612 + optional: true 2613 + 2614 + '@esbuild/win32-arm64@0.23.1': 2615 + optional: true 2616 + 2617 + '@esbuild/win32-ia32@0.23.1': 2618 + optional: true 2619 + 2620 + '@esbuild/win32-x64@0.23.1': 2621 + optional: true 2622 + 2379 2623 '@eslint-community/eslint-utils@4.4.0(eslint@9.13.0(jiti@1.21.6))': 2380 2624 dependencies: 2381 2625 eslint: 9.13.0(jiti@1.21.6) ··· 3133 3377 is-callable: 1.2.7 3134 3378 is-date-object: 1.0.5 3135 3379 is-symbol: 1.0.4 3380 + 3381 + esbuild@0.23.1: 3382 + optionalDependencies: 3383 + '@esbuild/aix-ppc64': 0.23.1 3384 + '@esbuild/android-arm': 0.23.1 3385 + '@esbuild/android-arm64': 0.23.1 3386 + '@esbuild/android-x64': 0.23.1 3387 + '@esbuild/darwin-arm64': 0.23.1 3388 + '@esbuild/darwin-x64': 0.23.1 3389 + '@esbuild/freebsd-arm64': 0.23.1 3390 + '@esbuild/freebsd-x64': 0.23.1 3391 + '@esbuild/linux-arm': 0.23.1 3392 + '@esbuild/linux-arm64': 0.23.1 3393 + '@esbuild/linux-ia32': 0.23.1 3394 + '@esbuild/linux-loong64': 0.23.1 3395 + '@esbuild/linux-mips64el': 0.23.1 3396 + '@esbuild/linux-ppc64': 0.23.1 3397 + '@esbuild/linux-riscv64': 0.23.1 3398 + '@esbuild/linux-s390x': 0.23.1 3399 + '@esbuild/linux-x64': 0.23.1 3400 + '@esbuild/netbsd-x64': 0.23.1 3401 + '@esbuild/openbsd-arm64': 0.23.1 3402 + '@esbuild/openbsd-x64': 0.23.1 3403 + '@esbuild/sunos-x64': 0.23.1 3404 + '@esbuild/win32-arm64': 0.23.1 3405 + '@esbuild/win32-ia32': 0.23.1 3406 + '@esbuild/win32-x64': 0.23.1 3136 3407 3137 3408 escalade@3.2.0: {} 3138 3409 ··· 4634 4905 4635 4906 tslib@2.8.0: {} 4636 4907 4908 + tsx@4.19.1: 4909 + dependencies: 4910 + esbuild: 0.23.1 4911 + get-tsconfig: 4.8.1 4912 + optionalDependencies: 4913 + fsevents: 2.3.3 4914 + 4637 4915 type-check@0.4.0: 4638 4916 dependencies: 4639 4917 prelude-ls: 1.2.1 ··· 4669 4947 has-proto: 1.0.3 4670 4948 is-typed-array: 1.1.13 4671 4949 possible-typed-array-names: 1.0.0 4950 + 4951 + typescript-eslint@8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3): 4952 + dependencies: 4953 + '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) 4954 + '@typescript-eslint/parser': 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) 4955 + '@typescript-eslint/utils': 8.11.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) 4956 + optionalDependencies: 4957 + typescript: 5.6.3 4958 + transitivePeerDependencies: 4959 + - eslint 4960 + - supports-color 4672 4961 4673 4962 typescript@5.6.3: {} 4674 4963
+2 -2
src/app/layout.tsx
··· 2 2 import NextPlausible from "next-plausible"; 3 3 import { Inter, Libre_Baskerville } from "next/font/google"; 4 4 import localFont from "next/font/local"; 5 - 6 5 import { cx } from "#/lib/cx"; 7 6 8 7 import "./globals.css"; 8 + 9 9 import { HOSTNAME } from "#/lib/config"; 10 10 11 11 const sans = Inter({ ··· 58 58 sans.variable, 59 59 serif.variable, 60 60 mono.variable, 61 - "antialiased font-sans", 61 + "font-sans antialiased", 62 62 )} 63 63 > 64 64 {children}
+6 -4
src/app/page.tsx
··· 8 8 9 9 export default function Home() { 10 10 return ( 11 - <div className="grid grid-rows-[20px_1fr_20px] justify-items-center min-h-dvh py-2 px-4 xs:px-8 pb-20 sm:p-8"> 12 - <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start w-full max-w-[400px]"> 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"> 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"> 15 15 {TITLE} 16 16 </Title> 17 - <span className="font-bold text-xs flex flex-row justify-end">{DESCRIPTION}</span> 17 + <span className="flex flex-row justify-end text-xs font-bold"> 18 + {DESCRIPTION} 19 + </span> 18 20 </div> 19 21 20 - <div className="flex flex-col gap-4 w-full"> 22 + <div className="flex w-full flex-col gap-4"> 21 23 <PostList /> 22 24 </div> 23 25 </main>
+17 -17
src/app/post/[rkey]/page.tsx
··· 1 + import { type ComWhtwndBlogEntry } from "@atcute/client/lexicons"; 2 + import { Code as SyntaxHighlighter } from "bright"; 1 3 import { ArrowLeftIcon } from "lucide-react"; 2 - import Markdown from "react-markdown"; 3 4 import { type Metadata } from "next"; 4 5 import Image from "next/image"; 5 6 import Link from "next/link"; 6 - import { type ComWhtwndBlogEntry } from "@atcute/client/lexicons"; 7 - import { Code as SyntaxHighlighter } from "bright"; 7 + import Markdown from "react-markdown"; 8 8 import readingTime from "reading-time"; 9 9 import rehypeSanitize from "rehype-sanitize"; 10 - 11 10 import { Footer } from "#/components/footer"; 12 11 import { PostInfo } from "#/components/post-info"; 13 12 import { Code, Paragraph, Title } from "#/components/typography"; 14 13 import { getPosts } from "#/lib/api"; 15 14 import { bsky } from "#/lib/bsky"; 16 - import { HOSTNAME, MY_DID } from "#/lib/config"; 15 + import { AUTHOR_NAME, HOSTNAME, MY_DID } from "#/lib/config"; 16 + 17 17 export const dynamic = "force-static"; 18 18 export const revalidate = 3600; // 1 hour 19 19 ··· 36 36 37 37 return { 38 38 title: entry.title + ` — ${HOSTNAME}`, 39 - authors: [{ name: "alice", url: `https://bsky.app/profile/${MY_DID}` }], 40 - description: `by alice · ${readingTime(entry.content).text}`, 39 + authors: [{ name: AUTHOR_NAME, url: `https://bsky.app/profile/${MY_DID}` }], 40 + description: `by ${AUTHOR_NAME} · ${readingTime(entry.content).text}`, 41 41 }; 42 42 } 43 43 ··· 59 59 const entry = post.data.value as ComWhtwndBlogEntry.Record; 60 60 61 61 return ( 62 - <div className="grid grid-rows-[10px_1fr_20px] justify-items-center min-h-dvh py-2 px-4 xs:px-8 pb-20 sm:p-8"> 63 - <main className="flex flex-col gap-0 row-start-2 items-center sm:items-start w-full max-w-[600px] overflow-hidden"> 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"> 63 + <main className="row-start-2 flex w-full max-w-[600px] flex-col items-center gap-0 overflow-hidden sm:items-start"> 64 64 <article className="w-full space-y-4"> 65 - <div className="space-y-4 w-full"> 65 + <div className="w-full space-y-4"> 66 66 <Link 67 67 href="/" 68 - className="hover:underline hover:underline-offset-4 font-medium" 68 + className="font-medium hover:underline hover:underline-offset-4" 69 69 > 70 - <ArrowLeftIcon className="inline size-4 align-middle mb-px mr-1" /> 70 + <ArrowLeftIcon className="mb-px mr-1 inline size-4 align-middle" /> 71 71 Back 72 72 </Link> 73 73 <Title>{entry.title}</Title> ··· 95 95 ), 96 96 blockquote: (props) => ( 97 97 <blockquote 98 - className="mt-6 border-l-2 pl-4 italic border-slate-200 text-slate-600 dark:border-slate-800 dark:text-slate-400" 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 99 {...props} 100 100 /> 101 101 ), 102 102 ul: (props) => ( 103 103 <ul 104 - className="my-6 ml-6 list-disc [&>ul]:my-2 [&>ol]:my-2 [&>li]:mt-2" 104 + className="my-6 ml-6 list-disc [&>li]:mt-2 [&>ol]:my-2 [&>ul]:my-2" 105 105 {...props} 106 106 /> 107 107 ), 108 108 ol: (props) => ( 109 109 <ol 110 - className="my-6 ml-6 list-decimal [&>ul]:my-2 [&>ol]:my-2 [&>li]:mt-2" 110 + className="my-6 ml-6 list-decimal [&>li]:mt-2 [&>ol]:my-2 [&>ul]:my-2" 111 111 {...props} 112 112 /> 113 113 ), ··· 122 122 // eslint-disable-next-line react/no-children-prop 123 123 children={String(children).replace(/\n$/, "")} 124 124 lang={match[1]} 125 - className="!mt-8 text-sm !max-w-full overflow-hidden" 125 + className="!mt-8 !max-w-full overflow-hidden text-sm" 126 126 /> 127 127 ); 128 128 } else { ··· 137 137 /> 138 138 ), 139 139 img: ({ src, alt }) => ( 140 - <span className="block mt-8 w-full aspect-video relative"> 140 + <span className="relative mt-8 block aspect-video w-full"> 141 141 <Image 142 142 src={src!} 143 143 alt={alt!}
+2 -2
src/app/purge/actions.ts
··· 8 8 if (password === process.env.PURGE_PASSWORD) { 9 9 revalidatePath("/", "page"); 10 10 revalidatePath("/post/[rkey]", "page"); 11 - redirect('/') 11 + redirect("/"); 12 12 } else { 13 13 console.error(`Invalid password: ${password}`); 14 - redirect('/?error=yeah') 14 + redirect("/?error=yeah"); 15 15 } 16 16 }
+8 -4
src/app/purge/page.tsx
··· 1 1 import { purgeCache } from "./actions"; 2 2 3 - export default async function Purge({searchParams}: {searchParams: Promise<{error?: string}>}) { 3 + export default async function Purge({ 4 + searchParams, 5 + }: { 6 + searchParams: Promise<{ error?: string }>; 7 + }) { 4 8 const { error } = await searchParams; 5 9 return ( 6 10 <form 7 11 action={purgeCache} 8 12 method="post" 9 - className="flex flex-col gap-4 p-2 mt-24 mx-auto max-w-96" 13 + className="mx-auto mt-24 flex max-w-96 flex-col gap-4 p-2" 10 14 > 11 15 <input 12 16 type="password" 13 17 name="password" 14 18 placeholder="Password" 15 - className="p-2 border rounded-xs" 19 + className="rounded-xs border p-2" 16 20 /> 17 21 {error && <p className="text-red-500">wrong password mate</p>} 18 - <button type="submit" className="p-2 border rounded-xs"> 22 + <button type="submit" className="rounded-xs border p-2"> 19 23 Purge 20 24 </button> 21 25 </form>
-1
src/app/rss/route.ts
··· 4 4 import remarkRehype from "remark-rehype"; 5 5 import RSS from "rss"; 6 6 import { unified } from "unified"; 7 - 8 7 import { getPosts } from "#/lib/api"; 9 8 import { HOSTNAME } from "#/lib/config"; 10 9
+13 -4
src/components/footer.tsx
··· 1 1 import { siBluesky as BlueskyIcon, siGithub as GithubIcon } from "simple-icons"; 2 - 3 2 import { GITHUB_USERNAME, MY_DID } from "#/lib/config"; 4 3 5 4 export function Footer() { 6 5 return ( 7 - <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center"> 6 + <footer className="row-start-3 flex flex-wrap items-center justify-center gap-6"> 8 7 <a 9 8 className="flex items-center gap-2 hover:underline hover:underline-offset-4" 10 9 href={`https://bsky.app/profile/${MY_DID}`} 11 10 target="_blank" 12 11 rel="noopener noreferrer" 13 12 > 14 - <svg width={16} height={16} viewBox="0 0 24 24" className="fill-black dark:fill-white"> 13 + <svg 14 + width={16} 15 + height={16} 16 + viewBox="0 0 24 24" 17 + className="fill-black dark:fill-white" 18 + > 15 19 <path d={BlueskyIcon.path} /> 16 20 </svg> 17 21 Bluesky ··· 22 26 target="_blank" 23 27 rel="noopener noreferrer" 24 28 > 25 - <svg width={16} height={16} viewBox="0 0 24 24" className="fill-black dark:fill-white"> 29 + <svg 30 + width={16} 31 + height={16} 32 + viewBox="0 0 24 24" 33 + className="fill-black dark:fill-white" 34 + > 26 35 <path d={GithubIcon.path} /> 27 36 </svg> 28 37 GitHub
+2 -3
src/components/post-info.tsx
··· 1 1 import { ClockIcon } from "lucide-react"; 2 2 import Image from "next/image"; 3 3 import readingTime from "reading-time"; 4 - 5 4 import me from "#/assets/me.png"; 6 5 import { MY_DID } from "#/lib/config"; 7 6 import { date } from "#/lib/date"; ··· 26 25 height={14} 27 26 src={me} 28 27 alt="alice's profile picture" 29 - className="inline rounded-full mr-1 mb-1" 28 + className="mb-1 mr-1 inline rounded-full" 30 29 /> 31 30 <a 32 31 href={`https://bsky.app/profile/${MY_DID}`} ··· 42 41 <time dateTime={createdAt}>{date(new Date(createdAt))}</time> &middot;{" "} 43 42 </> 44 43 )} 45 - <ClockIcon className="text-inherit inline size-3.5 mb-0.5" />{" "} 44 + <ClockIcon className="mb-0.5 inline size-3.5 text-inherit" />{" "} 46 45 {readingTime(content).text} 47 46 </Paragraph> 48 47 );
+4 -5
src/components/post-list.tsx
··· 1 1 import Link from "next/link"; 2 - 3 2 import { getPosts } from "#/lib/api"; 4 3 5 4 import { PostInfo } from "./post-info"; ··· 12 11 const post = record.value; 13 12 const rkey = record.uri.split("/").pop(); 14 13 return ( 15 - <Link key={record.uri} href={`/post/${rkey}`} className="w-full group"> 14 + <Link key={record.uri} href={`/post/${rkey}`} className="group w-full"> 16 15 <article 17 16 key={record.uri} 18 - className="w-full flex flex-row border-b items-stretch relative after:absolute after:inset-0 after:origin-bottom after:scale-y-0 hover:after:scale-y-100 after:transition-transform after:bg-slate-800/10 dark:after:bg-slate-100/10" 17 + className="relative flex w-full flex-row items-stretch border-b after:absolute after:inset-0 after:origin-bottom after:scale-y-0 after:bg-slate-800/10 after:transition-transform hover:after:scale-y-100 dark:after:bg-slate-100/10" 19 18 > 20 - <div className="w-1.5 diagonal-pattern flex-shrink-0 opacity-20 group-hover:opacity-100 transition-opacity" /> 21 - <div className="flex-1 pt-2 pb-2 px-4"> 19 + <div className="diagonal-pattern w-1.5 flex-shrink-0 opacity-20 transition-opacity group-hover:opacity-100" /> 20 + <div className="flex-1 px-4 pb-2 pt-2"> 22 21 <Title className="text-lg" level="h3"> 23 22 {post.title} 24 23 </Title>
+12 -7
src/components/typography.tsx
··· 12 12 let style; 13 13 switch (level) { 14 14 case "h1": 15 - style = 16 - "text-3xl lg:text-5xl"; 15 + style = "text-3xl lg:text-5xl"; 17 16 break; 18 17 case "h2": 19 - style = 20 - "border-b pb-2 text-3xl"; 18 + style = "border-b pb-2 text-3xl"; 21 19 break; 22 20 case "h3": 23 21 style = "text-2xl"; ··· 34 32 } 35 33 36 34 return ( 37 - <Tag className={cx("font-sans-serif font-bold text-balance tracking-tight scroll-m-20 uppercase mt-16 [&>code]:text-[length:inherit] first:mt-0", style, className)} {...props} /> 35 + <Tag 36 + className={cx( 37 + "font-sans-serif mt-16 scroll-m-20 text-balance font-bold uppercase tracking-tight first:mt-0 [&>code]:text-[length:inherit]", 38 + style, 39 + className, 40 + )} 41 + {...props} 42 + /> 38 43 ); 39 44 } 40 45 ··· 42 47 className, 43 48 ...props 44 49 }: React.HTMLProps<HTMLParagraphElement>) { 45 - return <p className={cx("font-system text-pretty", className)} {...props} />; 50 + return <p className={cx("text-pretty font-system", className)} {...props} />; 46 51 } 47 52 48 53 export function Code({ className, ...props }: React.HTMLProps<HTMLElement>) { 49 54 return ( 50 55 <code 51 56 className={cx( 52 - "font-mono normal-case relative px-[0.3rem] py-[0.2rem] bg-slate-100 text-sm dark:bg-slate-800 dark:text-slate-400", 57 + "relative bg-slate-100 px-[0.3rem] py-[0.2rem] font-mono text-sm normal-case dark:bg-slate-800 dark:text-slate-400", 53 58 className, 54 59 )} 55 60 {...props}
+23 -10
src/lib/api.ts
··· 1 + import { XRPCResponse } from "@atcute/client"; 1 2 import { 2 3 type ComAtprotoRepoListRecords, 3 4 type ComWhtwndBlogEntry, ··· 5 6 6 7 import { bsky } from "./bsky"; 7 8 import { MY_DID } from "./config"; 9 + 8 10 export async function getPosts() { 9 - const posts = await bsky.get("com.atproto.repo.listRecords", { 10 - params: { 11 - repo: MY_DID, 12 - collection: "com.whtwnd.blog.entry", 13 - // todo: pagination 14 - }, 15 - }); 16 - return posts.data.records.filter( 17 - drafts, 18 - ) as (ComAtprotoRepoListRecords.Record & { 11 + let allPosts: ComAtprotoRepoListRecords.Record[] = []; 12 + let cursor; 13 + 14 + do { 15 + const page: XRPCResponse<ComAtprotoRepoListRecords.Output> = await bsky.get( 16 + "com.atproto.repo.listRecords", 17 + { 18 + params: { 19 + repo: MY_DID, 20 + collection: "com.whtwnd.blog.entry", 21 + cursor, 22 + limit: 100, 23 + }, 24 + }, 25 + ); 26 + 27 + allPosts = [...allPosts, ...page.data.records]; 28 + cursor = page.data.cursor; 29 + } while (cursor); 30 + 31 + return allPosts.filter(drafts) as (ComAtprotoRepoListRecords.Record & { 19 32 value: ComWhtwndBlogEntry.Record; 20 33 })[]; 21 34 }
+1
src/lib/bsky.ts
··· 1 1 import { CredentialManager, XRPC } from "@atcute/client"; 2 + 2 3 import { MY_PDS } from "./config"; 3 4 4 5 const handler = new CredentialManager({ service: `https://${MY_PDS}`, fetch });
+3 -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 4 export const HOSTNAME = "alice.bsky.sh"; 4 5 export const TITLE = "alice.bsky.sh"; 5 - export const DESCRIPTION = "this is a blog. there are many like it, but this one is mine."; 6 + export const DESCRIPTION = 7 + "this is a blog. there are many like it, but this one is mine."; 6 8 export const GITHUB_USERNAME = "aliceisjustplaying";