docs: update changelog page with v1.24.3 and v1.24.4 releases

- Added v1.24.4 entry documenting showInNav field and hardcodedNavItems configuration
- Added v1.24.3 entry documenting inner page logo configuration
- Includes configuration examples and updated file lists
- Maintains consistent format with existing changelog entries
This commit is contained in:
Wayne Sutton
2025-12-23 17:01:22 -08:00
parent 3d6700739d
commit fa9651f62e
22 changed files with 537 additions and 31 deletions

11
TASK.md
View File

@@ -15,7 +15,7 @@
## Current Status
v1.24.2 deployed. Mobile menu redesigned with sidebar integration and typography standardization.
v1.24.4 deployed. Added `showInNav` field for pages and hardcoded navigation items configuration for React routes.
## Completed
@@ -172,6 +172,15 @@ v1.24.2 deployed. Mobile menu redesigned with sidebar integration and typography
- [x] SidebarContext created to share sidebar data between components
- [x] Mobile menu typography standardized with CSS variables
- [x] Font-family standardized using inherit for consistency
- [x] `showInNav` field for pages to control navigation visibility
- [x] Pages can be published but hidden from navigation menu
- [x] Defaults to `true` for backwards compatibility
- [x] Pages with `showInNav: false` remain accessible via direct URL, searchable, and available via API
- [x] Hardcoded navigation items configuration in siteConfig.ts
- [x] Add React route pages (like /stats, /write) to navigation via hardcodedNavItems
- [x] Configure navigation order, title, and visibility per route
- [x] Navigation combines Blog link, hardcoded nav items, and markdown pages
- [x] All nav items sorted by order field (lower = first)
## Deployment Steps

View File

@@ -4,6 +4,61 @@ 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.24.4] - 2025-12-23
### Added
- `showInNav` field for pages to control navigation visibility
- Pages can be published and accessible but hidden from navigation menu
- Set `showInNav: false` in page frontmatter to hide from nav
- Defaults to `true` for backwards compatibility (all existing pages show in nav)
- Pages with `showInNav: false` remain:
- Published and accessible via direct URL
- Searchable via search indexes
- Available via API endpoints
- Just hidden from the navigation menu
- Matches the pattern used for `blogPage.showInNav` in siteConfig.ts
- Hardcoded navigation items configuration for React routes
- Add React route pages (like `/stats`, `/write`) to navigation via `siteConfig.hardcodedNavItems`
- Configure navigation order, title, and visibility per route
- Set `showInNav: false` to hide from nav while keeping route accessible
- Navigation combines Blog link, hardcoded nav items, and markdown pages
- All nav items sorted by `order` field (lower = first)
- Example: Configure `/stats` and `/write` routes in `siteConfig.ts`
### Technical
- Updated `convex/schema.ts`: Added optional `showInNav` field to pages table
- Updated `convex/pages.ts`: `getAllPages` query filters out pages where `showInNav === false`
- Updated `scripts/sync-posts.ts`: Parses `showInNav` from page frontmatter
- Updated `src/pages/Write.tsx`: Added `showInNav` field to page template and PAGE_FIELDS reference
- Updated `src/config/siteConfig.ts`: Added `HardcodedNavItem` interface and `hardcodedNavItems` config array
- Updated `src/components/Layout.tsx`: Reads `hardcodedNavItems` from siteConfig and combines with Blog link and pages
### Documentation
- Updated `content/pages/docs.md`: Added `showInNav` to static pages frontmatter table
- Updated `content/blog/setup-guide.md`: Added `showInNav` to static pages frontmatter table
## [1.24.3] - 2025-12-23
### Added
- Inner page logo configuration
- Logo displays in header on blog page, individual posts, and static pages
- Desktop: logo positioned on the left (before back button)
- Mobile: logo positioned on the right (smaller size for compact header)
- Configurable via `siteConfig.innerPageLogo.enabled` and `siteConfig.innerPageLogo.size`
- Does not affect homepage logo (controlled separately)
- Logo links to homepage when clicked
### Technical
- Updated `src/config/siteConfig.ts`: Added `InnerPageLogoConfig` interface and `innerPageLogo` config option
- Updated `src/pages/Blog.tsx`: Added logo to header navigation
- Updated `src/pages/Post.tsx`: Added logo to header navigation for both posts and pages
- Updated `src/styles/global.css`: Added CSS for desktop (left) and mobile (right) logo positioning with responsive sizing
## [1.24.2] - 2025-12-23
### Changed

View File

@@ -570,10 +570,12 @@ To use a different letter or icon, edit the SVG directly or replace the file.
### Change the Site Logo
The logo appears on the homepage. Edit `src/pages/Home.tsx`:
The site uses two logo configurations:
**Homepage logo:** Edit `src/config/siteConfig.ts`:
```typescript
const siteConfig = {
export default {
logo: "/images/logo.svg", // Set to null to hide the logo
// ...
};
@@ -581,6 +583,17 @@ const siteConfig = {
Replace `public/images/logo.svg` with your own logo file. Recommended: SVG format, 512x512 pixels.
**Inner page logo:** Shows on blog page, individual posts, and static pages. Configure in `src/config/siteConfig.ts`:
```typescript
innerPageLogo: {
enabled: true, // Set to false to hide logo on inner pages
size: 28, // Logo height in pixels (keeps aspect ratio)
},
```
The inner page logo appears in the top left corner on desktop and top right on mobile. It uses the same logo file as the homepage logo. Set `enabled: false` to hide it on inner pages while keeping the homepage logo.
### Change the Default Open Graph Image
The default OG image is used when a post does not have an `image` field in its frontmatter. Replace `public/images/og-default.svg` with your own image.
@@ -601,6 +614,7 @@ Edit `src/config/siteConfig.ts` to customize:
export default {
name: "Your Name",
title: "Your Title",
logo: "/images/logo.svg", // null to hide homepage logo
intro: "Your introduction...",
bio: "Your bio...",
@@ -611,7 +625,28 @@ export default {
title: "Blog", // Nav link and page title
order: 0, // Nav order (lower = first)
},
displayOnHomepage: true, // Show posts on homepage
// Hardcoded navigation items for React routes
hardcodedNavItems: [
{
slug: "stats",
title: "Stats",
order: 10,
showInNav: true, // Set to false to hide from nav
},
{
slug: "write",
title: "Write",
order: 20,
showInNav: true,
},
],
// Inner page logo configuration
innerPageLogo: {
enabled: true, // Set to false to hide logo on inner pages
size: 28, // Logo height in pixels (keeps aspect ratio)
},
// Featured section options
featuredViewMode: "list", // 'list' or 'cards'
@@ -827,6 +862,39 @@ Cards display post thumbnails (from `image` frontmatter field), titles, excerpts
**View preference:** User's view mode choice is saved to localStorage and persists across page visits.
### Hardcoded Navigation Items
Add React route pages (like `/stats`, `/write`) to the navigation menu via `siteConfig.ts`. These pages are React components, not markdown files.
Configure in `src/config/siteConfig.ts`:
```typescript
hardcodedNavItems: [
{
slug: "stats",
title: "Stats",
order: 10,
showInNav: true, // Set to false to hide from nav
},
{
slug: "write",
title: "Write",
order: 20,
showInNav: true,
},
],
```
Navigation combines three sources in this order:
1. Blog link (if `blogPage.enabled` and `blogPage.showInNav` are true)
2. Hardcoded nav items (from `hardcodedNavItems` array)
3. Markdown pages (from `content/pages/` with `showInNav: true`)
All items sort by `order` field (lower numbers first), then alphabetically by title.
**Hide from navigation:** Set `showInNav: false` to keep a route accessible but hidden from the nav menu. The route still works at its URL, just won't appear in navigation links.
### Scroll-to-top button
A scroll-to-top button appears after scrolling down on posts and pages. Configure it in `src/components/Layout.tsx`:
@@ -922,6 +990,7 @@ Your page content here...
| `slug` | Yes | URL path (e.g., `/about`) |
| `published` | Yes | Set `true` to show |
| `order` | No | Display order (lower = first) |
| `showInNav` | No | Show in navigation menu (default: `true`) |
| `authorName` | No | Author display name shown next to date |
| `authorImage` | No | Round author avatar image URL |
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
@@ -930,6 +999,8 @@ Your page content here...
Pages appear automatically in the navigation when published.
**Hide pages from navigation:** Set `showInNav: false` in page frontmatter to keep a page published and accessible via direct URL, but hidden from the navigation menu. Useful for pages like `/projects` that you want to link directly but not show in the main nav. Pages with `showInNav: false` remain searchable and available via API endpoints.
**Sidebar layout:** Add `layout: "sidebar"` to any post or page frontmatter to enable a docs-style layout with a table of contents sidebar. The sidebar extracts headings (H1, H2, H3) automatically and provides smooth scroll navigation. Only appears if headings exist in the content.
### Update SEO Meta Tags

View File

@@ -8,6 +8,79 @@ layout: "sidebar"
All notable changes to this project.
## v1.24.4
Released December 23, 2025
**Navigation visibility control and hardcoded nav items**
- `showInNav` field for pages to control navigation visibility
- Pages can be published and accessible but hidden from navigation menu
- Set `showInNav: false` in page frontmatter to hide from nav
- Defaults to `true` for backwards compatibility (all existing pages show in nav)
- Pages with `showInNav: false` remain:
- Published and accessible via direct URL
- Searchable via search indexes
- Available via API endpoints
- Just hidden from the navigation menu
- Matches the pattern used for `blogPage.showInNav` in siteConfig.ts
- Hardcoded navigation items configuration for React routes
- Add React route pages (like `/stats`, `/write`) to navigation via `siteConfig.hardcodedNavItems`
- Configure navigation order, title, and visibility per route
- Set `showInNav: false` to hide from nav while keeping route accessible
- Navigation combines Blog link, hardcoded nav items, and markdown pages
- All nav items sorted by `order` field (lower = first)
Example configuration:
```typescript
// src/config/siteConfig.ts
hardcodedNavItems: [
{
slug: "stats",
title: "Stats",
order: 10,
showInNav: true,
},
{
slug: "write",
title: "Write",
order: 20,
showInNav: true,
},
],
```
Updated files: `convex/schema.ts`, `convex/pages.ts`, `scripts/sync-posts.ts`, `src/pages/Write.tsx`, `src/config/siteConfig.ts`, `src/components/Layout.tsx`
Documentation updated: `content/pages/docs.md`, `content/blog/setup-guide.md`
## v1.24.3
Released December 23, 2025
**Inner page logo configuration**
- Logo displays in header on blog page, individual posts, and static pages
- Desktop: logo positioned on the left (before back button)
- Mobile: logo positioned on the right (smaller size for compact header)
- Configurable via `siteConfig.innerPageLogo.enabled` and `siteConfig.innerPageLogo.size`
- Does not affect homepage logo (controlled separately)
- Logo links to homepage when clicked
Configuration:
```typescript
// src/config/siteConfig.ts
innerPageLogo: {
enabled: true, // Set to false to hide logo on inner pages
size: 28, // Logo height in pixels (keeps aspect ratio)
},
```
Updated files: `src/config/siteConfig.ts`, `src/pages/Blog.tsx`, `src/pages/Post.tsx`, `src/styles/global.css`
## v1.24.2
Released December 23, 2025

View File

@@ -121,6 +121,7 @@ Content here...
| `slug` | Yes | URL path |
| `published` | Yes | `true` to show |
| `order` | No | Nav order (lower = first) |
| `showInNav` | No | Show in navigation menu (default: `true`) |
| `excerpt` | No | Short text for card view |
| `image` | No | Thumbnail for featured card view |
| `featured` | No | `true` to show in featured section |
@@ -129,6 +130,8 @@ Content here...
| `authorImage` | No | Round author avatar image URL |
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
**Hide pages from navigation:** Set `showInNav: false` to keep a page published and accessible via direct URL, but hidden from the navigation menu. Pages with `showInNav: false` remain searchable and available via API endpoints. Useful for pages you want to link directly but not show in the main nav.
### Sidebar layout
Posts and pages can use a docs-style layout with a table of contents sidebar. Add `layout: "sidebar"` to the frontmatter:
@@ -320,7 +323,7 @@ Edit `src/config/siteConfig.ts`:
export default {
name: "Site Name",
title: "Tagline",
logo: "/images/logo.svg", // null to hide
logo: "/images/logo.svg", // null to hide homepage logo
intro: "Introduction text...",
bio: "Bio text...",
@@ -331,7 +334,28 @@ export default {
title: "Blog", // Nav link and page title
order: 0, // Nav order (lower = first)
},
displayOnHomepage: true, // Show posts on homepage
// Hardcoded navigation items for React routes
hardcodedNavItems: [
{
slug: "stats",
title: "Stats",
order: 10,
showInNav: true, // Set to false to hide from nav
},
{
slug: "write",
title: "Write",
order: 20,
showInNav: true,
},
],
// Inner page logo configuration
innerPageLogo: {
enabled: true, // Set to false to hide logo on inner pages
size: 28, // Logo height in pixels (keeps aspect ratio)
},
// Featured section
featuredViewMode: "list", // 'list' or 'cards'
@@ -355,6 +379,21 @@ export default {
};
```
**Logo configuration:**
- `logo`: Homepage logo path (set to `null` to hide). Uses `public/images/logo.svg` by default.
- `innerPageLogo`: Logo shown on blog page, posts, and static pages. Desktop: top left. Mobile: top right. Set `enabled: false` to hide on inner pages while keeping homepage logo.
**Navigation structure:**
Navigation combines three sources sorted by `order`:
1. Blog link (if `blogPage.enabled` and `blogPage.showInNav` are true)
2. Hardcoded nav items (React routes from `hardcodedNavItems`)
3. Markdown pages (from `content/pages/` with `showInNav: true`)
All items sort by `order` (lower first), then alphabetically by title.
### Featured items
Posts and pages appear in the featured section when marked with `featured: true` in frontmatter.
@@ -604,6 +643,11 @@ Mobile sizes defined in `@media (max-width: 768px)` block.
The `npm run sync` command only syncs markdown text content. Images are deployed when Netlify builds your site.
**Logo options:**
- **Homepage logo:** Configured via `logo` in `siteConfig.ts`. Set to `null` to hide.
- **Inner page logo:** Configured via `innerPageLogo` in `siteConfig.ts`. Shows on blog page, posts, and static pages. Desktop: top left corner. Mobile: top right corner (smaller). Set `enabled: false` to hide on inner pages while keeping homepage logo.
## Search
Press `Command+K` (Mac) or `Ctrl+K` (Windows/Linux) to open the search modal. Click the search icon in the nav or use the keyboard shortcut.

View File

@@ -2,6 +2,7 @@
title: "Projects"
slug: "projects"
published: true
showInNav: false
order: 3
---

View File

@@ -11,6 +11,7 @@ export const getAllPages = query({
title: v.string(),
published: v.boolean(),
order: v.optional(v.number()),
showInNav: v.optional(v.boolean()),
excerpt: v.optional(v.string()),
image: v.optional(v.string()),
featured: v.optional(v.boolean()),
@@ -26,8 +27,14 @@ export const getAllPages = query({
.withIndex("by_published", (q) => q.eq("published", true))
.collect();
// Filter out pages where showInNav is explicitly false
// Default to true for backwards compatibility (undefined/null = show in nav)
const visiblePages = pages.filter(
(page) => page.showInNav !== false,
);
// Sort by order (lower numbers first), then by title
const sortedPages = pages.sort((a, b) => {
const sortedPages = visiblePages.sort((a, b) => {
const orderA = a.order ?? 999;
const orderB = b.order ?? 999;
if (orderA !== orderB) return orderA - orderB;
@@ -40,6 +47,7 @@ export const getAllPages = query({
title: page.title,
published: page.published,
order: page.order,
showInNav: page.showInNav,
excerpt: page.excerpt,
image: page.image,
featured: page.featured,
@@ -103,6 +111,7 @@ export const getPageBySlug = query({
content: v.string(),
published: v.boolean(),
order: v.optional(v.number()),
showInNav: v.optional(v.boolean()),
excerpt: v.optional(v.string()),
image: v.optional(v.string()),
featured: v.optional(v.boolean()),
@@ -130,6 +139,7 @@ export const getPageBySlug = query({
content: page.content,
published: page.published,
order: page.order,
showInNav: page.showInNav,
excerpt: page.excerpt,
image: page.image,
featured: page.featured,
@@ -151,6 +161,7 @@ export const syncPagesPublic = mutation({
content: v.string(),
published: v.boolean(),
order: v.optional(v.number()),
showInNav: v.optional(v.boolean()),
excerpt: v.optional(v.string()),
image: v.optional(v.string()),
featured: v.optional(v.boolean()),
@@ -189,6 +200,7 @@ export const syncPagesPublic = mutation({
content: page.content,
published: page.published,
order: page.order,
showInNav: page.showInNav,
excerpt: page.excerpt,
image: page.image,
featured: page.featured,

View File

@@ -41,6 +41,7 @@ export default defineSchema({
content: v.string(),
published: v.boolean(),
order: v.optional(v.number()), // Display order in nav
showInNav: v.optional(v.boolean()), // Show in navigation menu (default: true)
excerpt: v.optional(v.string()), // Short excerpt for card view
image: v.optional(v.string()), // Thumbnail/OG image URL for featured cards
featured: v.optional(v.boolean()), // Show in featured section

View File

@@ -33,7 +33,7 @@ A brief description of each file in the codebase.
| File | Description |
| --------------- | --------------------------------------------------------------------------------------------------------- |
| `siteConfig.ts` | Centralized site configuration (name, logo, blog page, posts display, GitHub contributions, nav order) |
| `siteConfig.ts` | Centralized site configuration (name, logo, blog page, posts display, GitHub contributions, nav order, inner page logo settings, hardcoded navigation items for React routes) |
### Pages (`src/pages/`)
@@ -49,7 +49,7 @@ A brief description of each file in the codebase.
| File | Description |
| ------------------------- | ---------------------------------------------------------- |
| `Layout.tsx` | Page wrapper with search button, theme toggle, mobile menu (left-aligned on mobile), and scroll-to-top |
| `Layout.tsx` | Page wrapper with search button, theme toggle, mobile menu (left-aligned on mobile), and scroll-to-top. Combines Blog link, hardcoded nav items, and markdown pages for navigation |
| `ThemeToggle.tsx` | Theme switcher (dark/light/tan/cloud) |
| `PostList.tsx` | Year-grouped blog post list or card grid (supports list/cards view modes) |
| `BlogPost.tsx` | Markdown renderer with syntax highlighting and collapsible sections (details/summary) |
@@ -149,10 +149,11 @@ Markdown files for static pages like About, Projects, Contact, Changelog.
| `slug` | URL path for the page |
| `published` | Whether page is public |
| `order` | Display order in navigation (lower first) |
| `showInNav` | Show in navigation menu (default: true) |
| `excerpt` | Short excerpt for card view (optional) |
| `featured` | Show in featured section (optional) |
| `featuredOrder` | Order in featured section (optional) |
| `authorName` | Author display name (optional) |
| `featured` | Show in featured section (optional) |
| `featuredOrder` | Order in featured section (optional) |
| `authorName` | Author display name (optional) |
| `authorImage` | Round author avatar image URL (optional) |
## Scripts (`scripts/`)

View File

@@ -2,7 +2,7 @@
---
Type: page
Date: 2025-12-23
Date: 2025-12-24
---
An open-source publishing framework for AI agents and developers. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify.

View File

@@ -2,11 +2,84 @@
---
Type: page
Date: 2025-12-23
Date: 2025-12-24
---
All notable changes to this project.
## v1.24.4
Released December 23, 2025
**Navigation visibility control and hardcoded nav items**
- `showInNav` field for pages to control navigation visibility
- Pages can be published and accessible but hidden from navigation menu
- Set `showInNav: false` in page frontmatter to hide from nav
- Defaults to `true` for backwards compatibility (all existing pages show in nav)
- Pages with `showInNav: false` remain:
- Published and accessible via direct URL
- Searchable via search indexes
- Available via API endpoints
- Just hidden from the navigation menu
- Matches the pattern used for `blogPage.showInNav` in siteConfig.ts
- Hardcoded navigation items configuration for React routes
- Add React route pages (like `/stats`, `/write`) to navigation via `siteConfig.hardcodedNavItems`
- Configure navigation order, title, and visibility per route
- Set `showInNav: false` to hide from nav while keeping route accessible
- Navigation combines Blog link, hardcoded nav items, and markdown pages
- All nav items sorted by `order` field (lower = first)
Example configuration:
```typescript
// src/config/siteConfig.ts
hardcodedNavItems: [
{
slug: "stats",
title: "Stats",
order: 10,
showInNav: true,
},
{
slug: "write",
title: "Write",
order: 20,
showInNav: true,
},
],
```
Updated files: `convex/schema.ts`, `convex/pages.ts`, `scripts/sync-posts.ts`, `src/pages/Write.tsx`, `src/config/siteConfig.ts`, `src/components/Layout.tsx`
Documentation updated: `content/pages/docs.md`, `content/blog/setup-guide.md`
## v1.24.3
Released December 23, 2025
**Inner page logo configuration**
- Logo displays in header on blog page, individual posts, and static pages
- Desktop: logo positioned on the left (before back button)
- Mobile: logo positioned on the right (smaller size for compact header)
- Configurable via `siteConfig.innerPageLogo.enabled` and `siteConfig.innerPageLogo.size`
- Does not affect homepage logo (controlled separately)
- Logo links to homepage when clicked
Configuration:
```typescript
// src/config/siteConfig.ts
innerPageLogo: {
enabled: true, // Set to false to hide logo on inner pages
size: 28, // Logo height in pixels (keeps aspect ratio)
},
```
Updated files: `src/config/siteConfig.ts`, `src/pages/Blog.tsx`, `src/pages/Post.tsx`, `src/styles/global.css`
## v1.24.2
Released December 23, 2025

View File

@@ -2,7 +2,7 @@
---
Type: page
Date: 2025-12-23
Date: 2025-12-24
---
You found the contact page. Nice

View File

@@ -2,7 +2,7 @@
---
Type: page
Date: 2025-12-23
Date: 2025-12-24
---
Reference documentation for setting up, customizing, and deploying this markdown framework.
@@ -120,6 +120,7 @@ Content here...
| `slug` | Yes | URL path |
| `published` | Yes | `true` to show |
| `order` | No | Nav order (lower = first) |
| `showInNav` | No | Show in navigation menu (default: `true`) |
| `excerpt` | No | Short text for card view |
| `image` | No | Thumbnail for featured card view |
| `featured` | No | `true` to show in featured section |
@@ -128,6 +129,8 @@ Content here...
| `authorImage` | No | Round author avatar image URL |
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
**Hide pages from navigation:** Set `showInNav: false` to keep a page published and accessible via direct URL, but hidden from the navigation menu. Pages with `showInNav: false` remain searchable and available via API endpoints. Useful for pages you want to link directly but not show in the main nav.
### Sidebar layout
Posts and pages can use a docs-style layout with a table of contents sidebar. Add `layout: "sidebar"` to the frontmatter:
@@ -319,7 +322,7 @@ Edit `src/config/siteConfig.ts`:
export default {
name: "Site Name",
title: "Tagline",
logo: "/images/logo.svg", // null to hide
logo: "/images/logo.svg", // null to hide homepage logo
intro: "Introduction text...",
bio: "Bio text...",
@@ -330,7 +333,28 @@ export default {
title: "Blog", // Nav link and page title
order: 0, // Nav order (lower = first)
},
displayOnHomepage: true, // Show posts on homepage
// Hardcoded navigation items for React routes
hardcodedNavItems: [
{
slug: "stats",
title: "Stats",
order: 10,
showInNav: true, // Set to false to hide from nav
},
{
slug: "write",
title: "Write",
order: 20,
showInNav: true,
},
],
// Inner page logo configuration
innerPageLogo: {
enabled: true, // Set to false to hide logo on inner pages
size: 28, // Logo height in pixels (keeps aspect ratio)
},
// Featured section
featuredViewMode: "list", // 'list' or 'cards'
@@ -354,6 +378,21 @@ export default {
};
```
**Logo configuration:**
- `logo`: Homepage logo path (set to `null` to hide). Uses `public/images/logo.svg` by default.
- `innerPageLogo`: Logo shown on blog page, posts, and static pages. Desktop: top left. Mobile: top right. Set `enabled: false` to hide on inner pages while keeping homepage logo.
**Navigation structure:**
Navigation combines three sources sorted by `order`:
1. Blog link (if `blogPage.enabled` and `blogPage.showInNav` are true)
2. Hardcoded nav items (React routes from `hardcodedNavItems`)
3. Markdown pages (from `content/pages/` with `showInNav: true`)
All items sort by `order` (lower first), then alphabetically by title.
### Featured items
Posts and pages appear in the featured section when marked with `featured: true` in frontmatter.
@@ -603,6 +642,11 @@ Mobile sizes defined in `@media (max-width: 768px)` block.
The `npm run sync` command only syncs markdown text content. Images are deployed when Netlify builds your site.
**Logo options:**
- **Homepage logo:** Configured via `logo` in `siteConfig.ts`. Set to `null` to hide.
- **Inner page logo:** Configured via `innerPageLogo` in `siteConfig.ts`. Shows on blog page, posts, and static pages. Desktop: top left corner. Mobile: top right corner (smaller). Set `enabled: false` to hide on inner pages while keeping homepage logo.
## Search
Press `Command+K` (Mac) or `Ctrl+K` (Windows/Linux) to open the search modal. Click the search icon in the nav or use the keyboard shortcut.

View File

@@ -2,7 +2,7 @@
---
Type: page
Date: 2025-12-23
Date: 2025-12-24
---
This markdown framework is open source and built to be extended. Here is what ships out of the box.

View File

@@ -564,10 +564,12 @@ To use a different letter or icon, edit the SVG directly or replace the file.
### Change the Site Logo
The logo appears on the homepage. Edit `src/pages/Home.tsx`:
The site uses two logo configurations:
**Homepage logo:** Edit `src/config/siteConfig.ts`:
```typescript
const siteConfig = {
export default {
logo: "/images/logo.svg", // Set to null to hide the logo
// ...
};
@@ -575,6 +577,17 @@ const siteConfig = {
Replace `public/images/logo.svg` with your own logo file. Recommended: SVG format, 512x512 pixels.
**Inner page logo:** Shows on blog page, individual posts, and static pages. Configure in `src/config/siteConfig.ts`:
```typescript
innerPageLogo: {
enabled: true, // Set to false to hide logo on inner pages
size: 28, // Logo height in pixels (keeps aspect ratio)
},
```
The inner page logo appears in the top left corner on desktop and top right on mobile. It uses the same logo file as the homepage logo. Set `enabled: false` to hide it on inner pages while keeping the homepage logo.
### Change the Default Open Graph Image
The default OG image is used when a post does not have an `image` field in its frontmatter. Replace `public/images/og-default.svg` with your own image.
@@ -595,6 +608,7 @@ Edit `src/config/siteConfig.ts` to customize:
export default {
name: "Your Name",
title: "Your Title",
logo: "/images/logo.svg", // null to hide homepage logo
intro: "Your introduction...",
bio: "Your bio...",
@@ -605,7 +619,28 @@ export default {
title: "Blog", // Nav link and page title
order: 0, // Nav order (lower = first)
},
displayOnHomepage: true, // Show posts on homepage
// Hardcoded navigation items for React routes
hardcodedNavItems: [
{
slug: "stats",
title: "Stats",
order: 10,
showInNav: true, // Set to false to hide from nav
},
{
slug: "write",
title: "Write",
order: 20,
showInNav: true,
},
],
// Inner page logo configuration
innerPageLogo: {
enabled: true, // Set to false to hide logo on inner pages
size: 28, // Logo height in pixels (keeps aspect ratio)
},
// Featured section options
featuredViewMode: "list", // 'list' or 'cards'
@@ -821,6 +856,39 @@ Cards display post thumbnails (from `image` frontmatter field), titles, excerpts
**View preference:** User's view mode choice is saved to localStorage and persists across page visits.
### Hardcoded Navigation Items
Add React route pages (like `/stats`, `/write`) to the navigation menu via `siteConfig.ts`. These pages are React components, not markdown files.
Configure in `src/config/siteConfig.ts`:
```typescript
hardcodedNavItems: [
{
slug: "stats",
title: "Stats",
order: 10,
showInNav: true, // Set to false to hide from nav
},
{
slug: "write",
title: "Write",
order: 20,
showInNav: true,
},
],
```
Navigation combines three sources in this order:
1. Blog link (if `blogPage.enabled` and `blogPage.showInNav` are true)
2. Hardcoded nav items (from `hardcodedNavItems` array)
3. Markdown pages (from `content/pages/` with `showInNav: true`)
All items sort by `order` field (lower numbers first), then alphabetically by title.
**Hide from navigation:** Set `showInNav: false` to keep a route accessible but hidden from the nav menu. The route still works at its URL, just won't appear in navigation links.
### Scroll-to-top button
A scroll-to-top button appears after scrolling down on posts and pages. Configure it in `src/components/Layout.tsx`:
@@ -916,6 +984,7 @@ Your page content here...
| `slug` | Yes | URL path (e.g., `/about`) |
| `published` | Yes | Set `true` to show |
| `order` | No | Display order (lower = first) |
| `showInNav` | No | Show in navigation menu (default: `true`) |
| `authorName` | No | Author display name shown next to date |
| `authorImage` | No | Round author avatar image URL |
| `layout` | No | Set to `"sidebar"` for docs-style layout with TOC |
@@ -924,6 +993,8 @@ Your page content here...
Pages appear automatically in the navigation when published.
**Hide pages from navigation:** Set `showInNav: false` in page frontmatter to keep a page published and accessible via direct URL, but hidden from the navigation menu. Useful for pages like `/projects` that you want to link directly but not show in the main nav. Pages with `showInNav: false` remain searchable and available via API endpoints.
**Sidebar layout:** Add `layout: "sidebar"` to any post or page frontmatter to enable a docs-style layout with a table of contents sidebar. The sidebar extracts headings (H1, H2, H3) automatically and provides smooth scroll navigation. Only appears if headings exist in the content.
### Update SEO Meta Tags

View File

@@ -63,6 +63,7 @@ interface PageFrontmatter {
slug: string;
published: boolean;
order?: number; // Display order in navigation
showInNav?: boolean; // Show in navigation menu (default: true)
excerpt?: string; // Short excerpt for card view
image?: string; // Thumbnail/OG image URL for featured cards
featured?: boolean; // Show in featured section
@@ -78,6 +79,7 @@ interface ParsedPage {
content: string;
published: boolean;
order?: number;
showInNav?: boolean; // Show in navigation menu (default: true)
excerpt?: string; // Short excerpt for card view
image?: string; // Thumbnail/OG image URL for featured cards
featured?: boolean; // Show in featured section
@@ -168,6 +170,7 @@ function parsePageFile(filePath: string): ParsedPage | null {
content: content.trim(),
published: frontmatter.published ?? true,
order: frontmatter.order,
showInNav: frontmatter.showInNav, // Show in navigation menu (default: true)
excerpt: frontmatter.excerpt, // Short excerpt for card view
image: frontmatter.image, // Thumbnail/OG image URL for featured cards
featured: frontmatter.featured, // Show in featured section

View File

@@ -1,4 +1,5 @@
import { useQuery } from "convex/react";
import { Link } from "react-router-dom";
import { api } from "../../convex/_generated/api";
// Type for featured item from Convex (used for backwards compatibility)
@@ -142,7 +143,7 @@ export default function FeaturedCards({
return (
<div className="featured-cards">
{featuredData.map((item) => (
<a key={item.slug} href={`/${item.slug}`} className="featured-card">
<Link key={item.slug} to={`/${item.slug}`} className="featured-card">
{/* Thumbnail image displayed as square using object-fit: cover */}
{item.image && (
<div className="featured-card-image-wrapper">
@@ -160,7 +161,7 @@ export default function FeaturedCards({
<p className="featured-card-excerpt">{item.excerpt}</p>
)}
</div>
</a>
</Link>
))}
</div>
);

View File

@@ -80,8 +80,8 @@ export default function Layout({ children }: LayoutProps) {
const showBlogInNav =
siteConfig.blogPage.enabled && siteConfig.blogPage.showInNav;
// Combine Blog link with pages and sort by order
// This allows Blog to be positioned anywhere in the nav via siteConfig.blogPage.order
// Combine Blog link, hardcoded nav items, and pages, then sort by order
// This allows all nav items to be positioned anywhere via order field
type NavItem = {
slug: string;
title: string;
@@ -101,6 +101,20 @@ export default function Layout({ children }: LayoutProps) {
});
}
// Add hardcoded nav items (React routes like /stats, /write)
if (siteConfig.hardcodedNavItems && siteConfig.hardcodedNavItems.length > 0) {
siteConfig.hardcodedNavItems.forEach((item) => {
// Only add if showInNav is true (defaults to true)
if (item.showInNav !== false) {
navItems.push({
slug: item.slug,
title: item.title,
order: item.order ?? 999,
});
}
});
}
// Add pages from Convex
if (pages && pages.length > 0) {
pages.forEach((page) => {

View File

@@ -47,6 +47,15 @@ export interface PostsDisplayConfig {
showOnBlogPage: boolean; // Show post list on /blog page (requires blogPage.enabled)
}
// Hardcoded navigation item configuration
// For React route pages (like /stats, /write) that aren't markdown pages
export interface HardcodedNavItem {
slug: string; // URL path (e.g., "stats", "write")
title: string; // Display name in navigation
order?: number; // Nav order (lower = first, matches page frontmatter order)
showInNav?: boolean; // Show in navigation menu (default: true)
}
// Site configuration interface
export interface SiteConfig {
// Basic site info
@@ -75,6 +84,9 @@ export interface SiteConfig {
// Blog page configuration
blogPage: BlogPageConfig;
// Hardcoded navigation items for React routes (like /stats, /write)
hardcodedNavItems: HardcodedNavItem[];
// Posts display configuration
postsDisplay: PostsDisplayConfig;
@@ -175,6 +187,24 @@ export const siteConfig: SiteConfig = {
showViewToggle: true, // Show toggle button to switch between list and card views
},
// Hardcoded navigation items for React routes
// Add React route pages (like /stats, /write) that should appear in navigation
// Set showInNav: false to hide from nav while keeping the route accessible
hardcodedNavItems: [
{
slug: "stats",
title: "Stats",
order: 10,
showInNav: true,
},
{
slug: "write",
title: "Write",
order: 20,
showInNav: true,
},
],
// Posts display configuration
// Controls where the post list appears
// Both can be true to show posts on homepage AND blog page

View File

@@ -1,4 +1,5 @@
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { useQuery } from "convex/react";
import { api } from "../../convex/_generated/api";
import PostList from "../components/PostList";
@@ -166,9 +167,9 @@ export default function Home() {
<ul className="home-featured-list">
{featuredList.map((item) => (
<li key={item.slug}>
<a href={`/${item.slug}`} className="home-featured-link">
<Link to={`/${item.slug}`} className="home-featured-link">
{item.title}
</a>
</Link>
</li>
))}
</ul>
@@ -229,9 +230,9 @@ export default function Home() {
project on GitHub
</a>{" "}
to fork and deploy your own. View{" "}
<a href="/stats" className="home-text-link">
<Link to="/stats" className="home-text-link">
real-time site stats
</a>
</Link>
.
</p>
<p></p>

View File

@@ -46,6 +46,7 @@ const PAGE_FIELDS = [
{ name: "slug", required: true, example: '"page-url"' },
{ name: "published", required: true, example: "true" },
{ name: "order", required: false, example: "1" },
{ name: "showInNav", required: false, example: "true" },
{ name: "excerpt", required: false, example: '"Short description"' },
{ name: "image", required: false, example: '"/images/thumbnail.png"' },
{ name: "featured", required: false, example: "true" },
@@ -96,6 +97,7 @@ title: "Page Title"
slug: "page-url"
published: true
order: 1
showInNav: true
layout: "sidebar"
---

View File

@@ -300,7 +300,7 @@ body {
.main-content {
flex: 1;
max-width: 680px;
max-width: 800px;
width: 100%;
margin: 0 auto;
padding: 40px 24px;