redirecter for ao3 that adds opengraph metadata
1import { ImageResponse } from "next/og" 2import General from "@/icons/general.js" 3import Teen from "@/icons/teen.js" 4import Mature from "@/icons/mature.js" 5import Explicit from "@/icons/explicit.js" 6import NotRated from "@/icons/notrated.js" 7import Gen from "@/icons/gen.js" 8import Yaoi from "@/icons/yaoi.js" 9import Yuri from "@/icons/yuri.js" 10import Het from "@/icons/het.js" 11import OtherShip from "@/icons/other.js" 12import MultiShip from "@/icons/multi.js" 13import NoWarnings from "@/icons/nowarnings.js" 14import Warnings from "@/icons/warnings.js" 15import ChoseNotToWarn from "@/icons/chosenottowarn.js" 16 17export default async function OGImage ({ theme, baseFont, titleFont, image, addr, opts }) { 18 return new ImageResponse( 19 ( 20 <div 21 style={{ 22 display: "flex", 23 flexDirection: "column", 24 color: theme.color, 25 backgroundColor: theme.background, 26 fontFamily: baseFont, 27 fontSize: 24, 28 padding: 20, 29 width: "100%", 30 height: "100%", 31 }} 32 > 33 <div 34 style={{ 35 display: "flex", 36 flexDirection: "column", 37 marginBottom: 20 38 }} 39 > 40 <div 41 style={{ 42 textTransform: "uppercase", 43 display: "flex", 44 justifyContent: "center", 45 color: theme.accent, 46 alignItems: "center", 47 display: "flex", 48 justifyContent: "center", 49 alignItems: "center", 50 textAlign: "center" 51 }} 52 > 53 {image.topLine} 54 </div> 55 <div 56 style={{ 57 display: "flex", 58 justifyContent: "center", 59 alignItems: "center", 60 gap: 10 61 }} 62 > 63 {image.props.rating && image.rating === 'E' && (<Explicit fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)} 64 {image.props.rating && image.rating === 'M' && (<Mature fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)} 65 {image.props.rating && image.rating === 'T' && (<Teen fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)} 66 {image.props.rating && image.rating === 'G' && (<General fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)} 67 {image.props.rating && image.rating === 'NR' && (<NotRated fg={theme.accentColor} bg={theme.accent} width={28} height={28} />)} 68 69 {image.props.warnings && image.warning === 'NW' && (<NoWarnings fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)} 70 {image.props.warnings && image.warning === 'CNTW' && (<ChoseNotToWarn fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)} 71 {image.props.warnings && image.warning === 'W' && (<Warnings fg={theme.accent2Color} bg={theme.accent2} width={28} height={28} />)} 72 73 {image.props.category && image.category === 'F' && (<Yuri fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)} 74 {image.props.category && image.category === 'M' && (<Yaoi fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)} 75 {image.props.category && image.category === 'FM' && (<Het fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)} 76 {image.props.category && image.category === 'G' && (<Gen fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)} 77 {image.props.category && image.category === 'MX' && (<MultiShip fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)} 78 {image.props.category && image.category === 'O' && (<OtherShip fg={theme.accent3Color} bg={theme.accent3} width={28} height={28} />)} 79 </div> 80 <div 81 style={{ 82 fontSize: 54, 83 justifyContent: "center", 84 textAlign: "center", 85 fontFamily: titleFont, 86 fontWeight: "bold", 87 color: theme.color, 88 textTransform: (image.props.uppercaseTitle ? 'uppercase' : 'none') 89 }} 90 > 91 {image.titleLine} 92 </div> 93 <div 94 style={{ 95 fontSize: 42, 96 display: "flex", 97 justifyContent: "center", 98 fontFamily: titleFont, 99 color: theme.color 100 }} 101 > 102 {`by ${image.authorLine}`} 103 </div> 104 {image.chapterLine !== '' && (<div 105 style={{ 106 fontStyle: "italic", 107 fontSize: 36, 108 fontFamily: titleFont, 109 display: "flex", 110 justifyContent: "center", 111 color: theme.color, 112 textTransform: (image.props.uppercaseChapterName ? 'uppercase' : 'none') 113 }} 114 > 115 {image.chapterLine} 116 </div>)} 117 </div> 118 <div 119 style={{ 120 backgroundColor: theme.descBackground, 121 padding: 20, 122 display: "flex", 123 flexDirection: "column", 124 flexGrow: 1, 125 color: theme.descColor, 126 alignItems: "flex-end" 127 }} 128 > 129 {image.props.charTags && (<div 130 style={{ 131 display: "flex", 132 flexWrap: "wrap", 133 gap: 5, 134 fontSize: 18, 135 width: "100%", 136 marginBottom: 5 137 }} 138 > 139 {image.charTags.map(c => ( 140 <span 141 style={{ 142 backgroundColor: theme.accent2, 143 color: theme.accent2Color, 144 padding: "3px 5px", 145 borderRadius: 5 146 }} 147 > 148 {c} 149 </span> 150 ))} 151 </div>)} 152 {image.props.relTags && (<div 153 style={{ 154 display: "flex", 155 flexWrap: "wrap", 156 gap: 5, 157 fontSize: 18, 158 width: "100%", 159 marginBottom: 5 160 }} 161 > 162 {image.relTags.map(r => ( 163 <span 164 style={{ 165 backgroundColor: theme.accent3, 166 color: theme.accent3Color, 167 padding: "3px 5px", 168 borderRadius: 5 169 }} 170 > 171 {r} 172 </span> 173 ))} 174 </div>)} 175 {image.props.freeTags && (<div 176 style={{ 177 display: "flex", 178 flexWrap: "wrap", 179 gap: 5, 180 fontSize: 18, 181 width: "100%", 182 marginBottom: 5 183 }} 184 > 185 {image.freeTags.map(f => ( 186 <span 187 style={{ 188 backgroundColor: theme.accent4, 189 color: theme.accent4Color, 190 padding: "3px 5px", 191 borderRadius: 5 192 }} 193 > 194 {f} 195 </span> 196 ))} 197 </div>)} 198 {image.props.summary && (<div 199 style={{ 200 display: "flex", 201 flexDirection: "column", 202 flexGrow: 1, 203 width: '100%' 204 }} 205 > 206 {image.summary.map(l => ( 207 <div 208 style={{ 209 width: "100%", 210 marginBottom: 10 211 }} 212 > 213 {l} 214 </div> 215 ))} 216 </div>)} 217 <div 218 style={{ 219 textAlign: "right", 220 fontSize: 18, 221 display: "flex", 222 justifyContent: "flex-end", 223 alignItems: "center", 224 color: theme.accent2 225 }} 226 > 227 {image.props.wordcount && `${image.words} words • `}{(image.props.chapters && image.chapterCount !== null) && `${image.chapterCount} chapters • `}{image.props.postedAt && `posted on ${image.postedAt}`}{image.props.updatedAt && `updated on ${image.updatedAt}`}{addr} 228 </div> 229 </div> 230 </div> 231 ), 232 opts 233 ) 234}