for-humanity 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +5 -5
  3. package/dist/browser.js +39 -0
  4. package/dist/cli.css +350 -262
  5. package/dist/cli.js +1123 -363
  6. package/package.json +8 -11
  7. package/src/component/widget/{shell → cursor-face}/_constant/cursor-face.ts +5 -0
  8. package/src/component/widget/cursor-face/wg-cursor-face.css +15 -0
  9. package/src/component/widget/cursor-face/wg-cursor-face.tsx +153 -0
  10. package/src/component/widget/navigation/_constant/navigation-data-schema.ts +35 -0
  11. package/src/component/widget/navigation/_constant/navigation.ts +9 -0
  12. package/src/component/widget/navigation/_function/restore-navigation-scroll.ts +59 -0
  13. package/src/{content → component/widget/navigation/_function}/to-doc-groups/_to-doc-branches.ts +2 -2
  14. package/src/{content → component/widget/navigation/_function}/to-doc-groups/_to-doc-group-branches.ts +3 -3
  15. package/src/{content → component/widget/navigation/_function}/to-doc-groups/to-doc-groups.ts +1 -1
  16. package/src/component/widget/navigation/_function/to-navigation-branch-id.ts +6 -0
  17. package/src/component/widget/navigation/_function/to-navigation-data/_to-navigation-document.ts +14 -0
  18. package/src/component/widget/navigation/_function/to-navigation-data/_to-navigation-group.ts +14 -0
  19. package/src/component/widget/navigation/_function/to-navigation-data/to-navigation-data.ts +25 -0
  20. package/src/component/widget/navigation/_function/to-navigation-json.ts +8 -0
  21. package/src/component/widget/{shell → navigation}/_function/to-toc-groups.ts +2 -2
  22. package/src/{type → component/widget/navigation/_type}/doc-group.ts +1 -1
  23. package/src/component/widget/navigation/_type/navigation-data.ts +83 -0
  24. package/src/component/widget/navigation/_type/navigation-focus-snapshot.ts +21 -0
  25. package/src/component/widget/navigation/_wg-navigation-branch-toggle.css +28 -0
  26. package/src/component/widget/navigation/_wg-navigation-branch-toggle.tsx +48 -0
  27. package/src/component/widget/{shell/_wg-shell-nav-list.css → navigation/_wg-navigation-nav-list.css} +24 -16
  28. package/src/component/widget/navigation/_wg-navigation-nav-list.tsx +106 -0
  29. package/src/component/widget/{shell/_wg-shell-nav.css → navigation/_wg-navigation-nav.css} +9 -3
  30. package/src/component/widget/navigation/_wg-navigation-nav.tsx +43 -0
  31. package/src/component/widget/navigation/_wg-navigation-scroll.tsx +117 -0
  32. package/src/component/widget/{shell/_wg-shell-toc.css → navigation/_wg-navigation-toc.css} +39 -24
  33. package/src/component/widget/navigation/_wg-navigation-toc.tsx +155 -0
  34. package/src/component/widget/navigation/wg-navigation.css +282 -0
  35. package/src/component/widget/navigation/wg-navigation.tsx +479 -0
  36. package/src/component/widget/prose/_function/rehype-sections/rehype-sections.ts +1 -1
  37. package/src/component/widget/shell/wg-shell.css +13 -269
  38. package/src/component/widget/shell/wg-shell.tsx +16 -97
  39. package/src/component/widget/shell-controls/wg-shell-controls.css +4 -0
  40. package/src/component/widget/shell-controls/wg-shell-controls.tsx +170 -0
  41. package/src/constant/asset.ts +0 -5
  42. package/src/entry/browser/browser.tsx +22 -0
  43. package/src/{app.tsx → entry/cli/_function/create-app.tsx} +6 -6
  44. package/src/{content → entry/cli/_function/create-processor}/create-processor.ts +3 -3
  45. package/src/{content → entry/cli/_function}/read-docs.ts +1 -1
  46. package/src/{content → entry/cli/_function}/read-home.ts +1 -1
  47. package/src/{content/read-docs.test.ts → entry/cli/cli.test.ts} +105 -30
  48. package/src/{cli.ts → entry/cli/cli.ts} +10 -11
  49. package/src/store/navigation/_constant/tree.ts +6 -0
  50. package/src/store/navigation/create-navigation-stores.test.ts +92 -0
  51. package/src/store/navigation/create-navigation-stores.ts +73 -0
  52. package/src/store/navigation/navigation-state.ts +46 -0
  53. package/src/store/navigation/navigation-storage.ts +14 -0
  54. package/src/store/navigation/to-navigation-scroll-state.ts +42 -0
  55. package/src/store/navigation/to-navigation-tree-state.ts +19 -0
  56. package/src/store/navigation/use-navigation-tree-store.ts +12 -0
  57. package/src/style/token.css +3 -2
  58. package/src/type/doc-content.ts +1 -1
  59. package/src/type/doc-file-data.ts +1 -1
  60. package/src/{component/widget/shell/_type → type}/doc-outline.ts +1 -1
  61. package/src/type/site-assets.ts +2 -2
  62. package/dist/client.js +0 -7
  63. package/src/client.ts +0 -335
  64. package/src/component/widget/shell/_constant/navigation.ts +0 -9
  65. package/src/component/widget/shell/_function/bind-mobile-navigation.ts +0 -147
  66. package/src/component/widget/shell/_function/bind-navigation-overflow.ts +0 -47
  67. package/src/component/widget/shell/_function/bind-navigation-scroll.ts +0 -84
  68. package/src/component/widget/shell/_function/restore-navigation-scroll.ts +0 -47
  69. package/src/component/widget/shell/_wg-shell-nav-list.tsx +0 -87
  70. package/src/component/widget/shell/_wg-shell-nav.tsx +0 -49
  71. package/src/component/widget/shell/_wg-shell-toc.tsx +0 -57
  72. /package/src/{constant → component/widget/navigation/_constant}/locale.ts +0 -0
  73. /package/src/component/widget/{shell → navigation}/_constant/reading-line.ts +0 -0
  74. /package/src/{type → component/widget/navigation/_type}/doc-branch.ts +0 -0
  75. /package/src/component/widget/{shell → navigation}/_type/toc-group.ts +0 -0
  76. /package/src/{constant → entry/cli/_constant}/cli.ts +0 -0
  77. /package/src/{dev.ts → entry/cli/_function/create-dev-app.ts} +0 -0
  78. /package/src/{content/rehype-assets.ts → entry/cli/_function/create-processor/_rehype-assets.ts} +0 -0
  79. /package/src/{content/rehype-heading-ids.ts → entry/cli/_function/create-processor/_rehype-heading-ids.ts} +0 -0
  80. /package/src/{content/remark-report.ts → entry/cli/_function/create-processor/_remark-report.ts} +0 -0
  81. /package/src/{component/widget/prose/_type → type}/doc-section.ts +0 -0
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/cli.ts
3
+ // src/entry/cli/cli.ts
4
4
  import { EventEmitter } from "node:events";
5
- import { existsSync as existsSync3, watch } from "node:fs";
5
+ import { existsSync as existsSync3, readFileSync as readFileSync3, watch } from "node:fs";
6
6
  import { copyFile, mkdir, rm, writeFile } from "node:fs/promises";
7
7
  import { basename as basename2, dirname as dirname4, extname as extname4, join as join4, relative as relative4, resolve as resolve3, sep as sep3 } from "node:path";
8
8
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -12,180 +12,31 @@ import { debounce } from "es-toolkit";
12
12
  import { Hono as Hono3 } from "hono";
13
13
  import { toSSG } from "hono/ssg";
14
14
 
15
- // src/app.tsx
16
- import { Hono } from "hono";
17
- import { ssgParams } from "hono/ssg";
18
- import { renderToStaticMarkup } from "react-dom/server";
19
-
20
- // src/component/widget/prose/wg-prose.tsx
21
- import clsx from "clsx";
22
- import { jsx, jsxs } from "react/jsx-runtime";
23
- var WgProse = (props) => {
24
- if (props.html !== void 0) {
25
- return /* @__PURE__ */ jsx("article", { className: clsx("wg_prose__root"), dangerouslySetInnerHTML: { __html: props.html } });
26
- }
27
- return /* @__PURE__ */ jsx("article", { className: clsx("wg_prose__root"), children: props.head !== void 0 && /* @__PURE__ */ jsxs("header", { className: clsx("wg_prose__head"), children: [
28
- /* @__PURE__ */ jsx("h1", { children: props.head.title }),
29
- props.head.lead !== void 0 && /* @__PURE__ */ jsx("p", { children: props.head.lead })
30
- ] }) });
31
- };
32
-
33
- // src/component/widget/shell/wg-shell.tsx
34
- import clsx5 from "clsx";
35
-
36
- // src/component/widget/shell/_constant/navigation.ts
37
- var navigation_mobile_query = "(width < 1024px)";
38
- var navigation_scroll_storage_key = "for-humanity:navigation-scroll";
39
-
40
- // src/component/widget/shell/_function/bind-navigation-overflow.ts
41
- var bindNavigationOverflow = () => {
42
- const ports = [...document.querySelectorAll("[data-navigation-scroll]")];
43
- const frame = { id: 0 };
44
- const edgeTolerance = 1;
45
- const updateOverflow = () => {
46
- frame.id = 0;
47
- for (const port of ports) {
48
- port.classList.add("wg_shell__scroll--ready");
49
- port.classList.toggle("wg_shell__scroll--above", port.scrollTop > edgeTolerance);
50
- port.classList.toggle("wg_shell__scroll--below", port.scrollHeight - port.clientHeight - port.scrollTop > edgeTolerance);
51
- }
52
- };
53
- const scheduleOverflow = () => {
54
- if (frame.id === 0) {
55
- frame.id = requestAnimationFrame(updateOverflow);
56
- }
57
- };
58
- const observer = new ResizeObserver(scheduleOverflow);
59
- for (const port of ports) {
60
- port.addEventListener("scroll", scheduleOverflow, { passive: true });
61
- observer.observe(port);
62
- if (port.firstElementChild) {
63
- observer.observe(port.firstElementChild);
64
- }
65
- }
66
- addEventListener("pageshow", scheduleOverflow);
67
- updateOverflow();
68
- };
69
-
70
- // src/component/widget/shell/_function/restore-navigation-scroll.ts
71
- var restoreNavigationScroll = (storageKey) => {
72
- const navigation = document.querySelector("[data-navigation]");
73
- const documents = document.querySelector('[data-navigation-scroll="documents"]');
74
- const outline = document.querySelector('[data-navigation-scroll="outline"]');
75
- if (!navigation || !documents || !outline) {
76
- return;
77
- }
78
- try {
79
- const value = sessionStorage.getItem(storageKey);
80
- if (value === null) {
81
- return;
82
- }
83
- const stored = JSON.parse(value);
84
- const signature = [...navigation.querySelectorAll(".wg_shellNav__root a[href]")].map((link) => `${link.pathname}:${link.textContent}`).join("\n");
85
- if (typeof stored !== "object" || stored === null || !("navigation" in stored) || stored.navigation !== signature) {
86
- return;
87
- }
88
- if ("documents" in stored && typeof stored.documents === "number" && Number.isFinite(stored.documents) && stored.documents >= 0) {
89
- documents.scrollTop = stored.documents;
90
- }
91
- if ("pathname" in stored && stored.pathname === location.pathname && "outline" in stored && typeof stored.outline === "number" && Number.isFinite(stored.outline) && stored.outline >= 0) {
92
- outline.scrollTop = stored.outline;
93
- }
94
- } catch {
95
- }
96
- };
97
-
98
- // src/component/widget/shell/_function/to-toc-groups.ts
99
- import { takeWhile } from "es-toolkit/array";
100
-
101
- // src/component/widget/prose/_constant/section.ts
102
- var section_heading_depth = {
103
- section: 2,
104
- sub: 3
105
- };
106
-
107
- // src/component/widget/shell/_function/to-toc-groups.ts
108
- var toTocGroups = (outline) => {
109
- const entries = outline.headings.filter((heading) => heading.depth === section_heading_depth.section || heading.depth === section_heading_depth.sub).map((heading, index) => ({ heading, section: outline.sections[index] }));
110
- const sections = entries.flatMap(
111
- (entry, index) => entry.heading.depth === section_heading_depth.section ? [
112
- {
113
- heading: entry.heading,
114
- part: entry.section.part,
115
- subs: takeWhile(entries.slice(index + 1), (sub) => sub.heading.depth !== section_heading_depth.section).map((sub) => ({
116
- heading: sub.heading
117
- }))
118
- }
119
- ] : []
120
- );
121
- return sections.flatMap(
122
- (section, index) => index === 0 || section.part !== void 0 ? [
123
- {
124
- part: section.part,
125
- sections: [section, ...takeWhile(sections.slice(index + 1), (next) => next.part === void 0)]
126
- }
127
- ] : []
128
- );
129
- };
130
-
131
- // src/component/widget/shell/_wg-shell-nav.tsx
132
- import clsx3 from "clsx";
133
-
134
- // src/component/widget/shell/_wg-shell-nav-list.tsx
135
- import clsx2 from "clsx";
136
- import { Fragment } from "react";
137
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
138
- var WgShellNavList = (props) => {
139
- const currentAncestors = new Set(props.currentDoc?.ancestors);
140
- const entries = [
141
- ...props.group.docs.map((doc) => {
142
- const active = doc.id === props.currentDoc?.id;
143
- return { kind: "document", key: `doc:${doc.id}`, doc, active, current: active || currentAncestors.has(doc.id) };
144
- }),
145
- ...props.group.groups.map((group) => ({
146
- kind: "group",
147
- key: `group:${JSON.stringify(group.path)}`,
148
- group,
149
- current: group.path.every((name, index) => name === props.currentDoc?.data.group[index])
150
- }))
151
- ];
152
- const currentIndex = entries.findIndex((entry) => entry.current);
153
- return /* @__PURE__ */ jsx2("ul", { className: clsx2("wg_shellNavList__root"), "aria-label": props.group.name, children: entries.map((entry, index) => /* @__PURE__ */ jsxs2(
154
- "li",
155
- {
156
- className: clsx2("wg_shellNavList__item", {
157
- "wg_shellNavList__item--trail": index < currentIndex,
158
- "wg_shellNavList__item--current": entry.current
159
- }),
160
- children: [
161
- entry.kind === "document" && /* @__PURE__ */ jsxs2(Fragment, { children: [
162
- /* @__PURE__ */ jsx2(
163
- "a",
164
- {
165
- className: clsx2("wg_shellNavList__link", { "wg_shellNavList__link--active": entry.active }),
166
- href: `/${entry.doc.id}/`,
167
- title: entry.doc.data.label,
168
- "aria-current": entry.active ? "page" : void 0,
169
- children: entry.doc.data.name
170
- }
171
- ),
172
- entry.doc.children !== void 0 && /* @__PURE__ */ jsx2(
173
- WgShellNavList,
174
- {
175
- group: { name: entry.doc.data.name, path: entry.doc.data.group, docs: entry.doc.children, groups: [] },
176
- currentDoc: props.currentDoc
177
- }
178
- )
179
- ] }),
180
- entry.kind === "group" && /* @__PURE__ */ jsxs2(Fragment, { children: [
181
- /* @__PURE__ */ jsx2("span", { className: clsx2("wg_shellNavList__branch"), children: entry.group.name }),
182
- /* @__PURE__ */ jsx2(WgShellNavList, { group: entry.group, currentDoc: props.currentDoc })
183
- ] })
184
- ]
185
- },
186
- entry.key
187
- )) });
15
+ // src/constant/asset.ts
16
+ var asset_dir = "_fh";
17
+ var asset_style_path = `/${asset_dir}/style.css`;
18
+ var asset_font_dir = `/${asset_dir}/fonts`;
19
+ var asset_reload_path = `/${asset_dir}/reload`;
20
+ var asset_favicon_path = "/favicon.svg";
21
+ var asset_content_types = {
22
+ ".css": "text/css; charset=utf-8",
23
+ ".js": "text/javascript; charset=utf-8",
24
+ ".woff2": "font/woff2",
25
+ ".svg": "image/svg+xml",
26
+ ".png": "image/png",
27
+ ".jpg": "image/jpeg",
28
+ ".jpeg": "image/jpeg",
29
+ ".gif": "image/gif",
30
+ ".webp": "image/webp",
31
+ ".avif": "image/avif",
32
+ ".pdf": "application/pdf",
33
+ ".csv": "text/csv; charset=utf-8",
34
+ ".txt": "text/plain; charset=utf-8",
35
+ ".zip": "application/zip"
188
36
  };
37
+ var asset_content_type_default = "application/octet-stream";
38
+ var asset_media_dir = `/${asset_dir}/media`;
39
+ var asset_media_extensions = /* @__PURE__ */ new Set([".svg", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif", ".pdf", ".csv", ".txt", ".zip"]);
189
40
 
190
41
  // src/constant/copy.ts
191
42
  var copy_site_title_default = "for humanity";
@@ -214,11 +65,52 @@ var copy_warn_broken_link = "\uC5C6\uB294 \uBB38\uC11C\uB85C \uAC74 \uB9C1\uD06C
214
65
  var copy_warn_unknown_status = "\uC124\uC815\uC5D0 \uC5C6\uB294 \uC0C1\uD0DC \uBB38\uAD6C\uB2E4";
215
66
  var copy_error_asset = "\uC790\uC6D0\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uB2E4";
216
67
 
217
- // src/constant/locale.ts
68
+ // src/constant/font.ts
69
+ var font_sans_css = "src/asset/font/pretendard/pretendardvariable-dynamic-subset.css";
70
+ var font_mono_css = "src/asset/font/jetbrains-mono/jetbrains-mono.css";
71
+ var font_brand_css = "src/asset/font/architects-daughter/architects-daughter.css";
72
+ var font_sans_preload_file = "PretendardVariable.subset.91.woff2";
73
+ var font_display_strategy = "optional";
74
+ var font_hash_length = 12;
75
+ var font_cache_control = "public, max-age=31536000, immutable";
76
+
77
+ // src/constant/site.ts
78
+ var site_config_absent = {};
79
+ var site_navigation_default = [];
80
+
81
+ // src/entry/cli/_constant/cli.ts
82
+ var cli_default_command = "dev";
83
+ var cli_default_docs_dir = ".";
84
+ var cli_config_file_name = "for-humanity.config.mjs";
85
+ var cli_dev_port = 4321;
86
+ var cli_reload_delay_ms = 150;
87
+
88
+ // src/entry/cli/_function/create-app.tsx
89
+ import { Hono } from "hono";
90
+ import { html } from "hono/html";
91
+ import { ssgParams } from "hono/ssg";
92
+
93
+ // src/component/widget/prose/wg-prose.tsx
94
+ import clsx from "clsx";
95
+ import { jsx, jsxs } from "hono/jsx/jsx-runtime";
96
+ var WgProse = (props) => {
97
+ if (props.html !== void 0) {
98
+ return /* @__PURE__ */ jsx("article", { className: clsx("wg_prose__root"), dangerouslySetInnerHTML: { __html: props.html } });
99
+ }
100
+ return /* @__PURE__ */ jsx("article", { className: clsx("wg_prose__root"), children: props.head !== void 0 && /* @__PURE__ */ jsxs("header", { className: clsx("wg_prose__head"), children: [
101
+ /* @__PURE__ */ jsx("h1", { children: props.head.title }),
102
+ props.head.lead !== void 0 && /* @__PURE__ */ jsx("p", { children: props.head.lead })
103
+ ] }) });
104
+ };
105
+
106
+ // src/component/widget/shell/wg-shell.tsx
107
+ import clsx10 from "clsx";
108
+
109
+ // src/component/widget/navigation/_constant/locale.ts
218
110
  var locale_doc_name = "en";
219
111
  var locale_doc_group = "ko";
220
112
 
221
- // src/content/to-doc-groups/_to-doc-branches.ts
113
+ // src/component/widget/navigation/_function/to-doc-groups/_to-doc-branches.ts
222
114
  var toDocBranches = (options) => {
223
115
  return options.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)).map((doc) => {
224
116
  const children = options.children.get(doc.id);
@@ -226,7 +118,7 @@ var toDocBranches = (options) => {
226
118
  });
227
119
  };
228
120
 
229
- // src/content/to-doc-groups/_to-doc-group-branches.ts
121
+ // src/component/widget/navigation/_function/to-doc-groups/_to-doc-group-branches.ts
230
122
  var toDocGroupBranches = (options) => {
231
123
  return [
232
124
  ...Map.groupBy(
@@ -260,176 +152,1086 @@ var toDocGroupBranches = (options) => {
260
152
  });
261
153
  };
262
154
 
263
- // src/content/to-doc-groups/to-doc-groups.ts
155
+ // src/component/widget/navigation/_function/to-doc-groups/to-doc-groups.ts
264
156
  var toDocGroups = (options) => {
265
157
  const positions = new Map(options.navigation.flatMap((path, index) => path.map((_name, depth) => [JSON.stringify(path.slice(0, depth + 1)), index])).toReversed());
266
158
  const children = Map.groupBy(options.docs, (doc) => doc.data.parent);
267
159
  return toDocGroupBranches({ docs: options.docs, children, path: [], positions, unlistedPosition: options.navigation.length });
268
160
  };
269
161
 
270
- // src/component/widget/shell/_wg-shell-nav.tsx
271
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
272
- var WgShellNav = (props) => {
273
- const docGroups = toDocGroups({ docs: props.docs, navigation: props.site.navigation });
274
- const currentDoc = props.docs.find((doc) => doc.id === props.current);
275
- 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: [
276
- /* @__PURE__ */ jsx3("div", { className: clsx3("wg_shellNav__group"), children: group.name }),
277
- /* @__PURE__ */ jsx3(WgShellNavList, { group, currentDoc })
278
- ] }, group.name)) }) });
162
+ // src/component/widget/navigation/_function/to-navigation-data/_to-navigation-document.ts
163
+ var toNavigationDocument = (options) => ({
164
+ id: options.doc.id,
165
+ name: options.doc.data.name,
166
+ label: options.doc.data.label,
167
+ active: options.doc.id === options.current,
168
+ current: options.doc.id === options.current || options.ancestors.has(options.doc.id),
169
+ children: options.doc.children === void 0 ? [] : options.doc.children.map((doc) => toNavigationDocument({ ...options, doc }))
170
+ });
171
+
172
+ // src/component/widget/navigation/_function/to-navigation-data/_to-navigation-group.ts
173
+ var toNavigationGroup = (options) => ({
174
+ name: options.group.name,
175
+ path: options.group.path,
176
+ current: options.group.path.every((name, index) => name === options.currentGroup?.[index]),
177
+ docs: options.group.docs.map((doc) => toNavigationDocument({ doc, current: options.current, ancestors: options.ancestors })),
178
+ groups: options.group.groups.map((group) => toNavigationGroup({ ...options, group }))
179
+ });
180
+
181
+ // src/component/widget/navigation/_function/to-toc-groups.ts
182
+ import { takeWhile } from "es-toolkit/array";
183
+
184
+ // src/component/widget/prose/_constant/section.ts
185
+ var section_heading_depth = {
186
+ section: 2,
187
+ sub: 3
188
+ };
189
+
190
+ // src/component/widget/navigation/_function/to-toc-groups.ts
191
+ var toTocGroups = (outline) => {
192
+ const entries = outline.headings.filter((heading) => heading.depth === section_heading_depth.section || heading.depth === section_heading_depth.sub).map((heading, index) => ({ heading, section: outline.sections[index] }));
193
+ const sections = entries.flatMap(
194
+ (entry, index) => entry.heading.depth === section_heading_depth.section ? [
195
+ {
196
+ heading: entry.heading,
197
+ part: entry.section.part,
198
+ subs: takeWhile(entries.slice(index + 1), (sub) => sub.heading.depth !== section_heading_depth.section).map((sub) => ({
199
+ heading: sub.heading
200
+ }))
201
+ }
202
+ ] : []
203
+ );
204
+ return sections.flatMap(
205
+ (section, index) => index === 0 || section.part !== void 0 ? [
206
+ {
207
+ part: section.part,
208
+ sections: [section, ...takeWhile(sections.slice(index + 1), (next) => next.part === void 0)]
209
+ }
210
+ ] : []
211
+ );
212
+ };
213
+
214
+ // src/component/widget/navigation/_function/to-navigation-data/to-navigation-data.ts
215
+ var toNavigationData = (options) => {
216
+ const currentDoc = options.docs.find((doc) => doc.id === options.current);
217
+ const ancestors = new Set(currentDoc?.ancestors);
218
+ return {
219
+ title: options.site.title,
220
+ groups: toDocGroups({ docs: options.docs, navigation: options.site.navigation }).map(
221
+ (group) => toNavigationGroup({ group, current: options.current, ancestors, currentGroup: currentDoc?.data.group })
222
+ ),
223
+ outline: options.outline === void 0 ? [] : toTocGroups(options.outline),
224
+ pageId: options.current === void 0 ? "/" : options.current,
225
+ home: options.current === void 0
226
+ };
227
+ };
228
+
229
+ // src/component/widget/navigation/_function/to-navigation-json.ts
230
+ var toNavigationJson = (data) => {
231
+ return JSON.stringify(data).replaceAll("<", "\\u003c").replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
232
+ };
233
+
234
+ // src/component/widget/shell-controls/wg-shell-controls.tsx
235
+ import clsx9 from "clsx";
236
+ import { Fragment as Fragment3, useEffect, useLayoutEffect as useLayoutEffect5, useRef as useRef4 } from "hono/jsx";
237
+
238
+ // src/component/widget/cursor-face/wg-cursor-face.tsx
239
+ import clsx2 from "clsx";
240
+ import { useLayoutEffect, useRef } from "hono/jsx";
241
+
242
+ // src/component/widget/cursor-face/_constant/cursor-face.ts
243
+ var cursor_face_offset_px = 16;
244
+ var cursor_face_size_px = 24;
245
+ var cursor_face_storage_key = "fh-cursor-face";
246
+
247
+ // src/component/widget/cursor-face/wg-cursor-face.tsx
248
+ import { jsx as jsx2 } from "hono/jsx/jsx-runtime";
249
+ var WgCursorFace = (props) => {
250
+ const faceRef = useRef(null);
251
+ const mediaRef = useRef(null);
252
+ const positionRef = useRef({ x: 0, y: 0, frame: 0 });
253
+ const handleCursorHide = () => {
254
+ cancelAnimationFrame(positionRef.current.frame);
255
+ positionRef.current.frame = 0;
256
+ faceRef.current?.classList.remove("wg_cursorFace__root--visible");
257
+ };
258
+ const handleCursorMediaChange = () => {
259
+ if (!mediaRef.current?.matches) handleCursorHide();
260
+ };
261
+ const renderCursorFace = () => {
262
+ if (positionRef.current.frame !== 0) return;
263
+ positionRef.current.frame = requestAnimationFrame(() => {
264
+ positionRef.current.frame = 0;
265
+ const face = faceRef.current;
266
+ if (face === null) return;
267
+ face.style.transform = `translate3d(
268
+ ${Math.max(0, Math.min(positionRef.current.x + cursor_face_offset_px, document.documentElement.clientWidth - face.width - cursor_face_offset_px))}px,
269
+ ${Math.max(0, Math.min(positionRef.current.y + cursor_face_offset_px, document.documentElement.clientHeight - face.height - cursor_face_offset_px))}px,
270
+ 0
271
+ )`;
272
+ face.classList.add("wg_cursorFace__root--visible");
273
+ });
274
+ };
275
+ const handleDocumentPointer = (event) => {
276
+ if (!mediaRef.current?.matches || event.pointerType !== "mouse") {
277
+ handleCursorHide();
278
+ return;
279
+ }
280
+ positionRef.current.x = event.clientX;
281
+ positionRef.current.y = event.clientY;
282
+ renderCursorFace();
283
+ };
284
+ const handleCursorResize = () => {
285
+ if (faceRef.current?.classList.contains("wg_cursorFace__root--visible")) renderCursorFace();
286
+ };
287
+ const handlePageHide = () => {
288
+ if (!faceRef.current?.classList.contains("wg_cursorFace__root--visible")) return;
289
+ try {
290
+ sessionStorage.setItem(cursor_face_storage_key, JSON.stringify({ x: positionRef.current.x, y: positionRef.current.y }));
291
+ } catch {
292
+ }
293
+ };
294
+ const handlePageShow = () => {
295
+ try {
296
+ const stored = sessionStorage.getItem(cursor_face_storage_key);
297
+ sessionStorage.removeItem(cursor_face_storage_key);
298
+ if (stored === null || !mediaRef.current?.matches) return;
299
+ const point = JSON.parse(stored);
300
+ if (typeof point === "object" && point !== null && "x" in point && "y" in point && typeof point.x === "number" && typeof point.y === "number" && Number.isFinite(point.x) && Number.isFinite(point.y)) {
301
+ positionRef.current.x = point.x;
302
+ positionRef.current.y = point.y;
303
+ renderCursorFace();
304
+ }
305
+ } catch {
306
+ }
307
+ };
308
+ useLayoutEffect(() => {
309
+ if (!props.ready) return;
310
+ const lifetime = new AbortController();
311
+ const media = matchMedia("(hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference)");
312
+ mediaRef.current = media;
313
+ handlePageShow();
314
+ document.addEventListener("pointermove", handleDocumentPointer, { passive: true, signal: lifetime.signal });
315
+ document.addEventListener("pointerdown", handleDocumentPointer, { passive: true, signal: lifetime.signal });
316
+ document.documentElement.addEventListener("pointerleave", handleCursorHide, { signal: lifetime.signal });
317
+ addEventListener("blur", handleCursorHide, { signal: lifetime.signal });
318
+ addEventListener("resize", handleCursorResize, { signal: lifetime.signal });
319
+ addEventListener("pagehide", handlePageHide, { signal: lifetime.signal });
320
+ addEventListener("pageshow", handlePageShow, { signal: lifetime.signal });
321
+ media.addEventListener("change", handleCursorMediaChange, { signal: lifetime.signal });
322
+ return () => {
323
+ lifetime.abort();
324
+ mediaRef.current = null;
325
+ handleCursorHide();
326
+ };
327
+ }, [props.ready]);
328
+ return /* @__PURE__ */ jsx2(
329
+ "img",
330
+ {
331
+ ref: props.ready ? faceRef : void 0,
332
+ className: clsx2("wg_cursorFace__root"),
333
+ src: asset_favicon_path,
334
+ width: cursor_face_size_px,
335
+ height: cursor_face_size_px,
336
+ alt: "",
337
+ "aria-hidden": "true",
338
+ draggable: false,
339
+ "data-cursor-face": ""
340
+ }
341
+ );
342
+ };
343
+
344
+ // src/component/widget/navigation/wg-navigation.tsx
345
+ import clsx8 from "clsx";
346
+ import { Fragment as Fragment2, useLayoutEffect as useLayoutEffect4, useRef as useRef3, useState as useState3 } from "hono/jsx";
347
+ import { flushSync } from "hono/jsx/dom";
348
+
349
+ // src/component/widget/navigation/_constant/navigation.ts
350
+ var navigation_mobile_query = "(width < 1024px)";
351
+ var navigation_wide_query = "(width >= 1536px)";
352
+
353
+ // src/component/widget/navigation/_function/restore-navigation-scroll.ts
354
+ var restoreNavigationScroll = (options) => {
355
+ const navigation = document.querySelector("[data-navigation]");
356
+ const documents = document.querySelector('[data-navigation-scroll="documents"]');
357
+ const outline = document.querySelector('[data-navigation-scroll="outline"]');
358
+ if (!navigation || !documents || !outline) return;
359
+ navigation.dataset.navigationLayout = options.layout;
360
+ try {
361
+ const value = sessionStorage.getItem(options.key);
362
+ const stored = value === null ? void 0 : JSON.parse(value);
363
+ const state = typeof stored === "object" && stored !== null && "version" in stored && stored.version === options.version && "state" in stored ? options.toState(stored.state) : { positions: {} };
364
+ const legacy = sessionStorage.getItem(`${options.key}:${options.layout === "drawer" ? "drawer" : "desktop"}`);
365
+ const previous = state.positions[options.layout] === void 0 && legacy !== null ? options.toState({ positions: { [options.layout]: JSON.parse(legacy) } }).positions[options.layout] : void 0;
366
+ const position = state.positions[options.layout] ?? previous;
367
+ const signature = [...navigation.querySelectorAll(".wg_navigationNav__root a[href]")].map((link) => `${link.pathname}:${link.textContent}`).join("\n");
368
+ if (position === void 0 || position.navigation !== signature) return;
369
+ documents.scrollTop = position.documents;
370
+ if (position.pathname === location.pathname) outline.scrollTop = position.outline;
371
+ } catch {
372
+ }
373
+ };
374
+
375
+ // src/component/widget/navigation/_wg-navigation-nav.tsx
376
+ import clsx5 from "clsx";
377
+
378
+ // src/component/widget/navigation/_wg-navigation-branch-toggle.tsx
379
+ import clsx3 from "clsx";
380
+
381
+ // src/component/widget/navigation/_function/to-navigation-branch-id.ts
382
+ var toNavigationBranchId = (key) => {
383
+ return `fh-branch-${encodeURIComponent(key)}`;
384
+ };
385
+
386
+ // src/component/widget/navigation/_wg-navigation-branch-toggle.tsx
387
+ import { jsx as jsx3 } from "hono/jsx/jsx-runtime";
388
+ var WgNavigationBranchToggle = (props) => {
389
+ const handleToggleClick = () => props.onToggle?.(props.branchKey);
390
+ return /* @__PURE__ */ jsx3(
391
+ "button",
392
+ {
393
+ className: clsx3("wg_navigationBranchToggle__root"),
394
+ type: "button",
395
+ "aria-label": `${props.collapsed[props.branchKey] === true ? "Expand" : "Collapse"} ${props.label}`,
396
+ "aria-expanded": String(props.collapsed[props.branchKey] !== true),
397
+ "aria-controls": toNavigationBranchId(props.branchKey),
398
+ "data-navigation-toggle": props.branchKey,
399
+ "data-navigation-label": props.label,
400
+ hidden: props.onToggle === void 0,
401
+ onClick: handleToggleClick,
402
+ children: props.collapsed[props.branchKey] === true ? "+" : "\u2212"
403
+ }
404
+ );
279
405
  };
280
406
 
281
- // src/component/widget/shell/_wg-shell-toc.tsx
407
+ // src/component/widget/navigation/_wg-navigation-nav-list.tsx
282
408
  import clsx4 from "clsx";
283
- import { Fragment as Fragment2 } from "react";
284
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
285
- var WgShellToc = (props) => {
286
- return /* @__PURE__ */ jsxs4("nav", { className: clsx4("wg_shellToc__root"), "aria-label": copy_nav_toc_label, children: [
287
- /* @__PURE__ */ jsx4("div", { className: clsx4("wg_shellToc__label"), children: copy_nav_toc_label }),
288
- props.groups.map((group) => /* @__PURE__ */ jsxs4(Fragment2, { children: [
289
- group.part !== void 0 && /* @__PURE__ */ jsx4("div", { className: clsx4("wg_shellToc__group"), children: group.part }),
290
- /* @__PURE__ */ jsx4("ul", { className: clsx4("wg_shellToc__list"), children: group.sections.map((section) => /* @__PURE__ */ jsxs4("li", { className: clsx4("wg_shellToc__item"), children: [
291
- /* @__PURE__ */ jsx4("a", { className: clsx4("wg_shellToc__link"), href: `#${section.heading.slug}`, "data-toc-link": "", children: section.heading.text }),
292
- 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)) })
293
- ] }, section.heading.slug)) })
294
- ] }, group.sections[0].heading.slug))
409
+ import { Fragment } from "hono/jsx";
410
+ import { jsx as jsx4, jsxs as jsxs2 } from "hono/jsx/jsx-runtime";
411
+ var WgNavigationNavList = (props) => {
412
+ const entries = [
413
+ ...props.group.docs.map((doc) => {
414
+ const active = doc.active;
415
+ return { kind: "document", key: `doc:${doc.id}`, doc, active, current: doc.current };
416
+ }),
417
+ ...props.group.groups.map((group) => ({
418
+ kind: "group",
419
+ key: `group:${JSON.stringify(group.path)}`,
420
+ group,
421
+ current: group.current
422
+ }))
423
+ ];
424
+ const currentIndex = entries.findIndex((entry) => entry.current);
425
+ return /* @__PURE__ */ jsx4(
426
+ "ul",
427
+ {
428
+ className: clsx4("wg_navigationNavList__root"),
429
+ "aria-label": props.group.name,
430
+ id: toNavigationBranchId(props.branchKey),
431
+ hidden: props.collapsed[props.branchKey] === true,
432
+ children: entries.map((entry, index) => /* @__PURE__ */ jsxs2(
433
+ "li",
434
+ {
435
+ className: clsx4("wg_navigationNavList__item", {
436
+ "wg_navigationNavList__item--trail": index < currentIndex,
437
+ "wg_navigationNavList__item--current": entry.current
438
+ }),
439
+ children: [
440
+ entry.kind === "document" && /* @__PURE__ */ jsxs2(Fragment, { children: [
441
+ /* @__PURE__ */ jsx4(
442
+ "a",
443
+ {
444
+ className: clsx4("wg_navigationNavList__link", { "wg_navigationNavList__link--active": entry.active }),
445
+ href: `/${entry.doc.id}/`,
446
+ title: entry.doc.label,
447
+ "aria-current": entry.active ? "page" : void 0,
448
+ children: entry.doc.name
449
+ }
450
+ ),
451
+ entry.doc.children.length > 0 && /* @__PURE__ */ jsx4(WgNavigationBranchToggle, { branchKey: entry.key, label: entry.doc.name, collapsed: props.collapsed, onToggle: props.onToggle }),
452
+ entry.doc.children.length > 0 && /* @__PURE__ */ jsx4(
453
+ WgNavigationNavList,
454
+ {
455
+ group: { name: entry.doc.name, path: props.group.path, current: entry.current, docs: entry.doc.children, groups: [] },
456
+ collapsed: props.collapsed,
457
+ onToggle: props.onToggle,
458
+ branchKey: entry.key
459
+ }
460
+ )
461
+ ] }),
462
+ entry.kind === "group" && /* @__PURE__ */ jsxs2(Fragment, { children: [
463
+ /* @__PURE__ */ jsx4("span", { className: clsx4("wg_navigationNavList__branch"), children: entry.group.name }),
464
+ /* @__PURE__ */ jsx4(WgNavigationBranchToggle, { branchKey: entry.key, label: entry.group.name, collapsed: props.collapsed, onToggle: props.onToggle }),
465
+ /* @__PURE__ */ jsx4(WgNavigationNavList, { group: entry.group, collapsed: props.collapsed, onToggle: props.onToggle, branchKey: entry.key })
466
+ ] })
467
+ ]
468
+ },
469
+ entry.key
470
+ ))
471
+ }
472
+ );
473
+ };
474
+
475
+ // src/component/widget/navigation/_wg-navigation-nav.tsx
476
+ import { jsx as jsx5, jsxs as jsxs3 } from "hono/jsx/jsx-runtime";
477
+ var WgNavigationNav = (props) => {
478
+ return /* @__PURE__ */ jsx5("nav", { className: clsx5("wg_navigationNav__root"), "aria-label": copy_nav_aria_label, children: /* @__PURE__ */ jsx5("div", { className: clsx5("wg_navigationNav__docs"), children: props.groups.map((group) => /* @__PURE__ */ jsxs3("section", { className: clsx5("wg_navigationNav__docGroup"), "aria-label": group.name, children: [
479
+ /* @__PURE__ */ jsx5("div", { className: clsx5("wg_navigationNav__group"), children: group.name }),
480
+ /* @__PURE__ */ jsx5(WgNavigationBranchToggle, { branchKey: `group:${JSON.stringify(group.path)}`, label: group.name, collapsed: props.collapsed, onToggle: props.onToggle }),
481
+ /* @__PURE__ */ jsx5(WgNavigationNavList, { group, branchKey: `group:${JSON.stringify(group.path)}`, collapsed: props.collapsed, onToggle: props.onToggle })
482
+ ] }, JSON.stringify(group.path))) }) });
483
+ };
484
+
485
+ // src/component/widget/navigation/_wg-navigation-scroll.tsx
486
+ import clsx6 from "clsx";
487
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef2, useState } from "hono/jsx";
488
+ import { jsx as jsx6 } from "hono/jsx/jsx-runtime";
489
+ var WgNavigationScroll = (props) => {
490
+ const [overflow, setOverflow] = useState({ above: false, below: false });
491
+ const frameRef = useRef2(0);
492
+ const updateOverflow = () => {
493
+ frameRef.current = 0;
494
+ if (props.portRef.current === null) return;
495
+ const above = props.portRef.current.scrollTop > 1;
496
+ const below = props.portRef.current.scrollHeight - props.portRef.current.clientHeight - props.portRef.current.scrollTop > 1;
497
+ setOverflow((state) => state.above === above && state.below === below ? state : { above, below });
498
+ };
499
+ const scheduleOverflow = () => {
500
+ if (frameRef.current === 0) frameRef.current = requestAnimationFrame(updateOverflow);
501
+ };
502
+ const handlePortScroll = (_event) => {
503
+ props.onScroll?.();
504
+ scheduleOverflow();
505
+ };
506
+ useLayoutEffect2(() => {
507
+ if (!props.ready || props.portRef.current === null) return;
508
+ const observer = new ResizeObserver(scheduleOverflow);
509
+ observer.observe(props.portRef.current);
510
+ if (props.portRef.current.firstElementChild !== null) observer.observe(props.portRef.current.firstElementChild);
511
+ let active = true;
512
+ addEventListener("pageshow", scheduleOverflow);
513
+ if (props.portRef.current.isConnected) {
514
+ updateOverflow();
515
+ } else {
516
+ queueMicrotask(() => {
517
+ if (active) updateOverflow();
518
+ });
519
+ }
520
+ return () => {
521
+ active = false;
522
+ observer.disconnect();
523
+ removeEventListener("pageshow", scheduleOverflow);
524
+ cancelAnimationFrame(frameRef.current);
525
+ frameRef.current = 0;
526
+ };
527
+ }, [props.portRef, props.ready]);
528
+ useLayoutEffect2(() => {
529
+ if (props.ready && props.portRef.current?.isConnected) updateOverflow();
530
+ }, [props.children, props.ready]);
531
+ return (
532
+ // biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: 부모의 링크 이벤트 위임이며 자식 a·button의 네이티브 키보드 동작을 그대로 소비한다
533
+ /* @__PURE__ */ jsx6(
534
+ "div",
535
+ {
536
+ ref: props.ready ? props.portRef : void 0,
537
+ className: clsx6(
538
+ "wg_navigation__scroll",
539
+ props.kind === "documents" && "wg_navigation__documents",
540
+ props.kind === "outline" && "wg_navigation__headings",
541
+ props.ready && "wg_navigation__scroll--ready",
542
+ props.ready && overflow.above && "wg_navigation__scroll--above",
543
+ props.ready && overflow.below && "wg_navigation__scroll--below"
544
+ ),
545
+ "data-navigation-scroll": props.kind,
546
+ onClick: props.onClick,
547
+ onScroll: handlePortScroll,
548
+ children: props.children
549
+ }
550
+ )
551
+ );
552
+ };
553
+
554
+ // src/component/widget/navigation/_wg-navigation-toc.tsx
555
+ import clsx7 from "clsx";
556
+ import { useLayoutEffect as useLayoutEffect3, useState as useState2 } from "hono/jsx";
557
+
558
+ // src/component/widget/navigation/_constant/reading-line.ts
559
+ var reading_line_slack_px = 1;
560
+
561
+ // src/util/dom/find-hash-target.ts
562
+ var findHashTarget = (hash) => {
563
+ if (!hash) {
564
+ return null;
565
+ }
566
+ try {
567
+ return document.getElementById(decodeURIComponent(hash.slice(1)));
568
+ } catch (error) {
569
+ if (error instanceof URIError) {
570
+ return null;
571
+ }
572
+ throw error;
573
+ }
574
+ };
575
+
576
+ // src/component/widget/navigation/_wg-navigation-toc.tsx
577
+ import { jsx as jsx7, jsxs as jsxs4 } from "hono/jsx/jsx-runtime";
578
+ var WgNavigationToc = (props) => {
579
+ const [currentSlug, setCurrentSlug] = useState2();
580
+ useLayoutEffect3(() => {
581
+ const sections = props.groups.flatMap((group) => group.sections);
582
+ const headings = sections.flatMap((section) => [section.heading, ...section.subs.map((sub) => sub.heading)]);
583
+ const frame = { id: 0 };
584
+ const updateCurrentHeading = () => {
585
+ frame.id = 0;
586
+ const visible = headings.flatMap((heading) => {
587
+ const target = findHashTarget(`#${heading.slug}`);
588
+ return target?.checkVisibility() ? [{ slug: heading.slug, top: target.getBoundingClientRect().top - Number.parseFloat(getComputedStyle(target).scrollMarginTop) }] : [];
589
+ });
590
+ setCurrentSlug(visible.findLast((heading) => heading.top <= reading_line_slack_px)?.slug ?? visible[0]?.slug);
591
+ };
592
+ const handleReadingPositionChange = () => {
593
+ if (frame.id === 0) frame.id = requestAnimationFrame(updateCurrentHeading);
594
+ };
595
+ addEventListener("scroll", handleReadingPositionChange, { passive: true });
596
+ addEventListener("resize", handleReadingPositionChange);
597
+ addEventListener("toggle", handleReadingPositionChange, true);
598
+ addEventListener("pageshow", handleReadingPositionChange);
599
+ document.addEventListener("DOMContentLoaded", handleReadingPositionChange);
600
+ if (document.readyState !== "loading") updateCurrentHeading();
601
+ return () => {
602
+ cancelAnimationFrame(frame.id);
603
+ removeEventListener("scroll", handleReadingPositionChange);
604
+ removeEventListener("resize", handleReadingPositionChange);
605
+ removeEventListener("toggle", handleReadingPositionChange, true);
606
+ removeEventListener("pageshow", handleReadingPositionChange);
607
+ document.removeEventListener("DOMContentLoaded", handleReadingPositionChange);
608
+ };
609
+ }, [props.groups]);
610
+ return /* @__PURE__ */ jsxs4("nav", { className: clsx7("wg_navigationToc__root"), "aria-label": copy_nav_toc_label, children: [
611
+ /* @__PURE__ */ jsx7("div", { className: clsx7("wg_navigationToc__label"), children: copy_nav_toc_label }),
612
+ props.groups.map((group) => {
613
+ const currentIndex = group.sections.findIndex((section) => section.heading.slug === currentSlug || section.subs.some((sub) => sub.heading.slug === currentSlug));
614
+ const partKey = `outline:${props.pageId}:part:${group.sections[0].heading.slug}`;
615
+ return /* @__PURE__ */ jsxs4(
616
+ "div",
617
+ {
618
+ className: clsx7("wg_navigationToc__groupSection", { "wg_navigationToc__groupSection--named": group.part !== void 0 }),
619
+ children: [
620
+ group.part !== void 0 && /* @__PURE__ */ jsx7("div", { className: clsx7("wg_navigationToc__group"), children: group.part }),
621
+ group.part !== void 0 && /* @__PURE__ */ jsx7(WgNavigationBranchToggle, { branchKey: partKey, label: group.part, collapsed: props.collapsed, onToggle: props.onToggle }),
622
+ /* @__PURE__ */ jsx7("ul", { className: clsx7("wg_navigationToc__list"), id: toNavigationBranchId(partKey), hidden: props.collapsed[partKey] === true, children: group.sections.map((section, index) => {
623
+ const currentSubIndex = section.subs.findIndex((sub) => sub.heading.slug === currentSlug);
624
+ const sectionKey = `outline:${props.pageId}:heading:${section.heading.slug}`;
625
+ return /* @__PURE__ */ jsxs4(
626
+ "li",
627
+ {
628
+ className: clsx7("wg_navigationToc__item", {
629
+ "wg_navigationToc__item--trail": index < currentIndex,
630
+ "wg_navigationToc__item--current": index === currentIndex
631
+ }),
632
+ children: [
633
+ /* @__PURE__ */ jsx7(
634
+ "a",
635
+ {
636
+ className: clsx7("wg_navigationToc__link", { "wg_navigationToc__link--active": section.heading.slug === currentSlug }),
637
+ href: `#${section.heading.slug}`,
638
+ "data-toc-link": "",
639
+ "aria-current": section.heading.slug === currentSlug ? "location" : void 0,
640
+ children: section.heading.text
641
+ }
642
+ ),
643
+ section.subs.length > 0 && /* @__PURE__ */ jsx7(WgNavigationBranchToggle, { branchKey: sectionKey, label: section.heading.text, collapsed: props.collapsed, onToggle: props.onToggle }),
644
+ section.subs.length > 0 && /* @__PURE__ */ jsx7("ul", { className: clsx7("wg_navigationToc__sub"), id: toNavigationBranchId(sectionKey), hidden: props.collapsed[sectionKey] === true, children: section.subs.map((sub, subIndex) => /* @__PURE__ */ jsx7(
645
+ "li",
646
+ {
647
+ className: clsx7("wg_navigationToc__subItem", {
648
+ "wg_navigationToc__subItem--trail": subIndex < currentSubIndex,
649
+ "wg_navigationToc__subItem--current": subIndex === currentSubIndex
650
+ }),
651
+ children: /* @__PURE__ */ jsx7(
652
+ "a",
653
+ {
654
+ className: clsx7("wg_navigationToc__subLink", { "wg_navigationToc__subLink--active": sub.heading.slug === currentSlug }),
655
+ href: `#${sub.heading.slug}`,
656
+ "data-toc-sub-link": "",
657
+ "aria-current": sub.heading.slug === currentSlug ? "location" : void 0,
658
+ children: sub.heading.text
659
+ }
660
+ )
661
+ },
662
+ sub.heading.slug
663
+ )) })
664
+ ]
665
+ },
666
+ section.heading.slug
667
+ );
668
+ }) })
669
+ ]
670
+ },
671
+ group.sections[0].heading.slug
672
+ );
673
+ })
295
674
  ] });
296
675
  };
297
676
 
298
- // src/constant/asset.ts
299
- var asset_dir = "_fh";
300
- var asset_style_path = `/${asset_dir}/style.css`;
301
- var asset_client_path = `/${asset_dir}/client.js`;
302
- var asset_font_dir = `/${asset_dir}/fonts`;
303
- var asset_reload_path = `/${asset_dir}/reload`;
304
- var asset_favicon_path = "/favicon.svg";
305
- var asset_content_types = {
306
- ".css": "text/css; charset=utf-8",
307
- ".js": "text/javascript; charset=utf-8",
308
- ".woff2": "font/woff2",
309
- ".svg": "image/svg+xml",
310
- ".png": "image/png",
311
- ".jpg": "image/jpeg",
312
- ".jpeg": "image/jpeg",
313
- ".gif": "image/gif",
314
- ".webp": "image/webp",
315
- ".avif": "image/avif",
316
- ".pdf": "application/pdf",
317
- ".csv": "text/csv; charset=utf-8",
318
- ".txt": "text/plain; charset=utf-8",
319
- ".zip": "application/zip"
677
+ // src/store/navigation/navigation-storage.ts
678
+ var navigation_tree_storage_key = "for-humanity:navigation-tree";
679
+ var navigation_storage_version = 1;
680
+ var navigation_scroll_storage_key = "for-humanity:navigation-scroll";
681
+
682
+ // src/store/navigation/to-navigation-scroll-state.ts
683
+ var toNavigationScrollState = (value) => {
684
+ const toPosition = (position) => {
685
+ 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) {
686
+ return;
687
+ }
688
+ return { navigation: position.navigation, pathname: position.pathname, documents: position.documents, outline: position.outline };
689
+ };
690
+ if (typeof value !== "object" || value === null || !("positions" in value) || typeof value.positions !== "object" || value.positions === null) {
691
+ return { positions: {} };
692
+ }
693
+ const positions = {
694
+ wide: toPosition("wide" in value.positions ? value.positions.wide : void 0),
695
+ desktop: toPosition("desktop" in value.positions ? value.positions.desktop : void 0),
696
+ drawer: toPosition("drawer" in value.positions ? value.positions.drawer : void 0)
697
+ };
698
+ return { positions: Object.fromEntries(Object.entries(positions).filter((entry) => entry[1] !== void 0)) };
320
699
  };
321
- var asset_content_type_default = "application/octet-stream";
322
- var asset_media_dir = `/${asset_dir}/media`;
323
- var asset_media_extensions = /* @__PURE__ */ new Set([".svg", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif", ".pdf", ".csv", ".txt", ".zip"]);
324
700
 
325
- // src/component/widget/shell/wg-shell.tsx
326
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
327
- var reloadScript = `new EventSource(${JSON.stringify(asset_reload_path)}).onmessage=function(){location.reload()}`;
328
- var navigationScript = `(${bindNavigationOverflow.toString()})();if(!matchMedia(${JSON.stringify(navigation_mobile_query)}).matches){(${restoreNavigationScroll.toString()})(${JSON.stringify(`${navigation_scroll_storage_key}:desktop`)})}`;
329
- var WgShell = (props) => {
330
- const tocGroups = props.outline === void 0 ? [] : toTocGroups(props.outline);
331
- return /* @__PURE__ */ jsxs5("html", { lang: "ko", children: [
332
- /* @__PURE__ */ jsxs5("head", { children: [
333
- /* @__PURE__ */ jsx5("meta", { charSet: "utf-8" }),
334
- /* @__PURE__ */ jsx5("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
335
- props.site.description !== void 0 && /* @__PURE__ */ jsx5("meta", { name: "description", content: props.site.description }),
336
- /* @__PURE__ */ jsx5("title", { children: props.title === void 0 ? props.site.title : `${props.title} \xB7 ${props.site.title}` }),
337
- /* @__PURE__ */ jsx5("link", { rel: "icon", href: asset_favicon_path, type: "image/svg+xml" }),
338
- /* @__PURE__ */ jsx5("style", { dangerouslySetInnerHTML: { __html: props.assets.fontCss } }),
339
- props.assets.preload.map((href) => /* @__PURE__ */ jsx5("link", { rel: "preload", href, as: "font", type: "font/woff2", crossOrigin: "" }, href)),
340
- /* @__PURE__ */ jsx5("script", { dangerouslySetInnerHTML: { __html: 'document.documentElement.style.scrollBehavior="auto"' } }),
341
- /* @__PURE__ */ jsx5("link", { rel: "stylesheet", href: props.assets.stylePath }),
342
- props.assets.reload && /* @__PURE__ */ jsx5("script", { dangerouslySetInnerHTML: { __html: reloadScript } })
701
+ // src/store/navigation/use-navigation-tree-store.ts
702
+ import { useSyncExternalStore } from "hono/jsx";
703
+
704
+ // src/store/navigation/_constant/tree.ts
705
+ var tree_server_state = { collapsed: {} };
706
+
707
+ // src/store/navigation/use-navigation-tree-store.ts
708
+ var useNavigationTreeStore = (tree) => {
709
+ const subscribe = tree === void 0 ? () => () => {
710
+ } : tree.subscribe;
711
+ const getSnapshot = tree === void 0 ? () => tree_server_state : tree.getState;
712
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
713
+ };
714
+
715
+ // src/util/dom/reveal-hash-target.ts
716
+ var revealHashTarget = (hash) => {
717
+ const target = findHashTarget(hash);
718
+ for (let fold = target?.closest("details"); fold; fold = fold.parentElement?.closest("details")) {
719
+ fold.open = true;
720
+ }
721
+ return target;
722
+ };
723
+
724
+ // src/component/widget/navigation/wg-navigation.tsx
725
+ import { jsx as jsx8, jsxs as jsxs5 } from "hono/jsx/jsx-runtime";
726
+ var WgNavigation = (props) => {
727
+ const navigationTreeStore = useNavigationTreeStore(props.stores?.tree);
728
+ const [layout, setLayout] = useState3(() => {
729
+ if (props.stores === void 0) return "desktop";
730
+ if (matchMedia(navigation_mobile_query).matches) return "drawer";
731
+ return matchMedia(navigation_wide_query).matches ? "wide" : "desktop";
732
+ });
733
+ const [drawerOpen, setDrawerOpen] = useState3(false);
734
+ const navigationRef = useRef3(null);
735
+ const documentsRef = useRef3(null);
736
+ const drawerNavigationRef = useRef3(null);
737
+ const drawerDocumentsRef = useRef3(null);
738
+ const outlineRef = useRef3(null);
739
+ const dialogRef = useRef3(null);
740
+ const openButtonRef = useRef3(null);
741
+ const brandRef = useRef3(null);
742
+ const mobileRef = useRef3(null);
743
+ const wideRef = useRef3(null);
744
+ const layoutRef = useRef3(layout);
745
+ const focusRef = useRef3(null);
746
+ const restorePendingRef = useRef3(true);
747
+ const getNavigationLayout = () => {
748
+ if (mobileRef.current === null || wideRef.current === null) return layoutRef.current;
749
+ if (mobileRef.current.matches) return "drawer";
750
+ return wideRef.current.matches ? "wide" : "desktop";
751
+ };
752
+ const getNavigationPort = () => {
753
+ return layoutRef.current === "drawer" ? drawerNavigationRef.current : navigationRef.current;
754
+ };
755
+ const getDocumentsPort = () => {
756
+ return layoutRef.current === "drawer" ? drawerDocumentsRef.current : documentsRef.current;
757
+ };
758
+ const getNavigationSignature = () => {
759
+ const navigation = getNavigationPort();
760
+ if (navigation === null) return void 0;
761
+ return [...navigation.querySelectorAll(".wg_navigationNav__root a[href]")].map((link) => `${link.pathname}:${link.textContent}`).join("\n");
762
+ };
763
+ const saveScroll = () => {
764
+ const documents = getDocumentsPort();
765
+ if (props.stores === void 0 || documents === null || outlineRef.current === null || restorePendingRef.current || layoutRef.current !== getNavigationLayout() || layoutRef.current === "drawer" && dialogRef.current?.open !== true) {
766
+ return;
767
+ }
768
+ const signature = getNavigationSignature();
769
+ if (signature === void 0) return;
770
+ const navigationPosition = {
771
+ navigation: signature,
772
+ pathname: location.pathname,
773
+ documents: documents.scrollTop,
774
+ outline: outlineRef.current.scrollTop
775
+ };
776
+ props.stores.scroll.setState((state) => ({
777
+ positions: {
778
+ ...state.positions,
779
+ [layoutRef.current]: navigationPosition
780
+ }
781
+ }));
782
+ };
783
+ const restoreScroll = () => {
784
+ layoutRef.current = getNavigationLayout();
785
+ const documents = getDocumentsPort();
786
+ const navigation = getNavigationPort();
787
+ if (props.stores === void 0 || documents === null || outlineRef.current === null || navigation === null) return;
788
+ if (layoutRef.current === "drawer" && dialogRef.current?.open !== true) return;
789
+ const signature = getNavigationSignature();
790
+ if (signature === void 0) return;
791
+ const position = props.stores.scroll.getState().positions[layoutRef.current];
792
+ documents.scrollTop = 0;
793
+ outlineRef.current.scrollTop = 0;
794
+ if (position === void 0) {
795
+ restoreNavigationScroll({ key: navigation_scroll_storage_key, version: navigation_storage_version, layout: layoutRef.current, toState: toNavigationScrollState });
796
+ } else if (position.navigation === signature) {
797
+ documents.scrollTop = position.documents;
798
+ if (position.pathname === location.pathname) outlineRef.current.scrollTop = position.outline;
799
+ }
800
+ navigation.dataset.navigationLayout = layoutRef.current;
801
+ restorePendingRef.current = false;
802
+ saveScroll();
803
+ documents.dispatchEvent(new Event("scroll"));
804
+ outlineRef.current.dispatchEvent(new Event("scroll"));
805
+ };
806
+ const handleBranchToggle = (key) => {
807
+ props.stores?.tree.setState((state) => ({ collapsed: { ...state.collapsed, [key]: state.collapsed[key] !== true } }));
808
+ };
809
+ const handleDrawerClose = (_event) => {
810
+ if (dialogRef.current?.open === true) return;
811
+ document.body.classList.remove("wg_shell__root--drawerOpen");
812
+ setDrawerOpen(false);
813
+ };
814
+ const closeNavigation = () => {
815
+ saveScroll();
816
+ dialogRef.current?.close();
817
+ document.body.classList.remove("wg_shell__root--drawerOpen");
818
+ setDrawerOpen(false);
819
+ };
820
+ const handleOpenButtonClick = (_event) => {
821
+ if (getNavigationLayout() !== "drawer" || dialogRef.current === null || dialogRef.current.open) return;
822
+ dialogRef.current.showModal();
823
+ document.body.classList.add("wg_shell__root--drawerOpen");
824
+ setDrawerOpen(true);
825
+ restoreScroll();
826
+ };
827
+ const handleCloseButtonClick = (_event) => {
828
+ closeNavigation();
829
+ };
830
+ const handleDrawerCancel = (event) => {
831
+ event.preventDefault();
832
+ closeNavigation();
833
+ };
834
+ const handleDrawerClick = (event) => {
835
+ if (dialogRef.current === null || event.target !== dialogRef.current) return;
836
+ const bounds = dialogRef.current.getBoundingClientRect();
837
+ if (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom) closeNavigation();
838
+ };
839
+ const handleNavigationClick = (event) => {
840
+ saveScroll();
841
+ if (dialogRef.current?.open !== true || event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || !(event.target instanceof Element)) {
842
+ return;
843
+ }
844
+ const link = event.target.closest("a[href]");
845
+ if (!(link instanceof HTMLAnchorElement) || link.target !== "" && link.target !== "_self" || link.hasAttribute("download")) return;
846
+ closeNavigation();
847
+ if (link.origin === location.origin && link.pathname === location.pathname && link.search === location.search && link.hash) {
848
+ const target = revealHashTarget(link.hash);
849
+ if (target !== null) {
850
+ target.tabIndex = -1;
851
+ target.focus({ preventScroll: true });
852
+ }
853
+ }
854
+ };
855
+ const handleLayoutChange = () => {
856
+ const nextLayout = getNavigationLayout();
857
+ if (nextLayout === layoutRef.current) return;
858
+ if (document.activeElement instanceof HTMLElement) {
859
+ focusRef.current = {
860
+ navigation: document.activeElement.closest("[data-navigation]") !== null,
861
+ drawer: document.activeElement === openButtonRef.current || document.activeElement.closest("[data-navigation-drawer]") !== null,
862
+ branch: document.activeElement.getAttribute("data-navigation-toggle"),
863
+ href: document.activeElement.getAttribute("href")
864
+ };
865
+ }
866
+ if (nextLayout !== "drawer") closeNavigation();
867
+ restorePendingRef.current = true;
868
+ layoutRef.current = nextLayout;
869
+ flushSync(() => setLayout(nextLayout));
870
+ };
871
+ const handlePageShow = (event) => {
872
+ if (!event.persisted || props.stores === void 0) return;
873
+ restorePendingRef.current = true;
874
+ flushSync(() => {
875
+ props.stores?.tree.persist.rehydrate();
876
+ props.stores?.scroll.persist.rehydrate();
877
+ });
878
+ handleLayoutChange();
879
+ restoreScroll();
880
+ };
881
+ const handleStorageChange = (event) => {
882
+ if (event.key === navigation_tree_storage_key || event.key === null) {
883
+ flushSync(() => props.stores?.tree.persist.rehydrate());
884
+ }
885
+ };
886
+ const handleTreeChange = (state) => {
887
+ if (!(document.activeElement instanceof HTMLElement)) return;
888
+ for (const button of document.querySelectorAll("[data-navigation-toggle]")) {
889
+ const key = button.dataset.navigationToggle;
890
+ const controls = button.getAttribute("aria-controls");
891
+ if (key === void 0 || controls === null || state.collapsed[key] !== true) continue;
892
+ const branch = document.getElementById(controls);
893
+ if (branch?.contains(document.activeElement)) button.focus({ preventScroll: true });
894
+ }
895
+ };
896
+ useLayoutEffect4(() => {
897
+ if (props.stores === void 0) return;
898
+ mobileRef.current = matchMedia(navigation_mobile_query);
899
+ wideRef.current = matchMedia(navigation_wide_query);
900
+ mobileRef.current.addEventListener("change", handleLayoutChange);
901
+ wideRef.current.addEventListener("change", handleLayoutChange);
902
+ addEventListener("pagehide", saveScroll);
903
+ addEventListener("pageshow", handlePageShow);
904
+ addEventListener("storage", handleStorageChange);
905
+ const unsubscribeTree = props.stores.tree.subscribe(handleTreeChange);
906
+ return () => {
907
+ mobileRef.current?.removeEventListener("change", handleLayoutChange);
908
+ wideRef.current?.removeEventListener("change", handleLayoutChange);
909
+ mobileRef.current = null;
910
+ wideRef.current = null;
911
+ removeEventListener("pagehide", saveScroll);
912
+ removeEventListener("pageshow", handlePageShow);
913
+ removeEventListener("storage", handleStorageChange);
914
+ unsubscribeTree();
915
+ document.body.classList.remove("wg_shell__root--drawerOpen");
916
+ };
917
+ }, [props.stores]);
918
+ useLayoutEffect4(() => {
919
+ if (props.stores === void 0) return;
920
+ let active = true;
921
+ const restoreNavigation = () => {
922
+ if (!active) return;
923
+ restoreScroll();
924
+ if (focusRef.current === null) return;
925
+ const navigation = getNavigationPort();
926
+ if (layout === "drawer") {
927
+ if (focusRef.current.navigation) openButtonRef.current?.focus({ preventScroll: true });
928
+ } else if (focusRef.current.navigation && navigation !== null) {
929
+ const target = [...navigation.querySelectorAll("a[href], button")].find((element) => {
930
+ if (focusRef.current === null) return false;
931
+ return focusRef.current.branch !== null ? element.getAttribute("data-navigation-toggle") === focusRef.current.branch : focusRef.current.href !== null && element.getAttribute("href") === focusRef.current.href;
932
+ });
933
+ target?.focus({ preventScroll: true });
934
+ } else if (focusRef.current.drawer) {
935
+ brandRef.current?.focus({ preventScroll: true });
936
+ }
937
+ focusRef.current = null;
938
+ };
939
+ if (getDocumentsPort()?.isConnected) {
940
+ restoreNavigation();
941
+ } else {
942
+ queueMicrotask(restoreNavigation);
943
+ }
944
+ return () => {
945
+ active = false;
946
+ };
947
+ }, [layout, props.stores]);
948
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
949
+ /* @__PURE__ */ jsxs5("div", { className: clsx8("wg_navigation__sidebar", props.stores !== void 0 && "wg_navigation__sidebar--navigationReady"), children: [
950
+ /* @__PURE__ */ jsxs5("div", { className: clsx8("wg_navigation__brandRow"), children: [
951
+ /* @__PURE__ */ jsx8(
952
+ "a",
953
+ {
954
+ ref: props.stores === void 0 ? void 0 : brandRef,
955
+ className: clsx8("wg_navigation__brand"),
956
+ href: "/",
957
+ "aria-current": props.data.home ? "page" : void 0,
958
+ children: props.data.title
959
+ }
960
+ ),
961
+ /* @__PURE__ */ jsx8(
962
+ "button",
963
+ {
964
+ ref: props.stores === void 0 ? void 0 : openButtonRef,
965
+ className: clsx8("wg_navigation__menuToggle", props.stores !== void 0 && layout === "drawer" && "wg_navigation__menuToggle--visible"),
966
+ type: "button",
967
+ "aria-label": copy_nav_open,
968
+ "aria-controls": "fh-navigation-drawer",
969
+ "aria-expanded": String(drawerOpen),
970
+ "data-navigation-open": "",
971
+ onClick: handleOpenButtonClick,
972
+ children: /* @__PURE__ */ jsx8("svg", { className: clsx8("wg_navigation__menuIcon"), viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx8("path", { d: "M4 6h16M4 12h16M4 18h16" }) })
973
+ }
974
+ )
975
+ ] }),
976
+ layout !== "drawer" && /* @__PURE__ */ jsx8("div", { ref: props.stores === void 0 ? void 0 : navigationRef, className: clsx8("wg_navigation__navigation"), "data-navigation": "", children: /* @__PURE__ */ jsx8(WgNavigationScroll, { portRef: documentsRef, kind: "documents", ready: props.stores !== void 0, onScroll: saveScroll, onClick: handleNavigationClick, children: /* @__PURE__ */ jsx8(
977
+ WgNavigationNav,
978
+ {
979
+ groups: props.data.groups,
980
+ collapsed: navigationTreeStore.collapsed,
981
+ onToggle: props.stores === void 0 ? void 0 : handleBranchToggle
982
+ }
983
+ ) }) })
343
984
  ] }),
344
- /* @__PURE__ */ jsxs5("body", { className: clsx5("wg_shell__root"), children: [
345
- /* @__PURE__ */ jsxs5("div", { className: clsx5("wg_shell__sidebar"), children: [
346
- /* @__PURE__ */ jsxs5("div", { className: clsx5("wg_shell__brandRow"), children: [
347
- /* @__PURE__ */ jsx5("a", { className: clsx5("wg_shell__brand"), href: "/", "aria-current": props.current === void 0 ? "page" : void 0, children: props.site.title }),
348
- /* @__PURE__ */ jsx5(
349
- "button",
985
+ /* @__PURE__ */ jsx8("aside", { className: clsx8("wg_navigation__tocRail"), "data-navigation-rail": "", children: /* @__PURE__ */ jsx8("div", { className: clsx8("wg_navigation__outline"), "data-navigation-outline": "", children: /* @__PURE__ */ jsx8(WgNavigationScroll, { portRef: outlineRef, kind: "outline", ready: props.stores !== void 0, onScroll: saveScroll, onClick: handleNavigationClick, children: props.data.outline.length > 0 && /* @__PURE__ */ jsx8(
986
+ WgNavigationToc,
987
+ {
988
+ groups: props.data.outline,
989
+ pageId: props.data.pageId,
990
+ collapsed: navigationTreeStore.collapsed,
991
+ onToggle: props.stores === void 0 ? void 0 : handleBranchToggle
992
+ }
993
+ ) }) }) }),
994
+ /* @__PURE__ */ jsxs5(
995
+ "dialog",
996
+ {
997
+ ref: props.stores === void 0 ? void 0 : dialogRef,
998
+ className: clsx8("wg_navigation__drawer"),
999
+ id: "fh-navigation-drawer",
1000
+ "aria-labelledby": "fh-navigation-title",
1001
+ "data-navigation-drawer": "",
1002
+ onClose: handleDrawerClose,
1003
+ onCancel: handleDrawerCancel,
1004
+ onClick: handleDrawerClick,
1005
+ children: [
1006
+ /* @__PURE__ */ jsxs5("div", { className: clsx8("wg_navigation__drawerHeader"), children: [
1007
+ /* @__PURE__ */ jsx8("h2", { className: clsx8("wg_navigation__drawerTitle"), id: "fh-navigation-title", children: copy_nav_title }),
1008
+ /* @__PURE__ */ jsx8("button", { className: clsx8("wg_navigation__drawerClose"), type: "button", "aria-label": copy_nav_close, "data-navigation-close": "", onClick: handleCloseButtonClick, children: /* @__PURE__ */ jsx8("svg", { className: clsx8("wg_navigation__menuIcon"), viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx8("path", { d: "m6 6 12 12M18 6 6 18" }) }) })
1009
+ ] }),
1010
+ /* @__PURE__ */ jsx8("div", { className: clsx8("wg_navigation__drawerContent"), "data-navigation-content": "", children: layout === "drawer" && /* @__PURE__ */ jsx8("div", { ref: props.stores === void 0 ? void 0 : drawerNavigationRef, className: clsx8("wg_navigation__navigation"), "data-navigation": "", children: /* @__PURE__ */ jsx8(
1011
+ WgNavigationScroll,
350
1012
  {
351
- className: clsx5("wg_shell__menuToggle"),
352
- type: "button",
353
- "aria-label": copy_nav_open,
354
- "aria-controls": "fh-navigation-drawer",
355
- "aria-expanded": "false",
356
- "data-navigation-open": "",
357
- 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" }) })
1013
+ portRef: drawerDocumentsRef,
1014
+ kind: "documents",
1015
+ ready: props.stores !== void 0,
1016
+ onScroll: saveScroll,
1017
+ onClick: handleNavigationClick,
1018
+ children: /* @__PURE__ */ jsx8(
1019
+ WgNavigationNav,
1020
+ {
1021
+ groups: props.data.groups,
1022
+ collapsed: navigationTreeStore.collapsed,
1023
+ onToggle: props.stores === void 0 ? void 0 : handleBranchToggle
1024
+ }
1025
+ )
358
1026
  }
359
- )
360
- ] }),
361
- /* @__PURE__ */ jsxs5("div", { className: clsx5("wg_shell__navigation"), "data-navigation": "", children: [
362
- /* @__PURE__ */ jsx5("div", { className: clsx5("wg_shell__documents", "wg_shell__scroll"), "data-navigation-scroll": "documents", children: /* @__PURE__ */ jsx5(WgShellNav, { site: props.site, docs: props.docs, current: props.current }) }),
363
- /* @__PURE__ */ jsx5("div", { className: clsx5("wg_shell__outline"), children: /* @__PURE__ */ jsx5("div", { className: clsx5("wg_shell__headings", "wg_shell__scroll"), "data-navigation-scroll": "outline", children: tocGroups.length > 0 && /* @__PURE__ */ jsx5(WgShellToc, { groups: tocGroups }) }) })
364
- ] })
365
- ] }),
366
- /* @__PURE__ */ jsx5("script", { dangerouslySetInnerHTML: { __html: navigationScript } }),
367
- /* @__PURE__ */ jsx5("main", { className: clsx5("wg_shell__main"), children: props.children }),
368
- /* @__PURE__ */ jsxs5("dialog", { className: clsx5("wg_shell__drawer"), id: "fh-navigation-drawer", "aria-labelledby": "fh-navigation-title", "data-navigation-drawer": "", children: [
369
- /* @__PURE__ */ jsxs5("div", { className: clsx5("wg_shell__drawerHeader"), children: [
370
- /* @__PURE__ */ jsx5("h2", { className: clsx5("wg_shell__drawerTitle"), id: "fh-navigation-title", children: copy_nav_title }),
371
- /* @__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" }) }) })
372
- ] }),
373
- /* @__PURE__ */ jsx5("div", { className: clsx5("wg_shell__drawerContent"), "data-navigation-content": "" })
374
- ] }),
375
- /* @__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": "" }),
376
- /* @__PURE__ */ jsx5("script", { type: "module", src: props.assets.clientPath })
1027
+ ) }) })
1028
+ ]
1029
+ }
1030
+ )
1031
+ ] });
1032
+ };
1033
+
1034
+ // src/component/widget/prose/_constant/heading-highlight.ts
1035
+ var heading_highlight_hold_ms = 3e3;
1036
+
1037
+ // src/component/widget/shell-controls/wg-shell-controls.tsx
1038
+ import { jsx as jsx9, jsxs as jsxs6 } from "hono/jsx/jsx-runtime";
1039
+ var WgShellControls = (props) => {
1040
+ const headingRef = useRef4(null);
1041
+ const timerRef = useRef4(0);
1042
+ const arrivalRef = useRef4(null);
1043
+ const lifetimeRef = useRef4(null);
1044
+ const userScrollRef = useRef4(null);
1045
+ const clearHeadingHighlight = () => {
1046
+ clearTimeout(timerRef.current);
1047
+ timerRef.current = 0;
1048
+ arrivalRef.current?.abort();
1049
+ arrivalRef.current = null;
1050
+ headingRef.current?.classList.remove("wg_prose__headingText--highlighted");
1051
+ headingRef.current = null;
1052
+ };
1053
+ const handleHighlightArrival = (event) => {
1054
+ if (event.target !== document) return;
1055
+ clearTimeout(timerRef.current);
1056
+ arrivalRef.current?.abort();
1057
+ arrivalRef.current = null;
1058
+ timerRef.current = window.setTimeout(clearHeadingHighlight, heading_highlight_hold_ms);
1059
+ };
1060
+ const highlightHeading = (target) => {
1061
+ clearHeadingHighlight();
1062
+ headingRef.current = target === null ? null : target.querySelector(".wg_prose__headingText");
1063
+ if (headingRef.current === null) return;
1064
+ headingRef.current.classList.add("wg_prose__headingText--highlighted");
1065
+ timerRef.current = window.setTimeout(clearHeadingHighlight, heading_highlight_hold_ms);
1066
+ arrivalRef.current = new AbortController();
1067
+ document.addEventListener("scrollend", handleHighlightArrival, { signal: arrivalRef.current.signal });
1068
+ };
1069
+ const handleHashClick = (event) => {
1070
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
1071
+ const link = event.target instanceof Element ? event.target.closest("a[href]") : null;
1072
+ if (link instanceof HTMLAnchorElement && (link.target === "" || link.target === "_self") && !link.hasAttribute("download") && link.origin === location.origin && link.pathname === location.pathname && link.search === location.search && link.hash) {
1073
+ const target = revealHashTarget(link.hash);
1074
+ if (link.hash === location.hash) highlightHeading(target);
1075
+ }
1076
+ };
1077
+ const handleHashChange = () => {
1078
+ const target = revealHashTarget(location.hash);
1079
+ target?.scrollIntoView();
1080
+ highlightHeading(target);
1081
+ };
1082
+ const handleUserScroll = () => {
1083
+ userScrollRef.current?.abort();
1084
+ };
1085
+ const handlePageLoad = async () => {
1086
+ const lifetime = lifetimeRef.current;
1087
+ const hash = location.hash;
1088
+ const target = revealHashTarget(hash);
1089
+ await document.fonts.ready;
1090
+ if (lifetime === null || lifetime.signal.aborted) return;
1091
+ if (target !== null && !userScrollRef.current?.signal.aborted && hash === location.hash) {
1092
+ target.scrollIntoView({ behavior: "instant" });
1093
+ highlightHeading(target);
1094
+ }
1095
+ document.documentElement.style.removeProperty("scroll-behavior");
1096
+ };
1097
+ const handleBodyReady = () => {
1098
+ revealHashTarget(location.hash);
1099
+ if (document.readyState === "complete") void handlePageLoad();
1100
+ };
1101
+ useLayoutEffect5(() => {
1102
+ const lifetime = new AbortController();
1103
+ lifetimeRef.current = lifetime;
1104
+ userScrollRef.current = new AbortController();
1105
+ document.documentElement.style.scrollBehavior = "auto";
1106
+ document.addEventListener("click", handleHashClick, { signal: lifetime.signal });
1107
+ addEventListener("hashchange", handleHashChange, { signal: lifetime.signal });
1108
+ addEventListener("load", handlePageLoad, { once: true, signal: lifetime.signal });
1109
+ for (const type of ["wheel", "touchmove", "keydown", "mousedown"]) {
1110
+ addEventListener(type, handleUserScroll, { once: true, passive: true, signal: lifetime.signal });
1111
+ }
1112
+ if (document.readyState === "loading") {
1113
+ document.addEventListener("DOMContentLoaded", handleBodyReady, { once: true, signal: lifetime.signal });
1114
+ } else {
1115
+ handleBodyReady();
1116
+ }
1117
+ return () => {
1118
+ lifetime.abort();
1119
+ lifetimeRef.current = null;
1120
+ userScrollRef.current?.abort();
1121
+ userScrollRef.current = null;
1122
+ clearHeadingHighlight();
1123
+ document.documentElement.style.removeProperty("scroll-behavior");
1124
+ };
1125
+ }, []);
1126
+ useEffect(() => {
1127
+ if (!props.reload || lifetimeRef.current === null) return;
1128
+ const source = new EventSource(asset_reload_path);
1129
+ source.onmessage = () => location.reload();
1130
+ return () => source.close();
1131
+ }, [props.reload]);
1132
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
1133
+ /* @__PURE__ */ jsx9("div", { className: clsx9("wg_shellControls__navigation"), children: /* @__PURE__ */ jsx9(WgNavigation, { data: props.data, stores: props.stores }) }),
1134
+ /* @__PURE__ */ jsx9(WgCursorFace, { ready: props.stores !== void 0 })
1135
+ ] });
1136
+ };
1137
+
1138
+ // src/component/widget/shell/wg-shell.tsx
1139
+ import { jsx as jsx10, jsxs as jsxs7 } from "hono/jsx/jsx-runtime";
1140
+ var WgShell = (props) => {
1141
+ const navigationData = toNavigationData({ site: props.site, docs: props.docs, current: props.current, outline: props.outline });
1142
+ return /* @__PURE__ */ jsxs7("html", { lang: "ko", children: [
1143
+ /* @__PURE__ */ jsxs7("head", { children: [
1144
+ /* @__PURE__ */ jsx10("meta", { charSet: "utf-8" }),
1145
+ /* @__PURE__ */ jsx10("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
1146
+ props.site.description !== void 0 && /* @__PURE__ */ jsx10("meta", { name: "description", content: props.site.description }),
1147
+ /* @__PURE__ */ jsx10("title", { children: props.title === void 0 ? props.site.title : `${props.title} \xB7 ${props.site.title}` }),
1148
+ /* @__PURE__ */ jsx10("link", { rel: "icon", href: asset_favicon_path, type: "image/svg+xml" }),
1149
+ /* @__PURE__ */ jsx10("style", { dangerouslySetInnerHTML: { __html: props.assets.fontCss } }),
1150
+ props.assets.preload.map((href) => /* @__PURE__ */ jsx10("link", { rel: "preload", href, as: "font", type: "font/woff2", crossOrigin: "" }, href)),
1151
+ /* @__PURE__ */ jsx10("link", { rel: "stylesheet", href: props.assets.stylePath })
1152
+ ] }),
1153
+ /* @__PURE__ */ jsxs7("body", { className: clsx10("wg_shell__root"), children: [
1154
+ /* @__PURE__ */ jsx10("div", { className: clsx10("wg_shell__browserRoot"), "data-shell-browser-root": "", "data-shell-reload": props.assets.reload ? "" : void 0, children: /* @__PURE__ */ jsx10(WgShellControls, { data: navigationData, reload: props.assets.reload }) }),
1155
+ /* @__PURE__ */ jsx10("script", { type: "application/json", id: "fh-navigation-data", dangerouslySetInnerHTML: { __html: toNavigationJson(navigationData) } }),
1156
+ /* @__PURE__ */ jsx10("script", { dangerouslySetInnerHTML: { __html: props.assets.browserScript } }),
1157
+ /* @__PURE__ */ jsx10("main", { className: clsx10("wg_shell__main"), children: props.children })
377
1158
  ] })
378
1159
  ] });
379
1160
  };
380
1161
 
381
1162
  // src/page/doc/pg-doc.tsx
382
- import { jsx as jsx6 } from "react/jsx-runtime";
1163
+ import { jsx as jsx11 } from "hono/jsx/jsx-runtime";
383
1164
  var PgDoc = (props) => {
384
- 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 }) });
1165
+ return /* @__PURE__ */ jsx11(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__ */ jsx11(WgProse, { html: props.doc.html }) });
385
1166
  };
386
1167
 
387
1168
  // src/page/home/pg-home.tsx
388
- import { jsx as jsx7 } from "react/jsx-runtime";
1169
+ import { jsx as jsx12 } from "hono/jsx/jsx-runtime";
389
1170
  var PgHome = (props) => {
390
- 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 }) });
1171
+ return /* @__PURE__ */ jsx12(WgShell, { site: props.site, docs: props.docs, assets: props.assets, outline: props.home?.outline, children: props.home === void 0 ? /* @__PURE__ */ jsx12(WgProse, { head: { title: props.site.title, lead: copy_home_missing } }) : /* @__PURE__ */ jsx12(WgProse, { html: props.home.html }) });
391
1172
  };
392
1173
 
393
- // src/app.tsx
394
- import { jsx as jsx8 } from "react/jsx-runtime";
1174
+ // src/entry/cli/_function/create-app.tsx
1175
+ import { jsx as jsx13 } from "hono/jsx/jsx-runtime";
395
1176
  var toHtml = (page) => {
396
- return `<!DOCTYPE html>${renderToStaticMarkup(page)}`;
1177
+ return html`<!DOCTYPE html>${page}`;
397
1178
  };
398
1179
  var createApp = (options) => {
399
1180
  const app = new Hono();
400
- app.get("/", (c) => c.html(toHtml(/* @__PURE__ */ jsx8(PgHome, { site: options.site, docs: options.store.docs, assets: options.assets, home: options.store.home }))));
1181
+ app.get("/", (c) => c.html(toHtml(/* @__PURE__ */ jsx13(PgHome, { site: options.site, docs: options.store.docs, assets: options.assets, home: options.store.home }))));
401
1182
  app.get(
402
1183
  "/:slug{.+}/",
403
1184
  ssgParams(() => options.store.docs.map((doc) => ({ slug: doc.id }))),
404
1185
  (c) => {
405
1186
  const doc = options.store.docs.find((entry) => entry.id === c.req.param("slug"));
406
- return doc === void 0 ? c.notFound() : c.html(toHtml(/* @__PURE__ */ jsx8(PgDoc, { site: options.site, docs: options.store.docs, assets: options.assets, doc })));
1187
+ return doc === void 0 ? c.notFound() : c.html(toHtml(/* @__PURE__ */ jsx13(PgDoc, { site: options.site, docs: options.store.docs, assets: options.assets, doc })));
407
1188
  }
408
1189
  );
409
1190
  return app;
410
1191
  };
411
1192
 
412
- // src/constant/cli.ts
413
- var cli_default_command = "dev";
414
- var cli_default_docs_dir = ".";
415
- var cli_config_file_name = "for-humanity.config.mjs";
416
- var cli_dev_port = 4321;
417
- var cli_reload_delay_ms = 150;
418
-
419
- // src/constant/font.ts
420
- var font_sans_css = "src/asset/font/pretendard/pretendardvariable-dynamic-subset.css";
421
- var font_mono_css = "src/asset/font/jetbrains-mono/jetbrains-mono.css";
422
- var font_brand_css = "src/asset/font/architects-daughter/architects-daughter.css";
423
- var font_sans_preload_file = "PretendardVariable.subset.91.woff2";
424
- var font_display_strategy = "optional";
425
- var font_hash_length = 12;
426
- var font_cache_control = "public, max-age=31536000, immutable";
427
-
428
- // src/constant/site.ts
429
- var site_config_absent = {};
430
- var site_navigation_default = [];
1193
+ // src/entry/cli/_function/create-dev-app.ts
1194
+ import { readFile } from "node:fs/promises";
1195
+ import { extname } from "node:path";
1196
+ import { Hono as Hono2 } from "hono";
1197
+ import { streamSSE } from "hono/streaming";
1198
+ var createDevApp = (options) => {
1199
+ const app = new Hono2();
1200
+ const handleAsset = async (c) => {
1201
+ const file = options.files.get(c.req.path);
1202
+ if (file === void 0) {
1203
+ return c.notFound();
1204
+ }
1205
+ c.header("content-type", asset_content_types[extname(file).toLowerCase()] ?? asset_content_type_default);
1206
+ if (extname(file) === ".woff2") {
1207
+ c.header("Cache-Control", font_cache_control);
1208
+ }
1209
+ return c.body(await readFile(file));
1210
+ };
1211
+ app.get(
1212
+ asset_reload_path,
1213
+ (c) => streamSSE(
1214
+ c,
1215
+ (stream) => new Promise((done) => {
1216
+ const handleChange = async () => {
1217
+ await stream.writeSSE({ data: "change" });
1218
+ done();
1219
+ };
1220
+ options.reload.once("change", handleChange);
1221
+ stream.onAbort(() => {
1222
+ options.reload.off("change", handleChange);
1223
+ done();
1224
+ });
1225
+ })
1226
+ )
1227
+ );
1228
+ app.get(`/${asset_dir}/*`, handleAsset);
1229
+ app.get(asset_favicon_path, handleAsset);
1230
+ app.all("/*", (c) => options.pages.fetch(c.req.raw, c.env));
1231
+ return app;
1232
+ };
431
1233
 
432
- // src/content/create-processor.ts
1234
+ // src/entry/cli/_function/create-processor/create-processor.ts
433
1235
  import rehypeShiki from "@shikijs/rehype";
434
1236
  import rehypeRaw from "rehype-raw";
435
1237
  import rehypeStringify from "rehype-stringify";
@@ -1047,9 +1849,9 @@ var remarkUnknownDirectives = () => (tree, file) => {
1047
1849
  });
1048
1850
  };
1049
1851
 
1050
- // src/content/rehype-assets.ts
1852
+ // src/entry/cli/_function/create-processor/_rehype-assets.ts
1051
1853
  import { existsSync as existsSync2, realpathSync, statSync } from "node:fs";
1052
- import { dirname as dirname2, extname, isAbsolute, relative as relative2, resolve as resolve2, sep } from "node:path";
1854
+ import { dirname as dirname2, extname as extname2, isAbsolute, relative as relative2, resolve as resolve2, sep } from "node:path";
1053
1855
  import { visit as visit7 } from "unist-util-visit";
1054
1856
 
1055
1857
  // src/util/file/to-asset-path.ts
@@ -1061,7 +1863,7 @@ var toAssetPath = (options) => {
1061
1863
  return `${posix2.dirname(options.path)}/${posix2.basename(options.path, extension)}.${createHash("sha256").update(readFileSync(options.file)).digest("hex")}${extension}`;
1062
1864
  };
1063
1865
 
1064
- // src/content/rehype-assets.ts
1866
+ // src/entry/cli/_function/create-processor/_rehype-assets.ts
1065
1867
  var rehypeAssets = (options) => (tree, file) => {
1066
1868
  visit7(tree, "element", (node) => {
1067
1869
  const property = node.tagName === "img" ? "src" : "href";
@@ -1074,7 +1876,7 @@ var rehypeAssets = (options) => (tree, file) => {
1074
1876
  return;
1075
1877
  }
1076
1878
  const path = decodeURIComponent(match[1]);
1077
- const extension = extname(path).toLowerCase();
1879
+ const extension = extname2(path).toLowerCase();
1078
1880
  if (!asset_media_extensions.has(extension)) {
1079
1881
  if (node.tagName === "img") {
1080
1882
  file.fail(`${copy_error_asset}: \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC774\uBBF8\uC9C0 \uD615\uC2DD \xB7 ${url}`, node);
@@ -1100,7 +1902,7 @@ var rehypeAssets = (options) => (tree, file) => {
1100
1902
  });
1101
1903
  };
1102
1904
 
1103
- // src/content/rehype-heading-ids.ts
1905
+ // src/entry/cli/_function/create-processor/_rehype-heading-ids.ts
1104
1906
  import Slugger from "github-slugger";
1105
1907
  import { toString as toPlainText3 } from "hast-util-to-string";
1106
1908
  import { visit as visit8 } from "unist-util-visit";
@@ -1122,7 +1924,7 @@ var rehypeHeadingIds = () => (tree, file) => {
1122
1924
  }
1123
1925
  };
1124
1926
 
1125
- // src/content/remark-report.ts
1927
+ // src/entry/cli/_function/create-processor/_remark-report.ts
1126
1928
  import { relative as relative3 } from "node:path";
1127
1929
  var remarkReport = (options) => (_tree, file) => {
1128
1930
  const path = relative3(options.root, file.path);
@@ -1131,13 +1933,13 @@ var remarkReport = (options) => (_tree, file) => {
1131
1933
  }
1132
1934
  };
1133
1935
 
1134
- // src/content/create-processor.ts
1936
+ // src/entry/cli/_function/create-processor/create-processor.ts
1135
1937
  var createProcessor = (options) => {
1136
1938
  return unified().use(remarkParse).use(remarkFrontmatter, ["yaml"]).use(remarkGfm).use(remarkSmartypants).use(remarkDirective).use(remarkBlocks).use(remarkParts).use(remarkFlow).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, { theme: createCssVariablesTheme({ name: "for-humanity", variablePrefix: "--app-code-" }) }).use(rehypeHead).use(rehypeHeadingIds).use(rehypeSections).use(rehypeTables).use(rehypeRaw).use(rehypeAssets, { root: options.root, files: options.files ?? /* @__PURE__ */ new Map() }).use(rehypeStringify, { allowDangerousHtml: true });
1137
1939
  };
1138
1940
 
1139
- // src/content/read-docs.ts
1140
- import { readdir, readFile } from "node:fs/promises";
1941
+ // src/entry/cli/_function/read-docs.ts
1942
+ import { readdir, readFile as readFile2 } from "node:fs/promises";
1141
1943
  import { join, posix as posix3, sep as sep2 } from "node:path";
1142
1944
  import { VFile } from "vfile";
1143
1945
  import { parse as parseYaml } from "yaml";
@@ -1184,7 +1986,7 @@ var docDataSchema = z2.object({
1184
1986
  order: z2.number().int().nonnegative().default(doc_order_default)
1185
1987
  });
1186
1988
 
1187
- // src/content/read-docs.ts
1989
+ // src/entry/cli/_function/read-docs.ts
1188
1990
  var readDocs = async (options) => {
1189
1991
  const paths = (await readdir(options.root, { recursive: true })).filter(
1190
1992
  (path) => /\.mdx?$/i.test(path) && !/^(readme|agents|claude)\.md$/i.test(path) && !path.split(sep2).includes("node_modules") && !path.startsWith(`dist${sep2}`)
@@ -1196,7 +1998,7 @@ var readDocs = async (options) => {
1196
1998
  if (segments.some((segment) => segment === "" || segment === "." || segment === ".." || segment === "index.html") || /[#?%*:\\]/.test(id) || /[\p{Cc}]/u.test(id) || segments[0] === asset_dir || segments[0] === asset_favicon_path.slice(1)) {
1197
1999
  throw new Error(`${copy_error_doc_id}: ${path}`);
1198
2000
  }
1199
- const source = await readFile(join(options.root, path), "utf8");
2001
+ const source = await readFile2(join(options.root, path), "utf8");
1200
2002
  const file = new VFile({ value: source.replace(/^\uFEFF/, ""), path: join(options.root, path) });
1201
2003
  const tree = options.processor.parse(file);
1202
2004
  const frontmatter = tree.children[0];
@@ -1246,8 +2048,8 @@ var readDocs = async (options) => {
1246
2048
  });
1247
2049
  };
1248
2050
 
1249
- // src/content/read-home.ts
1250
- import { readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
2051
+ // src/entry/cli/_function/read-home.ts
2052
+ import { readdir as readdir2, readFile as readFile3 } from "node:fs/promises";
1251
2053
  import { join as join2 } from "node:path";
1252
2054
  import { VFile as VFile2 } from "vfile";
1253
2055
  var readHome = async (options) => {
@@ -1260,52 +2062,11 @@ var readHome = async (options) => {
1260
2062
  }
1261
2063
  const path = join2(options.root, paths[0]);
1262
2064
  const fh = { headings: [], sections: [] };
1263
- const file = new VFile2({ value: (await readFile2(path, "utf8")).replace(/^\uFEFF/, ""), path, data: { fh } });
2065
+ const file = new VFile2({ value: (await readFile3(path, "utf8")).replace(/^\uFEFF/, ""), path, data: { fh } });
1264
2066
  await options.processor.process(file);
1265
2067
  return { html: String(file), outline: { headings: fh.headings, sections: fh.sections } };
1266
2068
  };
1267
2069
 
1268
- // src/dev.ts
1269
- import { readFile as readFile3 } from "node:fs/promises";
1270
- import { extname as extname2 } from "node:path";
1271
- import { Hono as Hono2 } from "hono";
1272
- import { streamSSE } from "hono/streaming";
1273
- var createDevApp = (options) => {
1274
- const app = new Hono2();
1275
- const handleAsset = async (c) => {
1276
- const file = options.files.get(c.req.path);
1277
- if (file === void 0) {
1278
- return c.notFound();
1279
- }
1280
- c.header("content-type", asset_content_types[extname2(file).toLowerCase()] ?? asset_content_type_default);
1281
- if (extname2(file) === ".woff2") {
1282
- c.header("Cache-Control", font_cache_control);
1283
- }
1284
- return c.body(await readFile3(file));
1285
- };
1286
- app.get(
1287
- asset_reload_path,
1288
- (c) => streamSSE(
1289
- c,
1290
- (stream) => new Promise((done) => {
1291
- const handleChange = async () => {
1292
- await stream.writeSSE({ data: "change" });
1293
- done();
1294
- };
1295
- options.reload.once("change", handleChange);
1296
- stream.onAbort(() => {
1297
- options.reload.off("change", handleChange);
1298
- done();
1299
- });
1300
- })
1301
- )
1302
- );
1303
- app.get(`/${asset_dir}/*`, handleAsset);
1304
- app.get(asset_favicon_path, handleAsset);
1305
- app.all("/*", (c) => options.pages.fetch(c.req.raw, c.env));
1306
- return app;
1307
- };
1308
-
1309
2070
  // src/type/site-config.ts
1310
2071
  import { z as z3 } from "zod";
1311
2072
 
@@ -1407,7 +2168,7 @@ var toFontCss = (options) => {
1407
2168
  return { css, files };
1408
2169
  };
1409
2170
 
1410
- // src/cli.ts
2171
+ // src/entry/cli/cli.ts
1411
2172
  var commands = ["dev", "build", "preview"];
1412
2173
  var [command = cli_default_command, docsDir = cli_default_docs_dir] = process.argv.slice(2);
1413
2174
  var main = async () => {
@@ -1448,10 +2209,9 @@ var main = async () => {
1448
2209
  }
1449
2210
  const fontCss = [sans.css, mono.css, brand.css].join("\n");
1450
2211
  const stylePath = toAssetPath({ path: asset_style_path, file: join4(kitRoot, "dist/cli.css") });
1451
- const clientPath = toAssetPath({ path: asset_client_path, file: join4(kitRoot, "dist/client.js") });
2212
+ const browserScript = readFileSync3(join4(kitRoot, "dist/browser.js"), "utf8");
1452
2213
  const kitFiles = new Map([
1453
2214
  [stylePath, join4(kitRoot, "dist/cli.css")],
1454
- [clientPath, join4(kitRoot, "dist/client.js")],
1455
2215
  [asset_favicon_path, join4(kitRoot, "src/asset/favicon.svg")],
1456
2216
  ...sans.files,
1457
2217
  ...mono.files,
@@ -1464,7 +2224,7 @@ var main = async () => {
1464
2224
  const app = createApp({
1465
2225
  site: siteConfig,
1466
2226
  store,
1467
- assets: { stylePath, clientPath, fontCss, preload: [...brand.files.keys(), sansPreload[0], ...mono.files.keys()], reload: command === "dev" }
2227
+ assets: { stylePath, browserScript, fontCss, preload: [...brand.files.keys(), sansPreload[0], ...mono.files.keys()], reload: command === "dev" }
1468
2228
  });
1469
2229
  if (command === "build") {
1470
2230
  await rm(outDir, { recursive: true, force: true });