返回 CodeWhale
page.tsx
根目录 / web / app / [locale] / plugins / page.tsx
1 import Link from "next/link";
2 import { Icon } from "@/components/icon";
3 import { PageHeader, Section } from "@/components/page-header";
4 import { buildPageMetadata } from "@/lib/page-meta";
5 import { PLUGINS_COPY, PLUGIN_COMMANDS } from "@/lib/content/plugins";
6 import type { LocalizedText } from "@/lib/content/vocabulary";
7 import { pickText } from "@/lib/i18n/dictionaries";
8
9 export const revalidate = 300;
10
11 const MARKETPLACE_REPO = "https://github.com/codewhale-hq/codewhale-plugin-marketplace";
12 const AUTHORING_DOC = "https://github.com/codewhale-hq/CodeWhale/blob/main/docs/PLUGIN_AUTHORING.md";
13
14 export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
15 const { locale } = await params;
16 return buildPageMetadata({ path: "/plugins", locale,
17 title: pickText(PLUGINS_COPY.metaTitle, locale),
18 description: pickText(PLUGINS_COPY.metaDescription, locale) });
19 }
20
21 /**
22 * /plugins — what a plugin adds, the marketplace catalogue, where plugins
23 * come from, how trust works, and the commands. Nothing runs until the
24 * reader reviews, trusts and enables it; the page says so where it matters.
25 */
26 export default async function PluginsPage({ params }: { params: Promise<{ locale: string }> }) {
27 const { locale } = await params;
28 const t = (copy: LocalizedText) => pickText(copy, locale);
29 const authoringLink = (
30 <Link href={AUTHORING_DOC} className="section-link">
31 {t(PLUGINS_COPY.docsCta)}
32 <Icon name="external" className="icon" />
33 </Link>
34 );
35
36 return (
37 <>
38 <PageHeader
39 kicker={t(PLUGINS_COPY.kicker)}
40 title={t(PLUGINS_COPY.title)}
41 lede={t(PLUGINS_COPY.lead)}
42 pose="connect"
43 actions={
44 <>
45 <Link href={MARKETPLACE_REPO} className="btn btn-primary btn-lg">{t(PLUGINS_COPY.marketplaceCta)}</Link>
46 <Link href={AUTHORING_DOC} className="btn btn-secondary btn-lg">{t(PLUGINS_COPY.docsCta)}</Link>
47 </>
48 }
49 />
50
51 <div className="page-body">
52 <Section id="cu-title" title={t(PLUGINS_COPY.cuTitle)} scope={t(PLUGINS_COPY.cuLead)}>
53 <div className="split">
54 <div className="stack">
55 <pre tabIndex={0} className="code-block">{t(PLUGINS_COPY.cuInstall)}</pre>
56 <p className="section-scope">{t(PLUGINS_COPY.cuToolsNote)}</p>
57 <Link href={`/${locale}/computer-use`} className="section-link">
58 Computer Use
59 <Icon name="arrow-right" className="icon icon-flip" />
60 </Link>
61 </div>
62 <dl className="def-rows">
63 {PLUGINS_COPY.cuFeatures.map((feature) => (
64 <div key={t(feature.title)} className="def-row">
65 <dt>{t(feature.title)}</dt>
66 <dd>{t(feature.detail)}</dd>
67 </div>
68 ))}
69 </dl>
70 </div>
71 </Section>
72
73 <Section id="catalog-title" title={t(PLUGINS_COPY.catalogTitle)} scope={t(PLUGINS_COPY.catalogLead)}>
74 <pre tabIndex={0} className="code-block mb-4">{t(PLUGINS_COPY.catalogBrowse)}</pre>
75 <ul className="dir-list dir-list-card" role="list">
76 {PLUGINS_COPY.catalogEntries.map((entry) => (
77 <li key={entry.reference}>
78 <Link href={MARKETPLACE_REPO} className="dir-row">
79 <span className="dir-mark" aria-hidden="true"><Icon name="plug" /></span>
80 <span className="dir-text">
81 <span className="dir-title">{t(entry.title)}</span>
82 <span className="dir-purpose">{t(entry.detail)}</span>
83 <span className="dir-meta">{entry.reference}</span>
84 </span>
85 <span className="dir-action" aria-hidden="true"><Icon name="external" /></span>
86 </Link>
87 </li>
88 ))}
89 </ul>
90 <p className="section-scope mt-4">{t(PLUGINS_COPY.catalogNote)}</p>
91 </Section>
92
93 <Section id="sources-title" title={t(PLUGINS_COPY.sourcesTitle)} scope={t(PLUGINS_COPY.sourcesLead)}>
94 <ul className="dir-list dir-list-card" role="list">
95 {PLUGINS_COPY.sources.map((source) => (
96 <li key={source.reference}>
97 <div className="dir-row">
98 <span className="dir-mark" aria-hidden="true"><Icon name="package" /></span>
99 <span className="dir-text">
100 <span className="dir-title">{t(source.title)}</span>
101 <span className="dir-purpose">{t(source.detail)}</span>
102 <span className="dir-meta">{source.reference}</span>
103 </span>
104 </div>
105 </li>
106 ))}
107 </ul>
108 </Section>
109
110 <Section id="trust-title" title={t(PLUGINS_COPY.trustTitle)} scope={t(PLUGINS_COPY.trustLead)}>
111 <ol className="steps">
112 {PLUGINS_COPY.trustSteps.map((step, index) => (
113 <li key={t(step.title)}>
114 <span className="gs-step-index" aria-hidden="true">{index + 1}</span>
115 <h3>{t(step.title)}</h3>
116 <p>{t(step.detail)}</p>
117 </li>
118 ))}
119 </ol>
120 <p className="section-scope mt-4">{t(PLUGINS_COPY.trustNote)}</p>
121 </Section>
122
123 <Section id="commands-title" title={t(PLUGINS_COPY.commandsTitle)} scope={t(PLUGINS_COPY.commandsLead)} link={authoringLink}>
124 <pre tabIndex={0} className="code-block">{PLUGIN_COMMANDS.join("\n")}</pre>
125 </Section>
126 </div>
127 </>
128 );
129 }
130
130 lines Plain Text