|
| 1 | +import { ImageResponse } from "next/og"; |
| 2 | +import { readFileSync } from "fs"; |
| 3 | +import path from "path"; |
| 4 | + |
| 5 | +export function generateWebsiteOgImage(title: string, description: string) { |
| 6 | + const bgBuffer = readFileSync( |
| 7 | + path.join(process.cwd(), "public/website-bg.png") |
| 8 | + ); |
| 9 | + const bgDataUrl = `data:image/png;base64,${bgBuffer.toString("base64")}`; |
| 10 | + |
| 11 | + return new ImageResponse( |
| 12 | + ( |
| 13 | + <div |
| 14 | + style={{ |
| 15 | + display: "flex", |
| 16 | + width: "100%", |
| 17 | + height: "100%", |
| 18 | + position: "relative", |
| 19 | + backgroundColor: "#000", |
| 20 | + }} |
| 21 | + > |
| 22 | + {/* Background */} |
| 23 | + <img |
| 24 | + src={bgDataUrl} |
| 25 | + style={{ |
| 26 | + position: "absolute", |
| 27 | + top: 0, |
| 28 | + left: 0, |
| 29 | + width: "100%", |
| 30 | + height: "100%", |
| 31 | + objectFit: "cover", |
| 32 | + }} |
| 33 | + alt="" |
| 34 | + /> |
| 35 | + |
| 36 | + {/* Top-left icon — width:height matches viewBox 467.43:496.7 ratio */} |
| 37 | + <svg |
| 38 | + xmlns="http://www.w3.org/2000/svg" |
| 39 | + viewBox="0 0 2880 551.3" |
| 40 | + style={{ position: "absolute", top: 52, left: 60, height: 38, width: 198 }} |
| 41 | + > |
| 42 | + <path fill="#22b7cb" d="M293.24,126.6V0L0,158.84v337.86l233.72-126.61v126.62l293.23-158.86V0l-233.71,126.6ZM233.72,158.84v143.56l-174.2,94.37v-202.48l174.2-94.37v58.92ZM467.43,302.4l-174.19,94.37v-202.48l174.19-94.37v202.48Z"/> |
| 43 | + <path fill="#ffffff" d="M956.43,333.77h-1c-63.07-96.85-125.65-187.24-188.72-279.12h-65.57v387.39h62.59V167.4h.98c63.09,95.37,126.66,183.76,188.73,274.66h65.55V54.66h-62.57v279.12Z"/> |
| 44 | + <path fill="#ffffff" d="M1218.22,397.84c-43.21,0-77.48-28.81-81.95-77.97h209.09v-17.38c0-104.29-67.05-144.52-131.12-144.52-80.45,0-137.57,62.09-137.57,147s60.59,145.52,141.55,145.52c62.57,0,110.26-34.26,129.12-82.95l-55.12-21.84c-12.92,31.79-39.74,52.14-74,52.14ZM1213.74,208.63c34.26,0,68.04,18.86,72.02,69.03h-146.52c6.95-40.73,33.78-69.03,74.5-69.03Z"/> |
| 45 | + <path fill="#ffffff" d="M1531.62,301.49l91.88-135.58h-66.55l-63.57,98.33h-1l-63.57-98.33h-68.53l92.38,135.58v1c-30.8,46.19-61.59,92.86-92.38,139.56h68.04c21.36-32.78,42.71-65.57,64.06-98.85h1c21.36,33.28,42.71,66.06,64.07,98.85h67.05c-31.29-46.7-62.08-93.38-92.88-139.56v-1Z"/> |
| 46 | + <path fill="#ffffff" d="M2065.43,254.3c0-65.05-46.19-96.34-108.76-96.34-51.66,0-101.33,21.36-118.21,77.48l55.14,18.86c9.43-32.27,32.28-48.16,60.09-48.16,31.79,0,51.65,19.36,51.65,42.7,0,25.83-24.83,27.82-83.43,40.23-49.67,10.43-92.38,31.29-92.38,83.43s44.2,77.97,91.38,77.97c40.23,0,72.5-20.36,87.91-51.15h1c0,15.89,1.48,28.81,5.46,42.71h59.1c-5.46-21.86-8.93-40.73-8.93-86.42v-101.33ZM2005.83,336.25c0,30.8-26.33,68.54-69.04,68.54-25.32,0-42.7-12.41-42.7-34.26,0-26.81,29.79-37.75,54.62-43.21,21.36-4.47,42.71-10.43,57.12-17.88v26.81Z"/> |
| 47 | + <path fill="#ffffff" d="M2230.31,373.01h-.98c-25.33-72.52-49.18-139.56-74.5-207.1h-64.57l108.26,285.57-38.74,99.82h61.59l146.5-385.39h-63.07c-24.83,67.05-49.17,134.58-74.5,207.1Z"/> |
| 48 | + <path fill="#ffffff" d="M2526.36,397.84c-43.21,0-77.48-28.81-81.95-77.97h209.09v-17.38c0-104.29-67.05-144.52-131.12-144.52-80.45,0-137.57,62.09-137.57,147s60.59,145.52,141.55,145.52c62.57,0,110.26-34.26,129.12-82.95l-55.12-21.84c-12.92,31.79-39.74,52.14-74,52.14ZM2521.89,208.63c34.26,0,68.04,18.86,72.02,69.03h-146.52c6.95-40.73,33.78-69.03,74.5-69.03Z"/> |
| 49 | + <path fill="#ffffff" d="M2852.19,157.96c-38.25,0-67.55,24.33-81.45,54.62h-1.99v-46.67h-57.62v276.14h58.61v-114.73c0-45.19,13.91-104.3,76.48-104.3,8.95,0,23.35,1.5,33.78,5.47v-65.57c-9.94-3.96-18.88-4.96-27.81-4.96Z"/> |
| 50 | + <path fill="#ffffff" d="M1777.39,387.47c-16.3,0-27.49-3.5-33.54-10.52-6.07-7.01-9.1-19.8-9.1-38.37V54.66h-62.54v288.47c0,24.26,3.31,43.69,9.95,58.27,6.63,14.59,16.86,25.01,30.7,31.27,13.83,6.25,31.55,9.38,53.16,9.38h10.52c3.98,0,7.96-.2,11.94-.57,3.98-.38,6.91-.76,8.81-1.14v-54.01c-3.04.38-6.07.67-9.1.85-3.04.2-6.64.28-10.8.28Z"/> |
| 51 | + </svg> |
| 52 | + |
| 53 | + {/* Bottom-left content — bottom:88 keeps text within canvas even for wrapped titles */} |
| 54 | + <div |
| 55 | + style={{ |
| 56 | + position: "absolute", |
| 57 | + bottom: 88, |
| 58 | + left: 60, |
| 59 | + right: 60, |
| 60 | + display: "flex", |
| 61 | + flexDirection: "column", |
| 62 | + }} |
| 63 | + > |
| 64 | + {/* Title */} |
| 65 | + <div |
| 66 | + style={{ |
| 67 | + display: "flex", |
| 68 | + fontSize: 64, |
| 69 | + fontWeight: 700, |
| 70 | + lineHeight: 1.5, |
| 71 | + marginBottom: 16, |
| 72 | + background: "linear-gradient(90deg, #1DD0E8 0%, #2C6ED9 100%)", |
| 73 | + backgroundClip: "text", |
| 74 | + color: "transparent", |
| 75 | + maxWidth: 900, |
| 76 | + }} |
| 77 | + > |
| 78 | + {title} |
| 79 | + </div> |
| 80 | + |
| 81 | + {/* Description */} |
| 82 | + <div |
| 83 | + style={{ |
| 84 | + display: "flex", |
| 85 | + fontSize: 19, |
| 86 | + color: "rgba(255,255,255,0.6)", |
| 87 | + lineHeight: 1.5, |
| 88 | + maxWidth: 780, |
| 89 | + }} |
| 90 | + > |
| 91 | + {description} |
| 92 | + </div> |
| 93 | + </div> |
| 94 | + </div> |
| 95 | + ), |
| 96 | + { width: 1200, height: 630 } |
| 97 | + ); |
| 98 | +} |
0 commit comments