Skip to content

Commit 7042f22

Browse files
bloveclaude
andauthored
feat(playground): add StreamingProof section linking to streaming demo (#16)
The pitch landing now surfaces the streaming wedge between the inspection grid and the receipts band. Section copy frames the wedge ("stream tokens, render 500 rows in 30s") and shows three H13-derived metrics — frame p95 9ms, 0 long tasks, 1k updates/sec sustained. CTA links to /streaming/, which is where the standalone apps/streaming-demo app is expected to deploy. Numbers come from the H13 hypothesis report (S5 streaming-updates, hypothesis scale, 3 repeats) merged in PR #15. They are not yet wired to a live source; treating them as a placeholder is consistent with how ReceiptsBand currently handles its own metrics. Order of sections in the pitch landing is now: PitchHero → PitchGrid → StreamingProof → ReceiptsBand Tests: 3 new for the section + 1 updated app.test.tsx ordering check. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 322fade commit 7042f22

4 files changed

Lines changed: 119 additions & 12 deletions

File tree

‎apps/playground/src/__tests__/app.test.tsx‎

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,35 @@ describe("<App />", () => {
4040
expect(screen.getByRole("main")).toBeInTheDocument();
4141
});
4242

43-
test("renders the three section components in order inside <main>", () => {
43+
test("renders sections in order inside <main>: hero → grid → streaming → receipts", () => {
4444
render(<App />);
4545
const main = screen.getByRole("main");
4646

47-
// hero h1 appears before receipts h2; grid section has id="grid"
4847
const h1 = within(main).getByRole("heading", { level: 1 });
49-
const h2 = within(main).getByRole("heading", { level: 2 });
5048
const grid = main.querySelector("#grid");
49+
const streamingHeading = within(main).getByRole("heading", {
50+
name: /stream.*tokens/i,
51+
});
52+
const receiptsHeading = within(main).getByRole("heading", {
53+
name: /receipts.*not claims/i,
54+
});
5155

5256
expect(h1).toBeInTheDocument();
53-
expect(h2).toBeInTheDocument();
5457
expect(grid).toBeInTheDocument();
58+
expect(streamingHeading).toBeInTheDocument();
59+
expect(receiptsHeading).toBeInTheDocument();
5560

56-
// DOM order: h1 (hero) → grid section → h2 (receipts)
57-
const h1Pos = Array.from(main.querySelectorAll("*")).indexOf(h1);
58-
const gridPos = Array.from(main.querySelectorAll("*")).indexOf(
59-
grid as Element,
60-
);
61-
const h2Pos = Array.from(main.querySelectorAll("*")).indexOf(h2);
61+
const all = Array.from(main.querySelectorAll("*"));
62+
const positions = {
63+
h1: all.indexOf(h1),
64+
grid: all.indexOf(grid as Element),
65+
streaming: all.indexOf(streamingHeading),
66+
receipts: all.indexOf(receiptsHeading),
67+
};
6268

63-
expect(h1Pos).toBeLessThan(gridPos);
64-
expect(gridPos).toBeLessThan(h2Pos);
69+
expect(positions.h1).toBeLessThan(positions.grid);
70+
expect(positions.grid).toBeLessThan(positions.streaming);
71+
expect(positions.streaming).toBeLessThan(positions.receipts);
6572
});
6673

6774
test("no InspectionDemo-era status card or sidebar is rendered", () => {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import "@testing-library/jest-dom/vitest";
2+
import { cleanup, render, screen } from "@testing-library/react";
3+
import { afterEach, describe, expect, test } from "vitest";
4+
5+
import { StreamingProof } from "../streaming-proof";
6+
7+
afterEach(() => {
8+
cleanup();
9+
});
10+
11+
describe("<StreamingProof />", () => {
12+
test("renders the wedge label and section heading", () => {
13+
render(<StreamingProof />);
14+
expect(screen.getByText(/wedge.*streaming/i)).toBeInTheDocument();
15+
const heading = screen.getByRole("heading", { level: 2 });
16+
expect(heading).toHaveTextContent(/stream/i);
17+
expect(heading).toHaveTextContent(/tokens/i);
18+
expect(heading).toHaveTextContent(/500 rows/i);
19+
});
20+
21+
test("renders the three H13 metrics in order", () => {
22+
render(<StreamingProof />);
23+
const metrics = screen.getAllByRole("listitem");
24+
expect(metrics).toHaveLength(3);
25+
26+
expect(metrics[0]).toHaveTextContent(/9ms/i);
27+
expect(metrics[0]).toHaveTextContent(/frame p95/i);
28+
29+
expect(metrics[1]).toHaveTextContent(/^0/);
30+
expect(metrics[1]).toHaveTextContent(/long tasks/i);
31+
32+
expect(metrics[2]).toHaveTextContent(/1k\/s/i);
33+
expect(metrics[2]).toHaveTextContent(/updates sustained/i);
34+
});
35+
36+
test("renders a link to the streaming demo", () => {
37+
render(<StreamingProof />);
38+
const link = screen.getByRole("link", { name: /watch it stream/i });
39+
expect(link).toHaveAttribute("href", "/streaming/");
40+
});
41+
});

‎apps/playground/src/app.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Footer, Nav } from "@pretable/ui";
33
import { PitchGrid } from "./pitch-grid";
44
import { PitchHero } from "./pitch-hero";
55
import { ReceiptsBand } from "./receipts-band";
6+
import { StreamingProof } from "./streaming-proof";
67

78
const APP_VERSION = import.meta.env.VITE_APP_VERSION as string;
89

@@ -13,6 +14,7 @@ export function App() {
1314
<main>
1415
<PitchHero />
1516
<PitchGrid />
17+
<StreamingProof />
1618
<ReceiptsBand />
1719
</main>
1820
{/* TODO(direction-D): wire ciStatus to a real source (status/runsets/*.json
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Receipt } from "@pretable/ui";
2+
3+
interface StreamingMetric {
4+
value: string;
5+
caption: string;
6+
}
7+
8+
// Real numbers from H13 (S5 streaming-updates, hypothesis scale, 3 repeats,
9+
// merged in PR #15). The hypothesis report at status/runsets/ is the source
10+
// of truth; these get refreshed when bench:matrix re-runs.
11+
const METRICS: readonly StreamingMetric[] = [
12+
{ value: "9ms", caption: "frame p95 (≤ 16ms budget)" },
13+
{ value: "0", caption: "long tasks" },
14+
{ value: "1k/s", caption: "updates sustained" },
15+
];
16+
17+
export function StreamingProof() {
18+
return (
19+
<section className="bg-cream text-ink border-b border-cream-rule px-7 py-[52px] md:px-10">
20+
<div className="mx-auto max-w-[1240px]">
21+
<p className="font-mono text-[11px] uppercase tracking-[0.08em] text-amber-ink">
22+
Wedge · streaming
23+
</p>
24+
<h2 className="mt-3 font-display text-[36px] leading-[1.08] tracking-[-0.02em] md:text-[44px]">
25+
Stream <em className="italic text-amber-ink">tokens</em>.
26+
<br className="hidden md:block" /> Render <Receipt>500 rows</Receipt>{" "}
27+
in 30s.
28+
</h2>
29+
<p className="mt-5 max-w-[760px] font-display text-[18px] leading-[1.44] text-ink-dim">
30+
An OpenAI Responses stream, parsed live, batched on{" "}
31+
<Receipt>requestAnimationFrame</Receipt>, applied to the same grid
32+
that proved it scrolls. The frame budget stays under one 60Hz frame.
33+
</p>
34+
<ul className="mt-8 grid grid-cols-3 gap-6">
35+
{METRICS.map((metric) => (
36+
<li key={metric.caption} className="border-t border-ink pt-3">
37+
<div className="font-display text-[36px] leading-[1] tracking-[-0.02em] md:text-[44px]">
38+
{metric.value}
39+
</div>
40+
<div className="mt-1 font-mono text-[12px] text-ink-dim">
41+
{metric.caption}
42+
</div>
43+
</li>
44+
))}
45+
</ul>
46+
<div className="mt-8">
47+
<a
48+
href="/streaming/"
49+
className="inline-flex items-center gap-2 rounded-[2px] bg-ink px-[18px] py-[10px] text-[13px] text-cream-hi hover:bg-ink/90 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-ink focus-visible:ring-offset-2 focus-visible:ring-offset-cream"
50+
>
51+
Watch it stream →
52+
</a>
53+
</div>
54+
</div>
55+
</section>
56+
);
57+
}

0 commit comments

Comments
 (0)