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.
- package/CHANGELOG.md +39 -0
- package/README.md +16 -9
- package/dist/browser.js +14 -16
- package/dist/cli.css +2 -968
- package/dist/cli.js +92 -2318
- package/dist/config.js +1 -0
- package/dist/constant/copy.d.ts +100 -0
- package/dist/constant/site.d.ts +20 -0
- package/dist/constant/status.d.ts +19 -0
- package/dist/copy-C1uzvEnr.js +103 -0
- package/dist/group-path-XAThmzvC.js +10 -0
- package/dist/load-site-B-u5-jv0.js +2343 -0
- package/dist/logger-Dv1i9xSV.js +9 -0
- package/dist/mermaid/THIRD-PARTY-NOTICES.txt +3134 -0
- package/dist/mermaid/chunk--TN-qHgG.js +2 -0
- package/dist/mermaid/chunk-0kM1Lv4D.js +2 -0
- package/dist/mermaid/chunk-3FHei3pH.js +2001 -0
- package/dist/mermaid/chunk-3_2p2GqN.js +2 -0
- package/dist/mermaid/chunk-6HLei9i0.js +84 -0
- package/dist/mermaid/chunk-7Sz3kAwl.js +2214 -0
- package/dist/mermaid/chunk-7Tarqbpa.js +18047 -0
- package/dist/mermaid/chunk-9kkt8lzL.js +10 -0
- package/dist/mermaid/chunk-B--pNBQF.js +884 -0
- package/dist/mermaid/chunk-B4F-pysn.js +4241 -0
- package/dist/mermaid/chunk-B58sEIY_.js +14 -0
- package/dist/mermaid/chunk-B9_UWzSH.js +1517 -0
- package/dist/mermaid/chunk-BB8uZHwY.js +75 -0
- package/dist/mermaid/chunk-BFfk0LEc.js +717 -0
- package/dist/mermaid/chunk-BKri0qr7.js +89 -0
- package/dist/mermaid/chunk-BL404pH5.js +26 -0
- package/dist/mermaid/chunk-BLeiQSWo.js +354 -0
- package/dist/mermaid/chunk-BOmH_UFd.js +2 -0
- package/dist/mermaid/chunk-BSej9Nhy.js +1361 -0
- package/dist/mermaid/chunk-BYzrzvCI.js +12 -0
- package/dist/mermaid/chunk-BariMtWr.js +3020 -0
- package/dist/mermaid/chunk-BcrBoMwH.js +2352 -0
- package/dist/mermaid/chunk-Bok8z-Yo.js +67 -0
- package/dist/mermaid/chunk-BpTHLKQM.js +1970 -0
- package/dist/mermaid/chunk-BqjuxRsC.js +733 -0
- package/dist/mermaid/chunk-BuvPd-s5.js +1073 -0
- package/dist/mermaid/chunk-BvVehae3.js +5069 -0
- package/dist/mermaid/chunk-C-JABYpd.js +3075 -0
- package/dist/mermaid/chunk-C9XlTm4w.js +2 -0
- package/dist/mermaid/chunk-CApNbSU9.js +6 -0
- package/dist/mermaid/chunk-CCM2AH-C.js +9 -0
- package/dist/mermaid/chunk-CD1E0F_N.js +2 -0
- package/dist/mermaid/chunk-CDWvKG7D.js +2 -0
- package/dist/mermaid/chunk-CEVPpC1F.js +34 -0
- package/dist/mermaid/chunk-CLDr12lf.js +47504 -0
- package/dist/mermaid/chunk-CLVYqOBI.js +61 -0
- package/dist/mermaid/chunk-CLeOkFb5.js +15 -0
- package/dist/mermaid/chunk-CLgRAtOz.js +37 -0
- package/dist/mermaid/chunk-CMqFNUBl.js +134 -0
- package/dist/mermaid/chunk-CTPaBJXB.js +2 -0
- package/dist/mermaid/chunk-CVi6IGBL.js +2 -0
- package/dist/mermaid/chunk-CWV_EN-4.js +4745 -0
- package/dist/mermaid/chunk-CZ0dAs3g.js +2 -0
- package/dist/mermaid/chunk-C_YGy4Fn.js +236 -0
- package/dist/mermaid/chunk-CcVH22Yt.js +100 -0
- package/dist/mermaid/chunk-CgG6CD9_.js +7175 -0
- package/dist/mermaid/chunk-CgOP6IDB.js +3397 -0
- package/dist/mermaid/chunk-Cgg2UTSf.js +13 -0
- package/dist/mermaid/chunk-ChJfJJ1A.js +214 -0
- package/dist/mermaid/chunk-Ck5XRA46.js +447 -0
- package/dist/mermaid/chunk-Ckuw16cl.js +203 -0
- package/dist/mermaid/chunk-CpAbRCbF.js +1880 -0
- package/dist/mermaid/chunk-CuYekbSv.js +2 -0
- package/dist/mermaid/chunk-CwS4wZ5a.js +2 -0
- package/dist/mermaid/chunk-D-bcrBO3.js +82 -0
- package/dist/mermaid/chunk-D3IEAkCW.js +76 -0
- package/dist/mermaid/chunk-D8XfBJ_K.js +645 -0
- package/dist/mermaid/chunk-D9Ddd9y4.js +2228 -0
- package/dist/mermaid/chunk-DBh1mIbK.js +12 -0
- package/dist/mermaid/chunk-DD6Df6Ic.js +5 -0
- package/dist/mermaid/chunk-DGVDaXjx.js +8 -0
- package/dist/mermaid/chunk-DK9KqIXj.js +632 -0
- package/dist/mermaid/chunk-DORnIAsy.js +4487 -0
- package/dist/mermaid/chunk-DU69_VcF.js +363 -0
- package/dist/mermaid/chunk-DVNvSFoz.js +2144 -0
- package/dist/mermaid/chunk-DZe-4kde.js +20 -0
- package/dist/mermaid/chunk-D_a06Rz4.js +287 -0
- package/dist/mermaid/chunk-DfyFJJi2.js +972 -0
- package/dist/mermaid/chunk-DhKYPp1Z.js +2 -0
- package/dist/mermaid/chunk-DjFqgNTG.js +94 -0
- package/dist/mermaid/chunk-DmbDDrr-.js +65 -0
- package/dist/mermaid/chunk-DpuxlyL_.js +696 -0
- package/dist/mermaid/chunk-DqGhppWe.js +2 -0
- package/dist/mermaid/chunk-DqPZxHXa.js +5 -0
- package/dist/mermaid/chunk-Dt_1Jyif.js +293 -0
- package/dist/mermaid/chunk-DyQBE_NW.js +1935 -0
- package/dist/mermaid/chunk-JckyC4mW.js +1009 -0
- package/dist/mermaid/chunk-K7Myuj2-.js +1334 -0
- package/dist/mermaid/chunk-KuPYb48V.js +275 -0
- package/dist/mermaid/chunk-MFVRUoO0.js +4020 -0
- package/dist/mermaid/chunk-MbInCS_6.js +28 -0
- package/dist/mermaid/chunk-MexOLyKV.js +355 -0
- package/dist/mermaid/chunk-NnbDtJzg.js +21856 -0
- package/dist/mermaid/chunk-SGpOYZbI.js +2139 -0
- package/dist/mermaid/chunk-TbxLDlcU.js +198 -0
- package/dist/mermaid/chunk-TftHqGLg.js +490 -0
- package/dist/mermaid/chunk-UjQ8vA_Z.js +4138 -0
- package/dist/mermaid/chunk-YgmVTKqc.js +16 -0
- package/dist/mermaid/chunk-_a9EZE1v.js +1798 -0
- package/dist/mermaid/chunk-dTb8jqeP.js +2 -0
- package/dist/mermaid/chunk-fFdMfF8S.js +2240 -0
- package/dist/mermaid/chunk-feYbMzBj.js +1267 -0
- package/dist/mermaid/chunk-g3Pn8DkW.js +46 -0
- package/dist/mermaid/chunk-hM77LvCb.js +19832 -0
- package/dist/mermaid/chunk-hUDb-eAW.js +527 -0
- package/dist/mermaid/chunk-tpABtZ-3.js +4252 -0
- package/dist/mermaid/chunk-xIbz3oFK.js +59 -0
- package/dist/mermaid/chunk-yBz-vst0.js +976 -0
- package/dist/mermaid/mermaid-BhtR3wyB.js +1145 -0
- package/dist/run-site-command-cqCjpIlc.js +129 -0
- package/dist/site-config-bllORGpm.js +90 -0
- package/dist/site.js +1 -0
- package/dist/start-dev-server-x7Ja5uwv.js +184 -0
- package/dist/to-change-queue-DbEnZGNK.js +34 -0
- package/dist/to-error-message-pF6_7wGK.js +16 -0
- package/dist/type/group-path.d.ts +7 -0
- package/dist/type/site-config.d.ts +57 -0
- package/package.json +34 -17
- package/src/component/widget/cursor-face/_constant/cursor-face.ts +0 -14
- package/src/component/widget/cursor-face/wg-cursor-face.css +0 -15
- package/src/component/widget/cursor-face/wg-cursor-face.tsx +0 -153
- package/src/component/widget/navigation/_constant/locale.ts +0 -9
- package/src/component/widget/navigation/_constant/navigation-data-schema.ts +0 -33
- package/src/component/widget/navigation/_constant/navigation.ts +0 -9
- package/src/component/widget/navigation/_constant/reading-line.ts +0 -4
- package/src/component/widget/navigation/_function/restore-navigation-scroll.ts +0 -59
- package/src/component/widget/navigation/_function/to-doc-groups/_to-doc-branches.ts +0 -15
- package/src/component/widget/navigation/_function/to-doc-groups/_to-doc-group-branches.ts +0 -70
- package/src/component/widget/navigation/_function/to-doc-groups/to-doc-groups.ts +0 -15
- package/src/component/widget/navigation/_function/to-navigation-data/_to-navigation-document.ts +0 -13
- package/src/component/widget/navigation/_function/to-navigation-data/_to-navigation-group.ts +0 -13
- package/src/component/widget/navigation/_function/to-navigation-data/to-navigation-data.ts +0 -18
- package/src/component/widget/navigation/_function/to-navigation-json.ts +0 -8
- package/src/component/widget/navigation/_function/to-toc-groups.ts +0 -38
- package/src/component/widget/navigation/_type/doc-branch.ts +0 -11
- package/src/component/widget/navigation/_type/doc-group.ts +0 -24
- package/src/component/widget/navigation/_type/navigation-data.ts +0 -75
- package/src/component/widget/navigation/_type/navigation-focus-snapshot.ts +0 -17
- package/src/component/widget/navigation/_type/toc-group.ts +0 -39
- package/src/component/widget/navigation/_wg-navigation-nav-list.css +0 -48
- package/src/component/widget/navigation/_wg-navigation-nav-list.tsx +0 -61
- package/src/component/widget/navigation/_wg-navigation-nav.css +0 -21
- package/src/component/widget/navigation/_wg-navigation-nav.tsx +0 -33
- package/src/component/widget/navigation/_wg-navigation-scroll.tsx +0 -117
- package/src/component/widget/navigation/_wg-navigation-toc.css +0 -91
- package/src/component/widget/navigation/_wg-navigation-toc.tsx +0 -114
- package/src/component/widget/navigation/wg-navigation.css +0 -282
- package/src/component/widget/navigation/wg-navigation.tsx +0 -420
- package/src/component/widget/prose/_constant/heading-highlight.ts +0 -4
- package/src/component/widget/prose/_constant/section.ts +0 -7
- package/src/component/widget/prose/_constant/status.ts +0 -9
- package/src/component/widget/prose/_function/has-block-content.ts +0 -30
- package/src/component/widget/prose/_function/rehype-head.ts +0 -31
- package/src/component/widget/prose/_function/rehype-sections/_to-section-headings.ts +0 -20
- package/src/component/widget/prose/_function/rehype-sections/rehype-sections.ts +0 -21
- package/src/component/widget/prose/_function/rehype-tables.ts +0 -22
- package/src/component/widget/prose/_function/remark-blocks.ts +0 -112
- package/src/component/widget/prose/_function/remark-flow.ts +0 -26
- package/src/component/widget/prose/_function/remark-links.ts +0 -33
- package/src/component/widget/prose/_function/remark-parts.ts +0 -26
- package/src/component/widget/prose/_function/remark-status.ts +0 -59
- package/src/component/widget/prose/_function/remark-swatch.ts +0 -16
- package/src/component/widget/prose/_function/remark-unknown-directives.ts +0 -25
- package/src/component/widget/prose/wg-prose.css +0 -484
- package/src/component/widget/prose/wg-prose.tsx +0 -43
- package/src/component/widget/shell/wg-shell.css +0 -63
- package/src/component/widget/shell/wg-shell.tsx +0 -88
- package/src/component/widget/shell-controls/wg-shell-controls.css +0 -4
- package/src/component/widget/shell-controls/wg-shell-controls.tsx +0 -170
- package/src/constant/asset.ts +0 -63
- package/src/constant/copy.ts +0 -133
- package/src/constant/doc.ts +0 -13
- package/src/constant/font.ts +0 -34
- package/src/constant/site.ts +0 -24
- package/src/constant/status.ts +0 -15
- package/src/entry/browser/browser.tsx +0 -22
- package/src/entry/cli/_constant/cli.ts +0 -19
- package/src/entry/cli/_constant/crawling.ts +0 -14
- package/src/entry/cli/_constant/snippet.ts +0 -21
- package/src/entry/cli/_function/create-app.tsx +0 -57
- package/src/entry/cli/_function/create-dev-app.ts +0 -87
- package/src/entry/cli/_function/create-processor/_rehype-assets.ts +0 -70
- package/src/entry/cli/_function/create-processor/_rehype-heading-ids.ts +0 -34
- package/src/entry/cli/_function/create-processor/_remark-report.ts +0 -15
- package/src/entry/cli/_function/create-processor/_remark-snippets.ts +0 -117
- package/src/entry/cli/_function/create-processor/_shiki-transformers.ts +0 -91
- package/src/entry/cli/_function/create-processor/create-processor.ts +0 -64
- package/src/entry/cli/_function/read-docs.ts +0 -128
- package/src/entry/cli/_function/read-home.ts +0 -32
- package/src/entry/cli/_function/to-crawling-app.ts +0 -42
- package/src/entry/cli/cli.test.ts +0 -804
- package/src/entry/cli/cli.ts +0 -206
- package/src/env.d.ts +0 -6
- package/src/page/doc/pg-doc.tsx +0 -35
- package/src/page/home/pg-home.tsx +0 -40
- package/src/store/navigation/create-navigation-stores.test.ts +0 -74
- package/src/store/navigation/create-navigation-stores.ts +0 -59
- package/src/store/navigation/navigation-state.ts +0 -36
- package/src/store/navigation/navigation-storage.ts +0 -9
- package/src/store/navigation/to-navigation-scroll-state.ts +0 -42
- package/src/style/base.css +0 -28
- package/src/style/token.css +0 -188
- package/src/type/doc-content.ts +0 -15
- package/src/type/doc-data.ts +0 -38
- package/src/type/doc-file-data.ts +0 -21
- package/src/type/doc-heading.ts +0 -17
- package/src/type/doc-outline.ts +0 -16
- package/src/type/doc-section.ts +0 -9
- package/src/type/doc.ts +0 -20
- package/src/type/group-path.ts +0 -7
- package/src/type/site-assets.ts +0 -25
- package/src/type/site-config.ts +0 -73
- package/src/type/vfile-data.d.ts +0 -10
- package/src/util/async/to-change-queue.test.ts +0 -58
- package/src/util/async/to-change-queue.ts +0 -43
- package/src/util/dom/find-hash-target.ts +0 -18
- package/src/util/dom/reveal-hash-target.ts +0 -15
- package/src/util/error/to-error-message.ts +0 -10
- package/src/util/file/to-asset-path.ts +0 -26
- package/src/util/font/to-font-css.test.ts +0 -41
- package/src/util/font/to-font-css.ts +0 -28
- 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, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
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));
|