docs(nx-dev): update ai page (#31669)
Co-authored-by: Juri <juri.strumpflohner@gmail.com>
7
.cursor/mcp.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"nx-mcp": {
|
||||||
|
"url": "http://localhost:9470/sse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,76 +0,0 @@
|
|||||||
import type { Metadata } from 'next';
|
|
||||||
import { DefaultLayout } from '@nx/nx-dev/ui-common';
|
|
||||||
import { Hero } from '@nx/nx-dev/ui-ai-landing-page';
|
|
||||||
import { ProblemStatement } from '@nx/nx-dev/ui-ai-landing-page';
|
|
||||||
import { Features } from '@nx/nx-dev/ui-ai-landing-page';
|
|
||||||
import { CallToAction } from '@nx/nx-dev/ui-ai-landing-page';
|
|
||||||
import { TechnicalImplementation } from '@nx/nx-dev/ui-ai-landing-page';
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: 'Nx - Make AI work in large codebases',
|
|
||||||
description:
|
|
||||||
'Empower your AI assistants with workspace intelligence to understand your codebase structure, project dependencies, and build processes at a glance.',
|
|
||||||
alternates: {
|
|
||||||
canonical: 'https://nx.dev/ai',
|
|
||||||
},
|
|
||||||
openGraph: {
|
|
||||||
title: 'Nx - Make AI work in large codebases',
|
|
||||||
description:
|
|
||||||
'Empower your AI assistants with workspace intelligence to understand your codebase structure, project dependencies, and build processes at a glance.',
|
|
||||||
url: 'https://nx.dev/ai',
|
|
||||||
siteName: 'Nx',
|
|
||||||
images: [
|
|
||||||
{
|
|
||||||
url: 'https://nx.dev/images/nx-ai-landing-og.png',
|
|
||||||
width: 1200,
|
|
||||||
height: 630,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
locale: 'en_US',
|
|
||||||
type: 'website',
|
|
||||||
},
|
|
||||||
keywords: [
|
|
||||||
'nx',
|
|
||||||
'ai',
|
|
||||||
'workspace',
|
|
||||||
'architecture',
|
|
||||||
'codebase',
|
|
||||||
'llm',
|
|
||||||
'AI workspace development',
|
|
||||||
'LLM code assistant',
|
|
||||||
'Nx AI integration',
|
|
||||||
'multi-project AI tools',
|
|
||||||
'enterprise AI development',
|
|
||||||
'intelligent code generation',
|
|
||||||
'MCP server',
|
|
||||||
'workspace AI tools',
|
|
||||||
'monorepo AI',
|
|
||||||
'architectural intelligence',
|
|
||||||
'code assistant',
|
|
||||||
'workspace intelligence',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function AiLandingPage() {
|
|
||||||
return (
|
|
||||||
<DefaultLayout>
|
|
||||||
<Hero />
|
|
||||||
|
|
||||||
<div className="mt-32 lg:mt-56" id="problem-statement">
|
|
||||||
<ProblemStatement />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-32 lg:mt-56" id="features">
|
|
||||||
<Features />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-32 lg:mt-56" id="how-it-works">
|
|
||||||
<TechnicalImplementation />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-32 lg:mt-56">
|
|
||||||
<CallToAction />
|
|
||||||
</div>
|
|
||||||
</DefaultLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
60
nx-dev/nx-dev/pages/ai.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import type { Metadata } from 'next';
|
||||||
|
import { DefaultLayout } from '@nx/nx-dev/ui-common';
|
||||||
|
import {
|
||||||
|
AiHero,
|
||||||
|
CallToAction,
|
||||||
|
WhileCoding,
|
||||||
|
WhileRunningCi,
|
||||||
|
WhileScalingYourOrganization,
|
||||||
|
} from '@nx/nx-dev/ui-ai-landing-page';
|
||||||
|
import type { ReactElement } from 'react';
|
||||||
|
import { NextSeo } from 'next-seo';
|
||||||
|
|
||||||
|
export function Ai(): ReactElement {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NextSeo
|
||||||
|
title="From your editor to CI, Nx makes your AI a lot more powerful."
|
||||||
|
description="Empower your AI assistants with workspace intelligence to understand your codebase structure, project dependencies, and build processes at a glance."
|
||||||
|
openGraph={{
|
||||||
|
url: 'https://nx.dev/ai',
|
||||||
|
title:
|
||||||
|
'From your editor to CI, Nx makes your AI a lot more powerful.',
|
||||||
|
description:
|
||||||
|
'Empower your AI assistants with workspace intelligence to understand your codebase structure, project dependencies, and build processes at a glance.',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://nx.dev/socials/nx-media.png',
|
||||||
|
width: 800,
|
||||||
|
height: 421,
|
||||||
|
alt: 'Nx: Smart Repos · Fast Builds',
|
||||||
|
type: 'image/jpeg',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
siteName: 'Nx',
|
||||||
|
type: 'website',
|
||||||
|
}}
|
||||||
|
canonical="https://nx.dev/ai"
|
||||||
|
/>
|
||||||
|
<DefaultLayout>
|
||||||
|
<AiHero />
|
||||||
|
|
||||||
|
<div className="mt-12 lg:mt-24">
|
||||||
|
<WhileCoding />
|
||||||
|
</div>
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<WhileRunningCi />
|
||||||
|
</div>
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<WhileScalingYourOrganization />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<CallToAction />
|
||||||
|
</div>
|
||||||
|
</DefaultLayout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Ai;
|
||||||
|
After Width: | Height: | Size: 36 KiB |
BIN
nx-dev/nx-dev/public/images/ai/ci-querying-thumb.avif
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 28 KiB |
BIN
nx-dev/nx-dev/public/images/ai/self-healing-ci-thumb.avif
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 87 KiB |
@ -1,4 +1,8 @@
|
|||||||
export * from './lib/hero';
|
export * from './lib/hero';
|
||||||
|
export * from './lib/new/ai-hero';
|
||||||
|
export * from './lib/new/while-coding';
|
||||||
|
export * from './lib/new/while-running-ci';
|
||||||
|
export * from './lib/new/while-scaling-your-organization';
|
||||||
export * from './lib/problem-statement';
|
export * from './lib/problem-statement';
|
||||||
export * from './lib/features';
|
export * from './lib/features';
|
||||||
export * from './lib/technical-implementation';
|
export * from './lib/technical-implementation';
|
||||||
|
|||||||
@ -56,7 +56,7 @@ export function CallToAction(): ReactElement {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="mx-auto max-w-2xl text-center">
|
<div className="mx-auto max-w-3xl text-center">
|
||||||
<h2 className="text-3xl font-medium tracking-tight text-slate-950 sm:text-5xl dark:text-white">
|
<h2 className="text-3xl font-medium tracking-tight text-slate-950 sm:text-5xl dark:text-white">
|
||||||
Transform your AI assistant in minutes
|
Transform your AI assistant in minutes
|
||||||
</h2>
|
</h2>
|
||||||
@ -75,14 +75,6 @@ export function CallToAction(): ReactElement {
|
|||||||
→
|
→
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
{/* <Link
|
|
||||||
href="https://youtu.be/RNilYmJJzdk"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-sm font-semibold leading-6 text-slate-900 dark:text-white"
|
|
||||||
>
|
|
||||||
Watch 3-min Demo <span aria-hidden="true">→</span>
|
|
||||||
</Link> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
69
nx-dev/ui-ai-landing-page/src/lib/new/ai-hero.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import type { ReactElement } from 'react';
|
||||||
|
import { ButtonLink, SectionHeading } from '@nx/nx-dev/ui-common';
|
||||||
|
import {
|
||||||
|
CodeBracketIcon,
|
||||||
|
ServerStackIcon,
|
||||||
|
UserGroupIcon,
|
||||||
|
} from '@heroicons/react/24/outline';
|
||||||
|
import { NxPowerAi } from './nx-power-ai';
|
||||||
|
|
||||||
|
export function AiHero(): ReactElement {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<div className="mx-auto flex max-w-7xl">
|
||||||
|
<div className="max-w-4xl px-6 pb-24 pt-12 lg:mx-0 lg:shrink-0 lg:px-8">
|
||||||
|
<SectionHeading
|
||||||
|
id="get-speed-and-scale"
|
||||||
|
as="h1"
|
||||||
|
variant="display"
|
||||||
|
className="text-pretty tracking-tight"
|
||||||
|
>
|
||||||
|
From editor to CI, <br /> Nx makes your AI <br />
|
||||||
|
<span className="rounded-lg bg-gradient-to-r from-pink-500 to-fuchsia-500 bg-clip-text text-transparent">
|
||||||
|
a lot more powerful.
|
||||||
|
</span>
|
||||||
|
</SectionHeading>
|
||||||
|
<div className="mt-6">
|
||||||
|
<ButtonLink
|
||||||
|
href="/getting-started/ai-integration"
|
||||||
|
title="Nx AI Integration"
|
||||||
|
variant="primary"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
Integrate Nx with your Coding Assistant
|
||||||
|
</ButtonLink>
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 flex flex-wrap items-center gap-4">
|
||||||
|
<a
|
||||||
|
href="#while-coding"
|
||||||
|
className="inline-flex items-center gap-1.5 rounded-full bg-slate-100 px-2 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-200 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700"
|
||||||
|
aria-label="Jump to While Coding section"
|
||||||
|
>
|
||||||
|
<CodeBracketIcon aria-hidden="true" className="size-4 shrink-0" />
|
||||||
|
<span>Coding</span>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#while-running-ci"
|
||||||
|
className="inline-flex items-center gap-1.5 rounded-full bg-slate-100 px-2 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-200 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700"
|
||||||
|
aria-label="Jump to While Running CI section"
|
||||||
|
>
|
||||||
|
<ServerStackIcon aria-hidden="true" className="size-4 shrink-0" />
|
||||||
|
<span>Running CI</span>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#while-scaling-your-organization"
|
||||||
|
className="inline-flex items-center gap-1.5 rounded-full bg-slate-100 px-2 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-200 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700"
|
||||||
|
aria-label="Jump to Scaling Your Organization section"
|
||||||
|
>
|
||||||
|
<UserGroupIcon aria-hidden="true" className="size-4 shrink-0" />
|
||||||
|
<span>Scaling Your Organization</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden w-auto grow grid-cols-1 place-items-center lg:grid">
|
||||||
|
<NxPowerAi className="-mt-8" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
84
nx-dev/ui-ai-landing-page/src/lib/new/feature-card.tsx
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { ReactElement, ReactNode } from 'react';
|
||||||
|
import Image from 'next/image';
|
||||||
|
import { PlayButton } from './play-button';
|
||||||
|
|
||||||
|
type VideoFeature = {
|
||||||
|
type: 'video';
|
||||||
|
videoUrl: string;
|
||||||
|
onPlayClick?: (videoUrl: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LinkFeature = {
|
||||||
|
type?: 'link';
|
||||||
|
videoUrl?: never;
|
||||||
|
onPlayClick?: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface BaseFeatureCardProps {
|
||||||
|
isAvailable: boolean;
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
description: ReactNode;
|
||||||
|
imageUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FeatureCardProps = BaseFeatureCardProps &
|
||||||
|
(VideoFeature | LinkFeature);
|
||||||
|
|
||||||
|
export function FeatureCard({
|
||||||
|
isAvailable,
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
description,
|
||||||
|
type = 'link',
|
||||||
|
imageUrl,
|
||||||
|
videoUrl,
|
||||||
|
onPlayClick,
|
||||||
|
}: FeatureCardProps): ReactElement {
|
||||||
|
const handlePlayClick = () => {
|
||||||
|
if (type === 'video' && videoUrl && onPlayClick) {
|
||||||
|
onPlayClick(videoUrl);
|
||||||
|
} else if (type === 'video' && !videoUrl) {
|
||||||
|
console.warn(
|
||||||
|
`Video type specified for ${title} but no videoUrl provided`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={id} className="flex flex-col">
|
||||||
|
<dt className="text-base/7 font-semibold">
|
||||||
|
<div className="relative mb-6 aspect-video max-h-52 w-full overflow-hidden rounded-xl bg-slate-200 dark:bg-slate-700/60">
|
||||||
|
<Image
|
||||||
|
src={imageUrl}
|
||||||
|
alt={`Thumbnail for ${title}`}
|
||||||
|
width={1280}
|
||||||
|
height={720}
|
||||||
|
loading="lazy"
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
|
{type === 'video' && videoUrl && onPlayClick ? (
|
||||||
|
<div className="absolute inset-0 grid h-full w-full items-center justify-center">
|
||||||
|
<PlayButton onClick={handlePlayClick} />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{!isAvailable && (
|
||||||
|
<span className="absolute bottom-2 right-2 inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10 dark:bg-blue-400/10 dark:text-blue-400 dark:ring-blue-400/30">
|
||||||
|
Coming soon
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{title}
|
||||||
|
<div className="text-xs/7 font-normal italic opacity-80">
|
||||||
|
{subtitle}
|
||||||
|
</div>
|
||||||
|
</dt>
|
||||||
|
<dd className="mt-1 flex flex-auto flex-col text-base/7">
|
||||||
|
{description}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
182
nx-dev/ui-ai-landing-page/src/lib/new/nx-power-ai.tsx
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import type { FC, ReactElement, SVGProps } from 'react';
|
||||||
|
import {
|
||||||
|
ClaudeIcon,
|
||||||
|
GoogleGeminiIcon,
|
||||||
|
MetaIcon,
|
||||||
|
OpenAiIcon,
|
||||||
|
} from '@nx/nx-dev/ui-icons';
|
||||||
|
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||||
|
|
||||||
|
const HexagonShape: FC<SVGProps<SVGSVGElement>> = (props) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 22 24"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path d="M8.6 1.386C9.474.88 9.911.628 10.376.53a3 3 0 0 1 1.248 0c.464.098.902.35 1.776.856l5.592 3.228c.875.505 1.312.758 1.63 1.11.281.313.494.681.623 1.081.147.452.147.957.147 1.966v6.458c0 1.01 0 1.514-.146 1.966a3 3 0 0 1-.624 1.08c-.318.353-.755.606-1.63 1.11l-5.592 3.23c-.874.504-1.312.757-1.776.855a2.997 2.997 0 0 1-1.248 0c-.465-.098-.902-.35-1.776-.856l-5.592-3.228c-.875-.505-1.312-.758-1.63-1.11a3 3 0 0 1-.623-1.081c-.147-.452-.147-.957-.147-1.966V8.77c0-1.01 0-1.514.147-1.966a3 3 0 0 1 .623-1.08c.318-.353.755-.606 1.63-1.11L8.6 1.384Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export function NxPowerAi({
|
||||||
|
className = '',
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className={cx('relative mx-auto w-full max-w-md p-4', className)}>
|
||||||
|
<div className="pointer-events-none">
|
||||||
|
<ul className="grid grid-cols-5 gap-x-2">
|
||||||
|
{/*Row 1*/}
|
||||||
|
<li className="invisible aspect-square" />
|
||||||
|
<li className="relative isolate flex aspect-square h-20 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-slate-50/90 drop-shadow-sm dark:text-white/5"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="relative isolate flex aspect-square h-20 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-white/70 drop-shadow-lg dark:text-white/15"
|
||||||
|
/>
|
||||||
|
<OpenAiIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="z-10 size-8 text-black dark:text-white"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="relative isolate flex aspect-square h-20 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-slate-50/90 drop-shadow-sm dark:text-white/5"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="invisible aspect-square" />
|
||||||
|
|
||||||
|
{/*Row 2*/}
|
||||||
|
<li className="flex aspect-square translate-x-1/2 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-slate-50/90 drop-shadow-sm dark:text-white/5"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="relative isolate flex aspect-square h-20 translate-x-1/2 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-white/70 drop-shadow-lg dark:text-white/15"
|
||||||
|
/>
|
||||||
|
<ClaudeIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="z-10 size-8 text-[#D97757]"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="relative isolate flex aspect-square h-20 translate-x-1/2 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-white/70 drop-shadow-lg dark:text-white/15"
|
||||||
|
/>
|
||||||
|
<GoogleGeminiIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="z-10 size-8 text-[#8E75B2]"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="flex aspect-square translate-x-1/2 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-slate-50/90 drop-shadow-sm dark:text-white/5"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="invisible"></li>
|
||||||
|
|
||||||
|
{/*Row 3*/}
|
||||||
|
<li className="invisible" />
|
||||||
|
<li className="relative isolate flex aspect-square h-20 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-slate-50/90 drop-shadow-sm dark:text-white/5"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="relative isolate flex aspect-square h-20 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-white/70 drop-shadow-lg dark:text-white/15"
|
||||||
|
/>
|
||||||
|
<MetaIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="z-10 size-8 text-[#0467DF]"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="relative isolate flex aspect-square h-20 items-center justify-center">
|
||||||
|
<HexagonShape
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute inset-0 z-0 text-slate-50/90 drop-shadow-sm dark:text-white/5"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="invisible" />
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Steps List */}
|
||||||
|
<div className="relative mt-12 space-y-2">
|
||||||
|
{/* Active Step */}
|
||||||
|
<div className="rounded-xl border border-slate-200 bg-slate-50 p-4 shadow-sm backdrop-blur-sm dark:border-slate-800/40 dark:bg-slate-800/60">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="flex size-5 items-center justify-center rounded-full border border-white/25 bg-gradient-to-br from-slate-200 to-slate-300 dark:border-slate-900 dark:from-slate-700 dark:to-slate-800">
|
||||||
|
<div className="size-2 rounded-full bg-white dark:bg-slate-900" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="font-mono text-xs leading-tight text-slate-900 dark:text-slate-100">
|
||||||
|
Generating a fix for test "ui-profile:test:save-preferences"
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<span className="text-xs text-slate-600 dark:text-slate-400">
|
||||||
|
Working...
|
||||||
|
</span>
|
||||||
|
<span className="relative flex size-1.5">
|
||||||
|
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-orange-400 opacity-75" />
|
||||||
|
<span className="relative inline-flex size-1.5 rounded-full bg-orange-500" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Completed Step */}
|
||||||
|
<div className="rounded-xl border border-slate-200 bg-slate-50 p-4 opacity-50 shadow-sm backdrop-blur-sm dark:border-slate-800/40 dark:bg-slate-800/60">
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="flex grow items-center space-x-3">
|
||||||
|
<div className="flex size-5 items-center justify-center rounded-full border border-white/25 bg-gradient-to-br from-slate-200 to-slate-300 dark:border-slate-900 dark:from-slate-700 dark:to-slate-800">
|
||||||
|
<div className="size-2 rounded-full bg-white dark:bg-slate-900" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full grow space-y-1">
|
||||||
|
<div className="h-1 w-8 rounded-sm bg-slate-800/15 dark:bg-slate-200/35" />
|
||||||
|
<div className="h-1 w-1/2 rounded-sm bg-slate-500/10 dark:bg-slate-400/35" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<span className="text-xs text-slate-600 dark:text-slate-300">
|
||||||
|
Done
|
||||||
|
</span>
|
||||||
|
<div className="size-1.5 rounded-full bg-green-500 shadow-sm" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pending Step */}
|
||||||
|
<div className="rounded-xl border border-slate-200 bg-slate-50 p-4 opacity-40 shadow-sm backdrop-blur-sm dark:border-slate-800/40 dark:bg-slate-800/60">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="flex size-5 items-center justify-center rounded-full border border-white/25 bg-gradient-to-br from-slate-200 to-slate-300 dark:border-slate-900 dark:from-slate-700 dark:to-slate-800">
|
||||||
|
<div className="size-2 rounded-full bg-white dark:bg-slate-900" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="h-1 w-16 rounded-sm bg-slate-400/20" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
70
nx-dev/ui-ai-landing-page/src/lib/new/play-button.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { ComponentProps, ReactElement } from 'react';
|
||||||
|
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||||
|
import { MovingBorder } from '@nx/nx-dev/ui-animations';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { PlayIcon } from '@heroicons/react/24/outline';
|
||||||
|
|
||||||
|
export function PlayButton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: ComponentProps<'div'>): ReactElement {
|
||||||
|
const parent = {
|
||||||
|
initial: {
|
||||||
|
width: 82,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
width: 296,
|
||||||
|
transition: {
|
||||||
|
duration: 0.125,
|
||||||
|
type: 'tween',
|
||||||
|
ease: 'easeOut',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const child = {
|
||||||
|
initial: {
|
||||||
|
opacity: 0,
|
||||||
|
x: -6,
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
x: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.015,
|
||||||
|
type: 'tween',
|
||||||
|
ease: 'easeOut',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
'group relative overflow-hidden rounded-full bg-transparent p-[1px] shadow-md',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<MovingBorder duration={5000} rx="5%" ry="5%">
|
||||||
|
<div className="size-20 bg-[radial-gradient(var(--blue-500)_40%,transparent_60%)] opacity-[0.8] dark:bg-[radial-gradient(var(--pink-500)_40%,transparent_60%)]" />
|
||||||
|
</MovingBorder>
|
||||||
|
</div>
|
||||||
|
<motion.div
|
||||||
|
initial="initial"
|
||||||
|
whileHover="hover"
|
||||||
|
variants={parent}
|
||||||
|
className="relative isolate flex size-20 cursor-pointer items-center justify-center gap-6 rounded-full border-2 border-slate-100 bg-white/10 p-6 text-white antialiased backdrop-blur-xl"
|
||||||
|
>
|
||||||
|
<PlayIcon aria-hidden="true" className="absolute left-6 top-6 size-8" />
|
||||||
|
<motion.div variants={child} className="absolute left-20 top-4 w-48">
|
||||||
|
<p className="text-base font-medium">Watch the video</p>
|
||||||
|
<p className="text-xs">Make your AI work.</p>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
146
nx-dev/ui-ai-landing-page/src/lib/new/while-coding.tsx
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import { ReactElement, useState } from 'react';
|
||||||
|
import { SectionHeading, VideoModal } from '@nx/nx-dev/ui-common';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { FeatureCard, type FeatureCardProps } from './feature-card';
|
||||||
|
|
||||||
|
const features: FeatureCardProps[] = [
|
||||||
|
{
|
||||||
|
isAvailable: true,
|
||||||
|
id: 'deep-project-understanding',
|
||||||
|
title: 'Deep Project Understanding Through MCP',
|
||||||
|
subtitle: '"Context is worth 80 IQ points" - Alan Kay',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p className="flex-auto">
|
||||||
|
Nx connects your AI assistant to its comprehensive workspace knowledge
|
||||||
|
through the Model Context Protocol (MCP), delivering complete insights
|
||||||
|
into project graphs, dependencies, and code ownership.
|
||||||
|
</p>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Link
|
||||||
|
href="/blog/nx-mcp-vscode-copilot"
|
||||||
|
title="How to setup Nx MCP to your LLM"
|
||||||
|
className="text-sm/6 font-semibold"
|
||||||
|
>
|
||||||
|
How to setup Nx MCP to your LLM <span aria-hidden="true">→</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
type: 'video',
|
||||||
|
videoUrl: 'https://youtu.be/RNilYmJJzdk',
|
||||||
|
imageUrl: '/images/ai/nx-copilot-mcp-yt-thumb.avif',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isAvailable: true,
|
||||||
|
id: 'terminal-awareness-in-real-time',
|
||||||
|
title: 'Terminal Awareness in Real-Time',
|
||||||
|
subtitle: 'Your AI sees what you see, when you see it.',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p className="flex-auto">
|
||||||
|
Your AI assistant can access the terminal output on-demand through
|
||||||
|
MCP. When you ask about failing builds or broken tests, it retrieves
|
||||||
|
the relevant error messages and combines them with full codebase
|
||||||
|
context.
|
||||||
|
</p>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Link
|
||||||
|
href="/blog/nx-terminal-integration-ai"
|
||||||
|
title="How to set AI terminal integration"
|
||||||
|
className="text-sm/6 font-semibold"
|
||||||
|
>
|
||||||
|
How to set terminal AI integration with Nx{' '}
|
||||||
|
<span aria-hidden="true">→</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
type: 'video',
|
||||||
|
videoUrl: 'https://youtu.be/Cbc9_W5J6DA',
|
||||||
|
imageUrl: '/images/ai/terminal-llm-comm-thumb.avif',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isAvailable: true,
|
||||||
|
id: 'predictable-workspace-aware-code-generation',
|
||||||
|
title: 'Predictable, Workspace-Aware Code Generation',
|
||||||
|
subtitle:
|
||||||
|
'Combine AI intelligence with consistent generators that follow team standards',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p className="flex-auto">
|
||||||
|
Your AI assistant can trigger code generation using predictable Nx
|
||||||
|
generators, then take it from there to intelligently integrate the
|
||||||
|
result into your existing workspace architecture.
|
||||||
|
</p>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Link
|
||||||
|
href="/blog/nx-generators-ai-integration"
|
||||||
|
title="How to generate code that works with AI"
|
||||||
|
className="text-sm/6 font-semibold"
|
||||||
|
>
|
||||||
|
How to generate code that works with AI{' '}
|
||||||
|
<span aria-hidden="true">→</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
type: 'video',
|
||||||
|
videoUrl: 'https://youtu.be/PXNjedYhZDs',
|
||||||
|
imageUrl: '/images/ai/video-code-gen-and-ai-thumb.avif',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function WhileCoding(): ReactElement {
|
||||||
|
const [openVideoUrl, setOpenVideoUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="while-coding"
|
||||||
|
className="mx-auto max-w-7xl scroll-mt-32 px-6 lg:px-8"
|
||||||
|
>
|
||||||
|
<div className="max-w-2xl">
|
||||||
|
<div className="h-8 w-36 border-t-2 border-blue-500 dark:border-sky-500" />
|
||||||
|
<SectionHeading as="h2" variant="title" id="while-coding-title">
|
||||||
|
While Coding
|
||||||
|
</SectionHeading>
|
||||||
|
</div>
|
||||||
|
<div className="mt-16 sm:mt-20 lg:mt-24">
|
||||||
|
<dl className="grid grid-cols-1 gap-x-8 gap-y-16 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{features.map((feature) =>
|
||||||
|
feature.type === 'video' ? (
|
||||||
|
<FeatureCard
|
||||||
|
key={feature.id}
|
||||||
|
isAvailable={feature.isAvailable}
|
||||||
|
id={feature.id}
|
||||||
|
title={feature.title}
|
||||||
|
subtitle={feature.subtitle}
|
||||||
|
description={feature.description}
|
||||||
|
type={feature.type}
|
||||||
|
imageUrl={feature.imageUrl}
|
||||||
|
videoUrl={feature.videoUrl}
|
||||||
|
onPlayClick={setOpenVideoUrl}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FeatureCard
|
||||||
|
key={feature.id}
|
||||||
|
isAvailable={feature.isAvailable}
|
||||||
|
id={feature.id}
|
||||||
|
title={feature.title}
|
||||||
|
subtitle={feature.subtitle}
|
||||||
|
description={feature.description}
|
||||||
|
type={feature.type}
|
||||||
|
imageUrl={feature.imageUrl}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<VideoModal
|
||||||
|
isOpen={openVideoUrl !== null}
|
||||||
|
onClose={() => setOpenVideoUrl(null)}
|
||||||
|
videoUrl={openVideoUrl || ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
144
nx-dev/ui-ai-landing-page/src/lib/new/while-running-ci.tsx
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import { ReactElement, useState } from 'react';
|
||||||
|
import { SectionHeading, VideoModal } from '@nx/nx-dev/ui-common';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { FeatureCard, type FeatureCardProps } from './feature-card';
|
||||||
|
|
||||||
|
const features: FeatureCardProps[] = [
|
||||||
|
{
|
||||||
|
isAvailable: true,
|
||||||
|
id: 'reliable-tests',
|
||||||
|
title: 'Reliable Tests',
|
||||||
|
subtitle: 'Turn flaky tests from time-wasters into non-issues.',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p className="flex-auto">
|
||||||
|
Nx automatically identifies and fixes flaky tests using AI that
|
||||||
|
analyzes failures with workspace context and generates actual code
|
||||||
|
fixes.
|
||||||
|
</p>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Link
|
||||||
|
href="/ci/features/flaky-tasks"
|
||||||
|
title="How to identify and rerun flaky tasks"
|
||||||
|
className="text-sm/6 font-semibold"
|
||||||
|
>
|
||||||
|
How to identify and rerun flaky tasks{' '}
|
||||||
|
<span aria-hidden="true">→</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
type: 'link',
|
||||||
|
imageUrl: '/images/ai/nx-flaky-tasks-detection-thumb.avif',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isAvailable: true,
|
||||||
|
id: 'self-healing-ci',
|
||||||
|
title: 'Self-Healing CI',
|
||||||
|
subtitle: 'Stop babysitting PRs. AI fixes your CI failures automatically.',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p className="flex-auto">
|
||||||
|
AI agents detect, analyze, and propose fixes for CI failures using
|
||||||
|
your workspace context. Stay focused on features while AI handles the
|
||||||
|
debugging.
|
||||||
|
</p>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Link
|
||||||
|
href="/blog/nx-self-healing-ci"
|
||||||
|
title="Stop babysitting PRs with Self-Healing CI"
|
||||||
|
className="text-sm/6 font-semibold"
|
||||||
|
>
|
||||||
|
Stop babysitting PRs with Self-Healing CI{' '}
|
||||||
|
<span aria-hidden="true">→</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
type: 'video',
|
||||||
|
videoUrl: 'https://youtu.be/JW5Ki3PkRWA',
|
||||||
|
imageUrl: '/images/ai/self-healing-ci-thumb.avif',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isAvailable: true,
|
||||||
|
id: 'autonomous-ci-optimization',
|
||||||
|
title: 'Autonomous CI Optimization',
|
||||||
|
subtitle: 'Let AI manage your CI resources for optimal performance.',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p className="flex-auto">
|
||||||
|
AI-driven resource allocation that learns from your CI patterns to
|
||||||
|
automatically scale agents and optimize build times.
|
||||||
|
</p>
|
||||||
|
<div className="mt-4">
|
||||||
|
<Link
|
||||||
|
href="/ci/features/dynamic-agents"
|
||||||
|
title="How to setup dynamic agents"
|
||||||
|
className="text-sm/6 font-semibold"
|
||||||
|
>
|
||||||
|
How to setup Autonomous CI Optimization{' '}
|
||||||
|
<span aria-hidden="true">→</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
type: 'link',
|
||||||
|
imageUrl: '/images/ai/autonomous-ci-optimization-thumb.avif',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function WhileRunningCi(): ReactElement {
|
||||||
|
const [openVideoUrl, setOpenVideoUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative bg-slate-50 py-32 dark:bg-slate-800">
|
||||||
|
<div
|
||||||
|
id="while-running-ci"
|
||||||
|
className="mx-auto max-w-7xl scroll-mt-32 px-6 lg:px-8"
|
||||||
|
>
|
||||||
|
<div className="max-w-2xl">
|
||||||
|
<div className="h-8 w-36 border-t-2 border-emerald-500" />
|
||||||
|
<SectionHeading as="h2" variant="title" id="while-running-ci-title">
|
||||||
|
While Running CI
|
||||||
|
</SectionHeading>
|
||||||
|
</div>
|
||||||
|
<div className="mt-16 sm:mt-20 lg:mt-24">
|
||||||
|
<dl className="grid grid-cols-1 gap-x-8 gap-y-16 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{features.map((feature) =>
|
||||||
|
feature.type === 'video' ? (
|
||||||
|
<FeatureCard
|
||||||
|
key={feature.id}
|
||||||
|
isAvailable={feature.isAvailable}
|
||||||
|
id={feature.id}
|
||||||
|
title={feature.title}
|
||||||
|
subtitle={feature.subtitle}
|
||||||
|
description={feature.description}
|
||||||
|
type={feature.type}
|
||||||
|
imageUrl={feature.imageUrl}
|
||||||
|
videoUrl={feature.videoUrl}
|
||||||
|
onPlayClick={setOpenVideoUrl}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FeatureCard
|
||||||
|
key={feature.id}
|
||||||
|
isAvailable={feature.isAvailable}
|
||||||
|
id={feature.id}
|
||||||
|
title={feature.title}
|
||||||
|
subtitle={feature.subtitle}
|
||||||
|
description={feature.description}
|
||||||
|
type={feature.type}
|
||||||
|
imageUrl={feature.imageUrl}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<VideoModal
|
||||||
|
isOpen={openVideoUrl !== null}
|
||||||
|
onClose={() => setOpenVideoUrl(null)}
|
||||||
|
videoUrl={openVideoUrl || ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
import { ReactElement, useState } from 'react';
|
||||||
|
import { SectionHeading, VideoModal } from '@nx/nx-dev/ui-common';
|
||||||
|
import { FeatureCard, type FeatureCardProps } from './feature-card';
|
||||||
|
|
||||||
|
const features: FeatureCardProps[] = [
|
||||||
|
{
|
||||||
|
isAvailable: false,
|
||||||
|
id: 'architectural-queries',
|
||||||
|
title: 'Architectural Queries',
|
||||||
|
subtitle: 'Ask questions about your entire system in plain English.',
|
||||||
|
description: (
|
||||||
|
<p className="flex-auto">
|
||||||
|
Query your workspace data naturally: "Extract all failed CI runs from
|
||||||
|
the last month" or "What patterns are causing our tests to fail?" AI
|
||||||
|
analyzes your CI pipeline data, cache performance, and build patterns to
|
||||||
|
identify bottlenecks and optimization opportunities.
|
||||||
|
</p>
|
||||||
|
),
|
||||||
|
type: 'link',
|
||||||
|
imageUrl: '/images/ai/ci-querying-thumb.avif',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isAvailable: false,
|
||||||
|
id: 'cross-repository-intelligence',
|
||||||
|
title: 'Cross-Repository Intelligence',
|
||||||
|
subtitle: 'AI that understands your entire organization.',
|
||||||
|
description: (
|
||||||
|
<p className="flex-auto">
|
||||||
|
Nx Polygraph will extend AI context across multiple repositories,
|
||||||
|
enabling system-wide refactoring and cross-repo analysis.
|
||||||
|
</p>
|
||||||
|
),
|
||||||
|
type: 'link',
|
||||||
|
imageUrl: '/images/ai/cross-repository-intelligence-thumb.avif',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isAvailable: false,
|
||||||
|
id: 'cross-repository-agentic-refactorings',
|
||||||
|
title: 'Cross-Repository Agentic Refactorings',
|
||||||
|
subtitle: 'Modernize your entire organization with AI agents.',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<p className="flex-auto">
|
||||||
|
Use autonomous agents to perform large-scale migrations and tech debt
|
||||||
|
cleanup across all repositories. They understand cross-repo
|
||||||
|
dependencies to execute complex, org-wide changes safely.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
type: 'link',
|
||||||
|
imageUrl: '/images/ai/cross-repository-agentic-refactorings-thumb.avif',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function WhileScalingYourOrganization(): ReactElement {
|
||||||
|
const [openVideoUrl, setOpenVideoUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<div
|
||||||
|
id="while-scaling-your-organization"
|
||||||
|
className="mx-auto max-w-7xl scroll-mt-32 px-6 lg:px-8"
|
||||||
|
>
|
||||||
|
<div className="max-w-2xl">
|
||||||
|
<div className="h-8 w-36 border-t-2 border-fuchsia-500" />
|
||||||
|
<SectionHeading
|
||||||
|
as="h2"
|
||||||
|
variant="title"
|
||||||
|
id="while-scaling-your-organization-title"
|
||||||
|
>
|
||||||
|
While Scaling Your Organization
|
||||||
|
</SectionHeading>
|
||||||
|
</div>
|
||||||
|
<div className="mt-16 sm:mt-20 lg:mt-24">
|
||||||
|
<dl className="grid grid-cols-1 gap-x-8 gap-y-16 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{features.map((feature) =>
|
||||||
|
feature.type === 'video' ? (
|
||||||
|
<FeatureCard
|
||||||
|
key={feature.id}
|
||||||
|
isAvailable={feature.isAvailable}
|
||||||
|
id={feature.id}
|
||||||
|
title={feature.title}
|
||||||
|
subtitle={feature.subtitle}
|
||||||
|
description={feature.description}
|
||||||
|
type={feature.type}
|
||||||
|
imageUrl={feature.imageUrl}
|
||||||
|
videoUrl={feature.videoUrl}
|
||||||
|
onPlayClick={setOpenVideoUrl}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<FeatureCard
|
||||||
|
key={feature.id}
|
||||||
|
isAvailable={feature.isAvailable}
|
||||||
|
id={feature.id}
|
||||||
|
title={feature.title}
|
||||||
|
subtitle={feature.subtitle}
|
||||||
|
description={feature.description}
|
||||||
|
type={feature.type}
|
||||||
|
imageUrl={feature.imageUrl}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<VideoModal
|
||||||
|
isOpen={openVideoUrl !== null}
|
||||||
|
onClose={() => setOpenVideoUrl(null)}
|
||||||
|
videoUrl={openVideoUrl || ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -210,6 +210,14 @@ export function Header({ ctaButtons }: HeaderProps): ReactElement {
|
|||||||
)}
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
<div className="hidden h-6 w-px bg-slate-200 md:block dark:bg-slate-700" />
|
<div className="hidden h-6 w-px bg-slate-200 md:block dark:bg-slate-700" />
|
||||||
|
<Link
|
||||||
|
href="/ai"
|
||||||
|
title="AI"
|
||||||
|
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
||||||
|
prefetch={false}
|
||||||
|
>
|
||||||
|
AI
|
||||||
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/remote-cache"
|
href="/remote-cache"
|
||||||
title="Nx Remote Cache"
|
title="Nx Remote Cache"
|
||||||
@ -253,9 +261,11 @@ export function Header({ ctaButtons }: HeaderProps): ReactElement {
|
|||||||
{/*SECONDARY NAVIGATION*/}
|
{/*SECONDARY NAVIGATION*/}
|
||||||
<div className="flex-shrink-0 text-sm">
|
<div className="flex-shrink-0 text-sm">
|
||||||
<nav className="flex items-center justify-center space-x-1">
|
<nav className="flex items-center justify-center space-x-1">
|
||||||
{buttonsToRender.map((buttonProps, index) => (
|
<div className="hidden xl:block">
|
||||||
<ButtonLink key={index} {...buttonProps} />
|
{buttonsToRender.map((buttonProps, index) => (
|
||||||
))}
|
<ButtonLink key={index} {...buttonProps} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
<a
|
<a
|
||||||
title="Nx is open source, check the code on GitHub"
|
title="Nx is open source, check the code on GitHub"
|
||||||
href="https://github.com/nrwl/nx"
|
href="https://github.com/nrwl/nx"
|
||||||
@ -473,6 +483,22 @@ export function Header({ ctaButtons }: HeaderProps): ReactElement {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Disclosure>
|
</Disclosure>
|
||||||
|
<Link
|
||||||
|
href="/ai"
|
||||||
|
title="AI"
|
||||||
|
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
||||||
|
prefetch={false}
|
||||||
|
>
|
||||||
|
AI
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/remote-cache"
|
||||||
|
title="Nx Remote Cache"
|
||||||
|
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
||||||
|
prefetch={false}
|
||||||
|
>
|
||||||
|
Remote Cache
|
||||||
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/nx-cloud"
|
href="/nx-cloud"
|
||||||
title="Nx Cloud"
|
title="Nx Cloud"
|
||||||
@ -489,14 +515,6 @@ export function Header({ ctaButtons }: HeaderProps): ReactElement {
|
|||||||
>
|
>
|
||||||
Pricing
|
Pricing
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
|
||||||
href="/remote-cache"
|
|
||||||
title="Nx Remote Cache"
|
|
||||||
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
|
||||||
prefetch={false}
|
|
||||||
>
|
|
||||||
Remote Cache
|
|
||||||
</Link>
|
|
||||||
<Disclosure as="div">
|
<Disclosure as="div">
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
// AI
|
// AI
|
||||||
|
export * from './lib/ai/claude';
|
||||||
export * from './lib/ai/cursor';
|
export * from './lib/ai/cursor';
|
||||||
export * from './lib/ai/github-copilot';
|
export * from './lib/ai/github-copilot';
|
||||||
|
export * from './lib/ai/google-gemini';
|
||||||
export * from './lib/ai/intellij-ai';
|
export * from './lib/ai/intellij-ai';
|
||||||
export * from './lib/ai/model-context-protocol';
|
export * from './lib/ai/model-context-protocol';
|
||||||
|
export * from './lib/ai/open-ai';
|
||||||
|
|
||||||
// CI PROVIDERS
|
// CI PROVIDERS
|
||||||
export * from './lib/ci-providers/azure-devops';
|
export * from './lib/ci-providers/azure-devops';
|
||||||
@ -112,6 +115,7 @@ export * from './lib/products';
|
|||||||
|
|
||||||
// SOCIALS
|
// SOCIALS
|
||||||
export * from './lib/socials/discord-icon';
|
export * from './lib/socials/discord-icon';
|
||||||
|
export * from './lib/socials/meta';
|
||||||
export * from './lib/socials/x-icon';
|
export * from './lib/socials/x-icon';
|
||||||
export * from './lib/socials/youtube';
|
export * from './lib/socials/youtube';
|
||||||
|
|
||||||
|
|||||||
17
nx-dev/ui-icons/src/lib/ai/claude.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { FC, SVGProps } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use `#D97757` for a colored version.
|
||||||
|
*/
|
||||||
|
export const ClaudeIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
|
||||||
|
<svg
|
||||||
|
role="img"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<title>Claude</title>
|
||||||
|
<path d="m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
17
nx-dev/ui-icons/src/lib/ai/google-gemini.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { FC, SVGProps } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use `#8E75B2` for a colored version.
|
||||||
|
*/
|
||||||
|
export const GoogleGeminiIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
|
||||||
|
<svg
|
||||||
|
role="img"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<title>Google Gemini</title>
|
||||||
|
<path d="M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
17
nx-dev/ui-icons/src/lib/ai/open-ai.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { FC, SVGProps } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use `#412991` for a colored version.
|
||||||
|
*/
|
||||||
|
export const OpenAiIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
|
||||||
|
<svg
|
||||||
|
role="img"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<title>OpenAI</title>
|
||||||
|
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
17
nx-dev/ui-icons/src/lib/socials/meta.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { FC, SVGProps } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use `#0467DF` for a colored version.
|
||||||
|
*/
|
||||||
|
export const MetaIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
|
||||||
|
<svg
|
||||||
|
role="img"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<title>Meta</title>
|
||||||
|
<path d="M6.915 4.03c-1.968 0-3.683 1.28-4.871 3.113C.704 9.208 0 11.883 0 14.449c0 .706.07 1.369.21 1.973a6.624 6.624 0 0 0 .265.86 5.297 5.297 0 0 0 .371.761c.696 1.159 1.818 1.927 3.593 1.927 1.497 0 2.633-.671 3.965-2.444.76-1.012 1.144-1.626 2.663-4.32l.756-1.339.186-.325c.061.1.121.196.183.3l2.152 3.595c.724 1.21 1.665 2.556 2.47 3.314 1.046.987 1.992 1.22 3.06 1.22 1.075 0 1.876-.355 2.455-.843a3.743 3.743 0 0 0 .81-.973c.542-.939.861-2.127.861-3.745 0-2.72-.681-5.357-2.084-7.45-1.282-1.912-2.957-2.93-4.716-2.93-1.047 0-2.088.467-3.053 1.308-.652.57-1.257 1.29-1.82 2.05-.69-.875-1.335-1.547-1.958-2.056-1.182-.966-2.315-1.303-3.454-1.303zm10.16 2.053c1.147 0 2.188.758 2.992 1.999 1.132 1.748 1.647 4.195 1.647 6.4 0 1.548-.368 2.9-1.839 2.9-.58 0-1.027-.23-1.664-1.004-.496-.601-1.343-1.878-2.832-4.358l-.617-1.028a44.908 44.908 0 0 0-1.255-1.98c.07-.109.141-.224.211-.327 1.12-1.667 2.118-2.602 3.358-2.602zm-10.201.553c1.265 0 2.058.791 2.675 1.446.307.327.737.871 1.234 1.579l-1.02 1.566c-.757 1.163-1.882 3.017-2.837 4.338-1.191 1.649-1.81 1.817-2.486 1.817-.524 0-1.038-.237-1.383-.794-.263-.426-.464-1.13-.464-2.046 0-2.221.63-4.535 1.66-6.088.454-.687.964-1.226 1.533-1.533a2.264 2.264 0 0 1 1.088-.285z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||