for-humanity 0.13.0 → 0.13.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 +10 -0
- package/README.md +3 -3
- package/dist/browser.js +4 -6
- package/dist/cli.css +1 -1
- package/dist/cli.js +3 -3
- package/dist/{load-site-CbpLEhwu.js → load-site-B-u5-jv0.js} +204 -240
- package/dist/{run-site-command-C80YoNOe.js → run-site-command-cqCjpIlc.js} +2 -2
- package/dist/site.js +1 -1
- package/dist/{start-dev-server-C4_Lu1nZ.js → start-dev-server-x7Ja5uwv.js} +12 -4
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as copy_nav_toc_label, T as copy_warn_unknown_status,
|
|
1
|
+
import { S as copy_nav_toc_label, T as copy_warn_unknown_status, a as copy_error_doc_id_clash, b as copy_nav_open, c as copy_error_doc_parent_missing, g as copy_error_unknown_directive, h as copy_error_snippet, i as copy_error_doc_id, l as copy_error_frontmatter, o as copy_error_doc_parent_cycle, r as copy_error_directive_syntax, s as copy_error_doc_parent_group, t as copy_error_asset, u as copy_error_home_clash, v as copy_nav_aria_label, w as copy_warn_broken_link, x as copy_nav_title, y as copy_nav_close } from "./copy-C1uzvEnr.js";
|
|
2
2
|
import { t as logger } from "./logger-Dv1i9xSV.js";
|
|
3
3
|
import { t as groupPathSchema } from "./group-path-XAThmzvC.js";
|
|
4
4
|
import { t as toChangeQueue } from "./to-change-queue-DbEnZGNK.js";
|
|
@@ -21,10 +21,12 @@ import { SKIP, visit } from "unist-util-visit";
|
|
|
21
21
|
import { createHash } from "node:crypto";
|
|
22
22
|
import Slugger from "github-slugger";
|
|
23
23
|
import { toString } from "hast-util-to-string";
|
|
24
|
+
import { dropRightWhile, dropWhile, minBy, takeWhile, uniqBy } from "es-toolkit/array";
|
|
24
25
|
import { transformerMetaHighlight, transformerNotationDiff, transformerNotationErrorLevel, transformerNotationHighlight, transformerNotationWordHighlight } from "@shikijs/transformers";
|
|
25
26
|
import { toString as toString$1 } from "mdast-util-to-string";
|
|
26
27
|
import { escape, escapeRegExp } from "es-toolkit/string";
|
|
27
28
|
import { findAndReplace } from "mdast-util-find-and-replace";
|
|
29
|
+
import { isEqual } from "es-toolkit/predicate";
|
|
28
30
|
import { VFile } from "vfile";
|
|
29
31
|
import { parse } from "yaml";
|
|
30
32
|
import { Hono } from "hono";
|
|
@@ -33,7 +35,6 @@ import { ssgParams } from "hono/ssg";
|
|
|
33
35
|
import clsx from "clsx";
|
|
34
36
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
35
37
|
import { createHead, escapeHtml } from "unhead/server";
|
|
36
|
-
import { takeWhile } from "es-toolkit/array";
|
|
37
38
|
import { Fragment, useEffect, useLayoutEffect, useRef, useState } from "hono/jsx";
|
|
38
39
|
import { flushSync } from "hono/jsx/dom";
|
|
39
40
|
//#region src/app/_constant/crawling.ts
|
|
@@ -212,11 +213,12 @@ var snippet_language_by_extension = {
|
|
|
212
213
|
".yml": "yaml"
|
|
213
214
|
};
|
|
214
215
|
//#endregion
|
|
215
|
-
//#region src/app/_function/create-processor/
|
|
216
|
+
//#region src/app/_function/create-processor/_to-snippet-body.ts
|
|
216
217
|
/**
|
|
217
218
|
* region 지정 시 해당 범위만 추출, 미지정 시 전체 사용
|
|
218
219
|
*
|
|
219
220
|
* - region 경계 줄과 공통 들여쓰기 제거
|
|
221
|
+
* - 빈 region은 빈 문자열, 없는 region은 undefined 반환
|
|
220
222
|
*/
|
|
221
223
|
var toSnippetBody = (source, region) => {
|
|
222
224
|
const lines = source.replace(/\r\n/g, "\n").split("\n");
|
|
@@ -235,12 +237,14 @@ var toSnippetBody = (source, region) => {
|
|
|
235
237
|
if (inside) kept.push(line);
|
|
236
238
|
}
|
|
237
239
|
if (!found) return;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const indent =
|
|
242
|
-
return
|
|
240
|
+
const content = dropRightWhile(dropWhile(kept, (line) => line.trim() === ""), (line) => line.trim() === "");
|
|
241
|
+
const leastIndentedLine = minBy(content.filter((line) => line.trim() !== ""), (line) => line.length - line.trimStart().length);
|
|
242
|
+
if (leastIndentedLine === void 0) return "";
|
|
243
|
+
const indent = leastIndentedLine.length - leastIndentedLine.trimStart().length;
|
|
244
|
+
return content.map((line) => line.slice(Math.min(indent, line.length - line.trimStart().length))).join("\n");
|
|
243
245
|
};
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/app/_function/create-processor/_remark-snippets.ts
|
|
244
248
|
/**
|
|
245
249
|
* 문서 상대 경로의 코드 스니펫 삽입
|
|
246
250
|
*
|
|
@@ -286,49 +290,43 @@ var shikiTransformers = [
|
|
|
286
290
|
transformerNotationWordHighlight({ matchAlgorithm: "v3" }),
|
|
287
291
|
transformerMetaHighlight(),
|
|
288
292
|
{
|
|
289
|
-
name: "for-humanity:
|
|
290
|
-
pre(node) {
|
|
291
|
-
const meta = this.options.meta?.__raw;
|
|
292
|
-
if (typeof meta === "string" && /\bline-numbers\b/.test(meta)) this.addClassToHast(node, "wg_prose__lineNumbers");
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
name: "for-humanity:code-tone",
|
|
297
|
-
pre(node) {
|
|
298
|
-
const meta = this.options.meta?.__raw;
|
|
299
|
-
if (typeof meta !== "string") return;
|
|
300
|
-
for (const word of ["bad", "good"]) if (new RegExp(`\\b${word}\\b`).test(meta)) this.addClassToHast(node, `wg_prose__code--${word}`);
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
{
|
|
304
|
-
name: "for-humanity:code-title",
|
|
293
|
+
name: "for-humanity:code-metadata",
|
|
305
294
|
root(root) {
|
|
306
|
-
|
|
307
|
-
|
|
295
|
+
this.pre.properties.style = void 0;
|
|
296
|
+
this.addClassToHast(this.pre, "wg_prose__code");
|
|
297
|
+
if (this.options.meta?.__raw === void 0) return;
|
|
298
|
+
if (/\bline-numbers\b/.test(this.options.meta.__raw)) this.addClassToHast(this.pre, "wg_prose__lineNumbers");
|
|
299
|
+
const isBad = /\bbad\b/.test(this.options.meta.__raw);
|
|
300
|
+
const isGood = /\bgood\b/.test(this.options.meta.__raw);
|
|
301
|
+
if (isBad) this.addClassToHast(this.pre, "wg_prose__code--bad");
|
|
302
|
+
if (isGood) this.addClassToHast(this.pre, "wg_prose__code--good");
|
|
303
|
+
const title = /\[([^\]]+)\]/.exec(this.options.meta.__raw)?.[1];
|
|
308
304
|
if (title === void 0) return;
|
|
309
|
-
|
|
310
|
-
const caption = {
|
|
305
|
+
root.children = [{
|
|
311
306
|
type: "element",
|
|
312
|
-
tagName: "
|
|
313
|
-
properties: { className: ["
|
|
307
|
+
tagName: "figure",
|
|
308
|
+
properties: { className: ["wg_prose__codeFile"] },
|
|
314
309
|
children: [{
|
|
315
|
-
type: "text",
|
|
316
|
-
value: title
|
|
317
|
-
}, {
|
|
318
310
|
type: "element",
|
|
319
|
-
tagName: "
|
|
320
|
-
properties: { className: [
|
|
311
|
+
tagName: "figcaption",
|
|
312
|
+
properties: { className: [
|
|
313
|
+
"wg_prose__codeFileTitle",
|
|
314
|
+
...isBad ? ["wg_prose__codeFileTitle--bad"] : [],
|
|
315
|
+
...isGood ? ["wg_prose__codeFileTitle--good"] : []
|
|
316
|
+
] },
|
|
321
317
|
children: [{
|
|
322
318
|
type: "text",
|
|
323
|
-
value:
|
|
319
|
+
value: title
|
|
320
|
+
}, {
|
|
321
|
+
type: "element",
|
|
322
|
+
tagName: "span",
|
|
323
|
+
properties: { className: ["wg_prose__codeFileLang"] },
|
|
324
|
+
children: [{
|
|
325
|
+
type: "text",
|
|
326
|
+
value: this.options.lang
|
|
327
|
+
}]
|
|
324
328
|
}]
|
|
325
|
-
}]
|
|
326
|
-
};
|
|
327
|
-
root.children = [{
|
|
328
|
-
type: "element",
|
|
329
|
-
tagName: "figure",
|
|
330
|
-
properties: { className: ["wg_prose__codeFile", ...tones.map((tone) => `wg_prose__codeFile--${tone}`)] },
|
|
331
|
-
children: [caption, ...root.children.filter((child) => child.type !== "doctype")]
|
|
329
|
+
}, ...root.children.filter((child) => child.type !== "doctype")]
|
|
332
330
|
}];
|
|
333
331
|
}
|
|
334
332
|
}
|
|
@@ -363,25 +361,25 @@ var section_heading_depth = {
|
|
|
363
361
|
* name 제목과 첫 절 앞 본문의 공통 머리. README는 원문의 h1 사용
|
|
364
362
|
*/
|
|
365
363
|
var rehypeHead = () => (tree, file) => {
|
|
366
|
-
const frontmatter = file.data.fh?.frontmatter;
|
|
367
364
|
const firstSection = tree.children.findIndex((node) => node.type === "element" && node.tagName === `h${section_heading_depth.section}`);
|
|
368
365
|
const leadEnd = firstSection === -1 ? tree.children.length : firstSection;
|
|
369
|
-
|
|
370
|
-
tree.children = [{
|
|
366
|
+
const head = {
|
|
371
367
|
type: "element",
|
|
372
368
|
tagName: "header",
|
|
373
369
|
properties: { className: ["wg_prose__head"] },
|
|
374
|
-
children:
|
|
375
|
-
}
|
|
376
|
-
if (frontmatter !== void 0
|
|
370
|
+
children: tree.children.slice(0, leadEnd).filter((node) => node.type !== "doctype")
|
|
371
|
+
};
|
|
372
|
+
if (file.data.fh?.frontmatter !== void 0) head.children.unshift({
|
|
377
373
|
type: "element",
|
|
378
374
|
tagName: "h1",
|
|
379
|
-
properties: {},
|
|
375
|
+
properties: { className: ["wg_prose__title"] },
|
|
380
376
|
children: [{
|
|
381
377
|
type: "text",
|
|
382
|
-
value: frontmatter.name
|
|
378
|
+
value: file.data.fh.frontmatter.name
|
|
383
379
|
}]
|
|
384
380
|
});
|
|
381
|
+
else if (leadEnd === 0) return;
|
|
382
|
+
tree.children = [head, ...tree.children.slice(leadEnd)];
|
|
385
383
|
};
|
|
386
384
|
//#endregion
|
|
387
385
|
//#region src/component/widget/prose/_function/rehype-sections/_to-section-headings.ts
|
|
@@ -407,7 +405,9 @@ var toSectionHeadings = (node) => {
|
|
|
407
405
|
*/
|
|
408
406
|
var rehypeSections = () => (tree, file) => {
|
|
409
407
|
if (file.data.fh === void 0) return;
|
|
410
|
-
file.data.fh.sections = toSectionHeadings(tree).map((heading) =>
|
|
408
|
+
file.data.fh.sections = toSectionHeadings(tree).map((heading) => {
|
|
409
|
+
return { part: typeof heading.properties.dataPart === "string" ? heading.properties.dataPart : void 0 };
|
|
410
|
+
});
|
|
411
411
|
};
|
|
412
412
|
//#endregion
|
|
413
413
|
//#region src/component/widget/prose/_function/rehype-tables.ts
|
|
@@ -626,12 +626,13 @@ var remarkUnknownDirectives = () => (tree, file) => {
|
|
|
626
626
|
*
|
|
627
627
|
* - 코드와 문서 머리 생성 후 제목 ID와 가름 수집
|
|
628
628
|
* - 플러그인 HTML 조각은 마지막 rehype-raw에서 해석
|
|
629
|
-
* - Shiki
|
|
629
|
+
* - 코드 상자의 색상은 prose CSS 소유, Shiki 토큰 색상은 --app-code-* 참조
|
|
630
630
|
*/
|
|
631
631
|
var createProcessor = (options) => {
|
|
632
|
+
const files = options.files ?? /* @__PURE__ */ new Map();
|
|
632
633
|
return unified().use(remarkParse).use(remarkFrontmatter, ["yaml"]).use(remarkGfm).use(remarkSnippets, { root: options.root }).use(remarkSmartypants).use(remarkDirective).use(remarkBlocks).use(remarkParts).use(remarkMermaid, {
|
|
633
634
|
assets: options.mermaid,
|
|
634
|
-
files
|
|
635
|
+
files
|
|
635
636
|
}).use(remarkStatus, { status: options.site.status }).use(remarkSwatch).use(remarkLinks, { root: options.root }).use(remarkUnknownDirectives).use(remarkReport, { root: options.root }).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeShiki, {
|
|
636
637
|
theme: createCssVariablesTheme({
|
|
637
638
|
name: "for-humanity",
|
|
@@ -640,7 +641,7 @@ var createProcessor = (options) => {
|
|
|
640
641
|
transformers: shikiTransformers
|
|
641
642
|
}).use(rehypeHead).use(rehypeHeadingIds).use(rehypeSections).use(rehypeTables).use(rehypeRaw).use(rehypeAssets, {
|
|
642
643
|
root: options.root,
|
|
643
|
-
files
|
|
644
|
+
files
|
|
644
645
|
}).use(rehypeStringify, { allowDangerousHtml: true });
|
|
645
646
|
};
|
|
646
647
|
//#endregion
|
|
@@ -759,7 +760,7 @@ var readDocs = async (options) => {
|
|
|
759
760
|
const parent = docsById.get(parentId);
|
|
760
761
|
if (parent === void 0) throw new Error(`${copy_error_doc_parent_missing}: ${doc.id} → ${parentId}`);
|
|
761
762
|
if (ancestors.has(parent.id)) throw new Error(`${copy_error_doc_parent_cycle}: ${[...ancestors, parent.id].join(" → ")}`);
|
|
762
|
-
if (
|
|
763
|
+
if (!isEqual(parent.data.group, doc.data.group)) throw new Error(`${copy_error_doc_parent_group}: ${doc.id} → ${parent.id}`);
|
|
763
764
|
ancestors.add(parent.id);
|
|
764
765
|
parentId = parent.data.parent;
|
|
765
766
|
}
|
|
@@ -808,11 +809,13 @@ var readHome = async (options) => {
|
|
|
808
809
|
var toCrawlingApp = (params) => {
|
|
809
810
|
const app = new Hono();
|
|
810
811
|
app.get(crawling_robots_path, (c) => c.text(`User-agent: *\nAllow: /\n\nSitemap: ${params.url}${crawling_sitemap_path}\n`));
|
|
811
|
-
app.get(crawling_sitemap_path, (_c) =>
|
|
812
|
+
app.get(crawling_sitemap_path, (_c) => {
|
|
813
|
+
return new Response(html`<?xml version="1.0" encoding="UTF-8"?>
|
|
812
814
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
813
815
|
<url><loc>${params.url}/</loc></url>
|
|
814
816
|
${params.store.docs.map((doc) => html`<url><loc>${params.url}/${doc.id.split("/").map(encodeURIComponent).join("/")}/</loc></url>`)}
|
|
815
|
-
</urlset>`.toString(), { headers: { "Content-Type": "application/xml; charset=utf-8" } })
|
|
817
|
+
</urlset>`.toString(), { headers: { "Content-Type": "application/xml; charset=utf-8" } });
|
|
818
|
+
});
|
|
816
819
|
return app;
|
|
817
820
|
};
|
|
818
821
|
//#endregion
|
|
@@ -863,6 +866,14 @@ var toHttpApp = (options) => {
|
|
|
863
866
|
return app;
|
|
864
867
|
};
|
|
865
868
|
//#endregion
|
|
869
|
+
//#region src/app/_function/to-page-app/_to-html.ts
|
|
870
|
+
/**
|
|
871
|
+
* Hono JSX 페이지의 HTML 문서 선언, 서버와 정적 출력의 공통 직렬화
|
|
872
|
+
*/
|
|
873
|
+
var toHtml = (page) => {
|
|
874
|
+
return html`<!DOCTYPE html>${page}`;
|
|
875
|
+
};
|
|
876
|
+
//#endregion
|
|
866
877
|
//#region src/component/widget/prose/wg-prose.tsx
|
|
867
878
|
var WgProse = (props) => {
|
|
868
879
|
if (props.html !== void 0) return /* @__PURE__ */ jsx("article", {
|
|
@@ -873,7 +884,13 @@ var WgProse = (props) => {
|
|
|
873
884
|
className: clsx("wg_prose__root"),
|
|
874
885
|
children: props.head !== void 0 && /* @__PURE__ */ jsxs("header", {
|
|
875
886
|
className: clsx("wg_prose__head"),
|
|
876
|
-
children: [/* @__PURE__ */ jsx("h1", {
|
|
887
|
+
children: [/* @__PURE__ */ jsx("h1", {
|
|
888
|
+
className: clsx("wg_prose__title"),
|
|
889
|
+
children: props.head.title
|
|
890
|
+
}), props.head.lead !== void 0 && /* @__PURE__ */ jsx("p", {
|
|
891
|
+
className: clsx("wg_prose__lead"),
|
|
892
|
+
children: props.head.lead
|
|
893
|
+
})]
|
|
877
894
|
})
|
|
878
895
|
});
|
|
879
896
|
};
|
|
@@ -931,10 +948,13 @@ var toDocGroupBranches = (options) => {
|
|
|
931
948
|
/**
|
|
932
949
|
* 메타데이터 경로로 읽는 순서를 가진 문서 계층 구성
|
|
933
950
|
*
|
|
934
|
-
* -
|
|
951
|
+
* - 같은 조상의 중복 지정은 최초 순서 유지
|
|
952
|
+
* - 미지정 형제 묶음은 이름순
|
|
935
953
|
*/
|
|
936
954
|
var toDocGroups = (options) => {
|
|
937
|
-
const positions = new Map(options.navigation.flatMap((path, index) =>
|
|
955
|
+
const positions = new Map(uniqBy(options.navigation.flatMap((path, index) => {
|
|
956
|
+
return path.map((_name, depth) => [JSON.stringify(path.slice(0, depth + 1)), index]);
|
|
957
|
+
}), (entry) => entry[0]));
|
|
938
958
|
const children = Map.groupBy(options.docs, (doc) => doc.data.parent);
|
|
939
959
|
return toDocGroupBranches({
|
|
940
960
|
docs: options.docs,
|
|
@@ -992,15 +1012,23 @@ var toTocGroups = (outline) => {
|
|
|
992
1012
|
heading,
|
|
993
1013
|
section: outline.sections[index]
|
|
994
1014
|
}));
|
|
995
|
-
const sections = entries.flatMap((entry, index) =>
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1015
|
+
const sections = entries.flatMap((entry, index) => {
|
|
1016
|
+
if (entry.heading.depth !== section_heading_depth.section) return [];
|
|
1017
|
+
return [{
|
|
1018
|
+
heading: entry.heading,
|
|
1019
|
+
part: entry.section.part,
|
|
1020
|
+
subs: takeWhile(entries.slice(index + 1), (sub) => sub.heading.depth !== section_heading_depth.section).map((sub) => {
|
|
1021
|
+
return { heading: sub.heading };
|
|
1022
|
+
})
|
|
1023
|
+
}];
|
|
1024
|
+
});
|
|
1025
|
+
return sections.flatMap((section, index) => {
|
|
1026
|
+
if (index > 0 && section.part === void 0) return [];
|
|
1027
|
+
return [{
|
|
1028
|
+
part: section.part,
|
|
1029
|
+
sections: [section, ...takeWhile(sections.slice(index + 1), (next) => next.part === void 0)]
|
|
1030
|
+
}];
|
|
1031
|
+
});
|
|
1004
1032
|
};
|
|
1005
1033
|
//#endregion
|
|
1006
1034
|
//#region src/component/widget/navigation/_function/to-navigation-data/to-navigation-data.ts
|
|
@@ -1366,30 +1394,6 @@ var navigation_mobile_query = "(width < 1024px)";
|
|
|
1366
1394
|
*/
|
|
1367
1395
|
var navigation_wide_query = "(width >= 1360px)";
|
|
1368
1396
|
//#endregion
|
|
1369
|
-
//#region src/component/widget/navigation/_function/restore-navigation-scroll.ts
|
|
1370
|
-
/**
|
|
1371
|
-
* 탐색 진입점에서 첫 paint 전 복원. 기존 배치별 저장 자료도 한 번 수용
|
|
1372
|
-
*/
|
|
1373
|
-
var restoreNavigationScroll = (options) => {
|
|
1374
|
-
const navigation = document.querySelector("[data-navigation]");
|
|
1375
|
-
const documents = document.querySelector("[data-navigation-scroll=\"documents\"]");
|
|
1376
|
-
const outline = document.querySelector("[data-navigation-scroll=\"outline\"]");
|
|
1377
|
-
if (!navigation || !documents || !outline) return;
|
|
1378
|
-
navigation.dataset.navigationLayout = options.layout;
|
|
1379
|
-
try {
|
|
1380
|
-
const value = sessionStorage.getItem(options.key);
|
|
1381
|
-
const stored = value === null ? void 0 : JSON.parse(value);
|
|
1382
|
-
const state = typeof stored === "object" && stored !== null && "version" in stored && stored.version === options.version && "state" in stored ? options.toState(stored.state) : { positions: {} };
|
|
1383
|
-
const legacy = sessionStorage.getItem(`${options.key}:${options.layout === "drawer" ? "drawer" : "desktop"}`);
|
|
1384
|
-
const previous = state.positions[options.layout] === void 0 && legacy !== null ? options.toState({ positions: { [options.layout]: JSON.parse(legacy) } }).positions[options.layout] : void 0;
|
|
1385
|
-
const position = state.positions[options.layout] ?? previous;
|
|
1386
|
-
const signature = [...navigation.querySelectorAll(".wg_navigationNav__root a[href]")].map((link) => `${link.pathname}:${link.textContent}`).join("\n");
|
|
1387
|
-
if (position === void 0 || position.navigation !== signature) return;
|
|
1388
|
-
documents.scrollTop = position.documents;
|
|
1389
|
-
if (position.pathname === location.pathname) outline.scrollTop = position.outline;
|
|
1390
|
-
} catch {}
|
|
1391
|
-
};
|
|
1392
|
-
//#endregion
|
|
1393
1397
|
//#region src/component/widget/navigation/_wg-navigation-nav-list.tsx
|
|
1394
1398
|
var WgNavigationNavList = (props) => {
|
|
1395
1399
|
const entries = [...props.group.docs.map((doc) => ({
|
|
@@ -1404,24 +1408,26 @@ var WgNavigationNavList = (props) => {
|
|
|
1404
1408
|
return /* @__PURE__ */ jsx("ul", {
|
|
1405
1409
|
className: clsx("wg_navigationNavList__root"),
|
|
1406
1410
|
"aria-label": props.group.name,
|
|
1407
|
-
children: entries.map((entry) =>
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1411
|
+
children: entries.map((entry) => {
|
|
1412
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
1413
|
+
className: clsx("wg_navigationNavList__item"),
|
|
1414
|
+
children: [entry.kind === "document" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("a", {
|
|
1415
|
+
className: clsx("wg_navigationNavList__link", { "wg_navigationNavList__link--active": entry.doc.active }),
|
|
1416
|
+
href: `/${entry.doc.id}/`,
|
|
1417
|
+
title: entry.doc.label,
|
|
1418
|
+
"aria-current": entry.doc.active ? "page" : void 0,
|
|
1419
|
+
children: entry.doc.name
|
|
1420
|
+
}), entry.doc.children.length > 0 && /* @__PURE__ */ jsx(WgNavigationNavList, { group: {
|
|
1421
|
+
name: entry.doc.name,
|
|
1422
|
+
path: props.group.path,
|
|
1423
|
+
docs: entry.doc.children,
|
|
1424
|
+
groups: []
|
|
1425
|
+
} })] }), entry.kind === "group" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
1426
|
+
className: clsx("wg_navigationNavList__branch"),
|
|
1427
|
+
children: entry.group.name
|
|
1428
|
+
}), /* @__PURE__ */ jsx(WgNavigationNavList, { group: entry.group })] })]
|
|
1429
|
+
}, entry.key);
|
|
1430
|
+
})
|
|
1425
1431
|
});
|
|
1426
1432
|
};
|
|
1427
1433
|
//#endregion
|
|
@@ -1432,14 +1438,16 @@ var WgNavigationNav = (props) => {
|
|
|
1432
1438
|
"aria-label": copy_nav_aria_label,
|
|
1433
1439
|
children: /* @__PURE__ */ jsx("div", {
|
|
1434
1440
|
className: clsx("wg_navigationNav__docs"),
|
|
1435
|
-
children: props.groups.map((group) =>
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1441
|
+
children: props.groups.map((group) => {
|
|
1442
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
1443
|
+
className: clsx("wg_navigationNav__docGroup"),
|
|
1444
|
+
"aria-label": group.name,
|
|
1445
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1446
|
+
className: clsx("wg_navigationNav__group"),
|
|
1447
|
+
children: group.name
|
|
1448
|
+
}), /* @__PURE__ */ jsx(WgNavigationNavList, { group })]
|
|
1449
|
+
}, JSON.stringify(group.path));
|
|
1450
|
+
})
|
|
1443
1451
|
})
|
|
1444
1452
|
});
|
|
1445
1453
|
};
|
|
@@ -1579,72 +1587,46 @@ var WgNavigationToc = (props) => {
|
|
|
1579
1587
|
children: [/* @__PURE__ */ jsx("div", {
|
|
1580
1588
|
className: clsx("wg_navigationToc__label"),
|
|
1581
1589
|
children: copy_nav_toc_label
|
|
1582
|
-
}), props.groups.map((group) =>
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1590
|
+
}), props.groups.map((group) => {
|
|
1591
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1592
|
+
className: clsx("wg_navigationToc__groupSection", { "wg_navigationToc__groupSection--named": group.part !== void 0 }),
|
|
1593
|
+
children: [group.part !== void 0 && /* @__PURE__ */ jsx("div", {
|
|
1594
|
+
className: clsx("wg_navigationToc__group"),
|
|
1595
|
+
children: group.part
|
|
1596
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
1597
|
+
className: clsx("wg_navigationToc__list"),
|
|
1598
|
+
children: group.sections.map((section) => {
|
|
1599
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
1600
|
+
className: clsx("wg_navigationToc__item"),
|
|
1601
|
+
children: [/* @__PURE__ */ jsx("a", {
|
|
1602
|
+
className: clsx("wg_navigationToc__link", { "wg_navigationToc__link--active": section.heading.slug === currentSlug }),
|
|
1603
|
+
href: `#${section.heading.slug}`,
|
|
1604
|
+
"data-toc-link": "",
|
|
1605
|
+
"aria-current": section.heading.slug === currentSlug ? "location" : void 0,
|
|
1606
|
+
children: section.heading.text
|
|
1607
|
+
}), section.subs.length > 0 && /* @__PURE__ */ jsx("ul", {
|
|
1608
|
+
className: clsx("wg_navigationToc__sub"),
|
|
1609
|
+
children: section.subs.map((sub) => {
|
|
1610
|
+
return /* @__PURE__ */ jsx("li", {
|
|
1611
|
+
className: clsx("wg_navigationToc__subItem"),
|
|
1612
|
+
children: /* @__PURE__ */ jsx("a", {
|
|
1613
|
+
className: clsx("wg_navigationToc__subLink", { "wg_navigationToc__subLink--active": sub.heading.slug === currentSlug }),
|
|
1614
|
+
href: `#${sub.heading.slug}`,
|
|
1615
|
+
"data-toc-sub-link": "",
|
|
1616
|
+
"aria-current": sub.heading.slug === currentSlug ? "location" : void 0,
|
|
1617
|
+
children: sub.heading.text
|
|
1618
|
+
})
|
|
1619
|
+
}, sub.heading.slug);
|
|
1620
|
+
})
|
|
1621
|
+
})]
|
|
1622
|
+
}, section.heading.slug);
|
|
1623
|
+
})
|
|
1624
|
+
})]
|
|
1625
|
+
}, group.sections[0].heading.slug);
|
|
1626
|
+
})]
|
|
1613
1627
|
});
|
|
1614
1628
|
};
|
|
1615
1629
|
//#endregion
|
|
1616
|
-
//#region src/store/navigation/navigation-storage.ts
|
|
1617
|
-
/**
|
|
1618
|
-
* 같은 탭에서 문서 이동 후 탐색 위치 복원. 데스크톱과 드로어는 별도 저장
|
|
1619
|
-
*/
|
|
1620
|
-
var navigation_scroll_storage_key = "for-humanity:navigation-scroll";
|
|
1621
|
-
//#endregion
|
|
1622
|
-
//#region src/store/navigation/to-navigation-scroll-state.ts
|
|
1623
|
-
/**
|
|
1624
|
-
* 알 수 없는 저장 형태, 음수, 무한 좌표 제외. 초기 HTML과 Zustand에 같은 검증 적용
|
|
1625
|
-
*/
|
|
1626
|
-
var toNavigationScrollState = (value) => {
|
|
1627
|
-
/**
|
|
1628
|
-
* 한 배치의 완전한 좌표만 복원 대상으로 수용
|
|
1629
|
-
*/
|
|
1630
|
-
const toPosition = (position) => {
|
|
1631
|
-
if (typeof position !== "object" || position === null || !("navigation" in position) || typeof position.navigation !== "string" || !("pathname" in position) || typeof position.pathname !== "string" || !("documents" in position) || typeof position.documents !== "number" || !Number.isFinite(position.documents) || position.documents < 0 || !("outline" in position) || typeof position.outline !== "number" || !Number.isFinite(position.outline) || position.outline < 0) return;
|
|
1632
|
-
return {
|
|
1633
|
-
navigation: position.navigation,
|
|
1634
|
-
pathname: position.pathname,
|
|
1635
|
-
documents: position.documents,
|
|
1636
|
-
outline: position.outline
|
|
1637
|
-
};
|
|
1638
|
-
};
|
|
1639
|
-
if (typeof value !== "object" || value === null || !("positions" in value) || typeof value.positions !== "object" || value.positions === null) return { positions: {} };
|
|
1640
|
-
const positions = {
|
|
1641
|
-
wide: toPosition("wide" in value.positions ? value.positions.wide : void 0),
|
|
1642
|
-
desktop: toPosition("desktop" in value.positions ? value.positions.desktop : void 0),
|
|
1643
|
-
drawer: toPosition("drawer" in value.positions ? value.positions.drawer : void 0)
|
|
1644
|
-
};
|
|
1645
|
-
return { positions: Object.fromEntries(Object.entries(positions).filter((entry) => entry[1] !== void 0)) };
|
|
1646
|
-
};
|
|
1647
|
-
//#endregion
|
|
1648
1630
|
//#region src/util/dom/reveal-hash-target.ts
|
|
1649
1631
|
/**
|
|
1650
1632
|
* 접힌 내용의 hash 대상과 상위 Details 공개
|
|
@@ -1678,6 +1660,7 @@ var WgNavigation = (props) => {
|
|
|
1678
1660
|
const layoutRef = useRef(layout);
|
|
1679
1661
|
const focusRef = useRef(null);
|
|
1680
1662
|
const restorePendingRef = useRef(true);
|
|
1663
|
+
const navigationSignature = JSON.stringify(props.data.groups, (key, value) => key === "active" ? void 0 : value);
|
|
1681
1664
|
/**
|
|
1682
1665
|
* 저장된 배치와 현재 CSS 조건을 구분. 조건 변경 직후 잘린 좌표를 이전 배치에 저장하지 않음
|
|
1683
1666
|
*/
|
|
@@ -1699,31 +1682,23 @@ var WgNavigation = (props) => {
|
|
|
1699
1682
|
return layoutRef.current === "drawer" ? drawerDocumentsRef.current : documentsRef.current;
|
|
1700
1683
|
};
|
|
1701
1684
|
/**
|
|
1702
|
-
* 모든 링크의 URL, 표시명으로 사이트 공통 문서 목록의 동일성 판정
|
|
1703
|
-
*/
|
|
1704
|
-
const getNavigationSignature = () => {
|
|
1705
|
-
const navigation = getNavigationPort();
|
|
1706
|
-
if (navigation === null) return void 0;
|
|
1707
|
-
return [...navigation.querySelectorAll(".wg_navigationNav__root a[href]")].map((link) => `${link.pathname}:${link.textContent}`).join("\n");
|
|
1708
|
-
};
|
|
1709
|
-
/**
|
|
1710
1685
|
* 닫기, 이동보다 먼저 실제 좌표 보존. 숨긴 모달과 아직 복원하지 않은 배치는 저장하지 않음
|
|
1711
1686
|
*/
|
|
1712
1687
|
const saveScroll = () => {
|
|
1713
1688
|
const documents = getDocumentsPort();
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
}
|
|
1689
|
+
const outline = outlineRef.current;
|
|
1690
|
+
if (props.stores === void 0 || documents === null || outline === null || restorePendingRef.current || layoutRef.current !== getNavigationLayout() || layoutRef.current === "drawer" && dialogRef.current?.open !== true) return;
|
|
1691
|
+
props.stores.scroll.setState((state) => {
|
|
1692
|
+
return { positions: {
|
|
1693
|
+
...state.positions,
|
|
1694
|
+
[layoutRef.current]: {
|
|
1695
|
+
navigation: navigationSignature,
|
|
1696
|
+
pathname: location.pathname,
|
|
1697
|
+
documents: documents.scrollTop,
|
|
1698
|
+
outline: outline.scrollTop
|
|
1699
|
+
}
|
|
1700
|
+
} };
|
|
1701
|
+
});
|
|
1727
1702
|
};
|
|
1728
1703
|
/**
|
|
1729
1704
|
* 연결된 영역에서 첫 paint 전 복원. 문서는 페이지 간 공유하고 목차는 같은 경로에서만 재사용
|
|
@@ -1731,25 +1706,16 @@ var WgNavigation = (props) => {
|
|
|
1731
1706
|
const restoreScroll = () => {
|
|
1732
1707
|
layoutRef.current = getNavigationLayout();
|
|
1733
1708
|
const documents = getDocumentsPort();
|
|
1734
|
-
|
|
1735
|
-
if (props.stores === void 0 || documents === null || outlineRef.current === null || navigation === null) return;
|
|
1709
|
+
if (props.stores === void 0 || documents === null || outlineRef.current === null) return;
|
|
1736
1710
|
if (layoutRef.current === "drawer" && dialogRef.current?.open !== true) return;
|
|
1737
|
-
const signature = getNavigationSignature();
|
|
1738
|
-
if (signature === void 0) return;
|
|
1739
1711
|
const position = props.stores.scroll.getState().positions[layoutRef.current];
|
|
1740
|
-
|
|
1741
|
-
outlineRef.current.scrollTop = 0;
|
|
1742
|
-
if (position === void 0) restoreNavigationScroll({
|
|
1743
|
-
key: navigation_scroll_storage_key,
|
|
1744
|
-
version: 1,
|
|
1745
|
-
layout: layoutRef.current,
|
|
1746
|
-
toState: toNavigationScrollState
|
|
1747
|
-
});
|
|
1748
|
-
else if (position.navigation === signature) {
|
|
1712
|
+
if (position?.navigation === navigationSignature) {
|
|
1749
1713
|
documents.scrollTop = position.documents;
|
|
1750
|
-
|
|
1714
|
+
outlineRef.current.scrollTop = position.pathname === location.pathname ? position.outline : 0;
|
|
1715
|
+
} else {
|
|
1716
|
+
documents.scrollTop = 0;
|
|
1717
|
+
outlineRef.current.scrollTop = 0;
|
|
1751
1718
|
}
|
|
1752
|
-
navigation.dataset.navigationLayout = layoutRef.current;
|
|
1753
1719
|
restorePendingRef.current = false;
|
|
1754
1720
|
saveScroll();
|
|
1755
1721
|
documents.dispatchEvent(new Event("scroll"));
|
|
@@ -1834,12 +1800,12 @@ var WgNavigation = (props) => {
|
|
|
1834
1800
|
flushSync(() => setLayout(nextLayout));
|
|
1835
1801
|
};
|
|
1836
1802
|
/**
|
|
1837
|
-
* BFCache 복귀 시 저장
|
|
1803
|
+
* BFCache 복귀 시 persist의 최신 저장 좌표를 다시 읽어 복원
|
|
1838
1804
|
*/
|
|
1839
1805
|
const handlePageShow = (event) => {
|
|
1840
1806
|
if (!event.persisted || props.stores === void 0) return;
|
|
1841
1807
|
restorePendingRef.current = true;
|
|
1842
|
-
|
|
1808
|
+
props.stores.scroll.persist.rehydrate();
|
|
1843
1809
|
handleLayoutChange();
|
|
1844
1810
|
restoreScroll();
|
|
1845
1811
|
};
|
|
@@ -1921,6 +1887,7 @@ var WgNavigation = (props) => {
|
|
|
1921
1887
|
ref: props.stores === void 0 ? void 0 : navigationRef,
|
|
1922
1888
|
className: clsx("wg_navigation__navigation"),
|
|
1923
1889
|
"data-navigation": "",
|
|
1890
|
+
"data-navigation-layout": layout,
|
|
1924
1891
|
children: /* @__PURE__ */ jsx(WgNavigationScroll, {
|
|
1925
1892
|
portRef: documentsRef,
|
|
1926
1893
|
kind: "documents",
|
|
@@ -1983,6 +1950,7 @@ var WgNavigation = (props) => {
|
|
|
1983
1950
|
ref: props.stores === void 0 ? void 0 : drawerNavigationRef,
|
|
1984
1951
|
className: clsx("wg_navigation__navigation"),
|
|
1985
1952
|
"data-navigation": "",
|
|
1953
|
+
"data-navigation-layout": layout,
|
|
1986
1954
|
children: /* @__PURE__ */ jsx(WgNavigationScroll, {
|
|
1987
1955
|
portRef: drawerDocumentsRef,
|
|
1988
1956
|
kind: "documents",
|
|
@@ -2224,25 +2192,19 @@ var PgDoc = (props) => {
|
|
|
2224
2192
|
//#endregion
|
|
2225
2193
|
//#region src/page/home/pg-home.tsx
|
|
2226
2194
|
var PgHome = (props) => {
|
|
2227
|
-
return /* @__PURE__ */
|
|
2195
|
+
return /* @__PURE__ */ jsxs(WgShell, {
|
|
2228
2196
|
site: props.site,
|
|
2229
2197
|
docs: props.docs,
|
|
2230
2198
|
assets: props.assets,
|
|
2231
2199
|
outline: props.home?.outline,
|
|
2232
|
-
children: props.home === void 0
|
|
2200
|
+
children: [props.home !== void 0 && /* @__PURE__ */ jsx(WgProse, { html: props.home.html }), props.home === void 0 && /* @__PURE__ */ jsx(WgProse, { head: {
|
|
2233
2201
|
title: props.site.title,
|
|
2234
|
-
lead:
|
|
2235
|
-
} })
|
|
2202
|
+
lead: "문서 폴더에 README.md를 추가하면 이곳에 홈 화면으로 표시됩니다."
|
|
2203
|
+
} })]
|
|
2236
2204
|
});
|
|
2237
2205
|
};
|
|
2238
2206
|
//#endregion
|
|
2239
|
-
//#region src/app/_function/to-page-app.tsx
|
|
2240
|
-
/**
|
|
2241
|
-
* Hono JSX 페이지에 HTML 문서 선언 추가. 서버와 정적 출력에서 같은 렌더러 사용
|
|
2242
|
-
*/
|
|
2243
|
-
var toHtml = (page) => {
|
|
2244
|
-
return html`<!DOCTYPE html>${page}`;
|
|
2245
|
-
};
|
|
2207
|
+
//#region src/app/_function/to-page-app/to-page-app.tsx
|
|
2246
2208
|
/**
|
|
2247
2209
|
* 첫 화면 /. 문서 /:slug{.+}/. 하위 폴더 id 지원
|
|
2248
2210
|
*
|
|
@@ -2278,14 +2240,16 @@ var toPageApp = (options) => {
|
|
|
2278
2240
|
var toFontCss = (options) => {
|
|
2279
2241
|
const files = /* @__PURE__ */ new Map();
|
|
2280
2242
|
return {
|
|
2281
|
-
css: [...readFileSync(options.css, "utf8").matchAll(/@font-face\s*\{[^}]*\}/g)].map((match) =>
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2243
|
+
css: [...readFileSync(options.css, "utf8").matchAll(/@font-face\s*\{[^}]*\}/g)].map((match) => {
|
|
2244
|
+
return match[0].replace(/font-display:\s*[^;]+;/g, `font-display: ${font_display_strategy};`).replace(/url\(([^)]+)\)/g, (_match, relativeUrl) => {
|
|
2245
|
+
const file = join(dirname(options.css), relativeUrl);
|
|
2246
|
+
const extension = extname(file);
|
|
2247
|
+
const hash = createHash("sha256").update(readFileSync(file)).digest("hex").slice(0, 12);
|
|
2248
|
+
const url = `${options.fontDir}/${basename(file, extension)}.${hash}${extension}`;
|
|
2249
|
+
files.set(url, file);
|
|
2250
|
+
return `url(${url})`;
|
|
2251
|
+
});
|
|
2252
|
+
}).join("\n"),
|
|
2289
2253
|
files
|
|
2290
2254
|
};
|
|
2291
2255
|
};
|