+ );
+}
+
+/**
+ * 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 (
+
+
) {
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!
+---
+
+
+
+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