mirror of
https://github.com/waynesutton/markdown-site.git
synced 2026-01-12 04:09:14 +00:00
Raw index.md now includes home.md and footer.md content
This commit is contained in:
78
.claude/plans/docs-navigation-performance.md
Normal file
78
.claude/plans/docs-navigation-performance.md
Normal file
@@ -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
|
||||||
10
TASK.md
10
TASK.md
@@ -4,10 +4,18 @@
|
|||||||
|
|
||||||
## Current Status
|
## 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
|
## 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] 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] 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
|
- [x] Updated all 4 Footer component calls to use `post.footer || footerPage?.content` pattern
|
||||||
|
|||||||
16
changelog.md
16
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/).
|
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
|
## [2.8.2] - 2026-01-03
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -12,6 +12,26 @@ docsSectionOrder: 4
|
|||||||
All notable changes to this project.
|
All notable changes to this project.
|
||||||

|

|
||||||
|
|
||||||
|
## 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
|
## v2.8.2
|
||||||
|
|
||||||
Released January 3, 2026
|
Released January 3, 2026
|
||||||
|
|||||||
2
files.md
2
files.md
@@ -222,7 +222,7 @@ Markdown files for static pages like About, Projects, Contact, Changelog.
|
|||||||
|
|
||||||
| File | Description |
|
| 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 |
|
| `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) |
|
| `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. |
|
| `configure-fork.ts` | Automated fork configuration (reads fork-config.json). ES module compatible using fileURLToPath for __dirname equivalent. |
|
||||||
|
|||||||
@@ -8,6 +8,26 @@ Date: 2026-01-04
|
|||||||
All notable changes to this project.
|
All notable changes to this project.
|
||||||

|

|
||||||
|
|
||||||
|
## 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
|
## v2.8.2
|
||||||
|
|
||||||
Released January 3, 2026
|
Released January 3, 2026
|
||||||
|
|||||||
@@ -1,6 +1,30 @@
|
|||||||
# Homepage
|
# 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.
|
||||||
|
|
||||||
|
<!-- This is a comments
|
||||||
|
Your content is instantly available to browsers, LLMs, and AI
|
||||||
|
agents. -->
|
||||||
|
|
||||||
|
## 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)
|
## Blog Posts (18)
|
||||||
|
|
||||||
@@ -57,3 +81,9 @@ This is the homepage index of all published content.
|
|||||||
**Total Content:** 18 posts, 8 pages
|
**Total Content:** 18 posts, 8 pages
|
||||||
|
|
||||||
All content is available as raw markdown files at `/raw/{slug}.md`
|
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).
|
||||||
|
|||||||
@@ -476,9 +476,21 @@ function generateHomepageIndex(posts: ParsedPost[], pages: ParsedPage[]): void {
|
|||||||
return new Date(b.date).getTime() - new Date(a.date).getTime();
|
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
|
// Build markdown content
|
||||||
let markdown = `# Homepage\n\n`;
|
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
|
// Add posts section
|
||||||
if (sortedPosts.length > 0) {
|
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 += `**Total Content:** ${sortedPosts.length} posts, ${publishedPages.length} pages\n`;
|
||||||
markdown += `\nAll content is available as raw markdown files at \`/raw/{slug}.md\`\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
|
// Write index.md file
|
||||||
const indexPath = path.join(RAW_OUTPUT_DIR, "index.md");
|
const indexPath = path.join(RAW_OUTPUT_DIR, "index.md");
|
||||||
fs.writeFileSync(indexPath, markdown);
|
fs.writeFileSync(indexPath, markdown);
|
||||||
|
|||||||
@@ -454,7 +454,7 @@ export const siteConfig: SiteConfig = {
|
|||||||
// Visitor map configuration
|
// Visitor map configuration
|
||||||
// Displays real-time visitor locations on the stats page
|
// Displays real-time visitor locations on the stats page
|
||||||
visitorMap: {
|
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
|
title: "Live Visitors", // Optional title above the map
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { useSidebar } from "../context/SidebarContext";
|
|||||||
import { format, parseISO } from "date-fns";
|
import { format, parseISO } from "date-fns";
|
||||||
import { ArrowLeft, Link as LinkIcon, Rss, Tag } from "lucide-react";
|
import { ArrowLeft, Link as LinkIcon, Rss, Tag } from "lucide-react";
|
||||||
import { XLogo, LinkedinLogo } from "@phosphor-icons/react";
|
import { XLogo, LinkedinLogo } from "@phosphor-icons/react";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import siteConfig from "../config/siteConfig";
|
import siteConfig from "../config/siteConfig";
|
||||||
|
|
||||||
// Site configuration - update these for your site (or run npm run configure)
|
// Site configuration - update these for your site (or run npm run configure)
|
||||||
@@ -43,8 +43,36 @@ export default function Post({
|
|||||||
const slug = propSlug || routeSlug;
|
const slug = propSlug || routeSlug;
|
||||||
|
|
||||||
// Check for page first, then post
|
// Check for page first, then post
|
||||||
const page = useQuery(api.pages.getPageBySlug, slug ? { slug } : "skip");
|
const pageQuery = useQuery(api.pages.getPageBySlug, slug ? { slug } : "skip");
|
||||||
const post = useQuery(api.posts.getPostBySlug, 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<DocsCache | null>(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)
|
// Fetch related posts based on current post's tags (only for blog posts, not pages)
|
||||||
const relatedPosts = useQuery(
|
const relatedPosts = useQuery(
|
||||||
|
|||||||
@@ -12385,6 +12385,16 @@ body {
|
|||||||
.docs-article {
|
.docs-article {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin: 0 auto;
|
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 */
|
/* Docs loading skeleton - prevents flash when navigating between docs pages */
|
||||||
|
|||||||
Reference in New Issue
Block a user