for-humanity 0.1.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 (177) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +118 -0
  4. package/dist/cli.css +962 -0
  5. package/dist/cli.js +1241 -0
  6. package/dist/client.js +5 -0
  7. package/package.json +92 -0
  8. package/src/app.tsx +57 -0
  9. package/src/asset/favicon.svg +5 -0
  10. package/src/asset/font/architects-daughter/LICENSE.txt +93 -0
  11. package/src/asset/font/architects-daughter/architects-daughter-latin-400-normal.woff2 +0 -0
  12. package/src/asset/font/architects-daughter/architects-daughter.css +10 -0
  13. package/src/asset/font/jetbrains-mono/LICENSE +93 -0
  14. package/src/asset/font/jetbrains-mono/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  15. package/src/asset/font/jetbrains-mono/jetbrains-mono.css +11 -0
  16. package/src/asset/font/pretendard/LICENSE.txt +94 -0
  17. package/src/asset/font/pretendard/pretendardvariable-dynamic-subset.css +1008 -0
  18. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.0.woff2 +0 -0
  19. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.1.woff2 +0 -0
  20. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.10.woff2 +0 -0
  21. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.11.woff2 +0 -0
  22. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.12.woff2 +0 -0
  23. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.13.woff2 +0 -0
  24. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.14.woff2 +0 -0
  25. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.15.woff2 +0 -0
  26. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.16.woff2 +0 -0
  27. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.17.woff2 +0 -0
  28. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.18.woff2 +0 -0
  29. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.19.woff2 +0 -0
  30. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.2.woff2 +0 -0
  31. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.20.woff2 +0 -0
  32. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.21.woff2 +0 -0
  33. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.22.woff2 +0 -0
  34. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.23.woff2 +0 -0
  35. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.24.woff2 +0 -0
  36. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.25.woff2 +0 -0
  37. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.26.woff2 +0 -0
  38. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.27.woff2 +0 -0
  39. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.28.woff2 +0 -0
  40. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.29.woff2 +0 -0
  41. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.3.woff2 +0 -0
  42. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.30.woff2 +0 -0
  43. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.31.woff2 +0 -0
  44. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.32.woff2 +0 -0
  45. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.33.woff2 +0 -0
  46. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.34.woff2 +0 -0
  47. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.35.woff2 +0 -0
  48. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.36.woff2 +0 -0
  49. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.37.woff2 +0 -0
  50. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.38.woff2 +0 -0
  51. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.39.woff2 +0 -0
  52. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.4.woff2 +0 -0
  53. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.40.woff2 +0 -0
  54. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.41.woff2 +0 -0
  55. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.42.woff2 +0 -0
  56. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.43.woff2 +0 -0
  57. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.44.woff2 +0 -0
  58. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.45.woff2 +0 -0
  59. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.46.woff2 +0 -0
  60. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.47.woff2 +0 -0
  61. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.48.woff2 +0 -0
  62. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.49.woff2 +0 -0
  63. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.5.woff2 +0 -0
  64. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.50.woff2 +0 -0
  65. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.51.woff2 +0 -0
  66. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.52.woff2 +0 -0
  67. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.53.woff2 +0 -0
  68. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.54.woff2 +0 -0
  69. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.55.woff2 +0 -0
  70. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.56.woff2 +0 -0
  71. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.57.woff2 +0 -0
  72. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.58.woff2 +0 -0
  73. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.59.woff2 +0 -0
  74. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.6.woff2 +0 -0
  75. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.60.woff2 +0 -0
  76. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.61.woff2 +0 -0
  77. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.62.woff2 +0 -0
  78. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.63.woff2 +0 -0
  79. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.64.woff2 +0 -0
  80. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.65.woff2 +0 -0
  81. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.66.woff2 +0 -0
  82. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.67.woff2 +0 -0
  83. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.68.woff2 +0 -0
  84. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.69.woff2 +0 -0
  85. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.7.woff2 +0 -0
  86. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.70.woff2 +0 -0
  87. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.71.woff2 +0 -0
  88. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.72.woff2 +0 -0
  89. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.73.woff2 +0 -0
  90. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.74.woff2 +0 -0
  91. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.75.woff2 +0 -0
  92. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.76.woff2 +0 -0
  93. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.77.woff2 +0 -0
  94. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.78.woff2 +0 -0
  95. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.79.woff2 +0 -0
  96. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.8.woff2 +0 -0
  97. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.80.woff2 +0 -0
  98. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.81.woff2 +0 -0
  99. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.82.woff2 +0 -0
  100. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.83.woff2 +0 -0
  101. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.84.woff2 +0 -0
  102. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.85.woff2 +0 -0
  103. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.86.woff2 +0 -0
  104. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.87.woff2 +0 -0
  105. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.88.woff2 +0 -0
  106. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.89.woff2 +0 -0
  107. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.9.woff2 +0 -0
  108. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.90.woff2 +0 -0
  109. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.91.woff2 +0 -0
  110. package/src/cli.ts +162 -0
  111. package/src/client.ts +322 -0
  112. package/src/component/widget/prose/_constant/heading-highlight.ts +4 -0
  113. package/src/component/widget/prose/_constant/section.ts +7 -0
  114. package/src/component/widget/prose/_constant/status.ts +9 -0
  115. package/src/component/widget/prose/_function/has-block-content.ts +30 -0
  116. package/src/component/widget/prose/_function/rehype-head.ts +31 -0
  117. package/src/component/widget/prose/_function/rehype-sections/_to-section-headings.ts +20 -0
  118. package/src/component/widget/prose/_function/rehype-sections/rehype-sections.ts +21 -0
  119. package/src/component/widget/prose/_function/rehype-tables.ts +22 -0
  120. package/src/component/widget/prose/_function/remark-blocks.ts +112 -0
  121. package/src/component/widget/prose/_function/remark-flow.ts +26 -0
  122. package/src/component/widget/prose/_function/remark-links.ts +33 -0
  123. package/src/component/widget/prose/_function/remark-parts.ts +26 -0
  124. package/src/component/widget/prose/_function/remark-status.ts +59 -0
  125. package/src/component/widget/prose/_function/remark-swatch.ts +16 -0
  126. package/src/component/widget/prose/_function/remark-unknown-directives.ts +25 -0
  127. package/src/component/widget/prose/_type/doc-section.ts +9 -0
  128. package/src/component/widget/prose/wg-prose.css +381 -0
  129. package/src/component/widget/prose/wg-prose.tsx +43 -0
  130. package/src/component/widget/shell/_constant/cursor-face.ts +9 -0
  131. package/src/component/widget/shell/_constant/navigation.ts +4 -0
  132. package/src/component/widget/shell/_constant/reading-line.ts +4 -0
  133. package/src/component/widget/shell/_function/bind-mobile-navigation.ts +140 -0
  134. package/src/component/widget/shell/_function/to-toc-groups.ts +38 -0
  135. package/src/component/widget/shell/_type/doc-outline.ts +16 -0
  136. package/src/component/widget/shell/_type/toc-group.ts +39 -0
  137. package/src/component/widget/shell/_wg-shell-nav.css +134 -0
  138. package/src/component/widget/shell/_wg-shell-nav.tsx +75 -0
  139. package/src/component/widget/shell/_wg-shell-toc.css +268 -0
  140. package/src/component/widget/shell/_wg-shell-toc.tsx +57 -0
  141. package/src/component/widget/shell/wg-shell.css +261 -0
  142. package/src/component/widget/shell/wg-shell.tsx +147 -0
  143. package/src/constant/asset.ts +48 -0
  144. package/src/constant/cli.ts +19 -0
  145. package/src/constant/copy.ts +108 -0
  146. package/src/constant/doc.ts +13 -0
  147. package/src/constant/font.ts +39 -0
  148. package/src/constant/locale.ts +9 -0
  149. package/src/constant/site.ts +9 -0
  150. package/src/constant/status.ts +15 -0
  151. package/src/content/create-processor.ts +59 -0
  152. package/src/content/read-docs.test.ts +242 -0
  153. package/src/content/read-docs.ts +75 -0
  154. package/src/content/read-home.ts +32 -0
  155. package/src/content/rehype-heading-ids.ts +34 -0
  156. package/src/content/remark-report.ts +15 -0
  157. package/src/content/to-doc-groups.ts +18 -0
  158. package/src/dev.ts +79 -0
  159. package/src/env.d.ts +6 -0
  160. package/src/page/doc/pg-doc.tsx +35 -0
  161. package/src/page/home/pg-home.tsx +40 -0
  162. package/src/style/base.css +28 -0
  163. package/src/style/token.css +162 -0
  164. package/src/type/doc-content.ts +15 -0
  165. package/src/type/doc-data.ts +33 -0
  166. package/src/type/doc-file-data.ts +21 -0
  167. package/src/type/doc-heading.ts +17 -0
  168. package/src/type/doc.ts +16 -0
  169. package/src/type/site-assets.ts +17 -0
  170. package/src/type/site-config.ts +55 -0
  171. package/src/type/vfile-data.d.ts +10 -0
  172. package/src/util/dom/find-hash-target.ts +18 -0
  173. package/src/util/dom/reveal-hash-target.ts +15 -0
  174. package/src/util/error/to-error-message.ts +10 -0
  175. package/src/util/font/to-font-css.test.ts +41 -0
  176. package/src/util/font/to-font-css.ts +28 -0
  177. package/src/util/mermaid/render-flow.js +321 -0
@@ -0,0 +1,28 @@
1
+ /*
2
+ * 전역 상자 크기와 스크롤·움직임 기본값
3
+ */
4
+
5
+ * {
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ /* 페이지 바탕과 절 이동 · 초기 hash 보정 중 smooth scroll 일시 중지 */
10
+ :root {
11
+ background: var(--app-color-ground);
12
+ scrollbar-gutter: stable;
13
+ scroll-behavior: smooth;
14
+ }
15
+
16
+ /* 움직임 줄이기 · 컴포넌트보다 우선하는 전역 !important 예외 */
17
+ @media (prefers-reduced-motion: reduce) {
18
+ *,
19
+ *::before,
20
+ *::after {
21
+ animation-duration: 0.01ms !important;
22
+ animation-delay: 0s !important;
23
+ animation-iteration-count: 1 !important;
24
+ transition-duration: 0.01ms !important;
25
+ transition-delay: 0s !important;
26
+ scroll-behavior: auto !important;
27
+ }
28
+ }
@@ -0,0 +1,162 @@
1
+ /*
2
+ * 공통 토큰의 단일 출처 · --app-종류-쓰임
3
+ * 사용 기준: templates/document/design.md · 테마 분기는 color-scheme에 한정
4
+ * 폭 기준 1024px·640px · 공유 사이드바와 본문 배치
5
+ */
6
+
7
+ :root {
8
+ /* 시스템의 밝게·어둡게 설정을 그대로 따름 */
9
+ color-scheme: light dark;
10
+
11
+ /* 바탕 · ground 페이지, surface 상자, code 코드 */
12
+ --app-color-ground: light-dark(#fff, #181818);
13
+ --app-color-surface: light-dark(#fff, #181818);
14
+ --app-color-code: light-dark(#f1f1eb, #22221f);
15
+ --app-color-backdrop: light-dark(rgb(0 0 0 / 30%), rgb(0 0 0 / 55%));
16
+
17
+ /* 글 · text 본문, strong 강조, muted 라벨 */
18
+ --app-color-text: light-dark(#333, #d5d5d5);
19
+ --app-color-text-strong: light-dark(#222, #eee);
20
+ --app-color-text-muted: light-dark(#666, #aaa);
21
+
22
+ /* 앵커로 도착한 제목 · 형광펜 바탕과 테마별 읽기 대비 */
23
+ --app-color-anchor-highlight: light-dark(#f4f58a, #666322);
24
+
25
+ /* 제목 강조 해제 · 움직임 감소는 base.css에서 일괄 차단 */
26
+ --app-motion-duration-highlight-fade: 600ms;
27
+ --app-motion-easing-out: ease-out;
28
+
29
+ /* 선 · border 상자·목록, soft 표 구분, layout 열 경계 */
30
+ --app-color-border: light-dark(#aaa, #555);
31
+ --app-color-border-soft: light-dark(#ddd, #333);
32
+ --app-color-border-layout: light-dark(#eee, #2b2b2b);
33
+
34
+ /* 강조 · 현재 절·코드 · soft 행 hover 바탕 */
35
+ --app-color-accent: light-dark(#555, #b5b5b5);
36
+ --app-color-accent-soft: light-dark(#f5f5f2, #222);
37
+
38
+ /* 링크 · 기본·방문·hover */
39
+ --app-color-link: light-dark(#11a, #9db8ff);
40
+ --app-color-link-visited: light-dark(#51a, #c5a5ed);
41
+ --app-color-link-hover: light-dark(#44d, #c1d1ff);
42
+
43
+ /* 상태 표지 · verified 초록, unverified amber */
44
+ --app-color-verified: light-dark(#176b36, #82cf95);
45
+ --app-color-verified-soft: light-dark(#edf5ed, #1d2c20);
46
+ --app-color-unverified: light-dark(#855700, #dfb26c);
47
+ --app-color-unverified-soft: light-dark(#faf2df, #30291c);
48
+
49
+ /*
50
+ * 내장 글꼴 · CLI는 @font-face와 자원 URL 생성
51
+ * 본문·라벨 Pretendard · 코드·흐름도 JetBrains Mono · 사이트 이름 Architects Daughter
52
+ * 영문 폭 0.6em = 흐름도 12px 격자의 7.2px
53
+ */
54
+ --app-font-sans: "Pretendard Variable", system-ui, "Apple SD Gothic Neo", sans-serif;
55
+ --app-font-mono: "JetBrains Mono Variable", var(--app-font-sans);
56
+ --app-font-brand: "Architects Daughter", var(--app-font-sans);
57
+
58
+ /* 글자 크기 · 12px 기본과 역할별 제목 크기 */
59
+ --app-font-size-label: 12px;
60
+ --app-font-size-detail: 12px;
61
+ --app-font-size-dense: 12px;
62
+ --app-font-size-body: 12px;
63
+ --app-font-size-lead: 12px;
64
+ --app-font-size-part: 13px;
65
+ --app-font-size-section: 14px;
66
+ --app-font-size-title: 16px;
67
+
68
+ /* 굵기 · 기본 400 · 현재 위치 500 · 제목·강조 600 */
69
+ --app-font-weight-mark: 400;
70
+ --app-font-weight-active: 500;
71
+ --app-font-weight-strong: 600;
72
+ --app-font-weight-title: 600;
73
+
74
+ /* 행간 · 작은 제목·탐색은 1.5, 본문은 한글 획 사이 여유 확보 */
75
+ --app-line-height-title: 1.5;
76
+ --app-line-height-heading: 1.5;
77
+ --app-line-height-tight: 1.5;
78
+ --app-line-height-code: 1.65;
79
+ --app-line-height-body: 1.7;
80
+
81
+ /* 자간 · 12px 본문·라벨은 기본 폭, 제목만 미세하게 조임 */
82
+ --app-letter-spacing-label: 0;
83
+ --app-letter-spacing-brand: 0;
84
+ --app-letter-spacing-heading: -0.01em;
85
+ --app-letter-spacing-title: -0.015em;
86
+
87
+ /* 공통 간격 · margin·padding·gap의 리듬 · 단독 보정은 지역 값 */
88
+ --app-space-inline: 6px;
89
+ --app-space-stack: 10px;
90
+ --app-space-block: 14px;
91
+ --app-space-group: 20px;
92
+ --app-space-section: 32px;
93
+
94
+ /* 탐색 트리 · 항목 위아래 여백·묶음 안쪽 여백·가지와 중첩 들여쓰기 */
95
+ --app-space-tree-row: 2px;
96
+ --app-space-tree-inset: 4px;
97
+ --app-space-tree-indent: 12px;
98
+
99
+ /* 페이지 틀 · 좁은 화면 값은 아래 @media */
100
+ --app-space-page-top: 28px;
101
+ --app-space-gutter: 28px;
102
+ --app-space-column: 32px;
103
+ --app-space-sidebar-gap: 32px;
104
+ --app-size-sidebar: 200px;
105
+ --app-size-main: 900px;
106
+ --app-size-page: calc(var(--app-size-main) + (var(--app-size-sidebar) + var(--app-space-sidebar-gap) + var(--app-space-gutter)) * 2);
107
+ --app-size-brand: calc(var(--app-font-size-title) * var(--app-line-height-title) + var(--app-space-page-top) + var(--app-space-group));
108
+ --app-size-mobile-header: 56px;
109
+ --app-size-menu-button: 44px;
110
+ --app-size-drawer: 320px;
111
+
112
+ /* 화면 위쪽 제목 여백 · hash 도착점과 목차 읽는 선의 공통 기준 */
113
+ --app-space-anchor: 80px;
114
+
115
+ /* 모서리 · 직각 기본값 */
116
+ --app-radius-pill: 0;
117
+ --app-radius-card: 0;
118
+ --app-radius-box: 0;
119
+ --app-radius-inline: 0;
120
+ --app-radius-mark: 0;
121
+
122
+ /* 키보드 포커스 표시 */
123
+ --app-outline-focus: 2px solid var(--app-color-link);
124
+ --app-outline-focus-offset: 2px;
125
+
126
+ /* 사이드바 사이트 이름 · 커서 장식 */
127
+ --app-z-index-sticky: 100;
128
+ --app-z-index-popper: 300;
129
+
130
+ /*
131
+ * Shiki CSS 변수 · 기존 역할색 재사용
132
+ * 키워드 strong · 함수 accent
133
+ */
134
+ --app-code-foreground: var(--app-color-text);
135
+ --app-code-background: var(--app-color-code);
136
+ --app-code-token-keyword: var(--app-color-text-strong);
137
+ --app-code-token-function: var(--app-color-accent);
138
+ --app-code-token-string: var(--app-color-verified);
139
+ --app-code-token-string-expression: var(--app-color-verified);
140
+ --app-code-token-constant: var(--app-color-unverified);
141
+ --app-code-token-parameter: var(--app-color-text);
142
+ --app-code-token-comment: var(--app-color-text-muted);
143
+ --app-code-token-punctuation: var(--app-color-text-muted);
144
+ --app-code-token-link: var(--app-color-link);
145
+ }
146
+
147
+ /* 폭 조건 · shell의 breakpoint와 일치 */
148
+ @media (width < 1024px) {
149
+ :root {
150
+ --app-space-page-top: 24px;
151
+ --app-space-gutter: 20px;
152
+ --app-space-tree-row: 7px;
153
+ --app-space-anchor: calc(var(--app-size-mobile-header) + var(--app-space-block));
154
+ }
155
+ }
156
+
157
+ @media (width < 640px) {
158
+ :root {
159
+ --app-space-page-top: 20px;
160
+ --app-space-gutter: 16px;
161
+ }
162
+ }
@@ -0,0 +1,15 @@
1
+ import type {DocOutline} from "@/component/widget/shell/_type/doc-outline";
2
+
3
+ /**
4
+ * README 홈과 일반 문서가 공유하는 렌더링 결과
5
+ */
6
+ export interface DocContent {
7
+ /**
8
+ * 문서 머리와 본문을 포함한 HTML
9
+ */
10
+ html: string;
11
+ /**
12
+ * 현재 문서의 독립 목차 입력
13
+ */
14
+ outline: DocOutline;
15
+ }
@@ -0,0 +1,33 @@
1
+ import {z} from "zod";
2
+ import {doc_order_default, doc_type} from "@/constant/doc";
3
+
4
+ /**
5
+ * 문서 frontmatter 스키마 · 검증 실패 시 빌드 중단
6
+ */
7
+ export const docDataSchema = z.object({
8
+ /**
9
+ * 문서 이름 · 제목·목록 · 같은 첫 글자 허용
10
+ */
11
+ name: z.string().trim().min(1),
12
+ /**
13
+ * 짧은 문서 설명 · 사이드바 툴팁
14
+ */
15
+ label: z.string(),
16
+ /**
17
+ * 문서 종류 · blueprint 전용 표현 미구현
18
+ */
19
+ type: z.enum(doc_type).default(doc_type.document),
20
+ /**
21
+ * 사이드바의 문서 묶음
22
+ */
23
+ group: z.string().trim().min(1),
24
+ /**
25
+ * 묶음 안의 읽는 순서 · 미지정 문서는 뒤에서 이름순
26
+ */
27
+ order: z.number().int().nonnegative().default(doc_order_default),
28
+ });
29
+
30
+ /**
31
+ * 기본값을 적용한 문서 frontmatter
32
+ */
33
+ export type DocData = z.infer<typeof docDataSchema>;
@@ -0,0 +1,21 @@
1
+ import type {DocSection} from "@/component/widget/prose/_type/doc-section";
2
+ import type {DocData} from "@/type/doc-data";
3
+ import type {DocHeading} from "@/type/doc-heading";
4
+
5
+ /**
6
+ * 문서 처리 중간 계약 · read-docs의 머리말과 rehype의 제목·절 정보
7
+ */
8
+ export interface DocFileData {
9
+ /**
10
+ * 일반 문서의 검증된 머리말 · README 홈은 생략
11
+ */
12
+ frontmatter?: DocData;
13
+ /**
14
+ * 작성한 제목 · 문서 순서
15
+ */
16
+ headings: DocHeading[];
17
+ /**
18
+ * 제목별 가름 · headings의 h2·h3 순서와 일치
19
+ */
20
+ sections: DocSection[];
21
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 제목 하나의 목차 계약 · 문서 순서
3
+ */
4
+ export interface DocHeading {
5
+ /**
6
+ * 제목 깊이 · h1 = 1, h2 = 2
7
+ */
8
+ depth: number;
9
+ /**
10
+ * 제목 id · URL hash 대상
11
+ */
12
+ slug: string;
13
+ /**
14
+ * 작성한 제목 텍스트
15
+ */
16
+ text: string;
17
+ }
@@ -0,0 +1,16 @@
1
+ import type {DocContent} from "@/type/doc-content";
2
+ import type {DocData} from "@/type/doc-data";
3
+
4
+ /**
5
+ * Markdown 파일 하나의 렌더링 결과
6
+ */
7
+ export interface Doc extends DocContent {
8
+ /**
9
+ * 문서 URL id · 확장자 제외 소문자 경로 · 하위 폴더 포함
10
+ */
11
+ id: string;
12
+ /**
13
+ * 검증된 머리말
14
+ */
15
+ data: DocData;
16
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 모든 페이지가 공유하는 HTML 머리 자원
3
+ */
4
+ export interface SiteAssets {
5
+ /**
6
+ * 내장 @font-face · head의 style 내용
7
+ */
8
+ fontCss: string;
9
+ /**
10
+ * 선요청 글꼴 URL · 사이트 이름·본문 공통 조각·코드
11
+ */
12
+ preload: string[];
13
+ /**
14
+ * dev 전용 문서 새로고침 스크립트 포함 여부
15
+ */
16
+ reload: boolean;
17
+ }
@@ -0,0 +1,55 @@
1
+ import {z} from "zod";
2
+ import {copy_error_navigation_clash, copy_site_title_default} from "@/constant/copy";
3
+ import {site_navigation_default} from "@/constant/site";
4
+ import {status_default_phrases, status_kind} from "@/constant/status";
5
+
6
+ /**
7
+ * for-humanity.config.mjs 설정 스키마
8
+ * 명령 시작 시 검증과 기본값 적용 · 앱은 검증 결과만 사용
9
+ */
10
+ export const siteConfigSchema = z.object({
11
+ /**
12
+ * 사이드바 이름과 탭 제목
13
+ */
14
+ title: z.string().default(copy_site_title_default),
15
+ /**
16
+ * HTML 설명 메타데이터 · 선택
17
+ */
18
+ description: z.string().optional(),
19
+ /**
20
+ * 탐색 묶음의 읽는 순서 · 미지정 묶음은 뒤에서 이름순
21
+ */
22
+ navigation: z
23
+ .array(z.string().trim().min(1))
24
+ .refine((groups) => new Set(groups).size === groups.length, copy_error_navigation_clash)
25
+ .default([...site_navigation_default]),
26
+ /**
27
+ * 본문에서 상태 표지로 표시할 문구
28
+ */
29
+ status: z
30
+ .array(
31
+ z.object({
32
+ /**
33
+ * 찾을 문구 원문
34
+ */
35
+ phrase: z
36
+ .string()
37
+ .min(1)
38
+ .refine((phrase) => phrase.trim().length > 0),
39
+ /**
40
+ * 상태 표지의 색 종류
41
+ */
42
+ kind: z.enum(status_kind),
43
+ /**
44
+ * 문구 뒤의 YYYY-MM-DD 날짜 포함 여부
45
+ */
46
+ date: z.boolean().optional(),
47
+ }),
48
+ )
49
+ .default(status_default_phrases),
50
+ });
51
+
52
+ /**
53
+ * 기본값을 적용한 사이트 설정
54
+ */
55
+ export type SiteConfig = z.infer<typeof siteConfigSchema>;
@@ -0,0 +1,10 @@
1
+ import type {DocFileData} from "@/type/doc-file-data";
2
+
3
+ declare module "vfile" {
4
+ interface DataMap {
5
+ /**
6
+ * 문서 처리 플러그인 간 공유 데이터
7
+ */
8
+ fh?: DocFileData;
9
+ }
10
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * URL hash 대상 · 한국어 제목의 percent encoding 해석
3
+ */
4
+ export const findHashTarget = (hash: string): HTMLElement | null => {
5
+ if (!hash) {
6
+ return null;
7
+ }
8
+
9
+ try {
10
+ return document.getElementById(decodeURIComponent(hash.slice(1)));
11
+ } catch (error) {
12
+ if (error instanceof URIError) {
13
+ return null;
14
+ }
15
+
16
+ throw error;
17
+ }
18
+ };
@@ -0,0 +1,15 @@
1
+ import {findHashTarget} from "@/util/dom/find-hash-target";
2
+
3
+ /**
4
+ * 접힌 내용의 hash 대상과 상위 Details 공개
5
+ * 초기 접속·문서 안 링크·hash 변경에 동일 적용
6
+ */
7
+ export const revealHashTarget = (hash: string): HTMLElement | null => {
8
+ const target = findHashTarget(hash);
9
+
10
+ for (let fold = target?.closest("details"); fold; fold = fold.parentElement?.closest("details")) {
11
+ fold.open = true;
12
+ }
13
+
14
+ return target;
15
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * CLI·dev 공통 오류 문구 · 문법 오류의 파일과 줄 번호 포함
3
+ */
4
+ export const toErrorMessage = (error: unknown): string => {
5
+ if (error instanceof Error && "file" in error && typeof error.file === "string" && "line" in error && typeof error.line === "number") {
6
+ return `${error.file}:${error.line}: ${error.message}`;
7
+ }
8
+
9
+ return error instanceof Error ? error.message : String(error);
10
+ };
@@ -0,0 +1,41 @@
1
+ import assert from "node:assert/strict";
2
+ import {mkdtempSync, rmSync, writeFileSync} from "node:fs";
3
+ import {tmpdir} from "node:os";
4
+ import {join} from "node:path";
5
+ import {test} from "node:test";
6
+ import {asset_font_dir} from "@/constant/asset";
7
+ import {font_brand_css, font_mono_css, font_sans_css} from "@/constant/font";
8
+ import {toFontCss} from "@/util/font/to-font-css";
9
+
10
+ test("bundled fonts prevent a late swap after fallback text is visible", () => {
11
+ for (const css of [font_sans_css, font_mono_css, font_brand_css]) {
12
+ const result = toFontCss({css: join(process.cwd(), css), fontDir: asset_font_dir});
13
+
14
+ assert.match(result.css, /font-display:\s*optional;/);
15
+ assert.doesNotMatch(result.css, /font-display:\s*swap;/);
16
+ }
17
+ });
18
+
19
+ test("font URLs stay stable for unchanged bytes and change when the font changes", (t) => {
20
+ const root = mkdtempSync(join(tmpdir(), "for-humanity-font-"));
21
+ const file = join(root, "example.woff2");
22
+ const css = join(root, "font.css");
23
+
24
+ t.after(() => rmSync(root, {recursive: true, force: true}));
25
+ writeFileSync(css, "@font-face { font-family: Example; font-display: swap; src: url(./example.woff2); }");
26
+ writeFileSync(file, "first font bytes");
27
+
28
+ const first = toFontCss({css, fontDir: asset_font_dir});
29
+ const repeated = toFontCss({css, fontDir: asset_font_dir});
30
+
31
+ assert.deepEqual([...first.files], [...repeated.files]);
32
+ assert.match(first.css, /example\.[a-f0-9]+\.woff2/);
33
+ assert.deepEqual([...first.files.values()], [file]);
34
+
35
+ writeFileSync(file, "changed font bytes");
36
+
37
+ const changed = toFontCss({css, fontDir: asset_font_dir});
38
+
39
+ assert.notDeepEqual([...first.files.keys()], [...changed.files.keys()]);
40
+ assert.notEqual(first.css, changed.css);
41
+ });
@@ -0,0 +1,28 @@
1
+ import {createHash} from "node:crypto";
2
+ import {readFileSync} from "node:fs";
3
+ import {basename, dirname, extname, join} from "node:path";
4
+ import {font_display_strategy, font_hash_length} from "@/constant/font";
5
+
6
+ /**
7
+ * 내장 @font-face의 표시 정책과 내용 지문 URL 적용 · 원본 파일 수집
8
+ * 늦은 글꼴 교체와 문서 이동마다 같은 글꼴을 다시 받는 현상 방지
9
+ */
10
+ export const toFontCss = (options: {css: string; fontDir: string}) => {
11
+ const files = new Map<string, string>();
12
+ const css = [...readFileSync(options.css, "utf8").matchAll(/@font-face\s*\{[^}]*\}/g)]
13
+ .map((match) =>
14
+ match[0].replace(/font-display:\s*[^;]+;/g, `font-display: ${font_display_strategy};`).replace(/url\(([^)]+)\)/g, (_match: string, relativeUrl: string) => {
15
+ const file = join(dirname(options.css), relativeUrl);
16
+ const extension = extname(file);
17
+ const hash = createHash("sha256").update(readFileSync(file)).digest("hex").slice(0, font_hash_length);
18
+ const url = `${options.fontDir}/${basename(file, extension)}.${hash}${extension}`;
19
+
20
+ files.set(url, file);
21
+
22
+ return `url(${url})`;
23
+ }),
24
+ )
25
+ .join("\n");
26
+
27
+ return {css, files};
28
+ };