Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions showcase/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Link from "next/link";
import { groupedTutorials, tutorials } from "@/content/tutorials";
import { LANGUAGE_LABEL } from "@/lib/types";
import { languageTags } from "@/lib/types";
import { repoUrl } from "@/lib/repo";
import HeroHandshake from "@/components/HeroHandshake";
import { ArrowRight, GitHub, Spark } from "@/components/icons";
Expand Down Expand Up @@ -85,7 +85,11 @@ export default function HomePage() {
<div className="idx-desc">{t.tagline}</div>
</div>
<div className="idx-meta">
<span className="t-lang">{LANGUAGE_LABEL[t.language]}</span>
{languageTags(t.language).map((tag) => (
<span key={tag} className="t-lang">
{tag}
</span>
))}
<span className={`tier ${t.tier}`}>{t.tier}</span>
<a
className="idx-gh"
Expand Down
38 changes: 36 additions & 2 deletions showcase/app/t/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Link from "next/link";
import { notFound } from "next/navigation";
import type { Metadata } from "next";
import { tutorials, getTutorial } from "@/content/tutorials";
import { PROTOCOL_LABEL, LANGUAGE_LABEL } from "@/lib/types";
import { PROTOCOL_LABEL, languageTags } from "@/lib/types";
import { repoUrl, repoFileUrl } from "@/lib/repo";
import LiveRunPanel from "@/components/LiveRunPanel";
import RecapPanel from "@/components/RecapPanel";
Expand Down Expand Up @@ -31,6 +31,8 @@ export default async function TutorialPage({ params }: { params: Promise<{ slug:
if (!t) notFound();

const isRecap = t.tier === "recap";
// discover tutorials keep the demo (§4) and the video in a separate §5
const discoverVideo = t.run.kind === "discover" ? t.run.video : undefined;
Comment thread
aaitor marked this conversation as resolved.

return (
<div className="container">
Expand All @@ -46,7 +48,11 @@ export default async function TutorialPage({ params }: { params: Promise<{ slug:

<div className="tut-taglist">
<span className="t-proto">{PROTOCOL_LABEL[t.protocol]}</span>
<span className="t-lang">{LANGUAGE_LABEL[t.language]}</span>
{languageTags(t.language).map((tag) => (
<span key={tag} className="t-lang">
{tag}
</span>
))}
<span className={`tier ${t.tier}`}>
{t.tier === "recap"
? "recap · watch it run"
Expand Down Expand Up @@ -168,6 +174,34 @@ export default async function TutorialPage({ params }: { params: Promise<{ slug:
<RecapPanel run={t.run} />
)}
</section>

{/* 5 — Catalog video (discover tutorials only) */}
{discoverVideo ? (
<section className="block wide" id="video">
<div className="h2">
<span className="num">5</span> Catalog video
</div>
<div className="videowrap">
<video controls preload="metadata" playsInline>
<source src={discoverVideo.src} type="video/mp4" />
{(discoverVideo.subtitles ?? []).map((s) => (
<track
key={s.srcLang}
kind="subtitles"
src={s.src}
srcLang={s.srcLang}
label={s.label}
default={s.default}
/>
))}
Your browser can&apos;t play this video — see {discoverVideo.caption}.
</video>
<div className="vidcap">
{discoverVideo.caption} · {discoverVideo.duration}
</div>
</div>
</section>
) : null}
</div>
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion showcase/components/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useEffect, useState } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import type { Language, Tier, Protocol } from "@/lib/types";
import { languageTags } from "@/lib/types";
import {
Menu,
Chevron,
Expand Down Expand Up @@ -107,7 +108,7 @@ export default function AppShell({
<span className={`tdot ${it.tier}`} aria-hidden="true" />
<span className="label">{it.title}</span>
{it.featured ? <Spark className="spark" size={12} /> : null}
<span className="lang">{it.language}</span>
<span className="lang">{languageTags(it.language).join(" · ")}</span>
</Link>
);
})}
Expand Down
16 changes: 0 additions & 16 deletions showcase/components/DiscoverPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,22 +137,6 @@ export default function DiscoverPanel({ run }: { run: DiscoverRun }) {

return (
<div className="dsc">
{/* guided-tour video — watch the concept, then run it live below */}
{run.video ? (
<div className="videowrap" style={{ marginBottom: "22px" }}>
<video controls preload="metadata" playsInline>
<source src={run.video.src} type="video/mp4" />
{(run.video.subtitles ?? []).map((s) => (
<track key={s.srcLang} kind="subtitles" src={s.src} srcLang={s.srcLang} label={s.label} default={s.default} />
))}
Your browser can&apos;t play this video — see {run.video.caption}.
</video>
<div className="vidcap">
{run.video.caption} · {run.video.duration}
</div>
</div>
) : null}

{/* readout */}
<div className="dsc-readout">
<Stat value={stats?.total} label="agents" />
Expand Down
2 changes: 1 addition & 1 deletion showcase/content/tutorials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ weather.ensureCity # prompt — guide the LLM to request weather`,
tagline:
"The half that comes before payment: the same live catalog of pay-per-use AI agents, read three ways — by eye, by an agent, and by a crawler. Public, unauthenticated, and free to read.",
protocol: "catalog",
language: "ts",
language: "agnostic",
tier: "discover",
repoPath: "catalog/discover-the-catalog/",
learn: {
Expand Down
15 changes: 14 additions & 1 deletion showcase/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// One shape for all tutorials → uniform pages out of wildly different READMEs.

export type Protocol = "x402" | "mcp" | "langchain" | "catalog";
export type Language = "ts" | "py" | "autonomous";
export type Language = "ts" | "py" | "autonomous" | "agnostic";
export type Tier = "live" | "recap" | "discover";

export interface CodeSample {
Expand Down Expand Up @@ -141,4 +141,17 @@ export const LANGUAGE_LABEL: Record<Language, string> = {
ts: "ts",
py: "py",
autonomous: "autonomous",
// Required (Record is total) but unreachable for chips: languageTags() returns
// LANGUAGE_TAGS["agnostic"] instead. The rendered text lives there, not here.
agnostic: "agnostic",
};

// A tutorial's language, as the chip(s) to show. Most languages are one chip; a
// language-agnostic tutorial is labelled by the interfaces it teaches instead — its own
// chip each (e.g. "mcp", "api"). Single source of truth for every render site.
const LANGUAGE_TAGS: Partial<Record<Language, string[]>> = {
agnostic: ["mcp", "api"],
Comment thread
aaitor marked this conversation as resolved.
};
export function languageTags(language: Language): string[] {
return LANGUAGE_TAGS[language] ?? [LANGUAGE_LABEL[language]];
}
Loading