diff --git a/app/layout.tsx b/app/layout.tsx index 182158f..6d0ce99 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -13,8 +13,8 @@ import "@/app/globals.css"; const inter = Inter({ subsets: ["latin"] }); const title = { - default: "Ru Chern", - template: "%s | Ru Chern", + default: "Home - Ru Chern", + template: "%s - Ru Chern", }; const description = "Frontend Developer from Singapore. Interested in automating workflows and building in React, Node, and Typescript."; diff --git a/app/notes/[slug]/page.tsx b/app/notes/[slug]/page.tsx index 5067ca7..40efdad 100644 --- a/app/notes/[slug]/page.tsx +++ b/app/notes/[slug]/page.tsx @@ -15,7 +15,7 @@ export const generateMetadata = async (props: { const note = allNotes.find((note) => note.slug === params.slug)!; const title = note.title; - const description = note.title; + const description = `Notes on ${note.title}`; const publishedTime = note.publishedAt; const images = `${BASE_URL}/og?title=${title}`; const url = note.url; diff --git a/app/posts/[slug]/page.tsx b/app/posts/[slug]/page.tsx index a02d62e..6c32ac9 100644 --- a/app/posts/[slug]/page.tsx +++ b/app/posts/[slug]/page.tsx @@ -16,6 +16,7 @@ import { EyeIcon, InformationCircleIcon, } from "@heroicons/react/24/outline"; +import { truncate } from "@/utils/truncate"; type Params = Promise<{ slug: string }>; @@ -26,7 +27,7 @@ export const generateMetadata = async (props: { const post = allPosts.find((post) => post.slug === params.slug)!; const title = post.title; - const description = post.excerpt; + const description = truncate(post.excerpt); const publishedTime = post.publishedAt; const url = post.url; const images = `${BASE_URL}/og?title=${title}`; diff --git a/contentlayer.config.ts b/contentlayer.config.ts index cd28ff4..3ac81af 100644 --- a/contentlayer.config.ts +++ b/contentlayer.config.ts @@ -6,6 +6,7 @@ import remarkGfm from "remark-gfm"; import rehypeSlug from "rehype-slug"; import rehypeAutolinkHeadings from "rehype-autolink-headings"; import remarkUnwrapImages from "remark-unwrap-images"; +import { truncate } from "@/utils/truncate"; // import rehypePrettyCode, { // type Options as PrettyCodeOptions, // } from "rehype-pretty-code"; @@ -42,7 +43,7 @@ export const Post = defineDocumentType(() => ({ headline: post.title, dateModified: post.publishedAt, datePublished: post.publishedAt, - description: post.excerpt, + description: truncate(post.excerpt), image: [ post.image ? encodeURI(`${BASE_URL}/${post.image}`) diff --git a/utils/__tests__/truncate.test.ts b/utils/__tests__/truncate.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/utils/truncate.ts b/utils/truncate.ts new file mode 100644 index 0000000..6c91dd8 --- /dev/null +++ b/utils/truncate.ts @@ -0,0 +1,15 @@ +/** + * Truncates a string to a specified maximum length and adds an ellipsis if truncated. + * + * @param str - The input string to truncate + * @param maxLength - Maximum length of the output string (default: 160 characters) + * @returns The truncated string with ellipsis if truncated, or the original string if shorter than maxLength + * + * @example + * truncate("This is a long string", 10) // Returns "This is..." + * truncate("Short", 10) // Returns "Short" + */ +export const truncate = (str: string, maxLength: number = 160) => { + if (str.length <= maxLength) return str; + return str.slice(0, 157) + "..."; +};