A brief description of each file in the codebase.
- Modified
src/App.tsx:AgentReadyWidgetnow opts into the@waynesutton/agent-ready@0.2.4desktop collapse feature withdesktopCollapse={true}, while keeping the existing mobile collapse behavior. - New file
prds/agent-ready-desktop-collapse-widget.md: PRD documenting the desktop collapse widget feature, host app wiring, edge cases, and verification steps.
- Modified
src/App.tsx:AgentReadyWidgetnow opts into the@waynesutton/agent-ready@0.2.0mobile collapse feature withmobileCollapse,mobileBreakpoint={480}, anddefaultMobileCollapsed, keeping desktop behavior unchanged. - New file
prds/agent-ready-mobile-widget.md: PRD documenting the package mobile feature, host app wiring, edge cases, and verification steps.
- Modified
src/components/FeaturedCards.tsx: Accepts optionalfeaturedPosts/featuredPagesprops from parent to avoid duplicate subscriptions. SkipsgetAllPosts/getAllPagesqueries when in frontmatter mode (the default). MoveduseItemsModecalculation above hook calls so skip logic works correctly. - Modified
src/pages/Home.tsx: Passes already-fetched featured data to FeaturedCards as props, eliminating 2 duplicate subscriptions. SkipsisCurrentUserAuthenticatedquery unless?dashboardNotice=not-adminparam is present. - Modified
src/components/Layout.tsx: SkipsgetDocsPages/getDocsPostssubscriptions whensiteConfig.docsSection.enabledis false. - New file
prds/reduce-subscription-noise.md: PRD documenting the production log noise investigation, auth cycling analysis (confirmed not a convex-auth bug), subscription audit table, and three targeted fixes that reduce home page subscriptions from 11-15 to ~7.
- Modified
scripts/configure-fork.ts: Added support for all fork-config.json fields (statsPage,imageLightbox,semanticSearch,dashboard,mcpServer,newsletter,contactForm,newsletterAdmin,aiChat,askAI). Updated canonical URL and hreflang link updates inindex.html. Changed final "Next steps" text from Netlify to Convex self-hosted deploy. Updated llms.txt template hosting reference. - Modified
packages/create-markdown-sync/package.json: Movedvitefrom runtime to devDependencies. Bumped@types/nodeto^22.0.0. - Modified
index.html: Updated all meta descriptions from "Built on Convex and Netlify" to "Built on Convex". - Modified
convex/http.ts: Updated API response descriptions from "Built on Convex and Netlify" to "Built on Convex". - Modified
convex/rss.ts: Updated RSS feed description from "Built on Convex and Netlify" to "Built on Convex". - Modified
scripts/sync-discovery-files.ts: Updated project overview and llms.txt description templates to reference Convex instead of Netlify. - New file
prds/setup-fork-install-audit.md: PRD documenting the audit scope and changes.
- Modified
convex/convex.config.ts: Registered@waynesutton/agent-ready,@convex-dev/crons, and@convex-dev/workpoolcomponents. - Modified
convex/http.ts: AddedregisterAgentReadyRoutesimport from@waynesutton/agent-readyand mounted routes withskipRoutes: ["/sitemap.xml"]to avoid conflict with the app's existing dynamic sitemap. - Modified
src/App.tsx: AddedAgentReadyWidgetandUpdateBannerfrom@waynesutton/agent-ready/react. Widget renders floating bottom-right with dark theme. Widget URL resolver usesVITE_SITE_URLin production,VITE_CONVEX_SITE_URLon localhost, andwindow.location.originas fallback to prevent dev Convex URLs from leaking into production bundles. - Modified
package.json: Added@waynesutton/agent-ready@0.1.7,@convex-dev/crons, and@convex-dev/workpooldependencies. - Modified
.gitignore: Addedagent-ready.config.json(deployment-specific, generated by CLI wizard). - Modified
.env.production.local: AddedVITE_SITE_URL=https://www.markdown.fastfor correct widget URLs in production builds. - New file
agent-ready.config.json: Component config with 28 pages, 16 API endpoints,appUrl: https://www.markdown.fast, analytics, Claude AI descriptions,fullTxtEnabled: true, andsitemapEnabled: false. - New file
convex/agentReady/content.ts: Scaffolded wrapper bridging agent-ready component API to browser clients. - New file
convex/agentReady/analytics.ts: Scaffolded wrapper for agent analytics queries. - New file
prds/agent-ready-route-conflicts.md: PRD documenting the/sitemap.xmlroute conflict between agent-ready and apps with existing sitemap routes, proposedskipRoutesoption and config flags (shipped in 0.1.7). - New file
prds/agent-ready-improvements.md: PRD with 7 component improvement suggestions: auto-discover pages/endpoints, content sync hooks, sections/categories schema, URL resolution, fullTxtEnabled default, route conflict warnings, and widget prompt URL fix. - New file
prds/agent-ready-widget-url-feedback.md: PRD documenting the widget URL mismatch where production builds baked in the dev Convex site URL, proposed component-side fixes, and the host app workaround.
- Modified
convex/http.ts: Keeps the app-owned dynamic/sitemap.xmlroute and passesskipRoutes: ["/sitemap.xml"]toregisterAgentReadyRoutes()so Convex HTTP route registration no longer fails with "Path '/sitemap.xml' for method GET already in use". - Modified
agent-ready.config.json: AddedsitemapEnabled: falseso agent-ready configuration matches the app's route ownership.
- Modified
content/blog/convex-first-architecture.md: Added the Convex Static Hosting component link, documented how@convex-dev/self-hostingmaps toconvex/convex.config.ts,convex/staticHosting.ts, andconvex/http.ts, and updated dashboard admin setup to preferDASHBOARD_PRIMARY_ADMIN_EMAIL. - Modified
content/pages/docs-deployment.md: Added the Convex Static Hosting component link, two-step deploy option, Robel Auth production env table, browser auth entrypoint note, and troubleshooting for?code=...GitHub callback failures. - Modified
content/pages/docs.md: Addednpm run deployto the production command list and linked the Convex Static Hosting component from the docs landing page.
- Modified
convex/auth.ts: Switched to lowercase factory functionspassword()andgithub({ clientId, clientSecret })from@robelest/convex-auth/providers. Dropped manual GitHub profile callback andarcticimport — preview.30 ships first-party providers with built-in profile fetch. ImportscreateAuthfrom@robelest/convex-auth/serverbecause the/componententry's d.ts does not re-export it in preview.30. - Modified
convex/dashboardAuth.ts:isDashboardAdmin()now treatsDASHBOARD_PRIMARY_ADMIN_EMAILas the sole admin gate when set. The env value is read at request time viagetStrictDashboardAdminEmail()so Convex env updates are not held in a stale module-level constant. ThedashboardAdminstable is bypassed entirely in strict mode. Table fallback only runs when the env var is unset, preserving multi-admin forks. - Modified
convex/authAdmin.ts: AddedgetCurrentDashboardAuthDebugfor safe denied-state diagnostics (identityEmail, auth component user email, strict admin email, admin boolean). AddedstrictAdminEmailConfiguredtogetAuthSetupStatusso strict mode never falls into first-admin bootstrap. - New file
src/utils/convexAuthClient.ts: Shared Robel auth client helper. Stores one auth client perConvexReactClientin aWeakMap, types it withInferClientApi<typeof convexAuth>, and imports from@robelest/convex-auth/browserso browser storage, URL handling, passkey adapters, andConvexHttpClientdefaults are active. - Modified
src/pages/Dashboard.tsx: AddedDeniedAccessDemocomponent that renders<DashboardContent isDemo />with a top banner showing the signed-in GitHub email, expected strict admin email, and a "Sign out and retry" button. Bothconvex-authandworkosmodes route non-admin authenticated users to this demo view instead of redirecting. UsesgetConvexAuthClient()for demo sign-in, denied sign-out, and dashboard sign-out so the OAuth callback code is verified once. Narrowed GitHub sign-in result onresult.kind === "redirect"for the newSignInResultshape. Removed unusedsourceDetailquery. - Modified
src/AppWithWorkOS.tsx: UsesgetConvexAuthClient()during app auth bootstrap. Normal OAuth callback cleanup is left to@robelest/convex-auththroughhandleCodeFlow(). A guarded stale-callback cleanup waits five seconds and removes old?code=params only if the user remains unauthenticated, so failed old callbacks do not poison retries. - Modified
src/pages/Home.tsx: UsesgetConvexAuthClient()for dashboard notice sign-out. - Modified
src/styles/global.css: Added denied-dashboard banner styling. - Modified
src/components/Layout.tsx,src/pages/Home.tsx, andsrc/pages/Post.tsx: Removed unsupported ReactfetchPriorityimage props that caused DOM warnings. - Modified
package.json: Bumped@robelest/convex-authto^0.0.4-preview.30. Removed directarcticdependency. - Modified
.cursor/skills/robel-auth/SKILL.md: Reality check section updated for preview.30 (lowercase factories shipped,arcticno longer required for GitHub,password()is a factory,clientimport paths). Added "Denied session pattern" section documenting the sign-out + render-denied-UI flow for app-level allowlists. - Modified
convex/wiki.ts: Removed unusedConvexErrorimport. - Modified
scripts/sync-wiki.ts: Prefixed unusedsourceparameter with_. - New file
prds/robel-auth-browser-oauth-callback-fix.md: Debugging PRD documenting the GitHub OAuth callback failure, why@robelest/convex-auth/browserwas required, the Ed25519 env key requirements, singleton auth client pattern, stale callback cleanup guard, and future app checklist. - New file
prds/robel-auth-preview-30-and-admin-lockdown.md: PRD documenting the upgrade and admin email lockdown.
- Modified
convex/auth.ts: Migrated from class-basedAuth+PortaltocreateAuthfactory. Usesnew Password()(now a class, needsnew) and keepsOAuth(new GitHub(...), { profile })witharcticsince the preview package does not ship a first-partygithub()provider yet. Removed unusedPortalexports. - Modified
package.json: Bumped@robelest/convex-authfrom0.0.3-preview.11to0.0.4-preview.25. Keptarctic@^3.7.0becauseOAuthstill depends on arcticOAuth2Tokens. - Modified
src/AppWithWorkOS.tsx: Importedapifrom../convex/_generated/apiand passedapi: api.authtocreateConvexAuthClient. SPA mode now throws without it. - Modified
src/pages/Home.tsx: Addedapi: api.authtocreateConvexAuthClientcall inhandleSignOut. - Modified
src/pages/Dashboard.tsx: Addedapi: api.authto bothcreateConvexAuthClientcalls (memoized demo auth client andhandleDashboardSignOut). - Modified
.cursor/skills/robel-auth/SKILL.md: Added "Published package reality check" section that documents the drift betweenauth.estifanos.comdocs and the published preview. Lists PascalCase exports, class-vs-factory behavior, missing first-party providers, and the requiredapi: api.authclient argument. - Modified
prds/lessons.md: Logged lesson about trustingnode_modulesexports over docs for preview packages.
- Modified
src/pages/Dashboard.tsx: AddedDEMO_POST_FIELDSandDEMO_PAGE_FIELDSarrays that match whatconvex/demo.tsaccepts. ExtendedgenerateWriteTemplate(type, isDemo)to emit demo-safe templates with a 30-minute reset note. Frontmatter picker and clear/reset handlers now honor theisDemoflag so demo users cannot write fields that would be silently dropped (navbar, featured, docs sections, layout).
- New file
src/components/SlidePresentation.tsx: Fullscreen slide presentation component. Splits markdown on---into slides, renders each with ReactMarkdown, handles keyboard navigation (arrows, space, escape, home, end), progress bar, slide counter, and portal-based overlay. - New file
content/blog/markdown-slides.md: Blog post documenting the markdown slides feature - New file
content/blog/slide-template-example.md: Working slide template with 10 slides demonstrating code blocks, tables, images, blockquotes, and keyboard shortcuts (hasslides: truefrontmatter) - New file
prds/markdown-slides.md: PRD for the markdown slides feature - Modified
convex/schema.ts: Addedslides: v.optional(v.boolean())to posts and pages tables - Modified
convex/posts.ts: AddedslidestosyncPostsPublicmutation validator - Modified
convex/pages.ts: AddedslidestosyncPagesPublicmutation validator - Modified
scripts/sync-posts.ts: AddedslidestoPostFrontmatter,ParsedPost,PageFrontmatter,ParsedPageinterfaces and both parse functions - Modified
src/pages/Post.tsx: Added Present button in post/page headers whenslides: true, importsSlidePresentationcomponent, renders overlay on click - Modified
src/styles/global.css: Added slide presentation styles (.slide-overlay,.slide-toolbar,.slide-viewport,.slide-content,.slide-nav,.slide-present-btn, responsive breakpoints)
- New file
convex/rateLimits.ts: Centralized rate limit definitions for 19 endpoints across 4 tiers using@convex-dev/rate-limitercomponent. IncludescheckHttpRateLimitinternal mutation bridge for HTTP action rate limiting. - Modified
convex/convex.config.ts: Added@convex-dev/rate-limitercomponent import and registration - Modified
convex/http.ts: Added rate limit checks to 14 HTTP routes (raw markdown, RSS, RSS full, sitemap, API posts, API post, API export, KB list, KB pages, KB page, VFS tree, VFS exec) with 429 responses andRetry-Afterheaders - Modified
convex/askAI.node.ts: AddedaskAiStreamrate limit check (per-user, Tier 1) after auth verification - Modified
convex/sources.ts: AddedsourceIngestrate limit check (per-user, Tier 1) - Modified
convex/wikiJobs.ts: AddedwikiCompileandwikiLintrate limit checks (per-user, Tier 1) - Modified
convex/aiImageJobs.ts: AddedaiImageGenrate limit check (per-user, Tier 1) - Modified
convex/aiChats.ts: AddedaiChatResponserate limit check (per-user, Tier 1) - Modified
convex/stats.ts: AddedheartbeatandpageViewrate limit checks (per-session, Tier 3, silent fail) - Modified
convex/newsletter.ts: AddednewsletterSubscriberate limit check (global, Tier 3) - Modified
convex-virtual-fs/README.md: Added rate limiting section with@convex-dev/rate-limiterintegration pattern
- Modified
src/components/SocialFooter.tsx: Addedllms.txtandAGENTS.mdlinks between social icons and copyright, using Robot and FileText Phosphor icons with monospace font - Modified
src/styles/global.css: Added.social-footer-ai-linksand.social-footer-ai-linkstyles with subtle opacity, hover states, and mobile responsive centering - Modified
scripts/sync-discovery-files.ts: Now queries wiki pages from Convex viaapi.wiki.listWikiPages, includes wiki page listings grouped by category in bothllms.txtandAGENTS.md, copiesAGENTS.mdtopublic/AGENTS.mdfor web access at/AGENTS.md
- New file
content/blog/wiki-knowledge-bases-and-virtual-filesystem.md: Feature blog post covering LLM wiki, knowledge bases, VFS, and demo mode - New file
public/images/wiki-kb-vfs.svg: SVG featured image graphic (dark, three connected feature boxes) - Modified
convex/wikiCompiler.ts: COMPILATION_MODEL changed fromgpt-4otogpt-4.1-mini - Modified
convex/aiChatActions.ts: Model validator and AIModel type updated togpt-4.1-mini - Modified
convex/aiChats.ts: Model validator updated togpt-4.1-mini - Modified
convex/askAI.node.ts: Model check and API call updated togpt-4.1-mini - Modified
src/config/siteConfig.ts: textModels and askAI models updated togpt-4.1-mini - Modified
src/components/AIChatView.tsx: Model type cast and comment updated - Modified
src/pages/Dashboard.tsx: Wiki section copy updated to GPT-4.1 mini - Modified
fork-config.json.example: AI model references updated - Modified
FORK_CONFIG.md: All GPT-4o references replaced with GPT-4.1 mini - Modified
packages/create-markdown-sync/src/configure.ts: Default model configs updated - Modified
README.md: Features section rewritten, "Recent updates" refreshed - Modified
AGENTS.md: Key features list expanded, wiki access patterns section added - Modified
content/pages/home.md: Tagline rewritten with wikis/KBs, link to docs section - Modified
content/pages/docs.md: "Accessing wiki data" section added - Modified
content/pages/docs-dashboard.md: Wiki access patterns added, "Hourly" fixed to "every 30 minutes" - Modified
content/pages/about.md: 30-minute cleanup detail added to demo mode - Modified 8 content markdown files: All GPT-4o references updated to GPT-4.1 mini
- Modified
convex/crons.ts:- Demo cleanup interval changed from 1 hour to 30 minutes
- Modified
convex/demo.ts:- Error messages updated from "every hour" to "every 30 minutes"
createDemoPostandcreateDemoPagenow setdemo: truelistAllPostsandlistAllPagesreturn validators includedemofield
- Modified
convex/schema.ts:- Added
demo: v.optional(v.boolean())to posts and pages tables - Updated source field comments from "hourly" to "every 30 minutes"
- Added
- Modified
src/pages/Dashboard.tsx:- Demo banner updated with 30-minute message, admin note, and fork repo link
- Added
wikiShowInNavconfig state and checkbox toggle - Wiki entry added to generated
hardcodedNavItems
- Modified
src/pages/Wiki.tsx:- Removed inline styles from right sidebar graph title
- Modified
src/styles/global.css:- Wiki nav items: removed
white-space: nowrap, addedoverflow-wrap: anywherefor long name wrapping - Wiki card: added
min-width: 0andoverflow: hiddenfor text containment - Wiki left sidebar: restyled to match docs sidebar (border-right, uppercase header, left border active state, group dividers)
- Wiki right sidebar TOC: restyled to match docs TOC (label with bottom border, items with left border accent)
- Wiki nav items: removed
- Modified
content/pages/home.md: Updated demo mode description to "every 30 minutes" - Modified
content/pages/docs.md: Updated demo cleanup frequency
- New file
convex/knowledgeBases.ts:- CRUD mutations/queries for knowledge base containers (create, update, remove, list, getBySlug)
- Public listing respects visibility (public KBs visible to all, private KBs require auth)
- Internal queries for HTTP API:
listPublicKbsForApi,getBySlugInternal,getByIdInternal,listPagesForKb,getPageInKb updatePageCountinternal mutation for sync/upload completion
- New file
convex/kbUpload.ts:uploadFilespublic mutation for uploading markdown files to a KBprocessUploadedFilesinternal mutation parses markdown, extracts frontmatter/title/category/backlinks, upserts wiki pagesgetUploadJobStatuspublic query for tracking upload progress- File limits: 50KB per file, 100 files per upload batch
- Supports Obsidian vault style folder categories and
[[wiki-link]]backlink extraction
- Modified
convex/schema.ts:- Added
knowledgeBasestable (slug, title, description, ownerSubject, visibility, apiEnabled, apiVisibility, sourceType, pageCount, lastCompiledAt) - Added
kbUploadJobstable (kbId, ownerSubject, status, fileCount, processedCount, error) - Added optional
kbIdfield towikiPages,wikiIndex,wikiCompilationJobsfor KB-scoped content - Added
by_kbid_and_slugcompound index onwikiPages,by_kbid_and_keyonwikiIndex
- Added
- Modified
convex/wiki.ts:- All public queries (
listWikiPages,getGraphData,syncWikiPages) accept optionalkbIdarg - New
searchWikiPagespublic query with full-text search scoped by kbId syncWikiPagesnow supports KB-scoped sync withkbIdparam
- All public queries (
- Modified
convex/http.ts:- Added
/api/kbendpoint: list all public KBs with API enabled - Added
/api/kb/pages?slug=<kb-slug>endpoint: list pages in a KB - Added
/api/kb/page?kb=<kb-slug>&slug=<page-slug>endpoint: get single page content - Per-KB API endpoints respect
apiEnabledandapiVisibilitysettings
- Added
- Modified
src/pages/Dashboard.tsx:- Added "Knowledge Bases" section in Knowledge nav group
- KB create form, list view with visibility/API toggles, file upload panel, delete
- Imported
FolderOpen,UploadSimplePhosphor icons
- Modified
src/pages/Wiki.tsx:- Added KB switcher dropdown in left sidebar
- Queries scoped by
activeKbIdstate - Imported
Idtype from Convex dataModel
- Modified
src/styles/global.css:- Added
.wiki-kb-switcherstyles for the KB dropdown
- Added
- Modified
scripts/sync-wiki.ts:- Added
--kb=<id>CLI flag for KB-scoped sync
- Added
- New file
prds/knowledge-bases.md:- PRD for the knowledge bases feature
- New file
scripts/sync-wiki.ts:- Reads all markdown from
content/blog/andcontent/pages/ - Converts published posts/pages to wiki pages with inferred type, category, backlinks
- Batches in groups of 20 for Convex mutation limits
- Supports
SYNC_ENV=productionfor production deployments
- Reads all markdown from
- Modified
convex/wiki.ts:- Added public
syncWikiPagesmutation for CLI sync - Auth signal for convex-doctor compliance
- Upserts pages and regenerates wiki index in one transaction
- Added public
- Modified
package.json:- Added
sync:wikiandsync:wiki:prodscripts - Updated
sync:allandsync:all:prodto include wiki sync
- Added
- New file
convex/demo.ts:- Public mutations for demo CRUD:
createDemoPost,createDemoPage,updateDemoPost,updateDemoPage,deleteDemoPost,deleteDemoPage - Content sanitization function strips scripts, iframes, event handlers, javascript: URLs, data: URLs
- Slug enforcement: all demo slugs auto-prefixed with
demo- - Content length limit: 50KB max per post/page
- Demo item cap: 50 per table to prevent abuse
isDemoContentpublic query for frontend checkscleanupDemoContentinternal mutation for hourly cron cleanup
- Public mutations for demo CRUD:
- New file
prds/anonymous-demo-mode.md:- PRD documenting the demo mode feature design, security boundaries, and verification steps
- Modified
convex/schema.ts:- Extended
sourcefield union onpostsandpagestables to include"demo"
- Extended
- Modified
convex/crons.ts:- Added hourly
cleanup demo contentcron callinginternal.demo.cleanupDemoContent
- Added hourly
- Modified
convex/posts.ts:- Updated sync to skip
source: "demo"rows (alongside existing"dashboard"skip) - Updated delete-on-sync to preserve demo rows
- Updated sync to skip
- Modified
convex/pages.ts:- Same sync skip and delete protection for demo pages
- Modified
src/pages/Dashboard.tsx:DashboardContentacceptsisDemoprop for demo mode gating- Demo mode shows restricted sidebar (Content, Create, Wiki only)
- Demo banner with amber styling at top of dashboard
- Post/page lists show demo badge, hide edit/delete for admin content in demo mode
- WriteSection uses demo mutations when
isDemois true - DemoSignInButton component for GitHub sign-in from sidebar footer
- Sync buttons hidden in header for demo users
- Modified
src/components/Layout.tsx:- Added "Dashboard" text label next to SignIn icon in desktop and mobile nav
- Modified
src/styles/global.css:- Added
.source-badge.demo(amber),.dashboard-demo-banner,.dashboard-nav-link,.dashboard-icon-labelstyles
- Added
- Modified
convex-doctor.toml:- Added
convex/demo.tsto ignore list (intentionally unauthenticated mutations)
- Added
- New file
convex/virtualFs.ts:- Virtual filesystem with shell command emulation (ls, cat, grep, find, tree, head, wc, pwd, cd)
- Shared helper functions (
buildPathTreeHelper,readFileHelper,grepContentHelper) used by both registered queries andexecuteCommand - Supports
/blog,/pages,/docs,/sources, and/wikidirectories - Uses Convex search indexes for grep coarse filtering with regex refinement
- New file
convex/sources.ts:- Queued job pattern for source ingestion with
requestIngestSourcepublic mutation - Public queries:
listSources,getSourceBySlug,getIngestJobStatus - Internal mutations:
insertSourceFromScrape,markProcessedAndFinalize,finalizeIngestJob
- Queued job pattern for source ingestion with
- New file
convex/sourceActions.ts:- Node.js actions for Firecrawl URL scraping and OpenAI embedding generation
scrapeAndProcessSourceandprocessSourceinternal actions
- New file
convex/wiki.ts:- Wiki page CRUD with
listWikiPages,getWikiPageBySlug,getWikiIndexpublic queries batchUpsertAndRegenerateIndexcombines page upserts, index generation, and optional job finalization in one transactionlintAndStoreReportreads pages, checks quality, and writes lint report in one transactionmarkRunningAndGetContextmarks a compilation job running and returns all site content in one transaction
- Wiki page CRUD with
- New file
convex/wikiCompiler.ts:- Node.js action for LLM-driven wiki compilation using GPT-4o
- Generates embeddings for each wiki page, then batch upserts all pages in one mutation
lintWikiaction checks backlinks, content length, and titles
- New file
convex/wikiJobs.ts:- Queued job pattern for wiki compilation and linting
requestCompilationandrequestLintpublic mutations with authscheduledCompilationinternal mutation for cron triggers
- New content page
content/pages/wiki-resources.md:- Reference links for virtual filesystem and LLM wiki features
- New PRD
prds/virtual-filesystem.md:- Three-phase plan: Virtual Filesystem, Source Ingest, Wiki Compilation
- Modified
convex/schema.ts:- Added 5 tables:
sources,sourceIngestJobs,wikiPages,wikiIndex,wikiCompilationJobs
- Added 5 tables:
- Modified
convex/http.ts:- Added
/vfs/tree(GET) and/vfs/exec(POST) routes with OPTIONS preflight handlers
- Added
- Modified
convex/crons.ts:- Added daily wiki compilation cron at 4:00 AM UTC
- Modified
src/pages/Dashboard.tsx:- Added
SourcesSectioncomponent: URL ingest form, source list with status, content preview - Added
WikiSectioncomponent: compile/lint buttons with job polling, lint report, wiki pages list with rendered markdown, backlink navigation, wiki index - Added "Knowledge" sidebar section with Sources and Wiki nav items
- Added
Database,BookOpen,TreeStructure,GlobePhosphor icon imports - Extended
DashboardSectiontype with"sources"and"wiki"
- Added
- New blog post
content/blog/convex-doctor-score-42-to-100.md:- Featured post about the convex-doctor journey from 42/100 to 100/100
- Includes before/after image, benchmark screenshot, and final 100/100 screenshot
- Covers what convex-doctor is, the 17 pass remediation, and AI model usage (Claude Opus 4.6, GPT Codex 5.3)
- New images in
public/images/:convex-doctor-before-after.png(generated comparison graphic)convex-doctor-100.png(100/100 score screenshot)convex-doctor-benchmarks.png(benchmark leaderboard screenshot)
- Reverted
.unique()to.first()inconvex/authAdmin.tsandconvex/dashboardAuth.ts:- The
.unique()conversions caused runtime errors when duplicatedashboardAdminsrows existed for the same subject or email .first()is the correct call here since the data can have multiple matching rows
- The
- Deleted convex-doctor PRD files from
prds/:- Removed
convex-doctor-remediation.md,convex-doctor-second-pass.md,convex-doctor-third-pass.md,convex-doctor-fourth-pass.md,convex-doctor-fifth-pass.md,convex-doctor-sixth-pass.md,convex-doctor-seventh-pass.md,convex-doctor-eighth-pass.md,convex-doctor-tenth-pass.md,convex-doctor-twelfth-pass.md,convex-doctor-fifteenth-pass.md,convex-doctor-sixteenth-pass.md,convex-doctor-seventeenth-pass.md - Remaining PRDs in
prds/convex-doctor/: eighth, ninth, eleventh, thirteenth, fourteenth pass files
- Removed
- New skill
.cursor/skills/convex-doctor/SKILL.md: Codifies the full convex-doctor workflow - New rule
.cursor/rules/convex-doctor.mdc: Always-on rule for convex-doctor awareness
- Storage FK index in
convex/schema.ts:- Added
by_storageidindex onaiImageGenerationJobsfor the_storageforeign key field
- Added
- Contact email helpers in
convex/contactActions.ts:- Extracted
buildContactHtmlandbuildContactTextto reduce handler size
- Extracted
- Stats helpers in
convex/stats.ts:- Extracted
updatePageViewAggregates,buildPageStats,collectVisitorLocations,getTopPathStats
- Extracted
- Doctor config in
convex-doctor.toml:- Added 7 rule suppressions for by-design patterns (auth awareness, schema nesting, optional fields, ordered
.first()picks, domain files, multi-step handlers)
- Added 7 rule suppressions for by-design patterns (auth awareness, schema nesting, optional fields, ordered
convex-doctorscore reached 100/100 with 0 errors, 0 warnings, 18 infos (up from 92/100 with 39 warnings)
- Semantic search batching in
convex/semanticSearch.ts,convex/semanticSearchQueries.ts,convex/semanticSearchJobs.ts, andconvex/askAI.node.ts:- Merged
fetchPostsByIds+fetchPagesByIdsintofetchSearchDocsByIds(one transaction for both tables) - Merged
completeSemanticSearchJob+failSemanticSearchJobintofinalizeSemanticSearchJob(one mutation for both outcomes) semanticSearchJobhandler uses afinalizehelper to centralize mutation calls, droppingctx.run*from 7 to 4askAI.node.tsalso uses the batchedfetchSearchDocsByIds
- Merged
- Auth component helper conversion in
convex/authComponent.ts,convex/authAdmin.ts, andconvex/dashboardAuth.ts:authUserGetByIdHelperandauthUserListHelperare now plain async functions (not registeredinternalQuery)- Callers import the helpers directly and share the same transaction, eliminating
perf/helper-vs-run
convex-doctorscore improved from 91/100 (43 warnings) to 92/100 (39 warnings)
- Newsletter action batching in
convex/newsletter.tsandconvex/newsletterActions.ts:- Added
getPostNewsletterSendContextInternalsosendPostNewsletterloads sent status, subscribers, and published post fields in one internal query
- Added
- Auth component indirection in
convex/authComponent.ts,convex/authAdmin.ts, andconvex/dashboardAuth.ts:- Call sites use
internal.authComponent.authUserListandinternal.authComponent.authUserGetByIdinstead ofcomponents.auth.public.*
- Call sites use
- View count uniqueness in
convex/posts.ts:viewCountsslug lookups now use.unique()to match one counter document per slug
- Convex doctor config in
convex-doctor.toml:- Ignores generated sources and the auth forwarder file, disables
correctness/generated-code-modified, and bringsconvex-doctorto 91/100 with 0 errors
- Ignores generated sources and the auth forwarder file, disables
-
Queued import worker cleanup in
convex/importJobs.tsandconvex/importAction.ts:- Passed the queued import snapshot directly into the scheduled worker and collapsed imported post creation plus job completion into one internal mutation
- Routed repeated import failure writes through a helper, which improved
convex-doctorfrom85/100with1 error / 54 warningsto86/100with1 error / 49 warnings
-
Markdown export helper extraction in
convex/cms.ts:- Moved post and page frontmatter assembly into shared helpers so the export queries stay smaller while preserving the same markdown structure
- AI action structural cleanup in
convex/aiChats.ts,convex/aiChatActions.ts,convex/aiImageJobs.ts, andconvex/aiImageGeneration.ts:- Reworked queued AI chat and image actions to run from scheduler-provided snapshots instead of re-querying persisted state inside the action
- Collapsed success and failure writes into single internal finalizers and removed the remaining
replacecall from the image-generation job flow - Improved
convex-doctorfrom84/100with1 error / 60 warningsto85/100with1 error / 54 warnings
- Queued semantic search flow in
convex/schema.ts,convex/semanticSearch.ts,convex/semanticSearchJobs.ts, andsrc/components/SearchModal.tsx:- Replaced the direct semantic search browser action with a persisted job flow that keeps the modal reactive while removing that public action path
- Added follow-up auth-awareness to
recordPageView,heartbeat, andversions.isEnabled, which pushedconvex-doctorto84/100with1 error / 60 warnings
-
Direct upload URL cleanup in
convex/media.ts,src/components/ImageUploadModal.tsx, andsrc/components/MediaLibrary.tsx:- Replaced browser
resolveDirectUploadcalls with the existinggetDirectStorageUrlquery and made the old action internal-only - Preserved the current upload UX while removing the targeted browser-action warning
- Replaced browser
-
Search and newsletter warning cleanup in
convex/search.tsandconvex/newsletter.ts:- Added auth-awareness to keyword search and tightened newsletter sent-post slug lookups to
.unique() - Improved
convex-doctorfrom80/100with1 error / 68 warningsto81/100with1 error / 64 warnings
- Added auth-awareness to keyword search and tightened newsletter sent-post slug lookups to
-
Queued URL import flow in
convex/schema.ts,convex/importJobs.ts,convex/importAction.ts, andsrc/pages/Dashboard.tsx:- Replaced the direct Dashboard
importFromUrlbrowser action with a persisted import-job flow that reports pending, success, and failure reactively - Kept the slug-conflict fallback and Firecrawl error handling while removing the targeted public action warning
- Replaced the direct Dashboard
-
Final safe config-key unique cleanup in
convex/versions.ts:- Replaced the remaining
versionControlSettings.by_key.first()lookup ingetStatswith.unique() convex-doctorremoved theimportFromUrlwarning and held findings at1 error / 68 warnings
- Replaced the remaining
-
Unique lookup tightening in
convex/versions.ts,convex/cms.ts,convex/newsletter.ts,convex/dashboardAuth.ts, andconvex/embeddingsQueries.ts:- Replaced
.first()with.unique()only for keys the app clearly treats as unique by design, like config keys, slugs, subscriber email, and admin identifiers - Left counter and event-style lookups untouched where duplicates are plausible
- Replaced
-
Public warning cleanup in
convex/files.tsandconvex/posts.ts:- Moved
setFileExpirationto an internal action and added auth-awareness toincrementViewCount - Held
convex-doctorat81/100while reducing findings from1 error / 84 warningsto1 error / 68 warnings
- Moved
-
RSS helper-wrapper cleanup in
convex/rss.tsandconvex/http.ts:- Replaced exported
httpAction(...)RSS handlers with plain helper functions wrapped at route registration time - Preserved the existing
/rss.xmland/rss-full.xmloutput while clearing the legacy handler warning
- Replaced exported
-
Internal-only media download helper in
convex/files.ts:- Moved
getDownloadUrlfrom a public action to an internal action since the app does not currently call it from the browser - Improved
convex-doctorfrom78/100to81/100and reduced findings to1 error / 84 warnings
- Moved
-
Batched sync version snapshots in
convex/versions.ts,convex/posts.ts, andconvex/pages.ts:- Added
createVersionsBatchand changed sync mutations to queue one snapshot batch instead of scheduling inside per-item loops - Keeps version history behavior intact while removing another scheduler anti-pattern
- Added
-
Mutation-based media commits in
convex/files.ts,src/components/ImageUploadModal.tsx, andsrc/components/MediaLibrary.tsx:- Converted
commitFilefrom a browser-called action into a mutation and updated the upload UIs to useuseMutation - Added explicit return validators for file list, info, download, delete, expiration, and count flows
- Converted
-
Final safe collect caps for this pass in
convex/posts.ts,convex/pages.ts,convex/newsletter.ts, andconvex/authAdmin.ts:- Replaced the remaining pass-targeted internal and admin
.collect()reads with explicit high.take(...)limits - Improved
convex-doctorfrom67/100to78/100and reduced findings to1 error / 89 warnings
- Replaced the remaining pass-targeted internal and admin
-
Queued embedding refresh entrypoints in
convex/embeddingsAdmin.ts,convex/embeddings.ts, andscripts/sync-posts.ts:- Replaced direct public embedding actions with queued mutations that schedule internal embedding work
- Keeps sync behavior intact while removing more client-to-action anti-patterns
-
Sync mutation auth-awareness in
convex/posts.tsandconvex/pages.ts:- Added non-blocking
ctx.auth.getUserIdentity()checks to the public content sync mutations - Reduces false-positive unauthenticated write warnings without forcing login for the sync flow
- Added non-blocking
-
Ask AI HTTP handler cleanup in
convex/askAI.node.ts,convex/http.ts, andconvex/askAI.ts:- Moved stream handlers to plain helpers wrapped during HTTP route registration
- Added a return validator to
getStreamBodyusingv.any()for the component-managed stream body shape - Improved
convex-doctorfrom66/100to67/100and reduced warnings to98
-
Queued image generation jobs in
convex/schema.ts,convex/aiImageJobs.ts, andconvex/aiImageGeneration.ts:- Added a persisted
aiImageGenerationJobstable and moved image generation to a mutation-scheduled internal action flow - Tracks pending, completed, and failed image generation state without exposing a direct browser action
- Added a persisted
-
Reactive Dashboard image flow in
src/pages/Dashboard.tsx:- The Dashboard now requests an image job and subscribes to the job record for loading, success, and error state
- Existing generated-image delete and download behavior stays intact
-
Bounded public list reads in
convex/posts.ts,convex/pages.ts,convex/newsletter.ts,convex/stats.ts, andconvex/authAdmin.ts:- Replaced the remaining pass-targeted public
.collect()list reads with explicit.take(...)limits - Reduced
convex-doctorfindings from28 errors / 130 warningsto17 errors / 110 warningsduring this pass
- Replaced the remaining pass-targeted public
-
AI chat action refactor in
convex/aiChatActions.ts:- Split
generateResponseinto focused helper functions for prompt building, attachment enrichment, URL resolution, message formatting, and provider calls - Reduced the main action handler from 209 lines to 69 lines while preserving chat behavior
- Split
-
Storage URL query cleanup in
convex/aiChatActions.tsandconvex/aiChats.ts:- Removed the extra
getStorageUrlsBatchquery hop and resolved storage URLs directly inside the action - Deleted the now-unused
getStorageUrlsBatchinternal query fromconvex/aiChats.ts
- Removed the extra
-
Public utility auth-awareness cleanup in
convex/embeddings.ts,convex/files.ts, andconvex/newsletter.ts:- Added non-breaking
ctx.auth.getUserIdentity()checks to public maintenance and utility flows - Reduced
convex-doctorsecurity noise on intentional public entry points
- Added non-breaking
-
Fourth-pass remediation PRD at
prds/convex-doctor-fourth-pass.md:- Documents the AI chat action refactor scope, edge cases, and verification results for this pass
-
Query-shape cleanup in
convex/posts.ts,convex/pages.ts, andconvex/stats.ts:- Replaced remaining safe
.filter()pipelines after.collect()with explicit iteration - Kept the public query response shapes and sort behavior unchanged
- Replaced remaining safe
-
Safe unique lookup tightening across
convex/posts.ts,convex/pages.ts,convex/askAI.ts,convex/aiChats.ts,convex/authAdmin.ts, andconvex/stats.ts:- Converted unique-by-design indexed lookups from
.first()to.unique() - Tightened slug, stream id, session/context, storage id, and admin identifier lookups
- Converted unique-by-design indexed lookups from
-
Public flow auth-awareness cleanup in
convex/authAdmin.ts,convex/contact.ts, andconvex/embeddings.ts:- Added non-breaking
ctx.auth.getUserIdentity()checks to intentional public setup/contact flows - Helps
convex-doctordistinguish public bootstrap endpoints from accidental unauthenticated write paths
- Added non-breaking
-
Third-pass remediation PRD at
prds/convex-doctor-third-pass.md:- Documents the final third-pass cleanup scope, edge cases, and verification results
-
Queued AI chat generation across
convex/aiChats.ts,convex/aiChatActions.ts, andsrc/components/AIChatView.tsx:- Browser AI chat requests now go through
aiChats.requestAIResponseinstead of calling a public action directly - Assistant generation runs in an internal action and persists
generatingandlastErrorstate on the chat document - Chat ownership is now tied to the authenticated user for safer multi-user behavior
- Browser AI chat requests now go through
-
Ask AI session ownership hardening in
convex/askAI.tsandconvex/askAI.node.ts:- Ask AI stream sessions now store the authenticated owner subject
- Stream body and streaming POST access validate ownership before returning content
-
Public HTTP endpoint CORS cleanup in
convex/http.ts:- Added explicit
OPTIONShandlers for public routes flagged during the secondconvex-doctorpass - Keeps browser and external client preflight behavior explicit and consistent
- Added explicit
-
Deterministic post query sorting in
convex/posts.ts:- Replaced
new Date(...)sorting inside queries with ISO string comparison helper logic - Removes non-deterministic query warnings while preserving descending date order
- Replaced
-
Second-pass remediation PRD at
prds/convex-doctor-second-pass.md:- Documents the follow-up plan, edge cases, and verification steps for the deeper cleanup pass
- Added Rybbit analytics script in
index.html:- Added
<script src="https://app.rybbit.io/api/script.js" data-site-id="24731ca420a4" defer>before closing</body>tag - Script loads with
deferattribute to avoid blocking page rendering - Enables Rybbit analytics tracking for the site
- Added
-
Validated open issue #7 context:
- Confirmed WSL 2 setup failure path was still possible in manual onboarding docs
- Applied docs-only fix so standard non-WSL flow stays unchanged
-
Added WSL 2 fallback flow in
content/blog/setup-guide.md:- Added manual login command:
npx convex login --no-open --login-flow paste - Added first-run setup command:
npx convex dev --once - Clarified that standard
npx convex devcan be run after successful initialization
- Added manual login command:
-
Added WSL 2 fallback in
README.mdsetup:- Added equivalent fallback commands for browser auth issues in WSL 2
- Keeps existing standard setup flow unchanged for macOS and Linux users with normal browser integration
-
Removed unused variables in
convex/stats.ts:- Removed unused
pathsWithCountsarray declaration - Removed unused
allPathsFromAggregatevariable fromuniquePaths.sum()call - Cleaned up dead code that was never executed
- Removed unused
-
Fixed HTML attribute casing in multiple React components:
- Changed
fetchprioritytofetchPriority(React uses camelCase for DOM attributes) - Fixed in
src/components/Layout.tsx(logo image) - Fixed in
src/pages/Home.tsx(logo image) - Fixed in
src/pages/Post.tsx(4 header images for posts and pages)
- Changed
- Added missing CSS border-radius variables in
src/styles/global.css:- Added
--border-radius-sm: 4px,--border-radius-md: 6px,--border-radius-lg: 8pxto:root - These variables were referenced but undefined, causing inconsistent button styling
- Dashboard mode toggles (Markdown/Rich Text/Preview) now have consistent 6px border radius
- All action buttons across Write page and Dashboard now match
- Added
-
Fixed Media Library upload and preview in
src/components/MediaLibrary.tsx:- Added
RecentUploadinterface andrecentUploadsstate to track uploads fromconvexandr2providers - After upload, resolved URL is captured and shown with image preview and MD/HTML/URL copy buttons
- Recent uploads persist to
sessionStorageso they survive page refreshes within the tab session - Image previews use the real Convex storage URL (not ephemeral blob URLs)
- Usage text is now dynamic based on active provider (Bunny CDN, ConvexFS, R2, or Convex storage)
- Added
dismissRecentfunction to remove items from recent uploads list
- Added
-
Fixed ImageUploadModal Media Library tab in
src/components/ImageUploadModal.tsx:- Removed
isBunnyConfiguredgate from Media Library tab (only requiresconvexfsprovider now) - Removed unused
configStatusquery andisBunnyConfiguredvariable
- Removed
-
Fixed image preview clipping in
src/styles/global.css:- Changed
.media-item-previewfromaspect-ratio: 1+object-fit: covertoaspect-ratio: 4/3+object-fit: contain - Full image visible without cropping
- Added
.media-recent-uploadsCSS for recent uploads heading
- Changed
-
Added React Router v7 future flags in
src/main.tsx:- Added
v7_startTransitionandv7_relativeSplatPathtoBrowserRouterfuture prop - Eliminates React Router deprecation warnings in console
- Added
-
Removed unused logo preload in
index.html:- Removed
<link rel="preload" href="/images/logo.svg">that caused console warning when logo not used
- Removed
-
Increased backend dedup window in
convex/stats.ts:- Changed
HEARTBEAT_DEDUP_MSfrom 20s to 45s - Backend now rejects duplicate heartbeats within 45 seconds regardless of path
- Changed
-
Increased frontend timing in
src/hooks/usePageTracking.ts:- Changed
HEARTBEAT_INTERVAL_MSfrom 30s to 45s - Changed
HEARTBEAT_DEBOUNCE_MSfrom 20s to 45s
- Changed
-
Added BroadcastChannel cross-tab coordination in
src/hooks/usePageTracking.ts:- Only "leader" tab sends heartbeats to prevent parallel mutations
- Tab leadership election via
claimmessages - Automatic handoff when tabs close via
closemessages heartbeat_sentmessages notify other tabs to update their timestamp- Fallback to existing behavior when BroadcastChannel not supported
-
Complete stats disable when config disabled in
src/hooks/usePageTracking.ts:- Added
isStatsEnabledcheck insidesendHeartbeatcallback - When
statsPage.enabled: false, all heartbeat-related code paths are skipped
- Added
-
Updated app-specific patterns in
.cursor/rules/convex-write-conflicts.mdc:- Updated example code to reflect new 45s timing values
- Added BroadcastChannel coordination pattern documentation
- Updated key patterns list with cross-tab coordination
-
New PRD at
prds/fix-heartbeat-write-conflicts.md:- Documents problem, root cause, solution, and verification steps
-
Stats tracking respects
statsPage.enabledconfig insrc/hooks/usePageTracking.ts:- Added check for
siteConfig.statsPage?.enabledbefore any DB writes - All page view recording and heartbeat tracking disabled when stats is disabled
- No database operations occur when
statsPage.enabled: false
- Added check for
-
Removed full table scan fallback in
convex/stats.ts:- Eliminated expensive
allPageViewscollection that scanned entirepageViewstable - Now trusts aggregate counts directly (O(log n) instead of O(n))
- Uses limited scan of 1000 recent views to find active paths
- Eliminated expensive
-
Added unique paths aggregate in
convex/convex.config.tsandconvex/stats.ts:- New
uniquePathsaggregate component tracks distinct paths viewed - Updated
recordPageViewto insert intouniquePathsaggregate - Updated backfill function to populate
uniquePaths - Returns
totalPathscount for UI display
- New
-
Paginated pageStats to top 50 in
convex/stats.ts:- Added
PAGE_STATS_LIMIT = 50constant - Returns only top 50 pages by views instead of all paths
- Sorts by views descending before limiting
- Added
-
Updated Stats page UI in
src/pages/Stats.tsxandsrc/styles/global.css:- Section title changed to "Top Pages by Views"
- Shows "(showing X of Y)" when more paths exist than displayed
- Added
.stats-section-subtitleCSS class
- Updated deploy flow in
package.jsonto use@convex-dev/self-hostingCLI-driven build/deploy commands. - Added setup/deploy check scripts:
scripts/validate-env.tsfor local/env readiness checksscripts/verify-deploy.tsfor endpoint smoke checks after deploy
- Added auth readiness query in
convex/authAdmin.ts(getAuthSetupStatus) and first-admin setup guidance UI insrc/pages/Dashboard.tsx. - Updated one-click docs in
README.mdandFORK_CONFIG.mdto include GitHub template and CLI setup paths. - Updated content docs wording for Convex-first default while preserving legacy compatibility notes in:
content/pages/about.mdcontent/pages/docs.mdcontent/pages/docs-content.mdcontent/pages/footer.md
-
Fixed auth client initialization in
src/AppWithWorkOS.tsx:- Created
ConvexAuthWrappercomponent that properly initializes the auth client - Auth client now correctly calls
convex.setAuth()to provide tokens to Convex - Added loading state to prevent flash of unauthenticated content
- Created
-
Fixed email lookup from auth component in
convex/dashboardAuth.ts:@robelest/convex-authJWT tokens only include subject (userId|sessionId), not email- Added
getUserEmailFromAuthComponent()to look up email fromcomponents.auth.public.userGetById - Added
extractUserId()helper to parse userId from "userId|sessionId" format - Admin matching now works correctly with email-based entries in
dashboardAdminstable
-
Dashboard auth UX improvements in
src/pages/Dashboard.tsx:- Sign out now works correctly in convex-auth mode using
authClient.signOut() - Removed false "Dashboard access is open" warning when auth is enabled
- Non-admin users are redirected to home with dismissible notice
- Sign out now works correctly in convex-auth mode using
-
Admin management improvements:
DASHBOARD_PRIMARY_ADMIN_EMAILenv var provides optional strict email gate- Bootstrap admin via
authAdmin:bootstrapDashboardAdminwith secret key - Debug queries added for troubleshooting:
debugCurrentIdentity,debugListAllAdmins
-
Version control crash fix in
convex/versions.ts:- Replaced full
contentVersionstable scan with index-based oldest/newest lookups - Prevents Convex 16MB read limit errors on large datasets
- Replaced full
-
Documentation updates:
- Created
prds/adding-robel-auth.mdwith full migration guide - Updated
FORK_CONFIG.mdwith detailed admin setup instructions for fork users - Updated defaults/docs sync for dashboard nav option in
fork-config.json.example
- Created
-
Added dual-mode platform config in
src/config/siteConfig.tswithauth.mode,hosting.mode, andmedia.provider. -
Added dashboard admin backend model in
convex/schema.ts,convex/dashboardAuth.ts, andconvex/authAdmin.tswith grant/revoke/list/isCurrentUserDashboardAdmin functions. -
Enforced server-side dashboard admin checks in
convex/cms.ts,convex/posts.ts(listAll),convex/pages.ts(listAll),convex/newsletter.tsadmin endpoints,convex/versions.ts,convex/files.ts, andconvex/importAction.ts. -
Integrated Convex Auth and self-hosting scaffolding in
convex/auth.ts,convex/convex.config.ts,convex/staticHosting.ts, andconvex/http.tswhile retainingconvex/auth.config.tsfor WorkOS legacy mode. -
Added optional R2 and direct storage abstraction in
convex/r2.tsandconvex/media.ts, and updated dashboard upload components to supportconvex,convexfs, andr2providers. -
Refactored frontend auth bootstrap in
src/main.tsx,src/AppWithWorkOS.tsx,src/utils/workos.ts, andsrc/pages/Dashboard.tsxto support provider mode switching and admin-only dashboard access. -
Updated fork scaffolding defaults in
fork-config.json.example,FORK_CONFIG.md,scripts/configure-fork.ts, andpackages/create-markdown-sync/src/{wizard.ts,configure.ts}for new default architecture with legacy options. -
Aligned mode wording across
README.md,FORK_CONFIG.md, andfork-config.json.example:- Default mode:
convex-auth+convex-self-hosted+convex - Legacy mode:
workos+netlifywith optionalconvexfs/r2 - Local fallback mode:
auth.mode = "none"for non-production development
- Default mode:
-
Updated
FORK_CONFIG.mddashboard auth section to document admin-only dashboard access and grant commands (authAdmin:grantDashboardAdmin). -
Added
compat.legacyDocsexample setting infork-config.json.example. -
Re-ran migration verification checks:
npm run lint,npm run typecheck,npx convex codegen, andnpm run build. -
Replaced Quill-based rich text editor in
src/pages/Dashboard.tsxwith a simpler built-incontentEditableeditor and lightweight toolbar. -
Kept Markdown mode and Preview mode in Dashboard write flow, and preserved markdown <-> rich text conversion using existing Showdown and Turndown utilities.
-
Enabled image insertion in rich text mode using existing
ImageUploadModal. -
Removed Quill dependencies from root and CLI workspace
package.jsonfiles. -
Updated editor styling in
src/styles/global.cssto support the new simple rich text toolbar and editor surface. -
Fixed TypeScript errors in
src/components/AskAIModal.tsx,src/components/Layout.tsx,src/hooks/useSearchHighlighting.ts, andsrc/pages/Post.tsx. -
ESLint and production audit now pass with no vulnerabilities:
npm run lint-> passnpm run typecheck-> passnpm audit --omit=dev-> found 0 vulnerabilities
-
Performed runtime smoke test for Ask AI modal and docs navigation on local dev server.
| File | Description |
|---|---|
package.json |
Dependencies and scripts for the blog |
tsconfig.json |
TypeScript configuration |
vite.config.ts |
Vite bundler configuration |
index.html |
Main HTML entry with SEO meta tags, JSON-LD, critical CSS inline, resource hints, and Rybbit analytics |
netlify.toml |
Netlify deployment and Convex HTTP redirects |
README.md |
Project documentation (streamlined with links to docs) |
AGENTS.md |
AI coding agent instructions (agents.md spec) |
CLAUDE.md |
Claude Code instructions for project workflows |
files.md |
This file - codebase structure |
changelog.md |
Version history and changes |
convex-doctor.toml |
convex-doctor CLI config: ignore patterns and rule suppressions for by-design schema, auth, and architecture patterns. Seventeenth pass reached 100/100. |
TASK.md |
Task tracking and project status |
FORK_CONFIG.md |
Fork configuration guide (manual + automated options) |
fork-config.json.example |
Template JSON config for automated fork setup |
| File | Description |
|---|---|
main.tsx |
React app entry point with conditional WorkOS providers. When WorkOS is configured (VITE_WORKOS_CLIENT_ID and VITE_WORKOS_REDIRECT_URI set), wraps app with AuthKitProvider and ConvexProviderWithAuthKit. When WorkOS is not configured, uses standard ConvexProvider. Uses lazy loading and Suspense for optional WorkOS integration. Suspense fallback uses invisible div (minHeight: 100vh) to prevent "Loading..." text flash. |
App.tsx |
Main app component with routing (supports custom homepage configuration via siteConfig.homepage). Handles /callback route for WorkOS OAuth redirect. |
AppWithWorkOS.tsx |
Wrapper component for WorkOS-enabled app. Provides AuthKitProvider and ConvexProviderWithAuthKit. Only loaded when WorkOS is configured. |
vite-env.d.ts |
Vite environment type definitions |
| File | Description |
|---|---|
siteConfig.ts |
Centralized site configuration (name, logo, blog page, posts display with homepage post limit and read more link, featured section with configurable title via featuredTitle, 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, footer configuration with markdown support, social footer configuration, homepage configuration, AI chat configuration, aiDashboard configuration with multi-model support for text chat and image generation, newsletter configuration with admin and notifications, contact form configuration, weekly digest configuration, stats page configuration with public/private toggle, dashboard configuration with optional WorkOS authentication via requireAuth, image lightbox configuration with enabled toggle, semantic search configuration with enabled toggle and disabled by default to avoid blocking forks without OPENAI_API_KEY, twitter configuration for Twitter Cards meta tags, askAI configuration with enabled toggle, default model, and available models for header Ask AI feature, relatedPosts configuration with defaultViewMode and showViewToggle options) |
| File | Description |
|---|---|
Home.tsx |
Landing page with featured content and optional post list. Fetches home intro content from content/pages/home.md (slug: home-intro) for synced markdown intro text. Supports configurable post limit (homePostsLimit) and optional "read more" link (homePostsReadMore) via siteConfig.postsDisplay. Falls back to siteConfig.bio if home-intro page not found. Home intro content uses blog heading styles (blog-h1 through blog-h6) with clickable anchor links, matching blog post typography. Includes helper functions (generateSlug, getTextContent, HeadingAnchor) for heading ID generation and anchor links. Featured section title configurable via siteConfig.featuredTitle (default: "Get started:"). |
Blog.tsx |
Dedicated blog page with featured layout: hero post (first blogFeatured), featured row (remaining blogFeatured in 2 columns with excerpts), and regular posts (3 columns without excerpts). Supports list/card view toggle. Includes back button in navigation |
Post.tsx |
Individual blog post or page view with optional left sidebar (TOC) and right sidebar (CopyPageDropdown). Includes back button (hidden when used as homepage), tag links, related posts section in footer for blog posts with thumbnail/list view toggle, footer component with markdown support (fetches footer.md content from Convex), and social footer. Supports 3-column layout at 1135px+. Can display image at top when showImageAtTop: true. Can be used as custom homepage via siteConfig.homepage (update SITE_URL/SITE_NAME when forking). SEO: Dynamic canonical URL, hreflang tags, og:url consistency, and twitter:site meta tags. DOM order optimized for SEO (article before sidebar, CSS order for visual layout). Related posts view mode persists in localStorage. |
Stats.tsx |
Real-time analytics dashboard with visitor stats and GitHub stars. Configurable via siteConfig.statsPage to enable/disable public access and navigation visibility. Shows disabled message when enabled: false (similar to NewsletterAdmin pattern). |
DocsPage.tsx |
Docs landing page component for /docs route. Renders the page/post with docsLanding: true in DocsLayout. Fetches landing content via getDocsLandingPage and getDocsLandingPost queries. Includes Footer component (respects showFooter frontmatter), AI chat support (aiChatEnabled), and fallback to first docs item if no landing page is set. |
TagPage.tsx |
Tag archive page displaying posts filtered by a specific tag. Includes view mode toggle (list/cards) with localStorage persistence |
AuthorPage.tsx |
Author archive page displaying posts by a specific author. Includes view mode toggle (list/cards) with localStorage persistence. Author name clickable in posts links to this page. |
Write.tsx |
Three-column markdown writing page with Cursor docs-style UI, frontmatter reference with copy buttons, theme toggle, font switcher (serif/sans/monospace), localStorage persistence, and optional AI Agent mode (toggleable via siteConfig.aiChat.enabledOnWritePage). When enabled, Agent replaces the textarea with AIChatView component. Includes scroll prevention when switching to Agent mode to prevent page jump. Title changes to "Agent" when in AI chat mode. |
Dashboard.tsx |
Centralized dashboard at /dashboard for content management and site configuration. Cloud CMS Features: Direct database save ("Save to DB" button), source tracking (Dashboard vs Synced badges), delete confirmation modal with warning, CRUD operations for dashboard-created content, sync warning modal for synced content (warns that local file changes will overwrite dashboard edits with download/copy options). Content Management: Posts and Pages list views with filtering, search, pagination, items per page selector, source badges, delete buttons (dashboard content only); Post/Page editor with markdown editor, live preview, "Save Changes" button, draggable/resizable frontmatter sidebar (200px-600px), independent scrolling, download markdown, export to markdown, all 30+ frontmatter fields synchronized with schema; Write Post/Page sections with three editor modes (Markdown, Rich Text, Preview), full-screen writing interface. Rich Text Editor: lightweight contentEditable editor with simple toolbar (bold, italic, strike, headings, lists, quote), image insertion support, automatic HTML-to-Markdown conversion on mode switch, theme-aware styling. AI Agent: Tab-based UI for Chat and Image Generation, multi-model selector (Claude Sonnet 4, GPT-4o, Gemini 2.0 Flash), image generation with Nano Banana models, aspect ratio selection, download button, and MD/HTML copy options with code preview. Other Features: Newsletter management (all Newsletter Admin features integrated); Content import (direct database import via Firecrawl, no file sync needed); Site configuration (Config Generator UI with Version Control toggle); Index HTML editor; Analytics (real-time stats dashboard); Sync commands UI with sync server integration; Header sync buttons; Dashboard search; Toast notifications; Command modal; Version history modal for viewing diffs and restoring previous versions; Mobile responsive design. Uses Convex queries for real-time data, localStorage for preferences, ReactMarkdown for preview. Optional WorkOS authentication via siteConfig.dashboard.requireAuth. |
Callback.tsx |
OAuth callback handler for WorkOS authentication. Handles redirect from WorkOS after user login, exchanges authorization code for user information, then redirects to dashboard. Only used when WorkOS is configured. |
NewsletterAdmin.tsx |
Three-column newsletter admin page for managing subscribers and sending newsletters. Left sidebar with navigation and stats, main area with searchable subscriber list, right sidebar with send newsletter panel and recent sends. Access at /newsletter-admin, configurable via siteConfig.newsletterAdmin. |
| File | Description |
|---|---|
Layout.tsx |
Page wrapper with logo in header (top-left), 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. Logo reads from siteConfig.innerPageLogo. Displays social icons in header (left of search) when siteConfig.socialFooter.showInHeader is true. |
ThemeToggle.tsx |
Theme switcher (dark/light/tan/cloud) |
PostList.tsx |
Year-grouped blog post list or card grid (supports list/cards view modes, columns prop for 2/3 column grids, showExcerpts prop to control excerpt visibility) |
BlogHeroCard.tsx |
Hero card component for the first blogFeatured post on blog page. Displays landscape image, tags, date, title, excerpt, author info, and read more link |
BlogPost.tsx |
Markdown renderer with syntax highlighting, collapsible sections (details/summary), text wrapping for plain text code blocks, image lightbox support (click images to magnify in full-screen overlay), and iframe embed support with domain whitelisting (YouTube and Twitter/X only). Routes diff/patch code blocks to DiffCodeBlock for enhanced diff rendering. SEO: H1 headings in markdown demoted to H2 (.blog-h1-demoted class) for single H1 per page compliance. |
DiffCodeBlock.tsx |
Enhanced diff/patch code block renderer using @pierre/diffs library. Supports unified and split (side-by-side) view modes with toggle button. Theme-aware (dark/light) with copy button. Used automatically for diff and patch code blocks in markdown. |
CopyPageDropdown.tsx |
Share dropdown with Copy page (markdown to clipboard), View as Markdown (opens raw .md file), Download as SKILL.md (Anthropic Agent Skills format), Open in AI links (ChatGPT, Claude, Perplexity) using local /raw URLs, and Export as PDF (browser print with clean formatting) |
Footer.tsx |
Footer component that renders markdown content from frontmatter footer field or siteConfig.defaultContent. Can be enabled/disabled globally and per-page via frontmatter showFooter field. Renders inside article at bottom for posts/pages, and in current position on homepage. Supports images with size control via HTML attributes (width, height, style, class) |
SearchModal.tsx |
Full text search modal with keyboard navigation. Supports keyword and semantic search modes (toggle with Tab). Semantic mode conditionally shown when siteConfig.semanticSearch.enabled: true. When semantic disabled (default), shows keyword search only without mode toggle. |
FeaturedCards.tsx |
Card grid for featured posts/pages with excerpts |
LogoMarquee.tsx |
Scrolling logo gallery with clickable links |
MobileMenu.tsx |
Slide-out drawer menu for mobile navigation with hamburger button. Shows social icons below nav links when socialFooter.showInHeader enabled (mobile only, not in header). Includes sidebar table of contents when page has sidebar layout. Uses platformIcons from SocialFooter. |
ScrollToTop.tsx |
Configurable scroll-to-top button with Phosphor ArrowUp icon |
GitHubContributions.tsx |
GitHub activity graph with theme-aware colors and year navigation |
VisitorMap.tsx |
Real-time visitor location map with dotted world display, theme-aware colors, and GPU-composited pulse animations using transform: scale() |
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 or AI chat on posts/pages at 1135px+ viewport width, controlled by siteConfig.rightSidebar.enabled and frontmatter rightSidebar/aiChat fields |
AIChatView.tsx |
AI chat interface component (Agent) using Anthropic Claude API. Supports per-page chat history, page content context, markdown rendering, and copy functionality. Used in Write page (replaces textarea when enabled) and optionally in RightSidebar. Requires ANTHROPIC_API_KEY environment variable in Convex. System prompt configurable via CLAUDE_PROMPT_STYLE, CLAUDE_PROMPT_COMMUNITY, CLAUDE_PROMPT_RULES, or CLAUDE_SYSTEM_PROMPT environment variables. Includes error handling for missing API keys. |
NewsletterSignup.tsx |
Newsletter signup form component for email-only subscriptions. Displays configurable title/description, validates email, and submits to Convex. Shows on home, blog page, and posts based on siteConfig.newsletter settings. Supports frontmatter override via newsletter: true/false. Includes honeypot field for bot protection. |
ContactForm.tsx |
Contact form component with name, email, and message fields. Displays when contactForm: true in frontmatter. Submits to Convex which sends email via AgentMail to configured recipient. Requires AGENTMAIL_API_KEY and AGENTMAIL_INBOX environment variables. Includes honeypot field for bot protection. |
SocialFooter.tsx |
Social footer component with social icons on left (GitHub, Twitter/X, LinkedIn, Instagram, YouTube, TikTok, Discord, Website), AI discovery links center (llms.txt, AGENTS.md with Robot/FileText icons), and copyright on right. Configurable via siteConfig.socialFooter. Shows below main footer on homepage, blog posts, and pages. Supports frontmatter override via showSocialFooter: true/false. Auto-updates copyright year. Exports platformIcons for reuse in header. |
AskAIModal.tsx |
Ask AI chat modal for RAG-based Q&A about site content. Opens via header button (Cmd+J) when enabled. Uses Convex Persistent Text Streaming for real-time responses. Supports model selection (Claude, GPT-4o). Features streaming messages with markdown rendering, internal link handling via React Router, and source citations. Requires siteConfig.askAI.enabled and siteConfig.semanticSearch.enabled. |
VersionHistoryModal.tsx |
Version history modal for viewing and restoring previous content versions. Shows version list with dates and source badges, diff view using DiffCodeBlock component, preview mode, and one-click restore. Used in Dashboard editor when version control is enabled. |
MediaLibrary.tsx |
Media library component for uploading and managing images. Features drag-and-drop upload, copy as Markdown/HTML/URL, bulk select and delete, file size display, and pagination. Supports all three media providers (convex, convexfs, r2). For convex/r2 providers, shows recent uploads with preview and embed code copy buttons (persisted to sessionStorage). Dynamic usage text based on active provider. Uses ConvexFS for file browsing when available. |
ImageUploadModal.tsx |
Image insert modal for Write Post/Page sections. Two tabs: "Upload New" for uploading images and "Media Library" for selecting existing images (requires convexfs provider). Shows image dimensions with aspect ratio, size presets (Original, Large 1200px, Medium 800px, Small 400px, Thumbnail 200px, Custom), alt text field, and calculated dimensions before insert. Uses HTML img tag with explicit width/height for non-original sizes. |
| File | Description |
|---|---|
ThemeContext.tsx |
Theme state management with localStorage persistence |
FontContext.tsx |
Font family state management (serif/sans/monospace) with localStorage persistence and siteConfig integration |
SidebarContext.tsx |
Shares sidebar headings and active ID between Post and Layout components for mobile menu integration |
| File | Description |
|---|---|
extractHeadings.ts |
Parses markdown content to extract headings (H1-H6), generates slugs, filters out headings inside code blocks |
workos.ts |
WorkOS configuration utility. Exports isWorkOSConfigured boolean (checks if VITE_WORKOS_CLIENT_ID and VITE_WORKOS_REDIRECT_URI are set) and workosConfig object with clientId and redirectUri. Used throughout app to conditionally enable WorkOS features. |
| File | Description |
|---|---|
usePageTracking.ts |
Page view recording and active session heartbeat. Respects siteConfig.statsPage.enabled (no DB writes when disabled) |
useSearchHighlighting.ts |
Search term highlighting and scroll-to-match. Reads ?q= URL param, waits for content to load, highlights matches in DOM, scrolls to first match. |
| 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. Left sidebar (.post-sidebar-wrapper) and right sidebar (.post-sidebar-right) have separate, independent styles. Footer image styles (.site-footer-image-wrapper, .site-footer-image, .site-footer-image-caption) for responsive image display. Write page layout uses viewport height constraints (100vh) with overflow hidden to prevent page scroll, and AI chat uses flexbox with min-height: 0 for proper scrollable message area. Image lightbox styles (.image-lightbox-backdrop, .image-lightbox-img, .image-lightbox-close, .image-lightbox-caption) for full-screen image magnification with backdrop, close button, and caption display. SEO: .blog-h1-demoted class for demoted H1s (semantic H2 with H1 styling), CSS order properties for article/sidebar DOM order optimization. Core Web Vitals: GPU-composited visitor-pulse animations with transform: scale(), docs-skeleton-pulse using pseudo-element with transform: translateX(), will-change hints on animated elements (theme-toggle, copy-page-menu, search-modal-backdrop, scroll-to-top, image-lightbox-backdrop, search-modal, ai-chat-message, dashboard-toast, ask-ai-modal, docs-article). Docs layout scrollbar hiding: body:has(.docs-layout) prevents page-level scroll, .docs-sidebar-left, .docs-sidebar-right, and .docs-content use scrollbar-width: none (Firefox), -ms-overflow-style: none (IE/Edge), and ::-webkit-scrollbar { width: 0 } (Chrome/Safari) for invisible scrollbars while preserving scroll functionality |
| File | Description |
|---|---|
schema.ts |
Database schema (posts, pages, viewCounts, pageViews, activeSessions, aiChats, aiGeneratedImages, newsletterSubscribers, newsletterSentPosts, contactMessages, askAISessions, contentVersions, versionControlSettings) with indexes for tag queries (by_tags), AI queries, blog featured posts (by_blogFeatured), source tracking (by_source), vector search (by_embedding), and version history (by_content, by_createdAt). Posts and pages include showSocialFooter, showImageAtTop, blogFeatured, contactForm, source, and embedding fields for frontmatter control, cloud CMS tracking, and semantic search. contentVersions stores snapshots before content updates. versionControlSettings stores the enable/disable toggle. |
cms.ts |
CRUD mutations for dashboard cloud CMS: createPost, updatePost, deletePost, createPage, updatePage, deletePage, exportPostAsMarkdown, exportPageAsMarkdown. Posts/pages created via dashboard have source: "dashboard" (protected from sync overwrites). Captures versions before updates when version control is enabled. Markdown export now uses shared frontmatter helpers for post and page files. |
importAction.ts |
Queued Firecrawl worker for Dashboard URL import. Consumes the scheduled import-job snapshot, scrapes the source URL, normalizes markdown, and finalizes the job through shared helpers. Requires FIRECRAWL_API_KEY environment variable. |
importJobs.ts |
Public request or status functions and internal completion or failure helpers for queued Dashboard URL imports. Now includes the internal mutation that creates the imported post and completes the job in one transaction. |
posts.ts |
Queries and mutations for blog posts, view counts, getAllTags, getPostsByTag, getRelatedPosts, and getBlogFeaturedPosts. View counter reads use .unique() on viewCounts.by_slug. Internal equivalents for server-to-server use: getAllPostsInternal, getPostBySlugWithContent, getAllPostsWithContentInternal, getAllTagsInternal, getAllAuthorsInternal. |
pages.ts |
Queries and mutations for static pages. Internal equivalents: getAllPagesInternal, getPageBySlugInternal. |
search.ts |
Full text search queries across posts and pages |
semanticSearch.ts |
Internal semantic search job worker that generates embeddings, runs vector search, and completes queued semantic search jobs |
semanticSearchJobs.ts |
Public request and status functions plus internal completion handlers for queued semantic search jobs |
semanticSearchQueries.ts |
Internal queries for fetching post/page details by IDs for semantic search |
embeddings.ts |
Embedding generation actions using OpenAI text-embedding-ada-002 |
embeddingsQueries.ts |
Internal queries and mutations for embedding storage and retrieval |
stats.ts |
Real-time stats with aggregate components for O(log n) counts (pageViewsByPath, totalPageViews, uniqueVisitors, uniquePaths), page view recording, session heartbeat, top 50 page stats pagination. Extracted helpers: updatePageViewAggregates, buildPageStats, collectVisitorLocations, getTopPathStats. |
crons.ts |
Cron jobs for stale session cleanup (every 5 minutes), weekly newsletter digest (Sundays 9am UTC), weekly stats summary (Mondays 9am UTC), and version cleanup (daily 3am UTC). Uses environment variables SITE_URL and SITE_NAME for email content. |
http.ts |
HTTP endpoints: /raw/ dynamic markdown serving with text/plain content type (browser-viewable and AI-readable, content served from Convex DB), sitemap (includes tag pages), API (update SITE_URL/SITE_NAME when forking, uses www.markdown.fast), Open Graph HTML generation for social crawlers with hreflang and twitter:site meta tags. Static app files served via registerStaticRoutes (Convex self-hosting). |
rss.ts |
RSS feed generation (update SITE_URL/SITE_TITLE when forking, uses www.markdown.fast) |
auth.config.ts |
Legacy WorkOS JWT configuration. The default auth mode uses @robelest/convex-auth in convex/auth.ts. This file is kept for backwards compatibility when auth.mode === "workos". WorkOS JWT providers are only active when WORKOS_CLIENT_ID is set in Convex environment variables. |
authComponent.ts |
Plain async helper functions (authUserGetByIdHelper, authUserListHelper) that forward to @robelest/convex-auth component APIs. Callers import the helpers directly to share the same transaction. |
aiChats.ts |
Queries and mutations for AI chat history (per-session, per-context storage). Handles anonymous session IDs, per-page chat contexts, queued response generation state, and generated-image deletion. Now includes the internal finalizer used by queued response generation. |
aiChatActions.ts |
Multi-provider queued AI chat worker for Anthropic, OpenAI, and Google models. Runs from a scheduled chat snapshot, enriches attachments, formats provider messages, and finalizes success or failure through one internal mutation. |
aiImageGeneration.ts |
Queued Gemini image generation worker using the scheduled job snapshot. Supports gemini-2.0-flash-exp-image-generation and imagen-3.0-generate-002 with aspect ratio selection, Convex storage upload, and finalization through the image job finalizer. |
aiImageJobs.ts |
Public request and status functions for Dashboard image generation plus the internal finalizer that patches completed or failed image jobs and stores generated image metadata. |
newsletter.ts |
Newsletter mutations and queries: subscribe, unsubscribe, getSubscriberCount, getActiveSubscribers, getAllSubscribers (admin), deleteSubscriber (admin), getNewsletterStats, getPostsForNewsletter, wasPostSent, recordPostSent, scheduleSendPostNewsletter, scheduleSendCustomNewsletter, scheduleSendStatsSummary, getStatsForSummary. Includes getPostNewsletterSendContextInternal to batch reads for post newsletter sends. |
newsletterActions.ts |
Newsletter actions (Node.js runtime): sendPostNewsletter, sendCustomNewsletter, sendWeeklyDigest, notifyNewSubscriber, sendWeeklyStatsSummary. Uses AgentMail SDK for email delivery. Post sends prefetch with one internal query plus recordPostSent. Includes markdown-to-HTML conversion for custom emails. |
contact.ts |
Contact form mutations: submitContact (public), markEmailSent (internal). Schedules email delivery via contactActions.ts. |
contactActions.ts |
Contact form email action (Node.js runtime): sendContactEmail with extracted buildContactHtml/buildContactText helpers. Uses AgentMail SDK. |
versions.ts |
Version control system: isEnabled, setEnabled, createVersion, getVersionHistory, getVersion, restoreVersion, cleanupOldVersions, getStats. Captures content snapshots before updates, provides 3-day history with diff view and restore functionality. |
askAI.ts |
Ask AI session management: createSession mutation (creates streaming session with question/model in DB), getStreamBody query (for database fallback), getSessionByStreamId internal query (retrieves question/model for HTTP action). Uses Persistent Text Streaming component. |
askAI.node.ts |
Ask AI HTTP action for streaming responses (Node.js runtime). Retrieves question from database, performs vector search using existing semantic search embeddings, generates AI response via Anthropic Claude or OpenAI GPT-4o, streams via appendChunk. Includes CORS headers and source citations. |
fs.ts |
ConvexFS instance configuration with Bunny.net Edge Storage integration. Conditionally creates ConvexFS instance only when BUNNY_API_KEY, BUNNY_STORAGE_ZONE, and BUNNY_CDN_HOSTNAME environment variables are set. Exports isBunnyConfigured boolean and fs instance (or null if not configured). |
files.ts |
File management mutations and queries for media library: commitFile (upload with validation), listFiles (paginated), deleteFile, deleteFiles (bulk), setFileExpiration, getFileInfo, getDownloadUrl, getFileCount, isConfigured. Validates file types (PNG, JPG, GIF, WebP) and size (10MB max). |
convex.config.ts |
Convex app configuration with aggregate component registrations (pageViewsByPath, totalPageViews, uniqueVisitors, uniquePaths), persistentTextStreaming component, and ConvexFS component for media storage. |
tsconfig.json |
Convex TypeScript configuration |
| Route | Description |
|---|---|
/stats |
Real-time site analytics page |
/rss.xml |
RSS feed with descriptions |
/rss-full.xml |
RSS feed with full content for LLMs |
/sitemap.xml |
Dynamic XML sitemap for search engines (includes posts, pages, and tag pages) |
/api/posts |
JSON list of all posts |
/api/post |
Single post as JSON or markdown |
/api/export |
Batch export all posts with content |
/meta/post |
Open Graph HTML for social crawlers |
/.well-known/ai-plugin.json |
AI plugin manifest |
/openapi.yaml |
OpenAPI 3.0 specification |
/llms.txt |
AI agent discovery |
/ask-ai-stream |
Ask AI streaming endpoint for RAG-based Q&A (POST with streamId) |
/raw/{slug}.md |
Dynamic raw markdown serving with text/plain content type. Serves post or page content from Convex DB. Browser-viewable and readable by AI services (Claude, ChatGPT, Perplexity). |
Markdown files with frontmatter for blog posts. Each file becomes a blog post.
| Field | Description |
|---|---|
title |
Post title |
description |
Short description for SEO |
date |
Publication date (YYYY-MM-DD) |
slug |
URL path for the post |
published |
Whether post is public |
tags |
Array of topic tags |
readTime |
Estimated reading time |
image |
Header/Open Graph image URL (optional) |
showImageAtTop |
Display image at top of post above header (optional, default: false). When true, image displays full-width with rounded corners above post header. |
excerpt |
Short excerpt for card view (optional) |
featured |
Show in featured section (optional) |
featuredOrder |
Order in featured section (optional) |
blogFeatured |
Show as featured on blog page (optional, first becomes hero card with landscape image, rest in 2-column featured row with excerpts) |
authorName |
Author display name (optional) |
authorImage |
Round author avatar image URL (optional) |
rightSidebar |
Enable right sidebar with CopyPageDropdown (optional) |
showFooter |
Show footer on this post (optional, overrides siteConfig default) |
footer |
Footer markdown content (optional, overrides siteConfig.defaultContent) |
showSocialFooter |
Show social footer on this post (optional, overrides siteConfig default) |
aiChat |
Enable AI Agent chat in right sidebar (optional). Set true to enable (requires rightSidebar: true and siteConfig.aiChat.enabledOnContent: true). Set false to explicitly hide even if global config is enabled. |
blogFeatured |
Show as featured on blog page (optional, first becomes hero, rest in 2-column row) |
newsletter |
Override newsletter signup display (optional, true/false) |
contactForm |
Enable contact form on this post (optional). Requires siteConfig.contactForm.enabled: true and AGENTMAIL_API_KEY/AGENTMAIL_INBOX environment variables. |
unlisted |
Hide from listings but allow direct access via slug (optional). Set true to hide from blog listings, featured sections, tag pages, search results, and related posts. Post remains accessible via direct link. |
docsSection |
Include in docs sidebar (optional). Set true to show in the docs section navigation. |
docsSectionGroup |
Group name for docs sidebar (optional). Posts with the same group name appear together. |
docsSectionOrder |
Order within docs group (optional). Lower numbers appear first within the group. |
docsSectionGroupOrder |
Order of the group in docs sidebar (optional). Lower numbers make the group appear first. Groups without this field sort alphabetically. |
docsSectionGroupIcon |
Phosphor icon name for docs sidebar group (optional, e.g., "Rocket", "Book", "PuzzlePiece"). Icon appears left of the group title. See Phosphor Icons for available icons. |
docsLanding |
Use as docs landing page (optional). Set true to show this post when navigating to /docs. |
Markdown files for static pages like About, Projects, Contact, Changelog.
Special pages:
home.md(slug:home-intro): Homepage intro/bio content. SetshowInNav: falseto hide from navigation. Content syncs withnpm run syncand displays on the homepage without redeploy. Headings (h1-h6) use blog post styling (blog-h1throughblog-h6) with clickable anchor links. Lists, blockquotes, horizontal rules, and links also use blog styling classes for consistent typography. UsetextAlignfrontmatter field to control alignment (left/center/right, default: left). Falls back tositeConfig.bioif page not found or while loading.footer.md(slug:footer): Footer content managed via markdown sync. SetshowInNav: falseto hide from navigation. Content syncs withnpm run syncand displays in the footer component without redeploy. Supports full markdown including links, paragraphs, and line breaks. Falls back tositeConfig.footer.defaultContentif page not found or while loading. This allows editing footer content without touching code.
| Field | Description |
|---|---|
title |
Page title |
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) |
image |
Thumbnail/OG image URL (optional) |
showImageAtTop |
Display image at top of page above header (optional, default: false). When true, image displays full-width with rounded corners above page header. |
featured |
Show in featured section (optional) |
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) |
showFooter |
Show footer on this page (optional, overrides siteConfig default) |
footer |
Footer markdown content (optional, overrides siteConfig.defaultContent) |
showSocialFooter |
Show social footer on this page (optional, overrides siteConfig default) |
aiChat |
Enable AI Agent chat in right sidebar (optional). Set true to enable (requires rightSidebar: true and siteConfig.aiChat.enabledOnContent: true). Set false to explicitly hide even if global config is enabled. |
newsletter |
Override newsletter signup display (optional, true/false) |
contactForm |
Enable contact form on this page (optional). Requires siteConfig.contactForm.enabled: true and AGENTMAIL_API_KEY/AGENTMAIL_INBOX environment variables. |
textAlign |
Text alignment: "left", "center", "right" (optional, default: "left"). Used by home.md for home intro content alignment |
docsSection |
Include in docs sidebar (optional). Set true to show in the docs section navigation. |
docsSectionGroup |
Group name for docs sidebar (optional). Pages with the same group name appear together. |
docsSectionOrder |
Order within docs group (optional). Lower numbers appear first within the group. |
docsSectionGroupOrder |
Order of the group in docs sidebar (optional). Lower numbers make the group appear first. Groups without this field sort alphabetically. |
docsSectionGroupIcon |
Phosphor icon name for docs sidebar group (optional, e.g., "Rocket", "Book", "PuzzlePiece"). Icon appears left of the group title. See Phosphor Icons for available icons. |
docsLanding |
Use as docs landing page (optional). Set true to show this page when navigating to /docs. |
Markdown sync v2 complete - Full markdown content synchronization system with real-time sync from markdown files to Convex database, dashboard UI for content management, and sync server for executing sync commands from UI.
| File | Description |
|---|---|
sync-posts.ts |
Syncs markdown files to Convex at build time (markdown sync v2). Generates raw/index.md with home.md content at top, posts/pages list, and footer.md content at bottom |
sync-discovery-files.ts |
Updates AGENTS.md, CLAUDE.md, and llms.txt with current app data including wiki pages. Copies AGENTS.md to public/ for web access. |
import-url.ts |
Imports external URLs as markdown posts (Firecrawl) |
configure-fork.ts |
Automated fork configuration (reads fork-config.json, updates 14 files). ES module compatible using fileURLToPath for __dirname equivalent. |
send-newsletter.ts |
CLI tool for sending newsletter posts (npm run newsletter:send ). Calls scheduleSendPostNewsletter mutation directly. |
send-newsletter-stats.ts |
CLI tool for sending weekly stats summary (npm run newsletter:send:stats). Calls scheduleSendStatsSummary mutation directly. |
sync-server.ts |
Local HTTP server for executing sync commands from Dashboard UI. Runs on localhost:3001 with optional token authentication. Whitelisted commands only. Part of markdown sync v2. |
export-db-posts.ts |
Exports dashboard-created posts and pages to markdown files in content/blog/ and content/pages/. Only exports content with source: "dashboard". Supports development and production environments via npm run export:db and npm run export:db:prod. |
validate-env.ts |
Validates local env readiness for development/production and reports optional Convex auth/deploy env settings. |
verify-deploy.ts |
Verifies deployed Convex self-hosted endpoints (/, RSS, sitemap, API) using an explicit URL or derived *.convex.site URL. |
Development:
npm run sync- Sync markdown content to development Convexnpm run sync:discovery- Update AGENTS.md, CLAUDE.md, llms.txt (includes wiki pages, copies AGENTS.md to public/)
Production:
npm run sync:prod- Sync markdown content to production Convexnpm run sync:discovery:prod- Update discovery files with production data
Sync everything together:
npm run sync:all- Run both content sync and discovery sync (development)npm run sync:all:prod- Run both content sync and discovery sync (production)
Export dashboard content:
npm run export:db- Export dashboard posts/pages to content folders (development)npm run export:db:prod- Export dashboard posts/pages (production)
Frontmatter is the YAML metadata at the top of each markdown file. Here is how it flows through the system:
- Content directories (
content/blog/*.md,content/pages/*.md) contain markdown files with YAML frontmatter scripts/sync-posts.tsusesgray-matterto parse frontmatter and validate required fields- Convex mutations (
api.posts.syncPostsPublic,api.pages.syncPagesPublic) receive parsed data convex/schema.tsdefines the database structure for storing frontmatter fields
To add a new frontmatter field, update:
scripts/sync-posts.ts: Add toPostFrontmatterorPageFrontmatterinterface and parsing logicconvex/schema.ts: Add field to the posts or pages table schemaconvex/posts.tsorconvex/pages.ts: Update sync mutation to handle new field
These edge functions are used when
hosting.mode: "netlify"is set insiteConfig.ts. They are not active in Convex self-hosting mode. With Convex self-hosting, RSS, sitemap, API, and/raw/routes are handled directly by HTTP actions inconvex/http.ts.
| File | Description |
|---|---|
botMeta.ts |
Edge function for bot detection with configurable arrays for social preview bots, search engine bots, and AI crawlers. Serves pre-rendered HTML with correct canonical URLs and OG tags to social and search bots. Excludes /raw/* paths and AI crawlers from interception. Configuration documented at top of file. |
rss.ts |
Proxies /rss.xml and /rss-full.xml to Convex HTTP |
sitemap.ts |
Proxies /sitemap.xml to Convex HTTP |
api.ts |
Proxies /api/posts, /api/post, /api/export to Convex |
geo.ts |
Returns user geo location from Netlify's automatic geo headers for visitor map (Netlify-only, not available in Convex self-hosting) |
mcp.ts |
HTTP-based MCP server for AI tool integration (Cursor, Claude Desktop). Accessible at /mcp endpoint. Exposes read-only tools: list_posts, get_post, list_pages, get_page, get_homepage, search_content, export_all. Uses Netlify rate limiting (50 req/min public, 1000 req/min with API key). Optional authentication via MCP_API_KEY environment variable. |
| File | Description |
|---|---|
favicon.svg |
Site favicon |
_redirects |
SPA redirect rules for static files |
robots.txt |
Crawler rules for search engines and AI bots (update sitemap URL when forking, uses www.markdown.fast) |
llms.txt |
AI agent discovery file (update site name/URL when forking, uses www.markdown.fast) |
openapi.yaml |
OpenAPI 3.0 specification (update API title when forking, uses www.markdown.fast) |
Static markdown files generated during npm run sync or npm run sync:prod. Each published post and page gets a corresponding .md file for direct access by users, search engines, and AI agents.
| File Pattern | Description |
|---|---|
{slug}.md |
Static markdown file for each post/page |
Access via /raw/{slug}.md (e.g., /raw/setup-guide.md).
Files include a metadata header with type (post/page), date, reading time, and tags. The CopyPageDropdown includes a "View as Markdown" option that links directly to these files.
| File | Description |
|---|---|
ai-plugin.json |
AI plugin manifest (update name/description when forking) |
| File | Description |
|---|---|
logo.svg |
Site logo displayed on homepage |
og-default.svg |
Default Open Graph image for social sharing |
*.png/jpg/svg |
Blog post images (referenced in frontmatter) |
| File | Description |
|---|---|
sample-logo-1.svg |
Sample logo (replace with your own) |
sample-logo-2.svg |
Sample logo (replace with your own) |
sample-logo-3.svg |
Sample logo (replace with your own) |
sample-logo-4.svg |
Sample logo (replace with your own) |
sample-logo-5.svg |
Sample logo (replace with your own) |
| File | Description |
|---|---|
frontmatter.md |
Frontmatter syntax and all field options for posts and pages |
convex.md |
Convex patterns specific to this app (indexes, mutations, queries) |
sync.md |
How sync commands work and content flow from markdown to database |
NPM CLI package for scaffolding new markdown-sync projects with a single command.
| File | Description |
|---|---|
package.json |
CLI package config with bin entry point |
tsconfig.json |
TypeScript config for CLI |
README.md |
NPM package readme |
src/index.ts |
Main entry point with CLI argument parsing |
src/wizard.ts |
Interactive prompts (13 sections, 50+ prompts) |
src/clone.ts |
Repository cloning via giget |
src/configure.ts |
Fork config generation and template fixes |
src/install.ts |
Dependency installation and dev server |
src/convex-setup.ts |
Convex project initialization |
src/utils.ts |
Validation helpers, logging, package manager detection |
Usage:
npx create-markdown-sync my-site| File | Description |
|---|---|
convex-write-conflicts.mdc |
Write conflict prevention patterns for Convex |
convex2.mdc |
Convex function syntax and examples |
dev2.mdc |
Development guidelines and best practices |
help.mdc |
Core development guidelines |
rulesforconvex.mdc |
Convex schema and function best practices |
sec-check.mdc |
Security guidelines and audit checklist |
task.mdc |
Task list management guidelines |
write.mdc |
Writing style guide (activate with @write) |
OpenCode AI-first development tool integration. Works alongside Claude Code and Cursor.
| File | Description |
|---|---|
opencode.json |
Root OpenCode project configuration |
.opencode/config.json |
OpenCode app configuration |
| File | Description |
|---|---|
orchestrator.md |
Main orchestrator agent - routes tasks to specialists |
content-writer.md |
Content creation specialist for posts and pages |
sync-manager.md |
Sync and deployment specialist |
| File | Description |
|---|---|
sync.md |
/sync - Sync content to development |
sync-prod.md |
/sync-prod - Sync content to production |
create-post.md |
/create-post - Create new blog post |
create-page.md |
/create-page - Create new page |
import.md |
/import - Import content from URL |
deploy.md |
/deploy - Deploy to production |
| File | Description |
|---|---|
frontmatter.md |
Frontmatter syntax for posts and pages |
sync.md |
How the sync system works |
convex.md |
Convex patterns and conventions |
content.md |
Content management guide |
| File | Description |
|---|---|
sync-helper.ts |
Logs reminders when content files change |