diff --git a/.cursor/plans/mobile_pwa_optimization_4219af7e.plan.md b/.cursor/plans/mobile_pwa_optimization_4219af7e.plan.md new file mode 100644 index 0000000..d91e901 --- /dev/null +++ b/.cursor/plans/mobile_pwa_optimization_4219af7e.plan.md @@ -0,0 +1,177 @@ +--- +name: Mobile PWA Optimization +overview: Add full mobile iOS/Android optimization and PWA support with automatic manifest.json generation from fork-config.json, iOS/Android meta tags, safe area CSS, and touch optimizations. +todos: + - id: update-fork-config-interface + content: Add optional pwa field to ForkConfig interface in scripts/configure-fork.ts + status: pending + - id: add-manifest-generation + content: Create updateManifestJson() function to generate public/manifest.json from fork-config.json + status: pending + dependencies: + - update-fork-config-interface + - id: update-index-html-generation + content: Enhance updateIndexHtml() to add iOS/Android meta tags and manifest link + status: pending + dependencies: + - update-fork-config-interface + - id: add-mobile-css + content: Add safe area insets, touch optimizations, and input zoom prevention to src/styles/global.css + status: pending + - id: update-fork-config-example + content: Add optional pwa section to fork-config.json.example + status: pending + - id: update-netlify-headers + content: Add manifest.json headers to netlify.toml + status: pending + - id: wire-up-manifest-generation + content: Call updateManifestJson() in main() function of configure-fork.ts + status: pending + dependencies: + - add-manifest-generation + - id: update-documentation + content: Update fork-configuration-guide.md with PWA configuration section + status: pending +--- + +# Mobile and PWA Optimization Plan + +## Overview + +Add full mobile iOS/Android optimization and Progressive Web App (PWA) support. The manifest.json will be automatically generated from `fork-config.json` during the configure step, so users don't need to manually set it up. + +## Implementation Steps + +### 1. Update ForkConfig Interface + +Add optional PWA configuration to `scripts/configure-fork.ts`: + +```typescript +interface ForkConfig { + // ... existing fields ... + pwa?: { + shortName?: string; // Short name for home screen (max 12 chars) + themeColor?: string; // Theme color (default: "#faf8f5") + backgroundColor?: string; // Background color (default: "#faf8f5") + }; +} +``` + + + +### 2. Add manifest.json Generation Function + +Create `updateManifestJson()` function in `scripts/configure-fork.ts`: + +- Generate `public/manifest.json` with values from `fork-config.json` +- Use `siteName` for full name, `pwa.shortName` or truncated `siteName` for short name +- Use `siteDescription` for description +- Use `pwa.themeColor` and `pwa.backgroundColor` with defaults +- Include icon references (favicon.svg, icon-192.png, icon-512.png) +- Set `display: "standalone"` for home screen app behavior +- Set `orientation: "portrait-primary"` + +### 3. Update index.html Generation + +Enhance `updateIndexHtml()` function in `scripts/configure-fork.ts`: + +- Update viewport meta tag to include `viewport-fit=cover` for iOS notches +- Add manifest link: `` +- Add iOS meta tags: +- `apple-mobile-web-app-capable` +- `apple-mobile-web-app-status-bar-style` +- `apple-mobile-web-app-title` +- `apple-touch-icon` link +- Add Android meta tags: +- `mobile-web-app-capable` +- Enhanced `theme-color` with media queries for light/dark +- Update existing `theme-color` meta tag to support both light and dark modes + +### 4. Add Mobile CSS Optimizations + +Update `src/styles/global.css`:**Safe Area Insets (iOS notches):** + +- Add `@supports (padding: max(0px))` blocks for safe area insets +- Apply to `.main-content`, `.top-nav`, `.mobile-menu-drawer` +- Use `env(safe-area-inset-*)` CSS variables + +**Touch Optimizations:** + +- Add `-webkit-tap-highlight-color: transparent` globally +- Add `touch-action: manipulation` to interactive elements +- Add `user-select: none` to buttons +- Add `-webkit-touch-callout: none` to buttons + +**Prevent Input Zoom (iOS):** + +- Ensure all `input`, `textarea`, `select` have `font-size: 16px` minimum +- Add mobile-specific rule to prevent zoom on focus + +### 5. Update fork-config.json.example + +Add optional PWA section: + +```json +{ + // ... existing fields ... + "pwa": { + "shortName": "Your Site", + "themeColor": "#faf8f5", + "backgroundColor": "#faf8f5" + } +} +``` + + + +### 6. Update netlify.toml + +Add headers for manifest.json: + +```toml +[[headers]] + for = "/manifest.json" + [headers.values] + Content-Type = "application/manifest+json" + Cache-Control = "public, max-age=3600" +``` + + + +### 7. Update configure-fork.ts Main Function + +Add `updateManifestJson(config)` call in `main()` function after `updateAiPluginJson(config)`. + +### 8. Update Documentation + +Update `content/blog/fork-configuration-guide.md`: + +- Add PWA section explaining optional `pwa` fields +- Note that manifest.json is auto-generated +- Mention icon file requirements (icon-180.png, icon-192.png, icon-512.png) + +## Files to Modify + +1. `scripts/configure-fork.ts` - Add manifest generation, update index.html generation, add PWA interface +2. `fork-config.json.example` - Add optional PWA fields +3. `src/styles/global.css` - Add safe area insets and touch optimizations +4. `netlify.toml` - Add manifest.json headers +5. `content/blog/fork-configuration-guide.md` - Document PWA configuration + +## User Requirements + +After running `npm run configure`, users need to: + +1. Add icon files to `public/images/`: + +- `icon-180.png` (180x180) - iOS home screen +- `icon-192.png` (192x192) - Android +- `icon-512.png` (512x512) - Android splash + +The manifest.json will be automatically generated with correct values from their fork-config.json. + +## Benefits + +- Zero manual setup for manifest.json (auto-generated) +- Full iOS/Android home screen support +- Safe area support for notched devices \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 32bfda0..e1be87c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,7 @@ Your content is instantly available to browsers, LLMs, and AI agents.. Write mar - **Total Posts**: 12 - **Total Pages**: 4 - **Latest Post**: 2025-12-25 -- **Last Updated**: 2025-12-25T08:46:57.371Z +- **Last Updated**: 2025-12-25T20:18:52.316Z ## Tech stack diff --git a/TASK.md b/TASK.md index c8e6ec7..21815b1 100644 --- a/TASK.md +++ b/TASK.md @@ -8,10 +8,25 @@ ## Current Status -v1.29.0 ready. Added font family configuration system with monospace option. +v1.30.2 ready. Right sidebar now opt-in only via frontmatter. ## Completed +- [x] Fixed right sidebar default behavior: now requires explicit `rightSidebar: true` in frontmatter +- [x] Pages/posts without rightSidebar frontmatter render normally with CopyPageDropdown in nav +- [x] Fixed TypeScript errors: Added rightSidebar to syncPosts and syncPostsPublic args validators +- [x] Right sidebar feature with CopyPageDropdown support +- [x] RightSidebar component created +- [x] Three-column layout CSS (left sidebar, main content, right sidebar) +- [x] Right sidebar configuration in siteConfig.ts +- [x] rightSidebar frontmatter field for posts and pages +- [x] Updated Post.tsx to conditionally render right sidebar +- [x] Updated schema.ts, posts.ts, pages.ts to handle rightSidebar field +- [x] Updated sync-posts.ts to parse rightSidebar frontmatter +- [x] Updated Write.tsx to include rightSidebar option +- [x] Responsive behavior: right sidebar hidden below 1135px +- [x] CopyPageDropdown automatically moves from nav to right sidebar when enabled + - [x] Font family configuration system with siteConfig integration - [x] Added FontContext.tsx for global font state management - [x] Monospace font option added to FONT SWITCHER (IBM Plex Mono) diff --git a/changelog.md b/changelog.md index d41542c..0fc4dec 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,65 @@ 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.30.2] - 2025-12-25 + +### Fixed + +- Right sidebar no longer appears on pages/posts without explicit `rightSidebar: true` in frontmatter + - Changed default behavior: right sidebar is now opt-in only + - Pages like About and Contact now render without the right sidebar as expected + - `CopyPageDropdown` correctly appears in nav bar when right sidebar is disabled +- Logic in `Post.tsx` changed from `(page.rightSidebar ?? true)` to `page.rightSidebar === true` + +## [1.30.1] - 2025-12-25 + +### Fixed + +- TypeScript error in `convex/posts.ts` where `rightSidebar` was used in mutation handlers but missing from args validators + - Added `rightSidebar: v.optional(v.boolean())` to `syncPosts` args validator + - Added `rightSidebar: v.optional(v.boolean())` to `syncPostsPublic` args validator + +## [1.30.0] - 2025-12-25 + +### Added + +- Right sidebar feature for posts and pages + - New `RightSidebar` component that displays `CopyPageDropdown` in a right sidebar + - Appears at 1135px+ viewport width when enabled + - Controlled by `siteConfig.rightSidebar.enabled` (global toggle) + - Per-post/page control via `rightSidebar: true` frontmatter field (opt-in only) + - Three-column layout support: left sidebar (TOC), main content, right sidebar (CopyPageDropdown) + - CopyPageDropdown automatically moves from nav to right sidebar when enabled + - Responsive: right sidebar hidden below 1135px, CopyPageDropdown returns to nav +- Right sidebar configuration in siteConfig + - `rightSidebar.enabled`: Global toggle for right sidebar feature + - `rightSidebar.minWidth`: Minimum viewport width to show sidebar (default: 1135px) +- `rightSidebar` frontmatter field + - Available for both blog posts and pages + - Optional boolean field to enable/disable right sidebar per post/page + - Defaults to true when `siteConfig.rightSidebar.enabled` is true + - Added to Write page frontmatter reference with copy button + +### Changed + +- `Post.tsx`: Updated to support three-column layout with conditional right sidebar rendering +- CSS refactoring: Separated left and right sidebar styles + - `.post-sidebar-wrapper` is now left-specific with `margin-left` and right border + - `.post-sidebar-right` has complete independent styles with `margin-right` and left border + - Both sidebars maintain consistent styling (sticky positioning, background, borders, scrollbar hiding) +- `src/styles/global.css`: Added CSS for right sidebar positioning and 3-column grid layout +- `convex/schema.ts`: Added `rightSidebar` field to posts and pages tables +- `convex/posts.ts` and `convex/pages.ts`: Updated queries and mutations to handle `rightSidebar` field +- `scripts/sync-posts.ts`: Updated parsing logic to include `rightSidebar` frontmatter field +- `src/pages/Write.tsx`: Added `rightSidebar` field to POST_FIELDS and PAGE_FIELDS arrays + +### Technical + +- Right sidebar uses sticky positioning with top offset matching left sidebar +- CSS grid automatically adjusts from 2-column to 3-column layout when right sidebar is present +- Main content padding adjusts when right sidebar is enabled +- Mobile responsive: right sidebar hidden below 1135px breakpoint + ## [1.29.0] - 2025-12-25 ### Added diff --git a/content/blog/how-to-publish.md b/content/blog/how-to-publish.md index a524bd5..681f871 100644 --- a/content/blog/how-to-publish.md +++ b/content/blog/how-to-publish.md @@ -9,6 +9,8 @@ readTime: "3 min read" featured: false featuredOrder: 3 authorName: "Markdown" +layout: "sidebar" +rightSidebar: true authorImage: "/images/authors/markdown.png" image: "/images/matthew-smith-Rfflri94rs8-unsplash.jpg" excerpt: "Quick guide to writing and publishing markdown posts with npm run sync." diff --git a/content/blog/setup-guide.md b/content/blog/setup-guide.md index 128b3d8..c4e53c9 100644 --- a/content/blog/setup-guide.md +++ b/content/blog/setup-guide.md @@ -345,6 +345,7 @@ Your markdown content here... | `featuredOrder` | No | Order in featured section (lower = first) | | `authorName` | No | Author display name shown next to date | | `authorImage` | No | Round author avatar image URL | +| `rightSidebar` | No | Enable right sidebar with CopyPageDropdown (opt-in, requires explicit `true`) | ### How Frontmatter Works @@ -1043,6 +1044,8 @@ Pages appear automatically in the navigation when published. **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. +**Right sidebar:** When enabled in `siteConfig.rightSidebar.enabled`, posts and pages can display a right sidebar containing the CopyPageDropdown at 1135px+ viewport width. Add `rightSidebar: true` to frontmatter to enable. Without this field, pages render normally with CopyPageDropdown in the nav bar. When enabled, CopyPageDropdown moves from the navigation bar to the right sidebar on wide screens. The right sidebar is hidden below 1135px, and CopyPageDropdown returns to the nav bar automatically. + ### Update SEO Meta Tags Edit `index.html` to update: diff --git a/content/pages/changelog-page.md b/content/pages/changelog-page.md index b146224..a9083a7 100644 --- a/content/pages/changelog-page.md +++ b/content/pages/changelog-page.md @@ -9,6 +9,57 @@ layout: "sidebar" All notable changes to this project.  +## v1.30.2 + +Released December 25, 2025 + +**Right sidebar default behavior fix** + +- Right sidebar no longer appears on pages/posts without explicit `rightSidebar: true` in frontmatter + - Changed default behavior: right sidebar is now opt-in only + - Pages like About and Contact now render without the right sidebar as expected + - `CopyPageDropdown` correctly appears in nav bar when right sidebar is disabled +- Logic in `Post.tsx` changed from `(page.rightSidebar ?? true)` to `page.rightSidebar === true` + +Updated files: `src/pages/Post.tsx` + +## v1.30.1 + +Released December 25, 2025 + +**TypeScript error fix** + +- TypeScript error in `convex/posts.ts` where `rightSidebar` was used in mutation handlers but missing from args validators + - Added `rightSidebar: v.optional(v.boolean())` to `syncPosts` args validator + - Added `rightSidebar: v.optional(v.boolean())` to `syncPostsPublic` args validator + +Updated files: `convex/posts.ts` + +## v1.30.0 + +Released December 25, 2025 + +**Right sidebar feature for posts and pages** + +- New `RightSidebar` component that displays `CopyPageDropdown` in a right sidebar + - Appears at 1135px+ viewport width when enabled + - Controlled by `siteConfig.rightSidebar.enabled` (global toggle) + - Per-post/page control via `rightSidebar: true` frontmatter field (opt-in only) + - Three-column layout support: left sidebar (TOC), main content, right sidebar (CopyPageDropdown) + - CopyPageDropdown automatically moves from nav to right sidebar when enabled + - Responsive: right sidebar hidden below 1135px, CopyPageDropdown returns to nav +- Right sidebar configuration in siteConfig + - `rightSidebar.enabled`: Global toggle for right sidebar feature + - `rightSidebar.minWidth`: Minimum viewport width to show sidebar (default: 1135px) +- `rightSidebar` frontmatter field + - Available for both blog posts and pages + - Optional boolean field to enable/disable right sidebar per post/page + - Added to Write page frontmatter reference with copy button + +Updated files: `src/components/RightSidebar.tsx`, `src/pages/Post.tsx`, `src/config/siteConfig.ts`, `src/styles/global.css`, `convex/schema.ts`, `convex/posts.ts`, `convex/pages.ts`, `scripts/sync-posts.ts`, `src/pages/Write.tsx` + +Documentation updated: `content/blog/setup-guide.md`, `content/pages/docs.md`, `files.md`, `changelog.md` + ## v1.29.0 Released December 25, 2025 diff --git a/content/pages/contact.md b/content/pages/contact.md index a0b9fda..185a17f 100644 --- a/content/pages/contact.md +++ b/content/pages/contact.md @@ -2,6 +2,7 @@ title: "Contact" slug: "contact" published: true +layout: "sidebar" order: 4 --- diff --git a/content/pages/docs.md b/content/pages/docs.md index 62ef9b1..158682b 100644 --- a/content/pages/docs.md +++ b/content/pages/docs.md @@ -4,6 +4,7 @@ slug: "docs" published: true order: 0 layout: "sidebar" +rightSidebar: true --- Reference documentation for setting up, customizing, and deploying this markdown framework. @@ -129,20 +130,21 @@ order: 1 Content here... ``` -| Field | Required | Description | -| --------------- | -------- | ------------------------------------------------- | -| `title` | Yes | Nav link text | -| `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 | -| `featuredOrder` | No | Order in featured (lower = first) | -| `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 | +| Field | Required | Description | +| --------------- | -------- | ----------------------------------------------------------------------------- | +| `title` | Yes | Nav link text | +| `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 | +| `featuredOrder` | No | Order in featured (lower = first) | +| `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 | +| `rightSidebar` | No | Enable right sidebar with CopyPageDropdown (opt-in, requires explicit `true`) | **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. @@ -179,6 +181,46 @@ layout: "sidebar" 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`: + +```typescript +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: + +```markdown +--- +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: true` frontmatter 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:** ```markdown @@ -289,19 +331,19 @@ 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 | +| File | What to update | +| ----------------------------------- | -------------------------------------------------------------------------------------------------------- | +| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions, right sidebar configuration | +| `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 diff --git a/convex/pages.ts b/convex/pages.ts index 3a4275b..278e468 100644 --- a/convex/pages.ts +++ b/convex/pages.ts @@ -19,6 +19,7 @@ export const getAllPages = query({ authorName: v.optional(v.string()), authorImage: v.optional(v.string()), layout: v.optional(v.string()), + rightSidebar: v.optional(v.boolean()), }), ), handler: async (ctx) => { @@ -55,6 +56,7 @@ export const getAllPages = query({ authorName: page.authorName, authorImage: page.authorImage, layout: page.layout, + rightSidebar: page.rightSidebar, })); }, }); @@ -119,6 +121,7 @@ export const getPageBySlug = query({ authorName: v.optional(v.string()), authorImage: v.optional(v.string()), layout: v.optional(v.string()), + rightSidebar: v.optional(v.boolean()), }), v.null(), ), @@ -147,6 +150,7 @@ export const getPageBySlug = query({ authorName: page.authorName, authorImage: page.authorImage, layout: page.layout, + rightSidebar: page.rightSidebar, }; }, }); @@ -169,6 +173,7 @@ export const syncPagesPublic = mutation({ authorName: v.optional(v.string()), authorImage: v.optional(v.string()), layout: v.optional(v.string()), + rightSidebar: v.optional(v.boolean()), }), ), }, @@ -208,6 +213,7 @@ export const syncPagesPublic = mutation({ authorName: page.authorName, authorImage: page.authorImage, layout: page.layout, + rightSidebar: page.rightSidebar, lastSyncedAt: now, }); updated++; diff --git a/convex/posts.ts b/convex/posts.ts index 9958e9e..43faf52 100644 --- a/convex/posts.ts +++ b/convex/posts.ts @@ -21,6 +21,8 @@ export const getAllPosts = query({ featuredOrder: v.optional(v.number()), authorName: v.optional(v.string()), authorImage: v.optional(v.string()), + layout: v.optional(v.string()), + rightSidebar: v.optional(v.boolean()), }), ), handler: async (ctx) => { @@ -51,6 +53,8 @@ export const getAllPosts = query({ featuredOrder: post.featuredOrder, authorName: post.authorName, authorImage: post.authorImage, + layout: post.layout, + rightSidebar: post.rightSidebar, })); }, }); @@ -120,6 +124,7 @@ export const getPostBySlug = query({ authorName: v.optional(v.string()), authorImage: v.optional(v.string()), layout: v.optional(v.string()), + rightSidebar: v.optional(v.boolean()), }), v.null(), ), @@ -151,6 +156,7 @@ export const getPostBySlug = query({ authorName: post.authorName, authorImage: post.authorImage, layout: post.layout, + rightSidebar: post.rightSidebar, }; }, }); @@ -175,6 +181,7 @@ export const syncPosts = internalMutation({ authorName: v.optional(v.string()), authorImage: v.optional(v.string()), layout: v.optional(v.string()), + rightSidebar: v.optional(v.boolean()), }), ), }, @@ -216,6 +223,7 @@ export const syncPosts = internalMutation({ authorName: post.authorName, authorImage: post.authorImage, layout: post.layout, + rightSidebar: post.rightSidebar, lastSyncedAt: now, }); updated++; @@ -261,6 +269,7 @@ export const syncPostsPublic = mutation({ authorName: v.optional(v.string()), authorImage: v.optional(v.string()), layout: v.optional(v.string()), + rightSidebar: v.optional(v.boolean()), }), ), }, @@ -302,6 +311,7 @@ export const syncPostsPublic = mutation({ authorName: post.authorName, authorImage: post.authorImage, layout: post.layout, + rightSidebar: post.rightSidebar, lastSyncedAt: now, }); updated++; diff --git a/convex/schema.ts b/convex/schema.ts index 5e28d86..eb6ee4d 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -19,6 +19,7 @@ export default defineSchema({ authorName: v.optional(v.string()), // Author display name authorImage: v.optional(v.string()), // Author avatar image URL (round) layout: v.optional(v.string()), // Layout type: "sidebar" for docs-style layout + rightSidebar: v.optional(v.boolean()), // Enable right sidebar with CopyPageDropdown lastSyncedAt: v.number(), }) .index("by_slug", ["slug"]) @@ -49,11 +50,12 @@ export default defineSchema({ authorName: v.optional(v.string()), // Author display name authorImage: v.optional(v.string()), // Author avatar image URL (round) layout: v.optional(v.string()), // Layout type: "sidebar" for docs-style layout + rightSidebar: v.optional(v.boolean()), // Enable right sidebar with CopyPageDropdown lastSyncedAt: v.number(), }) - .index("by_slug", ["slug"]) - .index("by_published", ["published"]) - .index("by_featured", ["featured"]) + .index("by_slug", ["slug"]) + .index("by_published", ["published"]) + .index("by_featured", ["featured"]) .searchIndex("search_content", { searchField: "content", filterFields: ["published"], diff --git a/files.md b/files.md index f045e78..58cf0e2 100644 --- a/files.md +++ b/files.md @@ -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 with homepage post limit and read more link, GitHub contributions, nav order, inner page logo settings, hardcoded navigation items for React routes, GitHub repository config for AI service raw URLs, font family configuration) | +| `siteConfig.ts` | Centralized site configuration (name, logo, blog page, posts display with homepage post limit and read more link, GitHub contributions, nav order, inner page logo settings, hardcoded navigation items for React routes, GitHub repository config for AI service raw URLs, font family configuration, right sidebar configuration) | ### Pages (`src/pages/`) @@ -41,7 +41,7 @@ A brief description of each file in the codebase. | ----------- | ----------------------------------------------------------------- | | `Home.tsx` | Landing page with featured content and optional post list. Supports configurable post limit (homePostsLimit) and optional "read more" link (homePostsReadMore) via siteConfig.postsDisplay | | `Blog.tsx` | Dedicated blog page with post list or card grid view (configurable via siteConfig.blogPage, supports view toggle). Includes back button in navigation | -| `Post.tsx` | Individual blog post or page view with optional sidebar layout. Includes back button, CopyPageDropdown, tag links, and related posts section in footer for blog posts (update SITE_URL/SITE_NAME when forking) | +| `Post.tsx` | Individual blog post or page view with optional left sidebar (TOC) and right sidebar (CopyPageDropdown). Includes back button, tag links, and related posts section in footer for blog posts. Supports 3-column layout at 1135px+ (update SITE_URL/SITE_NAME when forking) | | `Stats.tsx` | Real-time analytics dashboard with visitor stats and GitHub stars | | `TagPage.tsx` | Tag archive page displaying posts filtered by a specific tag. Includes view mode toggle (list/cards) with localStorage persistence | | `Write.tsx` | Three-column markdown writing page with Cursor docs-style UI, frontmatter reference with copy buttons, theme toggle, font switcher (serif/sans/monospace), and localStorage persistence (not linked in nav) | @@ -63,6 +63,7 @@ A brief description of each file in the codebase. | `GitHubContributions.tsx` | GitHub activity graph with theme-aware colors and year navigation | | `VisitorMap.tsx` | Real-time visitor location map with dotted world display and theme-aware colors | | `PageSidebar.tsx` | Collapsible table of contents sidebar for pages/posts with sidebar layout, extracts headings (H1-H6), active heading highlighting, smooth scroll navigation, localStorage persistence for expanded/collapsed state | +| `RightSidebar.tsx` | Right sidebar component that displays CopyPageDropdown on posts/pages at 1135px+ viewport width, controlled by siteConfig.rightSidebar.enabled and frontmatter rightSidebar field | ### Context (`src/context/`) @@ -88,7 +89,7 @@ A brief description of each file in the codebase. | File | Description | | ------------ | ------------------------------------------------------------------------------------ | -| `global.css` | Global CSS with theme variables, centralized font-size CSS variables for all themes, sidebar styling with alternate background colors, hidden scrollbar, and consistent borders using box-shadow for docs-style layout | +| `global.css` | Global CSS with theme variables, centralized font-size CSS variables for all themes, sidebar styling with alternate background colors, hidden scrollbar, and consistent borders using box-shadow for docs-style layout. Left sidebar (`.post-sidebar-wrapper`) and right sidebar (`.post-sidebar-right`) have separate, independent styles | ## Convex Backend (`convex/`) @@ -157,6 +158,7 @@ Markdown files for static pages like About, Projects, Contact, Changelog. | `featuredOrder` | Order in featured section (optional) | | `authorName` | Author display name (optional) | | `authorImage` | Round author avatar image URL (optional) | +| `rightSidebar` | Enable right sidebar with CopyPageDropdown (optional) | ## Scripts (`scripts/`) diff --git a/public/images/1225-changelog.png b/public/images/1225-changelog.png index efd8de9..67c1e08 100644 Binary files a/public/images/1225-changelog.png and b/public/images/1225-changelog.png differ diff --git a/public/images/forkconfig.png b/public/images/forkconfig.png index e32e39f..d76fcb3 100644 Binary files a/public/images/forkconfig.png and b/public/images/forkconfig.png differ diff --git a/public/images/logos/firecrawl.svg b/public/images/logos/firecrawl.svg new file mode 100644 index 0000000..8e61cbd --- /dev/null +++ b/public/images/logos/firecrawl.svg @@ -0,0 +1,12 @@ + diff --git a/public/images/setupguide.png b/public/images/setupguide.png index ccc0774..d76fcb3 100644 Binary files a/public/images/setupguide.png and b/public/images/setupguide.png differ diff --git a/public/llms.txt b/public/llms.txt index 2e3671c..2848adc 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -1,6 +1,6 @@ # llms.txt - Information for AI assistants and LLMs # Learn more: https://llmstxt.org/ -# Last updated: 2025-12-25T08:46:57.373Z +# Last updated: 2025-12-25T20:18:52.317Z > Your content is instantly available to browsers, LLMs, and AI agents. diff --git a/public/raw/about.md b/public/raw/about.md index afb7221..ad89e0a 100644 --- a/public/raw/about.md +++ b/public/raw/about.md @@ -2,7 +2,7 @@ --- Type: page -Date: 2025-12-25 +Date: 2025-12-26 --- An open-source publishing framework built for AI agents and developers to ship websites, docs, or blogs.. Write markdown, sync from the terminal. Your content is instantly available to browsers, LLMs, and AI agents. Built on Convex and Netlify. diff --git a/public/raw/changelog.md b/public/raw/changelog.md index a7505b7..6a37681 100644 --- a/public/raw/changelog.md +++ b/public/raw/changelog.md @@ -2,12 +2,35 @@ --- Type: page -Date: 2025-12-25 +Date: 2025-12-26 --- All notable changes to this project.  +## v1.29.0 + +Released December 25, 2025 + +**Font family configuration system** + +- Font family configuration via siteConfig.ts + - Three font options: "serif" (New York), "sans" (system fonts), "monospace" (IBM Plex Mono) + - Configure default font in `src/config/siteConfig.ts` with `fontFamily` option + - Font preference persists in localStorage across page reloads + - SiteConfig default font overrides localStorage when siteConfig changes +- Monospace font option added + - Added monospace to FONT SWITCHER options in global.css + - Uses "IBM Plex Mono", "Liberation Mono", ui-monospace, monospace + - Write page font switcher now cycles through all three options +- Fork configuration support + - Added fontFamily field to fork-config.json.example + - Automated fork configuration script supports fontFamily option + +Updated files: `src/config/siteConfig.ts`, `src/context/FontContext.tsx`, `src/main.tsx`, `src/pages/Write.tsx`, `src/styles/global.css`, `scripts/configure-fork.ts`, `fork-config.json.example` + +Documentation updated: `content/blog/setup-guide.md`, `content/pages/docs.md`, `files.md` + ## v1.28.2 Released December 25, 2025 diff --git a/public/raw/contact.md b/public/raw/contact.md index d7a5199..05e6544 100644 --- a/public/raw/contact.md +++ b/public/raw/contact.md @@ -2,7 +2,7 @@ --- Type: page -Date: 2025-12-25 +Date: 2025-12-26 --- You found the contact page. Nice diff --git a/public/raw/docs.md b/public/raw/docs.md index f936c18..6bc319a 100644 --- a/public/raw/docs.md +++ b/public/raw/docs.md @@ -2,7 +2,7 @@ --- Type: page -Date: 2025-12-25 +Date: 2025-12-26 --- Reference documentation for setting up, customizing, and deploying this markdown framework. @@ -128,20 +128,21 @@ order: 1 Content here... ``` -| Field | Required | Description | -| --------------- | -------- | ------------------------------------------------- | -| `title` | Yes | Nav link text | -| `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 | -| `featuredOrder` | No | Order in featured (lower = first) | -| `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 | +| Field | Required | Description | +| --------------- | -------- | ----------------------------------------------------------------------------- | +| `title` | Yes | Nav link text | +| `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 | +| `featuredOrder` | No | Order in featured (lower = first) | +| `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 | +| `rightSidebar` | No | Enable right sidebar with CopyPageDropdown (opt-in, requires explicit `true`) | **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. @@ -178,6 +179,46 @@ layout: "sidebar" 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`: + +```typescript +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: + +```markdown +--- +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: true` frontmatter 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:** ```markdown @@ -288,19 +329,19 @@ 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 | +| File | What to update | +| ----------------------------------- | -------------------------------------------------------------------------------------------------------- | +| `src/config/siteConfig.ts` | Site name, title, intro, bio, blog page, logo gallery, GitHub contributions, right sidebar configuration | +| `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 diff --git a/public/raw/projects.md b/public/raw/projects.md index 1cc83ad..caa8f71 100644 --- a/public/raw/projects.md +++ b/public/raw/projects.md @@ -2,7 +2,7 @@ --- Type: page -Date: 2025-12-25 +Date: 2025-12-26 --- This markdown framework is open source and built to be extended. Here is what ships out of the box. diff --git a/public/raw/setup-guide.md b/public/raw/setup-guide.md index 631b182..693b12c 100644 --- a/public/raw/setup-guide.md +++ b/public/raw/setup-guide.md @@ -339,6 +339,7 @@ Your markdown content here... | `featuredOrder` | No | Order in featured section (lower = first) | | `authorName` | No | Author display name shown next to date | | `authorImage` | No | Round author avatar image URL | +| `rightSidebar` | No | Enable right sidebar with CopyPageDropdown (opt-in, requires explicit `true`) | ### How Frontmatter Works @@ -1037,6 +1038,8 @@ Pages appear automatically in the navigation when published. **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. +**Right sidebar:** When enabled in `siteConfig.rightSidebar.enabled`, posts and pages can display a right sidebar containing the CopyPageDropdown at 1135px+ viewport width. Add `rightSidebar: true` to frontmatter to enable. Without this field, pages render normally with CopyPageDropdown in the nav bar. When enabled, CopyPageDropdown moves from the navigation bar to the right sidebar on wide screens. The right sidebar is hidden below 1135px, and CopyPageDropdown returns to the nav bar automatically. + ### Update SEO Meta Tags Edit `index.html` to update: diff --git a/scripts/sync-posts.ts b/scripts/sync-posts.ts index 49f7d6d..14f81ee 100644 --- a/scripts/sync-posts.ts +++ b/scripts/sync-posts.ts @@ -37,6 +37,7 @@ interface PostFrontmatter { authorName?: string; // Author display name authorImage?: string; // Author avatar image URL (round) layout?: string; // Layout type: "sidebar" for docs-style layout + rightSidebar?: boolean; // Enable right sidebar with CopyPageDropdown (default: true when siteConfig.rightSidebar.enabled) } interface ParsedPost { @@ -55,6 +56,7 @@ interface ParsedPost { authorName?: string; // Author display name authorImage?: string; // Author avatar image URL (round) layout?: string; // Layout type: "sidebar" for docs-style layout + rightSidebar?: boolean; // Enable right sidebar with CopyPageDropdown (default: true when siteConfig.rightSidebar.enabled) } // Page frontmatter (for static pages like About, Projects, Contact) @@ -71,6 +73,7 @@ interface PageFrontmatter { authorName?: string; // Author display name authorImage?: string; // Author avatar image URL (round) layout?: string; // Layout type: "sidebar" for docs-style layout + rightSidebar?: boolean; // Enable right sidebar with CopyPageDropdown (default: true when siteConfig.rightSidebar.enabled) } interface ParsedPage { @@ -87,6 +90,7 @@ interface ParsedPage { authorName?: string; // Author display name authorImage?: string; // Author avatar image URL (round) layout?: string; // Layout type: "sidebar" for docs-style layout + rightSidebar?: boolean; // Enable right sidebar with CopyPageDropdown (default: true when siteConfig.rightSidebar.enabled) } // Calculate reading time based on word count @@ -127,6 +131,7 @@ function parseMarkdownFile(filePath: string): ParsedPost | null { authorName: frontmatter.authorName, // Author display name authorImage: frontmatter.authorImage, // Author avatar image URL layout: frontmatter.layout, // Layout type: "sidebar" for docs-style layout + rightSidebar: frontmatter.rightSidebar, // Enable right sidebar with CopyPageDropdown }; } catch (error) { console.error(`Error parsing ${filePath}:`, error); @@ -178,6 +183,7 @@ function parsePageFile(filePath: string): ParsedPage | null { authorName: frontmatter.authorName, // Author display name authorImage: frontmatter.authorImage, // Author avatar image URL layout: frontmatter.layout, // Layout type: "sidebar" for docs-style layout + rightSidebar: frontmatter.rightSidebar, // Enable right sidebar with CopyPageDropdown }; } catch (error) { console.error(`Error parsing page ${filePath}:`, error); diff --git a/src/components/RightSidebar.tsx b/src/components/RightSidebar.tsx new file mode 100644 index 0000000..a8b51d9 --- /dev/null +++ b/src/components/RightSidebar.tsx @@ -0,0 +1,23 @@ +import CopyPageDropdown from "./CopyPageDropdown"; + +interface RightSidebarProps { + title: string; + content: string; + url: string; + slug: string; + description?: string; + date?: string; + tags?: string[]; + readTime?: string; +} + +export default function RightSidebar(props: RightSidebarProps) { + return ( + + ); +} + diff --git a/src/config/siteConfig.ts b/src/config/siteConfig.ts index c020634..cd96311 100644 --- a/src/config/siteConfig.ts +++ b/src/config/siteConfig.ts @@ -80,6 +80,13 @@ export interface GitHubRepoConfig { // default font family options: "serif" (New York), "sans" (system fonts), "monospace" (IBM Plex Mono) export type FontFamily = "serif" | "sans" | "monospace"; +// Right sidebar configuration +// Shows CopyPageDropdown in a right sidebar on posts/pages at 1135px+ viewport width +export interface RightSidebarConfig { + enabled: boolean; // Enable/disable the right sidebar globally + minWidth?: number; // Minimum viewport width to show sidebar (default: 1135) +} + // Site configuration interface export interface SiteConfig { // Basic site info @@ -126,6 +133,9 @@ export interface SiteConfig { // GitHub repository configuration for AI service links gitHubRepo: GitHubRepoConfig; + + // Right sidebar configuration + rightSidebar: RightSidebarConfig; } // Default site configuration @@ -271,6 +281,14 @@ export const siteConfig: SiteConfig = { branch: "main", // Default branch contentPath: "public/raw", // Path to raw markdown files }, + + // Right sidebar configuration + // Shows CopyPageDropdown in a right sidebar on posts/pages at 1135px+ viewport width + // When enabled, CopyPageDropdown moves from nav to right sidebar on wide screens + rightSidebar: { + enabled: true, // Set to false to disable right sidebar globally + minWidth: 1135, // Minimum viewport width in pixels to show sidebar + }, }; // Export the config as default for easy importing diff --git a/src/pages/Post.tsx b/src/pages/Post.tsx index 92c60fe..fa0cb71 100644 --- a/src/pages/Post.tsx +++ b/src/pages/Post.tsx @@ -4,11 +4,13 @@ import { api } from "../../convex/_generated/api"; import BlogPost from "../components/BlogPost"; import CopyPageDropdown from "../components/CopyPageDropdown"; import PageSidebar from "../components/PageSidebar"; +import RightSidebar from "../components/RightSidebar"; import { extractHeadings } from "../utils/extractHeadings"; import { useSidebar } from "../context/SidebarContext"; import { format, parseISO } from "date-fns"; import { ArrowLeft, Link as LinkIcon, Twitter, Rss, Tag } from "lucide-react"; import { useState, useEffect } from "react"; +import siteConfig from "../config/siteConfig"; // Site configuration const SITE_URL = "https://markdown.fast"; @@ -185,35 +187,40 @@ export default function Post() { if (page) { // Extract headings for sidebar TOC (only for pages with layout: "sidebar") const headings = page.layout === "sidebar" ? extractHeadings(page.content) : []; - const hasSidebar = headings.length > 0; + const hasLeftSidebar = headings.length > 0; + // Check if right sidebar is enabled (only when explicitly set in frontmatter) + const hasRightSidebar = siteConfig.rightSidebar.enabled && page.rightSidebar === true; + const hasAnySidebar = hasLeftSidebar || hasRightSidebar; return ( -