diff --git a/.github/workflows/build-and-deploy-docs-workflow.yml b/.github/workflows/build-and-deploy-docs-workflow.yml deleted file mode 100644 index 3f5e627..0000000 --- a/.github/workflows/build-and-deploy-docs-workflow.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: Shared Build DocC docs and Deploy -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true -on: - workflow_call: - inputs: - package_name: - type: string - required: false - description: "The name of the package to build docs for." - modules: - type: string - required: true - description: "The modules in the package to build docs for." - pathsToInvalidate: - type: string - required: false - description: "The paths to invalidate in CloudFront, e.g. '/vapor/* /xctvapor/*'." - workflow_dispatch: - inputs: - repository: - type: string - required: true - description: "The repository of the package to build docs for." - package_name: - type: string - required: true - description: "The name of the package to build docs for." - modules: - type: string - required: true - description: "The modules in the package to build docs for." - pathsToInvalidate: - type: string - required: true - description: "The paths to invalidate in CloudFront, e.g. '/vapor/* /xctvapor/*'." -env: - INPUT_REPOSITORY: ${{ inputs.repository || github.repository }} - INPUT_PACKAGE_NAME: ${{ inputs.package_name }} - INPUT_MODULES: ${{ inputs.modules }} - INPUT_INVALIDATE_PATHS: ${{ inputs.pathsToInvalidate }} - -jobs: - build-docs: - runs-on: ubuntu-latest - permissions: { id-token: write, contents: read } - env: { AWS_PAGER: '' } - steps: - - name: Check out docc-render fork - uses: actions/checkout@v7 - with: - repository: vapor/swift-docc-render - path: swift-docc-render - - name: Setup node 22 - uses: actions/setup-node@v7 - with: - node-version: 22 - - name: Build swift-docc-render - working-directory: swift-docc-render - run: npm ci && npm run build - - name: Check out repo to build docs for - uses: actions/checkout@v7 - with: - repository: ${{ inputs.repository || github.repository }} - path: build-repo - fetch-depth: 0 - - name: Install latest Swift - uses: vapor/swiftly-action@v0.2 - with: { toolchain: latest } - - name: Download files - run: | - curl -sL \ - "https://raw.githubusercontent.com/vapor/api-docs/main/generate-package-api-docs.swift" \ - -o build-repo/generate-package-api-docs.swift \ - "https://raw.githubusercontent.com/vapor/api-docs/main/theme-settings.json" \ - -o build-repo/theme-settings.json - - name: Build docs - working-directory: build-repo - env: - DOCC_HTML_DIR: '../swift-docc-render/dist' - run: 'swift generate-package-api-docs.swift "${INPUT_PACKAGE_NAME}" ${INPUT_MODULES}' - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v6 - with: - role-to-assume: ${{ vars.OIDC_ROLE_ARN }} - aws-region: ${{ vars.OIDC_ROLE_REGION }} - - name: Deploy to S3 - env: - S3_BUCKET_URL: ${{ secrets.VAPOR_API_DOCS_S3_BUCKET_URL }} - run: | - aws s3 sync ./build-repo/public "${S3_BUCKET_URL}" --no-progress --acl public-read - - name: Invalidate CloudFront - env: - DISTRIBUTION_ID: ${{ secrets.VAPOR_API_DOCS_DISTRIBUTION_ID }} - run: | - aws cloudfront create-invalidation --distribution-id "${DISTRIBUTION_ID}" --paths ${INPUT_INVALIDATE_PATHS} diff --git a/.github/workflows/deploy-api-docs.yml b/.github/workflows/deploy-api-docs.yml deleted file mode 100644 index eead52a..0000000 --- a/.github/workflows/deploy-api-docs.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: API Docs website deploy -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true -on: - push: { branches: [ main ] } - -jobs: - deploy-site: - runs-on: ubuntu-22.04 - permissions: { id-token: write, contents: read } - env: { AWS_PAGER: '' } - steps: - - name: Check out code - uses: actions/checkout@v7 - - name: Install latest Swift - uses: vapor/swiftly-action@v0.2 - with: { toolchain: latest } - - name: Build site - run: swift generate-api-docs.swift - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v6 - with: - role-to-assume: ${{ vars.OIDC_ROLE_ARN }} - aws-region: ${{ vars.OIDC_ROLE_REGION }} - - name: Deploy to CloudFormation - uses: aws-actions/aws-cloudformation-github-deploy@v2 - with: - name: vapor-api-docs - template: stack.yaml - parameter-overrides: >- - BucketName=vapor-api-docs-site, - SubDomainName=api, - HostedZoneName=vapor.codes, - AcmCertificateArn=${{ secrets.API_DOCS_CERTIFICATE_ARN }} - - name: Deploy to S3 - env: - S3_BUCKET_URL: ${{ secrets.VAPOR_API_DOCS_S3_BUCKET_URL }} - run: 'aws s3 sync ./public "${S3_BUCKET_URL}" --no-progress --acl public-read' - - name: Invalidate CloudFront - env: - DISTRIBUTION_ID: ${{ secrets.VAPOR_API_DOCS_DISTRIBUTION_ID }} - run: 'aws cloudfront create-invalidation --distribution-id "${DISTRIBUTION_ID}" --paths "/*"' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..1a44822 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,114 @@ +name: Deploy API Docs + +on: + push: + branches: [main] + workflow_call: + inputs: + package: + description: "Package repo to rebuild e.g. vapor/jwt-kit" + type: string + required: true + ref: + description: "Branch to use" + type: string + required: false + default: "" + workflow_dispatch: + inputs: + package: + description: "Package repo to rebuild (leave blank to build all packages)" + type: string + required: false + default: "" + ref: + description: "Branch of the package to rebuild" + type: string + required: false + default: "" + rebuild-all: + description: "Regenerate every DocC archive from scratch." + type: boolean + required: false + default: false + +concurrency: + group: deploy-api-docs + cancel-in-progress: false + +permissions: + id-token: write + contents: read + +env: + AWS_PAGER: "" + ARCHIVE_BUCKET: s3://vapor-api-docs-archives + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + repository: vapor/api-docs + ref: main + + - name: Install Swift + uses: vapor/swiftly-action@bedb227456c5f495afbef80baebee17a8a02cef4 # v0.2.1 + with: + toolchain: latest + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1 + with: + role-to-assume: ${{ vars.OIDC_ROLE_ARN }} + aws-region: ${{ vars.OIDC_ROLE_REGION }} + + - name: Restore DocC archive cache + run: aws s3 sync "$ARCHIVE_BUCKET" ./Content/archives --no-progress + - name: Build site + env: + PACKAGE: ${{ inputs.package }} + REF: ${{ inputs.ref }} + REBUILD_ALL: ${{ inputs.rebuild-all }} + run: | + args=() + if [ "$REBUILD_ALL" = "true" ]; then + args+=(--rebuild-all) + elif [ -n "$PACKAGE" ] && [ -n "$REF" ]; then + args+=(--rebuild "${PACKAGE}@${REF}") + elif [ -n "$PACKAGE" ]; then + args+=(--rebuild "$PACKAGE") + fi + swift run APIDocs "${args[@]}" + + # Re-assume: a cold-cache build can outrun the 1h assume-role session. + - name: Refresh AWS credentials + uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1 + with: + role-to-assume: ${{ vars.OIDC_ROLE_ARN }} + aws-region: ${{ vars.OIDC_ROLE_REGION }} + + - name: Save DocC archive cache + run: aws s3 sync ./Content/archives "$ARCHIVE_BUCKET" --no-progress --delete + + - name: Deploy CloudFormation stack + uses: aws-actions/aws-cloudformation-github-deploy@81e3b03d2266bcb76c4bcc37a7d71d9cb67838bb # v2.2.0 + with: + name: vapor-api-docs + template: stack.yaml + parameter-overrides: >- + BucketName=vapor-api-docs-site, + SubDomainName=api, + HostedZoneName=vapor.codes, + AcmCertificateArn=${{ secrets.API_DOCS_CERTIFICATE_ARN }} + + - name: Deploy site to S3 + env: + S3_BUCKET_URL: ${{ secrets.VAPOR_API_DOCS_S3_BUCKET_URL }} + run: aws s3 sync ./site "$S3_BUCKET_URL" --no-progress --acl public-read --delete + + - name: Invalidate CloudFront + env: + DISTRIBUTION_ID: ${{ secrets.VAPOR_API_DOCS_DISTRIBUTION_ID }} + run: aws cloudfront create-invalidation --distribution-id "$DISTRIBUTION_ID" --paths "/*" diff --git a/.github/workflows/pr-closed.yml b/.github/workflows/pr-closed.yml new file mode 100644 index 0000000..a750cb1 --- /dev/null +++ b/.github/workflows/pr-closed.yml @@ -0,0 +1,32 @@ +name: PR - Closed +on: + pull_request: + branches: + - main + types: [closed] +permissions: + contents: read + id-token: write + deployments: write + +env: + AWS_PAGER: "" + +jobs: + delete-s3: + name: Delete S3 Website + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.head.repo.full_name == 'vapor/api-docs' && github.actor != 'dependabot[bot]' }} + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1 + with: + role-to-assume: ${{ vars.OIDC_ROLE_ARN }} + aws-region: ${{ vars.OIDC_ROLE_REGION }} + - name: Delete Website Bucket + uses: brokenhandsio/s3-website-pr-action@6ebd9b622b4bb33358228571a50e05d375cbd4de # v3.1.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + bucket-prefix: "vapor-api-docs-pulls" + bucket-region: "eu-west-2" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..cc57b16 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,62 @@ +name: PR +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +on: + pull_request: + branches: + - main +permissions: + contents: read + id-token: write + deployments: write + +env: + AWS_PAGER: "" + ARCHIVE_BUCKET: s3://vapor-api-docs-archives + +jobs: + build: + name: Build and Deploy PR + runs-on: ubuntu-latest + steps: + - name: Install latest Swift + uses: vapor/swiftly-action@bedb227456c5f495afbef80baebee17a8a02cef4 # v0.2.1 + with: + toolchain: latest + - name: Checkout code + uses: actions/checkout@v7 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.14" + - name: Setup CloudFormation linter + uses: ScottBrenner/cfn-lint-action@ed184e91f5085a2932501da8314e899e5e0ef5be # v2.7.1 + - name: Run CloudFormation linter + run: cfn-lint -t stack.yaml + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1 + with: + role-to-assume: ${{ vars.OIDC_ROLE_ARN }} + aws-region: ${{ vars.OIDC_ROLE_REGION }} + - name: Restore DocC archive cache + run: aws s3 sync "$ARCHIVE_BUCKET" ./Content/archives --no-progress + - name: Build site + run: swift run APIDocs + # Re-assume: a cold-cache build can outrun the 1h assume-role session. + - name: Refresh AWS credentials + uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # v6.2.1 + with: + role-to-assume: ${{ vars.OIDC_ROLE_ARN }} + aws-region: ${{ vars.OIDC_ROLE_REGION }} + - name: Save DocC archive cache + run: aws s3 sync ./Content/archives "$ARCHIVE_BUCKET" --no-progress + - name: Deploy S3 Website + if: ${{ github.event.pull_request.head.repo.full_name == 'vapor/api-docs' && github.actor != 'dependabot[bot]' }} + uses: brokenhandsio/s3-website-pr-action@6ebd9b622b4bb33358228571a50e05d375cbd4de # v3.1.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + bucket-prefix: "vapor-api-docs-pulls" + folder-to-copy: "./site" + bucket-region: "eu-west-2" diff --git a/.gitignore b/.gitignore index b8ef960..859cd9f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,7 @@ code /packages /swift-doc /public +/site +/.build +/Content/archives +.vscode/ diff --git a/Content/assets/api-og-2x.png b/Content/assets/api-og-2x.png new file mode 100644 index 0000000..e23f8b1 Binary files /dev/null and b/Content/assets/api-og-2x.png differ diff --git a/Content/assets/vapor-logo.png b/Content/assets/vapor-logo.png new file mode 100644 index 0000000..f7145e5 Binary files /dev/null and b/Content/assets/vapor-logo.png differ diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..755fa2f --- /dev/null +++ b/Package.resolved @@ -0,0 +1,114 @@ +{ + "originHash" : "1a66eecb6d8ea89d7653c4a24e98a9939cc60ba3222b7c25d98c06831e96660b", + "pins" : [ + { + "identity" : "design", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/design.git", + "state" : { + "revision" : "217f9af21356224feaea3a2596fcdfff9796dc14", + "version" : "1.0.0" + } + }, + { + "identity" : "kiln", + "kind" : "remoteSourceControl", + "location" : "https://github.com/brokenhandsio/kiln.git", + "state" : { + "revision" : "7b63c3440a796ed5debde8f0b7d4b51965e58b67", + "version" : "1.8.4" + } + }, + { + "identity" : "leaf-kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vapor/leaf-kit.git", + "state" : { + "revision" : "6044b844caa858a0c5f2505ac166f5a057c990dc", + "version" : "1.14.2" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382", + "version" : "1.8.2" + } + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "0442cb5a3f98ab802acb777929fdb446bda11a34", + "version" : "1.3.1" + } + }, + { + "identity" : "swift-cmark", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-cmark.git", + "state" : { + "revision" : "924936d0427cb25a61169739a7660230bffa6ea6", + "version" : "0.8.0" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "a0cb0954ecb21e4e31b0070e6ed5674e8556685a", + "version" : "1.6.0" + } + }, + { + "identity" : "swift-markdown", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-markdown.git", + "state" : { + "revision" : "3c6f9523da3a1ec2fd829673e472d95b8097a3b8", + "version" : "0.8.0" + } + }, + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio.git", + "state" : { + "revision" : "0b18836bd8b0162e7e17a995a3fbee20ed8f3b2b", + "version" : "2.101.3" + } + }, + { + "identity" : "swift-subprocess", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-subprocess.git", + "state" : { + "revision" : "13d087685b95d64d6aac9b94500d347bbe84c39b", + "version" : "0.4.0" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system.git", + "state" : { + "revision" : "b5544ba79a70a0cb3563e75bf26dc198d6b40ed3", + "version" : "1.7.4" + } + }, + { + "identity" : "yams", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/Yams.git", + "state" : { + "revision" : "3d6871d5b4a5cd519adf233fbb576e0a2af71c17", + "version" : "5.4.0" + } + } + ], + "version" : 3 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..6eee508 --- /dev/null +++ b/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version:6.3 +import PackageDescription + +let package = Package( + name: "APIDocs", + platforms: [ + .macOS(.v13) + ], + dependencies: [ + .package(url: "https://github.com/brokenhandsio/kiln.git", from: "1.8.4"), + .package(url: "https://github.com/vapor/design.git", from: "1.0.0"), + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"), + ], + targets: [ + .executableTarget( + name: "APIDocs", + dependencies: [ + .product(name: "Kiln", package: "kiln"), + .product(name: "VaporDesignTheme", package: "design"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ] + ), + ] +) diff --git a/README.md b/README.md index 8d77471..e32b6ff 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,18 @@ -# API Documentation +# Vapor API Docs -View the documentation at [api.vapor.codes](http://api.vapor.codes). +Source for [api.vapor.codes](https://api.vapor.codes) — the unified API reference for Vapor and its ecosystem. -This repo contains the script that generates all vapor api-docs when a new release is cut. -For every repo, on release a github action calls into a script that pulls in the latest version of this repo to the server, then running the provided script. +It's a [Kiln](https://github.com/brokenhandsio/kiln) site that renders pre-built [DocC](https://www.swift.org/documentation/docc/) archives into the shared Vapor design system, with a module catalog, search, and per-package version switchers. + +## Running locally + +```sh +kiln serve # build + preview at http://127.0.0.1:8080, rebuilding on change +kiln build # build the static site into ./site +``` + +A build reuses the DocC archives already in `Content/archives` and only generates missing ones. Delete an archive (or the whole folder) to regenerate it on the next build. + +## Adding or changing a package + +Edit [`Sources/APIDocs/Packages.swift`](Sources/APIDocs/Packages.swift): each `APIPackage` is one repo, and each `PackageVersion` declares the modules it ships. See the [Kiln DocC docs](https://github.com/brokenhandsio/kiln) for the config API. diff --git a/Sources/APIDocs/APIDocs.swift b/Sources/APIDocs/APIDocs.swift new file mode 100644 index 0000000..30071df --- /dev/null +++ b/Sources/APIDocs/APIDocs.swift @@ -0,0 +1,86 @@ +import ArgumentParser +import Kiln +import VaporDesignTheme + +let site = KilnSite( + name: "Vapor API Docs", + url: "https://api.vapor.codes", + author: "Vapor Community", + description: "API reference documentation for Vapor and its ecosystem (web framework for Swift).", + image: "assets/api-og-2x.png", + twitterSite: "@codevapor", + repository: .init( + name: "Vapor GitHub", + url: "https://github.com/vapor/vapor" + ), + copyright: "API Documentation © 2026 Vapor.", + theme: .default( + sharedLayers: [VaporDesignTheme.directory], + palette: .autoLightDark(primary: .black, accent: .blue), + logo: "assets/logo.png", + favicon: "assets/favicon.png", + fonts: Fonts(text: "Roboto", code: "Roboto Mono") + ), + social: [ + .init(icon: .github, link: "https://github.com/vapor"), + .init(icon: .discord, link: "https://discord.gg/vapor"), + .init(icon: .twitter, link: "https://twitter.com/codevapor"), + .init(icon: .mastodon, link: "https://hachyderm.io/@codevapor"), + ], + languages: languages, + docc: DocCSite( + packages: packages, + groupOrder: ["Core", "Authentication", "Templating", "Database", "Queues", "Push Notifications", "Testing"] + ) +) + +@main +struct APIDocs: AsyncParsableCommand { + static let configuration = CommandConfiguration( + commandName: "api-docs", + abstract: "Build the Vapor API reference site (api.vapor.codes) from DocC archives.", + discussion: """ + Generates any DocC archives that aren't already present (checking each \ + package out at its ref and running `swift package generate-documentation`), \ + then renders the whole site. In CI, restore the archive cache first, pass \ + --rebuild for the package that changed, and the rest is served from cache. + """ + ) + + @Option( + name: .customLong("rebuild"), + help: ArgumentHelp( + "Force-rebuild a package's DocC archives, reusing the rest.", + discussion: "A repo (jwt-kit or vapor/jwt-kit) rebuilds all its versions; pin one with repo@ref, e.g. routing-kit@main. Repeatable." + ) + ) + var rebuildPackages: [String] = [] + + @Flag(name: .customLong("rebuild-all"), help: "Rebuild every DocC archive from scratch.") + var rebuildAll = false + + @Option(help: "The content directory (DocC archives live under /archives).") + var content = "Content" + + @Option(help: "The output directory (the `kiln serve` default).") + var output = "site" + + func run() async throws { + let rebuild: DocCArchiveBuilder.Rebuild + if rebuildAll { + rebuild = .all + } else if !rebuildPackages.isEmpty { + rebuild = .rebuilding(rebuildPackages) + } else { + rebuild = .missing + } + + print("Ensuring DocC archives …") + try Kiln.buildDocCArchives(site, contentDirectory: content, rebuild: rebuild) + + print("Building Vapor API docs into ./\(output) …") + try await Kiln.build(site, contentDirectory: content, outputDirectory: output, incremental: true, + leafTags: VaporDesignTheme.leafTags) + print("Done. Serve it with: kiln serve") + } +} diff --git a/Sources/APIDocs/Languages.swift b/Sources/APIDocs/Languages.swift new file mode 100644 index 0000000..3a8fa80 --- /dev/null +++ b/Sources/APIDocs/Languages.swift @@ -0,0 +1,32 @@ +import Kiln + +let languages: [Language] = [ + Language( + .english, + isDefault: true, + customStrings: [ + "siteId": "apiDocs", + "head.defaultOgType": "article", + "head.homeSuffix": "", + "head.titleSeparator": " · ", + "tagline": "Reference documentation for Vapor and its ecosystem.", + "footer.tagline": "Reference documentation for Vapor and its ecosystem.", + "joinDiscord": "Join our Discord", + "footer.joinDiscord": "Join our Discord", + "supporters": "Supporters", + "footer.supporters": "Supporters", + "frameworkDocs": "Framework Docs", + "footer.frameworkDocs": "Framework Docs", + "apiDocs": "API Docs", + "footer.apiDocs": "API Docs", + "frameworkDocsCaption": "Learn how to use Vapor", + "apiDocsCaption": "Reference documentation for Vapor", + "selectLanguage": "Select language", + "selectVersion": "Select documentation version", + "selectTheme": "Select theme", + "closeMenu": "Close menu", + "skipToContent": "Skip to content", + ], + image: "assets/api-og-2x.png" + ) +] diff --git a/Sources/APIDocs/Packages.swift b/Sources/APIDocs/Packages.swift new file mode 100644 index 0000000..dfbef8d --- /dev/null +++ b/Sources/APIDocs/Packages.swift @@ -0,0 +1,112 @@ +import Kiln + +private let routingKit = Module("RoutingKit", description: "High-performance routing engine for HTTP requests.") +private let consoleKit = Module("ConsoleKit", description: "APIs for creating interactive CLI tools.") +private let consoleLogger = Module("ConsoleLogger", description: "A SwiftLog LogHandler implementation for customizable logging to a console.") +private let multipartKit = Module("MultipartKit", description: "Multipart form data parsing and encoding.") + +let packages: [APIPackage] = [ + APIPackage("vapor/vapor", group: "Core", versions: [ + .single(ref: "vapor4", modules: [ + Module("Vapor", description: "Core web framework for building server-side Swift applications."), + Module("XCTVapor", group: "Testing", description: "Testing utilities for Vapor applications when using XCTest."), + Module("VaporTesting", group: "Testing", description: "Modern testing framework for Vapor apps when using Swift Testing."), + ]), + ]), + APIPackage("vapor/async-kit", group: "Core", versions: [ + .single(ref: "main", modules: [Module("AsyncKit", description: "Async/await utilities and helpers for concurrent programming.")]), + ]), + APIPackage("vapor/routing-kit", group: "Core", versions: [ + PackageVersion("4", name: "4.x", ref: "v4", isDefault: true, modules: [routingKit]), + PackageVersion("5-beta", name: "5.0 (beta)", ref: "main", isPrerelease: true, modules: [routingKit]), + ]), + APIPackage("vapor/console-kit", group: "Core", versions: [ + PackageVersion("4", name: "4.x", ref: "v4", isDefault: true, modules: [consoleKit]), + PackageVersion("5-beta", name: "5.0 (beta)", ref: "main", isPrerelease: true, modules: [consoleKit, consoleLogger]), + ]), + APIPackage("vapor/websocket-kit", group: "Core", versions: [ + .single(ref: "main", modules: [Module("WebSocketKit", description: "WebSocket client and server implementation.")]), + ]), + APIPackage("vapor/multipart-kit", group: "Core", versions: [ + PackageVersion("4", name: "4.x", ref: "v4", isDefault: true, modules: [multipartKit]), + PackageVersion("5-alpha", name: "5.0 (alpha)", ref: "main", isPrerelease: true, modules: [multipartKit]), + ]), + + APIPackage("vapor/jwt", group: "Authentication", versions: [ + .single(ref: "main", modules: [Module("JWT", description: "JWT integration for Vapor authentication.")]), + ]), + APIPackage("vapor/jwt-kit", group: "Authentication", versions: [ + .single(ref: "main", modules: [Module("JWTKit", description: "JSON Web Token signing and verification framework.")]), + ]), + APIPackage("vapor/authentication", group: "Authentication", versions: [ + .single(ref: "main", modules: [Module("Authentication", description: "Authentication framework for Swift applications.")]), + ]), + + APIPackage("vapor/leaf", group: "Templating", versions: [ + .single(ref: "main", modules: [Module("Leaf", description: "Vapor integration for LeafKit.")]), + ]), + APIPackage("vapor/leaf-kit", group: "Templating", versions: [ + .single(ref: "main", modules: [Module("LeafKit", description: "Core templating engine framework.")]), + ]), + + APIPackage("vapor/fluent", group: "Database", versions: [ + .single(ref: "main", modules: [Module("Fluent", description: "Vapor integration package for FluentKit.")]), + ]), + APIPackage("vapor/fluent-kit", group: "Database", versions: [ + .single(ref: "main", modules: [ + Module("FluentKit", description: "Core ORM framework for database operations."), + Module("FluentSQL", description: "SQL dialect support for Fluent ORM."), + Module("XCTFluent", group: "Testing", description: "Testing utilities for Fluent ORM."), + ]), + ]), + APIPackage("vapor/fluent-postgres-driver", group: "Database", versions: [ + .single(ref: "main", modules: [Module("FluentPostgresDriver", description: "PostgreSQL driver for Fluent ORM.")]), + ]), + APIPackage("vapor/fluent-mongo-driver", group: "Database", versions: [ + .single(ref: "main", modules: [Module("FluentMongoDriver", description: "MongoDB driver for Fluent ORM.")]), + ]), + APIPackage("vapor/fluent-mysql-driver", group: "Database", versions: [ + .single(ref: "main", modules: [Module("FluentMySQLDriver", description: "MySQL driver for Fluent ORM.")]), + ]), + APIPackage("vapor/fluent-sqlite-driver", group: "Database", versions: [ + .single(ref: "main", modules: [Module("FluentSQLiteDriver", description: "SQLite driver for Fluent ORM.")]), + ]), + APIPackage("vapor/sql-kit", group: "Database", versions: [ + .single(ref: "main", modules: [Module("SQLKit", description: "SQL query building and execution framework.")]), + ]), + APIPackage("vapor/postgres-kit", group: "Database", versions: [ + .single(ref: "main", modules: [Module("PostgresKit", description: "PostgreSQL integration for SQLKit.")]), + ]), + APIPackage("vapor/mysql-kit", group: "Database", versions: [ + .single(ref: "main", modules: [Module("MySQLKit", description: "MySQL integration for SQLKit.")]), + ]), + APIPackage("vapor/sqlite-kit", group: "Database", versions: [ + .single(ref: "main", modules: [Module("SQLiteKit", description: "SQLite integration for SQLKit.")]), + ]), + APIPackage("vapor/postgres-nio", group: "Database", versions: [ + .single(ref: "main", modules: [Module("PostgresNIO", description: "Non-blocking PostgreSQL client built on SwiftNIO.")]), + ]), + APIPackage("vapor/mysql-nio", group: "Database", versions: [ + .single(ref: "main", modules: [Module("MySQLNIO", description: "Non-blocking MySQL client built on SwiftNIO.")]), + ]), + APIPackage("vapor/sqlite-nio", group: "Database", versions: [ + .single(ref: "main", modules: [Module("SQLiteNIO", description: "Non-blocking SQLite client built on SwiftNIO.")]), + ]), + APIPackage("vapor/redis", group: "Database", versions: [ + .single(ref: "main", modules: [Module("Redis", description: "Vapor wrapper for using Redis.")]), + ]), + + APIPackage("vapor/queues", group: "Queues", versions: [ + .single(ref: "main", modules: [ + Module("Queues", description: "Job queue system for background processing."), + Module("XCTQueues", group: "Testing", description: "Testing utilities for queue system."), + ]), + ]), + APIPackage("vapor/queues-redis-driver", group: "Queues", versions: [ + .single(ref: "main", modules: [Module("QueuesRedisDriver", description: "Redis driver for job queue system.")]), + ]), + + APIPackage("vapor/apns", group: "Push Notifications", versions: [ + .single(ref: "main", modules: [Module("VaporAPNS", title: "APNS", description: "Apple Push Notification Service integration.")]), + ]), +] diff --git a/api-docs.png b/api-docs.png deleted file mode 100644 index 0995107..0000000 Binary files a/api-docs.png and /dev/null differ diff --git a/error.html b/error.html deleted file mode 100644 index 2a93a7d..0000000 --- a/error.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - Error Page | Vapor API Docs - - - - -
-
-

Oops! Something went wrong

-
-

- We couldn't find what you were looking for. Try heading back to the home page. -

-
-
- - diff --git a/generate-api-docs.swift b/generate-api-docs.swift deleted file mode 100755 index 47b9294..0000000 --- a/generate-api-docs.swift +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env swift - -import Foundation - -let packages: [String: [String]] = [ - "vapor": ["Vapor", "XCTVapor", "VaporTesting"], - - "async-kit": ["AsyncKit"], - "routing-kit": ["RoutingKit"], - "console-kit": ["ConsoleKit", "ConsoleLogger"], - "websocket-kit": ["WebSocketKit"], - "multipart-kit": ["MultipartKit"], - - "postgres-nio": ["PostgresNIO"], - "mysql-nio": ["MySQLNIO"], - "sqlite-nio": ["SQLiteNIO"], - - "sql-kit": ["SQLKit"], - "postgres-kit": ["PostgresKit"], - "mysql-kit": ["MySQLKit"], - "sqlite-kit": ["SQLiteKit"], - - "fluent-kit": ["FluentKit", "FluentSQL", "XCTFluent"], - "fluent": ["Fluent"], - "fluent-postgres-driver": ["FluentPostgresDriver"], - "fluent-mongo-driver": ["FluentMongoDriver"], - "fluent-mysql-driver": ["FluentMySQLDriver"], - "fluent-sqlite-driver": ["FluentSQLiteDriver"], - - "redis": ["Redis"], - "queues-redis-driver": ["QueuesRedisDriver"], - "queues": ["Queues", "XCTQueues"], - - "leaf-kit": ["LeafKit"], - "leaf": ["Leaf"], - - "jwt-kit": ["JWTKit"], - "jwt": ["JWT"], - "apns": ["VaporAPNS"], - "authentication": ["Authentication"] -] - -// 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": "APIs for creating interactive CLI tools", - "ConsoleLogger": "A SwiftLog LogHandler implementation for customizable logging to a console", - "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", - "Authentication": "Authentication framework for Swift applications", -] - -// 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 """ - -

\(module)

-

\(description)

-
-""" -}.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("{{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) - try FileManager.default.copyItem(at: URL(fileURLWithPath: "./error.html", isDirectory: false), into: publicDirUrl) - try FileManager.default.copyItem(at: URL(fileURLWithPath: "./styles.css", isDirectory: false), into: publicDirUrl) -} catch let error as NSError { - print("❌ ERROR: \(String(reflecting: error)): \(error.userInfo)") - exit(1) -} - -extension NSError { - func isCocoaError(_ code: CocoaError.Code) -> Bool { - self.domain == CocoaError.errorDomain && self.code == code.rawValue - } -} - -extension FileManager { - func removeItemIfExists(at url: URL) throws { - do { - try self.removeItem(at: url) - } catch let error as NSError where error.isCocoaError(.fileNoSuchFile) { - // ignore - } - } - - func copyItem(at src: URL, into dst: URL) throws { - assert(dst.hasDirectoryPath) - - let dstItem = dst.appendingPathComponent(src.lastPathComponent, isDirectory: dst.hasDirectoryPath) - try self.copyItem(at: src, to: dstItem) - } -} diff --git a/generate-package-api-docs.swift b/generate-package-api-docs.swift deleted file mode 100755 index 7f73776..0000000 --- a/generate-package-api-docs.swift +++ /dev/null @@ -1,173 +0,0 @@ -import Foundation - -guard CommandLine.argc == 3 else { - print("❌ ERROR: You must provide the package name and modules as a comma separated string.") - exit(1) -} - -let packageName = CommandLine.arguments[1] -let moduleList = CommandLine.arguments[2] - -let modules = moduleList.components(separatedBy: ",") - -let publicDirectoryUrl = URL.currentDirectory().appending(component: "public") - -do { - try run() -} catch { - print("❌ ERROR: \(error).") - exit(1) -} - -// MARK: Functions - -func run() throws { - // Set up - print("⚙️ Ensuring public directory...") - try FileManager.default.removeItemIfExists(at: publicDirectoryUrl) - try FileManager.default.createDirectory(at: publicDirectoryUrl, withIntermediateDirectories: true) - - print("⚙️ Ensuring plugin...") - try ensurePluginAvailable() - - // Run - for module in modules { - print("⚙️ Generating api-docs for package: \(packageName), module: \(module)") - try generateDocs(module: module) - } - - print("✅ Finished generating api-docs for package: \(packageName)") -} - -func ensurePluginAvailable() throws { - var foundAtLeastOne = false - for manifestName in ["6.2", "6.1", "6.0", "5.10", "5.9", "5.8", "5.7"].map({ "Package@swift-\($0).swift" }) + ["Package.swift"] { - print("⚙️ Checking for manifest \(manifestName)") - let manifestUrl = URL.currentDirectory().appending(component: manifestName) - var manifestContents: String - do { manifestContents = try String(contentsOf: manifestUrl, encoding: .utf8) } - catch let error as NSError where error.isCocoaError(.fileReadNoSuchFile) { continue } - catch let error as NSError where error.isPOSIXError(.ENOENT) { continue } - - if !manifestContents.contains(".package(url: \"https://github.com/apple/swift-docc-plugin") { - // This is freely admitted to be quick and dirty. Unfortunately, swift package add-dependency doesn't understand version-tagged manifests. - print("🧬 Injecting DocC plugin dependency into \(manifestName)") - guard let depsArrayRange = manifestContents.firstRange(of: "dependencies: [") else { - print("❌ ERROR: Can't inject swift-docc-plugin dependency (can't find deps array).") - exit(1) - } - manifestContents.insert( - contentsOf: "\n.package(url: \"https://github.com/apple/swift-docc-plugin.git\", from: \"1.4.0\"),\n", - at: depsArrayRange.upperBound - ) - try manifestContents.write(to: manifestUrl, atomically: true, encoding: .utf8) - } - foundAtLeastOne = true - } - guard foundAtLeastOne else { - print("❌ ERROR: Can't inject swift-docc-plugin dependency (no usable manifest found).") - exit(1) - } -} - -func generateDocs(module: String) throws { - print("🔎 Finding DocC catalog") - let doccCatalogs = try FileManager.default.contentsOfDirectory( - at: URL.currentDirectory().appending(components: "Sources", "\(module)"), - includingPropertiesForKeys: nil, - options: [.skipsSubdirectoryDescendants] - ).filter { $0.hasDirectoryPath && $0.pathExtension == "docc" } - guard !doccCatalogs.isEmpty else { - print("❌ ERROR: No DocC catalog found for \(module)") - exit(1) - } - guard doccCatalogs.count == 1 else { - print("❌ ERROR: More than one DocC catalog found for \(module):\n\(doccCatalogs.map(\.lastPathComponent))") - exit(1) - } - let doccCatalogUrl = doccCatalogs[0] - print("🗂️ Using DocC catalog \(doccCatalogUrl.lastPathComponent)") - - print("📐 Copying theme") - try FileManager.default.copyItemIfExistsWithoutOverwrite( - at: URL.currentDirectory().appending(component: "theme-settings.json"), - to: doccCatalogUrl.appending(component: "theme-settings.json") - ) - - print("📝 Generating docs") - try shell([ - "swift", "package", - "--allow-writing-to-directory", publicDirectoryUrl.path, - "generate-documentation", - "--target", module, - "--disable-indexing", - "--experimental-skip-synthesized-symbols", - "--enable-inherited-docs", - "--enable-experimental-overloaded-symbol-presentation", - "--enable-experimental-mentioned-in", - "--hosting-base-path", "/\(module.lowercased())", - "--output-path", publicDirectoryUrl.appending(component: "\(module.lowercased())").path, - ]) -} - -func shell(_ args: String...) throws { try shell(args) } -func shell(_ args: [String]) throws { - // For fun, echo the command: - var sawXOpt = false, seenOpt = false, lastWasOpt = false - print("+ /usr/bin/env \\\n ", terminator: "") - for arg in (args.dropLast() + [args.last! + "\n"]) { - if (seenOpt && !lastWasOpt) || ((!seenOpt || (lastWasOpt && !sawXOpt)) && arg.starts(with: "-")) { - print(" \\\n ", terminator: "") - } - print(" \(arg)", terminator: "") - lastWasOpt = arg.starts(with: "-") - (seenOpt, sawXOpt) = (seenOpt || lastWasOpt, arg.starts(with: "-X")) - } - - // Run the command: - let task = try Process.run(URL(filePath: "/usr/bin/env"), arguments: args) - task.waitUntilExit() - guard task.terminationStatus == 0 else { - throw ShellError(terminationStatus: task.terminationStatus) - } -} - -struct ShellError: Error { - var terminationStatus: Int32 -} - -extension FileManager { - func removeItemIfExists(at url: URL) throws { - do { - try self.removeItem(at: url) - } catch let error as NSError where error.isCocoaError(.fileNoSuchFile) { - // ignore - } - } - - func copyItemIfExistsWithoutOverwrite(at src: URL, to dst: URL) throws { - do { - // https://github.com/apple/swift-corelibs-foundation/pull/4808 - #if !canImport(Darwin) - do { - _ = try dst.checkResourceIsReachable() - throw NSError(domain: CocoaError.errorDomain, code: CocoaError.fileWriteFileExists.rawValue) - } catch let error as NSError where error.isCocoaError(.fileReadNoSuchFile) {} - #endif - try self.copyItem(at: src, to: dst) - } catch let error as NSError where error.isCocoaError(.fileReadNoSuchFile) { - // ignore - } catch let error as NSError where error.isCocoaError(.fileWriteFileExists) { - // ignore - } - } -} - -extension NSError { - func isCocoaError(_ code: CocoaError.Code) -> Bool { - self.domain == CocoaError.errorDomain && self.code == code.rawValue - } - func isPOSIXError(_ code: POSIXError.Code) -> Bool { - self.domain == POSIXError.errorDomain && self.code == code.rawValue - } -} diff --git a/images/article.svg b/images/article.svg deleted file mode 100644 index 3dc6a66..0000000 --- a/images/article.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/images/collection.svg b/images/collection.svg deleted file mode 100644 index 6e73828..0000000 --- a/images/collection.svg +++ /dev/null @@ -1,4 +0,0 @@ - - Collection - - diff --git a/images/curly-brackets.svg b/images/curly-brackets.svg deleted file mode 100644 index a333bb5..0000000 --- a/images/curly-brackets.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/images/technology.svg b/images/technology.svg deleted file mode 100644 index a7e831b..0000000 --- a/images/technology.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/index.html b/index.html deleted file mode 100644 index 4c50619..0000000 --- a/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - Vapor API Docs - - - - - -
-
-

Vapor API Docs

-

Explore the complete API documentation for Vapor and its ecosystem

-
- -
- {{PackageCards}} -
- - -
- - diff --git a/stack.yaml b/stack.yaml index debabcf..87f70e2 100644 --- a/stack.yaml +++ b/stack.yaml @@ -21,20 +21,9 @@ Resources: Type: AWS::S3::Bucket Properties: BucketName: !Ref 'BucketName' - AccessControl: PublicRead WebsiteConfiguration: IndexDocument: index.html - ErrorDocument: error.html - RoutingRules: - - RoutingRuleCondition: - HttpErrorCodeReturnedEquals: '404' - KeyPrefixEquals: / - RedirectRule: - HostName: !Sub - - '${SubDomainName}.${HostedZoneName}.' - - SubDomainName: !Ref SubDomainName - HostedZoneName: !Ref HostedZoneName - ReplaceKeyPrefixWith: error/404/ + ErrorDocument: 404.html WebsiteCloudFront: Type: AWS::CloudFront::Distribution Properties: @@ -55,7 +44,7 @@ Resources: CustomErrorResponses: - ErrorCode: 403 ResponseCode: 404 - ResponsePagePath: '/error.html' + ResponsePagePath: '/404.html' DefaultCacheBehavior: TargetOriginId: S3Origin ViewerProtocolPolicy: redirect-to-https @@ -89,7 +78,7 @@ Resources: Value: 'Vapor API Docs' - Header: Content-Security-Policy-Report-Only Override: false - Value: "default-src 'none'; script-src 'self'; img-src 'self'; style-src 'self'; font-src 'self'; connect-src 'self'; base-uri 'self'; form-action 'self'" + Value: "default-src 'none'; script-src 'self' https://design.vapor.codes; style-src 'self' https://design.vapor.codes; img-src 'self' https://design.vapor.codes; font-src https://design.vapor.codes; connect-src 'self'; manifest-src 'self' https://design.vapor.codes; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests" SecurityHeadersConfig: ContentTypeOptions: Override: false diff --git a/styles.css b/styles.css deleted file mode 100644 index 244a1d9..0000000 --- a/styles.css +++ /dev/null @@ -1,180 +0,0 @@ -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -html, body { - min-height: 100%; - background: #0a0a0a; - color: #ffffff; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - line-height: 1.6; -} - -body { - background: linear-gradient(135deg, #0a0a0a 0%, #1a0a2e 100%); - background-attachment: fixed; -} - -.container { - max-width: 1200px; - margin: 0 auto; - padding: 2rem; -} - -.header { - text-align: center; - margin-bottom: 3rem; -} - -.logo { - background-image: url(api-docs.png); - width: 300px; - height: 78px; - background-size: contain; - background-repeat: no-repeat; - background-position: center; - text-indent: -9999px; - 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; } \ No newline at end of file diff --git a/theme-settings.json b/theme-settings.json deleted file mode 100644 index 45b8d40..0000000 --- a/theme-settings.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "theme": { - "aside": { - "border-radius": "16px", - "border-style": "double", - "border-width": "3px" - }, - "border-radius": "0", - "button": { - "border-radius": "16px", - "border-width": "1px", - "border-style": "solid" - }, - "code": { - "border-radius": "16px", - "border-width": "1px", - "border-style": "solid" - }, - "color": { - "fill": { - "dark": "rgb(0, 0, 0)", - "light": "rgb(255, 255, 255)" - }, - "documentation-intro-fill": "radial-gradient(circle at top, var(--color-documentation-intro-accent) 30%, #000 100%)", - "documentation-intro-accent": "rgb(204, 204, 204)", - "hero-eyebrow": "white", - "documentation-intro-figure": "white", - "hero-title": "white", - "logo-base": { "dark": "#fff", "light": "#000" }, - "logo-shape": { "dark": "#000", "light": "#fff" } - }, - "icons": { - "article": "/images/article.svg", - "collection": "/images/collection.svg", - "curly-brackets": "/images/curly-brackets.svg" - } - }, - "features": { - "quickNavigation": { - "enable": true - }, - "i18n": { - "enable": true - } - } -}