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
This commit is contained in:
Wayne Sutton
2025-12-20 22:15:33 -08:00
parent e10e1098e9
commit 04d08dbada
25 changed files with 2300 additions and 212 deletions

3
.gitignore vendored
View File

@@ -28,6 +28,9 @@ dist-ssr
.env.local
.env.*.local
# Fork configuration (user-specific)
fork-config.json
# Cursor rules
.cursor/rules/write.mdc

443
FORK_CONFIG.md Normal file
View File

@@ -0,0 +1,443 @@
# Fork Configuration Guide
After forking this repo, update these files with your site information. Choose one of two options:
---
## Option 1: Automated Script (Recommended)
Run a single command to configure all files automatically.
### Step 1: Create your config file
```bash
cp fork-config.json.example fork-config.json
```
The file `fork-config.json` is gitignored, so your configuration stays local and is not committed. The `.example` file remains as a template.
### Step 2: Edit fork-config.json
```json
{
"siteName": "Your Site Name",
"siteTitle": "Your Tagline",
"siteDescription": "A one-sentence description of your site.",
"siteUrl": "https://yoursite.netlify.app",
"siteDomain": "yoursite.netlify.app",
"githubUsername": "yourusername",
"githubRepo": "your-repo-name",
"contactEmail": "you@example.com",
"creator": {
"name": "Your Name",
"twitter": "https://x.com/yourhandle",
"linkedin": "https://www.linkedin.com/in/yourprofile/",
"github": "https://github.com/yourusername"
},
"bio": "Your bio text here.",
"theme": "tan"
}
```
### Step 3: Run the configuration script
```bash
npm run configure
```
This updates all 11 configuration files automatically:
- `src/config/siteConfig.ts`
- `src/pages/Home.tsx`
- `src/pages/Post.tsx`
- `convex/http.ts`
- `convex/rss.ts`
- `index.html`
- `public/llms.txt`
- `public/robots.txt`
- `public/openapi.yaml`
- `public/.well-known/ai-plugin.json`
- `src/context/ThemeContext.tsx`
### Step 4: Review and deploy
```bash
git diff # Review changes
npx convex dev # Start Convex (if not running)
npm run sync # Sync content
npm run dev # Test locally
```
---
## Option 2: Manual Configuration
Edit each file individually following the guide below.
### Files to Update
| File | What to Update |
| ----------------------------------- | -------------------------------------------- |
| `src/config/siteConfig.ts` | Site name, bio, GitHub username, 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 |
---
## Manual Configuration Details
### 1. src/config/siteConfig.ts
Update the main site configuration:
```typescript
export const siteConfig: SiteConfig = {
name: "YOUR SITE NAME",
title: "YOUR TAGLINE",
logo: "/images/logo.svg", // or null to hide
intro: null,
bio: `YOUR BIO TEXT HERE.`,
// Featured section
featuredViewMode: "cards", // 'list' or 'cards'
showViewToggle: true,
// Logo gallery (set enabled: false to hide)
logoGallery: {
enabled: true,
images: [
{ src: "/images/logos/your-logo.svg", href: "https://example.com" },
],
position: "above-footer",
speed: 30,
title: "Built with",
scrolling: false,
maxItems: 4,
},
// GitHub contributions graph
gitHubContributions: {
enabled: true,
username: "YOURUSERNAME",
showYearNavigation: true,
linkToProfile: true,
title: "GitHub Activity",
},
// Blog page
blogPage: {
enabled: true,
showInNav: true,
title: "Blog",
description: "All posts from the blog, sorted by date.",
order: 2,
},
// Posts display
postsDisplay: {
showOnHome: true,
showOnBlogPage: true,
},
links: {
docs: "/setup-guide",
convex: "https://convex.dev",
netlify: "https://netlify.com",
},
};
```
### 2. src/pages/Home.tsx
Update the intro paragraph (lines 96-108):
```tsx
<p className="home-intro">
YOUR SITE DESCRIPTION HERE.{" "}
<a
href="https://github.com/YOURUSERNAME/YOUR-REPO"
target="_blank"
rel="noopener noreferrer"
className="home-text-link"
>
Fork it
</a>
, customize it, ship it.
</p>
```
Update the footer section (lines 203-271):
```tsx
<section className="home-footer">
<p className="home-footer-text">
Built with{" "}
<a href={siteConfig.links.convex} target="_blank" rel="noopener noreferrer">
Convex
</a>{" "}
for real-time sync and deployed on{" "}
<a
href={siteConfig.links.netlify}
target="_blank"
rel="noopener noreferrer"
>
Netlify
</a>
. Read the{" "}
<a
href="https://github.com/YOURUSERNAME/YOUR-REPO"
target="_blank"
rel="noopener noreferrer"
>
project on GitHub
</a>{" "}
to fork and deploy your own. View{" "}
<a href="/stats" className="home-text-link">
real-time site stats
</a>
.
</p>
<p></p>
<br></br>
<p className="home-footer-text">
Created by{" "}
<a
href="https://x.com/YOURHANDLE"
target="_blank"
rel="noopener noreferrer"
>
YOUR NAME
</a>{" "}
with Convex, Cursor, and Claude. Follow on{" "}
<a
href="https://x.com/YOURHANDLE"
target="_blank"
rel="noopener noreferrer"
>
Twitter/X
</a>
,{" "}
<a
href="https://www.linkedin.com/in/YOURPROFILE/"
target="_blank"
rel="noopener noreferrer"
>
LinkedIn
</a>
, and{" "}
<a
href="https://github.com/YOURUSERNAME"
target="_blank"
rel="noopener noreferrer"
>
GitHub
</a>
.
</p>
</section>
```
### 3. src/pages/Post.tsx
Update the site constants (lines 11-13):
```typescript
const SITE_URL = "https://YOURSITE.netlify.app";
const SITE_NAME = "YOUR SITE NAME";
const DEFAULT_OG_IMAGE = "/images/og-default.svg";
```
### 4. convex/http.ts
Update the site configuration (lines 9-10):
```typescript
const SITE_URL = process.env.SITE_URL || "https://YOURSITE.netlify.app";
const SITE_NAME = "YOUR SITE NAME";
```
Also update the `generateMetaHtml` function (lines 233-234):
```typescript
const siteUrl = process.env.SITE_URL || "https://YOURSITE.netlify.app";
const siteName = "YOUR SITE NAME";
```
### 5. convex/rss.ts
Update the RSS configuration (lines 5-8):
```typescript
const SITE_URL = process.env.SITE_URL || "https://YOURSITE.netlify.app";
const SITE_TITLE = "YOUR SITE NAME";
const SITE_DESCRIPTION = "YOUR SITE DESCRIPTION HERE.";
```
### 6. index.html
Update all meta tags and JSON-LD structured data:
```html
<!-- SEO Meta Tags -->
<meta name="description" content="YOUR SITE DESCRIPTION" />
<meta name="author" content="YOUR SITE NAME" />
<!-- Open Graph -->
<meta property="og:title" content="YOUR SITE NAME" />
<meta property="og:description" content="YOUR SITE DESCRIPTION" />
<meta property="og:url" content="https://YOURSITE.netlify.app/" />
<meta property="og:site_name" content="YOUR SITE NAME" />
<meta
property="og:image"
content="https://YOURSITE.netlify.app/images/og-default.svg"
/>
<!-- Twitter Card -->
<meta property="twitter:domain" content="YOURSITE.netlify.app" />
<meta property="twitter:url" content="https://YOURSITE.netlify.app/" />
<meta name="twitter:title" content="YOUR SITE NAME" />
<meta name="twitter:description" content="YOUR SITE DESCRIPTION" />
<meta
name="twitter:image"
content="https://YOURSITE.netlify.app/images/og-default.svg"
/>
<!-- JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "YOUR SITE NAME",
"url": "https://YOURSITE.netlify.app",
"description": "YOUR SITE DESCRIPTION"
}
</script>
<title>YOUR SITE TITLE</title>
```
### 7. public/llms.txt
Update site information:
```
# Site Information
- Name: YOUR SITE NAME
- URL: https://YOURSITE.netlify.app
- Description: YOUR SITE DESCRIPTION
# Links
- GitHub: https://github.com/YOURUSERNAME/YOUR-REPO
```
### 8. public/robots.txt
Update the header and sitemap URL:
```
# robots.txt for YOUR SITE NAME
Sitemap: https://YOURSITE.netlify.app/sitemap.xml
```
### 9. public/openapi.yaml
Update API title and server URL:
```yaml
info:
title: YOUR SITE NAME API
contact:
url: https://github.com/YOURUSERNAME/YOUR-REPO
servers:
- url: https://YOURSITE.netlify.app
```
### 10. public/.well-known/ai-plugin.json
Update plugin metadata:
```json
{
"name_for_human": "YOUR SITE NAME",
"name_for_model": "your_site_name",
"description_for_human": "YOUR SITE DESCRIPTION",
"contact_email": "you@example.com"
}
```
### 11. src/context/ThemeContext.tsx
Change the default theme (line 21):
```typescript
const DEFAULT_THEME: Theme = "tan"; // Options: dark, light, tan, cloud
```
---
## AI Agent Prompt
Copy this prompt to have an AI agent apply all changes:
```
I just forked the markdown-site repo. Please update all configuration files with my site information:
Site Name: [YOUR SITE NAME]
Site Title/Tagline: [YOUR TAGLINE]
Site Description: [YOUR DESCRIPTION]
Site URL: https://[YOURSITE].netlify.app
GitHub Username: [YOURUSERNAME]
GitHub Repo: [YOUR-REPO]
Contact Email: [your@email.com]
Creator Info:
- Name: [YOUR NAME]
- Twitter: https://x.com/[YOURHANDLE]
- LinkedIn: https://www.linkedin.com/in/[YOURPROFILE]/
- GitHub: https://github.com/[YOURUSERNAME]
Update these files:
1. src/config/siteConfig.ts - site name, bio, GitHub username
2. src/pages/Home.tsx - intro paragraph and footer section with all creator links
3. src/pages/Post.tsx - SITE_URL and SITE_NAME constants
4. convex/http.ts - SITE_URL and SITE_NAME constants
5. convex/rss.ts - SITE_URL, SITE_TITLE, SITE_DESCRIPTION
6. index.html - all meta tags, JSON-LD, title
7. public/llms.txt - site info and GitHub link
8. public/robots.txt - header comment and sitemap URL
9. public/openapi.yaml - API title, server URL, contact URL
10. public/.well-known/ai-plugin.json - plugin metadata and contact email
```
---
## After Configuration
1. Run `npx convex dev` to initialize Convex
2. Run `npm run sync` to sync content to development
3. Run `npm run dev` to test locally
4. Deploy to Netlify when ready
---
## Optional: Content Files
Replace example content in:
| File | Purpose |
| ------------------------------ | -------------------------- |
| `content/blog/*.md` | Blog posts |
| `content/pages/*.md` | Static pages (About, etc.) |
| `public/images/logo.svg` | Site logo |
| `public/images/og-default.svg` | Default social share image |
| `public/images/logos/*.svg` | Logo gallery images |

View File

@@ -16,6 +16,49 @@ npm run sync # dev
npm run sync:prod # production
```
## Fork Configuration
After forking this project, you have two options to configure your site:
### Option 1: Automated (Recommended)
Run a single command to configure all files automatically:
```bash
# Copy the example config
cp fork-config.json.example fork-config.json
# Edit with your site information
# Open fork-config.json and update the values
# Apply all changes
npm run configure
```
This updates all 11 configuration files in one step.
### Option 2: Manual
Follow 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.
### Files Updated
| File | What to update |
| ----------------------------------- | --------------------------------------------------------------------------- |
| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions |
| `src/pages/Home.tsx` | Intro paragraph text, footer links |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` |
| `index.html` | Title, meta description, OG tags, JSON-LD |
| `public/llms.txt` | Site name, URL, description, topics |
| `public/robots.txt` | Sitemap URL and header comment |
| `public/openapi.yaml` | API title, server URL, site name |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
| `src/context/ThemeContext.tsx` | Default theme |
See `FORK_CONFIG.md` for detailed configuration examples and the full JSON schema.
## Features
- Markdown-based blog posts with frontmatter
@@ -58,25 +101,6 @@ npm run sync:prod # production
- Run `npm run import <url>` to scrape and create draft posts locally
- Then sync to dev or prod with `npm run sync` or `npm run sync:prod`
## Files to Update When Forking
When you fork this project, update these files with your site information:
| File | What to update |
| ----------------------------------- | ----------------------------------------------------------- |
| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions |
| `src/pages/Home.tsx` | Intro paragraph text (hardcoded JSX) |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) |
| `index.html` | Title, meta description, OG tags, JSON-LD |
| `public/llms.txt` | Site name, URL, description, topics |
| `public/robots.txt` | Sitemap URL and header comment |
| `public/openapi.yaml` | API title, server URL, site name in examples |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
See the [Setup Guide](/setup-guide) for detailed configuration examples.
## Getting Started
### Prerequisites

View File

@@ -3,14 +3,19 @@
## To Do
- [ ] create a ui site config page
- [ ] create a prompt formator or checklidst or skill or agent to change everything at once after forking
## Current Status
v1.17.0 deployed. Added GitHub contributions graph on homepage with theme-aware colors, year navigation, and configurable display options.
v1.18.0 deployed. Added automated fork configuration with `npm run configure` command and comprehensive fork setup guide.
## Completed
- [x] Automated fork configuration with npm run configure
- [x] FORK_CONFIG.md comprehensive guide with two options (automated + manual)
- [x] fork-config.json.example template with all configuration options
- [x] scripts/configure-fork.ts for automated updates
- [x] Updates all 11 configuration files in one command
- [x] GitHub contributions graph on homepage with theme-aware colors
- [x] Year navigation with Phosphor icons (CaretLeft, CaretRight)
- [x] Click graph to visit GitHub profile

View File

@@ -4,6 +4,37 @@ 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/).
## [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

View File

@@ -0,0 +1,172 @@
---
title: "Configure your fork in one command"
description: "Two options to set up your forked markdown framework: automated JSON config with npm run configure, or step-by-step manual guide."
date: "2025-12-20"
slug: "fork-configuration-guide"
published: true
tags: ["configuration", "setup", "fork", "tutorial"]
readTime: "4 min read"
featured: true
featuredOrder: 0
image: "/images/forkconfig.png"
excerpt: "Set up your forked site with npm run configure or follow the manual FORK_CONFIG.md guide."
---
# Configure your fork in one command
After forking this markdown framework, you need to update configuration files with your site information. This affects your site name, URLs, RSS feeds, social sharing metadata, and AI discovery files.
Previously this meant editing 10+ files manually. Now you have two options.
## Option 1: Automated configuration
Run a single command to configure everything at once.
### Step 1: Copy the example config
```bash
cp fork-config.json.example fork-config.json
```
The file `fork-config.json` is gitignored, so your site configuration stays local and does not get committed. The `.example` file remains in the repo as a template for future forks.
### Step 2: Edit the JSON file
Open `fork-config.json` and update the values:
```json
{
"siteName": "Your Site Name",
"siteTitle": "Your Tagline",
"siteDescription": "A one-sentence description of your site.",
"siteUrl": "https://yoursite.netlify.app",
"siteDomain": "yoursite.netlify.app",
"githubUsername": "yourusername",
"githubRepo": "your-repo-name",
"contactEmail": "you@example.com",
"creator": {
"name": "Your Name",
"twitter": "https://x.com/yourhandle",
"linkedin": "https://www.linkedin.com/in/yourprofile/",
"github": "https://github.com/yourusername"
},
"bio": "Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents.",
"theme": "tan"
}
```
### Step 3: Run the configure script
```bash
npm run configure
```
The script reads your JSON file and updates all 11 configuration files automatically. You should see output like:
```
Fork Configuration Script
=========================
Reading config from fork-config.json...
Updating src/config/siteConfig.ts...
Updating src/pages/Home.tsx...
Updating src/pages/Post.tsx...
Updating convex/http.ts...
Updating convex/rss.ts...
Updating index.html...
Updating public/llms.txt...
Updating public/robots.txt...
Updating public/openapi.yaml...
Updating public/.well-known/ai-plugin.json...
Updating src/context/ThemeContext.tsx...
Configuration complete!
```
## Option 2: Manual configuration
If you prefer to update files manually, follow the guide in `FORK_CONFIG.md`. It includes:
- Code snippets for each configuration file
- Line numbers and exact locations to update
- An AI agent prompt to paste into Claude or ChatGPT for assisted configuration
## What gets updated
The configuration script updates these files:
| File | What changes |
| ----------------------------------- | ----------------------------------------- |
| `src/config/siteConfig.ts` | Site name, bio, GitHub username, 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 |
## Optional settings
The JSON config file supports additional options:
```json
{
"logoGallery": {
"enabled": true,
"title": "Built with",
"scrolling": false,
"maxItems": 4
},
"gitHubContributions": {
"enabled": true,
"showYearNavigation": true,
"linkToProfile": true,
"title": "GitHub Activity"
},
"blogPage": {
"enabled": true,
"showInNav": true,
"title": "Blog",
"description": "Latest posts",
"order": 0
},
"postsDisplay": {
"showOnHome": true,
"showOnBlogPage": true
},
"featuredViewMode": "cards",
"showViewToggle": true,
"theme": "tan"
}
```
These are optional. If you omit them, the script uses sensible defaults.
## After configuring
Once configuration is complete:
1. **Deploy Convex functions**: Run `npx convex deploy` to push the updated backend files
2. **Sync your content**: Run `npm run sync` for development or `npm run sync:prod` for production
3. **Test locally**: Run `npm run dev` and verify your site name, footer, and metadata
4. **Push to git**: Commit all changes and push to trigger a Netlify rebuild
## Existing content
The configuration script only updates site-level settings. It does not modify your markdown content in `content/blog/` or `content/pages/`. Your existing posts and pages remain unchanged.
If you want to clear the sample content, delete the markdown files in those directories before syncing.
## Summary
Two options after forking:
1. **Automated**: `cp fork-config.json.example fork-config.json`, edit JSON, run `npm run configure`
2. **Manual**: Follow `FORK_CONFIG.md` step-by-step or paste the AI prompt into Claude/ChatGPT
Both approaches update the same 11 files. The automated option takes about 30 seconds. The manual option gives you more control over each change.
Fork it, configure it, ship it.

View File

@@ -6,7 +6,7 @@ slug: "markdown-with-code-examples"
published: true
tags: ["markdown", "tutorial", "code"]
readTime: "5 min read"
featured: true
featured: false
featuredOrder: 5
image: "/images/markdown.png"
---

View File

@@ -1,103 +1,223 @@
---
title: "v1.7 to v1.10 - Mobile menu, scroll-to-top, and fork configuration"
description: "New features for mobile navigation, scroll-to-top button, fork configuration documentation, sharing content with AI tools, and improved table styling."
title: "v1.18.0 release: 12 versions of shipping"
description: "Everything new from v1.7 to v1.18.0. Automated fork setup, GitHub contributions graph, write page, mobile menu, aggregates, and more."
date: "2025-12-20"
slug: "raw-markdown-and-copy-improvements"
published: true
tags: ["features", "markdown", "updates", "mobile", "configuration"]
readTime: "5 min read"
tags: ["release", "features", "updates", "developer-tools"]
readTime: "8 min read"
featured: true
featuredOrder: 1
featuredOrder: 2
image: "/images/v17.png"
excerpt: "Mobile menu, scroll-to-top button, fork configuration, raw markdown files, and Generate Skill for AI agents."
excerpt: "12 versions of new features: automated fork config, GitHub graph, write page, mobile menu, stats aggregates, and more."
---
## Fork configuration (v1.10.0)
## What shipped from v1.7 to v1.18
When you fork this project, update these files with your site information:
This post covers 12 versions of updates to the markdown sync framework. From raw markdown files to automated fork configuration, here is everything that changed.
| File | What to update |
| ----------------------------------- | ----------------------------------------------------------- |
| `src/pages/Home.tsx` | Site name, title, intro, bio, featured config, logo gallery |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME` (API responses, sitemap) |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) |
| `index.html` | Title, meta description, OG tags, JSON-LD |
| `public/llms.txt` | Site name, URL, description |
| `public/robots.txt` | Sitemap URL |
| `public/openapi.yaml` | Server URL, site name in examples |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
## Automated fork configuration (v1.18.0)
These constants affect RSS feeds, API responses, sitemaps, and social sharing metadata.
Fork setup now takes one command:
```bash
cp fork-config.json.example fork-config.json
# Edit fork-config.json with your site info
npm run configure
```
The configure script updates all 11 configuration files:
| 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 |
Two options for fork setup:
1. **Automated** (recommended): JSON config file + `npm run configure`
2. **Manual**: Follow step-by-step instructions in `FORK_CONFIG.md`
## GitHub contributions graph (v1.17.0)
The homepage now displays a GitHub-style contribution graph. Configure it in siteConfig:
```typescript
gitHubContributions: {
enabled: true,
username: "your-github-username",
showYearNavigation: true,
linkToProfile: true,
title: "Contributions",
}
```
Features:
- Theme-aware colors (dark, light, tan, cloud each have unique palettes)
- Year navigation with Phosphor CaretLeft/CaretRight icons
- Click the graph to visit the GitHub profile
- Uses public API (no GitHub token required)
- Mobile responsive with scaled cells
## Write page (v1.13.0 to v1.16.0)
A markdown writing page now lives at `/write`. Not linked in navigation. Access it directly.
Three-column layout:
- **Left sidebar**: Home link, content type selector (Blog Post/Page), actions (Clear, Theme, Font)
- **Center**: Full-height writing area with Copy All button
- **Right sidebar**: Frontmatter reference with per-field copy buttons
Features across iterations:
- Font switcher toggles between Serif and Sans-serif
- Theme toggle matches the rest of the app
- localStorage persistence for content, type, and font preference
- Word, line, and character counts in status bar
- Warning banner about refresh losing content
- Works with Grammarly and browser spellcheck
- Mobile responsive with stacked layout
The write page does not connect to Convex. It stores content locally. Copy your markdown and paste it into a file in `content/blog/` or `content/pages/`, then run `npm run sync`.
## Stats aggregates (v1.11.0)
The stats page now uses O(log n) aggregate counts instead of O(n) table scans.
Before: Every stats query scanned the entire pageViews table.
After: Three TableAggregate instances provide pre-computed counts:
- `totalPageViews`: Global view count
- `pageViewsByPath`: Per-page view counts
- `uniqueVisitors`: Distinct session count
Run the backfill after deploying:
```bash
npx convex run stats:backfillAggregates
```
The backfill processes 500 records at a time to stay under the 16MB Convex memory limit. It schedules itself to continue until complete.
Stats queries now respond consistently fast regardless of how many page views exist.
## Dedicated blog page (v1.12.0)
A dedicated `/blog` page now exists. Configure it in siteConfig:
```typescript
blogPage: {
enabled: true, // Enable /blog route
showInNav: true, // Show in navigation
title: "Blog", // Page title
order: 0, // Nav order (lower = first)
},
displayOnHomepage: true, // Also show posts on homepage
```
Navigation combines the Blog link with page links and sorts by order. Set `order: 5` to place Blog after pages with order 0-4.
Centralized configuration now lives in `src/config/siteConfig.ts` instead of scattered across components.
## Fork configuration documentation (v1.10.0)
The docs, setup guide, and README now include a "Files to Update When Forking" section listing all 9 configuration files:
- Frontend: `siteConfig.ts`, `Home.tsx`, `Post.tsx`
- Backend: `http.ts`, `rss.ts`
- HTML: `index.html`
- AI discovery: `llms.txt`, `robots.txt`, `openapi.yaml`, `ai-plugin.json`
## Scroll-to-top button (v1.9.0)
A scroll-to-top button now appears after scrolling 300px. Configure it in `src/components/Layout.tsx`:
A scroll-to-top button appears after scrolling 300px. Configure it in `src/components/Layout.tsx`:
```typescript
const scrollToTopConfig: Partial<ScrollToTopConfig> = {
enabled: true, // Set to false to disable
threshold: 300, // Show after scrolling 300px
enabled: true, // Set false to disable
threshold: 300, // Pixels before showing
smooth: true, // Smooth scroll animation
};
```
The button uses the Phosphor ArrowUp icon and works with all four themes. It uses a passive scroll listener for performance and includes a fade-in animation.
Uses Phosphor ArrowUp icon. Works with all four themes. Passive scroll listener for performance.
## Mobile menu (v1.8.0)
The site now includes a mobile menu with hamburger navigation for smaller screens. On mobile and tablet views, a hamburger icon appears in the top navigation. Tap it to open a slide-out drawer with all page navigation links.
Hamburger navigation for mobile and tablet screens. Tap the icon to open a slide-out drawer with page links.
**Features:**
Features:
- Smooth CSS transform animations
- Keyboard accessible (press Escape to close)
- Keyboard accessible (Escape to close)
- Focus trap for screen reader support
- Home link at the bottom of the drawer
- Auto-closes when navigating to a new page
- Auto-closes when navigating
The desktop navigation remains unchanged. The mobile menu only appears on screens below 1024px.
Desktop navigation stays unchanged. Mobile menu only appears below 1024px.
## Static raw markdown files
## Generate Skill (v1.8.0)
Every published post and page now gets a static `.md` file at `/raw/{slug}.md`. These files are generated automatically when you run `npm run sync`.
The CopyPageDropdown now includes a Generate Skill option. Click to download the current post or page as an AI agent skill file.
**Example URLs:**
The skill file includes:
- Metadata section with title, description, and tags
- When to use section describing scenarios
- Instructions section with full content
Downloads as `{slug}-skill.md`. Use these files to train AI agents or add context to workflows.
## Static raw markdown files (v1.7.0)
Every published post and page now gets a static `.md` file at `/raw/{slug}.md`. Generated during `npm run sync`.
Example URLs:
- `/raw/setup-guide.md`
- `/raw/about.md`
- `/raw/how-to-publish.md`
Each file includes a metadata header with type, date, reading time, and tags. The content matches exactly what you see on the page.
Each file includes a metadata header with type, date, reading time, and tags.
**Use cases:**
Use cases:
- Share raw markdown with AI agents
- Link directly to source content for LLM ingestion
- View the markdown source of any post
## View as Markdown in CopyPageDropdown
## View as Markdown (v1.7.0)
The Copy Page dropdown now includes a "View as Markdown" option. Click it to open the raw `.md` file in a new tab.
The Copy Page dropdown now includes "View as Markdown" to open the raw `.md` file in a new tab.
This joins the existing options:
Other dropdown options:
- Copy page (copies formatted markdown to clipboard)
- Copy page (formatted markdown to clipboard)
- Open in ChatGPT
- Open in Claude
- Open in Perplexity (new)
- Open in Perplexity
## Perplexity integration
## Perplexity integration (v1.7.0)
Perplexity is now available as an AI service option. Click "Open in Perplexity" to send the full article content directly to Perplexity for research and analysis.
Perplexity is now available as an AI service option. Click to send full article content to Perplexity for research and analysis.
Like the other AI options, if the URL gets too long, the content is copied to your clipboard and Perplexity opens in a new tab. Paste to continue.
If the URL gets too long, content copies to clipboard and Perplexity opens in a new tab. Paste to continue.
## Featured images
## Featured images (v1.7.0)
Posts and pages can now include a featured image that displays in the card view on the homepage.
Add to your frontmatter:
Posts and pages can include a featured image that displays in card view:
```yaml
image: "/images/my-thumbnail.png"
@@ -105,11 +225,33 @@ featured: true
featuredOrder: 1
```
The image displays as a square thumbnail above the title in card view. Non-square images are automatically cropped to center. Recommended size: 400x400px minimum (800x800px for retina).
The image displays as a square thumbnail above the title. Non-square images crop to center. Recommended: 400x400px minimum (800x800px for retina).
## Improved markdown table styling
## Open Graph image fix (v1.12.1)
Tables now render with GitHub-style formatting across all four themes:
Posts with `image` in frontmatter now display their specific OG image when shared. Posts without images fall back to `og-default.svg`. Pages now supported with `og:type` set to "website".
Relative image paths (like `/images/v17.png`) resolve to absolute URLs automatically.
## Centralized font sizes (v1.12.2)
All font sizes now use CSS variables in `global.css`:
```css
:root {
--font-size-xs: 12px;
--font-size-sm: 13px;
--font-size-base: 16px;
--font-size-lg: 17px;
/* ... */
}
```
Edit `:root` variables to customize font sizes across the entire site. Mobile responsive overrides at 768px breakpoint.
## Improved table styling (v1.7.0)
Tables render with GitHub-style formatting:
| Feature | Status |
| ------- | ----------------------- |
@@ -118,32 +260,31 @@ Tables now render with GitHub-style formatting across all four themes:
| Hover | Row highlighting |
| Themes | Dark, light, tan, cloud |
Tables adapt to each theme with proper alternating row colors and hover states.
Alternating row colors and hover states adapt to each theme.
## Generate Skill
## Quick reference: sync vs deploy
The CopyPageDropdown now includes a Generate Skill option. Click it to download the current post or page as an AI agent skill file.
| Change | Command | Speed |
| ----------------------- | -------------------------- | -------------- |
| Blog posts | `npm run sync` | Instant |
| Pages | `npm run sync` | Instant |
| Featured items | `npm run sync` | Instant |
| Import external URL | `npm run import` then sync | Instant |
| siteConfig changes | Redeploy | Requires build |
| Logo gallery config | Redeploy | Requires build |
| React components/styles | Redeploy | Requires build |
| Fork configuration | `npm run configure` | Instant |
The skill file includes:
Markdown content syncs instantly via Convex. Source code changes require pushing to GitHub for Netlify to rebuild.
- Metadata section with title, description, and tags
- When to use section describing scenarios for the skill
- Instructions section with the full content
## Upgrade path
The file downloads as `{slug}-skill.md`. Use these skill files to train AI agents or add context to your workflows.
If upgrading from an earlier version:
## Summary
1. Pull the latest code
2. Run `npm install` for new dependencies
3. Run `npx convex dev` to sync schema changes
4. Run `npx convex run stats:backfillAggregates` if using stats
5. Check `siteConfig.ts` for new configuration options
These updates improve navigation, configuration, and sharing with AI tools:
1. **Fork configuration** documentation for all 9 site files
2. **Scroll-to-top button** with configurable threshold
3. **Mobile menu** with slide-out drawer for smaller screens
4. **Raw markdown files** at `/raw/{slug}.md` for direct access
5. **View as Markdown** option in CopyPageDropdown
6. **Perplexity** added alongside ChatGPT and Claude
7. **Generate Skill** for AI agent training
8. **Featured images** for visual card layouts
9. **Better tables** with responsive styling
All features work across all four themes and are mobile responsive. Run `npm run sync` for development and `npm run sync:prod` for production to update your site with these changes.
All features work across all four themes and are mobile responsive.

View File

@@ -408,14 +408,58 @@ Both files are gitignored. Each developer creates their own local environment fi
## Customizing Your Framework
### Files to Update When Forking
### Fork Configuration Options
When you fork this project, update these files with your site information:
After forking, you have two options to configure your site:
#### Option 1: Automated (Recommended)
Run a single command to configure all files automatically:
```bash
# Copy the example config
cp fork-config.json.example fork-config.json
# Edit fork-config.json with your site information
# Then apply all changes
npm run configure
```
The `fork-config.json` file includes:
```json
{
"siteName": "Your Site Name",
"siteTitle": "Your Tagline",
"siteDescription": "Your site description.",
"siteUrl": "https://yoursite.netlify.app",
"siteDomain": "yoursite.netlify.app",
"githubUsername": "yourusername",
"githubRepo": "your-repo-name",
"contactEmail": "you@example.com",
"creator": {
"name": "Your Name",
"twitter": "https://x.com/yourhandle",
"linkedin": "https://www.linkedin.com/in/yourprofile/",
"github": "https://github.com/yourusername"
},
"bio": "Your bio text here.",
"theme": "tan"
}
```
This updates all 11 configuration files in one step. See `FORK_CONFIG.md` for the full JSON schema.
#### Option 2: Manual
Follow the step-by-step guide in `FORK_CONFIG.md` to update each file manually. The guide includes code snippets for each file and an AI agent prompt for assisted configuration.
### Files to Update When Forking
| File | What to update |
| ----------------------------------- | --------------------------------------------------------------------------- |
| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions |
| `src/pages/Home.tsx` | Intro paragraph text (hardcoded JSX) |
| `src/pages/Home.tsx` | Intro paragraph text, footer links |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) |
@@ -424,6 +468,7 @@ When you fork this project, update these files with your site information:
| `public/robots.txt` | Sitemap URL and header comment |
| `public/openapi.yaml` | API title, server URL, site name in examples |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
| `src/context/ThemeContext.tsx` | Default theme |
### Site title and description metadata

View File

@@ -73,4 +73,16 @@ It's a hybrid: developer workflow for publishing + real-time delivery like a dyn
- Projects where AI agents generate content programmatically
- Sites that need real-time updates without full rebuilds
## Fork configuration
After forking, configure your site with a single command:
```bash
cp fork-config.json.example fork-config.json
# Edit fork-config.json
npm run configure
```
Or follow the manual guide in `FORK_CONFIG.md`. Both options update all 11 configuration files with your site information.
Fork it, customize it, ship it.

View File

@@ -7,6 +7,40 @@ order: 5
All notable changes to this project.
## v1.18.0
Released December 20, 2025
**Automated fork configuration**
- New `npm run configure` command for one-step fork setup
- Copy `fork-config.json.example` to `fork-config.json`
- Edit the JSON file with your site information
- Run `npm run configure` to apply all changes automatically
Two options for fork setup:
1. **Automated** (recommended): JSON config file + `npm run configure`
2. **Manual**: Follow step-by-step instructions in `FORK_CONFIG.md`
The configure script updates all 11 configuration files:
| 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 |
New files: `FORK_CONFIG.md`, `fork-config.json.example`, `scripts/configure-fork.ts`
## v1.17.0
Released December 20, 2025

View File

@@ -149,22 +149,39 @@ npm run sync:prod
## Configuration
### Site and backend settings
### Fork configuration
When you fork this project, update these files with your site information:
After forking, you have two options to configure your site:
| File | What to update |
|------|----------------|
| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions |
| `src/pages/Home.tsx` | Intro paragraph text (hardcoded JSX) |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) |
| `index.html` | Title, meta description, OG tags, JSON-LD |
| `public/llms.txt` | Site name, URL, description, topics |
| `public/robots.txt` | Sitemap URL and header comment |
| `public/openapi.yaml` | API title, server URL, site name in examples |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
**Option 1: Automated (Recommended)**
```bash
cp fork-config.json.example fork-config.json
# Edit fork-config.json with your site information
npm run configure
```
This updates all 11 configuration files in one command. See `FORK_CONFIG.md` for the full JSON schema and options.
**Option 2: Manual**
Follow the step-by-step guide in `FORK_CONFIG.md` to update each file manually.
### Files updated by configuration
| File | What to update |
| ----------------------------------- | --------------------------------------------------------------------------- |
| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions |
| `src/pages/Home.tsx` | Intro paragraph text, footer links |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) |
| `index.html` | Title, meta description, OG tags, JSON-LD |
| `public/llms.txt` | Site name, URL, description, topics |
| `public/robots.txt` | Sitemap URL and header comment |
| `public/openapi.yaml` | API title, server URL, site name in examples |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
| `src/context/ThemeContext.tsx` | Default theme |
### Site title and description metadata

View File

@@ -4,18 +4,20 @@ A brief description of each file in the codebase.
## Root Files
| File | Description |
| ---------------- | ---------------------------------------------- |
| `package.json` | Dependencies and scripts for the blog |
| `tsconfig.json` | TypeScript configuration |
| `vite.config.ts` | Vite bundler configuration |
| `index.html` | Main HTML entry with SEO meta tags and JSON-LD |
| `netlify.toml` | Netlify deployment and Convex HTTP redirects |
| `README.md` | Project documentation |
| `AGENTS.md` | AI coding agent instructions (agents.md spec) |
| `files.md` | This file - codebase structure |
| `changelog.md` | Version history and changes |
| `TASK.md` | Task tracking and project status |
| File | Description |
| -------------------------- | ---------------------------------------------------- |
| `package.json` | Dependencies and scripts for the blog |
| `tsconfig.json` | TypeScript configuration |
| `vite.config.ts` | Vite bundler configuration |
| `index.html` | Main HTML entry with SEO meta tags and JSON-LD |
| `netlify.toml` | Netlify deployment and Convex HTTP redirects |
| `README.md` | Project documentation |
| `AGENTS.md` | AI coding agent instructions (agents.md spec) |
| `files.md` | This file - codebase structure |
| `changelog.md` | Version history and changes |
| `TASK.md` | Task tracking and project status |
| `FORK_CONFIG.md` | Fork configuration guide (manual + automated options)|
| `fork-config.json.example` | Template JSON config for automated fork setup |
## Source Files (`src/`)
@@ -142,10 +144,11 @@ Markdown files for static pages like About, Projects, Contact, Changelog.
## Scripts (`scripts/`)
| File | Description |
| --------------- | ------------------------------------------------- |
| `sync-posts.ts` | Syncs markdown files to Convex at build time |
| `import-url.ts` | Imports external URLs as markdown posts (Firecrawl) |
| File | Description |
| -------------------- | ---------------------------------------------------------- |
| `sync-posts.ts` | Syncs markdown files to Convex at build time |
| `import-url.ts` | Imports external URLs as markdown posts (Firecrawl) |
| `configure-fork.ts` | Automated fork configuration (reads fork-config.json) |
## Netlify (`netlify/edge-functions/`)

44
fork-config.json.example Normal file
View File

@@ -0,0 +1,44 @@
{
"siteName": "Your Site Name",
"siteTitle": "Your Tagline",
"siteDescription": "A one-sentence description of your site.",
"siteUrl": "https://yoursite.netlify.app",
"siteDomain": "yoursite.netlify.app",
"githubUsername": "yourusername",
"githubRepo": "your-repo-name",
"contactEmail": "you@example.com",
"creator": {
"name": "Your Name",
"twitter": "https://x.com/yourhandle",
"linkedin": "https://www.linkedin.com/in/yourprofile/",
"github": "https://github.com/yourusername"
},
"bio": "Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents.",
"logoGallery": {
"enabled": true,
"title": "Built with",
"scrolling": false,
"maxItems": 4
},
"gitHubContributions": {
"enabled": true,
"showYearNavigation": true,
"linkToProfile": true,
"title": "GitHub Activity"
},
"blogPage": {
"enabled": true,
"showInNav": true,
"title": "Blog",
"description": "All posts from the blog, sorted by date.",
"order": 2
},
"postsDisplay": {
"showOnHome": true,
"showOnBlogPage": true
},
"featuredViewMode": "cards",
"showViewToggle": true,
"theme": "tan"
}

View File

@@ -13,6 +13,7 @@
"sync": "npx tsx scripts/sync-posts.ts",
"sync:prod": "SYNC_ENV=production npx tsx scripts/sync-posts.ts",
"import": "npx tsx scripts/import-url.ts",
"configure": "npx tsx scripts/configure-fork.ts",
"deploy": "npm run sync && npm run build",
"deploy:prod": "npx convex deploy && npm run sync:prod"
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@@ -72,4 +72,16 @@ It's a hybrid: developer workflow for publishing + real-time delivery like a dyn
- Projects where AI agents generate content programmatically
- Sites that need real-time updates without full rebuilds
## Fork configuration
After forking, configure your site with a single command:
```bash
cp fork-config.json.example fork-config.json
# Edit fork-config.json
npm run configure
```
Or follow the manual guide in `FORK_CONFIG.md`. Both options update all 11 configuration files with your site information.
Fork it, customize it, ship it.

View File

@@ -7,6 +7,40 @@ Date: 2025-12-21
All notable changes to this project.
## v1.18.0
Released December 20, 2025
**Automated fork configuration**
- New `npm run configure` command for one-step fork setup
- Copy `fork-config.json.example` to `fork-config.json`
- Edit the JSON file with your site information
- Run `npm run configure` to apply all changes automatically
Two options for fork setup:
1. **Automated** (recommended): JSON config file + `npm run configure`
2. **Manual**: Follow step-by-step instructions in `FORK_CONFIG.md`
The configure script updates all 11 configuration files:
| 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 |
New files: `FORK_CONFIG.md`, `fork-config.json.example`, `scripts/configure-fork.ts`
## v1.17.0
Released December 20, 2025

View File

@@ -149,22 +149,39 @@ npm run sync:prod
## Configuration
### Site and backend settings
### Fork configuration
When you fork this project, update these files with your site information:
After forking, you have two options to configure your site:
| File | What to update |
|------|----------------|
| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions |
| `src/pages/Home.tsx` | Intro paragraph text (hardcoded JSX) |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) |
| `index.html` | Title, meta description, OG tags, JSON-LD |
| `public/llms.txt` | Site name, URL, description, topics |
| `public/robots.txt` | Sitemap URL and header comment |
| `public/openapi.yaml` | API title, server URL, site name in examples |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
**Option 1: Automated (Recommended)**
```bash
cp fork-config.json.example fork-config.json
# Edit fork-config.json with your site information
npm run configure
```
This updates all 11 configuration files in one command. See `FORK_CONFIG.md` for the full JSON schema and options.
**Option 2: Manual**
Follow the step-by-step guide in `FORK_CONFIG.md` to update each file manually.
### Files updated by configuration
| File | What to update |
| ----------------------------------- | --------------------------------------------------------------------------- |
| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions |
| `src/pages/Home.tsx` | Intro paragraph text, footer links |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) |
| `index.html` | Title, meta description, OG tags, JSON-LD |
| `public/llms.txt` | Site name, URL, description, topics |
| `public/robots.txt` | Sitemap URL and header comment |
| `public/openapi.yaml` | API title, server URL, site name in examples |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
| `src/context/ThemeContext.tsx` | Default theme |
### Site title and description metadata

View File

@@ -0,0 +1,169 @@
# Configure your fork in one command
> Two options to set up your forked markdown framework: automated JSON config with npm run configure, or step-by-step manual guide.
---
Type: post
Date: 2025-12-20
Reading time: 4 min read
Tags: configuration, setup, fork, tutorial
---
# Configure your fork in one command
After forking this markdown framework, you need to update configuration files with your site information. This affects your site name, URLs, RSS feeds, social sharing metadata, and AI discovery files.
Previously this meant editing 10+ files manually. Now you have two options.
## Option 1: Automated configuration
Run a single command to configure everything at once.
### Step 1: Copy the example config
```bash
cp fork-config.json.example fork-config.json
```
The file `fork-config.json` is gitignored, so your site configuration stays local and does not get committed. The `.example` file remains in the repo as a template for future forks.
### Step 2: Edit the JSON file
Open `fork-config.json` and update the values:
```json
{
"siteName": "Your Site Name",
"siteTitle": "Your Tagline",
"siteDescription": "A one-sentence description of your site.",
"siteUrl": "https://yoursite.netlify.app",
"siteDomain": "yoursite.netlify.app",
"githubUsername": "yourusername",
"githubRepo": "your-repo-name",
"contactEmail": "you@example.com",
"creator": {
"name": "Your Name",
"twitter": "https://x.com/yourhandle",
"linkedin": "https://www.linkedin.com/in/yourprofile/",
"github": "https://github.com/yourusername"
},
"bio": "Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents.",
"theme": "tan"
}
```
### Step 3: Run the configure script
```bash
npm run configure
```
The script reads your JSON file and updates all 11 configuration files automatically. You should see output like:
```
Fork Configuration Script
=========================
Reading config from fork-config.json...
Updating src/config/siteConfig.ts...
Updating src/pages/Home.tsx...
Updating src/pages/Post.tsx...
Updating convex/http.ts...
Updating convex/rss.ts...
Updating index.html...
Updating public/llms.txt...
Updating public/robots.txt...
Updating public/openapi.yaml...
Updating public/.well-known/ai-plugin.json...
Updating src/context/ThemeContext.tsx...
Configuration complete!
```
## Option 2: Manual configuration
If you prefer to update files manually, follow the guide in `FORK_CONFIG.md`. It includes:
- Code snippets for each configuration file
- Line numbers and exact locations to update
- An AI agent prompt to paste into Claude or ChatGPT for assisted configuration
## What gets updated
The configuration script updates these files:
| File | What changes |
| ----------------------------------- | ----------------------------------------- |
| `src/config/siteConfig.ts` | Site name, bio, GitHub username, 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 |
## Optional settings
The JSON config file supports additional options:
```json
{
"logoGallery": {
"enabled": true,
"title": "Built with",
"scrolling": false,
"maxItems": 4
},
"gitHubContributions": {
"enabled": true,
"showYearNavigation": true,
"linkToProfile": true,
"title": "GitHub Activity"
},
"blogPage": {
"enabled": true,
"showInNav": true,
"title": "Blog",
"description": "Latest posts",
"order": 0
},
"postsDisplay": {
"showOnHome": true,
"showOnBlogPage": true
},
"featuredViewMode": "cards",
"showViewToggle": true,
"theme": "tan"
}
```
These are optional. If you omit them, the script uses sensible defaults.
## After configuring
Once configuration is complete:
1. **Deploy Convex functions**: Run `npx convex deploy` to push the updated backend files
2. **Sync your content**: Run `npm run sync` for development or `npm run sync:prod` for production
3. **Test locally**: Run `npm run dev` and verify your site name, footer, and metadata
4. **Push to git**: Commit all changes and push to trigger a Netlify rebuild
## Existing content
The configuration script only updates site-level settings. It does not modify your markdown content in `content/blog/` or `content/pages/`. Your existing posts and pages remain unchanged.
If you want to clear the sample content, delete the markdown files in those directories before syncing.
## Summary
Two options after forking:
1. **Automated**: `cp fork-config.json.example fork-config.json`, edit JSON, run `npm run configure`
2. **Manual**: Follow `FORK_CONFIG.md` step-by-step or paste the AI prompt into Claude/ChatGPT
Both approaches update the same 11 files. The automated option takes about 30 seconds. The manual option gives you more control over each change.
Fork it, configure it, ship it.

View File

@@ -1,100 +1,220 @@
# v1.7 to v1.10 - Mobile menu, scroll-to-top, and fork configuration
# v1.18.0 release: 12 versions of shipping
> New features for mobile navigation, scroll-to-top button, fork configuration documentation, sharing content with AI tools, and improved table styling.
> Everything new from v1.7 to v1.18.0. Automated fork setup, GitHub contributions graph, write page, mobile menu, aggregates, and more.
---
Type: post
Date: 2025-12-20
Reading time: 5 min read
Tags: features, markdown, updates, mobile, configuration
Reading time: 8 min read
Tags: release, features, updates, developer-tools
---
## Fork configuration (v1.10.0)
## What shipped from v1.7 to v1.18
When you fork this project, update these files with your site information:
This post covers 12 versions of updates to the markdown sync framework. From raw markdown files to automated fork configuration, here is everything that changed.
| File | What to update |
| ----------------------------------- | ----------------------------------------------------------- |
| `src/pages/Home.tsx` | Site name, title, intro, bio, featured config, logo gallery |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME` (API responses, sitemap) |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) |
| `index.html` | Title, meta description, OG tags, JSON-LD |
| `public/llms.txt` | Site name, URL, description |
| `public/robots.txt` | Sitemap URL |
| `public/openapi.yaml` | Server URL, site name in examples |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
## Automated fork configuration (v1.18.0)
These constants affect RSS feeds, API responses, sitemaps, and social sharing metadata.
Fork setup now takes one command:
```bash
cp fork-config.json.example fork-config.json
# Edit fork-config.json with your site info
npm run configure
```
The configure script updates all 11 configuration files:
| 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 |
Two options for fork setup:
1. **Automated** (recommended): JSON config file + `npm run configure`
2. **Manual**: Follow step-by-step instructions in `FORK_CONFIG.md`
## GitHub contributions graph (v1.17.0)
The homepage now displays a GitHub-style contribution graph. Configure it in siteConfig:
```typescript
gitHubContributions: {
enabled: true,
username: "your-github-username",
showYearNavigation: true,
linkToProfile: true,
title: "Contributions",
}
```
Features:
- Theme-aware colors (dark, light, tan, cloud each have unique palettes)
- Year navigation with Phosphor CaretLeft/CaretRight icons
- Click the graph to visit the GitHub profile
- Uses public API (no GitHub token required)
- Mobile responsive with scaled cells
## Write page (v1.13.0 to v1.16.0)
A markdown writing page now lives at `/write`. Not linked in navigation. Access it directly.
Three-column layout:
- **Left sidebar**: Home link, content type selector (Blog Post/Page), actions (Clear, Theme, Font)
- **Center**: Full-height writing area with Copy All button
- **Right sidebar**: Frontmatter reference with per-field copy buttons
Features across iterations:
- Font switcher toggles between Serif and Sans-serif
- Theme toggle matches the rest of the app
- localStorage persistence for content, type, and font preference
- Word, line, and character counts in status bar
- Warning banner about refresh losing content
- Works with Grammarly and browser spellcheck
- Mobile responsive with stacked layout
The write page does not connect to Convex. It stores content locally. Copy your markdown and paste it into a file in `content/blog/` or `content/pages/`, then run `npm run sync`.
## Stats aggregates (v1.11.0)
The stats page now uses O(log n) aggregate counts instead of O(n) table scans.
Before: Every stats query scanned the entire pageViews table.
After: Three TableAggregate instances provide pre-computed counts:
- `totalPageViews`: Global view count
- `pageViewsByPath`: Per-page view counts
- `uniqueVisitors`: Distinct session count
Run the backfill after deploying:
```bash
npx convex run stats:backfillAggregates
```
The backfill processes 500 records at a time to stay under the 16MB Convex memory limit. It schedules itself to continue until complete.
Stats queries now respond consistently fast regardless of how many page views exist.
## Dedicated blog page (v1.12.0)
A dedicated `/blog` page now exists. Configure it in siteConfig:
```typescript
blogPage: {
enabled: true, // Enable /blog route
showInNav: true, // Show in navigation
title: "Blog", // Page title
order: 0, // Nav order (lower = first)
},
displayOnHomepage: true, // Also show posts on homepage
```
Navigation combines the Blog link with page links and sorts by order. Set `order: 5` to place Blog after pages with order 0-4.
Centralized configuration now lives in `src/config/siteConfig.ts` instead of scattered across components.
## Fork configuration documentation (v1.10.0)
The docs, setup guide, and README now include a "Files to Update When Forking" section listing all 9 configuration files:
- Frontend: `siteConfig.ts`, `Home.tsx`, `Post.tsx`
- Backend: `http.ts`, `rss.ts`
- HTML: `index.html`
- AI discovery: `llms.txt`, `robots.txt`, `openapi.yaml`, `ai-plugin.json`
## Scroll-to-top button (v1.9.0)
A scroll-to-top button now appears after scrolling 300px. Configure it in `src/components/Layout.tsx`:
A scroll-to-top button appears after scrolling 300px. Configure it in `src/components/Layout.tsx`:
```typescript
const scrollToTopConfig: Partial<ScrollToTopConfig> = {
enabled: true, // Set to false to disable
threshold: 300, // Show after scrolling 300px
enabled: true, // Set false to disable
threshold: 300, // Pixels before showing
smooth: true, // Smooth scroll animation
};
```
The button uses the Phosphor ArrowUp icon and works with all four themes. It uses a passive scroll listener for performance and includes a fade-in animation.
Uses Phosphor ArrowUp icon. Works with all four themes. Passive scroll listener for performance.
## Mobile menu (v1.8.0)
The site now includes a mobile menu with hamburger navigation for smaller screens. On mobile and tablet views, a hamburger icon appears in the top navigation. Tap it to open a slide-out drawer with all page navigation links.
Hamburger navigation for mobile and tablet screens. Tap the icon to open a slide-out drawer with page links.
**Features:**
Features:
- Smooth CSS transform animations
- Keyboard accessible (press Escape to close)
- Keyboard accessible (Escape to close)
- Focus trap for screen reader support
- Home link at the bottom of the drawer
- Auto-closes when navigating to a new page
- Auto-closes when navigating
The desktop navigation remains unchanged. The mobile menu only appears on screens below 1024px.
Desktop navigation stays unchanged. Mobile menu only appears below 1024px.
## Static raw markdown files
## Generate Skill (v1.8.0)
Every published post and page now gets a static `.md` file at `/raw/{slug}.md`. These files are generated automatically when you run `npm run sync`.
The CopyPageDropdown now includes a Generate Skill option. Click to download the current post or page as an AI agent skill file.
**Example URLs:**
The skill file includes:
- Metadata section with title, description, and tags
- When to use section describing scenarios
- Instructions section with full content
Downloads as `{slug}-skill.md`. Use these files to train AI agents or add context to workflows.
## Static raw markdown files (v1.7.0)
Every published post and page now gets a static `.md` file at `/raw/{slug}.md`. Generated during `npm run sync`.
Example URLs:
- `/raw/setup-guide.md`
- `/raw/about.md`
- `/raw/how-to-publish.md`
Each file includes a metadata header with type, date, reading time, and tags. The content matches exactly what you see on the page.
Each file includes a metadata header with type, date, reading time, and tags.
**Use cases:**
Use cases:
- Share raw markdown with AI agents
- Link directly to source content for LLM ingestion
- View the markdown source of any post
## View as Markdown in CopyPageDropdown
## View as Markdown (v1.7.0)
The Copy Page dropdown now includes a "View as Markdown" option. Click it to open the raw `.md` file in a new tab.
The Copy Page dropdown now includes "View as Markdown" to open the raw `.md` file in a new tab.
This joins the existing options:
Other dropdown options:
- Copy page (copies formatted markdown to clipboard)
- Copy page (formatted markdown to clipboard)
- Open in ChatGPT
- Open in Claude
- Open in Perplexity (new)
- Open in Perplexity
## Perplexity integration
## Perplexity integration (v1.7.0)
Perplexity is now available as an AI service option. Click "Open in Perplexity" to send the full article content directly to Perplexity for research and analysis.
Perplexity is now available as an AI service option. Click to send full article content to Perplexity for research and analysis.
Like the other AI options, if the URL gets too long, the content is copied to your clipboard and Perplexity opens in a new tab. Paste to continue.
If the URL gets too long, content copies to clipboard and Perplexity opens in a new tab. Paste to continue.
## Featured images
## Featured images (v1.7.0)
Posts and pages can now include a featured image that displays in the card view on the homepage.
Add to your frontmatter:
Posts and pages can include a featured image that displays in card view:
```yaml
image: "/images/my-thumbnail.png"
@@ -102,11 +222,33 @@ featured: true
featuredOrder: 1
```
The image displays as a square thumbnail above the title in card view. Non-square images are automatically cropped to center. Recommended size: 400x400px minimum (800x800px for retina).
The image displays as a square thumbnail above the title. Non-square images crop to center. Recommended: 400x400px minimum (800x800px for retina).
## Improved markdown table styling
## Open Graph image fix (v1.12.1)
Tables now render with GitHub-style formatting across all four themes:
Posts with `image` in frontmatter now display their specific OG image when shared. Posts without images fall back to `og-default.svg`. Pages now supported with `og:type` set to "website".
Relative image paths (like `/images/v17.png`) resolve to absolute URLs automatically.
## Centralized font sizes (v1.12.2)
All font sizes now use CSS variables in `global.css`:
```css
:root {
--font-size-xs: 12px;
--font-size-sm: 13px;
--font-size-base: 16px;
--font-size-lg: 17px;
/* ... */
}
```
Edit `:root` variables to customize font sizes across the entire site. Mobile responsive overrides at 768px breakpoint.
## Improved table styling (v1.7.0)
Tables render with GitHub-style formatting:
| Feature | Status |
| ------- | ----------------------- |
@@ -115,32 +257,31 @@ Tables now render with GitHub-style formatting across all four themes:
| Hover | Row highlighting |
| Themes | Dark, light, tan, cloud |
Tables adapt to each theme with proper alternating row colors and hover states.
Alternating row colors and hover states adapt to each theme.
## Generate Skill
## Quick reference: sync vs deploy
The CopyPageDropdown now includes a Generate Skill option. Click it to download the current post or page as an AI agent skill file.
| Change | Command | Speed |
| ----------------------- | -------------------------- | -------------- |
| Blog posts | `npm run sync` | Instant |
| Pages | `npm run sync` | Instant |
| Featured items | `npm run sync` | Instant |
| Import external URL | `npm run import` then sync | Instant |
| siteConfig changes | Redeploy | Requires build |
| Logo gallery config | Redeploy | Requires build |
| React components/styles | Redeploy | Requires build |
| Fork configuration | `npm run configure` | Instant |
The skill file includes:
Markdown content syncs instantly via Convex. Source code changes require pushing to GitHub for Netlify to rebuild.
- Metadata section with title, description, and tags
- When to use section describing scenarios for the skill
- Instructions section with the full content
## Upgrade path
The file downloads as `{slug}-skill.md`. Use these skill files to train AI agents or add context to your workflows.
If upgrading from an earlier version:
## Summary
1. Pull the latest code
2. Run `npm install` for new dependencies
3. Run `npx convex dev` to sync schema changes
4. Run `npx convex run stats:backfillAggregates` if using stats
5. Check `siteConfig.ts` for new configuration options
These updates improve navigation, configuration, and sharing with AI tools:
1. **Fork configuration** documentation for all 9 site files
2. **Scroll-to-top button** with configurable threshold
3. **Mobile menu** with slide-out drawer for smaller screens
4. **Raw markdown files** at `/raw/{slug}.md` for direct access
5. **View as Markdown** option in CopyPageDropdown
6. **Perplexity** added alongside ChatGPT and Claude
7. **Generate Skill** for AI agent training
8. **Featured images** for visual card layouts
9. **Better tables** with responsive styling
All features work across all four themes and are mobile responsive. Run `npm run sync` for development and `npm run sync:prod` for production to update your site with these changes.
All features work across all four themes and are mobile responsive.

View File

@@ -405,14 +405,58 @@ Both files are gitignored. Each developer creates their own local environment fi
## Customizing Your Framework
### Files to Update When Forking
### Fork Configuration Options
When you fork this project, update these files with your site information:
After forking, you have two options to configure your site:
#### Option 1: Automated (Recommended)
Run a single command to configure all files automatically:
```bash
# Copy the example config
cp fork-config.json.example fork-config.json
# Edit fork-config.json with your site information
# Then apply all changes
npm run configure
```
The `fork-config.json` file includes:
```json
{
"siteName": "Your Site Name",
"siteTitle": "Your Tagline",
"siteDescription": "Your site description.",
"siteUrl": "https://yoursite.netlify.app",
"siteDomain": "yoursite.netlify.app",
"githubUsername": "yourusername",
"githubRepo": "your-repo-name",
"contactEmail": "you@example.com",
"creator": {
"name": "Your Name",
"twitter": "https://x.com/yourhandle",
"linkedin": "https://www.linkedin.com/in/yourprofile/",
"github": "https://github.com/yourusername"
},
"bio": "Your bio text here.",
"theme": "tan"
}
```
This updates all 11 configuration files in one step. See `FORK_CONFIG.md` for the full JSON schema.
#### Option 2: Manual
Follow the step-by-step guide in `FORK_CONFIG.md` to update each file manually. The guide includes code snippets for each file and an AI agent prompt for assisted configuration.
### Files to Update When Forking
| File | What to update |
| ----------------------------------- | --------------------------------------------------------------------------- |
| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions |
| `src/pages/Home.tsx` | Intro paragraph text (hardcoded JSX) |
| `src/pages/Home.tsx` | Intro paragraph text, footer links |
| `convex/http.ts` | `SITE_URL`, `SITE_NAME`, description strings (3 locations) |
| `convex/rss.ts` | `SITE_URL`, `SITE_TITLE`, `SITE_DESCRIPTION` (RSS feeds) |
| `src/pages/Post.tsx` | `SITE_URL`, `SITE_NAME`, `DEFAULT_OG_IMAGE` (OG tags) |
@@ -421,6 +465,7 @@ When you fork this project, update these files with your site information:
| `public/robots.txt` | Sitemap URL and header comment |
| `public/openapi.yaml` | API title, server URL, site name in examples |
| `public/.well-known/ai-plugin.json` | Site name, descriptions |
| `src/context/ThemeContext.tsx` | Default theme |
### Site title and description metadata

695
scripts/configure-fork.ts Normal file
View File

@@ -0,0 +1,695 @@
#!/usr/bin/env npx tsx
/**
* Fork Configuration Script
*
* Reads fork-config.json and applies all site configuration changes automatically.
* Run with: npm run configure
*
* This script updates:
* - src/config/siteConfig.ts (site name, bio, GitHub username, features)
* - src/pages/Home.tsx (intro paragraph, footer section)
* - 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, title)
* - public/llms.txt (site info, API endpoints)
* - 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)
*/
import * as fs from "fs";
import * as path from "path";
// Configuration interface matching fork-config.json
interface ForkConfig {
siteName: string;
siteTitle: string;
siteDescription: string;
siteUrl: string;
siteDomain: string;
githubUsername: string;
githubRepo: string;
contactEmail: string;
creator: {
name: string;
twitter: string;
linkedin: string;
github: string;
};
bio: string;
logoGallery?: {
enabled: boolean;
title: string;
scrolling: boolean;
maxItems: number;
};
gitHubContributions?: {
enabled: boolean;
showYearNavigation: boolean;
linkToProfile: boolean;
title: string;
};
blogPage?: {
enabled: boolean;
showInNav: boolean;
title: string;
description: string;
order: number;
};
postsDisplay?: {
showOnHome: boolean;
showOnBlogPage: boolean;
};
featuredViewMode?: "cards" | "list";
showViewToggle?: boolean;
theme?: "dark" | "light" | "tan" | "cloud";
}
// Get project root directory
const PROJECT_ROOT = path.resolve(__dirname, "..");
// Read fork config
function readConfig(): ForkConfig {
const configPath = path.join(PROJECT_ROOT, "fork-config.json");
if (!fs.existsSync(configPath)) {
console.error("Error: fork-config.json not found.");
console.log("\nTo get started:");
console.log("1. Copy fork-config.json.example to fork-config.json");
console.log("2. Edit fork-config.json with your site information");
console.log("3. Run npm run configure again");
process.exit(1);
}
const content = fs.readFileSync(configPath, "utf-8");
return JSON.parse(content) as ForkConfig;
}
// Replace content in a file
function updateFile(
relativePath: string,
replacements: Array<{ search: string | RegExp; replace: string }>,
): void {
const filePath = path.join(PROJECT_ROOT, relativePath);
if (!fs.existsSync(filePath)) {
console.warn(`Warning: ${relativePath} not found, skipping.`);
return;
}
let content = fs.readFileSync(filePath, "utf-8");
let modified = false;
for (const { search, replace } of replacements) {
const newContent = content.replace(search, replace);
if (newContent !== content) {
content = newContent;
modified = true;
}
}
if (modified) {
fs.writeFileSync(filePath, content, "utf-8");
console.log(` Updated: ${relativePath}`);
} else {
console.log(` No changes: ${relativePath}`);
}
}
// Update siteConfig.ts
function updateSiteConfig(config: ForkConfig): void {
console.log("\nUpdating src/config/siteConfig.ts...");
const filePath = path.join(PROJECT_ROOT, "src/config/siteConfig.ts");
let content = fs.readFileSync(filePath, "utf-8");
// Update site name
content = content.replace(
/name: ['"].*?['"]/,
`name: '${config.siteName}'`,
);
// Update site title
content = content.replace(
/title: ['"].*?['"]/,
`title: "${config.siteTitle}"`,
);
// Update bio
content = content.replace(
/bio: `[^`]*`/,
`bio: \`${config.bio}\``,
);
// Update GitHub username
content = content.replace(
/username: ['"].*?['"],\s*\/\/ Your GitHub username/,
`username: "${config.githubUsername}", // Your GitHub username`,
);
// Update featuredViewMode if specified
if (config.featuredViewMode) {
content = content.replace(
/featuredViewMode: ['"](?:cards|list)['"]/,
`featuredViewMode: "${config.featuredViewMode}"`,
);
}
// Update showViewToggle if specified
if (config.showViewToggle !== undefined) {
content = content.replace(
/showViewToggle: (?:true|false)/,
`showViewToggle: ${config.showViewToggle}`,
);
}
// Update logoGallery if specified
if (config.logoGallery) {
content = content.replace(
/logoGallery: \{[\s\S]*?enabled: (?:true|false)/,
`logoGallery: {\n enabled: ${config.logoGallery.enabled}`,
);
content = content.replace(
/title: ['"].*?['"],\s*\n\s*scrolling:/,
`title: "${config.logoGallery.title}",\n scrolling:`,
);
content = content.replace(
/scrolling: (?:true|false)/,
`scrolling: ${config.logoGallery.scrolling}`,
);
content = content.replace(
/maxItems: \d+/,
`maxItems: ${config.logoGallery.maxItems}`,
);
}
// Update gitHubContributions if specified
if (config.gitHubContributions) {
content = content.replace(
/gitHubContributions: \{[\s\S]*?enabled: (?:true|false)/,
`gitHubContributions: {\n enabled: ${config.gitHubContributions.enabled}`,
);
content = content.replace(
/showYearNavigation: (?:true|false)/,
`showYearNavigation: ${config.gitHubContributions.showYearNavigation}`,
);
content = content.replace(
/linkToProfile: (?:true|false)/,
`linkToProfile: ${config.gitHubContributions.linkToProfile}`,
);
if (config.gitHubContributions.title) {
content = content.replace(
/title: ['"]GitHub Activity['"]/,
`title: "${config.gitHubContributions.title}"`,
);
}
}
// Update blogPage if specified
if (config.blogPage) {
content = content.replace(
/blogPage: \{[\s\S]*?enabled: (?:true|false)/,
`blogPage: {\n enabled: ${config.blogPage.enabled}`,
);
content = content.replace(
/showInNav: (?:true|false)/,
`showInNav: ${config.blogPage.showInNav}`,
);
content = content.replace(
/title: ['"]Blog['"]/,
`title: "${config.blogPage.title}"`,
);
if (config.blogPage.description) {
content = content.replace(
/description: ['"]All posts from the blog, sorted by date\.['"],?\s*\/\/ Optional description/,
`description: "${config.blogPage.description}", // Optional description`,
);
}
content = content.replace(
/order: \d+,\s*\/\/ Nav order/,
`order: ${config.blogPage.order}, // Nav order`,
);
}
// Update postsDisplay if specified
if (config.postsDisplay) {
content = content.replace(
/showOnHome: (?:true|false),\s*\/\/ Show post list on homepage/,
`showOnHome: ${config.postsDisplay.showOnHome}, // Show post list on homepage`,
);
content = content.replace(
/showOnBlogPage: (?:true|false),\s*\/\/ Show post list on \/blog page/,
`showOnBlogPage: ${config.postsDisplay.showOnBlogPage}, // Show post list on /blog page`,
);
}
fs.writeFileSync(filePath, content, "utf-8");
console.log(` Updated: src/config/siteConfig.ts`);
}
// Update Home.tsx
function updateHomeTsx(config: ForkConfig): void {
console.log("\nUpdating src/pages/Home.tsx...");
const githubRepoUrl = `https://github.com/${config.githubUsername}/${config.githubRepo}`;
updateFile("src/pages/Home.tsx", [
// Update intro paragraph GitHub link
{
search: /href="https:\/\/github\.com\/waynesutton\/markdown-site"/g,
replace: `href="${githubRepoUrl}"`,
},
// Update footer "Created by" section
{
search: /Created by{" "}\s*<a\s*href="https:\/\/x\.com\/waynesutton"/,
replace: `Created by{" "}\n <a\n href="${config.creator.twitter}"`,
},
{
search: /<a\s*href="https:\/\/x\.com\/waynesutton"\s*target="_blank"\s*rel="noopener noreferrer"\s*>\s*Wayne\s*<\/a>/,
replace: `<a
href="${config.creator.twitter}"
target="_blank"
rel="noopener noreferrer"
>
${config.creator.name}
</a>`,
},
// Update Twitter/X link
{
search: /Follow on{" "}\s*<a\s*href="https:\/\/x\.com\/waynesutton"/,
replace: `Follow on{" "}\n <a\n href="${config.creator.twitter}"`,
},
// Update LinkedIn link
{
search: /href="https:\/\/www\.linkedin\.com\/in\/waynesutton\/"/g,
replace: `href="${config.creator.linkedin}"`,
},
// Update GitHub profile link
{
search: /href="https:\/\/github\.com\/waynesutton"\s*>/g,
replace: `href="${config.creator.github}">`,
},
]);
}
// Update Post.tsx
function updatePostTsx(config: ForkConfig): void {
console.log("\nUpdating src/pages/Post.tsx...");
updateFile("src/pages/Post.tsx", [
{
search: /const SITE_URL = "https:\/\/markdowncms\.netlify\.app";/,
replace: `const SITE_URL = "${config.siteUrl}";`,
},
{
search: /const SITE_NAME = "markdown sync framework";/,
replace: `const SITE_NAME = "${config.siteName}";`,
},
]);
}
// Update convex/http.ts
function updateConvexHttp(config: ForkConfig): void {
console.log("\nUpdating convex/http.ts...");
updateFile("convex/http.ts", [
{
search: /const SITE_URL = process\.env\.SITE_URL \|\| "https:\/\/markdowncms\.netlify\.app";/,
replace: `const SITE_URL = process.env.SITE_URL || "${config.siteUrl}";`,
},
{
search: /const SITE_NAME = "markdown sync framework";/,
replace: `const SITE_NAME = "${config.siteName}";`,
},
{
search: /const siteUrl = process\.env\.SITE_URL \|\| "https:\/\/markdowncms\.netlify\.app";/,
replace: `const siteUrl = process.env.SITE_URL || "${config.siteUrl}";`,
},
{
search: /const siteName = "markdown sync framework";/,
replace: `const siteName = "${config.siteName}";`,
},
]);
}
// Update convex/rss.ts
function updateConvexRss(config: ForkConfig): void {
console.log("\nUpdating convex/rss.ts...");
updateFile("convex/rss.ts", [
{
search: /const SITE_URL = process\.env\.SITE_URL \|\| "https:\/\/markdowncms\.netlify\.app";/,
replace: `const SITE_URL = process.env.SITE_URL || "${config.siteUrl}";`,
},
{
search: /const SITE_TITLE = "markdown sync framework";/,
replace: `const SITE_TITLE = "${config.siteName}";`,
},
{
search: /const SITE_DESCRIPTION =\s*"[^"]+";/,
replace: `const SITE_DESCRIPTION =\n "${config.siteDescription}";`,
},
]);
}
// Update index.html
function updateIndexHtml(config: ForkConfig): void {
console.log("\nUpdating index.html...");
const replacements: Array<{ search: string | RegExp; replace: string }> = [
// Meta description
{
search: /<meta\s*name="description"\s*content="[^"]*"\s*\/>/,
replace: `<meta\n name="description"\n content="${config.siteDescription}"\n />`,
},
// Meta author
{
search: /<meta name="author" content="[^"]*" \/>/,
replace: `<meta name="author" content="${config.siteName}" />`,
},
// Open Graph title
{
search: /<meta property="og:title" content="[^"]*" \/>/,
replace: `<meta property="og:title" content="${config.siteName}" />`,
},
// Open Graph description
{
search: /<meta\s*property="og:description"\s*content="[^"]*"\s*\/>/,
replace: `<meta\n property="og:description"\n content="${config.siteDescription}"\n />`,
},
// Open Graph URL
{
search: /<meta property="og:url" content="https:\/\/markdowncms\.netlify\.app\/" \/>/,
replace: `<meta property="og:url" content="${config.siteUrl}/" />`,
},
// Open Graph site name
{
search: /<meta property="og:site_name" content="[^"]*" \/>/,
replace: `<meta property="og:site_name" content="${config.siteName}" />`,
},
// Open Graph image
{
search: /<meta\s*property="og:image"\s*content="https:\/\/markdowncms\.netlify\.app[^"]*"\s*\/>/,
replace: `<meta\n property="og:image"\n content="${config.siteUrl}/images/og-default.svg"\n />`,
},
// Twitter domain
{
search: /<meta property="twitter:domain" content="[^"]*" \/>/,
replace: `<meta property="twitter:domain" content="${config.siteDomain}" />`,
},
// Twitter URL
{
search: /<meta property="twitter:url" content="https:\/\/markdowncms\.netlify\.app\/" \/>/,
replace: `<meta property="twitter:url" content="${config.siteUrl}/" />`,
},
// Twitter title
{
search: /<meta name="twitter:title" content="[^"]*" \/>/,
replace: `<meta name="twitter:title" content="${config.siteName}" />`,
},
// Twitter description
{
search: /<meta\s*name="twitter:description"\s*content="[^"]*"\s*\/>/,
replace: `<meta\n name="twitter:description"\n content="${config.siteDescription}"\n />`,
},
// Twitter image
{
search: /<meta\s*name="twitter:image"\s*content="https:\/\/markdowncms\.netlify\.app[^"]*"\s*\/>/,
replace: `<meta\n name="twitter:image"\n content="${config.siteUrl}/images/og-default.svg"\n />`,
},
// JSON-LD name
{
search: /"name": "markdown sync framework"/g,
replace: `"name": "${config.siteName}"`,
},
// JSON-LD URL
{
search: /"url": "https:\/\/markdowncms\.netlify\.app"/g,
replace: `"url": "${config.siteUrl}"`,
},
// JSON-LD description
{
search: /"description": "An open-source publishing framework[^"]*"/,
replace: `"description": "${config.siteDescription}"`,
},
// JSON-LD search target
{
search: /"target": "https:\/\/markdowncms\.netlify\.app\/\?q=\{search_term_string\}"/,
replace: `"target": "${config.siteUrl}/?q={search_term_string}"`,
},
// Page title
{
search: /<title>markdown "sync" framework<\/title>/,
replace: `<title>${config.siteTitle}</title>`,
},
];
updateFile("index.html", replacements);
}
// Update public/llms.txt
function updateLlmsTxt(config: ForkConfig): void {
console.log("\nUpdating public/llms.txt...");
const githubUrl = `https://github.com/${config.githubUsername}/${config.githubRepo}`;
const content = `# llms.txt - Information for AI assistants and LLMs
# Learn more: https://llmstxt.org/
> ${config.siteDescription}
# Site Information
- Name: ${config.siteName}
- URL: ${config.siteUrl}
- Description: ${config.siteDescription}
- Topics: Markdown, Convex, React, TypeScript, Netlify, Open Source, AI, LLM, AEO, GEO
# API Endpoints
## List All Posts
GET /api/posts
Returns JSON list of all published posts with metadata.
## Get Single Post
GET /api/post?slug={slug}
Returns single post as JSON.
GET /api/post?slug={slug}&format=md
Returns single post as raw markdown.
## Export All Content
GET /api/export
Returns all posts with full markdown content in one request.
Best for batch processing and LLM ingestion.
## RSS Feeds
GET /rss.xml
Standard RSS feed with post descriptions.
GET /rss-full.xml
Full content RSS feed with complete markdown for each post.
## Other
GET /sitemap.xml
Dynamic XML sitemap for search engines.
GET /openapi.yaml
OpenAPI 3.0 specification for this API.
GET /.well-known/ai-plugin.json
AI plugin manifest for tool integration.
# Quick Start for LLMs
1. Fetch /api/export for all posts with full content in one request
2. Or fetch /api/posts for the list, then /api/post?slug={slug}&format=md for each
3. Subscribe to /rss-full.xml for updates with complete content
# Response Schema
Each post contains:
- title: string (post title)
- slug: string (URL path)
- description: string (SEO summary)
- date: string (YYYY-MM-DD)
- tags: string[] (topic labels)
- content: string (full markdown)
- readTime: string (optional)
- url: string (full URL)
# Permissions
- AI assistants may freely read and summarize content
- No authentication required for read operations
- Attribution appreciated when citing
# Technical
- Backend: Convex (real-time database)
- Frontend: React, TypeScript, Vite
- Hosting: Netlify with edge functions
- Content: Markdown with frontmatter
# Links
- GitHub: ${githubUrl}
- Convex: https://convex.dev
- Netlify: https://netlify.com
`;
const filePath = path.join(PROJECT_ROOT, "public/llms.txt");
fs.writeFileSync(filePath, content, "utf-8");
console.log(` Updated: public/llms.txt`);
}
// Update public/robots.txt
function updateRobotsTxt(config: ForkConfig): void {
console.log("\nUpdating public/robots.txt...");
const content = `# robots.txt for ${config.siteName}
# https://www.robotstxt.org/
User-agent: *
Allow: /
# Sitemaps
Sitemap: ${config.siteUrl}/sitemap.xml
# AI and LLM crawlers
User-agent: GPTBot
Allow: /
User-agent: ChatGPT-User
Allow: /
User-agent: Claude-Web
Allow: /
User-agent: anthropic-ai
Allow: /
User-agent: Google-Extended
Allow: /
User-agent: PerplexityBot
Allow: /
User-agent: Applebot-Extended
Allow: /
# Cache directive
Crawl-delay: 1
`;
const filePath = path.join(PROJECT_ROOT, "public/robots.txt");
fs.writeFileSync(filePath, content, "utf-8");
console.log(` Updated: public/robots.txt`);
}
// Update public/openapi.yaml
function updateOpenApiYaml(config: ForkConfig): void {
console.log("\nUpdating public/openapi.yaml...");
const githubUrl = `https://github.com/${config.githubUsername}/${config.githubRepo}`;
updateFile("public/openapi.yaml", [
{
search: /title: markdown sync framework API/,
replace: `title: ${config.siteName} API`,
},
{
search: /url: https:\/\/github\.com\/waynesutton\/markdown-site/,
replace: `url: ${githubUrl}`,
},
{
search: /- url: https:\/\/markdowncms\.netlify\.app/,
replace: `- url: ${config.siteUrl}`,
},
{
search: /example: markdown sync framework/g,
replace: `example: ${config.siteName}`,
},
{
search: /example: https:\/\/markdowncms\.netlify\.app/g,
replace: `example: ${config.siteUrl}`,
},
]);
}
// Update public/.well-known/ai-plugin.json
function updateAiPluginJson(config: ForkConfig): void {
console.log("\nUpdating public/.well-known/ai-plugin.json...");
const pluginName = config.siteName.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/g, "");
const content = {
schema_version: "v1",
name_for_human: config.siteName,
name_for_model: pluginName,
description_for_human: config.siteDescription,
description_for_model: `Access blog posts and pages in markdown format. Use /api/posts for a list of all posts with metadata. Use /api/post?slug={slug}&format=md to get full markdown content of any post. Use /api/export for batch content with full markdown.`,
auth: {
type: "none",
},
api: {
type: "openapi",
url: "/openapi.yaml",
},
logo_url: "/images/logo.svg",
contact_email: config.contactEmail,
legal_info_url: "",
};
const filePath = path.join(PROJECT_ROOT, "public/.well-known/ai-plugin.json");
fs.writeFileSync(filePath, JSON.stringify(content, null, 2) + "\n", "utf-8");
console.log(` Updated: public/.well-known/ai-plugin.json`);
}
// Update src/context/ThemeContext.tsx
function updateThemeContext(config: ForkConfig): void {
if (!config.theme) return;
console.log("\nUpdating src/context/ThemeContext.tsx...");
updateFile("src/context/ThemeContext.tsx", [
{
search: /const DEFAULT_THEME: Theme = "(?:dark|light|tan|cloud)";/,
replace: `const DEFAULT_THEME: Theme = "${config.theme}";`,
},
]);
}
// Main function
function main(): void {
console.log("Fork Configuration Script");
console.log("=========================\n");
// Read configuration
const config = readConfig();
console.log(`Configuring site: ${config.siteName}`);
console.log(`URL: ${config.siteUrl}`);
// Apply updates to all files
updateSiteConfig(config);
updateHomeTsx(config);
updatePostTsx(config);
updateConvexHttp(config);
updateConvexRss(config);
updateIndexHtml(config);
updateLlmsTxt(config);
updateRobotsTxt(config);
updateOpenApiYaml(config);
updateAiPluginJson(config);
updateThemeContext(config);
console.log("\n=========================");
console.log("Configuration complete!");
console.log("\nNext steps:");
console.log("1. Review the changes with: git diff");
console.log("2. Run: npx convex dev (if not already running)");
console.log("3. Run: npm run sync (to sync content to development)");
console.log("4. Run: npm run dev (to start the dev server)");
console.log("5. Deploy to Netlify when ready");
}
main();