Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
59 changes: 53 additions & 6 deletions generate-api-docs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,65 @@ let packages: [String: [String]] = [
"apns": ["VaporAPNS"],
]

let htmlMenu = packages.values.flatMap { $0 }
.sorted()
.map { "<option value=\"\($0.lowercased())/documentation/\($0.lowercased())\">\($0)</option>" }
.joined(separator: "\n")
// Package descriptions
let packageDescriptions: [String: String] = [
"Vapor": "Core web framework for building server-side Swift applications",
"XCTVapor": "Testing utilities for Vapor applications when using XCTest",
"VaporTesting": "Modern testing framework for Vapor apps when using Swift Testing",
"AsyncKit": "Async/await utilities and helpers for concurrent programming",
"RoutingKit": "High-performance routing engine for HTTP requests",
"ConsoleKit": "Terminal UI and command-line tools framework",
"ConsoleKitCommands": "Command parsing and execution for CLI apps",
"ConsoleKitTerminal": "Terminal formatting and interaction utilities",
"WebSocketKit": "WebSocket client and server implementation",
"MultipartKit": "Multipart form data parsing and encoding",
"PostgresNIO": "Non-blocking PostgreSQL client built on SwiftNIO",
"MySQLNIO": "Non-blocking MySQL client built on SwiftNIO",
"SQLiteNIO": "Non-blocking SQLite client built on SwiftNIO",
"SQLKit": "SQL query building and execution framework",
"PostgresKit": "PostgreSQL integration for SQLKit",
"MySQLKit": "MySQL integration for SQLKit",
"SQLiteKit": "SQLite integration for SQLKit",
"FluentKit": "Core ORM framework for database operations",
"FluentSQL": "SQL dialect support for Fluent ORM",
"XCTFluent": "Testing utilities for Fluent ORM",
"Fluent": "Vapor integration package for FluentKit",
"FluentPostgresDriver": "PostgreSQL driver for Fluent ORM",
"FluentMongoDriver": "MongoDB driver for Fluent ORM",
"FluentMySQLDriver": "MySQL driver for Fluent ORM",
"FluentSQLiteDriver": "SQLite driver for Fluent ORM",
"Redis": "Vapor wrapper for using Redis",
"QueuesRedisDriver": "Redis driver for job queue system",
"Queues": "Job queue system for background processing",
"XCTQueues": "Testing utilities for queue system",
"LeafKit": "Core templating engine framework",
"Leaf": "Vapor integration for LeafKit",
"JWTKit": "JSON Web Token signing and verification framework",
"JWT": "JWT integration for Vapor authentication",
"VaporAPNS": "Apple Push Notification Service integration"
]

// Generate package cards HTML
let allModules = packages.values.flatMap { $0 }.sorted()
let packageCards = allModules.map { module in
let description = packageDescriptions[module] ?? "API documentation for \(module)"
let href = "\(module.lowercased())/documentation/\(module.lowercased())"

return """
<a href="\(href)" class="package-card" tabindex="0">
<h2 class="package-name">\(module)</h2>
<p class="package-description">\(description)</p>
</a>
"""
}.joined(separator: "\n")

do {
let publicDirUrl = URL(fileURLWithPath: "./public", isDirectory: true)
try FileManager.default.removeItemIfExists(at: publicDirUrl)
try FileManager.default.createDirectory(at: publicDirUrl, withIntermediateDirectories: true)

var htmlIndex = try String(contentsOf: URL(fileURLWithPath: "./index.html", isDirectory: false), encoding: .utf8)
htmlIndex.replace("{{Options}}", with: "\(htmlMenu)\n", maxReplacements: 1)
htmlIndex.replace("{{PackageCards}}", with: packageCards, maxReplacements: 1)

try htmlIndex.write(to: publicDirUrl.appendingPathComponent("index.html", isDirectory: false), atomically: true, encoding: .utf8)
try FileManager.default.copyItem(at: URL(fileURLWithPath: "./api-docs.png", isDirectory: false), into: publicDirUrl)
Expand Down Expand Up @@ -81,4 +128,4 @@ extension FileManager {
let dstItem = dst.appendingPathComponent(src.lastPathComponent, isDirectory: dst.hasDirectoryPath)
try self.copyItem(at: src, to: dstItem)
}
}
}
206 changes: 181 additions & 25 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,202 @@
<title>Vapor API Docs</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

html, body {
height: 100%;
background: #0d0d0d;
min-height: 100%;
background: #0a0a0a;
color: #ffffff;
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
}

.wrapper {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
body {
background: linear-gradient(135deg, #0a0a0a 0%, #1a0a2e 100%);
background-attachment: fixed;
}

.main {
margin-top: -50px;
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}

.title {
.header {
text-align: center;
margin-bottom: 3rem;
}

.logo {
background-image: url(api-docs.png);
width: 300px;
height: 78px;
background-size: 300px auto;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
text-indent: -9999px;
margin-bottom: 0;
margin: 0 auto 1rem;
}

.subtitle {
color: #a0a0a0;
font-size: 1.1rem;
margin-bottom: 2rem;
}

.package-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
}

.package-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 1.5rem;
text-decoration: none;
color: inherit;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
}

.package-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, #7c3aed 0%, #3b82f6 100%);
transform: scaleX(0);
transition: transform 0.3s ease;
}

.package-card:hover {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.2);
}

.package-card:hover::before {
transform: scaleX(1);
}

.package-name {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: #ffffff;
}

.package-description {
font-size: 0.9rem;
color: #a0a0a0;
line-height: 1.5;
}

.footer {
text-align: center;
margin-top: 4rem;
padding-top: 2rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
color: #666;
font-size: 0.9rem;
}

.footer a {
color: #7c3aed;
text-decoration: none;
}

.footer a:hover {
text-decoration: underline;
}

/* Responsive design */
@media (max-width: 768px) {
.container {
padding: 1rem;
}

.package-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}

.logo {
width: 250px;
height: 65px;
}

.subtitle {
font-size: 1rem;
}
}

@media (max-width: 480px) {
.package-grid {
grid-template-columns: 1fr;
}
}

/* Focus styles for accessibility */
.package-card:focus {
outline: 2px solid #7c3aed;
outline-offset: 2px;
}

/* Loading animation */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

.package-card {
animation: fadeIn 0.5s ease forwards;
opacity: 0;
}

.package-card:nth-child(1) { animation-delay: 0.05s; }
.package-card:nth-child(2) { animation-delay: 0.1s; }
.package-card:nth-child(3) { animation-delay: 0.15s; }
.package-card:nth-child(4) { animation-delay: 0.2s; }
.package-card:nth-child(5) { animation-delay: 0.25s; }
.package-card:nth-child(6) { animation-delay: 0.3s; }
.package-card:nth-child(7) { animation-delay: 0.35s; }
.package-card:nth-child(8) { animation-delay: 0.4s; }
.package-card:nth-child(n+9) { animation-delay: 0.45s; }
</style>
</head>
<body>
<div class="wrapper">
<div class="main">
<h1 class="title">Vapor API Docs</h1>
<br>
<select onchange="this.options[this.selectedIndex].value && (window.location = this.options[this.selectedIndex].value); this.selectedIndex = 0;">
<option value="">Choose package...</option>
{{Options}}
</select>
</div>
<div class="container">
<header class="header">
<h1 class="logo">Vapor API Docs</h1>
<p class="subtitle">Explore the complete API documentation for Vapor and its ecosystem</p>
</header>

<main class="package-grid">
{{PackageCards}}
</main>

<footer class="footer">
<p>Built with <a href="https://vapor.codes" target="_blank" rel="noopener">Vapor</a> • <a href="https://github.com/vapor/api-docs" target="_blank" rel="noopener">View on GitHub</a></p>
</footer>
</div>
</body>
</html>
</html>