Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5611e6c
Add SiteKitOpenAPI target with an OpenAPI 3.0/3.1 spec loader
Jeehut Jun 20, 2026
eefded6
Nest the OpenAPI model under OpenAPISpec and harden the loader
Jeehut Jun 20, 2026
e46d480
Add the OpenAPI shell and landing page
Jeehut Jun 20, 2026
6fb0730
Add tag, operation, and schema pages
Jeehut Jun 20, 2026
18525ec
Test the OpenAPI page renderers and the sample-site output
Jeehut Jun 20, 2026
f50969f
Resolve in-file component $refs for operation params, bodies, responses
Jeehut Jun 20, 2026
1f35755
Fold slugs to ASCII and guard against slug collisions
Jeehut Jun 20, 2026
cc7c4e8
Cross-list multi-tag operations on every tag page
Jeehut Jun 20, 2026
7bae514
Add the OpenAPI sidebar navigation tree
Jeehut Jun 20, 2026
e0fe361
Style the OpenAPI docs and wire the nav interactivity
Jeehut Jun 20, 2026
7960103
Meet WCAG AA on verb badges and harden the nav rail
Jeehut Jun 20, 2026
d99ebfd
Add a content-section provider seam for synthetic pages
Jeehut Jun 20, 2026
7384307
Register OpenAPI pages so every machine index includes them
Jeehut Jun 20, 2026
d7ace76
Finish the OpenAPI chrome: footer, 404, theme toggle, drawer
Jeehut Jun 20, 2026
f671cde
Drop internal work-block ids from the public comments
Jeehut Jun 20, 2026
7da3a73
Harden the content-section provider seam
Jeehut Jun 20, 2026
aafa347
Contain focus in the open mobile nav drawer
Jeehut Jun 20, 2026
b43e900
Render the 404 through the full OpenAPI shell
Jeehut Jun 20, 2026
cba4e93
Add the OpenAPI blueprint starter and a configPath overload
Jeehut Jun 20, 2026
e432b48
Document the OpenAPI blueprint in the skill and use-case map
Jeehut Jun 20, 2026
afcfd9b
Add OpenAPI blueprint reference screenshots
Jeehut Jun 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Docs/Screenshots/OpenAPI/01-landing-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Screenshots/OpenAPI/02-operation-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Screenshots/OpenAPI/03-operation-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Screenshots/OpenAPI/04-schema.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Screenshots/OpenAPI/06-edge-deprecated.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Screenshots/OpenAPI/07-edge-no-tags.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Screenshots/OpenAPI/08-mobile-drawer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Screenshots/OpenAPI/09-404-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Screenshots/OpenAPI/10-404-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Screenshots/OpenAPI/11-theme-toggle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 10 additions & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 31 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ let package = Package(
// ONLY into builds that actually use it. A consumer depending only on the `SiteKit` product never
// compiles swift-syntax (SE-0226 target-based dependency resolution prunes it).
.library(name: "SiteKitSyntaxHighlighting", targets: ["SiteKitSyntaxHighlighting"]),
// Optional add-on library: renders an OpenAPI 3.0/3.1 spec (YAML or JSON) into a multi-page,
// style-conforming API-documentation site. It lives in its own product+target so the OpenAPIKit
// parser pulls in ONLY for builds that use it. A consumer depending solely on the `SiteKit`
// product never compiles OpenAPIKit (SE-0226 target-based dependency resolution prunes it).
.library(name: "SiteKitOpenAPI", targets: ["SiteKitOpenAPI"]),
// The executable *product* is `sitekit` (the durable public command name); its *target*
// is `SiteKitCLI` because a target literally named `sitekit` collides with the `SiteKit`
// library target on a case-insensitive filesystem – at both the `Sources/` directory and
Expand All @@ -26,6 +31,13 @@ let package = Package(
.package(url: "https://github.com/apple/swift-crypto.git", from: "3.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"),

// OpenAPIKit powers ONLY the optional SiteKitOpenAPI target: it parses an OpenAPI 3.0/3.1
// document into a runtime model the blueprint walks to render API-documentation pages. It
// attaches solely to SiteKitOpenAPI, so a consumer depending only on the base `SiteKit`
// product never compiles it (SE-0226 target-based dependency resolution prunes it). OpenAPIKit
// declares Yams only for ITS test targets, so this adds zero new transitive runtime deps.
.package(url: "https://github.com/mattpolzin/OpenAPIKit.git", from: "6.2.0"),

// swift-syntax powers ONLY the optional SiteKitSyntaxHighlighting target. The 6xx.x line tracks
// the Swift toolchain (603.x = Swift 6.3, the toolchain SiteKit builds with). Only the parser +
// tree + syntactic-classification modules are used; the macro/compiler-plugin modules (the heavy,
Expand All @@ -47,7 +59,7 @@ let package = Package(
.executableTarget(
name: "SiteKitCLI",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "ArgumentParser", package: "swift-argument-parser")
]
),
// Optional SwiftSyntax-based highlighter. Depends on the base `SiteKit` library (for the
Expand All @@ -74,6 +86,24 @@ let package = Package(
name: "SiteKitSyntaxHighlightingTests",
dependencies: ["SiteKitSyntaxHighlighting"]
),
// Optional OpenAPI blueprint. Depends on the base `SiteKit` library (for the pipeline,
// SiteBuilder and PageShell seams), OpenAPIKitCompat (which re-exports both the 3.0 and 3.1
// parsers and the 3.0→3.1 conversion used to normalize every spec to one 3.1 shape), and Yams
// (already a SiteKit dependency – reused here to decode YAML specs, no new transitive dep).
.target(
name: "SiteKitOpenAPI",
dependencies: [
"SiteKit",
.product(name: "OpenAPIKitCompat", package: "OpenAPIKit"),
"Yams",
],
resources: [.process("Resources")]
),
.testTarget(
name: "SiteKitOpenAPITests",
dependencies: ["SiteKitOpenAPI"],
resources: [.copy("Fixtures")]
),
.testTarget(
name: "SiteKitCLITests",
dependencies: ["SiteKitCLI"]
Expand Down
8 changes: 5 additions & 3 deletions Plugin/blueprints/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Read `<Name>.md` first, then copy files from `<Name>/`.
| `Newsletter` | Email newsletter with issue archive, signup forms, email rendering | Topic newsletters, curated digests, weekly/monthly roundups | [Newsletter.md](Newsletter.md) | [evolutionkit.dev](https://evolutionkit.dev) |
| `AppLanding` | Single product landing page with hero, features, pricing, reviews | App marketing pages, SaaS products | [AppLanding.md](AppLanding.md) | [translatekit.pages.dev](https://translatekit.pages.dev) |
| `DocC` | DocC catalog → static, AI-fetchable HTML with a sidebar + full-text search | Documentation sites, API/guide docs | [DocC.md](DocC.md) | [wwdcnotes.com](https://wwdcnotes.com) |
| `OpenAPI` | OpenAPI/Swagger spec → static, searchable API-reference docs (operations, schemas, nav rail) | API reference docs generated from a spec | [OpenAPI.md](OpenAPI.md) | – |
| `Plain` | Minimal structure, no opinions | Experimentation, custom pipelines | [Plain.md](Plain.md) | – |

---
Expand All @@ -35,9 +36,10 @@ Read `<Name>.md` first, then copy files from `<Name>/`.

Use this decision tree:

0. **Do you have a DocC catalog (`.docc` – Markdown notes with DocC directives)?**
- Yes → **`DocC`** (renders it to static, AI-fetchable HTML with a sidebar + full-text search)
- No, continue ↓
0. **Do you have a machine-readable description to render as-is?**
- An OpenAPI/Swagger spec (`.yaml`/`.json`, 3.0 or 3.1) → **`OpenAPI`** (renders it to a static, searchable API-reference site)
- A DocC catalog (`.docc` – Markdown notes with DocC directives) → **`DocC`** (static, AI-fetchable HTML with a sidebar + full-text search)
- Neither, continue ↓

1. **Is your content audio episodes (a podcast)?**
- Yes → **`Podcast`**
Expand Down
42 changes: 42 additions & 0 deletions Plugin/blueprints/OpenAPI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Blueprint: OpenAPI

Generate a complete, static API-documentation site from an OpenAPI spec. The author provides one spec file; the blueprint renders every page.

## Quick Start

1. Copy the `OpenAPI/` template into a new project directory.
2. Replace `Content/openapi.yaml` with the user's spec (OpenAPI 3.0 or 3.1, YAML or JSON, auto-detected).
3. Edit `SiteConfig.yaml` (name, base URL, footer) and `Theme/theme.yaml` (color scheme, fonts).
4. `swift run Site build` → `_Site/`.

`Sources/Site/Main.swift` is one line: `try SiteBuilder.openAPI(configPath: "SiteConfig.yaml").run()`.

## When to Choose This

Choose `OpenAPI` when the user has an OpenAPI/Swagger spec (3.0 or 3.1) and wants browsable, linkable, search- and AI-indexed reference docs for it. If they want hand-written guides or a DocC catalog instead, use `DocC`; for narrative articles, use `Blog`.

The spec must be a single self-contained file. In-file component `$ref`s resolve; multi-file `$ref`s that point across files are out of scope.

## How It Works

One spec in, a full site out, all under the section's `urlPrefix` (default `api`):

- **Landing** (`/api/`) – the API title, version, the optional `Content/api-intro.md` prose, and a card per tag.
- **Tag pages** (`/api/<tag>/`) – each tag's operations, with method badges.
- **Operation pages** (`/api/<tag>/<operation>/`) – method + path, parameters, request body, per-status responses, referenced schemas, examples, and security. Static-first: shapes and examples, no interactive request widget in this version.
- **Schema pages** (`/api/schemas/<name>/`) – properties, composition (allOf/oneOf/anyOf), enums, and nullable/deprecated markers.

Slugs are ASCII-folded and collision-guarded, so non-ASCII tag/operation names and name clashes still produce distinct, stable URLs.

Around the pages the blueprint ships: a persistent **nav rail** (collapsible groups, a live filter, deprecated dimming, active-page tracking), full-text **search** (`/assets/search-index.json` + an appbar search box), per-page **SEO** (title, description, canonical), **sitemap.xml**, **llms.txt**, and **nav-index.json**, a config-driven **footer**, a styled **404**, and a light/dark **theme toggle** consistent with the rest of SiteKit.

## Style

The OpenAPI surface owns its own app-shell (the persistent rail + content area), so it looks consistent across all 15 color schemes in light and dark with no layout change. The layout *templates* (Classic / Sidebar / Minimal) do not alter it – only the chosen color scheme and font pairing do.

## Questions to Ask

- Where is the spec? (Default `Content/openapi.yaml`; pass `specPath:` to `.openAPI(...)` if it lives elsewhere.)
- What URL prefix? (Default `api`; set the section's `urlPrefix` in `SiteConfig.yaml`.)
- Any intro prose for the landing page? (Optional `Content/api-intro.md`.)
- Which color scheme and fonts? (Any of the 15 schemes / 6 font pairings in `Theme/theme.yaml`.)
4 changes: 4 additions & 0 deletions Plugin/blueprints/OpenAPI/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.build/
_Site/
Package.resolved
.DS_Store
3 changes: 3 additions & 0 deletions Plugin/blueprints/OpenAPI/Content/api-intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Welcome to the Tasks API. It lets you create tasks, track their status through their lifecycle, and assign them to people.

This prose block is optional: it comes from `Content/api-intro.md` and renders above the tag cards on the landing page. Delete the file to omit it, or edit it to introduce your own API. Everything below is generated from `Content/openapi.yaml`.
205 changes: 205 additions & 0 deletions Plugin/blueprints/OpenAPI/Content/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
openapi: 3.1.0
info:
title: Tasks API
version: 1.0.0
description: A small task-tracking API, used as the sample spec for the SiteKit OpenAPI blueprint.
servers:
- url: https://api.example.com/v1
tags:
- name: tasks
description: Create, inspect, and complete tasks.
- name: users
description: Look up the people a task can be assigned to.
paths:
/tasks:
get:
operationId: listTasks
summary: List tasks
description: Returns the tasks, most recently created first, optionally filtered by status.
tags:
- tasks
parameters:
- name: status
in: query
required: false
description: Only return tasks in this status.
schema:
$ref: "#/components/schemas/TaskStatus"
- name: limit
in: query
required: false
description: Maximum number of tasks to return (1-100).
schema:
type: integer
format: int32
default: 20
responses:
"200":
description: A page of tasks.
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Task"
post:
operationId: createTask
summary: Create a task
description: Creates a new task and returns it with its assigned id.
tags:
- tasks
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/NewTask"
responses:
"201":
description: The created task.
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
"400":
description: The request body was invalid.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/tasks/{taskId}:
get:
operationId: getTask
summary: Fetch a task
tags:
- tasks
parameters:
- name: taskId
in: path
required: true
description: The task identifier.
schema:
type: string
responses:
"200":
description: The task.
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
"404":
description: No task with that id exists.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
delete:
operationId: deleteTask
summary: Delete a task
tags:
- tasks
parameters:
- name: taskId
in: path
required: true
description: The task identifier.
schema:
type: string
responses:
"204":
description: The task was deleted.
/users/{userId}:
get:
operationId: getUser
summary: Fetch a user
tags:
- users
parameters:
- name: userId
in: path
required: true
description: The user identifier.
schema:
type: string
responses:
"200":
description: The user.
content:
application/json:
schema:
$ref: "#/components/schemas/User"
components:
schemas:
TaskStatus:
type: string
description: The lifecycle status of a task.
enum:
- open
- in_progress
- done
- archived
NewTask:
type: object
description: The fields needed to create a task.
required:
- title
properties:
title:
type: string
description: A short summary of the work.
details:
type: string
description: Optional longer description.
assigneeId:
type: [string, "null"]
description: The id of the user the task is assigned to, if any.
Task:
type: object
description: A unit of work.
required:
- id
- title
- status
- createdAt
properties:
id:
type: string
description: Stable unique identifier.
title:
type: string
details:
type: string
status:
$ref: "#/components/schemas/TaskStatus"
assignee:
$ref: "#/components/schemas/User"
createdAt:
type: string
format: date-time
description: When the task was created.
User:
type: object
description: A person a task can be assigned to.
required:
- id
- name
properties:
id:
type: string
name:
type: string
email:
type: string
format: email
Error:
type: object
description: A problem response.
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
21 changes: 21 additions & 0 deletions Plugin/blueprints/OpenAPI/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// swift-tools-version: 6.2
import PackageDescription

let package = Package(
name: "Site",
platforms: [.macOS(.v26)],
dependencies: [
// The OpenAPI blueprint ships in the optional SiteKitOpenAPI product (it pulls the
// OpenAPI parser only into builds that use it).
.package(url: "https://github.com/FlineDev/SiteKit.git", from: "1.1.0")
],
targets: [
.executableTarget(
name: "Site",
dependencies: [
.product(name: "SiteKit", package: "SiteKit"),
.product(name: "SiteKitOpenAPI", package: "SiteKit"),
]
)
]
)
Loading
Loading