返回 CodeWhale
page.tsx
根目录 / web / app / [locale] / docs / web / page.tsx
1 import { Fragment } from "react";
2 import { getDocsWeb, splitTokens } from "@/lib/i18n/dictionaries";
3 import { buildPageMetadata } from "@/lib/page-meta";
4
5 /**
6 * The commands, flags and addresses the overview and local paragraphs typeset
7 * as inline `<code>`. `docs/VOICE.md` keeps commands, flags and paths
8 * code-owned, so the dictionaries carry `{token}`s rather than the literals.
9 */
10 const CODE_SPANS: Record<string, string> = {
11 webCommand: "codewhale web",
12 loopbackHost: "127.0.0.1",
13 defaultUrl: "http://127.0.0.1:7878",
14 portExample: "codewhale web --port 8788",
15 portFlag: "--port",
16 hostFlag: "--host",
17 mobileCommand: "codewhale app-server --mobile",
18 httpFlag: "--http",
19 };
20
21 function withCodeSpans(template: string) {
22 return splitTokens(template).map((part, i) =>
23 "token" in part ? (
24 <code key={`${i}-${part.token}`} className="inline">
25 {CODE_SPANS[part.token] ?? `{${part.token}}`}
26 </code>
27 ) : (
28 <Fragment key={`${i}-text`}>{part.text}</Fragment>
29 ),
30 );
31 }
32
33 export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
34 const { locale } = await params;
35 const t = getDocsWeb(locale);
36 return buildPageMetadata({
37 path: "/docs/web",
38 locale,
39 title: t.metaTitle,
40 description: t.metaDescription,
41 });
42 }
43
44 export default async function WebClientPage({ params }: { params: Promise<{ locale: string }> }) {
45 const { locale } = await params;
46 const t = getDocsWeb(locale);
47
48 return (
49 <section className="space-y-10">
50 <section id="overview" className="scroll-mt-32">
51 <h1 className="font-display text-3xl mb-1">{t.overviewTitle}</h1>
52 <p className={`${t.bodyClassName} mt-3`}>{withCodeSpans(t.overviewLead)}</p>
53 <p className={`${t.bodyClassName} mt-3`}>{t.overviewBody}</p>
54 </section>
55
56 <section id="auth" className="scroll-mt-32">
57 <h2 className="font-display text-2xl mb-1">{t.authTitle}</h2>
58 <p className={`${t.bodyClassName} mt-3`}>{t.authLead}</p>
59 </section>
60
61 <section id="local" className="scroll-mt-32">
62 <h2 className="font-display text-2xl mb-1">{t.localTitle}</h2>
63 <p className={`${t.bodyClassName} mt-3`}>{withCodeSpans(t.localLead)}</p>
64 </section>
65
66 <section id="remote-control" className="scroll-mt-32">
67 <h2 className="font-display text-2xl mb-1">{t.remoteTitle}</h2>
68 <p className={`${t.bodyClassName} mt-3`}>{t.remoteLead}</p>
69 <p className={`${t.bodyClassName} mt-3`}>{t.remoteBody}</p>
70 </section>
71
72 <section id="troubleshooting" className="scroll-mt-32">
73 <h2 className="font-display text-2xl mb-1">{t.troubleshootingTitle}</h2>
74 <p className={`${t.bodyClassName} mt-3`}>{t.troubleshootingLead}</p>
75 </section>
76
77 <section id="source" className="hairline-t pt-8">
78 <p className="text-sm text-ink-mute">{t.sourceNote}</p>
79 </section>
80 </section>
81 );
82 }
83
83 lines Plain Text