Files
kycnotme/web/src/components/OgImage.tsx

124 lines
3.4 KiB
TypeScript
Raw Normal View History

2025-05-19 10:23:36 +00:00
import fs from 'node:fs'
import path from 'node:path'
import { ImageResponse } from '@vercel/og'
2025-05-19 22:13:13 +00:00
import defaultOGImageBg from '../assets/ogimage-bg.png'
import defaultOGImage from '../assets/ogimage.png'
2025-05-19 10:23:36 +00:00
import { urlWithParams } from '../lib/urls'
2025-05-19 22:13:13 +00:00
import type { APIContext } from 'astro'
2025-05-19 10:23:36 +00:00
import type { Prettify } from 'ts-essentials'
export type GenericOgImageProps = Partial<Record<string, string>>
//////////////////////////////////////////////////////
// NOTE //
// Use this website to create and preview templates //
// https://og-playground.vercel.app/ //
//////////////////////////////////////////////////////
const defaultOptions = {
width: 1200,
height: 630,
fonts: [
{
name: 'Inter',
weight: 400,
style: 'normal',
data: fs.readFileSync(
path.resolve(
process.cwd(),
'node_modules',
'@fontsource',
'inter',
'files',
'inter-latin-400-normal.woff'
)
),
},
{
name: 'Inter',
weight: 700,
style: 'normal',
data: fs.readFileSync(
path.resolve(
process.cwd(),
'node_modules',
'@fontsource',
'inter',
'files',
'inter-latin-700-normal.woff'
)
),
},
],
} as const satisfies ConstructorParameters<typeof ImageResponse>[1]
2025-05-19 22:13:13 +00:00
function absoluteUrl(url: string, context: Pick<APIContext, 'url'>) {
return new URL(url, context.url.origin).href
}
2025-05-19 21:38:37 +00:00
2025-05-19 10:23:36 +00:00
export const ogImageTemplates = {
2025-05-19 22:13:13 +00:00
default: (_props: Record<never, never> = {}, context: APIContext) => {
return new ImageResponse(
(
<img
src={absoluteUrl(defaultOGImage.src, context)}
style={{
width: '100%',
height: '100%',
}}
/>
),
defaultOptions
)
2025-05-19 10:23:36 +00:00
},
2025-05-19 22:13:13 +00:00
generic: ({ title }: { title?: string }, context) => {
2025-05-19 10:23:36 +00:00
return new ImageResponse(
(
<div
style={{
fontSize: 100,
fontWeight: 'bold',
color: 'white',
2025-05-19 22:13:13 +00:00
backgroundImage: `url(${absoluteUrl(defaultOGImageBg.src, context)})`,
2025-05-19 10:23:36 +00:00
width: '100%',
height: '100%',
padding: '50px 200px',
textAlign: 'center',
justifyContent: 'space-around',
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
}}
>
<span>{title}</span>
</div>
),
defaultOptions
)
},
2025-05-19 22:13:13 +00:00
} as const satisfies Record<string, (props: GenericOgImageProps, context: APIContext) => ImageResponse | null>
2025-05-19 10:23:36 +00:00
type OgImageTemplate = keyof typeof ogImageTemplates
type OgImageProps<T extends OgImageTemplate> = Parameters<(typeof ogImageTemplates)[T]>[0]
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
export type OgImageAllTemplatesWithGenericProps = { template: OgImageTemplate } & GenericOgImageProps
export type OgImageAllTemplatesWithProps = Prettify<
{
// eslint-disable-next-line @typescript-eslint/sort-type-constituents
[K in OgImageTemplate]: { template: K } & Omit<OgImageProps<K>, 'template'>
}[OgImageTemplate]
>
export function makeOgImageUrl(
ogImage: OgImageAllTemplatesWithProps | string | undefined,
baseUrl: URL | string
) {
return typeof ogImage === 'string'
? new URL(ogImage, baseUrl).href
: urlWithParams(new URL('/ogimage.png', baseUrl), ogImage ?? {})
}