@ktrysmt/beautiful-mermaid 1.3.0 → 1.4.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/dist/index.js +365 -154
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/cjk.test.ts +240 -0
- package/src/__tests__/testdata/unicode/cjk_class.txt +15 -0
- package/src/__tests__/testdata/unicode/cjk_er.txt +8 -0
- package/src/__tests__/testdata/unicode/cjk_flowchart.txt +8 -0
- package/src/__tests__/testdata/unicode/cjk_sequence.txt +17 -0
- package/src/__tests__/testdata/unicode/emoji_flowchart.txt +8 -0
- package/src/ascii/canvas.ts +5 -9
- package/src/ascii/cjk.ts +298 -0
- package/src/ascii/class-diagram.ts +7 -10
- package/src/ascii/draw.ts +10 -17
- package/src/ascii/edge-routing.ts +3 -1
- package/src/ascii/er-diagram.ts +10 -18
- package/src/ascii/grid.ts +125 -14
- package/src/ascii/multiline-utils.ts +3 -2
- package/src/ascii/sequence.ts +11 -23
- package/src/ascii/shapes/rectangle.ts +9 -10
- package/src/ascii/shapes/special.ts +7 -18
- package/src/ascii/shapes/stadium.ts +4 -9
- package/src/ascii/xychart.ts +16 -15
package/dist/index.js
CHANGED
|
@@ -1013,6 +1013,201 @@ function colorizeText(text, hex, mode) {
|
|
|
1013
1013
|
return `${code}${text}${RESET}`;
|
|
1014
1014
|
}
|
|
1015
1015
|
|
|
1016
|
+
// src/ascii/cjk.ts
|
|
1017
|
+
var CJK_PAD = "\uE000";
|
|
1018
|
+
function isFullwidthChar(code) {
|
|
1019
|
+
return (
|
|
1020
|
+
// --- CJK ---
|
|
1021
|
+
code >= 4352 && code <= 4447 || // Hangul Jamo
|
|
1022
|
+
code >= 9001 && code <= 9002 || // Angle Brackets (EAW=W)
|
|
1023
|
+
code >= 11904 && code <= 12031 || // CJK Radicals Supplement
|
|
1024
|
+
code >= 12032 && code <= 12255 || // Kangxi Radicals
|
|
1025
|
+
code >= 12288 && code <= 12351 || // CJK Symbols and Punctuation
|
|
1026
|
+
code >= 12352 && code <= 12447 || // Hiragana
|
|
1027
|
+
code >= 12448 && code <= 12543 || // Katakana
|
|
1028
|
+
code >= 12544 && code <= 12591 || // Bopomofo
|
|
1029
|
+
code >= 12592 && code <= 12687 || // Hangul Compatibility Jamo
|
|
1030
|
+
code >= 12688 && code <= 12799 || // Kanbun + extensions
|
|
1031
|
+
code >= 12800 && code <= 13311 || // Enclosed CJK + Compatibility
|
|
1032
|
+
code >= 13312 && code <= 19903 || // CJK Extension A
|
|
1033
|
+
code >= 19968 && code <= 40959 || // CJK Unified Ideographs
|
|
1034
|
+
code >= 44032 && code <= 55215 || // Hangul Syllables
|
|
1035
|
+
code >= 63744 && code <= 64255 || // CJK Compatibility Ideographs
|
|
1036
|
+
code >= 65072 && code <= 65103 || // CJK Compatibility Forms
|
|
1037
|
+
code >= 65280 && code <= 65376 || // Fullwidth ASCII
|
|
1038
|
+
code >= 65504 && code <= 65510 || // Fullwidth symbols
|
|
1039
|
+
// --- Emoji SMP (EAW=W, always 2 columns) ---
|
|
1040
|
+
code === 126980 || code === 127183 || code === 127374 || code >= 127377 && code <= 127386 || code >= 127456 && code <= 127487 || // Regional indicators
|
|
1041
|
+
code >= 127488 && code <= 127490 || // Enclosed Ideographic Supplement
|
|
1042
|
+
code >= 127504 && code <= 127547 || // Squared CJK Unified Ideograph
|
|
1043
|
+
code >= 127552 && code <= 127560 || // Tortoise Shell Bracketed CJK
|
|
1044
|
+
code >= 127568 && code <= 127569 || // Circled Ideograph
|
|
1045
|
+
code >= 127744 && code <= 128591 || // Misc Symbols & Emoticons
|
|
1046
|
+
code >= 128640 && code <= 128767 || // Transport & Map
|
|
1047
|
+
code >= 128992 && code <= 129003 || // Colored circles/squares
|
|
1048
|
+
code >= 129280 && code <= 129535 || // Supplemental Symbols
|
|
1049
|
+
code >= 129536 && code <= 129647 || // Chess Symbols
|
|
1050
|
+
code >= 129648 && code <= 129791 || // Symbols Extended-A
|
|
1051
|
+
// --- BMP emoji (EAW=W only, always 2 columns) ---
|
|
1052
|
+
code === 8986 || code === 8987 || code >= 9193 && code <= 9196 || code === 9200 || code === 9203 || code === 9725 || code === 9726 || code === 9748 || code === 9749 || code >= 9800 && code <= 9811 || // zodiac
|
|
1053
|
+
code === 9855 || code === 9875 || code === 9889 || // ⚡ EAW=W (NOT ⚠ which is EAW=N)
|
|
1054
|
+
code === 9898 || code === 9899 || code === 9917 || code === 9918 || code === 9924 || code === 9925 || code === 9934 || code === 9940 || code === 9962 || code === 9970 || code === 9971 || code === 9973 || code === 9978 || code === 9981 || code === 9989 || // ✅ EAW=W
|
|
1055
|
+
code === 9994 || code === 9995 || // ✊✋ EAW=W
|
|
1056
|
+
code === 10024 || // ✨ EAW=W
|
|
1057
|
+
code === 10060 || code === 10062 || code >= 10067 && code <= 10069 || code === 10071 || code >= 10133 && code <= 10135 || code === 10160 || code === 10175 || code === 11035 || code === 11036 || code === 11088 || code === 11093 || code === 12336 || code === 12349 || code === 12951 || code === 12953 || // --- CJK Extension B through I + Compatibility Supplement ---
|
|
1058
|
+
// FIX: replaced overly broad `>= 0x20000` with precise ranges
|
|
1059
|
+
code >= 131072 && code <= 173791 || // CJK Extension B
|
|
1060
|
+
code >= 173824 && code <= 177983 || // CJK Extension C
|
|
1061
|
+
code >= 177984 && code <= 178207 || // CJK Extension D
|
|
1062
|
+
code >= 178208 && code <= 183983 || // CJK Extension E
|
|
1063
|
+
code >= 183984 && code <= 191471 || // CJK Extension F
|
|
1064
|
+
code >= 194560 && code <= 195103 || // CJK Compatibility Ideographs Supplement
|
|
1065
|
+
code >= 196608 && code <= 201551 || // CJK Extension G
|
|
1066
|
+
code >= 201552 && code <= 205743 || // CJK Extension H
|
|
1067
|
+
code >= 191472 && code <= 194559
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
function isEmojiModifiable(code) {
|
|
1071
|
+
return code === 9888 || // ⚠
|
|
1072
|
+
code === 9986 || // ✂
|
|
1073
|
+
code === 9992 || code === 9993 || // ✈✉
|
|
1074
|
+
code === 9996 || code === 9997 || // ✌✍
|
|
1075
|
+
code === 9999 || code === 10002 || // ✏✒
|
|
1076
|
+
code === 10004 || code === 10006 || // ✔✖
|
|
1077
|
+
code === 10013 || code === 10017 || // ✝✡
|
|
1078
|
+
code === 10035 || code === 10036 || // ✳✴
|
|
1079
|
+
code === 10052 || code === 10055 || // ❄❇
|
|
1080
|
+
code === 10083 || code === 10084 || // ❣❤
|
|
1081
|
+
code === 10145 || // ➡
|
|
1082
|
+
code === 10548 || code === 10549 || // ⤴⤵
|
|
1083
|
+
code >= 11013 && code <= 11015 || // ⬅⬆⬇
|
|
1084
|
+
// FIX: added missing EAW=N emoji (©️ ®️ ™️ ℹ️ ↔️ etc.)
|
|
1085
|
+
code === 169 || // © copyright
|
|
1086
|
+
code === 174 || // ® registered
|
|
1087
|
+
code === 8482 || // ™ trade mark
|
|
1088
|
+
code === 8505 || // ℹ information
|
|
1089
|
+
code >= 8596 && code <= 8601 || // ↔↕↖↗↘↙
|
|
1090
|
+
code >= 8617 && code <= 8618 || // ↩↪
|
|
1091
|
+
code === 8986 || code === 8987 || // ⌚⌛ (also EAW=W, but FE0F doesn't hurt)
|
|
1092
|
+
code >= 9193 && code <= 9203 || // ⏩-⏳
|
|
1093
|
+
code >= 9208 && code <= 9210 || // ⏸⏹⏺
|
|
1094
|
+
code === 9642 || code === 9643 || // ▪▫
|
|
1095
|
+
code === 9654 || code === 9664 || // ▶◀
|
|
1096
|
+
code === 9723 || code === 9724 || // ◻◼
|
|
1097
|
+
code === 9728 || code === 9729 || // ☀☁
|
|
1098
|
+
code >= 9730 && code <= 9732 || // ☂☃☄
|
|
1099
|
+
code === 9742 || // ☎
|
|
1100
|
+
code === 9745 || // ☑
|
|
1101
|
+
code >= 9752 && code <= 9757 || // ☘-☝
|
|
1102
|
+
code === 9760 || // ☠
|
|
1103
|
+
code === 9762 || code === 9763 || // ☢☣
|
|
1104
|
+
code === 9766 || code === 9770 || // ☦☪
|
|
1105
|
+
code === 9774 || code === 9775 || // ☮☯
|
|
1106
|
+
code >= 9784 && code <= 9786 || // ☸☹☺
|
|
1107
|
+
code === 9792 || code === 9794 || // ♀♂
|
|
1108
|
+
code >= 9824 && code <= 9832 || // ♠-♨
|
|
1109
|
+
code === 9851 || code === 9854;
|
|
1110
|
+
}
|
|
1111
|
+
function isZeroWidth(code) {
|
|
1112
|
+
return code >= 65024 && code <= 65039 || // Variation Selectors (VS1-VS16)
|
|
1113
|
+
code === 8203 || // Zero Width Space
|
|
1114
|
+
code === 8204 || // Zero Width Non-Joiner
|
|
1115
|
+
code === 8205 || // Zero Width Joiner
|
|
1116
|
+
code === 65279 || // BOM / Zero Width No-Break Space
|
|
1117
|
+
code >= 917760 && code <= 917999 || // Variation Selectors Supplement
|
|
1118
|
+
// FIX: added combining marks (zero display width)
|
|
1119
|
+
code >= 768 && code <= 879 || // Combining Diacritical Marks
|
|
1120
|
+
code >= 1155 && code <= 1161 || // Combining Cyrillic
|
|
1121
|
+
code >= 1425 && code <= 1469 || // Hebrew combining
|
|
1122
|
+
code >= 1552 && code <= 1562 || // Arabic combining
|
|
1123
|
+
code >= 1611 && code <= 1631 || // Arabic combining
|
|
1124
|
+
code >= 8400 && code <= 8447;
|
|
1125
|
+
}
|
|
1126
|
+
function displayWidth(str) {
|
|
1127
|
+
let w = 0;
|
|
1128
|
+
let prevWasNarrowEmoji = false;
|
|
1129
|
+
for (const ch of str) {
|
|
1130
|
+
const code = ch.codePointAt(0);
|
|
1131
|
+
if (code === void 0) continue;
|
|
1132
|
+
if (code === 65039) {
|
|
1133
|
+
if (prevWasNarrowEmoji) {
|
|
1134
|
+
w += 1;
|
|
1135
|
+
prevWasNarrowEmoji = false;
|
|
1136
|
+
}
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
if (isZeroWidth(code)) continue;
|
|
1140
|
+
if (isFullwidthChar(code)) {
|
|
1141
|
+
w += 2;
|
|
1142
|
+
prevWasNarrowEmoji = false;
|
|
1143
|
+
} else {
|
|
1144
|
+
w += 1;
|
|
1145
|
+
prevWasNarrowEmoji = isEmojiModifiable(code);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
return w;
|
|
1149
|
+
}
|
|
1150
|
+
function drawCJKText(canvas, x, y, text, forceOverwrite = false, roleCanvas, role, maxCols) {
|
|
1151
|
+
let offset = 0;
|
|
1152
|
+
let lastWrittenCx = -1;
|
|
1153
|
+
let lastWasNarrowEmoji = false;
|
|
1154
|
+
let lastCharWritten = false;
|
|
1155
|
+
const h = canvas[0]?.length ?? 0;
|
|
1156
|
+
for (const ch of text) {
|
|
1157
|
+
const code = ch.codePointAt(0);
|
|
1158
|
+
if (code === void 0) continue;
|
|
1159
|
+
if (code === 65039) {
|
|
1160
|
+
if (lastWasNarrowEmoji && lastCharWritten) {
|
|
1161
|
+
if (maxCols === void 0 || offset < maxCols) {
|
|
1162
|
+
if (lastWrittenCx >= 0 && lastWrittenCx < canvas.length && y >= 0 && y < h) {
|
|
1163
|
+
canvas[lastWrittenCx][y] = (canvas[lastWrittenCx][y] ?? "") + ch;
|
|
1164
|
+
}
|
|
1165
|
+
const px = x + offset;
|
|
1166
|
+
if (px >= 0 && px < canvas.length && y >= 0 && y < h) {
|
|
1167
|
+
canvas[px][y] = CJK_PAD;
|
|
1168
|
+
}
|
|
1169
|
+
offset++;
|
|
1170
|
+
}
|
|
1171
|
+
lastWasNarrowEmoji = false;
|
|
1172
|
+
} else if (lastCharWritten && lastWrittenCx >= 0 && lastWrittenCx < canvas.length && y >= 0 && y < h) {
|
|
1173
|
+
canvas[lastWrittenCx][y] = (canvas[lastWrittenCx][y] ?? "") + ch;
|
|
1174
|
+
}
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
if (isZeroWidth(code)) continue;
|
|
1178
|
+
const charWidth = isFullwidthChar(code) ? 2 : 1;
|
|
1179
|
+
if (maxCols !== void 0 && offset + charWidth > maxCols) break;
|
|
1180
|
+
const cx = x + offset;
|
|
1181
|
+
let written = false;
|
|
1182
|
+
if (cx >= 0 && cx < canvas.length && y >= 0 && y < h) {
|
|
1183
|
+
if (forceOverwrite || canvas[cx][y] === " ") {
|
|
1184
|
+
canvas[cx][y] = ch;
|
|
1185
|
+
if (roleCanvas && role !== void 0 && cx < roleCanvas.length && y < (roleCanvas[0]?.length ?? 0)) {
|
|
1186
|
+
roleCanvas[cx][y] = role;
|
|
1187
|
+
}
|
|
1188
|
+
written = true;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
if (written) {
|
|
1192
|
+
lastWrittenCx = cx;
|
|
1193
|
+
}
|
|
1194
|
+
lastCharWritten = written;
|
|
1195
|
+
offset++;
|
|
1196
|
+
if (isFullwidthChar(code)) {
|
|
1197
|
+
lastWasNarrowEmoji = false;
|
|
1198
|
+
if (written) {
|
|
1199
|
+
const px = x + offset;
|
|
1200
|
+
if (px >= 0 && px < canvas.length && y >= 0 && y < h) {
|
|
1201
|
+
canvas[px][y] = CJK_PAD;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
offset++;
|
|
1205
|
+
} else {
|
|
1206
|
+
lastWasNarrowEmoji = isEmojiModifiable(code);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1016
1211
|
// src/ascii/canvas.ts
|
|
1017
1212
|
function mkCanvas(x, y) {
|
|
1018
1213
|
const canvas = [];
|
|
@@ -1173,6 +1368,7 @@ function canvasToString(canvas, options) {
|
|
|
1173
1368
|
if (colorMode === "none" || !roleCanvas) {
|
|
1174
1369
|
let line = "";
|
|
1175
1370
|
for (let x = 0; x <= maxX; x++) {
|
|
1371
|
+
if (canvas[x][y] === CJK_PAD) continue;
|
|
1176
1372
|
line += canvas[x][y];
|
|
1177
1373
|
}
|
|
1178
1374
|
lines.push(line);
|
|
@@ -1180,6 +1376,7 @@ function canvasToString(canvas, options) {
|
|
|
1180
1376
|
const chars = [];
|
|
1181
1377
|
const roles = [];
|
|
1182
1378
|
for (let x = 0; x <= maxX; x++) {
|
|
1379
|
+
if (canvas[x][y] === CJK_PAD) continue;
|
|
1183
1380
|
chars.push(canvas[x][y]);
|
|
1184
1381
|
roles.push(roleCanvas[x]?.[y] ?? null);
|
|
1185
1382
|
}
|
|
@@ -1230,14 +1427,8 @@ function flipRoleCanvasVertically(roleCanvas) {
|
|
|
1230
1427
|
return roleCanvas;
|
|
1231
1428
|
}
|
|
1232
1429
|
function drawText(canvas, start, text, forceOverwrite = false) {
|
|
1233
|
-
increaseSize(canvas, start.x + text
|
|
1234
|
-
|
|
1235
|
-
const x = start.x + i;
|
|
1236
|
-
const current = canvas[x][start.y];
|
|
1237
|
-
if (forceOverwrite || current === " ") {
|
|
1238
|
-
canvas[x][start.y] = text[i];
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1430
|
+
increaseSize(canvas, start.x + displayWidth(text), start.y);
|
|
1431
|
+
drawCJKText(canvas, start.x, start.y, text, forceOverwrite);
|
|
1241
1432
|
}
|
|
1242
1433
|
function setCanvasSizeToGrid(canvas, columnWidth, rowHeight) {
|
|
1243
1434
|
let maxX = 0;
|
|
@@ -1415,6 +1606,18 @@ function buildSgMap(mSgs, aSgs, result) {
|
|
|
1415
1606
|
}
|
|
1416
1607
|
}
|
|
1417
1608
|
|
|
1609
|
+
// src/ascii/multiline-utils.ts
|
|
1610
|
+
function splitLines(label) {
|
|
1611
|
+
return label.split("\n");
|
|
1612
|
+
}
|
|
1613
|
+
function maxLineWidth(label) {
|
|
1614
|
+
const lines = splitLines(label);
|
|
1615
|
+
return Math.max(...lines.map((l) => displayWidth(l)), 0);
|
|
1616
|
+
}
|
|
1617
|
+
function lineCount(label) {
|
|
1618
|
+
return splitLines(label).length;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1418
1621
|
// src/ascii/pathfinder.ts
|
|
1419
1622
|
var MinHeap = class {
|
|
1420
1623
|
items = [];
|
|
@@ -1703,7 +1906,7 @@ function determinePath(graph, edge) {
|
|
|
1703
1906
|
}
|
|
1704
1907
|
function determineLabelLine(graph, edge) {
|
|
1705
1908
|
if (edge.text.length === 0) return;
|
|
1706
|
-
const lenLabel = edge.text
|
|
1909
|
+
const lenLabel = maxLineWidth(edge.text);
|
|
1707
1910
|
const pathLen = edge.path.length;
|
|
1708
1911
|
const isVerticalFlow = graph.config.graphDirection === "TD";
|
|
1709
1912
|
const segments = [];
|
|
@@ -1904,18 +2107,6 @@ function processBundles(graph) {
|
|
|
1904
2107
|
}
|
|
1905
2108
|
}
|
|
1906
2109
|
|
|
1907
|
-
// src/ascii/multiline-utils.ts
|
|
1908
|
-
function splitLines(label) {
|
|
1909
|
-
return label.split("\n");
|
|
1910
|
-
}
|
|
1911
|
-
function maxLineWidth(label) {
|
|
1912
|
-
const lines = splitLines(label);
|
|
1913
|
-
return Math.max(...lines.map((l) => l.length), 0);
|
|
1914
|
-
}
|
|
1915
|
-
function lineCount(label) {
|
|
1916
|
-
return splitLines(label).length;
|
|
1917
|
-
}
|
|
1918
|
-
|
|
1919
2110
|
// src/ascii/shapes/corners.ts
|
|
1920
2111
|
var SHAPE_CORNERS = {
|
|
1921
2112
|
// Standard rectangular shapes
|
|
@@ -1994,9 +2185,10 @@ function getCorners(shape, useAscii) {
|
|
|
1994
2185
|
// src/ascii/shapes/rectangle.ts
|
|
1995
2186
|
function getBoxDimensions(label, options) {
|
|
1996
2187
|
const lines = splitLines(label);
|
|
1997
|
-
const maxLineWidth3 = Math.max(...lines.map((l) => l
|
|
2188
|
+
const maxLineWidth3 = Math.max(...lines.map((l) => displayWidth(l)), 0);
|
|
1998
2189
|
const lineCount3 = lines.length;
|
|
1999
|
-
const
|
|
2190
|
+
const rawInnerWidth = 2 * options.padding + maxLineWidth3;
|
|
2191
|
+
const innerWidth = rawInnerWidth % 2 === 0 ? rawInnerWidth + 1 : rawInnerWidth;
|
|
2000
2192
|
const width = innerWidth + 2;
|
|
2001
2193
|
const rawInnerHeight = lineCount3 + 2 * options.padding;
|
|
2002
2194
|
const innerHeight = rawInnerHeight % 2 === 0 ? rawInnerHeight + 1 : rawInnerHeight;
|
|
@@ -2041,14 +2233,9 @@ function renderBox(label, dimensions, corners, useAscii) {
|
|
|
2041
2233
|
const startY = centerY - Math.floor((lines.length - 1) / 2);
|
|
2042
2234
|
for (let i = 0; i < lines.length; i++) {
|
|
2043
2235
|
const line = lines[i];
|
|
2044
|
-
const
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
const y = startY + i;
|
|
2048
|
-
if (x >= 0 && x < canvas.length && y >= 0 && y < canvas[0].length) {
|
|
2049
|
-
canvas[x][y] = line[j];
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2236
|
+
const innerW = width - 2;
|
|
2237
|
+
const textX = 1 + Math.floor((innerW - displayWidth(line)) / 2);
|
|
2238
|
+
drawCJKText(canvas, textX, startY + i, line);
|
|
2052
2239
|
}
|
|
2053
2240
|
return canvas;
|
|
2054
2241
|
}
|
|
@@ -2227,7 +2414,7 @@ var roundedRenderer = {
|
|
|
2227
2414
|
var stadiumRenderer = {
|
|
2228
2415
|
getDimensions(label, options) {
|
|
2229
2416
|
const lines = splitLines(label);
|
|
2230
|
-
const maxLineWidth3 = Math.max(...lines.map((l) => l
|
|
2417
|
+
const maxLineWidth3 = Math.max(...lines.map((l) => displayWidth(l)), 0);
|
|
2231
2418
|
const lineCount3 = lines.length;
|
|
2232
2419
|
const innerWidth = 2 * options.padding + maxLineWidth3;
|
|
2233
2420
|
const width = innerWidth + 4;
|
|
@@ -2279,14 +2466,8 @@ var stadiumRenderer = {
|
|
|
2279
2466
|
const startY = centerY - Math.floor((lines.length - 1) / 2);
|
|
2280
2467
|
for (let i = 0; i < lines.length; i++) {
|
|
2281
2468
|
const line = lines[i];
|
|
2282
|
-
const textX = Math.floor(width / 2) - Math.floor(line
|
|
2283
|
-
|
|
2284
|
-
const x = textX + j;
|
|
2285
|
-
const y = startY + i;
|
|
2286
|
-
if (x > 0 && x < width - 1 && y >= 0 && y < height) {
|
|
2287
|
-
canvas[x][y] = line[j];
|
|
2288
|
-
}
|
|
2289
|
-
}
|
|
2469
|
+
const textX = Math.floor(width / 2) - Math.floor(displayWidth(line) / 2);
|
|
2470
|
+
drawCJKText(canvas, textX, startY + i, line);
|
|
2290
2471
|
}
|
|
2291
2472
|
return canvas;
|
|
2292
2473
|
},
|
|
@@ -2307,7 +2488,7 @@ var hexagonRenderer = {
|
|
|
2307
2488
|
var subroutineRenderer = {
|
|
2308
2489
|
getDimensions(label, options) {
|
|
2309
2490
|
const lines = splitLines(label);
|
|
2310
|
-
const maxLineWidth3 = Math.max(...lines.map((l) => l
|
|
2491
|
+
const maxLineWidth3 = Math.max(...lines.map((l) => displayWidth(l)), 0);
|
|
2311
2492
|
const lineCount3 = lines.length;
|
|
2312
2493
|
const innerWidth = 2 * options.padding + maxLineWidth3;
|
|
2313
2494
|
const width = innerWidth + 4;
|
|
@@ -2352,14 +2533,8 @@ var subroutineRenderer = {
|
|
|
2352
2533
|
const startY = centerY - Math.floor((lines.length - 1) / 2);
|
|
2353
2534
|
for (let i = 0; i < lines.length; i++) {
|
|
2354
2535
|
const line = lines[i];
|
|
2355
|
-
const textX = Math.floor(width / 2) - Math.floor(line
|
|
2356
|
-
|
|
2357
|
-
const x = textX + j;
|
|
2358
|
-
const y = startY + i;
|
|
2359
|
-
if (x > 1 && x < width - 2 && y > 0 && y < height - 1) {
|
|
2360
|
-
canvas[x][y] = line[j];
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2536
|
+
const textX = Math.floor(width / 2) - Math.floor(displayWidth(line) / 2);
|
|
2537
|
+
drawCJKText(canvas, textX, startY + i, line);
|
|
2363
2538
|
}
|
|
2364
2539
|
return canvas;
|
|
2365
2540
|
},
|
|
@@ -2376,7 +2551,7 @@ var doublecircleRenderer = {
|
|
|
2376
2551
|
var cylinderRenderer = {
|
|
2377
2552
|
getDimensions(label, options) {
|
|
2378
2553
|
const lines = splitLines(label);
|
|
2379
|
-
const maxLineWidth3 = Math.max(...lines.map((l) => l
|
|
2554
|
+
const maxLineWidth3 = Math.max(...lines.map((l) => displayWidth(l)), 0);
|
|
2380
2555
|
const lineCount3 = lines.length;
|
|
2381
2556
|
const innerWidth = 2 * options.padding + maxLineWidth3;
|
|
2382
2557
|
const width = innerWidth + 2;
|
|
@@ -2421,14 +2596,8 @@ var cylinderRenderer = {
|
|
|
2421
2596
|
const startY = centerY - Math.floor((lines.length - 1) / 2);
|
|
2422
2597
|
for (let i = 0; i < lines.length; i++) {
|
|
2423
2598
|
const line = lines[i];
|
|
2424
|
-
const textX = Math.floor(width / 2) - Math.floor(line
|
|
2425
|
-
|
|
2426
|
-
const x = textX + j;
|
|
2427
|
-
const y = startY + i;
|
|
2428
|
-
if (x > 0 && x < width - 1 && y > 1 && y < height - 2) {
|
|
2429
|
-
canvas[x][y] = line[j];
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2599
|
+
const textX = Math.floor(width / 2) - Math.floor(displayWidth(line) / 2);
|
|
2600
|
+
drawCJKText(canvas, textX, startY + i, line);
|
|
2432
2601
|
}
|
|
2433
2602
|
return canvas;
|
|
2434
2603
|
},
|
|
@@ -2530,12 +2699,9 @@ function drawBoxWithGridDimensions(node, graph) {
|
|
|
2530
2699
|
const startY = textCenterY - Math.floor((lines.length - 1) / 2);
|
|
2531
2700
|
for (let i = 0; i < lines.length; i++) {
|
|
2532
2701
|
const line = lines[i];
|
|
2533
|
-
const
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
box[textX + j][startY + i] = line[j];
|
|
2537
|
-
}
|
|
2538
|
-
}
|
|
2702
|
+
const boxInnerW = to.x - from.x - 1;
|
|
2703
|
+
const textX = from.x + 1 + Math.floor((boxInnerW - displayWidth(line)) / 2);
|
|
2704
|
+
drawCJKText(box, textX, startY + i, line);
|
|
2539
2705
|
}
|
|
2540
2706
|
return box;
|
|
2541
2707
|
}
|
|
@@ -2546,7 +2712,7 @@ function drawMultiBox(sections, useAscii, padding = 1) {
|
|
|
2546
2712
|
let maxTextWidth = 0;
|
|
2547
2713
|
for (const section of sections) {
|
|
2548
2714
|
for (const line of section) {
|
|
2549
|
-
maxTextWidth = Math.max(maxTextWidth, line
|
|
2715
|
+
maxTextWidth = Math.max(maxTextWidth, displayWidth(line));
|
|
2550
2716
|
}
|
|
2551
2717
|
}
|
|
2552
2718
|
const innerWidth = maxTextWidth + 2 * padding;
|
|
@@ -2582,9 +2748,7 @@ function drawMultiBox(sections, useAscii, padding = 1) {
|
|
|
2582
2748
|
const lines = section.length > 0 ? section : [""];
|
|
2583
2749
|
for (const line of lines) {
|
|
2584
2750
|
const startX = 1 + padding;
|
|
2585
|
-
|
|
2586
|
-
canvas[startX + i][row] = line[i];
|
|
2587
|
-
}
|
|
2751
|
+
drawCJKText(canvas, startX, row, line);
|
|
2588
2752
|
row++;
|
|
2589
2753
|
}
|
|
2590
2754
|
if (s < sections.length - 1) {
|
|
@@ -2875,7 +3039,7 @@ function drawTextOnLine(canvas, line, label, isUpwardEdge) {
|
|
|
2875
3039
|
const startY = middleY - Math.floor((lines.length - 1) / 2);
|
|
2876
3040
|
for (let i = 0; i < lines.length; i++) {
|
|
2877
3041
|
const lineText = lines[i];
|
|
2878
|
-
const startX = middleX - Math.floor(lineText
|
|
3042
|
+
const startX = middleX - Math.floor(displayWidth(lineText) / 2);
|
|
2879
3043
|
drawText(canvas, { x: startX, y: startY + i }, lineText);
|
|
2880
3044
|
}
|
|
2881
3045
|
}
|
|
@@ -3175,13 +3339,9 @@ function drawSubgraphLabel(sg, graph) {
|
|
|
3175
3339
|
for (let i = 0; i < lines.length; i++) {
|
|
3176
3340
|
const line = lines[i];
|
|
3177
3341
|
const labelY = 1 + i;
|
|
3178
|
-
let labelX = Math.floor(width / 2) - Math.floor(line
|
|
3342
|
+
let labelX = Math.floor(width / 2) - Math.floor(displayWidth(line) / 2);
|
|
3179
3343
|
if (labelX < 1) labelX = 1;
|
|
3180
|
-
|
|
3181
|
-
if (labelX + j < width && labelY < height) {
|
|
3182
|
-
canvas[labelX + j][labelY] = line[j];
|
|
3183
|
-
}
|
|
3184
|
-
}
|
|
3344
|
+
drawCJKText(canvas, labelX, labelY, line);
|
|
3185
3345
|
}
|
|
3186
3346
|
return [canvas, { x: sg.minX, y: sg.minY }];
|
|
3187
3347
|
}
|
|
@@ -3492,18 +3652,37 @@ function ensureSubgraphSpacing(graph) {
|
|
|
3492
3652
|
for (let j = i + 1; j < rootSubgraphs.length; j++) {
|
|
3493
3653
|
const sg1 = rootSubgraphs[i];
|
|
3494
3654
|
const sg2 = rootSubgraphs[j];
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3655
|
+
const hOverlap = sg1.minX <= sg2.maxX && sg1.maxX >= sg2.minX;
|
|
3656
|
+
const vOverlap = sg1.minY <= sg2.maxY && sg1.maxY >= sg2.minY;
|
|
3657
|
+
if (!hOverlap || !vOverlap) continue;
|
|
3658
|
+
if (hOverlap) {
|
|
3659
|
+
if (sg1.maxY >= sg2.minY - minSpacing && sg1.minY <= sg2.minY) {
|
|
3660
|
+
const newMinY = sg1.maxY + minSpacing + 1;
|
|
3661
|
+
if (newMinY > sg2.maxY) {
|
|
3662
|
+
sg2.maxY = newMinY + (sg2.maxY - sg2.minY);
|
|
3663
|
+
}
|
|
3664
|
+
sg2.minY = newMinY;
|
|
3665
|
+
} else if (sg2.maxY >= sg1.minY - minSpacing && sg2.minY <= sg1.minY) {
|
|
3666
|
+
const newMinY = sg2.maxY + minSpacing + 1;
|
|
3667
|
+
if (newMinY > sg1.maxY) {
|
|
3668
|
+
sg1.maxY = newMinY + (sg1.maxY - sg1.minY);
|
|
3669
|
+
}
|
|
3670
|
+
sg1.minY = newMinY;
|
|
3500
3671
|
}
|
|
3501
3672
|
}
|
|
3502
|
-
if (sg1.minY
|
|
3503
|
-
if (sg1.maxX >= sg2.minX - minSpacing && sg1.minX
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3673
|
+
if (sg1.minY <= sg2.maxY && sg1.maxY >= sg2.minY) {
|
|
3674
|
+
if (sg1.maxX >= sg2.minX - minSpacing && sg1.minX <= sg2.minX) {
|
|
3675
|
+
const newMinX = sg1.maxX + minSpacing + 1;
|
|
3676
|
+
if (newMinX > sg2.maxX) {
|
|
3677
|
+
sg2.maxX = newMinX + (sg2.maxX - sg2.minX);
|
|
3678
|
+
}
|
|
3679
|
+
sg2.minX = newMinX;
|
|
3680
|
+
} else if (sg2.maxX >= sg1.minX - minSpacing && sg2.minX <= sg1.minX) {
|
|
3681
|
+
const newMinX = sg2.maxX + minSpacing + 1;
|
|
3682
|
+
if (newMinX > sg1.maxX) {
|
|
3683
|
+
sg1.maxX = newMinX + (sg1.maxX - sg1.minX);
|
|
3684
|
+
}
|
|
3685
|
+
sg1.minX = newMinX;
|
|
3507
3686
|
}
|
|
3508
3687
|
}
|
|
3509
3688
|
}
|
|
@@ -3572,6 +3751,14 @@ function createMapping(graph) {
|
|
|
3572
3751
|
}
|
|
3573
3752
|
}
|
|
3574
3753
|
const shouldSeparate = dir === "LR" && hasExternalRoots && hasSubgraphRootsWithEdges;
|
|
3754
|
+
const downstreamSgs = /* @__PURE__ */ new Set();
|
|
3755
|
+
for (const edge of graph.edges) {
|
|
3756
|
+
const fromSg = getNodeSubgraph(graph, edge.from);
|
|
3757
|
+
const toSg = getNodeSubgraph(graph, edge.to);
|
|
3758
|
+
if (fromSg && toSg && fromSg !== toSg && !fromSg.parent && !toSg.parent) {
|
|
3759
|
+
downstreamSgs.add(toSg);
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3575
3762
|
let externalRootNodes;
|
|
3576
3763
|
let subgraphRootNodes = [];
|
|
3577
3764
|
if (shouldSeparate) {
|
|
@@ -3580,8 +3767,18 @@ function createMapping(graph) {
|
|
|
3580
3767
|
} else {
|
|
3581
3768
|
externalRootNodes = rootNodes;
|
|
3582
3769
|
}
|
|
3583
|
-
const
|
|
3770
|
+
const deferredRoots = /* @__PURE__ */ new Set();
|
|
3771
|
+
const primaryRoots = [];
|
|
3584
3772
|
for (const root of externalRootNodes) {
|
|
3773
|
+
const sg = getNodeSubgraph(graph, root);
|
|
3774
|
+
if (sg && downstreamSgs.has(sg)) {
|
|
3775
|
+
deferredRoots.add(root);
|
|
3776
|
+
} else {
|
|
3777
|
+
primaryRoots.push(root);
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
const rootsByTarget = /* @__PURE__ */ new Map();
|
|
3781
|
+
for (const root of primaryRoots) {
|
|
3585
3782
|
const children = getChildren(graph, root);
|
|
3586
3783
|
const targetName = children.length > 0 ? children[0].name : "__ungrouped__";
|
|
3587
3784
|
const group = rootsByTarget.get(targetName) ?? [];
|
|
@@ -3607,7 +3804,7 @@ function createMapping(graph) {
|
|
|
3607
3804
|
for (const edge of graph.edges) {
|
|
3608
3805
|
inDegree.set(edge.to.name, (inDegree.get(edge.to.name) ?? 0) + 1);
|
|
3609
3806
|
}
|
|
3610
|
-
let placedCount =
|
|
3807
|
+
let placedCount = primaryRoots.length + subgraphRootNodes.length;
|
|
3611
3808
|
while (placedCount < graph.nodes.length) {
|
|
3612
3809
|
const prevCount = placedCount;
|
|
3613
3810
|
for (const node of graph.nodes) {
|
|
@@ -3618,7 +3815,25 @@ function createMapping(graph) {
|
|
|
3618
3815
|
const parentSg = getNodeSubgraph(graph, node);
|
|
3619
3816
|
const childSg = getNodeSubgraph(graph, child);
|
|
3620
3817
|
const edgeDir = parentSg && parentSg === childSg && parentSg.direction ? parentSg.direction : graph.config.graphDirection;
|
|
3621
|
-
|
|
3818
|
+
let childLevel = edgeDir === "LR" ? gc.x + 4 : gc.y + 4;
|
|
3819
|
+
if (childSg && parentSg !== childSg && deferredRoots.size > 0) {
|
|
3820
|
+
let placedDeferred = false;
|
|
3821
|
+
for (const dr of [...deferredRoots]) {
|
|
3822
|
+
if (dr.gridCoord !== null) continue;
|
|
3823
|
+
const drSg = getNodeSubgraph(graph, dr);
|
|
3824
|
+
if (drSg !== childSg) continue;
|
|
3825
|
+
const drPerp = highestPositionPerLevel[childLevel] ?? 0;
|
|
3826
|
+
const drRequested = edgeDir === "LR" ? { x: childLevel, y: drPerp } : { x: drPerp, y: childLevel };
|
|
3827
|
+
reserveSpotInGrid(graph, graph.nodes[dr.index], drRequested, edgeDir);
|
|
3828
|
+
highestPositionPerLevel[childLevel] = drPerp + 4;
|
|
3829
|
+
deferredRoots.delete(dr);
|
|
3830
|
+
placedCount++;
|
|
3831
|
+
placedDeferred = true;
|
|
3832
|
+
}
|
|
3833
|
+
if (placedDeferred) {
|
|
3834
|
+
childLevel += 4;
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3622
3837
|
let highestPosition;
|
|
3623
3838
|
if (edgeDir !== graph.config.graphDirection) {
|
|
3624
3839
|
highestPosition = edgeDir === "LR" ? gc.y : gc.x;
|
|
@@ -3628,6 +3843,30 @@ function createMapping(graph) {
|
|
|
3628
3843
|
} else {
|
|
3629
3844
|
highestPosition = highestPositionPerLevel[childLevel];
|
|
3630
3845
|
}
|
|
3846
|
+
if (childSg) {
|
|
3847
|
+
const existingSgNodes = childSg.nodes.filter((n) => n !== child && n.gridCoord !== null);
|
|
3848
|
+
if (existingSgNodes.length > 0) {
|
|
3849
|
+
const perpPositions = existingSgNodes.map(
|
|
3850
|
+
(n) => graph.config.graphDirection === "TD" ? n.gridCoord.x : n.gridCoord.y
|
|
3851
|
+
);
|
|
3852
|
+
const sgMinPerp = Math.min(...perpPositions);
|
|
3853
|
+
highestPosition = Math.max(highestPosition, sgMinPerp);
|
|
3854
|
+
} else if (parentSg && parentSg !== childSg) {
|
|
3855
|
+
const parentSgNodes = parentSg.nodes.filter((n) => n.gridCoord !== null);
|
|
3856
|
+
if (parentSgNodes.length > 0) {
|
|
3857
|
+
const parentLevels = parentSgNodes.map(
|
|
3858
|
+
(n) => graph.config.graphDirection === "TD" ? n.gridCoord.y : n.gridCoord.x
|
|
3859
|
+
);
|
|
3860
|
+
const parentMaxLevel = Math.max(...parentLevels) + 2;
|
|
3861
|
+
if (childLevel <= parentMaxLevel) {
|
|
3862
|
+
const maxPerp = Math.max(...parentSgNodes.map(
|
|
3863
|
+
(n) => graph.config.graphDirection === "TD" ? n.gridCoord.x : n.gridCoord.y
|
|
3864
|
+
));
|
|
3865
|
+
highestPosition = Math.max(highestPosition, maxPerp + 4);
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3631
3870
|
const requested = edgeDir === "LR" ? { x: childLevel, y: highestPosition } : { x: highestPosition, y: childLevel };
|
|
3632
3871
|
reserveSpotInGrid(graph, graph.nodes[child.index], requested, edgeDir);
|
|
3633
3872
|
if (edgeDir === graph.config.graphDirection) {
|
|
@@ -3912,7 +4151,7 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
3912
4151
|
curY += 1;
|
|
3913
4152
|
const note = diagram.notes[n];
|
|
3914
4153
|
const nLines = splitLines(note.text);
|
|
3915
|
-
const nWidth = Math.max(...nLines.map((l) => l
|
|
4154
|
+
const nWidth = Math.max(...nLines.map((l) => displayWidth(l))) + 4;
|
|
3916
4155
|
const nHeight = nLines.length + 2;
|
|
3917
4156
|
const aIdx = actorIdx.get(note.actorIds[0]) ?? 0;
|
|
3918
4157
|
let nx;
|
|
@@ -3951,7 +4190,7 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
3951
4190
|
const msg = diagram.messages[m];
|
|
3952
4191
|
if (msg.from === msg.to) {
|
|
3953
4192
|
const fi = actorIdx.get(msg.from);
|
|
3954
|
-
const selfRight = llX[fi] + 6 + 2 + msg.label
|
|
4193
|
+
const selfRight = llX[fi] + 6 + 2 + displayWidth(msg.label);
|
|
3955
4194
|
totalW = Math.max(totalW, selfRight + 1);
|
|
3956
4195
|
}
|
|
3957
4196
|
}
|
|
@@ -3980,10 +4219,8 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
3980
4219
|
setC(left, row, V, "border");
|
|
3981
4220
|
setC(left + w - 1, row, V, "border");
|
|
3982
4221
|
const line = lines2[i];
|
|
3983
|
-
const ls = left + 1 + boxPad + Math.floor((maxW - line
|
|
3984
|
-
|
|
3985
|
-
setC(ls + j, row, line[j], "text");
|
|
3986
|
-
}
|
|
4222
|
+
const ls = left + 1 + boxPad + Math.floor((maxW - displayWidth(line)) / 2);
|
|
4223
|
+
drawCJKText(canvas, ls, row, line, true, rc, "text");
|
|
3987
4224
|
}
|
|
3988
4225
|
const bottomY = topY + h - 1;
|
|
3989
4226
|
setC(left, bottomY, BL, "border");
|
|
@@ -4023,9 +4260,7 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
4023
4260
|
setC(fromX + loopW, y0, useAscii ? "+" : "\u2510", "corner");
|
|
4024
4261
|
setC(fromX + loopW, y0 + 1, V, "line");
|
|
4025
4262
|
const labelX = fromX + loopW + 2;
|
|
4026
|
-
|
|
4027
|
-
if (labelX + i < totalW) setC(labelX + i, y0 + 1, msg.label[i], "text");
|
|
4028
|
-
}
|
|
4263
|
+
drawCJKText(canvas, labelX, y0 + 1, msg.label, true, rc, "text");
|
|
4029
4264
|
const arrowChar = isFilled ? useAscii ? "<" : "\u25C0" : useAscii ? "<" : "\u25C1";
|
|
4030
4265
|
setC(fromX, y0 + 2, arrowChar, "arrow");
|
|
4031
4266
|
for (let x = fromX + 1; x < fromX + loopW; x++) setC(x, y0 + 2, lineChar, "line");
|
|
@@ -4038,12 +4273,9 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
4038
4273
|
const msgLines = splitLines(msg.label);
|
|
4039
4274
|
for (let lineIdx = 0; lineIdx < msgLines.length; lineIdx++) {
|
|
4040
4275
|
const line = msgLines[lineIdx];
|
|
4041
|
-
const labelStart = midX - Math.floor(line
|
|
4276
|
+
const labelStart = midX - Math.floor(displayWidth(line) / 2);
|
|
4042
4277
|
const y = labelY + lineIdx;
|
|
4043
|
-
|
|
4044
|
-
const lx = labelStart + i;
|
|
4045
|
-
if (lx >= 0 && lx < totalW) setC(lx, y, line[i], "text");
|
|
4046
|
-
}
|
|
4278
|
+
drawCJKText(canvas, labelStart, y, line, true, rc, "text");
|
|
4047
4279
|
}
|
|
4048
4280
|
if (leftToRight) {
|
|
4049
4281
|
for (let x = fromX + 1; x < toX; x++) setC(x, arrowY, lineChar, "line");
|
|
@@ -4080,9 +4312,7 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
4080
4312
|
const hdrLines = splitLines(hdrLabel);
|
|
4081
4313
|
for (let lineIdx = 0; lineIdx < hdrLines.length && topY + lineIdx < botY; lineIdx++) {
|
|
4082
4314
|
const line = hdrLines[lineIdx];
|
|
4083
|
-
|
|
4084
|
-
setC(bLeft + 1 + i, topY + lineIdx, line[i], "text");
|
|
4085
|
-
}
|
|
4315
|
+
drawCJKText(canvas, bLeft + 1, topY + lineIdx, line, true, rc, "text", bRight - bLeft - 1);
|
|
4086
4316
|
}
|
|
4087
4317
|
setC(bLeft, botY, BL, "border");
|
|
4088
4318
|
for (let x = bLeft + 1; x < bRight; x++) setC(x, botY, H, "border");
|
|
@@ -4101,9 +4331,7 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
4101
4331
|
const dLabel = block.dividers[d].label;
|
|
4102
4332
|
if (dLabel) {
|
|
4103
4333
|
const dStr = `[${dLabel}]`;
|
|
4104
|
-
|
|
4105
|
-
setC(bLeft + 1 + i, dY, dStr[i], "text");
|
|
4106
|
-
}
|
|
4334
|
+
drawCJKText(canvas, bLeft + 1, dY, dStr, true, rc, "text", bRight - bLeft - 1);
|
|
4107
4335
|
}
|
|
4108
4336
|
}
|
|
4109
4337
|
}
|
|
@@ -4117,9 +4345,7 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
4117
4345
|
const ly = np.y + 1 + l;
|
|
4118
4346
|
setC(np.x, ly, V, "border");
|
|
4119
4347
|
setC(np.x + np.width - 1, ly, V, "border");
|
|
4120
|
-
|
|
4121
|
-
setC(np.x + 2 + i, ly, np.lines[l][i], "text");
|
|
4122
|
-
}
|
|
4348
|
+
drawCJKText(canvas, np.x + 2, ly, np.lines[l], true, rc, "text");
|
|
4123
4349
|
}
|
|
4124
4350
|
const by = np.y + np.height - 1;
|
|
4125
4351
|
setC(np.x, by, BL, "border");
|
|
@@ -4421,7 +4647,7 @@ function renderClassAscii(text, config, colorMode, theme) {
|
|
|
4421
4647
|
classSections.set(cls.id, sections);
|
|
4422
4648
|
let maxTextW = 0;
|
|
4423
4649
|
for (const section of sections) {
|
|
4424
|
-
for (const line of section) maxTextW = Math.max(maxTextW, line
|
|
4650
|
+
for (const line of section) maxTextW = Math.max(maxTextW, displayWidth(line));
|
|
4425
4651
|
}
|
|
4426
4652
|
const boxW = maxTextW + 4;
|
|
4427
4653
|
let totalLines = 0;
|
|
@@ -4737,7 +4963,7 @@ function renderClassAscii(text, config, colorMode, theme) {
|
|
|
4737
4963
|
}
|
|
4738
4964
|
if (rel.label) {
|
|
4739
4965
|
const lines2 = splitLines(rel.label);
|
|
4740
|
-
const maxLabelWidth = Math.max(...lines2.map((l) => l
|
|
4966
|
+
const maxLabelWidth = Math.max(...lines2.map((l) => displayWidth(l))) + 2;
|
|
4741
4967
|
let baseMidY;
|
|
4742
4968
|
let idealMidX;
|
|
4743
4969
|
if (fromBY < toTY) {
|
|
@@ -4788,20 +5014,16 @@ function renderClassAscii(text, config, colorMode, theme) {
|
|
|
4788
5014
|
const startY = labelY - halfHeight;
|
|
4789
5015
|
for (let lineIdx = 0; lineIdx < lines2.length; lineIdx++) {
|
|
4790
5016
|
const paddedLine = ` ${lines2[lineIdx]} `;
|
|
4791
|
-
const
|
|
5017
|
+
const paddedLineW = displayWidth(paddedLine);
|
|
5018
|
+
const idealLabelStart = idealMidX - Math.floor(paddedLineW / 2);
|
|
4792
5019
|
const labelStart = Math.max(0, idealLabelStart);
|
|
4793
5020
|
const y = startY + lineIdx;
|
|
4794
|
-
const labelEnd = labelStart +
|
|
5021
|
+
const labelEnd = labelStart + paddedLineW;
|
|
4795
5022
|
if (labelEnd > 0 && y >= 0) {
|
|
4796
5023
|
increaseSize(canvas, Math.max(labelEnd, 1), Math.max(y + 1, 1));
|
|
4797
5024
|
increaseRoleCanvasSize(rc, Math.max(labelEnd, 1), Math.max(y + 1, 1));
|
|
4798
5025
|
}
|
|
4799
|
-
|
|
4800
|
-
const lx = labelStart + i;
|
|
4801
|
-
if (lx >= 0 && y >= 0) {
|
|
4802
|
-
setC(lx, y, paddedLine[i], "text");
|
|
4803
|
-
}
|
|
4804
|
-
}
|
|
5026
|
+
drawCJKText(canvas, labelStart, y, paddedLine, true, rc, "text");
|
|
4805
5027
|
}
|
|
4806
5028
|
}
|
|
4807
5029
|
}
|
|
@@ -4998,7 +5220,7 @@ function renderErAscii(text, config, colorMode, theme) {
|
|
|
4998
5220
|
entitySections.set(ent.id, sections);
|
|
4999
5221
|
let maxTextW = 0;
|
|
5000
5222
|
for (const section of sections) {
|
|
5001
|
-
for (const line of section) maxTextW = Math.max(maxTextW, line
|
|
5223
|
+
for (const line of section) maxTextW = Math.max(maxTextW, displayWidth(line));
|
|
5002
5224
|
}
|
|
5003
5225
|
const boxW = maxTextW + 4;
|
|
5004
5226
|
let totalLines = 0;
|
|
@@ -5108,16 +5330,11 @@ function renderErAscii(text, config, colorMode, theme) {
|
|
|
5108
5330
|
const gapMid = Math.floor((startX + endX) / 2);
|
|
5109
5331
|
for (let lineIdx = 0; lineIdx < lines2.length; lineIdx++) {
|
|
5110
5332
|
const line = lines2[lineIdx];
|
|
5111
|
-
const labelStart = Math.max(startX, gapMid - Math.floor(line
|
|
5333
|
+
const labelStart = Math.max(startX, gapMid - Math.floor(displayWidth(line) / 2));
|
|
5112
5334
|
const labelY = lineY + 1 + lineIdx;
|
|
5113
|
-
increaseSize(canvas, Math.max(labelStart + line
|
|
5114
|
-
increaseRoleCanvasSize(rc, Math.max(labelStart + line
|
|
5115
|
-
|
|
5116
|
-
const lx = labelStart + i;
|
|
5117
|
-
if (lx >= startX && lx <= endX) {
|
|
5118
|
-
setC(lx, labelY, line[i], "text");
|
|
5119
|
-
}
|
|
5120
|
-
}
|
|
5335
|
+
increaseSize(canvas, Math.max(labelStart + displayWidth(line), 1), Math.max(labelY + 1, 1));
|
|
5336
|
+
increaseRoleCanvasSize(rc, Math.max(labelStart + displayWidth(line), 1), Math.max(labelY + 1, 1));
|
|
5337
|
+
drawCJKText(canvas, labelStart, labelY, line, true, rc, "text", endX - labelStart + 1);
|
|
5121
5338
|
}
|
|
5122
5339
|
}
|
|
5123
5340
|
} else {
|
|
@@ -5159,14 +5376,9 @@ function renderErAscii(text, config, colorMode, theme) {
|
|
|
5159
5376
|
const labelX = lineX + 2;
|
|
5160
5377
|
const y = startLabelY + lineIdx;
|
|
5161
5378
|
if (y >= 0) {
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
increaseSize(canvas, lx + 1, y + 1);
|
|
5166
|
-
increaseRoleCanvasSize(rc, lx + 1, y + 1);
|
|
5167
|
-
setC(lx, y, line[i], "text");
|
|
5168
|
-
}
|
|
5169
|
-
}
|
|
5379
|
+
increaseSize(canvas, labelX + displayWidth(line) + 1, y + 1);
|
|
5380
|
+
increaseRoleCanvasSize(rc, labelX + displayWidth(line) + 1, y + 1);
|
|
5381
|
+
drawCJKText(canvas, labelX, y, line, true, rc, "text");
|
|
5170
5382
|
}
|
|
5171
5383
|
}
|
|
5172
5384
|
}
|
|
@@ -5391,7 +5603,7 @@ function renderVertical(chart, ch, colorMode, theme) {
|
|
|
5391
5603
|
const yRange = chart.yAxis.range;
|
|
5392
5604
|
const yTicks = niceTickValues(yRange.min, yRange.max);
|
|
5393
5605
|
const yLabels = yTicks.map((v) => formatTickValue(v));
|
|
5394
|
-
const yGutter = Math.max(...yLabels.map((l) => l
|
|
5606
|
+
const yGutter = Math.max(...yLabels.map((l) => displayWidth(l))) + 1;
|
|
5395
5607
|
const plotW = Math.max(PLOT_WIDTH, dataCount * 6);
|
|
5396
5608
|
const plotH = PLOT_HEIGHT;
|
|
5397
5609
|
const bandW = Math.floor(plotW / dataCount);
|
|
@@ -5417,7 +5629,7 @@ function renderVertical(chart, ch, colorMode, theme) {
|
|
|
5417
5629
|
};
|
|
5418
5630
|
const bandCenter = (i) => plotLeft + Math.floor(bandW * (i + 0.5));
|
|
5419
5631
|
if (hasTitle && titleRow >= 0) {
|
|
5420
|
-
writeText(canvas, roles, titleRow, Math.floor(totalW / 2 - chart.title
|
|
5632
|
+
writeText(canvas, roles, titleRow, Math.floor(totalW / 2 - displayWidth(chart.title) / 2), chart.title, "text");
|
|
5421
5633
|
}
|
|
5422
5634
|
if (hasLegend) {
|
|
5423
5635
|
const legendRow = hasTitle ? 1 : 0;
|
|
@@ -5434,7 +5646,7 @@ function renderVertical(chart, ch, colorMode, theme) {
|
|
|
5434
5646
|
const displayRow = plotTop + (plotH - 1 - row);
|
|
5435
5647
|
const label = formatTickValue(tick);
|
|
5436
5648
|
set(canvas, roles, displayRow, plotLeft - 1, row === 0 ? ch.origin : ch.yTick, "border");
|
|
5437
|
-
const labelStart = yGutter - label
|
|
5649
|
+
const labelStart = yGutter - displayWidth(label);
|
|
5438
5650
|
writeText(canvas, roles, displayRow, Math.max(0, labelStart), label, "text");
|
|
5439
5651
|
}
|
|
5440
5652
|
for (let c = plotLeft; c < plotLeft + bandW * dataCount; c++) {
|
|
@@ -5444,12 +5656,12 @@ function renderVertical(chart, ch, colorMode, theme) {
|
|
|
5444
5656
|
const cx = bandCenter(i);
|
|
5445
5657
|
set(canvas, roles, xAxisRow, cx, ch.xTick, "border");
|
|
5446
5658
|
const label = catLabels[i];
|
|
5447
|
-
const labelStart = cx - Math.floor(label
|
|
5659
|
+
const labelStart = cx - Math.floor(displayWidth(label) / 2);
|
|
5448
5660
|
writeText(canvas, roles, xLabelRow, Math.max(0, labelStart), label, "text");
|
|
5449
5661
|
}
|
|
5450
5662
|
if (hasXTitle && xTitleRow >= 0) {
|
|
5451
5663
|
const title = chart.xAxis.title;
|
|
5452
|
-
writeText(canvas, roles, xTitleRow, Math.floor(totalW / 2 - title
|
|
5664
|
+
writeText(canvas, roles, xTitleRow, Math.floor(totalW / 2 - displayWidth(title) / 2), title, "text");
|
|
5453
5665
|
}
|
|
5454
5666
|
for (const tick of yTicks) {
|
|
5455
5667
|
const row = valueToRow(tick);
|
|
@@ -5507,7 +5719,7 @@ function renderHorizontal(chart, ch, colorMode, theme) {
|
|
|
5507
5719
|
const yRange = chart.yAxis.range;
|
|
5508
5720
|
const valueTicks = niceTickValues(yRange.min, yRange.max);
|
|
5509
5721
|
const catLabels = getCategoryLabels(chart, dataCount);
|
|
5510
|
-
const catGutter = Math.max(...catLabels.map((l) => l
|
|
5722
|
+
const catGutter = Math.max(...catLabels.map((l) => displayWidth(l))) + 1;
|
|
5511
5723
|
const plotW = Math.max(PLOT_WIDTH, 40);
|
|
5512
5724
|
const bandH = Math.max(2, Math.floor(PLOT_HEIGHT / dataCount));
|
|
5513
5725
|
const plotH = bandH * dataCount;
|
|
@@ -5529,7 +5741,7 @@ function renderHorizontal(chart, ch, colorMode, theme) {
|
|
|
5529
5741
|
};
|
|
5530
5742
|
const bandMid = (i) => plotTop + Math.floor(bandH * (i + 0.5));
|
|
5531
5743
|
if (hasTitle) {
|
|
5532
|
-
writeText(canvas, roles, 0, Math.floor(totalW / 2 - chart.title
|
|
5744
|
+
writeText(canvas, roles, 0, Math.floor(totalW / 2 - displayWidth(chart.title) / 2), chart.title, "text");
|
|
5533
5745
|
}
|
|
5534
5746
|
if (hasLegend) {
|
|
5535
5747
|
const legendRow = hasTitle ? 1 : 0;
|
|
@@ -5542,7 +5754,7 @@ function renderHorizontal(chart, ch, colorMode, theme) {
|
|
|
5542
5754
|
for (let i = 0; i < dataCount; i++) {
|
|
5543
5755
|
const my = bandMid(i);
|
|
5544
5756
|
const label = catLabels[i];
|
|
5545
|
-
const labelStart = catGutter - label
|
|
5757
|
+
const labelStart = catGutter - displayWidth(label);
|
|
5546
5758
|
writeText(canvas, roles, my, Math.max(0, labelStart), label, "text");
|
|
5547
5759
|
}
|
|
5548
5760
|
for (let c = plotLeft; c < plotLeft + plotW; c++) {
|
|
@@ -5553,11 +5765,11 @@ function renderHorizontal(chart, ch, colorMode, theme) {
|
|
|
5553
5765
|
if (cx < plotLeft || cx >= plotLeft + plotW) continue;
|
|
5554
5766
|
set(canvas, roles, xAxisRow, cx, ch.xTick, "border");
|
|
5555
5767
|
const label = formatTickValue(tick);
|
|
5556
|
-
writeText(canvas, roles, xAxisRow + 1, cx - Math.floor(label
|
|
5768
|
+
writeText(canvas, roles, xAxisRow + 1, cx - Math.floor(displayWidth(label) / 2), label, "text");
|
|
5557
5769
|
}
|
|
5558
5770
|
if (hasYTitle) {
|
|
5559
5771
|
const title = chart.yAxis.title;
|
|
5560
|
-
writeText(canvas, roles, totalH - 1, Math.floor(totalW / 2 - title
|
|
5772
|
+
writeText(canvas, roles, totalH - 1, Math.floor(totalW / 2 - displayWidth(title) / 2), title, "text");
|
|
5561
5773
|
}
|
|
5562
5774
|
for (const tick of valueTicks) {
|
|
5563
5775
|
const cx = valueToCol(tick);
|
|
@@ -5733,7 +5945,7 @@ function drawLegend(canvas, roles, hexCanvas, chart, row, totalW, ch, seriesColo
|
|
|
5733
5945
|
let totalLen = 0;
|
|
5734
5946
|
for (let i = 0; i < items.length; i++) {
|
|
5735
5947
|
if (i > 0) totalLen += 2;
|
|
5736
|
-
totalLen += 1 + 1 + items[i].label
|
|
5948
|
+
totalLen += 1 + 1 + displayWidth(items[i].label);
|
|
5737
5949
|
}
|
|
5738
5950
|
const startCol = Math.max(0, Math.floor(totalW / 2 - totalLen / 2));
|
|
5739
5951
|
let col = startCol;
|
|
@@ -5744,7 +5956,7 @@ function drawLegend(canvas, roles, hexCanvas, chart, row, totalW, ch, seriesColo
|
|
|
5744
5956
|
col += 1;
|
|
5745
5957
|
col += 1;
|
|
5746
5958
|
writeText(canvas, roles, row, col, item.label, "text");
|
|
5747
|
-
col += item.label
|
|
5959
|
+
col += displayWidth(item.label);
|
|
5748
5960
|
}
|
|
5749
5961
|
}
|
|
5750
5962
|
function createCanvas(width, height) {
|
|
@@ -5770,9 +5982,7 @@ function get(canvas, row, col) {
|
|
|
5770
5982
|
return " ";
|
|
5771
5983
|
}
|
|
5772
5984
|
function writeText(canvas, roles, row, startCol, text, role) {
|
|
5773
|
-
|
|
5774
|
-
set(canvas, roles, row, startCol + i, text[i], role);
|
|
5775
|
-
}
|
|
5985
|
+
drawCJKText(canvas, startCol, row, text, true, roles, role);
|
|
5776
5986
|
}
|
|
5777
5987
|
function canvasToString2(canvas, roles, hexCanvas, colorMode, theme) {
|
|
5778
5988
|
if (canvas.length === 0) return "";
|
|
@@ -5784,6 +5994,7 @@ function canvasToString2(canvas, roles, hexCanvas, colorMode, theme) {
|
|
|
5784
5994
|
const rowRoles = [];
|
|
5785
5995
|
const rowHex = [];
|
|
5786
5996
|
for (let col = 0; col < width; col++) {
|
|
5997
|
+
if (canvas[col][row] === CJK_PAD) continue;
|
|
5787
5998
|
chars.push(canvas[col][row]);
|
|
5788
5999
|
rowRoles.push(roles[col][row]);
|
|
5789
6000
|
rowHex.push(hexCanvas[col][row]);
|