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.
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/cli.css +962 -0
- package/dist/cli.js +1241 -0
- package/dist/client.js +5 -0
- package/package.json +92 -0
- package/src/app.tsx +57 -0
- package/src/asset/favicon.svg +5 -0
- package/src/asset/font/architects-daughter/LICENSE.txt +93 -0
- package/src/asset/font/architects-daughter/architects-daughter-latin-400-normal.woff2 +0 -0
- package/src/asset/font/architects-daughter/architects-daughter.css +10 -0
- package/src/asset/font/jetbrains-mono/LICENSE +93 -0
- package/src/asset/font/jetbrains-mono/jetbrains-mono-latin-wght-normal.woff2 +0 -0
- package/src/asset/font/jetbrains-mono/jetbrains-mono.css +11 -0
- package/src/asset/font/pretendard/LICENSE.txt +94 -0
- package/src/asset/font/pretendard/pretendardvariable-dynamic-subset.css +1008 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.0.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.1.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.10.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.11.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.12.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.13.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.14.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.15.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.16.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.17.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.18.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.19.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.2.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.20.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.21.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.22.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.23.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.24.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.25.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.26.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.27.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.28.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.29.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.3.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.30.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.31.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.32.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.33.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.34.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.35.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.36.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.37.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.38.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.39.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.4.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.40.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.41.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.42.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.43.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.44.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.45.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.46.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.47.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.48.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.49.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.5.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.50.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.51.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.52.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.53.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.54.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.55.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.56.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.57.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.58.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.59.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.6.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.60.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.61.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.62.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.63.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.64.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.65.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.66.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.67.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.68.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.69.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.7.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.70.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.71.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.72.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.73.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.74.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.75.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.76.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.77.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.78.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.79.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.8.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.80.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.81.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.82.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.83.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.84.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.85.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.86.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.87.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.88.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.89.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.9.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.90.woff2 +0 -0
- package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.91.woff2 +0 -0
- package/src/cli.ts +162 -0
- package/src/client.ts +322 -0
- package/src/component/widget/prose/_constant/heading-highlight.ts +4 -0
- package/src/component/widget/prose/_constant/section.ts +7 -0
- package/src/component/widget/prose/_constant/status.ts +9 -0
- package/src/component/widget/prose/_function/has-block-content.ts +30 -0
- package/src/component/widget/prose/_function/rehype-head.ts +31 -0
- package/src/component/widget/prose/_function/rehype-sections/_to-section-headings.ts +20 -0
- package/src/component/widget/prose/_function/rehype-sections/rehype-sections.ts +21 -0
- package/src/component/widget/prose/_function/rehype-tables.ts +22 -0
- package/src/component/widget/prose/_function/remark-blocks.ts +112 -0
- package/src/component/widget/prose/_function/remark-flow.ts +26 -0
- package/src/component/widget/prose/_function/remark-links.ts +33 -0
- package/src/component/widget/prose/_function/remark-parts.ts +26 -0
- package/src/component/widget/prose/_function/remark-status.ts +59 -0
- package/src/component/widget/prose/_function/remark-swatch.ts +16 -0
- package/src/component/widget/prose/_function/remark-unknown-directives.ts +25 -0
- package/src/component/widget/prose/_type/doc-section.ts +9 -0
- package/src/component/widget/prose/wg-prose.css +381 -0
- package/src/component/widget/prose/wg-prose.tsx +43 -0
- package/src/component/widget/shell/_constant/cursor-face.ts +9 -0
- package/src/component/widget/shell/_constant/navigation.ts +4 -0
- package/src/component/widget/shell/_constant/reading-line.ts +4 -0
- package/src/component/widget/shell/_function/bind-mobile-navigation.ts +140 -0
- package/src/component/widget/shell/_function/to-toc-groups.ts +38 -0
- package/src/component/widget/shell/_type/doc-outline.ts +16 -0
- package/src/component/widget/shell/_type/toc-group.ts +39 -0
- package/src/component/widget/shell/_wg-shell-nav.css +134 -0
- package/src/component/widget/shell/_wg-shell-nav.tsx +75 -0
- package/src/component/widget/shell/_wg-shell-toc.css +268 -0
- package/src/component/widget/shell/_wg-shell-toc.tsx +57 -0
- package/src/component/widget/shell/wg-shell.css +261 -0
- package/src/component/widget/shell/wg-shell.tsx +147 -0
- package/src/constant/asset.ts +48 -0
- package/src/constant/cli.ts +19 -0
- package/src/constant/copy.ts +108 -0
- package/src/constant/doc.ts +13 -0
- package/src/constant/font.ts +39 -0
- package/src/constant/locale.ts +9 -0
- package/src/constant/site.ts +9 -0
- package/src/constant/status.ts +15 -0
- package/src/content/create-processor.ts +59 -0
- package/src/content/read-docs.test.ts +242 -0
- package/src/content/read-docs.ts +75 -0
- package/src/content/read-home.ts +32 -0
- package/src/content/rehype-heading-ids.ts +34 -0
- package/src/content/remark-report.ts +15 -0
- package/src/content/to-doc-groups.ts +18 -0
- package/src/dev.ts +79 -0
- package/src/env.d.ts +6 -0
- package/src/page/doc/pg-doc.tsx +35 -0
- package/src/page/home/pg-home.tsx +40 -0
- package/src/style/base.css +28 -0
- package/src/style/token.css +162 -0
- package/src/type/doc-content.ts +15 -0
- package/src/type/doc-data.ts +33 -0
- package/src/type/doc-file-data.ts +21 -0
- package/src/type/doc-heading.ts +17 -0
- package/src/type/doc.ts +16 -0
- package/src/type/site-assets.ts +17 -0
- package/src/type/site-config.ts +55 -0
- package/src/type/vfile-data.d.ts +10 -0
- package/src/util/dom/find-hash-target.ts +18 -0
- package/src/util/dom/reveal-hash-target.ts +15 -0
- package/src/util/error/to-error-message.ts +10 -0
- package/src/util/font/to-font-css.test.ts +41 -0
- package/src/util/font/to-font-css.ts +28 -0
- 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
|
+
}
|
package/src/type/doc.ts
ADDED
|
@@ -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,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
|
+
};
|