for-humanity 0.1.2 → 0.2.1
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/CHANGELOG.md +11 -0
- package/README.md +4 -3
- package/dist/cli.css +52 -18
- package/dist/cli.js +166 -109
- package/package.json +1 -1
- package/src/cli.ts +6 -3
- package/src/component/widget/shell/_wg-shell-nav-list.css +158 -0
- package/src/component/widget/shell/_wg-shell-nav-list.tsx +72 -0
- package/src/component/widget/shell/_wg-shell-nav.css +0 -114
- package/src/component/widget/shell/_wg-shell-nav.tsx +9 -35
- package/src/component/widget/shell/wg-shell.tsx +3 -3
- package/src/constant/asset.ts +2 -2
- package/src/content/read-docs.test.ts +96 -3
- package/src/content/to-doc-groups/_to-doc-group-branches.ts +58 -0
- package/src/content/to-doc-groups/to-doc-groups.ts +14 -0
- package/src/type/doc-data.ts +3 -2
- package/src/type/doc-group.ts +23 -0
- package/src/type/group-path.ts +7 -0
- package/src/type/site-assets.ts +8 -0
- package/src/type/site-config.ts +5 -4
- package/src/util/file/to-asset-path.ts +26 -0
- package/src/content/to-doc-groups.ts +0 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.1 — 2026-10-04
|
|
4
|
+
|
|
5
|
+
- Fingerprint stylesheet and browser-script URLs so new pages cannot reuse assets cached from an older deployment.
|
|
6
|
+
- Cache fingerprinted CSS, scripts and fonts with immutable headers on Cloudflare Pages.
|
|
7
|
+
|
|
8
|
+
## 0.2.0 — 2026-10-04
|
|
9
|
+
|
|
10
|
+
- Support nested navigation with `group` name arrays while keeping single-name groups compatible.
|
|
11
|
+
- Order nested sibling groups with paths in `navigation`, independently of document folders.
|
|
12
|
+
- Extend active, hover and keyboard focus tree paths through ancestor groups.
|
|
13
|
+
|
|
3
14
|
## 0.1.2 — 2026-10-03
|
|
4
15
|
|
|
5
16
|
Documentation and release automation update. The CLI commands and Node.js requirement are unchanged.
|
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ Write Markdown. Keep the context. Share a static site.
|
|
|
33
33
|
Node.js 22 or later and pnpm. Install the CLI in your document project:
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
|
-
pnpm add -D --save-exact for-humanity@0.1
|
|
36
|
+
pnpm add -D --save-exact for-humanity@0.2.1
|
|
37
37
|
mkdir docs
|
|
38
38
|
```
|
|
39
39
|
|
|
@@ -52,7 +52,7 @@ pnpm exec for-humanity preview docs
|
|
|
52
52
|
|
|
53
53
|
If your Markdown files are in the project root, use `.` instead of `docs`. The root `README.md` becomes the home page; root `AGENTS.md` and `CLAUDE.md` stay out of the site.
|
|
54
54
|
|
|
55
|
-
Version `0.1
|
|
55
|
+
Version `0.2.1` supports `dev`, `build` and `preview`. `init` is planned; see the [Roadmap](https://github.com/minu-ha/for-humanity/blob/main/templates/document/roadmap.md) and [Changelog](https://github.com/minu-ha/for-humanity/blob/main/CHANGELOG.md).
|
|
56
56
|
Cloudflare Pages can host the generated site. See [Deployment](https://github.com/minu-ha/for-humanity/blob/main/templates/document/deployment.md) for the build settings.
|
|
57
57
|
|
|
58
58
|
### Develop the kit
|
|
@@ -74,7 +74,7 @@ pnpm preview # Preview the static site
|
|
|
74
74
|
pnpm build:pages # Add Cloudflare Pages headers and a 404 page
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
Changes pushed to `main` update the documentation site. A stable version tag such as `v0.1
|
|
77
|
+
Changes pushed to `main` update the documentation site. A stable version tag such as `v0.2.1` runs checks, publishes the package to npm, then creates a GitHub Release with the same package file.
|
|
78
78
|
The npm README updates with each package release. See [Maintenance](https://github.com/minu-ha/for-humanity/blob/main/templates/document/maintenance.md#packaging-and-release) for the release steps.
|
|
79
79
|
|
|
80
80
|
## Make it yours
|
|
@@ -94,6 +94,7 @@ export default {
|
|
|
94
94
|
|
|
95
95
|
See [Commands](https://github.com/minu-ha/for-humanity/blob/main/templates/document/commands.md) for using another document folder, and [Settings](https://github.com/minu-ha/for-humanity/blob/main/templates/document/settings.md) for configuration.
|
|
96
96
|
Each document other than the root README chooses a `group` and optional `order`. Pages stay independent; groups do not need parent documents or nested folders. Document names may share the same first letter.
|
|
97
|
+
Use `group: Guide` for one group or `group: [Projects, Example project, Research]` for nested navigation. Group paths do not change document URLs.
|
|
97
98
|
|
|
98
99
|
## Write with context
|
|
99
100
|
|
package/dist/cli.css
CHANGED
|
@@ -295,22 +295,32 @@
|
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
/* src/component/widget/shell/_wg-shell-nav.css */
|
|
299
|
-
.
|
|
300
|
-
font-size: var(--app-font-size-dense);
|
|
301
|
-
line-height: var(--app-line-height-tight);
|
|
302
|
-
}
|
|
303
|
-
.wg_shellNav__docs {
|
|
304
|
-
display: flex;
|
|
305
|
-
flex-direction: column;
|
|
306
|
-
gap: var(--app-space-stack);
|
|
307
|
-
}
|
|
308
|
-
.wg_shellNav__list {
|
|
298
|
+
/* src/component/widget/shell/_wg-shell-nav-list.css */
|
|
299
|
+
.wg_shellNavList__root {
|
|
309
300
|
margin: 0 0 0 var(--app-space-tree-inset);
|
|
310
301
|
padding: 0;
|
|
311
302
|
list-style: none;
|
|
312
303
|
}
|
|
313
|
-
.
|
|
304
|
+
.wg_shellNavList__branch {
|
|
305
|
+
position: relative;
|
|
306
|
+
display: block;
|
|
307
|
+
padding: var(--app-space-tree-row) 0 var(--app-space-tree-row) calc(var(--app-space-tree-indent) + var(--app-space-inline));
|
|
308
|
+
color: var(--app-color-text-muted);
|
|
309
|
+
font-size: var(--app-font-size-label);
|
|
310
|
+
line-height: var(--app-line-height-tight);
|
|
311
|
+
overflow-wrap: anywhere;
|
|
312
|
+
&::before {
|
|
313
|
+
position: absolute;
|
|
314
|
+
top: calc(0.5lh + var(--app-space-tree-row));
|
|
315
|
+
left: 0;
|
|
316
|
+
width: var(--app-space-tree-indent);
|
|
317
|
+
border-top: 1px solid var(--app-color-border-soft);
|
|
318
|
+
content: "";
|
|
319
|
+
pointer-events: none;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
.wg_shellNavList__item {
|
|
323
|
+
display: flow-root;
|
|
314
324
|
position: relative;
|
|
315
325
|
&::before {
|
|
316
326
|
position: absolute;
|
|
@@ -340,24 +350,36 @@
|
|
|
340
350
|
&:where(:has(:focus-visible))::after {
|
|
341
351
|
border-left-color: var(--app-color-text-strong);
|
|
342
352
|
}
|
|
343
|
-
&:where(:has(~ .
|
|
353
|
+
&:where(:has(~ .wg_shellNavList__item:hover))::before {
|
|
344
354
|
border-left-color: var(--app-color-text-strong);
|
|
345
355
|
}
|
|
346
|
-
&:where(:has(~ .
|
|
356
|
+
&:where(:has(~ .wg_shellNavList__item :focus-visible))::before {
|
|
347
357
|
border-left-color: var(--app-color-text-strong);
|
|
348
358
|
}
|
|
359
|
+
& > .wg_shellNavList__root {
|
|
360
|
+
margin-left: var(--app-space-tree-indent);
|
|
361
|
+
}
|
|
362
|
+
&:where(:hover) > .wg_shellNavList__branch::before {
|
|
363
|
+
border-top-color: var(--app-color-text-strong);
|
|
364
|
+
}
|
|
365
|
+
&:where(:has(:focus-visible)) > .wg_shellNavList__branch::before {
|
|
366
|
+
border-top-color: var(--app-color-text-strong);
|
|
367
|
+
}
|
|
349
368
|
}
|
|
350
|
-
.
|
|
369
|
+
.wg_shellNavList__item--trail {
|
|
351
370
|
&::before {
|
|
352
371
|
border-left-color: var(--app-color-text-strong);
|
|
353
372
|
}
|
|
354
373
|
}
|
|
355
|
-
.
|
|
374
|
+
.wg_shellNavList__item--current {
|
|
356
375
|
&::after {
|
|
357
376
|
border-left-color: var(--app-color-text-strong);
|
|
358
377
|
}
|
|
378
|
+
& > .wg_shellNavList__branch::before {
|
|
379
|
+
border-top-color: var(--app-color-text-strong);
|
|
380
|
+
}
|
|
359
381
|
}
|
|
360
|
-
.
|
|
382
|
+
.wg_shellNavList__link {
|
|
361
383
|
position: relative;
|
|
362
384
|
display: block;
|
|
363
385
|
padding: var(--app-space-tree-row) 0 var(--app-space-tree-row) calc(var(--app-space-tree-indent) + var(--app-space-inline));
|
|
@@ -365,6 +387,7 @@
|
|
|
365
387
|
font-size: var(--app-font-size-dense);
|
|
366
388
|
line-height: var(--app-line-height-tight);
|
|
367
389
|
text-decoration: none;
|
|
390
|
+
overflow-wrap: anywhere;
|
|
368
391
|
&::before {
|
|
369
392
|
position: absolute;
|
|
370
393
|
top: calc(0.5lh + var(--app-space-tree-row));
|
|
@@ -388,13 +411,24 @@
|
|
|
388
411
|
border-top-color: currentcolor;
|
|
389
412
|
}
|
|
390
413
|
}
|
|
391
|
-
.
|
|
414
|
+
.wg_shellNavList__link--active {
|
|
392
415
|
color: var(--app-color-text-strong);
|
|
393
416
|
font-weight: var(--app-font-weight-active);
|
|
394
417
|
&::before {
|
|
395
418
|
border-top-color: currentcolor;
|
|
396
419
|
}
|
|
397
420
|
}
|
|
421
|
+
|
|
422
|
+
/* src/component/widget/shell/_wg-shell-nav.css */
|
|
423
|
+
.wg_shellNav__root {
|
|
424
|
+
font-size: var(--app-font-size-dense);
|
|
425
|
+
line-height: var(--app-line-height-tight);
|
|
426
|
+
}
|
|
427
|
+
.wg_shellNav__docs {
|
|
428
|
+
display: flex;
|
|
429
|
+
flex-direction: column;
|
|
430
|
+
gap: var(--app-space-stack);
|
|
431
|
+
}
|
|
398
432
|
.wg_shellNav__group {
|
|
399
433
|
margin: 0 0 4px;
|
|
400
434
|
color: var(--app-color-text-muted);
|
package/dist/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ var WgProse = (props) => {
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
// src/component/widget/shell/wg-shell.tsx
|
|
33
|
-
import
|
|
33
|
+
import clsx5 from "clsx";
|
|
34
34
|
|
|
35
35
|
// src/component/widget/shell/_function/to-toc-groups.ts
|
|
36
36
|
import { takeWhile } from "es-toolkit/array";
|
|
@@ -66,7 +66,50 @@ var toTocGroups = (outline) => {
|
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
// src/component/widget/shell/_wg-shell-nav.tsx
|
|
69
|
+
import clsx3 from "clsx";
|
|
70
|
+
|
|
71
|
+
// src/component/widget/shell/_wg-shell-nav-list.tsx
|
|
69
72
|
import clsx2 from "clsx";
|
|
73
|
+
import { Fragment } from "react";
|
|
74
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
75
|
+
var WgShellNavList = (props) => {
|
|
76
|
+
const entries = [
|
|
77
|
+
...props.group.docs.map((doc) => ({ kind: "document", key: `doc:${doc.id}`, doc, current: doc.id === props.currentDoc?.id })),
|
|
78
|
+
...props.group.groups.map((group) => ({
|
|
79
|
+
kind: "group",
|
|
80
|
+
key: `group:${JSON.stringify(group.path)}`,
|
|
81
|
+
group,
|
|
82
|
+
current: group.path.every((name, index) => name === props.currentDoc?.data.group[index])
|
|
83
|
+
}))
|
|
84
|
+
];
|
|
85
|
+
const currentIndex = entries.findIndex((entry) => entry.current);
|
|
86
|
+
return /* @__PURE__ */ jsx2("ul", { className: clsx2("wg_shellNavList__root"), "aria-label": props.group.name, children: entries.map((entry, index) => /* @__PURE__ */ jsxs2(
|
|
87
|
+
"li",
|
|
88
|
+
{
|
|
89
|
+
className: clsx2("wg_shellNavList__item", {
|
|
90
|
+
"wg_shellNavList__item--trail": index < currentIndex,
|
|
91
|
+
"wg_shellNavList__item--current": entry.current
|
|
92
|
+
}),
|
|
93
|
+
children: [
|
|
94
|
+
entry.kind === "document" && /* @__PURE__ */ jsx2(
|
|
95
|
+
"a",
|
|
96
|
+
{
|
|
97
|
+
className: clsx2("wg_shellNavList__link", { "wg_shellNavList__link--active": entry.current }),
|
|
98
|
+
href: `/${entry.doc.id}/`,
|
|
99
|
+
title: entry.doc.data.label,
|
|
100
|
+
"aria-current": entry.current ? "page" : void 0,
|
|
101
|
+
children: entry.doc.data.name
|
|
102
|
+
}
|
|
103
|
+
),
|
|
104
|
+
entry.kind === "group" && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
105
|
+
/* @__PURE__ */ jsx2("span", { className: clsx2("wg_shellNavList__branch"), children: entry.group.name }),
|
|
106
|
+
/* @__PURE__ */ jsx2(WgShellNavList, { group: entry.group, currentDoc: props.currentDoc })
|
|
107
|
+
] })
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
entry.key
|
|
111
|
+
)) });
|
|
112
|
+
};
|
|
70
113
|
|
|
71
114
|
// src/constant/copy.ts
|
|
72
115
|
var copy_site_title_default = "for humanity";
|
|
@@ -95,61 +138,60 @@ var copy_warn_unknown_status = "\uC124\uC815\uC5D0 \uC5C6\uB294 \uC0C1\uD0DC \uB
|
|
|
95
138
|
var locale_doc_name = "en";
|
|
96
139
|
var locale_doc_group = "ko";
|
|
97
140
|
|
|
98
|
-
// src/content/to-doc-groups.ts
|
|
141
|
+
// src/content/to-doc-groups/_to-doc-group-branches.ts
|
|
142
|
+
var toDocGroupBranches = (options) => {
|
|
143
|
+
return [
|
|
144
|
+
...Map.groupBy(
|
|
145
|
+
options.docs.filter((doc) => doc.data.group.length > options.path.length),
|
|
146
|
+
(doc) => doc.data.group[options.path.length]
|
|
147
|
+
)
|
|
148
|
+
].map(([name, docs]) => {
|
|
149
|
+
const path = [...options.path, name];
|
|
150
|
+
return {
|
|
151
|
+
name,
|
|
152
|
+
path,
|
|
153
|
+
docs,
|
|
154
|
+
position: options.positions.get(JSON.stringify(path)) ?? options.unlistedPosition
|
|
155
|
+
};
|
|
156
|
+
}).toSorted((a, b) => a.position - b.position || a.name.localeCompare(b.name, locale_doc_group)).map((group) => {
|
|
157
|
+
return {
|
|
158
|
+
name: group.name,
|
|
159
|
+
path: group.path,
|
|
160
|
+
docs: group.docs.filter((doc) => doc.data.group.length === group.path.length).toSorted((a, b) => a.data.order - b.data.order || a.data.name.localeCompare(b.data.name, locale_doc_name) || a.id.localeCompare(b.id, locale_doc_name)),
|
|
161
|
+
groups: toDocGroupBranches({ docs: group.docs, path: group.path, positions: options.positions, unlistedPosition: options.unlistedPosition })
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/content/to-doc-groups/to-doc-groups.ts
|
|
99
167
|
var toDocGroups = (options) => {
|
|
100
|
-
const positions = new Map(options.navigation.
|
|
101
|
-
return
|
|
102
|
-
name,
|
|
103
|
-
docs: docs.toSorted((a, b) => a.data.order - b.data.order || a.data.name.localeCompare(b.data.name, locale_doc_name) || a.id.localeCompare(b.id, locale_doc_name))
|
|
104
|
-
}));
|
|
168
|
+
const positions = new Map(options.navigation.flatMap((path, index) => path.map((_name, depth) => [JSON.stringify(path.slice(0, depth + 1)), index])).toReversed());
|
|
169
|
+
return toDocGroupBranches({ docs: options.docs, path: [], positions, unlistedPosition: options.navigation.length });
|
|
105
170
|
};
|
|
106
171
|
|
|
107
172
|
// src/component/widget/shell/_wg-shell-nav.tsx
|
|
108
|
-
import { jsx as
|
|
173
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
109
174
|
var WgShellNav = (props) => {
|
|
110
175
|
const docGroups = toDocGroups({ docs: props.docs, navigation: props.site.navigation });
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
"li",
|
|
117
|
-
{
|
|
118
|
-
className: clsx2("wg_shellNav__item", {
|
|
119
|
-
"wg_shellNav__item--trail": index < currentIndex,
|
|
120
|
-
"wg_shellNav__item--current": index === currentIndex
|
|
121
|
-
}),
|
|
122
|
-
children: /* @__PURE__ */ jsx2(
|
|
123
|
-
"a",
|
|
124
|
-
{
|
|
125
|
-
className: clsx2("wg_shellNav__link", {
|
|
126
|
-
"wg_shellNav__link--active": index === currentIndex
|
|
127
|
-
}),
|
|
128
|
-
href: `/${doc.id}/`,
|
|
129
|
-
title: doc.data.label,
|
|
130
|
-
"aria-current": index === currentIndex ? "page" : void 0,
|
|
131
|
-
children: doc.data.name
|
|
132
|
-
}
|
|
133
|
-
)
|
|
134
|
-
},
|
|
135
|
-
doc.id
|
|
136
|
-
)) })
|
|
137
|
-
] }, group.name);
|
|
138
|
-
}) }) });
|
|
176
|
+
const currentDoc = props.docs.find((doc) => doc.id === props.current);
|
|
177
|
+
return /* @__PURE__ */ jsx3("nav", { className: clsx3("wg_shellNav__root"), "aria-label": copy_nav_aria_label, children: /* @__PURE__ */ jsx3("div", { className: clsx3("wg_shellNav__docs"), children: docGroups.map((group) => /* @__PURE__ */ jsxs3("section", { className: clsx3("wg_shellNav__docGroup"), "aria-label": group.name, children: [
|
|
178
|
+
/* @__PURE__ */ jsx3("div", { className: clsx3("wg_shellNav__group"), children: group.name }),
|
|
179
|
+
/* @__PURE__ */ jsx3(WgShellNavList, { group, currentDoc })
|
|
180
|
+
] }, group.name)) }) });
|
|
139
181
|
};
|
|
140
182
|
|
|
141
183
|
// src/component/widget/shell/_wg-shell-toc.tsx
|
|
142
|
-
import
|
|
143
|
-
import { Fragment } from "react";
|
|
144
|
-
import { jsx as
|
|
184
|
+
import clsx4 from "clsx";
|
|
185
|
+
import { Fragment as Fragment2 } from "react";
|
|
186
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
145
187
|
var WgShellToc = (props) => {
|
|
146
|
-
return /* @__PURE__ */
|
|
147
|
-
/* @__PURE__ */
|
|
148
|
-
props.groups.map((group) => /* @__PURE__ */
|
|
149
|
-
group.part !== void 0 && /* @__PURE__ */
|
|
150
|
-
/* @__PURE__ */
|
|
151
|
-
/* @__PURE__ */
|
|
152
|
-
section.subs.length > 0 && /* @__PURE__ */
|
|
188
|
+
return /* @__PURE__ */ jsxs4("nav", { className: clsx4("wg_shellToc__root"), "aria-label": copy_nav_toc_label, children: [
|
|
189
|
+
/* @__PURE__ */ jsx4("div", { className: clsx4("wg_shellToc__label"), children: copy_nav_toc_label }),
|
|
190
|
+
props.groups.map((group) => /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
191
|
+
group.part !== void 0 && /* @__PURE__ */ jsx4("div", { className: clsx4("wg_shellToc__group"), children: group.part }),
|
|
192
|
+
/* @__PURE__ */ jsx4("ul", { className: clsx4("wg_shellToc__list"), children: group.sections.map((section) => /* @__PURE__ */ jsxs4("li", { className: clsx4("wg_shellToc__item"), children: [
|
|
193
|
+
/* @__PURE__ */ jsx4("a", { className: clsx4("wg_shellToc__link"), href: `#${section.heading.slug}`, "data-toc-link": "", children: section.heading.text }),
|
|
194
|
+
section.subs.length > 0 && /* @__PURE__ */ jsx4("ul", { className: clsx4("wg_shellToc__sub"), children: section.subs.map((sub) => /* @__PURE__ */ jsx4("li", { className: clsx4("wg_shellToc__subItem"), children: /* @__PURE__ */ jsx4("a", { className: clsx4("wg_shellToc__subLink"), href: `#${sub.heading.slug}`, "data-toc-sub-link": "", children: sub.heading.text }) }, sub.heading.slug)) })
|
|
153
195
|
] }, section.heading.slug)) })
|
|
154
196
|
] }, group.sections[0].heading.slug))
|
|
155
197
|
] });
|
|
@@ -171,85 +213,85 @@ var asset_content_types = {
|
|
|
171
213
|
var asset_content_type_default = "application/octet-stream";
|
|
172
214
|
|
|
173
215
|
// src/component/widget/shell/wg-shell.tsx
|
|
174
|
-
import { jsx as
|
|
216
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
175
217
|
var reloadScript = `new EventSource(${JSON.stringify(asset_reload_path)}).onmessage=function(){location.reload()}`;
|
|
176
218
|
var WgShell = (props) => {
|
|
177
219
|
const tocGroups = props.outline === void 0 ? [] : toTocGroups(props.outline);
|
|
178
|
-
return /* @__PURE__ */
|
|
179
|
-
/* @__PURE__ */
|
|
180
|
-
/* @__PURE__ */
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
-
props.site.description !== void 0 && /* @__PURE__ */
|
|
183
|
-
/* @__PURE__ */
|
|
184
|
-
/* @__PURE__ */
|
|
185
|
-
/* @__PURE__ */
|
|
186
|
-
props.assets.preload.map((href) => /* @__PURE__ */
|
|
187
|
-
/* @__PURE__ */
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
-
props.assets.reload && /* @__PURE__ */
|
|
220
|
+
return /* @__PURE__ */ jsxs5("html", { lang: "ko", children: [
|
|
221
|
+
/* @__PURE__ */ jsxs5("head", { children: [
|
|
222
|
+
/* @__PURE__ */ jsx5("meta", { charSet: "utf-8" }),
|
|
223
|
+
/* @__PURE__ */ jsx5("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
224
|
+
props.site.description !== void 0 && /* @__PURE__ */ jsx5("meta", { name: "description", content: props.site.description }),
|
|
225
|
+
/* @__PURE__ */ jsx5("title", { children: props.title === void 0 ? props.site.title : `${props.title} \xB7 ${props.site.title}` }),
|
|
226
|
+
/* @__PURE__ */ jsx5("link", { rel: "icon", href: asset_favicon_path, type: "image/svg+xml" }),
|
|
227
|
+
/* @__PURE__ */ jsx5("style", { dangerouslySetInnerHTML: { __html: props.assets.fontCss } }),
|
|
228
|
+
props.assets.preload.map((href) => /* @__PURE__ */ jsx5("link", { rel: "preload", href, as: "font", type: "font/woff2", crossOrigin: "" }, href)),
|
|
229
|
+
/* @__PURE__ */ jsx5("script", { dangerouslySetInnerHTML: { __html: 'document.documentElement.style.scrollBehavior="auto"' } }),
|
|
230
|
+
/* @__PURE__ */ jsx5("link", { rel: "stylesheet", href: props.assets.stylePath }),
|
|
231
|
+
props.assets.reload && /* @__PURE__ */ jsx5("script", { dangerouslySetInnerHTML: { __html: reloadScript } })
|
|
190
232
|
] }),
|
|
191
|
-
/* @__PURE__ */
|
|
192
|
-
/* @__PURE__ */
|
|
193
|
-
/* @__PURE__ */
|
|
194
|
-
/* @__PURE__ */
|
|
195
|
-
/* @__PURE__ */
|
|
233
|
+
/* @__PURE__ */ jsxs5("body", { className: clsx5("wg_shell__root"), children: [
|
|
234
|
+
/* @__PURE__ */ jsxs5("div", { className: clsx5("wg_shell__sidebar"), children: [
|
|
235
|
+
/* @__PURE__ */ jsxs5("div", { className: clsx5("wg_shell__brandRow"), children: [
|
|
236
|
+
/* @__PURE__ */ jsx5("a", { className: clsx5("wg_shell__brand"), href: "/", "aria-current": props.current === void 0 ? "page" : void 0, children: props.site.title }),
|
|
237
|
+
/* @__PURE__ */ jsx5(
|
|
196
238
|
"button",
|
|
197
239
|
{
|
|
198
|
-
className:
|
|
240
|
+
className: clsx5("wg_shell__menuToggle"),
|
|
199
241
|
type: "button",
|
|
200
242
|
"aria-label": copy_nav_open,
|
|
201
243
|
"aria-controls": "fh-navigation-drawer",
|
|
202
244
|
"aria-expanded": "false",
|
|
203
245
|
"data-navigation-open": "",
|
|
204
|
-
children: /* @__PURE__ */
|
|
246
|
+
children: /* @__PURE__ */ jsx5("svg", { className: clsx5("wg_shell__menuIcon"), viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx5("path", { d: "M4 6h16M4 12h16M4 18h16" }) })
|
|
205
247
|
}
|
|
206
248
|
)
|
|
207
249
|
] }),
|
|
208
|
-
/* @__PURE__ */
|
|
209
|
-
/* @__PURE__ */
|
|
210
|
-
tocGroups.length > 0 && /* @__PURE__ */
|
|
250
|
+
/* @__PURE__ */ jsxs5("div", { className: clsx5("wg_shell__navigation"), "data-navigation": "", children: [
|
|
251
|
+
/* @__PURE__ */ jsx5(WgShellNav, { site: props.site, docs: props.docs, current: props.current }),
|
|
252
|
+
tocGroups.length > 0 && /* @__PURE__ */ jsx5(WgShellToc, { groups: tocGroups })
|
|
211
253
|
] })
|
|
212
254
|
] }),
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
-
/* @__PURE__ */
|
|
215
|
-
/* @__PURE__ */
|
|
216
|
-
/* @__PURE__ */
|
|
217
|
-
/* @__PURE__ */
|
|
255
|
+
/* @__PURE__ */ jsx5("main", { className: clsx5("wg_shell__main"), children: props.children }),
|
|
256
|
+
/* @__PURE__ */ jsxs5("dialog", { className: clsx5("wg_shell__drawer"), id: "fh-navigation-drawer", "aria-labelledby": "fh-navigation-title", "data-navigation-drawer": "", children: [
|
|
257
|
+
/* @__PURE__ */ jsxs5("div", { className: clsx5("wg_shell__drawerHeader"), children: [
|
|
258
|
+
/* @__PURE__ */ jsx5("h2", { className: clsx5("wg_shell__drawerTitle"), id: "fh-navigation-title", children: copy_nav_title }),
|
|
259
|
+
/* @__PURE__ */ jsx5("button", { className: clsx5("wg_shell__drawerClose"), type: "button", "aria-label": copy_nav_close, "data-navigation-close": "", children: /* @__PURE__ */ jsx5("svg", { className: clsx5("wg_shell__menuIcon"), viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx5("path", { d: "m6 6 12 12M18 6 6 18" }) }) })
|
|
218
260
|
] }),
|
|
219
|
-
/* @__PURE__ */
|
|
261
|
+
/* @__PURE__ */ jsx5("div", { className: clsx5("wg_shell__drawerContent"), "data-navigation-content": "" })
|
|
220
262
|
] }),
|
|
221
|
-
/* @__PURE__ */
|
|
222
|
-
/* @__PURE__ */
|
|
263
|
+
/* @__PURE__ */ jsx5("img", { className: clsx5("wg_shell__cursorFace"), src: asset_favicon_path, width: "24", height: "24", alt: "", "aria-hidden": "true", draggable: false, "data-cursor-face": "" }),
|
|
264
|
+
/* @__PURE__ */ jsx5("script", { type: "module", src: props.assets.clientPath })
|
|
223
265
|
] })
|
|
224
266
|
] });
|
|
225
267
|
};
|
|
226
268
|
|
|
227
269
|
// src/page/doc/pg-doc.tsx
|
|
228
|
-
import { jsx as
|
|
270
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
229
271
|
var PgDoc = (props) => {
|
|
230
|
-
return /* @__PURE__ */
|
|
272
|
+
return /* @__PURE__ */ jsx6(WgShell, { site: props.site, docs: props.docs, assets: props.assets, title: props.doc.data.name, current: props.doc.id, outline: props.doc.outline, children: /* @__PURE__ */ jsx6(WgProse, { html: props.doc.html }) });
|
|
231
273
|
};
|
|
232
274
|
|
|
233
275
|
// src/page/home/pg-home.tsx
|
|
234
|
-
import { jsx as
|
|
276
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
235
277
|
var PgHome = (props) => {
|
|
236
|
-
return /* @__PURE__ */
|
|
278
|
+
return /* @__PURE__ */ jsx7(WgShell, { site: props.site, docs: props.docs, assets: props.assets, outline: props.home?.outline, children: props.home === void 0 ? /* @__PURE__ */ jsx7(WgProse, { head: { title: props.site.title, lead: copy_home_missing } }) : /* @__PURE__ */ jsx7(WgProse, { html: props.home.html }) });
|
|
237
279
|
};
|
|
238
280
|
|
|
239
281
|
// src/app.tsx
|
|
240
|
-
import { jsx as
|
|
282
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
241
283
|
var toHtml = (page) => {
|
|
242
284
|
return `<!DOCTYPE html>${renderToStaticMarkup(page)}`;
|
|
243
285
|
};
|
|
244
286
|
var createApp = (options) => {
|
|
245
287
|
const app = new Hono();
|
|
246
|
-
app.get("/", (c) => c.html(toHtml(/* @__PURE__ */
|
|
288
|
+
app.get("/", (c) => c.html(toHtml(/* @__PURE__ */ jsx8(PgHome, { site: options.site, docs: options.store.docs, assets: options.assets, home: options.store.home }))));
|
|
247
289
|
app.get(
|
|
248
290
|
"/:slug{.+}/",
|
|
249
291
|
ssgParams(() => options.store.docs.map((doc) => ({ slug: doc.id }))),
|
|
250
292
|
(c) => {
|
|
251
293
|
const doc = options.store.docs.find((entry) => entry.id === c.req.param("slug"));
|
|
252
|
-
return doc === void 0 ? c.notFound() : c.html(toHtml(/* @__PURE__ */
|
|
294
|
+
return doc === void 0 ? c.notFound() : c.html(toHtml(/* @__PURE__ */ jsx8(PgDoc, { site: options.site, docs: options.store.docs, assets: options.assets, doc })));
|
|
253
295
|
}
|
|
254
296
|
);
|
|
255
297
|
return app;
|
|
@@ -935,7 +977,7 @@ import { VFile } from "vfile";
|
|
|
935
977
|
import { parse as parseYaml } from "yaml";
|
|
936
978
|
|
|
937
979
|
// src/type/doc-data.ts
|
|
938
|
-
import { z } from "zod";
|
|
980
|
+
import { z as z2 } from "zod";
|
|
939
981
|
|
|
940
982
|
// src/constant/doc.ts
|
|
941
983
|
var doc_type = {
|
|
@@ -944,28 +986,32 @@ var doc_type = {
|
|
|
944
986
|
};
|
|
945
987
|
var doc_order_default = Number.MAX_SAFE_INTEGER;
|
|
946
988
|
|
|
989
|
+
// src/type/group-path.ts
|
|
990
|
+
import { z } from "zod";
|
|
991
|
+
var groupPathSchema = z.union([z.string().trim().min(1), z.array(z.string().trim().min(1)).min(1)]).transform((path) => typeof path === "string" ? [path] : path);
|
|
992
|
+
|
|
947
993
|
// src/type/doc-data.ts
|
|
948
|
-
var docDataSchema =
|
|
994
|
+
var docDataSchema = z2.object({
|
|
949
995
|
/**
|
|
950
996
|
* 문서 이름 · 제목·목록 · 같은 첫 글자 허용
|
|
951
997
|
*/
|
|
952
|
-
name:
|
|
998
|
+
name: z2.string().trim().min(1),
|
|
953
999
|
/**
|
|
954
1000
|
* 짧은 문서 설명 · 사이드바 툴팁
|
|
955
1001
|
*/
|
|
956
|
-
label:
|
|
1002
|
+
label: z2.string(),
|
|
957
1003
|
/**
|
|
958
1004
|
* 문서 종류 · blueprint 전용 표현 미구현
|
|
959
1005
|
*/
|
|
960
|
-
type:
|
|
1006
|
+
type: z2.enum(doc_type).default(doc_type.document),
|
|
961
1007
|
/**
|
|
962
|
-
* 사이드바의 문서 묶음
|
|
1008
|
+
* 사이드바의 문서 묶음 경로 · 문자열도 배열로 정규화
|
|
963
1009
|
*/
|
|
964
|
-
group:
|
|
1010
|
+
group: groupPathSchema,
|
|
965
1011
|
/**
|
|
966
1012
|
* 묶음 안의 읽는 순서 · 미지정 문서는 뒤에서 이름순
|
|
967
1013
|
*/
|
|
968
|
-
order:
|
|
1014
|
+
order: z2.number().int().nonnegative().default(doc_order_default)
|
|
969
1015
|
});
|
|
970
1016
|
|
|
971
1017
|
// src/content/read-docs.ts
|
|
@@ -1068,7 +1114,7 @@ var createDevApp = (options) => {
|
|
|
1068
1114
|
};
|
|
1069
1115
|
|
|
1070
1116
|
// src/type/site-config.ts
|
|
1071
|
-
import { z as
|
|
1117
|
+
import { z as z3 } from "zod";
|
|
1072
1118
|
|
|
1073
1119
|
// src/constant/status.ts
|
|
1074
1120
|
var status_kind = {
|
|
@@ -1081,36 +1127,36 @@ var status_default_phrases = [
|
|
|
1081
1127
|
];
|
|
1082
1128
|
|
|
1083
1129
|
// src/type/site-config.ts
|
|
1084
|
-
var siteConfigSchema =
|
|
1130
|
+
var siteConfigSchema = z3.object({
|
|
1085
1131
|
/**
|
|
1086
1132
|
* 사이드바 이름과 탭 제목
|
|
1087
1133
|
*/
|
|
1088
|
-
title:
|
|
1134
|
+
title: z3.string().default(copy_site_title_default),
|
|
1089
1135
|
/**
|
|
1090
1136
|
* HTML 설명 메타데이터 · 선택
|
|
1091
1137
|
*/
|
|
1092
|
-
description:
|
|
1138
|
+
description: z3.string().optional(),
|
|
1093
1139
|
/**
|
|
1094
|
-
* 탐색
|
|
1140
|
+
* 탐색 묶음 경로의 읽는 순서 · 미지정 형제 묶음은 뒤에서 이름순
|
|
1095
1141
|
*/
|
|
1096
|
-
navigation:
|
|
1142
|
+
navigation: z3.array(groupPathSchema).refine((groups) => new Set(groups.map((path) => JSON.stringify(path))).size === groups.length, copy_error_navigation_clash).default(site_navigation_default.map((name) => [name])),
|
|
1097
1143
|
/**
|
|
1098
1144
|
* 본문에서 상태 표지로 표시할 문구
|
|
1099
1145
|
*/
|
|
1100
|
-
status:
|
|
1101
|
-
|
|
1146
|
+
status: z3.array(
|
|
1147
|
+
z3.object({
|
|
1102
1148
|
/**
|
|
1103
1149
|
* 찾을 문구 원문
|
|
1104
1150
|
*/
|
|
1105
|
-
phrase:
|
|
1151
|
+
phrase: z3.string().min(1).refine((phrase) => phrase.trim().length > 0),
|
|
1106
1152
|
/**
|
|
1107
1153
|
* 상태 표지의 색 종류
|
|
1108
1154
|
*/
|
|
1109
|
-
kind:
|
|
1155
|
+
kind: z3.enum(status_kind),
|
|
1110
1156
|
/**
|
|
1111
1157
|
* 문구 뒤의 YYYY-MM-DD 날짜 포함 여부
|
|
1112
1158
|
*/
|
|
1113
|
-
date:
|
|
1159
|
+
date: z3.boolean().optional()
|
|
1114
1160
|
})
|
|
1115
1161
|
).default(status_default_phrases)
|
|
1116
1162
|
});
|
|
@@ -1123,17 +1169,26 @@ var toErrorMessage = (error) => {
|
|
|
1123
1169
|
return error instanceof Error ? error.message : String(error);
|
|
1124
1170
|
};
|
|
1125
1171
|
|
|
1126
|
-
// src/util/
|
|
1172
|
+
// src/util/file/to-asset-path.ts
|
|
1127
1173
|
import { createHash } from "node:crypto";
|
|
1128
1174
|
import { readFileSync } from "node:fs";
|
|
1175
|
+
import { posix as posix2 } from "node:path";
|
|
1176
|
+
var toAssetPath = (options) => {
|
|
1177
|
+
const extension = posix2.extname(options.path);
|
|
1178
|
+
return `${posix2.dirname(options.path)}/${posix2.basename(options.path, extension)}.${createHash("sha256").update(readFileSync(options.file)).digest("hex")}${extension}`;
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
// src/util/font/to-font-css.ts
|
|
1182
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1183
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
1129
1184
|
import { basename, dirname as dirname2, extname as extname2, join as join3 } from "node:path";
|
|
1130
1185
|
var toFontCss = (options) => {
|
|
1131
1186
|
const files = /* @__PURE__ */ new Map();
|
|
1132
|
-
const css = [...
|
|
1187
|
+
const css = [...readFileSync2(options.css, "utf8").matchAll(/@font-face\s*\{[^}]*\}/g)].map(
|
|
1133
1188
|
(match) => match[0].replace(/font-display:\s*[^;]+;/g, `font-display: ${font_display_strategy};`).replace(/url\(([^)]+)\)/g, (_match, relativeUrl) => {
|
|
1134
1189
|
const file = join3(dirname2(options.css), relativeUrl);
|
|
1135
1190
|
const extension = extname2(file);
|
|
1136
|
-
const hash =
|
|
1191
|
+
const hash = createHash2("sha256").update(readFileSync2(file)).digest("hex").slice(0, font_hash_length);
|
|
1137
1192
|
const url = `${options.fontDir}/${basename(file, extension)}.${hash}${extension}`;
|
|
1138
1193
|
files.set(url, file);
|
|
1139
1194
|
return `url(${url})`;
|
|
@@ -1182,9 +1237,11 @@ var main = async () => {
|
|
|
1182
1237
|
throw new Error(`${copy_error_font_preload}: ${font_sans_preload_file}`);
|
|
1183
1238
|
}
|
|
1184
1239
|
const fontCss = [sans.css, mono.css, brand.css].join("\n");
|
|
1240
|
+
const stylePath = toAssetPath({ path: asset_style_path, file: join4(kitRoot, "dist/cli.css") });
|
|
1241
|
+
const clientPath = toAssetPath({ path: asset_client_path, file: join4(kitRoot, "dist/client.js") });
|
|
1185
1242
|
const files = new Map([
|
|
1186
|
-
[
|
|
1187
|
-
[
|
|
1243
|
+
[stylePath, join4(kitRoot, "dist/cli.css")],
|
|
1244
|
+
[clientPath, join4(kitRoot, "dist/client.js")],
|
|
1188
1245
|
[asset_favicon_path, join4(kitRoot, "src/asset/favicon.svg")],
|
|
1189
1246
|
...sans.files,
|
|
1190
1247
|
...mono.files,
|
|
@@ -1196,7 +1253,7 @@ var main = async () => {
|
|
|
1196
1253
|
const app = createApp({
|
|
1197
1254
|
site: siteConfig,
|
|
1198
1255
|
store,
|
|
1199
|
-
assets: { fontCss, preload: [...brand.files.keys(), sansPreload[0], ...mono.files.keys()], reload: command === "dev" }
|
|
1256
|
+
assets: { stylePath, clientPath, fontCss, preload: [...brand.files.keys(), sansPreload[0], ...mono.files.keys()], reload: command === "dev" }
|
|
1200
1257
|
});
|
|
1201
1258
|
if (command === "build") {
|
|
1202
1259
|
await rm(outDir, { recursive: true, force: true });
|