From 02d5e0e49eebc215164832049e32b0f9cc2e0532 Mon Sep 17 00:00:00 2001 From: Myastr0 <14167316+Myastr0@users.noreply.github.com> Date: Sun, 30 Nov 2025 12:15:51 +0100 Subject: [PATCH] docs(setup blog): setup blog --- .../(home)/blog/[...slug]/-parts/author.tsx | 267 ++++++++++++++++++ docs/app/(home)/blog/[...slug]/page.tsx | 101 +++++++ docs/app/(home)/blog/page.tsx | 92 ++++++ docs/app/(home)/layout.tsx | 10 +- docs/app/docs/[[...slug]]/page.tsx | 6 +- docs/app/sitemap.ts | 53 +++- docs/collections/authors.ts | 30 ++ docs/collections/blogs.ts | 32 +++ docs/collections/docs.ts | 27 ++ docs/components/codeblock.tsx | 22 +- docs/content-collections.ts | 34 +-- .../blog/2025-02-07-alpha-release/index.mdx | 14 + .../blog/2025-06-15-public-release/index.mdx | 33 +++ .../2025-11-30-v3-database-support/index.mdx | 96 +++++++ docs/content/blog/authors.yml | 9 + docs/lib/source.ts | 48 +++- 16 files changed, 827 insertions(+), 47 deletions(-) create mode 100644 docs/app/(home)/blog/[...slug]/-parts/author.tsx create mode 100644 docs/app/(home)/blog/[...slug]/page.tsx create mode 100644 docs/app/(home)/blog/page.tsx create mode 100644 docs/collections/authors.ts create mode 100644 docs/collections/blogs.ts create mode 100644 docs/collections/docs.ts create mode 100644 docs/content/blog/2025-02-07-alpha-release/index.mdx create mode 100644 docs/content/blog/2025-06-15-public-release/index.mdx create mode 100644 docs/content/blog/2025-11-30-v3-database-support/index.mdx create mode 100644 docs/content/blog/authors.yml diff --git a/docs/app/(home)/blog/[...slug]/-parts/author.tsx b/docs/app/(home)/blog/[...slug]/-parts/author.tsx new file mode 100644 index 0000000..1ebcdcd --- /dev/null +++ b/docs/app/(home)/blog/[...slug]/-parts/author.tsx @@ -0,0 +1,267 @@ +import Image from 'next/image'; +import Link from 'next/link'; +import { getAuthorById } from '@/lib/source'; +import type { AuthorWithId } from '@/collections/authors'; +import { cn } from '@/lib/cn'; + +interface AuthorProps { + authorId: string; + variant?: 'compact' | 'full'; + className?: string; + /** Disable link wrapping - use when Author is inside another clickable element */ + disableLink?: boolean; +} + +function GithubIcon({ className }: { className?: string }) { + return ( + + ); +} + +function LinkedInIcon({ className }: { className?: string }) { + return ( + + ); +} + +function TwitterIcon({ className }: { className?: string }) { + return ( + + ); +} + +function AuthorAvatar({ + author, + size = 'md', +}: { + author: AuthorWithId; + size?: 'sm' | 'md' | 'lg'; +}) { + const sizeClasses = { + sm: 'h-8 w-8', + md: 'h-12 w-12', + lg: 'h-16 w-16', + }; + + if (!author.image_url) { + return ( +
+ {author.name.charAt(0).toUpperCase()} +
+ ); + } + + return ( + {author.name} + ); +} + +function SocialLinks({ author }: { author: AuthorWithId }) { + if (!author.socials) return null; + + const { github, linkedin, twitter } = author.socials; + + return ( +
+ {github && ( + + + + )} + {linkedin && ( + + + + )} + {twitter && ( + + + + )} +
+ ); +} + +/** + * Compact author display - shows avatar, name, and title in a row + */ +function AuthorCompact({ + author, + className, + disableLink = false, +}: { + author: AuthorWithId; + className?: string; + disableLink?: boolean; +}) { + const content = ( +
+ +
+ + {author.name} + + {author.title && ( + + {author.title} + + )} +
+
+ ); + + if (author.url && !disableLink) { + return ( + + {content} + + ); + } + + return content; +} + +/** + * Full author display - shows avatar, name, title, and social links + */ +function AuthorFull({ + author, + className, +}: { + author: AuthorWithId; + className?: string; +}) { + return ( +
+ +
+ {author.url ? ( + + {author.name} + + ) : ( + + {author.name} + + )} + {author.title && ( + + {author.title} + + )} + +
+
+ ); +} + +/** + * Author component that displays author information + * Can be used with an author ID to automatically look up author data + */ +export function Author({ + authorId, + variant = 'compact', + className, + disableLink = false, +}: AuthorProps) { + const author = getAuthorById(authorId); + + if (!author) { + return ( +
+ {authorId} +
+ ); + } + + if (variant === 'full') { + return ; + } + + return ( + + ); +} + +/** + * Export individual components for custom compositions + */ +export { AuthorCompact, AuthorFull, AuthorAvatar, SocialLinks }; +export type { AuthorProps }; diff --git a/docs/app/(home)/blog/[...slug]/page.tsx b/docs/app/(home)/blog/[...slug]/page.tsx new file mode 100644 index 0000000..994e7bd --- /dev/null +++ b/docs/app/(home)/blog/[...slug]/page.tsx @@ -0,0 +1,101 @@ +import type { Metadata } from 'next'; +import { notFound } from 'next/navigation'; +import Link from 'next/link'; +import { InlineTOC } from 'fumadocs-ui/components/inline-toc'; +import { blog } from '@/lib/source'; +import { buttonVariants } from '@/components/ui/button'; +import path from 'node:path'; +import { cn } from '@/lib/cn'; +import { MDXContent } from '@content-collections/mdx/react'; +import { getMDXComponents } from '@/mdx-components'; +import { Author } from './-parts/author'; + +export default async function Page(props: PageProps<'/blog/[...slug]'>) { + const params = await props.params; + const page = blog.getPage(params.slug); + + if (!page) notFound(); + const { toc } = page.data; + + return ( +
+
+
+

+ Written by +

+ +
+
+
+

+ Published +

+

+ {new Date( + page.data.date ?? + path.basename(page.path, path.extname(page.path)) + ).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + })} +

+
+
+ +

{page.data.title}

+

{page.data.description}

+ +
+
+ + Back + +
+ + {toc && toc.length > 0 ? ( + + ) : ( +
+ )} + +
+ + {/* Full author card at the bottom */} +
+

+ About the author +

+ +
+
+ ); +} + +export async function generateMetadata( + props: PageProps<'/blog/[...slug]'> +): Promise { + const params = await props.params; + const page = blog.getPage(params.slug); + + if (!page) notFound(); + + return { + title: page.data.title, + description: + page.data.description ?? 'Latest news and updates about Mk Notes', + }; +} + +export function generateStaticParams(): { slug: string }[] { + return blog.generateParams(); +} diff --git a/docs/app/(home)/blog/page.tsx b/docs/app/(home)/blog/page.tsx new file mode 100644 index 0000000..a421493 --- /dev/null +++ b/docs/app/(home)/blog/page.tsx @@ -0,0 +1,92 @@ +import { blog } from '@/lib/source'; +import type { Metadata } from 'next'; +import Link from 'next/link'; +import { basename, extname } from 'path'; +import { Author } from './[...slug]/-parts/author'; + +export const metadata: Metadata = { + title: 'Blog', + description: 'Latest news and updates about MK Notes', +}; + +const getName = (path: string) => { + return basename(path, extname(path)); +}; + +function GrainOverlay() { + return ( + + + + + + + + ); +} + +export default function BlogIndex() { + const posts = [...blog.getPages()].sort( + (a, b) => + new Date(b.data.date ?? getName(b.path)).getTime() - + new Date(a.data.date ?? getName(a.path)).getTime() + ); + + return ( +
+
+ {/* Gradient background */} +
+ {/* Grain texture overlay */} + +

+ Mk Notes Blog +

+

+ Latest announcements of Mk Notes. +

+
+
+ {posts.map((post) => ( + +

{post.data.title}

+

+ {post.data.description} +

+ +
+ +

+ {new Date( + post.data.date ?? getName(post.path) + ).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + })} +

+
+ + ))} +
+
+ ); +} diff --git a/docs/app/(home)/layout.tsx b/docs/app/(home)/layout.tsx index 1dde10c..af5ccc5 100644 --- a/docs/app/(home)/layout.tsx +++ b/docs/app/(home)/layout.tsx @@ -1,6 +1,6 @@ import { HomeLayout } from 'fumadocs-ui/layouts/home'; import { baseOptions } from '@/lib/layout.shared'; -import { BookIcon } from 'lucide-react'; +import { BookIcon, NewspaperIcon } from 'lucide-react'; export default function Layout({ children }: LayoutProps<'/'>) { return ) { label: "Visit documentation", text: 'Documentation', url: '/docs', - } + }, + { + icon: , + label: "Visit blog", + text: 'Blog', + url: '/blog', + }, ] } >{children}; diff --git a/docs/app/docs/[[...slug]]/page.tsx b/docs/app/docs/[[...slug]]/page.tsx index 2033025..556d8f2 100644 --- a/docs/app/docs/[[...slug]]/page.tsx +++ b/docs/app/docs/[[...slug]]/page.tsx @@ -19,7 +19,9 @@ export default async function Page(props: PageProps<'/docs/[[...slug]]'>) { return ( {page.data.title} - {page.data.description} + + {page.data.description} + , + props: PageProps<'/docs/[[...slug]]'> ): Promise { const params = await props.params; const page = source.getPage(params.slug); diff --git a/docs/app/sitemap.ts b/docs/app/sitemap.ts index fbdd700..e4f26a2 100644 --- a/docs/app/sitemap.ts +++ b/docs/app/sitemap.ts @@ -10,7 +10,7 @@ const BASE_URL = 'https://www.mk-notes.io' // Update this to your actual domain /** * Recursively scan directory for .mdx files to generate sitemap URLs */ -function scanDirectory(dir: string, basePath: string = ''): string[] { +function scanDocsDirectory(dir: string, basePath: string = ''): string[] { const pages: string[] = [] try { @@ -22,7 +22,7 @@ function scanDirectory(dir: string, basePath: string = ''): string[] { if (item.isDirectory()) { // Recursively scan subdirectories - pages.push(...scanDirectory(fullPath, relativePath)) + pages.push(...scanDocsDirectory(fullPath, relativePath)) } else if (item.isFile() && item.name.endsWith('.mdx')) { // Convert .mdx file to URL path const urlPath = relativePath @@ -44,6 +44,40 @@ function scanDirectory(dir: string, basePath: string = ''): string[] { return pages } +function scanBlogDirectory(dir: string, basePath: string = ''): string[] { + const pages: string[] = [] + + try { + const items = fs.readdirSync(dir, { withFileTypes: true }) + + for (const item of items) { + const fullPath = path.join(dir, item.name) + const relativePath = path.join(basePath, item.name) + + if (item.isDirectory()) { + // Recursively scan subdirectories + pages.push(...scanBlogDirectory(fullPath, relativePath)) + } else if (item.isFile() && item.name.endsWith('.mdx')) { + // Convert .mdx file to URL path + const urlPath = relativePath + .replace(/\.mdx$/, '') + .replace(/\\/g, '/') // Normalize path separators + + // Skip index files as they represent the directory itself + if (item.name === 'index.mdx') { + pages.push(`/blog/${basePath.replace(/\\/g, '/')}`) + } else { + pages.push(`/blog/${urlPath}`) + } + } + } + } catch (error) { + console.warn(`Warning: Could not scan directory ${dir}:`, (error as Error).message) + } + + return pages +} + export default function sitemap(): MetadataRoute.Sitemap { // Static pages that should always be included const staticPages = [ @@ -63,7 +97,18 @@ export default function sitemap(): MetadataRoute.Sitemap { // Scan for documentation pages const contentDir = path.join(process.cwd(), 'content/docs') - const docPages = scanDirectory(contentDir) + const docPages = scanDocsDirectory(contentDir) + + // Scan for blog pages + const blogDir = path.join(process.cwd(), 'content/blog') + const blogPages = scanBlogDirectory(blogDir) + + // Convert blog pages to sitemap format + const blogPagesSitemap = blogPages.map((page) => ({ + url: `${BASE_URL}${page}`, + changeFrequency: 'monthly' as const, + priority: 0.7, + })) // Convert documentation pages to sitemap format const documentationPages = docPages.map((page) => ({ @@ -73,5 +118,5 @@ export default function sitemap(): MetadataRoute.Sitemap { priority: 0.8, })) - return [...staticPages, ...documentationPages] + return [...staticPages, ...documentationPages, ...blogPagesSitemap] } diff --git a/docs/collections/authors.ts b/docs/collections/authors.ts new file mode 100644 index 0000000..61c6dde --- /dev/null +++ b/docs/collections/authors.ts @@ -0,0 +1,30 @@ +import { defineCollection } from '@content-collections/core'; +import { z } from 'zod'; + +export const authorSchema = z.object({ + name: z.string(), + title: z.string().optional(), + url: z.string().url().optional(), + image_url: z.string().url().optional(), + page: z.boolean().optional().default(false), + socials: z + .object({ + github: z.string().optional(), + linkedin: z.string().optional(), + twitter: z.string().optional(), + }) + .optional(), +}); + +export type Author = z.infer; +export type AuthorWithId = Author & { id: string }; +export type AuthorsRecord = Record; + +export const authors = defineCollection({ + name: 'authors', + directory: 'content/blog', + include: 'authors.yml', + parser: 'yaml', + schema: z.record(z.string(), authorSchema), +}); + diff --git a/docs/collections/blogs.ts b/docs/collections/blogs.ts new file mode 100644 index 0000000..95c9310 --- /dev/null +++ b/docs/collections/blogs.ts @@ -0,0 +1,32 @@ +import { z } from 'zod'; +import { defineCollection } from '@content-collections/core'; +import { + frontmatterSchema, + metaSchema, + transformMDX, +} from '@fumadocs/content-collections/configuration'; +import { remarkNpm } from 'fumadocs-core/mdx-plugins'; + +export const blogs = defineCollection({ + name: 'blogs', + directory: 'content/blog', + include: '**/*.mdx', + schema: frontmatterSchema.extend({ + author: z.string(), + date: z.string().date().or(z.date()), + }), + transform: (document, context) => { + return transformMDX(document, context, { + remarkPlugins: [remarkNpm], + }); + }, +}); + +export const blogsMetas = defineCollection({ + name: 'blogsMetas', + directory: 'content/blog', + include: '**/meta.json', + parser: 'json', + schema: metaSchema, +}); + diff --git a/docs/collections/docs.ts b/docs/collections/docs.ts new file mode 100644 index 0000000..fc3ce00 --- /dev/null +++ b/docs/collections/docs.ts @@ -0,0 +1,27 @@ +import { defineCollection } from '@content-collections/core'; +import { + frontmatterSchema, + metaSchema, + transformMDX, +} from '@fumadocs/content-collections/configuration'; +import { remarkNpm } from 'fumadocs-core/mdx-plugins'; + +export const docs = defineCollection({ + name: 'docs', + directory: 'content/docs', + include: '**/*.mdx', + schema: frontmatterSchema, + transform: (document, context) => + transformMDX(document, context, { + remarkPlugins: [remarkNpm], + }), +}); + +export const docsMetas = defineCollection({ + name: 'docsMetas', + directory: 'content/docs', + include: '**/meta.json', + parser: 'json', + schema: metaSchema, +}); + diff --git a/docs/components/codeblock.tsx b/docs/components/codeblock.tsx index a1cf75d..95617f5 100644 --- a/docs/components/codeblock.tsx +++ b/docs/components/codeblock.tsx @@ -6,7 +6,7 @@ import { type HTMLAttributes, type ReactNode, type RefObject, - useContext, + use, useMemo, useRef, } from 'react'; @@ -87,7 +87,7 @@ export function CodeBlock({ ), ...props }: CodeBlockProps) { - const inTab = useContext(TabsContext) !== null; + const inTab = use(TabsContext) !== null; const areaRef = useRef(null); return ( @@ -95,13 +95,14 @@ export function CodeBlock({ ref={ref} dir="ltr" {...props} + tabIndex={-1} className={cn( inTab ? 'bg-fd-secondary -mx-px -mb-px last:rounded-b-xl' : 'my-4 bg-fd-card rounded-xl', keepBackground && 'bg-(--shiki-light-bg) dark:bg-(--shiki-dark-bg)', - 'shiki relative border shadow-sm outline-none not-prose overflow-hidden text-sm', + 'shiki relative border shadow-sm not-prose overflow-hidden text-sm', props.className, )} > @@ -133,8 +134,10 @@ export function CodeBlock({
) { const containerRef = useRef(null); - const nested = useContext(TabsContext) !== null; + const nested = use(TabsContext) !== null; return ( ) { props.className, )} > - ({ containerRef, @@ -218,7 +221,7 @@ export function CodeBlockTabs({ ref, ...props }: ComponentProps) { )} > {props.children} - + ); } @@ -255,7 +258,6 @@ export function CodeBlockTabsTrigger({ ); } -// TODO: currently Vite RSC plugin has problem with `asChild` due to children is automatically wrapped in , maybe revisit this in future export function CodeBlockTab(props: ComponentProps) { return ; } diff --git a/docs/content-collections.ts b/docs/content-collections.ts index 93b53e0..e4354b8 100644 --- a/docs/content-collections.ts +++ b/docs/content-collections.ts @@ -1,30 +1,12 @@ -import { defineCollection, defineConfig } from '@content-collections/core'; -import { - frontmatterSchema, - metaSchema, - transformMDX, -} from '@fumadocs/content-collections/configuration'; -import {remarkNpm} from 'fumadocs-core/mdx-plugins'; +import { defineConfig } from '@content-collections/core'; +import { docs, docsMetas } from './collections/docs'; +import { blogs, blogsMetas } from './collections/blogs'; +import { authors } from './collections/authors'; -const docs = defineCollection({ - name: 'docs', - directory: 'content/docs', - include: '**/*.mdx', - schema: frontmatterSchema, - transform: (document, context) => - transformMDX(document, context, { - remarkPlugins: [remarkNpm], - }), -}); - -const metas = defineCollection({ - name: 'meta', - directory: 'content/docs', - include: '**/meta.json', - parser: 'json', - schema: metaSchema, -}); +export { docs, docsMetas } from './collections/docs'; +export { blogs, blogsMetas } from './collections/blogs'; +export { authors } from './collections/authors'; export default defineConfig({ - collections: [docs, metas], + collections: [docs, docsMetas, blogs, blogsMetas, authors], }); diff --git a/docs/content/blog/2025-02-07-alpha-release/index.mdx b/docs/content/blog/2025-02-07-alpha-release/index.mdx new file mode 100644 index 0000000..74d4860 --- /dev/null +++ b/docs/content/blog/2025-02-07-alpha-release/index.mdx @@ -0,0 +1,14 @@ +--- +slug: alpha-release +date: 2025-02-07 +title: Alpha release of Mk Notes +author: Myastr0 +description: Announcing the alpha release of Mk Notes - a tool to sync Markdown documentation to Notion. +--- + +I'm excited to announce the **Alpha Release** of Mk Notes! ๐ŸŽ‰ + +{/* truncate */} + +This tool coming from a personal frustration where I have to choose where to write technical documentation to be able to share it with my team. +I wanted to write my documentation in markdown and share it with my team in Notion. diff --git a/docs/content/blog/2025-06-15-public-release/index.mdx b/docs/content/blog/2025-06-15-public-release/index.mdx new file mode 100644 index 0000000..e38daf2 --- /dev/null +++ b/docs/content/blog/2025-06-15-public-release/index.mdx @@ -0,0 +1,33 @@ +--- +slug: producthunt-launch +date: 2025-06-15 +title: Mk Notes launches on Product Hunt! +author: Myastr0 +description: We're excited to announce that Mk Notes is now live on Product Hunt! +--- + +![Product Hunt Badge](https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=978916&theme=light) + +We are thrilled to announce that **Mk Notes** is now live on [Product Hunt](https://www.producthunt.com/products/mk-notes)! ๐Ÿš€ + +{/* truncate */} + +**Mk Notes** makes it effortless to migrate and sync your Markdown documentation to Notion. Whether you're an individual or a team, Mk Notes lets you keep your documentation workflowโ€”write in Markdown, version with Git, and sync everything to Notion with a single command. + +### Why Mk Notes? + +The motivation behind Mk Notes is to ease the migration from simple markdown docs to Notion. We love the simplicity and flexibility of Markdown, but also want to leverage Notion's collaboration and publishing features. Mk Notes bridges that gapโ€”no more manual copy-paste, just seamless sync. + +### What can you do with Mk Notes? + +- Sync Markdown files or entire directories to Notion +- Mirror your folder structure as Notion pages +- Preview Notion structure before syncing +- Use Notion AI on your synced docs +- Publish docs to the web via Notion Publish + +### Support us! + +If you love Mk Notes or find it useful, please support us on Product Hunt by upvoting and sharing your feedback. Your support means a lot and helps us reach more people who can benefit from Mk Notes! + +๐Ÿ‘‰ [Check out Mk Notes on Product Hunt](https://www.producthunt.com/products/mk-notes?launch=mk-notes) diff --git a/docs/content/blog/2025-11-30-v3-database-support/index.mdx b/docs/content/blog/2025-11-30-v3-database-support/index.mdx new file mode 100644 index 0000000..ea541d5 --- /dev/null +++ b/docs/content/blog/2025-11-30-v3-database-support/index.mdx @@ -0,0 +1,96 @@ +--- +slug: v3-database-support +date: 2025-11-30 +title: 'Mk Notes v3.0.0: Sync to Notion Databases' +author: Myastr0 +description: Introducing Notion database destination support โ€“ organize your markdown content with filters, views, and custom properties. +--- + +We're thrilled to announce **Mk Notes v3.0.0** โ€“ a major release that brings one of our most requested features: **Notion database synchronization**! ๐Ÿ—„๏ธ + +{/* truncate */} + +Until now, Mk Notes could sync your markdown files to Notion pages, mirroring your folder structure. With v3.0.0, you can now sync directly to **Notion databases**, unlocking powerful organizational features like filtering, sorting, and custom views. + +## Why Database Sync? + +Notion databases are incredibly powerful for managing collections of documents. By syncing your markdown content to a database, you can: + +- **Organize with views**: Table, board, gallery, or calendar โ€“ pick your favorite way to browse your docs +- **Filter & sort**: Quickly find content using Notion's native filtering system +- **Add custom metadata**: Enrich your pages with properties like status, tags, categories, or dates +- **Scale effortlessly**: Perfect for managing large documentation sets, knowledge bases, or content libraries + +## How It Works + +Getting started with database sync is simple. Just point your `--destination` flag to a Notion database URL instead of a page: + +```bash +mk-notes sync \ + --input ./docs \ + --destination https://notion.so/myworkspace/my-database-123456 \ + --notion-api-key secret_abc123... +``` + +Mk Notes automatically detects whether your destination is a page or a database and handles the sync accordingly. + +## Custom Properties from Frontmatter + +One of the most exciting features is the ability to populate database properties directly from your markdown frontmatter. Add a `properties` field to your frontmatter, and Mk Notes will map the values to your Notion database columns: + +```markdown +--- +id: getting-started-guide +title: Getting Started +properties: + - name: status + value: published + - name: category + value: Documentation + - name: priority + value: high +--- + +Your content here... +``` + +Mk Notes supports a wide range of property types including text, numbers, select, multi-select, checkboxes, URLs, emails, dates, and more! + +## Clean Sync for Databases + +We've also introduced a clean sync mode for databases. By adding a `mk-notes-id` text property to your database and using the `--clean` flag, Mk Notes can identify and update existing pages instead of creating duplicates: + +```bash + mk-notes sync \ + --input ./docs \ + --destination \ + --notion-api-key \ + --clean +``` + +Combined with the `id` frontmatter property in your markdown files, this enables a true **idempotent sync** workflow โ€“ run it as many times as you want, and your database stays clean. + +## Real-World Use Cases + +Here are some ways you can leverage database sync: + +- **Documentation portals**: Sync your docs and use a board view to track review status +- **Blog management**: Manage draft and published posts with a table view +- **Knowledge bases**: Organize articles by category with a gallery view +- **Release notes**: Track versions with dates using a timeline view + +## Getting Started + +Ready to try it out? Update to v3.0.0: + +```bash +npm install -g @mk-notes/cli@latest +``` + +Then check out our [Database Synchronization Guide](/docs/cli/guides/database-sync) for detailed setup instructions and best practices. + +--- + +This release represents a significant step forward for Mk Notes. Database support has been one of the most requested features, and we're excited to finally deliver it. As always, we'd love to hear your feedback โ€“ [open an issue](https://github.com/Myastr0/mk-notes/issues) or reach out on GitHub! + +Happy syncing! ๐Ÿš€ diff --git a/docs/content/blog/authors.yml b/docs/content/blog/authors.yml new file mode 100644 index 0000000..9176a8e --- /dev/null +++ b/docs/content/blog/authors.yml @@ -0,0 +1,9 @@ +Myastr0: + name: Myastr0 + title: Software Engineer @ Shine + url: https://github.com/Myastr0 + image_url: https://github.com/Myastr0.png + page: true + socials: + github: Myastr0 + linkedin: l%C3%A9o-dumon-67903b107 diff --git a/docs/lib/source.ts b/docs/lib/source.ts index 1069a07..6e6b58c 100644 --- a/docs/lib/source.ts +++ b/docs/lib/source.ts @@ -1,12 +1,24 @@ -import { allDocs, allMetas } from 'content-collections'; +import { + allDocs, + allDocsMetas, + allBlogs, + allBlogsMetas, + allAuthors, +} from 'content-collections'; import { loader } from 'fumadocs-core/source'; import { createMDXSource } from '@fumadocs/content-collections'; import { icons } from 'lucide-react'; import { createElement } from 'react'; +import type { AuthorWithId, AuthorsRecord } from '@/collections/authors'; + +export const blog = loader({ + baseUrl: '/blog', + source: createMDXSource(allBlogs, allBlogsMetas), +}); export const source = loader({ baseUrl: '/docs', - source: createMDXSource(allDocs, allMetas), + source: createMDXSource(allDocs, allDocsMetas), icon(icon) { if (!icon) { // You may set a default icon @@ -14,4 +26,34 @@ export const source = loader({ } if (icon in icons) return createElement(icons[icon as keyof typeof icons]); }, -}); \ No newline at end of file +}); + +/** + * Get the authors record from the authors.yml file + * Returns a record where keys are author IDs and values are author data + */ +export function getAuthorsRecord(): AuthorsRecord { + // allAuthors is an array with a single element (the parsed authors.yml) + return allAuthors[0] ?? {}; +} + +/** + * Get all authors as an array with their IDs + */ +export function getAllAuthors(): AuthorWithId[] { + const record = getAuthorsRecord(); + return Object.entries(record).map(([id, author]) => ({ + id, + ...author, + })); +} + +/** + * Get a single author by their ID + */ +export function getAuthorById(id: string): AuthorWithId | null { + const record = getAuthorsRecord(); + const author = record[id]; + if (!author) return null; + return { id, ...author }; +} \ No newline at end of file