meowpress 0.3.0-alpha.3 → 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 +13 -4
- package/dist/src/build.d.ts +2 -2
- package/dist/src/fonts.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/site.js +130 -282
- 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,142 +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, baseHref, 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("meta", { name: "theme-color", media: "(prefers-color-scheme: light)", content: "#faf8f3" }),
|
|
46
|
-
h("meta", { name: "theme-color", media: "(prefers-color-scheme: dark)", content: "#191816" }),
|
|
47
46
|
baseHref ? h("base", { href: baseHref }) : null,
|
|
48
47
|
h("link", { rel: "stylesheet", href: styleHref }),
|
|
49
|
-
h("title", null,
|
|
48
|
+
h("title", null, title === site.name ? site.name : `${title} · ${site.name}`)),
|
|
50
49
|
h("body", { "data-site": site.id },
|
|
51
|
-
h("
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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 }))))));
|
|
55
56
|
}
|
|
56
57
|
function pageHtml(props) {
|
|
57
|
-
|
|
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 }))}`;
|
|
58
80
|
}
|
|
59
81
|
function dateLabel(dateKey) {
|
|
60
82
|
const date = new Date(`${dateKey}T00:00:00Z`);
|
|
61
83
|
if (Number.isNaN(date.getTime()))
|
|
62
84
|
return { date: "Undated", weekday: "" };
|
|
63
|
-
const weekday = new Intl.DateTimeFormat("en-US", {
|
|
64
|
-
timeZone: "UTC",
|
|
65
|
-
weekday: "short",
|
|
66
|
-
}).format(date);
|
|
67
85
|
return {
|
|
68
86
|
date: new Intl.DateTimeFormat("en-US", {
|
|
69
87
|
timeZone: "UTC",
|
|
70
88
|
month: "long",
|
|
71
89
|
day: "numeric",
|
|
72
90
|
}).format(date),
|
|
73
|
-
weekday: `${date.getUTCFullYear()} ${weekday}`,
|
|
91
|
+
weekday: `${date.getUTCFullYear()} ${new Intl.DateTimeFormat("en-US", { timeZone: "UTC", weekday: "short" }).format(date)}`,
|
|
74
92
|
};
|
|
75
93
|
}
|
|
76
94
|
function itemUrl(item) {
|
|
77
95
|
return `/${item.kind}s/${item.slug}/`;
|
|
78
96
|
}
|
|
79
|
-
function Hero({ image }) {
|
|
80
|
-
return (h("section", { className: "hero", "aria-labelledby": "hero-title" },
|
|
81
|
-
h("div", { className: "hero-copy" },
|
|
82
|
-
h("h1", { id: "hero-title" },
|
|
83
|
-
"Ten thousand years is too long;",
|
|
84
|
-
h("br", null),
|
|
85
|
-
h("em", null, "only this day is true."))),
|
|
86
|
-
h("div", { className: "hero-work", "aria-hidden": "true" },
|
|
87
|
-
h("svg", { className: "hero-doodle", viewBox: "0 0 260 190" },
|
|
88
|
-
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" }),
|
|
89
|
-
h("circle", { className: "doodle-sun", cx: "194", cy: "55", r: "27" }),
|
|
90
|
-
h("path", { className: "doodle-page", d: "M47 100c28-9 57-5 83 11v49c-24-13-52-16-83-7Z" }),
|
|
91
|
-
h("path", { className: "doodle-page", d: "M130 111c24-15 52-19 82-9v51c-31-7-58-4-82 7Z" }),
|
|
92
|
-
h("path", { className: "doodle-spine", d: "M130 111v49" }),
|
|
93
|
-
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" }),
|
|
94
|
-
h("circle", { className: "doodle-dot", cx: "41", cy: "61", r: "5" }),
|
|
95
|
-
h("path", { className: "doodle-spark", d: "M72 44v12M66 50h12" })),
|
|
96
|
-
image ? h("img", { className: "hero-image", src: image, alt: "", decoding: "async" }) : null)));
|
|
97
|
-
}
|
|
98
|
-
function DoodleGithubIcon() {
|
|
99
|
-
return (h("svg", { className: "github-icon", viewBox: "0 0 48 48", "aria-hidden": "true" },
|
|
100
|
-
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" }),
|
|
101
|
-
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" }),
|
|
102
|
-
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" })));
|
|
103
|
-
}
|
|
104
|
-
function DoodleExternalArrow() {
|
|
105
|
-
return (h("svg", { className: "external-arrow", viewBox: "0 0 28 28", "aria-hidden": "true" },
|
|
106
|
-
h("path", { d: "M7 21 21 7M12 7h9v9" })));
|
|
107
|
-
}
|
|
108
97
|
function Tags({ item }) {
|
|
109
|
-
|
|
110
|
-
return null;
|
|
111
|
-
return (h("div", { className: "tags" }, item.tags.map((tag) => (h("span", null,
|
|
112
|
-
"#",
|
|
113
|
-
tag)))));
|
|
98
|
+
return item.tags.length ? (h(Inline, { gap: "compact" }, item.tags.map((tag) => (h(Tag, null, tag))))) : null;
|
|
114
99
|
}
|
|
115
100
|
function ThoughtCard({ item, linked = false }) {
|
|
116
|
-
|
|
117
|
-
h(
|
|
118
|
-
h("span", { className: "t" }, item.timeLabel || item.dateKey),
|
|
119
|
-
" \u00B7 Thought"),
|
|
120
|
-
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) } }),
|
|
121
103
|
h(Tags, { item: item })));
|
|
122
|
-
if (!linked)
|
|
123
|
-
return (h("article", { className: "thought", "data-kind": "thought" }, body));
|
|
124
|
-
return (h("article", { className: "thought", "data-kind": "thought" },
|
|
125
|
-
h("a", { className: "thought-link", href: itemUrl(item) }, body)));
|
|
126
104
|
}
|
|
127
105
|
function PostCard({ item }) {
|
|
128
106
|
const category = typeof item.metadata.category === "string" ? item.metadata.category : "Post";
|
|
129
|
-
return (h(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
item.readingMinutes,
|
|
135
|
-
" min \u00B7 ",
|
|
136
|
-
item.characterCount,
|
|
137
|
-
" characters")),
|
|
138
|
-
h("h3", null, item.title),
|
|
139
|
-
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
|
+
] }) },
|
|
140
112
|
h(Tags, { item: item })));
|
|
141
113
|
}
|
|
142
114
|
function groupByDate(items) {
|
|
@@ -148,108 +120,34 @@ function groupByDate(items) {
|
|
|
148
120
|
}
|
|
149
121
|
return groups;
|
|
150
122
|
}
|
|
151
|
-
function FeedDecor({ image }) {
|
|
152
|
-
return (h("div", { className: "feed-decor", "aria-hidden": "true" },
|
|
153
|
-
h("svg", { className: "yarn-doodle", viewBox: "0 0 88 70" },
|
|
154
|
-
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" }),
|
|
155
|
-
h("circle", { className: "yarn-fill", cx: "48", cy: "34", r: "22" }),
|
|
156
|
-
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" })),
|
|
157
|
-
h("svg", { className: "paw-trail", viewBox: "0 0 130 82" },
|
|
158
|
-
h("g", { className: "paw-print", transform: "translate(12 54) rotate(-18)" },
|
|
159
|
-
h("ellipse", { cx: "9", cy: "11", rx: "6", ry: "5" }),
|
|
160
|
-
h("circle", { cx: "3", cy: "3", r: "2.5" }),
|
|
161
|
-
h("circle", { cx: "9", cy: "1", r: "2.5" }),
|
|
162
|
-
h("circle", { cx: "15", cy: "3", r: "2.5" })),
|
|
163
|
-
h("g", { className: "paw-print", transform: "translate(45 37) rotate(8)" },
|
|
164
|
-
h("ellipse", { cx: "9", cy: "11", rx: "6", ry: "5" }),
|
|
165
|
-
h("circle", { cx: "3", cy: "3", r: "2.5" }),
|
|
166
|
-
h("circle", { cx: "9", cy: "1", r: "2.5" }),
|
|
167
|
-
h("circle", { cx: "15", cy: "3", r: "2.5" })),
|
|
168
|
-
h("g", { className: "paw-print", transform: "translate(79 19) rotate(-12)" },
|
|
169
|
-
h("ellipse", { cx: "9", cy: "11", rx: "6", ry: "5" }),
|
|
170
|
-
h("circle", { cx: "3", cy: "3", r: "2.5" }),
|
|
171
|
-
h("circle", { cx: "9", cy: "1", r: "2.5" }),
|
|
172
|
-
h("circle", { cx: "15", cy: "3", r: "2.5" })),
|
|
173
|
-
h("g", { className: "paw-print paw-print-accent", transform: "translate(111 5) rotate(11)" },
|
|
174
|
-
h("ellipse", { cx: "7", cy: "9", rx: "5", ry: "4" }),
|
|
175
|
-
h("circle", { cx: "2", cy: "2", r: "2" }),
|
|
176
|
-
h("circle", { cx: "7", cy: "1", r: "2" }),
|
|
177
|
-
h("circle", { cx: "12", cy: "2", r: "2" }))),
|
|
178
|
-
image ? h("img", { className: "feed-image", src: image, alt: "", decoding: "async" }) : null));
|
|
179
|
-
}
|
|
180
123
|
function Feed({ items, image }) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
h("p", { className: "empty-state" }, "No public content yet."),
|
|
184
|
-
h(FeedDecor, { image: image })));
|
|
185
|
-
}
|
|
186
|
-
return (h("main", { className: "feed" },
|
|
187
|
-
[...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]) => {
|
|
188
126
|
const label = dateLabel(dateKey);
|
|
189
|
-
return (h(
|
|
190
|
-
h("
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
h("span", { className: "weekday" }, label.weekday)),
|
|
194
|
-
group.map((item) => item.kind === "thought" ? (h(ThoughtCard, { item: item, linked: true })) : (h(PostCard, { item: item })))));
|
|
195
|
-
}),
|
|
196
|
-
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));
|
|
197
131
|
}
|
|
198
132
|
function Filter({ posts, thoughts }) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
h("button", { type: "button", "data-filter": "thought", "aria-pressed": "false" },
|
|
208
|
-
"Thoughts",
|
|
209
|
-
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))))));
|
|
210
141
|
}
|
|
211
142
|
const FILTER_SCRIPT = `(() => {
|
|
212
143
|
const links = document.querySelectorAll('[data-filter-group="feed"] [data-filter]');
|
|
213
144
|
const items = document.querySelectorAll('.feed [data-kind]');
|
|
214
145
|
const days = document.querySelectorAll('.feed .day');
|
|
215
|
-
|
|
216
|
-
if ('IntersectionObserver' in window && !reduced) {
|
|
217
|
-
const observer = new IntersectionObserver((entries) => {
|
|
218
|
-
entries.forEach((entry) => {
|
|
219
|
-
if (entry.isIntersecting) {
|
|
220
|
-
entry.target.classList.add('in');
|
|
221
|
-
observer.unobserve(entry.target);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
}, { threshold: 0.08 });
|
|
225
|
-
days.forEach((day) => observer.observe(day));
|
|
226
|
-
} else {
|
|
227
|
-
days.forEach((day) => day.classList.add('in'));
|
|
228
|
-
}
|
|
229
|
-
links.forEach((link) => link.addEventListener('click', (event) => {
|
|
230
|
-
event.preventDefault();
|
|
146
|
+
links.forEach((link) => link.addEventListener('click', () => {
|
|
231
147
|
const filter = link.dataset.filter;
|
|
232
|
-
links.forEach((
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
item.setAttribute('aria-pressed', String(active));
|
|
236
|
-
});
|
|
237
|
-
items.forEach((item) => {
|
|
238
|
-
item.style.display = filter === 'all' || item.dataset.kind === filter ? '' : 'none';
|
|
239
|
-
});
|
|
240
|
-
days.forEach((day) => {
|
|
241
|
-
const visible = Array.from(day.querySelectorAll('[data-kind]'))
|
|
242
|
-
.some((item) => item.style.display !== 'none');
|
|
243
|
-
day.style.display = visible ? '' : 'none';
|
|
244
|
-
if (!visible) day.classList.remove('in');
|
|
245
|
-
});
|
|
246
|
-
if (!reduced) {
|
|
247
|
-
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
248
|
-
days.forEach((day) => {
|
|
249
|
-
if (day.style.display !== 'none') day.classList.add('in');
|
|
250
|
-
});
|
|
251
|
-
}));
|
|
252
|
-
}
|
|
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); });
|
|
253
151
|
}));
|
|
254
152
|
})();`;
|
|
255
153
|
export function homePage(items, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
@@ -258,29 +156,27 @@ export function homePage(items, theme = blogTheme, site = DEFAULT_SITE, styleshe
|
|
|
258
156
|
theme,
|
|
259
157
|
title: site.name,
|
|
260
158
|
styleHref: styleHref(0, stylesheet),
|
|
261
|
-
children: (h(
|
|
262
|
-
|
|
263
|
-
|
|
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 }) },
|
|
264
164
|
h(Feed, { items: items, image: site.images?.feed ? `./${site.images.feed}` : undefined }),
|
|
265
165
|
h("script", { dangerouslySetInnerHTML: { __html: FILTER_SCRIPT } }))),
|
|
266
166
|
});
|
|
267
167
|
}
|
|
268
168
|
export function listPage(kind, items, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
269
169
|
const isPost = kind === "post";
|
|
170
|
+
const title = isPost ? "Posts" : "Thoughts";
|
|
270
171
|
return pageHtml({
|
|
271
172
|
site,
|
|
272
173
|
theme,
|
|
273
|
-
title
|
|
174
|
+
title,
|
|
274
175
|
activePath: isPost ? "/posts/" : "/thoughts/",
|
|
275
176
|
styleHref: styleHref(1, stylesheet),
|
|
276
|
-
children: (h(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
h("h1", null, isPost ? "Posts" : "Thoughts"),
|
|
280
|
-
h("p", null, isPost
|
|
281
|
-
? "Complete arguments grown from fragments."
|
|
282
|
-
: "Short, trimmed thoughts ready to show as they are.")),
|
|
283
|
-
h("div", { className: isPost ? "post-list" : "thought-list" }, items.length === 0 ? (h("p", { className: "empty-state" }, "No public content yet.")) : (items.map((item) => isPost ? h(PostCard, { item: item }) : h(ThoughtCard, { item: item, linked: true })))))),
|
|
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.")))),
|
|
284
180
|
});
|
|
285
181
|
}
|
|
286
182
|
export function notFoundPage(copy, theme, site, stylesheet) {
|
|
@@ -292,14 +188,9 @@ export function notFoundPage(copy, theme, site, stylesheet) {
|
|
|
292
188
|
title,
|
|
293
189
|
description: message,
|
|
294
190
|
styleHref: styleHref(0, stylesheet),
|
|
295
|
-
// Static hosts can serve 404.html at the missing URL, including deep paths.
|
|
296
191
|
baseHref: "/",
|
|
297
|
-
children: (h(
|
|
298
|
-
h(
|
|
299
|
-
h("span", { className: "kind" }, "404"),
|
|
300
|
-
h("h1", null, title),
|
|
301
|
-
h("p", null, message)),
|
|
302
|
-
h("div", { className: "prose" },
|
|
192
|
+
children: (h(Page, { eyebrow: "404", title: title, description: message },
|
|
193
|
+
h(Prose, null,
|
|
303
194
|
h("a", { href: "/" }, copy.homeLabel ?? "Back to home")))),
|
|
304
195
|
});
|
|
305
196
|
}
|
|
@@ -308,22 +199,13 @@ function PostNavigation({ item, posts }) {
|
|
|
308
199
|
const older = index >= 0 ? posts[index + 1] : undefined;
|
|
309
200
|
const newer = index > 0 ? posts[index - 1] : undefined;
|
|
310
201
|
return (h("nav", { className: "post-nav", "aria-label": "Post navigation" },
|
|
311
|
-
older ? (h("
|
|
312
|
-
|
|
313
|
-
older.title)) : (h("span", null)),
|
|
314
|
-
newer ? (h("a", { className: "next", href: itemUrl(newer) },
|
|
315
|
-
h("span", { className: "label" }, "Next post (newer) \u2192"),
|
|
316
|
-
newer.title)) : (h("span", { className: "next" },
|
|
317
|
-
h("span", { className: "label" }, "Next post (newer) \u2192"),
|
|
318
|
-
"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."))));
|
|
319
204
|
}
|
|
320
205
|
function RelatedThoughts({ item, thoughts, }) {
|
|
321
206
|
const related = thoughts.filter((thought) => thought.dateKey === item.dateKey).slice(0, 3);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
return (h("aside", { className: "related-thoughts" },
|
|
325
|
-
h("h2", null, "Thoughts from the same day"),
|
|
326
|
-
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;
|
|
327
209
|
}
|
|
328
210
|
export function postPage(item, posts, thoughts, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
329
211
|
const category = typeof item.metadata.category === "string" ? item.metadata.category : "Post";
|
|
@@ -334,21 +216,16 @@ export function postPage(item, posts, thoughts, theme = blogTheme, site = DEFAUL
|
|
|
334
216
|
description: item.excerpt,
|
|
335
217
|
activePath: "/posts/",
|
|
336
218
|
styleHref: styleHref(1 + slugDepth(item.slug), stylesheet),
|
|
337
|
-
children: (h(
|
|
338
|
-
h(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
item.characterCount,
|
|
348
|
-
" characters")),
|
|
349
|
-
h("div", { className: "prose", dangerouslySetInnerHTML: { __html: item.html } }),
|
|
350
|
-
h(RelatedThoughts, { item: item, thoughts: thoughts }),
|
|
351
|
-
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 }))),
|
|
352
229
|
});
|
|
353
230
|
}
|
|
354
231
|
export function thoughtPage(item, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
@@ -359,15 +236,10 @@ export function thoughtPage(item, theme = blogTheme, site = DEFAULT_SITE, styles
|
|
|
359
236
|
description: item.excerpt,
|
|
360
237
|
activePath: "/thoughts/",
|
|
361
238
|
styleHref: styleHref(1 + slugDepth(item.slug), stylesheet),
|
|
362
|
-
children: (h("
|
|
363
|
-
h(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
h("h1", null, item.title),
|
|
367
|
-
h("div", { className: "meta" },
|
|
368
|
-
item.dateKey,
|
|
369
|
-
item.timeLabel ? ` · ${item.timeLabel}` : "")),
|
|
370
|
-
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 }))),
|
|
371
243
|
});
|
|
372
244
|
}
|
|
373
245
|
export function aboutPage(theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
@@ -378,19 +250,13 @@ export function aboutPage(theme = blogTheme, site = DEFAULT_SITE, stylesheet = D
|
|
|
378
250
|
title: "About",
|
|
379
251
|
activePath: "/about/",
|
|
380
252
|
styleHref: styleHref(1, stylesheet),
|
|
381
|
-
children: (h(
|
|
382
|
-
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"))),
|
|
383
|
-
about ? (h(
|
|
384
|
-
|
|
385
|
-
about.
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
about.links.map((link) => (h("a", { className: "github-link", href: link.href, target: "_blank", rel: "me noopener noreferrer" },
|
|
389
|
-
h(DoodleGithubIcon, null),
|
|
390
|
-
h("span", { className: "github-copy" },
|
|
391
|
-
h("span", null, link.label),
|
|
392
|
-
h("strong", null, link.handle)),
|
|
393
|
-
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)),
|
|
394
260
|
});
|
|
395
261
|
}
|
|
396
262
|
function skillUrl(skill) {
|
|
@@ -402,20 +268,12 @@ function skillInstallCommand(site, skill) {
|
|
|
402
268
|
: skill.sourceBasePath;
|
|
403
269
|
}
|
|
404
270
|
function SkillCard({ skill, site }) {
|
|
405
|
-
return (h(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
h(
|
|
410
|
-
h("
|
|
411
|
-
h("span", null, skill.description)),
|
|
412
|
-
h("span", { className: "skill-card-foot" },
|
|
413
|
-
h("span", null,
|
|
414
|
-
skill.files.length,
|
|
415
|
-
" Markdown files \u00B7 ",
|
|
416
|
-
skill.relations.length,
|
|
417
|
-
" references"),
|
|
418
|
-
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))));
|
|
419
277
|
}
|
|
420
278
|
function skillTree(files) {
|
|
421
279
|
const root = { name: "", children: [] };
|
|
@@ -459,7 +317,7 @@ function SkillGraphNodeView({ node }) {
|
|
|
459
317
|
function SkillRelationGraph({ skill, direction }) {
|
|
460
318
|
const graph = layoutSkillGraph(skill, direction);
|
|
461
319
|
const markerId = `skill-arrow-${skill.slug}-${direction.toLowerCase()}`;
|
|
462
|
-
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` },
|
|
463
321
|
h("defs", null,
|
|
464
322
|
h("marker", { id: markerId, viewBox: "0 0 10 10", refX: "8", refY: "5", markerWidth: "5", markerHeight: "5", orient: "auto-start-reverse" },
|
|
465
323
|
h("path", { className: "skill-graph-arrow", d: "M 0 0 L 10 5 L 0 10 z" }))),
|
|
@@ -499,9 +357,8 @@ function SkillRelations({ skill }) {
|
|
|
499
357
|
h("strong", null, missingNodes.length),
|
|
500
358
|
" missing targets")),
|
|
501
359
|
hasRelations ? (h(Fragment, null,
|
|
502
|
-
h("div", { className: `skill-graph-canvas${compactGraph ? " compact" : ""}
|
|
503
|
-
h(SkillRelationGraph, { skill: skill, direction: "LR" }),
|
|
504
|
-
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" })),
|
|
505
362
|
h("section", { className: "skill-reading-paths", "aria-labelledby": `${skill.slug}-paths` },
|
|
506
363
|
h("h3", { id: `${skill.slug}-paths` }, "Where references happen"),
|
|
507
364
|
h("ol", null, skill.relations.map((relation) => {
|
|
@@ -636,24 +493,11 @@ export function skillsIndexPage(skills, theme = blogTheme, site = DEFAULT_SITE,
|
|
|
636
493
|
description: "Methods and working conventions that can be handed to an Agent.",
|
|
637
494
|
activePath: "/skills/",
|
|
638
495
|
styleHref: styleHref(1, stylesheet),
|
|
639
|
-
children: (h(
|
|
640
|
-
h("
|
|
641
|
-
|
|
642
|
-
h(
|
|
643
|
-
|
|
644
|
-
h("section", { className: "skill-group", "aria-labelledby": "public-skills-heading" },
|
|
645
|
-
h("header", { className: "skill-group-head" },
|
|
646
|
-
h("h2", { id: "public-skills-heading" }, "Public Skills"),
|
|
647
|
-
h("span", null, "Public content")),
|
|
648
|
-
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."))),
|
|
649
|
-
projectSkills.length > 0 ? (h("section", { className: "skill-group project-skills", "aria-labelledby": "project-skills-heading" },
|
|
650
|
-
h("header", { className: "skill-group-head" },
|
|
651
|
-
h("div", null,
|
|
652
|
-
h("h2", { id: "project-skills-heading" }, "Project Skills"),
|
|
653
|
-
h("small", null, "Development and content workflows used inside this project")),
|
|
654
|
-
h("span", null, ".agents/skills/")),
|
|
655
|
-
h("div", { className: "skill-list" }, projectSkills.map((skill) => (h(SkillCard, { skill: skill, site: site })))))) : null,
|
|
656
|
-
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."))),
|
|
657
501
|
});
|
|
658
502
|
}
|
|
659
503
|
export function skillPage(skill, theme = blogTheme, site = DEFAULT_SITE, stylesheet = DEFAULT_THEME_STYLESHEET) {
|
|
@@ -663,12 +507,13 @@ export function skillPage(skill, theme = blogTheme, site = DEFAULT_SITE, stylesh
|
|
|
663
507
|
site,
|
|
664
508
|
theme,
|
|
665
509
|
title: skill.name,
|
|
510
|
+
wide: true,
|
|
666
511
|
description: skill.description,
|
|
667
512
|
activePath: "/skills/",
|
|
668
513
|
styleHref: skill.scope === "public"
|
|
669
514
|
? styleHref(1 + slugDepth(skill.slug), stylesheet)
|
|
670
515
|
: styleHref(2 + slugDepth(skill.slug), stylesheet),
|
|
671
|
-
children: (h("
|
|
516
|
+
children: (h(Page, { width: "full", className: "skill-page", title: skill.name, eyebrow: "Skill", description: skill.description },
|
|
672
517
|
h("a", { className: "skill-back", href: "/skills/" }, "\u2190 All Skills"),
|
|
673
518
|
h("header", { className: "skill-browser-head" },
|
|
674
519
|
h("span", { className: "skill-command" },
|
|
@@ -687,8 +532,11 @@ export function skillPage(skill, theme = blogTheme, site = DEFAULT_SITE, stylesh
|
|
|
687
532
|
h("header", { className: "skill-viewer-bar" },
|
|
688
533
|
h("span", { "data-current-file": true }, firstFile.path),
|
|
689
534
|
h("span", null, "Markdown")),
|
|
690
|
-
skill.files.map((file, index) => (h("article",
|
|
691
|
-
|
|
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}>`),
|
|
692
540
|
} }))))),
|
|
693
541
|
h(SkillRelations, { skill: skill })),
|
|
694
542
|
h("script", { dangerouslySetInnerHTML: { __html: SKILL_BROWSER_SCRIPT } }))),
|