{"id":34553245,"url":"https://github.com/waynesutton/markdown-site","last_synced_at":"2025-12-24T08:17:51.797Z","repository":{"id":328695467,"uuid":"1116360773","full_name":"waynesutton/markdown-site","owner":"waynesutton","description":"A minimalist markdown sync site that's always in sync built with React, Convex, and Vite. Optimized for SEO, AI agents, and LLM discovery.","archived":false,"fork":false,"pushed_at":"2025-12-15T20:52:44.000Z","size":220,"stargazers_count":274,"open_issues_count":1,"forks_count":39,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-17T07:14:01.101Z","etag":null,"topics":["cms","convex","markdown","sync"],"latest_commit_sha":null,"homepage":"https://markdowncms.netlify.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/waynesutton.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-14T17:54:14.000Z","updated_at":"2025-12-17T06:40:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/waynesutton/markdown-site","commit_stats":null,"previous_names":["waynesutton/markdown-site"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/waynesutton/markdown-site","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waynesutton%2Fmarkdown-site","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waynesutton%2Fmarkdown-site/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waynesutton%2Fmarkdown-site/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waynesutton%2Fmarkdown-site/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/waynesutton","download_url":"https://codeload.github.com/waynesutton/markdown-site/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waynesutton%2Fmarkdown-site/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27998479,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-24T02:00:07.193Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cms","convex","markdown","sync"],"created_at":"2025-12-24T08:17:48.373Z","updated_at":"2025-12-24T08:17:51.790Z","avatar_url":"https://github.com/waynesutton.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# markdown \"sync\" framework\n\nAn open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify.\n\nWrite markdown locally, run `npm run sync` (dev) or `npm run sync:prod` (production), and content appears instantly across all connected browsers. Built with React, Convex, and Vite. Optimized for AEO, GEO, and LLM discovery.\n\n**How publishing works:** Write posts in markdown, run `npm run sync` for development or `npm run sync:prod` for production, and they appear on your live site immediately. No rebuild or redeploy needed. Convex handles real-time data sync, so all connected browsers update automatically.\n\n**How versioning works:** Markdown files live in `content/blog/` and `content/pages/`. These are regular files in your git repo. Commit changes, review diffs, roll back like any codebase. The sync command pushes content to Convex.\n\n```bash\n# Edit, commit, sync\ngit add content/blog/my-post.md\ngit commit -m \"Update post\"\nnpm run sync        # dev\nnpm run sync:prod   # production\n```\n\n## Documentation\n\n- **[Setup Guide](https://www.markdown.fast/setup-guide)** - Complete fork and deployment guide\n- **[Configuration Guide](https://www.markdown.fast/fork-configuration-guide)** - Automated or manual fork setup\n- **[Full Documentation](https://www.markdown.fast/docs)** - Docs for all features and configuration\n\n## Fork Configuration\n\nAfter forking this project, you have two options to configure your site:\n\n### Option 1: Automated (Recommended)\n\nRun a single command to configure all files automatically:\n\n```bash\n# Copy the example config\ncp fork-config.json.example fork-config.json\n\n# Edit with your site information\n# Open fork-config.json and update the values\n\n# Apply all changes\nnpm run configure\n```\n\nThis updates all 11 configuration files in one step.\n\n### Option 2: Manual\n\nFollow the step-by-step guide in `FORK_CONFIG.md` to update each file manually. This guide includes code snippets and an AI agent prompt for assistance.\n\n## Features\n\n- Markdown-based blog posts with frontmatter\n- Syntax highlighting for code blocks\n- Four theme options: Dark, Light, Tan (default), Cloud\n- Real-time data with Convex\n- Fully responsive design\n- Real-time analytics at `/stats`\n- Full text search with Command+K shortcut\n- Featured section with list/card view toggle\n- Logo gallery with continuous marquee scroll\n- GitHub contributions graph with year navigation\n- Static raw markdown files at `/raw/{slug}.md`\n- Dedicated blog page with configurable navigation order\n- Markdown writing page at `/write` with frontmatter reference\n\n### SEO and Discovery\n\n- RSS feeds at `/rss.xml` and `/rss-full.xml` (with full content)\n- Dynamic sitemap at `/sitemap.xml`\n- JSON-LD structured data for Google rich results\n- Open Graph and Twitter Card meta tags\n- `robots.txt` with AI crawler rules\n- `llms.txt` for AI agent discovery\n\n### AI and LLM Access\n\n- `/api/posts` - JSON list of all posts for agents\n- `/api/post?slug=xxx` - Single post JSON or markdown\n- `/api/export` - Batch export all posts with full content\n- `/raw/{slug}.md` - Static raw markdown files for each post and page\n- `/rss-full.xml` - Full content RSS for LLM ingestion\n- `/.well-known/ai-plugin.json` - AI plugin manifest\n- `/openapi.yaml` - OpenAPI 3.0 specification\n- Copy Page dropdown for sharing to ChatGPT, Claude, Perplexity (uses raw markdown URLs for better AI parsing)\n\n### Content Import\n\n- Import external URLs as markdown posts using Firecrawl\n- Run `npm run import \u003curl\u003e` to scrape and create draft posts locally\n- Then sync to dev or prod with `npm run sync` or `npm run sync:prod`\n\n## Getting Started\n\n### Prerequisites\n\n- Node.js 18 or higher\n- A Convex account\n\n### Setup\n\n1. Install dependencies:\n\n```bash\nnpm install\n```\n\n2. Initialize Convex:\n\n```bash\nnpx convex dev\n```\n\nThis will create your Convex project and generate the `.env.local` file.\n\n3. Start the development server:\n\n```bash\nnpm run dev\n```\n\n4. Open http://localhost:5173\n\n## Writing Blog Posts\n\nCreate markdown files in `content/blog/` with frontmatter:\n\n## Static Pages (Optional)\n\nCreate optional pages like About, Projects, or Contact in `content/pages/`:\n\n```markdown\n---\ntitle: \"About\"\nslug: \"about\"\npublished: true\norder: 1\n---\n\nYour page content here...\n```\n\nPages appear as navigation links in the top right, next to the theme toggle. The `order` field controls display order (lower numbers first).\n\n```markdown\n---\ntitle: \"Your Post Title\"\ndescription: \"A brief description\"\ndate: \"2025-01-15\"\nslug: \"your-post-slug\"\npublished: true\ntags: [\"tag1\", \"tag2\"]\nreadTime: \"5 min read\"\nimage: \"/images/my-header.png\"\nexcerpt: \"Short text for featured cards\"\n---\n\nYour markdown content here...\n```\n\n## Logo Gallery\n\nThe homepage includes a scrolling logo gallery with sample logos. Configure in `siteConfig`:\n\n### Disable the gallery\n\n```typescript\nlogoGallery: {\n  enabled: false,\n  // ...\n},\n```\n\n## GitHub Contributions Graph\n\nDisplay your GitHub contribution activity on the homepage. Configure in `src/config/siteConfig.ts`:\n\n```typescript\ngitHubContributions: {\n  enabled: true,           // Set to false to hide\n  username: \"yourusername\", // Your GitHub username\n  showYearNavigation: true, // Show arrows to navigate between years\n  linkToProfile: true,      // Click graph to open GitHub profile\n  title: \"GitHub Activity\", // Optional title above the graph\n},\n```\n\n## Visitor Map\n\nDisplay real-time visitor locations on a world map on the stats page. Uses Netlify's built-in geo detection (no third-party API needed). Privacy friendly: only stores city, country, and coordinates. No IP addresses stored.\n\nConfigure in `src/config/siteConfig.ts`:\n\n```typescript\nvisitorMap: {\n  enabled: true,        // Set to false to hide the visitor map\n  title: \"Live Visitors\", // Optional title above the map\n},\n```\n\n## Syncing Posts\n\nPosts are synced to Convex. The sync script reads markdown files from `content/blog/` and `content/pages/`, then uploads them to your Convex database.\n\n### Environment Files\n\n| File                    | Purpose                                                  |\n| ----------------------- | -------------------------------------------------------- |\n| `.env.local`            | Development deployment URL (created by `npx convex dev`) |\n| `.env.production.local` | Production deployment URL (create manually)              |\n\nBoth files are gitignored. Each developer creates their own.\n\n### Sync Commands\n\n| Command             | Target      | When to use                 |\n| ------------------- | ----------- | --------------------------- |\n| `npm run sync`      | Development | Local testing, new posts    |\n| `npm run sync:prod` | Production  | Deploy content to live site |\n\n**Development sync:**\n\n```bash\nnpm run sync\n```\n\n**Production sync:**\n\nFirst, create `.env.production.local` with your production Convex URL:\n\n```\nVITE_CONVEX_URL=https://your-prod-deployment.convex.cloud\n```\n\nThen sync:\n\n```bash\nnpm run sync:prod\n```\n\n## Deployment\n\n### Netlify\n\n[![Netlify Status](https://api.netlify.com/api/v1/badges/d8c4d83d-7486-42de-844b-6f09986dc9aa/deploy-status)](https://app.netlify.com/projects/markdowncms/deploys)\n\nFor detailed setup, see the [Convex Netlify Deployment Guide](https://docs.convex.dev/production/hosting/netlify).\n\n1. Deploy Convex functions to production:\n\n```bash\nnpx convex deploy\n```\n\nNote the production URL (e.g., `https://your-deployment.convex.cloud`).\n\n2. Connect your repository to Netlify\n3. Configure build settings:\n   - Build command: `npm ci --include=dev \u0026\u0026 npx convex deploy --cmd 'npm run build'`\n   - Publish directory: `dist`\n4. Add environment variables in Netlify dashboard:\n   - `CONVEX_DEPLOY_KEY` - Generate from [Convex Dashboard](https://dashboard.convex.dev) \u003e Project Settings \u003e Deploy Key\n   - `VITE_CONVEX_URL` - Your production Convex URL (e.g., `https://your-deployment.convex.cloud`)\n\nThe `CONVEX_DEPLOY_KEY` deploys functions at build time. The `VITE_CONVEX_URL` is required for edge functions (RSS, sitemap, API) to proxy requests at runtime.\n\n**Build issues?** Netlify sets `NODE_ENV=production` which skips devDependencies. The `--include=dev` flag fixes this. See [netlify-deploy-fix.md](./netlify-deploy-fix.md) for detailed troubleshooting.\n\n## Project Structure\n\n```\nmarkdown-site/\n├── content/blog/      # Markdown blog posts\n├── convex/            # Convex backend\n│   ├── http.ts        # HTTP endpoints (sitemap, API, RSS)\n│   ├── posts.ts       # Post queries and mutations\n│   ├── rss.ts         # RSS feed generation\n│   └── schema.ts      # Database schema\n├── netlify/           # Netlify edge functions\n│   └── edge-functions/\n│       ├── rss.ts     # RSS feed proxy\n│       ├── sitemap.ts # Sitemap proxy\n│       ├── api.ts     # API endpoint proxy\n│       └── botMeta.ts # OG crawler detection\n├── public/            # Static assets\n│   ├── images/        # Blog images and OG images\n│   ├── robots.txt     # Crawler rules\n│   └── llms.txt       # AI agent discovery\n├── scripts/           # Build scripts\n└── src/\n    ├── components/    # React components\n    ├── context/       # Theme context\n    ├── pages/         # Page components\n    └── styles/        # Global CSS\n```\n\n## Scripts Reference\n\n| Script                | Description                                    |\n| --------------------- | ---------------------------------------------- |\n| `npm run dev`         | Start Vite dev server                          |\n| `npm run dev:convex`  | Start Convex dev backend                       |\n| `npm run sync`        | Sync posts to dev deployment                   |\n| `npm run sync:prod`   | Sync posts to production deployment            |\n| `npm run import`      | Import URL as local markdown draft (then sync) |\n| `npm run build`       | Build for production                           |\n| `npm run deploy`      | Sync + build (for manual deploys)              |\n| `npm run deploy:prod` | Deploy Convex functions + sync to production   |\n\n## Tech Stack\n\n- React 18\n- TypeScript\n- Vite\n- Convex\n- react-markdown\n- react-syntax-highlighter\n- date-fns\n- lucide-react\n- @phosphor-icons/react\n- Netlify\n\n## Search\n\nPress `Command+K` (Mac) or `Ctrl+K` (Windows/Linux) to open the search modal. The search uses Convex full text search to find posts and pages by title and content.\n\nFeatures:\n\n- Real-time results as you type\n- Keyboard navigation (arrow keys, Enter, Escape)\n- Result snippets with context around matches\n- Distinguishes between posts and pages\n- Works with all four themes\n\nThe search icon appears in the top navigation bar next to the theme toggle.\n\n## Real-time Stats\n\nThe `/stats` page shows real-time analytics powered by Convex:\n\n- **Active visitors**: Current visitors on the site with per-page breakdown\n- **Total page views**: All-time view count\n- **Unique visitors**: Based on anonymous session IDs\n- **Views by page**: List of all pages sorted by view count\n\nStats update automatically via Convex subscriptions. No page refresh needed.\n\nHow it works:\n\n- Page views are recorded as event records (not counters) to avoid write conflicts\n- Active sessions use heartbeat presence (30s interval, 2min timeout)\n- A cron job cleans up stale sessions every 5 minutes\n- No PII stored (only anonymous session UUIDs)\n\n## API Endpoints\n\n| Endpoint                       | Description                         |\n| ------------------------------ | ----------------------------------- |\n| `/stats`                       | Real-time site analytics            |\n| `/rss.xml`                     | RSS feed with post descriptions     |\n| `/rss-full.xml`                | RSS feed with full post content     |\n| `/sitemap.xml`                 | Dynamic XML sitemap                 |\n| `/api/posts`                   | JSON list of all posts              |\n| `/api/post?slug=xxx`           | Single post as JSON                 |\n| `/api/post?slug=xxx\u0026format=md` | Single post as markdown             |\n| `/api/export`                  | Batch export all posts with content |\n| `/meta/post?slug=xxx`          | Open Graph HTML for crawlers        |\n| `/.well-known/ai-plugin.json`  | AI plugin manifest                  |\n| `/openapi.yaml`                | OpenAPI 3.0 specification           |\n| `/llms.txt`                    | AI agent discovery                  |\n\n## Import External Content\n\nUse Firecrawl to import articles from external URLs as markdown posts:\n\n```bash\nnpm run import https://example.com/article\n```\n\nThis will:\n\n1. Scrape the URL using Firecrawl API\n2. Convert to clean markdown\n3. Create a draft post in `content/blog/` locally\n4. Add frontmatter with title, description, and today's date\n\n**Setup:**\n\n1. Get an API key from [firecrawl.dev](https://firecrawl.dev)\n2. Add to `.env.local`:\n\n```\nFIRECRAWL_API_KEY=fc-your-api-key\n```\n\n**Why no `npm run import:prod`?** The import command only creates local markdown files. It does not interact with Convex. After importing, sync to your target environment:\n\n- `npm run sync` for development\n- `npm run sync:prod` for production\n\nImported posts are created as drafts (`published: false`). Review, edit, set `published: true`, then sync.\n\n## Theme Configuration\n\nThe default theme is Tan. Users can cycle through themes using the toggle:\n\n- Dark (Moon icon)\n- Light (Sun icon)\n- Tan (Half icon) - default\n- Cloud (Cloud icon)\n\nTo change the default theme, edit `src/context/ThemeContext.tsx`:\n\n```typescript\nconst DEFAULT_THEME: Theme = \"tan\"; // Change to \"dark\", \"light\", or \"cloud\"\n```\n\n## Font Configuration\n\nThe blog uses a serif font (New York) by default. To switch fonts, edit `src/styles/global.css`:\n\n### Font Sizes\n\nAll font sizes use CSS variables defined in `:root`. Customize sizes by editing the variables:\n\n## Write Page\n\nA public markdown writing page at `/write` (not linked in navigation).\n\nAccess directly at `yourdomain.com/write`. Content is stored in localStorage only (not synced to database). Use it to draft posts, then copy the content to a markdown file in `content/blog/` or `content/pages/` and run `npm run sync`.\n\n## Source\n\nFork this project: [github.com/waynesutton/markdown-site](https://github.com/waynesutton/markdown-site)\n\n## License\n\nThis project is licensed under the [MIT License](https://github.com/waynesutton/markdown-site/blob/main/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaynesutton%2Fmarkdown-site","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwaynesutton%2Fmarkdown-site","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaynesutton%2Fmarkdown-site/lists"}