A dark editor window that tells a full story in three seconds. A change request is typed into an inline bar, two removed lines turn red while their replacements slide open in green, the change is accepted, and the test panel reruns until every row has a green check. Syntax colours come from a small built-in highlighter. On phones the test panel moves under the code and long lines scroll inside the code area only.
import { useEffect, useRef, useState, type ReactNode } from "react";
import { motion, useInView, useReducedMotion } from "framer-motion";
/** What gets typed into the inline edit bar. */
const REQUEST = "Count quantity and round to cents";
/** The file. "del" rows are removed by the edit, "add" rows are what replaces them. */
const code: { text: string; kind?: "del" | "add" }[] = [
{ text: "type Item = { price: number; qty: number };" },
{ text: "" },
{ text: "export function cartTotal(items: Item[]) {" },
{ text: " let total = 0;" },
{ text: " for (const item of items) {" },
{ text: " total += item.price;", kind: "del" },
{ text: " total += item.price * item.qty;", kind: "add" },
{ text: " }" },
{ text: " return total;", kind: "del" },
{ text: " return Math.round(total * 100) / 100;", kind: "add" },
{ text: "}" },
];
/** `failed` is what the test printed before the edit. */
const tests = [
{ name: "adds up a single item", ms: 2 },
{ name: "multiplies price by quantity", ms: 3, failed: "expected 59.97, got 19.99" },
{ name: "rounds to two decimals", ms: 1, failed: "expected 0.3, got 0.30000000000000004" },
{ name: "returns 0 for an empty cart", ms: 1 },
];
// Timeline in milliseconds.
const TYPE_MS = 22;
const DIFF_AT = 900; // the suggested change appears
const APPLY_AT = 1750; // it is accepted
const TEST_AT = 2050; // the test run starts
const TEST_GAP = 200; // one test finishes every gap
const END = TEST_AT + TEST_GAP * tests.length + 150;
const HOLD = 4200;
const TICK = 40;
const ROW = 26; // line height in pixels
function useClock(playing: boolean, run: number, still: boolean | null) {
const [now, setNow] = useState(END);
const t = useRef(END);
useEffect(() => {
if (still) return;
t.current = 0;
setNow(0);
}, [run, still]);
useEffect(() => {
if (!playing || still) return;
const id = window.setInterval(() => {
if (document.hidden) return;
t.current += TICK;
if (t.current > END + HOLD) t.current = 0;
setNow(Math.min(t.current, END));
}, TICK);
return () => window.clearInterval(id);
}, [playing, still]);
return now;
}
/** A very small highlighter: comments, strings, keywords, numbers, type names and calls. */
const TOKEN = /(\/\/.*$)|("[^"]*"|'[^']*')|\b(type|export|function|let|const|for|of|return|if|number)\b|\b(\d+(?:\.\d+)?)\b|\b([A-Z][A-Za-z]*)\b|([A-Za-z_]\w*)(?=\()/g;
const COLOURS = ["", "text-slate-500 italic", "text-amber-200", "text-violet-300", "text-orange-300", "text-teal-300", "text-sky-300"];
function paint(text: string): ReactNode[] {
const out: ReactNode[] = [];
let last = 0;
for (const m of text.matchAll(TOKEN)) {
const at = m.index ?? 0;
if (at > last) out.push(text.slice(last, at));
const group = [1, 2, 3, 4, 5, 6].find((g) => m[g] !== undefined) ?? 0;
out.push(<span key={at} className={COLOURS[group]}>{m[0]}</span>);
last = at + m[0].length;
}
if (last < text.length) out.push(text.slice(last));
return out;
}
function Check({ className = "" }: { className?: string }) {
return (
<svg viewBox="0 0 20 20" className={className} fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="m4.5 10.5 3.5 3.5 7.5-8" />
</svg>
);
}
export default function CodeEditorWindowWithAiDiffAndPassingTests() {
const reduce = useReducedMotion();
const ref = useRef<HTMLDivElement>(null);
const inView = useInView(ref, { amount: 0.25 });
const [run, setRun] = useState(0);
const now = useClock(inView, run, reduce);
// Entrance animations only play once the page is live, so the server-rendered HTML shows the finished state.
const [live, setLive] = useState(false);
useEffect(() => setLive(true), []);
const enter = live && !reduce;
const typed = Math.min(REQUEST.length, Math.floor(now / TYPE_MS));
const diffing = now >= DIFF_AT && now < APPLY_AT;
const applied = now >= APPLY_AT;
const testing = now >= TEST_AT && now < END;
const done = now >= END;
const passed = now < TEST_AT ? 0 : Math.min(tests.length, Math.floor((now - TEST_AT) / TEST_GAP));
let n = 0;
const rows = code.map((row) => {
if (row.kind !== "add") n += 1;
return { ...row, n };
});
return (
<section className="relative overflow-hidden bg-[#0a0c1b] px-4 py-16 sm:px-6 sm:py-20">
<style>{`
@keyframes code-editor-ai-diff-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
@keyframes code-editor-ai-diff-spin { to { transform: rotate(360deg); } }
@keyframes code-editor-ai-diff-ping { 0% { box-shadow: 0 0 0 0 rgba(167,139,250,0.55); } 100% { box-shadow: 0 0 0 10px rgba(167,139,250,0); } }
@keyframes code-editor-ai-diff-sweep { from { transform: translateX(-120%); } to { transform: translateX(320%); } }
.code-editor-ai-diff-caret { animation: code-editor-ai-diff-caret 0.9s step-end infinite; }
.code-editor-ai-diff-spin { animation: code-editor-ai-diff-spin 0.7s linear infinite; }
.code-editor-ai-diff-ping { animation: code-editor-ai-diff-ping 0.9s ease-out infinite; }
.code-editor-ai-diff-sweep { animation: code-editor-ai-diff-sweep 1.1s ease-out both; }
@media (prefers-reduced-motion: reduce) {
.code-editor-ai-diff-caret, .code-editor-ai-diff-spin, .code-editor-ai-diff-ping, .code-editor-ai-diff-sweep { animation: none; }
.code-editor-ai-diff-sweep { display: none; }
}
`}</style>
<div aria-hidden="true" className="pointer-events-none absolute left-1/2 top-40 h-[520px] w-[820px] -translate-x-1/2 rounded-full bg-violet-600/25 blur-[120px]" />
<div aria-hidden="true" className={`pointer-events-none absolute left-1/2 top-[55%] h-[360px] w-[560px] rounded-full bg-emerald-500/20 blur-[110px] transition-opacity duration-1000 ${done ? "opacity-100" : "opacity-0"}`} />
<div className="relative mx-auto max-w-5xl">
<div className="mx-auto max-w-2xl text-center">
<p className="font-mono text-xs font-medium uppercase tracking-[0.2em] text-violet-300">Inline edits</p>
<h2 className="mt-4 text-4xl font-semibold tracking-tight text-white sm:text-5xl">Ask for the fix. Watch the tests go green.</h2>
<p className="mt-4 text-lg leading-relaxed text-slate-300">Describe the change in plain words. You get a diff to read before anything is written, and the test run right beside it.</p>
</div>
<div ref={ref} className="mt-12 overflow-hidden rounded-2xl bg-[#11142a] shadow-2xl shadow-black/60 ring-1 ring-white/10">
{/* Title bar and tabs */}
<div className="flex items-center gap-3 border-b border-white/10 bg-[#0d1024] pl-4 pr-2">
<div className="flex gap-1.5" aria-hidden="true">
<span className="h-2.5 w-2.5 rounded-full bg-rose-400/80" />
<span className="h-2.5 w-2.5 rounded-full bg-amber-300/80" />
<span className="h-2.5 w-2.5 rounded-full bg-emerald-400/80" />
</div>
<div className="flex min-w-0 flex-1 font-mono text-xs">
<span className="flex items-center gap-2 border-b-2 border-violet-400 bg-[#11142a] px-3 py-3 text-white">
cart.ts
<span className={`h-1.5 w-1.5 rounded-full transition-colors duration-300 ${diffing ? "bg-violet-400" : "bg-transparent"}`} aria-hidden="true" />
</span>
<span className="hidden px-3 py-3 text-slate-400 sm:block">cart.test.ts</span>
</div>
<button type="button" onClick={() => setRun((r) => r + 1)} className="h-10 rounded-md px-3 font-mono text-xs text-slate-300 transition-colors hover:bg-white/10 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-violet-400">
Replay
</button>
</div>
<div className="grid lg:grid-cols-[1.45fr_1fr]">
{/* Code pane */}
<div className="min-w-0 p-3 sm:p-4">
<div className="flex items-center gap-2.5 rounded-lg bg-white/[0.04] py-1.5 pl-3 pr-1.5 ring-1 ring-violet-400/30">
<svg viewBox="0 0 20 20" className="h-4 w-4 shrink-0 text-violet-300" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="m7 6-4 4 4 4M13 6l4 4-4 4" />
</svg>
<p className="min-w-0 flex-1 truncate text-sm text-white" aria-label={`Edit request: ${REQUEST}`}>
<span aria-hidden="true">
{REQUEST.slice(0, typed)}
{now < DIFF_AT && <span className="code-editor-ai-diff-caret ml-px inline-block h-[1.05em] w-0.5 translate-y-[0.18em] bg-violet-300" />}
</span>
</p>
<motion.span
initial={false}
animate={{ scale: now >= APPLY_AT - 120 && now < APPLY_AT + 80 ? 0.92 : 1 }}
transition={{ type: "spring", stiffness: 600, damping: 20 }}
className={`inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md px-2.5 font-mono text-[11px] font-semibold transition-colors duration-300 ${applied ? "bg-emerald-400/15 text-emerald-300" : diffing ? "code-editor-ai-diff-ping bg-violet-500 text-white" : "bg-white/5 text-slate-400"}`}
>
{applied ? (<><Check className="h-3.5 w-3.5" />Applied</>) : diffing ? "Accept" : (<><span className="code-editor-ai-diff-spin h-3 w-3 rounded-full border-2 border-slate-500 border-t-slate-200" aria-hidden="true" />Reading</>)}
</motion.span>
</div>
<pre className="mt-3 overflow-x-auto font-mono text-[11px] sm:text-[13px]" aria-label="Source of cart.ts after the edit" style={{ lineHeight: `${ROW}px`, minHeight: ROW * code.length }}>
<code className="block min-w-max">
{rows.map((row, i) => {
const visible = row.kind === "del" ? !applied : row.kind === "add" ? now >= DIFF_AT : true;
const marked = diffing && row.kind;
const fresh = row.kind === "add" && applied && now < APPLY_AT + 900;
return (
<motion.span
key={i}
initial={false}
animate={{ height: visible ? ROW : 0, opacity: visible ? 1 : 0 }}
transition={{ duration: 0.32, ease: [0.22, 1, 0.36, 1] }}
aria-hidden={row.kind === "del" ? true : undefined}
className={`flex overflow-hidden rounded-sm transition-colors duration-500 ${marked === "del" ? "bg-rose-500/15" : marked === "add" ? "bg-emerald-400/15" : fresh ? "bg-emerald-400/[0.07]" : "bg-transparent"}`}
>
<span className="w-8 shrink-0 select-none pr-2 text-right text-slate-600" aria-hidden="true">{marked === "add" ? "" : row.n}</span>
<span className={`w-4 shrink-0 select-none ${marked === "del" ? "text-rose-400" : "text-emerald-400"}`} aria-hidden="true">{marked === "del" ? "-" : marked === "add" ? "+" : ""}</span>
<span className="whitespace-pre pr-4 text-slate-200">
{row.text.match(/^\s*/)?.[0]}
<span className={marked === "del" ? "line-through decoration-rose-400/70" : ""}>{paint(row.text.trimStart())}</span>
</span>
</motion.span>
);
})}
</code>
</pre>
</div>
{/* Test pane */}
<div className="relative overflow-hidden border-t border-white/10 bg-[#0d1024] lg:border-l lg:border-t-0">
{done && <div key={run} aria-hidden="true" className="code-editor-ai-diff-sweep pointer-events-none absolute inset-y-0 left-0 w-1/3 bg-gradient-to-r from-transparent via-emerald-300/15 to-transparent" />}
<div className="flex items-center justify-between gap-3 px-4 py-3">
<p className="font-mono text-[11px] font-semibold uppercase tracking-wider text-slate-400">Tests</p>
<p className={`font-mono text-xs font-semibold transition-colors duration-300 ${done ? "text-emerald-300" : testing ? "text-slate-300" : "text-rose-300"}`} aria-live="polite">
{done ? `${tests.length} passed` : testing ? `Running ${passed} of ${tests.length}` : "2 failed, 2 passed"}
</p>
</div>
<ul className="space-y-1 px-2 pb-3">
{tests.map((t, i) => {
const state = now < TEST_AT ? (t.failed ? "fail" : "pass") : i < passed ? "pass" : "run";
return (
<li key={t.name} className={`flex min-h-[44px] items-center gap-2.5 rounded-lg px-2 py-1.5 transition-colors duration-300 ${state === "fail" ? "bg-rose-500/10" : "bg-transparent"}`}>
<span className="grid h-5 w-5 shrink-0 place-items-center">
{state === "run" ? (
<span className="code-editor-ai-diff-spin h-3.5 w-3.5 rounded-full border-2 border-slate-600 border-t-violet-300" aria-hidden="true" />
) : state === "fail" ? (
<svg viewBox="0 0 20 20" className="h-4 w-4 text-rose-400" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" aria-hidden="true"><path d="m5.5 5.5 9 9m0-9-9 9" /></svg>
) : (
<motion.span key={`${run}-${now < TEST_AT ? "a" : "b"}`} initial={enter ? { scale: 0.3, opacity: 0 } : false} animate={{ scale: 1, opacity: 1 }} transition={{ type: "spring", stiffness: 520, damping: 18 }} className="grid h-5 w-5 place-items-center rounded-full bg-emerald-400/15 text-emerald-300">
<Check className="h-3 w-3" />
</motion.span>
)}
</span>
<span className="min-w-0 flex-1">
<span className="block truncate text-[13px] text-slate-100">{t.name}</span>
{state === "fail" && <span className="block truncate font-mono text-[11px] text-rose-300">{t.failed}</span>}
</span>
<span className="shrink-0 font-mono text-[11px] text-slate-400">{state === "pass" ? `${t.ms} ms` : state === "run" ? "" : "failed"}</span>
</li>
);
})}
</ul>
<div className={`mx-3 mb-3 flex items-center gap-2 rounded-lg px-3 py-2.5 font-mono text-xs transition-colors duration-500 ${done ? "bg-emerald-400/15 text-emerald-200" : "bg-white/[0.04] text-slate-400"}`}>
<span className={`h-2 w-2 rounded-full transition-colors duration-500 ${done ? "bg-emerald-400" : testing ? "bg-violet-400" : "bg-rose-400"}`} aria-hidden="true" />
{done ? "All green in 0.42s. Safe to commit." : testing ? "Running cart.test.ts" : "Last run 2 minutes ago"}
</div>
</div>
</div>
{/* Status bar */}
<div className="flex items-center gap-4 border-t border-white/10 bg-[#0d1024] px-4 py-2 font-mono text-[11px] text-slate-400">
<span className="text-violet-300">main</span>
<span className="hidden sm:inline">TypeScript</span>
<span className="hidden sm:inline">Ln 6, Col 36</span>
<span className="ml-auto">{applied ? "2 lines changed" : diffing ? "Review the suggested change" : "No pending changes"}</span>
</div>
</div>
</div>
</section>
);
}
Install with one command: npx shadcn@latest add https://we.inc/r/code-editor-window-with-ai-diff-and-passing-tests.json
Every line is a fixed height. Removed and added lines animate between that height and zero, so the surrounding lines glide instead of snapping. When the change is accepted the red lines close while the green lines simply lose their tint.
A single regular expression named `TOKEN` matches comments, strings, keywords, numbers, capitalised type names and function calls. The `paint` function wraps each match in a span with a colour class. It is about fifteen lines and has no dependency, which is enough for a short snippet.
No. It is a scripted demo. The lines, the diff and the test results are fixed data at the top of the file, and nothing is executed or sent anywhere.
Yes. Any number of `del` and `add` rows can sit in the `code` array. They all open at `DIFF_AT` and all apply at `APPLY_AT`. Keep each added row directly after the row it replaces so the line numbers stay correct.
No. The `pre` has `overflow-x-auto`, so a long line scrolls inside the code area while the window and the page stay the width of the screen.
Related: Animated Terminal Window with Typed Commands, File Tree with Collapsible Folders, AI Site Builder Demo with Self-Assembling Wireframe.
More app and game ui or all free UI components. Start for free to have a full site built around it.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.