import type { ComponentType, ReactNode } from 'react' /** * Top-level mkdnsite configuration. * * Can be defined in mkdnsite.config.ts at the project root, * or passed programmatically when creating a handler. */ export interface MkdnSiteConfig { /** Directory containing .md files (default: ./content) */ contentDir: string /** Site metadata */ site: SiteConfig /** Server options (local dev / self-hosted only) */ server: ServerConfig /** Theme and rendering configuration */ theme: ThemeConfig /** Content negotiation options */ negotiation: NegotiationConfig /** Auto-generate /llms.txt */ llmsTxt: LlmsTxtConfig /** Client-side enhancement modules */ client: ClientConfig /** * Preset that configures sensible defaults for common use cases. * Applied before user config — any explicit user setting overrides the preset. * - 'docs': nav + TOC + prev/next (default-like, best for documentation sites) * - 'blog': page title + date + reading time + prev/next, no nav sidebar */ preset?: 'docs' | 'blog' /** Markdown renderer engine (default: 'portable') */ renderer: RendererEngine /** Static files directory for images, videos, etc. */ staticDir?: string /** * Glob patterns to include. Only matching files will be served. * Mutually exclusive with `exclude` — define one or the other, not both. * e.g. ['docs', 'guides/*.md'] */ include?: string[] /** * Glob patterns to exclude. Matching files will not be served. * Mutually exclusive with `include` — define one or the other, not both. * e.g. ['private', '*.draft.md'] */ exclude?: string[] /** GitHub repository source (alternative to local contentDir) */ github?: import('../content/types.ts').GitHubSourceConfig /** MCP (Model Context Protocol) server configuration */ mcp: McpConfig /** Analytics configuration (disabled by default) */ analytics?: AnalyticsConfig /** Content Security Policy configuration */ csp?: CspConfig /** Response caching and CDN header configuration */ cache?: CacheConfig } /** MCP (Model Context Protocol) server configuration */ export interface McpConfig { /** Enable the built-in MCP server (default: true) */ enabled: boolean /** MCP endpoint path (default: '/mcp') */ endpoint?: string } /** Content Security Policy configuration */ export interface CspConfig { /** Enable the Content-Security-Policy header on HTML responses (default: true) */ enabled: boolean /** Additional script-src sources */ extraScriptSrc?: string[] /** Additional style-src sources */ extraStyleSrc?: string[] /** Additional img-src sources */ extraImgSrc?: string[] /** Additional connect-src sources */ extraConnectSrc?: string[] /** Additional font-src sources */ extraFontSrc?: string[] /** Report URI for CSP violation reports */ reportUri?: string } /** Response caching and CDN header configuration */ export interface CacheConfig { /** Enable response caching (default: false — opt-in) */ enabled?: boolean /** Cache-Control max-age in seconds for HTML responses (default: 300) */ maxAge?: number /** Cache-Control max-age in seconds for markdown responses (default: 300) */ maxAgeMarkdown?: number /** stale-while-revalidate in seconds (default: 0, meaning omitted) */ staleWhileRevalidate?: number /** Version tag for ETag and CDN cache busting (e.g. 'v1.2.3' or git SHA) */ versionTag?: string } /** Analytics configuration */ export interface AnalyticsConfig { /** Google Analytics 4 (GA4) configuration */ googleAnalytics?: { /** GA4 measurement ID, e.g. 'G-XXXXXXXXXX' */ measurementId: string } /** Server-side traffic analytics (default: disabled) */ traffic?: TrafficAnalyticsConfig } /** Server-side traffic analytics configuration */ export interface TrafficAnalyticsConfig { /** * Enable server-side traffic analytics. * When false (default) the analytics hook is skipped entirely. */ enabled?: boolean /** * Log each request as a JSON line to stdout. * Useful for development / debugging. Requires enabled: true. */ console?: boolean } /** OpenGraph / social sharing meta tag configuration */ export interface OgConfig { /** Default OG image URL (can be overridden per-page via frontmatter `og_image`) */ image?: string /** Default OG type (default: 'website' for index, 'article' for other pages) */ type?: string /** Twitter card type: 'summary' or 'summary_large_image' (default: 'summary') */ twitterCard?: 'summary' | 'summary_large_image' /** Twitter @handle for the site (e.g. '@mkdnsite') */ twitterSite?: string } /** Favicon configuration */ export interface FaviconConfig { /** Path or URL to the favicon file (.ico, .png, .svg) */ src: string } export interface SiteConfig { title: string description?: string url?: string lang?: string /** OpenGraph / social sharing meta tag configuration */ og?: OgConfig /** Favicon configuration */ favicon?: FaviconConfig } export interface ServerConfig { port: number hostname: string } /** CSS color token overrides for the built-in theme */ export interface ColorTokens { /** Primary accent color (links, highlights) */ accent?: string /** Main text color */ text?: string /** Muted text color */ textMuted?: string /** Page background color */ bg?: string /** Alternate background (nav, code blocks) */ bgAlt?: string /** Border color */ border?: string /** Link color */ link?: string /** Link hover color */ linkHover?: string /** Inline code background */ codeBg?: string /** Code block (pre) background */ preBg?: string } /** Font stack overrides for the built-in theme */ export interface FontTokens { /** Body/prose font stack */ body?: string /** Monospace font stack */ mono?: string /** Heading font stack (defaults to body font) */ heading?: string } /** Logo image configuration */ export interface LogoConfig { /** Path or URL to the logo image */ src: string /** Alt text for the logo image */ alt?: string /** Logo display width in pixels (default: 32) */ width?: number /** Logo display height in pixels (default: 32) */ height?: number } export interface ThemeConfig { /** * Rendering mode for markdown content. * - 'prose': Uses @tailwindcss/typography prose classes (default) * - 'components': Full custom React component overrides per element */ mode: 'prose' | 'components' /** Custom React components to override default element rendering */ components?: ComponentOverrides /** * Inline CSS string appended after the built-in theme styles. * Use this for small tweaks. For full replacement, set builtinCss: false. */ customCss?: string /** URL to an external stylesheet loaded via */ customCssUrl?: string /** * Include the built-in theme CSS. Set to false to strip all default * styles and start from a blank slate. (default: true) */ builtinCss?: boolean /** Light mode CSS color token overrides */ colors?: ColorTokens /** Dark mode CSS color token overrides */ colorsDark?: ColorTokens /** Font stack overrides */ fonts?: FontTokens /** Logo image displayed in the nav header */ logo?: LogoConfig /** Site name / text logo displayed in the nav header */ logoText?: string /** Render page title from frontmatter as

above article content */ pageTitle?: boolean /** Render publish/update date from frontmatter below the page title */ pageDate?: boolean /** Show prev/next page navigation links at the bottom of the article */ prevNext?: boolean /** Show estimated reading time near the page date */ readingTime?: boolean /** Show navigation sidebar */ showNav: boolean /** Show table of contents per page */ showToc: boolean /** Show "Powered by mkdnsite" footer. Default true. */ showFooter?: boolean /** Edit URL template (e.g. https://github.com/org/repo/edit/main/{path}) */ editUrl?: string /** Color scheme: 'system' (default), 'light', or 'dark' */ colorScheme: 'system' | 'light' | 'dark' /** Syntax highlighting theme for Shiki */ syntaxTheme?: string /** Dark mode syntax highlighting theme */ syntaxThemeDark?: string } export interface NegotiationConfig { /** Enable serving raw markdown via Accept: text/markdown (default: true) */ enabled: boolean /** Include x-markdown-tokens header (default: true) */ includeTokenCount: boolean /** Content-Signal header values */ contentSignals: ContentSignals } export interface ContentSignals { aiTrain: 'yes' | 'no' search: 'yes' | 'no' aiInput: 'yes' | 'no' } export interface LlmsTxtConfig { enabled: boolean description?: string sections?: Record } export interface ClientConfig { /** * Enable client-side JavaScript enhancements. * When false, only static HTML/CSS is served (performance mode). * Default: true */ enabled: boolean /** Enable Mermaid diagram rendering (default: true when client enabled) */ mermaid: boolean /** Enable copy-to-clipboard on code blocks (default: true when client enabled) */ copyButton: boolean /** Enable light/dark theme toggle button (default: true when client enabled) */ themeToggle: boolean /** Enable KaTeX math rendering (default: true when client enabled) */ math: boolean /** Enable client-side search (default: true when client enabled) */ search: boolean /** Enable Chart.js chart rendering (default: true when client enabled) */ charts: boolean /** * Syntax highlighting engine. * - 'client': load Prism.js from CDN at runtime (works everywhere, default) * - 'server': use Shiki for SSR highlighting (WASM — not supported in CF Workers) * - false: disable syntax highlighting entirely * Default: 'client' */ syntaxHighlight?: 'client' | 'server' | false } /** * Markdown renderer engine selection. * - 'portable': react-markdown + remark/rehype (works everywhere) * - 'bun-native': Bun.markdown.react() (Bun only, faster) */ export type RendererEngine = 'portable' | 'bun-native' /** * React component overrides for markdown elements. * Each key maps to an HTML element produced by the markdown renderer. */ export interface ComponentOverrides { h1?: ComponentType h2?: ComponentType h3?: ComponentType h4?: ComponentType h5?: ComponentType h6?: ComponentType p?: ComponentType<{ children?: ReactNode }> a?: ComponentType img?: ComponentType pre?: ComponentType code?: ComponentType blockquote?: ComponentType<{ children?: ReactNode }> table?: ComponentType<{ children?: ReactNode }> thead?: ComponentType<{ children?: ReactNode }> tbody?: ComponentType<{ children?: ReactNode }> tr?: ComponentType<{ children?: ReactNode }> th?: ComponentType<{ children?: ReactNode, align?: string }> td?: ComponentType<{ children?: ReactNode, align?: string }> ul?: ComponentType<{ children?: ReactNode }> ol?: ComponentType<{ children?: ReactNode, start?: number }> li?: ComponentType<{ children?: ReactNode, checked?: boolean }> hr?: ComponentType> } export interface HeadingProps { children?: ReactNode id?: string level: number } export interface LinkProps { children?: ReactNode href?: string title?: string } export interface ImageProps { src?: string alt?: string title?: string } export interface CodeBlockProps { children?: ReactNode language?: string raw?: string } export interface InlineCodeProps { children?: ReactNode }