265 lines
12 KiB
TypeScript
265 lines
12 KiB
TypeScript
import { SparklesIcon } from '@heroicons/react/24/outline';
|
|
import { SectionHeading } from '@nx/nx-dev/ui-common';
|
|
import {
|
|
animate,
|
|
motion,
|
|
MotionValue,
|
|
useAnimation,
|
|
useMotionValue,
|
|
useTransform,
|
|
} from 'framer-motion';
|
|
import Link from 'next/link';
|
|
import { useEffect, useRef } from 'react';
|
|
import { useInView } from 'react-intersection-observer';
|
|
|
|
function Counter({
|
|
from = 0,
|
|
to = 10,
|
|
round = 0,
|
|
progress,
|
|
}: {
|
|
from: number;
|
|
to: number;
|
|
round: number;
|
|
progress: MotionValue<number>;
|
|
}): JSX.Element {
|
|
const ref = useRef<any>();
|
|
const value = useTransform(progress, [0, 1000], [from, to], {
|
|
clamp: false,
|
|
});
|
|
|
|
const { format: formatNumber } = new Intl.NumberFormat('en-US', {
|
|
minimumFractionDigits: round,
|
|
maximumFractionDigits: round,
|
|
});
|
|
|
|
useEffect(() => {
|
|
return value.onChange((v) => {
|
|
if (ref !== undefined && ref.current !== undefined)
|
|
ref.current.firstChild.data = formatNumber(
|
|
round === 0 ? Math.round(v) : Number(v.toFixed(round))
|
|
);
|
|
});
|
|
}, [formatNumber, round, value]);
|
|
|
|
return <span ref={ref}>{formatNumber(value.get())}</span>;
|
|
}
|
|
|
|
export function NxWithCi(): JSX.Element {
|
|
const progress: MotionValue<number> = useMotionValue(0);
|
|
const controls = useAnimation();
|
|
const [ref, inView] = useInView({ triggerOnce: true });
|
|
|
|
useEffect(() => {
|
|
if (!inView) return;
|
|
controls.start('visible');
|
|
animate(progress, 1000, {
|
|
type: 'spring',
|
|
damping: 100,
|
|
});
|
|
}, [controls, inView, progress]);
|
|
|
|
return (
|
|
<article id="nx-is-fast" className="relative pt-12 sm:pt-28">
|
|
<motion.div
|
|
ref={ref}
|
|
animate={controls}
|
|
className="mx-auto max-w-7xl px-4 pb-12 sm:grid sm:grid-cols-2 sm:gap-8 sm:px-6 lg:px-8 lg:pb-16"
|
|
>
|
|
<div>
|
|
<header>
|
|
<SectionHeading as="h1" variant="title" id="nx-is-fast">
|
|
From 90 to 10 minutes
|
|
</SectionHeading>
|
|
<SectionHeading
|
|
as="p"
|
|
variant="display"
|
|
id="nx-ci"
|
|
className="mt-4"
|
|
>
|
|
Effortless, Fast CI
|
|
</SectionHeading>
|
|
</header>
|
|
<div className="mt-8 flex gap-16 font-normal">
|
|
<p className="max-w-xl text-lg text-slate-700 dark:text-slate-400">
|
|
Nx comes with the building blocks to not only scale your monorepo
|
|
locally and provide great DX while developing, but also to address
|
|
one of the major pain points:{' '}
|
|
<span className="font-bold">fast, maintainable CI.</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-end">
|
|
<div className="w-full">
|
|
<div className="flex">
|
|
<div className="shrink-0 w-28 border-r border-slate-200 dark:border-slate-500 py-3 text-slate-700 dark:text-slate-400">
|
|
CI without Nx
|
|
</div>
|
|
<div className="flex-grow py-1">
|
|
<motion.div
|
|
initial={{ display: 'none', width: 0 }}
|
|
variants={{ visible: { display: 'flex', width: '90%' } }}
|
|
transition={{ type: 'tween', duration: 1 }}
|
|
className="flex-grow items-center justify-end text-slate-600 dark:text-slate-400 bg-slate-200 dark:bg-slate-700 py-2 px-4 rounded-r-lg"
|
|
>
|
|
<Counter from={0} to={90} round={0} progress={progress} />
|
|
<span className="ml-1">minutes</span>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
<div className="flex">
|
|
<div className="shrink-0 font-medium w-28 border-r border-slate-200 dark:border-slate-500 py-3 text-slate-700 dark:text-slate-400">
|
|
CI with Nx
|
|
</div>
|
|
<div className="flex-grow flex gap-4 py-1 font-medium">
|
|
<motion.div
|
|
initial={{ display: 'none', width: 0 }}
|
|
variants={{ visible: { display: 'flex', width: '10%' } }}
|
|
transition={{ type: 'tween', duration: 1 }}
|
|
className="bg-blue-500 dark:bg-sky-500 rounded-r-lg"
|
|
/>
|
|
<div className="flex items-center text-slate-700 dark:text-slate-400">
|
|
<Counter from={0} to={10} round={0} progress={progress} />
|
|
<span className="ml-1">minutes</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex w-full justify-end">
|
|
<a
|
|
href="https://www.youtube.com/watch?v=KPCMg_Dn0Eo"
|
|
target="_blank"
|
|
className="hover:underline text-sm font-medium"
|
|
title="Find out how to reduce CI time with Nx"
|
|
rel="noreferrer"
|
|
>
|
|
Find out how
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-8 lg:pb-16">
|
|
<dl className="grid grid-cols-1 gap-x-8 gap-y-16 sm:grid-cols-3">
|
|
<div className="relative">
|
|
<dt className="text-base font-semibold leading-7 text-slate-900 dark:text-slate-100">
|
|
<div className="relative group">
|
|
<div className="absolute -inset-1 bg-gradient-to-r from-cyan-500 to-blue-500 rounded-lg blur-sm opacity-25 group-hover:opacity-90 transition duration-1000 group-hover:duration-200"></div>
|
|
<div className="relative flex gap-4 items-center rounded-lg border border-slate-200 bg-white p-4 text-lg shadow-sm transition focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 dark:border-slate-800/40 dark:bg-slate-800">
|
|
<NxCacheIcon className="h-8 w-8" aria-hidden="true" />
|
|
<Link
|
|
href="/ci/features/remote-cache"
|
|
title="Discover Nx Replay"
|
|
>
|
|
<span className="absolute inset-0"></span>Nx Replay
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</dt>
|
|
<dd className="mt-4 text-base leading-7 text-slate-700 dark:text-slate-400">
|
|
Built-in local and remote caching to speed up your tasks and save
|
|
you time and money.
|
|
</dd>
|
|
</div>
|
|
<div className="relative">
|
|
<dt className="text-base font-semibold leading-7 text-slate-900 dark:text-slate-100">
|
|
<div className="relative group">
|
|
<div className="absolute -inset-1 bg-gradient-to-r from-emerald-500 to-green-500 rounded-lg blur-sm opacity-25 group-hover:opacity-90 transition duration-1000 group-hover:duration-200"></div>
|
|
<div className="relative flex gap-4 items-center rounded-lg border border-slate-200 bg-white p-4 text-lg shadow-sm transition focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 dark:border-slate-800/40 dark:bg-slate-800">
|
|
<NxAgentsIcon className="h-8 w-8" aria-hidden="true" />
|
|
<a href="/ci/features/nx-agents" title="Discover Nx Agents">
|
|
<span className="absolute inset-0"></span>Nx Agents
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</dt>
|
|
<dd className="mt-4 text-base leading-7 text-slate-700 dark:text-slate-400">
|
|
A single line to enable distributed computation, across multiple
|
|
machines. Fully managed agents, dynamically allocated based on PR
|
|
size.
|
|
</dd>
|
|
</div>
|
|
<div className="relative">
|
|
<dt className="text-base font-semibold leading-7 text-slate-900 dark:text-slate-100">
|
|
<div className="relative group opacity-70">
|
|
<div className="absolute -inset-1 bg-slate-500 rounded-lg blur-sm opacity-25"></div>
|
|
<div className="relative flex gap-4 items-center rounded-lg border border-slate-200 bg-white p-4 text-lg shadow-sm transition focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 dark:border-slate-800/40 dark:bg-slate-800">
|
|
<SparklesIcon
|
|
className="block h-8 w-8 sm:hidden md:block"
|
|
aria-hidden="true"
|
|
/>
|
|
{/*<a href="/ci/features/tusky" title="Discover Tusky">*/}
|
|
<span className="absolute inset-0"></span>Tusky
|
|
{/*</a>*/}
|
|
<div className="flex-grow" />
|
|
<span className="dark:bg-slate-400/10 dark:text-slate-400 dark:ring-slate-400/20 inline-flex items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-slate-600 ring-1 ring-inset ring-slate-500/10">
|
|
Coming soon
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</dt>
|
|
<dd className="mt-4 text-base leading-7 text-slate-700 dark:text-slate-400">
|
|
A powerful Artificial Intelligence equipped with context of your
|
|
workspace, commit history, and historical build timing data.
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
</article>
|
|
);
|
|
}
|
|
|
|
export const NxAgentsIcon = (props: any) => (
|
|
<svg
|
|
role="img"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
stroke="currentColor"
|
|
fill="none"
|
|
{...props}
|
|
>
|
|
<path
|
|
strokeLinejoin="round"
|
|
d="M21 12.5h-4m4 0a1 1 0 1 0 2 0 1 1 0 0 0-2 0Zm-2 8h-5v-3m5 3a1 1 0 1 0 2 0 1 1 0 0 0-2 0Zm-16-8h4m-4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm2 8h5v-3m-5 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm0-17h5v4m-5-4a1 1 0 1 0-2 0 1 1 0 0 0 2 0Zm14 0h-5v4m5-4a1 1 0 1 1 2 0 1 1 0 0 1-2 0Zm-3 14H8a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1Zm-5-8h2l-.667 1.875L14 11.346 11.333 15.5l.334-2.77H10l1-3.23Z"
|
|
/>
|
|
</svg>
|
|
);
|
|
|
|
export const NxCacheIcon = (props: any) => (
|
|
<svg
|
|
role="img"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
stroke="currentColor"
|
|
fill="none"
|
|
{...props}
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
d="m8.625 2.621-.014.004c-.317.09-.622.22-.907.387m4.796-.965-.014-.002a3.958 3.958 0 0 0-.986.002m3.875.574.014.004c.317.09.622.22.907.387m-.92 18.367.013-.004c.317-.09.622-.22.907-.387m-4.796.965.014.002c.328.04.659.04.986-.002m-3.875-.574-.014-.004a3.96 3.96 0 0 1-.907-.387M21.38 8.625l-.004-.014a3.96 3.96 0 0 0-.387-.907m.965 4.796.002-.014c.04-.328.04-.659-.002-.986m-.574 3.875-.004.014a3.96 3.96 0 0 1-.387.907m-18.367-.92.004.013c.09.317.22.622.387.907M2.047 11.5l-.002.014c-.04.328-.04.659.002.986m.574-3.875.004-.014c.09-.317.22-.622.387-.907M7.5 10.516h9m-9 0a1.5 1.5 0 0 1 0-3h9a1.5 1.5 0 0 1 0 3m-9 0a1.5 1.5 0 0 0 0 3m9-3a1.5 1.5 0 0 1 0 3m-6.412-4.5h.01m-1.883 0h.01m-.725 4.5h9m-9 0a1.5 1.5 0 0 0 0 3h9a1.5 1.5 0 0 0 0-3m-6.412-1.5h.01m-1.883 0h.01m1.863 3h.01m-1.883 0h.01M3 4.516a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0Zm15 0a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0Zm-15 15a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0Zm15 0a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0Z"
|
|
/>
|
|
</svg>
|
|
);
|
|
|
|
export const NxWorkflowsIcon = (props: any) => (
|
|
<svg
|
|
role="img"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
stroke="currentColor"
|
|
fill="none"
|
|
{...props}
|
|
>
|
|
<g clipPath="url(#a)">
|
|
<path
|
|
strokeLinecap="round"
|
|
d="M13.315 10h7.37m-7.37-6h7.37m-5.95-1v2m-1.42 2h7.37m-1.159-1v2M17 9v2M4.678 3.5h.75m2.29 0h.75m-3.79 7h.75m2.29 0h.75m1.605-5.395v.75m0 2.29v.75m-7-3.79v.75m0 2.29v.75M3.698 21h6a1 1 0 0 0 1-1v-.763a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1V20a1 1 0 0 0 1 1Zm0-4.605h6a1 1 0 0 0 1-1v-.763a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v.763a1 1 0 0 0 1 1Zm14.723.976a1.42 1.42 0 1 1-2.842 0 1.42 1.42 0 0 1 2.842 0ZM2.499.499H21.5a2 2 0 0 1 2 2V21.5a2 2 0 0 1-2 2h-19a2 2 0 0 1-2-2v-19a2 2 0 0 1 2-2ZM20.11 16.087l.599.126a.6.6 0 0 1 .476.587v1.133a.6.6 0 0 1-.477.588l-.594.123a.6.6 0 0 0-.448.773l.19.583a.6.6 0 0 1-.27.706l-.983.566a.6.6 0 0 1-.747-.12l-.408-.456a.6.6 0 0 0-.896.001l-.403.454a.6.6 0 0 1-.748.12l-.98-.565a.6.6 0 0 1-.27-.704l.19-.586a.6.6 0 0 0-.449-.773l-.594-.124a.6.6 0 0 1-.478-.588l.001-1.13a.6.6 0 0 1 .476-.587l.6-.127a.6.6 0 0 0 .446-.775l-.19-.576a.6.6 0 0 1 .27-.707l.978-.567a.6.6 0 0 1 .747.119l.408.454a.6.6 0 0 0 .894 0l.405-.454a.6.6 0 0 1 .747-.12l.982.567a.6.6 0 0 1 .27.707l-.19.578a.6.6 0 0 0 .446.774Z"
|
|
/>
|
|
</g>
|
|
<defs>
|
|
<clipPath id="a">
|
|
<path fill="transparent" d="M0 0h24v24H0z" />
|
|
</clipPath>
|
|
</defs>
|
|
</svg>
|
|
);
|