From 4b187cff5306c924defb5ae61b46bd3644ffc9a1 Mon Sep 17 00:00:00 2001 From: Wayne Sutton Date: Sat, 20 Dec 2025 16:34:48 -0800 Subject: [PATCH] fix: use frontmatter image for OG meta instead of default --- .cursor/rules/dev2.mdc | 7 +- .gitignore | 3 + AGENTS.md | 11 ++- README.md | 33 +++++++- TASK.md | 20 +++-- changelog.md | 56 ++++++++++++++ content/blog/setup-guide.md | 50 +++++++++++-- content/pages/about.md | 3 +- content/pages/changelog-page.md | 42 +++++++++++ content/pages/contact.md | 2 +- content/pages/docs.md | 49 ++++++++++-- content/pages/projects.md | 2 +- convex/http.ts | 102 +++++++++++++++++-------- files.md | 9 ++- public/raw/about.md | 3 +- public/raw/changelog.md | 56 +++++++++++++- public/raw/contact.md | 2 +- public/raw/docs.md | 51 +++++++++++-- public/raw/projects.md | 2 +- public/raw/setup-guide.md | 50 +++++++++++-- src/App.tsx | 8 +- src/components/Layout.tsx | 97 +++++++++++++++++------- src/components/MobileMenu.tsx | 11 ++- src/config/siteConfig.ts | 129 ++++++++++++++++++++++++++++++++ src/pages/Blog.tsx | 58 ++++++++++++++ src/pages/Home.tsx | 121 +++++++++--------------------- src/styles/global.css | 63 +++++++++++++++- 27 files changed, 836 insertions(+), 204 deletions(-) create mode 100644 src/config/siteConfig.ts create mode 100644 src/pages/Blog.tsx 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 ? ` + ` : ""} - +