redirecter for ao3 that adds opengraph metadata

remove extra copies of files, implement ratings/categories/warnings

-498
src/app/baseFonts.js
··· 1 - const baseFonts = { 2 - opensans: { 3 - displayName: 'Open Sans', 4 - defs: [ 5 - { 6 - path: '/fonts/OpenSans-Regular.ttf', 7 - style: 'normal', 8 - weight: 400 9 - }, 10 - { 11 - path: '/fonts/OpenSans-Italic.ttf', 12 - style: 'italic', 13 - weight: 400 14 - }, 15 - { 16 - path: '/fonts/OpenSans-Bold.ttf', 17 - style: 'normal', 18 - weight: 700 19 - }, 20 - { 21 - path: '/fonts/OpenSans-BoldItalic.ttf', 22 - style: 'italic', 23 - weight: 700 24 - } 25 - ] 26 - }, 27 - bricolagegrotesque: { 28 - displayName: 'Bricolage Grotesque', 29 - defs: [ 30 - { 31 - path: '/fonts/BricolageGrotesque-Regular.ttf', 32 - style: 'normal', 33 - weight: 400 34 - }, 35 - { 36 - path: '/fonts/BricolageGrotesque-Bold.ttf', 37 - style: 'normal', 38 - weight: 700 39 - } 40 - ] 41 - }, 42 - spacemono: { 43 - displayName: 'Space Mono', 44 - defs: [ 45 - { 46 - path: '/fonts/SpaceMono-Regular.ttf', 47 - style: 'normal', 48 - weight: 400 49 - }, 50 - { 51 - path: '/fonts/SpaceMono-Italic.ttf', 52 - style: 'italic', 53 - weight: 400 54 - }, 55 - { 56 - path: '/fonts/SpaceMono-Bold.ttf', 57 - style: 'normal', 58 - weight: 700 59 - }, 60 - { 61 - path: '/fonts/SpaceMono-BoldItalic.ttf', 62 - style: 'italic', 63 - weight: 700 64 - } 65 - ] 66 - }, 67 - inconsolata: { 68 - displayName: 'Inconsolata', 69 - defs: [ 70 - { 71 - path: '/fonts/Inconsolata.otf', 72 - style: 'normal' 73 - } 74 - ] 75 - }, 76 - bitter: { 77 - displayName: 'Bitter', 78 - defs: [ 79 - { 80 - path: '/fonts/Bitter-Regular.otf', 81 - style: 'normal', 82 - weight: 400 83 - }, 84 - { 85 - path: '/fonts/Bitter-Italic.otf', 86 - style: 'italic', 87 - weight: 400 88 - }, 89 - { 90 - path: '/fonts/Bitter-Bold.otf', 91 - style: 'normal', 92 - weight: 700 93 - }, 94 - { 95 - path: '/fonts/Bitter-BoldItalic.otf', 96 - style: 'italic', 97 - weight: 700 98 - } 99 - ] 100 - }, 101 - archivo: { 102 - displayName: 'Archivo', 103 - defs: [ 104 - { 105 - path: '/fonts/Archivo-Regular.ttf', 106 - style: 'normal', 107 - weight: 400 108 - }, 109 - { 110 - path: '/fonts/Archivo-Italic.ttf', 111 - style: 'italic', 112 - weight: 400 113 - }, 114 - { 115 - path: '/fonts/Archivo-Bold.ttf', 116 - style: 'normal', 117 - weight: 700 118 - }, 119 - { 120 - path: '/fonts/Archivo-BoldItalic.ttf', 121 - style: 'italic', 122 - weight: 700 123 - } 124 - ] 125 - }, 126 - outfit: { 127 - displayName: 'Outfit', 128 - defs: [ 129 - { 130 - path: '/fonts/outfit-regular-webfont.woff2', 131 - style: 'normal', 132 - weight: 400 133 - }, 134 - { 135 - path: '/fonts/outfit-italic-webfont.woff2', 136 - style: 'italic', 137 - weight: 400 138 - } 139 - ] 140 - }, 141 - notosans: { 142 - displayName: 'Noto Sans', 143 - defs: [ 144 - { 145 - path: '/fonts/NotoSans-Regular.ttf', 146 - style: 'normal', 147 - weight: 400 148 - }, 149 - { 150 - path: '/fonts/NotoSans-Italic.ttf', 151 - style: 'italic', 152 - weight: 400 153 - }, 154 - { 155 - path: '/fonts/NotoSans-Bold.ttf', 156 - style: 'normal', 157 - weight: 700 158 - }, 159 - { 160 - path: '/fonts/NotoSans-BoldItalic.ttf', 161 - style: 'italic', 162 - weight: 700 163 - } 164 - ] 165 - }, 166 - alegreya: { 167 - displayName: 'Alegreya', 168 - defs: [ 169 - { 170 - path: '/fonts/Alegreya-Regular.otf', 171 - style: 'normal', 172 - weight: 400 173 - }, 174 - { 175 - path: '/fonts/Alegreya-Italic.otf', 176 - style: 'italic', 177 - weight: 400 178 - }, 179 - { 180 - path: '/fonts/Alegreya-Bold.otf', 181 - style: 'normal', 182 - weight: 700 183 - }, 184 - { 185 - path: '/fonts/Alegreya-BoldItalic.otf', 186 - style: 'italic', 187 - weight: 700 188 - } 189 - ] 190 - }, 191 - alegreyasans: { 192 - displayName: 'Alegreya Sans', 193 - defs: [ 194 - { 195 - path: '/fonts/AlegreyaSans-Regular.otf', 196 - style: 'normal', 197 - weight: 400 198 - }, 199 - { 200 - path: '/fonts/AlegreyaSans-Italic.otf', 201 - style: 'italic', 202 - weight: 400 203 - }, 204 - { 205 - path: '/fonts/AlegreyaSans-Bold.otf', 206 - style: 'normal', 207 - weight: 700 208 - }, 209 - { 210 - path: '/fonts/AlegreyaSans-BoldItalic.otf', 211 - style: 'italic', 212 - weight: 700 213 - } 214 - ] 215 - }, 216 - stacksanstext: { 217 - displayName: 'Stack Sans Text', 218 - defs: [ 219 - { 220 - path: '/fonts/StackSansText-Regular.ttf', 221 - style: 'normal', 222 - weight: 400 223 - }, 224 - { 225 - path: '/fonts/StackSansText-Bold.ttf', 226 - style: 'normal', 227 - weight: 700 228 - } 229 - ], 230 - }, 231 - momotrustsans: { 232 - displayName: 'Momo Trust Sans', 233 - defs: [ 234 - { 235 - path: '/fonts/MomoTrustSans-Regular.ttf', 236 - style: 'normal', 237 - weight: 400 238 - }, 239 - { 240 - path: '/fonts/MomoTrustSans-Bold.ttf', 241 - style: 'normal', 242 - weight: 700 243 - } 244 - ] 245 - }, 246 - montserrat: { 247 - displayName: 'Montserrat', 248 - defs: [ 249 - { 250 - path: '/fonts/Montserrat-Regular.otf', 251 - style: 'normal', 252 - weight: 400 253 - }, 254 - { 255 - path: '/fonts/Montserrat-Italic.otf', 256 - style: 'italic', 257 - weight: 400 258 - }, 259 - { 260 - path: '/fonts/Montserrat-Bold.otf', 261 - style: 'normal', 262 - weight: 700 263 - }, 264 - { 265 - path: '/fonts/Montserrat-BoldItalic.otf', 266 - style: 'italic', 267 - weight: 700 268 - } 269 - ] 270 - }, 271 - robotoslab: { 272 - displayName: 'Roboto Slab', 273 - defs: [ 274 - { 275 - path: '/fonts/RobotoSlab-Regular.ttf', 276 - style: 'normal', 277 - weight: 400 278 - }, 279 - { 280 - path: '/fonts/RobotoSlab-Bold.ttf', 281 - style: 'normal', 282 - weight: 700 283 - } 284 - ] 285 - }, 286 - quicksand: { 287 - displayName: 'Quicksand', 288 - defs: [ 289 - { 290 - path: '/fonts/Quicksand-Regular.otf', 291 - style: 'normal', 292 - weight: 400 293 - }, 294 - { 295 - path: '/fonts/Quicksand-Italic.otf', 296 - style: 'italic', 297 - weight: 400 298 - }, 299 - { 300 - path: '/fonts/Quicksand-Bold.otf', 301 - style: 'normal', 302 - weight: 700 303 - }, 304 - { 305 - path: '/fonts/Quicksand-BoldItalic.otf', 306 - style: 'italic', 307 - weight: 700 308 - } 309 - ] 310 - }, 311 - worksans: { 312 - displayName: 'Work Sans', 313 - defs: [ 314 - { 315 - path: '/fonts/WorkSans-Regular.ttf', 316 - style: 'normal', 317 - weight: 400 318 - }, 319 - { 320 - path: '/fonts/WorkSans-Italic.ttf', 321 - style: 'italic', 322 - weight: 400 323 - }, 324 - { 325 - path: '/fonts/WorkSans-Bold.ttf', 326 - style: 'normal', 327 - weight: 700 328 - }, 329 - { 330 - path: '/fonts/WorkSans-BoldItalic.ttf', 331 - style: 'italic', 332 - weight: 700 333 - } 334 - ] 335 - }, 336 - notosans: { 337 - displayName: 'Noto Sans', 338 - defs: [ 339 - { 340 - path: '/fonts/NotoSans-Regular.ttf', 341 - style: 'normal', 342 - weight: 400 343 - }, 344 - { 345 - path: '/fonts/NotoSans-Italic.ttf', 346 - style: 'italic', 347 - weight: 400 348 - }, 349 - { 350 - path: '/fonts/NotoSans-Bold.ttf', 351 - style: 'normal', 352 - weight: 700 353 - }, 354 - { 355 - path: '/fonts/NotoSans-BoldItalic.ttf', 356 - style: 'italic', 357 - weight: 700 358 - } 359 - ] 360 - }, 361 - notoserif: { 362 - displayName: 'Noto Serif', 363 - defs: [ 364 - { 365 - path: '/fonts/NotoSerif-Regular.ttf', 366 - style: 'normal', 367 - weight: 400 368 - }, 369 - { 370 - path: '/fonts/NotoSerif-Italic.ttf', 371 - style: 'italic', 372 - weight: 400 373 - }, 374 - { 375 - path: '/fonts/NotoSerif-Bold.ttf', 376 - style: 'normal', 377 - weight: 700 378 - }, 379 - { 380 - path: '/fonts/NotoSerif-BoldItalic.ttf', 381 - style: 'italic', 382 - weight: 700 383 - } 384 - ] 385 - }, 386 - librebaskerville: { 387 - displayName: 'Libre Baskerville', 388 - defs: [ 389 - { 390 - path: '/fonts/LibreBaskerville-Regular.otf', 391 - style: 'normal', 392 - weight: 400 393 - }, 394 - { 395 - path: '/fonts/LibreBaskerville-Italic.otf', 396 - style: 'italic', 397 - weight: 400 398 - }, 399 - { 400 - path: '/fonts/LibreBaskerville-Bold.otf', 401 - style: 'normal', 402 - weight: 700 403 - } 404 - ] 405 - }, 406 - ubuntu: { 407 - displayName: 'Ubuntu', 408 - defs: [ 409 - { 410 - path: '/fonts/Ubuntu-Regular.ttf', 411 - style: 'normal', 412 - weight: 400 413 - }, 414 - { 415 - path: '/fonts/Ubuntu-Italic.ttf', 416 - style: 'italic', 417 - weight: 400 418 - }, 419 - { 420 - path: '/fonts/Ubuntu-Bold.ttf', 421 - style: 'normal', 422 - weight: 700 423 - }, 424 - { 425 - path: '/fonts/Ubuntu-BoldItalic.ttf', 426 - style: 'italic', 427 - weight: 700 428 - } 429 - ] 430 - }, 431 - parkinsans: { 432 - displayName: 'Parkinsans', 433 - defs: [ 434 - { 435 - path: '/fonts/Parkinsans-Regular.ttf', 436 - style: 'normal', 437 - weight: 400 438 - }, 439 - { 440 - path: '/fonts/Parkinsans-Bold.ttf', 441 - style: 'normal', 442 - weight: 700 443 - } 444 - ] 445 - }, 446 - lora: { 447 - displayName: 'Lora', 448 - defs: [ 449 - { 450 - path: '/fonts/Lora-Regular.ttf', 451 - style: 'normal', 452 - weight: 400 453 - }, 454 - { 455 - path: '/fonts/Lora-Italic.ttf', 456 - style: 'italic', 457 - weight: 400 458 - }, 459 - { 460 - path: '/fonts/Lora-Bold.ttf', 461 - style: 'normal', 462 - weight: 700 463 - }, 464 - { 465 - path: '/fonts/Lora-BoldItalic.ttf', 466 - style: 'italic', 467 - weight: 700 468 - } 469 - ] 470 - }, 471 - josefinsans: { 472 - displayName: 'Josefin Sans', 473 - defs: [ 474 - { 475 - path: '/fonts/JosefinSans-Regular.ttf', 476 - style: 'normal', 477 - weight: 400 478 - }, 479 - { 480 - path: '/fonts/JosefinSans-Italic.ttf', 481 - style: 'italic', 482 - weight: 400 483 - }, 484 - { 485 - path: '/fonts/JosefinSans-Bold.ttf', 486 - style: 'normal', 487 - weight: 700 488 - }, 489 - { 490 - path: '/fonts/JosefinSans-BoldItalic.ttf', 491 - style: 'italic', 492 - weight: 700 493 - } 494 - ] 495 - } 496 - } 497 - 498 - export default baseFonts
+4 -4
src/app/generator/page.js
··· 1 1 "use client" 2 2 3 3 import { useEffect, useState } from "react" 4 - import themes from "./themes.js" 5 - import baseFonts from "../baseFonts.js" 6 - import titleFonts from "../titleFonts.js" 4 + import themes from "@/lib/themes.js" 5 + import baseFonts from "@/lib/baseFonts.js" 6 + import titleFonts from "@/lib/titleFonts.js" 7 7 import styles from "./page.module.css" 8 8 9 9 export default function Generator() { ··· 122 122 <li><label><input type="checkbox" name="features[]" value="warnings" defaultChecked={props.warnings} onChange={e => updateProp(e.target.value, e.target.checked)} /> Archive Warnings</label></li> 123 123 <li><label><input type="checkbox" name="features[]" value="charTags" defaultChecked={props.charTags} onChange={e => updateProp(e.target.value, e.target.checked)} /> Character Tags</label></li> 124 124 <li><label><input type="checkbox" name="features[]" value="relTags" defaultChecked={props.relTags} onChange={e => updateProp(e.target.value, e.target.checked)} /> Relationship Tags</label></li> 125 - <li><label><input type="checkbox" name="features[]" value="freetags" defaultChecked={props.freeTags} onChange={e => updateProp(e.target.value, e.target.checked)} /> Free Tags</label></li> 125 + <li><label><input type="checkbox" name="features[]" value="freeTags" defaultChecked={props.freeTags} onChange={e => updateProp(e.target.value, e.target.checked)} /> Free Tags</label></li> 126 126 <li><label><input type="checkbox" name="features[]" value="summary" defaultChecked={props.summary} onChange={e => updateProp(e.target.value, e.target.checked)} /> Summary</label></li> 127 127 <li><label><input type="checkbox" name="features[]" value="wordcount" defaultChecked={props.wordcount} onChange={e => updateProp(e.target.value, e.target.checked)} /> Wordcount</label></li> 128 128 <li><label><input type="checkbox" name="features[]" value="chapters" defaultChecked={props.chapters} onChange={e => updateProp(e.target.value, e.target.checked)} /> Chapters</label></li>
+3 -2
src/app/layout.js
··· 4 4 5 5 export const metadata = { 6 6 title: process.env.SITENAME, 7 - description: process.env.DESCRIPTION, 8 - viewport: "width=device-width, initial-scale=1.0" 7 + description: process.env.DESCRIPTION 9 8 }; 9 + 10 + export const viewport = "width=device-width, initial-scale=1.0" 10 11 11 12 export default function RootLayout({ children }) { 12 13 return (
-94
src/app/themes.js
··· 1 - const themes = { 2 - ao3: { 3 - name: 'AO3', 4 - background: '#990000', 5 - color: '#FFFFFF', 6 - descBackground: '#FFFFFF', 7 - descColor: '#000000', 8 - accent: '#FFFFFF', 9 - accent2: '#990000' 10 - }, 11 - softEra: { 12 - name: 'Soft Era', 13 - background: '#F9F5F5', 14 - color: '#C8B3B3', 15 - descBackground: '#F9F5F5', 16 - descColor: '#414141', 17 - accent: '#DB90A7', 18 - accent2: '#EEAABE' 19 - }, 20 - wildCherry: { 21 - name: 'Wild Cherry', 22 - background: '#2B1F32', 23 - color: '#FFFFFF', 24 - descBackground: '#FFFFFF', 25 - descColor: '#2B1F32', 26 - accent: '#E15D97', 27 - accent2: '#0AACC5' 28 - }, 29 - rosePine: { 30 - name: 'Rosé Pine', 31 - background: '#191724', 32 - color: '#e0def4', 33 - descBackground: '#1f1d2e', 34 - descColor: '#e0def4', 35 - accent: '#eb6f92', 36 - accent2: '#31748f' 37 - }, 38 - rosePineDawn: { 39 - name: 'Rosé Pine Dawn', 40 - background: '#faf4ed', 41 - color: '#575279', 42 - descBackground: '#fffaf3', 43 - descColor: '#575279', 44 - accent: '#eb6f92', 45 - accent2: '#286983' 46 - }, 47 - rosePineMoon: { 48 - name: 'Rosé Pine Moon', 49 - background: '#232136', 50 - color: '#e0def4', 51 - descBackground: '#2a273f', 52 - descColor: '#e0def4', 53 - accent: '#b4637a', 54 - accent2: '#3e8fb0' 55 - }, 56 - solarizedLight: { 57 - name: 'Solarized Light', 58 - background: '#fdf6e3', 59 - color: '#b58900', 60 - descBackground: '#eee8d5', 61 - descColor: '#002b36', 62 - accent: '#d33682', 63 - accent2: '#2aa198' 64 - }, 65 - solarizedDark: { 66 - name: 'Solarized Dark', 67 - background: '#002b36', 68 - color: '#b58900', 69 - descBackground: '#073642', 70 - descColor: '#fdf6e3', 71 - accent: '#d33682', 72 - accent2: '#2aa198' 73 - }, 74 - squidgeworld: { 75 - name: 'Squidgeworld', 76 - background: '#b8860b', 77 - color: '#f5f5dc', 78 - descBackground: '#f5f5dc', 79 - color: '#2a2a2a', 80 - accent: '#fece3f', 81 - accent2: '#818D4C' 82 - }, 83 - superlove: { 84 - name: 'Superlove', 85 - background: '#df6191', 86 - color: '#ffffff', 87 - descBackground: '#FFFFFF', 88 - color: '#2a2a2a', 89 - accent: '#F9E4E6', 90 - accent2: '#a33961' 91 - } 92 - } 93 - 94 - export default themes
-226
src/app/titleFonts.js
··· 1 - import baseFonts from "./baseFonts.js" 2 - 3 - const titleFonts = { 4 - ...baseFonts, 5 - playfairdisplay: { 6 - displayName: 'Playfair Display', 7 - defs: [ 8 - { 9 - path: '/fonts/Playfair-Regular.ttf', 10 - style: 'normal', 11 - weight: 400 12 - }, 13 - { 14 - path: '/fonts/Playfair-Italic.ttf', 15 - style: 'italic', 16 - weight: 400 17 - }, 18 - { 19 - path: '/fonts/Playfair-Bold.ttf', 20 - style: 'normal', 21 - weight: 700 22 - }, 23 - { 24 - path: '/fonts/Playfair-BoldItalic.ttf', 25 - style: 'italic', 26 - weight: 700 27 - } 28 - ] 29 - }, 30 - ultra: { 31 - displayName: 'Ultra', 32 - defs: [ 33 - { 34 - path: '/fonts/Ultra-Regular.ttf', 35 - style: 'normal', 36 - weight: 400 37 - } 38 - ] 39 - }, 40 - stacksansheadline: { 41 - displayName: 'Stack Sans Headline', 42 - defs: [ 43 - { 44 - path: '/fonts/StackSansHeadline-Regular.ttf', 45 - style: 'normal', 46 - weight: 400 47 - }, 48 - { 49 - path: '/fonts/StackSansHeadline-Bold.ttf', 50 - style: 'normal', 51 - weight: 700 52 - } 53 - ] 54 - }, 55 - stacksansnotch: { 56 - displayName: 'Stack Sans Notch', 57 - defs: [ 58 - { 59 - path: '/fonts/StackSansNotch-Regular.ttf', 60 - style: 'normal', 61 - weight: 400 62 - }, 63 - { 64 - path: '/fonts/StackSansNotch-Bold.ttf', 65 - style: 'normal', 66 - weight: 700 67 - } 68 - ] 69 - }, 70 - titanone: { 71 - displayName: 'Titan One', 72 - defs: [] 73 - }, 74 - momotrustdisplay: { 75 - displayName: 'Momo Trust Display', 76 - defs: [ 77 - { 78 - path: '/fonts/MomoTrustDisplay-Regular.ttf', 79 - style: 'normal', 80 - weight: 400 81 - }, 82 - { 83 - path: '/fonts/MomoTrustDisplay-Bold.ttf', 84 - style: 'normal', 85 - weight: 700 86 - } 87 - ] 88 - }, 89 - momosignature: { 90 - displayName: 'Momo Signature', 91 - defs: [ 92 - { 93 - path: '/fonts/MomoSignature-Regular.ttf', 94 - style: 'normal', 95 - weight: 400 96 - } 97 - ] 98 - }, 99 - londrinasketch: { 100 - displayName: 'Londrina Sketch', 101 - defs: [ 102 - { 103 - path: '/fonts/LondrinaSketch-Regular.ttf', 104 - style: 'normal', 105 - weight: 400 106 - } 107 - ] 108 - }, 109 - londrinashadow: { 110 - displayName: 'Londrina Shadow', 111 - defs: [ 112 - { 113 - path: '/fonts/LondrinaShadow-Regular.ttf', 114 - style: 'normal', 115 - weight: 400 116 - } 117 - ] 118 - }, 119 - londrinasolid: { 120 - displayName: 'Londrina Solid', 121 - defs: [ 122 - { 123 - path: '/fonts/LondrinaSolid-Regular.ttf', 124 - style: 'normal', 125 - weight: 400 126 - }, 127 - { 128 - path: '/fonts/LondrinaSolid-Black.ttf', 129 - style: 'normal', 130 - weight: 700 131 - } 132 - ] 133 - }, 134 - bebasneue: { 135 - displayName: 'Bebas Neue', 136 - defs: [ 137 - { 138 - path: '/fonts/BebasNeue-Regular.ttf', 139 - style: 'normal', 140 - weight: 400 141 - } 142 - ] 143 - }, 144 - oswald: { 145 - displayName: 'Oswald', 146 - defs: [ 147 - { 148 - path: '/fonts/Oswald-Regular.ttf', 149 - style: 'normal', 150 - weight: 400 151 - }, 152 - { 153 - path: '/fonts/Oswald-Bold.ttf', 154 - style: 'normal', 155 - weight: 700 156 - } 157 - ] 158 - }, 159 - archivoblack: { 160 - displayName: 'Archivo Black', 161 - defs: [ 162 - { 163 - path: '/fonts/ArchivoBlack.otf', 164 - style: 'normal', 165 - weight: 400 166 - } 167 - ] 168 - }, 169 - alfaslabone: { 170 - displayName: 'Alfa Slab One', 171 - defs: [ 172 - { 173 - path: '/fonts/AlfaSlabOne-Regular.ttf', 174 - style: 'normal', 175 - weight: 400 176 - } 177 - ] 178 - }, 179 - sixtyfour: { 180 - displayName: 'SixtyFour', 181 - defs: [ 182 - { 183 - path: '/fonts/Sixtyfour-Regular.ttf', 184 - style: 'normal', 185 - weight: 400 186 - }, 187 - { 188 - path: '/fonts/Sixtyfour-Regular.ttf', 189 - style: 'normal', 190 - weight: 700 191 - } 192 - ] 193 - }, 194 - datalegreyathin: { 195 - displayName: 'Datalegreya Thin', 196 - defs: [ 197 - { 198 - path: '/fonts/Datalegreya-Thin.otf', 199 - style: 'normal', 200 - weight: 400 201 - } 202 - ] 203 - }, 204 - datalegreyadot: { 205 - displayName: 'Datalegreya Dot', 206 - defs: [ 207 - { 208 - path: '/fonts/Datalegreya-Dot.otf', 209 - style: 'normal', 210 - weight: 400 211 - } 212 - ] 213 - }, 214 - datalegreyagradient: { 215 - displayName: 'Datalegreya Gradient', 216 - defs: [ 217 - { 218 - path: '/fonts/Datalegreya-Gradient.otf', 219 - style: 'normal', 220 - weight: 400 221 - } 222 - ] 223 - } 224 - } 225 - 226 - export default titleFonts
+141 -9
src/lib/ogimage.js
··· 11 11 backgroundColor: theme.background, 12 12 fontFamily: baseFont, 13 13 fontSize: 24, 14 - padding: 40, 14 + padding: 20, 15 15 width: "100%", 16 16 height: "100%", 17 17 }} ··· 28 28 textTransform: "uppercase", 29 29 display: "flex", 30 30 justifyContent: "center", 31 - color: theme.accent 31 + gap: 10, 32 + color: theme.accent, 33 + alignItems: "stretch" 32 34 }} 33 35 > 34 - {image.topLine} 36 + <div 37 + style={{ 38 + display: "flex" 39 + }} 40 + > 41 + {image.topLine} 42 + </div> 43 + {image.rating && (<div 44 + style={{ 45 + borderRadius: '100%', 46 + backgroundColor: theme.accent, 47 + color: theme.background, 48 + padding: "0 10px", 49 + display: "flex", 50 + minWidth: 28, 51 + justifyContent: "center", 52 + alignItems: "center", 53 + fontWeight: "bold" 54 + }} 55 + > 56 + {image.rating} 57 + </div>)} 58 + {image.warnings && ( 59 + <div 60 + style={{ 61 + borderRadius: '100%', 62 + backgroundColor: theme.accent2, 63 + color: theme.background, 64 + padding: "0 5px", 65 + display: "flex", 66 + minWidth: 28, 67 + justifyContent: "center", 68 + alignItems: "center", 69 + fontWeight: "bold" 70 + }} 71 + > 72 + {image.warning} 73 + </div> 74 + )} 75 + {image.category && ( 76 + <div 77 + style={{ 78 + borderRadius: '100%', 79 + backgroundColor: theme.accent3, 80 + color: theme.background, 81 + padding: "0 10px", 82 + display: "flex", 83 + minWidth: 28, 84 + justifyContent: "center", 85 + alignItems: "center", 86 + fontWeight: "bold" 87 + }} 88 + > 89 + {image.category} 90 + </div> 91 + )} 35 92 </div> 36 93 <div 37 94 style={{ 38 95 fontSize: 54, 39 96 justifyContent: "center", 40 97 fontFamily: titleFont, 41 - fontWeight: "bold" 98 + fontWeight: "bold", 99 + color: theme.color 42 100 }} 43 101 > 44 102 {image.titleLine} ··· 48 106 fontSize: 42, 49 107 display: "flex", 50 108 justifyContent: "center", 51 - fontFamily: titleFont 109 + fontFamily: titleFont, 110 + color: theme.color 52 111 }} 53 112 > 54 113 {`by ${image.authorLine}`} ··· 59 118 fontSize: 36, 60 119 fontFamily: titleFont, 61 120 display: "flex", 62 - justifyContent: "center" 121 + justifyContent: "center", 122 + color: theme.color 63 123 }} 64 124 > 65 125 {image.chapterLine} ··· 76 136 alignItems: "flex-end" 77 137 }} 78 138 > 79 - <div 139 + {image.props.get("charTags") === 'true' && (<div 140 + style={{ 141 + display: "flex", 142 + flexWrap: "wrap", 143 + gap: 5, 144 + fontSize: 18, 145 + width: "100%", 146 + marginBottom: 5 147 + }} 148 + > 149 + {image.charTags.map(c => ( 150 + <span 151 + style={{ 152 + backgroundColor: theme.accent2, 153 + color: theme.descBackground, 154 + padding: "3px 5px", 155 + borderRadius: 5 156 + }} 157 + > 158 + {c} 159 + </span> 160 + ))} 161 + </div>)} 162 + {image.props.get("relTags") === 'true' && (<div 163 + style={{ 164 + display: "flex", 165 + flexWrap: "wrap", 166 + gap: 5, 167 + fontSize: 18, 168 + width: "100%", 169 + marginBottom: 5 170 + }} 171 + > 172 + {image.relTags.map(r => ( 173 + <span 174 + style={{ 175 + backgroundColor: theme.accent3, 176 + color: theme.descBackground, 177 + padding: "3px 5px", 178 + borderRadius: 5 179 + }} 180 + > 181 + {r} 182 + </span> 183 + ))} 184 + </div>)} 185 + {image.props.get("freeTags") === 'true' && (<div 186 + style={{ 187 + display: "flex", 188 + flexWrap: "wrap", 189 + gap: 5, 190 + fontSize: 18, 191 + width: "100%", 192 + marginBottom: 5 193 + }} 194 + > 195 + {image.freeTags.map(f => ( 196 + <span 197 + style={{ 198 + backgroundColor: theme.accent4, 199 + color: theme.descBackground, 200 + padding: "3px 5px", 201 + borderRadius: 5 202 + }} 203 + > 204 + {f} 205 + </span> 206 + ))} 207 + </div>)} 208 + {image.props.get("summary") === 'true' && (<div 80 209 style={{ 81 210 display: "flex", 82 211 flexDirection: "column", ··· 94 223 {l} 95 224 </div> 96 225 ))} 97 - </div> 226 + </div>)} 98 227 <div 99 228 style={{ 100 229 textAlign: "right", 101 230 fontSize: 18, 231 + display: "flex", 232 + justifyContent: "flex-end", 233 + alignItems: "center", 102 234 color: theme.accent2 103 235 }} 104 236 > 105 - {`https://archiveofourown.org/${addr}`} 237 + {image.props.get("wordcount") === 'true' && `${image.words} words • `}{(image.props.get("chapters") === 'true' && image.chapterCount !== null) && `${image.chapterCount} chapters • `}https://archiveofourown.org/{addr} 106 238 </div> 107 239 </div> 108 240 </div>
+74 -7
src/lib/sanitizeData.js
··· 6 6 import baseFonts from '@/lib/baseFonts.js' 7 7 import titleFonts from '@/lib/titleFonts.js' 8 8 9 + const getHighestRating = async (works) => { 10 + const ratings = await Promise.all(works.map(async (w) => { 11 + const work = await getWork({workId: w.id}) 12 + return work.rating 13 + })) 14 + if (ratings.includes("Not Rated")) { 15 + return "NR" 16 + } else if (ratings.includes("Explicit")) { 17 + return "E" 18 + } else if (ratings.includes("Mature")) { 19 + return "M" 20 + } else if (ratings.includes("Teen")) { 21 + return "T" 22 + } 23 + return "G" 24 + } 25 + 26 + const getHighestWarning = async (works) => { 27 + const warnings = await Promise.all(works.map(async (w) => { 28 + const work = await getWork({workId: w.id}) 29 + return work.tags.warnings 30 + })) 31 + const warningsUnique = warnings.reduce((a, b) => { return a.concat(b) }).filter((w, i) => { return i === warnings.indexOf(w) }) 32 + if (warningsUnique.length === 1 && warningsUnique[0] === "Creator Chose Not To Use Archive Warnings") { 33 + return "⁉️" 34 + } else if (warningsUnique.length === 1 && warningsUnique[0] === "No Archive Warnings Apply") { 35 + return "⭕️" 36 + } 37 + return "⚠️" 38 + } 39 + 40 + const getCategory = async (works) => { 41 + const categories = await Promise.all(works.map(async (w) => { 42 + const work = await getWork({workId: w.id}) 43 + return work.category 44 + })) 45 + const categoriesUnique = categories.reduce((a, b) => { return a.concat(b) }).filter((w, i) => { return i === categories.indexOf(w) }) 46 + if (categoriesUnique.length === 1) { 47 + if (categoriesUnique[0] === "F/F") return "⚢" 48 + if (categoriesUnique[0] === "M/M") return "⚣" 49 + if (categoriesUnique[0] === "F/M") return "⚤" 50 + if (categoriesUnique[0] === "Gen") return "◦" 51 + if (categoriesUnique[0] === "Multi") return "⁕" 52 + if (categoriesUnique[0] === "Other") return "⚧" 53 + } 54 + return "⁕" 55 + } 56 + 9 57 export default async function sanitizeData ({ type, data, props}) { 10 58 const baseFont = props.has('baseFont') ? props.get('baseFont') : process.env.DEFAULT_BASE_FONT 11 59 const baseFontData = baseFonts[baseFont] ··· 40 88 return a.username 41 89 }) 42 90 : [] 91 + const rating = type === 'work' ? data.rating : await getHighestRating(data.works) 92 + const warning = type === 'work' ? await getHighestWarning([data]) : await getHighestWarning(data.works) 93 + const category = type === 'work' ? await getCategory([data]) : await getCategory(data.works) 43 94 const authorString = authorsFormatted.length > 1 44 95 ? authorsFormatted.slice(0, -1).join(", ") + " & " + 45 96 authorsFormatted.slice(-1)[0] ··· 47 98 const summaryContent = type === 'work' 48 99 ? (props.get('summaryType') === 'chapter' && data.chapterInfo && data.chapterInfo.summary ? data.chapterInfo.summary : (props.get('summaryType') === 'custom' && props.has('customSummary') ? props.get('customSummary') : (data.summary ? data.summary : (parentWork ? parentWork.summary : '')))) 49 100 : (props.get('summaryType') === 'custom' && props.has('customSummary') ? props.get('customSummary') : data.notes) 101 + const formatter = new Intl.NumberFormat('en-US') 102 + const words = formatter.format(data.words) 50 103 const summaryDOM = new DOM(summaryContent, {decodeEntities: true}); 51 104 const summaryFormatted = summaryDOM.innerHTML.replace(/\<br(?: \/)?\>/g, "\n").replace( 52 105 /(<([^>]+)>)/ig, ··· 56 109 const chapterString = data.chapterInfo ? (data.chapterInfo.name 57 110 ? data.chapterInfo.name 58 111 : "Chapter " + data.chapterInfo.index) : null 59 - const chapterCountString = data.chapters 60 - ? ' | <b>Chapters:</b> '+data.chapters.published+' / '+( 112 + const chapterCountString = type === 'work' ? (data.chapters 113 + ? data.chapters.published+'/'+( 61 114 data.chapters.total 62 115 ? data.chapters.total 63 116 : '?' 64 117 ) 65 - : '' 118 + : '') : null 66 119 const fandomString = type === 'work' ? ( 67 120 data.fandoms.length > 1 68 121 ? ( 69 - data.fandoms.length <= 5 122 + data.fandoms.length <= 2 70 123 ? data.fandoms.slice(0, -1).join(", ")+" & "+data.fandoms.slice(-1) 71 - : data.fandoms.join(", ")+" (+"+(data.fandoms.length - 4)+")" 124 + : data.fandoms.join(", ")+" (+"+(data.fandoms.length - 2)+")" 72 125 ) 73 126 : data.fandoms[0] 74 127 ) : ( 75 - data.works.map(w => w.fandoms).reduce((a, b) => { return a.concat(b) }).filter((w, i) => { return i === data.works.indexOf(w) }) 128 + '' 76 129 ) 130 + const charTags = type === 'work' ? data.tags.characters : data.works.map(w => w.tags.characters).reduce((a, b) => { return b ? (a ? a.concat(b) : []) : (a ? a : []) }).filter((w, i) => { return i === data.works.indexOf(w) }) 131 + const relTags = type === 'work' ? data.tags.relationships : data.works.map(w => w.tags.relationships).reduce((a, b) => { return b ? (a ? a.concat(b) : []) : (a ? a : []) }).filter((w, i) => { return i === data.works.indexOf(w) }) 132 + const freeTags = type === 'work' ? data.tags.additional : data.works.map(w => w.tags.additional).reduce((a, b) => { return b ? (a ? a.concat(b) : []) : (a ? a : []) }).filter((w, i) => { return i === data.works.indexOf(w) }) 133 + const warnings = type === 'work' ? data.tags.warnings : data.works.map(w => w.tags.warnings).reduce((a, b) => { return b ? (a ? a.concat(b) : []) : (a ? a : []) }).filter((w, i) => { return i === data.works.indexOf(w) }) 134 + 77 135 return { 78 136 topLine: fandomString, 79 137 titleLine: titleString, 80 138 authorLine: authorString, 81 139 chapterLine: chapterString, 140 + chapterCount: chapterCountString, 141 + words: words, 142 + rating: rating, 143 + warning: warning, 144 + category: category, 82 145 summary: summaryFormatted, 83 - url: 'https://archiveofourown.org/', 84 146 theme: themeData, 147 + charTags: charTags, 148 + relTags: relTags, 149 + freeTags: freeTags, 150 + warnings: warnings, 85 151 baseFont: baseFont, 86 152 titleFont: titleFont, 153 + props: props, 87 154 opts: { 88 155 fonts: bfs.concat(tfs) 89 156 }
+32 -12
src/lib/themes.js
··· 6 6 descBackground: '#FFFFFF', 7 7 descColor: '#000000', 8 8 accent: '#FFFFFF', 9 - accent2: '#990000' 9 + accent2: '#990000', 10 + accent3: '#009900', 11 + accent4: '#000099' 10 12 }, 11 13 softEra: { 12 14 name: 'Soft Era', ··· 15 17 descBackground: '#F9F5F5', 16 18 descColor: '#414141', 17 19 accent: '#DB90A7', 18 - accent2: '#EEAABE' 20 + accent2: '#EEAABE', 21 + accent3: '#82B4E3', 22 + accent4: '#a29acb' 19 23 }, 20 24 wildCherry: { 21 25 name: 'Wild Cherry', ··· 24 28 descBackground: '#FFFFFF', 25 29 descColor: '#2B1F32', 26 30 accent: '#E15D97', 27 - accent2: '#0AACC5' 31 + accent2: '#0AACC5', 32 + accent3: '#FFB86C', 33 + accent4: '#35BA66' 28 34 }, 29 35 rosePine: { 30 36 name: 'Rosé Pine', ··· 33 39 descBackground: '#1f1d2e', 34 40 descColor: '#e0def4', 35 41 accent: '#eb6f92', 36 - accent2: '#31748f' 42 + accent2: '#31748f', 43 + accent3: '#f6c177', 44 + accent4: '#c4a7e7' 37 45 }, 38 46 rosePineDawn: { 39 47 name: 'Rosé Pine Dawn', ··· 42 50 descBackground: '#fffaf3', 43 51 descColor: '#575279', 44 52 accent: '#eb6f92', 45 - accent2: '#286983' 53 + accent2: '#286983', 54 + accent3: '#ea9d34', 55 + accent4: '#907aa9' 46 56 }, 47 57 rosePineMoon: { 48 58 name: 'Rosé Pine Moon', ··· 51 61 descBackground: '#2a273f', 52 62 descColor: '#e0def4', 53 63 accent: '#b4637a', 54 - accent2: '#3e8fb0' 64 + accent2: '#3e8fb0', 65 + accent3: '#f6c177', 66 + accent4: '#c4a7e7' 55 67 }, 56 68 solarizedLight: { 57 69 name: 'Solarized Light', ··· 60 72 descBackground: '#eee8d5', 61 73 descColor: '#002b36', 62 74 accent: '#d33682', 63 - accent2: '#2aa198' 75 + accent2: '#2aa198', 76 + accent3: '#859900', 77 + accent4: '#6c71c4' 64 78 }, 65 79 solarizedDark: { 66 80 name: 'Solarized Dark', ··· 69 83 descBackground: '#073642', 70 84 descColor: '#fdf6e3', 71 85 accent: '#d33682', 72 - accent2: '#2aa198' 86 + accent2: '#2aa198', 87 + accent3: '#859900', 88 + accent4: '#6c71c4' 73 89 }, 74 90 squidgeworld: { 75 91 name: 'Squidgeworld', 76 92 background: '#b8860b', 77 93 color: '#f5f5dc', 78 94 descBackground: '#f5f5dc', 79 - color: '#2a2a2a', 95 + descColor: '#2a2a2a', 80 96 accent: '#fece3f', 81 - accent2: '#818D4C' 97 + accent2: '#818D4C', 98 + accent3: '#6D7A34', 99 + accent4: '#556121' 82 100 }, 83 101 superlove: { 84 102 name: 'Superlove', 85 103 background: '#df6191', 86 104 color: '#ffffff', 87 105 descBackground: '#FFFFFF', 88 - color: '#2a2a2a', 106 + descColor: '#2a2a2a', 89 107 accent: '#F9E4E6', 90 - accent2: '#a33961' 108 + accent2: '#a33961', 109 + accent3: '#87254A', 110 + accent4: '#6A1133' 91 111 } 92 112 } 93 113