Add vector-based semantic search to complement keyword search. Users can toggle between "Keyword" and "Semantic" modes in the search modal (Cmd+K, then Tab to switch). Semantic search: - Uses OpenAI text-embedding-ada-002 (1536 dimensions) - Finds content by meaning, not exact words - Shows similarity scores as percentages - ~300ms latency, ~$0.0001/query - Graceful fallback if OPENAI_API_KEY not set New files: - convex/embeddings.ts - Embedding generation actions - convex/embeddingsQueries.ts - Queries/mutations for embeddings - convex/semanticSearch.ts - Vector search action - convex/semanticSearchQueries.ts - Result hydration queries - content/pages/docs-search.md - Keyword search docs - content/pages/docs-semantic-search.md - Semantic search docs Changes: - convex/schema.ts: Add embedding field and by_embedding vectorIndex - SearchModal.tsx: Add mode toggle (TextAa/Brain icons) - sync-posts.ts: Generate embeddings after content sync - global.css: Search mode toggle styles Documentation updated: - changelog.md, TASK.md, files.md, about.md, home.md Configuration: npx convex env set OPENAI_API_KEY sk-your-key Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Status: Ready to commit. All semantic search files are staged. The TypeScript warnings are pre-existing (unused variables) and don't affect the build.
9.5 KiB
title, slug, published, order, showInNav, layout, rightSidebar, showFooter, docsSection, docsSectionOrder, docsSectionGroup, docsSectionGroupIcon
| title | slug | published | order | showInNav | layout | rightSidebar | showFooter | docsSection | docsSectionOrder | docsSectionGroup | docsSectionGroupIcon |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Content | docs-content | true | 2 | false | sidebar | true | true | true | 2 | Setup | Rocket |
Content
Markdown examples: For complete markdown syntax examples including code blocks, tables, lists, links, images, collapsible sections, and all formatting options, see Writing Markdown with Code Examples. That post includes copy-paste examples for every markdown feature.
Blog posts
Create files in content/blog/ with frontmatter:
---
title: "Post Title"
description: "SEO description"
date: "2025-01-15"
slug: "url-path"
published: true
tags: ["tag1", "tag2"]
readTime: "5 min read"
image: "/images/og-image.png"
---
Content here...
See the Frontmatter page for all available fields.
Static pages
Create files in content/pages/ with frontmatter:
---
title: "Page Title"
slug: "url-path"
published: true
order: 1
---
Content here...
See the Frontmatter page for all available fields.
Home intro content
The homepage intro text can be synced from markdown via content/pages/home.md (slug: home-intro). This allows you to update homepage text without redeploying.
Create home intro:
- Create
content/pages/home.md:
---
title: "Home Intro"
slug: "home-intro"
published: true
showInNav: false
order: -1
textAlign: "left"
---
Your homepage intro text here.
## Features
**Feature one** - Description here.
**Feature two** - Description here.
-
Run
npm run syncto sync to Convex -
Content appears on homepage instantly (no rebuild needed)
Blog heading styles: Headings (h1-h6) in home intro content use the same styling as blog posts (blog-h1 through blog-h6 classes). Each heading gets an automatic ID and a clickable anchor link (#) that appears on hover. Lists, blockquotes, horizontal rules, and links also use blog styling classes for consistent typography.
Fallback: If home-intro page is not found, the homepage falls back to siteConfig.bio text.
Footer content
The footer content can be synced from markdown via content/pages/footer.md (slug: footer). This allows you to update footer text without touching code.
Create footer content:
- Create
content/pages/footer.md:
---
title: "Footer"
slug: "footer"
published: true
showInNav: false
order: -1
---
Built with [Convex](https://convex.dev) for real-time sync and deployed on [Netlify](https://netlify.com).
Created by [Your Name](https://x.com/yourhandle). Follow on [Twitter/X](https://x.com/yourhandle) and [GitHub](https://github.com/yourusername).
-
Run
npm run syncto sync to Convex -
Footer content appears on homepage, blog page, and all posts/pages instantly (no rebuild needed)
Markdown support: Footer content supports full markdown including links, paragraphs, line breaks, and images. External links automatically open in new tabs.
Fallback: If footer page is not found, the footer falls back to siteConfig.footer.defaultContent.
Priority order: Per-post/page frontmatter footer: field (custom override) > synced footer.md content > siteConfig.footer.defaultContent.
Relationship with siteConfig: The content/pages/footer.md page takes priority over siteConfig.footer.defaultContent when present. Use the markdown page for dynamic content that changes frequently, or keep using siteConfig for static footer content.
Sidebar layout
Posts and pages can use a docs-style layout with a table of contents sidebar. Add layout: "sidebar" to the frontmatter:
---
title: "Documentation"
slug: "docs"
published: true
layout: "sidebar"
---
# Introduction
## Section One
### Subsection
## Section Two
Features:
- Left sidebar displays table of contents extracted from H1, H2, H3 headings
- Two-column layout: 220px sidebar + flexible content area
- Sidebar only appears if headings exist in the content
- Active heading highlighting as you scroll
- Smooth scroll navigation when clicking TOC links
- Mobile responsive: stacks to single column below 1024px
- Works for both blog posts and static pages
The sidebar extracts headings automatically from your markdown content. No manual TOC needed.
Right sidebar
When enabled in siteConfig.rightSidebar.enabled, posts and pages can display a right sidebar containing the CopyPageDropdown at 1135px+ viewport width.
Configuration:
Enable globally in src/config/siteConfig.ts:
rightSidebar: {
enabled: true, // Set to false to disable right sidebar globally
minWidth: 1135, // Minimum viewport width to show sidebar
},
Control per post/page with frontmatter:
---
title: "My Post"
rightSidebar: true # Enable right sidebar for this post
---
Features:
- Right sidebar appears at 1135px+ viewport width
- Contains CopyPageDropdown with all sharing options
- Three-column layout: left sidebar (TOC), main content, right sidebar
- CopyPageDropdown automatically moves from nav to right sidebar when enabled
- Hidden below 1135px breakpoint, CopyPageDropdown returns to nav
- Per-post/page control via
rightSidebar: truefrontmatter field - Opt-in only: right sidebar only appears when explicitly enabled in frontmatter
Use cases:
- Keep CopyPageDropdown accessible on wide screens without cluttering the nav
- Provide quick access to sharing options while reading long content
- Works alongside left sidebar TOC for comprehensive navigation
Example for blog post:
---
title: "My Tutorial"
description: "A detailed guide"
date: "2025-01-20"
slug: "my-tutorial"
published: true
tags: ["tutorial"]
layout: "sidebar"
---
# Introduction
## Getting Started
### Prerequisites
## Advanced Topics
How frontmatter works
Frontmatter is the YAML metadata at the top of each markdown file between --- markers. Here is how it flows through the system:
Content directories:
content/blog/*.mdcontains blog posts with frontmattercontent/pages/*.mdcontains static pages with frontmatter
Processing flow:
- Markdown files in
content/blog/andcontent/pages/contain YAML frontmatter scripts/sync-posts.tsusesgray-matterto parse frontmatter and validate required fields- Parsed data is sent to Convex mutations (
api.posts.syncPostsPublic,api.pages.syncPagesPublic) convex/schema.tsdefines the database structure for storing the data
Adding a new frontmatter field:
To add a custom frontmatter field, update these files:
- The interface in
scripts/sync-posts.ts(PostFrontmatterorPageFrontmatter) - The parsing logic in
parseMarkdownFile()orparsePageFile()functions - The schema in
convex/schema.ts - The sync mutation in
convex/posts.tsorconvex/pages.ts
Syncing content
Development:
npm run sync # Sync markdown content
npm run sync:discovery # Update discovery files (AGENTS.md, llms.txt)
npm run sync:all # Sync content + discovery files together
Production:
npm run sync:prod # Sync markdown content
npm run sync:discovery:prod # Update discovery files
npm run sync:all:prod # Sync content + discovery files together
Sync everything together:
npm run sync:all # Development: content + discovery
npm run sync:all:prod # Production: content + discovery
When to sync vs deploy
| What you're changing | Command | Timing |
|---|---|---|
Blog posts in content/blog/ |
npm run sync |
Instant (no rebuild) |
Pages in content/pages/ |
npm run sync |
Instant (no rebuild) |
| Featured items (via frontmatter) | npm run sync |
Instant (no rebuild) |
| Site config changes | npm run sync:discovery |
Updates discovery files |
| Import external URL | npm run import then sync |
Instant (no rebuild) |
Images in public/images/ |
Git commit + push | Requires rebuild |
siteConfig in Home.tsx |
Redeploy | Requires rebuild |
| Logo gallery config | Redeploy | Requires rebuild |
| React components/styles | Redeploy | Requires rebuild |
Markdown content syncs instantly to Convex. Images and source code require pushing to GitHub for Netlify to rebuild.
Tag pages and related posts
Tag pages are available at /tags/[tag] for each tag used in your posts. They display all posts with that tag in a list or card view with localStorage persistence for view mode preference.
Related posts: Individual blog posts show up to 3 related posts in the footer based on shared tags. Posts are sorted by relevance (number of shared tags) then by date. Only appears on blog posts (not static pages).
Tag links: Tags in post footers link to their respective tag archive pages.
Blog page featured layout
Posts can be marked as featured on the blog page using the blogFeatured frontmatter field:
---
title: "My Featured Post"
blogFeatured: true
---
The first blogFeatured post displays as a hero card with landscape image, tags, date, title, excerpt, author info, and read more link. Remaining blogFeatured posts display in a 2-column featured row with excerpts. Regular (non-featured) posts display in a 3-column grid without excerpts.