返回 CodeWhale
hero.tsx
根目录 / web / components / ratatui / hero.tsx
1 import Link from "next/link";
2 import type { RatatuiCopy } from "@/lib/content/ratatui";
3 import heroEntry from "@/public/ratatui/entries/showcase-work.json";
4 import type { EntryPreview } from "@/lib/ratatui/catalogue";
5 import "./hero.css";
6
7 const HERO_PROFILE = "dark-truecolor";
8 const HERO_SIZE = "native";
9
10 /**
11 * The hub hero: one real library render, painted with the page HTML.
12 *
13 * The SVG is a generated Ratatui buffer, sanitized by the exporter and
14 * validated by catalogue.test.ts (no scripts, handlers or external refs),
15 * so it is safe to inline. Inlining keeps the hero visible with no client
16 * fetch and no layout shift: the viewBox fixes the aspect ratio.
17 */
18 export function RatatuiHero({ locale, copy }: { locale: string; copy: RatatuiCopy }) {
19 const preview = (heroEntry as EntryPreview).previews[HERO_PROFILE][HERO_SIZE];
20 const narrow = (heroEntry as EntryPreview).previews[HERO_PROFILE]["40"];
21 const width = (heroEntry as EntryPreview).width;
22 const height = (heroEntry as EntryPreview).height;
23 return (
24 <section className="rat-hero" aria-label={copy.heroLabel}>
25 <figure className="rat-hero-frame">
26 <div
27 className="rat-hero-terminal rat-hero-wide"
28 role="img"
29 aria-label={copy.heroAlt}
30 tabIndex={0}
31 dir="ltr"
32 dangerouslySetInnerHTML={{ __html: preview }}
33 />
34 <div
35 className="rat-hero-terminal rat-hero-narrow"
36 role="img"
37 aria-label={copy.heroAlt}
38 tabIndex={0}
39 dir="ltr"
40 dangerouslySetInnerHTML={{ __html: narrow }}
41 />
42 <figcaption className="rat-hero-caption">
43 <span className="rat-hero-wide">{copy.heroCaption.replace("{width}", String(width)).replace("{height}", String(height))}</span>
44 <span className="rat-hero-narrow">{copy.heroCaption.replace("{width}", "40").replace("{height}", String(height))}</span>
45 <span className="rat-hero-links">
46 <a href="#ratatui-explorer">{copy.heroOpenExplorer} ↓</a>
47 <Link href={`/${locale}/ratatui/showcase-work`}>{copy.heroOpenComponent} →</Link>
48 </span>
49 </figcaption>
50 </figure>
51 </section>
52 );
53 }
54
54 lines Plain Text