Spaces:
Running
Running
add og image
Browse files- app/[roastId]/page.tsx +1 -1
- app/api/og/[slug]/route.tsx +88 -0
- app/page.tsx +1 -0
- package-lock.json +155 -3
- package.json +2 -1
- public/inter.ttf +0 -0
- utils/svg.ts +32 -0
app/[roastId]/page.tsx
CHANGED
|
@@ -23,7 +23,7 @@ export default async function Roast({
|
|
| 23 |
}
|
| 24 |
return (
|
| 25 |
<div>
|
| 26 |
-
<header className="flex items-start max-lg:gap-1 lg:items-center justify-between max-lg:flex-col
|
| 27 |
<Image
|
| 28 |
src={Logo}
|
| 29 |
alt="logo hugging face"
|
|
|
|
| 23 |
}
|
| 24 |
return (
|
| 25 |
<div>
|
| 26 |
+
<header className="flex items-start max-lg:gap-1 lg:items-center justify-between max-lg:flex-col mb-5">
|
| 27 |
<Image
|
| 28 |
src={Logo}
|
| 29 |
alt="logo hugging face"
|
app/api/og/[slug]/route.tsx
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { getRoast } from "@/app/actions/roast";
|
| 2 |
+
import { ImageResponse } from "next/og";
|
| 3 |
+
import { NextRequest } from "next/server";
|
| 4 |
+
// App router includes @vercel/og.
|
| 5 |
+
// No need to install it.
|
| 6 |
+
|
| 7 |
+
export async function GET(
|
| 8 |
+
request: NextRequest,
|
| 9 |
+
{ params }: { params: { slug: string } }
|
| 10 |
+
) {
|
| 11 |
+
const { slug } = params;
|
| 12 |
+
const roast = await getRoast({ id: slug });
|
| 13 |
+
|
| 14 |
+
if (!roast?.data) {
|
| 15 |
+
return undefined;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
return new ImageResponse(
|
| 19 |
+
(
|
| 20 |
+
<div
|
| 21 |
+
style={{
|
| 22 |
+
fontSize: 40,
|
| 23 |
+
color: "black",
|
| 24 |
+
background: "white",
|
| 25 |
+
width: "100%",
|
| 26 |
+
height: "100%",
|
| 27 |
+
padding: "60px 60px",
|
| 28 |
+
textAlign: "left",
|
| 29 |
+
justifyContent: "center",
|
| 30 |
+
alignItems: "flex-start",
|
| 31 |
+
display: "flex",
|
| 32 |
+
position: "relative",
|
| 33 |
+
flexDirection: "column",
|
| 34 |
+
}}
|
| 35 |
+
>
|
| 36 |
+
<p
|
| 37 |
+
style={{
|
| 38 |
+
letterSpacing: 10,
|
| 39 |
+
fontSize: 26,
|
| 40 |
+
margin: 0,
|
| 41 |
+
marginBottom: 20,
|
| 42 |
+
color: "#71717a",
|
| 43 |
+
}}
|
| 44 |
+
>
|
| 45 |
+
HUGGER ROASTER
|
| 46 |
+
</p>
|
| 47 |
+
<p
|
| 48 |
+
style={{
|
| 49 |
+
fontSize: 40,
|
| 50 |
+
margin: 0,
|
| 51 |
+
lineHeight: 1.3,
|
| 52 |
+
color: "##27272a",
|
| 53 |
+
whiteSpace: "break-spaces",
|
| 54 |
+
lineClamp: 2,
|
| 55 |
+
}}
|
| 56 |
+
>
|
| 57 |
+
{roast.data.text}...
|
| 58 |
+
</p>
|
| 59 |
+
<p
|
| 60 |
+
style={{
|
| 61 |
+
position: "absolute",
|
| 62 |
+
bottom: -100,
|
| 63 |
+
right: -10,
|
| 64 |
+
fontSize: 240,
|
| 65 |
+
opacity: 0.5,
|
| 66 |
+
}}
|
| 67 |
+
>
|
| 68 |
+
🧨
|
| 69 |
+
</p>
|
| 70 |
+
<div
|
| 71 |
+
style={{
|
| 72 |
+
width: "100vw",
|
| 73 |
+
height: "200px",
|
| 74 |
+
background:
|
| 75 |
+
"linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, white 100%)",
|
| 76 |
+
position: "absolute",
|
| 77 |
+
bottom: 0,
|
| 78 |
+
left: 0,
|
| 79 |
+
}}
|
| 80 |
+
></div>
|
| 81 |
+
</div>
|
| 82 |
+
),
|
| 83 |
+
{
|
| 84 |
+
width: 1200,
|
| 85 |
+
height: 630,
|
| 86 |
+
}
|
| 87 |
+
);
|
| 88 |
+
}
|
app/page.tsx
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
import { useState } from "react";
|
| 3 |
import Image from "next/image";
|
| 4 |
import classNames from "classnames";
|
|
|
|
| 5 |
|
| 6 |
import { roast } from "@/app/actions/roast";
|
| 7 |
import { share, ShareProps } from "@/app/actions/share";
|
|
|
|
| 2 |
import { useState } from "react";
|
| 3 |
import Image from "next/image";
|
| 4 |
import classNames from "classnames";
|
| 5 |
+
import satori from "satori";
|
| 6 |
|
| 7 |
import { roast } from "@/app/actions/roast";
|
| 8 |
import { share, ShareProps } from "@/app/actions/share";
|
package-lock.json
CHANGED
|
@@ -17,7 +17,8 @@
|
|
| 17 |
"prisma": "^5.19.0",
|
| 18 |
"react": "^18",
|
| 19 |
"react-dom": "^18",
|
| 20 |
-
"react-use": "^17.5.1"
|
|
|
|
| 21 |
},
|
| 22 |
"devDependencies": {
|
| 23 |
"@types/node": "^20",
|
|
@@ -579,6 +580,21 @@
|
|
| 579 |
"integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==",
|
| 580 |
"dev": true
|
| 581 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 582 |
"node_modules/@swc/counter": {
|
| 583 |
"version": "0.1.3",
|
| 584 |
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
|
@@ -1278,6 +1294,14 @@
|
|
| 1278 |
"node": ">= 6"
|
| 1279 |
}
|
| 1280 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1281 |
"node_modules/caniuse-lite": {
|
| 1282 |
"version": "1.0.30001653",
|
| 1283 |
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz",
|
|
@@ -1438,6 +1462,24 @@
|
|
| 1438 |
"node": ">= 8"
|
| 1439 |
}
|
| 1440 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1441 |
"node_modules/css-in-js-utils": {
|
| 1442 |
"version": "3.1.0",
|
| 1443 |
"resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
|
|
@@ -1446,6 +1488,16 @@
|
|
| 1446 |
"hyphenate-style-name": "^1.0.3"
|
| 1447 |
}
|
| 1448 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1449 |
"node_modules/css-tree": {
|
| 1450 |
"version": "1.1.3",
|
| 1451 |
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
|
|
@@ -1906,6 +1958,11 @@
|
|
| 1906 |
"url": "https://github.com/sponsors/ljharb"
|
| 1907 |
}
|
| 1908 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1909 |
"node_modules/escape-string-regexp": {
|
| 1910 |
"version": "4.0.0",
|
| 1911 |
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
|
@@ -2417,6 +2474,11 @@
|
|
| 2417 |
"reusify": "^1.0.4"
|
| 2418 |
}
|
| 2419 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2420 |
"node_modules/file-entry-cache": {
|
| 2421 |
"version": "6.0.1",
|
| 2422 |
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
|
@@ -2838,6 +2900,17 @@
|
|
| 2838 |
"node": ">= 0.4"
|
| 2839 |
}
|
| 2840 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2841 |
"node_modules/hyphenate-style-name": {
|
| 2842 |
"version": "1.1.0",
|
| 2843 |
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz",
|
|
@@ -3507,6 +3580,23 @@
|
|
| 3507 |
"node": ">=10"
|
| 3508 |
}
|
| 3509 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3510 |
"node_modules/lines-and-columns": {
|
| 3511 |
"version": "1.2.4",
|
| 3512 |
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
|
@@ -4032,6 +4122,11 @@
|
|
| 4032 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 4033 |
}
|
| 4034 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4035 |
"node_modules/parent-module": {
|
| 4036 |
"version": "1.0.1",
|
| 4037 |
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
|
@@ -4044,6 +4139,15 @@
|
|
| 4044 |
"node": ">=6"
|
| 4045 |
}
|
| 4046 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4047 |
"node_modules/path-exists": {
|
| 4048 |
"version": "4.0.0",
|
| 4049 |
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
|
@@ -4303,8 +4407,7 @@
|
|
| 4303 |
"node_modules/postcss-value-parser": {
|
| 4304 |
"version": "4.2.0",
|
| 4305 |
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
| 4306 |
-
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
| 4307 |
-
"dev": true
|
| 4308 |
},
|
| 4309 |
"node_modules/prebuild-install": {
|
| 4310 |
"version": "7.1.2",
|
|
@@ -4798,6 +4901,31 @@
|
|
| 4798 |
"url": "https://github.com/sponsors/ljharb"
|
| 4799 |
}
|
| 4800 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4801 |
"node_modules/scheduler": {
|
| 4802 |
"version": "0.23.2",
|
| 4803 |
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
|
@@ -5163,6 +5291,11 @@
|
|
| 5163 |
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
| 5164 |
}
|
| 5165 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5166 |
"node_modules/string.prototype.includes": {
|
| 5167 |
"version": "2.0.0",
|
| 5168 |
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz",
|
|
@@ -5489,6 +5622,11 @@
|
|
| 5489 |
"node": ">=10"
|
| 5490 |
}
|
| 5491 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5492 |
"node_modules/to-regex-range": {
|
| 5493 |
"version": "5.0.1",
|
| 5494 |
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
|
@@ -5687,6 +5825,15 @@
|
|
| 5687 |
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
| 5688 |
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
| 5689 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5690 |
"node_modules/uri-js": {
|
| 5691 |
"version": "4.4.1",
|
| 5692 |
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
|
@@ -5926,6 +6073,11 @@
|
|
| 5926 |
"funding": {
|
| 5927 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 5928 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5929 |
}
|
| 5930 |
}
|
| 5931 |
}
|
|
|
|
| 17 |
"prisma": "^5.19.0",
|
| 18 |
"react": "^18",
|
| 19 |
"react-dom": "^18",
|
| 20 |
+
"react-use": "^17.5.1",
|
| 21 |
+
"satori": "^0.10.14"
|
| 22 |
},
|
| 23 |
"devDependencies": {
|
| 24 |
"@types/node": "^20",
|
|
|
|
| 580 |
"integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==",
|
| 581 |
"dev": true
|
| 582 |
},
|
| 583 |
+
"node_modules/@shuding/opentype.js": {
|
| 584 |
+
"version": "1.4.0-beta.0",
|
| 585 |
+
"resolved": "https://registry.npmjs.org/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz",
|
| 586 |
+
"integrity": "sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==",
|
| 587 |
+
"dependencies": {
|
| 588 |
+
"fflate": "^0.7.3",
|
| 589 |
+
"string.prototype.codepointat": "^0.2.1"
|
| 590 |
+
},
|
| 591 |
+
"bin": {
|
| 592 |
+
"ot": "bin/ot"
|
| 593 |
+
},
|
| 594 |
+
"engines": {
|
| 595 |
+
"node": ">= 8.0.0"
|
| 596 |
+
}
|
| 597 |
+
},
|
| 598 |
"node_modules/@swc/counter": {
|
| 599 |
"version": "0.1.3",
|
| 600 |
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
|
|
|
| 1294 |
"node": ">= 6"
|
| 1295 |
}
|
| 1296 |
},
|
| 1297 |
+
"node_modules/camelize": {
|
| 1298 |
+
"version": "1.0.1",
|
| 1299 |
+
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
|
| 1300 |
+
"integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
|
| 1301 |
+
"funding": {
|
| 1302 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1303 |
+
}
|
| 1304 |
+
},
|
| 1305 |
"node_modules/caniuse-lite": {
|
| 1306 |
"version": "1.0.30001653",
|
| 1307 |
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz",
|
|
|
|
| 1462 |
"node": ">= 8"
|
| 1463 |
}
|
| 1464 |
},
|
| 1465 |
+
"node_modules/css-background-parser": {
|
| 1466 |
+
"version": "0.1.0",
|
| 1467 |
+
"resolved": "https://registry.npmjs.org/css-background-parser/-/css-background-parser-0.1.0.tgz",
|
| 1468 |
+
"integrity": "sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA=="
|
| 1469 |
+
},
|
| 1470 |
+
"node_modules/css-box-shadow": {
|
| 1471 |
+
"version": "1.0.0-3",
|
| 1472 |
+
"resolved": "https://registry.npmjs.org/css-box-shadow/-/css-box-shadow-1.0.0-3.tgz",
|
| 1473 |
+
"integrity": "sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg=="
|
| 1474 |
+
},
|
| 1475 |
+
"node_modules/css-color-keywords": {
|
| 1476 |
+
"version": "1.0.0",
|
| 1477 |
+
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
| 1478 |
+
"integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
|
| 1479 |
+
"engines": {
|
| 1480 |
+
"node": ">=4"
|
| 1481 |
+
}
|
| 1482 |
+
},
|
| 1483 |
"node_modules/css-in-js-utils": {
|
| 1484 |
"version": "3.1.0",
|
| 1485 |
"resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
|
|
|
|
| 1488 |
"hyphenate-style-name": "^1.0.3"
|
| 1489 |
}
|
| 1490 |
},
|
| 1491 |
+
"node_modules/css-to-react-native": {
|
| 1492 |
+
"version": "3.2.0",
|
| 1493 |
+
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
|
| 1494 |
+
"integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
|
| 1495 |
+
"dependencies": {
|
| 1496 |
+
"camelize": "^1.0.0",
|
| 1497 |
+
"css-color-keywords": "^1.0.0",
|
| 1498 |
+
"postcss-value-parser": "^4.0.2"
|
| 1499 |
+
}
|
| 1500 |
+
},
|
| 1501 |
"node_modules/css-tree": {
|
| 1502 |
"version": "1.1.3",
|
| 1503 |
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
|
|
|
|
| 1958 |
"url": "https://github.com/sponsors/ljharb"
|
| 1959 |
}
|
| 1960 |
},
|
| 1961 |
+
"node_modules/escape-html": {
|
| 1962 |
+
"version": "1.0.3",
|
| 1963 |
+
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
| 1964 |
+
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
| 1965 |
+
},
|
| 1966 |
"node_modules/escape-string-regexp": {
|
| 1967 |
"version": "4.0.0",
|
| 1968 |
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
|
|
|
| 2474 |
"reusify": "^1.0.4"
|
| 2475 |
}
|
| 2476 |
},
|
| 2477 |
+
"node_modules/fflate": {
|
| 2478 |
+
"version": "0.7.4",
|
| 2479 |
+
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz",
|
| 2480 |
+
"integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw=="
|
| 2481 |
+
},
|
| 2482 |
"node_modules/file-entry-cache": {
|
| 2483 |
"version": "6.0.1",
|
| 2484 |
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
|
|
|
| 2900 |
"node": ">= 0.4"
|
| 2901 |
}
|
| 2902 |
},
|
| 2903 |
+
"node_modules/hex-rgb": {
|
| 2904 |
+
"version": "4.3.0",
|
| 2905 |
+
"resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.3.0.tgz",
|
| 2906 |
+
"integrity": "sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==",
|
| 2907 |
+
"engines": {
|
| 2908 |
+
"node": ">=6"
|
| 2909 |
+
},
|
| 2910 |
+
"funding": {
|
| 2911 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 2912 |
+
}
|
| 2913 |
+
},
|
| 2914 |
"node_modules/hyphenate-style-name": {
|
| 2915 |
"version": "1.1.0",
|
| 2916 |
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz",
|
|
|
|
| 3580 |
"node": ">=10"
|
| 3581 |
}
|
| 3582 |
},
|
| 3583 |
+
"node_modules/linebreak": {
|
| 3584 |
+
"version": "1.1.0",
|
| 3585 |
+
"resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
|
| 3586 |
+
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
|
| 3587 |
+
"dependencies": {
|
| 3588 |
+
"base64-js": "0.0.8",
|
| 3589 |
+
"unicode-trie": "^2.0.0"
|
| 3590 |
+
}
|
| 3591 |
+
},
|
| 3592 |
+
"node_modules/linebreak/node_modules/base64-js": {
|
| 3593 |
+
"version": "0.0.8",
|
| 3594 |
+
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
|
| 3595 |
+
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
|
| 3596 |
+
"engines": {
|
| 3597 |
+
"node": ">= 0.4"
|
| 3598 |
+
}
|
| 3599 |
+
},
|
| 3600 |
"node_modules/lines-and-columns": {
|
| 3601 |
"version": "1.2.4",
|
| 3602 |
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
|
|
|
| 4122 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 4123 |
}
|
| 4124 |
},
|
| 4125 |
+
"node_modules/pako": {
|
| 4126 |
+
"version": "0.2.9",
|
| 4127 |
+
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
| 4128 |
+
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="
|
| 4129 |
+
},
|
| 4130 |
"node_modules/parent-module": {
|
| 4131 |
"version": "1.0.1",
|
| 4132 |
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
|
|
|
| 4139 |
"node": ">=6"
|
| 4140 |
}
|
| 4141 |
},
|
| 4142 |
+
"node_modules/parse-css-color": {
|
| 4143 |
+
"version": "0.2.1",
|
| 4144 |
+
"resolved": "https://registry.npmjs.org/parse-css-color/-/parse-css-color-0.2.1.tgz",
|
| 4145 |
+
"integrity": "sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==",
|
| 4146 |
+
"dependencies": {
|
| 4147 |
+
"color-name": "^1.1.4",
|
| 4148 |
+
"hex-rgb": "^4.1.0"
|
| 4149 |
+
}
|
| 4150 |
+
},
|
| 4151 |
"node_modules/path-exists": {
|
| 4152 |
"version": "4.0.0",
|
| 4153 |
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
|
|
|
| 4407 |
"node_modules/postcss-value-parser": {
|
| 4408 |
"version": "4.2.0",
|
| 4409 |
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
| 4410 |
+
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
|
|
|
| 4411 |
},
|
| 4412 |
"node_modules/prebuild-install": {
|
| 4413 |
"version": "7.1.2",
|
|
|
|
| 4901 |
"url": "https://github.com/sponsors/ljharb"
|
| 4902 |
}
|
| 4903 |
},
|
| 4904 |
+
"node_modules/satori": {
|
| 4905 |
+
"version": "0.10.14",
|
| 4906 |
+
"resolved": "https://registry.npmjs.org/satori/-/satori-0.10.14.tgz",
|
| 4907 |
+
"integrity": "sha512-abovcqmwl97WKioxpkfuMeZmndB1TuDFY/R+FymrZyiGP+pMYomvgSzVPnbNMWHHESOPosVHGL352oFbdAnJcA==",
|
| 4908 |
+
"dependencies": {
|
| 4909 |
+
"@shuding/opentype.js": "1.4.0-beta.0",
|
| 4910 |
+
"css-background-parser": "^0.1.0",
|
| 4911 |
+
"css-box-shadow": "1.0.0-3",
|
| 4912 |
+
"css-to-react-native": "^3.0.0",
|
| 4913 |
+
"emoji-regex": "^10.2.1",
|
| 4914 |
+
"escape-html": "^1.0.3",
|
| 4915 |
+
"linebreak": "^1.1.0",
|
| 4916 |
+
"parse-css-color": "^0.2.1",
|
| 4917 |
+
"postcss-value-parser": "^4.2.0",
|
| 4918 |
+
"yoga-wasm-web": "^0.3.3"
|
| 4919 |
+
},
|
| 4920 |
+
"engines": {
|
| 4921 |
+
"node": ">=16"
|
| 4922 |
+
}
|
| 4923 |
+
},
|
| 4924 |
+
"node_modules/satori/node_modules/emoji-regex": {
|
| 4925 |
+
"version": "10.4.0",
|
| 4926 |
+
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
| 4927 |
+
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="
|
| 4928 |
+
},
|
| 4929 |
"node_modules/scheduler": {
|
| 4930 |
"version": "0.23.2",
|
| 4931 |
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
|
|
|
| 5291 |
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
| 5292 |
}
|
| 5293 |
},
|
| 5294 |
+
"node_modules/string.prototype.codepointat": {
|
| 5295 |
+
"version": "0.2.1",
|
| 5296 |
+
"resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
|
| 5297 |
+
"integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
|
| 5298 |
+
},
|
| 5299 |
"node_modules/string.prototype.includes": {
|
| 5300 |
"version": "2.0.0",
|
| 5301 |
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz",
|
|
|
|
| 5622 |
"node": ">=10"
|
| 5623 |
}
|
| 5624 |
},
|
| 5625 |
+
"node_modules/tiny-inflate": {
|
| 5626 |
+
"version": "1.0.3",
|
| 5627 |
+
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
| 5628 |
+
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
|
| 5629 |
+
},
|
| 5630 |
"node_modules/to-regex-range": {
|
| 5631 |
"version": "5.0.1",
|
| 5632 |
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
|
|
|
| 5825 |
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
| 5826 |
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
| 5827 |
},
|
| 5828 |
+
"node_modules/unicode-trie": {
|
| 5829 |
+
"version": "2.0.0",
|
| 5830 |
+
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
|
| 5831 |
+
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
|
| 5832 |
+
"dependencies": {
|
| 5833 |
+
"pako": "^0.2.5",
|
| 5834 |
+
"tiny-inflate": "^1.0.0"
|
| 5835 |
+
}
|
| 5836 |
+
},
|
| 5837 |
"node_modules/uri-js": {
|
| 5838 |
"version": "4.4.1",
|
| 5839 |
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
|
|
|
| 6073 |
"funding": {
|
| 6074 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 6075 |
}
|
| 6076 |
+
},
|
| 6077 |
+
"node_modules/yoga-wasm-web": {
|
| 6078 |
+
"version": "0.3.3",
|
| 6079 |
+
"resolved": "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz",
|
| 6080 |
+
"integrity": "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA=="
|
| 6081 |
}
|
| 6082 |
}
|
| 6083 |
}
|
package.json
CHANGED
|
@@ -18,7 +18,8 @@
|
|
| 18 |
"prisma": "^5.19.0",
|
| 19 |
"react": "^18",
|
| 20 |
"react-dom": "^18",
|
| 21 |
-
"react-use": "^17.5.1"
|
|
|
|
| 22 |
},
|
| 23 |
"devDependencies": {
|
| 24 |
"@types/node": "^20",
|
|
|
|
| 18 |
"prisma": "^5.19.0",
|
| 19 |
"react": "^18",
|
| 20 |
"react-dom": "^18",
|
| 21 |
+
"react-use": "^17.5.1",
|
| 22 |
+
"satori": "^0.10.14"
|
| 23 |
},
|
| 24 |
"devDependencies": {
|
| 25 |
"@types/node": "^20",
|
public/inter.ttf
ADDED
|
Binary file (343 kB). View file
|
|
|
utils/svg.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export const svgToPngURL = (svg: string) =>
|
| 2 |
+
new Promise<string>((resolve, reject) => {
|
| 3 |
+
const img = new Image();
|
| 4 |
+
img.onload = () => {
|
| 5 |
+
const canvas = document.createElement("canvas");
|
| 6 |
+
canvas.width = img.naturalWidth;
|
| 7 |
+
canvas.height = img.naturalHeight;
|
| 8 |
+
const ctx = canvas.getContext("2d");
|
| 9 |
+
ctx!.drawImage(img, 0, 0);
|
| 10 |
+
resolve(canvas.toDataURL("image/png"));
|
| 11 |
+
URL.revokeObjectURL(img.src);
|
| 12 |
+
};
|
| 13 |
+
img.onerror = (e) => {
|
| 14 |
+
reject(e);
|
| 15 |
+
URL.revokeObjectURL(img.src);
|
| 16 |
+
};
|
| 17 |
+
img.src = URL.createObjectURL(new Blob([svg], { type: "image/svg+xml" }));
|
| 18 |
+
});
|
| 19 |
+
|
| 20 |
+
export const downloadSvgAsPng = async (svg: string) => {
|
| 21 |
+
const pngURL = await svgToPngURL(svg);
|
| 22 |
+
try {
|
| 23 |
+
const a = document.createElement("a");
|
| 24 |
+
a.href = pngURL;
|
| 25 |
+
a.download = "Image.png";
|
| 26 |
+
document.body.appendChild(a);
|
| 27 |
+
a.click();
|
| 28 |
+
document.body.removeChild(a);
|
| 29 |
+
} finally {
|
| 30 |
+
URL.revokeObjectURL(pngURL);
|
| 31 |
+
}
|
| 32 |
+
};
|