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

120 lines
10 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'
import { urlWithParams } from '../lib/urls'
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 21:38:37 +00:00
const defaultOGImageData = fs.readFileSync(path.resolve(process.cwd(), 'src', 'assets', 'ogimage.png'))
2025-05-19 10:23:36 +00:00
export const ogImageTemplates = {
default: () => {
2025-05-19 21:38:37 +00:00
return new Response(Buffer.from(defaultOGImageData), {
status: 200,
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=604800', // Cache for 1 week
},
})
2025-05-19 10:23:36 +00:00
},
generic: ({ title }: { title?: string }) => {
return new ImageResponse(
(
<div
style={{
fontSize: 100,
fontWeight: 'bold',
color: 'white',
backgroundImage: 'linear-gradient(135deg, #061b21, #152619)',
width: '100%',
height: '100%',
padding: '50px 200px',
textAlign: 'center',
justifyContent: 'space-around',
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
}}
>
<span>{title}</span>
<svg viewBox="0 0 640 128" width="480" height="96">
<path
fill="#7CFF00"
d="M66 0a20 20 0 0 0-3 1h-4l-2 1H44a30 30 0 0 0-18 10 22 22 0 0 1-4 5l-1-1a38 38 0 0 0-8-4l-3-1-2-1-1-1-2-1-1 1-2 4-1 2-1 3v1l2 1 2 1 3 1h2a237 237 0 0 0 4 1 17 17 0 0 0-3 5 11 11 0 0 0-1 4l-1 3v2l-1 2a37 37 0 0 1-2 3 21 21 0 0 0-3 4l-1 2v2l-1 1v3a185 185 0 0 0 1 8 22 22 0 0 1 0 7 24 24 0 0 0 1 10 42 42 0 0 0 2 5l1 2a170 170 0 0 1 2 5l1 3a88 88 0 0 0 4 6l1 1 1 2 2 1 2 3a26 26 0 0 0 4 4l2 2 3 2 2 1 3 2 2 1a378 378 0 0 1 4 2l3 2a99 99 0 0 0 5 2 30 30 0 0 0 4 1h2a72 72 0 0 0 20 1l2-1h3l4-1a37 37 0 0 0 6-2l3-2 2-1a26 26 0 0 0 4-2 18 18 0 0 0 3-2l2-2 2-1 1-1 1-1 3-1 1-2h1l3 3a76 76 0 0 0 5 6l1 2h4l2-2 1-1 1-2 1-1-1-2-1-2v-2l-1-1-3-2a54 54 0 0 1-6-4 51 51 0 0 0 4-5 71 71 0 0 0 3-8 179 179 0 0 1 3-8l1-2a56 56 0 0 0 0-6l1-4v-8a70 70 0 0 1-2-11 22 22 0 0 0-2-9l-1-1-1-1a20 20 0 0 1-3-5v-1l-1-2-2-2-1-2a25 25 0 0 1-3-3l-2-2-1-2-1-1a15 15 0 0 0-3-3l-2-1-3-2-3-3-1-1a46 46 0 0 0-15-6h-6l-3-1h-6zm6 10a61 61 0 0 1 6 0 51 51 0 0 1 5 2l2 1 2 1h2l1 1 1 1 1 2 2 1 2 1 3 2a108 108 0 0 0 6 8l3 2 1 2 1 2 2 2a62 62 0 0 1 3 4l1 2 1 2v2l1 4v4l1 2v10a24 24 0 0 1 0 6v5a22 22 0 0 1-2 5l-1 1-1 2-1 1-1 1-1 2-1 1-1 2-3-3-3-2-2-1-1-1-2-1 1-2a59 59 0 0 1-2 1 99 99 0 0 1-2-1 152 152 0 0 0 3-3l1-2v-1l1-1 1-2v-1h-1a29 29 0 0 1-4 6l-2 2a99 99 0 0 1-2-1 72 72 0 0 0 4-4 26 26 0 0 0 3-6 21 21 0 0 0 1-4h-1a63 63 0 0 1-5 8l-2 2a60 60 0 0 1-2 2 469 469 0 0 0-2-1 52 52 0 0 0 5-4 70 70 0 0 0 3-5l1-2a36 36 0 0 0 2-5 29 29 0 0 0 1-6v-1l-1 2a57 57 0 0 1-3 8l-2 3-1 2-1 1-2 1-2 2-2 2h-1a469 469 0 0 0-2-1l-1-1a191 191 0 0 0 1 0l2-1 2-1 3-2a13 13 0 0 0 4-4 11 11 0 0 0 2-3 64 64 0 0 0 3-11v-9a27 27 0 0 0-2-6 26 26 0 0 0-4-5l-2-2-2-2a85 85 0 0 0-11-7l-4-2-2-1-2-1h-2a29 29 0 0 0-6-1l-3 1a46 46 0 0 0-9 1 68 68 0 0 0-5 2 15 15 0 0 0-5 3l-2 2-2 2a15 15 0 0 0-2 3 292 292 0 0 0-6-7 31 31 0 0 0 2-2l1-1 1-1 2-2 2-2 2-1a124 124 0 0 1 22-4h5a61 61 0 0 1 6 0zm-9 9a20 20 0 0 1 9 2l2 1a39 39 0 0 1 8 4l2 1a104 104 0 0 1 6 4 24 24 0 0 1 4 5 97 97 0 0 1 3 7v10l-1 7-2 3-1 2-1 1h-2a30 30 0 0 0-3 3l-3 2a52 52 0 0 1-4 1 43 43 0 0 1-2-1 30 30 0 0 0 5-2 106 106 0 0 1 5-4 65 65 0 0 1 3-2l2-1 1-1 1-3a65 65 0 0 0 1-6l-1-4v-3a18 18 0 0 0-1-5l-1-2a36 36 0 0 0-3-4l-2-2-2-2a14 14 0 0 0-4-3l-2-1a29 29 0 0 1-6-2l-3-1a12 12 0 0 0-4-2h-7a33 33 0 0 0-5 1h-2a25 25 0 0 0-7 3l-1 1-3 3-3 3-1 2-1-1v-1l2-2 2-3 3-2a34 34 0 0 1 6-3l1-1a55 55 0 0 1 12-2zm22 2 1 1a29 29 0 0 1 1 0 381 381 0 0 0-2-1zm2 1a381 381 0 0 1 4 4l2 2 2 1 1 1a16 16 0 0 0-2-3l-3-2a29 29 0 0 0-4-3zm-27 2 4 1h4l1 1a37 37 0 0 0 6 2l4 1h1l2 2 3 3 3 3a138 138 0 0 1 4 6v2a37 37 0 0 1 1 8v3l-1 1-2 2a54 54 0 0 0-5 4l-3 3-3 1-2 1a52 52 0 0 0-1 0 43 43 0 0 1-2-1 71 71 0 0 0 2 0l2-1a28 28 0 0 0 6-4l3-3 3-2 1-2 1-2-1-4a11 11 0 0 0-1-4l-1-3a21 21 0 0 0-2-4l-2-2a36 36 0 0 0-6-5l-2-1a113 113 0 0 1-8-2l-2-1-3-1h-6l-1 1h-1a48 48 0 0 0-6 2l-4 1-2 2a26 26 0 0 0-3 4 66 66 0 0 1-1-1l1-1 2-2 2-2 1-1a47 47 0 0 1 7-4 29 29 0 0 1 7-1zm-40 5a18 18 0 0 1 2 4l1 1 1 1 2 1a24 24 0 0 1 3 3 37 37 0 0 1 1 1 89 89 0 0 0-2 6v3l-1 2v4l1-1 1-2a76 76 0 0 0 3-10 37 37 0 0 1 1 1l1 1a85 85 0 0 0-5 17 42 42 0 0 0-1 13l1 3v2a34 34 0 0 0 3 6l1 2a57 57 0 0 0 10 10 16 16 0 0 0 4 3l3 1 3 1 3 1 2 1 2 2a131 131 0 0 1 8-1h6a18 18 0 0 0 8-3h1a42 42 0 0 1 2 0 18 18 0 0 0 4-2 46 46 0 0 0 5-3l1-1h-2a57 57 0 0 1-6 3l-3 1h-2a26 26 0 0 1-5 2h-6l-5 1h-5l-2-2-1-1h-2l-3-1-2-1-3-1-2-1-2-2a41 41 0 0 0-5-5l-2-3a38 38 0 0 1-5-8l-1-2a55 55 0 0 1 0-8 55 55 0 0 1 1-5l1-4a73 73 0 0 1 2-9l1-4 1 2a93 93 0 0 0-1 8l-1 2v3l-1 2-1 1a43 43 0 0 0 0 7v7l1 2 1 2 2 2a56 56 0 0 1 5 5 34 34 0 0 1 3 4l1 1 1 1 2 1a45 45 0 0 0 6 2h4l1 2 1 1h1a32 32 0 0 1 5-1h6a22 22 0 0 0 6-1l2-1 3-1a22 22 0 0 1 2-1l3-1 2-1a21 21 0 0 0 1-1l3 3 1 1 5 4 1 1a83 83 0 0 1-1 0l-1 1a118 118 0 0 0-3 2l-2 2a67 67 0 0 1-4 3 72 72 0 0 1-7 3l-3 1-2 1-3 1-3 2h-3a25 25 0 0 1-5 2 14 14 0 0 1-4-1 21 21 0 0 0-6-1 61 61 0 0 0-3-1l-1-1h-1a215 215 0 0 1-6-2l-2-1-3-2-2-1-3-2-2-1-2-1-2-2a11 11 0 0 1-2-2l-1-1-1-1-1-2-1-1-2-3-2-2a15 15 0 0 1-2-4l-1-2-1-2-2-4a27 27 0 0 1-2-6v-2l1-2v-3l-1-3V52a24 24 0 0 1 2-4l1-2h1l1-2 1-2a28 28 0 0 0 1-6 33 33 0 0 1 1-4l1-2a22 22 0 0 1 1-1zm41 1a44 44 0 0 1 16 3l2 2 3 2a21 21 0 0 1 3 3 70 70 0 0 1 2 4l1 3 1 4v1l-2 2a44 44 0
/>
</svg>
</div>
),
defaultOptions
)
},
} as const satisfies Record<string, (props: GenericOgImageProps) => ImageResponse | null>
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 ?? {})
}