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,261 @@
1
+ /*
2
+ * 넓은 화면은 본문 중앙 정렬 · 폭이 줄면 오른쪽 여백부터 축소
3
+ */
4
+ .wg_shell__root {
5
+ display: grid;
6
+ max-width: var(--app-size-page);
7
+ margin: 0 auto;
8
+ padding: 0 var(--app-space-gutter);
9
+ grid-template: "nav main ." 1fr / calc(var(--app-size-sidebar) + var(--app-space-sidebar-gap)) minmax(0, var(--app-size-main)) minmax(0, 1fr);
10
+ background: var(--app-color-ground);
11
+ color: var(--app-color-text);
12
+ font-family: var(--app-font-sans);
13
+ font-size: var(--app-font-size-body);
14
+ line-height: var(--app-line-height-body);
15
+
16
+ /* 한국어 낱말 단위 줄바꿈 · 긴 식별자만 예외 */
17
+ word-break: keep-all;
18
+ overflow-wrap: break-word;
19
+ text-wrap: pretty;
20
+ -webkit-font-smoothing: antialiased;
21
+ }
22
+
23
+ .wg_shell__root--drawerOpen {
24
+ overflow: hidden;
25
+ }
26
+
27
+ .wg_shell__brandRow {
28
+ display: flex;
29
+
30
+ /* 공유 사이드바의 스크롤 기준 · 목록과 목차 위에서 이름 고정 */
31
+ position: sticky;
32
+ top: 0;
33
+ z-index: var(--app-z-index-sticky);
34
+ align-self: start;
35
+ min-width: 0;
36
+ height: var(--app-size-brand);
37
+ padding-block: var(--app-space-page-top) var(--app-space-group);
38
+ background: var(--app-color-ground);
39
+ gap: var(--app-space-inline);
40
+ align-items: start;
41
+ }
42
+
43
+ .wg_shell__brand {
44
+ display: block;
45
+ flex: 1;
46
+ min-width: 0;
47
+ overflow: hidden;
48
+ color: var(--app-color-text-strong);
49
+ font-family: var(--app-font-brand);
50
+ font-size: var(--app-font-size-title);
51
+ font-weight: var(--app-font-weight-mark);
52
+ line-height: var(--app-line-height-title);
53
+ letter-spacing: var(--app-letter-spacing-brand);
54
+ text-decoration: none;
55
+ text-overflow: ellipsis;
56
+ white-space: nowrap;
57
+
58
+ &:hover {
59
+ text-decoration: underline;
60
+ }
61
+
62
+ &:focus-visible {
63
+ /* 브랜드도 hover와 같은 밑줄로 키보드 위치 표시 */
64
+ outline: none;
65
+ text-decoration: underline;
66
+ }
67
+ }
68
+
69
+ .wg_shell__menuIcon {
70
+ width: 20px;
71
+ height: 20px;
72
+ fill: none;
73
+ stroke: currentcolor;
74
+ stroke-linecap: round;
75
+ stroke-width: 1.5;
76
+ }
77
+
78
+ .wg_shell__menuToggle {
79
+ display: none;
80
+ align-items: center;
81
+ justify-content: center;
82
+ flex: none;
83
+ width: var(--app-size-menu-button);
84
+ height: var(--app-size-menu-button);
85
+ padding: 0;
86
+ border: 0;
87
+ border-radius: var(--app-radius-box);
88
+ background: transparent;
89
+ color: var(--app-color-text-strong);
90
+ cursor: pointer;
91
+
92
+ &:hover {
93
+ background: var(--app-color-accent-soft);
94
+ }
95
+
96
+ &:focus-visible {
97
+ outline: var(--app-outline-focus);
98
+ outline-offset: var(--app-outline-focus-offset);
99
+ }
100
+ }
101
+
102
+ .wg_shell__menuToggle--visible {
103
+ display: flex;
104
+ }
105
+
106
+ .wg_shell__sidebar {
107
+ grid-area: nav;
108
+
109
+ /* 뷰포트 sticky · 문서 탐색과 목차는 이 컨테이너에서 함께 스크롤 */
110
+ position: sticky;
111
+ top: 0;
112
+ align-self: start;
113
+ min-width: 0;
114
+ min-height: 100vh;
115
+ max-height: 100vh;
116
+ margin-inline-end: var(--app-space-sidebar-gap);
117
+ overflow-y: auto;
118
+ scroll-padding-block: var(--app-size-brand) var(--app-space-block);
119
+ }
120
+
121
+ .wg_shell__navigation {
122
+ min-width: 0;
123
+ }
124
+
125
+ .wg_shell__main {
126
+ grid-area: main;
127
+ min-width: 0;
128
+ max-width: var(--app-size-main);
129
+ border-inline: 1px solid var(--app-color-border-layout);
130
+
131
+ /* 문서 목록의 시작선 · 마지막 절도 읽는 선까지 이동 가능한 끝 여백 */
132
+ padding-block: var(--app-size-brand) calc(100vh - var(--app-space-anchor));
133
+ padding-inline: var(--app-space-column);
134
+ }
135
+
136
+ .wg_shell__drawer {
137
+ /* 네이티브 모달의 top layer · 뷰포트 왼쪽에 고정 */
138
+ position: fixed;
139
+ inset: 0 auto 0 0;
140
+ width: min(var(--app-size-drawer), calc(100% - var(--app-space-gutter) * 2));
141
+ max-width: none;
142
+ height: 100dvh;
143
+ max-height: 100dvh;
144
+ margin: 0;
145
+ padding: 0 var(--app-space-gutter) var(--app-space-group);
146
+ border: 0;
147
+ border-right: 1px solid var(--app-color-border-layout);
148
+ background: var(--app-color-ground);
149
+ color: var(--app-color-text);
150
+ overscroll-behavior: contain;
151
+ scroll-padding-block: var(--app-size-mobile-header) var(--app-space-group);
152
+
153
+ &::backdrop {
154
+ background: var(--app-color-backdrop);
155
+ }
156
+ }
157
+
158
+ .wg_shell__drawerHeader {
159
+ /* 스크롤하는 drawer 안에서 닫기 버튼 유지 */
160
+ position: sticky;
161
+ top: 0;
162
+ z-index: var(--app-z-index-sticky);
163
+ display: flex;
164
+ align-items: center;
165
+ justify-content: space-between;
166
+ gap: var(--app-space-inline);
167
+ min-height: var(--app-size-mobile-header);
168
+ margin-bottom: var(--app-space-block);
169
+ background: var(--app-color-ground);
170
+ }
171
+
172
+ .wg_shell__drawerTitle {
173
+ margin: 0;
174
+ font-size: var(--app-font-size-part);
175
+ font-weight: var(--app-font-weight-strong);
176
+ }
177
+
178
+ .wg_shell__drawerClose {
179
+ display: flex;
180
+ align-items: center;
181
+ justify-content: center;
182
+ flex: none;
183
+ width: var(--app-size-menu-button);
184
+ height: var(--app-size-menu-button);
185
+ padding: 0;
186
+ border: 0;
187
+ border-radius: var(--app-radius-box);
188
+ background: transparent;
189
+ color: var(--app-color-text-strong);
190
+ cursor: pointer;
191
+
192
+ &:hover {
193
+ background: var(--app-color-accent-soft);
194
+ }
195
+
196
+ &:focus-visible {
197
+ outline: var(--app-outline-focus);
198
+ outline-offset: var(--app-outline-focus-offset);
199
+ }
200
+ }
201
+
202
+ .wg_shell__drawerContent {
203
+ min-width: 0;
204
+ }
205
+
206
+ .wg_shell__cursorFace {
207
+ /* 뷰포트 기준 · 본문 흐름과 포인터 입력에 참여하지 않는 장식 */
208
+ position: fixed;
209
+ top: 0;
210
+ left: 0;
211
+ z-index: var(--app-z-index-popper);
212
+ visibility: hidden;
213
+ pointer-events: none;
214
+ user-select: none;
215
+ image-rendering: pixelated;
216
+ }
217
+
218
+ .wg_shell__cursorFace--visible {
219
+ visibility: visible;
220
+ }
221
+
222
+ /* 좁은 화면 · 상단 브랜드와 메뉴 버튼 · 탐색 목록은 네이티브 모달 */
223
+ @media (width < 1024px) {
224
+ .wg_shell__root {
225
+ max-width: calc(var(--app-size-main) + var(--app-space-gutter) * 2);
226
+ grid-template:
227
+ "nav" auto
228
+ "main" 1fr / minmax(0, 1fr);
229
+ }
230
+
231
+ .wg_shell__sidebar {
232
+ position: static;
233
+ min-height: 0;
234
+ max-height: none;
235
+ overflow-y: visible;
236
+ margin-block-end: var(--app-space-block);
237
+ margin-inline-end: 0;
238
+ border-bottom: 1px solid var(--app-color-border-layout);
239
+ }
240
+
241
+ .wg_shell__sidebar--navigationReady {
242
+ /* 본문을 스크롤해도 메뉴 진입 유지 · 스크립트 없는 긴 목록에는 적용하지 않음 */
243
+ position: sticky;
244
+ top: 0;
245
+ z-index: var(--app-z-index-sticky);
246
+ background: var(--app-color-ground);
247
+ }
248
+
249
+ .wg_shell__brandRow {
250
+ position: static;
251
+ height: var(--app-size-mobile-header);
252
+ padding-block: 0;
253
+ align-items: center;
254
+ }
255
+
256
+ .wg_shell__main {
257
+ padding-block-start: var(--app-space-page-top);
258
+ padding-inline: 0;
259
+ border-inline: 0;
260
+ }
261
+ }
@@ -0,0 +1,147 @@
1
+ import clsx from "clsx";
2
+ import type {ReactNode} from "react";
3
+ import {toTocGroups} from "@/component/widget/shell/_function/to-toc-groups";
4
+ import type {DocOutline} from "@/component/widget/shell/_type/doc-outline";
5
+ import {WgShellNav} from "@/component/widget/shell/_wg-shell-nav";
6
+ import {WgShellToc} from "@/component/widget/shell/_wg-shell-toc";
7
+ import {asset_client_path, asset_favicon_path, asset_reload_path, asset_style_path} from "@/constant/asset";
8
+ import {copy_nav_close, copy_nav_open, copy_nav_title} from "@/constant/copy";
9
+ import type {Doc} from "@/type/doc";
10
+ import type {SiteAssets} from "@/type/site-assets";
11
+ import type {SiteConfig} from "@/type/site-config";
12
+ import "@/style/base.css";
13
+ import "@/style/token.css";
14
+ import "./wg-shell.css";
15
+
16
+ /**
17
+ * dev 문서 변경 시 SSE 새로고침
18
+ */
19
+ const reloadScript = `new EventSource(${JSON.stringify(asset_reload_path)}).onmessage=function(){location.reload()}`;
20
+
21
+ /**
22
+ * 공통 HTML 틀의 입력 · 문서 탐색·현재 목차·본문
23
+ */
24
+ export interface WgShellProps {
25
+ /**
26
+ * 사이트 이름 · 탭과 사이드바
27
+ */
28
+ site: SiteConfig;
29
+ /**
30
+ * 사이드바 전체 문서 목록
31
+ */
32
+ docs: Doc[];
33
+ /**
34
+ * HTML 머리의 글꼴·dev 스크립트
35
+ */
36
+ assets: SiteAssets;
37
+ /**
38
+ * 탭 제목의 문서 이름 · 첫 화면 생략
39
+ */
40
+ title?: string;
41
+ /**
42
+ * 현재 문서 id · 첫 화면 생략
43
+ */
44
+ current?: string;
45
+ /**
46
+ * 현재 문서 또는 README 홈의 목차
47
+ */
48
+ outline?: DocOutline;
49
+ /**
50
+ * 현재 페이지 본문
51
+ */
52
+ children: ReactNode;
53
+ }
54
+
55
+ export const WgShell = (props: WgShellProps) => {
56
+ const tocGroups = props.outline === undefined ? [] : toTocGroups(props.outline);
57
+
58
+ return (
59
+ <html lang="ko">
60
+ {/**
61
+ * 공통 자원 · 초기 smooth scroll은 client.ts의 hash 위치 보정 후 복원
62
+ */}
63
+ <head>
64
+ <meta charSet="utf-8" />
65
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
66
+ {props.site.description !== undefined && <meta name="description" content={props.site.description} />}
67
+ <title>{props.title === undefined ? props.site.title : `${props.title} · ${props.site.title}`}</title>
68
+ <link rel="icon" href={asset_favicon_path} type="image/svg+xml" />
69
+ <style dangerouslySetInnerHTML={{__html: props.assets.fontCss}} />
70
+ {props.assets.preload.map((href) => (
71
+ <link key={href} rel="preload" href={href} as="font" type="font/woff2" crossOrigin="" />
72
+ ))}
73
+ <script dangerouslySetInnerHTML={{__html: 'document.documentElement.style.scrollBehavior="auto"'}} />
74
+ <link rel="stylesheet" href={asset_style_path} />
75
+ {props.assets.reload && <script dangerouslySetInnerHTML={{__html: reloadScript}} />}
76
+ </head>
77
+ {/**
78
+ * 문서 탐색·현재 목차·본문 · 브라우저 동작 연결
79
+ */}
80
+ <body className={clsx("wg_shell__root")}>
81
+ {/**
82
+ * 데스크톱의 공유 사이드바 · 모바일에서는 브랜드와 메뉴 버튼
83
+ */}
84
+ <div className={clsx("wg_shell__sidebar")}>
85
+ {/**
86
+ * 브랜드와 모바일 메뉴 진입 · 버튼은 동작 연결 후 표시
87
+ */}
88
+ <div className={clsx("wg_shell__brandRow")}>
89
+ {/**
90
+ * 어느 문서에서도 README 홈으로 이동
91
+ */}
92
+ <a className={clsx("wg_shell__brand")} href="/" aria-current={props.current === undefined ? "page" : undefined}>
93
+ {props.site.title}
94
+ </a>
95
+ {/**
96
+ * 접힌 모바일 탐색의 진입점과 열림 상태 전달
97
+ */}
98
+ <button
99
+ className={clsx("wg_shell__menuToggle")}
100
+ type="button"
101
+ aria-label={copy_nav_open}
102
+ aria-controls="fh-navigation-drawer"
103
+ aria-expanded="false"
104
+ data-navigation-open=""
105
+ >
106
+ <svg className={clsx("wg_shell__menuIcon")} viewBox="0 0 24 24" aria-hidden="true" focusable="false">
107
+ <path d="M4 6h16M4 12h16M4 18h16" />
108
+ </svg>
109
+ </button>
110
+ </div>
111
+ {/**
112
+ * 같은 탐색 DOM을 모바일 대화상자로 이동 · 스크립트 없이도 기본 목록 제공
113
+ */}
114
+ <div className={clsx("wg_shell__navigation")} data-navigation="">
115
+ <WgShellNav site={props.site} docs={props.docs} current={props.current} />
116
+ {tocGroups.length > 0 && <WgShellToc groups={tocGroups} />}
117
+ </div>
118
+ </div>
119
+ <main className={clsx("wg_shell__main")}>{props.children}</main>
120
+ {/**
121
+ * 브라우저의 모달 포커스·Escape 처리 · 목록은 한 번만 렌더링
122
+ */}
123
+ <dialog className={clsx("wg_shell__drawer")} id="fh-navigation-drawer" aria-labelledby="fh-navigation-title" data-navigation-drawer="">
124
+ <div className={clsx("wg_shell__drawerHeader")}>
125
+ {/**
126
+ * 대화상자의 접근 가능한 이름
127
+ */}
128
+ <h2 className={clsx("wg_shell__drawerTitle")} id="fh-navigation-title">
129
+ {copy_nav_title}
130
+ </h2>
131
+ {/**
132
+ * 목록을 스크롤해도 보이는 닫기 동작
133
+ */}
134
+ <button className={clsx("wg_shell__drawerClose")} type="button" aria-label={copy_nav_close} data-navigation-close="">
135
+ <svg className={clsx("wg_shell__menuIcon")} viewBox="0 0 24 24" aria-hidden="true" focusable="false">
136
+ <path d="m6 6 12 12M18 6 6 18" />
137
+ </svg>
138
+ </button>
139
+ </div>
140
+ <div className={clsx("wg_shell__drawerContent")} data-navigation-content="" />
141
+ </dialog>
142
+ <img className={clsx("wg_shell__cursorFace")} src={asset_favicon_path} width="24" height="24" alt="" aria-hidden="true" draggable={false} data-cursor-face="" />
143
+ <script type="module" src={asset_client_path} />
144
+ </body>
145
+ </html>
146
+ );
147
+ };
@@ -0,0 +1,48 @@
1
+ /*
2
+ * 정적 빌드와 dev의 공통 자원 URL
3
+ */
4
+
5
+ /**
6
+ * 문서 경로와 구분한 자원 폴더
7
+ */
8
+ export const asset_dir = "_fh";
9
+
10
+ /**
11
+ * esbuild의 컴포넌트 CSS 묶음
12
+ */
13
+ export const asset_style_path = `/${asset_dir}/style.css`;
14
+
15
+ /**
16
+ * 테마·목차·hash 보정 브라우저 스크립트
17
+ */
18
+ export const asset_client_path = `/${asset_dir}/client.js`;
19
+
20
+ /**
21
+ * 자체 호스팅 글꼴 폴더
22
+ */
23
+ export const asset_font_dir = `/${asset_dir}/fonts`;
24
+
25
+ /**
26
+ * dev 문서 변경 알림 SSE URL
27
+ */
28
+ export const asset_reload_path = `/${asset_dir}/reload`;
29
+
30
+ /**
31
+ * 탭 아이콘 URL
32
+ */
33
+ export const asset_favicon_path = "/favicon.svg";
34
+
35
+ /**
36
+ * dev 자원 응답의 확장자별 content-type
37
+ */
38
+ export const asset_content_types: Record<string, string> = {
39
+ ".css": "text/css; charset=utf-8",
40
+ ".js": "text/javascript; charset=utf-8",
41
+ ".woff2": "font/woff2",
42
+ ".svg": "image/svg+xml",
43
+ };
44
+
45
+ /**
46
+ * 미등록 확장자의 content-type
47
+ */
48
+ export const asset_content_type_default = "application/octet-stream";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 명령 생략 시 dev
3
+ */
4
+ export const cli_default_command = "dev";
5
+
6
+ /**
7
+ * 문서 폴더 생략 시 현재 폴더
8
+ */
9
+ export const cli_default_docs_dir = ".";
10
+
11
+ /**
12
+ * 문서 폴더의 선택 설정 파일
13
+ */
14
+ export const cli_config_file_name = "for-humanity.config.mjs";
15
+
16
+ /**
17
+ * dev·preview 공통 포트
18
+ */
19
+ export const cli_dev_port = 4321;
@@ -0,0 +1,108 @@
1
+ /*
2
+ * 화면·CLI 공통 문구 · 번역 분리의 단일 출처
3
+ */
4
+
5
+ /**
6
+ * 사이트 제목의 기본값 · 사이드바와 탭
7
+ */
8
+ export const copy_site_title_default = "for humanity";
9
+
10
+ /**
11
+ * 사이드바의 접근 가능한 이름
12
+ */
13
+ export const copy_nav_aria_label = "Document navigation";
14
+
15
+ /**
16
+ * 모바일 문서 탐색 열기 버튼
17
+ */
18
+ export const copy_nav_open = "Open navigation";
19
+
20
+ /**
21
+ * 모바일 문서 탐색 닫기 버튼
22
+ */
23
+ export const copy_nav_close = "Close navigation";
24
+
25
+ /**
26
+ * 모바일 문서 탐색 대화상자 제목
27
+ */
28
+ export const copy_nav_title = "Navigation";
29
+
30
+ /**
31
+ * README가 없는 홈의 안내
32
+ */
33
+ export const copy_home_missing = "문서 폴더에 README.md를 추가하면 이곳에 홈 화면으로 표시됩니다.";
34
+
35
+ /**
36
+ * 현재 문서 목차 라벨
37
+ */
38
+ export const copy_nav_toc_label = "Contents";
39
+
40
+ /**
41
+ * 대소문자만 다른 README 중복 오류
42
+ */
43
+ export const copy_error_home_clash = "홈 README가 겹친다";
44
+
45
+ /**
46
+ * CLI 오류 접두사
47
+ */
48
+ export const copy_error_prefix = "for-humanity";
49
+
50
+ /**
51
+ * 내장 본문 글꼴의 공통 조각이 누락된 패키지 오류
52
+ */
53
+ export const copy_error_font_preload = "Missing bundled font for preload";
54
+
55
+ /**
56
+ * 미지원 명령 오류
57
+ */
58
+ export const copy_error_unknown_command = "모르는 명령이다";
59
+
60
+ /**
61
+ * 설정 검증 오류 · 항목 경로와 이유 추가
62
+ */
63
+ export const copy_error_config = "설정이 틀렸다";
64
+
65
+ /**
66
+ * 문서 frontmatter 오류 · 파일과 항목 추가
67
+ */
68
+ export const copy_error_frontmatter = "frontmatter가 올바르지 않다";
69
+
70
+ /**
71
+ * 탐색 순서에 같은 묶음을 두 번 지정한 설정 오류
72
+ */
73
+ export const copy_error_navigation_clash = "navigation의 묶음이 겹친다";
74
+
75
+ /**
76
+ * 빈 id·예약 경로·URL 예약 문자 오류
77
+ */
78
+ export const copy_error_doc_id = "문서 id에 사용할 수 없는 경로";
79
+
80
+ /**
81
+ * 확장자·대소문자 정규화 후 문서 경로 중복
82
+ */
83
+ export const copy_error_doc_id_clash = "문서 id가 겹친다";
84
+
85
+ /**
86
+ * 흐름도 렌더링 실패
87
+ */
88
+ export const copy_error_flow = "흐름도를 그리지 못했다";
89
+
90
+ /**
91
+ * 미지원 블록 지시문 오류
92
+ */
93
+ export const copy_error_unknown_directive = "모르는 부품이다";
94
+
95
+ /**
96
+ * 부품의 작성 계약 오류 · 이름과 수정 기준 추가
97
+ */
98
+ export const copy_error_directive_syntax = "부품 문법이 올바르지 않다";
99
+
100
+ /**
101
+ * 없는 Markdown 링크 경고 · 빌드 계속
102
+ */
103
+ export const copy_warn_broken_link = "없는 문서로 건 링크다";
104
+
105
+ /**
106
+ * 미등록 날짜 상태 문구 경고
107
+ */
108
+ export const copy_warn_unknown_status = "설정에 없는 상태 문구다";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 문서 종류 · document 글 정리, blueprint 화면 설계
3
+ * blueprint 전용 표현 미구현
4
+ */
5
+ export const doc_type = {
6
+ document: "document",
7
+ blueprint: "blueprint",
8
+ } as const;
9
+
10
+ /**
11
+ * 순서 미지정 문서는 번호가 있는 문서 뒤에서 이름순 배치
12
+ */
13
+ export const doc_order_default = Number.MAX_SAFE_INTEGER;
@@ -0,0 +1,39 @@
1
+ /*
2
+ * 패키지 내장 글꼴 · OFL 라이선스는 각 폴더
3
+ * CLI에서 @font-face와 자원 생성 · font-family는 token.css 소유
4
+ */
5
+
6
+ /**
7
+ * 본문 글꼴 CSS · 패키지 기준 경로 · dynamic subset 92개
8
+ */
9
+ export const font_sans_css = "src/asset/font/pretendard/pretendardvariable-dynamic-subset.css";
10
+
11
+ /**
12
+ * 코드 글꼴 CSS · 라틴 subset · 한글은 본문 글꼴
13
+ */
14
+ export const font_mono_css = "src/asset/font/jetbrains-mono/jetbrains-mono.css";
15
+
16
+ /**
17
+ * 사이트 이름 글꼴 CSS · Architects Daughter Regular · 라틴 subset
18
+ */
19
+ export const font_brand_css = "src/asset/font/architects-daughter/architects-daughter.css";
20
+
21
+ /**
22
+ * 탐색·라틴 문자에 필요한 본문 조각 · 92개 전체 선요청 방지
23
+ */
24
+ export const font_sans_preload_file = "PretendardVariable.subset.91.woff2";
25
+
26
+ /**
27
+ * 늦게 받은 글꼴로 이미 보이는 대체 글꼴을 교체하지 않는 표시 정책
28
+ */
29
+ export const font_display_strategy = "optional";
30
+
31
+ /**
32
+ * 글꼴 파일 내용의 URL 지문 길이 · 파일 변경 시 이전 캐시와 분리
33
+ */
34
+ export const font_hash_length = 12;
35
+
36
+ /**
37
+ * 내용 지문이 붙은 글꼴만 장기 재사용 · CSS와 브라우저 스크립트는 대상 제외
38
+ */
39
+ export const font_cache_control = "public, max-age=31536000, immutable";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 영어 문서 이름의 정렬 locale
3
+ */
4
+ export const locale_doc_name = "en";
5
+
6
+ /**
7
+ * 한국어 문서 묶음의 정렬 locale
8
+ */
9
+ export const locale_doc_group = "ko";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 설정 파일 부재 시 입력 · 스키마 기본값 적용
3
+ */
4
+ export const site_config_absent = {};
5
+
6
+ /**
7
+ * 탐색 순서 생략 시 모든 묶음을 이름순 배치
8
+ */
9
+ export const site_navigation_default: readonly string[] = [];
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 상태 표지 종류 · verified 초록, unverified amber
3
+ */
4
+ export const status_kind = {
5
+ verified: "verified",
6
+ unverified: "unverified",
7
+ } as const;
8
+
9
+ /**
10
+ * 기본 상태 문구 · 확인됨은 뒤의 날짜 포함
11
+ */
12
+ export const status_default_phrases = [
13
+ {phrase: "확인됨", kind: status_kind.verified, date: true},
14
+ {phrase: "확인되지 않았다", kind: status_kind.unverified},
15
+ ];