Files
wiki/changelog.md

2917 lines
130 KiB
Markdown
Raw Normal View History

# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [2.19.0] - 2026-01-10
### Added
- `npx create-markdown-sync` CLI for scaffolding new projects
- Interactive wizard with 13 sections covering all configuration options
- Clones template from GitHub via giget
- Configures site settings automatically
- Installs dependencies
- Sets up Convex project (optional WorkOS auth disabled by default)
- Starts dev server and opens browser
- Clear next steps with docs, deployment, and WorkOS setup links
### Technical
- New `packages/create-markdown-sync/` monorepo package
- CLI files: index.ts, wizard.ts, clone.ts, configure.ts, install.ts, convex-setup.ts, utils.ts
- Template fixes for siteConfig.ts embedded quotes
- Empty auth.config.ts when auth not required (prevents WorkOS blocking)
- Added workspaces to root package.json
- Updated .gitignore for packages/*/dist/ and packages/*/node_modules/
## [2.18.2] - 2026-01-10
### Added
- Related posts thumbnail view with toggle
- New thumbnail view shows post image, title, description, author, and date
- Toggle button to switch between thumbnail and list views (same icons as homepage featured)
- View preference saved to localStorage
- Default view mode and toggle visibility configurable via siteConfig.relatedPosts
- Dashboard Config section for related posts settings
### Changed
- Updated getRelatedPosts query to return image, excerpt, authorName, authorImage fields
- Related posts section now has header with title and optional toggle button
### Technical
- Added `RelatedPostsConfig` interface to siteConfig.ts
- Added `relatedPosts` configuration to SiteConfig interface
- Updated convex/posts.ts getRelatedPosts query with additional return fields
- Added related posts thumbnail CSS styles (~100 lines)
- Added relatedPostsDefaultViewMode and relatedPostsShowViewToggle to Dashboard ConfigSection
## [2.18.1] - 2026-01-10
### Changed
- README.md streamlined from 609 lines to 155 lines
- Removed detailed feature documentation (now links to live docs)
- Kept sync commands, setup, and Netlify deployment sections
- Added Documentation section with links to markdown.fast/docs
- Added Guides subsection with links to specific doc pages
- Simplified Features section with link to About page
- Simplified Fork Configuration to quick commands with doc link
## [2.18.0] - 2026-01-10
### Added
- OpenCode AI development tool integration
- Full `.opencode/` directory structure for OpenCode CLI compatibility
- 3 specialized agents: orchestrator, content-writer, sync-manager
- 6 commands: /sync, /sync-prod, /create-post, /create-page, /import, /deploy
- 4 skills: frontmatter, sync, convex, content
- sync-helper plugin for content change reminders
- Works alongside Claude Code and Cursor without conflicts
- OpenCode documentation page at /docs-opencode
- How OpenCode integration works
- Directory structure reference
- Command and agent descriptions
- Getting started guide
### Technical
- `opencode.json` - Root OpenCode project configuration
- `.opencode/config.json` - OpenCode app configuration
- `.opencode/agent/orchestrator.md` - Main routing agent
- `.opencode/agent/content-writer.md` - Content creation specialist
- `.opencode/agent/sync-manager.md` - Sync and deployment specialist
- `.opencode/command/sync.md` - /sync command definition
- `.opencode/command/sync-prod.md` - /sync-prod command
- `.opencode/command/create-post.md` - /create-post command
- `.opencode/command/create-page.md` - /create-page command
- `.opencode/command/import.md` - /import command
- `.opencode/command/deploy.md` - /deploy command
- `.opencode/skill/frontmatter.md` - Frontmatter reference (adapted from .claude/skills/)
- `.opencode/skill/sync.md` - Sync system reference
- `.opencode/skill/convex.md` - Convex patterns reference
- `.opencode/skill/content.md` - Content management guide
- `.opencode/plugin/sync-helper.ts` - Minimal reminder plugin
- `content/pages/docs-opencode.md` - Documentation page
- `files.md` - Added OpenCode Configuration section
## [2.17.0] - 2026-01-10
### Added
- ConvexFS Media Library with Bunny CDN integration
- Upload images via drag-and-drop or click to upload
- Copy as Markdown, HTML, or direct URL
- Bulk select and delete multiple images
- File size display and pagination
- Configuration warning when Bunny CDN not configured
- Enhanced Image Insert Modal in Write Post/Page
- Two tabs: "Upload New" and "Media Library" for selecting existing images
- Image dimensions display (original size with aspect ratio)
- Size presets: Original, Large (1200px), Medium (800px), Small (400px), Thumbnail (200px), Custom
- Custom dimensions input with automatic aspect ratio preservation
- Alt text field for accessibility
- Calculated dimensions shown before insert
- File expiration support via ConvexFS
- `setFileExpiration` action to set time-based auto-deletion
- Pass `expiresInMs` for automatic cleanup after specified time
- Pass `null` to remove expiration and make file permanent
### Technical
- `convex/convex.config.ts` - Added ConvexFS component registration
- `convex/fs.ts` - ConvexFS instance with Bunny CDN configuration, conditional instantiation
- `convex/files.ts` - File mutations/queries: commitFile, listFiles, deleteFile, deleteFiles, setFileExpiration, isConfigured
- `convex/http.ts` - ConvexFS routes for /fs/upload and /fs/blobs/{blobId}
- `src/components/MediaLibrary.tsx` - Media library gallery with bulk select/delete
- `src/components/ImageUploadModal.tsx` - Enhanced modal with library selection and size presets
- `src/styles/global.css` - Added ~400 lines for media library and image modal styles
- `content/pages/docs-media-setup.md` - Setup documentation with ConvexFS links
## [2.16.4] - 2026-01-10
### Added
- AI image generation download and copy options
- Download button to save generated image to computer
- MD button to copy Markdown code (`![prompt](url)`) to clipboard
- HTML button to copy HTML code (`<img src="url" alt="prompt" />`) to clipboard
- Code preview section showing both Markdown and HTML snippets
- Filename generated from prompt (sanitized and truncated)
### Technical
- `src/pages/Dashboard.tsx` - Added copiedFormat state, getMarkdownCode/getHtmlCode helpers, handleCopyCode, handleDownloadImage functions, updated generated image display JSX
- `src/styles/global.css` - Added CSS for .ai-image-actions, .ai-image-action-btn, .ai-image-code-preview, .ai-image-code-block
## [2.16.3] - 2026-01-10
### Added
- Social icons in hamburger menu (MobileMenu)
- Social icons now appear below navigation links in mobile menu
- Only shows when `socialFooter.enabled` and `socialFooter.showInHeader` are true
- Imported `platformIcons` from SocialFooter for consistent icon rendering
- Dashboard Config options for social and AI features
- Added `socialFooter.showInHeader` toggle to Social Footer config card
- Added new Ask AI config card with `askAI.enabled` toggle
- Generated siteConfig.ts includes both new options
- Configuration alignment documentation for AI/LLMs
- Added "Configuration alignment" section to CLAUDE.md
- Added sync comment to top of `src/config/siteConfig.ts`
- Added JSDoc comment to ConfigSection in Dashboard.tsx
- Explains relationship between siteConfig.ts and Dashboard Config
### Changed
- Removed social icons from mobile header
- Social icons no longer display in `mobile-nav-controls` (header on mobile)
- Social icons now exclusively in hamburger menu for cleaner mobile header
- Added comment in Layout.tsx noting social icons are in MobileMenu
### Technical
- `src/components/MobileMenu.tsx` - Added social icons section with platformIcons import
- `src/components/Layout.tsx` - Removed social icons from mobile-nav-controls
- `src/pages/Dashboard.tsx` - Added socialFooterShowInHeader and askAIEnabled to ConfigSection
- `src/styles/global.css` - Added mobile-menu-social CSS styles
- `src/config/siteConfig.ts` - Added alignment comment header
- `CLAUDE.md` - Added Configuration alignment section and Dashboard.tsx to key files
## [2.16.2] - 2026-01-10
### Added
- Ask AI configuration documentation alignment
- Added `askAI` config to `fork-config.json.example` with enabled, defaultModel, and models fields
- Added Ask AI Configuration section to `FORK_CONFIG.md` with fork-config.json and manual configuration examples
- Added Ask AI (header chat) section to `docs-dashboard.md` with configuration and requirements
- Added Ask AI (header chat) section to `how-to-use-the-markdown-sync-dashboard.md` with step-by-step setup
### Technical
- `fork-config.json.example` now includes askAI config matching siteConfig.ts structure
- All dashboard documentation now includes Ask AI feature alongside AI Agent and AI Dashboard sections
## [2.16.1] - 2026-01-10
### Fixed
- Docs layout scrollbar hiding for cleaner UI
- Hidden scrollbars on left sidebar, right sidebar, and main docs content
- Scrolling still works via trackpad, mouse wheel, and touch
- Added `body:has(.docs-layout)` to prevent page-level scrolling on docs pages
- Cross-browser support: `-ms-overflow-style: none` (IE/Edge), `scrollbar-width: none` (Firefox), `::-webkit-scrollbar { width: 0 }` (Chrome/Safari)
### Technical
- Updated `src/styles/global.css`:
- Added `body:has(.docs-layout) { overflow: hidden; }` rule
- Added scrollbar hiding rules for `.docs-sidebar-left`, `.docs-sidebar-right`, `.docs-content`
- Existing scrollbar thumb/track styles remain but are invisible with width: 0
## [2.16.0] - 2026-01-09
### Added
- Sync version control system
- 3-day version history for posts, pages, home content, and footer
- Dashboard toggle to enable/disable version control
- Version history modal with unified diff visualization using DiffCodeBlock component
- Preview mode to view previous version content
- One-click restore with automatic backup of current state
- Automatic cleanup of versions older than 3 days (daily cron at 3 AM UTC)
- Version stats display in Config section (total, posts, pages)
### Technical
- New `convex/versions.ts` with 7 functions:
- `isEnabled` / `setEnabled` - Toggle version control
- `createVersion` - Capture content snapshot (internal mutation)
- `getVersionHistory` / `getVersion` - Query version data
- `restoreVersion` - Restore with backup creation
- `cleanupOldVersions` - Batch delete old versions
- `getStats` - Version count statistics
- New `contentVersions` table in schema with indexes:
- `by_content` - Query by content type and ID
- `by_slug` - Query by content type and slug
- `by_createdAt` - For cleanup queries
- `by_content_createdAt` - Compound index for history
- New `versionControlSettings` table for toggle state
- New `src/components/VersionHistoryModal.tsx` component
- Updated `convex/cms.ts` to capture versions before dashboard edits
- Updated `convex/posts.ts` to capture versions before sync updates
- Updated `convex/pages.ts` to capture versions before sync updates
- Updated `convex/crons.ts` with daily cleanup job
- Added ~370 lines of CSS for version modal UI
2026-01-09 14:54:00 -08:00
## [2.15.3] - 2026-01-09
### Fixed
- Footer not displaying on `/docs` landing page when `showFooter: true` in frontmatter
- `DocsPage.tsx` was missing the Footer component entirely
- Added Footer import, footerPage query, and footer rendering logic to DocsPage.tsx
- Footer now respects `showFooter` frontmatter field on docs landing pages
- AI chat support added to DocsLayout via `aiChatEnabled` and `pageContent` props
### Changed
- Updated `getDocsLandingPage` query in `convex/pages.ts` to return `showFooter`, `footer`, `excerpt`, and `aiChat` fields
- Updated `getDocsLandingPost` query in `convex/posts.ts` to return `showFooter`, `footer`, and `aiChat` fields
2026-01-08 22:58:34 -08:00
## [2.15.2] - 2026-01-08
### Fixed
- Docs section layout CSS conflict with main-content container
- Fixed `.main-content` max-width: 800px constraint preventing docs layout from being full width
- Added `.main-content:has(.docs-layout)` rule to expand to 100% width when docs layout is used
- Updated Layout.tsx to use `main-content-wide` class for docs pages
- Fixed left sidebar not flush left and right sidebar not flush right
- Fixed responsive margins for docs layout (280px desktop, 240px tablet, 0 mobile)
### Technical
- Updated `src/styles/global.css`:
- Added `.main-content:has(.docs-layout) { max-width: 100%; padding: 0; }`
- Fixed `.docs-content` margins: 280px left/right for fixed sidebars
- Added responsive margin adjustments at 1200px, 900px, 768px breakpoints
- Updated `src/components/Layout.tsx`:
- Added `isDocsPage` check to className logic for main element
- Docs pages now use `main-content-wide` class for full width layout
## [2.15.1] - 2026-01-08
### Fixed
- Additional Core Web Vitals improvements for CLS and INP
- Added `aspect-ratio: 16/10` to `.blog-image` to reserve space before images load
- Added `aspect-ratio: 16/9` to `.post-header-image-img` to prevent layout shift
- Added `contain: layout style` to `.main-content` and `.main-content-wide` to isolate layout recalculations
- Added `fetchPriority="high"` to logo image for faster LCP
- Added `fetchPriority="high"` to header images (`showImageAtTop`) for faster LCP
- Added `will-change: transform` to continuous spin animations (`.spinner-icon`, `.animate-spin`, `.ai-chat-spinner`, `.ai-image-spinner`, `.spinning`, `.dashboard-import-btn .spin`)
- Added `will-change: transform` to `.logo-marquee-track` for smoother marquee animation
- Added `will-change: opacity` to `.visitor-map-badge-dot` for smoother pulse animation
### Technical
- Updated `src/styles/global.css` with CLS prevention and animation optimization
- Updated `src/components/Layout.tsx` with fetchPriority on logo
- Updated `src/pages/Post.tsx` with fetchPriority on header images
## [2.15.0] - 2026-01-07
### Added
- Export as PDF option in CopyPageDropdown
- Browser-based print dialog for saving pages as PDF
- Clean formatted output (no markdown syntax visible)
- Title displayed as proper heading
- Metadata shown as clean line (date, read time, tags)
- Content with markdown stripped for readable document
- Uses Phosphor FilePdf icon
- Positioned at end of dropdown menu
### Technical
- Added `formatForPrint` function to strip markdown syntax from content
- Added `handleExportPDF` handler with styled print window
- Imports `FilePdf` from `@phosphor-icons/react` (already installed)
2026-01-08 22:58:34 -08:00
## [2.14.1] - 2026-01-07
### Fixed
- Additional Core Web Vitals animation fixes
- Fixed `docs-skeleton-pulse` animation (converted from `background-position` to `transform: translateX()` via pseudo-element)
- Added `will-change` hints to 6 more animated elements for GPU compositing
### Technical
- Updated `src/styles/global.css`:
- Converted docs-loading-skeleton from background animation to pseudo-element with translateX
- Added `will-change` to `.image-lightbox-backdrop`, `.search-modal`, `.ai-chat-message`, `.dashboard-toast`, `.ask-ai-modal`, `.docs-article`
## [2.14.0] - 2026-01-07
### Fixed
- Core Web Vitals performance optimizations
- Fixed non-composited animations in visitor map (SVG `r` attribute changed to `transform: scale()`)
- Removed 5 duplicate `@keyframes spin` definitions from global.css
- Added `will-change` hints to animated elements for GPU compositing
### Added
- Critical CSS inlined in index.html for faster first paint
- Theme variables (dark/light/tan/cloud)
- Reset and base body styles
- Layout skeleton and navigation styles
- Additional resource hints in index.html
- Preconnect to convex.site for faster API calls
### Technical
- Updated `src/styles/global.css`:
- Converted visitor-pulse animations from SVG `r` to `transform: scale()` (GPU-composited)
- Added `transform-origin`, `transform-box`, and `will-change` to pulse ring elements
- Added `will-change` to `.theme-toggle`, `.copy-page-menu`, `.search-modal-backdrop`, `.scroll-to-top`
- Removed duplicate `@keyframes spin` at lines 9194, 10091, 10243, 10651, 13726
- Updated `src/components/VisitorMap.tsx`:
- Changed pulse ring `r` values from 12/8 to base value 5 (scaling handled by CSS)
- Updated `index.html`:
- Added inline critical CSS (~2KB) for instant first contentful paint
- Added preconnect/dns-prefetch for convex.site
## [2.13.0] - 2026-01-07
### Added
- Enhanced diff code block rendering with @pierre/diffs library
- Diff and patch code blocks now render with Shiki-based syntax highlighting
- Unified and split (side-by-side) view modes with toggle button
- Theme-aware colors (dark/light/tan/cloud support)
- Copy button for diff content
- Automatic routing: `diff and `patch blocks use enhanced renderer
- New blog post: "How to Use Code Blocks" with syntax highlighting and diff examples
- DiffCodeBlock component (`src/components/DiffCodeBlock.tsx`)
### Technical
- Added `@pierre/diffs` package for enhanced diff visualization
- Updated `BlogPost.tsx` to route diff/patch language blocks to DiffCodeBlock
- Added diff block CSS styles to `global.css`
- Added `vendor-diffs` chunk to Vite config for code splitting
- Updated `files.md` with DiffCodeBlock documentation
## [2.12.0] - 2026-01-07
### Fixed
- Canonical URL mismatch between raw and rendered HTML (GitHub Issue #6)
- Raw HTML was showing homepage canonical URL instead of page-specific canonical
- Added search engine bot detection to serve pre-rendered HTML with correct canonical URLs
- Search engines (Google, Bing, DuckDuckGo, etc.) now receive correct canonical tags in initial HTML
### Added
- SEO Bot Configuration section in FORK_CONFIG.md for developers who fork the app
- SEO and Bot Detection section in setup-guide.md with configuration examples
- `SEARCH_ENGINE_BOTS` array in `netlify/edge-functions/botMeta.ts` for customizable bot detection
- `isSearchEngineBot()` helper function for search engine crawler detection
- Documentation header in botMeta.ts explaining bot detection configuration
### Technical
- Updated `netlify/edge-functions/botMeta.ts`:
- Added configuration documentation header explaining three bot categories
- Added SEARCH_ENGINE_BOTS array (googlebot, bingbot, yandexbot, duckduckbot, baiduspider, sogou, yahoo! slurp, applebot)
- Added isSearchEngineBot() function
- Updated condition to serve pre-rendered HTML to both social preview and search engine bots
## [2.11.0] - 2026-01-06
### Added
- Ask AI header button with RAG-based Q&A about site content
- Header button with sparkle icon (before search button, after social icons)
- Keyboard shortcuts: Cmd+J or Cmd+/ (Mac), Ctrl+J or Ctrl+/ (Windows/Linux)
- Real-time streaming responses via Convex Persistent Text Streaming
- Model selector: Claude Sonnet 4 (default) or GPT-4o
- Markdown rendering with syntax highlighting in responses
- Internal links use React Router for seamless navigation
- Source citations with links to referenced posts/pages
- Copy response button (hover to reveal) for copying AI answers
- Clear chat button to reset conversation
- AskAIConfig in siteConfig.ts for configuration
- `enabled`: Toggle Ask AI feature
- `defaultModel`: Default model ID
- `models`: Array of available models with id, name, and provider
### How It Works
1. User question stored in database with session ID
2. Query converted to embedding using OpenAI text-embedding-ada-002
3. Vector search finds top 5 relevant posts/pages
4. Content sent to selected AI model with RAG system prompt
5. Response streams in real-time with source citations appended
### Technical
- New component: `src/components/AskAIModal.tsx` with StreamingMessage subcomponent
- New file: `convex/askAI.ts` - Session mutations and queries (regular runtime)
- New file: `convex/askAI.node.ts` - HTTP streaming action (Node.js runtime)
- New table: `askAISessions` with question, streamId, model, createdAt, sources fields
- New HTTP endpoint: `/ask-ai-stream` for streaming responses
- Updated `convex/convex.config.ts` with persistentTextStreaming component
- Updated `convex/http.ts` with /ask-ai-stream route and OPTIONS handler
- Updated `src/components/Layout.tsx` with Ask AI button and modal
- Updated `src/styles/global.css` with Ask AI modal styles
### Requirements
- `semanticSearch.enabled: true` in siteConfig (for embeddings)
- `OPENAI_API_KEY` in Convex (for embedding generation)
- `ANTHROPIC_API_KEY` in Convex (for Claude models)
- Run `npm run sync` to generate embeddings for content
## [2.10.2] - 2026-01-06
### Added
- SEO fixes for GitHub Issue #4 (7 issues resolved)
- Canonical URL: Client-side dynamic canonical link tags for posts and pages
- Single H1 per page: Markdown H1s demoted to H2 (`.blog-h1-demoted` class with H1 visual styling)
- DOM order fix: Article loads before sidebar in DOM for SEO (CSS `order` property maintains visual layout)
- X-Robots-Tag: HTTP header added via netlify.toml (`index, follow` for public, `noindex` for dashboard/api)
- Hreflang tags: Self-referencing hreflang (en, x-default) for all pages
- og:url consistency: Uses same canonicalUrl variable as canonical link
- twitter:site meta tag: New TwitterConfig in siteConfig.ts for Twitter Cards
### Technical
- New `TwitterConfig` interface in `src/config/siteConfig.ts` with site and creator fields
- Updated `src/pages/Post.tsx` with SEO meta tags for both posts and pages (canonical, hreflang, og:url, twitter)
- Updated `src/pages/Post.tsx` DOM order: article before sidebar with CSS order for visual positioning
- Updated `src/components/BlogPost.tsx` h1 renderer outputs h2 with `.blog-h1-demoted` class
- Updated `src/styles/global.css` with `.blog-h1-demoted` styling and CSS order properties for sidebar
- Updated `convex/http.ts` generateMetaHtml() with hreflang and twitter:site tags
- Updated `netlify.toml` with X-Robots-Tag headers for public, dashboard, and API routes
- Updated `index.html` with canonical, hreflang, and twitter:site placeholder tags
- Updated `fork-config.json.example` with twitter configuration fields
## [2.10.1] - 2026-01-05
### Added
- Optional semantic search configuration via `siteConfig.semanticSearch`
- New `enabled` toggle (default: `false` to avoid blocking forks without API key)
- When disabled, search modal shows only keyword search (no mode toggle)
- Embedding generation skipped during sync when disabled (saves API costs)
- Existing embeddings preserved in database when disabled (no data loss)
- Tab key shortcut hints hidden when semantic search is disabled
- Dashboard config generator includes semantic search toggle
### Technical
- New `SemanticSearchConfig` interface in `src/config/siteConfig.ts`
- Updated `src/components/SearchModal.tsx` to conditionally render mode toggle
- Updated `scripts/sync-posts.ts` to check config before embedding generation
- Updated `src/pages/Dashboard.tsx` with semantic search config option
- Updated `FORK_CONFIG.md` with semantic search configuration section
- Updated `fork-config.json.example` with semanticSearch option
- Updated documentation: `docs-semantic-search.md`, `docs.md`
## [2.10.0] - 2026-01-05
### Added
- Semantic search using vector embeddings to complement existing keyword search
- Toggle between "Keyword" and "Semantic" modes in search modal (Cmd+K)
- Keyword search: exact word matching via Convex full-text search indexes (instant, free)
- Semantic search: finds content by meaning using OpenAI text-embedding-ada-002 embeddings (~300ms, ~$0.0001/query)
- Similarity scores displayed as percentages (90%+ = very similar, 70-90% = related)
- Graceful fallback: semantic search returns empty results if OPENAI_API_KEY not configured
- Embedding generation during content sync
- Embeddings generated automatically for posts and pages during `npm run sync`
- Title and content combined for embedding generation
- Content truncated to 8000 characters to stay within token limits
- New documentation pages
- `docs-search.md`: Keyword search implementation with ASCII flowchart
- `docs-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 `embedding` field (optional float64 array) to posts and pages tables in schema
- Added `by_embedding` vector index (1536 dimensions, filterFields: ["published"]) to posts and pages
- Updated `src/components/SearchModal.tsx` with mode toggle (TextAa/Brain icons) and semantic search integration
- Updated `scripts/sync-posts.ts` to call `generateMissingEmbeddings` after content sync
- Added search mode toggle CSS styles (.search-mode-toggle, .search-mode-btn)
### Environment Variables
- `OPENAI_API_KEY`: Required for semantic search (set via `npx 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:db` and `npm run export:db:prod`
- 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
### Technical
- New file: `convex/cms.ts` with CRUD mutations for dashboard content
- New file: `convex/importAction.ts` with Firecrawl server-side action
- New file: `scripts/export-db-posts.ts` for bulk markdown export
- Added `source` field (optional union: "dashboard" | "sync") to posts and pages tables
- Added `by_source` index 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-collapsed` CSS rule (grid-template-columns: 220px 1fr 56px)
- Added `.write-layout.sidebar-collapsed.frontmatter-collapsed` CSS 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.yaml` handling for all example URLs
- Logo gallery hrefs now use relative URLs instead of hardcoded markdown.fast URLs
- Links like `/how-to-use-firecrawl`, `/docs`, `/setup-guide` work on any forked site
- Updated `fork-config.json.example` with missing options (statsPage, mcpServer, imageLightbox)
### Technical
- Updated `scripts/configure-fork.ts` with new update functions: `updateDocsPageTsx()`, `updateMcpEdgeFunction()`, `updateSendNewsletter()`
- Updated `FORK_CONFIG.md` with complete file list and updated AI agent prompt
- Updated `content/blog/fork-configuration-guide.md` with 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.ts` hook with polling mechanism to wait for content load
- Updated `src/components/SearchModal.tsx` to pass search query via `?q=` URL parameter
- Updated `src/components/BlogPost.tsx` with article ref for highlighting
- Updated `src/pages/Post.tsx` to defer scroll handling to highlighting hook when `?q=` present
- Added `.search-highlight` and `.search-highlight-active` CSS styles with theme-specific colors
## [2.8.4] - 2026-01-03
### Changed
- AI service links (ChatGPT, Claude, Perplexity) now use local `/raw/{slug}.md` URLs instead of GitHub raw URLs
- Simplified AI prompt from multi-line instructions to "Read this URL and summarize it:"
### Technical
- Updated `src/components/CopyPageDropdown.tsx` to construct URLs using `window.location.origin`
- Removed unused `siteConfig` import and `getGitHubRawUrl` function
## [2.8.3] - 2026-01-03
### Changed
- `raw/index.md` now includes home.md and footer.md content
- Home intro content from `content/pages/home.md` (slug: home-intro) displays at top
- Footer content from `content/pages/footer.md` (slug: footer) displays at bottom
- Mirrors the actual homepage structure for AI agents reading raw markdown
- Falls back to generic message if home-intro page not found
### Technical
- Updated `generateHomepageIndex` function in `scripts/sync-posts.ts`
- Finds home-intro and footer pages from published pages array
- Adds horizontal rule separators between sections
## [2.8.2] - 2026-01-03
### Fixed
- Footer not displaying on docs section posts/pages even with `showFooter: true` in 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?.content` pattern
## [2.8.1] - 2026-01-03
### Changed
- Centralized `defaultTheme` configuration in `siteConfig.ts`
- Theme is now configured via `defaultTheme` field in siteConfig instead of ThemeContext.tsx
- ThemeContext.tsx now imports and uses `siteConfig.defaultTheme` with fallback to "tan"
- Fork configuration script (`configure-fork.ts`) now updates siteConfig.ts for theme changes
- Backward compatible: existing sites work without changes
### Technical
- Added `Theme` type export to `src/config/siteConfig.ts`
- Added `defaultTheme?: Theme` field to SiteConfig interface
- Updated `src/context/ThemeContext.tsx` to import from siteConfig
- Renamed `updateThemeContext` to `updateThemeConfig` in `scripts/configure-fork.ts`
- Updated documentation: `docs.md`, `setup-guide.md`, `FORK_CONFIG.md`, `fork-configuration-guide.md`
## [2.8.0] - 2026-01-03
### Added
- `docsSectionGroupIcon` frontmatter 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.ts` to include `docsSectionGroupIcon` field in posts and pages tables
- Updated `convex/posts.ts` and `convex/pages.ts` queries and mutations to handle `docsSectionGroupIcon`
- Updated `scripts/sync-posts.ts` to parse `docsSectionGroupIcon` from frontmatter
- Updated `src/components/DocsSidebar.tsx` with Phosphor icon imports and rendering
- Added CSS styles for `.docs-sidebar-group-icon` in `src/styles/global.css`
- Updated `.claude/skills/frontmatter.md` with icon documentation and supported icon list
## [2.7.0] - 2026-01-02
### Added
- `docsSectionGroupOrder` frontmatter field for controlling docs sidebar group order
- Groups are sorted by the minimum `docsSectionGroupOrder` value among items in each group
- Lower numbers appear first, groups without this field sort alphabetically
- Works alongside `docsSection`, `docsSectionGroup`, and `docsSectionOrder` fields
### Technical
- Updated `convex/schema.ts` to include `docsSectionGroupOrder` field in posts and pages tables
- Updated `convex/posts.ts` and `convex/pages.ts` queries and mutations to handle `docsSectionGroupOrder`
- Updated `scripts/sync-posts.ts` to parse `docsSectionGroupOrder` from frontmatter
- Updated `src/components/DocsSidebar.tsx` to sort groups by `docsSectionGroupOrder`
## [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 `aiDashboard` configuration in siteConfig
- `enableImageGeneration`: Toggle image generation tab
- `defaultTextModel`: Set default AI model for chat
- `textModels`: Configure available text chat models
- `imageModels`: Configure available image generation models
### Technical
- Updated `convex/aiChatActions.ts` to support multiple AI providers
- Added `callAnthropicApi`, `callOpenAIApi`, `callGeminiApi` helper functions
- Added `getProviderFromModel` to determine provider from model ID
- Added `getApiKeyForProvider` for lazy API key retrieval
- Added `getNotConfiguredMessage` for provider-specific setup instructions
- Updated `src/components/AIChatView.tsx` with `selectedModel` prop
- Updated `src/pages/Dashboard.tsx` with new `AIAgentSection`
- 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-tab` for tab navigation
- `.ai-model-selector`, `.ai-model-dropdown` for model selection
- `.ai-aspect-ratio-selector` for aspect ratio options
- `.ai-generated-image`, `.ai-image-error`, `.ai-image-loading` for image display
### Environment Variables
- `ANTHROPIC_API_KEY`: Required for Claude models
- `OPENAI_API_KEY`: Required for GPT-4o
- `GOOGLE_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 `showInHeader` option in `siteConfig.socialFooter` to 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: true` to enable)
### Technical
- Exported `platformIcons` from `SocialFooter.tsx` for reuse in Layout component
- Added social icon rendering in `Layout.tsx` header controls
- Added `.header-social-links` and `.header-social-link` CSS styles in `global.css`
- Updated `SocialFooterConfig` interface with `showInHeader: boolean`
- Added socialFooter support to `configure-fork.ts` script
- 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_DOMAINS` constant in `src/components/BlogPost.tsx`
- Added `iframe` to sanitize schema tagNames with allowed attributes (`src`, `width`, `height`, `allow`, `allowfullscreen`, `frameborder`, `title`, `style`)
- Added custom `iframe` component handler with URL validation against whitelisted domains
- Added `.embed-container` CSS styles to `src/styles/global.css` for responsive embeds
## [2.3.0] - 2025-12-31
### Added
- Author pages at `/author/:authorSlug` with 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 counts
- `getPostsByAuthor`: 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_authorName` index to posts table in `convex/schema.ts`
- New queries in `convex/posts.ts`: `getAllAuthors`, `getPostsByAuthor`
- New component: `src/pages/AuthorPage.tsx` (based on TagPage.tsx pattern)
- Added route `/author/:authorSlug` in `src/App.tsx`
- Updated `src/pages/Post.tsx` to 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 from `homeIntro ?` to `homeIntro === undefined ? null : homeIntro ?`
## [2.2.1] - 2025-12-31
### Fixed
- ES module compatibility for configure-fork.ts
- Fixed `__dirname is not defined` error when running `npm run configure`
- Added `fileURLToPath` import from `url` module
- Created ES module equivalent of `__dirname` using `import.meta.url`
- Script now works correctly with `"type": "module"` in package.json
### 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.md` for managing footer content via markdown sync
- Footer content syncs with `npm run sync` without redeploy needed
- Edit footer text, links, and formatting through markdown instead of code
- Falls back to `siteConfig.footer.defaultContent` when page not found
- Set `showInNav: false` to hide from navigation (page remains accessible via direct URL)
- Supports full markdown including links, paragraphs, and line breaks
### Changed
- `src/pages/Home.tsx`: Fetches footer page by slug "footer" and passes content to Footer component
- `src/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.md` with frontmatter (slug: "footer", showInNav: false)
- Uses existing `api.pages.getPageBySlug` query to fetch footer content
- Pattern matches `home-intro` page 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/` directory
- `frontmatter.md`: Complete frontmatter syntax with all 25+ field options for posts and pages
- `convex.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
- Unlisted posts feature
- New `unlisted` frontmatter field for blog posts
- Set `unlisted: true` to 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
### Technical
- New file: `CLAUDE.md` in project root
- New directory: `.claude/skills/` with three markdown files
- Updated: `scripts/sync-discovery-files.ts` to update CLAUDE.md alongside AGENTS.md and llms.txt
- Updated: `convex/schema.ts` - Added `unlisted` optional 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` - Added `unlisted` to PostFrontmatter and ParsedPost interfaces and parsing logic
- Updated: `src/pages/Write.tsx` - Added `unlisted` to 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 `recordPageView` mutation 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: `ImageLightbox` in `src/components/BlogPost.tsx`
- New interface: `ImageLightboxConfig` in `src/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_TOKEN` environment variable
- Whitelisted commands only (sync, sync:prod, sync:discovery, sync:discovery:prod, sync:all, sync:all:prod)
- Health check endpoint at `/health` for 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-server` command in dashboard sync settings
### 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 `requireAuth` is `false`, dashboard is open access
- When `requireAuth` is `true` and WorkOS is configured, dashboard requires login
- Shows setup instructions if `requireAuth` is `true` but WorkOS is not configured
- Warning banner displayed when authentication is not enabled
- 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 `/dashboard` for 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.ts` settings, generates downloadable config file
- Index HTML editor: View and edit `index.html` content with meta tags, Open Graph, Twitter Cards, JSON-LD
- Analytics: Real-time stats dashboard (clone of `/stats` page, 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 `StatsPageConfig` interface in `siteConfig.ts` with `enabled` and `showInNav` options
- 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)
### 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.md` now 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
- Additional blog styling for home intro
- Lists (`ul`, `ol`, `li`) use `blog-ul`, `blog-ol`, `blog-li` classes
- Blockquotes use `blog-blockquote` class
- Horizontal rules use `blog-hr` class
- Links use `blog-link` class
### 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.md` file for homepage intro/bio text
- Home intro content now syncs with `npm run sync` like 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.bio` if page not found or while loading
- New `textAlign` frontmatter field for pages
- Control text alignment: "left", "center", "right"
- Default: "left" (previously was always centered)
- Used by `home.md` to control home intro alignment
- New `featuredTitle` config 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-intro` and `home-bio` into 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-content` styles)
- Updated: `convex/schema.ts` (added `textAlign` field to pages table)
- Updated: `convex/pages.ts` (added `textAlign` to getPageBySlug and syncPagesPublic)
- Updated: `scripts/sync-posts.ts` (added `textAlign` to PageFrontmatter and ParsedPage)
- Updated: `src/config/siteConfig.ts` (added `featuredTitle` to 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)
- Blog post: "How to Use the MCP Server" with client configuration examples
- MCP Server section in documentation (docs.md)
- MCP configuration in siteConfig.ts (`mcpServer` object)
### Changed
- Updated setup-guide.md with MCP server section
- Added `@modelcontextprotocol/sdk` to 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:send` now calls `scheduleSendPostNewsletter` mutation directly
- New `newsletter:send:stats` command to send weekly stats summary
- Both commands provide clear success/error feedback
- New mutation `scheduleSendStatsSummary` for 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 instructions
- `convex/newsletter.ts`: Added `scheduleSendStatsSummary` mutation
### 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_EMAIL` or `AGENTMAIL_INBOX` as recipient
- Configurable via `siteConfig.newsletterNotifications`
- Admin queries and mutations for newsletter management
- `getAllSubscribers`: Paginated subscriber list with search/filter
- `deleteSubscriber`: Remove subscriber from database
- `getNewsletterStats`: Stats for admin dashboard
- `getPostsForNewsletter`: List of posts with sent status
### Changed
- `convex/newsletter.ts`: Added admin queries (getAllSubscribers, deleteSubscriber, getNewsletterStats, getPostsForNewsletter, getStatsForSummary) and scheduleSendCustomNewsletter mutation
- `convex/newsletterActions.ts`: Added sendWeeklyDigest, notifyNewSubscriber, sendWeeklyStatsSummary, sendCustomNewsletter actions with markdown-to-HTML conversion
- `convex/posts.ts`: Added getRecentPostsInternal query for weekly digest
- `convex/crons.ts`: Added weekly digest (Sunday 9am) and stats summary (Monday 9am) cron jobs
- `src/config/siteConfig.ts`: Added NewsletterAdminConfig, NewsletterNotificationsConfig, WeeklyDigestConfig interfaces
- `src/App.tsx`: Added /newsletter-admin route
- `src/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.socialFooter` in `src/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
- `showSocialFooter` field for posts and pages to override siteConfig defaults
- Set `showSocialFooter: false` to hide on specific posts/pages
- Works like existing `showFooter` field pattern
- Social footer configuration options
- `enabled`: Global toggle for social footer
- `showOnHomepage`, `showOnPosts`, `showOnPages`, `showOnBlogPage`: Per-location visibility
- `socialLinks`: Array of social link objects with platform and URL
- `copyright.siteName`: Site/company name for copyright display
- `copyright.showYear`: Toggle for auto-updating year
### Changed
- `src/config/siteConfig.ts`: Added `SocialLink`, `SocialFooterConfig` interfaces and `socialFooter` configuration
- `convex/schema.ts`: Added `showSocialFooter` optional boolean field to posts and pages tables
- `convex/posts.ts` and `convex/pages.ts`: Updated queries and mutations to include `showSocialFooter` field
- `scripts/sync-posts.ts`: Updated to parse `showSocialFooter` from frontmatter for both posts and pages
- `src/pages/Home.tsx`: Added SocialFooter component below Footer
- `src/pages/Post.tsx`: Added SocialFooter component below Footer for both posts and pages
- `src/pages/Blog.tsx`: Added SocialFooter component below Footer
- `src/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
- `showImageAtTop` frontmatter field for posts and pages
- Set `showImageAtTop: true` to display the `image` field at the top of the post/page above the header
- Image displays full-width with rounded corners above the post header
- Default behavior: if `showImageAtTop` is not set or `false`, 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
### Changed
- `convex/schema.ts`: Added `showImageAtTop` optional boolean field to posts and pages tables
- `scripts/sync-posts.ts`: Updated to parse `showImageAtTop` from frontmatter for both posts and pages
- `convex/posts.ts` and `convex/pages.ts`: Updated queries and mutations to include `showImageAtTop` field
- `src/pages/Post.tsx`: Added conditional rendering to display image at top when `showImageAtTop: true`
- `src/pages/Write.tsx`: Added `showImageAtTop` to POST_FIELDS and PAGE_FIELDS frontmatter reference
- `src/styles/global.css`: Added `.post-header-image` and `.post-header-image-img` styles 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
- `blogFeatured` frontmatter field for posts to mark as featured on blog page
- First `blogFeatured` post displays as hero card with landscape image, tags, date, title, excerpt, author info, and read more link
- Remaining `blogFeatured` posts display in 2-column featured row with excerpts
- Regular (non-featured) posts display in 3-column grid without excerpts
- New `BlogHeroCard` component (`src/components/BlogHeroCard.tsx`) for hero display
- New `getBlogFeaturedPosts` query returns all published posts with `blogFeatured: true` sorted by date
- `PostList` component updated with `columns` prop (2 or 3) and `showExcerpts` prop
- Card images use 16:10 landscape aspect ratio
- Footer support on blog page via `siteConfig.footer.showOnBlogPage`
### Changed
- `convex/schema.ts`: Added `blogFeatured` field to posts table with `by_blogFeatured` index
- `convex/posts.ts`: Added `getBlogFeaturedPosts` query, updated sync mutations to handle `blogFeatured` field
- `scripts/sync-posts.ts`: Updated to parse `blogFeatured` from post frontmatter
- `src/pages/Blog.tsx`: Refactored to display hero, featured row, and regular posts sections
- `src/components/PostList.tsx`: Added `columns` and `showExcerpts` props for layout control
- `src/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-cards` and `blog-page-list` for 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: auto` within its `1fr` grid column
- Works with both two-column (left sidebar only) and three-column (both sidebars) layouts
- Consistent `max-width: 800px` for article content across all sidebar configurations
### Technical
- Updated `.post-article-with-sidebar` in `src/styles/global.css` with auto margins for centering
- Added `padding-right: 48px` to match left padding for balanced spacing
feat: add AI Agent chat integration with Anthropic Claude API Add AI writing assistant (Agent) powered by Anthropic Claude API. Agent can be enabled on Write page (replaces textarea) and optionally in RightSidebar on posts/pages via frontmatter. Features: - AIChatView component with per-page chat history - Page content context support for AI responses - Markdown rendering for AI responses - User-friendly error handling for missing API keys - System prompt configurable via Convex environment variables - Anonymous session authentication using localStorage Environment variables required: - ANTHROPIC_API_KEY (required) - CLAUDE_PROMPT_STYLE, CLAUDE_PROMPT_COMMUNITY, CLAUDE_PROMPT_RULES (optional split prompts) - CLAUDE_SYSTEM_PROMPT (optional single prompt fallback) Configuration: - siteConfig.aiChat.enabledOnWritePage: Enable Agent toggle on /write page - siteConfig.aiChat.enabledOnContent: Allow Agent on posts/pages via frontmatter - Frontmatter aiChat: true (requires rightSidebar: true) Updated files: - src/components/AIChatView.tsx: AI chat interface component - src/components/RightSidebar.tsx: Conditional Agent rendering - src/pages/Write.tsx: Agent mode toggle (title changes to Agent) - convex/aiChats.ts: Chat history queries and mutations - convex/aiChatActions.ts: Claude API integration with error handling - convex/schema.ts: aiChats table with indexes - src/config/siteConfig.ts: AIChatConfig interface - Documentation updated across all files Documentation: - files.md: Updated component descriptions - changelog.md: Added v1.33.0 entry - TASK.md: Marked AI chat tasks as completed - README.md: Added AI Agent Chat section - content/pages/docs.md: Added AI Agent chat documentation - content/blog/setup-guide.md: Added AI Agent chat setup instructions - public/raw/changelog.md: Added v1.33.0 entry
2025-12-26 12:31:33 -08:00
## [1.33.0] - 2025-12-26
### Added
- AI Chat Write Agent integration with Anthropic Claude
- New `AIChatView` component (`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: true` field
- 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
- Convex backend for AI chat
- New `convex/aiChats.ts` with queries and mutations for chat history
- New `convex/aiChatActions.ts` with 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 configuration options
- `siteConfig.aiChat` interface with `enabledOnWritePage` and `enabledOnContent` boolean flags
- Both flags default to false (opt-in feature)
- New `aiChat` frontmatter 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/sdk` dependency 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.homepage` with `type` ("default", "page", or "post"), `slug` (required for page/post), and `originalHomeRoute` (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 `/home` route (or configured `originalHomeRoute`) 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 `homepage` field to `fork-config.json.example`
- Updated `configure-fork.ts` to handle homepage configuration
- Documentation added to `FORK_CONFIG.md` with usage examples
### Changed
- `src/App.tsx`: Conditionally renders Home or Post component based on `siteConfig.homepage` configuration
- `src/pages/Post.tsx`: Added optional `slug`, `isHomepage`, and `homepageType` props to support homepage mode
- `src/config/siteConfig.ts`: Added `HomepageConfig` interface and default homepage configuration
### Technical
- New interface: `HomepageConfig` in `src/config/siteConfig.ts`
- Conditional routing in `App.tsx` checks `homepage.type` and `homepage.slug` to determine homepage component
- Post component accepts optional props for homepage mode (hides back button when `isHomepage` is 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`, or `class` HTML 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`, and `class` attributes 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 `Footer` component (`src/components/Footer.tsx`) that renders markdown content
- Footer content can be set in frontmatter `footer` field (markdown) or use `siteConfig.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 `showFooter` frontmatter field for posts and pages to override siteConfig defaults
- New `footer` frontmatter 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)
### Changed
- Homepage footer section now uses the new `Footer` component 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.ts` now uses `defaultContent` (markdown string) instead of `builtWith`/`createdBy` objects
## [1.30.2] - 2025-12-25
### Fixed
- Right sidebar no longer appears on pages/posts without explicit `rightSidebar: true` in frontmatter
- Changed default behavior: right sidebar is now opt-in only
- Pages like About and Contact now render without the right sidebar as expected
- `CopyPageDropdown` correctly appears in nav bar when right sidebar is disabled
- Logic in `Post.tsx` changed from `(page.rightSidebar ?? true)` to `page.rightSidebar === true`
## [1.30.1] - 2025-12-25
### Fixed
- TypeScript error in `convex/posts.ts` where `rightSidebar` was used in mutation handlers but missing from args validators
- Added `rightSidebar: v.optional(v.boolean())` to `syncPosts` args validator
- Added `rightSidebar: v.optional(v.boolean())` to `syncPostsPublic` args validator
## [1.30.0] - 2025-12-25
### Added
- Right sidebar feature for posts and pages
- New `RightSidebar` component that displays `CopyPageDropdown` in a right sidebar
- Appears at 1135px+ viewport width when enabled
- Controlled by `siteConfig.rightSidebar.enabled` (global toggle)
- Per-post/page control via `rightSidebar: true` frontmatter 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
- Right sidebar configuration in siteConfig
- `rightSidebar.enabled`: Global toggle for right sidebar feature
- `rightSidebar.minWidth`: Minimum viewport width to show sidebar (default: 1135px)
- `rightSidebar` frontmatter 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.enabled` is 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-wrapper` is now left-specific with `margin-left` and right border
- `.post-sidebar-right` has complete independent styles with `margin-right` and 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 layout
- `convex/schema.ts`: Added `rightSidebar` field to posts and pages tables
- `convex/posts.ts` and `convex/pages.ts`: Updated queries and mutations to handle `rightSidebar` field
- `scripts/sync-posts.ts`: Updated parsing logic to include `rightSidebar` frontmatter field
- `src/pages/Write.tsx`: Added `rightSidebar` field 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 `fontFamily` option to `siteConfig.ts` with three options: "serif" (New York), "sans" (system fonts), "monospace" (IBM Plex Mono)
- Created `FontContext.tsx` for 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-family` dynamically updates based on selected font
- 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)
- Fork configuration support for fontFamily
- Added `fontFamily` field to `fork-config.json.example`
- Updated `configure-fork.ts` to handle fontFamily configuration
### Changed
- `src/styles/global.css`: Updated body font-family to use CSS variable `--font-family` with fallback
- `src/main.tsx`: Added FontProvider wrapper around app
- `src/pages/Write.tsx`: Updated font switcher to cycle through serif/sans/monospace options
- `content/blog/setup-guide.md`: Updated font configuration documentation with siteConfig option
- `content/pages/docs.md`: Updated font configuration documentation
### Technical
- New context: `src/context/FontContext.tsx` with `useFont()` 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-wrap` styling via SyntaxHighlighter `customStyle` and `codeTagProps`
- 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, added `textBlockStyle` with wrapping properties
- Updated `src/styles/global.css`: Added `.code-block-text` class for CSS fallback wrapping
## [1.28.1] - 2025-12-25
### Fixed
- RSS feed validation errors resolved
- Standardized all URLs to `www.markdown.fast` across the application
- Fixed `atom:link rel="self"` attribute mismatch that caused RSS validation failures
- Updated `index.html` meta tags (og:url, og:image, twitter:domain, twitter:url, twitter:image, JSON-LD)
- Updated `convex/rss.ts` and `convex/http.ts` SITE_URL constants to use www.markdown.fast
- Updated `public/robots.txt`, `public/openapi.yaml`, and `public/llms.txt` with www URLs
- RSS exclusions already present in `netlify.toml` for botMeta edge function
### 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.ts` script 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 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 `gitHubRepoConfig` to fork-config.json.example
- Updated configure-fork.ts to handle gitHubRepo with backward compatibility
- Legacy githubUsername/githubRepo fields still work
### Changed
- `fork-config.json.example`: Added gitHubRepoConfig object with owner, repo, branch, contentPath
- `scripts/configure-fork.ts`: Added gitHubRepo update logic with legacy field fallback
- `FORK_CONFIG.md`: Added gitHubRepo documentation and sync:discovery command reference
- `files.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 `undefined` to show all posts (no limit)
- 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 `/blog` page
- Can be disabled by setting `enabled: false`
### Changed
- `src/config/siteConfig.ts`: Added `homePostsLimit` and `homePostsReadMore` to `PostsDisplayConfig` interface
- `src/pages/Home.tsx`: Post list now respects `homePostsLimit` configuration and shows "read more" link when applicable
- `src/styles/global.css`: Added styles for `.home-posts-read-more` and `.home-posts-read-more-link` with centered button styling and hover effects
### Technical
- New interface: `HomePostsReadMoreConfig` in `src/config/siteConfig.ts`
- Post limiting logic uses `.slice()` to limit array before passing to `PostList` component
- 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
- 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`: Added `gitHubRepo` configuration for constructing raw GitHub URLs
- `convex/schema.ts`: Added `by_tags` index to posts table for efficient tag queries
- `convex/posts.ts`: Added `getAllTags`, `getPostsByTag`, and `getRelatedPosts` queries
- `convex/http.ts`: Sitemap now includes dynamically generated tag pages
- Updated `content/pages/docs.md` and `content/blog/setup-guide.md` with git push requirement for AI links
### Technical
- New component: `src/pages/TagPage.tsx`
- New route: `/tags/:tag` in `src/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-right` to `box-shadow: inset` for 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
### Technical
- `src/styles/global.css`: Changed `.post-sidebar-wrapper` border from `border-right` to `box-shadow: inset -1px 0 0`
- `src/styles/global.css`: Added scrollbar hiding with `-ms-overflow-style: none`, `scrollbar-width: none`, and `::-webkit-scrollbar`
- `src/styles/global.css`: Added `border-top: 1px solid var(--border-sidebar)` and `border-radius: 8px` to sidebar wrapper
- `src/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: 768px` to `max-width: 1024px`
- Changed desktop hide breakpoint from `min-width: 769px` to `min-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
### Technical
- `src/styles/global.css`: Updated mobile nav controls media query to `max-width: 1024px`
- `src/styles/global.css`: Updated desktop hide media query to `min-width: 1025px`
- `src/styles/global.css`: Updated tablet drawer width breakpoint to `max-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/*.md` files 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/:slug` endpoint
- Removed due to build failures and dependency conflicts
- Static `/raw/*.md` files 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 options
- `netlify.toml`: Removed `/api/raw/*` redirect rule
- `netlify/functions/raw.js`: Deleted Netlify Function file
- `content/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.innerPageLogo` and `siteConfig.logo` configuration
- 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 siteConfig
- `src/pages/Post.tsx`: Removed logo from post navigation (was next to back button)
- `src/pages/Blog.tsx`: Removed logo from blog navigation
- `src/styles/global.css`: Added `.top-nav-logo-link` and `.top-nav-logo` styles, updated `.top-nav` layout to span left-to-right, removed old `.inner-page-logo` styles
## [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
### Technical
- `src/styles/global.css`: Added `--sidebar-alt-bg` CSS variables for each theme, updated `.post-sidebar-wrapper` with 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/plain` with minimal headers for reliable AI ingestion
- Reads from `dist/raw/` (production) or `public/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
### Changed
- AI service links (ChatGPT, Claude, Perplexity) now use `/api/raw/:slug` instead of `/raw/:slug.md`
- Netlify Function endpoint more reliable for AI crawler fetch
- "View as Markdown" menu item still uses `/raw/:slug.md` for browser viewing
### Technical
- `netlify/functions/raw.ts`: New Netlify Function to serve raw markdown
- `netlify.toml`: Added redirect from `/api/raw/*` to the function
- `src/components/CopyPageDropdown.tsx`: AI services use `/api/raw/:slug` endpoint
- `package.json`: Added `@netlify/functions` dev dependency
## [1.24.8] - 2025-12-23
### Fixed
- Raw markdown URL construction now uses `window.location.origin` instead of `props.url`
- Prevents incorrect URLs when `props.url` points 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
### Technical
- `src/components/CopyPageDropdown.tsx`: Changed raw URL construction from `new URL(props.url).origin` to `window.location.origin` with `new URL()` constructor
## [1.24.7] - 2025-12-23
### Fixed
- Removed `Link` header from `/raw/*` endpoints to fix AI crawler fetch failures
- Netlify merges headers, so global `Link` header was being applied to `/raw/*` despite specific block
- Moved `Link` header from global `/*` scope to `/index.html` only
- 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
### Technical
- `netlify.toml`: Removed `Link` from global headers, added specific `/index.html` block, removed `noindex` from `/raw/*`
## [1.24.6] - 2025-12-23
### Added
- Homepage raw markdown index file (`/raw/index.md`)
- Automatically generated during `npm run sync` and `npm 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
### Technical
- Updated `scripts/sync-posts.ts`: Added `generateHomepageIndex()` function to create `index.md` in `public/raw/`
## [1.24.5] - 2025-12-23
### Fixed
- AI crawlers (ChatGPT, Perplexity) can now fetch raw markdown from `/raw/*.md` URLs
- Added explicit `/raw/*` redirect passthrough in `netlify.toml` before SPA fallback
- Expanded `excludedPath` array to cover all static file patterns
- Refactored `botMeta.ts` edge 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
### Technical
- `netlify.toml`: Added `force = true` to `/raw/*` redirect, expanded `excludedPath` array
- `botMeta.ts`: Complete refactor with `SOCIAL_PREVIEW_BOTS` and `AI_CRAWLERS` lists, hard path bypass
## [1.24.4] - 2025-12-23
### Added
- `showInNav` field for pages to control navigation visibility
- Pages can be published and accessible but hidden from navigation menu
- Set `showInNav: false` in page frontmatter to hide from nav
- Defaults to `true` for backwards compatibility (all existing pages show in nav)
- Pages with `showInNav: false` remain:
- 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.showInNav` in siteConfig.ts
- Hardcoded navigation items configuration for React routes
- Add React route pages (like `/stats`, `/write`) to navigation via `siteConfig.hardcodedNavItems`
- Configure navigation order, title, and visibility per route
- Set `showInNav: false` to hide from nav while keeping route accessible
- Navigation combines Blog link, hardcoded nav items, and markdown pages
- All nav items sorted by `order` field (lower = first)
- Example: Configure `/stats` and `/write` routes in `siteConfig.ts`
### Technical
- Updated `convex/schema.ts`: Added optional `showInNav` field to pages table
- Updated `convex/pages.ts`: `getAllPages` query filters out pages where `showInNav === false`
- Updated `scripts/sync-posts.ts`: Parses `showInNav` from page frontmatter
- Updated `src/pages/Write.tsx`: Added `showInNav` field to page template and PAGE_FIELDS reference
- Updated `src/config/siteConfig.ts`: Added `HardcodedNavItem` interface and `hardcodedNavItems` config array
- Updated `src/components/Layout.tsx`: Reads `hardcodedNavItems` from siteConfig and combines with Blog link and pages
### Documentation
- Updated `content/pages/docs.md`: Added `showInNav` to static pages frontmatter table
- Updated `content/blog/setup-guide.md`: Added `showInNav` to 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.enabled` and `siteConfig.innerPageLogo.size`
- Does not affect homepage logo (controlled separately)
- Logo links to homepage when clicked
### Technical
- Updated `src/config/siteConfig.ts`: Added `InnerPageLogoConfig` interface and `innerPageLogo` config 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 `inherit` to match body font from global.css
- Mobile menu TOC links use consistent font sizing with desktop sidebar
- Added CSS variables: `--font-size-mobile-toc-title` and `--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.ts` to 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`: Added `removeCodeBlocks` function 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
### Changed
- Updated `Post.tsx` to handle sidebar layout for both posts and pages
- Updated `Write.tsx` to include `layout` field in blog post frontmatter reference
### Technical
- Updated `convex/schema.ts`: Added optional `layout` field to posts table
- Updated `scripts/sync-posts.ts`: Parses `layout` field from post frontmatter
- Updated `convex/posts.ts`: Includes `layout` field in queries, mutations, and sync operations
- Reuses existing sidebar components and CSS (no new components needed)
### Documentation
- Updated `docs.md`: Added `layout` field to blog posts frontmatter table, updated sidebar layout section
- Updated `setup-guide.md`: Clarified sidebar layout works for both posts and pages
- Updated `how-to-publish.md`: Added `layout` field to frontmatter reference table
## [1.23.0] - 2025-12-23
### Added
- 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-raw` package to allow raw HTML pass-through in react-markdown
- Added `rehype-sanitize` package to strip dangerous tags while allowing safe ones
- Custom sanitize schema allows `details`, `summary` tags and the `open` attribute
- Updated `src/components/BlogPost.tsx` with rehype plugins
- CSS styles for collapsible sections in `src/styles/global.css`
### Documentation
- Updated `markdown-with-code-examples.md` with collapsible section examples
- Updated `docs.md` with collapsible sections documentation
- Updated `files.md` with 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
### Technical
- New utility: `src/utils/extractHeadings.ts` for parsing markdown headings
- New component: `src/components/PageSidebar.tsx` for TOC navigation
- Updated `convex/schema.ts`: Added optional `layout` field to pages table
- Updated `scripts/sync-posts.ts`: Parses `layout` field from page frontmatter
- Updated `convex/pages.ts`: Includes `layout` field 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 `PostList` component to support both list and card view modes
- Updated `Blog.tsx` to include view toggle button and state management
- Updated `siteConfig.ts` with `blogPage.viewMode` and `blogPage.showViewToggle` options
### 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
- 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 to `activeSessions` table (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`: Added `VisitorMapConfig` interface and `visitorMap` config 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-site` repository
- Fetches from GitHub public API (no token required)
- Uses Phosphor GithubLogo icon
- Updates on page load
### 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 `useState` and `useEffect` to `src/pages/Stats.tsx` for GitHub API fetch
- Added `GithubLogo` import from `@phosphor-icons/react`
- Updated `.stats-cards-modern` grid to `repeat(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 `authorName` and `authorImage` frontmatter 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"` and `authorImage: "/images/authors/photo.png"`
- 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 `authorName` and `authorImage` options for both posts and pages
### Technical
- Updated `convex/schema.ts` with authorName and authorImage fields
- Updated `scripts/sync-posts.ts` interfaces and parsing
- Updated `convex/posts.ts` and `convex/pages.ts` queries and mutations
- Updated `src/pages/Post.tsx` to render author info
- Updated `src/pages/Write.tsx` with 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}.md` URLs 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
### Technical
- Renamed `buildUrlFromPageUrl` to `buildUrlFromRawMarkdown` in AIService interface
- Handler builds raw markdown URL from page origin and slug
- Updated prompt text to reference "raw markdown file URL"
feat(fork-config): add automated fork configuration with npm run configure Add a complete fork configuration system that allows users to set up their forked site with a single command or follow manual instructions. ## New files - FORK_CONFIG.md: Comprehensive guide with two setup options - Option 1: Automated JSON config + npm run configure - Option 2: Manual step-by-step instructions with code snippets - AI agent prompt for automated updates - fork-config.json.example: JSON template with all configuration fields - Site info (name, title, description, URL, domain) - GitHub and contact details - Creator section for footer links - Optional feature toggles (logo gallery, GitHub graph, blog page) - Theme selection - scripts/configure-fork.ts: Automated configuration script - Reads fork-config.json and applies changes to all files - Updates 11 configuration files in one command - Type-safe with ForkConfig interface - Detailed console output showing each file updated ## Updated files - package.json: Added configure script (npm run configure) - .gitignore: Added fork-config.json to keep user config local - files.md: Added new fork configuration files - changelog.md: Added v1.18.0 entry - changelog-page.md: Added v1.18.0 section with full details - TASK.md: Updated status and completed tasks - README.md: Replaced Files to Update section with Fork Configuration - content/blog/setup-guide.md: Added Fork Configuration Options section - content/pages/docs.md: Added Fork Configuration section - content/pages/about.md: Added fork configuration mention - content/blog/fork-configuration-guide.md: New featured blog post ## Files updated by configure script | File | What it updates | | ----------------------------------- | -------------------------------------- | | src/config/siteConfig.ts | Site name, bio, GitHub, features | | src/pages/Home.tsx | Intro paragraph, footer links | | src/pages/Post.tsx | SITE_URL, SITE_NAME constants | | convex/http.ts | SITE_URL, SITE_NAME constants | | convex/rss.ts | SITE_URL, SITE_TITLE, SITE_DESCRIPTION | | index.html | Meta tags, JSON-LD, page title | | public/llms.txt | Site info, GitHub link | | public/robots.txt | Sitemap URL | | public/openapi.yaml | Server URL, site name | | public/.well-known/ai-plugin.json | Plugin metadata | | src/context/ThemeContext.tsx | Default theme | ## Usage Automated: cp fork-config.json.example fork-config.json # Edit fork-config.json npm run configure Manual: Follow FORK_CONFIG.md step-by-step guide
2025-12-20 22:15:33 -08:00
## [1.18.0] - 2025-12-20
### Added
- Automated fork configuration with `npm run configure`
- Copy `fork-config.json.example` to `fork-config.json`
- Edit JSON with your site information
- Run `npm run configure` to apply all changes automatically
- Updates all 11 configuration files in one command
- 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.md` comprehensive fork guide
- YAML template for AI agent configuration
- Manual code snippets for each file
- AI agent prompt for automated updates
- `fork-config.json.example` template 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.de` public API
- CSS variables for contribution level colors per theme
- Configuration interface: `GitHubContributionsConfig`
- Set `enabled: false` in 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 to `src/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 `/write` page now match `ThemeToggle.tsx` component
- 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
- `handleTypeChange` now always regenerates template when switching types
## [1.15.0] - 2025-12-21
### Changed
- Redesigned `/write` page 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 `/write` page 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 to `src/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 `:root` selector 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 `image` field 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:type` set to "website"
- Relative image paths resolved to absolute URLs
### Changed
- Renamed `generatePostMetaHtml` to `generateMetaHtml` in `convex/http.ts`
- `/meta/post` endpoint now checks for pages if no post found
- Meta HTML generation accepts optional `image` and `type` parameters
### Technical
- Updated `convex/http.ts` with 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 `/blog` with 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)
- Centralized site configuration in `src/config/siteConfig.ts`
- Moved all site settings from `Home.tsx` to dedicated config file
- Easier to customize when forking
- Flexible post display options
- `displayOnHomepage`: Show posts on the homepage
- `blogPage.enabled`: Show posts on dedicated `/blog` page
- 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 `order` field (lower = first)
- Items without order default to 999 (appear last, alphabetically)
- `Home.tsx` imports siteConfig instead of defining inline
- `Layout.tsx` uses 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 `getStats` to use direct counting until aggregates are fully backfilled
- Ensures accurate stats display even if aggregate backfilling is incomplete
### 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
- `backfillAggregatesChunk` internal mutation handles pagination
- Uses `ctx.scheduler.runAfter` to 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/aggregate` package for TableAggregate
- Three aggregates: totalPageViews, pageViewsByPath, uniqueVisitors
- Backfill mutation for existing page view data
- `stats:backfillAggregates` populates counts from existing data
- Idempotent and safe to run multiple times
### Changed
- `recordPageView` mutation 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
- `getStats` query 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.md` with 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 header
- `public/llms.txt`: Updated site name and description
- `public/.well-known/ai-plugin.json`: Updated name and description for AI plugins
- `public/openapi.yaml`: Updated API title and site name example
- `convex/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 `ScrollToTopConfig` interface
- Exports `defaultScrollToTopConfig` for customization
- Uses passive scroll listener for performance
- 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.md` with 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) or `npm run sync:prod` (production) in `public/raw/` directory
- Each published post and page gets a corresponding static `.md` file
- SEO indexable and accessible to AI agents
- Includes metadata header (type, date, reading time, tags)
- View as Markdown option in CopyPageDropdown
- Opens raw `.md` file in new tab
- Available on all post and page views
- 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
- `image` field 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 `slug` prop for raw file links
- Updated `_redirects` to 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.ts` to generate `public/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:prod` needed
- Updated README, setup-guide, how-to-publish, docs page, about-this-blog
- Renamed `content/pages/changelog.md` to `changelog-page.md` to 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_KEY` in `.env.local`)
- Then sync to dev (`npm run sync`) or prod (`npm run sync:prod`)
- No separate `import:prod` command needed (import creates local files only)
- New API endpoint `/api/export` for 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.txt` with 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.ts` for `/api/export` proxy
- Updated `convex/http.ts` with export endpoint
- Created `public/.well-known/` directory
## [1.5.0] - 2025-12-17
### Added
- Frontmatter-controlled featured items
- Add `featured: true` to any post or page frontmatter
- Use `featuredOrder` to control display order (lower = first)
- Featured items sync instantly with `npm run sync` (no redeploy needed)
- New Convex queries for featured content
- `getFeaturedPosts`: returns posts with `featured: true`
- `getFeaturedPages`: returns pages with `featured: true`
- Schema updates with `featured` and `featuredOrder` fields
- Added `by_featured` index for efficient queries
### Changed
- Home.tsx now queries featured items from Convex instead of siteConfig
- FeaturedCards component uses Convex queries for real-time updates
- Removed hardcoded `featuredItems` and `featuredEssays` from siteConfig
### Technical
- Updated sync script to parse `featured` and `featuredOrder` from frontmatter
- Added index on `featured` field 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 `excerpt` field for posts and pages frontmatter
- Used for card view descriptions
- Falls back to description field for posts
- Expanded `siteConfig` in Home.tsx
- `featuredViewMode`: 'list' or 'cards'
- `showViewToggle`: enable user toggle
- `logoGallery`: 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/react` dependency 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 `/stats` with 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: `pageViews` and `activeSessions`
- 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.ts` proxies `/rss.xml` and `/rss-full.xml`
- `sitemap.ts` proxies `/sitemap.xml`
- `api.ts` proxies `/api/posts` and `/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_URL` from 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.xml` and `/rss-full.xml` (full content for LLMs)
- AI agent discovery with `llms.txt` following llmstxt.org standard
- `robots.txt` with 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