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}