hd-garden 0.3.12 → 0.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import Link from "next/link";
2
- import { ArrowSquareOut, GithubLogo, LockKey } from "@phosphor-icons/react/ssr";
2
+ import { ArrowSquareOut, GithubLogo } from "@phosphor-icons/react/ssr";
3
3
  import { AdminDashboard } from "../components/AdminDashboard";
4
4
  import { getAdminUser, isGitHubLoginAvailable } from "../lib/admin-auth";
5
5
  import { columns, posts, thoughts } from "../lib/content";
@@ -27,17 +27,15 @@ export default async function AdminPage({
27
27
  return (
28
28
  <main className="admin-access-page">
29
29
  <div className="admin-access-card">
30
- <LockKey size={28} weight="duotone" />
31
- <p className="eyebrow">CONTENT MANAGEMENT</p>
32
- <h1>内容后台独立运行。</h1>
33
- <p>公开博客由 GitHub Pages 静态托管;发布和分发请前往独立后台。</p>
30
+ <h1>后台在另一个地址。</h1>
31
+ <p>这个页面是公开博客。写随想、上传和转发,去后台。</p>
34
32
  <div className="admin-access-actions">
35
33
  {adminUrl ? (
36
34
  <a className="admin-github-login" href={adminUrl}>
37
- 打开内容后台 <ArrowSquareOut size={15} />
35
+ 打开后台 <ArrowSquareOut size={15} />
38
36
  </a>
39
37
  ) : (
40
- <p>尚未配置 ADMIN_URL。</p>
38
+ <p>还没有填写后台地址。</p>
41
39
  )}
42
40
  <Link href="/">返回公开博客</Link>
43
41
  </div>
@@ -52,22 +50,20 @@ export default async function AdminPage({
52
50
  return (
53
51
  <main className="admin-access-page">
54
52
  <div className="admin-access-card">
55
- <LockKey size={28} weight="duotone" />
56
- <p className="eyebrow">{siteConfig.name.toUpperCase()} / ADMIN</p>
57
- <h1>用 GitHub 身份进入内容管理后台。</h1>
53
+ <h1>登录后台</h1>
58
54
  <p>
59
55
  {query.auth === "denied"
60
- ? `登录账号 @${query.account ?? "未知"} 没有后台权限。`
56
+ ? `@${query.account ?? "这个账号"} 不在允许名单里。`
61
57
  : oauthReady
62
- ? "登录后会检查你的 GitHub 用户名是否在允许名单中。"
58
+ ? `用 GitHub 登录。只有 ${siteConfig.name} 允许的账号能进来。`
63
59
  : process.env.GARDEN_DEV === "1"
64
- ? "本机还没有 GitHub 登录。在终端运行 gh auth login 后,重新启动本地服务。"
65
- : "GitHub OAuth 尚未配置;请先设置后台所需的环境变量。"}
60
+ ? "这台机器还没登录 GitHub。在终端运行 gh auth login,然后重新启动。"
61
+ : "还没配好 GitHub 登录。先填后台的环境变量。"}
66
62
  </p>
67
63
  <div className="admin-access-actions">
68
64
  {oauthReady && (
69
65
  <Link className="admin-github-login" href="/api/auth/github">
70
- <GithubLogo size={18} weight="fill" /> 使用 GitHub 登录
66
+ <GithubLogo size={18} weight="fill" /> 用 GitHub 登录
71
67
  </Link>
72
68
  )}
73
69
  <Link href="/">
@@ -3,7 +3,7 @@ import { UiStudio } from "../../components/UiStudio";
3
3
  import { modelConfig, recipeSources, remainingDrafts } from "../../lib/ui-model";
4
4
 
5
5
  export const dynamic = process.env.STATIC_EXPORT === "1" ? "force-static" : "force-dynamic";
6
- export const metadata = { title: "组件工作台", robots: { index: false, follow: false } };
6
+ export const metadata = { title: "写作台", robots: { index: false, follow: false } };
7
7
 
8
8
  export default async function UiStudioPage() {
9
9
  if (isStaticExport()) return <StaticAdminNotice />;
@@ -8,9 +8,6 @@ import {
8
8
  ArrowUpRight,
9
9
  CheckCircle,
10
10
  Copy,
11
- DotsThree,
12
- FileText,
13
- LinkSimple,
14
11
  PaperPlaneTilt,
15
12
  Plus,
16
13
  Trash,
@@ -55,7 +52,7 @@ function MediaFields({
55
52
  <div className="admin-field-heading">
56
53
  <div>
57
54
  <span>附件</span>
58
- <small>支持一条随想中混合多个图片、音频、视频与链接。</small>
55
+ <small>一条随想里可以混放图片、音频、视频和链接。</small>
59
56
  </div>
60
57
  <button type="button" className="admin-quiet-button" onClick={() => onChange([...attachments, emptyAttachment()])}>
61
58
  <Plus size={14} /> 添加附件
@@ -153,20 +150,17 @@ function IntelligentUiCard({ ui }: { ui: UiOverview }) {
153
150
  return (
154
151
  <section className="admin-panel admin-ui-card">
155
152
  <div className="admin-panel-heading">
156
- <div>
157
- <span>Intelligent UI</span>
158
- <h2>交互块</h2>
159
- </div>
160
- <Link href="/admin/ui">打开工作台</Link>
153
+ <h2>文章里的交互</h2>
154
+ <Link href="/admin/ui">打开写作台</Link>
161
155
  </div>
162
156
  <p className="ui-studio-meta">
163
- {ui.modelName ? ui.modelName : "模型未连接"}
157
+ {ui.modelName ? ui.modelName : "还没接上模型"}
164
158
  {" · "}
165
159
  {counts
166
160
  ? <Link href="/admin/ui/blocks">{`未编译 ${counts.uncompiled} · 过期 ${counts.stale} · 有错 ${counts.error} · 正常 ${counts.ok}`}</Link>
167
- : failed ? "交互块读取失败" : "正在统计交互块…"}
161
+ : failed ? "没读到文章里的块" : "正在查看文章…"}
168
162
  {" · "}
169
- <Link href="/admin/ui/catalog">组件清单</Link>
163
+ <Link href="/admin/ui/catalog">看组件</Link>
170
164
  </p>
171
165
  </section>
172
166
  );
@@ -367,7 +361,7 @@ export function AdminDashboard({
367
361
  return (
368
362
  <main className="admin-shell">
369
363
  <header className="admin-topbar">
370
- <Link className="admin-brand" href="/admin">HEDON <span>STUDIO</span></Link>
364
+ <Link className="admin-brand" href="/admin">Garden<span>后台</span></Link>
371
365
  <div>
372
366
  <span>{userName}</span>
373
367
  <form action="/api/auth/logout" method="post">
@@ -380,16 +374,15 @@ export function AdminDashboard({
380
374
  </header>
381
375
 
382
376
  <section className="admin-hero">
383
- <div>
384
- <p className="eyebrow">CONTENT OPERATIONS</p>
385
- <h1>写作留在本地,发布与分发留在这里。</h1>
386
- <p>博文和专栏仍由 Typora 与 Git 管理;这里负责随想、发布状态和跨平台分发。</p>
387
- </div>
388
- <dl className="admin-metrics">
389
- <div><dt>公开随想</dt><dd>{publishedCount + staticThoughtCount}</dd></div>
390
- <div><dt>本地博文</dt><dd>{catalogEntries.filter((entry) => entry.kind === "post").length}</dd></div>
391
- <div><dt>专栏篇目</dt><dd>{catalogEntries.filter((entry) => entry.kind === "column").length}</dd></div>
392
- </dl>
377
+ <h1>写一条随想。</h1>
378
+ <p>长文仍在编辑器里改。这里写随想、上传附件、置顶,以及发到其他网站。</p>
379
+ <p className="admin-quiet-count">
380
+ {publishedCount + staticThoughtCount} 条公开随想
381
+ {" · "}
382
+ {catalogEntries.filter((entry) => entry.kind === "post").length} 篇博文
383
+ {" · "}
384
+ {catalogEntries.filter((entry) => entry.kind === "column").length} 篇专栏
385
+ </p>
393
386
  </section>
394
387
 
395
388
  <IntelligentUiCard ui={ui} />
@@ -400,12 +393,11 @@ export function AdminDashboard({
400
393
  <section className="admin-grid">
401
394
  <div className="admin-panel admin-compose-panel">
402
395
  <div className="admin-panel-heading">
403
- <div><span>发布随想</span><h2>一条记录,可以有多种媒介。</h2></div>
404
- <PaperPlaneTilt size={22} />
396
+ <h2>写一条</h2>
405
397
  </div>
406
- <label>标题<input value={title} onChange={(event) => setTitle(event.target.value)} placeholder="给这条记录一个标题" /></label>
407
- <label>正文(Markdown)<textarea value={content} onChange={(event) => setContent(event.target.value)} placeholder="写下此刻想留下的文字…" rows={8} /></label>
408
- <label>标签(用逗号分隔)<input value={tags} onChange={(event) => setTags(event.target.value)} placeholder="例如:产品, 夜晚, 摄影" /></label>
398
+ <label>标题<input value={title} onChange={(event) => setTitle(event.target.value)} placeholder="标题" /></label>
399
+ <label>正文<textarea value={content} onChange={(event) => setContent(event.target.value)} placeholder="写下来。可以用 Markdown。" rows={8} /></label>
400
+ <label>标签<input value={tags} onChange={(event) => setTags(event.target.value)} placeholder="用逗号分开,例如:夜晚, 散步" /></label>
409
401
  <MediaFields attachments={attachments} onChange={setAttachments} onUpload={uploadMedia} uploading={uploadingCount > 0} />
410
402
  <div className="admin-actions">
411
403
  <button type="button" className="admin-secondary-button" disabled={isSaving || uploadingCount > 0} onClick={() => publishThought("draft")}>保存草稿</button>
@@ -415,11 +407,10 @@ export function AdminDashboard({
415
407
 
416
408
  <div className="admin-panel admin-thoughts-panel">
417
409
  <div className="admin-panel-heading">
418
- <div><span>后台随想</span><h2>已发布与草稿</h2></div>
419
- <DotsThree size={23} />
410
+ <h2>草稿和已发布</h2>
420
411
  </div>
421
412
  <div className="admin-thought-list">
422
- {thoughts.length === 0 ? <p className="admin-empty">还没有从后台创建的随想。</p> : thoughts.map((thought) => (
413
+ {thoughts.length === 0 ? <p className="admin-empty">还没有在这里写过随想。</p> : thoughts.map((thought) => (
423
414
  <article key={thought.id}>
424
415
  <div><span className={`admin-status ${thought.status}`}>{thought.status === "published" ? "已发布" : "草稿"}</span><h3>{thought.title}</h3><p>{thought.content.slice(0, 76)}{thought.content.length > 76 ? "…" : ""}</p></div>
425
416
  <div className="admin-item-actions">
@@ -434,10 +425,9 @@ export function AdminDashboard({
434
425
 
435
426
  <section className="admin-panel admin-distribution-panel">
436
427
  <div className="admin-panel-heading">
437
- <div><span>博文与专栏分发</span><h2>后台不改正文,只生成发布动作。</h2></div>
438
- <FileText size={22} />
428
+ <h2>置顶,或发到别处</h2>
439
429
  </div>
440
- <p className="admin-distribution-note"><LinkSimple size={16} /> CSDN、知乎、掘金采用你配置的同步服务;X 在填写用户访问令牌后可直接发布。没有配置时仍可一键复制分发包。</p>
430
+ <p className="admin-distribution-note">正文仍在仓库里改。这里可以置顶,发到 X,或同步到 CSDN、知乎、掘金。没配接口时,复制标题和链接。</p>
441
431
  <div className="admin-catalog">
442
432
  {catalogEntries.map((entry) => (
443
433
  <article key={`${entry.kind}-${entry.column ?? ""}-${entry.path}`}>
@@ -83,9 +83,9 @@ export function AdminBlocks({ siteUrl }: { siteUrl: string }) {
83
83
  return (
84
84
  <section className="admin-panel ui-admin-blocks">
85
85
  <div className="admin-panel-heading">
86
- <div><span>交互块</span><h2>所有稿件里的 intent 和 ui 块</h2></div>
86
+ <h2>文章里的块</h2>
87
87
  </div>
88
- <p className="ui-studio-meta">看到的是仓库里已推送的内容(含草稿);本地没推的改动看不到。</p>
88
+ <p className="ui-studio-meta">这里是仓库里已经推送的内容,包括草稿。还没推的本地改动看不到。</p>
89
89
  <div className="ui-studio-switch" role="group" aria-label="按状态筛选">
90
90
  {(["todo", "uncompiled", "stale", "error", "ok", "all"] as const).map((value) => (
91
91
  <button key={value} type="button" aria-pressed={filter === value} onClick={() => setFilter(value)}>
@@ -115,7 +115,7 @@ export function AdminBlocks({ siteUrl }: { siteUrl: string }) {
115
115
  <td><span className={`ui-admin-status ui-admin-status-${block.status}`}>{STATUS_LABELS[block.status]}</span></td>
116
116
  <td>{block.caption ?? block.intent?.split("\n")[0] ?? ""}{block.diagnostics.length ? `(${block.diagnostics.length} 条诊断)` : ""}</td>
117
117
  <td className="ui-admin-actions">
118
- <Link href={`/admin/ui?post=${encodeURIComponent(file.path)}&line=${block.startLine}`}>在工作台打开</Link>
118
+ <Link href={`/admin/ui?post=${encodeURIComponent(file.path)}&line=${block.startLine}`}>在写作台打开</Link>
119
119
  {block.status !== "ok" ? (
120
120
  <button type="button" onClick={() => copyPrompt(file, block)}>
121
121
  {copied === `${file.path}:${block.startLine}` ? "已复制" : "复制修复提示词"}
@@ -218,9 +218,9 @@ export function AdminEvals({ cases, configured }: { cases: EvalCase[]; configure
218
218
  return (
219
219
  <section className="admin-panel ui-admin-evals">
220
220
  <div className="admin-panel-heading">
221
- <div><span>模型自检</span><h2>用 {cases.length} 段原话给当前模型和提示词打分</h2></div>
221
+ <h2>模型自检</h2>
222
222
  </div>
223
- <p className="ui-studio-meta">逐条调用工作台的生成接口,共 {cases.length} 次,计入每小时限额。换模型或改提示词后跑一次。</p>
223
+ <p className="ui-studio-meta">会逐条调用写作台,一共 {cases.length} 次,计入每小时次数。换了模型或提示词之后跑一次。</p>
224
224
  <div className="admin-actions">
225
225
  <button type="button" className="admin-primary-button" disabled={!configured || running} onClick={run}>{running ? `正在跑 ${results.length}/${cases.length}…` : "开始自检"}</button>
226
226
  </div>
@@ -5,10 +5,10 @@ import { getAdminUser } from "../lib/admin-auth";
5
5
 
6
6
  const SECTIONS = [
7
7
  { href: "/admin", label: "总览" },
8
- { href: "/admin/ui", label: "组件工作台" },
9
- { href: "/admin/ui/catalog", label: "组件清单" },
10
- { href: "/admin/ui/blocks", label: "交互块" },
11
- { href: "/admin/ui/evals", label: "模型自检" },
8
+ { href: "/admin/ui", label: "写作台" },
9
+ { href: "/admin/ui/catalog", label: "组件" },
10
+ { href: "/admin/ui/blocks", label: "文章里的块" },
11
+ { href: "/admin/ui/evals", label: "检查" },
12
12
  ];
13
13
 
14
14
  export const isStaticExport = () => process.env.STATIC_EXPORT === "1";
@@ -23,8 +23,8 @@ export function StaticAdminNotice() {
23
23
  return (
24
24
  <main className="admin-access-page">
25
25
  <div className="admin-access-card">
26
- <h1>内容后台独立运行。</h1>
27
- <p>公开博客由 GitHub Pages 静态托管;这一页只在独立后台里可用。</p>
26
+ <h1>后台在另一个地址。</h1>
27
+ <p>公开博客上看不到这一页。写随想和改交互,去单独部署的后台。</p>
28
28
  <Link href="/">返回公开博客</Link>
29
29
  </div>
30
30
  </main>
@@ -35,7 +35,7 @@ export function AdminUiShell({ active, userName, children }: { active: string; u
35
35
  return (
36
36
  <main className="admin-shell admin-ui-shell">
37
37
  <header className="admin-topbar">
38
- <Link className="admin-brand" href="/admin">HEDON <span>STUDIO</span></Link>
38
+ <Link className="admin-brand" href="/admin">Garden<span>后台</span></Link>
39
39
  <div>
40
40
  <span>{userName}</span>
41
41
  <form action="/api/auth/logout" method="post">
@@ -1,35 +1,31 @@
1
- const adminCapabilities = [
1
+ const adminJobs = [
2
2
  {
3
- number: "01",
4
- title: "发布和管理随想",
5
- description:
6
- "新建随想、保存草稿、正式发布、撤回或删除。每次修改都会写回 content/thoughts/,不会另存一份数据库副本。",
3
+ title: "写随想",
4
+ description: "保存草稿,或直接发布。文件写回 content/thoughts/,没有另一份数据库。",
7
5
  },
8
6
  {
9
- number: "02",
10
- title: "上传多媒体",
11
- description:
12
- "图片、音频和视频可以直接上传到阿里云 OSS,随想中只保存公开地址,避免把大文件塞进 Git 历史。",
7
+ title: "上传附件",
8
+ description: "图片、音频和视频可以传到对象存储。文章里只留下地址。",
13
9
  },
14
10
  {
15
- number: "03",
16
- title: "管理博文置顶",
17
- description:
18
- "后台可以切换博文的首页置顶状态,并查看博文所属专栏。正文仍然使用 Markdown 编辑,专栏顺序仍由 columns.yaml 管理。",
11
+ title: "置顶",
12
+ description: "让一篇博文出现在首页前面。正文还是在编辑器里改。",
19
13
  },
20
14
  {
21
- number: "04",
22
- title: "分发到其他平台",
23
- description:
24
- "可以直接发布到 X,或调用自建服务同步到 CSDN、知乎和掘金;没有配置接口时,也能复制标题、栏目和公开链接。",
15
+ title: "发到别处",
16
+ description: "可以发到 X,或同步到 CSDN、知乎、掘金。没配接口时,复制标题和链接。",
17
+ },
18
+ {
19
+ title: "改文章里的交互",
20
+ description: "写作台把一句话收成可交互的块。组件页能看到每一种块长什么样。",
25
21
  },
26
22
  ];
27
23
 
28
24
  const adminFlow = [
29
- ["GitHub 登录", "只允许白名单中的账号进入后台。"],
30
- ["执行内容操作", "发布随想、上传附件、置顶或分发博文。"],
31
- ["提交到内容仓库", "后台使用服务端令牌修改 Markdown。"],
32
- ["自动更新博客", "main 分支变更后由 Actions 重新发布。"],
25
+ ["用 GitHub 登录", "只有名单里的账号能进来。"],
26
+ ["做一件事", "写随想、上传、置顶,或发到别处。"],
27
+ ["写回仓库", "后台把改动提交到你的内容仓库。"],
28
+ ["网站更新", "main 变了之后,页面会重新发布。"],
33
29
  ];
34
30
 
35
31
  export function GardenAdminGuide() {
@@ -37,11 +33,10 @@ export function GardenAdminGuide() {
37
33
  <section className="garden-admin" id="admin" aria-labelledby="garden-admin-title">
38
34
  <header className="garden-section-heading">
39
35
  <div>
40
- <p className="eyebrow">ADMIN / 管理后台</p>
41
- <h2 id="garden-admin-title">管理后台能做什么</h2>
36
+ <h2 id="garden-admin-title">后台</h2>
42
37
  </div>
43
38
  <p>
44
- 后台是一个可选的独立服务,处理需要登录和服务端密钥的工作。公开博客仍然是静态站点,内容仍然保存在 Git 仓库中。
39
+ 长文继续在编辑器里写。后台只做需要登录的事:随想、附件、置顶、转发,以及文章里的交互。
45
40
  </p>
46
41
  </header>
47
42
 
@@ -50,40 +45,39 @@ export function GardenAdminGuide() {
50
45
  {/* eslint-disable-next-line @next/next/no-img-element */}
51
46
  <img
52
47
  src="/images/garden/admin-studio.png"
53
- alt="Garden 内容管理后台,包含随想发布、草稿管理与博文分发"
54
- width={1440}
55
- height={1000}
48
+ alt="Garden 后台:上面写一条随想,下面是草稿和已发布"
49
+ width={920}
50
+ height={1033}
56
51
  loading="lazy"
57
52
  />
58
53
  <figcaption>
59
- <strong>实际后台界面</strong>
60
- <span>随想编辑、已发布与草稿管理,以及博文置顶和分发都集中在同一个工作区。</span>
54
+ <strong>后台首页</strong>
55
+ <span>上面写一条随想。下面是草稿,以及可以置顶或转发的文章。</span>
61
56
  </figcaption>
62
57
  </figure>
63
58
 
64
59
  <div className="garden-admin-capabilities">
65
- {adminCapabilities.map((capability) => (
66
- <article key={capability.number}>
67
- <span>{capability.number}</span>
68
- <h3>{capability.title}</h3>
69
- <p>{capability.description}</p>
60
+ {adminJobs.map((job) => (
61
+ <article key={job.title}>
62
+ <h3>{job.title}</h3>
63
+ <p>{job.description}</p>
70
64
  </article>
71
65
  ))}
72
66
  </div>
73
67
 
74
68
  <div className="garden-admin-boundary">
75
- <strong>后台的边界</strong>
69
+ <strong>它不是整套编辑器</strong>
76
70
  <p>
77
- 它不是一套完整 CMS。长文章继续在 Typora 或其他 Markdown 编辑器中编写,专栏继续在
71
+ 长文仍用 Typora 或其他 Markdown 编辑器。专栏顺序仍在
78
72
  <code> content/columns.yaml </code>
79
- 中编排;后台主要补上随想发布、媒体上传、博文置顶和内容分发。
73
+ 里排。
80
74
  </p>
81
75
  </div>
82
76
 
83
- <ol className="garden-admin-flow" aria-label="管理后台发布流程">
77
+ <ol className="garden-admin-flow" aria-label="从登录到网站更新">
84
78
  {adminFlow.map(([title, description], index) => (
85
79
  <li key={title}>
86
- <span>{String(index + 1).padStart(2, "0")}</span>
80
+ <span>{index + 1}</span>
87
81
  <strong>{title}</strong>
88
82
  <p>{description}</p>
89
83
  </li>
@@ -92,21 +86,20 @@ export function GardenAdminGuide() {
92
86
 
93
87
  <div className="garden-admin-enable">
94
88
  <div>
95
- <p className="eyebrow">ENABLE / 启用后台</p>
96
- <h3>单独部署,再连接内容仓库</h3>
89
+ <h3>想用后台,再单独部署一份</h3>
97
90
  <ol>
98
91
  <li>把同一份 Garden 部署到 Node、Docker 或 Cloudflare Workers。</li>
99
- <li>配置 GitHub OAuth、允许登录的账号,以及仅能写入内容仓库的令牌。</li>
100
- <li>需要上传附件或跨平台分发时,再配置 OSS 和各平台接口。</li>
92
+ <li>配好 GitHub 登录、允许的账号,以及只能改内容仓库的令牌。</li>
93
+ <li>要上传附件或转发到其他网站时,再配存储和各平台接口。</li>
101
94
  </ol>
102
95
  </div>
103
- <pre aria-label="管理后台核心配置示例">
96
+ <pre aria-label="后台需要的环境变量">
104
97
  <code>{`ADMIN_URL=https://admin.example.com
105
98
  CONTENT_REPOSITORY=owner/repository
106
99
  CONTENT_BRANCH=main
107
100
  ADMIN_GITHUB_LOGINS=your-github-login
108
101
 
109
- # 令牌与密钥只保存在后台服务中
102
+ # 令牌只放在后台服务里
110
103
  CONTENT_GITHUB_TOKEN=github_pat_xxx
111
104
  GITHUB_OAUTH_CLIENT_ID=Ov23li_xxx`}</code>
112
105
  </pre>
@@ -10,11 +10,6 @@ function SourcePanel({ path, source, language }: SourcePanelProps) {
10
10
  return (
11
11
  <div className="garden-source-panel">
12
12
  <div className="garden-source-bar">
13
- <span aria-hidden="true">
14
- <i />
15
- <i />
16
- <i />
17
- </span>
18
13
  <code>{path}</code>
19
14
  </div>
20
15
  <pre>
@@ -69,12 +64,10 @@ const columnSource = `columns:
69
64
 
70
65
  type Guide = {
71
66
  id: string;
72
- number: string;
73
67
  label: string;
74
68
  title: string;
75
69
  introduction: string;
76
70
  steps: ReactNode[];
77
- features: string[];
78
71
  result: string;
79
72
  path: string;
80
73
  source: string;
@@ -84,88 +77,45 @@ type Guide = {
84
77
  const guides: Guide[] = [
85
78
  {
86
79
  id: "write-post",
87
- number: "01",
88
80
  label: "博文",
89
81
  title: "写一篇文章",
90
- introduction:
91
- "博文保存在 content/posts/ 中,可以按照主题建立任意层级的子目录。文件路径同时决定文章地址,正文仍然是普通 Markdown。",
82
+ introduction: "文件放在 content/posts/。路径就是网址,正文是普通 Markdown。可以按主题分子目录。",
92
83
  steps: [
93
- <>
94
- 在 <code>content/posts/</code> 中创建一个 <code>.md</code> 文件。
95
- </>,
96
- <>在 Front Matter 中填写标题、日期、摘要、主题、标签和封面。</>,
97
- <>使用 Markdown 编写正文,图片和其他附件可以使用相对路径。</>,
84
+ <>在 <code>content/posts/</code> 里新建一个 <code>.md</code> 文件。</>,
85
+ <>开头写下标题、日期、摘要、主题和标签。封面可有可无。</>,
86
+ <>接着写正文。图片用相对路径,和文件放在一起。</>,
98
87
  ],
99
- features: [
100
- "GFM 表格、任务列表与脚注",
101
- "TypeScript、Go、Rust 等代码高亮",
102
- "数学公式与 Mermaid 图表",
103
- "图片、音视频与平台嵌入",
104
- "草稿、定时发布与置顶",
105
- "文章目录、上一篇与下一篇",
106
- ],
107
- result:
108
- "构建时会生成文章页面,并把标题、摘要和正文加入博文列表、主题归档、全文搜索与 RSS。",
88
+ result: "推送后,这篇文章会出现在列表、主题、搜索和 RSS 里。",
109
89
  path: "content/posts/writing/first-post.md",
110
90
  source: postSource,
111
91
  language: "markdown",
112
92
  },
113
93
  {
114
94
  id: "write-thought",
115
- number: "02",
116
95
  label: "随想",
117
- title: "发布一条随想",
118
- introduction:
119
- "随想适合记录几句话、一个链接或一组多媒体。它同样保存在 Git 仓库中,只需要更少的字段,也可以通过可选管理后台发布。",
96
+ title: "写一条随想",
97
+ introduction: "几句话、一张图或一个链接。字段更少。也可以在后台写,写完同样进仓库。",
120
98
  steps: [
121
- <>
122
- 直接创建 <code>content/thoughts/&lt;文件名&gt;.md</code>,或者在管理后台填写内容。
123
- </>,
124
- <>文件名就是地址。front matter 填写标题和日期;需要暂存时加入草稿状态。</>,
125
- <>
126
- 在 <code>media</code> 中按顺序添加图片、音频、视频或外部链接。
127
- </>,
128
- ],
129
- features: [
130
- "纯文字短内容",
131
- "多张图片与混合图集",
132
- "音频和视频",
133
- "带标题的外部链接",
134
- "草稿与公开状态",
135
- "独立永久链接",
99
+ <>新建 <code>content/thoughts/文件名.md</code>,或在后台填写。</>,
100
+ <>文件名就是地址。标题和日期写在开头。先存着就标成草稿。</>,
101
+ <>图片、音频、视频和链接按顺序放进 <code>media</code>。</>,
136
102
  ],
137
- result:
138
- "发布后的内容会出现在随想时间线和首页最近随想中,每条随想都有可以单独分享的永久链接。",
103
+ result: "它会出现在随想页和首页,并且有自己的链接。",
139
104
  path: "content/thoughts/walk-after-rain.md",
140
105
  source: thoughtSource,
141
106
  language: "markdown",
142
107
  },
143
108
  {
144
109
  id: "make-column",
145
- number: "03",
146
110
  label: "专栏",
147
- title: "创建一个专栏",
148
- introduction:
149
- "专栏不复制文章,而是在 content/columns.yaml 中引用已经存在的博文。posts 的排列顺序就是专栏的阅读顺序。",
111
+ title: "把文章收成专栏",
112
+ introduction: "专栏不复制文章。它只是 content/columns.yaml 里的一份阅读顺序。",
150
113
  steps: [
151
- <>
152
- 在 <code>content/columns.yaml</code> 中添加专栏名称、介绍、状态和封面。
153
- </>,
154
- <>
155
- 在 <code>posts</code> 中填写博文相对 <code>content/posts/</code> 的路径。
156
- </>,
157
- <>按照希望读者阅读的顺序排列路径;同一篇文章可以加入多个专栏。</>,
158
- ],
159
- features: [
160
- "专栏标题、说明和状态",
161
- "独立专栏封面",
162
- "文章顺序可控",
163
- "复用同一份 Markdown",
164
- "一篇文章加入多个专栏",
165
- "系列文章导航",
114
+ <>在 <code>content/columns.yaml</code> 里写名称、介绍和封面。</>,
115
+ <>在 <code>posts</code> 里填文章相对 <code>content/posts/</code> 的路径。</>,
116
+ <>按你希望读者阅读的顺序排列。同一篇可以进多个专栏。</>,
166
117
  ],
167
- result:
168
- "构建后会生成独立专栏页面。读者可以按照配置顺序浏览文章,文章页也会显示它所属的系列与前后篇目。",
118
+ result: "读者按这个顺序读。文章页也会显示它属于哪一组,以及上一篇、下一篇。",
169
119
  path: "content/columns.yaml",
170
120
  source: columnSource,
171
121
  language: "yaml",
@@ -174,56 +124,22 @@ const guides: Guide[] = [
174
124
 
175
125
  export function GardenContentGuide() {
176
126
  return (
177
- <section className="garden-guide" aria-labelledby="garden-guide-title">
178
- <header className="garden-guide-heading">
179
- <div>
180
- <p className="eyebrow">CONTENT / 内容</p>
181
- <h2 id="garden-guide-title">博文、随想和专栏</h2>
182
- </div>
183
- <p>
184
- 先选择内容类型,再编辑对应文件。三种内容都保存在源码仓库中,直接编辑文件即可完成全部公开内容的维护和发布。
185
- </p>
186
- </header>
187
-
188
- <nav className="garden-guide-index" aria-label="内容类型">
189
- {guides.map((guide) => (
190
- <a href={`#${guide.id}`} key={guide.id}>
191
- <span>{guide.number}</span>
192
- <strong>{guide.title}</strong>
193
- <small>{guide.label}</small>
194
- </a>
195
- ))}
196
- </nav>
197
-
127
+ <div className="garden-guide">
198
128
  <div className="garden-guide-sections">
199
129
  {guides.map((guide) => (
200
130
  <article className="garden-content-guide" id={guide.id} key={guide.id}>
201
131
  <div className="garden-content-copy">
202
- <p className="eyebrow">
203
- {guide.number} / {guide.label}
204
- </p>
132
+ <p className="garden-content-label">{guide.label}</p>
205
133
  <h2>{guide.title}</h2>
206
134
  <p className="garden-content-introduction">{guide.introduction}</p>
207
135
 
208
136
  <ol className="garden-content-steps">
209
137
  {guide.steps.map((step, index) => (
210
- <li key={index}>
211
- <strong>{index + 1}</strong>
212
- <span>{step}</span>
213
- </li>
138
+ <li key={index}>{step}</li>
214
139
  ))}
215
140
  </ol>
216
141
 
217
- <ul className="garden-content-features" aria-label={`${guide.title}支持的能力`}>
218
- {guide.features.map((feature) => (
219
- <li key={feature}>{feature}</li>
220
- ))}
221
- </ul>
222
-
223
- <aside className="garden-content-result">
224
- <strong>发布后</strong>
225
- <p>{guide.result}</p>
226
- </aside>
142
+ <p className="garden-content-result">{guide.result}</p>
227
143
  </div>
228
144
 
229
145
  <SourcePanel
@@ -234,6 +150,6 @@ export function GardenContentGuide() {
234
150
  </article>
235
151
  ))}
236
152
  </div>
237
- </section>
153
+ </div>
238
154
  );
239
155
  }