meowpress 0.3.0-alpha.2 → 0.3.0-alpha.4
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/README.md +71 -29
- package/dist/src/build.d.ts +21 -15
- package/dist/src/build.js +64 -42
- package/dist/src/cli.js +105 -14
- package/dist/src/content.d.ts +9 -1
- package/dist/src/content.js +86 -22
- package/dist/src/fonts.js +1 -1
- package/dist/src/identity.d.ts +6 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/output.d.ts +7 -0
- package/dist/src/output.js +111 -0
- package/dist/src/preview.d.ts +1 -0
- package/dist/src/preview.js +61 -0
- package/dist/src/site.d.ts +3 -2
- package/dist/src/site.js +154 -281
- package/dist/src/skills.d.ts +1 -0
- package/dist/src/skills.js +13 -7
- package/dist/src/theme.d.ts +12 -43
- package/dist/src/theme.js +301 -2046
- package/package.json +10 -5
package/dist/src/site.js
CHANGED
|
@@ -1,141 +1,114 @@
|
|
|
1
1
|
import { themeAttributes } from "meowcss";
|
|
2
|
+
import { createRuntime, Fragment, h } from "meowjsx";
|
|
3
|
+
import { renderToHtmlWithRuntime } from "meowjsx/web";
|
|
4
|
+
import { Button, Container, Entry, EntryList, Inline, MetaRow, Page, PageHeader, Prose, Section, Tag, Text, createMeowUI, } from "meowtail";
|
|
2
5
|
import { blogTheme } from "./theme.js";
|
|
3
|
-
import { Fragment, h } from "meowjsx/runtime";
|
|
4
|
-
import { renderToHtml } from "meowjsx/web";
|
|
5
6
|
import { withoutLeadingHeading } from "./content.js";
|
|
6
7
|
import { DEFAULT_SITE } from "./identity.js";
|
|
7
8
|
import { sitePageAgentUrl } from "./pages.js";
|
|
8
9
|
import { layoutSkillGraph } from "./skill-graph.js";
|
|
9
10
|
/** The name of the theme stylesheet the package writes by default. */
|
|
10
11
|
export const DEFAULT_THEME_STYLESHEET = "blog-theme.css";
|
|
11
|
-
/** The href of the theme stylesheet for a page at a given depth. */
|
|
12
12
|
function styleHref(depth, stylesheet) {
|
|
13
13
|
return `${"../".repeat(depth)}${stylesheet}`;
|
|
14
14
|
}
|
|
15
|
-
/** The number of directories a slug puts a page below its section. */
|
|
16
15
|
function slugDepth(slug) {
|
|
17
16
|
return slug.split("/").filter(Boolean).length;
|
|
18
17
|
}
|
|
19
18
|
function SiteHeader({ activePath, site }) {
|
|
20
|
-
|
|
19
|
+
const links = [
|
|
20
|
+
["/posts/", "Posts"],
|
|
21
|
+
["/thoughts/", "Thoughts"],
|
|
22
|
+
["/skills/", "Skills"],
|
|
23
|
+
["/about/", "About"],
|
|
24
|
+
];
|
|
25
|
+
return (h("header", { className: "site-header" },
|
|
21
26
|
h("a", { className: "site-name", href: "/" }, site.name),
|
|
22
|
-
h("nav", { className: "site-nav", "aria-label": "Primary navigation" },
|
|
23
|
-
h("a", { className: activePath === "/skills/" ? "active" : "", href: "/skills/" }, "Skills"),
|
|
24
|
-
h("a", { className: activePath === "/about/" ? "active" : "", href: "/about/" }, "About"))));
|
|
27
|
+
h("nav", { className: "site-nav", "aria-label": "Primary navigation" }, links.map(([href, label]) => (h("a", { href: href, "aria-current": activePath === href ? "page" : undefined }, label))))));
|
|
25
28
|
}
|
|
26
29
|
function SiteFooter({ site }) {
|
|
27
|
-
return (h("footer", { className: "site" },
|
|
28
|
-
h(
|
|
30
|
+
return (h("footer", { className: "site-footer" },
|
|
31
|
+
h(Text, { variant: "caption" },
|
|
29
32
|
"\u00A9 ",
|
|
30
33
|
new Date().getFullYear(),
|
|
31
34
|
" ",
|
|
32
35
|
site.name),
|
|
33
|
-
h("
|
|
36
|
+
h(Inline, { gap: "compact" },
|
|
34
37
|
h("a", { href: "/llm.txt" }, "llm.txt"),
|
|
35
|
-
" \u00B7 ",
|
|
36
38
|
h("a", { href: "/agents.md" }, "agents.md"))));
|
|
37
39
|
}
|
|
38
|
-
function Document({ title, description, activePath, styleHref, children, theme, site, }) {
|
|
39
|
-
|
|
40
|
-
return (h("html", { lang: "en", ...themeAttributes(theme) },
|
|
40
|
+
function Document({ title, description, activePath, styleHref, baseHref, wide, children, theme, site, }) {
|
|
41
|
+
return (h("html", { lang: "en", "data-meowui-page": "", ...themeAttributes(theme) },
|
|
41
42
|
h("head", null,
|
|
42
43
|
h("meta", { charSet: "UTF-8" }),
|
|
43
44
|
h("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
44
45
|
h("meta", { name: "description", content: description ?? site.description }),
|
|
45
|
-
h("
|
|
46
|
-
h("meta", { name: "theme-color", media: "(prefers-color-scheme: dark)", content: "#191816" }),
|
|
46
|
+
baseHref ? h("base", { href: baseHref }) : null,
|
|
47
47
|
h("link", { rel: "stylesheet", href: styleHref }),
|
|
48
|
-
h("title", null,
|
|
48
|
+
h("title", null, title === site.name ? site.name : `${title} · ${site.name}`)),
|
|
49
49
|
h("body", { "data-site": site.id },
|
|
50
|
-
h("
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
h("a", { className: "skip-link", href: "#main-content" }, "Skip to content"),
|
|
51
|
+
h("div", { className: "site-shell" },
|
|
52
|
+
h(Container, { width: wide ? "wide" : "content" },
|
|
53
|
+
h(SiteHeader, { activePath: activePath, site: site }),
|
|
54
|
+
h("main", { id: "main-content", tabIndex: -1 }, children),
|
|
55
|
+
h(SiteFooter, { site: site }))))));
|
|
54
56
|
}
|
|
55
57
|
function pageHtml(props) {
|
|
56
|
-
|
|
58
|
+
// Static rendering needs the same part/option adapter as a browser mount. No hydration is needed.
|
|
59
|
+
const runtime = createRuntime({
|
|
60
|
+
plugins: [
|
|
61
|
+
createMeowUI({
|
|
62
|
+
components: [
|
|
63
|
+
Button,
|
|
64
|
+
Container,
|
|
65
|
+
Entry,
|
|
66
|
+
EntryList,
|
|
67
|
+
Inline,
|
|
68
|
+
MetaRow,
|
|
69
|
+
Page,
|
|
70
|
+
PageHeader,
|
|
71
|
+
Prose,
|
|
72
|
+
Section,
|
|
73
|
+
Tag,
|
|
74
|
+
Text,
|
|
75
|
+
],
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
return `<!doctype html>\n${renderToHtmlWithRuntime(runtime, h(Document, { ...props }))}`;
|
|
57
80
|
}
|
|
58
81
|
function dateLabel(dateKey) {
|
|
59
|
-
|
|
82
|
+
const date = new Date(`${dateKey}T00:00:00Z`);
|
|
83
|
+
if (Number.isNaN(date.getTime()))
|
|
60
84
|
return { date: "Undated", weekday: "" };
|
|
61
|
-
const date = new Date(`${dateKey}T00:00:00+08:00`);
|
|
62
85
|
return {
|
|
63
86
|
date: new Intl.DateTimeFormat("en-US", {
|
|
64
|
-
timeZone: "
|
|
87
|
+
timeZone: "UTC",
|
|
65
88
|
month: "long",
|
|
66
89
|
day: "numeric",
|
|
67
90
|
}).format(date),
|
|
68
|
-
weekday: new Intl.DateTimeFormat("en-US", {
|
|
69
|
-
timeZone: "Asia/Shanghai",
|
|
70
|
-
weekday: "short",
|
|
71
|
-
year: "numeric",
|
|
72
|
-
}).format(date),
|
|
91
|
+
weekday: `${date.getUTCFullYear()} ${new Intl.DateTimeFormat("en-US", { timeZone: "UTC", weekday: "short" }).format(date)}`,
|
|
73
92
|
};
|
|
74
93
|
}
|
|
75
94
|
function itemUrl(item) {
|
|
76
95
|
return `/${item.kind}s/${item.slug}/`;
|
|
77
96
|
}
|
|
78
|
-
function Hero({ image }) {
|
|
79
|
-
return (h("section", { className: "hero", "aria-labelledby": "hero-title" },
|
|
80
|
-
h("div", { className: "hero-copy" },
|
|
81
|
-
h("h1", { id: "hero-title" },
|
|
82
|
-
"Ten thousand years is too long;",
|
|
83
|
-
h("br", null),
|
|
84
|
-
h("em", null, "only this day is true."))),
|
|
85
|
-
h("div", { className: "hero-work", "aria-hidden": "true" },
|
|
86
|
-
h("svg", { className: "hero-doodle", viewBox: "0 0 260 190" },
|
|
87
|
-
h("path", { className: "doodle-loop", d: "M24 96C20 45 58 18 122 20c72 2 116 33 114 84-2 48-48 70-113 68-65-2-96-27-99-76Z" }),
|
|
88
|
-
h("circle", { className: "doodle-sun", cx: "194", cy: "55", r: "27" }),
|
|
89
|
-
h("path", { className: "doodle-page", d: "M47 100c28-9 57-5 83 11v49c-24-13-52-16-83-7Z" }),
|
|
90
|
-
h("path", { className: "doodle-page", d: "M130 111c24-15 52-19 82-9v51c-31-7-58-4-82 7Z" }),
|
|
91
|
-
h("path", { className: "doodle-spine", d: "M130 111v49" }),
|
|
92
|
-
h("path", { className: "doodle-writing", d: "M61 118c18-3 36-1 53 5M61 132c17-2 32 0 46 5M148 119c17-4 33-5 50-1M148 133c14-3 29-3 44 0" }),
|
|
93
|
-
h("circle", { className: "doodle-dot", cx: "41", cy: "61", r: "5" }),
|
|
94
|
-
h("path", { className: "doodle-spark", d: "M72 44v12M66 50h12" })),
|
|
95
|
-
image ? h("img", { className: "hero-image", src: image, alt: "", decoding: "async" }) : null)));
|
|
96
|
-
}
|
|
97
|
-
function DoodleGithubIcon() {
|
|
98
|
-
return (h("svg", { className: "github-icon", viewBox: "0 0 48 48", "aria-hidden": "true" },
|
|
99
|
-
h("path", { className: "github-icon-ring", d: "M24 3.8C11.7 3.5 4.2 11.2 4.6 24.1c.3 12.1 7.8 20.1 19.6 20 12.2-.1 19.4-7.7 19.2-20C43.2 11.7 36.2 4.1 24 3.8Z" }),
|
|
100
|
-
h("path", { className: "github-icon-cat", d: "M14.3 18.8c-.7-2.4-.3-5.1.8-7.1 3.2.1 5.4 1.3 6.6 2.2 1.5-.4 3.1-.6 4.8-.4 1.4-1 3.7-1.9 6.7-1.8 1 2.2 1.3 4.9.5 7.1 1.5 1.8 2.2 4.1 2 6.8-.4 5.2-3.2 8.2-8.2 8.8.8.7 1.3 1.9 1.3 3.8v4.3c-1.5.4-3.1.6-4.8.6-1.4 0-2.8-.1-4.2-.5v-3.4c0-1.8.5-3.1 1.4-4-4.8-.6-7.7-3.5-8.2-8.7-.3-2.7.4-5 1.3-6.7Z" }),
|
|
101
|
-
h("path", { className: "github-icon-face", d: "M20.2 24.3h.1M29.2 24.3h.1M21.2 28.9c2.2 1.4 4.5 1.4 6.8 0" })));
|
|
102
|
-
}
|
|
103
|
-
function DoodleExternalArrow() {
|
|
104
|
-
return (h("svg", { className: "external-arrow", viewBox: "0 0 28 28", "aria-hidden": "true" },
|
|
105
|
-
h("path", { d: "M7 21 21 7M12 7h9v9" })));
|
|
106
|
-
}
|
|
107
97
|
function Tags({ item }) {
|
|
108
|
-
|
|
109
|
-
return null;
|
|
110
|
-
return (h("div", { className: "tags" }, item.tags.map((tag) => (h("span", null,
|
|
111
|
-
"#",
|
|
112
|
-
tag)))));
|
|
98
|
+
return item.tags.length ? (h(Inline, { gap: "compact" }, item.tags.map((tag) => (h(Tag, null, tag))))) : null;
|
|
113
99
|
}
|
|
114
100
|
function ThoughtCard({ item, linked = false }) {
|
|
115
|
-
|
|
116
|
-
h(
|
|
117
|
-
h("span", { className: "t" }, item.timeLabel || item.dateKey),
|
|
118
|
-
" \u00B7 Thought"),
|
|
119
|
-
h("div", { className: "body", dangerouslySetInnerHTML: { __html: withoutLeadingHeading(item.html) } }),
|
|
101
|
+
return (h(Entry, { className: "thought", "data-kind": "thought", title: item.title, href: linked ? itemUrl(item) : undefined, eyebrow: `${item.timeLabel || item.dateKey} · Thought` },
|
|
102
|
+
h(Prose, { dangerouslySetInnerHTML: { __html: withoutLeadingHeading(item.html) } }),
|
|
120
103
|
h(Tags, { item: item })));
|
|
121
|
-
if (!linked)
|
|
122
|
-
return (h("article", { className: "thought", "data-kind": "thought" }, body));
|
|
123
|
-
return (h("article", { className: "thought", "data-kind": "thought" },
|
|
124
|
-
h("a", { className: "thought-link", href: itemUrl(item) }, body)));
|
|
125
104
|
}
|
|
126
105
|
function PostCard({ item }) {
|
|
127
106
|
const category = typeof item.metadata.category === "string" ? item.metadata.category : "Post";
|
|
128
|
-
return (h(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
item.readingMinutes,
|
|
134
|
-
" min \u00B7 ",
|
|
135
|
-
item.characterCount,
|
|
136
|
-
" characters")),
|
|
137
|
-
h("h3", null, item.title),
|
|
138
|
-
h("p", { className: "excerpt" }, item.excerpt || "This post has no summary yet."),
|
|
107
|
+
return (h(Entry, { size: "feature", "data-kind": "post", title: item.title, href: itemUrl(item), eyebrow: category, description: item.excerpt || "This post has no summary yet.", meta: h(MetaRow, { items: [
|
|
108
|
+
{ label: item.dateKey },
|
|
109
|
+
{ label: `${item.readingMinutes} min read` },
|
|
110
|
+
{ label: `${item.characterCount} characters` },
|
|
111
|
+
] }) },
|
|
139
112
|
h(Tags, { item: item })));
|
|
140
113
|
}
|
|
141
114
|
function groupByDate(items) {
|
|
@@ -147,108 +120,34 @@ function groupByDate(items) {
|
|
|
147
120
|
}
|
|
148
121
|
return groups;
|
|
149
122
|
}
|
|
150
|
-
function FeedDecor({ image }) {
|
|
151
|
-
return (h("div", { className: "feed-decor", "aria-hidden": "true" },
|
|
152
|
-
h("svg", { className: "yarn-doodle", viewBox: "0 0 88 70" },
|
|
153
|
-
h("path", { className: "yarn-tail", d: "M42 48c-10 7-17 4-22 9-5 4-2 9 4 8 8-1 11-8 18-6 6 1 10 6 16 4" }),
|
|
154
|
-
h("circle", { className: "yarn-fill", cx: "48", cy: "34", r: "22" }),
|
|
155
|
-
h("path", { className: "yarn-line", d: "M31 23c11 1 22 7 34 18M28 34c12-7 24-9 39-5M34 49c7-14 16-24 28-31M28 42c13 1 24 5 33 12" })),
|
|
156
|
-
h("svg", { className: "paw-trail", viewBox: "0 0 130 82" },
|
|
157
|
-
h("g", { className: "paw-print", transform: "translate(12 54) rotate(-18)" },
|
|
158
|
-
h("ellipse", { cx: "9", cy: "11", rx: "6", ry: "5" }),
|
|
159
|
-
h("circle", { cx: "3", cy: "3", r: "2.5" }),
|
|
160
|
-
h("circle", { cx: "9", cy: "1", r: "2.5" }),
|
|
161
|
-
h("circle", { cx: "15", cy: "3", r: "2.5" })),
|
|
162
|
-
h("g", { className: "paw-print", transform: "translate(45 37) rotate(8)" },
|
|
163
|
-
h("ellipse", { cx: "9", cy: "11", rx: "6", ry: "5" }),
|
|
164
|
-
h("circle", { cx: "3", cy: "3", r: "2.5" }),
|
|
165
|
-
h("circle", { cx: "9", cy: "1", r: "2.5" }),
|
|
166
|
-
h("circle", { cx: "15", cy: "3", r: "2.5" })),
|
|
167
|
-
h("g", { className: "paw-print", transform: "translate(79 19) rotate(-12)" },
|
|
168
|
-
h("ellipse", { cx: "9", cy: "11", rx: "6", ry: "5" }),
|
|
169
|
-
h("circle", { cx: "3", cy: "3", r: "2.5" }),
|
|
170
|
-
h("circle", { cx: "9", cy: "1", r: "2.5" }),
|
|
171
|
-
h("circle", { cx: "15", cy: "3", r: "2.5" })),
|
|
172
|
-
h("g", { className: "paw-print paw-print-accent", transform: "translate(111 5) rotate(11)" },
|
|
173
|
-
h("ellipse", { cx: "7", cy: "9", rx: "5", ry: "4" }),
|
|
174
|
-
h("circle", { cx: "2", cy: "2", r: "2" }),
|
|
175
|
-
h("circle", { cx: "7", cy: "1", r: "2" }),
|
|
176
|
-
h("circle", { cx: "12", cy: "2", r: "2" }))),
|
|
177
|
-
image ? h("img", { className: "feed-image", src: image, alt: "", decoding: "async" }) : null));
|
|
178
|
-
}
|
|
179
123
|
function Feed({ items, image }) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
h("p", { className: "empty-state" }, "No public content yet."),
|
|
183
|
-
h(FeedDecor, { image: image })));
|
|
184
|
-
}
|
|
185
|
-
return (h("main", { className: "feed" },
|
|
186
|
-
[...groupByDate(items)].map(([dateKey, group]) => {
|
|
124
|
+
return (h("div", { className: "feed" },
|
|
125
|
+
items.length === 0 ? (h(Text, { variant: "secondary" }, "No public content yet.")) : ([...groupByDate(items)].map(([dateKey, group]) => {
|
|
187
126
|
const label = dateLabel(dateKey);
|
|
188
|
-
return (h(
|
|
189
|
-
h("
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
h("span", { className: "weekday" }, label.weekday)),
|
|
193
|
-
group.map((item) => item.kind === "thought" ? (h(ThoughtCard, { item: item, linked: true })) : (h(PostCard, { item: item })))));
|
|
194
|
-
}),
|
|
195
|
-
h(FeedDecor, { image: image })));
|
|
127
|
+
return (h(Section, { className: "day", title: label.date, description: label.weekday },
|
|
128
|
+
h(EntryList, null, group.map((item) => item.kind === "thought" ? (h(ThoughtCard, { item: item, linked: true })) : (h(PostCard, { item: item }))))));
|
|
129
|
+
})),
|
|
130
|
+
image ? (h("img", { className: "feed-image", src: image, alt: "", loading: "lazy", decoding: "async" })) : null));
|
|
196
131
|
}
|
|
197
132
|
function Filter({ posts, thoughts }) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
h("button", { type: "button", "data-filter": "thought", "aria-pressed": "false" },
|
|
207
|
-
"Thoughts",
|
|
208
|
-
h("i", null, thoughts))));
|
|
133
|
+
return (h(Inline, { className: "feed-filter", gap: "compact", "data-filter-group": "feed", role: "group", "aria-label": "Filter home feed" }, [
|
|
134
|
+
["all", "All", posts + thoughts],
|
|
135
|
+
["post", "Posts", posts],
|
|
136
|
+
["thought", "Thoughts", thoughts],
|
|
137
|
+
].map(([value, label, count]) => (h(Button, { variant: "quiet", "data-filter": value, "aria-pressed": String(value === "all") },
|
|
138
|
+
label,
|
|
139
|
+
" ",
|
|
140
|
+
h("span", { className: "filter-count" }, count))))));
|
|
209
141
|
}
|
|
210
142
|
const FILTER_SCRIPT = `(() => {
|
|
211
143
|
const links = document.querySelectorAll('[data-filter-group="feed"] [data-filter]');
|
|
212
144
|
const items = document.querySelectorAll('.feed [data-kind]');
|
|
213
145
|
const days = document.querySelectorAll('.feed .day');
|
|
214
|
-
|
|
215
|
-
if ('IntersectionObserver' in window && !reduced) {
|
|
216
|
-
const observer = new IntersectionObserver((entries) => {
|
|
217
|
-
entries.forEach((entry) => {
|
|
218
|
-
if (entry.isIntersecting) {
|
|
219
|
-
entry.target.classList.add('in');
|
|
220
|
-
observer.unobserve(entry.target);
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
}, { threshold: 0.08 });
|
|
224
|
-
days.forEach((day) => observer.observe(day));
|
|
225
|
-
} else {
|
|
226
|
-
days.forEach((day) => day.classList.add('in'));
|
|
227
|
-
}
|
|
228
|
-
links.forEach((link) => link.addEventListener('click', (event) => {
|
|
229
|
-
event.preventDefault();
|
|
146
|
+
links.forEach((link) => link.addEventListener('click', () => {
|
|
230
147
|
const filter = link.dataset.filter;
|
|
231
|
-
links.forEach((
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
item.setAttribute('aria-pressed', String(active));
|
|
235
|
-
});
|
|
236
|
-
items.forEach((item) => {
|
|
237
|
-
item.style.display = filter === 'all' || item.dataset.kind === filter ? '' : 'none';
|
|
238
|
-
});
|
|
239
|
-
days.forEach((day) => {
|
|
240
|
-
const visible = Array.from(day.querySelectorAll('[data-kind]'))
|
|
241
|
-
.some((item) => item.style.display !== 'none');
|
|
242
|
-
day.style.display = visible ? '' : 'none';
|
|
243
|
-
if (!visible) day.classList.remove('in');
|
|
244
|
-
});
|
|
245
|
-
if (!reduced) {
|
|
246
|
-
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
247
|
-
days.forEach((day) => {
|
|
248
|
-
if (day.style.display !== 'none') day.classList.add('in');
|
|
249
|
-
});
|
|
250
|
-
}));
|
|
251
|
-
}
|
|
148
|
+
links.forEach((button) => button.setAttribute('aria-pressed', String(button === link)));
|
|
149
|
+
items.forEach((item) => { item.hidden = filter !== 'all' && item.dataset.kind !== filter; });
|
|
150
|
+
days.forEach((day) => { day.hidden = !Array.from(day.querySelectorAll('[data-kind]')).some((item) => !item.hidden); });
|
|
252
151
|
}));
|
|
253
152
|
})();`;
|
|
254
153
|
export function homePage(items, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
@@ -257,29 +156,42 @@ export function homePage(items, theme = blogTheme, site = DEFAULT_SITE, styleshe
|
|
|
257
156
|
theme,
|
|
258
157
|
title: site.name,
|
|
259
158
|
styleHref: styleHref(0, stylesheet),
|
|
260
|
-
children: (h(
|
|
261
|
-
|
|
262
|
-
|
|
159
|
+
children: (h(Page, { header: h("div", { className: "home-heading" },
|
|
160
|
+
h("div", { className: "home-heading-copy" },
|
|
161
|
+
h(Text, { variant: "eyebrow" }, "Journal"),
|
|
162
|
+
h(PageHeader, { title: site.name, description: site.description })),
|
|
163
|
+
site.images?.hero ? (h("img", { className: "hero-image", src: `./${site.images.hero}`, alt: "", decoding: "async" })) : null), toolbar: h(Filter, { posts: items.filter((item) => item.kind === "post").length, thoughts: items.filter((item) => item.kind === "thought").length }) },
|
|
263
164
|
h(Feed, { items: items, image: site.images?.feed ? `./${site.images.feed}` : undefined }),
|
|
264
165
|
h("script", { dangerouslySetInnerHTML: { __html: FILTER_SCRIPT } }))),
|
|
265
166
|
});
|
|
266
167
|
}
|
|
267
168
|
export function listPage(kind, items, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
268
169
|
const isPost = kind === "post";
|
|
170
|
+
const title = isPost ? "Posts" : "Thoughts";
|
|
269
171
|
return pageHtml({
|
|
270
172
|
site,
|
|
271
173
|
theme,
|
|
272
|
-
title
|
|
174
|
+
title,
|
|
273
175
|
activePath: isPost ? "/posts/" : "/thoughts/",
|
|
274
176
|
styleHref: styleHref(1, stylesheet),
|
|
275
|
-
children: (h(
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
177
|
+
children: (h(Page, { title: title, eyebrow: "Archive", description: isPost
|
|
178
|
+
? "Complete arguments grown from fragments."
|
|
179
|
+
: "Short, trimmed thoughts ready to show as they are." }, items.length ? (h(EntryList, null, items.map((item) => isPost ? h(PostCard, { item: item }) : h(ThoughtCard, { item: item, linked: true })))) : (h(Text, { variant: "secondary" }, "No public content yet.")))),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
export function notFoundPage(copy, theme, site, stylesheet) {
|
|
183
|
+
const title = copy.title ?? "Page not found";
|
|
184
|
+
const message = copy.message ?? "This page may have moved or been removed.";
|
|
185
|
+
return pageHtml({
|
|
186
|
+
site,
|
|
187
|
+
theme,
|
|
188
|
+
title,
|
|
189
|
+
description: message,
|
|
190
|
+
styleHref: styleHref(0, stylesheet),
|
|
191
|
+
baseHref: "/",
|
|
192
|
+
children: (h(Page, { eyebrow: "404", title: title, description: message },
|
|
193
|
+
h(Prose, null,
|
|
194
|
+
h("a", { href: "/" }, copy.homeLabel ?? "Back to home")))),
|
|
283
195
|
});
|
|
284
196
|
}
|
|
285
197
|
function PostNavigation({ item, posts }) {
|
|
@@ -287,22 +199,13 @@ function PostNavigation({ item, posts }) {
|
|
|
287
199
|
const older = index >= 0 ? posts[index + 1] : undefined;
|
|
288
200
|
const newer = index > 0 ? posts[index - 1] : undefined;
|
|
289
201
|
return (h("nav", { className: "post-nav", "aria-label": "Post navigation" },
|
|
290
|
-
older ? (h("
|
|
291
|
-
|
|
292
|
-
older.title)) : (h("span", null)),
|
|
293
|
-
newer ? (h("a", { className: "next", href: itemUrl(newer) },
|
|
294
|
-
h("span", { className: "label" }, "Next post (newer) \u2192"),
|
|
295
|
-
newer.title)) : (h("span", { className: "next" },
|
|
296
|
-
h("span", { className: "label" }, "Next post (newer) \u2192"),
|
|
297
|
-
"Nothing newer \u2014 this is the latest post."))));
|
|
202
|
+
older ? (h(Entry, { eyebrow: "\u2190 Previous post (older)", title: older.title, href: itemUrl(older) })) : (h("span", null)),
|
|
203
|
+
newer ? (h(Entry, { eyebrow: "Next post (newer) \u2192", title: newer.title, href: itemUrl(newer) })) : (h(Text, { variant: "caption" }, "Nothing newer \u2014 this is the latest post."))));
|
|
298
204
|
}
|
|
299
205
|
function RelatedThoughts({ item, thoughts, }) {
|
|
300
206
|
const related = thoughts.filter((thought) => thought.dateKey === item.dateKey).slice(0, 3);
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return (h("aside", { className: "related-thoughts" },
|
|
304
|
-
h("h2", null, "Thoughts from the same day"),
|
|
305
|
-
related.map((thought) => (h(ThoughtCard, { item: thought, linked: true })))));
|
|
207
|
+
return related.length ? (h(Section, { title: "Thoughts from the same day" },
|
|
208
|
+
h(EntryList, null, related.map((thought) => (h(ThoughtCard, { item: thought, linked: true })))))) : null;
|
|
306
209
|
}
|
|
307
210
|
export function postPage(item, posts, thoughts, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
308
211
|
const category = typeof item.metadata.category === "string" ? item.metadata.category : "Post";
|
|
@@ -313,21 +216,16 @@ export function postPage(item, posts, thoughts, theme = blogTheme, site = DEFAUL
|
|
|
313
216
|
description: item.excerpt,
|
|
314
217
|
activePath: "/posts/",
|
|
315
218
|
styleHref: styleHref(1 + slugDepth(item.slug), stylesheet),
|
|
316
|
-
children: (h(
|
|
317
|
-
h(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
item.characterCount,
|
|
327
|
-
" characters")),
|
|
328
|
-
h("div", { className: "prose", dangerouslySetInnerHTML: { __html: item.html } }),
|
|
329
|
-
h(RelatedThoughts, { item: item, thoughts: thoughts }),
|
|
330
|
-
h(PostNavigation, { item: item, posts: posts })))),
|
|
219
|
+
children: (h(Page, { title: item.title, eyebrow: category },
|
|
220
|
+
h(MetaRow, { items: [
|
|
221
|
+
{ label: item.dateKey },
|
|
222
|
+
{ label: `${item.readingMinutes} min read` },
|
|
223
|
+
{ label: `${item.characterCount} characters` },
|
|
224
|
+
] }),
|
|
225
|
+
h(Prose, { as: "article", dangerouslySetInnerHTML: { __html: withoutLeadingHeading(item.html) } }),
|
|
226
|
+
h(Tags, { item: item }),
|
|
227
|
+
h(RelatedThoughts, { item: item, thoughts: thoughts }),
|
|
228
|
+
h(PostNavigation, { item: item, posts: posts }))),
|
|
331
229
|
});
|
|
332
230
|
}
|
|
333
231
|
export function thoughtPage(item, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
@@ -338,15 +236,10 @@ export function thoughtPage(item, theme = blogTheme, site = DEFAULT_SITE, styles
|
|
|
338
236
|
description: item.excerpt,
|
|
339
237
|
activePath: "/thoughts/",
|
|
340
238
|
styleHref: styleHref(1 + slugDepth(item.slug), stylesheet),
|
|
341
|
-
children: (h("
|
|
342
|
-
h(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
h("h1", null, item.title),
|
|
346
|
-
h("div", { className: "meta" },
|
|
347
|
-
item.dateKey,
|
|
348
|
-
item.timeLabel ? ` · ${item.timeLabel}` : "")),
|
|
349
|
-
h("div", { className: "prose", dangerouslySetInnerHTML: { __html: item.html } })))),
|
|
239
|
+
children: (h(Page, { title: item.title, eyebrow: "Thought" },
|
|
240
|
+
h(MetaRow, { items: [{ label: item.dateKey }, ...(item.timeLabel ? [{ label: item.timeLabel }] : [])] }),
|
|
241
|
+
h(Prose, { as: "article", dangerouslySetInnerHTML: { __html: withoutLeadingHeading(item.html) } }),
|
|
242
|
+
h(Tags, { item: item }))),
|
|
350
243
|
});
|
|
351
244
|
}
|
|
352
245
|
export function aboutPage(theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
@@ -357,19 +250,13 @@ export function aboutPage(theme = blogTheme, site = DEFAULT_SITE, stylesheet = D
|
|
|
357
250
|
title: "About",
|
|
358
251
|
activePath: "/about/",
|
|
359
252
|
styleHref: styleHref(1, stylesheet),
|
|
360
|
-
children: (h(
|
|
361
|
-
h("div", { className: "avatar", "aria-hidden": "true" }, site.images?.about ? (h("img", { src: `../${site.images.about}`, alt: "", decoding: "async" })) : (h("span", { className: "avatar-initial" }, Array.from(site.name.trim())[0] ?? "M"))),
|
|
362
|
-
about ? (h(
|
|
363
|
-
|
|
364
|
-
about.
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
about.links.map((link) => (h("a", { className: "github-link", href: link.href, target: "_blank", rel: "me noopener noreferrer" },
|
|
368
|
-
h(DoodleGithubIcon, null),
|
|
369
|
-
h("span", { className: "github-copy" },
|
|
370
|
-
h("span", null, link.label),
|
|
371
|
-
h("strong", null, link.handle)),
|
|
372
|
-
h(DoodleExternalArrow, null)))))) : null)) : (h("h1", null, site.name)))),
|
|
253
|
+
children: (h(Page, { eyebrow: "About", title: about?.heading ?? site.name },
|
|
254
|
+
h("div", { className: "site-avatar", "aria-hidden": "true" }, site.images?.about ? (h("img", { src: `../${site.images.about}`, alt: "", decoding: "async" })) : (h("span", { className: "avatar-initial" }, Array.from(site.name.trim())[0] ?? "M"))),
|
|
255
|
+
about ? (h(Prose, null, about.paragraphs.map((paragraph) => (h("p", null, paragraph))))) : null,
|
|
256
|
+
about?.links?.length ? (h(Section, { title: about.linksHeading ?? "Find me" },
|
|
257
|
+
h(EntryList, null, about.links.map((link) => (h(Entry, { title: h("a", { href: link.href, target: "_blank", rel: "me noopener noreferrer" },
|
|
258
|
+
link.handle,
|
|
259
|
+
" \u2197"), eyebrow: link.label })))))) : null)),
|
|
373
260
|
});
|
|
374
261
|
}
|
|
375
262
|
function skillUrl(skill) {
|
|
@@ -381,20 +268,12 @@ function skillInstallCommand(site, skill) {
|
|
|
381
268
|
: skill.sourceBasePath;
|
|
382
269
|
}
|
|
383
270
|
function SkillCard({ skill, site }) {
|
|
384
|
-
return (h(
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
h(
|
|
389
|
-
h("
|
|
390
|
-
h("span", null, skill.description)),
|
|
391
|
-
h("span", { className: "skill-card-foot" },
|
|
392
|
-
h("span", null,
|
|
393
|
-
skill.files.length,
|
|
394
|
-
" Markdown files \u00B7 ",
|
|
395
|
-
skill.relations.length,
|
|
396
|
-
" references"),
|
|
397
|
-
h("span", { className: "skill-open" }, "Open directory\u00A0 \u2192"))));
|
|
271
|
+
return (h(Entry, { href: skillUrl(skill), title: skill.name, description: skill.description, eyebrow: skill.scope === "project" ? "Project Skill" : "Public Skill", meta: h(MetaRow, { items: [
|
|
272
|
+
{ label: `${skill.files.length} Markdown files` },
|
|
273
|
+
{ label: `${skill.relations.length} references` },
|
|
274
|
+
] }) },
|
|
275
|
+
h(Text, { variant: "caption" },
|
|
276
|
+
h("code", { className: "skill-command-text" }, skill.scope === "public" ? skillInstallCommand(site, skill) : skill.sourceBasePath))));
|
|
398
277
|
}
|
|
399
278
|
function skillTree(files) {
|
|
400
279
|
const root = { name: "", children: [] };
|
|
@@ -438,7 +317,7 @@ function SkillGraphNodeView({ node }) {
|
|
|
438
317
|
function SkillRelationGraph({ skill, direction }) {
|
|
439
318
|
const graph = layoutSkillGraph(skill, direction);
|
|
440
319
|
const markerId = `skill-arrow-${skill.slug}-${direction.toLowerCase()}`;
|
|
441
|
-
return (h("svg", { className: `skill-graph-svg skill-graph-${direction.toLowerCase()}`, viewBox: `0 0 ${graph.width} ${graph.height}`, "aria-label": `${skill.name} file reference graph` },
|
|
320
|
+
return (h("svg", { className: `skill-graph-svg skill-graph-${direction.toLowerCase()}`, viewBox: `0 0 ${graph.width} ${graph.height}`, width: graph.width, height: graph.height, "aria-label": `${skill.name} file reference graph` },
|
|
442
321
|
h("defs", null,
|
|
443
322
|
h("marker", { id: markerId, viewBox: "0 0 10 10", refX: "8", refY: "5", markerWidth: "5", markerHeight: "5", orient: "auto-start-reverse" },
|
|
444
323
|
h("path", { className: "skill-graph-arrow", d: "M 0 0 L 10 5 L 0 10 z" }))),
|
|
@@ -478,9 +357,8 @@ function SkillRelations({ skill }) {
|
|
|
478
357
|
h("strong", null, missingNodes.length),
|
|
479
358
|
" missing targets")),
|
|
480
359
|
hasRelations ? (h(Fragment, null,
|
|
481
|
-
h("div", { className: `skill-graph-canvas${compactGraph ? " compact" : ""}
|
|
482
|
-
h(SkillRelationGraph, { skill: skill, direction: "LR" }),
|
|
483
|
-
h(SkillRelationGraph, { skill: skill, direction: "TB" })),
|
|
360
|
+
h("div", { className: `skill-graph-canvas${compactGraph ? " compact" : ""}`, tabIndex: 0, role: "region", "aria-label": "Scrollable file reference graph" },
|
|
361
|
+
h(SkillRelationGraph, { skill: skill, direction: "LR" })),
|
|
484
362
|
h("section", { className: "skill-reading-paths", "aria-labelledby": `${skill.slug}-paths` },
|
|
485
363
|
h("h3", { id: `${skill.slug}-paths` }, "Where references happen"),
|
|
486
364
|
h("ol", null, skill.relations.map((relation) => {
|
|
@@ -615,24 +493,11 @@ export function skillsIndexPage(skills, theme = blogTheme, site = DEFAULT_SITE,
|
|
|
615
493
|
description: "Methods and working conventions that can be handed to an Agent.",
|
|
616
494
|
activePath: "/skills/",
|
|
617
495
|
styleHref: styleHref(1, stylesheet),
|
|
618
|
-
children: (h(
|
|
619
|
-
h("
|
|
620
|
-
|
|
621
|
-
h(
|
|
622
|
-
|
|
623
|
-
h("section", { className: "skill-group", "aria-labelledby": "public-skills-heading" },
|
|
624
|
-
h("header", { className: "skill-group-head" },
|
|
625
|
-
h("h2", { id: "public-skills-heading" }, "Public Skills"),
|
|
626
|
-
h("span", null, "Public content")),
|
|
627
|
-
publicSkills.length > 0 ? (h("div", { className: "skill-list" }, publicSkills.map((skill) => (h(SkillCard, { skill: skill, site: site }))))) : (h("p", { className: "skill-group-empty" }, "Public Skills have no content yet."))),
|
|
628
|
-
projectSkills.length > 0 ? (h("section", { className: "skill-group project-skills", "aria-labelledby": "project-skills-heading" },
|
|
629
|
-
h("header", { className: "skill-group-head" },
|
|
630
|
-
h("div", null,
|
|
631
|
-
h("h2", { id: "project-skills-heading" }, "Project Skills"),
|
|
632
|
-
h("small", null, "Development and content workflows used inside this project")),
|
|
633
|
-
h("span", null, ".agents/skills/")),
|
|
634
|
-
h("div", { className: "skill-list" }, projectSkills.map((skill) => (h(SkillCard, { skill: skill, site: site })))))) : null,
|
|
635
|
-
h("p", { className: "skills-prototype-note" }, "The reference graph starts from SKILL.md and is generated at build time from Markdown links."))),
|
|
496
|
+
children: (h(Page, { eyebrow: "~/skills", title: "Skills", description: "Methods shared publicly and usable directly by an Agent." },
|
|
497
|
+
h(Section, { title: "Public Skills" }, publicSkills.length ? (h(EntryList, null, publicSkills.map((skill) => (h(SkillCard, { skill: skill, site: site }))))) : (h(Text, { variant: "secondary" }, "Public Skills have no content yet."))),
|
|
498
|
+
projectSkills.length ? (h(Section, { title: "Project Skills", description: "Development and content workflows used inside this project" },
|
|
499
|
+
h(EntryList, null, projectSkills.map((skill) => (h(SkillCard, { skill: skill, site: site })))))) : null,
|
|
500
|
+
h(Text, { variant: "caption" }, "The reference graph starts from SKILL.md and is generated at build time from Markdown links."))),
|
|
636
501
|
});
|
|
637
502
|
}
|
|
638
503
|
export function skillPage(skill, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
@@ -642,12 +507,13 @@ export function skillPage(skill, theme = blogTheme, site = DEFAULT_SITE, stylesh
|
|
|
642
507
|
site,
|
|
643
508
|
theme,
|
|
644
509
|
title: skill.name,
|
|
510
|
+
wide: true,
|
|
645
511
|
description: skill.description,
|
|
646
512
|
activePath: "/skills/",
|
|
647
513
|
styleHref: skill.scope === "public"
|
|
648
514
|
? styleHref(1 + slugDepth(skill.slug), stylesheet)
|
|
649
515
|
: styleHref(2 + slugDepth(skill.slug), stylesheet),
|
|
650
|
-
children: (h("
|
|
516
|
+
children: (h(Page, { width: "full", className: "skill-page", title: skill.name, eyebrow: "Skill", description: skill.description },
|
|
651
517
|
h("a", { className: "skill-back", href: "/skills/" }, "\u2190 All Skills"),
|
|
652
518
|
h("header", { className: "skill-browser-head" },
|
|
653
519
|
h("span", { className: "skill-command" },
|
|
@@ -666,14 +532,17 @@ export function skillPage(skill, theme = blogTheme, site = DEFAULT_SITE, stylesh
|
|
|
666
532
|
h("header", { className: "skill-viewer-bar" },
|
|
667
533
|
h("span", { "data-current-file": true }, firstFile.path),
|
|
668
534
|
h("span", null, "Markdown")),
|
|
669
|
-
skill.files.map((file, index) => (h("article",
|
|
670
|
-
|
|
535
|
+
skill.files.map((file, index) => (h(Prose, { as: "article", className: `skill-file-panel${index === 0 ? " active" : ""}`, "data-skill-panel": file.path, hidden: index !== 0, dangerouslySetInnerHTML: {
|
|
536
|
+
// The page owns h1; file headings nest under it without dropping any content.
|
|
537
|
+
__html: Bun.markdown
|
|
538
|
+
.html(file.content, { tagFilter: true })
|
|
539
|
+
.replace(/<(\/?)h([1-6])(\s[^>]*)?>/g, (_, closing, level, attributes = "") => `<${closing}h${Math.min(6, Number(level) + 1)}${attributes}>`),
|
|
671
540
|
} }))))),
|
|
672
541
|
h(SkillRelations, { skill: skill })),
|
|
673
542
|
h("script", { dangerouslySetInnerHTML: { __html: SKILL_BROWSER_SCRIPT } }))),
|
|
674
543
|
});
|
|
675
544
|
}
|
|
676
|
-
export function agentMarkdown(posts, thoughts, site = DEFAULT_SITE, pages) {
|
|
545
|
+
export function agentMarkdown(posts, thoughts, site = DEFAULT_SITE, pages, mappedSections = false) {
|
|
677
546
|
const siteMap = pages
|
|
678
547
|
? pages.map((page) => `- [${page.title}](${sitePageAgentUrl(page)})`)
|
|
679
548
|
: [
|
|
@@ -694,8 +563,12 @@ export function agentMarkdown(posts, thoughts, site = DEFAULT_SITE, pages) {
|
|
|
694
563
|
"",
|
|
695
564
|
"## Content model",
|
|
696
565
|
"",
|
|
697
|
-
|
|
698
|
-
|
|
566
|
+
mappedSections
|
|
567
|
+
? "- Thoughts are trimmed, directly presentable notes."
|
|
568
|
+
: `- \`${site.contentDirectory}/thoughts/\` stores trimmed, directly presentable short thoughts.`,
|
|
569
|
+
mappedSections
|
|
570
|
+
? "- Posts are full articles. Post files whose name or path segment starts with `_` are drafts."
|
|
571
|
+
: `- \`${site.contentDirectory}/posts/\` stores full posts; files whose name or path segment starts with \`_\` are drafts and are not published or rendered.`,
|
|
699
572
|
"- Content files support `.md` and `.mdx`, and may start with YAML frontmatter.",
|
|
700
573
|
"- Frontmatter currently has no required fields; `title`, `date`, `created_at`, `category` and `tags` are recognized by the site.",
|
|
701
574
|
"",
|