/** * Marketing copy for the dedicated feature pages. One entry per `/features/` page; the landing * page, the features index and the footer all derive their links from this list. */ import type { SnippetLanguage } from "~/lib/snippet-highlight"; export type DemoKey = "review" | "workflow" | "ai" | "editor" | "security" | "hosting"; export type FeatureDetail = { title: string; body: string; code?: { title: string; language: SnippetLanguage; source: string }; }; export type FeaturePage = { slug: string; /** Short name for links and navigation. */ nav: string; /** Page headline: says what the feature is, plainly. */ title: string; /** One or two sentences on what it does. Also the meta description. */ lede: string; demo: DemoKey; highlights: ReadonlyArray<{ title: string; body: string }>; details: ReadonlyArray; }; const WORKFLOW_YAML = `name: CI on: push: branches: [main] pull_request: jobs: test: runs-on: cloudflare strategy: matrix: node: [22, 24] steps: - uses: actions/checkout@v4 - run: bun install && bun test deploy: needs: test if: github.ref == 'refs/heads/main' runs-on: [self-hosted, linux] environment: production steps: - run: ./deploy.sh env: TOKEN: \${{ secrets.DEPLOY_TOKEN }}`; const RUNNER_SH = `# On any Linux machine with Docker or Podman gfd runner register --url https://your-forge.example --token gitfor_reg_... gfd runner run`; export const FEATURE_PAGES: ReadonlyArray = [ { slug: "review", nav: "Code review", title: "Pull requests and code review", lede: "Comment on any line, suggest exact changes, require approvals and passing checks, and merge through a queue that tests each pull request against the latest main.", demo: "review", highlights: [ { title: "Suggested changes", body: "Write the change you want in a review comment. The author can apply one suggestion or a batch of them as a single commit.", }, { title: "Branch protection", body: "Require approvals, passing checks and code owner reviews before a pull request can merge.", }, { title: "Merge queue", body: "Queued pull requests are tested in order against the latest main and merge automatically when their checks pass.", }, { title: "Conflict resolution", body: "Fix merge conflicts in a side-by-side editor in the browser, without checking out the branch.", }, ], details: [ { title: "Review history on one timeline", body: "Approvals, change requests, dismissals and re-requests appear on the pull request timeline alongside commits, deployments and linked issues. Reviewers can see which commit they last reviewed.", }, { title: "Merge, squash or rebase", body: "Choose which merge methods each repository allows. With auto-merge on, gitfor merges as soon as reviews and checks pass, and closes the issues your commits reference.", }, ], }, { slug: "actions", nav: "Actions & runners", title: "CI/CD with Actions workflows", lede: "Workflows are YAML files with the same structure as GitHub Actions. Jobs run on managed runners, or on your own machines with the gfd runner.", demo: "workflow", highlights: [ { title: "Workflow syntax", body: "Jobs, steps, matrices, needs, if: expressions, secrets, variables, artifacts and caches.", }, { title: "Managed runners", body: "Each job runs in a fresh, isolated container. Logs stream to the run page while the job is running.", }, { title: "Self-hosted runners", body: "Register any Linux machine with gfd. Each job runs in its own non-root container behind an egress firewall.", }, { title: "Deployment approvals", body: "Environments can require a reviewer to approve a deploy job before it runs.", }, ], details: [ { title: "Add a workflow file", body: "Commit a YAML file under .gitfor/workflows and push. Each job reports a commit status, and branch protection can require any of them.", code: { title: ".gitfor/workflows/ci.yml", language: "yaml", source: WORKFLOW_YAML }, }, { title: "Register your own runner", body: "Create a registration token in repository or organization settings, then register and start the runner. Pull requests from forks don't run on your machines unless an admin allows it.", code: { title: "terminal", language: "bash", source: RUNNER_SH }, }, ], }, { slug: "ai", nav: "AI assist", title: "AI for merge conflicts and pull request descriptions", lede: "gitfor can resolve merge conflicts and draft pull request titles and descriptions. Its commits go through branch protection like anyone else's, and it stays off for private repositories until an admin turns it on.", demo: "ai", highlights: [ { title: "Conflict resolution", body: "Each proposed fix gets a confidence score. Confident fixes are committed. Uncertain ones are left as suggestions for you to review.", }, { title: "Pull request descriptions", body: "On the compare page, get a suggested title and description written from the diff. Edit it or ignore it.", }, { title: "Same checks as everyone", body: "AI fixes are ordinary merge commits, so required reviews and CI still apply.", }, { title: "Your own model key", body: "Bring a provider key, set confidence thresholds per repository, and see every request on the usage page.", }, ], details: [ { title: "Private repositories", body: "AI features are off for private repositories until an admin enables them. Detected secrets are redacted before anything is sent to a model.", }, { title: "Agents", body: "Agent identities and a built-in MCP server let your own agents read issues, open pull requests and work together, with scoped permissions and an audit log.", }, ], }, { slug: "editor", nav: "Editor", title: "A code editor in the browser", lede: "Press . on any repository to open a VS Code–based editor. Change several files, commit them together, and edit alongside teammates in the same session.", demo: "editor", highlights: [ { title: "Nothing to install", body: "Search, multi-cursor editing, keybindings and Open VSX web extensions, all in the browser.", }, { title: "Shared sessions", body: "See who else has a file open, including agents.", }, { title: "Commit from the editor", body: "Stage changes across the repository and commit them to a new or existing branch.", }, { title: "Containers", body: "On paid plans, attach a container to get a terminal, tasks and language servers.", }, ], details: [ { title: "Runs in your browser", body: "Once loaded, the editor runs entirely client-side. Leaving it open costs nothing, and it keeps working on a slow connection.", }, ], }, { slug: "security", nav: "Security", title: "Secret scanning, code scanning and signed commits", lede: "gitfor rejects pushes that contain credentials, shows code scanning alerts on pull requests, and verifies who signed each commit.", demo: "security", highlights: [ { title: "Push protection", body: "A push that contains a credential is rejected with the file and line, before the secret reaches your history.", }, { title: "Code scanning", body: "Upload SARIF from any analyzer. Alerts appear on the affected lines and on pull requests.", }, { title: "Advisories and dependencies", body: "Draft private security advisories and see every dependency your manifests declare.", }, { title: "Verified commits", body: "GPG and SSH signatures are checked against each author's registered keys and shown on every commit.", }, ], details: [ { title: "Account security", body: "Passkeys, TOTP and recovery codes, plus fine-grained and deploy tokens. Raw files and uploads are served from a separate domain that can't read your session.", }, ], }, { slug: "hosting", nav: "Sites", title: "Static and dynamic sites from your repository", lede: "Publish a site from any branch. Every pull request gets its own preview URL, and you can roll back to any earlier deploy.", demo: "hosting", highlights: [ { title: "Static and dynamic", body: "Serve static files from any branch, or deploy server code alongside your assets. Each site runs isolated and can only read the secrets you give it.", }, { title: "Previews", body: "Every pull request gets its own preview URL on its own origin, updated on each push.", }, { title: "Custom domains", body: "Point your own domain at a site. Certificates are issued and renewed automatically.", }, { title: "Rollback", body: "Every deploy is kept, so rolling back switches to an earlier one without rebuilding.", }, ], details: [ { title: "Access control", body: "Make a site public, or limit it to people who can read the repository or to members of your organization. Access is checked before your code runs.", }, ], }, ]; export function featurePage(slug: string): FeaturePage | undefined { return FEATURE_PAGES.find((page) => page.slug === slug); }