diff --git a/README.md b/README.md index d1c091e..2f97974 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Chiri 🌸 -![screenshot-light](public/images/screenshot-light.png) -![screenshot-dark](public/images/screenshot-dark.png) +![screenshot-light](public/screenshots/screenshot-light.png) +![screenshot-dark](public/screenshots/screenshot-dark.png) Chiri is a minimal blog theme built with [Astro](https://astro.build), offering customization options while preserving its clean aesthetic. diff --git a/package.json b/package.json index 167df43..5184d3d 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "@astrojs/sitemap": "^3.4.1", "@playform/inline": "^0.1.2", "astro": "^5.11.1", + "astro-og-canvas": "^0.7.0", + "canvaskit-wasm": "^0.40.0", "katex": "^0.16.22", "mdast-util-to-string": "^4.0.0", "reading-time": "^1.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c98dbea..3cb6c65 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,12 @@ importers: astro: specifier: ^5.11.1 version: 5.11.1(@types/node@24.0.13)(rollup@4.45.0)(tsx@4.20.3)(typescript@5.8.3) + astro-og-canvas: + specifier: ^0.7.0 + version: 0.7.0(astro@5.11.1(@types/node@24.0.13)(rollup@4.45.0)(tsx@4.20.3)(typescript@5.8.3)) + canvaskit-wasm: + specifier: ^0.40.0 + version: 0.40.0 katex: specifier: ^0.16.22 version: 0.16.22 @@ -851,6 +857,9 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@webgpu/types@0.1.21': + resolution: {integrity: sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -908,6 +917,12 @@ packages: resolution: {integrity: sha512-JepyLROIad6f44uyqMF6HKE2QbunNzp3mYKRcPoDGt0QkxXmH222FAFC64WTyQu2Kg8NNEXHTN/sWuUId9sSxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + astro-og-canvas@0.7.0: + resolution: {integrity: sha512-aamARDNDORPxXlPt+VJakzj4WpNUDjQqUZ06eE82pGMad4aTR/KMm8Ho12qvprgJcM8K7fgSOMg5OHa7KcX0pw==} + engines: {node: '>=18.14.1'} + peerDependencies: + astro: ^3.0.0 || ^4.0.0 || ^5.0.0 + astro@5.11.1: resolution: {integrity: sha512-32dpUh0tXSV/FR2q2/z7LOA6IXl7RqET9J51IA0pPSSi3exhRP3EOSQGjBq10DzXT7VrvplDrFqwfiiWBS8oYA==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} @@ -970,6 +985,12 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} + canvaskit-wasm@0.39.1: + resolution: {integrity: sha512-Gy3lCmhUdKq+8bvDrs9t8+qf7RvcjuQn+we7vTVVyqgOVO1UVfHpsnBxkTZw+R4ApEJ3D5fKySl9TU11hmjl/A==} + + canvaskit-wasm@0.40.0: + resolution: {integrity: sha512-Od2o+ZmoEw9PBdN/yCGvzfu0WVqlufBPEWNG452wY7E9aT8RBE+ChpZF526doOlg7zumO4iCS+RAeht4P0Gbpw==} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -3175,6 +3196,8 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + '@webgpu/types@0.1.21': {} + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -3234,6 +3257,13 @@ snapshots: transitivePeerDependencies: - supports-color + astro-og-canvas@0.7.0(astro@5.11.1(@types/node@24.0.13)(rollup@4.45.0)(tsx@4.20.3)(typescript@5.8.3)): + dependencies: + astro: 5.11.1(@types/node@24.0.13)(rollup@4.45.0)(tsx@4.20.3)(typescript@5.8.3) + canvaskit-wasm: 0.39.1 + deterministic-object-hash: 2.0.2 + entities: 4.5.0 + astro@5.11.1(@types/node@24.0.13)(rollup@4.45.0)(tsx@4.20.3)(typescript@5.8.3): dependencies: '@astrojs/compiler': 2.12.2 @@ -3396,6 +3426,14 @@ snapshots: camelcase@8.0.0: {} + canvaskit-wasm@0.39.1: + dependencies: + '@webgpu/types': 0.1.21 + + canvaskit-wasm@0.40.0: + dependencies: + '@webgpu/types': 0.1.21 + ccount@2.0.1: {} chalk@4.1.2: diff --git a/public/chiri-og.png b/public/og/chiri-og.png similarity index 100% rename from public/chiri-og.png rename to public/og/chiri-og.png diff --git a/public/og/og-bg.png b/public/og/og-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..6153ba334642ecd09e4a29b12eb1394c8cf874e4 GIT binary patch literal 4898 zcmeAS@N?(olHy`uVBq!ia0y~yVA;UHz*NS;#=yYf?@%4Xz`(#+;1OBOz`!jG!i)^F z=14FwFlJ^tI|sOA78m3sRw@J-GcZ)lDV?y_`*48BQUBFJ7pyw?WMzyxov&QtDl5#; z7I8HQVO^@9x*1FMhq}-x2A% zyG-Dwf6VlAdeh71F$LHEjlDYmy!&7Iu^_Lp<>0PO7w>ICnH~(t*^rJGb0?#B0t4VYP1U9m; zKCJ(9xzD~&w|CuWPe3nyQ_eXD{+LW&{pm5*tN`>TcDo)sPV&1o=$sqMYS=EP@*6w9mYd0tdM z{FBV{L6)g(>gV;!=iW-S&TrKI%Py{&wZ~A?-;RMnFy7O}F{Fa=?JY;%1_uF_gRGLT z<27sqE{d<3WO#Ar%=(-83=9kmGLPgy6o>Ez5alsS4UQn#eeW$OJ{+d7i-G8tHx+Xj zL9um|nVW%up}~Z4lo}0!p_~fXG)9Y>!BNzV#=~elFpLJlXb>=r2Ek|$FpLJlXb^z9 l9HTYDXmL1N7T_!ksu>(pShIr9+;IkF7Ef0{mvv4FO#p*uLdgIC literal 0 HcmV?d00001 diff --git a/public/og/og-logo.png b/public/og/og-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7ebd3c4f7058b745fed789bd505693dc10febd48 GIT binary patch literal 3145 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 @@ -47,11 +48,11 @@ const { title, description } = Astro.props as BaseHeadProps - + - + diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index 609c714..7851259 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -17,6 +17,9 @@ import BaseLayout from '@/layouts/BaseLayout.astro' import { themeConfig } from '@/config' const { title, pubDate, readingTime, toc } = Astro.props as PostLayoutProps + +const postSlug = Astro.url.pathname.split('/').filter(Boolean).pop() || '' +const ogImage = `/open-graph/${postSlug}.png` ---
diff --git a/src/pages/open-graph/[...route].ts b/src/pages/open-graph/[...route].ts new file mode 100644 index 0000000..9075f2c --- /dev/null +++ b/src/pages/open-graph/[...route].ts @@ -0,0 +1,49 @@ +import { getCollection } from 'astro:content' +import { OGImageRoute } from 'astro-og-canvas' +import { themeConfig } from '../../config' + +const collectionEntries = await getCollection('posts') + +// Map the array of content collection entries to create an object. +// Converts [{ id: 'post.md', data: { title: 'Example', pubDate: Date } }] +// to { 'post.md': { title: 'Example', pubDate: Date } } +const pages = Object.fromEntries( + collectionEntries.map(({ id, data }) => [id.replace(/\.(md|mdx)$/, ''), data]) +) + +export const { getStaticPaths, GET } = OGImageRoute({ + param: 'route', + pages, + getImageOptions: (_path, page) => ({ + title: page.title, + description: themeConfig.site.title, + logo: { + path: 'public/og/og-logo.png', + size: [80, 80] + }, + bgGradient: [[255, 255, 255]], + bgImage: { + path: 'public/og/og-bg.png', + fit: 'fill' + }, + padding: 64, + font: { + title: { + color: [28, 28, 28], + size: 68, + weight: 'SemiBold', + families: ['Inter'] + }, + description: { + color: [180, 180, 180], + size: 40, + weight: 'Medium', + families: ['Inter'] + } + }, + fonts: [ + 'https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-600-normal.ttf', + 'https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-500-normal.ttf' + ] + }) +}) diff --git a/src/types/component.types.ts b/src/types/component.types.ts index 0e6346f..1d41812 100644 --- a/src/types/component.types.ts +++ b/src/types/component.types.ts @@ -30,6 +30,7 @@ export interface LayoutProps extends TransitionProps { export interface BaseHeadProps { title: string description: string + ogImage?: string } // ImageOptimizer component props interface