for-humanity 0.12.0 → 0.13.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 (226) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +16 -9
  3. package/dist/browser.js +14 -16
  4. package/dist/cli.css +2 -968
  5. package/dist/cli.js +92 -2318
  6. package/dist/config.js +1 -0
  7. package/dist/constant/copy.d.ts +100 -0
  8. package/dist/constant/site.d.ts +20 -0
  9. package/dist/constant/status.d.ts +19 -0
  10. package/dist/copy-C1uzvEnr.js +103 -0
  11. package/dist/group-path-XAThmzvC.js +10 -0
  12. package/dist/load-site-B-u5-jv0.js +2343 -0
  13. package/dist/logger-Dv1i9xSV.js +9 -0
  14. package/dist/mermaid/THIRD-PARTY-NOTICES.txt +3134 -0
  15. package/dist/mermaid/chunk--TN-qHgG.js +2 -0
  16. package/dist/mermaid/chunk-0kM1Lv4D.js +2 -0
  17. package/dist/mermaid/chunk-3FHei3pH.js +2001 -0
  18. package/dist/mermaid/chunk-3_2p2GqN.js +2 -0
  19. package/dist/mermaid/chunk-6HLei9i0.js +84 -0
  20. package/dist/mermaid/chunk-7Sz3kAwl.js +2214 -0
  21. package/dist/mermaid/chunk-7Tarqbpa.js +18047 -0
  22. package/dist/mermaid/chunk-9kkt8lzL.js +10 -0
  23. package/dist/mermaid/chunk-B--pNBQF.js +884 -0
  24. package/dist/mermaid/chunk-B4F-pysn.js +4241 -0
  25. package/dist/mermaid/chunk-B58sEIY_.js +14 -0
  26. package/dist/mermaid/chunk-B9_UWzSH.js +1517 -0
  27. package/dist/mermaid/chunk-BB8uZHwY.js +75 -0
  28. package/dist/mermaid/chunk-BFfk0LEc.js +717 -0
  29. package/dist/mermaid/chunk-BKri0qr7.js +89 -0
  30. package/dist/mermaid/chunk-BL404pH5.js +26 -0
  31. package/dist/mermaid/chunk-BLeiQSWo.js +354 -0
  32. package/dist/mermaid/chunk-BOmH_UFd.js +2 -0
  33. package/dist/mermaid/chunk-BSej9Nhy.js +1361 -0
  34. package/dist/mermaid/chunk-BYzrzvCI.js +12 -0
  35. package/dist/mermaid/chunk-BariMtWr.js +3020 -0
  36. package/dist/mermaid/chunk-BcrBoMwH.js +2352 -0
  37. package/dist/mermaid/chunk-Bok8z-Yo.js +67 -0
  38. package/dist/mermaid/chunk-BpTHLKQM.js +1970 -0
  39. package/dist/mermaid/chunk-BqjuxRsC.js +733 -0
  40. package/dist/mermaid/chunk-BuvPd-s5.js +1073 -0
  41. package/dist/mermaid/chunk-BvVehae3.js +5069 -0
  42. package/dist/mermaid/chunk-C-JABYpd.js +3075 -0
  43. package/dist/mermaid/chunk-C9XlTm4w.js +2 -0
  44. package/dist/mermaid/chunk-CApNbSU9.js +6 -0
  45. package/dist/mermaid/chunk-CCM2AH-C.js +9 -0
  46. package/dist/mermaid/chunk-CD1E0F_N.js +2 -0
  47. package/dist/mermaid/chunk-CDWvKG7D.js +2 -0
  48. package/dist/mermaid/chunk-CEVPpC1F.js +34 -0
  49. package/dist/mermaid/chunk-CLDr12lf.js +47504 -0
  50. package/dist/mermaid/chunk-CLVYqOBI.js +61 -0
  51. package/dist/mermaid/chunk-CLeOkFb5.js +15 -0
  52. package/dist/mermaid/chunk-CLgRAtOz.js +37 -0
  53. package/dist/mermaid/chunk-CMqFNUBl.js +134 -0
  54. package/dist/mermaid/chunk-CTPaBJXB.js +2 -0
  55. package/dist/mermaid/chunk-CVi6IGBL.js +2 -0
  56. package/dist/mermaid/chunk-CWV_EN-4.js +4745 -0
  57. package/dist/mermaid/chunk-CZ0dAs3g.js +2 -0
  58. package/dist/mermaid/chunk-C_YGy4Fn.js +236 -0
  59. package/dist/mermaid/chunk-CcVH22Yt.js +100 -0
  60. package/dist/mermaid/chunk-CgG6CD9_.js +7175 -0
  61. package/dist/mermaid/chunk-CgOP6IDB.js +3397 -0
  62. package/dist/mermaid/chunk-Cgg2UTSf.js +13 -0
  63. package/dist/mermaid/chunk-ChJfJJ1A.js +214 -0
  64. package/dist/mermaid/chunk-Ck5XRA46.js +447 -0
  65. package/dist/mermaid/chunk-Ckuw16cl.js +203 -0
  66. package/dist/mermaid/chunk-CpAbRCbF.js +1880 -0
  67. package/dist/mermaid/chunk-CuYekbSv.js +2 -0
  68. package/dist/mermaid/chunk-CwS4wZ5a.js +2 -0
  69. package/dist/mermaid/chunk-D-bcrBO3.js +82 -0
  70. package/dist/mermaid/chunk-D3IEAkCW.js +76 -0
  71. package/dist/mermaid/chunk-D8XfBJ_K.js +645 -0
  72. package/dist/mermaid/chunk-D9Ddd9y4.js +2228 -0
  73. package/dist/mermaid/chunk-DBh1mIbK.js +12 -0
  74. package/dist/mermaid/chunk-DD6Df6Ic.js +5 -0
  75. package/dist/mermaid/chunk-DGVDaXjx.js +8 -0
  76. package/dist/mermaid/chunk-DK9KqIXj.js +632 -0
  77. package/dist/mermaid/chunk-DORnIAsy.js +4487 -0
  78. package/dist/mermaid/chunk-DU69_VcF.js +363 -0
  79. package/dist/mermaid/chunk-DVNvSFoz.js +2144 -0
  80. package/dist/mermaid/chunk-DZe-4kde.js +20 -0
  81. package/dist/mermaid/chunk-D_a06Rz4.js +287 -0
  82. package/dist/mermaid/chunk-DfyFJJi2.js +972 -0
  83. package/dist/mermaid/chunk-DhKYPp1Z.js +2 -0
  84. package/dist/mermaid/chunk-DjFqgNTG.js +94 -0
  85. package/dist/mermaid/chunk-DmbDDrr-.js +65 -0
  86. package/dist/mermaid/chunk-DpuxlyL_.js +696 -0
  87. package/dist/mermaid/chunk-DqGhppWe.js +2 -0
  88. package/dist/mermaid/chunk-DqPZxHXa.js +5 -0
  89. package/dist/mermaid/chunk-Dt_1Jyif.js +293 -0
  90. package/dist/mermaid/chunk-DyQBE_NW.js +1935 -0
  91. package/dist/mermaid/chunk-JckyC4mW.js +1009 -0
  92. package/dist/mermaid/chunk-K7Myuj2-.js +1334 -0
  93. package/dist/mermaid/chunk-KuPYb48V.js +275 -0
  94. package/dist/mermaid/chunk-MFVRUoO0.js +4020 -0
  95. package/dist/mermaid/chunk-MbInCS_6.js +28 -0
  96. package/dist/mermaid/chunk-MexOLyKV.js +355 -0
  97. package/dist/mermaid/chunk-NnbDtJzg.js +21856 -0
  98. package/dist/mermaid/chunk-SGpOYZbI.js +2139 -0
  99. package/dist/mermaid/chunk-TbxLDlcU.js +198 -0
  100. package/dist/mermaid/chunk-TftHqGLg.js +490 -0
  101. package/dist/mermaid/chunk-UjQ8vA_Z.js +4138 -0
  102. package/dist/mermaid/chunk-YgmVTKqc.js +16 -0
  103. package/dist/mermaid/chunk-_a9EZE1v.js +1798 -0
  104. package/dist/mermaid/chunk-dTb8jqeP.js +2 -0
  105. package/dist/mermaid/chunk-fFdMfF8S.js +2240 -0
  106. package/dist/mermaid/chunk-feYbMzBj.js +1267 -0
  107. package/dist/mermaid/chunk-g3Pn8DkW.js +46 -0
  108. package/dist/mermaid/chunk-hM77LvCb.js +19832 -0
  109. package/dist/mermaid/chunk-hUDb-eAW.js +527 -0
  110. package/dist/mermaid/chunk-tpABtZ-3.js +4252 -0
  111. package/dist/mermaid/chunk-xIbz3oFK.js +59 -0
  112. package/dist/mermaid/chunk-yBz-vst0.js +976 -0
  113. package/dist/mermaid/mermaid-BhtR3wyB.js +1145 -0
  114. package/dist/run-site-command-cqCjpIlc.js +129 -0
  115. package/dist/site-config-bllORGpm.js +90 -0
  116. package/dist/site.js +1 -0
  117. package/dist/start-dev-server-x7Ja5uwv.js +184 -0
  118. package/dist/to-change-queue-DbEnZGNK.js +34 -0
  119. package/dist/to-error-message-pF6_7wGK.js +16 -0
  120. package/dist/type/group-path.d.ts +7 -0
  121. package/dist/type/site-config.d.ts +57 -0
  122. package/package.json +34 -17
  123. package/src/component/widget/cursor-face/_constant/cursor-face.ts +0 -14
  124. package/src/component/widget/cursor-face/wg-cursor-face.css +0 -15
  125. package/src/component/widget/cursor-face/wg-cursor-face.tsx +0 -153
  126. package/src/component/widget/navigation/_constant/locale.ts +0 -9
  127. package/src/component/widget/navigation/_constant/navigation-data-schema.ts +0 -33
  128. package/src/component/widget/navigation/_constant/navigation.ts +0 -9
  129. package/src/component/widget/navigation/_constant/reading-line.ts +0 -4
  130. package/src/component/widget/navigation/_function/restore-navigation-scroll.ts +0 -59
  131. package/src/component/widget/navigation/_function/to-doc-groups/_to-doc-branches.ts +0 -15
  132. package/src/component/widget/navigation/_function/to-doc-groups/_to-doc-group-branches.ts +0 -70
  133. package/src/component/widget/navigation/_function/to-doc-groups/to-doc-groups.ts +0 -15
  134. package/src/component/widget/navigation/_function/to-navigation-data/_to-navigation-document.ts +0 -13
  135. package/src/component/widget/navigation/_function/to-navigation-data/_to-navigation-group.ts +0 -13
  136. package/src/component/widget/navigation/_function/to-navigation-data/to-navigation-data.ts +0 -18
  137. package/src/component/widget/navigation/_function/to-navigation-json.ts +0 -8
  138. package/src/component/widget/navigation/_function/to-toc-groups.ts +0 -38
  139. package/src/component/widget/navigation/_type/doc-branch.ts +0 -11
  140. package/src/component/widget/navigation/_type/doc-group.ts +0 -24
  141. package/src/component/widget/navigation/_type/navigation-data.ts +0 -75
  142. package/src/component/widget/navigation/_type/navigation-focus-snapshot.ts +0 -17
  143. package/src/component/widget/navigation/_type/toc-group.ts +0 -39
  144. package/src/component/widget/navigation/_wg-navigation-nav-list.css +0 -48
  145. package/src/component/widget/navigation/_wg-navigation-nav-list.tsx +0 -61
  146. package/src/component/widget/navigation/_wg-navigation-nav.css +0 -21
  147. package/src/component/widget/navigation/_wg-navigation-nav.tsx +0 -33
  148. package/src/component/widget/navigation/_wg-navigation-scroll.tsx +0 -117
  149. package/src/component/widget/navigation/_wg-navigation-toc.css +0 -91
  150. package/src/component/widget/navigation/_wg-navigation-toc.tsx +0 -114
  151. package/src/component/widget/navigation/wg-navigation.css +0 -282
  152. package/src/component/widget/navigation/wg-navigation.tsx +0 -420
  153. package/src/component/widget/prose/_constant/heading-highlight.ts +0 -4
  154. package/src/component/widget/prose/_constant/section.ts +0 -7
  155. package/src/component/widget/prose/_constant/status.ts +0 -9
  156. package/src/component/widget/prose/_function/has-block-content.ts +0 -30
  157. package/src/component/widget/prose/_function/rehype-head.ts +0 -31
  158. package/src/component/widget/prose/_function/rehype-sections/_to-section-headings.ts +0 -20
  159. package/src/component/widget/prose/_function/rehype-sections/rehype-sections.ts +0 -21
  160. package/src/component/widget/prose/_function/rehype-tables.ts +0 -22
  161. package/src/component/widget/prose/_function/remark-blocks.ts +0 -112
  162. package/src/component/widget/prose/_function/remark-flow.ts +0 -26
  163. package/src/component/widget/prose/_function/remark-links.ts +0 -33
  164. package/src/component/widget/prose/_function/remark-parts.ts +0 -26
  165. package/src/component/widget/prose/_function/remark-status.ts +0 -59
  166. package/src/component/widget/prose/_function/remark-swatch.ts +0 -16
  167. package/src/component/widget/prose/_function/remark-unknown-directives.ts +0 -25
  168. package/src/component/widget/prose/wg-prose.css +0 -484
  169. package/src/component/widget/prose/wg-prose.tsx +0 -43
  170. package/src/component/widget/shell/wg-shell.css +0 -63
  171. package/src/component/widget/shell/wg-shell.tsx +0 -88
  172. package/src/component/widget/shell-controls/wg-shell-controls.css +0 -4
  173. package/src/component/widget/shell-controls/wg-shell-controls.tsx +0 -170
  174. package/src/constant/asset.ts +0 -63
  175. package/src/constant/copy.ts +0 -133
  176. package/src/constant/doc.ts +0 -13
  177. package/src/constant/font.ts +0 -34
  178. package/src/constant/site.ts +0 -24
  179. package/src/constant/status.ts +0 -15
  180. package/src/entry/browser/browser.tsx +0 -22
  181. package/src/entry/cli/_constant/cli.ts +0 -19
  182. package/src/entry/cli/_constant/crawling.ts +0 -14
  183. package/src/entry/cli/_constant/snippet.ts +0 -21
  184. package/src/entry/cli/_function/create-app.tsx +0 -57
  185. package/src/entry/cli/_function/create-dev-app.ts +0 -87
  186. package/src/entry/cli/_function/create-processor/_rehype-assets.ts +0 -70
  187. package/src/entry/cli/_function/create-processor/_rehype-heading-ids.ts +0 -34
  188. package/src/entry/cli/_function/create-processor/_remark-report.ts +0 -15
  189. package/src/entry/cli/_function/create-processor/_remark-snippets.ts +0 -117
  190. package/src/entry/cli/_function/create-processor/_shiki-transformers.ts +0 -91
  191. package/src/entry/cli/_function/create-processor/create-processor.ts +0 -64
  192. package/src/entry/cli/_function/read-docs.ts +0 -128
  193. package/src/entry/cli/_function/read-home.ts +0 -32
  194. package/src/entry/cli/_function/to-crawling-app.ts +0 -42
  195. package/src/entry/cli/cli.test.ts +0 -804
  196. package/src/entry/cli/cli.ts +0 -206
  197. package/src/env.d.ts +0 -6
  198. package/src/page/doc/pg-doc.tsx +0 -35
  199. package/src/page/home/pg-home.tsx +0 -40
  200. package/src/store/navigation/create-navigation-stores.test.ts +0 -74
  201. package/src/store/navigation/create-navigation-stores.ts +0 -59
  202. package/src/store/navigation/navigation-state.ts +0 -36
  203. package/src/store/navigation/navigation-storage.ts +0 -9
  204. package/src/store/navigation/to-navigation-scroll-state.ts +0 -42
  205. package/src/style/base.css +0 -28
  206. package/src/style/token.css +0 -188
  207. package/src/type/doc-content.ts +0 -15
  208. package/src/type/doc-data.ts +0 -38
  209. package/src/type/doc-file-data.ts +0 -21
  210. package/src/type/doc-heading.ts +0 -17
  211. package/src/type/doc-outline.ts +0 -16
  212. package/src/type/doc-section.ts +0 -9
  213. package/src/type/doc.ts +0 -20
  214. package/src/type/group-path.ts +0 -7
  215. package/src/type/site-assets.ts +0 -25
  216. package/src/type/site-config.ts +0 -73
  217. package/src/type/vfile-data.d.ts +0 -10
  218. package/src/util/async/to-change-queue.test.ts +0 -58
  219. package/src/util/async/to-change-queue.ts +0 -43
  220. package/src/util/dom/find-hash-target.ts +0 -18
  221. package/src/util/dom/reveal-hash-target.ts +0 -15
  222. package/src/util/error/to-error-message.ts +0 -10
  223. package/src/util/file/to-asset-path.ts +0 -26
  224. package/src/util/font/to-font-css.test.ts +0 -41
  225. package/src/util/font/to-font-css.ts +0 -28
  226. package/src/util/mermaid/render-flow.js +0 -321
@@ -1,28 +0,0 @@
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
- };
@@ -1,321 +0,0 @@
1
- /*
2
- * ASCII 격자의 빌드 시 SVG 렌더링 · CSS 변수 테마
3
- * 전각 문자의 폭 0 문자 삽입으로 2칸 확보
4
- * 공유 blueprint 알고리즘 · Biome 제외 · 실행 로직 변경 시 원본 대조
5
- */
6
- import {renderMermaidASCII} from "beautiful-mermaid";
7
-
8
- const esc = (s) => String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
9
-
10
- const ASCII_OPT = {paddingX: 3, paddingY: 2, boxBorderPadding: 1, colorMode: "none"};
11
- const WIDE = /[ᄀ-ᇿ -〿㄰-㆏가-힯一-鿿぀-ヿ＀-⦆]/;
12
- const ZW = "​";
13
- const widenCjk = (src) => src.replace(new RegExp(WIDE.source, "g"), (c) => c + ZW);
14
- // 선 문자의 연결 방향 · L R U D · r 둥근 모서리
15
- const LINES = {"─": "LR", "│": "UD", "┌": "RD", "┐": "LD", "└": "RU", "┘": "LU", "├": "UDR", "┤": "UDL", "┬": "LRD", "┴": "LRU", "┼": "LRUD", "╭": "RDr", "╮": "LDr", "╰": "RUr", "╯": "LUr",
16
- "═": "LRb", "║": "UDb", "╔": "RDb", "╗": "LDb", "╚": "RUb", "╝": "LUb", "╟": "UDRb", "╢": "UDLb", "╌": "LRd", "╎": "UDd"};
17
- const ARROWS = {"►": "R", "◄": "L", "▼": "D", "▲": "U", "▶": "R", "◀": "L"};
18
- // 선·화살촉의 접점: 상자 벽 선의 가운데
19
- const isWall = (chr) => chr === "│" || chr === "◇" || chr === "├" || chr === "┤";
20
- const isHBorder = (chr) => chr !== undefined && chr !== " " && chr !== ZW && (LINES[chr] !== undefined || chr === "◇");
21
-
22
- // ASCII 라벨 구간의 3칸 여백 보정
23
- // 1) 글자를 가르지 않는 열 삽입 · 라벨 양옆 최소 6칸 확보
24
- // 2) 라벨 중앙 정렬 · 줄 길이 유지로 세로선 정렬 보존
25
- function widenLabelGaps(rows) {
26
- const width = Math.max.apply(null, rows.map((r) => r.length));
27
- let grid = rows.map((r) => r.padEnd(width));
28
- const H = new Set("─┬┴├┤┼◇┌┐└┘╭╮╰╯═╌►◄▶◀╔╗╚╝╟╢".split(""));
29
- const ANCHOR = new Set("├┤┬┴┼└┘┌┐╭╮╰╯".split(""));
30
- const END = new Set("►▶◄◀┤├┬┴┼┐┘┌└╮╯╭╰│".split(""));
31
- const isTxt = (chr) => chr !== undefined && chr !== " " && chr !== ZW && chr !== "│" && !H.has(chr);
32
- const txtish = (chr) => isTxt(chr) || chr === ZW;
33
- const splittable = (col) => grid.every((r) => !(txtish(r[col - 1]) && txtish(r[col])));
34
- const filler = (r, col) => {
35
- const l = r[col - 1], rt = r[col];
36
- if (l === undefined || rt === undefined) return " ";
37
- if ((H.has(l) || txtish(l)) && (H.has(rt) || rt === "│" || txtish(rt)) && !(txtish(l) && txtish(rt))) {
38
- if (!(H.has(l) || H.has(rt))) return " ";
39
- return l === "╌" || rt === "╌" ? "╌" : "─";
40
- }
41
- return " ";
42
- };
43
- // 선 위 라벨 · 양쪽 선 또는 모서리·화살표에 닿은 글자 묶음
44
- const runs = (row) => {
45
- const out = [];
46
- for (const m of row.matchAll(/(─*)((?:[가-힣A-Za-z0-9]​?)+)(─*)/g)) {
47
- if (m[2].length === 0) continue;
48
- const before = row[m.index - 1], after = row[m.index + m[0].length];
49
- const leftOk = m[1].length > 0 || ANCHOR.has(before);
50
- const rightOk = m[3].length > 0 || END.has(after);
51
- if (leftOk && rightOk && (m[1].length + m[3].length > 0 || (ANCHOR.has(before) && END.has(after)))) {
52
- out.push({start: m.index, label: m[2], left: m[1].length, right: m[3].length, end: m.index + m[0].length});
53
- }
54
- }
55
- return out;
56
- };
57
- const want = 6;
58
- // 1) 오른쪽부터 열 삽입 · 기존 인덱스 보존
59
- const inserts = [];
60
- grid.forEach((row) => {
61
- for (const run of runs(row)) {
62
- const missing = want - (run.left + run.right);
63
- if (missing > 0) inserts.push({at: run.end, alt: run.start + run.left, n: missing});
64
- }
65
- });
66
- inserts.sort((x, y) => y.at - x.at);
67
- for (const ins of inserts) {
68
- const candidates = [ins.at, ins.at + 1, ins.at + 2, ins.alt, ins.alt - 1];
69
- const col = candidates.find((c) => c > 0 && c < width + 40 && splittable(c));
70
- if (col === undefined) continue;
71
- grid = grid.map((r) => r.slice(0, col) + filler(r, col).repeat(ins.n) + r.slice(col));
72
- }
73
- // 2) 라벨 중앙 정렬
74
- return grid.map((row) => {
75
- let out = row;
76
- for (const run of runs(row).reverse()) {
77
- const total = run.left + run.right, l = Math.floor(total / 2), rgt = total - l;
78
- out = out.slice(0, run.start) + "─".repeat(l) + run.label + "─".repeat(rgt) + out.slice(run.end);
79
- }
80
- return out;
81
- });
82
- }
83
-
84
- function gridToSvg(ascii) {
85
- const cw = 6.336, ch = 17, fs = 12;
86
- const rows = widenLabelGaps(ascii.replace(/\s+$/, "").split("\n"));
87
- const cols = Math.max.apply(null, rows.map((r) => r.length));
88
- const f = (n) => n.toFixed(1);
89
- let path = "", bold = "", dashed = "", arcs = "", tris = "", marks = "", dots = "", texts = "";
90
-
91
- const isText = (chr) => chr !== undefined && chr !== " " && chr !== ZW && LINES[chr] === undefined && ARROWS[chr] === undefined && chr !== "◇";
92
- const center = (c) => c * cw + cw / 2;
93
- const textWidth = (chars) => chars.reduce((w, chr) => w + (WIDE.test(chr) ? cw * 2 : cw), 0);
94
-
95
- rows.forEach((row, r) => {
96
- const cy = r * ch + ch / 2, y0 = r * ch, y1 = y0 + ch;
97
- let run = null;
98
- // 전각 선 라벨의 1칸 오차 보정
99
- // 벽부터 화살촉까지 중앙 정렬 · 라벨 폭만큼 선 비움
100
- const skip = new Set();
101
- for (let c = 0; c < row.length; c++) {
102
- if (!isText(row[c]) || skip.has(c)) continue;
103
- let e = c;
104
- while (e < row.length && (isText(row[e]) || row[e] === ZW || (row[e] === " " && isText(row[e + 1])))) e++;
105
- // 테두리 라벨로 사라진 분기점 ┬ 복원
106
- // 테두리·세로선 연결 · 라벨은 다음 줄의 세로선 옆
107
- if (row[c - 1] === "─" && row[e] === "─") {
108
- const below = rows[r + 1] || "", above = rows[r - 1] || "";
109
- let exit = null;
110
- for (let k = c - 2; k < e + 2 && exit === null; k++) {
111
- if (below[k] === "│" || below[k] === "▼") exit = {k: k, dir: 1};
112
- else if (above[k] === "│" || above[k] === "▲") exit = {k: k, dir: -1};
113
- }
114
- if (exit) {
115
- const kx = center(exit.k), chars = row.slice(c, e).split("").filter((chr) => chr !== ZW);
116
- path += "M" + f(c * cw) + " " + f(cy) + "H" + f(e * cw) + " ";
117
- path += "M" + f(kx) + " " + f(cy) + "V" + f(exit.dir > 0 ? y1 : y0) + " ";
118
- texts += '<text x="' + f(kx + cw * 0.8 + textWidth(chars) / 2) + '" y="' + f(cy + exit.dir * ch + fs * 0.35) + '">' + esc(chars.join("")) + "</text>";
119
- for (let k = c; k < e; k++) skip.add(k);
120
- c = e - 1;
121
- continue;
122
- }
123
- }
124
-
125
- let l = c - 1, rr = e;
126
- while (l >= 0 && row[l] === " ") l--;
127
- while (rr < row.length && row[rr] === " ") rr++;
128
- const onLine = (row[l] === "─" || row[l] === "├") && (row[rr] === "─" || ARROWS[row[rr]] !== undefined || row[rr] === "┤");
129
- if (!onLine) { c = e - 1; continue; }
130
- let L = l;
131
- while (L - 1 >= 0 && row[L - 1] === "─") L--;
132
- // 출발점 ├ 앞의 ASCII 여백을 건너 상자 벽까지 연결
133
- let segStart = L * cw, drawStart = L * cw;
134
- if (row[L] === "├" || row[L - 1] === "├") {
135
- const j = row[L] === "├" ? L : L - 1;
136
- const above = rows[r - 1] ? rows[r - 1][j] : " ", below = rows[r + 1] ? rows[r + 1][j] : " ";
137
- const junction = (above !== undefined && LINES[above] !== undefined && /[UD]/.test(LINES[above])) ||
138
- (below !== undefined && LINES[below] !== undefined && /[UD]/.test(LINES[below]));
139
- if (junction) {
140
- // 상자 벽인 ├는 기본 루프에서 유지 · 연결선만 벽 가운데부터 시작
141
- segStart = center(j);
142
- drawStart = center(j);
143
- for (let m = j + 1; m < L; m++) skip.add(m);
144
- } else {
145
- // 벽과 떨어진 ├는 별도 표식 없이 연결선으로 대체
146
- let k = j - 1;
147
- while (k >= 0 && row[k] === " ") k--;
148
- segStart = isWall(row[k]) ? center(k) : center(j);
149
- drawStart = segStart;
150
- for (let m = j; m < L; m++) skip.add(m);
151
- }
152
- } else if (LINES[row[L - 1]] !== undefined || isWall(row[L - 1])) {
153
- segStart = center(L - 1);
154
- }
155
- let R = rr;
156
- while (R + 1 < row.length && row[R + 1] === "─") R++;
157
- let segEnd = (R + 1) * cw, drawEnd = (R + 1) * cw;
158
- if (ARROWS[row[R]] !== undefined) {
159
- drawEnd = R * cw;
160
- segEnd = isWall(row[R + 1]) ? center(R + 1) : center(R) + cw * 0.45;
161
- R--;
162
- } else if (ARROWS[row[R + 1]] !== undefined) {
163
- drawEnd = (R + 1) * cw;
164
- segEnd = isWall(row[R + 2]) ? center(R + 2) : center(R + 1) + cw * 0.45;
165
- } else if (LINES[row[R + 1]] !== undefined || isWall(row[R + 1])) {
166
- segEnd = center(R + 1);
167
- }
168
- const chars = row.slice(c, e).split("").filter((chr) => chr !== ZW);
169
- const mid = (segStart + segEnd) / 2, half = textWidth(chars) / 2 + 5;
170
- path += "M" + f(drawStart) + " " + f(cy) + "H" + f(mid - half) + " ";
171
- path += "M" + f(mid + half) + " " + f(cy) + "H" + f(drawEnd) + " ";
172
- texts += '<text x="' + f(mid) + '" y="' + f(cy + fs * 0.35) + '">' + esc(chars.join("")) + "</text>";
173
- for (let k = L; k <= R; k++) skip.add(k);
174
- c = e - 1;
175
- }
176
- // 영문은 격자 칸별 배치 · 한글 혼합은 예약 칸 중앙의 글자 묶음
177
- // 상자 내부 라벨은 실제 테두리 중앙 · ASCII의 반 칸 오차 보정
178
- // 테두리 없는 라벨은 예약 칸 기준 정렬
179
- const wall = (chr) => chr === "│" || chr === "◇" || chr === "├" || chr === "┤";
180
- const walls = (from, to) => {
181
- let l = from - 1, rgt = to;
182
- while (l >= 0 && (row[l] === " " || row[l] === ZW)) l--;
183
- while (rgt < row.length && (row[rgt] === " " || row[rgt] === ZW)) rgt++;
184
- return wall(row[l] || "") && wall(row[rgt] || "") ? [l, rgt] : null;
185
- };
186
- // 상자 내부와 라벨의 줄 수 홀짝 차이 · 반 줄 처짐 보정
187
- const lift = (box) => {
188
- const col = run.start;
189
- let top = r - 1, bottom = r + 1;
190
- while (top >= 0 && !isHBorder((rows[top] || "")[col])) top--;
191
- while (bottom < rows.length && !isHBorder((rows[bottom] || "")[col])) bottom++;
192
- // 실제 상자 모서리만 보정 · 시퀀스 생명선 제외
193
- const corner = (chr) => chr !== undefined && chr !== "─" && chr !== "═" && chr !== "╌" && (LINES[chr] !== undefined || chr === "◇");
194
- if (top < 0 || bottom >= rows.length || !corner((rows[top] || "")[box[0]]) || !corner((rows[bottom] || "")[box[0]])) return 0;
195
- let labelRows = 0;
196
- for (let k = top + 1; k < bottom; k++) {
197
- const inner = (rows[k] || "").slice(box[0] + 1, box[1]).split(ZW).join("").trim();
198
- if (inner.length > 0) labelRows++;
199
- }
200
- return (bottom - top - 1 - labelRows) % 2 === 1 ? -ch / 2 : 0;
201
- };
202
- const flush = () => {
203
- if (!run) return;
204
- const box = walls(run.start, run.end);
205
- const y = f(cy + fs * 0.35 + (box ? lift(box) : 0));
206
- const x = box ? f(((box[0] + 1) * cw + box[1] * cw) / 2) : run.wide ? f((run.start * cw + run.end * cw) / 2) : run.xs.join(" ");
207
- texts += '<text x="' + x + '" y="' + y + '">' + esc(run.chars.join("")) + "</text>";
208
- run = null;
209
- };
210
-
211
- for (let c = 0; c < row.length; c++) {
212
- const chr = row[c];
213
- const cx = c * cw + cw / 2, x0 = c * cw, x1 = x0 + cw;
214
-
215
- if (skip.has(c)) { flush(); continue; }
216
- if (chr === ZW) { if (run) run.end = c + 1; continue; }
217
-
218
- // 라벨 내부 단일 공백은 묶음 유지 · 연속 공백은 묶음 종료
219
- if (chr === " ") {
220
- const next = row[c + 1];
221
- const joins = run && next !== undefined && next !== " " && next !== ZW && !LINES[next] && !ARROWS[next] && next !== "◇";
222
- if (!joins) { flush(); continue; }
223
- }
224
-
225
- // 세로선 없는 ├·┤는 ASCII 가로선의 출발·도착점
226
- // 상자와의 여백까지 연결
227
- const above = rows[r - 1] ? rows[r - 1][c] : " ", below = rows[r + 1] ? rows[r + 1][c] : " ";
228
- const vertical = (v) => v !== undefined && LINES[v] !== undefined && /[UD]/.test(LINES[v]) || v === "◇";
229
- if ((chr === "├" || chr === "┤") && !vertical(above) && !vertical(below)) {
230
- flush();
231
- let l = c - 1, rgt = c + 1;
232
- while (l >= 0 && row[l] === " ") l--;
233
- while (rgt < row.length && row[rgt] === " ") rgt++;
234
- const gapL = isWall(row[l]) ? l * cw + cw / 2 : x0;
235
- const gapR = isWall(row[rgt]) ? rgt * cw + cw / 2 : x1;
236
- path += "M" + f(gapL) + " " + f(cy) + "H" + f(gapR) + " ";
237
- continue;
238
- }
239
-
240
- const ln = LINES[chr];
241
- if (ln) {
242
- flush();
243
- if (ln.indexOf("r") >= 0) {
244
- const ax = ln.indexOf("L") >= 0 ? x0 : x1, by = ln.indexOf("U") >= 0 ? y0 : y1;
245
- arcs += "M" + f(ax) + " " + f(cy) + "Q" + f(cx) + " " + f(cy) + " " + f(cx) + " " + f(by) + " ";
246
- } else {
247
- // 벽 접점까지 선 연장 · b 이중선, d 점선
248
- let seg = "";
249
- if (ln.indexOf("L") >= 0) seg += "M" + f(isWall(row[c - 1]) ? x0 - cw / 2 : x0) + " " + f(cy) + "H" + f(cx) + " ";
250
- if (ln.indexOf("R") >= 0) seg += "M" + f(cx) + " " + f(cy) + "H" + f(isWall(row[c + 1]) ? x1 + cw / 2 : x1) + " ";
251
- if (ln.indexOf("U") >= 0) seg += "M" + f(cx) + " " + f(y0) + "V" + f(cy) + " ";
252
- if (ln.indexOf("D") >= 0) seg += "M" + f(cx) + " " + f(cy) + "V" + f(y1) + " ";
253
- if (ln.indexOf("b") >= 0) bold += seg; else if (ln.indexOf("d") >= 0) dashed += seg; else path += seg;
254
- }
255
- continue;
256
- }
257
-
258
- const ar = ARROWS[chr];
259
- if (ar) {
260
- flush();
261
- // 화살촉의 벽 접점 보정
262
- const w = cw * 0.9, h = ch * 0.42;
263
- const tipR = isWall(row[c + 1]) ? x1 + cw / 2 : cx + w / 2, tipL = isWall(row[c - 1]) ? x0 - cw / 2 : cx - w / 2;
264
- const tipD = isHBorder(below) ? y1 + ch / 2 : cy + h / 2, tipU = isHBorder(above) ? y0 - ch / 2 : cy - h / 2;
265
- if (ar === "R") { path += "M" + f(x0) + " " + f(cy) + "H" + f(tipR - w) + " "; tris += "M" + f(tipR - w) + " " + f(cy - h / 2) + "L" + f(tipR) + " " + f(cy) + "L" + f(tipR - w) + " " + f(cy + h / 2) + "Z "; }
266
- if (ar === "L") { path += "M" + f(tipL + w) + " " + f(cy) + "H" + f(x1) + " "; tris += "M" + f(tipL + w) + " " + f(cy - h / 2) + "L" + f(tipL) + " " + f(cy) + "L" + f(tipL + w) + " " + f(cy + h / 2) + "Z "; }
267
- if (ar === "D") { path += "M" + f(cx) + " " + f(y0) + "V" + f(tipD - h) + " "; tris += "M" + f(cx - w / 2) + " " + f(tipD - h) + "L" + f(cx + w / 2) + " " + f(tipD - h) + "L" + f(cx) + " " + f(tipD) + "Z "; }
268
- if (ar === "U") { path += "M" + f(cx) + " " + f(tipU + h) + "V" + f(y1) + " "; tris += "M" + f(cx - w / 2) + " " + f(tipU + h) + "L" + f(cx + w / 2) + " " + f(tipU + h) + "L" + f(cx) + " " + f(tipU) + "Z "; }
269
- continue;
270
- }
271
-
272
- if (chr === "◇") {
273
- flush();
274
- const w = cw * 0.8, h = ch * 0.4;
275
- marks += "M" + f(cx) + " " + f(cy - h / 2) + "L" + f(cx + w / 2) + " " + f(cy) + "L" + f(cx) + " " + f(cy + h / 2) + "L" + f(cx - w / 2) + " " + f(cy) + "Z ";
276
- continue;
277
- }
278
-
279
- // 상태도 시작 ● · 클래스 상속 △
280
- if (chr === "●") {
281
- flush();
282
- const rr = cw * 0.45;
283
- dots += "M" + f(cx - rr) + " " + f(cy) + "a" + f(rr) + " " + f(rr) + " 0 1 0 " + f(rr * 2) + " 0a" + f(rr) + " " + f(rr) + " 0 1 0 " + f(-rr * 2) + " 0Z ";
284
- continue;
285
- }
286
-
287
- if (chr === "△") {
288
- flush();
289
- const w = cw * 0.9, h = ch * 0.42;
290
- marks += "M" + f(cx) + " " + f(cy - h / 2) + "L" + f(cx + w / 2) + " " + f(cy + h / 2) + "L" + f(cx - w / 2) + " " + f(cy + h / 2) + "Z ";
291
- path += "M" + f(cx) + " " + f(cy + h / 2) + "V" + f(y1) + " ";
292
- continue;
293
- }
294
-
295
- if (!run) run = {start: c, end: c + 1, xs: [], chars: [], wide: false};
296
- run.end = c + 1;
297
- run.xs.push(f(cx));
298
- run.chars.push(chr);
299
- if (WIDE.test(chr)) run.wide = true;
300
- }
301
-
302
- flush();
303
- });
304
-
305
- const W = f(cols * cw), H = f(rows.length * ch);
306
-
307
- return '<svg xmlns="http://www.w3.org/2000/svg" class="ascii-flow" viewBox="0 0 ' + W + " " + H + '" width="' + W + '" height="' + H + '">' +
308
- '<path d="' + path + '" fill="none" stroke="var(--app-color-text-muted)" stroke-width="1"/>' +
309
- '<path d="' + bold + '" fill="none" stroke="var(--app-color-text-muted)" stroke-width="2"/>' +
310
- '<path d="' + dashed + '" fill="none" stroke="var(--app-color-text-muted)" stroke-width="1" stroke-dasharray="3 3"/>' +
311
- '<path d="' + arcs + '" fill="none" stroke="var(--app-color-text-muted)" stroke-width="1"/>' +
312
- '<path d="' + tris + '" fill="var(--app-color-text-muted)"/>' +
313
- '<path d="' + dots + '" fill="var(--app-color-text-muted)"/>' +
314
- '<path d="' + marks + '" fill="var(--app-color-surface)" stroke="var(--app-color-text-muted)" stroke-width="1"/>' +
315
- '<g font-family="var(--app-font-mono)" font-size="' + fs + '" text-anchor="middle" fill="var(--app-color-text)">' + texts + "</g></svg>";
316
- }
317
-
318
- /**
319
- * Mermaid 원문의 격자 SVG 문자열
320
- */
321
- export const renderFlow = (source) => gridToSvg(renderMermaidASCII(widenCjk(source), ASCII_OPT));