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}