hd-garden 0.4.0 → 0.5.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.
Files changed (56) hide show
  1. package/.agents/skills/garden-interactive-chart/SKILL.md +10 -10
  2. package/.agents/skills/garden-ui/SKILL.md +4 -4
  3. package/Makefile +2 -3
  4. package/app/a2/[slug]/page.tsx +0 -1
  5. package/app/a2/page.tsx +1 -1
  6. package/app/about/page.tsx +8 -12
  7. package/app/admin/page.tsx +2 -2
  8. package/app/blog/[...path]/page.tsx +2 -13
  9. package/app/blog/page.tsx +2 -2
  10. package/app/columns/[column]/[...path]/page.tsx +2 -4
  11. package/app/columns/page.tsx +2 -2
  12. package/app/components/AdminDashboard.tsx +3 -3
  13. package/app/components/ArticleMeasure.tsx +114 -42
  14. package/app/components/ArticleTags.tsx +17 -0
  15. package/app/components/BlogArchive.tsx +85 -33
  16. package/app/components/ContentEmptyState.tsx +6 -6
  17. package/app/components/GardenAdminGuide.tsx +1 -1
  18. package/app/components/GardenContentGuide.tsx +1 -2
  19. package/app/components/GardenMediaGuide.tsx +3 -3
  20. package/app/components/GithubProfile.tsx +1 -1
  21. package/app/components/MarkdownImage.tsx +4 -2
  22. package/app/components/SiteFooter.tsx +5 -5
  23. package/app/components/SiteHeader.tsx +1 -5
  24. package/app/components/TagRedirect.tsx +17 -0
  25. package/app/garden/page.tsx +6 -6
  26. package/app/garden-modules.d.ts +0 -1
  27. package/app/globals.css +19 -101
  28. package/app/lib/a2-meta.ts +10 -0
  29. package/app/lib/a2-preview.ts +0 -2
  30. package/app/lib/content.ts +14 -8
  31. package/app/lib/fallback-site-config.ts +15 -15
  32. package/app/lib/generated-content.ts +58 -47
  33. package/app/lib/tag-filter.ts +114 -0
  34. package/app/not-found.tsx +13 -0
  35. package/app/page.tsx +10 -27
  36. package/app/styles/a2.css +96 -15
  37. package/app/styles/shell.css +56 -61
  38. package/app/styles/tokens.css +35 -22
  39. package/app/tags/page.tsx +20 -9
  40. package/package.json +1 -1
  41. package/scripts/build-content.mjs +3 -3
  42. package/scripts/check-post.mjs +0 -3
  43. package/scripts/new-post.mjs +1 -3
  44. package/site.config.yaml +15 -15
  45. package/templates/site/Makefile +2 -3
  46. package/templates/site/site.config.yaml +15 -15
  47. package/ui/garden/check.mjs +2 -4
  48. package/ui/garden/enhance.mjs +57 -0
  49. package/ui/garden/title.mjs +5 -8
  50. package/ui/host.mjs +13 -13
  51. package/ui/preview/dist/garden-ui.css +11 -11
  52. package/ui/preview/dist/garden-ui.esm.js +6 -6
  53. package/ui/preview/dist/garden-ui.iife.js +7 -7
  54. package/ui/react/components/media.tsx +2 -2
  55. package/ui/react/ui.css +11 -11
  56. package/app/components/TagIndex.tsx +0 -97
@@ -97,8 +97,8 @@ let mermaidQueue: Promise<unknown> = Promise.resolve();
97
97
 
98
98
  const MERMAID_FONT = 'ui-sans-serif, -apple-system, system-ui, "Segoe UI", Helvetica, Arial, "PingFang SC", sans-serif';
99
99
  const MERMAID_THEMES = {
100
- light: { background: "#F8F7F3", primaryColor: "#FFFFFF", primaryBorderColor: "#D6D2C9", primaryTextColor: "#1B1A18", lineColor: "#6E6B64", secondaryColor: "#F0EEE8", tertiaryColor: "#F0EEE8", fontSize: "14px" },
101
- dark: { background: "#161513", primaryColor: "#1E1D1A", primaryBorderColor: "#45423C", primaryTextColor: "#EEECE7", lineColor: "#A09B91", secondaryColor: "#22211D", tertiaryColor: "#22211D", fontSize: "14px" },
100
+ light: { background: "#FAF9F5", primaryColor: "#FFFFFF", primaryBorderColor: "#D6D2C9", primaryTextColor: "#1B1A18", lineColor: "#6E6B64", secondaryColor: "#F0EEE6", tertiaryColor: "#F0EEE6", fontSize: "14px" },
101
+ dark: { background: "#1F1E1D", primaryColor: "#262624", primaryBorderColor: "#45423C", primaryTextColor: "#EEECE7", lineColor: "#A09B91", secondaryColor: "#30302E", tertiaryColor: "#30302E", fontSize: "14px" },
102
102
  };
103
103
 
104
104
  function renderMermaid(source: string, id: string, dark: boolean) {
package/ui/react/ui.css CHANGED
@@ -22,23 +22,23 @@
22
22
  --gui-control-border: var(--rule-strong, #d6d2c9);
23
23
  --gui-check-border: var(--rule-strong, #d6d2c9);
24
24
  --gui-control-bg: var(--card, #fff);
25
- --gui-surface: var(--well, #f0eee8);
26
- --gui-chip: var(--well, #f0eee8);
27
- --gui-hover: var(--well, #f0eee8);
25
+ --gui-surface: var(--well, #f0eee6);
26
+ --gui-chip: var(--well, #f0eee6);
27
+ --gui-hover: var(--well, #f0eee6);
28
28
  --gui-solid: var(--ink, #1b1a18);
29
29
  --gui-solid-hover: var(--soft, #4a4844);
30
- --gui-on-solid: var(--paper, #f8f7f3);
31
- --gui-segment: var(--well, #f0eee8);
30
+ --gui-on-solid: var(--paper, #faf9f5);
31
+ --gui-segment: var(--well, #f0eee6);
32
32
  --gui-segment-selected: var(--card, #fff);
33
33
  --gui-segment-shadow: none;
34
- --gui-progress: var(--blue, #2c55b4);
35
- --gui-progress-track: var(--well, #f0eee8);
36
- --gui-focus: var(--blue, #2c55b4);
37
- --gui-bubble: var(--blue-tint, #eaf0fc);
34
+ --gui-progress: var(--blue, #30302e);
35
+ --gui-progress-track: var(--well, #f0eee6);
36
+ --gui-focus: var(--blue, #30302e);
37
+ --gui-bubble: var(--blue-tint, #f0eee6);
38
38
  --gui-popover-border: var(--rule, #e6e3dc);
39
39
  --gui-shadow: none;
40
- --gui-code-bg: var(--well, #f0eee8);
41
- --gui-accent: var(--blue, #2c55b4);
40
+ --gui-code-bg: var(--well, #f0eee6);
41
+ --gui-accent: var(--blue, #30302e);
42
42
  --gui-series-plain: var(--ink, #1b1a18);
43
43
  --gui-series-accent: var(--blue);
44
44
  --gui-series-new: #00a240;
@@ -1,97 +0,0 @@
1
- "use client";
2
-
3
- import Link from "next/link";
4
- import { usePathname, useRouter, useSearchParams } from "next/navigation";
5
- import { useMemo } from "react";
6
- import { postHref, type ContentEntry } from "../lib/content";
7
-
8
- function day(value: string) {
9
- const date = new Date(value);
10
- if (Number.isNaN(date.getTime())) return value.slice(0, 10);
11
- return new Intl.DateTimeFormat("en-CA", {
12
- timeZone: "Asia/Shanghai",
13
- year: "numeric",
14
- month: "2-digit",
15
- day: "2-digit",
16
- }).format(date);
17
- }
18
-
19
- function joinTags(names: string[]) {
20
- if (names.length <= 1) return names[0] ?? "";
21
- return `${names.slice(0, -1).join("、")}和${names[names.length - 1]}`;
22
- }
23
-
24
- export function TagIndex({ posts }: { posts: ContentEntry[] }) {
25
- const router = useRouter();
26
- const pathname = usePathname();
27
- const params = useSearchParams();
28
- const tags = useMemo(() => {
29
- const counts = new Map<string, number>();
30
- for (const post of posts) {
31
- for (const tag of post.tags ?? []) counts.set(tag, (counts.get(tag) ?? 0) + 1);
32
- }
33
- return [...counts.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0], "zh"));
34
- }, [posts]);
35
- const requested = params.get("t") ?? "";
36
- const active = tags.some(([tag]) => tag === requested) ? requested : "";
37
- const common = tags.filter(([, count]) => count > 1);
38
- const once = tags.filter(([, count]) => count === 1);
39
- const visible = posts
40
- .filter((post) => active && (post.tags ?? []).includes(active))
41
- .slice()
42
- .sort((left, right) => Date.parse(right.date) - Date.parse(left.date));
43
- const choose = (tag: string) => {
44
- const next = active === tag ? "" : tag;
45
- router.replace(next ? `${pathname}?t=${encodeURIComponent(next)}` : pathname, { scroll: false });
46
- };
47
-
48
- return (
49
- <div className="sh-listgrid" id="sh-tags">
50
- <div>
51
- <ul className="sh-tagcloud" aria-label="常用标签">
52
- {common.map(([tag, count]) => (
53
- <li key={tag}>
54
- <button type="button" className="sh-chip" data-tag={tag} aria-pressed={active === tag} onClick={() => choose(tag)}>
55
- {tag}<span className="n">{count}</span>
56
- </button>
57
- </li>
58
- ))}
59
- </ul>
60
- {once.length ? (
61
- <details className="sh-once" open={once.some(([tag]) => tag === active) || undefined}>
62
- <summary>只出现一次的标签<span className="num">{once.length}</span><svg viewBox="0 0 14 14" aria-hidden="true"><path d="M3.5 5.5 7 9l3.5-3.5" /></svg></summary>
63
- <ul className="sh-tagcloud">
64
- {once.map(([tag]) => (
65
- <li key={tag}>
66
- <button type="button" className="sh-chip" data-tag={tag} aria-pressed={active === tag} onClick={() => choose(tag)}>
67
- {tag}
68
- </button>
69
- </li>
70
- ))}
71
- </ul>
72
- </details>
73
- ) : null}
74
- <section className="sh-tagresult" id="sh-tagresult" aria-live="polite">
75
- <h2 id="sh-tag-h">{active ? `“${active}”的 ${visible.length} 篇文章` : "选择一个标签"}</h2>
76
- <p className="hint" hidden={Boolean(active)}>
77
- {common.length ? `还没有选择标签。常用的有 ${joinTags(common.slice(0, 4).map(([tag]) => tag))}。` : "还没有选择标签。"}
78
- </p>
79
- <ol className="sh-rows">
80
- {visible.map((post) => (
81
- <li className="sh-row" key={post.path}>
82
- <time className="num" dateTime={day(post.date)}>{day(post.date)}</time>
83
- <h3><Link href={postHref(post)}>{post.title}</Link></h3>
84
- {post.description ? <p>{post.description}</p> : null}
85
- {post.topic ? <span className="cat">{post.topic}</span> : null}
86
- </li>
87
- ))}
88
- </ol>
89
- </section>
90
- </div>
91
- <aside className="sh-rail" aria-label="说明">
92
- <h2>标签与分类</h2>
93
- <p>分类回答“这篇属于哪一块”,每篇一个;标签回答“提到了什么”,每篇可以有多个。</p>
94
- </aside>
95
- </div>
96
- );
97
- }