--- File: README.md --- # WingTip **Open-source, SEO-first documentation sites from Markdown.** ![WingTip social card](https://raw.githubusercontent.com/semanticentity/WingTip-Static-Site-Generator/main/social-card.png) [Live demo](https://semanticentity.github.io/WingTip-Static-Site-Generator) · [Migration guide](docs/migration.md) · [Source](https://github.com/semanticentity/WingTip-Static-Site-Generator) · [Roadmap](docs/roadmap.md) WingTip turns a repository `README.md` and `docs/` directory into fast, portable static documentation. It generates crawlable HTML, search and discovery metadata, structured data, machine-readable Markdown artifacts, and an offline-capable site without requiring a hosted documentation platform. Start with zero configuration, customize when needed, and deploy the generated files anywhere. --- ## Why WingTip - **Own the output:** Build ordinary HTML, CSS, JavaScript, feeds, and metadata that can be hosted on any static file server. - **Built for discovery:** Generate canonical URLs, robots directives, sitemap metadata, Open Graph and Twitter cards, `TechArticle` and breadcrumb JSON-LD, RSS, and hreflang alternates. - **Built for AI retrieval:** Publish `llms.txt`, full concatenated documentation, and a Markdown alternate beside every generated page. - **No mandatory CDN runtime:** Core styles, scripts, icons, KaTeX, and fonts are vendored into the Python package and copied into the build. External analytics remain opt-in. - **Your branding only:** No "powered by" badge, injected links, or generator watermarks — the generated site carries your identity, not WingTip's. - **Zero infrastructure:** Search, navigation, PWA support, and offline fallback work from static hosting. ## Moving from hosted documentation? Use the [migration guide](docs/migration.md) to assess what WingTip supports today, preserve URLs and metadata, validate generated output, and plan a safe cutover. Public repositories and sanitized reproductions can request a [structured migration review](https://github.com/semanticentity/WingTip-Static-Site-Generator/issues/new?template=migration.yml). ## Features ### Search, SEO, and machine-readable output - Per-page `title`, `description`, `keywords`, canonical URL, robots/noindex, language, Open Graph, and Twitter overrides - Automatic description fallback from the first paragraph - `TechArticle` and `BreadcrumbList` JSON-LD - `sitemap.xml`, `robots.txt`, RSS, `llms.txt`, and full concatenated documentation - Raw Markdown alternates emitted as `.html.md` - Published/updated dates with Git-based modified-date fallback - Per-page categories and versions, plus generated `categories.json` and `versions.json` - Per-page language and translation mappings with `hreflang` and `x-default` - Local client-side full-text search with keyboard navigation ([details](docs/search-features.md)) ### Documentation experience - `README.md` becomes `index.html`; `docs/` is discovered recursively with nested paths preserved in URLs (`docs/guides/intro.md` → `guides/intro.html`) - Automatic sidebar with collapsible directory groups (`_category.json` for names and ordering, `order` frontmatter for pages), table of contents, and previous/next navigation - GitHub “Edit this page” links - Responsive layout with light/dark mode and screen-reader/keyboard support - Pygments syntax highlighting and copy-to-clipboard controls - Tables, footnotes, definition lists, attributes, admonitions, and Markdown inside HTML - KaTeX math rendering ([examples](docs/math-examples.md)) - Configurable external-link handling and correct `.md` link rewriting, including fragments and queries - Custom Markdown-based `404.html` ### Performance, security, and extensibility - Local image copying, lazy loading, intrinsic dimensions, and responsive `srcset` generation - Locally vendored core frontend assets - PWA manifest, service worker, precache, and offline fallback - PWA icons generated only from a project-supplied `favicon.png` - Optional generated or custom Content Security Policy - Plausible, Umami, Fathom, Google Analytics, custom analytics, and global/per-page `` snippets - Plugin hooks before/after builds and conversions, plus plugin-provided Python-Markdown extensions - Theme variables through `theme.json` and static CSS overrides ([theming guide](docs/theming.md)) - Live development server with auto-reload and scroll restoration - Post-build auditor for missing local files, unexpected CDN references, required artifacts, and branding leaks --- ## Installation Python 3.9 or newer is required. ```bash pip install wingtip ``` Install the optional live server: ```bash pip install "wingtip[serve]" ``` For local development from a clone: ```bash pip install -e ".[dev]" ``` ## Quickstart Create a project containing `README.md` and, optionally, a `docs/` directory: ```text your-project/ ├── README.md ├── docs/ │ ├── guide.md │ └── api.md ├── config.json # optional ├── theme.json # optional └── favicon.png # optional; enables favicon and PWA icon generation ``` Run WingTip from the project directory: ```bash wingtip ``` The default output is `docs/site`. To build another source directory into a chosen destination: ```bash wingtip --source ./your-project --output ./build ``` Start the live development server after building: ```bash wingtip --serve ``` Use `wingtip --help` for all CLI options. --- ## Configuration Configuration is optional. Without `config.json`, WingTip derives the project name from the `README.md` heading or source-directory name and uses relative URLs for local portability. Add `config.json` when you want production URLs, repository links, analytics, security policy, or social-card customization: ```json { "base_url": "https://docs.example.com", "project_name": "Acme API", "version": "1.0.0", "description": "Integration documentation for the Acme API.", "author": "Acme", "repo_url": "https://github.com/acme/api-docs", "og_image": "social-card.png", "twitter_handle": "@acme", "github": { "repo": "acme/api-docs", "branch": "main" }, "analytics": { "provider": "plausible", "domain": "docs.example.com" }, "csp": true, "social_card": { "title": "Acme API", "tagline": "Build with Acme.", "theme": "light", "font": "Poppins" } } ``` Place a local `favicon.png` in the project root to emit favicon/nav-logo markup and generate 192×192 and 512×512 PWA icons. If it is absent, WingTip emits none of those branded assets. ### Per-page frontmatter Use YAML frontmatter to control individual pages: ```yaml --- title: Authentication API description: Authenticate server-side requests to the Acme API. keywords: - API authentication - OAuth canonical: https://docs.example.com/authentication noindex: false author: Acme Developer Relations date: 2026-07-16 lastmod: 2026-07-17 category: API reference version: v2 lang: en translations: es: https://docs.example.com/es/authentication og_title: Acme API authentication twitter_description: Implement Acme API authentication. --- ``` Noindexed pages are excluded from the sitemap, search index, category/version indexes, and structured data. ## Plugins and custom Markdown Place Python modules in `plugins/`. WingTip can auto-load every module or load only names listed in the `plugins` array in `config.json`. Plugins can expose: - `before_build(config, output_dir)` - `before_convert(frontmatter, markdown, input_path, output_path)` - `after_convert(html, frontmatter, input_path, output_path)` - `after_build(config, output_dir)` - `markdown_extensions`, as a list or callable returning Python-Markdown extensions Hook failures are reported as warnings so one extension does not silently stop the entire build. ## Build auditing The repository includes a post-build auditor used by CI: ```bash python audit_site.py --output docs/site --source . ``` It exits non-zero when it finds: - Missing local files referenced by generated HTML - Known CDN origins for dependencies that WingTip vendors locally - Missing required search, SEO, feed, or PWA artifacts - Missing PWA icons when a project favicon was supplied - Output assets byte-identical to packaged branding assets - WingTip branding in a project whose configured name is not WingTip CI also runs a negative fixture that deliberately injects a broken asset reference and verifies that the auditor fails. --- ## GitHub Pages deployment The included GitHub Actions workflow builds and deploys on pushes to `main`. For another repository, the essential build steps are: ```yaml - uses: actions/setup-python@v5 with: python-version: "3.12" - run: pip install wingtip - run: wingtip - uses: actions/upload-pages-artifact@v3 with: path: docs/site ``` Set `base_url` to the final Pages URL so canonical, sitemap, feed, social, and alternate URLs are absolute in production. --- ## Social cards WingTip generates `social-card.png` during a build. Force regeneration after changing card settings: ```bash wingtip --regen-card ``` The `social_card` object supports title, tagline, light/dark style, font, and an optional logo. Per-page `og_image` and `twitter_image` frontmatter can override the site image. --- ## Custom 404 page Create `404.md` in the project root. WingTip converts it to `404.html` with the same Markdown processing and site template as other pages: ```markdown --- permalink: /404.html noindex: true --- # Page not found The requested documentation page does not exist. ``` --- ## Generated output A normal build includes: ```text docs/site/ ├── index.html ├── guide.html ├── guide.html.md ├── search_index.json ├── sitemap.xml ├── robots.txt ├── feed.xml ├── llms.txt ├── llms-full.txt ├── manifest.json ├── sw.js ├── offline.html ├── social-card.png ├── syntax.css └── static/ ``` `categories.json` and `versions.json` are emitted when pages declare those values. `favicon.png`, `icon-192.png`, and `icon-512.png` are emitted only when the project supplies a favicon. ## Current limitations and roadmap Mermaid diagrams, broad MDX compatibility, an automated hosted-platform importer, and a theme marketplace are not yet built. See the [roadmap and feature comparison](docs/roadmap.md) for planned work. --- ## License MIT. Use freely. Modify ruthlessly. --- File: docs/admonitions.md --- # Admonition Examples WingTip supports admonition blocks to highlight important information. These are special callout blocks that can be used to draw attention to specific content. ## Basic Usage Use `!!!` followed by the type of admonition to create a block: !!! note This is a simple note admonition. It can contain multiple paragraphs and other Markdown elements. - Lists - Code blocks - etc. ## Available Types ### Note !!! note This is a note admonition. Use it for general information. ### Warning !!! warning This is a warning admonition. Use it to warn users about potential issues. ### Danger !!! danger This is a danger admonition. Use it for critical warnings or dangerous operations. ### Tip !!! tip This is a tip admonition. Use it for helpful suggestions and best practices. ### Info !!! info This is an info admonition. Use it for additional context or background information. ### Success !!! success This is a success admonition. Use it to highlight positive outcomes or completion states. ## Advanced Usage ### Nested Content Admonitions can contain any Markdown content: !!! note ### A Header Inside an Admonition 1. Ordered lists work great 2. Code blocks work too: ```python def hello(): print("Hello from an admonition!") ``` Tables work when not nested in lists: | Column 1 | Column 2 | |----------|----------| | Cell 1 | Cell 2 | ### Custom Titles You can add custom titles to admonitions: !!! tip "Custom Title" This tip has a custom title! ## Dark Mode Support All admonitions automatically adapt to dark mode when the theme is switched, maintaining readability and consistent styling with the rest of the content. --- File: docs/changelog.md --- # WingTip Changelog ## [v0.6.5] - 2026-07-18 ### Redirects - `config.json` accepts `"redirects": [{"from": "/old-path", "to": "/new-path"}]`. Every build emits a static redirect page per rule (instant redirect with a visible fallback link — works on GitHub Pages and any static host) plus a `_redirects` file in Netlify/Cloudflare Pages format. Platform wildcard patterns (`:slug*`) are translated to splats and covered host-level. Redirect stubs are `noindex` and stay out of the sitemap and search index; collisions with real pages and missing targets warn at build time. - `wingtip migrate` carries the source platform's redirects into the new project's `config.json` — previously they were only listed in the migration report as manual work. Non-wildcard rules now count as converted. ### Fixed - Search on configured-`base_url` sites fetched its index from the absolute base URL, so previews, mirrors, and local serves of the built site broke search with a cross-origin error. The index is now fetched relative to the page, working on the canonical domain and any same-origin copy alike. - The search error message met neither light- nor dark-theme contrast requirements; it is now themed and AA-compliant. ## [v0.6.4] - 2026-07-18 ### Navigation - Wide desktop (≥1200px) pins the site nav open as a persistent left sidebar: no interaction needed, the hamburger hides, and the logo reverts to a plain home link. Below 1200px the toggle behavior is unchanged. Content centers between the left nav and the right table of contents with a 900px reading measure. - Visible breadcrumb trail on every non-home page — Home › directory groups (named via `_category.json`) or frontmatter category › page title — alongside the existing JSON-LD breadcrumbs. - The pinned sidebar scrolls the active page into view when it sits below the fold. - Sidebar groups already auto-collapse to the active branch; now documented behavior. ### Section hub pages - `_category.json` accepts `"index": true`: the directory gets a generated landing page listing its pages (with frontmatter descriptions) and nested groups. A source `index.md` always wins. Hubs appear in the sitemap, search index, and AI artifacts, gain an **Overview** link in their nav group, and breadcrumb directory crumbs link to them. ### SEO - `og:url`, `og:image`, and `twitter:image` (plus the card type that depends on the image) are emitted only when absolute, as the OpenGraph spec requires — zero-config builds previously shipped relative values that scrapers ignore. Configured-`base_url` sites are unchanged. - The post-build audit now resolves same-origin absolute URLs against the local build instead of skipping them as external, closing the blind spot that let a dead reference survive on configured-base sites. ## [v0.6.3] - 2026-07-17 ### GitHub-flavored Markdown - Task lists (`- [ ]` / `- [x]`) render as checkboxes instead of literal brackets; each checkbox is label-wrapped so it carries its item text as an accessible name. - GitHub alerts (`> [!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, `[!CAUTION]`) render as styled admonitions; adjacent alert blocks that python-markdown merges into one blockquote are split back apart. - Bare `http(s)://` and `www.` URLs in prose autolink, with trailing punctuation kept outside the link. - Search-index text extraction now uses the same Markdown pipeline as page rendering: fence markers, fence language tags, table pipe rows, and task markers no longer leak into search snippets. ### Navigation - The site nav no longer depends on clicking the logo: an always-present hamburger button toggles it (sites without a favicon previously had no header navigation at all), the logo now toggles instead of only opening, and `aria-expanded` is tracked. - Categorized sites: the slideout now clones the entire navigation (one list per category plus headings) instead of only the first list, with styled category headings and nested groups. - The mobile table-of-contents button worked for the first time: it was positioned underneath the fixed header and its click handler was never attached. Now placed below the header and wired up. - Client-side TOC generation no longer overwrites the build's heading ids, which had recreated duplicate anchors (`#setup` twice) and mangled unicode slugs; the TOC also indexes only content headings, not navigation headings. ### Theming - Light mode was unreadable in the header: the navbar title and every slideout link were hardcoded white on a background that flips to white. Nav colors are now paired variables. - Manually toggling the theme now recolors the navbar too — previously it only tracked the OS preference, producing a white navbar on a dark page. ### Accessibility - Zero axe-core violations across every generated page in both color schemes, enforced in CI by a new `audit_a11y.js` gate (with a negative test, like the existing audit). - Skip-to-content link; the closed slideout's links no longer occupy the tab order (keyboard users previously tabbed through the entire site nav to reach search); focus moves into the nav on open and back to the toggle on close. - Landmarks and labels: header/banner, labeled navs, `role=search`, named theme toggle; decorative logo alt. - WCAG AA contrast: admonition titles, code tokens, footer text, and the light-theme link color; content links are underlined rather than color-only. - Code blocks are keyboard-scrollable (`tabindex="0"`); `prefers-reduced-motion` collapses all animation. ### PWA and artifacts - The offline fallback page is now actually precached — the service worker referenced `offline.html` but built its cache list before the file was written, so offline navigation to an uncached URL had always shown the browser error page. - `noindex` frontmatter is honored by `llms.txt`, `llms-full.txt`, and `skill.md`, matching the search index and sitemap. - Projects without a root `404.md` get a default styled 404 page; the raw-Markdown alternate (`.html.md`) is written for every page, fixing the dead alternate link on 404 pages. - Sites without a `repo_url` no longer render an empty GitHub link in the footer. ## [v0.6.2] - 2026-07-17 - GFM strikethrough support: `~~text~~` renders as `` instead of literal tildes. - Deploys now reach already-open pages: when an updated service worker activates, the page reloads once to pick up the new cache (first installs never reload). ## [v0.6.1] - 2026-07-17 - Fixed search-result links double-prefixing the site base on configured-base sites (clicking a result produced `https://site/https://site/page.html` → 404). Absolute index URLs are now used as-is; relative URLs still get the page-base prefix that nested zero-config pages need. ## [v0.6.0] - 2026-07-17 ### Migration importer - New `wingtip migrate [--output DIR]` command converts a hosted documentation project (`docs.json` / `mint.json` configuration) into a new WingTip project. Strictly read-only: the source is never modified. - Pages (`.md` and `.mdx`) copy with nested paths preserved in URLs; module `import`/`export` lines are stripped from MDX and JSX components are inventoried per page. - Navigation groups map to directory `_category.json` files where they align with directories; page order carries over as `order` frontmatter. - Platform-style absolute internal links (`/guides/intro`) rewrite to portable relative Markdown links. - Site name, description, brand colors, and favicon carry into `config.json` and `theme.json`. - Every migration emits `migration-report.md`: an executive summary (pages converted, URLs preserved, groups mapped, manual follow-ups) followed by converted items, manual-work details, and next steps. - Common MDX components are approximated as Markdown: callouts (`Note`/`Warning`/`Tip`/`Info`/`Check`) become blockquotes, `Step`/`Tab`/`Accordion` become bold labels, `Card` becomes a link, layout wrappers are dropped, and any leaf component with a `src` attribute becomes an image. JSX nesting indentation is removed outside code fences; remaining unknown components are inventoried per page. - Links in raw `href`/`src` attributes are rewritten alongside Markdown links; resolution tries page-relative then site-root paths, with a case-insensitive fallback that emits the canonical on-disk casing. - Dynamic link expressions (`href={variable}`) are stubbed to `#` and reported; unresolvable internal links are reported as suspected broken links in the source. - MDX comments (`{/* ... */}`) are stripped; multi-line JSX/HTML tags are joined before conversion. - Fixed WingTip's link rewriter stripping the `docs/` prefix from links whose URL space legitimately contains `docs/…`. - Validated against real public hosted-docs repositories of 46, 249, 315, and 5,387 pages: URLs preserved on every page, and every remaining audit finding is an itemized pre-existing source defect (stale links, OpenAPI-generated pages, platform runtime paths). ### Agent-native artifacts - Every build now emits `skill.md` alongside `llms.txt`: retrieval instructions for AI agents (whole-corpus file, per-page Markdown alternates, citation guidance). Required by the audit gate. - `wingtip migrate` writes `AGENTS.md` into the new project: docs-maintenance instructions any coding agent can follow — build, verify, add pages, preserve URLs — so the AI your team already uses maintains the docs, no hosted platform required. - Migration guide gains a "Serving your migrated site" section covering both topologies (dedicated docs domain and main-domain subpath) with copy-paste nginx, Caddy, Vercel, and Netlify configuration; subpath URL correctness (og:url, canonicals, RSS, sitemap, robots, PWA scope) verified against the demo deployment. ### Package and page-weight diet - Material Icons converted from TTF to WOFF2 (348KB → 125KB) — every generated site's deploy and service-worker precache shrinks accordingly; browsers only fetch the font when an icon glyph is actually used. - Removed an unreferenced bundled font (297KB). Package: 810KB → 608KB compressed. ### Audit gate - The post-build auditor now parses pages instead of regex-scanning raw text: only `href`/`src` attributes on rendered elements are checked, so code samples and the raw-markdown embed no longer produce false positives; custom URL schemes (`cursor:`, `vscode:`, …) are treated as external. - Link-resolution verdicts are memoized, making large-site audits fast (one filesystem check per unique target instead of pages × links). - The 5,387-page stress test recorded a scaling finding for the roadmap: per-page full navigation makes total output size quadratic in page count; migration and build times scale linearly. ## [v0.5.0] - 2026-07-17 ### Nested content and navigation - `docs/` is now discovered recursively; nested source paths are preserved in generated URLs (`docs/guides/intro.md` → `guides/intro.html`) so existing site structures survive migration. - Sidebar navigation renders nested directories as collapsible groups, auto-expanded along the active page's path. `_category.json` (`name`, `order`) names and orders groups; `order`/`nav_order` frontmatter orders pages. - A `README.md` inside a docs subdirectory maps to that directory's `index.html`. - Content images under `docs/` map to site-root URLs mirroring the source tree. - Zero-config builds now emit depth-correct relative URLs for assets, canonicals, search, feeds, and PWA files on nested pages. - Duplicate output paths are detected, warned about, and skipped. - Search, sitemap, RSS, llms.txt, concatenated docs, and service-worker precache all include nested pages. ### Packaging and CLI - Relative README links are absolutized at package-build time (`hatch-fancy-pypi-readme`) so they resolve on the PyPI project page; the repo README stays relative for GitHub and the demo site. - `wingtip --help` describes recursive docs discovery. ## [v0.4.2] - 2026-07-17 - Fixed the README social-card image on PyPI by using an absolute URL. - Reworded the branding guarantee: generated sites carry no "powered by" badge, injected links, or generator watermarks. - Optimized the social card image from 546KB to 244KB. ## [v0.4.1] - 2026-07-17 ### Packaging and first-run experience - Added a standard Python package with `pyproject.toml` and the `wingtip` console command. - Bundled templates, styles, scripts, fonts, and other runtime assets in the wheel. - Added `--source`, `--serve`, `--regen-card`, `--output`, and `--version` CLI options. - Made zero-configuration builds derive project identity from the source repository instead of inheriting WingTip branding. - Added a cold-install wheel fixture to verify the first-run experience outside the source checkout. ### Search and discovery - Added per-page title, description, keywords, canonical, robots/noindex, Open Graph, Twitter, author, date, language, category, and version metadata. - Added `TechArticle` and breadcrumb JSON-LD, RSS, `llms.txt`, concatenated documentation, and per-page Markdown alternates. - Added hreflang alternates and language-aware page metadata. - Excluded noindexed pages from search, sitemap, category/version indexes, and structured data. ### Performance, offline use, and security - Vendored core frontend dependencies so generated sites do not require dependency CDNs. - Added responsive local-image variants, intrinsic dimensions, and lazy loading. - Added a PWA manifest, service worker, and offline fallback. - Made favicon and PWA icon generation opt-in to project-provided branding. - Added configurable analytics, custom head snippets, and Content Security Policy output. ### Quality and extensibility - Added plugin hooks before and after conversion/build plus plugin-provided Markdown extensions. - Added a post-build auditor for missing local files, CDN regressions, required artifacts, PWA consistency, and branding leaks. - Added a CI negative test proving that the auditor rejects deliberately broken output. - Fixed Markdown links containing fragments or query strings and removed stale raw-documentation links. ### Documentation - Repositioned WingTip around portable, SEO-first documentation output. - Added an honest current-capability roadmap and hosted-platform migration guide. - Added a structured public migration-review intake form. - Documented current limitations, including top-level-only content discovery, MDX compatibility, and OpenAPI generation. ## [v0.4.0] ### ✨ Features #### GitHub-Flavored Markdown (GFM) - Added comprehensive GFM support with markdown-it-py plugins: - Task lists with checkboxes - Footnotes and references - Tables with full styling - Strikethrough text - Better autolinking - Definition lists #### Math & Diagrams - Added LaTeX math rendering with KaTeX: - Inline math with `$...$` - Display math with `$$...$$` - Fast client-side rendering - See [Math Examples](./math-examples.md) #### Document Versioning - Added version management system: - CLI support for versioned builds - Version selector dropdown in UI - Version-aware sitemap and links - "Latest" version redirect #### Plugin System - Introduced extensible plugin architecture: - Custom Python plugins in `plugins/` directory - Build process hooks for customization - Sample banner plugin included #### Admonitions - Added support for admonition blocks: - Multiple types (note, warning, danger, tip) - Custom styling for each type - Full dark mode support #### Search Enhancements - Enhanced search functionality: - `/` to focus search - Arrow keys to navigate results - Enter to select - Esc to clear - Added result highlighting in search matches - Improved search UI with theme integration - Fixed search index path handling for GitHub Pages ### 🎨 UI & Accessibility - Comprehensive dark mode support: - GFM elements (tables, task lists, footnotes) - Admonition blocks with distinct themes - KaTeX math expressions - Code syntax highlighting - Enhanced mobile responsiveness: - Improved tablet breakpoints (768px) - Full-width search on mobile - Better nav layout on small screens - Added ARIA labels and roles for better screen reader support - Improved nav-container styling and transitions - Consolidated and optimized CSS ### 🛠 Infrastructure - Added automatic .md to .html link conversion - Improved GitHub Pages compatibility - Enhanced build system for versioned docs - Optimized asset loading and paths ### 📚 Documentation - Added new guides: - Math rendering examples and usage - Versioning system setup - Plugin development guide - Admonition syntax reference - Updated existing docs: - Search features with keyboard shortcuts - Accessibility guidelines - Mobile-responsive design - Dark mode support ## [v0.3.0] ### ✨ Features - Added client-side search functionality to allow users to quickly find relevant documentation. See [Search Features](./search-features.md) for more details. - Implemented various improvements to mobile responsiveness for a better user experience on smaller devices. ### 🛠 Fixes & Improvements - General stability and performance improvements. ### 📚 Documentation - Updated roadmap and changelog to reflect recent feature additions and version changes. ## [v0.2.0] ### ✨ Features - Added support for custom 404 pages: Users can create a `404.md` in the project root, which will be converted to `docs/site/404.html` and included in the sitemap. ### 🛠 Fixes & Improvements - Corrected path handling in `main.py` and `serve.py` to ensure proper functionality when WingTip is used as a nested module (e.g., running `python wingtip/main.py` from a parent project directory). - Refined `killDocs.sh` script: - Now attempts a graceful process termination (SIGTERM) before resorting to a forceful one (SIGKILL). - Removed obsolete PID file handling logic. - Removed an unused internal function (`write_docs_index`) from `main.py`. - Removed a duplicate inclusion of `clipboard.js` in `template.html`. ### 📚 Documentation - Updated `README.md` to accurately reflect nested project structure usage (e.g., `wingtip/main.py` for commands and file paths). - Clarified `syntax.css` (generated) vs. `pygments.css` (manual reference) in `README.md`. - Corrected the GitHub Actions workflow example in `README.md` to use `python wingtip/main.py`. - Added `uv` pip install instructions as an alternative in `README.md`. - Documented the new custom 404 page feature in `README.md`. - Documented how to stop the development server (Ctrl+C) and the purpose/usage of `killDocs.sh` in `README.md`. - Corrected `main.py` help text for sitemap generation and removed outdated notes. - Removed mention of a non-implemented "TOC keyboard shortcut (`Alt+D`)" from this changelog (v0.1.0 entry). ## [v0.1.0](#v010) ### ✨ Features - Converts `README.md` and `docs/*.md` to static HTML - Dark/light mode with system theme detection - Slideout nav with project logo - Code syntax highlighting for both themes - Copy-to-clipboard buttons for all code blocks - Previous/next navigation links - GitHub "Edit this page" links - SEO optimization with meta tags and `sitemap.xml` - Built-in Open Graph social card generator (via Pillow) - Live reload development server with scroll state preservation - GitHub Actions workflow for automatic Pages deployment - Configurable `favicon`, `og_image`, and `twitter_handle` in `config.json` ### 🛠 Technical - Markdown to HTML conversion with Pygments highlighting - Clean Python codebase with minimal dependencies - JSON config with fallback defaults - CLI support for `--output` and `--regen-card` - Auto social card fallback and copy to root if `og_image` is unset - Auto-download of Google Fonts for social card rendering - Customizable navigation + dynamic Table of Contents ### 📚 Documentation - Full quickstart and config guide - GitHub Pages deployment with prebuilt `pages.yml` - Example `config.json` for SEO and appearance - Theme toggle and favicon support --- File: docs/configuration.md --- # Configuration WingTip uses a `config.json` file in your project root to control metadata, theming, GitHub integration, and social card generation. This file is **required** if you use: * A custom favicon * Social card generation (`--regen-card`) * SEO-friendly `base_url`, `og_image`, or `twitter_handle` * GitHub "Edit this page" links --- ## Example `config.json` ```json { "base_url": "https://yourusername.github.io/yourrepo", "project_name": "WingTip", "version": "0.1.0", "description": "SEO-friendly Markdown-to-HTML static site generator for GitHub Pages", "author": "Your Name", "repo_url": "https://github.com/yourusername/yourrepo/", "og_image": "social-card.png", "favicon": "https://yourcdn.com/favicon.png", "twitter_handle": "@yourhandle", "github": { "repo": "yourusername/yourrepo", "branch": "main" }, "social_card": { "title": "WingTip", "tagline": "Make your docs fly.", "theme": "light", "font": "Poppins", "image": "social-card.png" } } ``` --- ## Top-Level Fields | Key | Required | Description | | ---------------- | -------- | ------------------------------------------------------------- | | `base_url` | ✔︎ | Your GitHub Pages URL (used in canonical links, sitemap, etc) | | `project_name` | ✔︎ | Name of your project (shown in nav and footer) | | `version` | ✱ | Optional version string shown in footer | | `description` | ✔︎ | Used in meta tags, Open Graph, Twitter | | `author` | ✱ | Used in meta tags | | `repo_url` | ✱ | Used in footer GitHub link | | `og_image` | ✱ | Open Graph image (used unless generated) | | `favicon` | ✱ | PNG favicon shown in nav | | `twitter_handle` | ✱ | Shown in meta tags | --- ## GitHub Integration | Key | Required | Description | | --------------- | -------- | -------------------- | | `github.repo` | ✔︎ | e.g. `username/repo` | | `github.branch` | ✱ | Defaults to `main` | Used to generate **"Edit this page on GitHub"** links. --- ## Social Card Settings | Key | Required | Description | | --------------------- | -------- | --------------------- | | `social_card.title` | ✔︎ | Large text on card | | `social_card.tagline` | ✔︎ | Subtitle text | | `social_card.theme` | ✱ | `"light"` or `"dark"` | | `social_card.font` | ✱ | Any Google Font | | `social_card.image` | ✱ | Path to output PNG | To generate the card, run: ```bash python wingtip/main.py --regen-card ``` If no `og_image` is set, the PNG is also copied to `./social-card.png`. --- ## Font Notes * WingTip uses the Google Fonts API to fetch and cache fonts * If unavailable, it will fall back to `Arial` or a system font * Fonts are stored in `wingtip/fonts/` if downloaded --- ## Fallback Behavior | Scenario | Behavior | | --------------------- | ------------------------------------------------------------------------------ | | `config.json` missing | Falls back to hardcoded defaults (but no favicon/social card support) | | `og_image` unset | Falls back to generated `docs/site/social-card.png` and copies to project root | | `favicon` unset | Default browser icon will be used | --- ## Theming Your Site WingTip offers ways to customize the visual appearance of your documentation. ### Basic Theme Overrides (`theme.json`) You can easily customize global fonts and key colors for both light and dark modes by creating a `theme.json` file in your project's root directory. This allows for quick branding changes without needing to write custom CSS. For detailed instructions on how to structure `theme.json`, available customization options (fonts, colors for light/dark modes), and examples, please see the comprehensive **[Theming Guide](theming.md)**. ### Advanced CSS Customization For more fine-grained control or styles not covered by `theme.json`, you can incorporate your own custom CSS. Refer to the [Advanced CSS Customization section in the Theming Guide](theming.md#advanced-css-customization) for details. --- File: docs/external-links.md --- # External Link Handling WingTip provides flexible control over how external links (links to other websites) are handled in your documentation. ## Configuration External link behavior is controlled through the `external_links` section in your `config.json`: ```json { "external_links": { "open_in_new_tab": true, "exclude_domains": ["example.com", "mycompany.com"], "include_domains": ["external-docs.example.com"], "exclude_paths": ["/docs/internal/*"], "attributes": { "rel": "noopener noreferrer", "class": "external-link" } } } ``` ### Options * `open_in_new_tab` (boolean): Global setting that determines if external links should open in a new tab by default. * `exclude_domains` (array): List of domains that should NOT open in a new tab, even if `open_in_new_tab` is true. * `include_domains` (array): List of domains that should ALWAYS open in a new tab, regardless of other settings. * `exclude_paths` (array): URL path patterns (using glob syntax) that should NOT open in a new tab. * `attributes` (object): Additional HTML attributes to add to external links: * `rel`: Relationship attributes (default: "noopener noreferrer" for security) * `class`: CSS class to apply to external links (default: "external-link") ## Examples ### Basic Configuration The simplest configuration is to make all external links open in new tabs: ```json { "external_links": { "open_in_new_tab": true } } ``` ### Selective Control You can exempt certain domains from opening in new tabs while keeping the default behavior for others: ```json { "external_links": { "open_in_new_tab": true, "exclude_domains": ["trusted-site.com", "internal-docs.com"] } } ``` Or force specific domains to always open in new tabs while leaving others as regular links: ```json { "external_links": { "open_in_new_tab": false, "include_domains": ["external-api-docs.com", "github.com"] } } ``` ### Path-Based Control You can use path patterns to control behavior for specific URL paths: ```json { "external_links": { "open_in_new_tab": true, "exclude_paths": [ "/docs/internal/*", "*/preview/*" ] } } ``` ### Custom Attributes Add custom attributes to external links for styling or tracking: ```json { "external_links": { "open_in_new_tab": true, "attributes": { "rel": "noopener noreferrer", "class": "external-link branded", "data-track": "external-click" } } } ``` ## Default Behavior If no configuration is provided, WingTip will: 1. Open external links in new tabs 2. Add `rel="noopener noreferrer"` for security 3. Add `class="external-link"` for styling ## Styling External Links The default `external-link` class allows you to style external links differently. For example, in your custom CSS: ```css .external-link { /* Add an external link indicator */ background-image: url('data:image/svg+xml;utf8,'); background-position: right 3px center; background-repeat: no-repeat; background-size: 12px; padding-right: 20px; } ``` This will add a small external link icon to visually indicate links that open in new tabs. --- File: docs/live-reload.md --- # Live Reload in WingTip WingTip includes a development server with automatic rebuild and browser refresh on save. This makes local authoring fast and seamless. ## How It Works * `wingtip/serve.py` runs a `livereload` server on port 8000 * It watches these files: * `README.md` * All `docs/*.md` * `template.html` * `main.py` * When a change is detected: * It runs `main.py` in a temporary output folder first * If the build succeeds, it re-runs the actual build * If the build fails, the current site is preserved * The browser auto-refreshes via WebSocket connection * Scroll position is preserved after reload ## Run the Dev Server ```bash pip install livereload python wingtip/serve.py ``` This will: * Open `http://localhost:8000` in your browser * Rebuild the site automatically when you save `.md` or `.html` files * Refresh the browser instantly, even retaining scroll ## Notes * Temporary build directory: `docs/site_tmp` * Live output directory: `docs/site` * Scroll restoration is built-in via the injected livereload client * Errors during rebuild are logged but do not overwrite the current site --- File: docs/math-examples.md --- # Math Examples WingTip supports LaTeX math rendering using KaTeX. You can write both inline and display math expressions. ## Inline Math Use `\(...\)` for inline math. For example: The area of a circle is \(A = \pi r^2\) and its circumference is \(C = 2\pi r\). The quadratic formula is \(x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}\). ## Display Math Use `\[...\]` for display math. For example: The Pythagorean theorem: \[a^2 + b^2 = c^2\] Maxwell's equations in differential form: \[ \begin{aligned} \nabla \cdot \mathbf{E} &= \frac{\rho}{\varepsilon_0} \\ \nabla \cdot \mathbf{B} &= 0 \\ \nabla \times \mathbf{E} &= -\frac{\partial \mathbf{B}}{\partial t} \\ \nabla \times \mathbf{B} &= \mu_0\left(\mathbf{J} + \varepsilon_0\frac{\partial \mathbf{E}}{\partial t}\right) \end{aligned} \] ## Matrix Examples A 2x2 matrix: \[ \begin{pmatrix} a & b \\ c & d \end{pmatrix} \] ## Chemical Equations Chemical equations can be written using math mode: \[\ce{CO2 + H2O \rightleftharpoons H2CO3}\] ## Accessibility Math expressions are rendered with proper semantic markup for screen readers. When using a screen reader: 1. Inline math is announced naturally within the text flow 2. Display math is treated as a distinct block with appropriate ARIA labels 3. Complex expressions are broken down into meaningful parts ## Dark Mode Support Math expressions automatically adapt to dark mode when the theme is switched, maintaining readability and consistent styling with the rest of the content. --- File: docs/migration.md --- --- title: Migrate to WingTip description: Move documentation from a hosted platform or another static generator to portable WingTip output. keywords: - documentation migration - open source documentation - static site generator category: Getting started date: 2026-07-17 --- # Migrate to WingTip Move an existing documentation repository to static output you can inspect, audit, and host anywhere. ## Automated migration If the source project uses a `docs.json` or `mint.json` configuration, run the importer: ```bash pip install wingtip wingtip migrate ./existing-docs --output ./wingtip-docs ``` The importer is **strictly read-only** — the source project is never modified. It writes a new WingTip project containing: - Every `.md` and `.mdx` page, with nested paths preserved in URLs - Navigation order (`order` frontmatter) and directory groups (`_category.json`) derived from the source navigation - Internal links rewritten to portable relative links — Markdown links and raw `href`/`src` attributes, absolute or extensionless, with case-insensitive resolution - Common MDX components approximated as Markdown (callouts, steps, tabs, cards, images); anything unhandled is inventoried per page - Site name, description, brand colors, and favicon carried into `config.json` and `theme.json` - `migration-report.md` — a scored summary of what converted cleanly and what needs manual attention (custom components, dynamic link expressions, suspected broken links in the source, redirects, unmapped navigation, configuration not carried) - `AGENTS.md` — maintenance instructions for coding agents, so the AI assistant your team already uses can build, verify, and extend the docs (no hosted platform required) The importer is validated against real public hosted-docs repositories ranging from 46 to over 5,000 pages. Then preview and iterate: ```bash cd ./wingtip-docs && wingtip --serve ``` Broad MDX component compatibility and OpenAPI reference generation remain roadmap work; the report lists every affected page. The rest of this guide covers manual migration for repositories without a recognized configuration format. ## What works today WingTip currently supports: - A repository `README.md` as the documentation home page - Markdown files in `docs/`, discovered recursively with nested paths preserved in URLs - Collapsible sidebar groups from directory structure (`_category.json` for names and ordering) - YAML frontmatter for titles, descriptions, indexing, social metadata, dates, language, category, and version - Relative Markdown links, including fragments and query strings - Local images with responsive variants and lazy loading - Admonitions, tables, footnotes, definition lists, code highlighting, and KaTeX - Local search, sitemap, robots directives, RSS, structured data, and AI-readable artifacts - Static deployment to GitHub Pages or any host that serves ordinary files ## What requires manual work Plan for manual review when the source repository uses: - MDX or framework-specific components (the importer flags each affected page) - Navigation tabs, products, or language switchers (groups map automatically where they align with directories) - OpenAPI-generated endpoint pages or an interactive API playground - Platform-managed redirects, authentication, previews, analytics, or access controls - Server-side features that cannot run from static files WingTip never silently discards unsupported content — the importer leaves unsupported markup in place and lists it in `migration-report.md`. Use the checklist below and request a migration review when compatibility is uncertain. ## Migration process ### 1. Work on a branch Keep the current documentation deployment unchanged while validating WingTip. ```bash git switch -c wingtip-migration ``` ### 2. Install WingTip ```bash pip install "wingtip[serve]" ``` ### 3. Prepare the source layout Retain or create this minimal structure: ```text your-project/ ├── README.md ├── docs/ │ ├── getting-started.md │ ├── authentication.md │ └── api.md ├── config.json ├── theme.json └── favicon.png ``` Nested directories under `docs/` are supported directly — nested paths are preserved in generated URLs, so existing structures can move as-is. Record every old and new URL before changing any filenames. ### 4. Add production metadata Create `config.json` with the final public URL and project identity: ```json { "base_url": "https://docs.example.com", "project_name": "Acme Docs", "description": "Documentation for Acme developers.", "repo_url": "https://github.com/acme/docs", "github": { "repo": "acme/docs", "branch": "main" } } ``` A local `favicon.png` is optional. WingTip does not insert its own branding when your project does not provide one. ### 5. Build and preview ```bash wingtip --serve ``` Compare every source page with the preview. Pay particular attention to links, images, code examples, frontmatter, canonical URLs, noindex directives, and custom components. ### 6. Audit the output When working from this repository, run the post-build auditor: ```bash python audit_site.py --output docs/site --source . ``` The auditor checks required artifacts, missing local files, unexpected dependency CDNs, conditional PWA icons, and branding leaks. ### 7. Preserve URLs Create a URL inventory before switching production traffic: | Existing URL | WingTip URL | Action | | --- | --- | --- | | `/getting-started` | `/getting-started.html` | Redirect or preserve at the host | | `/guides/auth` | `/authentication.html` | Add a permanent redirect | WingTip does not yet generate platform redirects. Configure permanent redirects at your static host and verify that each destination returns the intended page. ### 8. Validate before cutover Confirm that: - Every public source page has a destination - Existing high-value URLs resolve or permanently redirect - Internal links and assets load without errors - Canonicals use the production domain - Intended public pages appear in `sitemap.xml` and `search_index.json` - Private, hidden, or obsolete pages are noindexed or excluded - `llms.txt`, `llms-full.txt`, `feed.xml`, and Markdown alternates are present - Mobile, keyboard, light-mode, dark-mode, and offline behavior are acceptable - Analytics and custom scripts are explicitly configured rather than inherited ## Serving your migrated site WingTip emits ordinary static files, so both common documentation topologies work. Pick one, set `base_url` to match, rebuild, and deploy `docs/site/`. ### Topology A — dedicated docs domain (`docs.example.com`) Set `"base_url": "https://docs.example.com"` and serve `docs/site/` as the web root. **GitHub Pages / Netlify / Vercel / Cloudflare Pages:** point the project at the `docs/site` output directory (or publish it from CI) and attach the custom domain in the host's dashboard. No further configuration. **nginx:** ```nginx server { server_name docs.example.com; root /var/www/docs-site; # contents of docs/site/ index index.html; error_page 404 /404.html; } ``` **Caddy:** ```caddy docs.example.com { root * /var/www/docs-site file_server handle_errors { rewrite * /404.html file_server } } ``` ### Topology B — subpath on the main domain (`example.com/docs`) Set `"base_url": "https://example.com/docs"` — generated URLs, canonicals, feeds, and the service worker all carry the `/docs` prefix — then map that prefix to the static files on your main site's proxy. **nginx (in the main site's server block):** ```nginx location /docs/ { alias /var/www/docs-site/; # contents of docs/site/ index index.html; try_files $uri $uri/ /docs/404.html; } ``` **Caddy:** ```caddy example.com { handle_path /docs/* { root * /var/www/docs-site file_server } # ... your main site ... } ``` **Vercel (`vercel.json` on the main site):** ```json { "rewrites": [{ "source": "/docs/:path*", "destination": "https://your-docs-deployment.example/:path*" }] } ``` **Netlify (`_redirects` on the main site):** ```text /docs/* https://your-docs-deployment.example/:splat 200 ``` The subpath topology is the one search engineers usually prefer: documentation authority accrues to the main domain rather than a subdomain. Both are fully supported — the demo site itself runs at a subpath (`/WingTip-Static-Site-Generator/` on GitHub Pages), so every generated URL scheme is exercised there. ## Request a migration review If the repository is public, submit a [migration review request](https://github.com/semanticentity/WingTip-Static-Site-Generator/issues/new?template=migration.yml). You will receive a structured compatibility assessment covering content, navigation, URLs, assets, metadata, and unsupported constructs. Do not post private repositories, credentials, customer data, internal URLs, or proprietary content in a public issue. For a private project, provide a sanitized reproduction that preserves the relevant file structure and component patterns. ## Migration guarantee The current promise is transparency, not automatic parity: supported content should build reproducibly, and unsupported content should be identified before a production cutover. The planned importer will formalize this as both human-readable and JSON migration reports. --- File: docs/plugins.md --- # Plugin Development Guide WingTip includes a plugin system that allows you to extend and customize the build process. This guide explains how to create and use plugins. ## Plugin Basics A WingTip plugin is a Python module that defines a class with specific hook methods. The plugin class can modify content at various stages of the build process. ### Plugin Structure Here's the basic structure of a plugin: ```python class MyPlugin: def __init__(self, config=None): """Initialize plugin with optional configuration.""" self.config = config or {} def before_markdown_conversion(self, md_content, metadata, filepath): """Called before markdown is converted to HTML.""" return md_content def after_html_generation(self, html_content, metadata, filepath): """Called after markdown is converted but before page assembly.""" return html_content def after_full_page_assembly(self, final_html, metadata, output_filepath): """Called after the full page HTML is assembled.""" return final_html # Required: Plugin class must be named 'Plugin' Plugin = MyPlugin ``` ### Hook Points Plugins can hook into three points in the build process: 1. **before_markdown_conversion**: Modify raw markdown before it's converted to HTML 2. **after_html_generation**: Modify generated HTML before it's inserted into the page template 3. **after_full_page_assembly**: Modify the complete HTML page before it's written to disk ## Sample Plugin WingTip includes a sample banner plugin that demonstrates the plugin system. You can find it in `plugins/sample_banner_plugin.py`: ```python class BannerPlugin: def __init__(self, config=None): self.config = config or {} self.banner_text = config.get('text', 'This is a sample banner!') self.banner_type = config.get('type', 'info') def after_html_generation(self, html_content, metadata, filepath): banner_html = f'' h1_end = html_content.find('') if h1_end != -1: return html_content[:h1_end + 5] + banner_html + html_content[h1_end + 5:] return html_content Plugin = BannerPlugin ``` ## Using Plugins To use a plugin: 1. Place your plugin file in the `plugins/` directory 2. Update your `config.json` to enable and configure the plugin: ```json { "plugins": { "banner": { "enabled": true, "config": { "text": "Welcome to my docs!", "type": "info" } } } } ``` ## Creating Your Own Plugin 1. Create a new Python file in the `plugins/` directory 2. Define your plugin class with the desired hook methods 3. Assign your class to `Plugin` at the module level 4. Configure the plugin in `config.json` ### Example: Code Stats Plugin Here's an example plugin that counts lines of code in code blocks: ```python class CodeStatsPlugin: def after_html_generation(self, html_content, metadata, filepath): import re code_blocks = re.findall(r'
(.*?)
', html_content, re.DOTALL) total_lines = sum(block.count('\n') + 1 for block in code_blocks) stats_html = f'

This page contains {total_lines} lines of code.

' return html_content + stats_html Plugin = CodeStatsPlugin ``` ## Best Practices 1. **Documentation**: Include docstrings and comments explaining your plugin's purpose and configuration options 2. **Error Handling**: Gracefully handle errors and edge cases 3. **Performance**: Keep modifications efficient, especially for large sites 4. **Testing**: Test your plugin with various content types and edge cases ## Plugin API Reference ### Hook Methods #### before_markdown_conversion - **Parameters**: - `md_content`: Raw markdown content - `metadata`: Page metadata from frontmatter - `filepath`: Source markdown file path - **Returns**: Modified markdown content #### after_html_generation - **Parameters**: - `html_content`: Generated HTML content - `metadata`: Page metadata - `filepath`: Source file path - **Returns**: Modified HTML content #### after_full_page_assembly - **Parameters**: - `final_html`: Complete page HTML - `metadata`: Page metadata - `output_filepath`: Output file path - **Returns**: Modified page HTML ### Configuration Plugins can access their configuration through `self.config` in the plugin class. The configuration is loaded from `config.json`. --- File: docs/quickstart.md --- # Quick Start Add beautiful documentation to your project in under 5 minutes. ## 1. Install WingTip (2 min) ```bash # In your project directory: git clone https://github.com/SemanticEntity/WingTip.git pip install -r WingTip/requirements.txt ``` ## 2. Add Your Docs (1 min) ``` your-project/ ├── README.md # Becomes the homepage └── docs/ # Add .md files for documentation └── guide.md ``` ## 3. Configure (Optional, 30s) Create `config.json` in your project root: ```json { "project": "Your Project", "description": "Your project description", "github": { "repo": "username/repo", "branch": "main" } } ``` ## 4. Generate and Preview (10s) ```bash python wingtip/main.py # Build the site python wingtip/serve.py # Start local dev server with live reload ``` Visit `http://localhost:8000` in your browser. ## 5. Deploy to GitHub Pages (1 min) 1. Push your changes to GitHub 2. Go to **Settings → Pages** 3. Set branch to `main` and folder to `/docs/site` 4. Your documentation will be published to `https://username.github.io/repo` --- Your docs now include: * Responsive layout with light/dark mode * Mobile-friendly sidebar and TOC * SEO-optimized output and social cards * GitHub "Edit this page" links * Instant preview with live reload No build tools. No config lock-in. Just Markdown in, HTML out. --- File: docs/roadmap.md --- # WingTip Roadmap ## Objective Make WingTip the most reliable way to move a documentation project from a hosted platform to portable static output: migrate an existing repository, preserve its information architecture and URLs, and prove the result with automated search, performance, and integrity audits. WingTip will not try to reproduce every hosted collaboration feature. It will compete on ownership, migration quality, technical discovery, build transparency, portability, and zero mandatory platform cost. ## Current position Status reflects the repository as of July 18, 2026. Competitor capabilities change; any public comparison must be generated from a reproducible fixture rather than unsupported marketing claims. | Capability | WingTip now | Competitive status | Priority | | --- | --- | --- | --- | | Portable static HTML output | Yes | Leadership target | Maintain | | No mandatory hosted runtime | Yes | Leadership target | Maintain | | No mandatory CDN frontend assets | Yes | Leadership target | Maintain | | No imposed generator branding | Yes | Leadership target | Maintain | | Canonical, robots, sitemap, RSS, OG/Twitter | Yes | Strong parity | Maintain | | `TechArticle` and breadcrumb JSON-LD | Yes | Strong parity | Expand validation | | `llms.txt`, full docs, Markdown alternates | Yes | Strong parity | Add agent skills | | Per-page noindex, dates, language, category, version | Yes | Strong parity | Improve UI | | Hreflang alternates | Yes | Search differentiator | Add cluster audit | | Local search | Yes | Basic parity | Ranking controls | | PWA and offline fallback | Yes | Differentiator | Maintain | | Responsive image generation | Yes | Differentiator | Add budgets | | Configurable analytics, head snippets, and CSP | Yes | Strong parity | Validate policies | | Plugin hooks and Markdown extensions | Yes | Extensibility base | Formalize API | | Post-build integrity/branding audit | Yes | Leadership target | Expand | | Recursive content trees | Yes | Parity | Maintain | | Grouped, collapsible navigation | Yes | Tabs, versions, languages pending | P1 | | Redirect migration and static-host outputs | Yes | Static redirect pages + `_redirects` emitted from config; migrate carries platform rules | Expand (redirect-chain validation) | | Hosted-platform configuration import | Yes | Differentiator | Expand | | MDX compatibility analysis | Yes | Differentiator | Expand | | OpenAPI reference generation | No | Major parity blocker | P1 | | API playground | No | Major parity blocker | P1 | | Agent `skill.md` discovery | Yes | Emerging differentiator | Maintain | | Preview deployments and visual review | No | Workflow gap | P2 | | Authentication and private docs | No | Enterprise gap | P3/partner | | Hosted collaborative editor | No | Intentionally not core | Ecosystem | ## Delivery plan ### Phase 1 — Migration quality (P0) #### Content and navigation model - ✅ Recursively discover `.md` content (`.mdx` compatibility still pending) - ✅ Preserve nested source paths in generated URLs (`docs/guides/intro.md` → `guides/intro.html`) - ✅ Ordered navigation groups: `_category.json` (`name`, `order`) per directory, `order`/`nav_order` frontmatter per page - ✅ Render grouped/collapsible navigation from the directory tree - ✅ Detect duplicate output paths (collisions are warned and skipped) - ✅ GFM fidelity: task lists, GitHub alerts, strikethrough, bare-URL autolinks (July 18, 2026) - ✅ Visible breadcrumb navigation in the page body, matching the JSON-LD trail (July 18, 2026) - ✅ Section hub pages: `_category.json` `"index": true` generates a landing page per directory, with an Overview nav link and linked breadcrumbs (July 18, 2026) - ✅ Pinned left sidebar on wide desktop (≥1200px); hamburger/slideout below (July 18, 2026) - Introduce tabs, products, versions, and languages in the navigation model — design decided July 18, 2026: locale/version become first-class dimensions (one nav tree definition, per-dimension instances, scoped search, hreflang/canonical mechanics per dimension) and this moves AHEAD of generic nav sharding, because dimensions both shrink the scaling problem and unblock migration fidelity for platform configs that use them - Detect orphaned navigation entries #### Hosted-platform importer ```bash wingtip migrate ./existing-docs --output ./wingtip-docs ``` Shipped (read-only — the source project is never modified): - ✅ Read current and legacy platform configuration formats (`docs.json`, `mint.json`) - ✅ Import project name, description, brand colors, and favicon - ✅ Convert navigation groups to directory `_category.json` files and per-page `order` frontmatter - ✅ Preserve page paths in generated URLs; report every preserved URL - ✅ Rewrite platform-style absolute internal links to portable relative links - ✅ Inventory MDX components per page; strip module `import`/`export` lines and MDX comments - ✅ Approximate common MDX components (callouts → blockquotes, titled sections → labels, `Card` → link, `src`-bearing components → images) with JSX dedenting - ✅ Rewrite links in raw `href`/`src` attributes with page-relative → site-root → case-insensitive resolution - ✅ Stub dynamic link expressions and report suspected broken links in the source - ✅ Carry platform redirects into `config.json`; every build emits static redirect pages plus a Netlify/Cloudflare `_redirects` file with wildcard→splat translation (July 18, 2026) - ✅ Never silently discard unsupported configuration or content — everything unhandled is listed in the report - ✅ Validated against real public hosted-docs repositories (46 → 5,387 pages) Still pending: - Resolve local JSON `$ref` configuration - Convert tabs, products, versions, and languages beyond flat group mapping - Preserve OpenAPI references for Phase 2 endpoint rendering - Unique-basename fallback for bare links that the source platform resolved via navigation context (found at the 5,000-page tail) - Treat `snippets/` include-partials as includes (inline where referenced, or skip and report) instead of standalone pages - Copy non-image media (video, PDF, downloads) referenced by pages into the build output — currently only rendered images and `static/` ship #### Migration report - ✅ Every migration emits `migration-report.md` in the new project: an executive summary (format detected, pages converted, URLs preserved, groups mapped, manual follow-up count) followed by ✓ converted items and ⚠ manual work (components per page, unmapped groups, orphan pages, redirects, configuration not carried) and next steps Still pending: - Machine-readable JSON report alongside the Markdown - Broken links and missing assets detection at migrate time (the post-build audit covers this after `wingtip` runs) - File/line locations for component occurrences #### Migration documentation and evidence - ✅ Open-source hosted-platform migration guide (July 17, 2026) - ✅ Migration intake through a dedicated GitHub issue template (July 17, 2026) - Create a sanitized real-world hosted-docs fixture for regression testing - Record a repository-to-deployed-site migration demo - Document output ownership, supported hosts, and unsupported constructs plainly **Exit criteria:** Representative hosted-platform repositories build without lost pages, URLs, metadata, or unreported incompatibilities, and a prospective user can verify that from the published guide, fixture, and demo. ### Phase 2 — Documentation parity (P1) Prioritize capabilities that block real migrations: - OpenAPI 3.x ingestion and endpoint-page generation - API operation navigation, parameter/schema tables, examples, and code samples - Optional static-first API playground enhancement - AsyncAPI inventory and design - Tags: `tags:` frontmatter → generated per-tag index pages and a tags hub; related-pages block per page (tag overlap + directory siblings, computed at build time) - Search weighting/boost frontmatter and section-level result anchors - Visible version, product, and language switchers - Redirect validation and redirect-chain detection - Mermaid diagrams — approach decided July 18, 2026: vendor `mermaid.min.js` locally (no CDN), lazy-loaded only on pages containing diagrams; build-time SVG rejected (would add a Node dependency to a Python tool) - Common hosted-platform MDX compatibility components: cards, columns, tabs, accordions, steps, callouts, frames, and code groups - Reusable snippets/includes and content variables - Navigation banners, badges, deprecation labels, and hidden-page controls - Formal, versioned plugin API and template/theme extension points **Exit criteria:** A typical public SaaS documentation repository does not need to remain on a hosted documentation platform solely for navigation, common MDX presentation, or OpenAPI reference pages. ### Phase 3 — Search-engineering leadership (P1) Turn the existing auditor into a differentiated discovery system: - Validate titles, descriptions, canonicals, robots directives, OG/Twitter metadata, and JSON-LD - ✅ Zero-config social metadata decided and shipped: og:url/og:image/twitter:image are emitted only when absolute, per the OpenGraph spec; canonicals deliberately stay relative-self-resolving (July 18, 2026) - Detect canonical conflicts, duplicate metadata, orphan pages, broken fragments, redirect chains, and accidental noindex - Validate hreflang reciprocity, language codes, canonicals, and `x-default` clusters - Audit internal-link depth and surface pages with weak contextual connectivity - Add configurable HTML, CSS, JavaScript, image, request-count, and font performance budgets - Shard or lazy-load sidebar navigation for very large sites: per-page full navigation makes total output size quadratic in page count (measured: a 5,387-page site emits a ~5,300-entry sidebar on every page, ~3.9GB total). Design decided July 18, 2026 — "context window" nav: each page inlines ancestors + siblings + children server-side (crawlable, bounded), full tree in a shared `nav.json` hydrating the slideout on demand; hub pages keep every page within ~3 clicks. Search-index and `llms-full` sharding follow the same per-section pattern. Sequenced after the dimension model (see Phase 1) - Audit at scale: parallelize the post-build audit per file (it is embarrassingly parallel) and add incremental auditing keyed on content hash so CI re-audits only changed pages. At the 5,387-page tail the full audit takes tens of minutes — a gate that slow gets skipped, and a skipped gate protects nothing. (Typical 50–300 page sites audit in seconds.) - ✅ Same-origin absolute URLs resolve against the local build (base detected from the index canonical), closing the blind spot that hid a dead 404 Markdown alternate on the demo site (July 18, 2026) - Emit machine-readable audit output for CI annotations - Generate a crawl graph and diff it between builds - Add optional answer-first/content-structure linting without making ranking promises - Generate `skill.md`, multiple skill manifests, integrity hashes, and well-known discovery endpoints from project-owned source files - Add schema types appropriate to API references, software applications, organizations, and FAQs only when content qualifies **Exit criteria:** WingTip catches technical discovery regressions before deployment and produces evidence that can be inspected in CI. ### Phase 4 — Reproducible competitive benchmark (P1) Build the same public fixture with WingTip and representative hosted and open-source documentation generators. Record: - Build inputs, product versions, commands, and generated artifacts - HTML availability without client-side JavaScript - Page weight, requests, external origins, and JavaScript execution - Lighthouse and Core Web Vitals lab results under identical conditions - Canonical, robots, sitemap, hreflang, social metadata, structured data, and AI-readable artifacts - Broken-link, accessibility, and schema validation results - Hosting portability and platform-dependent behavior Publish raw outputs and rerunnable automation. Do not claim leadership where the fixture does not prove it. **Exit criteria:** Competitive claims in the README and launch material are generated from public evidence. ### Phase 5 — Workflow and ecosystem (P2) - `wingtip new`, `wingtip check`, and `wingtip migrate` command groups - Browser build-error overlay and improved incremental rebuilds - Preview-deployment recipes for GitHub, Netlify, Cloudflare Pages, and Vercel - PR annotations for audit regressions and visual changes - Theme packages and template overrides - WCAG 2.2 AA audit and regression checks (✅ automated axe-core gate over every generated page in both color schemes, in CI with a negative test, July 17, 2026; expert manual audit still open) - Contributor guide, extension examples, and stable fixtures - Dependency, package-integrity, and generated-CSP checks ### Phase 6 — Optional hosted capabilities (P3) Keep the open-source generator complete on its own. Hosted or partner services may later provide: - Managed builds and previews - Password-protected or authenticated documentation - Team editing and approval workflows - Search/feedback analytics - Edge redirects, headers, and cache controls - Enterprise support and migration services These services must not make existing static generation, SEO metadata, auditing, or output ownership dependent on a WingTip account. ## Success metrics - Migration completion rate and median manual fixes per repository - Percentage of source pages, URLs, redirects, and metadata preserved - Unsupported MDX constructs per migrated repository - Zero unreported data loss - Audit findings caught before production - Generated page weight and external-origin count - Build time across small, medium, and large fixtures - Number of independent deployments that do not use WingTip-managed infrastructure - Search impressions, indexed pages, and AI referrals measured by adopters who opt to share results ## Explicit non-goals - Guaranteed rankings, rich results, or AI citations - A proprietary content repository - Mandatory cloud hosting - Recreating every collaborative editor feature before migration quality is excellent - Generating large volumes of low-quality programmatic content --- File: docs/search-features.md --- # Client-Side Search Functionality WingTip includes a powerful and lightweight client-side search feature that allows users to quickly find relevant information within the documentation. ## How it Works The search functionality is designed to be fast and entirely client-side, making it suitable for static hosting environments like GitHub Pages. Here's a brief overview: 1. **Search Index (`search_index.json`)**: When the site is built, WingTip generates a file named `search_index.json`. This file contains a structured list of all the pages in your documentation (excluding special pages like 404). For each page, the index stores: * The page title. * The plain text content of the page (with HTML tags and frontmatter removed). * The relative URL to the page. 2. **Client-Side Processing**: When a user types into the search bar: * The browser fetches the `search_index.json` file (this happens only once, on the first search interaction typically). * JavaScript running in the browser then filters this index based on the search query. * It matches the query against both the titles and the text content of the pages. * Matching results are displayed dynamically below the search bar as a list of links. ## Using the Search Bar - The search bar is located in the main navigation header for easy access. - Simply type your search terms into the input field. - Results will appear as you type (minimum 2 characters required). - Use keyboard navigation: - Press `/` to focus the search bar - Use `↑` and `↓` arrows to navigate results - Press `Enter` to go to the selected result - Press `Esc` to clear the search - Clicking on a search result will take you directly to the relevant page. - Search results highlight matching text for better visibility. ## Benefits - **Fast**: Since the search happens in the user's browser with a local index, it's very responsive. - **No Server-Side Dependencies**: Works perfectly on static hosting platforms. - **Comprehensive**: Searches both page titles and full text content. - **Accessible**: Full keyboard navigation and screen reader support. - **User-Friendly**: Results highlight matches and update in real-time. This approach ensures that your documentation remains easy to navigate and user-friendly without requiring complex backend infrastructure. --- File: docs/theming.md --- # Theming in WingTip WingTip provides a flexible way to customize the appearance of your documentation site. You can start with basic overrides for fonts and colors, and for more advanced needs, standard CSS practices can be employed. ## Basic Theme Overrides (`theme.json`) The primary way to customize your site's look and feel is by creating a `theme.json` file in the root of your project (the same directory as your `config.json`). This file allows you to define global font choices and specify colors for both light and dark modes. If `theme.json` is not present, or if specific keys within it are missing, WingTip will use its default styles, which are based on Water.css and predefined font stacks. ### Structure of `theme.json` The `theme.json` file has three main top-level keys: * `"fonts"`: For defining global font families. * `"light_mode"`: For defining colors specific to the light theme. * `"dark_mode"`: For defining colors specific to the dark theme. ### Example `theme.json` ```json { "fonts": { "sans_serif": "\"Inter\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen, Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif", "monospace": "\"JetBrains Mono\", Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace" }, "light_mode": { "background_body": "#FCFCFC", "background_element": "#FFFFFF", "text_main": "#333333", "text_bright": "#000000", "links_or_primary": "#005FB8", "border": "#E0E0E0" }, "dark_mode": { "background_body": "#1E1E1E", "background_element": "#2C2C2C", "text_main": "#E0E0E0", "text_bright": "#FFFFFF", "links_or_primary": "#3391FF", "border": "#4A4A4A" } } ``` ### Detailed Configuration #### 1. Fonts The `fonts` object accepts two keys: * **`sans_serif`**: (String) A CSS `font-family` stack for the main body text. * _Default_: `system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Segoe UI Emoji', 'Apple Color Emoji', 'Noto Color Emoji', sans-serif` * _Example_: `"Inter", sans-serif` * **`monospace`**: (String) A CSS `font-family` stack for code blocks (`
`) and inline code (``).
    *   _Default_: `Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace`
    *   _Example_: `"Fira Code", monospace`

WingTip will use these values to set the `--theme-font-family-sans-serif` and `--theme-font-family-monospace` CSS variables. The `body` and code elements are styled to use these variables.

#### 2. Light Mode & Dark Mode Colors

The `light_mode` and `dark_mode` objects accept the same set of keys to define colors for their respective themes. All values should be valid CSS color strings (e.g., hex codes like `#RRGGBB`, color names like `blue`, `rgb(...)`, `hsl(...)`).

| Key                  | Corresponding Water.css Variable | Description                                                                              |
| -------------------- | -------------------------------- | ---------------------------------------------------------------------------------------- |
| `background_body`    | `--background-body`              | The main background color of the page.                                                   |
| `background_element` | `--background`                   | Background for elements like inline code, inputs, default buttons, even-numbered table rows. |
| `text_main`          | `--text-main`                    | The primary color for most body text.                                                      |
| `text_bright`        | `--text-bright`                  | Color for headings (`

`-`

`) and bold/strong text. | | `links_or_primary` | `--links` | Color for hyperlinks. This often serves as the site's primary accent color. | | `border` | `--border` | Color for table borders, `
` elements, fieldset borders, etc. | _You can also add other keys if you wish to define additional theme-specific color variables (e.g., `"secondary_accent": "#value"`), which would generate `--theme-color-secondary-accent-light` and `--theme-color-secondary-accent-dark`. These would then need to be used in your own custom CSS._ When you define these color keys, WingTip generates CSS variables that override the default Water.css styles. For example, `light_mode.background_body = "#FAF7F0"` will set the `--background-body` CSS variable to `#FAF7F0` when the light theme is active. This system allows for quick and easy visual customization of your documentation. ### Tips for Choosing Colors and Fonts * **Contrast:** Ensure sufficient contrast between text colors and background colors for readability, especially for accessibility (WCAG AA guidelines are a good reference). * **Font Legibility:** Choose fonts that are clear and easy to read for body text and code. Consider fonts designed for UIs or reading. * **Consistency:** Try to maintain a consistent feel between your light and dark mode themes, even if colors are inverted. * **Test:** Always preview your changes in both light and dark modes to ensure they look as expected. Use tools like [Color Contrast Analyzer](https://webaim.org/resources/contrastchecker/) to check contrast ratios. ## Advanced CSS Customization While `theme.json` provides a straightforward way to change common visual elements, you might have more specific styling needs. For these scenarios, you can: 1. **Create a `custom.css` file:** Place this file in your `static/css/` directory (e.g., `static/css/custom.css`). 2. **Link it in `template.html`:** You would need to modify your local `template.html` (if you've ejected or copied it for customization) to include a link to this stylesheet. Make sure to link it *after* the Water.css link and after the injected theme variables style block if you want your custom CSS to override them. ```html ... ... ``` *(Note: Use a relative path like `./static/css/custom.css` if your site is not served from the root of a domain.)*