mirror of
https://github.com/waynesutton/markdown-site.git
synced 2026-01-12 04:09:14 +00:00
Add vector-based semantic search to complement keyword search. Users can toggle between "Keyword" and "Semantic" modes in the search modal (Cmd+K, then Tab to switch). Semantic search: - Uses OpenAI text-embedding-ada-002 (1536 dimensions) - Finds content by meaning, not exact words - Shows similarity scores as percentages - ~300ms latency, ~$0.0001/query - Graceful fallback if OPENAI_API_KEY not set New files: - convex/embeddings.ts - Embedding generation actions - convex/embeddingsQueries.ts - Queries/mutations for embeddings - convex/semanticSearch.ts - Vector search action - convex/semanticSearchQueries.ts - Result hydration queries - content/pages/docs-search.md - Keyword search docs - content/pages/docs-semantic-search.md - Semantic search docs Changes: - convex/schema.ts: Add embedding field and by_embedding vectorIndex - SearchModal.tsx: Add mode toggle (TextAa/Brain icons) - sync-posts.ts: Generate embeddings after content sync - global.css: Search mode toggle styles Documentation updated: - changelog.md, TASK.md, files.md, about.md, home.md Configuration: npx convex env set OPENAI_API_KEY sk-your-key Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Status: Ready to commit. All semantic search files are staged. The TypeScript warnings are pre-existing (unused variables) and don't affect the build.
107 KiB
107 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
[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