diff --git a/README.md b/README.md index 8bf7e26..8a7a27c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,12 @@ npm run sync # dev npm run sync:prod # production ``` +## Documentation + +- **[Setup Guide](content/blog/setup-guide.md)** - Complete fork and deployment guide +- **[Configuration Guide](content/blog/fork-configuration-guide.md)** - Automated or manual fork setup +- **[Full Documentation](content/pages/docs.md)** - Reference for all features and configuration + ## Fork Configuration After forking this project, you have two options to configure your site: @@ -41,24 +47,6 @@ This updates all 11 configuration files in one step. Follow the step-by-step guide in `FORK_CONFIG.md` to update each file manually. This guide includes code snippets and an AI agent prompt for assistance. -### Files Updated - -| File | What to update | -| ----------------------------------- | --------------------------------------------------------------------------- | -| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions | -| `src/pages/Home.tsx` | Intro paragraph text, footer links | -| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings | -| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` | -| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` | -| `index.html` | Title, meta description, OG tags, JSON-LD | -| `public/llms.txt` | Site name, URL, description, topics | -| `public/robots.txt` | Sitemap URL and header comment | -| `public/openapi.yaml` | API title, server URL, site name | -| `public/.well-known/ai-plugin.json` | Site name, descriptions | -| `src/context/ThemeContext.tsx` | Default theme | - -See `FORK_CONFIG.md` for detailed configuration examples and the full JSON schema. - ## Features - Markdown-based blog posts with frontmatter @@ -169,130 +157,6 @@ excerpt: "Short text for featured cards" Your markdown content here... ``` -## Images - -### Open Graph Images - -Add an `image` field to frontmatter for social media previews: - -```yaml -image: "/images/my-header.png" -``` - -Recommended dimensions: 1200x630 pixels. Images can be local (`/images/...`) or external URLs. - -### Inline Images - -Add images in markdown content: - -```markdown -![Alt text description](/images/screenshot.png) -``` - -Place image files in `public/images/`. The alt text displays as a caption. - -### Image deployment - -Images are served as static files from your git repository, not synced to Convex. After adding images: - -1. Add image files to `public/images/` -2. Reference in frontmatter (`image: "/images/my-image.png"`) or markdown (`![Alt](/images/my-image.png)`) -3. Commit and push to git -4. Netlify rebuilds and serves the images - -The `npm run sync` command only syncs markdown text content. Images require a full deploy via git push. - -### Site Logo - -Edit `src/pages/Home.tsx` to set your site logo: - -```typescript -const siteConfig = { - logo: "/images/logo.svg", // Set to null to hide - // ... -}; -``` - -Replace `public/images/logo.svg` with your own logo file. - -## Featured Section - -Posts and pages with `featured: true` in frontmatter appear in the featured section. - -### Add to Featured - -Add these fields to any post or page frontmatter: - -```yaml -featured: true -featuredOrder: 1 -excerpt: "A short description for the card view." -image: "/images/thumbnail.png" -``` - -Then run `npm run sync` (dev) or `npm run sync:prod` (production). No redeploy needed. - -| Field | Description | -| --------------- | ----------------------------------------- | -| `featured` | Set `true` to show in featured section | -| `featuredOrder` | Order in featured section (lower = first) | -| `excerpt` | Short description for card view | -| `image` | Thumbnail for card view (displays square) | -| `authorName` | Author display name shown next to date | -| `authorImage` | Round author avatar image URL | - -### Display Modes - -The featured section supports two display modes: - -- **List view** (default): Bullet list of links -- **Card view**: Grid of cards with thumbnail, title, and excerpt - -Users can toggle between views. To change the default: - -```typescript -const siteConfig = { - featuredViewMode: "cards", // 'list' or 'cards' - showViewToggle: true, // Allow users to switch views -}; -``` - -### Thumbnail Images - -In card view, the `image` field displays as a square thumbnail above the title. Non-square images are automatically cropped to center. The list view shows links only (no images). - -Square thumbnails: 400x400px minimum (800x800px for retina). The same image can serve as both the OG image for social sharing and the featured card thumbnail. - -## Blog Page - -The site supports a dedicated blog page at `/blog`. Configure in `src/config/siteConfig.ts`: - -```typescript -blogPage: { - enabled: true, // Enable /blog route - showInNav: true, // Show in navigation - title: "Blog", // Nav link and page title - order: 0, // Nav order (lower = first) -}, -displayOnHomepage: true, // Show posts on homepage -``` - -| Option | Description | -| ------------------- | -------------------------------------- | -| `enabled` | Enable the `/blog` route | -| `showInNav` | Show Blog link in navigation | -| `title` | Text for nav link and page heading | -| `order` | Position in navigation (lower = first) | -| `displayOnHomepage` | Show post list on homepage | - -**Display options:** - -- Homepage only: `displayOnHomepage: true`, `blogPage.enabled: false` -- Blog page only: `displayOnHomepage: false`, `blogPage.enabled: true` -- Both: `displayOnHomepage: true`, `blogPage.enabled: true` - -**Navigation order:** The Blog link merges with page links and sorts by order. Pages use the `order` field in frontmatter. Set `blogPage.order: 5` to position Blog after pages with order 0-4. - ## Logo Gallery The homepage includes a scrolling logo gallery with sample logos. Configure in `siteConfig`: @@ -306,35 +170,6 @@ logoGallery: { }, ``` -### Replace with your own logos - -1. Add logo images to `public/images/logos/` (SVG recommended) -2. Update the images array with logos and links: - -```typescript -logoGallery: { - enabled: true, - images: [ - { src: "/images/logos/your-logo-1.svg", href: "https://example.com" }, - { src: "/images/logos/your-logo-2.svg", href: "https://anothersite.com" }, - ], - position: "above-footer", // or "below-featured" - speed: 30, // Seconds for one scroll cycle - title: "Trusted by", // Set to undefined to hide -}, -``` - -Each logo object supports: - -- `src`: Path to the logo image (required) -- `href`: URL to link to when clicked (optional) - -### Remove sample logos - -Delete sample files from `public/images/logos/` and replace the images array with your own logos, or set `enabled: false` to hide the gallery entirely. - -The gallery uses CSS animations for smooth infinite scrolling. Logos appear grayscale and colorize on hover. - ## GitHub Contributions Graph Display your GitHub contribution activity on the homepage. Configure in `src/config/siteConfig.ts`: @@ -349,23 +184,6 @@ gitHubContributions: { }, ``` -| Option | Description | -| -------------------- | --------------------------------------------- | -| `enabled` | `true` to show, `false` to hide | -| `username` | Your GitHub username | -| `showYearNavigation` | Show prev/next year navigation buttons | -| `linkToProfile` | Click graph to visit GitHub profile | -| `title` | Text above graph (set to `undefined` to hide) | - -The graph displays with theme-aware colors that match each site theme: - -- **Dark**: GitHub green on dark background -- **Light**: Standard GitHub green -- **Tan**: Warm brown tones -- **Cloud**: Gray-blue tones - -Uses the public `github-contributions-api.jogruber.de` API (no GitHub token required). - ## Visitor Map Display real-time visitor locations on a world map on the stats page. Uses Netlify's built-in geo detection (no third-party API needed). Privacy friendly: only stores city, country, and coordinates. No IP addresses stored. @@ -379,27 +197,6 @@ visitorMap: { }, ``` -| Option | Description | -| --------- | ------------------------------------------- | -| `enabled` | `true` to show, `false` to hide | -| `title` | Text above map (set to `undefined` to hide) | - -The map displays with theme-aware colors. Visitor dots pulse to indicate live sessions. Location data comes from Netlify's automatic geo headers at the edge. - -### Favicon - -Replace `public/favicon.svg` with your own icon. The default is a rounded square with the letter "m". Edit the SVG to change the letter or style. - -### Default Open Graph Image - -The default OG image is used when posts do not have an `image` field. Replace `public/images/og-default.svg` with your own image (1200x630 recommended). - -Update the reference in `src/pages/Post.tsx`: - -```typescript -const DEFAULT_OG_IMAGE = "/images/og-default.svg"; -``` - ## Syncing Posts Posts are synced to Convex. The sync script reads markdown files from `content/blog/` and `content/pages/`, then uploads them to your Convex database. @@ -602,25 +399,6 @@ FIRECRAWL_API_KEY=fc-your-api-key Imported posts are created as drafts (`published: false`). Review, edit, set `published: true`, then sync. -## How Blog Post Slugs Work - -Slugs are defined in the frontmatter of each markdown file: - -```markdown ---- -slug: "my-post-slug" ---- -``` - -The slug becomes the URL path: `yourdomain.com/my-post-slug` - -Rules: - -- Slugs must be unique across all posts -- Use lowercase letters, numbers, and hyphens -- The sync script reads the `slug` field from frontmatter -- Posts are queried by slug using a Convex index - ## Theme Configuration The default theme is Tan. Users can cycle through themes using the toggle: @@ -640,67 +418,22 @@ const DEFAULT_THEME: Theme = "tan"; // Change to "dark", "light", or "cloud" The blog uses a serif font (New York) by default. To switch fonts, edit `src/styles/global.css`: -```css -body { - /* Sans-serif option */ - font-family: - -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, - Cantarell, sans-serif; - - /* Serif option (default) */ - font-family: - "New York", - -apple-system-ui-serif, - ui-serif, - Georgia, - Cambria, - "Times New Roman", - Times, - serif; -} -``` - -Replace the `font-family` property with your preferred font stack. - ### Font Sizes All font sizes use CSS variables defined in `:root`. Customize sizes by editing the variables: -```css -:root { - /* Base size scale */ - --font-size-base: 16px; - --font-size-sm: 13px; - --font-size-lg: 17px; - --font-size-xl: 18px; - --font-size-2xl: 20px; - --font-size-3xl: 24px; - - /* Component-specific (examples) */ - --font-size-blog-content: 17px; - --font-size-post-title: 32px; - --font-size-nav-link: 14px; -} -``` - -Mobile responsive sizes are defined in a `@media (max-width: 768px)` block with smaller values. - ## Write Page -A public markdown writing page at `/write` (not linked in navigation). Features: - -- Three-column Cursor docs-style layout -- Content type selector (Blog Post or Page) with dynamic frontmatter templates -- Frontmatter reference panel with copy buttons for each field -- Font switcher (Serif/Sans-serif) with localStorage persistence -- Theme toggle matching the site themes (Moon, Sun, Half2Icon, Cloud) -- Word, line, and character counts -- localStorage persistence for content, content type, and font preference -- Works with Grammarly and browser spellcheck -- Warning message about refresh losing content +A public markdown writing page at `/write` (not linked in navigation). Access directly at `yourdomain.com/write`. Content is stored in localStorage only (not synced to database). Use it to draft posts, then copy the content to a markdown file in `content/blog/` or `content/pages/` and run `npm run sync`. ## Source Fork this project: [github.com/waynesutton/markdown-site](https://github.com/waynesutton/markdown-site) + +## License + +This project is licensed under the [MIT License](https://github.com/waynesutton/markdown-site/blob/main/LICENSE). + +You are free to use, modify, and distribute this project in accordance with the [MIT license terms](https://opensource.org/licenses/MIT). diff --git a/TASK.md b/TASK.md index 5c753d3..01dfc9b 100644 --- a/TASK.md +++ b/TASK.md @@ -15,7 +15,7 @@ ## Current Status -v1.21.0 deployed. Blog page view mode toggle with list and card views. +v1.24.0 deployed. Sidebar layout support for blog posts. ## Completed diff --git a/changelog.md b/changelog.md index 48b080b..0655660 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,34 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [1.24.0] - 2025-12-23 + +### Added + +- Sidebar layout support for blog posts + - Blog posts can now use `layout: "sidebar"` frontmatter field (previously only available for pages) + - Enables docs-style layout with table of contents sidebar for long-form posts + - Same features as page sidebar: automatic TOC extraction, active heading highlighting, smooth scroll navigation + - Mobile responsive: stacks to single column below 1024px + +### Changed + +- Updated `Post.tsx` to handle sidebar layout for both posts and pages +- Updated `Write.tsx` to include `layout` field in blog post frontmatter reference + +### Technical + +- Updated `convex/schema.ts`: Added optional `layout` field to posts table +- Updated `scripts/sync-posts.ts`: Parses `layout` field from post frontmatter +- Updated `convex/posts.ts`: Includes `layout` field in queries, mutations, and sync operations +- Reuses existing sidebar components and CSS (no new components needed) + +### Documentation + +- Updated `docs.md`: Added `layout` field to blog posts frontmatter table, updated sidebar layout section +- Updated `setup-guide.md`: Clarified sidebar layout works for both posts and pages +- Updated `how-to-publish.md`: Added `layout` field to frontmatter reference table + ## [1.23.0] - 2025-12-23 ### Added diff --git a/content/blog/how-to-publish.md b/content/blog/how-to-publish.md index 6fe18b0..bf09201 100644 --- a/content/blog/how-to-publish.md +++ b/content/blog/how-to-publish.md @@ -88,6 +88,7 @@ readTime: "5 min read" | `featured` | No | Set `true` to show in featured section | | `featuredOrder` | No | Order in featured section (lower first) | | `excerpt` | No | Short description for card view | +| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC | ## Write Your Content diff --git a/content/blog/setup-guide.md b/content/blog/setup-guide.md index 70b317d..8444529 100644 --- a/content/blog/setup-guide.md +++ b/content/blog/setup-guide.md @@ -8,6 +8,7 @@ tags: ["convex", "netlify", "tutorial", "deployment"] readTime: "8 min read" featured: true featuredOrder: 6 +layout: "sidebar" image: "/images/setupguide.png" authorName: "Markdown" authorImage: "/images/authors/markdown.png" @@ -915,21 +916,21 @@ order: 1 Your page content here... ``` -| Field | Required | Description | -| ------------- | -------- | -------------------------------------- | -| `title` | Yes | Page title (shown in nav) | -| `slug` | Yes | URL path (e.g., `/about`) | -| `published` | Yes | Set `true` to show | -| `order` | No | Display order (lower = first) | -| `authorName` | No | Author display name shown next to date | -| `authorImage` | No | Round author avatar image URL | +| Field | Required | Description | +| ------------- | -------- | ------------------------------------------------- | +| `title` | Yes | Page title (shown in nav) | +| `slug` | Yes | URL path (e.g., `/about`) | +| `published` | Yes | Set `true` to show | +| `order` | No | Display order (lower = first) | +| `authorName` | No | Author display name shown next to date | +| `authorImage` | No | Round author avatar image URL | | `layout` | No | Set to `"sidebar"` for docs-style layout with TOC | 3. Run `npm run sync` to sync pages Pages appear automatically in the navigation when published. -**Sidebar layout:** Add `layout: "sidebar"` to any page frontmatter to enable a docs-style layout with a table of contents sidebar. The sidebar extracts headings (H1, H2, H3) automatically and provides smooth scroll navigation. Only appears if headings exist in the page content. +**Sidebar layout:** Add `layout: "sidebar"` to any post or page frontmatter to enable a docs-style layout with a table of contents sidebar. The sidebar extracts headings (H1, H2, H3) automatically and provides smooth scroll navigation. Only appears if headings exist in the content. ### Update SEO Meta Tags diff --git a/content/pages/changelog-page.md b/content/pages/changelog-page.md index ef260aa..9909736 100644 --- a/content/pages/changelog-page.md +++ b/content/pages/changelog-page.md @@ -8,6 +8,38 @@ layout: "sidebar" All notable changes to this project. +## v1.24.0 + +Released December 23, 2025 + +**Sidebar layout for blog posts** + +- Blog posts now support `layout: "sidebar"` frontmatter field +- Previously only available for static pages, now works for posts too +- Enables docs-style layout with table of contents sidebar for long-form content +- Same features as page sidebar: automatic TOC extraction, active heading highlighting, smooth scroll navigation +- Mobile responsive: stacks to single column below 1024px + +Add `layout: "sidebar"` to any blog post frontmatter to enable the sidebar layout. The sidebar extracts headings (H1, H2, H3) automatically and only appears if headings exist in the content. + +Example: + +```yaml +--- +title: "My Tutorial" +description: "A detailed guide" +date: "2025-01-20" +slug: "my-tutorial" +published: true +tags: ["tutorial"] +layout: "sidebar" +--- +``` + +Updated files: `convex/schema.ts`, `scripts/sync-posts.ts`, `convex/posts.ts`, `src/pages/Post.tsx`, `src/pages/Write.tsx` + +Documentation updated: `docs.md`, `setup-guide.md`, `how-to-publish.md` + ## v1.23.0 Released December 23, 2025 diff --git a/content/pages/docs.md b/content/pages/docs.md index 1ca9c90..a9e73d1 100644 --- a/content/pages/docs.md +++ b/content/pages/docs.md @@ -83,21 +83,22 @@ image: "/images/og-image.png" Content here... ``` -| Field | Required | Description | -| --------------- | -------- | -------------------------------------- | -| `title` | Yes | Post title | -| `description` | Yes | SEO description | -| `date` | Yes | YYYY-MM-DD format | -| `slug` | Yes | URL path (unique) | -| `published` | Yes | `true` to show | -| `tags` | Yes | Array of strings | -| `readTime` | No | Display time estimate | -| `image` | No | OG image and featured card thumbnail | -| `excerpt` | No | Short text for card view | -| `featured` | No | `true` to show in featured section | -| `featuredOrder` | No | Order in featured (lower = first) | -| `authorName` | No | Author display name shown next to date | -| `authorImage` | No | Round author avatar image URL | +| Field | Required | Description | +| --------------- | -------- | ------------------------------------------------- | +| `title` | Yes | Post title | +| `description` | Yes | SEO description | +| `date` | Yes | YYYY-MM-DD format | +| `slug` | Yes | URL path (unique) | +| `published` | Yes | `true` to show | +| `tags` | Yes | Array of strings | +| `readTime` | No | Display time estimate | +| `image` | No | OG image and featured card thumbnail | +| `excerpt` | No | Short text for card view | +| `featured` | No | `true` to show in featured section | +| `featuredOrder` | No | Order in featured (lower = first) | +| `authorName` | No | Author display name shown next to date | +| `authorImage` | No | Round author avatar image URL | +| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC | ### Static pages @@ -130,7 +131,7 @@ Content here... ### Sidebar layout -Pages can use a docs-style layout with a table of contents sidebar. Add `layout: "sidebar"` to the page frontmatter: +Posts and pages can use a docs-style layout with a table of contents sidebar. Add `layout: "sidebar"` to the frontmatter: ```markdown --- @@ -153,13 +154,36 @@ layout: "sidebar" - Left sidebar displays table of contents extracted from H1, H2, H3 headings - Two-column layout: 220px sidebar + flexible content area -- Sidebar only appears if headings exist in the page content +- Sidebar only appears if headings exist in the content - Active heading highlighting as you scroll - Smooth scroll navigation when clicking TOC links - Mobile responsive: stacks to single column below 1024px +- Works for both blog posts and static pages The sidebar extracts headings automatically from your markdown content. No manual TOC needed. +**Example for blog post:** + +```markdown +--- +title: "My Tutorial" +description: "A detailed guide" +date: "2025-01-20" +slug: "my-tutorial" +published: true +tags: ["tutorial"] +layout: "sidebar" +--- + +# Introduction + +## Getting Started + +### Prerequisites + +## Advanced Topics +``` + ### How frontmatter works Frontmatter is the YAML metadata at the top of each markdown file between `---` markers. Here is how it flows through the system: diff --git a/convex/posts.ts b/convex/posts.ts index e38bfc3..0e03073 100644 --- a/convex/posts.ts +++ b/convex/posts.ts @@ -119,6 +119,7 @@ export const getPostBySlug = query({ featuredOrder: v.optional(v.number()), authorName: v.optional(v.string()), authorImage: v.optional(v.string()), + layout: v.optional(v.string()), }), v.null(), ), @@ -149,6 +150,7 @@ export const getPostBySlug = query({ featuredOrder: post.featuredOrder, authorName: post.authorName, authorImage: post.authorImage, + layout: post.layout, }; }, }); @@ -172,6 +174,7 @@ export const syncPosts = internalMutation({ featuredOrder: v.optional(v.number()), authorName: v.optional(v.string()), authorImage: v.optional(v.string()), + layout: v.optional(v.string()), }), ), }, @@ -212,6 +215,7 @@ export const syncPosts = internalMutation({ featuredOrder: post.featuredOrder, authorName: post.authorName, authorImage: post.authorImage, + layout: post.layout, lastSyncedAt: now, }); updated++; @@ -256,6 +260,7 @@ export const syncPostsPublic = mutation({ featuredOrder: v.optional(v.number()), authorName: v.optional(v.string()), authorImage: v.optional(v.string()), + layout: v.optional(v.string()), }), ), }, @@ -296,6 +301,7 @@ export const syncPostsPublic = mutation({ featuredOrder: post.featuredOrder, authorName: post.authorName, authorImage: post.authorImage, + layout: post.layout, lastSyncedAt: now, }); updated++; diff --git a/convex/schema.ts b/convex/schema.ts index 64eb50b..48abab6 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -18,6 +18,7 @@ export default defineSchema({ featuredOrder: v.optional(v.number()), // Order in featured section (lower = first) authorName: v.optional(v.string()), // Author display name authorImage: v.optional(v.string()), // Author avatar image URL (round) + layout: v.optional(v.string()), // Layout type: "sidebar" for docs-style layout lastSyncedAt: v.number(), }) .index("by_slug", ["slug"]) diff --git a/files.md b/files.md index c057ed8..c6d35e6 100644 --- a/files.md +++ b/files.md @@ -41,7 +41,7 @@ A brief description of each file in the codebase. | ----------- | ----------------------------------------------------------------- | | `Home.tsx` | Landing page with featured content and optional post list | | `Blog.tsx` | Dedicated blog page with post list or card grid view (configurable via siteConfig.blogPage, supports view toggle) | -| `Post.tsx` | Individual blog post view (update SITE_URL/SITE_NAME when forking) | +| `Post.tsx` | Individual blog post or page view with optional sidebar layout (update SITE_URL/SITE_NAME when forking) | | `Stats.tsx` | Real-time analytics dashboard with visitor stats and GitHub stars | | `Write.tsx` | Three-column markdown writing page with Cursor docs-style UI, frontmatter reference with copy buttons, theme toggle, font switcher (serif/sans-serif), and localStorage persistence (not linked in nav) | diff --git a/public/raw/changelog.md b/public/raw/changelog.md index d193875..0fa6084 100644 --- a/public/raw/changelog.md +++ b/public/raw/changelog.md @@ -7,6 +7,38 @@ Date: 2025-12-23 All notable changes to this project. +## v1.24.0 + +Released December 23, 2025 + +**Sidebar layout for blog posts** + +- Blog posts now support `layout: "sidebar"` frontmatter field +- Previously only available for static pages, now works for posts too +- Enables docs-style layout with table of contents sidebar for long-form content +- Same features as page sidebar: automatic TOC extraction, active heading highlighting, smooth scroll navigation +- Mobile responsive: stacks to single column below 1024px + +Add `layout: "sidebar"` to any blog post frontmatter to enable the sidebar layout. The sidebar extracts headings (H1, H2, H3) automatically and only appears if headings exist in the content. + +Example: + +```yaml +--- +title: "My Tutorial" +description: "A detailed guide" +date: "2025-01-20" +slug: "my-tutorial" +published: true +tags: ["tutorial"] +layout: "sidebar" +--- +``` + +Updated files: `convex/schema.ts`, `scripts/sync-posts.ts`, `convex/posts.ts`, `src/pages/Post.tsx`, `src/pages/Write.tsx` + +Documentation updated: `docs.md`, `setup-guide.md`, `how-to-publish.md` + ## v1.23.0 Released December 23, 2025 diff --git a/public/raw/docs.md b/public/raw/docs.md index 96d73ff..74dafaa 100644 --- a/public/raw/docs.md +++ b/public/raw/docs.md @@ -82,21 +82,22 @@ image: "/images/og-image.png" Content here... ``` -| Field | Required | Description | -| --------------- | -------- | -------------------------------------- | -| `title` | Yes | Post title | -| `description` | Yes | SEO description | -| `date` | Yes | YYYY-MM-DD format | -| `slug` | Yes | URL path (unique) | -| `published` | Yes | `true` to show | -| `tags` | Yes | Array of strings | -| `readTime` | No | Display time estimate | -| `image` | No | OG image and featured card thumbnail | -| `excerpt` | No | Short text for card view | -| `featured` | No | `true` to show in featured section | -| `featuredOrder` | No | Order in featured (lower = first) | -| `authorName` | No | Author display name shown next to date | -| `authorImage` | No | Round author avatar image URL | +| Field | Required | Description | +| --------------- | -------- | ------------------------------------------------- | +| `title` | Yes | Post title | +| `description` | Yes | SEO description | +| `date` | Yes | YYYY-MM-DD format | +| `slug` | Yes | URL path (unique) | +| `published` | Yes | `true` to show | +| `tags` | Yes | Array of strings | +| `readTime` | No | Display time estimate | +| `image` | No | OG image and featured card thumbnail | +| `excerpt` | No | Short text for card view | +| `featured` | No | `true` to show in featured section | +| `featuredOrder` | No | Order in featured (lower = first) | +| `authorName` | No | Author display name shown next to date | +| `authorImage` | No | Round author avatar image URL | +| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC | ### Static pages @@ -129,7 +130,7 @@ Content here... ### Sidebar layout -Pages can use a docs-style layout with a table of contents sidebar. Add `layout: "sidebar"` to the page frontmatter: +Posts and pages can use a docs-style layout with a table of contents sidebar. Add `layout: "sidebar"` to the frontmatter: ```markdown --- @@ -152,13 +153,36 @@ layout: "sidebar" - Left sidebar displays table of contents extracted from H1, H2, H3 headings - Two-column layout: 220px sidebar + flexible content area -- Sidebar only appears if headings exist in the page content +- Sidebar only appears if headings exist in the content - Active heading highlighting as you scroll - Smooth scroll navigation when clicking TOC links - Mobile responsive: stacks to single column below 1024px +- Works for both blog posts and static pages The sidebar extracts headings automatically from your markdown content. No manual TOC needed. +**Example for blog post:** + +```markdown +--- +title: "My Tutorial" +description: "A detailed guide" +date: "2025-01-20" +slug: "my-tutorial" +published: true +tags: ["tutorial"] +layout: "sidebar" +--- + +# Introduction + +## Getting Started + +### Prerequisites + +## Advanced Topics +``` + ### How frontmatter works Frontmatter is the YAML metadata at the top of each markdown file between `---` markers. Here is how it flows through the system: diff --git a/public/raw/how-to-publish.md b/public/raw/how-to-publish.md index 9712f13..0de9b0e 100644 --- a/public/raw/how-to-publish.md +++ b/public/raw/how-to-publish.md @@ -83,6 +83,7 @@ readTime: "5 min read" | `featured` | No | Set `true` to show in featured section | | `featuredOrder` | No | Order in featured section (lower first) | | `excerpt` | No | Short description for card view | +| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC | ## Write Your Content diff --git a/public/raw/setup-guide.md b/public/raw/setup-guide.md index 055ecc9..b0dbb67 100644 --- a/public/raw/setup-guide.md +++ b/public/raw/setup-guide.md @@ -910,21 +910,21 @@ order: 1 Your page content here... ``` -| Field | Required | Description | -| ------------- | -------- | -------------------------------------- | -| `title` | Yes | Page title (shown in nav) | -| `slug` | Yes | URL path (e.g., `/about`) | -| `published` | Yes | Set `true` to show | -| `order` | No | Display order (lower = first) | -| `authorName` | No | Author display name shown next to date | -| `authorImage` | No | Round author avatar image URL | +| Field | Required | Description | +| ------------- | -------- | ------------------------------------------------- | +| `title` | Yes | Page title (shown in nav) | +| `slug` | Yes | URL path (e.g., `/about`) | +| `published` | Yes | Set `true` to show | +| `order` | No | Display order (lower = first) | +| `authorName` | No | Author display name shown next to date | +| `authorImage` | No | Round author avatar image URL | | `layout` | No | Set to `"sidebar"` for docs-style layout with TOC | 3. Run `npm run sync` to sync pages Pages appear automatically in the navigation when published. -**Sidebar layout:** Add `layout: "sidebar"` to any page frontmatter to enable a docs-style layout with a table of contents sidebar. The sidebar extracts headings (H1, H2, H3) automatically and provides smooth scroll navigation. Only appears if headings exist in the page content. +**Sidebar layout:** Add `layout: "sidebar"` to any post or page frontmatter to enable a docs-style layout with a table of contents sidebar. The sidebar extracts headings (H1, H2, H3) automatically and provides smooth scroll navigation. Only appears if headings exist in the content. ### Update SEO Meta Tags diff --git a/scripts/sync-posts.ts b/scripts/sync-posts.ts index c75f2ed..b1c4538 100644 --- a/scripts/sync-posts.ts +++ b/scripts/sync-posts.ts @@ -36,6 +36,7 @@ interface PostFrontmatter { featuredOrder?: number; // Order in featured section (lower = first) authorName?: string; // Author display name authorImage?: string; // Author avatar image URL (round) + layout?: string; // Layout type: "sidebar" for docs-style layout } interface ParsedPost { @@ -53,6 +54,7 @@ interface ParsedPost { featuredOrder?: number; // Order in featured section (lower = first) authorName?: string; // Author display name authorImage?: string; // Author avatar image URL (round) + layout?: string; // Layout type: "sidebar" for docs-style layout } // Page frontmatter (for static pages like About, Projects, Contact) @@ -122,6 +124,7 @@ function parseMarkdownFile(filePath: string): ParsedPost | null { featuredOrder: frontmatter.featuredOrder, // Order in featured section authorName: frontmatter.authorName, // Author display name authorImage: frontmatter.authorImage, // Author avatar image URL + layout: frontmatter.layout, // Layout type: "sidebar" for docs-style layout }; } catch (error) { console.error(`Error parsing ${filePath}:`, error); diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 7a09db7..ba2a296 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -269,7 +269,13 @@ export default function Home() { > GitHub - . + . This project is licensed under the MIT{" "} + + License. + {" "}

diff --git a/src/pages/Post.tsx b/src/pages/Post.tsx index 6abdddf..db83457 100644 --- a/src/pages/Post.tsx +++ b/src/pages/Post.tsx @@ -236,10 +236,14 @@ export default function Post() { ); }; + // Extract headings for sidebar TOC (only for posts with layout: "sidebar") + const headings = post?.layout === "sidebar" ? extractHeadings(post.content) : []; + const hasSidebar = headings.length > 0; + // Render blog post with full metadata return ( -
-