hd-garden 0.3.12 → 0.4.0
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.
- package/app/a2/[slug]/page.tsx +61 -0
- package/app/a2/page.tsx +27 -0
- package/app/admin/page.tsx +11 -15
- package/app/admin/ui/page.tsx +1 -1
- package/app/blog/[...path]/page.tsx +33 -22
- package/app/blog/page.tsx +6 -7
- package/app/columns/[column]/[...path]/page.tsx +33 -25
- package/app/columns/page.tsx +38 -52
- package/app/components/AdminDashboard.tsx +24 -34
- package/app/components/AdminUiPanels.tsx +5 -5
- package/app/components/AdminUiShell.tsx +7 -7
- package/app/components/ArticleMeasure.tsx +95 -0
- package/app/components/ArticleTitle.tsx +20 -0
- package/app/components/BlogArchive.tsx +106 -102
- package/app/components/GardenAdminGuide.tsx +38 -45
- package/app/components/GardenContentGuide.tsx +22 -106
- package/app/components/GardenMediaGuide.tsx +24 -45
- package/app/components/MarkdownArticle.tsx +145 -66
- package/app/components/MermaidDiagram.tsx +44 -71
- package/app/components/PageIntro.tsx +2 -2
- package/app/components/SiteFooter.tsx +3 -7
- package/app/components/SiteHeader.tsx +55 -41
- package/app/components/TableOfContents.tsx +3 -2
- package/app/components/TagIndex.tsx +97 -0
- package/app/components/UiStudio.tsx +2 -2
- package/app/components/garden/GardenBlock.tsx +45 -0
- package/app/components/garden/blocks.tsx +530 -0
- package/app/components/garden/chrome.tsx +350 -0
- package/app/components/garden/sandbox-message.ts +16 -0
- package/app/garden/page.tsx +85 -223
- package/app/garden-modules.d.ts +2 -0
- package/app/globals.css +947 -88
- package/app/layout.tsx +9 -0
- package/app/lib/a2-meta.ts +22 -0
- package/app/lib/a2-preview.ts +50 -0
- package/app/lib/content.ts +48 -5
- package/app/lib/fallback-site-config.ts +1 -1
- package/app/lib/markdown-plugins.ts +2 -0
- package/app/lib/remark-a2.mjs +208 -0
- package/app/lib/remark-garden.mjs +88 -0
- package/app/page.tsx +122 -93
- package/app/styles/a2.css +1383 -0
- package/app/styles/shell.css +438 -0
- package/app/styles/tokens.css +111 -0
- package/app/tags/page.tsx +28 -0
- package/app/ui-parity/page.tsx +3 -2
- package/package.json +2 -2
- package/public/images/garden/admin-studio.png +0 -0
- package/scripts/build-content.mjs +11 -1
- package/scripts/check-post.mjs +18 -4
- package/scripts/new-post.mjs +3 -0
- package/site.config.yaml +2 -2
- package/templates/garden-blocks.md +128 -0
- package/templates/garden-snippets.plist +121 -0
- package/templates/site/site.config.yaml +1 -1
- package/ui/garden/catalog.mjs +196 -0
- package/ui/garden/check.mjs +103 -0
- package/ui/garden/enhance.mjs +89 -0
- package/ui/garden/parse.mjs +198 -0
- package/ui/garden/snippets.mjs +109 -0
- package/ui/garden/title.mjs +37 -0
- package/ui/garden/word-count.mjs +266 -0
- package/ui/host.mjs +45 -28
- package/ui/parity-fixtures.mjs +1 -1
- package/ui/preview/dist/garden-ui.css +81 -77
- package/ui/preview/dist/garden-ui.esm.js +124 -89
- package/ui/preview/dist/garden-ui.iife.js +124 -89
- package/ui/prompt/rules.md +17 -1
- package/ui/react/CatalogPage.tsx +12 -0
- package/ui/react/components/display.tsx +1 -1
- package/ui/react/components/media.tsx +2 -2
- package/ui/react/ui.css +81 -77
package/app/page.tsx
CHANGED
|
@@ -1,119 +1,148 @@
|
|
|
1
1
|
import Link from "next/link";
|
|
2
|
-
import {
|
|
3
|
-
ArrowRight,
|
|
4
|
-
ArrowUpRight,
|
|
5
|
-
GithubLogo,
|
|
6
|
-
PushPin,
|
|
7
|
-
} from "@phosphor-icons/react/ssr";
|
|
8
|
-
import { columnHref, columns, formatDate, postHref, posts } from "./lib/content";
|
|
2
|
+
import { columns, postHref, posts } from "./lib/content";
|
|
9
3
|
import { ThoughtCard } from "./components/ThoughtCard";
|
|
10
|
-
import { PageIntro } from "./components/PageIntro";
|
|
11
4
|
import { getPublishedThoughts } from "./lib/public-thoughts";
|
|
12
|
-
import { githubUrl, siteConfig
|
|
5
|
+
import { githubUrl, siteConfig } from "./site.config";
|
|
13
6
|
|
|
14
7
|
export const metadata = {
|
|
15
|
-
title:
|
|
8
|
+
title: { absolute: siteConfig.name },
|
|
16
9
|
description: siteConfig.description,
|
|
17
10
|
};
|
|
18
11
|
|
|
12
|
+
function day(value: string) {
|
|
13
|
+
const date = new Date(value);
|
|
14
|
+
if (Number.isNaN(date.getTime())) return value.slice(0, 10);
|
|
15
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
16
|
+
timeZone: "Asia/Shanghai",
|
|
17
|
+
year: "numeric",
|
|
18
|
+
month: "2-digit",
|
|
19
|
+
day: "2-digit",
|
|
20
|
+
}).format(date);
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
export default function Home() {
|
|
24
|
+
const recent = [...posts].sort((left, right) => Date.parse(right.date) - Date.parse(left.date));
|
|
25
|
+
const feature = recent[0];
|
|
20
26
|
const pinned = posts.filter((post) => post.pinned);
|
|
21
27
|
const thoughts = getPublishedThoughts();
|
|
28
|
+
const series = Array.from(
|
|
29
|
+
columns.reduce((result, entry) => {
|
|
30
|
+
const existing = result.get(entry.column ?? "");
|
|
31
|
+
if (existing) existing.push(entry);
|
|
32
|
+
else result.set(entry.column ?? "", [entry]);
|
|
33
|
+
return result;
|
|
34
|
+
}, new Map<string, typeof columns>()),
|
|
35
|
+
);
|
|
22
36
|
|
|
23
37
|
return (
|
|
24
|
-
<main className="
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
rel="me noopener noreferrer"
|
|
35
|
-
>
|
|
36
|
-
<GithubLogo size={21} weight="fill" />
|
|
37
|
-
在 GitHub 找到我
|
|
38
|
-
<ArrowUpRight size={16} />
|
|
39
|
-
</a>
|
|
40
|
-
</PageIntro>
|
|
38
|
+
<main className="sh-main" id="content">
|
|
39
|
+
<section className="sh-intro">
|
|
40
|
+
<h1>{siteConfig.pages.home.title}</h1>
|
|
41
|
+
<p>{siteConfig.pages.home.subtitle || siteConfig.description}</p>
|
|
42
|
+
<div className="links">
|
|
43
|
+
<Link href="/about">关于我</Link>
|
|
44
|
+
<a href={githubUrl} rel="me">GitHub</a>
|
|
45
|
+
<a href="/rss.xml">RSS 订阅</a>
|
|
46
|
+
</div>
|
|
47
|
+
</section>
|
|
41
48
|
|
|
42
|
-
{
|
|
43
|
-
<
|
|
49
|
+
{posts.length > 0 && feature ? (
|
|
50
|
+
<article className="sh-feature" aria-labelledby="ft">
|
|
51
|
+
{feature.cover ? (
|
|
52
|
+
<div className="cover" style={{ background: "var(--cover)" }}>
|
|
53
|
+
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
54
|
+
<img src={feature.cover} alt="" />
|
|
55
|
+
</div>
|
|
56
|
+
) : null}
|
|
44
57
|
<div>
|
|
45
|
-
<
|
|
58
|
+
<p className="kicker">
|
|
59
|
+
<span>最新文章</span>
|
|
60
|
+
<time className="num" dateTime={day(feature.date)}>{day(feature.date)}</time>
|
|
61
|
+
{feature.topic ? <span>{feature.topic}</span> : null}
|
|
62
|
+
</p>
|
|
63
|
+
<h2 id="ft"><Link href={postHref(feature)}>{feature.title}</Link></h2>
|
|
64
|
+
{feature.description ? <p>{feature.description}</p> : null}
|
|
65
|
+
{feature.tags?.length ? (
|
|
66
|
+
<p className="tags">{feature.tags.map((tag) => <span key={tag}>#{tag}</span>)}</p>
|
|
67
|
+
) : null}
|
|
46
68
|
</div>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<div className="post-list">
|
|
50
|
-
{pinned.map((post) => (
|
|
51
|
-
<Link href={postHref(post)} key={post.path} className="post-row">
|
|
52
|
-
<time>{formatDate(post.date)}</time>
|
|
53
|
-
<span>
|
|
54
|
-
<strong>{post.title}</strong>
|
|
55
|
-
<small>{post.topic}</small>
|
|
56
|
-
</span>
|
|
57
|
-
<ArrowRight size={18} />
|
|
58
|
-
</Link>
|
|
59
|
-
))}
|
|
60
|
-
</div>
|
|
61
|
-
</section>}
|
|
69
|
+
</article>
|
|
70
|
+
) : null}
|
|
62
71
|
|
|
63
|
-
|
|
64
|
-
<
|
|
65
|
-
<div>
|
|
66
|
-
<h2
|
|
72
|
+
<div className="sh-home-grid">
|
|
73
|
+
<section className="sh-section" aria-labelledby="h-recent">
|
|
74
|
+
<div className="sh-sec-head">
|
|
75
|
+
<h2 id="h-recent">最近更新</h2>
|
|
76
|
+
<Link className="sh-more" href="/blog">全部 {posts.length} 篇</Link>
|
|
67
77
|
</div>
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
<ol className="sh-rows">
|
|
79
|
+
{recent.slice(0, 7).map((post) => (
|
|
80
|
+
<li className="sh-row" key={post.path}>
|
|
81
|
+
<time className="num" dateTime={day(post.date)}>{day(post.date)}</time>
|
|
82
|
+
<h3><Link href={postHref(post)}>{post.title}</Link></h3>
|
|
83
|
+
{post.description ? <p>{post.description}</p> : null}
|
|
84
|
+
{post.topic ? <Link className="cat" href={`/blog?topic=${encodeURIComponent(post.topic)}`}>{post.topic}</Link> : null}
|
|
85
|
+
</li>
|
|
86
|
+
))}
|
|
87
|
+
</ol>
|
|
88
|
+
</section>
|
|
89
|
+
{pinned.length > 0 ? (
|
|
90
|
+
<section className="sh-section" aria-labelledby="h-pin">
|
|
91
|
+
<div className="sh-sec-head section-heading accent-heading"><h2 id="h-pin">置顶</h2></div>
|
|
92
|
+
<ul className="sh-side-list">
|
|
93
|
+
{pinned.map((post) => (
|
|
94
|
+
<li key={post.path}>
|
|
95
|
+
<Link href={postHref(post)}>{post.title}</Link>
|
|
96
|
+
<span><time className="num" dateTime={day(post.date)}>{day(post.date)}</time>{post.topic ? ` ${post.topic}` : ""}</span>
|
|
97
|
+
</li>
|
|
98
|
+
))}
|
|
99
|
+
</ul>
|
|
100
|
+
</section>
|
|
101
|
+
) : null}
|
|
102
|
+
</div>
|
|
83
103
|
|
|
84
|
-
{
|
|
85
|
-
<
|
|
86
|
-
<div>
|
|
87
|
-
<h2
|
|
104
|
+
{columns[0] && series.length ? (
|
|
105
|
+
<section className="sh-section" aria-labelledby="h-cols">
|
|
106
|
+
<div className="sh-sec-head">
|
|
107
|
+
<h2 id="h-cols">专栏</h2>
|
|
108
|
+
<Link className="sh-more" href="/columns">全部专栏</Link>
|
|
88
109
|
</div>
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
110
|
+
<div className="sh-cols">
|
|
111
|
+
{series.map(([slug, entries]) => {
|
|
112
|
+
const first = entries[0];
|
|
113
|
+
return (
|
|
114
|
+
<article className="sh-col" key={slug}>
|
|
115
|
+
<div className="cover" style={{ background: "var(--well)" }}>
|
|
116
|
+
{first.columnCover ? (
|
|
117
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
118
|
+
<img src={first.columnCover} alt={first.columnCoverAlt ?? ""} />
|
|
119
|
+
) : null}
|
|
120
|
+
</div>
|
|
121
|
+
<div className="body">
|
|
122
|
+
<h3><Link href={`/columns#${slug}`}>{first.columnTitle}</Link></h3>
|
|
123
|
+
<p className="count">{entries.length} 篇,{first.columnStatus ?? "持续更新"}</p>
|
|
124
|
+
<p>{first.columnDescription ?? first.description}</p>
|
|
125
|
+
</div>
|
|
126
|
+
</article>
|
|
127
|
+
);
|
|
128
|
+
})}
|
|
129
|
+
</div>
|
|
130
|
+
</section>
|
|
131
|
+
) : null}
|
|
97
132
|
|
|
98
|
-
{
|
|
99
|
-
<
|
|
100
|
-
<div>
|
|
101
|
-
<h2
|
|
133
|
+
{thoughts.length > 0 ? (
|
|
134
|
+
<section className="sh-section" aria-labelledby="h-thoughts">
|
|
135
|
+
<div className="sh-sec-head">
|
|
136
|
+
<h2 id="h-thoughts">最近随想</h2>
|
|
137
|
+
<Link className="sh-more" href="/thoughts">全部随想</Link>
|
|
102
138
|
</div>
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
className="column-card"
|
|
108
|
-
>
|
|
109
|
-
<div>
|
|
110
|
-
<span>连载中 · {columns.length} 篇</span>
|
|
111
|
-
<h3>{columns[0].columnTitle}</h3>
|
|
112
|
-
<p>记录一个人从想法、最小闭环到稳定发布的完整构建过程。</p>
|
|
139
|
+
<div className="home-thought-list">
|
|
140
|
+
{thoughts.slice(0, 5).map((thought) => (
|
|
141
|
+
<ThoughtCard key={thought.slug} thought={thought} />
|
|
142
|
+
))}
|
|
113
143
|
</div>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
</section>}
|
|
144
|
+
</section>
|
|
145
|
+
) : null}
|
|
117
146
|
</main>
|
|
118
147
|
);
|
|
119
148
|
}
|