diff --git a/.cursor/rules/dev2.mdc b/.cursor/rules/dev2.mdc
index f673366..c742a86 100644
--- a/.cursor/rules/dev2.mdc
+++ b/.cursor/rules/dev2.mdc
@@ -16,7 +16,6 @@ alwaysApply: true
- always create type-safe code
- **!IMPORTANT**: **DO NOT** externalize or document your work, usage guidelines, or benchmarks (e.g. `README.md`, `CONTRIBUTING.md`, `SUMMARY.md`, `USAGE_GUIDELINES.md` after completing the task, unless explicitly instructed to do so. You may include a brief summary of your work, but do not create separate documentation files for it.
- When creating Convex mutations, always patch directly without reading first, use indexed queries for ownership checks instead of `ctx.db.get()`, make mutations idempotent with early returns, use timestamp-based ordering for new items, and use `Promise.all()` for parallel independent operations to avoid write conflicts.
- - - When a task touches changelog.md, the changelog page, or files.md, run git log --date=short (or check commit history) and set each release date to match the real commit timeline—no placeholders or future months.
-Do you understand, what I’m asking? Never assume anything on your own, if anything isn’t clear, please ask questions and clarify your doubts.
@@ -53,9 +52,11 @@ alwaysApply: true
- Treat me as an new developer
- Be accurate and thorough
- Keep a list of the codebase files, provide a brief description of what each file one does called files.md.
-- you keep a developer friendly changelog.md of new features added based on https://keepachangelog.com/en/1.0.0/
+- you keep a developer friendly changelog.md of new features added based on https://keepachangelog.com/en/1.0.0/ and keep changelog-page.md updated also.
+- keep a track of changes completed in task.md
+- When a task touches changelog.md, the changelog page changelog-page.md, or files.md, run git log --date=short (or check commit history) and set each release date to match the real commit timeline—no placeholders or future months.
- prd files always end with .MD and not .prd
-- prd files are located in the prds folder except forchangelog.M , files.MD, README.md and TASK.MDwhich can stay in the root folder
+- prd files are located in the prds folder except for changelog.MD, files.MD, README.md and TASK.MD which can stay in the root folder
- create type-safe code always, if the prds folder does not exist create one
- Give the answer immediately. Provide detailed explanations and restate my query in your own words if necessary after giving the answer
- Value good arguments over authorities, the source is irrelevant
diff --git a/.gitignore b/.gitignore
index b49b34e..3f0e1da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,6 @@ dist-ssr
# Cursor rules
.cursor/rules/write.mdc
+
+# PRD files
+prds/metadataforsubs.md
diff --git a/AGENTS.md b/AGENTS.md
index ce60c54..76ab856 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -361,13 +361,20 @@ See `prds/howtoavoidwriteconflicts.md` for full details.
## Configuration
-Site config lives in `src/pages/Home.tsx`:
+Site config lives in `src/config/siteConfig.ts`:
```typescript
-const siteConfig = {
+export default {
name: "Site Name",
title: "Tagline",
logo: "/images/logo.svg", // null to hide
+ blogPage: {
+ enabled: true, // Enable /blog route
+ showInNav: true, // Show in navigation
+ title: "Blog", // Nav link and page title
+ order: 0, // Nav order (lower = first)
+ },
+ displayOnHomepage: true, // Show posts on homepage
featuredViewMode: "list", // 'list' or 'cards'
showViewToggle: true,
logoGallery: {
diff --git a/README.md b/README.md
index a087518..c6bfaa5 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ npm run sync:prod # production
- Featured section with list/card view toggle
- Logo gallery with continuous marquee scroll
- Static raw markdown files at `/raw/{slug}.md`
+- Dedicated blog page with configurable navigation order
### SEO and Discovery
@@ -61,7 +62,7 @@ When you fork this project, update these files with your site information:
| File | What to update |
| ----------------------------------- | ----------------------------------------------------------- |
-| `src/pages/Home.tsx` | Site name, title, intro, bio, featured config, logo gallery |
+| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, 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) |
@@ -222,6 +223,36 @@ In card view, the `image` field displays as a square thumbnail above the title.
Square thumbnails: 400x400px minimum (800x800px for retina). The same image can serve as both the OG image for social sharing and the featured card thumbnail.
+## Blog Page
+
+The site supports a dedicated blog page at `/blog`. Configure in `src/config/siteConfig.ts`:
+
+```typescript
+blogPage: {
+ enabled: true, // Enable /blog route
+ showInNav: true, // Show in navigation
+ title: "Blog", // Nav link and page title
+ order: 0, // Nav order (lower = first)
+},
+displayOnHomepage: true, // Show posts on homepage
+```
+
+| Option | Description |
+| ------ | ----------- |
+| `enabled` | Enable the `/blog` route |
+| `showInNav` | Show Blog link in navigation |
+| `title` | Text for nav link and page heading |
+| `order` | Position in navigation (lower = first) |
+| `displayOnHomepage` | Show post list on homepage |
+
+**Display options:**
+
+- Homepage only: `displayOnHomepage: true`, `blogPage.enabled: false`
+- Blog page only: `displayOnHomepage: false`, `blogPage.enabled: true`
+- Both: `displayOnHomepage: true`, `blogPage.enabled: true`
+
+**Navigation order:** The Blog link merges with page links and sorts by order. Pages use the `order` field in frontmatter. Set `blogPage.order: 5` to position Blog after pages with order 0-4.
+
## Logo Gallery
The homepage includes a scrolling logo gallery with sample logos. Configure in `siteConfig`:
diff --git a/TASK.md b/TASK.md
index 0bafc61..82d8166 100644
--- a/TASK.md
+++ b/TASK.md
@@ -2,21 +2,23 @@
## To Do
-- [ ] add componet fork fix for stats
-- [ ] Add blog page list and config
-- [ ] add github code block
-- [ ] add home to mobile menu
- [ ] Add markdown write page with copy option
+- [ ] add github code block
- [ ] create a ui site config page
- [ ] create a prompt formator or skill or agent to change everything at once after forking
-- [ ] Add app background image option
## Current Status
-v1.10.0 ready for deployment. Build passes. TypeScript verified. Documentation updated.
+v1.12.1 deployed. OG images now use post/page image from frontmatter instead of always defaulting.
## Completed
+- [x] Open Graph image fix for posts and pages with frontmatter images
+- [x] Dedicated blog page with configurable display options
+- [x] Blog page navigation order via siteConfig.blogPage.order
+- [x] Centralized siteConfig.ts for site configuration
+- [x] Posts display toggle for homepage and/or blog page
+- [x] move home to the top of the mobile menu
- [x] Fork configuration documentation in docs.md and setup-guide.md
- [x] "Files to Update When Forking" section with all 9 configuration files
- [x] Backend configuration examples for Convex files
@@ -71,6 +73,12 @@ v1.10.0 ready for deployment. Build passes. TypeScript verified. Documentation u
- [x] Perplexity added to AI service options
- [x] Featured image support with square thumbnails in card view
- [x] Improved markdown table CSS styling
+- [x] Aggregate component integration for efficient stats counting (O(log n) vs O(n))
+- [x] Three aggregate components: pageViewsByPath, totalPageViews, uniqueVisitors
+- [x] Chunked backfilling mutation for existing page view data
+- [x] Aggregate component registration in convex.config.ts
+- [x] Stats query updated to use aggregate counts
+- [x] Aggregate component documentation in prds/howstatsworks.md
## Deployment Steps
diff --git a/changelog.md b/changelog.md
index 728d5e9..c83b55e 100644
--- a/changelog.md
+++ b/changelog.md
@@ -4,6 +4,62 @@ 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.12.1] - 2025-12-20
+
+### Fixed
+
+- Open Graph images now use post/page `image` field from frontmatter
+ - Posts with images in frontmatter display their specific OG image
+ - Posts without images fall back to `og-default.svg`
+ - Pages now supported with appropriate `og:type` set to "website"
+ - Relative image paths resolved to absolute URLs
+
+### Changed
+
+- Renamed `generatePostMetaHtml` to `generateMetaHtml` in `convex/http.ts`
+- `/meta/post` endpoint now checks for pages if no post found
+- Meta HTML generation accepts optional `image` and `type` parameters
+
+### Technical
+
+- Updated `convex/http.ts` with image resolution logic
+- Handles both absolute URLs and relative paths for images
+- Deployed to production Convex
+
+## [1.12.0] - 2025-12-20
+
+### Added
+
+- Dedicated blog page at `/blog` with configurable display
+ - Enable/disable via `siteConfig.blogPage.enabled`
+ - Show/hide from navigation via `siteConfig.blogPage.showInNav`
+ - Custom page title via `siteConfig.blogPage.title`
+ - Navigation order via `siteConfig.blogPage.order` (lower = first)
+- Centralized site configuration in `src/config/siteConfig.ts`
+ - Moved all site settings from `Home.tsx` to dedicated config file
+ - Easier to customize when forking
+- Flexible post display options
+ - `displayOnHomepage`: Show posts on the homepage
+ - `blogPage.enabled`: Show posts on dedicated `/blog` page
+ - Both can be enabled for dual display
+
+### Changed
+
+- Navigation now combines Blog link with pages and sorts by order
+ - Blog link position controlled by `siteConfig.blogPage.order`
+ - Pages sorted by frontmatter `order` field (lower = first)
+ - Items without order default to 999 (appear last, alphabetically)
+- `Home.tsx` imports siteConfig instead of defining inline
+- `Layout.tsx` uses unified nav item sorting for desktop and mobile
+
+### Technical
+
+- New file: `src/config/siteConfig.ts`
+- New page: `src/pages/Blog.tsx`
+- Updated: `src/App.tsx` (conditional blog route)
+- Updated: `src/components/Layout.tsx` (nav item ordering)
+- Updated: `src/styles/global.css` (blog page styles)
+
## [1.11.1] - 2025-12-20
### Fixed
diff --git a/content/blog/setup-guide.md b/content/blog/setup-guide.md
index 806520c..d74af97 100644
--- a/content/blog/setup-guide.md
+++ b/content/blog/setup-guide.md
@@ -400,7 +400,7 @@ When you fork this project, update these files with your site information:
| File | What to update |
| ----------------------------------- | ----------------------------------------------------------- |
-| `src/pages/Home.tsx` | Site name, title, intro, bio, featured config, logo gallery |
+| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, 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) |
@@ -493,23 +493,27 @@ const DEFAULT_OG_IMAGE = "/images/og-default.svg";
### Update Site Configuration
-Edit `src/pages/Home.tsx` to customize:
+Edit `src/config/siteConfig.ts` to customize:
```typescript
-const siteConfig = {
+export default {
name: "Your Name",
title: "Your Title",
intro: "Your introduction...",
bio: "Your bio...",
+ // Blog page configuration
+ blogPage: {
+ enabled: true, // Enable /blog route
+ showInNav: true, // Show in navigation
+ title: "Blog", // Nav link and page title
+ order: 0, // Nav order (lower = first)
+ },
+ displayOnHomepage: true, // Show posts on homepage
+
// Featured section options
featuredViewMode: "list", // 'list' or 'cards'
showViewToggle: true, // Let users switch between views
- featuredItems: [
- { slug: "post-slug", type: "post" },
- { slug: "page-slug", type: "page" },
- ],
- featuredEssays: [{ title: "Post Title", slug: "post-slug" }],
// Logo gallery (marquee scroll with clickable links)
logoGallery: {
@@ -621,6 +625,36 @@ Delete the sample files from `public/images/logos/` and clear the images array,
The gallery uses CSS animations for smooth infinite scrolling. Logos display in grayscale and colorize on hover.
+### Blog page
+
+The site supports a dedicated blog page at `/blog`. Configure in `src/config/siteConfig.ts`:
+
+```typescript
+blogPage: {
+ enabled: true, // Enable /blog route
+ showInNav: true, // Show in navigation
+ title: "Blog", // Nav link and page title
+ order: 0, // Nav order (lower = first)
+},
+displayOnHomepage: true, // Show posts on homepage
+```
+
+| Option | Description |
+| ------ | ----------- |
+| `enabled` | Enable the `/blog` route |
+| `showInNav` | Show Blog link in navigation |
+| `title` | Text for nav link and page heading |
+| `order` | Position in navigation (lower = first) |
+| `displayOnHomepage` | Show post list on homepage |
+
+**Display options:**
+
+- Homepage only: `displayOnHomepage: true`, `blogPage.enabled: false`
+- Blog page only: `displayOnHomepage: false`, `blogPage.enabled: true`
+- Both: `displayOnHomepage: true`, `blogPage.enabled: true`
+
+**Navigation order:** The Blog link merges with page links and sorts by order. Pages use the `order` field in frontmatter. Set `blogPage.order: 5` to position Blog after pages with order 0-4.
+
### Scroll-to-top button
A scroll-to-top button appears after scrolling down on posts and pages. Configure it in `src/components/Layout.tsx`:
diff --git a/content/pages/about.md b/content/pages/about.md
index 085021c..5bdcef0 100644
--- a/content/pages/about.md
+++ b/content/pages/about.md
@@ -2,7 +2,7 @@
title: "About"
slug: "about"
published: true
-order: 1
+order: 2
excerpt: "An open-source markdown sync site for developers and AI agents."
---
@@ -55,6 +55,7 @@ It's a hybrid: developer workflow for publishing + real-time delivery like a dyn
- Full text search with Command+K shortcut
- Featured section with list/card view toggle and excerpts
- Logo gallery with clickable links and marquee scroll
+- Dedicated blog page with configurable navigation order
- Real-time analytics at `/stats`
- RSS feeds and sitemap for SEO
- Static raw markdown files at `/raw/{slug}.md`
diff --git a/content/pages/changelog-page.md b/content/pages/changelog-page.md
index 4413c16..cac514e 100644
--- a/content/pages/changelog-page.md
+++ b/content/pages/changelog-page.md
@@ -7,6 +7,48 @@ order: 5
All notable changes to this project.
+## v1.12.1
+
+Released December 20, 2025
+
+**Open Graph image fix**
+
+- 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" instead of "article"
+- Relative image paths (like `/images/v17.png`) resolve to absolute URLs
+
+The `/meta/post` endpoint in `convex/http.ts` now passes the `image` field from posts and pages to the meta HTML generator. If no post matches the slug, it checks for a page with that slug.
+
+## v1.12.0
+
+Released December 20, 2025
+
+**Dedicated blog page with configurable navigation**
+
+- New `/blog` page for dedicated post listing
+- Enable/disable via `siteConfig.blogPage.enabled`
+- Control navigation position with `siteConfig.blogPage.order`
+- Centralized site configuration in `src/config/siteConfig.ts`
+- Flexible post display: homepage only, blog page only, or both
+
+Configuration options:
+
+```typescript
+// src/config/siteConfig.ts
+blogPage: {
+ enabled: true, // Enable /blog route
+ showInNav: true, // Show in navigation
+ title: "Blog", // Page title
+ order: 0, // Nav order (lower = first)
+},
+displayOnHomepage: true, // Show posts on homepage
+```
+
+The Blog link now integrates with page navigation ordering. Set `order: 5` to place it after pages with order 0-4, or `order: 0` to keep it first.
+
+New files: `src/config/siteConfig.ts`, `src/pages/Blog.tsx`
+
## v1.11.1
Released December 20, 2025
diff --git a/content/pages/contact.md b/content/pages/contact.md
index c648867..a0b9fda 100644
--- a/content/pages/contact.md
+++ b/content/pages/contact.md
@@ -2,7 +2,7 @@
title: "Contact"
slug: "contact"
published: true
-order: 3
+order: 4
---
You found the contact page. Nice
diff --git a/content/pages/docs.md b/content/pages/docs.md
index fe18260..f881f4f 100644
--- a/content/pages/docs.md
+++ b/content/pages/docs.md
@@ -154,7 +154,7 @@ When you fork this project, update these files with your site information:
| File | What to update |
|------|----------------|
-| `src/pages/Home.tsx` | Site name, title, intro, bio, featured config, logo gallery |
+| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, 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) |
@@ -203,23 +203,30 @@ const DEFAULT_OG_IMAGE = "/images/og-default.svg";
These constants affect RSS feeds, API responses, sitemaps, and social sharing metadata.
-### Homepage settings
+### Site settings
-Edit `src/pages/Home.tsx`:
+Edit `src/config/siteConfig.ts`:
```typescript
-const siteConfig = {
+export default {
name: "Site Name",
title: "Tagline",
logo: "/images/logo.svg", // null to hide
intro: "Introduction text...",
bio: "Bio text...",
+ // Blog page configuration
+ blogPage: {
+ enabled: true, // Enable /blog route
+ showInNav: true, // Show in navigation
+ title: "Blog", // Nav link and page title
+ order: 0, // Nav order (lower = first)
+ },
+ displayOnHomepage: true, // Show posts on homepage
+
// Featured section
featuredViewMode: "list", // 'list' or 'cards'
showViewToggle: true,
- featuredItems: [{ slug: "post-slug", type: "post" }],
- featuredEssays: [{ title: "Post Title", slug: "post-slug" }],
// Logo gallery (with clickable links)
logoGallery: {
@@ -312,6 +319,36 @@ logoGallery: {
**To remove samples:** Delete files from `public/images/logos/` or clear the images array.
+### Blog page
+
+The site supports a dedicated blog page at `/blog`. Configure in `src/config/siteConfig.ts`:
+
+```typescript
+blogPage: {
+ enabled: true, // Enable /blog route
+ showInNav: true, // Show in navigation
+ title: "Blog", // Nav link and page title
+ order: 0, // Nav order (lower = first)
+},
+displayOnHomepage: true, // Show posts on homepage
+```
+
+| Option | Description |
+| ------------------- | -------------------------------------- |
+| `enabled` | Enable the `/blog` route |
+| `showInNav` | Show Blog link in navigation |
+| `title` | Text for nav link and page heading |
+| `order` | Position in navigation (lower = first) |
+| `displayOnHomepage` | Show post list on homepage |
+
+**Display options:**
+
+- Homepage only: `displayOnHomepage: true`, `blogPage.enabled: false`
+- Blog page only: `displayOnHomepage: false`, `blogPage.enabled: true`
+- Both: `displayOnHomepage: true`, `blogPage.enabled: true`
+
+**Navigation order:** The Blog link merges with page links and sorts by order. Pages use the `order` field in frontmatter. Set `blogPage.order: 5` to position Blog after pages with order 0-4.
+
### Scroll-to-top button
A scroll-to-top button appears after scrolling down. Configure in `src/components/Layout.tsx`:
diff --git a/content/pages/projects.md b/content/pages/projects.md
index 4b0d220..0dc1890 100644
--- a/content/pages/projects.md
+++ b/content/pages/projects.md
@@ -2,7 +2,7 @@
title: "Projects"
slug: "projects"
published: true
-order: 2
+order: 3
---
This markdown site is open source and built to be extended. Here is what ships out of the box.
diff --git a/convex/http.ts b/convex/http.ts
index 18fddc3..4dae5ba 100644
--- a/convex/http.ts
+++ b/convex/http.ts
@@ -220,21 +220,34 @@ function escapeHtml(text: string): string {
.replace(/'/g, "'");
}
-// Generate Open Graph HTML for a post
-function generatePostMetaHtml(post: {
+// Generate Open Graph HTML for a post or page
+function generateMetaHtml(content: {
title: string;
description: string;
slug: string;
- date: string;
+ date?: string;
readTime?: string;
+ image?: string;
+ type?: "post" | "page";
}): string {
const siteUrl = process.env.SITE_URL || "https://markdowncms.netlify.app";
const siteName = "markdown sync site";
- const defaultImage = `${siteUrl}/og-image.png`;
- const canonicalUrl = `${siteUrl}/${post.slug}`;
+ const defaultImage = `${siteUrl}/images/og-default.svg`;
+ const canonicalUrl = `${siteUrl}/${content.slug}`;
- const safeTitle = escapeHtml(post.title);
- const safeDescription = escapeHtml(post.description);
+ // Resolve image URL: use post image if available, otherwise default
+ let ogImage = defaultImage;
+ if (content.image) {
+ // Handle both absolute URLs and relative paths
+ ogImage = content.image.startsWith("http")
+ ? content.image
+ : `${siteUrl}${content.image}`;
+ }
+
+ const safeTitle = escapeHtml(content.title);
+ const safeDescription = escapeHtml(content.description);
+ const contentType = content.type || "post";
+ const ogType = contentType === "post" ? "article" : "website";
return `
@@ -250,17 +263,17 @@ function generatePostMetaHtml(post: {
-
+
-
-
-
+
+ ${content.date ? `
+ ` : ""}
-
+