diff --git a/docs/integrations/index.md b/docs/integrations/index.md index 8242fb8..efa43f1 100644 --- a/docs/integrations/index.md +++ b/docs/integrations/index.md @@ -1,10 +1,10 @@ --- title: Integrations description: Connect Cadence to Git platforms, AI providers, AI assistants, and automation workflows -order: 0 +order: 3 --- -# Integrations +## Integrations Cadence provides four integration surfaces: a **webhook server** for continuous Git platform monitoring, **AI providers** (OpenAI and Anthropic) for enhanced detection, an **AI skills system** for embedding detection into agents and pipelines, and a **plugin API** for extending detection with custom strategies. diff --git a/src/components/landing/Header.tsx b/src/components/landing/Header.tsx index 7f21224..ac756ad 100644 --- a/src/components/landing/Header.tsx +++ b/src/components/landing/Header.tsx @@ -42,6 +42,12 @@ export function Header() { > Playground + + Downloads + Examples + setMobileMenuOpen(false)} + > + Playground + + setMobileMenuOpen(false)} + > + Downloads + rootRouteImport, } as any) +const DownloadsRoute = DownloadsRouteImport.update({ + id: '/downloads', + path: '/downloads', + getParentRoute: () => rootRouteImport, +} as any) const ChangelogRoute = ChangelogRouteImport.update({ id: '/changelog', path: '/changelog', @@ -63,6 +69,7 @@ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/analysis': typeof AnalysisRoute '/changelog': typeof ChangelogRoute + '/downloads': typeof DownloadsRoute '/examples': typeof ExamplesRoute '/features': typeof FeaturesRoute '/meta-preview': typeof MetaPreviewRoute @@ -73,6 +80,7 @@ export interface FileRoutesByTo { '/': typeof IndexRoute '/analysis': typeof AnalysisRoute '/changelog': typeof ChangelogRoute + '/downloads': typeof DownloadsRoute '/examples': typeof ExamplesRoute '/features': typeof FeaturesRoute '/meta-preview': typeof MetaPreviewRoute @@ -84,6 +92,7 @@ export interface FileRoutesById { '/': typeof IndexRoute '/analysis': typeof AnalysisRoute '/changelog': typeof ChangelogRoute + '/downloads': typeof DownloadsRoute '/examples': typeof ExamplesRoute '/features': typeof FeaturesRoute '/meta-preview': typeof MetaPreviewRoute @@ -96,6 +105,7 @@ export interface FileRouteTypes { | '/' | '/analysis' | '/changelog' + | '/downloads' | '/examples' | '/features' | '/meta-preview' @@ -106,6 +116,7 @@ export interface FileRouteTypes { | '/' | '/analysis' | '/changelog' + | '/downloads' | '/examples' | '/features' | '/meta-preview' @@ -116,6 +127,7 @@ export interface FileRouteTypes { | '/' | '/analysis' | '/changelog' + | '/downloads' | '/examples' | '/features' | '/meta-preview' @@ -127,6 +139,7 @@ export interface RootRouteChildren { IndexRoute: typeof IndexRoute AnalysisRoute: typeof AnalysisRoute ChangelogRoute: typeof ChangelogRoute + DownloadsRoute: typeof DownloadsRoute ExamplesRoute: typeof ExamplesRoute FeaturesRoute: typeof FeaturesRoute MetaPreviewRoute: typeof MetaPreviewRoute @@ -157,6 +170,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ExamplesRouteImport parentRoute: typeof rootRouteImport } + '/downloads': { + id: '/downloads' + path: '/downloads' + fullPath: '/downloads' + preLoaderRoute: typeof DownloadsRouteImport + parentRoute: typeof rootRouteImport + } '/changelog': { id: '/changelog' path: '/changelog' @@ -199,6 +219,7 @@ const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, AnalysisRoute: AnalysisRoute, ChangelogRoute: ChangelogRoute, + DownloadsRoute: DownloadsRoute, ExamplesRoute: ExamplesRoute, FeaturesRoute: FeaturesRoute, MetaPreviewRoute: MetaPreviewRoute, diff --git a/src/routes/downloads.tsx b/src/routes/downloads.tsx new file mode 100644 index 0000000..fc0a2bb --- /dev/null +++ b/src/routes/downloads.tsx @@ -0,0 +1,434 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { useEffect, useState } from "react"; +import { + VscLoading, + VscPackage, + VscTerminal, + VscWarning, +} from "react-icons/vsc"; +import { SiDocker, SiGithub } from "react-icons/si"; +import { LandingLayout } from "../components/layouts/LandingLayout"; +import { Button } from "../components/ui/Button"; + +interface Asset { + name: string; + download_count: number; + browser_download_url: string; + size: number; +} + +interface Release { + tag_name: string; + name: string; + published_at: string; + prerelease: boolean; + assets: Asset[]; +} + +const GITHUB_REPO = "TryCadence/Cadence"; + +export const Route = createFileRoute("/downloads")({ + head: () => ({ + meta: [ + { title: "Downloads | Cadence" }, + { + name: "description", + content: + "Download Cadence binaries for Linux, macOS, and Windows. Access current and previous releases.", + }, + { + property: "og:title", + content: "Downloads | Cadence", + }, + { + property: "og:description", + content: + "Download Cadence binaries for Linux, macOS, and Windows. Access current and previous releases.", + }, + { + property: "og:url", + content: "https://noslop.tech/downloads", + }, + ], + }), + component: DownloadsPage, +}); + +function DownloadsPage() { + const [releases, setReleases] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchReleases = async () => { + try { + const response = await fetch( + `https://api.github.com/repos/${GITHUB_REPO}/releases`, + { + headers: { + Accept: "application/vnd.github.v3+json", + }, + } + ); + + if (!response.ok) { + throw new Error("Failed to fetch releases"); + } + + const data: Release[] = await response.json(); + // Filter out pre-releases for production, but include them in the list + setReleases(data.filter((r) => !r.prerelease).slice(0, 20)); + setError(null); + } catch (err) { + setError( + err instanceof Error ? err.message : "Unknown error occurred" + ); + setReleases([]); + } finally { + setLoading(false); + } + }; + + fetchReleases(); + }, []); + + const latest = releases[0]; + const previous = releases.slice(1); + + return ( + +
+
+ {/* Header */} +
+

+ Downloads +

+

+ Get the latest Cadence binaries for your platform +

+
+ + {/* Error State */} + {error && ( +
+
+ +
+

+ Failed to load releases +

+

{error}

+

+ Visit{" "} + + GitHub Releases + {" "} + to download manually. +

+
+
+
+ )} + + {/* Loading State */} + {loading && ( +
+
+ +

Loading releases...

+
+
+ )} + + {/* Latest Release */} + {latest && !loading && ( +
+
+ + Latest Version + +
+ +
+
+
+

+ {latest.tag_name} +

+

+ Released{" "} + {new Date(latest.published_at).toLocaleDateString()} +

+
+ + + Release Notes + +
+ +
+ {/* Platform Cards */} + } + label="Linux" + /> + } + label="macOS" + /> + } + label="Windows" + /> +
+
+ +
+
+

+ Docker +

+

+ trycadence/cadence:{latest.tag_name.replace( + /^v/, + "" + )} +

+
+
+
+ +
+
+
+

+ {latest.assets.length} +

+

Binaries

+
+
+

+ {( + latest.assets.reduce( + (sum, a) => sum + a.size, + 0 + ) / (1024 * 1024) + ).toFixed(1)} + MB +

+

Total Size

+
+
+

+ {latest.assets.reduce( + (sum, a) => sum + a.download_count, + 0 + )} +

+

Downloads

+
+
+
+
+
+ )} + + {/* Previous Releases */} + {previous.length > 0 && !loading && ( +
+

+ Previous Versions +

+
+ {previous.map((release) => ( + +
+
+ +
+

+ {release.tag_name} +

+

+ {release.assets.length} files •{" "} + {new Date(release.published_at).toLocaleDateString()} +

+
+
+
+ {( + release.assets.reduce( + (sum, a) => sum + a.size, + 0 + ) / 1024 / 1024 + ).toFixed(1)} + MB +
+
+
+ ))} +
+
+ )} + + {/* Installation Methods */} +
+

+ Installation Methods +

+ +
+ {/* Direct Binary */} +
+
+ +
+

+ Direct Download +

+

+ Download the binary for your platform from the releases above. +

+ + chmod +x cadence +
+ ./cadence --version +
+
+ + {/* Docker */} +
+
+ +
+

Docker

+

+ Run Cadence in a Docker container. +

+ + docker pull +
+ trycadence/cadence +
+
+ + {/* From Source */} +
+
+ +
+

From Source

+

+ Build from the GitHub repository. +

+ + git clone +
+ ... && make build +
+
+
+
+ + {/* CTA Footer */} +
+

+ Need Help? +

+

+ Check out our documentation or report issues on GitHub +

+
+ + +
+
+
+
+ + ); +} + +function PlatformGroup({ + assets, + platform, + icon, + label, +}: { + assets: Asset[]; + platform: string; + icon: React.ReactNode; + label: string; +}) { + const binaries = assets.filter((a) => + a.name.toLowerCase().includes(platform.toLowerCase()) + ); + + if (binaries.length === 0) return null; + + return ( +
+
+
+ {icon} +
+
+

{label}

+
+ {binaries.map((binary) => ( + + + {binary.name} + + + {(binary.size / (1024 * 1024)).toFixed(1)}MB + + + ))} +
+
+
+
+ ); +} +