返回 CodeWhale
page.tsx
根目录 / web / app / [locale] / docs / subagents / page.tsx
1 import { Fragment } from "react";
2 import { getDocsSubagents, splitTokens } from "@/lib/i18n/dictionaries";
3 import { buildPageMetadata } from "@/lib/page-meta";
4
5 /** Role identifiers, in the order the page lists them. Not copy. */
6 const ROLE_ORDER = [
7 "worker",
8 "scout",
9 "planner",
10 "reviewer",
11 "builder",
12 "verifier",
13 "consultant",
14 "custom",
15 ] as const;
16
17 const CODE_SPANS: Record<string, string> = {
18 agentTool: "agent",
19 forkContext: "fork_context: true",
20 worktreeFlag: "worktree: true",
21 branchPattern: "codex/agent-<name>-<id>",
22 worktreeDir: ".codewhale-worktrees/",
23 writeAuthority: "write_authority",
24 writeRoots: "write_roots",
25 exactFiles: "exact_files",
26 coordinationContracts: "coordination_contracts",
27 };
28
29 function withCodeSpans(template: string) {
30 return splitTokens(template).map((part, i) =>
31 "token" in part ? (
32 <code key={`${i}-${part.token}`} className="inline">
33 {CODE_SPANS[part.token] ?? `{${part.token}}`}
34 </code>
35 ) : (
36 <Fragment key={`${i}-text`}>{part.text}</Fragment>
37 ),
38 );
39 }
40
41 export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
42 const { locale } = await params;
43 const t = getDocsSubagents(locale);
44 return buildPageMetadata({
45 path: "/docs/subagents",
46 locale,
47 title: t.metaTitle,
48 description: t.metaDescription,
49 });
50 }
51
52 export default async function SubagentsPage({ params }: { params: Promise<{ locale: string }> }) {
53 const { locale } = await params;
54 const t = getDocsSubagents(locale);
55 const roleDetails = new Map(t.roles);
56
57 return (
58 <section className="space-y-10">
59 <section id="overview" className="scroll-mt-32">
60 <h1 className="font-display text-3xl mb-1">{t.overviewTitle}</h1>
61 <p className={`${t.bodyClassName} mt-3`}>{t.overviewLead}</p>
62 <p className={`${t.bodyClassName} mt-3`}>{t.overviewFleetNote}</p>
63 <div className="hairline-t mt-6">
64 {ROLE_ORDER.map((role) => (
65 <section key={role} className="py-4 hairline-b">
66 <h3 className="font-display text-lg">{role}</h3>
67 <p className={`${t.bodyClassName} mt-1 text-sm`}>{roleDetails.get(role)}</p>
68 </section>
69 ))}
70 </div>
71 </section>
72
73 <section id="fork" className="scroll-mt-32">
74 <h2 className="font-display text-2xl mb-1">{t.forkTitle}</h2>
75 <p className={`${t.bodyClassName} mt-3`}>{withCodeSpans(t.forkLead)}</p>
76 </section>
77
78 <section id="worktree" className="scroll-mt-32">
79 <h2 className="font-display text-2xl mb-1">{t.worktreeTitle}</h2>
80 <p className={`${t.bodyClassName} mt-3`}>{withCodeSpans(t.worktreeLead)}</p>
81 </section>
82
83 <section id="capacity" className="scroll-mt-32">
84 <h2 className="font-display text-2xl mb-1">{t.capacityTitle}</h2>
85 <p className={`${t.bodyClassName} mt-3`}>{t.capacityLead}</p>
86 </section>
87
88 <section id="source" className="hairline-t pt-8">
89 <p className="text-sm text-ink-mute">{t.sourceNote}</p>
90 </section>
91 </section>
92 );
93 }
94
94 lines Plain Text