fix classes and make fic loader more responsive in dev
This commit is contained in:
		
							parent
							
								
									496ba125d0
								
							
						
					
					
						commit
						e7ed23bb39
					
				
							
								
								
									
										28
									
								
								bun.lock
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								bun.lock
									
									
									
									
									
								
							| @ -4,15 +4,17 @@ | |||||||
|     "": { |     "": { | ||||||
|       "name": "astro", |       "name": "astro", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@astrojs/db": "^0.16.1", |         "@astrojs/db": "0.17.1", | ||||||
|         "@astrojs/mdx": "^4.3.3", |         "@astrojs/mdx": "^4.3.3", | ||||||
|         "@astrojs/node": "^9.4.0", |         "@astrojs/node": "9.4.1", | ||||||
|         "@astrojs/rss": "4.0.12", |         "@astrojs/rss": "4.0.12", | ||||||
|         "@fujocoded/astro-dev-only": "0.0.3", |         "@fujocoded/astro-dev-only": "0.0.4", | ||||||
|         "astro": "5.12.3", |         "astro": "5.13.0", | ||||||
|         "astro-breadcrumbs": "^3.3.1", |         "astro-breadcrumbs": "^3.3.1", | ||||||
|         "dayjs": "^1.11.13", |         "dayjs": "^1.11.13", | ||||||
|  |         "dompurify": "^3.2.6", | ||||||
|         "markdown-it": "^14.1.0", |         "markdown-it": "^14.1.0", | ||||||
|  |         "marked": "^16.1.2", | ||||||
|         "node-html-parser": "^7.0.1", |         "node-html-parser": "^7.0.1", | ||||||
|         "sanitize-html": "^2.17.0", |         "sanitize-html": "^2.17.0", | ||||||
|       }, |       }, | ||||||
| @ -26,7 +28,7 @@ | |||||||
|   "packages": { |   "packages": { | ||||||
|     "@astrojs/compiler": ["@astrojs/compiler@2.12.2", "", {}, "sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw=="], |     "@astrojs/compiler": ["@astrojs/compiler@2.12.2", "", {}, "sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw=="], | ||||||
| 
 | 
 | ||||||
|     "@astrojs/db": ["@astrojs/db@0.16.1", "", { "dependencies": { "@libsql/client": "^0.15.2", "deep-diff": "^1.0.2", "drizzle-orm": "^0.42.0", "kleur": "^4.1.5", "nanoid": "^5.1.5", "prompts": "^2.4.2", "yargs-parser": "^21.1.1", "zod": "^3.24.4" } }, "sha512-pBX19Rh1Ds7riQReDH6gZ5gCf1Txk03WWqMhHIkZIW7lNPnyEx5cuYtl/V9fUnXnqMrBxL9bOHkW4e937DFtWg=="], |     "@astrojs/db": ["@astrojs/db@0.17.1", "", { "dependencies": { "@libsql/client": "^0.15.2", "deep-diff": "^1.0.2", "drizzle-orm": "^0.42.0", "kleur": "^4.1.5", "nanoid": "^5.1.5", "prompts": "^2.4.2", "yargs-parser": "^21.1.1", "zod": "^3.24.4" } }, "sha512-QL09xZf5Om8AshIlt+YhLDYf6M1QSzv+kfuljsPrhEXJ8U/tuKnbWs2M3wimFaLG3/fU0prFix8lWt7zU8ytfA=="], | ||||||
| 
 | 
 | ||||||
|     "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.1", "", {}, "sha512-7dwEVigz9vUWDw3nRwLQ/yH/xYovlUA0ZD86xoeKEBmkz9O6iELG1yri67PgAPW6VLL/xInA4t7H0CK6VmtkKQ=="], |     "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.1", "", {}, "sha512-7dwEVigz9vUWDw3nRwLQ/yH/xYovlUA0ZD86xoeKEBmkz9O6iELG1yri67PgAPW6VLL/xInA4t7H0CK6VmtkKQ=="], | ||||||
| 
 | 
 | ||||||
| @ -34,7 +36,7 @@ | |||||||
| 
 | 
 | ||||||
|     "@astrojs/mdx": ["@astrojs/mdx@4.3.3", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.5", "@mdx-js/mdx": "^3.1.0", "acorn": "^8.14.1", "es-module-lexer": "^1.6.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "kleur": "^4.1.5", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-+9+xGP2TBXxcm84cpiq4S9JbuHOHM1fcvREfqW7VHxlUyfUQPByoJ9YYliqHkLS6BMzG+O/+o7n8nguVhuEv4w=="], |     "@astrojs/mdx": ["@astrojs/mdx@4.3.3", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.5", "@mdx-js/mdx": "^3.1.0", "acorn": "^8.14.1", "es-module-lexer": "^1.6.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "kleur": "^4.1.5", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-+9+xGP2TBXxcm84cpiq4S9JbuHOHM1fcvREfqW7VHxlUyfUQPByoJ9YYliqHkLS6BMzG+O/+o7n8nguVhuEv4w=="], | ||||||
| 
 | 
 | ||||||
|     "@astrojs/node": ["@astrojs/node@9.4.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "send": "^1.2.0", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^5.3.0" } }, "sha512-Gxs0iVUvOmQmK+H1DBoabcgvdSDg277SwbujRv2cUBlnpcOTJQDFRhRvyJ7G+Zkd06/jhRphsTTmmrBY0PqI4g=="], |     "@astrojs/node": ["@astrojs/node@9.4.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.1", "send": "^1.2.0", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^5.3.0" } }, "sha512-lpSAQFS4IiCFGQHL/q/1CcX+AmFbma4NOoV5j7Z7Ml2wevyDe/2kAjScKIKk2DA7k/MrXSZ5ZN+IxJgpPbnAOQ=="], | ||||||
| 
 | 
 | ||||||
|     "@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], |     "@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], | ||||||
| 
 | 
 | ||||||
| @ -106,7 +108,7 @@ | |||||||
| 
 | 
 | ||||||
|     "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="], |     "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.8", "", { "os": "win32", "cpu": "x64" }, "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw=="], | ||||||
| 
 | 
 | ||||||
|     "@fujocoded/astro-dev-only": ["@fujocoded/astro-dev-only@0.0.3", "", {}, "sha512-BOLYZcivrJVUA60d4R2+yEGwDJZ+3Z/zndLACxk6YpClu5ETl0adghwCCt9yIHsq79KDx/Or8LH2aqS6fVlQbg=="], |     "@fujocoded/astro-dev-only": ["@fujocoded/astro-dev-only@0.0.4", "", {}, "sha512-1I/h/M5QguTjl+MxXUqwDiuLs1xUDhzQ3bzWwwhyrrZL8bhqXsHbEcuxw+AUT0RS3WTFclF7L/5wq7vw1YEXdg=="], | ||||||
| 
 | 
 | ||||||
|     "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], |     "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], | ||||||
| 
 | 
 | ||||||
| @ -268,6 +270,8 @@ | |||||||
| 
 | 
 | ||||||
|     "@types/sanitize-html": ["@types/sanitize-html@2.16.0", "", { "dependencies": { "htmlparser2": "^8.0.0" } }, "sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw=="], |     "@types/sanitize-html": ["@types/sanitize-html@2.16.0", "", { "dependencies": { "htmlparser2": "^8.0.0" } }, "sha512-l6rX1MUXje5ztPT0cAFtUayXF06DqPhRyfVXareEN5gGCFaP/iwsxIyKODr9XDhfxPpN6vXUFNfo5kZMXCxBtw=="], | ||||||
| 
 | 
 | ||||||
|  |     "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], | ||||||
|  | 
 | ||||||
|     "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], |     "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], | ||||||
| 
 | 
 | ||||||
|     "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], |     "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], | ||||||
| @ -294,7 +298,7 @@ | |||||||
| 
 | 
 | ||||||
|     "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], |     "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], | ||||||
| 
 | 
 | ||||||
|     "astro": ["astro@5.12.3", "", { "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.3", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "smol-toml": "^1.3.4", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-fU1hNPMkccm+FuonGsY5DFkC2QyuLCju++8L2ubzBtYBDBf6bmfgmVM7A2dK+Hl+ZJCUNgepsClhBpczj+2LRw=="], |     "astro": ["astro@5.13.0", "", { "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.7.1", "@astrojs/markdown-remark": "6.3.5", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "smol-toml": "^1.3.4", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.4", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-kWahyvrrxUtgxFRRWSH5X0DESvgg4ZZ0fk6tU7blVZRgmj4PY79nzfaXFT+N+Ac2vWXX7eYUPYs1kLLk5IjBfQ=="], | ||||||
| 
 | 
 | ||||||
|     "astro-breadcrumbs": ["astro-breadcrumbs@3.3.1", "", { "peerDependencies": { "astro": "^2.0.0-beta.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" } }, "sha512-O3wWzlc4HOU//ePefcwk2F4yQn830buVUVn6h7+sGmVq7+xvv9JmHTcmihU18ISte72ELnJlG2U8s58nIuPXfg=="], |     "astro-breadcrumbs": ["astro-breadcrumbs@3.3.1", "", { "peerDependencies": { "astro": "^2.0.0-beta.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" } }, "sha512-O3wWzlc4HOU//ePefcwk2F4yQn830buVUVn6h7+sGmVq7+xvv9JmHTcmihU18ISte72ELnJlG2U8s58nIuPXfg=="], | ||||||
| 
 | 
 | ||||||
| @ -408,6 +412,8 @@ | |||||||
| 
 | 
 | ||||||
|     "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], |     "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], | ||||||
| 
 | 
 | ||||||
|  |     "dompurify": ["dompurify@3.2.6", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ=="], | ||||||
|  | 
 | ||||||
|     "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], |     "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], | ||||||
| 
 | 
 | ||||||
|     "drizzle-orm": ["drizzle-orm@0.42.0", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-pS8nNJm2kBNZwrOjTHJfdKkaU+KuUQmV/vk5D57NojDq4FG+0uAYGMulXtYT///HfgsMF0hnFFvu1ezI3OwOkg=="], |     "drizzle-orm": ["drizzle-orm@0.42.0", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-pS8nNJm2kBNZwrOjTHJfdKkaU+KuUQmV/vk5D57NojDq4FG+0uAYGMulXtYT///HfgsMF0hnFFvu1ezI3OwOkg=="], | ||||||
| @ -570,6 +576,8 @@ | |||||||
| 
 | 
 | ||||||
|     "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], |     "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], | ||||||
| 
 | 
 | ||||||
|  |     "marked": ["marked@16.1.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-rNQt5EvRinalby7zJZu/mB+BvaAY2oz3wCuCjt1RDrWNpS1Pdf9xqMOeC9Hm5adBdcV/3XZPJpG58eT+WBc0XQ=="], | ||||||
|  | 
 | ||||||
|     "mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="], |     "mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="], | ||||||
| 
 | 
 | ||||||
|     "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], |     "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], | ||||||
| @ -960,10 +968,6 @@ | |||||||
| 
 | 
 | ||||||
|     "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], |     "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], | ||||||
| 
 | 
 | ||||||
|     "astro/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], |  | ||||||
| 
 |  | ||||||
|     "astro/@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.2.1", "smol-toml": "^1.3.4", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "vfile": "^6.0.3" } }, "sha512-DDRtD1sPvAuA7ms2btc9A7/7DApKqgLMNrE6kh5tmkfy8utD0Z738gqd3p5aViYYdUtHIyEJ1X4mCMxfCfu15w=="], |  | ||||||
| 
 |  | ||||||
|     "cross-fetch/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], |     "cross-fetch/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], | ||||||
| 
 | 
 | ||||||
|     "hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="], |     "hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="], | ||||||
|  | |||||||
							
								
								
									
										0
									
								
								guestbook.db
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								guestbook.db
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										10
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								package.json
									
									
									
									
									
								
							| @ -9,15 +9,17 @@ | |||||||
|     "astro": "astro" |     "astro": "astro" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@astrojs/db": "^0.16.1", |     "@astrojs/db": "0.17.1", | ||||||
|     "@astrojs/mdx": "^4.3.3", |     "@astrojs/mdx": "^4.3.3", | ||||||
|     "@astrojs/node": "^9.4.0", |     "@astrojs/node": "9.4.1", | ||||||
|     "@astrojs/rss": "4.0.12", |     "@astrojs/rss": "4.0.12", | ||||||
|     "@fujocoded/astro-dev-only": "0.0.3", |     "@fujocoded/astro-dev-only": "0.0.4", | ||||||
|     "astro": "5.12.3", |     "astro": "5.13.0", | ||||||
|     "astro-breadcrumbs": "^3.3.1", |     "astro-breadcrumbs": "^3.3.1", | ||||||
|     "dayjs": "^1.11.13", |     "dayjs": "^1.11.13", | ||||||
|  |     "dompurify": "^3.2.6", | ||||||
|     "markdown-it": "^14.1.0", |     "markdown-it": "^14.1.0", | ||||||
|  |     "marked": "^16.1.2", | ||||||
|     "node-html-parser": "^7.0.1", |     "node-html-parser": "^7.0.1", | ||||||
|     "sanitize-html": "^2.17.0" |     "sanitize-html": "^2.17.0" | ||||||
|   }, |   }, | ||||||
|  | |||||||
| @ -5,15 +5,15 @@ interface Props { | |||||||
|   imagePath: string; |   imagePath: string; | ||||||
|   alt: string; |   alt: string; | ||||||
|   caption?: string; |   caption?: string; | ||||||
|   class?: string | string[]; |   class?: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const { imagePath, alt, caption, class: className }: Props = Astro.props; | const { imagePath, alt, caption, class: className, ...rest }: Props = Astro.props; | ||||||
| const images = import.meta.glob<{ default: ImageMetadata }>("/src/assets/**/*.{jpeg,jpg,png,webp,gif}"); | const images = import.meta.glob<{ default: ImageMetadata }>("/src/assets/**/*.{jpeg,jpg,png,webp,gif}"); | ||||||
| const path = imagePath.startsWith("$") ? imagePath.replace("$", "/src/assets") : imagePath; | const path = imagePath.startsWith("$") ? imagePath.replace("$", "/src/assets") : imagePath; | ||||||
| if (!images[path]) throw new Error(`"${path}" does not exist in glob: "src/assets/**/*.{jpeg,jpg,png,webp,gif}"`); | if (!images[path]) throw new Error(`"${path}" does not exist in glob: "src/assets/**/*.{jpeg,jpg,png,webp,gif}"`); | ||||||
| --- | --- | ||||||
| <figure class:list={[className]}> | <figure class:list={className} {...rest}> | ||||||
|   <Image src={images[path]()} {alt} /> |   <Image src={images[path]()} {alt} /> | ||||||
|   {caption && <figcaption>{caption}</figcaption>} |   {caption && <figcaption>{caption}</figcaption>} | ||||||
| </figure> | </figure> | ||||||
| @ -1,7 +1,8 @@ | |||||||
| import { defineCollection, reference, z } from "astro:content"; | import { defineCollection, reference, z } from "astro:content"; | ||||||
| import { glob } from "astro/loaders"; | import { glob } from "astro/loaders"; | ||||||
| import { rssSchema } from "@astrojs/rss"; | import { rssSchema } from "@astrojs/rss"; | ||||||
| import MarkdownIt from "markdown-it"; | // import MarkdownIt from "markdown-it";
 | ||||||
|  | import { marked } from "marked"; | ||||||
| import moods from "@/utils/moods"; | import moods from "@/utils/moods"; | ||||||
| import { ficsLoader } from "@/utils/loader"; | import { ficsLoader } from "@/utils/loader"; | ||||||
| 
 | 
 | ||||||
| @ -19,10 +20,7 @@ function slugify(input: string) { | |||||||
|     .replace(/-+$/, ""); |     .replace(/-+$/, ""); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const parser = new MarkdownIt({ | const parser = marked.use({ gfm: true, breaks: true, }); | ||||||
|   html: true, |  | ||||||
|   breaks: true, |  | ||||||
| }); |  | ||||||
| 
 | 
 | ||||||
| const blog = defineCollection({ | const blog = defineCollection({ | ||||||
|   loader: glob({ pattern: "*.{md,mdx}", base: "./src/content/blog" }), |   loader: glob({ pattern: "*.{md,mdx}", base: "./src/content/blog" }), | ||||||
| @ -46,14 +44,13 @@ const chapters = defineCollection({ | |||||||
|   schema: z.object({ |   schema: z.object({ | ||||||
|     title: z.string(), |     title: z.string(), | ||||||
|     publishedAt: z.coerce.date(), |     publishedAt: z.coerce.date(), | ||||||
|     notes: z.ostring().transform(notes => parser.renderInline(notes ?? "", {})), |     notes: z.ostring().transform(async (notes) => await parser.parseInline(notes ?? "")), | ||||||
|     lastModified: z.coerce.date().optional(), |     lastModified: z.coerce.date().optional(), | ||||||
|     sortOrder: z.number().default(1), |     sortOrder: z.number().default(1), | ||||||
|     // fic: reference("fics"),
 |  | ||||||
|   }), |   }), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const fics = defineCollection({ | const test = defineCollection({ | ||||||
|   loader: glob({  |   loader: glob({  | ||||||
|     pattern: "**/*.{yml,yaml}",  |     pattern: "**/*.{yml,yaml}",  | ||||||
|     base: source,  |     base: source,  | ||||||
| @ -66,19 +63,19 @@ const fics = defineCollection({ | |||||||
|     title: z.string(), |     title: z.string(), | ||||||
|     series: z.union([z.string(), z.array(z.string())]), |     series: z.union([z.string(), z.array(z.string())]), | ||||||
|     publishedAt: z.coerce.date(), |     publishedAt: z.coerce.date(), | ||||||
|     summary: z.string().transform(summary => parser.renderInline(summary, {})), |     summary: z.string().transform(async (summary) => await parser.parseInline(summary ?? "")), | ||||||
|     characters: z.array(z.string()).optional(), |     characters: z.array(z.string()).optional(), | ||||||
|     ships: z.ostring(), |     ships: z.ostring(), | ||||||
|     tags: z.array(z.string()).optional(), |     tags: z.array(z.string()).optional(), | ||||||
|     notes: z.ostring().transform(notes => parser.renderInline(notes ?? "", {})), |     notes: z.ostring().transform(async (notes) => await parser.parseInline(notes ?? "")), | ||||||
|     lastModified: z.coerce.date().optional(), |     lastModified: z.coerce.date().optional(), | ||||||
|   }), |   }), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const test = defineCollection({ | const fics = defineCollection({ | ||||||
|   loader: ficsLoader( |   loader: ficsLoader( | ||||||
|     glob({ |     glob({ | ||||||
|       pattern: "**/*.{yml,yaml}", |       pattern: "**/*.{yml,yaml|toml}", | ||||||
|       base: source, |       base: source, | ||||||
|       generateId: ({ entry, data }) => { |       generateId: ({ entry, data }) => { | ||||||
|         if (data.slug) return data.slug as string; |         if (data.slug) return data.slug as string; | ||||||
| @ -90,14 +87,14 @@ const test = defineCollection({ | |||||||
|     title: z.string(), |     title: z.string(), | ||||||
|     series: z.union([z.string(), z.array(z.string())]), |     series: z.union([z.string(), z.array(z.string())]), | ||||||
|     publishedAt: z.coerce.date(), |     publishedAt: z.coerce.date(), | ||||||
|     summary: z.string().transform(summary => parser.renderInline(summary, {})), |     summary: z.string().transform(async (summary) => await parser.parseInline(summary ?? "")), | ||||||
|     characters: z.array(z.string()).optional(), |     characters: z.array(z.string()).optional(), | ||||||
|     ships: z.ostring(), |     ships: z.ostring(), | ||||||
|     tags: z.array(z.string()).optional(), |     tags: z.array(z.string()).optional(), | ||||||
|     notes: z.ostring().transform(notes => parser.renderInline(notes ?? "", {})), |     notes: z.ostring().transform(async (notes) => await parser.parseInline(notes ?? "")), | ||||||
|     lastModified: z.coerce.date().optional(), |     lastModified: z.coerce.date().optional(), | ||||||
|     chapters: z.array(reference("chapters")).optional(), |     chapters: z.array(reference("chapters")).optional(), | ||||||
|   }), |   }), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export const collections = { blog, fics, chapters, test }; | export const collections = { blog, fics, chapters }; | ||||||
							
								
								
									
										9
									
								
								src/content/fics/test/_index.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/content/fics/test/_index.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | title = "this is a test" | ||||||
|  | series = [ | ||||||
|  |   "test", | ||||||
|  |   "fandom 2" | ||||||
|  | ] | ||||||
|  | publishedAt = 2022-12-22 | ||||||
|  | summary = """ | ||||||
|  |   yeller | ||||||
|  |   """ | ||||||
| @ -1,5 +0,0 @@ | |||||||
| title: this is a test |  | ||||||
| series: test |  | ||||||
| publishedAt: 2020-12-20T19:18:00 |  | ||||||
| summary:  |  | ||||||
|   yeller |  | ||||||
| @ -1,6 +1,6 @@ | |||||||
| --- | --- | ||||||
| title: ch 1 | title: ch 1 | ||||||
| publishedAt: 2020-12-20T19:18:00 | publishedAt: 2022-11-01 | ||||||
| notes: i wrote this in a fugue state while listening to [Waste by Oh Wonder](https://www.youtube.com/watch?v=Ar1grAdGkec) | notes: i wrote this in a fugue state while listening to [Waste by Oh Wonder](https://www.youtube.com/watch?v=Ar1grAdGkec) | ||||||
| sortOrder: 1 | sortOrder: 1 | ||||||
| --- | --- | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| --- | --- | ||||||
| title: ch 2 | title: chapter 2 | ||||||
| publishedAt: 2020-12-20T19:18:00 | publishedAt: 2023-12-12 | ||||||
| notes: i wrote this in a fugue state while listening to [Waste by Oh Wonder](https://www.youtube.com/watch?v=Ar1grAdGkec) | notes: i wrote this in a fugue state while listening to [Waste by Oh Wonder](https://www.youtube.com/watch?v=Ar1grAdGkec) | ||||||
| sortOrder: 2 | sortOrder: 2 | ||||||
| --- | --- | ||||||
|  | |||||||
| @ -4,6 +4,6 @@ publishedAt: 2020-12-20T19:18:00 | |||||||
| summary:  | summary:  | ||||||
|   Sanemi survives. He learns to bear the weight of living. |   Sanemi survives. He learns to bear the weight of living. | ||||||
| 
 | 
 | ||||||
|   -- |   <hr/> | ||||||
| 
 | 
 | ||||||
|   MAJOR ending spoilers for the manga please don't read if you haven't finished the manga!! |   MAJOR ending spoilers for the manga please don't read if you haven't finished the manga!! | ||||||
| @ -75,7 +75,7 @@ const { frontmatter } = Astro.props; | |||||||
|     li::marker { content: " "; } |     li::marker { content: " "; } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   :global(.avatar) { |   .avatar { | ||||||
|     width: fit-content; |     width: fit-content; | ||||||
|     margin: 0 auto 2rem; |     margin: 0 auto 2rem; | ||||||
|     border-image: var(--borderImage) 5 4 / 10px 8px / 8px repeat; |     border-image: var(--borderImage) 5 4 / 10px 8px / 8px repeat; | ||||||
| @ -86,7 +86,6 @@ const { frontmatter } = Astro.props; | |||||||
|       place-content: center; |       place-content: center; | ||||||
|       width: 80px; |       width: 80px; | ||||||
|       height: auto; |       height: auto; | ||||||
|       image-rendering: pixelated; |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| </style> | </style> | ||||||
| @ -24,7 +24,8 @@ const { title, ficTitle, date, notes, lastModified }: Props = Astro.props; | |||||||
|   <main> |   <main> | ||||||
|     <ChapterContent lastModified={lastModified} notes={notes}> |     <ChapterContent lastModified={lastModified} notes={notes}> | ||||||
|       <Fragment slot="title"> |       <Fragment slot="title"> | ||||||
|         <h1>{title}</h1> |         <h1>{ficTitle}</h1> | ||||||
|  |         <h2>{title}</h2> | ||||||
|         <time datetime={formatDate(date, true)}> |         <time datetime={formatDate(date, true)}> | ||||||
|           Published on {formatDate(date)} |           Published on {formatDate(date)} | ||||||
|         </time> |         </time> | ||||||
|  | |||||||
| @ -5,7 +5,10 @@ import { getCollection, render } from "astro:content"; | |||||||
| import { Breadcrumbs } from "astro-breadcrumbs"; | import { Breadcrumbs } from "astro-breadcrumbs"; | ||||||
| 
 | 
 | ||||||
| export const getStaticPaths = (async () => { | export const getStaticPaths = (async () => { | ||||||
|   const chapters = await getCollection("chapters"); |   const fics = await getCollection("fics"); | ||||||
|  |   const chapters = await getCollection("chapters", ({ id }) => { | ||||||
|  |     return fics.find(({ data }) => data.chapters?.some(chapter => chapter.id === id)); | ||||||
|  |   }); | ||||||
|   return chapters.map(chapter => ({ |   return chapters.map(chapter => ({ | ||||||
|     params: {  |     params: {  | ||||||
|       ficId: chapter.id.split("/")[0], |       ficId: chapter.id.split("/")[0], | ||||||
| @ -19,12 +22,8 @@ const { ficId, chapterId } = Astro.params; | |||||||
| const { chapter } = Astro.props; | const { chapter } = Astro.props; | ||||||
| const { Content, remarkPluginFrontmatter } = await render(chapter); | const { Content, remarkPluginFrontmatter } = await render(chapter); | ||||||
| 
 | 
 | ||||||
| const chapters = await getCollection("chapters", ({ id }) => { | const chapters = await getCollection("chapters", ({ id }) => id.split("/")[0] === ficId); | ||||||
|   return id.split("/")[0] === ficId; | const fic = await getCollection("fics", ({ id }) => id === ficId); | ||||||
| }); |  | ||||||
| const fic = await getCollection("fics", ({ id }) => { |  | ||||||
|   return id === ficId; |  | ||||||
| }); |  | ||||||
| chapters.sort((a, b) => a.data.sortOrder - b.data.sortOrder); | chapters.sort((a, b) => a.data.sortOrder - b.data.sortOrder); | ||||||
| const current = chapters.findIndex(chapter => chapter.id === `${ficId}/${chapterId}`); | const current = chapters.findIndex(chapter => chapter.id === `${ficId}/${chapterId}`); | ||||||
| const next = current + 1 === chapters.length ? undefined : chapters[current + 1]; | const next = current + 1 === chapters.length ? undefined : chapters[current + 1]; | ||||||
| @ -66,12 +65,8 @@ const links = [ | |||||||
|         ))} |         ))} | ||||||
|       </select> |       </select> | ||||||
|     </div> |     </div> | ||||||
|     {previous && ( |     {previous && <a id="previous" href={`/fics/${previous.id}`}>{previous.data.title}</a>} | ||||||
|       <a id="previous" href={`/fics/${previous.id}`}>{previous.data.title}</a> |     {next && <a id="next" href={`/fics/${next.id}`}>{next.data.title}</a>} | ||||||
|     )} |  | ||||||
|     {next && ( |  | ||||||
|       <a id="next" href={`/fics/${next.id}`}>{next.data.title}</a> |  | ||||||
|     )} |  | ||||||
|   </nav> |   </nav> | ||||||
|   )} |   )} | ||||||
| </Chapter> | </Chapter> | ||||||
|  | |||||||
| @ -1,7 +1,9 @@ | |||||||
| --- | --- | ||||||
| import type { GetStaticPaths } from "astro"; | import type { GetStaticPaths } from "astro"; | ||||||
| import { Font } from "astro:assets"; | import { Font } from "astro:assets"; | ||||||
| import { getCollection, getEntry, render } from "astro:content"; | import { getCollection, getEntries, render } from "astro:content"; | ||||||
|  | import { marked } from "marked"; | ||||||
|  | 
 | ||||||
| import Layout from "@/layouts/Layout.astro"; | import Layout from "@/layouts/Layout.astro"; | ||||||
| import formatDate from "@/utils/formatDate"; | import formatDate from "@/utils/formatDate"; | ||||||
| import ChapterContent from "~/ChapterContent.astro"; | import ChapterContent from "~/ChapterContent.astro"; | ||||||
| @ -15,21 +17,17 @@ export const getStaticPaths = (async () => { | |||||||
| }) satisfies GetStaticPaths; | }) satisfies GetStaticPaths; | ||||||
| 
 | 
 | ||||||
| const { fic } = Astro.props; | const { fic } = Astro.props; | ||||||
| const chapters = await getCollection("chapters", ({ id }) => { | const chapters = await getEntries(fic.data.chapters ?? []); | ||||||
|   return id.startsWith(fic.id); |  | ||||||
| }); |  | ||||||
| chapters.length = Math.min(chapters.length, 5); | chapters.length = Math.min(chapters.length, 5); | ||||||
| chapters.sort((a, b) => a.data.sortOrder - b.data.sortOrder); | chapters.sort((a, b) => a.data.sortOrder - b.data.sortOrder); | ||||||
| const oneshot = chapters.length === 1 && await getEntry(chapters[0]); | const { Content } = await render(fic); | ||||||
| const { Content } = oneshot && await render(oneshot); | const notes = fic.rendered && await marked.use({ async: true, gfm: true, breaks: true }).parseInline((fic.rendered.metadata!.frontmatter as any)["notes"]); | ||||||
| --- | --- | ||||||
| <Layout title={fic.data.title}> | <Layout title={fic.data.title}> | ||||||
|   <Fragment slot="head"> |   <Fragment slot="head"> | ||||||
|     <link rel="alternate" type="application/rss+xml" title={fic.data.title} href={new URL(`/fics/${Astro.params.ficId}/rss.xml`, Astro.site)} /> |     <link rel="alternate" type="application/rss+xml" title={fic.data.title} href={new URL(`/fics/${Astro.params.ficId}/rss.xml`, Astro.site)} /> | ||||||
|     <meta name="description" content={fic.data.summary?.substring(0, 150) + "…"} /> |     <meta name="description" content={fic.data.summary?.substring(0, 150) + "…"} /> | ||||||
|     {oneshot && ( |     {fic.body && <Font cssVariable="--serif" preload />} | ||||||
|       <Font cssVariable="--serif" preload /> |  | ||||||
|     )} |  | ||||||
|   </Fragment> |   </Fragment> | ||||||
| 
 | 
 | ||||||
|   <main> |   <main> | ||||||
| @ -39,10 +37,8 @@ const { Content } = oneshot && await render(oneshot); | |||||||
|         <dl> |         <dl> | ||||||
|           <dt>Fandom</dt>  |           <dt>Fandom</dt>  | ||||||
|           {typeof fic.data.series === "object"  |           {typeof fic.data.series === "object"  | ||||||
|             ? (<ul> |             ? <ul>{fic.data.series.map(fandom => (<li>{fandom}</li>))}</ul> | ||||||
|               {fic.data.series.map(fandom => (<li>{fandom}</li>))} |             : <dd>{fic.data.series}</dd> | ||||||
|             </ul>) |  | ||||||
|             : (<dd>{fic.data.series}</dd>) |  | ||||||
|           } |           } | ||||||
|           <dt>Date</dt> |           <dt>Date</dt> | ||||||
|           <dd> |           <dd> | ||||||
| @ -59,14 +55,12 @@ const { Content } = oneshot && await render(oneshot); | |||||||
|         </dl> |         </dl> | ||||||
|          |          | ||||||
|         <div class="links"> |         <div class="links"> | ||||||
|           {!oneshot && ( |           {fic.data.chapters && <a class="button" href={`/fics/${chapters[0].id}`}>start reading</a>} | ||||||
|             <a class="button" href={`/fics/${chapters[0].id}`}>start reading</a> |  | ||||||
|           )} |  | ||||||
|           <a class="button" href={`/fics/${Astro.params.ficId}/rss.xml`}>rss feed</a> |           <a class="button" href={`/fics/${Astro.params.ficId}/rss.xml`}>rss feed</a> | ||||||
|         </div> |         </div> | ||||||
|       </header> |       </header> | ||||||
| 
 |        | ||||||
|       {!oneshot && ( |       {fic.data.chapters && ( | ||||||
|         <h2>chapters</h2> |         <h2>chapters</h2> | ||||||
|         <ul> |         <ul> | ||||||
|           {chapters.map(chapter => ( |           {chapters.map(chapter => ( | ||||||
| @ -76,9 +70,9 @@ const { Content } = oneshot && await render(oneshot); | |||||||
|       )} |       )} | ||||||
|     </section> |     </section> | ||||||
| 
 | 
 | ||||||
|     {oneshot && ( |     {fic.body && ( | ||||||
|       <section id="oneshot"> |       <section id="oneshot"> | ||||||
|         <ChapterContent lastModified={oneshot.data.lastModified} notes={oneshot.data.notes}> |         <ChapterContent lastModified={(fic.rendered?.metadata!.frontmatter as any)['lastModified']} notes={notes}> | ||||||
|           <Content /> |           <Content /> | ||||||
|         </ChapterContent> |         </ChapterContent> | ||||||
|       </section> |       </section> | ||||||
|  | |||||||
| @ -1,23 +1,25 @@ | |||||||
| import type { APIRoute } from "astro"; | import type { APIRoute } from "astro"; | ||||||
| import { getCollection } from "astro:content"; | import { experimental_AstroContainer as AstroContainer } from "astro/container"; | ||||||
|  | import { getCollection, render } from "astro:content"; | ||||||
| import rss, { type RSSFeedItem } from "@astrojs/rss"; | import rss, { type RSSFeedItem } from "@astrojs/rss"; | ||||||
| import MarkdownIt from "markdown-it"; | 
 | ||||||
|  | import { marked } from "marked"; | ||||||
| import { parse as htmlParser } from "node-html-parser"; | import { parse as htmlParser } from "node-html-parser"; | ||||||
| import sanitize from "sanitize-html"; | import sanitize from "sanitize-html"; | ||||||
| import fixRssImages from "@/utils/fixRssImages"; | import fixRssImages from "@/utils/fixRssImages"; | ||||||
| 
 | 
 | ||||||
| const parser = new MarkdownIt(); | const parser = marked.use({ gfm: true, breaks: true }); | ||||||
| const fics = await getCollection("fics"); | const fics = await getCollection("fics"); | ||||||
| 
 | 
 | ||||||
| export const GET: APIRoute = async (context) => { | export const GET: APIRoute = async (context) => { | ||||||
|   const chapters = await getCollection("chapters", ({ id }) => { |   const chapters = await getCollection("chapters", ({ id }) => id.split("/")[0] === context.params.ficId); | ||||||
|     return id.split("/")[0] === context.params.ficId; |  | ||||||
|   }); |  | ||||||
|   const fic = fics.find(({ id }) => id === context.params.ficId); |   const fic = fics.find(({ id }) => id === context.params.ficId); | ||||||
|  |   const container = await AstroContainer.create(); | ||||||
|   const feed: RSSFeedItem[] = []; |   const feed: RSSFeedItem[] = []; | ||||||
| 
 | 
 | ||||||
|   for (const entry of chapters) { |   for (const entry of chapters) { | ||||||
|     const content = parser.render(entry.body!); |     const { Content } = await render(entry); | ||||||
|  |     const content = await container.renderToString(Content); | ||||||
|     const html = htmlParser.parse(content); |     const html = htmlParser.parse(content); | ||||||
|     const images = html.querySelectorAll("img"); |     const images = html.querySelectorAll("img"); | ||||||
| 
 | 
 | ||||||
| @ -33,12 +35,11 @@ export const GET: APIRoute = async (context) => { | |||||||
|       categories: typeof fic?.data.series == "string" ? [fic?.data.series] : [...fic?.data.series!], |       categories: typeof fic?.data.series == "string" ? [fic?.data.series] : [...fic?.data.series!], | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |   const summary = await parser.parseInline(fic?.data.summary ?? ""); | ||||||
| 
 | 
 | ||||||
|   return rss({ |   return rss({ | ||||||
|     title: `${fic?.data.title}`, |     title: `${fic?.data.title}`, | ||||||
|     description: sanitize(parser.render(fic?.data.summary!), { |     description: sanitize(summary), | ||||||
|       allowedTags: sanitize.defaults.allowedTags.concat(["br"]), |  | ||||||
|     }), |  | ||||||
|     site: context.site!, |     site: context.site!, | ||||||
|     items: feed, |     items: feed, | ||||||
|   }); |   }); | ||||||
|  | |||||||
| @ -1,14 +1,13 @@ | |||||||
| --- | --- | ||||||
| import Layout from "@/layouts/Layout.astro"; |  | ||||||
| import { getCollection } from "astro:content"; | import { getCollection } from "astro:content"; | ||||||
| import dayjs from "dayjs"; | import Layout from "@/layouts/Layout.astro"; | ||||||
| import utc from "dayjs/plugin/utc"; | import formatDate from "@/utils/formatDate"; | ||||||
| 
 | 
 | ||||||
| const fics = await getCollection("fics"); | const fics = await getCollection("fics"); | ||||||
| const chapters = await getCollection("chapters"); | const chapters = await getCollection("chapters"); | ||||||
|  | fics.sort((a, b) => b.data.publishedAt.valueOf() - a.data.publishedAt.valueOf()); | ||||||
| chapters.length = Math.min(chapters.length, 5); | chapters.length = Math.min(chapters.length, 5); | ||||||
| chapters.sort((a, b) => a.data.publishedAt.valueOf() - b.data.publishedAt.valueOf()); | chapters.sort((a, b) => b.data.publishedAt.valueOf() - a.data.publishedAt.valueOf()); | ||||||
| dayjs.extend(utc); |  | ||||||
| --- | --- | ||||||
| <Layout> | <Layout> | ||||||
|   <h1>fanfics</h1> |   <h1>fanfics</h1> | ||||||
| @ -17,15 +16,20 @@ dayjs.extend(utc); | |||||||
|   <ul> |   <ul> | ||||||
|     {chapters.map(post => ( |     {chapters.map(post => ( | ||||||
|       <li> |       <li> | ||||||
|         <time datetime={dayjs(post.data.publishedAt).utc(true).toISOString()}> |         <time datetime={formatDate(post.data.publishedAt, true)}> | ||||||
|           {dayjs(post.data.publishedAt).utc(true).format("MMMM DD, YYYY")} |           {formatDate(post.data.publishedAt, false, "MMMM DD, YYYY")} | ||||||
|         </time> |         </time> | ||||||
|         {post.id} |         {fics.some(({ data }) => data.chapters?.some(({ id }) => id === post.id))  | ||||||
|         {JSON.stringify(fics.filter(fic => fic.id.includes(post.id)))} |           ? <> | ||||||
|         <a href={`/fics/${post.id}`}>{post.data.title}</a> in  |               <a href={`/fics/${post.id}`}>{post.data.title}</a> | ||||||
|         <a href={`/fics/${post.id.split("/")[0]}`}> |               in  | ||||||
|           {fics.find(({ id }) => post.id.startsWith(id))?.data.title} |               <a href={`/fics/${post.id.split("/")[0]}`}> | ||||||
|         </a> |                 {fics.filter(({ id }) => id === post.id.split("/")[0])[0].data.title} | ||||||
|  |               </a> | ||||||
|  |             </> | ||||||
|  |           : <a href={`/fics/${post.id.split("/")[0]}`}> | ||||||
|  |               {fics.filter(({ id }) => id === post.id.split("/")[0])[0].data.title} | ||||||
|  |             </a>} | ||||||
|       </li> |       </li> | ||||||
|     ))} |     ))} | ||||||
|   </ul> |   </ul> | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import bulletin from "$/acnl-bulletin.png"; | |||||||
| <Layout> | <Layout> | ||||||
|   <Navbar /> |   <Navbar /> | ||||||
|   <main> |   <main> | ||||||
|     <section id="welcome" class="board"> |     <section id="welcome"> | ||||||
|       <div class="card"> |       <div class="card"> | ||||||
|         <h1>welcome!</h1> |         <h1>welcome!</h1> | ||||||
|         <article> |         <article> | ||||||
| @ -16,7 +16,7 @@ import bulletin from "$/acnl-bulletin.png"; | |||||||
|       </div> |       </div> | ||||||
|     </section> |     </section> | ||||||
| 
 | 
 | ||||||
|     <section id="updates" class="board"> |     <section id="updates"> | ||||||
|       <article class="update card"> |       <article class="update card"> | ||||||
|         <h1>update title</h1> |         <h1>update title</h1> | ||||||
|         <time datetime="">05/01/25</time> |         <time datetime="">05/01/25</time> | ||||||
|  | |||||||
| @ -1,14 +0,0 @@ | |||||||
| --- |  | ||||||
| import Layout from "@/layouts/Layout.astro"; |  | ||||||
| import { getCollection } from "astro:content"; |  | ||||||
| 
 |  | ||||||
| const fics = await getCollection("test"); |  | ||||||
| --- |  | ||||||
| <Layout> |  | ||||||
|   {fics.map(fic => ( |  | ||||||
|     <article> |  | ||||||
|       <h2>{fic.data.title}</h2> |  | ||||||
|     </article> |  | ||||||
|   ))} |  | ||||||
| 
 |  | ||||||
| </Layout> |  | ||||||
| @ -14,55 +14,70 @@ async function getAllChapters(metaPath: string) { | |||||||
| export function ficsLoader(loader: Loader) { | export function ficsLoader(loader: Loader) { | ||||||
|   const oldLoad = loader.load; |   const oldLoad = loader.load; | ||||||
|   loader.load = async (context: LoaderContext) => { |   loader.load = async (context: LoaderContext) => { | ||||||
|     await oldLoad({ |     context.watcher?.on("all", async (_event, path) => { | ||||||
|       ...context, |       if (path.includes("fics")) { | ||||||
|       parseData: async (data) => data.data, |         await resolveFics(oldLoad, context); | ||||||
|  |       } | ||||||
|     }); |     }); | ||||||
|     await Promise.all( |     await resolveFics(oldLoad, context); | ||||||
|       context.store.values().map(async (value) => { |  | ||||||
|         const loadedPromise = Promise.withResolvers(); |  | ||||||
|         getAllChapters(value.filePath as string).then( |  | ||||||
|           async (chapters) => { |  | ||||||
|             const { digest, ...valueWithoutDigest } = value; |  | ||||||
|             const newData = await context.parseData({ |  | ||||||
|               id: value.id, |  | ||||||
|               data: { |  | ||||||
|                 ...valueWithoutDigest.data, |  | ||||||
|                 ...chapters.length > 1 && { chapters: chapters }, |  | ||||||
|               }, |  | ||||||
|             });           |  | ||||||
|             if (chapters.length === 1) { |  | ||||||
|               // i've committed unspeakable atrocities here
 |  | ||||||
|               const search = import.meta.glob<MarkdownInstance<any>>(`../content/fics/**/*.md`, { eager: true }); |  | ||||||
|               const body = Object.values(search).filter(path => path.file?.includes(chapters[0]))[0]; |  | ||||||
|               context.store.set({ |  | ||||||
|                 ...valueWithoutDigest, |  | ||||||
|                 data: newData, |  | ||||||
|                 body: body.rawContent(), |  | ||||||
|                 rendered: { |  | ||||||
|                   html: await body.compiledContent(), |  | ||||||
|                   metadata: { |  | ||||||
|                     headings: body.getHeadings(), |  | ||||||
|                     frontmatter: body.frontmatter, |  | ||||||
|                   }, |  | ||||||
|                 }, |  | ||||||
|                 digest: context.generateDigest(newData), |  | ||||||
|               }); |  | ||||||
|             } else { |  | ||||||
|               context.store.set({ |  | ||||||
|                 ...valueWithoutDigest, |  | ||||||
|                 data: newData, |  | ||||||
|                 digest: context.generateDigest(newData), |  | ||||||
|               }); |  | ||||||
|             } |  | ||||||
|             loadedPromise.resolve(chapters); |  | ||||||
|           } |  | ||||||
|         ); |  | ||||||
| 
 |  | ||||||
|         return loadedPromise.promise; |  | ||||||
|       }) |  | ||||||
|     ); |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return loader; |   return loader; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // please don't ask me why i did this. idk either.
 | ||||||
|  | async function resolveFics(loader: (oldContext: LoaderContext) => Promise<void>, context: LoaderContext) { | ||||||
|  |   await loader({ | ||||||
|  |     ...context, | ||||||
|  |     parseData: async (data) => data.data, | ||||||
|  |   }); | ||||||
|  |   await Promise.all( | ||||||
|  |     context.store.values().map(async (value) => { | ||||||
|  |       const loadedPromise = Promise.withResolvers(); | ||||||
|  |       getAllChapters(value.filePath as string).then( | ||||||
|  |         async (chapters) => { | ||||||
|  |           const { digest, ...valueWithoutDigest } = value; | ||||||
|  |           const newData = await context.parseData({ | ||||||
|  |             id: value.id, | ||||||
|  |             data: { | ||||||
|  |               ...valueWithoutDigest.data, | ||||||
|  |               ...chapters.length > 1 && { chapters: chapters }, | ||||||
|  |             }, | ||||||
|  |           }); | ||||||
|  |           if (chapters.length === 1) { | ||||||
|  |             // i've committed unspeakable atrocities here
 | ||||||
|  |             const search = import.meta.glob<MarkdownInstance<any>>(`../content/fics/**/*.md`); | ||||||
|  |             // const body = Object.values(search).filter(path => path.file?.includes(chapters[0]))[0];
 | ||||||
|  |             for (const path in search) { | ||||||
|  |               if (path.includes(chapters[0])) { | ||||||
|  |                 const body = await search[path](); | ||||||
|  |                 const html = await body.compiledContent(); | ||||||
|  |                 context.store.set({ | ||||||
|  |                   ...valueWithoutDigest, | ||||||
|  |                   data: newData, | ||||||
|  |                   body: body.rawContent(), | ||||||
|  |                   rendered: { | ||||||
|  |                     html, | ||||||
|  |                     metadata: { | ||||||
|  |                       headings: body.getHeadings(), | ||||||
|  |                       frontmatter: body.frontmatter, | ||||||
|  |                     }, | ||||||
|  |                   }, | ||||||
|  |                   digest: context.generateDigest(newData), | ||||||
|  |                 }); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             context.store.set({ | ||||||
|  |               ...valueWithoutDigest, | ||||||
|  |               data: newData, | ||||||
|  |               digest: context.generateDigest(newData), | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |           loadedPromise.resolve(chapters); | ||||||
|  |         } | ||||||
|  |       ); | ||||||
|  |       return loadedPromise.promise; | ||||||
|  |     }) | ||||||
|  |   ); | ||||||
| } | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user