for-humanity 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +1 -1
  3. package/README.md +3 -3
  4. package/dist/browser.js +9 -9
  5. package/dist/cli.css +39 -322
  6. package/dist/cli.js +123 -309
  7. package/package.json +4 -3
  8. package/src/asset/font/ibm-plex-sans-kr/IBMPlexSansKR-Regular.woff2 +0 -0
  9. package/src/asset/font/ibm-plex-sans-kr/IBMPlexSansKR-SemiBold.woff2 +0 -0
  10. package/src/asset/font/{pretendard → ibm-plex-sans-kr}/LICENSE.txt +4 -5
  11. package/src/asset/font/ibm-plex-sans-kr/ibm-plex-sans-kr.css +22 -0
  12. package/src/asset/font/{jetbrains-mono/LICENSE → monoplex-kr/LICENSE.md} +61 -8
  13. package/src/asset/font/monoplex-kr/MonoplexKR-Regular.woff2 +0 -0
  14. package/src/asset/font/monoplex-kr/MonoplexKR-SemiBold.woff2 +0 -0
  15. package/src/asset/font/monoplex-kr/monoplex-kr.css +20 -0
  16. package/src/component/widget/navigation/_constant/navigation-data-schema.ts +0 -3
  17. package/src/component/widget/navigation/_constant/navigation.ts +2 -2
  18. package/src/component/widget/navigation/_function/to-navigation-data/_to-navigation-document.ts +2 -3
  19. package/src/component/widget/navigation/_function/to-navigation-data/_to-navigation-group.ts +3 -4
  20. package/src/component/widget/navigation/_function/to-navigation-data/to-navigation-data.ts +6 -14
  21. package/src/component/widget/navigation/_type/navigation-data.ts +0 -12
  22. package/src/component/widget/navigation/_type/navigation-focus-snapshot.ts +0 -4
  23. package/src/component/widget/navigation/_wg-navigation-nav-list.css +5 -136
  24. package/src/component/widget/navigation/_wg-navigation-nav-list.tsx +10 -55
  25. package/src/component/widget/navigation/_wg-navigation-nav.css +2 -8
  26. package/src/component/widget/navigation/_wg-navigation-nav.tsx +1 -11
  27. package/src/component/widget/navigation/_wg-navigation-toc.css +12 -207
  28. package/src/component/widget/navigation/_wg-navigation-toc.tsx +39 -80
  29. package/src/component/widget/navigation/wg-navigation.css +1 -1
  30. package/src/component/widget/navigation/wg-navigation.tsx +10 -69
  31. package/src/component/widget/prose/wg-prose.css +7 -4
  32. package/src/component/widget/shell/wg-shell.css +7 -11
  33. package/src/constant/copy.ts +0 -5
  34. package/src/constant/font.ts +4 -9
  35. package/src/entry/browser/browser.tsx +1 -1
  36. package/src/entry/cli/cli.test.ts +6 -8
  37. package/src/entry/cli/cli.ts +7 -12
  38. package/src/store/navigation/create-navigation-stores.test.ts +10 -28
  39. package/src/store/navigation/create-navigation-stores.ts +4 -18
  40. package/src/store/navigation/navigation-state.ts +0 -10
  41. package/src/store/navigation/navigation-storage.ts +0 -5
  42. package/src/style/token.css +21 -20
  43. package/src/util/font/to-font-css.test.ts +2 -2
  44. package/src/util/mermaid/render-flow.js +2 -2
  45. package/src/asset/font/jetbrains-mono/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  46. package/src/asset/font/jetbrains-mono/jetbrains-mono.css +0 -11
  47. package/src/asset/font/pretendard/pretendardvariable-dynamic-subset.css +0 -1008
  48. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.0.woff2 +0 -0
  49. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.1.woff2 +0 -0
  50. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.10.woff2 +0 -0
  51. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.11.woff2 +0 -0
  52. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.12.woff2 +0 -0
  53. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.13.woff2 +0 -0
  54. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.14.woff2 +0 -0
  55. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.15.woff2 +0 -0
  56. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.16.woff2 +0 -0
  57. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.17.woff2 +0 -0
  58. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.18.woff2 +0 -0
  59. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.19.woff2 +0 -0
  60. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.2.woff2 +0 -0
  61. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.20.woff2 +0 -0
  62. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.21.woff2 +0 -0
  63. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.22.woff2 +0 -0
  64. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.23.woff2 +0 -0
  65. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.24.woff2 +0 -0
  66. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.25.woff2 +0 -0
  67. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.26.woff2 +0 -0
  68. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.27.woff2 +0 -0
  69. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.28.woff2 +0 -0
  70. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.29.woff2 +0 -0
  71. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.3.woff2 +0 -0
  72. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.30.woff2 +0 -0
  73. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.31.woff2 +0 -0
  74. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.32.woff2 +0 -0
  75. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.33.woff2 +0 -0
  76. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.34.woff2 +0 -0
  77. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.35.woff2 +0 -0
  78. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.36.woff2 +0 -0
  79. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.37.woff2 +0 -0
  80. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.38.woff2 +0 -0
  81. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.39.woff2 +0 -0
  82. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.4.woff2 +0 -0
  83. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.40.woff2 +0 -0
  84. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.41.woff2 +0 -0
  85. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.42.woff2 +0 -0
  86. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.43.woff2 +0 -0
  87. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.44.woff2 +0 -0
  88. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.45.woff2 +0 -0
  89. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.46.woff2 +0 -0
  90. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.47.woff2 +0 -0
  91. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.48.woff2 +0 -0
  92. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.49.woff2 +0 -0
  93. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.5.woff2 +0 -0
  94. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.50.woff2 +0 -0
  95. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.51.woff2 +0 -0
  96. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.52.woff2 +0 -0
  97. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.53.woff2 +0 -0
  98. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.54.woff2 +0 -0
  99. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.55.woff2 +0 -0
  100. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.56.woff2 +0 -0
  101. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.57.woff2 +0 -0
  102. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.58.woff2 +0 -0
  103. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.59.woff2 +0 -0
  104. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.6.woff2 +0 -0
  105. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.60.woff2 +0 -0
  106. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.61.woff2 +0 -0
  107. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.62.woff2 +0 -0
  108. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.63.woff2 +0 -0
  109. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.64.woff2 +0 -0
  110. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.65.woff2 +0 -0
  111. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.66.woff2 +0 -0
  112. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.67.woff2 +0 -0
  113. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.68.woff2 +0 -0
  114. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.69.woff2 +0 -0
  115. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.7.woff2 +0 -0
  116. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.70.woff2 +0 -0
  117. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.71.woff2 +0 -0
  118. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.72.woff2 +0 -0
  119. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.73.woff2 +0 -0
  120. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.74.woff2 +0 -0
  121. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.75.woff2 +0 -0
  122. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.76.woff2 +0 -0
  123. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.77.woff2 +0 -0
  124. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.78.woff2 +0 -0
  125. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.79.woff2 +0 -0
  126. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.8.woff2 +0 -0
  127. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.80.woff2 +0 -0
  128. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.81.woff2 +0 -0
  129. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.82.woff2 +0 -0
  130. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.83.woff2 +0 -0
  131. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.84.woff2 +0 -0
  132. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.85.woff2 +0 -0
  133. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.86.woff2 +0 -0
  134. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.87.woff2 +0 -0
  135. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.88.woff2 +0 -0
  136. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.89.woff2 +0 -0
  137. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.9.woff2 +0 -0
  138. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.90.woff2 +0 -0
  139. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.91.woff2 +0 -0
  140. package/src/component/widget/navigation/_function/to-navigation-branch-id.ts +0 -6
  141. package/src/component/widget/navigation/_wg-navigation-branch-toggle.css +0 -28
  142. package/src/component/widget/navigation/_wg-navigation-branch-toggle.tsx +0 -48
  143. package/src/store/navigation/_constant/tree.ts +0 -6
  144. package/src/store/navigation/to-navigation-tree-state.ts +0 -19
  145. package/src/store/navigation/use-navigation-tree-store.ts +0 -12
@@ -130,15 +130,15 @@
130
130
  margin: 4px 0 0;
131
131
  }
132
132
 
133
- /* JetBrains Mono 합자 비활성 · >=, != 원문 구분 */
134
133
  & code {
135
134
  padding: 1px 3px;
136
135
  border-radius: var(--app-radius-inline);
137
136
  background: var(--app-color-code);
138
137
  color: var(--app-color-text-strong);
139
- font-family: var(--app-font-mono);
138
+
139
+ /* UA monospace 대신 부모 글꼴 · 문단은 본문 정책, 코드 블록은 pre의 고정폭·자간 0 */
140
+ font-family: inherit;
140
141
  font-size: 1em;
141
- font-variant-ligatures: none;
142
142
  }
143
143
 
144
144
  /* 코드 블록 · Shiki의 --app-code-* 참조 */
@@ -151,8 +151,8 @@
151
151
  background: var(--app-color-code);
152
152
  font-family: var(--app-font-mono);
153
153
  font-size: var(--app-font-size-detail);
154
- font-variant-ligatures: none;
155
154
  line-height: var(--app-line-height-code);
155
+ letter-spacing: 0;
156
156
  }
157
157
 
158
158
  & pre code {
@@ -323,6 +323,9 @@
323
323
  margin: var(--app-space-block) 0;
324
324
  padding: var(--app-space-block) 0;
325
325
 
326
+ /* 라벨 위치는 자간 0의 격자로 계산 */
327
+ letter-spacing: 0;
328
+
326
329
  /* stylelint-disable-next-line selector-disallowed-list -- 외부 렌더러 생성 SVG */
327
330
  & svg {
328
331
  display: block;
@@ -1,20 +1,18 @@
1
1
  /*
2
- * 넓은 화면은 같은 폭의 양쪽 탐색 · 좁은 화면은 문서 탐색만 표시
2
+ * 넓은 화면은 같은 폭의 양쪽 탐색 사이를 본문이 채움 · 좁은 화면은 문서 탐색만 표시
3
3
  */
4
4
  .wg_shell__root {
5
5
  display: grid;
6
6
  max-width: var(--app-size-page);
7
7
  margin: 0 auto;
8
8
  padding: 0 var(--app-space-gutter);
9
- grid-template: "nav main outline" 1fr / calc(var(--app-size-sidebar) + var(--app-space-sidebar-gap)) minmax(0, var(--app-size-main)) calc(
10
- var(--app-size-sidebar) +
11
- var(--app-space-sidebar-gap)
12
- );
9
+ grid-template: "nav main outline" 1fr / calc(var(--app-size-sidebar) + var(--app-space-sidebar-gap)) minmax(0, 1fr) calc(var(--app-size-sidebar) + var(--app-space-sidebar-gap));
13
10
  background: var(--app-color-ground);
14
11
  color: var(--app-color-text);
15
- font-family: var(--app-font-sans);
12
+ font-family: var(--app-font-body);
16
13
  font-size: var(--app-font-size-body);
17
14
  line-height: var(--app-line-height-body);
15
+ letter-spacing: var(--app-letter-spacing-body);
18
16
 
19
17
  /* 한국어 낱말 단위 줄바꿈 · 긴 식별자만 예외 */
20
18
  word-break: keep-all;
@@ -35,7 +33,6 @@
35
33
  .wg_shell__main {
36
34
  grid-area: main;
37
35
  min-width: 0;
38
- max-width: var(--app-size-main);
39
36
  border-inline: 1px solid var(--app-color-border-layout);
40
37
 
41
38
  /* 문서 목록의 시작선 · 마지막 절도 읽는 선까지 이동 가능한 끝 여백 */
@@ -43,17 +40,16 @@
43
40
  padding-inline: var(--app-space-column);
44
41
  }
45
42
 
46
- /* 오른쪽 목차를 숨기는 화면에서 본문과 문서 목록의 간격 유지 */
47
- @media (width < 1536px) {
43
+ /* 본문 최소 약 800px을 남기도록 목차 숨김 · 본문이 오른쪽 여백까지 채움 */
44
+ @media (width < 1360px) {
48
45
  .wg_shell__root {
49
- grid-template: "nav main ." 1fr / calc(var(--app-size-sidebar) + var(--app-space-sidebar-gap)) minmax(0, var(--app-size-main)) minmax(0, 1fr);
46
+ grid-template: "nav main" 1fr / calc(var(--app-size-sidebar) + var(--app-space-sidebar-gap)) minmax(0, 1fr);
50
47
  }
51
48
  }
52
49
 
53
50
  /* 모바일의 브랜드·본문은 한 열 배치 */
54
51
  @media (width < 1024px) {
55
52
  .wg_shell__root {
56
- max-width: calc(var(--app-size-main) + var(--app-space-gutter) * 2);
57
53
  grid-template:
58
54
  "nav" auto
59
55
  "main" 1fr / minmax(0, 1fr);
@@ -47,11 +47,6 @@ export const copy_error_home_clash = "홈 README가 겹친다";
47
47
  */
48
48
  export const copy_error_prefix = "for-humanity";
49
49
 
50
- /**
51
- * 내장 본문 글꼴의 공통 조각이 누락된 패키지 오류
52
- */
53
- export const copy_error_font_preload = "Missing bundled font for preload";
54
-
55
50
  /**
56
51
  * 미지원 명령 오류
57
52
  */
@@ -4,25 +4,20 @@
4
4
  */
5
5
 
6
6
  /**
7
- * 본문 글꼴 CSS · 패키지 기준 경로 · dynamic subset 92개
7
+ * 본문 영문·코드·흐름도 글꼴 CSS · 패키지 기준 경로 · Regular·SemiBold 두 파일
8
8
  */
9
- export const font_sans_css = "src/asset/font/pretendard/pretendardvariable-dynamic-subset.css";
9
+ export const font_mono_css = "src/asset/font/monoplex-kr/monoplex-kr.css";
10
10
 
11
11
  /**
12
- * 코드 글꼴 CSS · 라틴 subset · 한글은 본문 글꼴
12
+ * 본문 한글 글꼴 CSS · IBM Plex Sans KR Regular·SemiBold · 한글 unicode-range
13
13
  */
14
- export const font_mono_css = "src/asset/font/jetbrains-mono/jetbrains-mono.css";
14
+ export const font_body_css = "src/asset/font/ibm-plex-sans-kr/ibm-plex-sans-kr.css";
15
15
 
16
16
  /**
17
17
  * 사이트 이름 글꼴 CSS · Architects Daughter Regular · 라틴 subset
18
18
  */
19
19
  export const font_brand_css = "src/asset/font/architects-daughter/architects-daughter.css";
20
20
 
21
- /**
22
- * 탐색·라틴 문자에 필요한 본문 조각 · 92개 전체 선요청 방지
23
- */
24
- export const font_sans_preload_file = "PretendardVariable.subset.91.woff2";
25
-
26
21
  /**
27
22
  * 늦게 받은 글꼴로 이미 보이는 대체 글꼴을 교체하지 않는 표시 정책
28
23
  */
@@ -13,7 +13,7 @@ if (root !== null && payload !== null) {
13
13
  try {
14
14
  const value: unknown = JSON.parse(payload.textContent);
15
15
  const data = navigationDataSchema.parse(value);
16
- const stores = createNavigationStores({local: () => localStorage, session: () => sessionStorage});
16
+ const stores = createNavigationStores({session: () => sessionStorage});
17
17
  createRoot(root).render(<WgShellControls data={data} stores={stores} reload={root.hasAttribute("data-shell-reload")} />);
18
18
  } catch (error) {
19
19
  // 초기화 오류에서도 서버의 링크·본문과 네이티브 스크롤 유지
@@ -314,12 +314,11 @@ test("nested groups follow metadata paths and keep sibling branches and document
314
314
  const html = toNavigationHtml({site, docs, current: "authoring"});
315
315
 
316
316
  assert.match(html, /href="\/authoring\/"[^>]*aria-current="page"/);
317
- assert.match(html, /Atlas<\/span><button\b[^>]*aria-expanded="true"[^>]*>−<\/button><ul\b/);
318
- assert.match(html, /Research<\/span><button\b[^>]*aria-expanded="true"[^>]*>−<\/button><ul\b/);
317
+ assert.match(html, /Atlas<\/span><ul\b/);
318
+ assert.match(html, /Research<\/span><ul\b/);
319
319
  assert.equal([...html.matchAll(/href="\/authoring\/"/g)].length, 1);
320
320
  assert.equal([...html.matchAll(/>Research<\/span>/g)].length, 2);
321
- assert.equal([...html.matchAll(/wg_navigationNavList__item--current/g)].length, 3);
322
- assert.doesNotMatch(toNavigationHtml({site, docs}), /aria-current="page"|wg_navigationNavList__item--current/);
321
+ assert.doesNotMatch(toNavigationHtml({site, docs}), /aria-current="page"/);
323
322
  assert.ok(html.indexOf('href="/parts/"') < html.indexOf('href="/authoring/"'));
324
323
  assert.doesNotMatch(html, /<details\b/);
325
324
  });
@@ -370,17 +369,16 @@ test("parent documents contain ordered descendants with independent URLs and one
370
369
  assert.deepEqual(toDocGroups({docs: docs.toReversed(), navigation: site.navigation}), groups);
371
370
 
372
371
  const html = toNavigationHtml({site, docs, current: "details"});
373
- assert.match(html, /href="\/settings\/"[^>]*>Settings<\/a><button\b[^>]*aria-label="Collapse Settings"[^>]*>−<\/button><ul\b[^>]*aria-label="Settings"/);
374
- assert.match(html, /href="\/guide\/site\/"[^>]*>Site<\/a><button\b[^>]*aria-label="Collapse Site"[^>]*>−<\/button><ul\b[^>]*aria-label="Site"/);
372
+ assert.match(html, /href="\/settings\/"[^>]*>Settings<\/a><ul\b[^>]*aria-label="Settings"/);
373
+ assert.match(html, /href="\/guide\/site\/"[^>]*>Site<\/a><ul\b[^>]*aria-label="Site"/);
375
374
  assert.match(html, /href="\/details\/"[^>]*aria-current="page"/);
376
375
  assert.equal([...html.matchAll(/aria-current="page"/g)].length, 1);
377
376
  assert.equal([...html.matchAll(/wg_navigationNavList__link--active/g)].length, 1);
378
- assert.equal([...html.matchAll(/wg_navigationNavList__item--current/g)].length, 5);
379
377
  for (const doc of docs) {
380
378
  assert.equal(html.split(`href="/${doc.id}/"`).length - 1, 1);
381
379
  }
382
380
  assert.ok(html.indexOf('href="/themes/"') < html.indexOf('href="/guide/site/"'));
383
- assert.doesNotMatch(toNavigationHtml({site, docs}), /aria-current="page"|wg_navigationNavList__item--current/);
381
+ assert.doesNotMatch(toNavigationHtml({site, docs}), /aria-current="page"/);
384
382
 
385
383
  const app = createApp({
386
384
  site,
@@ -7,7 +7,7 @@
7
7
  import {EventEmitter} from "node:events";
8
8
  import {existsSync, readFileSync, watch} from "node:fs";
9
9
  import {copyFile, mkdir, rm, writeFile} from "node:fs/promises";
10
- import {basename, dirname, extname, join, relative, resolve, sep} from "node:path";
10
+ import {dirname, extname, join, relative, resolve, sep} from "node:path";
11
11
  import {fileURLToPath, pathToFileURL} from "node:url";
12
12
  import {serve} from "@hono/node-server";
13
13
  import {serveStatic} from "@hono/node-server/serve-static";
@@ -15,8 +15,8 @@ import {debounce} from "es-toolkit";
15
15
  import {Hono} from "hono";
16
16
  import {toSSG} from "hono/ssg";
17
17
  import {asset_favicon_path, asset_font_dir, asset_media_extensions, asset_style_path} from "@/constant/asset";
18
- import {copy_error_config, copy_error_font_preload, copy_error_prefix, copy_error_unknown_command} from "@/constant/copy";
19
- import {font_brand_css, font_cache_control, font_mono_css, font_sans_css, font_sans_preload_file} from "@/constant/font";
18
+ import {copy_error_config, copy_error_prefix, copy_error_unknown_command} from "@/constant/copy";
19
+ import {font_body_css, font_brand_css, font_cache_control, font_mono_css} from "@/constant/font";
20
20
  import {site_config_absent} from "@/constant/site";
21
21
  import {cli_config_file_name, cli_default_command, cli_default_docs_dir, cli_dev_port, cli_reload_delay_ms} from "@/entry/cli/_constant/cli";
22
22
  import {crawling_reserved_roots} from "@/entry/cli/_constant/crawling";
@@ -77,23 +77,18 @@ const main = async () => {
77
77
 
78
78
  const siteConfig = parsedConfig.data;
79
79
  // 내장 @font-face와 파일 수집 · font-family는 token.css 소유
80
- const sans = toFontCss({css: join(kitRoot, font_sans_css), fontDir: asset_font_dir});
81
80
  const mono = toFontCss({css: join(kitRoot, font_mono_css), fontDir: asset_font_dir});
81
+ const body = toFontCss({css: join(kitRoot, font_body_css), fontDir: asset_font_dir});
82
82
  const brand = toFontCss({css: join(kitRoot, font_brand_css), fontDir: asset_font_dir});
83
- const sansPreload = [...sans.files].find((entry) => basename(entry[1]) === font_sans_preload_file);
84
83
 
85
- if (sansPreload === undefined) {
86
- throw new Error(`${copy_error_font_preload}: ${font_sans_preload_file}`);
87
- }
88
-
89
- const fontCss = [sans.css, mono.css, brand.css].join("\n");
84
+ const fontCss = [mono.css, body.css, brand.css].join("\n");
90
85
  const stylePath = toAssetPath({path: asset_style_path, file: join(kitRoot, "dist/cli.css")});
91
86
  const browserScript = readFileSync(join(kitRoot, "dist/browser.js"), "utf8");
92
87
  const kitFiles = new Map([
93
88
  [stylePath, join(kitRoot, "dist/cli.css")],
94
89
  [asset_favicon_path, join(kitRoot, "src/asset/favicon.svg")],
95
- ...sans.files,
96
90
  ...mono.files,
91
+ ...body.files,
97
92
  ...brand.files,
98
93
  ]);
99
94
  const files = new Map(kitFiles);
@@ -107,7 +102,7 @@ const main = async () => {
107
102
  const app = createApp({
108
103
  site: siteConfig,
109
104
  store,
110
- assets: {stylePath, browserScript, fontCss, preload: [...brand.files.keys(), sansPreload[0], ...mono.files.keys()], reload: command === "dev"},
105
+ assets: {stylePath, browserScript, fontCss, preload: [...brand.files.keys(), ...mono.files.keys(), ...body.files.keys()], reload: command === "dev"},
111
106
  });
112
107
 
113
108
  if (command === "build") {
@@ -1,35 +1,25 @@
1
1
  import assert from "node:assert/strict";
2
2
  import {test} from "node:test";
3
3
  import {createNavigationStores} from "@/store/navigation/create-navigation-stores";
4
- import {navigation_scroll_storage_key, navigation_tree_storage_key} from "@/store/navigation/navigation-storage";
4
+ import {navigation_scroll_storage_key} from "@/store/navigation/navigation-storage";
5
5
 
6
6
  const fixture = () => {
7
- const local = new Map<string, string>();
8
7
  const session = new Map<string, string>();
9
- const storage = (values: Map<string, string>) => ({
8
+ const storage = {
10
9
  getItem: (key: string) => {
11
- const value = values.get(key);
10
+ const value = session.get(key);
12
11
  return value === undefined ? null : value;
13
12
  },
14
13
  setItem: (key: string, value: string) => {
15
- values.set(key, value);
14
+ session.set(key, value);
16
15
  },
17
16
  removeItem: (key: string) => {
18
- values.delete(key);
17
+ session.delete(key);
19
18
  },
20
- });
21
- return {local, session, options: {local: () => storage(local), session: () => storage(session)}};
19
+ };
20
+ return {session, options: {session: () => storage}};
22
21
  };
23
22
 
24
- test("starts with every branch expanded and persists explicit choices across pages", () => {
25
- const setup = fixture();
26
- const stores = createNavigationStores(setup.options);
27
- assert.deepEqual(stores.tree.getState(), {collapsed: {}});
28
- stores.tree.setState({collapsed: {'group:["Projects"]': true, "outline:one:heading:setup": false}});
29
- assert.deepEqual(createNavigationStores(setup.options).tree.getState(), stores.tree.getState());
30
- assert.equal(setup.session.has(navigation_tree_storage_key), false);
31
- });
32
-
33
23
  test("retains independent wide, desktop and drawer scroll positions within a tab", () => {
34
24
  const setup = fixture();
35
25
  const stores = createNavigationStores(setup.options);
@@ -41,23 +31,19 @@ test("retains independent wide, desktop and drawer scroll positions within a tab
41
31
  },
42
32
  });
43
33
  assert.deepEqual(createNavigationStores(setup.options).scroll.getState(), stores.scroll.getState());
44
- assert.equal(setup.local.has(navigation_scroll_storage_key), false);
45
34
  });
46
35
 
47
36
  test("rejects malformed and unknown-version persistence without losing defaults", () => {
48
- for (const value of ["{", "null", '{"state":null,"version":1}', '{"state":{"collapsed":{"group:a":true}},"version":999}']) {
37
+ for (const value of ["{", "null", '{"state":null,"version":1}', '{"state":{"positions":{}},"version":999}']) {
49
38
  const setup = fixture();
50
- setup.local.set(navigation_tree_storage_key, value);
51
39
  setup.session.set(navigation_scroll_storage_key, value);
52
40
  const stores = createNavigationStores(setup.options);
53
- assert.deepEqual(stores.tree.getState(), {collapsed: {}});
54
41
  assert.deepEqual(stores.scroll.getState(), {positions: {}});
55
42
  }
56
43
  });
57
44
 
58
- test("narrows persisted branch flags and rejects invalid scroll coordinates", () => {
45
+ test("rejects invalid scroll coordinates", () => {
59
46
  const setup = fixture();
60
- setup.local.set(navigation_tree_storage_key, JSON.stringify({version: 1, state: {collapsed: {good: true, bad: "true"}, unexpected: true}}));
61
47
  setup.session.set(
62
48
  navigation_scroll_storage_key,
63
49
  JSON.stringify({
@@ -72,7 +58,6 @@ test("narrows persisted branch flags and rejects invalid scroll coordinates", ()
72
58
  }),
73
59
  );
74
60
  const stores = createNavigationStores(setup.options);
75
- assert.deepEqual(stores.tree.getState(), {collapsed: {good: true}});
76
61
  assert.deepEqual(stores.scroll.getState().positions.wide, {navigation: "docs", pathname: "/one/", documents: 100, outline: 20});
77
62
  assert.equal(stores.scroll.getState().positions.desktop, undefined);
78
63
  assert.equal(stores.scroll.getState().positions.drawer, undefined);
@@ -82,10 +67,7 @@ test("keeps in-memory interaction working when storage reads and writes are bloc
82
67
  const blocked = () => {
83
68
  throw new Error("Storage blocked");
84
69
  };
85
- const stores = createNavigationStores({local: blocked, session: blocked});
86
- stores.tree.setState({collapsed: {"document:one": true}});
87
- stores.tree.persist.rehydrate();
88
- assert.equal(stores.tree.getState().collapsed["document:one"], true);
70
+ const stores = createNavigationStores({session: blocked});
89
71
  stores.scroll.setState({positions: {desktop: {navigation: "docs", pathname: "/", documents: 100, outline: 200}}});
90
72
  stores.scroll.persist.rehydrate();
91
73
  assert.equal(stores.scroll.getState().positions.desktop?.documents, 100);
@@ -1,18 +1,13 @@
1
1
  import {createJSONStorage, persist, type StateStorage} from "zustand/middleware";
2
2
  import {createStore} from "zustand/vanilla";
3
- import type {NavigationScrollState, NavigationTreeState} from "@/store/navigation/navigation-state";
4
- import {navigation_scroll_storage_key, navigation_storage_version, navigation_tree_storage_key} from "@/store/navigation/navigation-storage";
3
+ import type {NavigationScrollState} from "@/store/navigation/navigation-state";
4
+ import {navigation_scroll_storage_key, navigation_storage_version} from "@/store/navigation/navigation-storage";
5
5
  import {toNavigationScrollState} from "@/store/navigation/to-navigation-scroll-state";
6
- import {toNavigationTreeState} from "@/store/navigation/to-navigation-tree-state";
7
6
 
8
7
  /**
9
8
  * 브라우저 저장소를 늦게 읽는 경계 · 차단 환경과 테스트의 메모리 저장소 지원
10
9
  */
11
10
  interface NavigationStorage {
12
- /**
13
- * 브라우저 재실행 후에도 유지할 접힘 선택
14
- */
15
- local: () => StateStorage;
16
11
  /**
17
12
  * 같은 탭 안에서만 복원할 스크롤 위치
18
13
  */
@@ -25,9 +20,9 @@ interface NavigationStorage {
25
20
  */
26
21
  export const createNavigationStores = (options: NavigationStorage) => {
27
22
  /**
28
- * 저장소 접근이 막혀도 persist 갱신이 버튼·스크롤 동작을 중단하지 않도록 격리
23
+ * 저장소 접근이 막혀도 persist 갱신이 스크롤 동작을 중단하지 않도록 격리
29
24
  */
30
- const toSafeStorage = (getStorage: NavigationStorage["local"]): StateStorage => {
25
+ const toSafeStorage = (getStorage: NavigationStorage["session"]): StateStorage => {
31
26
  return {
32
27
  getItem: (key) => {
33
28
  // persist의 읽기 오류 경로로 보내 기존 메모리 상태를 유지 · null은 실제 저장소 초기화에만 사용
@@ -51,15 +46,6 @@ export const createNavigationStores = (options: NavigationStorage) => {
51
46
  };
52
47
 
53
48
  return {
54
- tree: createStore<NavigationTreeState>()(
55
- persist(() => ({collapsed: {}}), {
56
- name: navigation_tree_storage_key,
57
- version: navigation_storage_version,
58
- storage: createJSONStorage(() => toSafeStorage(options.local)),
59
- merge: (persisted) => toNavigationTreeState(persisted),
60
- migrate: () => ({collapsed: {}}),
61
- }),
62
- ),
63
49
  scroll: createStore<NavigationScrollState>()(
64
50
  persist(() => ({positions: {}}), {
65
51
  name: navigation_scroll_storage_key,
@@ -3,16 +3,6 @@
3
3
  */
4
4
  export type NavigationLayout = "wide" | "desktop" | "drawer";
5
5
 
6
- /**
7
- * 사용자가 명시적으로 바꾼 가지 선택 · 없는 키는 펼침
8
- */
9
- export interface NavigationTreeState {
10
- /**
11
- * 문서 ID·그룹 전체 경로·페이지별 heading으로 구분한 접힘 상태
12
- */
13
- collapsed: Record<string, boolean>;
14
- }
15
-
16
6
  /**
17
7
  * 한 화면 배치의 마지막 탐색 위치 · 목록 변경과 다른 페이지의 목차는 재사용하지 않음
18
8
  */
@@ -1,8 +1,3 @@
1
- /**
2
- * 문서·현재 페이지 목차의 접힘 선택 · 기본은 전체 펼침
3
- */
4
- export const navigation_tree_storage_key = "for-humanity:navigation-tree";
5
-
6
1
  /**
7
2
  * 저장 형태가 바뀌면 기존 자료를 기본 상태로 돌리는 persist 버전
8
3
  */
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * 공통 토큰의 단일 출처 · --app-종류-쓰임
3
3
  * 사용 기준: templates/document/design.md · 테마 분기는 color-scheme에 한정
4
- * 폭 기준 1536px·1024px·640px · 양쪽 탐색과 본문 배치
4
+ * 폭 기준 1360px·1024px·640px · 양쪽 탐색과 본문 배치
5
5
  */
6
6
 
7
7
  :root {
@@ -48,16 +48,16 @@
48
48
 
49
49
  /*
50
50
  * 내장 글꼴 · CLI는 @font-face와 자원 URL 생성
51
- * 본문·라벨 Pretendard · 코드·흐름도 JetBrains Mono · 사이트 이름 Architects Daughter
52
- * 영문 폭 0.6em = 흐름도 12px 격자의 7.2px
51
+ * 본문·라벨·인라인 코드 한글 IBM Plex Sans KR·영문 Monoplex KR · 코드 블록·흐름도 Monoplex KR · 사이트 이름 Architects Daughter
52
+ * 영문 폭 0.528em = 흐름도 12px 격자의 6.336px · 코드 블록·흐름도의 한글은 정확히 2칸
53
+ * 본문 맨 앞의 IBM Plex Sans KR은 한글 unicode-range만 담당 · 영문·숫자·기호는 다음 순서의 Monoplex KR
53
54
  */
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);
55
+ --app-font-mono: "Monoplex KR", ui-monospace, monospace;
56
+ --app-font-body: "IBM Plex Sans KR", var(--app-font-mono);
57
+ --app-font-brand: "Architects Daughter", var(--app-font-body);
57
58
 
58
59
  /* 글자 크기 · 11px 비상호작용 묶음 · 12px 기본과 역할별 제목 크기 */
59
60
  --app-font-size-group: 11px;
60
- --app-font-size-label: 12px;
61
61
  --app-font-size-detail: 12px;
62
62
  --app-font-size-dense: 12px;
63
63
  --app-font-size-body: 12px;
@@ -72,18 +72,18 @@
72
72
  --app-font-weight-strong: 600;
73
73
  --app-font-weight-title: 600;
74
74
 
75
- /* 행간 · 작은 제목·탐색은 1.5, 본문은 한글 획 사이 여유 확보 */
75
+ /* 행간 · 작은 제목·탐색은 1.4, 본문은 한글 획 사이 여유 확보 */
76
76
  --app-line-height-title: 1.5;
77
77
  --app-line-height-heading: 1.5;
78
- --app-line-height-tight: 1.5;
78
+ --app-line-height-tight: 1.4;
79
79
  --app-line-height-code: 1.65;
80
80
  --app-line-height-body: 1.7;
81
81
 
82
- /* 자간 · 12px 본문·라벨은 기본 폭, 제목만 미세하게 조임 */
83
- --app-letter-spacing-label: 0;
82
+ /* 자간 · 본문·라벨·인라인 코드를 조이고 제목은 그보다 조금 더 · 사이트 이름은 기본 폭 · 코드 블록·흐름도는 격자를 위해 0 */
83
+ --app-letter-spacing-body: -0.03em;
84
84
  --app-letter-spacing-brand: 0;
85
- --app-letter-spacing-heading: -0.01em;
86
- --app-letter-spacing-title: -0.015em;
85
+ --app-letter-spacing-heading: -0.04em;
86
+ --app-letter-spacing-title: -0.045em;
87
87
 
88
88
  /* 공통 간격 · margin·padding·gap의 리듬 · 단독 보정은 지역 값 */
89
89
  --app-space-inline: 6px;
@@ -92,9 +92,10 @@
92
92
  --app-space-group: 20px;
93
93
  --app-space-section: 32px;
94
94
 
95
- /* 탐색 트리 · 항목 위아래 여백·묶음 안쪽 여백·가지와 중첩 들여쓰기 */
96
- --app-space-tree-row: 2px;
97
- --app-space-tree-inset: 4px;
95
+ /* 탐색 트리 · 항목 위아래 여백·묶음 사이·묶음 라벨 아래·중첩 들여쓰기 */
96
+ --app-space-tree-row: 0px;
97
+ --app-space-tree-group: 16px;
98
+ --app-space-tree-label: 2px;
98
99
  --app-space-tree-indent: 12px;
99
100
 
100
101
  /* 페이지 틀 · 좁은 화면 값은 아래 @media */
@@ -102,15 +103,15 @@
102
103
  --app-space-gutter: 28px;
103
104
  --app-space-column: 32px;
104
105
  --app-space-sidebar-gap: 32px;
105
- --app-size-sidebar: 240px;
106
- --app-size-main: 900px;
107
- --app-size-page: calc(var(--app-size-main) + (var(--app-size-sidebar) + var(--app-space-sidebar-gap) + var(--app-space-gutter)) * 2);
106
+ --app-size-sidebar: 200px;
107
+
108
+ /* 페이지 최대 폭 · 본문은 사이드바·간격을 뺀 나머지 전체 */
109
+ --app-size-page: 1920px;
108
110
  --app-size-brand: calc(var(--app-font-size-title) * var(--app-line-height-title) + var(--app-space-page-top) + var(--app-space-group));
109
111
  --app-size-mobile-header: 56px;
110
112
  --app-size-menu-button: 44px;
111
113
  --app-size-drawer: 320px;
112
114
  --app-size-navigation-fade: 28px;
113
- --app-size-tree-toggle: 24px;
114
115
 
115
116
  /* 화면 위쪽 제목 여백 · hash 도착점과 목차 읽는 선의 공통 기준 */
116
117
  --app-space-anchor: 80px;
@@ -4,11 +4,11 @@ import {tmpdir} from "node:os";
4
4
  import {join} from "node:path";
5
5
  import {test} from "node:test";
6
6
  import {asset_font_dir} from "@/constant/asset";
7
- import {font_brand_css, font_mono_css, font_sans_css} from "@/constant/font";
7
+ import {font_body_css, font_brand_css, font_mono_css} from "@/constant/font";
8
8
  import {toFontCss} from "@/util/font/to-font-css";
9
9
 
10
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]) {
11
+ for (const css of [font_mono_css, font_body_css, font_brand_css]) {
12
12
  const result = toFontCss({css: join(process.cwd(), css), fontDir: asset_font_dir});
13
13
 
14
14
  assert.match(result.css, /font-display:\s*optional;/);
@@ -82,7 +82,7 @@ function widenLabelGaps(rows) {
82
82
  }
83
83
 
84
84
  function gridToSvg(ascii) {
85
- const cw = 7.2, ch = 17, fs = 12;
85
+ const cw = 6.336, ch = 17, fs = 12;
86
86
  const rows = widenLabelGaps(ascii.replace(/\s+$/, "").split("\n"));
87
87
  const cols = Math.max.apply(null, rows.map((r) => r.length));
88
88
  const f = (n) => n.toFixed(1);
@@ -90,7 +90,7 @@ function gridToSvg(ascii) {
90
90
 
91
91
  const isText = (chr) => chr !== undefined && chr !== " " && chr !== ZW && LINES[chr] === undefined && ARROWS[chr] === undefined && chr !== "◇";
92
92
  const center = (c) => c * cw + cw / 2;
93
- const textWidth = (chars) => chars.reduce((w, chr) => w + (WIDE.test(chr) ? fs : cw), 0);
93
+ const textWidth = (chars) => chars.reduce((w, chr) => w + (WIDE.test(chr) ? cw * 2 : cw), 0);
94
94
 
95
95
  rows.forEach((row, r) => {
96
96
  const cy = r * ch + ch / 2, y0 = r * ch, y1 = y0 + ch;
@@ -1,11 +0,0 @@
1
- /* JetBrains Mono Variable, Fontsource 5.3.0 의 latin 조각. 라이선스는 옆의 LICENSE (OFL) */
2
- /* jetbrains-mono-latin-wght-normal */
3
- @font-face {
4
- font-family: "JetBrains Mono Variable";
5
- font-style: normal;
6
- font-display: swap;
7
- font-weight: 100 800;
8
- src: url(./jetbrains-mono-latin-wght-normal.woff2) format("woff2-variations");
9
- unicode-range:
10
- U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
11
- }