返回 CodeWhale
page.tsx
根目录 / web / app / [locale] / docs / constitution / page.tsx
1 import { Fragment } from "react";
2 import Link from "next/link";
3 import { getDocsConstitution, splitTokens } from "@/lib/i18n/dictionaries";
4 import { buildPageMetadata } from "@/lib/page-meta";
5
6 /**
7 * Paths and commands the overview sentence typesets as inline `<code>`.
8 * `docs/VOICE.md` keeps these code-owned, so the dictionaries carry a
9 * `{token}` for each one instead of the literal.
10 */
11 const CODE_SPANS: Record<string, string> = {
12 constitutionCommand: "/constitution",
13 homeConfig: "$CODEWHALE_HOME/constitution.json",
14 repoConfig: ".codewhale/constitution.json",
15 };
16
17 /**
18 * The en/zh badge on each principle row. Both halves render in every locale —
19 * it is a fixed bilingual glyph rather than copy — so it stays here and the
20 * dictionaries key their rows by the same names.
21 */
22 const PRINCIPLE_BADGES: Record<string, [string, string]> = {
23 userGlobal: ["User-global", "用户全局"],
24 repoLocal: ["Repo-local", "仓库本地"],
25 runtime: ["Runtime", "运行时"],
26 };
27
28 const CONFIG_DOCS_HREF =
29 "https://github.com/Hmbown/CodeWhale/blob/main/docs/CONFIGURATION.md#constitution-project-instructions-and-repo-authority";
30
31 export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
32 const { locale } = await params;
33 const t = getDocsConstitution(locale);
34 return buildPageMetadata({
35 path: "/docs/constitution",
36 locale,
37 title: t.metaTitle,
38 description: t.metaDescription,
39 });
40 }
41
42 export default async function ConstitutionPage({ params }: { params: Promise<{ locale: string }> }) {
43 const { locale } = await params;
44 const t = getDocsConstitution(locale);
45
46 return (
47 <section className="space-y-10">
48 <section id="overview" className="scroll-mt-32">
49 <h1 className="font-display text-3xl mb-1">
50 {t.overviewTitle}{" "}
51 <span className="font-cjk text-indigo text-2xl ml-2">{t.overviewTitleAside}</span>
52 </h1>
53 <p className={`${t.bodyClassName} mt-3`}>
54 {splitTokens(t.overviewLead).map((part, i) =>
55 "token" in part ? (
56 <code key={`${i}-${part.token}`} className="inline">
57 {CODE_SPANS[part.token] ?? `{${part.token}}`}
58 </code>
59 ) : (
60 <Fragment key={`${i}-text`}>{part.text}</Fragment>
61 ),
62 )}
63 </p>
64 <div className="hairline-t hairline-b mt-6 grid md:grid-cols-3 col-rule">
65 {t.principles.map(([key, detail]) => {
66 const [name, cn] = PRINCIPLE_BADGES[key] ?? [key, ""];
67 return (
68 <div key={key} className="p-5">
69 <div className="font-display text-lg text-indigo mb-1">
70 {name} <span className="font-cjk text-sm ml-1.5">{cn}</span>
71 </div>
72 <p className={`text-sm ${t.bodyClassName}`}>{detail}</p>
73 </div>
74 );
75 })}
76 </div>
77 <p className={`mt-4 text-sm ${t.bodyClassName}`}>
78 {splitTokens(t.authorityNote).map((part, i) =>
79 "token" in part ? (
80 <Link key={`${i}-${part.token}`} href={CONFIG_DOCS_HREF} className="body-link">
81 {t.configDocsLabel}
82 </Link>
83 ) : (
84 <Fragment key={`${i}-text`}>{part.text}</Fragment>
85 ),
86 )}
87 </p>
88 </section>
89 <section id="source" className="hairline-t pt-8">
90 <p className="text-sm text-ink-mute">{t.sourceNote}</p>
91 </section>
92 </section>
93 );
94 }
95
95 lines Plain Text