diff --git a/package-lock.json b/package-lock.json index f7d00fe..e78551e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@types/mdx": "^2.0.10", "@types/react-highlight": "^0.12.8", "@types/react-syntax-highlighter": "^15.5.11", + "@vercel/og": "^0.6.2", "@vercel/speed-insights": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", @@ -1070,11 +1071,34 @@ } } }, + "node_modules/@resvg/resvg-wasm": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@resvg/resvg-wasm/-/resvg-wasm-2.4.0.tgz", + "integrity": "sha512-C7c51Nn4yTxXFKvgh2txJFNweaVcfUPQxwEUFw4aWsCmfiBDJsTSwviIF8EcwjQ6k8bPyMWCl1vw4BdxE569Cg==", + "engines": { + "node": ">= 10" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.5.1", "dev": true, "license": "MIT" }, + "node_modules/@shuding/opentype.js": { + "version": "1.4.0-beta.0", + "resolved": "https://registry.npmjs.org/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz", + "integrity": "sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==", + "dependencies": { + "fflate": "^0.7.3", + "string.prototype.codepointat": "^0.2.1" + }, + "bin": { + "ot": "bin/ot" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "license": "MIT", @@ -1447,6 +1471,19 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, + "node_modules/@vercel/og": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@vercel/og/-/og-0.6.2.tgz", + "integrity": "sha512-OTe0KE37F5Y2eTys6eMnfopC+P4qr2ooXUTFyFPTplYSPwowmFk/HLD1FXtbKLjqsIH0SgekcJWad+C5uX4nkg==", + "dependencies": { + "@resvg/resvg-wasm": "2.4.0", + "satori": "0.10.9", + "yoga-wasm-web": "0.3.3" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@vercel/speed-insights": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@vercel/speed-insights/-/speed-insights-1.0.2.tgz", @@ -2057,6 +2094,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -2194,6 +2239,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001588", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz", @@ -2465,6 +2518,24 @@ "node": ">= 8" } }, + "node_modules/css-background-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/css-background-parser/-/css-background-parser-0.1.0.tgz", + "integrity": "sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA==" + }, + "node_modules/css-box-shadow": { + "version": "1.0.0-3", + "resolved": "https://registry.npmjs.org/css-box-shadow/-/css-box-shadow-1.0.0-3.tgz", + "integrity": "sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg==" + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-in-js-utils": { "version": "3.1.0", "license": "MIT", @@ -2472,6 +2543,16 @@ "hyphenate-style-name": "^1.0.3" } }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", @@ -2860,6 +2941,11 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "node_modules/escape-string-regexp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", @@ -3438,6 +3524,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4042,6 +4133,17 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/hex-rgb": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.3.0.tgz", + "integrity": "sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/highlight.js": { "version": "11.9.0", "license": "BSD-3-Clause", @@ -4770,6 +4872,15 @@ "node": ">=10" } }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, "node_modules/linkify-it": { "version": "3.0.3", "license": "MIT", @@ -6331,6 +6442,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6343,6 +6459,15 @@ "node": ">=6" } }, + "node_modules/parse-css-color": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/parse-css-color/-/parse-css-color-0.2.1.tgz", + "integrity": "sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==", + "dependencies": { + "color-name": "^1.1.4", + "hex-rgb": "^4.1.0" + } + }, "node_modules/parse-entities": { "version": "4.0.1", "license": "MIT", @@ -7314,6 +7439,31 @@ "node": ">=14.0.0" } }, + "node_modules/satori": { + "version": "0.10.9", + "resolved": "https://registry.npmjs.org/satori/-/satori-0.10.9.tgz", + "integrity": "sha512-XU9EELUEZuioT4acLIpCXxHcFzrsC8muvg0MY28d+TlqwxbkTzBmWbw+3+hnCzXT7YZ0Qm8k3eXktDaEu+qmEw==", + "dependencies": { + "@shuding/opentype.js": "1.4.0-beta.0", + "css-background-parser": "^0.1.0", + "css-box-shadow": "1.0.0-3", + "css-to-react-native": "^3.0.0", + "emoji-regex": "^10.2.1", + "escape-html": "^1.0.3", + "linebreak": "^1.1.0", + "parse-css-color": "^0.2.1", + "postcss-value-parser": "^4.2.0", + "yoga-wasm-web": "^0.3.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/satori/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -7650,6 +7800,11 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/string.prototype.codepointat": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", + "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==" + }, "node_modules/string.prototype.matchall": { "version": "4.0.10", "dev": true, @@ -8071,6 +8226,11 @@ "node": ">=10" } }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, "node_modules/tiny-invariant": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", @@ -8274,6 +8434,15 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, "node_modules/unified": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", @@ -8784,6 +8953,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoga-wasm-web": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz", + "integrity": "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==" + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 23bbe50..97ddf4b 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@types/mdx": "^2.0.10", "@types/react-highlight": "^0.12.8", "@types/react-syntax-highlighter": "^15.5.11", + "@vercel/og": "^0.6.2", "@vercel/speed-insights": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", @@ -55,6 +56,6 @@ "typescript": "^5" }, "volta": { - "node": "18.19.0" + "node": "18.19.1" } } diff --git a/public/background-gradient.png b/public/background-gradient.png new file mode 100644 index 0000000..98d18fc Binary files /dev/null and b/public/background-gradient.png differ diff --git a/src/app/(home)/story/page.tsx b/src/app/(home)/story/page.tsx index bb46221..a887b6e 100644 --- a/src/app/(home)/story/page.tsx +++ b/src/app/(home)/story/page.tsx @@ -1,6 +1,6 @@ import { NotionAPI } from "notion-client"; import Image from "next/image"; -import { getPageImageUrls, getPageTitle } from "notion-utils"; +import { getPageImageUrls, getPageProperty, getPageTitle } from "notion-utils"; import "@/components/notion/notion.scss"; import "react-notion-x/src/styles.css"; @@ -31,19 +31,39 @@ export async function generateMetadata( const recordMap = await notion.getPage(searchParams.id!); const title = getPageTitle(recordMap); - + const page_block = Object.values(recordMap.block)[0].value; + const description = getPageProperty("description", page_block, recordMap); + const author = getPageProperty("author", page_block, recordMap); + const github_username = getPageProperty("github", page_block, recordMap); const images = getPageImageUrls(recordMap, { mapImageUrl: (url) => url }); - const previousImages = (await parent).openGraph?.images || []; + console.log("desc", description); + + const params = new URLSearchParams({ + title: title.toString(), + description: description.toString(), + author: author.toString(), + images: images.join(","), + github_username: github_username.toString(), + }); + + const image_origin_url = + process.env.NODE_ENV !== "development" + ? "https://raj.how" + : "http://localhost:3000"; + + const og_image_url = new URL("/api/og", image_origin_url); + + og_image_url.search = params.toString(); return { title: title, description: "Written by raj", openGraph: { - images: [...images, ...previousImages], + images: [og_image_url.toString()], }, twitter: { - images: [...images, ...previousImages], + images: [og_image_url.toString()], }, }; } diff --git a/src/app/api/og/route.tsx b/src/app/api/og/route.tsx new file mode 100644 index 0000000..831e29f --- /dev/null +++ b/src/app/api/og/route.tsx @@ -0,0 +1,67 @@ +import { ImageResponse } from "next/og"; +import { NextRequest } from "next/server"; + +export const runtime = "edge"; + +export async function GET(props: NextRequest) { + const url = new URL(props.url); + + const title = url.searchParams.get("title"); + const description = url.searchParams.get("description"); + const author = url.searchParams.get("author"); + const github = url.searchParams.get("github_username"); + const images = url.searchParams.get("images"); + + const image_origin_url = + process.env.NODE_ENV !== "development" + ? "https://raj.how" + : "http://localhost:3000"; + + return new ImageResponse( + ( +
+ og +
+ + {title?.toString()} + + {description?.toString()} +
+ + + + {author?.toString()} + +
+
+ {images ? ( + og + ) : null} +
+ ), + { + width: 1200, + height: 630, + }, + ); +} diff --git a/src/components/notion/renderer.tsx b/src/components/notion/renderer.tsx index a88adc4..735ddb2 100644 --- a/src/components/notion/renderer.tsx +++ b/src/components/notion/renderer.tsx @@ -15,9 +15,9 @@ import { materialOceanic } from "react-syntax-highlighter/dist/esm/styles/prism" // import Highlight from "react-highlight"; -const Code = dynamic(() => import("react-highlight").then((m) => m.default), { - ssr: false, -}); +// const Code = dynamic(() => import("react-highlight").then((m) => m.default), { +// ssr: false, +// }); const Collection = dynamic(() => import("react-notion-x/build/third-party/collection").then(