From f377a3dde2a73a44f72e8b3086929faf815a794c Mon Sep 17 00:00:00 2001 From: Wayne Sutton Date: Sat, 3 Jan 2026 20:46:55 -0800 Subject: [PATCH] Raw index.md now includes home.md and footer.md content --- .claude/plans/docs-navigation-performance.md | 78 ++++++++++++++++++++ TASK.md | 10 ++- changelog.md | 16 ++++ content/pages/changelog-page.md | 20 +++++ files.md | 2 +- public/raw/changelog.md | 20 +++++ public/raw/index.md | 32 +++++++- scripts/sync-posts.ts | 20 ++++- src/config/siteConfig.ts | 2 +- src/pages/Post.tsx | 34 ++++++++- src/styles/global.css | 10 +++ 11 files changed, 236 insertions(+), 8 deletions(-) create mode 100644 .claude/plans/docs-navigation-performance.md diff --git a/.claude/plans/docs-navigation-performance.md b/.claude/plans/docs-navigation-performance.md new file mode 100644 index 0000000..d6e7f16 --- /dev/null +++ b/.claude/plans/docs-navigation-performance.md @@ -0,0 +1,78 @@ +# Docs Navigation Performance - Pinned for Later + +## Problem +Slight delay when switching docs groups on the live site (https://www.markdown.fast/docs) - blank middle screen appears while loading docs-content before showing the actual content. + +## Root Cause +When navigating between docs pages, `useQuery` in Post.tsx returns `undefined` for the new slug while loading, causing a loading skeleton flash. + +--- + +## Option A: React Stale-While-Revalidate (Already Implemented) + +**Status:** Implemented but can be reverted if Option B preferred + +**Files Modified:** +- `src/pages/Post.tsx` - Added client-side cache to show previous content during navigation +- `src/styles/global.css` - Added 150ms fade-in animation to `.docs-article` + +**How it works:** +- Cache last successfully loaded docs content in a React ref +- When navigating, show cached (previous) content while new content loads +- Once new data arrives, swap it in seamlessly + +**Pros:** +- Minimal change, doesn't touch Convex backend +- Fast initial page load (metadata-only sidebar) +- Low memory usage + +**Cons:** +- Brief moment showing "stale" content from previous page +- Not a Convex-native pattern (React UI workaround) + +--- + +## Option B: Prefetch All Docs Content (Future Consideration) + +**Status:** Not implemented - requires more planning + +**Files to Modify:** +- `convex/posts.ts` - Modify `getDocsPosts` to return full content +- `convex/pages.ts` - Modify `getDocsPages` to return full content +- `src/components/DocsSidebar.tsx` or parent - Store all content +- `src/pages/Post.tsx` - Use prefetched content instead of individual queries + +**How it would work:** +- Sidebar (or parent component) fetches ALL docs with FULL content upfront +- Store in React context or state +- When navigating, content is already in memory - instant render + +**Pros:** +- Truly instant navigation (no network request) +- More "Convex-native" - single query, real-time updates for all docs + +**Cons:** +- Slower initial page load (loading all content upfront) +- Higher memory usage +- Higher bandwidth cost +- May not scale well for large docs sites (50+ pages) + +--- + +## Decision Criteria + +Choose Option B if: +- Small docs site (10-20 pages) +- Users typically browse multiple docs per session +- Content is relatively short + +Stick with Option A if: +- Larger docs site (50+ pages) +- Users often visit just 1-2 pages +- Long-form content + +--- + +## Current State +- Option A is implemented and working +- Revisit Option B when docs site grows or if instant navigation becomes a priority diff --git a/TASK.md b/TASK.md index f0d67c0..af8c872 100644 --- a/TASK.md +++ b/TASK.md @@ -4,10 +4,18 @@ ## Current Status -v2.8.2 ready. Fixed footer not displaying on docs section pages. +v2.8.3 ready. Updated raw/index.md to include home.md and footer.md content. ## Completed +- [x] Update raw/index.md to include home.md and footer.md content + - [x] Updated `generateHomepageIndex` function in `scripts/sync-posts.ts` + - [x] Home intro content (slug: home-intro) now displays at top of index.md + - [x] Footer content (slug: footer) now displays at bottom of index.md + - [x] Horizontal rule separators between sections + - [x] Falls back to generic message if home-intro page not found + - [x] Mirrors actual homepage structure for AI agents reading raw markdown + - [x] Fix footer not displaying on docs section pages with showFooter: true - [x] Added footer.md content query to Post.tsx (matching Home.tsx and Blog.tsx pattern) - [x] Updated all 4 Footer component calls to use `post.footer || footerPage?.content` pattern diff --git a/changelog.md b/changelog.md index f496e1c..b52f333 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,22 @@ 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/). +## [2.8.3] - 2026-01-03 + +### Changed + +- `raw/index.md` now includes home.md and footer.md content + - Home intro content from `content/pages/home.md` (slug: home-intro) displays at top + - Footer content from `content/pages/footer.md` (slug: footer) displays at bottom + - Mirrors the actual homepage structure for AI agents reading raw markdown + - Falls back to generic message if home-intro page not found + +### Technical + +- Updated `generateHomepageIndex` function in `scripts/sync-posts.ts` +- Finds home-intro and footer pages from published pages array +- Adds horizontal rule separators between sections + ## [2.8.2] - 2026-01-03 ### Fixed diff --git a/content/pages/changelog-page.md b/content/pages/changelog-page.md index 04759d8..9470fe1 100644 --- a/content/pages/changelog-page.md +++ b/content/pages/changelog-page.md @@ -12,6 +12,26 @@ docsSectionOrder: 4 All notable changes to this project. ![](https://img.shields.io/badge/License-MIT-yellow.svg) +## v2.8.3 + +Released January 3, 2026 + +**Raw index.md now includes home.md and footer.md content** + +- `raw/index.md` now mirrors the actual homepage structure + - Home intro content from `content/pages/home.md` displays at top + - Footer content from `content/pages/footer.md` displays at bottom + - Horizontal rule separators between sections + - Falls back to generic message if home-intro page not found +- AI agents reading `/raw/index.md` now get the full homepage experience + +**Technical details:** + +- Updated `generateHomepageIndex` function in `scripts/sync-posts.ts` +- Finds home-intro and footer pages from published pages array by slug + +Updated files: `scripts/sync-posts.ts` + ## v2.8.2 Released January 3, 2026 diff --git a/files.md b/files.md index 655b8c0..6ed6a68 100644 --- a/files.md +++ b/files.md @@ -222,7 +222,7 @@ Markdown files for static pages like About, Projects, Contact, Changelog. | File | Description | | ------------------------- | ----------------------------------------------------- | -| `sync-posts.ts` | Syncs markdown files to Convex at build time (markdown sync v2) | +| `sync-posts.ts` | Syncs markdown files to Convex at build time (markdown sync v2). Generates `raw/index.md` with home.md content at top, posts/pages list, and footer.md content at bottom | | `sync-discovery-files.ts` | Updates AGENTS.md, CLAUDE.md, and llms.txt with current app data | | `import-url.ts` | Imports external URLs as markdown posts (Firecrawl) | | `configure-fork.ts` | Automated fork configuration (reads fork-config.json). ES module compatible using fileURLToPath for __dirname equivalent. | diff --git a/public/raw/changelog.md b/public/raw/changelog.md index 402441c..e6dc885 100644 --- a/public/raw/changelog.md +++ b/public/raw/changelog.md @@ -8,6 +8,26 @@ Date: 2026-01-04 All notable changes to this project. ![](https://img.shields.io/badge/License-MIT-yellow.svg) +## v2.8.3 + +Released January 3, 2026 + +**Raw index.md now includes home.md and footer.md content** + +- `raw/index.md` now mirrors the actual homepage structure + - Home intro content from `content/pages/home.md` displays at top + - Footer content from `content/pages/footer.md` displays at bottom + - Horizontal rule separators between sections + - Falls back to generic message if home-intro page not found +- AI agents reading `/raw/index.md` now get the full homepage experience + +**Technical details:** + +- Updated `generateHomepageIndex` function in `scripts/sync-posts.ts` +- Finds home-intro and footer pages from published pages array by slug + +Updated files: `scripts/sync-posts.ts` + ## v2.8.2 Released January 3, 2026 diff --git a/public/raw/index.md b/public/raw/index.md index 77d42b4..40bb664 100644 --- a/public/raw/index.md +++ b/public/raw/index.md @@ -1,6 +1,30 @@ # Homepage -This is the homepage index of all published content. +An open-source publishing framework built for AI agents and developers to ship **[docs](/docs)**, or **[blogs](/blog)** or **[websites](/)**. + +Write markdown, sync from the terminal. **[Fork it](https://github.com/waynesutton/markdown-site)**, customize it, ship it. + + + +## Features + +**AI agent integration** — API endpoints, raw markdown files, skills.md and MCP server included. + +**File-based publishing** — Write markdown locally, run `npm run sync`, content syncs everywhere. + +**URL content import** — Import urls to scrape any webpage into markdown with Firecrawl. + +**Newsletter automation** — Built-in subscription forms and admin dashboard powered by AgentMail. + +**Multiple output formats** — JSON via API endpoints, raw .md files, and RSS feeds. + +**Real-time team sync** — Multiple developers run npm run sync from different machines. + +**Sync Commands** - Sync discovery commands to update AGENTS.md, CLAUDE.md, and llms.txt + +--- ## Blog Posts (18) @@ -57,3 +81,9 @@ This is the homepage index of all published content. **Total Content:** 18 posts, 8 pages All content is available as raw markdown files at `/raw/{slug}.md` + +--- + +Built with [Convex](https://convex.dev) for real-time sync and deployed on [Netlify](https://netlify.com). Read the [project on GitHub](https://github.com/waynesutton/markdown-site) to fork and deploy your own. View [real-time site stats](/stats). + +Created by [Wayne](https://x.com/waynesutton) with Convex, Cursor, and Claude Opus 4.5. Follow on [Twitter/X](https://x.com/waynesutton), [LinkedIn](https://www.linkedin.com/in/waynesutton/), and [GitHub](https://github.com/waynesutton). This project is licensed under the MIT [License](https://github.com/waynesutton/markdown-site?tab=MIT-1-ov-file). diff --git a/scripts/sync-posts.ts b/scripts/sync-posts.ts index 9e5489b..861e412 100644 --- a/scripts/sync-posts.ts +++ b/scripts/sync-posts.ts @@ -476,9 +476,21 @@ function generateHomepageIndex(posts: ParsedPost[], pages: ParsedPage[]): void { return new Date(b.date).getTime() - new Date(a.date).getTime(); }); + // Find the home-intro page for homepage content + const homeIntroPage = publishedPages.find((p) => p.slug === "home-intro"); + // Find the footer page for footer content + const footerPage = publishedPages.find((p) => p.slug === "footer"); + // Build markdown content let markdown = `# Homepage\n\n`; - markdown += `This is the homepage index of all published content.\n\n`; + + // Include home intro content if available + if (homeIntroPage && homeIntroPage.content) { + markdown += `${homeIntroPage.content}\n\n`; + markdown += `---\n\n`; + } else { + markdown += `This is the homepage index of all published content.\n\n`; + } // Add posts section if (sortedPosts.length > 0) { @@ -528,6 +540,12 @@ function generateHomepageIndex(posts: ParsedPost[], pages: ParsedPage[]): void { markdown += `**Total Content:** ${sortedPosts.length} posts, ${publishedPages.length} pages\n`; markdown += `\nAll content is available as raw markdown files at \`/raw/{slug}.md\`\n`; + // Add footer content if available + if (footerPage && footerPage.content) { + markdown += `\n---\n\n`; + markdown += `${footerPage.content}\n`; + } + // Write index.md file const indexPath = path.join(RAW_OUTPUT_DIR, "index.md"); fs.writeFileSync(indexPath, markdown); diff --git a/src/config/siteConfig.ts b/src/config/siteConfig.ts index ba95b3c..7742302 100644 --- a/src/config/siteConfig.ts +++ b/src/config/siteConfig.ts @@ -454,7 +454,7 @@ export const siteConfig: SiteConfig = { // Visitor map configuration // Displays real-time visitor locations on the stats page visitorMap: { - enabled: true, // Set to false to hide the visitor map + enabled: false, // Set to false to hide the visitor map title: "Live Visitors", // Optional title above the map }, diff --git a/src/pages/Post.tsx b/src/pages/Post.tsx index 1d04a13..17fff80 100644 --- a/src/pages/Post.tsx +++ b/src/pages/Post.tsx @@ -15,7 +15,7 @@ import { useSidebar } from "../context/SidebarContext"; import { format, parseISO } from "date-fns"; import { ArrowLeft, Link as LinkIcon, Rss, Tag } from "lucide-react"; import { XLogo, LinkedinLogo } from "@phosphor-icons/react"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import siteConfig from "../config/siteConfig"; // Site configuration - update these for your site (or run npm run configure) @@ -43,8 +43,36 @@ export default function Post({ const slug = propSlug || routeSlug; // Check for page first, then post - const page = useQuery(api.pages.getPageBySlug, slug ? { slug } : "skip"); - const post = useQuery(api.posts.getPostBySlug, slug ? { slug } : "skip"); + const pageQuery = useQuery(api.pages.getPageBySlug, slug ? { slug } : "skip"); + const postQuery = useQuery(api.posts.getPostBySlug, slug ? { slug } : "skip"); + + // Cache last loaded docs content to prevent flash during navigation + // This implements stale-while-revalidate for seamless transitions + type DocsCache = { page: typeof pageQuery; post: typeof postQuery }; + const lastDocsContentRef = useRef(null); + + // Determine if this is a docs section page (for caching logic) + const isDocsContent = siteConfig.docsSection?.enabled && slug; + + // Check if queries are still loading + const isLoading = pageQuery === undefined || postQuery === undefined; + const isLoaded = pageQuery !== undefined && postQuery !== undefined; + + // Update cache when both queries have resolved and we have displayable content + useEffect(() => { + if (isDocsContent && isLoaded) { + const hasContent = pageQuery !== null || postQuery !== null; + if (hasContent) { + lastDocsContentRef.current = { page: pageQuery, post: postQuery }; + } + } + }, [pageQuery, postQuery, isDocsContent, isLoaded]); + + // Use cached data while loading new docs content (stale-while-revalidate) + // This prevents the blank flash when navigating between docs pages + const useCache = isDocsContent && isLoading && lastDocsContentRef.current !== null; + const page = useCache ? lastDocsContentRef.current!.page : pageQuery; + const post = useCache ? lastDocsContentRef.current!.post : postQuery; // Fetch related posts based on current post's tags (only for blog posts, not pages) const relatedPosts = useQuery( diff --git a/src/styles/global.css b/src/styles/global.css index 112c616..a9a4440 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -12385,6 +12385,16 @@ body { .docs-article { max-width: 800px; margin: 0 auto; + animation: docs-content-fade-in 150ms ease-out; +} + +@keyframes docs-content-fade-in { + from { + opacity: 0.7; + } + to { + opacity: 1; + } } /* Docs loading skeleton - prevents flash when navigating between docs pages */