mirror of
https://github.com/waynesutton/markdown-site.git
synced 2026-01-11 20:08:57 +00:00
116 KiB
116 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
[2.15.0] - 2026-01-07
Added
- Export as PDF option in CopyPageDropdown
- Browser-based print dialog for saving pages as PDF
- Clean formatted output (no markdown syntax visible)
- Title displayed as proper heading
- Metadata shown as clean line (date, read time, tags)
- Content with markdown stripped for readable document
- Uses Phosphor FilePdf icon
- Positioned at end of dropdown menu
Technical
- Added
formatForPrintfunction to strip markdown syntax from content - Added
handleExportPDFhandler with styled print window - Imports
FilePdffrom@phosphor-icons/react(already installed)
[2.14.0] - 2026-01-07
Fixed
- Core Web Vitals performance optimizations
- Fixed non-composited animations in visitor map (SVG
rattribute changed totransform: scale()) - Removed 5 duplicate
@keyframes spindefinitions from global.css - Added
will-changehints to animated elements for GPU compositing
- Fixed non-composited animations in visitor map (SVG
Added
- Critical CSS inlined in index.html for faster first paint
- Theme variables (dark/light/tan/cloud)
- Reset and base body styles
- Layout skeleton and navigation styles
- Additional resource hints in index.html
- Preconnect to convex.site for faster API calls
Technical
- Updated
src/styles/global.css:- Converted visitor-pulse animations from SVG
rtotransform: scale()(GPU-composited) - Added
transform-origin,transform-box, andwill-changeto pulse ring elements - Added
will-changeto.theme-toggle,.copy-page-menu,.search-modal-backdrop,.scroll-to-top - Removed duplicate
@keyframes spinat lines 9194, 10091, 10243, 10651, 13726
- Converted visitor-pulse animations from SVG
- Updated
src/components/VisitorMap.tsx:- Changed pulse ring
rvalues from 12/8 to base value 5 (scaling handled by CSS)
- Changed pulse ring
- Updated
index.html:- Added inline critical CSS (~2KB) for instant first contentful paint
- Added preconnect/dns-prefetch for convex.site
[2.13.0] - 2026-01-07
Added
- Enhanced diff code block rendering with @pierre/diffs library
- Diff and patch code blocks now render with Shiki-based syntax highlighting
- Unified and split (side-by-side) view modes with toggle button
- Theme-aware colors (dark/light/tan/cloud support)
- Copy button for diff content
- Automatic routing:
diff andpatch blocks use enhanced renderer
- New blog post: "How to Use Code Blocks" with syntax highlighting and diff examples
- DiffCodeBlock component (
src/components/DiffCodeBlock.tsx)
Technical
- Added
@pierre/diffspackage for enhanced diff visualization - Updated
BlogPost.tsxto route diff/patch language blocks to DiffCodeBlock - Added diff block CSS styles to
global.css - Added
vendor-diffschunk to Vite config for code splitting - Updated
files.mdwith DiffCodeBlock documentation
[2.12.0] - 2026-01-07
Fixed
- Canonical URL mismatch between raw and rendered HTML (GitHub Issue #6)
- Raw HTML was showing homepage canonical URL instead of page-specific canonical
- Added search engine bot detection to serve pre-rendered HTML with correct canonical URLs
- Search engines (Google, Bing, DuckDuckGo, etc.) now receive correct canonical tags in initial HTML
Added
- SEO Bot Configuration section in FORK_CONFIG.md for developers who fork the app
- SEO and Bot Detection section in setup-guide.md with configuration examples
SEARCH_ENGINE_BOTSarray innetlify/edge-functions/botMeta.tsfor customizable bot detectionisSearchEngineBot()helper function for search engine crawler detection- Documentation header in botMeta.ts explaining bot detection configuration
Technical
- Updated
netlify/edge-functions/botMeta.ts:- Added configuration documentation header explaining three bot categories
- Added SEARCH_ENGINE_BOTS array (googlebot, bingbot, yandexbot, duckduckbot, baiduspider, sogou, yahoo! slurp, applebot)
- Added isSearchEngineBot() function
- Updated condition to serve pre-rendered HTML to both social preview and search engine bots
[2.11.0] - 2026-01-06
Added
- Ask AI header button with RAG-based Q&A about site content
- Header button with sparkle icon (before search button, after social icons)
- Keyboard shortcuts: Cmd+J or Cmd+/ (Mac), Ctrl+J or Ctrl+/ (Windows/Linux)
- Real-time streaming responses via Convex Persistent Text Streaming
- Model selector: Claude Sonnet 4 (default) or GPT-4o
- Markdown rendering with syntax highlighting in responses
- Internal links use React Router for seamless navigation
- Source citations with links to referenced posts/pages
- Copy response button (hover to reveal) for copying AI answers
- Clear chat button to reset conversation
- AskAIConfig in siteConfig.ts for configuration
enabled: Toggle Ask AI featuredefaultModel: Default model IDmodels: Array of available models with id, name, and provider
How It Works
- User question stored in database with session ID
- Query converted to embedding using OpenAI text-embedding-ada-002
- Vector search finds top 5 relevant posts/pages
- Content sent to selected AI model with RAG system prompt
- Response streams in real-time with source citations appended
Technical
- New component:
src/components/AskAIModal.tsxwith StreamingMessage subcomponent - New file:
convex/askAI.ts- Session mutations and queries (regular runtime) - New file:
convex/askAI.node.ts- HTTP streaming action (Node.js runtime) - New table:
askAISessionswith question, streamId, model, createdAt, sources fields - New HTTP endpoint:
/ask-ai-streamfor streaming responses - Updated
convex/convex.config.tswith persistentTextStreaming component - Updated
convex/http.tswith /ask-ai-stream route and OPTIONS handler - Updated
src/components/Layout.tsxwith Ask AI button and modal - Updated
src/styles/global.csswith Ask AI modal styles
Requirements
semanticSearch.enabled: truein siteConfig (for embeddings)OPENAI_API_KEYin Convex (for embedding generation)ANTHROPIC_API_KEYin Convex (for Claude models)- Run
npm run syncto generate embeddings for content
[2.10.2] - 2026-01-06
Added
- SEO fixes for GitHub Issue #4 (7 issues resolved)
- Canonical URL: Client-side dynamic canonical link tags for posts and pages
- Single H1 per page: Markdown H1s demoted to H2 (
.blog-h1-demotedclass with H1 visual styling) - DOM order fix: Article loads before sidebar in DOM for SEO (CSS
orderproperty maintains visual layout) - X-Robots-Tag: HTTP header added via netlify.toml (
index, followfor public,noindexfor dashboard/api) - Hreflang tags: Self-referencing hreflang (en, x-default) for all pages
- og:url consistency: Uses same canonicalUrl variable as canonical link
- twitter:site meta tag: New TwitterConfig in siteConfig.ts for Twitter Cards
Technical
- New
TwitterConfiginterface insrc/config/siteConfig.tswith site and creator fields - Updated
src/pages/Post.tsxwith SEO meta tags for both posts and pages (canonical, hreflang, og:url, twitter) - Updated
src/pages/Post.tsxDOM order: article before sidebar with CSS order for visual positioning - Updated
src/components/BlogPost.tsxh1 renderer outputs h2 with.blog-h1-demotedclass - Updated
src/styles/global.csswith.blog-h1-demotedstyling and CSS order properties for sidebar - Updated
convex/http.tsgenerateMetaHtml() with hreflang and twitter:site tags - Updated
netlify.tomlwith X-Robots-Tag headers for public, dashboard, and API routes - Updated
index.htmlwith canonical, hreflang, and twitter:site placeholder tags - Updated
fork-config.json.examplewith twitter configuration fields
[2.10.1] - 2026-01-05
Added
- Optional semantic search configuration via
siteConfig.semanticSearch- New
enabledtoggle (default:falseto avoid blocking forks without API key) - When disabled, search modal shows only keyword search (no mode toggle)
- Embedding generation skipped during sync when disabled (saves API costs)
- Existing embeddings preserved in database when disabled (no data loss)
- Tab key shortcut hints hidden when semantic search is disabled
- Dashboard config generator includes semantic search toggle
- New
Technical
- New
SemanticSearchConfiginterface insrc/config/siteConfig.ts - Updated
src/components/SearchModal.tsxto conditionally render mode toggle - Updated
scripts/sync-posts.tsto check config before embedding generation - Updated
src/pages/Dashboard.tsxwith semantic search config option - Updated
FORK_CONFIG.mdwith semantic search configuration section - Updated
fork-config.json.examplewith semanticSearch option - Updated documentation:
docs-semantic-search.md,docs.md
[2.10.0] - 2026-01-05
Added
- Semantic search using vector embeddings to complement existing keyword search
- Toggle between "Keyword" and "Semantic" modes in search modal (Cmd+K)
- Keyword search: exact word matching via Convex full-text search indexes (instant, free)
- Semantic search: finds content by meaning using OpenAI text-embedding-ada-002 embeddings (~300ms, ~$0.0001/query)
- Similarity scores displayed as percentages (90%+ = very similar, 70-90% = related)
- Graceful fallback: semantic search returns empty results if OPENAI_API_KEY not configured
- Embedding generation during content sync
- Embeddings generated automatically for posts and pages during
npm run sync - Title and content combined for embedding generation
- Content truncated to 8000 characters to stay within token limits
- Embeddings generated automatically for posts and pages during
- New documentation pages
docs-search.md: Keyword search implementation with ASCII flowchartdocs-semantic-search.md: Semantic search guide with comparison table
Technical
- New file:
convex/embeddings.ts- Actions for embedding generation (Node.js runtime) - New file:
convex/embeddingsQueries.ts- Queries and mutations for embedding storage - New file:
convex/semanticSearch.ts- Vector search action with similarity scoring - New file:
convex/semanticSearchQueries.ts- Internal queries for hydrating search results - Added
embeddingfield (optional float64 array) to posts and pages tables in schema - Added
by_embeddingvector index (1536 dimensions, filterFields: ["published"]) to posts and pages - Updated
src/components/SearchModal.tsxwith mode toggle (TextAa/Brain icons) and semantic search integration - Updated
scripts/sync-posts.tsto callgenerateMissingEmbeddingsafter content sync - Added search mode toggle CSS styles (.search-mode-toggle, .search-mode-btn)
Environment Variables
OPENAI_API_KEY: Required for semantic search (set vianpx convex env set OPENAI_API_KEY sk-xxx)
[2.9.0] - 2026-01-04
Added
- Dashboard Cloud CMS features for WordPress-style content management
- Dual source architecture: dashboard-created content (
source: "dashboard") and synced content (source: "sync") coexist independently - Source badges in Posts and Pages list views (blue "Dashboard", gray "Synced")
- Direct database operations: "Save to DB" button in Write sections, "Save Changes" in editor
- Delete button for dashboard-created content with confirmation modal
- Server-side URL import via Firecrawl (direct to database, no file sync needed)
- Export to markdown functionality for backup or converting to file-based workflow
- Bulk export script:
npm run export:dbandnpm run export:db:prod
- Dual source architecture: dashboard-created content (
- Rich Text Editor in Write Post/Page sections
- Three editing modes: Markdown (default), Rich Text (Quill WYSIWYG), Preview
- Quill toolbar: headers, bold, italic, strikethrough, blockquote, code, lists, links
- Automatic HTML-to-Markdown conversion when switching modes
- Theme-aware styling
- Delete confirmation modal for posts and pages
- Warning icon and danger-styled delete button
- Shows item name and type being deleted
- Backdrop click and Escape key to cancel
Changed
- Posts and Pages list view grid layout adjusted for source badges
- Column widths: title (1fr), date (110px), status (170px), actions (110px)
- Added flex-wrap and gap for status column content
- Sync mutations now preserve dashboard-created content
- Only affects content with
source: "sync"or no source field
- Only affects content with
Technical
- New file:
convex/cms.tswith CRUD mutations for dashboard content - New file:
convex/importAction.tswith Firecrawl server-side action - New file:
scripts/export-db-posts.tsfor bulk markdown export - Added
sourcefield (optional union: "dashboard" | "sync") to posts and pages tables - Added
by_sourceindex to posts and pages tables in schema - Added ConfirmDeleteModal component with Warning icon from Phosphor
- Added source-badge CSS styles (.source-badge, .source-badge.dashboard, .source-badge.sync)
- Added delete modal styles (.dashboard-modal-delete, .dashboard-modal-icon-warning, .dashboard-modal-btn.danger)
[2.8.7] - 2026-01-04
Fixed
- Write page frontmatter sidebar toggle now works outside focus mode
- Grid layout adjusts properly when frontmatter sidebar is collapsed
- Previously only worked in focus mode due to missing CSS rules
Technical
- Added
.write-layout.frontmatter-collapsedCSS rule (grid-template-columns: 220px 1fr 56px) - Added
.write-layout.sidebar-collapsed.frontmatter-collapsedCSS rule for both sidebars collapsed - Added responsive tablet styles for frontmatter collapsed state
[2.8.6] - 2026-01-04
Changed
- Fork configuration script now updates 14 files (was 11)
- Added
src/pages/DocsPage.tsx(SITE_URL constant) - Added
netlify/edge-functions/mcp.ts(SITE_URL, SITE_NAME, MCP_SERVER_NAME) - Added
scripts/send-newsletter.ts(default SITE_URL) - Improved
public/openapi.yamlhandling for all example URLs
- Added
- Logo gallery hrefs now use relative URLs instead of hardcoded markdown.fast URLs
- Links like
/how-to-use-firecrawl,/docs,/setup-guidework on any forked site
- Links like
- Updated
fork-config.json.examplewith missing options (statsPage, mcpServer, imageLightbox)
Technical
- Updated
scripts/configure-fork.tswith new update functions:updateDocsPageTsx(),updateMcpEdgeFunction(),updateSendNewsletter() - Updated
FORK_CONFIG.mdwith complete file list and updated AI agent prompt - Updated
content/blog/fork-configuration-guide.mdwith accurate file count and output example
[2.8.5] - 2026-01-03
Added
- Search result highlighting and scroll-to-match feature
- Clicking a search result navigates to the exact match location (not just the heading)
- All matching text is highlighted with theme-appropriate colors
- Highlights pulse on arrival, then fade to subtle background after 4 seconds
- Press Escape to clear highlights
- Works across all four themes (dark, light, tan, cloud)
Technical
- Created
src/hooks/useSearchHighlighting.tshook with polling mechanism to wait for content load - Updated
src/components/SearchModal.tsxto pass search query via?q=URL parameter - Updated
src/components/BlogPost.tsxwith article ref for highlighting - Updated
src/pages/Post.tsxto defer scroll handling to highlighting hook when?q=present - Added
.search-highlightand.search-highlight-activeCSS styles with theme-specific colors
[2.8.4] - 2026-01-03
Changed
- AI service links (ChatGPT, Claude, Perplexity) now use local
/raw/{slug}.mdURLs instead of GitHub raw URLs - Simplified AI prompt from multi-line instructions to "Read this URL and summarize it:"
Technical
- Updated
src/components/CopyPageDropdown.tsxto construct URLs usingwindow.location.origin - Removed unused
siteConfigimport andgetGitHubRawUrlfunction
[2.8.3] - 2026-01-03
Changed
raw/index.mdnow 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
- Home intro content from
Technical
- Updated
generateHomepageIndexfunction inscripts/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
- Footer not displaying on docs section posts/pages even with
showFooter: truein frontmatter- Post.tsx now fetches footer.md content from Convex (matching Home.tsx and Blog.tsx pattern)
- Footer falls back to footer.md content when no per-post
footer:frontmatter is specified - Priority order: per-post frontmatter
footer:field > synced footer.md content > siteConfig.footer.defaultContent
Technical
- Added
useQuery(api.pages.getPageBySlug, { slug: "footer" })to Post.tsx - Updated all 4 Footer component calls to use
post.footer || footerPage?.contentpattern
[2.8.1] - 2026-01-03
Changed
- Centralized
defaultThemeconfiguration insiteConfig.ts- Theme is now configured via
defaultThemefield in siteConfig instead of ThemeContext.tsx - ThemeContext.tsx now imports and uses
siteConfig.defaultThemewith fallback to "tan" - Fork configuration script (
configure-fork.ts) now updates siteConfig.ts for theme changes - Backward compatible: existing sites work without changes
- Theme is now configured via
Technical
- Added
Themetype export tosrc/config/siteConfig.ts - Added
defaultTheme?: Themefield to SiteConfig interface - Updated
src/context/ThemeContext.tsxto import from siteConfig - Renamed
updateThemeContexttoupdateThemeConfiginscripts/configure-fork.ts - Updated documentation:
docs.md,setup-guide.md,FORK_CONFIG.md,fork-configuration-guide.md
[2.8.0] - 2026-01-03
Added
docsSectionGroupIconfrontmatter field for docs sidebar group icons- Display Phosphor icons next to docs sidebar group titles
- Icon appears left of the expand/collapse chevron
- 55 supported icon names (Rocket, Book, PuzzlePiece, Gear, Code, etc.)
- Icon weight: regular, size: 16px
- Only one item per group needs to specify the icon
- Graceful fallback if icon name not recognized
Technical
- Updated
convex/schema.tsto includedocsSectionGroupIconfield in posts and pages tables - Updated
convex/posts.tsandconvex/pages.tsqueries and mutations to handledocsSectionGroupIcon - Updated
scripts/sync-posts.tsto parsedocsSectionGroupIconfrom frontmatter - Updated
src/components/DocsSidebar.tsxwith Phosphor icon imports and rendering - Added CSS styles for
.docs-sidebar-group-iconinsrc/styles/global.css - Updated
.claude/skills/frontmatter.mdwith icon documentation and supported icon list
[2.7.0] - 2026-01-02
Added
docsSectionGroupOrderfrontmatter field for controlling docs sidebar group order- Groups are sorted by the minimum
docsSectionGroupOrdervalue among items in each group - Lower numbers appear first, groups without this field sort alphabetically
- Works alongside
docsSection,docsSectionGroup, anddocsSectionOrderfields
- Groups are sorted by the minimum
Technical
- Updated
convex/schema.tsto includedocsSectionGroupOrderfield in posts and pages tables - Updated
convex/posts.tsandconvex/pages.tsqueries and mutations to handledocsSectionGroupOrder - Updated
scripts/sync-posts.tsto parsedocsSectionGroupOrderfrom frontmatter - Updated
src/components/DocsSidebar.tsxto sort groups bydocsSectionGroupOrder
[2.6.0] - 2026-01-01
Added
- Multi-model AI chat support in Dashboard
- Model dropdown selector to choose between Anthropic (Claude Sonnet 4), OpenAI (GPT-4o), and Google (Gemini 2.0 Flash)
- Lazy API key validation: errors only shown when user tries to use a specific model
- Each provider has friendly setup instructions with links to get API keys
- AI Image Generation tab in Dashboard
- Generate images using Gemini models (Nano Banana and Nano Banana Pro)
- Aspect ratio selector (1:1, 16:9, 9:16, 4:3, 3:4)
- Generated images stored in Convex storage with session tracking
- Markdown-rendered error messages with setup instructions
- New
aiDashboardconfiguration in siteConfigenableImageGeneration: Toggle image generation tabdefaultTextModel: Set default AI model for chattextModels: Configure available text chat modelsimageModels: Configure available image generation models
Technical
- Updated
convex/aiChatActions.tsto support multiple AI providers- Added
callAnthropicApi,callOpenAIApi,callGeminiApihelper functions - Added
getProviderFromModelto determine provider from model ID - Added
getApiKeyForProviderfor lazy API key retrieval - Added
getNotConfiguredMessagefor provider-specific setup instructions
- Added
- Updated
src/components/AIChatView.tsxwithselectedModelprop - Updated
src/pages/Dashboard.tsxwith newAIAgentSection- Tab-based UI for Chat and Image Generation
- Model dropdowns with provider labels
- Aspect ratio selector for image generation
- Added CSS styles for AI Agent section in
src/styles/global.css.ai-agent-tabs,.ai-agent-tabfor tab navigation.ai-model-selector,.ai-model-dropdownfor model selection.ai-aspect-ratio-selectorfor aspect ratio options.ai-generated-image,.ai-image-error,.ai-image-loadingfor image display
Environment Variables
ANTHROPIC_API_KEY: Required for Claude modelsOPENAI_API_KEY: Required for GPT-4oGOOGLE_AI_API_KEY: Required for Gemini text chat and image generation
[2.5.0] - 2026-01-01
Added
- Social footer icons in header navigation
- New
showInHeaderoption insiteConfig.socialFooterto display social icons in the header - Social icons appear left of the search icon on desktop viewports
- Uses same icons and links as the social footer component
- Configurable via siteConfig, FORK_CONFIG.md, and fork-config.json
- Disabled by default (set
showInHeader: trueto enable)
- New
Technical
- Exported
platformIconsfromSocialFooter.tsxfor reuse in Layout component - Added social icon rendering in
Layout.tsxheader controls - Added
.header-social-linksand.header-social-linkCSS styles inglobal.css - Updated
SocialFooterConfiginterface withshowInHeader: boolean - Added socialFooter support to
configure-fork.tsscript - Updated documentation: FORK_CONFIG.md, fork-config.json.example, docs.md, setup-guide.md
[2.4.0] - 2026-01-01
Added
- YouTube and Twitter/X embed support with domain whitelisting
- Embed YouTube videos and Twitter/X posts directly in markdown
- Domain whitelisting for security (only trusted domains allowed)
- Whitelisted domains:
youtube.com,www.youtube.com,youtube-nocookie.com,www.youtube-nocookie.com,platform.twitter.com,platform.x.com - Auto-adds
sandbox="allow-scripts allow-same-origin allow-popups"for security - Auto-adds
loading="lazy"for performance - Non-whitelisted iframes silently blocked
- Works on both blog posts and pages
- Embeds section in markdown-with-code-examples.md with YouTube and Twitter/X examples
Technical
- Added
ALLOWED_IFRAME_DOMAINSconstant insrc/components/BlogPost.tsx - Added
iframeto sanitize schema tagNames with allowed attributes (src,width,height,allow,allowfullscreen,frameborder,title,style) - Added custom
iframecomponent handler with URL validation against whitelisted domains - Added
.embed-containerCSS styles tosrc/styles/global.cssfor responsive embeds
[2.3.0] - 2025-12-31
Added
- Author pages at
/author/:authorSlugwith post list- Click on any author name in a post to view all their posts
- View mode toggle (list/cards) with localStorage persistence
- Mobile responsive layout matching tag pages design
- Sitemap updated to include all author pages dynamically
- New Convex queries for author data
getAllAuthors: Returns all unique authors with post countsgetPostsByAuthor: Returns posts by a specific author slug
- Author name links in post headers
- Author names now clickable with hover underline effect
- Works on both blog posts and pages with authorName field
Technical
- Added
by_authorNameindex to posts table inconvex/schema.ts - New queries in
convex/posts.ts:getAllAuthors,getPostsByAuthor - New component:
src/pages/AuthorPage.tsx(based on TagPage.tsx pattern) - Added route
/author/:authorSluginsrc/App.tsx - Updated
src/pages/Post.tsxto make authorName a clickable Link - Added author link and page styles to
src/styles/global.css - Added author pages to sitemap in
convex/http.ts
[2.2.2] - 2025-12-31
Fixed
- Homepage intro loading flash
- Removed "Loading..." text from Suspense fallback in main.tsx to prevent flash on app load
- Updated Home.tsx to render nothing while homeIntro query loads (prevents bio text flash)
- Home intro content now appears without any visible loading state or fallback text
- Matches the same loading pattern used by Post.tsx for docs pages
Technical
- Updated:
src/main.tsx- Changed LoadingFallback to render empty div instead of "Loading..." text - Updated:
src/pages/Home.tsx- Changed conditional fromhomeIntro ?tohomeIntro === undefined ? null : homeIntro ?
[2.2.1] - 2025-12-31
Fixed
- ES module compatibility for configure-fork.ts
- Fixed
__dirname is not definederror when runningnpm run configure - Added
fileURLToPathimport fromurlmodule - Created ES module equivalent of
__dirnameusingimport.meta.url - Script now works correctly with
"type": "module"in package.json
- Fixed
Technical
- Updated:
scripts/configure-fork.ts- Added ES module compatible __dirname using fileURLToPath
[2.2.0] - 2025-12-30
Added
- Footer content via markdown page
- New
content/pages/footer.mdfor managing footer content via markdown sync - Footer content syncs with
npm run syncwithout redeploy needed - Edit footer text, links, and formatting through markdown instead of code
- Falls back to
siteConfig.footer.defaultContentwhen page not found - Set
showInNav: falseto hide from navigation (page remains accessible via direct URL) - Supports full markdown including links, paragraphs, and line breaks
- New
Changed
src/pages/Home.tsx: Fetches footer page by slug "footer" and passes content to Footer componentsrc/pages/Blog.tsx: Fetches footer page by slug "footer" and passes content to Footer component- Footer component now prioritizes page content over siteConfig.defaultContent
Technical
- New file:
content/pages/footer.mdwith frontmatter (slug: "footer", showInNav: false) - Uses existing
api.pages.getPageBySlugquery to fetch footer content - Pattern matches
home-intropage for consistent content management
[2.1.0] - 2025-12-30
Added
- CLAUDE.md for Claude Code project instructions
- Project context and quick start guide
- All available npm commands and workflows
- Code conventions and "do not" list
- Key file references and project structure
- Links to detailed skills documentation
- Claude skills documentation in
.claude/skills/directoryfrontmatter.md: Complete frontmatter syntax with all 25+ field options for posts and pagesconvex.md: Convex patterns specific to this app (indexes, idempotent mutations, write conflict prevention)sync.md: How sync commands work and content flow from markdown to Convex database
- Automated CLAUDE.md updates via sync-discovery-files.ts
- CLAUDE.md status comment updated during
npm run sync:discovery - Includes current site name, post count, page count, and last updated timestamp
- CLAUDE.md status comment updated during
- Unlisted posts feature
- New
unlistedfrontmatter field for blog posts - Set
unlisted: trueto hide posts from listings while keeping them accessible via direct link - Unlisted posts are excluded from: blog listings, featured sections, tag pages, search results, and related posts
- Posts remain accessible via direct URL (e.g.,
/blog/post-slug) - Useful for draft posts, private content, or posts you want to share via direct link only
- New
Technical
- New file:
CLAUDE.mdin project root - New directory:
.claude/skills/with three markdown files - Updated:
scripts/sync-discovery-files.tsto update CLAUDE.md alongside AGENTS.md and llms.txt - Updated:
convex/schema.ts- Addedunlistedoptional boolean field to posts table - Updated:
convex/posts.ts- All listing queries filter out unlisted posts (getAllPosts, getBlogFeaturedPosts, getFeaturedPosts, getAllTags, getPostsByTag, getRelatedPosts, getRecentPostsInternal) - Updated:
convex/search.ts- Search excludes unlisted posts from results - Updated:
scripts/sync-posts.ts- Addedunlistedto PostFrontmatter and ParsedPost interfaces and parsing logic - Updated:
src/pages/Write.tsx- Addedunlistedto POST_FIELDS frontmatter reference - Updated documentation:
.claude/skills/frontmatter.md,content/pages/docs.md,content/blog/setup-guide.md,files.md
[2.0.0] - 2025-12-29
Added
- Markdown sync v2 complete
- Full markdown content synchronization system
- Real-time sync from markdown files to Convex database
- Dashboard UI for content management
- Sync server for executing sync commands from UI
- Complete type safety across all Convex functions
- Security improvements and optimizations
Technical
- Optimized
recordPageViewmutation to reduce unnecessary reads - All mutations follow Convex best practices for write conflict prevention
- Type-safe Convex functions with proper validators
- Security review completed with all endpoints properly secured
[1.47.0] - 2025-12-29
Added
- Image lightbox for blog posts and pages
- Images automatically open in full-screen lightbox when clicked (if enabled)
- Lightbox includes dark backdrop, close button (X icon), and caption display
- Keyboard support: Press Escape to close lightbox
- Click outside image (backdrop) to close
- Alt text displayed as caption below image in lightbox
- Images show pointer cursor (
zoom-in) and subtle hover effect when lightbox is enabled - Configurable via
siteConfig.imageLightbox.enabled(default:true) - Dashboard config generator includes image lightbox toggle
- Responsive design: lightbox adapts to mobile screens
Technical
- New component:
ImageLightboxinsrc/components/BlogPost.tsx - New interface:
ImageLightboxConfiginsrc/config/siteConfig.ts - Updated:
src/components/BlogPost.tsx- Added lightbox state management and click handlers - Updated:
src/styles/global.css- Added lightbox styles (.image-lightbox-backdrop,.image-lightbox-img,.image-lightbox-close,.image-lightbox-caption) - Updated:
src/pages/Dashboard.tsx- Added image lightbox configuration option - Updated:
content/pages/docs.md- Added image lightbox documentation - Updated:
content/blog/setup-guide.md- Added image lightbox documentation
[1.46.0] - 2025-12-29
Added
- Dashboard sync server for executing sync commands from UI
- Local HTTP server (
scripts/sync-server.ts) runs on localhost:3001 - Execute sync commands directly from dashboard without opening terminal
- Real-time output streaming in dashboard terminal view
- Server status indicator (online/offline) in dashboard sync section
- Copy and Execute buttons for each sync command
- Optional token authentication via
SYNC_TOKENenvironment variable - Whitelisted commands only (sync, sync:prod, sync:discovery, sync:discovery:prod, sync:all, sync:all:prod)
- Health check endpoint at
/healthfor server availability - CORS enabled for localhost:5173 (dev server)
- Header sync buttons use sync server when available, fallback to command modal
- Copy icons for
npm run sync-servercommand in dashboard sync settings
- Local HTTP server (
Technical
- New file:
scripts/sync-server.ts- Local HTTP server using Node.js http module - New npm script:
sync-server- Start the local sync server - Updated:
src/pages/Dashboard.tsx- Sync server integration with health checks, execute functionality, and terminal output display - Updated:
src/styles/global.css- Styles for sync server status, terminal output, and copy buttons
[1.45.0] - 2025-12-29
Added
- Dashboard and WorkOS authentication integration
- Dashboard supports optional WorkOS authentication via
siteConfig.dashboard.requireAuth - WorkOS is optional - dashboard works with or without WorkOS configured
- When
requireAuthisfalse, dashboard is open access - When
requireAuthistrueand WorkOS is configured, dashboard requires login - Shows setup instructions if
requireAuthistruebut WorkOS is not configured - Warning banner displayed when authentication is not enabled
- Dashboard supports optional WorkOS authentication via
- Blog posts
- "How to use the Markdown sync dashboard" - Complete guide to dashboard features and usage
- "How to setup WorkOS" - Step-by-step WorkOS AuthKit setup guide
- Documentation updates
- README.md: Added dashboard and WorkOS section with links to blog posts
- docs.md: Added dashboard and WorkOS authentication sections
- setup-guide.md: Added dashboard and WorkOS authentication sections
- FORK_CONFIG.md: Added dashboard configuration information
- fork-config.json.example: Added dashboard configuration option
- files.md: Updated with dashboard and WorkOS file descriptions
Technical
- New file:
src/utils/workos.ts- WorkOS configuration utility - Updated:
src/main.tsx- Conditional WorkOS providers with lazy loading - Updated:
src/App.tsx- Callback route handling for WorkOS OAuth - Updated:
src/pages/Dashboard.tsx- Optional WorkOS authentication integration - Updated:
src/pages/Callback.tsx- OAuth callback handler for WorkOS - Updated:
convex/auth.config.ts- Convex authentication configuration for WorkOS - Updated:
src/config/siteConfig.ts- Dashboard configuration with requireAuth option
[1.44.0] - 2025-12-29
Added
- Dashboard at
/dashboardfor centralized content management and site configuration- Content management: Posts and Pages list views with filtering, search, pagination, and items per page selector (15, 25, 50, 100)
- Post and Page editor: Markdown editor with live preview, draggable/resizable frontmatter sidebar (200px-600px), independent scrolling, download markdown, copy to clipboard
- Write Post and Write Page: Full-screen writing interface with markdown editor, frontmatter reference, download markdown, localStorage persistence
- AI Agent section: Dedicated AI chat separate from Write page, uses Anthropic Claude API, per-session chat history, markdown rendering
- Newsletter management: All Newsletter Admin features integrated (subscribers, send newsletter, write email, recent sends, email stats)
- Content import: Firecrawl import UI for importing external URLs as markdown drafts
- Site configuration: Config Generator UI for all
siteConfig.tssettings, generates downloadable config file - Index HTML editor: View and edit
index.htmlcontent with meta tags, Open Graph, Twitter Cards, JSON-LD - Analytics: Real-time stats dashboard (clone of
/statspage, always accessible in dashboard) - Sync commands: UI with buttons for all sync operations (sync, sync:discovery, sync:all for dev and prod)
- Header sync buttons: Quick sync buttons in dashboard header for
npm run sync:all(dev and prod) - Dashboard search: Search bar in header to search dashboard features, page titles, and post content
- Toast notifications: Success, error, info, and warning notifications with auto-dismiss
- Command modal: Shows sync command output with copy to clipboard functionality
- Mobile responsive: Fully responsive design with mobile-optimized layout
- Theme and font: Theme toggle and font switcher with persistent preferences
Technical
- New page:
src/pages/Dashboard.tsx(4736 lines) - Dashboard uses Convex queries for real-time data
- All mutations follow Convex best practices (idempotent, indexed queries)
- Frontmatter sidebar width persisted in localStorage
- Editor content persisted in localStorage
- Independent scrolling for editor and sidebar sections
- Preview uses ReactMarkdown with remark-gfm, remark-breaks, rehype-raw, rehype-sanitize
- CSS styles added for dashboard layout, tables, editor, frontmatter sidebar, config generator, newsletter sections, stats sections, sync sections, toast notifications, command modal
- Mobile responsive breakpoints for all dashboard sections
[1.43.0] - 2025-12-29
Added
- Stats page configuration option for public/private access
- New
StatsPageConfiginterface insiteConfig.tswithenabledandshowInNavoptions - Stats page can be made private by setting
enabled: false(similar to NewsletterAdmin pattern) - When disabled, route shows "Stats page is disabled" message instead of analytics
- Navigation item automatically hidden when stats page is disabled
- Default configuration:
enabled: true(public),showInNav: true(visible in nav)
- New
Technical
- Updated:
src/config/siteConfig.ts(added StatsPageConfig interface and default config) - Updated:
src/App.tsx(conditionally renders /stats route based on config) - Updated:
src/pages/Stats.tsx(checks if enabled, shows disabled message if not) - Updated:
src/components/Layout.tsx(hides stats nav item when disabled)
[1.42.0] - 2025-12-29
Added
- Honeypot bot protection for contact and newsletter forms
- Hidden honeypot fields invisible to humans but visible to bots
- Contact form uses hidden "Website" field for bot detection
- Newsletter signup uses hidden "Fax" field for bot detection
- Bots that fill hidden fields receive fake success message (no data submitted)
- No external dependencies required (client-side only protection)
- Works with all four themes (dark, light, tan, cloud)
Technical
- Updated:
src/components/ContactForm.tsx(added honeypot state, hidden field, bot detection logic) - Updated:
src/components/NewsletterSignup.tsx(added honeypot state, hidden field, bot detection logic) - Honeypot fields use CSS positioning (position: absolute, left: -9999px) to hide from users
- Fields include aria-hidden="true" and tabIndex={-1} for accessibility
- Different field names per form (website/fax) to avoid pattern detection
[1.41.0] - 2025-12-28
Added
- Blog heading styles for home intro content
- Headings (h1-h6) in
content/pages/home.mdnow use same styling as blog posts - Classes:
blog-h1,blog-h2,blog-h3,blog-h4,blog-h5,blog-h6 - Clickable anchor links (#) appear on hover for each heading
- Automatic ID generation from heading text for anchor navigation
- Headings (h1-h6) in
- Additional blog styling for home intro
- Lists (
ul,ol,li) useblog-ul,blog-ol,blog-liclasses - Blockquotes use
blog-blockquoteclass - Horizontal rules use
blog-hrclass - Links use
blog-linkclass
- Lists (
Changed
src/pages/Home.tsx: Added custom ReactMarkdown components for headings and other elements- Home intro headings now match blog post typography and spacing
Technical
- Updated:
src/pages/Home.tsx(added generateSlug, getTextContent, HeadingAnchor helper functions, custom ReactMarkdown components for h1-h6, ul, ol, li, blockquote, hr, and updated a component to use blog-link class) - Headings in home intro content now have IDs and anchor links matching blog post behavior
[1.40.0] - 2025-12-28
Added
- Synced home intro content via markdown file
- New
content/pages/home.mdfile for homepage intro/bio text - Home intro content now syncs with
npm run synclike other pages - No redeploy needed for homepage text changes
- Full markdown support: links, headings, lists, blockquotes, horizontal rules
- External links automatically open in new tab
- Fallback to
siteConfig.bioif page not found or while loading
- New
- New
textAlignfrontmatter field for pages- Control text alignment: "left", "center", "right"
- Default: "left" (previously was always centered)
- Used by
home.mdto control home intro alignment
- New
featuredTitleconfig option in siteConfig.ts- Customize the featured section title (e.g., "Get started:", "Featured", "Popular")
- Previously hardcoded as "Get started:" in Home.tsx
Changed
src/pages/Home.tsx: Now fetches home intro from Convex instead of hardcoded JSX- Combined
home-introandhome-biointo single markdown-powered section - Home intro content defaults to left alignment (can be set to center/right via frontmatter)
Technical
- New file:
content/pages/home.md(slug:home-intro,showInNav: false,textAlign: left) - Updated:
src/pages/Home.tsx(added ReactMarkdown, useQuery for home-intro, textAlign support, featuredTitle from siteConfig) - Updated:
src/styles/global.css(added.home-intro-contentstyles) - Updated:
convex/schema.ts(addedtextAlignfield to pages table) - Updated:
convex/pages.ts(addedtextAlignto getPageBySlug and syncPagesPublic) - Updated:
scripts/sync-posts.ts(addedtextAlignto PageFrontmatter and ParsedPage) - Updated:
src/config/siteConfig.ts(addedfeaturedTitleto SiteConfig interface and config object)
[1.39.0] - 2025-12-28
Added
- HTTP-based MCP (Model Context Protocol) server deployed on Netlify Edge Functions
- Accessible 24/7 at
https://www.markdown.fast/mcp - Public access with Netlify built-in rate limiting (50 req/min per IP)
- Optional API key authentication for higher limits (1000 req/min)
- Read-only access to blog posts, pages, homepage, and search
- 7 tools:
list_posts,get_post,list_pages,get_page,get_homepage,search_content,export_all - JSON-RPC 2.0 protocol over HTTP POST
- CORS support for MCP clients
- No local machine required (unlike stdio-based MCP servers)
- Accessible 24/7 at
- Blog post: "How to Use the MCP Server" with client configuration examples
- MCP Server section in documentation (docs.md)
- MCP configuration in siteConfig.ts (
mcpServerobject)
Changed
- Updated setup-guide.md with MCP server section
- Added
@modelcontextprotocol/sdkto package.json dependencies
Technical
- New file:
netlify/edge-functions/mcp.ts(MCP server implementation) - New file:
content/blog/how-to-use-mcp-server.md - Updated:
netlify.toml(added /mcp edge function route) - Updated:
src/config/siteConfig.ts(MCPServerConfig interface and config) - Updated:
files.md(mcp.ts entry)
[1.38.0] - 2025-12-27
Added
- Newsletter CLI improvements
newsletter:sendnow callsscheduleSendPostNewslettermutation directly- New
newsletter:send:statscommand to send weekly stats summary - Both commands provide clear success/error feedback
- New mutation
scheduleSendStatsSummaryfor CLI stats sending - Blog post: "How to use AgentMail with Markdown Sync" with complete setup guide
Changed
scripts/send-newsletter.ts: Now calls mutation directly instead of printing instructionsconvex/newsletter.ts: AddedscheduleSendStatsSummarymutation
Technical
- New script:
scripts/send-newsletter-stats.ts - All AgentMail features verified to use environment variables (no hardcoded emails)
[1.37.0] - 2025-12-27
Added
- Newsletter Admin UI at
/newsletter-admin- Three-column layout similar to Write page
- View all subscribers with search and filter (all/active/unsubscribed)
- Stats showing active, total, and sent newsletter counts
- Delete subscribers directly from admin
- Send newsletter panel with two modes:
- Send Post: Select a blog post to send as newsletter
- Write Email: Compose custom email with markdown support
- Markdown-to-HTML conversion for custom emails (headers, bold, italic, links, lists)
- Copy icon on success messages to copy CLI commands
- Theme-aware success/error styling (no hardcoded green)
- Recent newsletters list showing sent history
- Configurable via
siteConfig.newsletterAdmin
- Weekly Digest automation
- Cron job runs every Sunday at 9:00 AM UTC
- Automatically sends all posts published in the last 7 days
- Uses AgentMail SDK for email delivery
- Configurable via
siteConfig.weeklyDigest
- Developer Notifications
- New subscriber alerts sent via email when someone subscribes
- Weekly stats summary sent every Monday at 9:00 AM UTC
- Uses
AGENTMAIL_CONTACT_EMAILorAGENTMAIL_INBOXas recipient - Configurable via
siteConfig.newsletterNotifications
- Admin queries and mutations for newsletter management
getAllSubscribers: Paginated subscriber list with search/filterdeleteSubscriber: Remove subscriber from databasegetNewsletterStats: Stats for admin dashboardgetPostsForNewsletter: List of posts with sent status
Changed
convex/newsletter.ts: Added admin queries (getAllSubscribers, deleteSubscriber, getNewsletterStats, getPostsForNewsletter, getStatsForSummary) and scheduleSendCustomNewsletter mutationconvex/newsletterActions.ts: Added sendWeeklyDigest, notifyNewSubscriber, sendWeeklyStatsSummary, sendCustomNewsletter actions with markdown-to-HTML conversionconvex/posts.ts: Added getRecentPostsInternal query for weekly digestconvex/crons.ts: Added weekly digest (Sunday 9am) and stats summary (Monday 9am) cron jobssrc/config/siteConfig.ts: Added NewsletterAdminConfig, NewsletterNotificationsConfig, WeeklyDigestConfig interfacessrc/App.tsx: Added /newsletter-admin routesrc/styles/global.css: Added newsletter admin styles with responsive design
Technical
- New page:
src/pages/NewsletterAdmin.tsx - Newsletter admin hidden from navigation by default (security through obscurity)
- All admin features togglable via siteConfig
- Uses Convex internal actions for email sending (Node.js runtime with AgentMail SDK)
- Cron jobs use environment variables: SITE_URL, SITE_NAME
[1.36.0] - 2025-12-27
Added
- Social footer component with customizable social links and copyright
- Displays social icons on the left (GitHub, Twitter/X, LinkedIn, and more)
- Shows copyright symbol, site name, and auto-updating year on the right
- Configurable via
siteConfig.socialFooterinsrc/config/siteConfig.ts - Supports 8 platform types: github, twitter, linkedin, instagram, youtube, tiktok, discord, website
- Uses Phosphor icons for consistent styling
- Appears below the main footer on homepage, blog posts, and pages
- Can work independently of the main footer when set via frontmatter
- Frontmatter control for social footer visibility
showSocialFooterfield for posts and pages to override siteConfig defaults- Set
showSocialFooter: falseto hide on specific posts/pages - Works like existing
showFooterfield pattern
- Social footer configuration options
enabled: Global toggle for social footershowOnHomepage,showOnPosts,showOnPages,showOnBlogPage: Per-location visibilitysocialLinks: Array of social link objects with platform and URLcopyright.siteName: Site/company name for copyright displaycopyright.showYear: Toggle for auto-updating year
Changed
src/config/siteConfig.ts: AddedSocialLink,SocialFooterConfiginterfaces andsocialFooterconfigurationconvex/schema.ts: AddedshowSocialFooteroptional boolean field to posts and pages tablesconvex/posts.tsandconvex/pages.ts: Updated queries and mutations to includeshowSocialFooterfieldscripts/sync-posts.ts: Updated to parseshowSocialFooterfrom frontmatter for both posts and pagessrc/pages/Home.tsx: Added SocialFooter component below Footersrc/pages/Post.tsx: Added SocialFooter component below Footer for both posts and pagessrc/pages/Blog.tsx: Added SocialFooter component below Footersrc/styles/global.css: Added social footer styles with flexbox layout and mobile responsive design
Technical
- New component:
src/components/SocialFooter.tsx - Uses Phosphor icons: GithubLogo, TwitterLogo, LinkedinLogo, InstagramLogo, YoutubeLogo, TiktokLogo, DiscordLogo, Globe
- Responsive design: stacks vertically on mobile (max-width: 480px)
- Year automatically updates using
new Date().getFullYear()
[1.35.0] - 2025-12-26
Added
showImageAtTopfrontmatter field for posts and pages- Set
showImageAtTop: trueto display theimagefield at the top of the post/page above the header - Image displays full-width with rounded corners above the post header
- Default behavior: if
showImageAtTopis not set orfalse, image only used for Open Graph previews and featured card thumbnails - Works for both blog posts and static pages
- Image appears above the post header when enabled
- Set
Changed
convex/schema.ts: AddedshowImageAtTopoptional boolean field to posts and pages tablesscripts/sync-posts.ts: Updated to parseshowImageAtTopfrom frontmatter for both posts and pagesconvex/posts.tsandconvex/pages.ts: Updated queries and mutations to includeshowImageAtTopfieldsrc/pages/Post.tsx: Added conditional rendering to display image at top whenshowImageAtTop: truesrc/pages/Write.tsx: AddedshowImageAtTopto POST_FIELDS and PAGE_FIELDS frontmatter referencesrc/styles/global.css: Added.post-header-imageand.post-header-image-imgstyles for header image display- Documentation updated:
content/pages/docs.md,content/blog/how-to-publish.md,content/blog/using-images-in-posts.md,files.md
Technical
- Header image displays with full-width responsive layout
- Image appears above post header with 32px bottom margin
- Rounded corners (8px border-radius) for modern appearance
- Maintains aspect ratio with
object-fit: cover
[1.34.0] - 2025-12-26
Added
- Blog page featured layout with hero post
blogFeaturedfrontmatter field for posts to mark as featured on blog page- First
blogFeaturedpost displays as hero card with landscape image, tags, date, title, excerpt, author info, and read more link - Remaining
blogFeaturedposts display in 2-column featured row with excerpts - Regular (non-featured) posts display in 3-column grid without excerpts
- New
BlogHeroCardcomponent (src/components/BlogHeroCard.tsx) for hero display - New
getBlogFeaturedPostsquery returns all published posts withblogFeatured: truesorted by date PostListcomponent updated withcolumnsprop (2 or 3) andshowExcerptsprop- Card images use 16:10 landscape aspect ratio
- Footer support on blog page via
siteConfig.footer.showOnBlogPage
Changed
convex/schema.ts: AddedblogFeaturedfield to posts table withby_blogFeaturedindexconvex/posts.ts: AddedgetBlogFeaturedPostsquery, updated sync mutations to handleblogFeaturedfieldscripts/sync-posts.ts: Updated to parseblogFeaturedfrom post frontmattersrc/pages/Blog.tsx: Refactored to display hero, featured row, and regular posts sectionssrc/components/PostList.tsx: AddedcolumnsandshowExcerptsprops for layout controlsrc/styles/global.css: Added styles for.blog-hero-section,.blog-hero-card,.blog-featured-row,.post-cards-2col
Technical
- Hero card responsive design: stacks content on mobile, side-by-side on desktop
- Featured row uses 2-column grid with excerpts visible
- Regular posts grid uses 3-column layout without excerpts for cleaner appearance
- Responsive breakpoints: 2 columns at 768px, 1 column at 480px
- Layout class names updated:
blog-page-cardsandblog-page-listfor view modes
[1.33.1] - 2025-12-26
Changed
- Article centering in sidebar layouts
- Article content now centers in the middle column when sidebars are present
- Left sidebar stays flush left, right sidebar stays flush right
- Article uses
margin-left: auto; margin-right: autowithin its1frgrid column - Works with both two-column (left sidebar only) and three-column (both sidebars) layouts
- Consistent
max-width: 800pxfor article content across all sidebar configurations
Technical
- Updated
.post-article-with-sidebarinsrc/styles/global.csswith auto margins for centering - Added
padding-right: 48pxto match left padding for balanced spacing
[1.33.0] - 2025-12-26
Added
-
AI Chat Write Agent integration with Anthropic Claude
- New
AIChatViewcomponent (src/components/AIChatView.tsx) for AI-powered chat interface - AI chat can be toggled on Write page via siteConfig.aiChat.enabledOnWritePage
- AI chat can appear in RightSidebar on posts/pages via frontmatter
aiChat: truefield - Per-session, per-context chat history stored in Convex (aiChats table)
- Supports page content as context for AI responses
- Markdown rendering for AI responses with copy functionality
- Theme-aware styling that matches the site's design system
- Uses Phosphor Icons for all UI elements
- New
-
Convex backend for AI chat
- New
convex/aiChats.tswith queries and mutations for chat history - New
convex/aiChatActions.tswith Claude API integration (requires ANTHROPIC_API_KEY environment variable) - System prompt configurable via Convex environment variables:
CLAUDE_PROMPT_STYLE,CLAUDE_PROMPT_COMMUNITY,CLAUDE_PROMPT_RULES(split prompts, joined with separators)CLAUDE_SYSTEM_PROMPT(single prompt, fallback if split prompts not set)
- Chat history limited to last 20 messages for context efficiency
- Error handling: displays "API key is not set" message when ANTHROPIC_API_KEY is missing in Convex environment variables
- New
-
New configuration options
siteConfig.aiChatinterface withenabledOnWritePageandenabledOnContentboolean flags- Both flags default to false (opt-in feature)
- New
aiChatfrontmatter field for posts and pages (requires rightSidebar: true)
Changed
- Write page now supports AI Agent mode toggle (replaces textarea when active)
- Title changes from "Blog Post" or "Page" to "Agent" when in AI chat mode
- Toggle button text changes between "Agent" and "Text Editor"
- Page scroll prevention when switching modes (no page jump)
- RightSidebar component updated to conditionally render AIChatView
- Post.tsx passes pageContent and slug to RightSidebar for AI context
- Schema updated with aiChats table and aiChat fields on posts/pages tables
- sync-posts.ts updated to handle aiChat frontmatter field
- AIChatView displays user-friendly error messages when API key is not configured
Technical
- Added
@anthropic-ai/sdkdependency for Claude API integration - Anonymous session authentication using localStorage session ID
- AI chat CSS styles in global.css with theme variable support
- New convex schema: aiChats table with indexes (by_sessionId_contextId, by_contextId)
[1.32.0] - 2025-12-25
Added
- Custom homepage configuration
- Set any page or blog post to serve as the homepage instead of the default Home component
- Configure via
siteConfig.homepagewithtype("default", "page", or "post"),slug(required for page/post), andoriginalHomeRoute(default: "/home") - Custom homepage retains all Post component features (sidebar, copy dropdown, author info, footer) but without the featured section
- Original homepage remains accessible at
/homeroute (or configuredoriginalHomeRoute) when custom homepage is set - SEO metadata uses the page/post's frontmatter when used as homepage
- Back button hidden when Post component is used as homepage
- Fork configuration support for homepage
- Added
homepagefield tofork-config.json.example - Updated
configure-fork.tsto handle homepage configuration - Documentation added to
FORK_CONFIG.mdwith usage examples
- Added
Changed
src/App.tsx: Conditionally renders Home or Post component based onsiteConfig.homepageconfigurationsrc/pages/Post.tsx: Added optionalslug,isHomepage, andhomepageTypeprops to support homepage modesrc/config/siteConfig.ts: AddedHomepageConfiginterface and default homepage configuration
Technical
- New interface:
HomepageConfiginsrc/config/siteConfig.ts - Conditional routing in
App.tsxcheckshomepage.typeandhomepage.slugto determine homepage component - Post component accepts optional props for homepage mode (hides back button when
isHomepageis true) - Original homepage route dynamically added when custom homepage is active
[1.31.1] - 2025-12-25
Added
- Image support in footer component with size control
- Footer markdown now supports images using standard markdown syntax or HTML
- Images can be sized using
width,height,style, orclassHTML attributes - Image attributes are sanitized by rehypeSanitize for security (removes dangerous CSS)
- Footer images support lazy loading and optional captions from alt text
- CSS styles added for footer images (
.site-footer-image-wrapper,.site-footer-image,.site-footer-image-caption)
Changed
- Footer sanitize schema updated to allow
width,height,style, andclassattributes on images - Footer image component handler updated to pass through size attributes from HTML
[1.31.0] - 2025-12-25
Added
- Customizable footer component with markdown support
- New
Footercomponent (src/components/Footer.tsx) that renders markdown content - Footer content can be set in frontmatter
footerfield (markdown) or usesiteConfig.footer.defaultContent - Footer can be enabled/disabled globally via
siteConfig.footer.enabled - Footer visibility controlled per-page type via
siteConfig.footer.showOnHomepage,showOnPosts,showOnPages - New
showFooterfrontmatter field for posts and pages to override siteConfig defaults - New
footerfrontmatter field for posts and pages to provide custom markdown content - Footer renders inside article at bottom for posts/pages, maintains current position on homepage
- Footer supports markdown formatting (links, paragraphs, line breaks)
- Sidebars flush to bottom when footer is enabled (using min-height)
- New
Changed
- Homepage footer section now uses the new
Footercomponent instead of hardcoded HTML - Post and page views now render footer inside article tag (before closing
</article>) - Footer component simplified to accept markdown content instead of structured link arrays
- Footer configuration in
siteConfig.tsnow usesdefaultContent(markdown string) instead ofbuiltWith/createdByobjects
[1.30.2] - 2025-12-25
Fixed
- Right sidebar no longer appears on pages/posts without explicit
rightSidebar: truein frontmatter- Changed default behavior: right sidebar is now opt-in only
- Pages like About and Contact now render without the right sidebar as expected
CopyPageDropdowncorrectly appears in nav bar when right sidebar is disabled
- Logic in
Post.tsxchanged from(page.rightSidebar ?? true)topage.rightSidebar === true
[1.30.1] - 2025-12-25
Fixed
- TypeScript error in
convex/posts.tswhererightSidebarwas used in mutation handlers but missing from args validators- Added
rightSidebar: v.optional(v.boolean())tosyncPostsargs validator - Added
rightSidebar: v.optional(v.boolean())tosyncPostsPublicargs validator
- Added
[1.30.0] - 2025-12-25
Added
- Right sidebar feature for posts and pages
- New
RightSidebarcomponent that displaysCopyPageDropdownin a right sidebar - Appears at 1135px+ viewport width when enabled
- Controlled by
siteConfig.rightSidebar.enabled(global toggle) - Per-post/page control via
rightSidebar: truefrontmatter field (opt-in only) - Three-column layout support: left sidebar (TOC), main content, right sidebar (CopyPageDropdown)
- CopyPageDropdown automatically moves from nav to right sidebar when enabled
- Responsive: right sidebar hidden below 1135px, CopyPageDropdown returns to nav
- New
- Right sidebar configuration in siteConfig
rightSidebar.enabled: Global toggle for right sidebar featurerightSidebar.minWidth: Minimum viewport width to show sidebar (default: 1135px)
rightSidebarfrontmatter field- Available for both blog posts and pages
- Optional boolean field to enable/disable right sidebar per post/page
- Defaults to true when
siteConfig.rightSidebar.enabledis true - Added to Write page frontmatter reference with copy button
Changed
Post.tsx: Updated to support three-column layout with conditional right sidebar rendering- CSS refactoring: Separated left and right sidebar styles
.post-sidebar-wrapperis now left-specific withmargin-leftand right border.post-sidebar-righthas complete independent styles withmargin-rightand left border- Both sidebars maintain consistent styling (sticky positioning, background, borders, scrollbar hiding)
src/styles/global.css: Added CSS for right sidebar positioning and 3-column grid layoutconvex/schema.ts: AddedrightSidebarfield to posts and pages tablesconvex/posts.tsandconvex/pages.ts: Updated queries and mutations to handlerightSidebarfieldscripts/sync-posts.ts: Updated parsing logic to includerightSidebarfrontmatter fieldsrc/pages/Write.tsx: AddedrightSidebarfield to POST_FIELDS and PAGE_FIELDS arrays
Technical
- Right sidebar uses sticky positioning with top offset matching left sidebar
- CSS grid automatically adjusts from 2-column to 3-column layout when right sidebar is present
- Main content padding adjusts when right sidebar is enabled
- Mobile responsive: right sidebar hidden below 1135px breakpoint
[1.29.0] - 2025-12-25
Added
- Font family configuration system
- Added
fontFamilyoption tositeConfig.tswith three options: "serif" (New York), "sans" (system fonts), "monospace" (IBM Plex Mono) - Created
FontContext.tsxfor global font state management with localStorage persistence - Font preference persists across page reloads
- SiteConfig default font is respected and overrides localStorage when siteConfig changes
- CSS variable
--font-familydynamically updates based on selected font
- Added
- Monospace font option
- Added monospace font family to FONT SWITCHER options in
global.css - Monospace uses "IBM Plex Mono", "Liberation Mono", ui-monospace, monospace
- Write page font switcher now supports all three font options (serif/sans/monospace)
- Added monospace font family to FONT SWITCHER options in
- Fork configuration support for fontFamily
- Added
fontFamilyfield tofork-config.json.example - Updated
configure-fork.tsto handle fontFamily configuration
- Added
Changed
src/styles/global.css: Updated body font-family to use CSS variable--font-familywith fallbacksrc/main.tsx: Added FontProvider wrapper around appsrc/pages/Write.tsx: Updated font switcher to cycle through serif/sans/monospace optionscontent/blog/setup-guide.md: Updated font configuration documentation with siteConfig optioncontent/pages/docs.md: Updated font configuration documentation
Technical
- New context:
src/context/FontContext.tsxwithuseFont()hook - Font detection logic compares siteConfig default with localStorage to detect changes
- CSS variable updates synchronously on mount for immediate font application
- Write page font state syncs with global font on initial load
[1.28.2] - 2025-12-25
Fixed
- Plain text code blocks now wrap text properly
- Code blocks without a language specifier were causing horizontal overflow
- Updated detection logic to distinguish inline code from block code
- Inline code: short content (< 80 chars), no newlines, no language
- Block code: longer content or has language specifier
- Text block wrapping uses
pre-wrapstyling via SyntaxHighlightercustomStyleandcodeTagProps - Long error messages and prose in code blocks now display correctly
Technical
- Updated
src/components/BlogPost.tsx: New detection logic for inline vs block code, addedtextBlockStylewith wrapping properties - Updated
src/styles/global.css: Added.code-block-textclass for CSS fallback wrapping
[1.28.1] - 2025-12-25
Fixed
- RSS feed validation errors resolved
- Standardized all URLs to
www.markdown.fastacross the application - Fixed
atom:link rel="self"attribute mismatch that caused RSS validation failures - Updated
index.htmlmeta tags (og:url, og:image, twitter:domain, twitter:url, twitter:image, JSON-LD) - Updated
convex/rss.tsandconvex/http.tsSITE_URL constants to use www.markdown.fast - Updated
public/robots.txt,public/openapi.yaml, andpublic/llms.txtwith www URLs - RSS exclusions already present in
netlify.tomlfor botMeta edge function
- Standardized all URLs to
Technical
- All URL references now consistently use
https://www.markdown.fast - RSS feed
rel="self"attribute now matches actual feed URL - Build passes successfully with URL standardization
[1.28.0] - 2025-12-25
Added
- Discovery files sync script
- New
sync-discovery-files.tsscript that updates AGENTS.md and llms.txt with current app data - Reads from siteConfig.ts and queries Convex for post/page counts and latest post date
- Preserves existing AGENTS.md instructional content while updating dynamic sections
- Regenerates llms.txt with current site information and GitHub URLs
- New
- New npm sync commands
npm run sync:discovery- Update discovery files (development)npm run sync:discovery:prod- Update discovery files (production)npm run sync:all- Sync content + discovery files together (development)npm run sync:all:prod- Sync content + discovery files together (production)
- Fork configuration support for gitHubRepo
- Added
gitHubRepoConfigto fork-config.json.example - Updated configure-fork.ts to handle gitHubRepo with backward compatibility
- Legacy githubUsername/githubRepo fields still work
- Added
Changed
fork-config.json.example: Added gitHubRepoConfig object with owner, repo, branch, contentPathscripts/configure-fork.ts: Added gitHubRepo update logic with legacy field fallbackFORK_CONFIG.md: Added gitHubRepo documentation and sync:discovery command referencefiles.md: Added sync-discovery-files.ts entry and sync commands documentation- Documentation updated across all files with new sync commands
Technical
- New script:
scripts/sync-discovery-files.ts - Uses ConvexHttpClient to query live data from Convex
- Regex-based siteConfig.ts parsing for gitHubRepo extraction
- Selective AGENTS.md updates preserve instructional content
- Error handling with graceful fallbacks for missing data
[1.27.0] - 2025-12-24
Added
- Homepage post limit configuration
- Configurable limit for number of posts shown on homepage via
siteConfig.postsDisplay.homePostsLimit - Default limit set to 10 most recent posts
- Set to
undefinedto show all posts (no limit)
- Configurable limit for number of posts shown on homepage via
- Optional "read more" link below limited post list
- Configurable via
siteConfig.postsDisplay.homePostsReadMore - Customizable link text and destination URL
- Only appears when posts are limited and there are more posts than the limit
- Default links to
/blogpage - Can be disabled by setting
enabled: false
- Configurable via
Changed
src/config/siteConfig.ts: AddedhomePostsLimitandhomePostsReadMoretoPostsDisplayConfiginterfacesrc/pages/Home.tsx: Post list now respectshomePostsLimitconfiguration and shows "read more" link when applicablesrc/styles/global.css: Added styles for.home-posts-read-moreand.home-posts-read-more-linkwith centered button styling and hover effects
Technical
- New interface:
HomePostsReadMoreConfiginsrc/config/siteConfig.ts - Post limiting logic uses
.slice()to limit array before passing toPostListcomponent - Conditional rendering ensures "read more" link only shows when needed
[1.26.0] - 2025-12-24
Added
- Tag pages at
/tags/[tag]route- Dynamic tag archive pages showing all posts with a specific tag
- View mode toggle (list/cards) with localStorage persistence
- Mobile responsive layout matching existing blog page design
- Sitemap updated to include all tag pages dynamically
- Related posts component for blog post footers
- Shows up to 3 related posts based on shared tags
- Sorted by relevance (number of shared tags) then by date
- Only displays on blog posts (not static pages)
- Improved tag links in post footers
- Tags now link to
/tags/[tag]archive pages - Visual styling consistent with existing theme
- Tags now link to
- Open in AI service links re-enabled in CopyPageDropdown
- Uses GitHub raw URLs instead of Netlify paths (bypasses edge function issues)
- ChatGPT, Claude, and Perplexity links with universal prompt
- "Requires git push" hint for users (npm sync alone doesn't update GitHub)
- Visual divider separating AI options from other menu items
Changed
src/config/siteConfig.ts: AddedgitHubRepoconfiguration for constructing raw GitHub URLsconvex/schema.ts: Addedby_tagsindex to posts table for efficient tag queriesconvex/posts.ts: AddedgetAllTags,getPostsByTag, andgetRelatedPostsqueriesconvex/http.ts: Sitemap now includes dynamically generated tag pages- Updated
content/pages/docs.mdandcontent/blog/setup-guide.mdwith git push requirement for AI links
Technical
- New component:
src/pages/TagPage.tsx - New route:
/tags/:taginsrc/App.tsx - CSS styles for tag pages, related posts, and post tag links in
src/styles/global.css - Mobile responsive breakpoints for all new components
[1.25.4] - 2025-12-24
Fixed
- Sidebar border width now consistent across all pages
- Fixed border appearing thicker on changelog page when sidebar scrolls
- Changed from
border-righttobox-shadow: insetfor consistent 1px width regardless of scrollbar presence - Border now renders correctly on both docs and changelog pages
Changed
-
Sidebar scrollbar hidden while maintaining scroll functionality
- Scrollbar no longer visible but scrolling still works
- Applied cross-browser scrollbar hiding (Chrome/Safari/Edge, Firefox, IE)
- Cleaner sidebar appearance matching Cursor docs style
-
Sidebar styling improvements
- Added top border using CSS variable (
var(--border-sidebar)) for theme consistency - Added border-radius for rounded corners
- Updated CSS comments to document border implementation approach
- Added top border using CSS variable (
Technical
src/styles/global.css: Changed.post-sidebar-wrapperborder fromborder-righttobox-shadow: inset -1px 0 0src/styles/global.css: Added scrollbar hiding with-ms-overflow-style: none,scrollbar-width: none, and::-webkit-scrollbarsrc/styles/global.css: Addedborder-top: 1px solid var(--border-sidebar)andborder-radius: 8pxto sidebar wrappersrc/styles/global.css: Updated CSS comments to explain border implementation choices
[1.25.3] - 2025-12-24
Fixed
- Mobile menu now appears correctly at all breakpoints where sidebar is hidden
- Changed mobile hamburger menu breakpoint from
max-width: 768pxtomax-width: 1024px - Changed desktop hide breakpoint from
min-width: 769pxtomin-width: 1025px - Mobile menu now shows whenever sidebar is hidden (matches sidebar breakpoint)
- Fixed gap where users had no navigation between 769px and 1024px viewport widths
- Changed mobile hamburger menu breakpoint from
Technical
src/styles/global.css: Updated mobile nav controls media query tomax-width: 1024pxsrc/styles/global.css: Updated desktop hide media query tomin-width: 1025pxsrc/styles/global.css: Updated tablet drawer width breakpoint tomax-width: 1024px
[1.25.2] - 2025-12-24
Changed
- Disabled AI service links (ChatGPT, Claude, Perplexity) in CopyPageDropdown
- Direct links to AI services removed due to Netlify edge function interception issues
- AI crawlers cannot reliably fetch
/raw/*.mdfiles despite multiple configuration attempts - Users can still copy markdown and paste directly into AI tools manually
- "Copy page", "View as Markdown", and "Download as SKILL.md" options remain available
Removed
- Netlify Function at
/api/raw/:slugendpoint- Removed due to build failures and dependency conflicts
- Static
/raw/*.mdfiles still work in browsers but not for AI crawler fetch tools
Technical
src/components/CopyPageDropdown.tsx: Commented out AI service buttons, kept manual copy/view/download optionsnetlify.toml: Removed/api/raw/*redirect rulenetlify/functions/raw.js: Deleted Netlify Function filecontent/blog/netlify-edge-excludedpath-ai-crawlers.md: Updated with detailed log of all attempted solutions and timestamps
[1.25.1] - 2025-12-24
Changed
- Logo moved to top navigation header on all pages
- Logo now appears in the header bar (top-left) on blog posts, pages, and blog page
- Logo is separate from back button and navigation links
- Reads from
siteConfig.innerPageLogoandsiteConfig.logoconfiguration - Works consistently across all pages (with and without sidebar)
- Mobile responsive: logo positioned on left in header
Technical
src/components/Layout.tsx: Added logo to top navigation header, reads from siteConfigsrc/pages/Post.tsx: Removed logo from post navigation (was next to back button)src/pages/Blog.tsx: Removed logo from blog navigationsrc/styles/global.css: Added.top-nav-logo-linkand.top-nav-logostyles, updated.top-navlayout to span left-to-right, removed old.inner-page-logostyles
[1.25.0] - 2025-12-24
Changed
- Sidebar styling updated to match Cursor docs style
- Sidebar now has alternate background color (
--sidebar-alt-bg) for visual separation - Vertical border line on right side of sidebar
- Theme-aware colors for all four themes (dark, light, tan, cloud)
- Sidebar width increased to 240px for better readability
- Mobile responsive: sidebar hidden on screens below 1024px
- Sidebar now has alternate background color (
Technical
src/styles/global.css: Added--sidebar-alt-bgCSS variables for each theme, updated.post-sidebar-wrapperwith alternate background and right border, adjusted grid layout for wider sidebar
[1.24.9] - 2025-12-24
Added
- Safety-net raw markdown endpoint for AI tools (
/api/raw/:slug)- New Netlify Function at
netlify/functions/raw.ts - Returns
text/plainwith minimal headers for reliable AI ingestion - Reads from
dist/raw/(production) orpublic/raw/(dev/preview) - Handles 400 (missing slug), 404 (not found), and 200 (success) responses
- No Link, X-Robots-Tag, or SEO headers that cause AI fetch failures
- New Netlify Function at
Changed
- AI service links (ChatGPT, Claude, Perplexity) now use
/api/raw/:sluginstead of/raw/:slug.md- Netlify Function endpoint more reliable for AI crawler fetch
- "View as Markdown" menu item still uses
/raw/:slug.mdfor browser viewing
Technical
netlify/functions/raw.ts: New Netlify Function to serve raw markdownnetlify.toml: Added redirect from/api/raw/*to the functionsrc/components/CopyPageDropdown.tsx: AI services use/api/raw/:slugendpointpackage.json: Added@netlify/functionsdev dependency
[1.24.8] - 2025-12-23
Fixed
- Raw markdown URL construction now uses
window.location.origininstead ofprops.url- Prevents incorrect URLs when
props.urlpoints to canonical/deploy preview domains - Uses
new URL()constructor for proper absolute URL building - Ensures raw URLs always match the current page origin
- Applied to both AI service links and "View as Markdown" option
- Prevents incorrect URLs when
Technical
src/components/CopyPageDropdown.tsx: Changed raw URL construction fromnew URL(props.url).origintowindow.location.originwithnew URL()constructor
[1.24.7] - 2025-12-23
Fixed
- Removed
Linkheader from/raw/*endpoints to fix AI crawler fetch failures- Netlify merges headers, so global
Linkheader was being applied to/raw/*despite specific block - Moved
Linkheader from global/*scope to/index.htmlonly - Removed
X-Robots-Tag = "noindex"from/raw/*to allow AI crawlers to index raw content - Raw markdown files now have clean headers optimized for AI consumption
- Netlify merges headers, so global
Technical
netlify.toml: RemovedLinkfrom global headers, added specific/index.htmlblock, removednoindexfrom/raw/*
[1.24.6] - 2025-12-23
Added
- Homepage raw markdown index file (
/raw/index.md)- Automatically generated during
npm run syncandnpm run sync:prod - Lists all published posts sorted by date (newest first)
- Lists all published pages sorted by order or alphabetically
- Includes post metadata: date, reading time, tags, description
- Provides direct links to all raw markdown files
- AI crawlers can now access homepage content as markdown
- Automatically generated during
Technical
- Updated
scripts/sync-posts.ts: AddedgenerateHomepageIndex()function to createindex.mdinpublic/raw/
[1.24.5] - 2025-12-23
Fixed
- AI crawlers (ChatGPT, Perplexity) can now fetch raw markdown from
/raw/*.mdURLs- Added explicit
/raw/*redirect passthrough innetlify.tomlbefore SPA fallback - Expanded
excludedPatharray to cover all static file patterns - Refactored
botMeta.tsedge function:- Added hard bypass at top of handler for static file paths
- Separated social preview bots from AI crawlers
- AI crawlers (GPTBot, ClaudeBot, PerplexityBot, etc.) now bypass OG interception
- Only social preview bots (Facebook, Twitter, LinkedIn, etc.) receive OG metadata HTML
- Added explicit
Technical
netlify.toml: Addedforce = trueto/raw/*redirect, expandedexcludedPatharraybotMeta.ts: Complete refactor withSOCIAL_PREVIEW_BOTSandAI_CRAWLERSlists, hard path bypass
[1.24.4] - 2025-12-23
Added
showInNavfield for pages to control navigation visibility- Pages can be published and accessible but hidden from navigation menu
- Set
showInNav: falsein page frontmatter to hide from nav - Defaults to
truefor backwards compatibility (all existing pages show in nav) - Pages with
showInNav: falseremain:- Published and accessible via direct URL
- Searchable via search indexes
- Available via API endpoints
- Just hidden from the navigation menu
- Matches the pattern used for
blogPage.showInNavin siteConfig.ts
- Hardcoded navigation items configuration for React routes
- Add React route pages (like
/stats,/write) to navigation viasiteConfig.hardcodedNavItems - Configure navigation order, title, and visibility per route
- Set
showInNav: falseto hide from nav while keeping route accessible - Navigation combines Blog link, hardcoded nav items, and markdown pages
- All nav items sorted by
orderfield (lower = first) - Example: Configure
/statsand/writeroutes insiteConfig.ts
- Add React route pages (like
Technical
- Updated
convex/schema.ts: Added optionalshowInNavfield to pages table - Updated
convex/pages.ts:getAllPagesquery filters out pages whereshowInNav === false - Updated
scripts/sync-posts.ts: ParsesshowInNavfrom page frontmatter - Updated
src/pages/Write.tsx: AddedshowInNavfield to page template and PAGE_FIELDS reference - Updated
src/config/siteConfig.ts: AddedHardcodedNavIteminterface andhardcodedNavItemsconfig array - Updated
src/components/Layout.tsx: ReadshardcodedNavItemsfrom siteConfig and combines with Blog link and pages
Documentation
- Updated
content/pages/docs.md: AddedshowInNavto static pages frontmatter table - Updated
content/blog/setup-guide.md: AddedshowInNavto static pages frontmatter table
[1.24.3] - 2025-12-23
Added
- Inner page logo configuration
- Logo displays in header on blog page, individual posts, and static pages
- Desktop: logo positioned on the left (before back button)
- Mobile: logo positioned on the right (smaller size for compact header)
- Configurable via
siteConfig.innerPageLogo.enabledandsiteConfig.innerPageLogo.size - Does not affect homepage logo (controlled separately)
- Logo links to homepage when clicked
Technical
- Updated
src/config/siteConfig.ts: AddedInnerPageLogoConfiginterface andinnerPageLogoconfig option - Updated
src/pages/Blog.tsx: Added logo to header navigation - Updated
src/pages/Post.tsx: Added logo to header navigation for both posts and pages - Updated
src/styles/global.css: Added CSS for desktop (left) and mobile (right) logo positioning with responsive sizing
[1.24.2] - 2025-12-23
Changed
- Mobile menu redesigned for better sidebar integration
- Mobile navigation controls moved to left side (hamburger, search, theme toggle)
- Hamburger menu order: hamburger first, then search, then theme toggle
- Sidebar table of contents now appears in mobile menu when page has sidebar layout
- Desktop sidebar hidden on mobile (max-width: 768px) since it's accessible via hamburger menu
- Back button and CopyPageDropdown remain visible above main content on mobile
- Mobile menu typography standardized
- All mobile menu elements now use CSS variables for font sizes
- Font-family standardized using
inheritto match body font from global.css - Mobile menu TOC links use consistent font sizing with desktop sidebar
- Added CSS variables:
--font-size-mobile-toc-titleand--font-size-mobile-toc-link
Technical
- Updated
src/components/Layout.tsx: Reordered mobile nav controls, added sidebar context integration - Updated
src/components/MobileMenu.tsx: Added sidebar headings rendering in mobile menu - Updated
src/pages/Post.tsx: Provides sidebar headings to context for mobile menu - Updated
src/context/SidebarContext.tsx: New context for sharing sidebar data between components - Updated
src/styles/global.css: Mobile menu positioning, sidebar hiding on mobile, font standardization
[1.24.1] - 2025-12-23
Fixed
- Sidebar navigation anchor links now work correctly when sections are collapsed or expanded
- Fixed navigation scroll calculation to use proper header offset (80px)
- Expand ancestors before scrolling to ensure target is visible
- Use requestAnimationFrame to ensure DOM updates complete before scrolling
- Removed auto-expand from scroll handler to prevent interfering with manual collapse/expand
- Collapse button now properly isolated from link clicks with event handlers
Changed
- Updated
extractHeadings.tsto filter out headings inside code blocks- Prevents sidebar from showing example headings from markdown code examples
- Removes fenced code blocks (```) and indented code blocks before extracting headings
- Ensures sidebar only shows actual page headings, not code examples
Technical
- Updated
src/components/PageSidebar.tsx: Improved navigation logic and collapse button event handling - Updated
src/utils/extractHeadings.ts: AddedremoveCodeBlocksfunction to filter code before heading extraction
[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
- Blog posts can now use
Changed
- Updated
Post.tsxto handle sidebar layout for both posts and pages - Updated
Write.tsxto includelayoutfield in blog post frontmatter reference
Technical
- Updated
convex/schema.ts: Added optionallayoutfield to posts table - Updated
scripts/sync-posts.ts: Parseslayoutfield from post frontmatter - Updated
convex/posts.ts: Includeslayoutfield in queries, mutations, and sync operations - Reuses existing sidebar components and CSS (no new components needed)
Documentation
- Updated
docs.md: Addedlayoutfield 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: Addedlayoutfield to frontmatter reference table
[1.23.0] - 2025-12-23
Added
- Collapsible sections in markdown using HTML
<details>and<summary>tags- Create expandable/collapsible content in blog posts and pages
- Use
<details open>attribute for sections that start expanded - Supports nested collapsible sections
- Theme-aware styling for all four themes (dark, light, tan, cloud)
- Works with all markdown content inside: lists, code blocks, bold, italic, etc.
Technical
- Added
rehype-rawpackage to allow raw HTML pass-through in react-markdown - Added
rehype-sanitizepackage to strip dangerous tags while allowing safe ones - Custom sanitize schema allows
details,summarytags and theopenattribute - Updated
src/components/BlogPost.tsxwith rehype plugins - CSS styles for collapsible sections in
src/styles/global.css
Documentation
- Updated
markdown-with-code-examples.mdwith collapsible section examples - Updated
docs.mdwith collapsible sections documentation - Updated
files.mdwith BlogPost.tsx description change
[1.22.0] - 2025-12-21
Added
- Sidebar layout for pages with table of contents
- Add
layout: "sidebar"to page frontmatter to enable docs-style layout - Left sidebar displays table of contents extracted from H1, H2, H3 headings
- Two-column grid layout: 220px sidebar + flexible content area
- Sidebar only appears if headings exist in the page content
- Active heading highlighting on scroll
- Smooth scroll navigation to sections
- CopyPageDropdown remains in top navigation for sidebar pages
- Mobile responsive: stacks to single column below 1024px
- Add
Technical
- New utility:
src/utils/extractHeadings.tsfor parsing markdown headings - New component:
src/components/PageSidebar.tsxfor TOC navigation - Updated
convex/schema.ts: Added optionallayoutfield to pages table - Updated
scripts/sync-posts.ts: Parseslayoutfield from page frontmatter - Updated
convex/pages.ts: Includeslayoutfield in queries and mutations - Updated
src/pages/Post.tsx: Conditionally renders sidebar layout - CSS grid layout with sticky sidebar positioning
- Full-width container breaks out of main-content constraints
[1.21.0] - 2025-12-21
Added
- Blog page view mode toggle (list and card views)
- Toggle button in blog header to switch between list and card views
- Card view displays posts in a 3-column grid with thumbnails, titles, excerpts, and metadata
- List view shows year-grouped posts (existing behavior)
- View preference saved to localStorage
- Default view mode configurable via
siteConfig.blogPage.viewMode - Toggle visibility controlled by
siteConfig.blogPage.showViewToggle
- Post cards component
- Displays post thumbnails, titles, excerpts, read time, and dates
- Responsive grid: 3 columns (desktop), 2 columns (tablet), 1 column (mobile)
- Theme-aware styling for all four themes (dark, light, tan, cloud)
- Square thumbnails with hover zoom effect
- Cards without images display with adjusted padding
Changed
- Updated
PostListcomponent to support both list and card view modes - Updated
Blog.tsxto include view toggle button and state management - Updated
siteConfig.tswithblogPage.viewModeandblogPage.showViewToggleoptions
Technical
- New CSS classes:
.post-cards,.post-card,.post-card-image-wrapper,.post-card-content,.post-card-meta - Reuses featured card styling patterns for consistency
- Mobile responsive with adjusted grid columns and image aspect ratios
[1.20.3] - 2025-12-21
Fixed
- Raw markdown files now accessible to AI crawlers (ChatGPT, Perplexity)
- Added
/raw/path bypass in botMeta edge function - AI services were receiving HTML instead of markdown content
- Added
Added
- SEO and AEO improvements
- Sitemap now includes static pages (about, docs, contact, etc.)
- Security headers: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy
- Link header pointing to llms.txt for AI discovery
- Raw markdown files served with proper Content-Type and CORS headers
- Preconnect hints for Convex backend (faster API calls)
Changed
- Fixed URL consistency: openapi.yaml and robots.txt now use www.markdown.fast
[1.20.2] - 2025-12-21
Fixed
- Write conflict prevention for heartbeat mutation
- Increased backend dedup window from 10s to 20s
- Increased frontend debounce from 10s to 20s to match backend
- Added random jitter (±5s) to heartbeat intervals to prevent synchronized calls across tabs
- Simplified early return to skip ANY update within dedup window (not just same path)
- Prevents "Documents read from or written to the activeSessions table changed" errors
[1.20.1] - 2025-12-21
Changed
- Visitor map styling improvements
- Removed box-shadow from map wrapper for cleaner flat design
- Increased land dot contrast for better globe visibility on all themes
- Increased land dot opacity from 0.6 to 0.85
- Darker/more visible land colors for light, tan, and cloud themes
- Lighter land color for dark theme to stand out on dark background
[1.20.0] - 2025-12-21
Added
- Real-time visitor map on stats page
- Displays live visitor locations on a dotted world map
- Uses Netlify's built-in geo detection via edge function (no third-party API needed)
- Privacy friendly: stores city, country, and coordinates only (no IP addresses)
- Theme-aware colors for all four themes (dark, light, tan, cloud)
- Animated pulsing dots for active visitors
- Visitor count badge showing online visitors
- Configurable via
siteConfig.visitorMap
- New Netlify edge function:
netlify/edge-functions/geo.ts- Returns user geo data from Netlify's automatic geo headers
- Endpoint:
/api/geo
- New React component:
src/components/VisitorMap.tsx- SVG-based world map with simplified continent outlines
- Lightweight (no external map library needed)
- Responsive design scales on mobile
Changed
- Updated
convex/schema.ts: Added optional location fields toactiveSessionstable (city, country, latitude, longitude) - Updated
convex/stats.ts: Heartbeat mutation accepts geo args, getStats returns visitor locations - Updated
src/hooks/usePageTracking.ts: Fetches geo data once on mount, passes to heartbeat - Updated
src/pages/Stats.tsx: Displays VisitorMap above "Currently Viewing" section - Updated
src/config/siteConfig.ts: AddedVisitorMapConfiginterface andvisitorMapconfig option
Documentation
- Updated setup-guide.md with Visitor Map section
- Updated docs.md with Visitor Map configuration
- Updated FORK_CONFIG.md with visitorMap config
- Updated fork-config.json.example with visitorMap option
- Updated fork-configuration-guide.md with visitorMap example
[1.19.1] - 2025-12-21
Added
- GitHub Stars card on Stats page
- Displays live star count from
waynesutton/markdown-siterepository - Fetches from GitHub public API (no token required)
- Uses Phosphor GithubLogo icon
- Updates on page load
- Displays live star count from
Changed
- Stats page now displays 6 cards in a single row (previously 5)
- Updated CSS grid for 6-column layout on desktop
- Responsive breakpoints adjusted for 6 cards (3x2 tablet, 2x3 mobile, 1x6 small mobile)
Technical
- Added
useStateanduseEffecttosrc/pages/Stats.tsxfor GitHub API fetch - Added
GithubLogoimport from@phosphor-icons/react - Updated
.stats-cards-moderngrid torepeat(6, 1fr) - Updated responsive nth-child selectors for proper borders
[1.19.0] - 2025-12-21
Added
- Author display for posts and pages
- New optional
authorNameandauthorImagefrontmatter fields - Round avatar image displayed next to date and read time
- Works on individual post and page views (not on blog list)
- Example:
authorName: "Your Name"andauthorImage: "/images/authors/photo.png"
- New optional
- Author images directory at
public/images/authors/- Place author avatar images here
- Recommended: square images (they display as circles)
- Write page updated with new frontmatter field reference
- Shows
authorNameandauthorImageoptions for both posts and pages
- Shows
Technical
- Updated
convex/schema.tswith authorName and authorImage fields - Updated
scripts/sync-posts.tsinterfaces and parsing - Updated
convex/posts.tsandconvex/pages.tsqueries and mutations - Updated
src/pages/Post.tsxto render author info - Updated
src/pages/Write.tsxwith new field definitions - CSS styles for
.post-author,.post-author-image,.post-author-name
Documentation
- Updated frontmatter tables in setup-guide.md, docs.md, files.md, README.md
- Added example usage in about-this-blog.md
[1.18.1] - 2025-12-21
Changed
- CopyPageDropdown AI services now use raw markdown URLs for better AI parsing
- ChatGPT, Claude, and Perplexity receive
/raw/{slug}.mdURLs instead of page URLs - AI services can fetch and parse clean markdown content directly
- Includes metadata headers (type, date, reading time, tags) for structured parsing
- No HTML parsing required by AI services
- ChatGPT, Claude, and Perplexity receive
Technical
- Renamed
buildUrlFromPageUrltobuildUrlFromRawMarkdownin AIService interface - Handler builds raw markdown URL from page origin and slug
- Updated prompt text to reference "raw markdown file URL"
[1.18.0] - 2025-12-20
Added
- Automated fork configuration with
npm run configure- Copy
fork-config.json.exampletofork-config.json - Edit JSON with your site information
- Run
npm run configureto apply all changes automatically - Updates all 11 configuration files in one command
- Copy
- Two options for fork setup
- Option 1: Automated (recommended): JSON config + single command
- Option 2: Manual: Follow step-by-step guide in
FORK_CONFIG.md
FORK_CONFIG.mdcomprehensive fork guide- YAML template for AI agent configuration
- Manual code snippets for each file
- AI agent prompt for automated updates
fork-config.json.exampletemplate with all configuration options- Site name, URL, description
- Creator social links (Twitter, LinkedIn, GitHub)
- Bio and intro text
- Logo gallery settings
- GitHub contributions config
- Blog page and theme options
Technical
- New script:
scripts/configure-fork.ts - New npm command:
npm run configure - Reads JSON config and updates 11 files with string replacements
- Updates: siteConfig.ts, Home.tsx, Post.tsx, http.ts, rss.ts, index.html, llms.txt, robots.txt, openapi.yaml, ai-plugin.json, ThemeContext.tsx
[1.17.0] - 2025-12-20
Added
- GitHub contributions graph on homepage
- Displays yearly contribution activity with theme-aware colors
- Fetches data from public API (no GitHub token required)
- Year navigation with Phosphor icons (CaretLeft, CaretRight)
- Click graph to visit GitHub profile
- Configurable via
siteConfig.gitHubContributions
- Theme-specific contribution colors
- Dark theme: GitHub green on dark background
- Light theme: Standard GitHub green
- Tan theme: Warm brown tones matching site palette
- Cloud theme: Gray-blue tones
- Mobile responsive design
- Scales down on tablets and phones
- Day labels hidden on small screens for space
- Touch-friendly navigation buttons
Technical
- New component:
src/components/GitHubContributions.tsx - Uses
github-contributions-api.jogruber.depublic API - CSS variables for contribution level colors per theme
- Configuration interface:
GitHubContributionsConfig - Set
enabled: falsein siteConfig to disable
[1.16.0] - 2025-12-21
Added
- Public markdown writing page at
/write(not linked in navigation)- Three-column Cursor docs-style layout
- Left sidebar: Home link, content type selector (Blog Post/Page), actions (Clear, Theme, Font)
- Center: Full-height writing area with title, Copy All button, and borderless textarea
- Right sidebar: Frontmatter reference with copy icon for each field
- Font switcher in Actions section
- Toggle between Serif and Sans-serif fonts
- Font preference saved to localStorage
- Theme toggle matching the rest of the app (Moon, Sun, Half2Icon, Cloud)
- localStorage persistence for content, type, and font preference
- Word, line, and character counts in status bar
- Warning banner: "Refresh loses content"
- Grammarly and browser spellcheck compatible
- Works with all four themes (dark, light, tan, cloud)
Technical
- New component:
src/pages/Write.tsx - Route:
/write(added tosrc/App.tsx) - Three localStorage keys:
markdown_write_content,markdown_write_type,markdown_write_font - CSS Grid layout (220px | 1fr | 280px)
- Uses Phosphor icons: House, Article, File, Trash, CopySimple, Warning, Check
- Uses lucide-react and radix-ui icons for theme toggle (consistent with ThemeToggle.tsx)
[1.15.1] - 2025-12-21
Fixed
- Theme toggle icons on
/writepage now matchThemeToggle.tsxcomponent- dark: Moon icon (lucide-react)
- light: Sun icon (lucide-react)
- tan: Half2Icon (radix-ui) - consistent with rest of app
- cloud: Cloud icon (lucide-react)
- Content type switching (Blog Post/Page) now always updates writing area template
Technical
- Replaced Phosphor icons (Moon, Sun, Leaf, CloudSun) with lucide-react and radix-ui icons
handleTypeChangenow always regenerates template when switching types
[1.15.0] - 2025-12-21
Changed
- Redesigned
/writepage with three-column Cursor docs-style layout- Left sidebar: Home link, content type selector (Blog Post/Page), actions (Clear, Theme)
- Center: Full-height writing area with title, Copy All button, and borderless textarea
- Right sidebar: Frontmatter reference with copy icon for each field
- Frontmatter fields panel with per-field copy buttons
- Each frontmatter field shows name, example value, and copy icon
- Click to copy individual field syntax to clipboard
- Required fields marked with red asterisk
- Fields update dynamically when switching between Blog Post and Page
- Warning banner for unsaved content
- "Refresh loses content" warning in left sidebar with warning icon
- Helps users remember localStorage persistence limitations
- Enhanced status bar
- Word, line, and character counts in sticky footer
- Save hint with content directory path
Technical
- Three-column CSS Grid layout (220px sidebar | 1fr main | 280px right sidebar)
- Theme toggle cycles through dark, light, tan, cloud with matching icons
- Collapsible sidebars on mobile (stacked layout)
- Uses Phosphor icons: House, Article, File, Trash, CopySimple, Warning, Check
[1.14.0] - 2025-12-20
Changed
- Redesigned
/writepage with Notion-like minimal UI- Full-screen distraction-free writing experience
- Removed site header for focused writing environment
- Wider writing area (900px max-width centered)
- Borderless textarea with transparent background
- Own minimal header with home link, type selector, and icon buttons
- Improved toolbar design
- Home icon link to return to main site
- Clean dropdown for content type selection (no borders)
- Collapsible frontmatter fields panel (hidden by default)
- Theme toggle in toolbar (cycles through dark, light, tan, cloud)
- Icon buttons with subtle hover states
- Copy button with inverted theme colors
- Enhanced status bar
- Sticky footer with word/line/character counts
- Save hint with content directory path
- Dot separators between stats
Technical
- Write page now renders without Layout component wrapper
- Added Phosphor icons: House, Sun, Moon, CloudSun, Leaf, Info, X
- CSS restructured for minimal aesthetic (
.write-wrapper,.write-header, etc.) - Mobile responsive with hidden copy text and save hint on small screens
[1.13.0] - 2025-12-20
Added
- Public markdown writing page at
/write(not linked in navigation)- Dropdown to select between "Blog Post" and "Page" content types
- Frontmatter fields reference panel with required/optional indicators
- Copy button using Phosphor CopySimple icon
- Clear button to reset content to template
- Status bar showing lines, words, and characters count
- Usage hint with instructions for saving content
- localStorage persistence for writing session
- Content persists across page refreshes within same browser
- Each browser has isolated content (session privacy)
- Content type selection saved separately
- Auto-generated frontmatter templates
- Blog post template with all common fields
- Page template with navigation fields
- Current date auto-populated in templates
Technical
- New component:
src/pages/Write.tsx - Route:
/write(added tosrc/App.tsx) - CSS styles added to
src/styles/global.css - Works with all four themes (dark, light, tan, cloud)
- Plain textarea for Grammarly and browser spellcheck compatibility
- Mobile responsive design with adjusted layout for smaller screens
- No Convex backend required (localStorage only)
[1.12.2] - 2025-12-20
Added
- Centralized font-size configuration using CSS variables in
global.css- Base size scale from 10px to 64px with semantic names
- Component-specific variables for consistent sizing
- Mobile responsive overrides at 768px breakpoint
- All hardcoded font sizes converted to CSS variables for easier customization
Technical
- Font sizes defined in
:rootselector with--font-size-*naming convention - Mobile breakpoint uses same variables with smaller values
- Base scale: 3xs (10px), 2xs (11px), xs (12px), sm (13px), md (14px), base (16px), lg (17px), xl (18px), 2xl (20px), 3xl (24px), 4xl (28px), 5xl (32px), 6xl (36px), hero (64px)
[1.12.1] - 2025-12-20
Fixed
- Open Graph images now use post/page
imagefield from frontmatter- Posts with images in frontmatter display their specific OG image
- Posts without images fall back to
og-default.svg - Pages now supported with appropriate
og:typeset to "website" - Relative image paths resolved to absolute URLs
Changed
- Renamed
generatePostMetaHtmltogenerateMetaHtmlinconvex/http.ts /meta/postendpoint now checks for pages if no post found- Meta HTML generation accepts optional
imageandtypeparameters
Technical
- Updated
convex/http.tswith image resolution logic - Handles both absolute URLs and relative paths for images
- Deployed to production Convex
[1.12.0] - 2025-12-20
Added
- Dedicated blog page at
/blogwith configurable display- Enable/disable via
siteConfig.blogPage.enabled - Show/hide from navigation via
siteConfig.blogPage.showInNav - Custom page title via
siteConfig.blogPage.title - Navigation order via
siteConfig.blogPage.order(lower = first)
- Enable/disable via
- Centralized site configuration in
src/config/siteConfig.ts- Moved all site settings from
Home.tsxto dedicated config file - Easier to customize when forking
- Moved all site settings from
- Flexible post display options
displayOnHomepage: Show posts on the homepageblogPage.enabled: Show posts on dedicated/blogpage- Both can be enabled for dual display
Changed
- Navigation now combines Blog link with pages and sorts by order
- Blog link position controlled by
siteConfig.blogPage.order - Pages sorted by frontmatter
orderfield (lower = first) - Items without order default to 999 (appear last, alphabetically)
- Blog link position controlled by
Home.tsximports siteConfig instead of defining inlineLayout.tsxuses unified nav item sorting for desktop and mobile
Technical
- New file:
src/config/siteConfig.ts - New page:
src/pages/Blog.tsx - Updated:
src/App.tsx(conditional blog route) - Updated:
src/components/Layout.tsx(nav item ordering) - Updated:
src/styles/global.css(blog page styles)
[1.11.1] - 2025-12-20
Fixed
- Stats page now shows all historical page views correctly
- Changed
getStatsto use direct counting until aggregates are fully backfilled - Ensures accurate stats display even if aggregate backfilling is incomplete
- Changed
Changed
- Chunked backfilling for aggregate component
- Backfill mutation now processes 500 records at a time
- Prevents memory limit issues with large datasets (16MB Convex limit)
- Schedules itself to continue processing until complete
- Progress visible in Convex dashboard logs
Technical
backfillAggregatesChunkinternal mutation handles pagination- Uses
ctx.scheduler.runAfterto chain batch processing - Tracks seen session IDs across chunks for unique visitor counting
[1.11.0] - 2025-12-20
Added
- Aggregate component for efficient O(log n) stats counts
- Replaces O(n) table scans with pre-computed denormalized counts
- Uses
@convex-dev/aggregatepackage for TableAggregate - Three aggregates: totalPageViews, pageViewsByPath, uniqueVisitors
- Backfill mutation for existing page view data
stats:backfillAggregatespopulates counts from existing data- Idempotent and safe to run multiple times
Changed
recordPageViewmutation now updates aggregate components- Inserts into pageViewsByPath aggregate for per-page counts
- Inserts into totalPageViews aggregate for global count
- Inserts into uniqueVisitors aggregate for new sessions only
getStatsquery now uses aggregate counts- O(log n) count operations instead of O(n) table scans
- Consistent fast response times regardless of data size
- Still queries posts/pages for title matching
Technical
- New file:
convex/convex.config.ts(updated with aggregate component registrations) - Three TableAggregate instances with different namespacing strategies
- Performance improvement scales better with growing page view data
Documentation
- Updated
prds/howstatsworks.mdwith old vs new implementation comparison - Added aggregate component usage examples and configuration
[1.10.0] - 2025-12-20
Added
- Fork configuration documentation
- "Files to Update When Forking" section in docs.md and setup-guide.md
- Lists all 9 files with site-specific configuration
- Backend configuration examples for Convex files
- Code snippets for
convex/http.ts,convex/rss.ts,src/pages/Post.tsx
- Same documentation added to README.md for discoverability
Changed
- Updated site branding across all configuration files
public/robots.txt: Updated sitemap URL and headerpublic/llms.txt: Updated site name and descriptionpublic/.well-known/ai-plugin.json: Updated name and description for AI pluginspublic/openapi.yaml: Updated API title and site name exampleconvex/http.ts: Updated SITE_URL and SITE_NAME constants
Documentation
- Setup guide table of contents now includes fork configuration sections
- Docs page configuration section expanded with backend file list
- All AI discovery files reflect new "markdown sync site" branding
[1.9.0] - 2025-12-20
Added
- Scroll-to-top button
- Appears after scrolling 300px (configurable)
- Uses Phosphor ArrowUp icon for consistency
- Smooth scroll animation (configurable)
- Works with all four themes (dark, light, tan, cloud)
- Enabled by default (can be disabled in Layout.tsx)
- Fade-in animation when appearing
- Responsive sizing for mobile devices
Technical
- New component:
src/components/ScrollToTop.tsx- Configurable via
ScrollToTopConfiginterface - Exports
defaultScrollToTopConfigfor customization - Uses passive scroll listener for performance
- Configurable via
- Configuration options in Layout.tsx
scrollToTopConfig - CSS styles added to global.css with theme-specific shadows
[1.8.0] - 2025-12-20
Added
- Mobile menu with hamburger navigation
- Slide-out drawer on mobile and tablet views
- Accessible with keyboard navigation (Escape to close)
- Focus trap for screen reader support
- Smooth CSS transform animations
- Page links and Home link in drawer
- Auto-closes on route change
- Generate Skill option in CopyPageDropdown
- Formats post/page content as an AI agent skill file
- Downloads as
{slug}-skill.mdwith skill structure - Includes metadata, when to use, and instructions sections
- Uses Download icon from lucide-react
Changed
- Layout.tsx now includes hamburger button and MobileMenu component
- Desktop navigation hidden on mobile, mobile menu hidden on desktop
- Improved responsive navigation across all breakpoints
Technical
- New component:
src/components/MobileMenu.tsx - HamburgerButton exported from MobileMenu for Layout use
- New
formatAsSkill()function for skill file generation - New
handleDownloadSkill()handler with blob download logic - Uses browser File API for client-side file download
- CSS styles for mobile menu in global.css
[1.7.0] - 2025-12-20
Added
- Static raw markdown files at
/raw/{slug}.md- Generated during
npm run sync(development) ornpm run sync:prod(production) inpublic/raw/directory - Each published post and page gets a corresponding static
.mdfile - SEO indexable and accessible to AI agents
- Includes metadata header (type, date, reading time, tags)
- Generated during
- View as Markdown option in CopyPageDropdown
- Opens raw
.mdfile in new tab - Available on all post and page views
- Opens raw
- Perplexity added to AI service options in CopyPageDropdown
- Sends full markdown content via URL parameter
- Research articles directly in Perplexity
- Featured image support for posts and pages
imagefield in frontmatter displays as square thumbnail in card view- Non-square images automatically cropped to center
- Recommended size: 400x400px minimum (800x800px for retina)
Changed
- CopyPageDropdown now accepts
slugprop for raw file links - Updated
_redirectsto serve/raw/*files directly - Improved markdown table CSS styling
- GitHub-style tables with proper borders
- Mobile responsive with horizontal scroll
- Theme-aware alternating row colors
- Hover states for better readability
Technical
- Updated
scripts/sync-posts.tsto generatepublic/raw/files - Files are regenerated on each sync (old files cleaned up)
- Only published posts and pages generate raw files
- CopyPageDropdown uses FileText icon from lucide-react for View as Markdown
[1.6.1] - 2025-12-18
Added
- AGENTS.md with codebase instructions for AI coding agents
Changed
- Added Firecrawl import to all "When to sync vs deploy" tables in docs
- Clarified import workflow: creates local files only, no
import:prodneeded - Updated README, setup-guide, how-to-publish, docs page, about-this-blog
- Renamed
content/pages/changelog.mdtochangelog-page.mdto avoid confusion with root changelog
[1.6.0] - 2025-12-18
Added
- Firecrawl content importer for external URLs
- New
npm run import <url>command - Scrapes URLs and converts to local markdown drafts
- Creates drafts in
content/blog/with frontmatter - Uses Firecrawl API (requires
FIRECRAWL_API_KEYin.env.local) - Then sync to dev (
npm run sync) or prod (npm run sync:prod) - No separate
import:prodcommand needed (import creates local files only)
- New
- New API endpoint
/api/exportfor batch content fetching- Returns all posts with full markdown content
- Single request for LLM ingestion
- AI plugin discovery at
/.well-known/ai-plugin.json- Standard format for AI tool integration
- OpenAPI 3.0 specification at
/openapi.yaml- Full API documentation
- Describes all endpoints, parameters, and responses
- Enhanced
llms.txtwith complete API documentation- Added all new endpoints
- Improved quick start section
- Added response schema documentation
Technical
- New script:
scripts/import-url.ts - New package dependency:
@mendable/firecrawl-js - Updated
netlify/edge-functions/api.tsfor/api/exportproxy - Updated
convex/http.tswith export endpoint - Created
public/.well-known/directory
[1.5.0] - 2025-12-17
Added
- Frontmatter-controlled featured items
- Add
featured: trueto any post or page frontmatter - Use
featuredOrderto control display order (lower = first) - Featured items sync instantly with
npm run sync(no redeploy needed)
- Add
- New Convex queries for featured content
getFeaturedPosts: returns posts withfeatured: truegetFeaturedPages: returns pages withfeatured: true
- Schema updates with
featuredandfeaturedOrderfields- Added
by_featuredindex for efficient queries
- Added
Changed
- Home.tsx now queries featured items from Convex instead of siteConfig
- FeaturedCards component uses Convex queries for real-time updates
- Removed hardcoded
featuredItemsandfeaturedEssaysfrom siteConfig
Technical
- Updated sync script to parse
featuredandfeaturedOrderfrom frontmatter - Added index on
featuredfield in posts and pages tables - Both list and card views now use frontmatter data
[1.4.0] - 2025-12-17
Added
- Featured section with list/card view toggle
- Card view displays title and excerpt in a responsive grid
- Toggle button in featured header to switch between views
- View preference saved to localStorage
- Logo gallery with continuous marquee scroll
- Clickable logos with configurable URLs
- CSS only animation for smooth infinite scrolling
- Configurable speed, position, and title
- Grayscale logos with color on hover
- Responsive sizing across breakpoints
- 5 sample logos included for easy customization
- New
excerptfield for posts and pages frontmatter- Used for card view descriptions
- Falls back to description field for posts
- Expanded
siteConfigin Home.tsxfeaturedViewMode: 'list' or 'cards'showViewToggle: enable user togglelogoGallery: full configuration object
Technical
- New components:
FeaturedCards.tsx,LogoMarquee.tsx - Updated schema with optional excerpt field
- Updated sync script to parse excerpt from frontmatter
- CSS uses theme variables for all four themes
- Mobile responsive grid (3 to 2 to 1 columns for cards)
[1.3.0] - 2025-12-17
Added
- Real-time search with Command+K keyboard shortcut
- Search icon in top nav using Phosphor Icons
- Modal with keyboard navigation (arrow keys, Enter, Escape)
- Full text search across posts and pages using Convex search indexes
- Result snippets with context around search matches
- Distinguishes between posts and pages with type badges
- Search indexes for pages table (title and content)
- New
@phosphor-icons/reactdependency for search icon
Technical
- Uses Convex full text search with reactive queries
- Deduplicates results from title and content searches
- Sorts results with title matches first
- Mobile responsive modal design
- All four themes supported (dark, light, tan, cloud)
[1.2.0] - 2025-12-15
Added
- Real-time stats page at
/statswith live visitor tracking- Active visitors count with per-page breakdown
- Total page views and unique visitors
- Views by page sorted by popularity
- Page view tracking via event records pattern (no write conflicts)
- Active session heartbeat system (30s interval, 2min timeout)
- Cron job for stale session cleanup every 5 minutes
- New Convex tables:
pageViewsandactiveSessions - Stats link in homepage footer
Technical
- Uses anonymous session UUIDs (no PII stored)
- All stats update in real-time via Convex subscriptions
- Mobile responsive stats grid (4 to 2 to 1 columns)
- Theme support with CSS variables (dark, light, tan, cloud)
[1.1.0] - 2025-12-14
Added
- Netlify Edge Functions for dynamic Convex HTTP proxying
rss.tsproxies/rss.xmland/rss-full.xmlsitemap.tsproxies/sitemap.xmlapi.tsproxies/api/postsand/api/post
- Vite dev server proxy for RSS, sitemap, and API endpoints
Changed
- Replaced hardcoded Convex URLs in netlify.toml with edge functions
- Edge functions dynamically read
VITE_CONVEX_URLfrom environment - Updated setup guide, docs, and README with edge function documentation
Fixed
- RSS feeds and sitemap now work without manual URL configuration
- Local development properly proxies API routes to Convex
[1.0.0] - 2025-12-14
Added
- Initial project setup with Vite, React, TypeScript
- Convex backend with posts, pages, viewCounts, and siteConfig tables
- Markdown blog post support with frontmatter parsing
- Static pages support (About, Projects, Contact) with navigation
- Four theme options: Dark, Light, Tan (default), Cloud
- Font configuration option in global.css with serif (New York) as default
- Syntax highlighting for code blocks using custom Prism themes
- Year-grouped post list on home page
- Individual post pages with share buttons
- SEO optimization with dynamic sitemap at
/sitemap.xml - JSON-LD structured data injection for blog posts
- RSS feeds at
/rss.xmland/rss-full.xml(full content for LLMs) - AI agent discovery with
llms.txtfollowing llmstxt.org standard robots.txtwith rules for AI crawlers- API endpoints for LLM access:
/api/posts- JSON list of all posts/api/post?slug=xxx- Single post as JSON or markdown
- Copy Page dropdown for sharing to ChatGPT, Claude
- Open Graph and Twitter Card meta tags
- Netlify edge function for social media crawler detection
- Build-time markdown sync from
content/blog/to Convex - Responsive design for mobile, tablet, and desktop
Security
- All HTTP endpoints properly escape HTML and XML output
- Convex queries use indexed lookups
- External links use rel="noopener noreferrer"
- No console statements in production code
Technical Details
- React 18 with TypeScript
- Convex for real-time database
- react-markdown for rendering
- react-syntax-highlighter for code blocks
- date-fns for date formatting
- lucide-react for icons
- Netlify deployment with edge functions
- SPA 404 fallback configured