mirror of
https://github.com/waynesutton/markdown-site.git
synced 2026-01-12 04:09:14 +00:00
v1.18.1 - CopyPageDropdown raw markdown URLs
- AI services (ChatGPT, Claude, Perplexity) now receive /raw/{slug}.md URLs
- Direct access to clean markdown content for better AI parsing
- No HTML parsing required by AI services
- Renamed buildUrlFromPageUrl to buildUrlFromRawMarkdown
v1.19.0 - Author display for posts and pages
- New optional authorName and authorImage frontmatter fields
- Round avatar image displayed next to date and read time
- Works on individual post and page views
- Write page updated with new field reference
v1.19.1 - GitHub Stars on Stats page
- Live star count from waynesutton/markdown-site repository
- Fetches from GitHub public API (no token required)
- Stats page now displays 6 cards with responsive grid
Documentation updates
- Frontmatter Flow section added to docs.md, setup-guide.md, files.md
- How frontmatter works with step-by-step processing flow
- Instructions for adding new frontmatter fields
Updated files:
- src/components/CopyPageDropdown.tsx
- src/pages/Stats.tsx
- src/pages/Post.tsx
- src/pages/Write.tsx
- src/styles/global.css
- convex/schema.ts
- convex/posts.ts
- convex/pages.ts
- scripts/sync-posts.ts
- content/blog/setup-guide.md
- content/pages/docs.md
- content/pages/changelog-page.md
- files.md
- README.md
- TASK.md
- changelog.md
- AGENTS.md
95 lines
3.1 KiB
TypeScript
95 lines
3.1 KiB
TypeScript
import { defineSchema, defineTable } from "convex/server";
|
|
import { v } from "convex/values";
|
|
|
|
export default defineSchema({
|
|
// Blog posts table
|
|
posts: defineTable({
|
|
slug: v.string(),
|
|
title: v.string(),
|
|
description: v.string(),
|
|
content: v.string(),
|
|
date: v.string(),
|
|
published: v.boolean(),
|
|
tags: v.array(v.string()),
|
|
readTime: v.optional(v.string()),
|
|
image: v.optional(v.string()), // Header/OG image URL
|
|
excerpt: v.optional(v.string()), // Short excerpt for card view
|
|
featured: v.optional(v.boolean()), // Show in featured section
|
|
featuredOrder: v.optional(v.number()), // Order in featured section (lower = first)
|
|
authorName: v.optional(v.string()), // Author display name
|
|
authorImage: v.optional(v.string()), // Author avatar image URL (round)
|
|
lastSyncedAt: v.number(),
|
|
})
|
|
.index("by_slug", ["slug"])
|
|
.index("by_date", ["date"])
|
|
.index("by_published", ["published"])
|
|
.index("by_featured", ["featured"])
|
|
.searchIndex("search_content", {
|
|
searchField: "content",
|
|
filterFields: ["published"],
|
|
})
|
|
.searchIndex("search_title", {
|
|
searchField: "title",
|
|
filterFields: ["published"],
|
|
}),
|
|
|
|
// Static pages (about, projects, contact, etc.)
|
|
pages: defineTable({
|
|
slug: v.string(),
|
|
title: v.string(),
|
|
content: v.string(),
|
|
published: v.boolean(),
|
|
order: v.optional(v.number()), // Display order in nav
|
|
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
|
|
featuredOrder: v.optional(v.number()), // Order in featured section (lower = first)
|
|
authorName: v.optional(v.string()), // Author display name
|
|
authorImage: v.optional(v.string()), // Author avatar image URL (round)
|
|
lastSyncedAt: v.number(),
|
|
})
|
|
.index("by_slug", ["slug"])
|
|
.index("by_published", ["published"])
|
|
.index("by_featured", ["featured"])
|
|
.searchIndex("search_content", {
|
|
searchField: "content",
|
|
filterFields: ["published"],
|
|
})
|
|
.searchIndex("search_title", {
|
|
searchField: "title",
|
|
filterFields: ["published"],
|
|
}),
|
|
|
|
// View counts for analytics
|
|
viewCounts: defineTable({
|
|
slug: v.string(),
|
|
count: v.number(),
|
|
}).index("by_slug", ["slug"]),
|
|
|
|
// Site configuration (about content, links, etc.)
|
|
siteConfig: defineTable({
|
|
key: v.string(),
|
|
value: v.any(),
|
|
}).index("by_key", ["key"]),
|
|
|
|
// Page view events for analytics (event records pattern)
|
|
pageViews: defineTable({
|
|
path: v.string(),
|
|
pageType: v.string(), // "blog" | "page" | "home" | "stats"
|
|
sessionId: v.string(),
|
|
timestamp: v.number(),
|
|
})
|
|
.index("by_path", ["path"])
|
|
.index("by_timestamp", ["timestamp"])
|
|
.index("by_session_path", ["sessionId", "path"]),
|
|
|
|
// Active sessions for real-time visitor tracking
|
|
activeSessions: defineTable({
|
|
sessionId: v.string(),
|
|
currentPath: v.string(),
|
|
lastSeen: v.number(),
|
|
})
|
|
.index("by_sessionId", ["sessionId"])
|
|
.index("by_lastSeen", ["lastSeen"]),
|
|
});
|