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 gap: 10, 46 color: theme.accent, 47 alignItems: "center" 48 }} 49 > 50 <div 51 style={{ 52 display: "flex" 53 }} 54 > 55 {image.topLine} 56 </div> 57 58 {image.props.rating && image.rating === 'E' && (<Explicit fg={theme.background} bg={theme.accent} width={28} height={28} />)} 59 {image.props.rating && image.rating === 'M' && (<Mature fg={theme.background} bg={theme.accent} width={28} height={28} />)} 60 {image.props.rating && image.rating === 'T' && (<Teen fg={theme.background} bg={theme.accent} width={28} height={28} />)} 61 {image.props.rating && image.rating === 'G' && (<General fg={theme.background} bg={theme.accent} width={28} height={28} />)} 62 {image.props.rating && image.rating === 'NR' && (<NotRated fg={theme.background} bg={theme.accent} width={28} height={28} />)} 63 64 {image.props.warnings && image.warning === 'NW' && (<NoWarnings fg={theme.background} bg={theme.accent2} width={28} height={28} />)} 65 {image.props.warnings && image.warning === 'CNTW' && (<ChoseNotToWarn fg={theme.background} bg={theme.accent2} width={28} height={28} />)} 66 {image.props.warnings && image.warning === 'W' && (<Warnings fg={theme.background} bg={theme.accent2} width={28} height={28} />)} 67 68 {image.props.category && image.category === 'F' && (<Yuri fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 69 {image.props.category && image.category === 'M' && (<Yaoi fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 70 {image.props.category && image.category === 'FM' && (<Het fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 71 {image.props.category && image.category === 'G' && (<Gen fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 72 {image.props.category && image.category === 'MX' && (<MultiShip fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 73 {image.props.category && image.category === 'O' && (<OtherShip fg={theme.background} bg={theme.accent3} width={28} height={28} />)} 74 </div> 75 <div 76 style={{ 77 fontSize: 54, 78 justifyContent: "center", 79 fontFamily: titleFont, 80 fontWeight: "bold", 81 color: theme.color, 82 textTransform: (image.props.uppercaseTitle ? 'uppercase' : 'none') 83 }} 84 > 85 {image.titleLine} 86 </div> 87 <div 88 style={{ 89 fontSize: 42, 90 display: "flex", 91 justifyContent: "center", 92 fontFamily: titleFont, 93 color: theme.color 94 }} 95 > 96 {`by ${image.authorLine}`} 97 </div> 98 {image.chapterLine !== '' && (<div 99 style={{ 100 fontStyle: "italic", 101 fontSize: 36, 102 fontFamily: titleFont, 103 display: "flex", 104 justifyContent: "center", 105 color: theme.color, 106 textTransform: (image.props.uppercaseChapterName ? 'uppercase' : 'none') 107 }} 108 > 109 {image.chapterLine} 110 </div>)} 111 </div> 112 <div 113 style={{ 114 backgroundColor: theme.descBackground, 115 padding: 20, 116 display: "flex", 117 flexDirection: "column", 118 flexGrow: 1, 119 color: theme.descColor, 120 alignItems: "flex-end" 121 }} 122 > 123 {image.props.charTags && (<div 124 style={{ 125 display: "flex", 126 flexWrap: "wrap", 127 gap: 5, 128 fontSize: 18, 129 width: "100%", 130 marginBottom: 5 131 }} 132 > 133 {image.charTags.map(c => ( 134 <span 135 style={{ 136 backgroundColor: theme.accent2, 137 color: theme.descBackground, 138 padding: "3px 5px", 139 borderRadius: 5 140 }} 141 > 142 {c} 143 </span> 144 ))} 145 </div>)} 146 {image.props.relTags && (<div 147 style={{ 148 display: "flex", 149 flexWrap: "wrap", 150 gap: 5, 151 fontSize: 18, 152 width: "100%", 153 marginBottom: 5 154 }} 155 > 156 {image.relTags.map(r => ( 157 <span 158 style={{ 159 backgroundColor: theme.accent3, 160 color: theme.descBackground, 161 padding: "3px 5px", 162 borderRadius: 5 163 }} 164 > 165 {r} 166 </span> 167 ))} 168 </div>)} 169 {image.props.freeTags && (<div 170 style={{ 171 display: "flex", 172 flexWrap: "wrap", 173 gap: 5, 174 fontSize: 18, 175 width: "100%", 176 marginBottom: 5 177 }} 178 > 179 {image.freeTags.map(f => ( 180 <span 181 style={{ 182 backgroundColor: theme.accent4, 183 color: theme.descBackground, 184 padding: "3px 5px", 185 borderRadius: 5 186 }} 187 > 188 {f} 189 </span> 190 ))} 191 </div>)} 192 {image.props.summary && (<div 193 style={{ 194 display: "flex", 195 flexDirection: "column", 196 flexGrow: 1, 197 width: '100%' 198 }} 199 > 200 {image.summary.map(l => ( 201 <div 202 style={{ 203 width: "100%", 204 marginBottom: 10 205 }} 206 > 207 {l} 208 </div> 209 ))} 210 </div>)} 211 <div 212 style={{ 213 textAlign: "right", 214 fontSize: 18, 215 display: "flex", 216 justifyContent: "flex-end", 217 alignItems: "center", 218 color: theme.accent2 219 }} 220 > 221 {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}`}{process.env.ARCHIVE}/{addr} 222 </div> 223 </div> 224 </div> 225 ), 226 opts 227 ) 228}