zombie-mermaid 2.2.2 → 2.2.4
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/README.md +4 -4
- package/dist/ascii.cjs +8 -8
- package/dist/ascii.cjs.map +1 -1
- package/dist/ascii.js +1137 -1070
- package/dist/ascii.js.map +1 -1
- package/dist/cli.js +3380 -3295
- package/dist/index.cjs +59 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1780 -1712
- package/dist/index.js.map +1 -1
- package/dist/mcp.cjs +246 -52
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +246 -52
- package/dist/mcp.js.map +1 -1
- package/package.json +25 -21
- package/packages/ascii-renderer/src/canvas.ts +7 -4
- package/packages/ascii-renderer/src/class-diagram.ts +192 -12
- package/packages/ascii-renderer/src/draw-arrows.ts +79 -35
- package/packages/ascii-renderer/src/sequence.ts +42 -8
- package/packages/ascii-renderer/src/shapes/hexagon.ts +7 -3
- package/packages/ascii-renderer/src/shapes/special.ts +12 -4
- package/packages/core/src/generated/mono-font-subset.ts +32 -0
- package/packages/core/src/text-metrics.ts +22 -0
- package/packages/core/src/theme.ts +29 -13
- package/packages/mermaid-parser/src/xychart/parser.ts +44 -1
- package/src/__tests__/ascii-arrowhead-direction-1083.test.ts +51 -0
- package/src/__tests__/ascii-class-box-occupancy.test.ts +82 -43
- package/src/__tests__/ascii-class-detour-label-routing-487.test.ts +8 -8
- package/src/__tests__/ascii-class-parent-alignment-971.test.ts +139 -0
- package/src/__tests__/ascii-class-parent-alignment-972.test.ts +208 -0
- package/src/__tests__/ascii-draw-arrows-coverage.test.ts +56 -0
- package/src/__tests__/ascii-edge-ending-glyphs.test.ts +3 -3
- package/src/__tests__/ascii-sequence-form-invariants.test.ts +51 -0
- package/src/__tests__/text-metrics.test.ts +13 -0
- package/src/__tests__/theme.test.ts +30 -0
- package/src/__tests__/workspace-package-boundaries.test.ts +2 -2
- package/src/__tests__/xychart-parser-categories.test.ts +78 -0
- package/src/cli/html-viewer.ts +5 -0
package/dist/mcp.cjs
CHANGED
|
@@ -812,12 +812,31 @@ function isGeometricShapesTextDefault(code) {
|
|
|
812
812
|
return code >= 9632 && code <= 9727;
|
|
813
813
|
}
|
|
814
814
|
/**
|
|
815
|
+
* Arrows-block glyphs that carry Extended_Pictographic (emoji-capable) but
|
|
816
|
+
* no default Emoji_Presentation, and render as a single narrow column in
|
|
817
|
+
* every real terminal — the same misclassification the Geometric Shapes
|
|
818
|
+
* block above hits. Covers:
|
|
819
|
+
*
|
|
820
|
+
* - ↖ U+2196, ↗ U+2197, ↘ U+2198, ↙ U+2199 — drawn by the ASCII renderer
|
|
821
|
+
* itself for diagonal edge-routing arrowheads
|
|
822
|
+
* (packages/ascii-renderer/src/draw-arrows.ts's `drawArrowHead`) in place
|
|
823
|
+
* of the filled triangles (◢◣◤◥) JetBrains Mono NL has no glyph for at
|
|
824
|
+
* all — see issue #1062.
|
|
825
|
+
* - ↔ U+2194, ↕ U+2195, ↩ U+21A9, ↪ U+21AA — not drawn by the renderer
|
|
826
|
+
* itself, but reachable if a diagram author types one into node/edge/note
|
|
827
|
+
* label text, which passes through to the ASCII grid verbatim.
|
|
828
|
+
*/
|
|
829
|
+
function isArrowsBlockTextDefault(code) {
|
|
830
|
+
return code >= 8596 && code <= 8601 || code === 8617 || code === 8618;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
815
833
|
* Check if a character is an emoji (fullwidth)
|
|
816
834
|
*/
|
|
817
835
|
function isEmoji(char) {
|
|
818
836
|
if (EMOJI_PRESENTATION_REGEX.test(char)) return true;
|
|
819
837
|
const code = char.codePointAt(0);
|
|
820
838
|
if (code !== void 0 && isGeometricShapesTextDefault(code)) return false;
|
|
839
|
+
if (code !== void 0 && isArrowsBlockTextDefault(code)) return false;
|
|
821
840
|
return EXTENDED_PICTOGRAPHIC_REGEX.test(char);
|
|
822
841
|
}
|
|
823
842
|
/**
|
|
@@ -1237,6 +1256,38 @@ function sanitizeClassName(className) {
|
|
|
1237
1256
|
return /^-?[a-zA-Z_][a-zA-Z0-9_-]*$/.test(className) ? className : void 0;
|
|
1238
1257
|
}
|
|
1239
1258
|
//#endregion
|
|
1259
|
+
//#region packages/core/src/generated/mono-font-subset.ts
|
|
1260
|
+
/**
|
|
1261
|
+
* GENERATED FILE — do not edit by hand.
|
|
1262
|
+
*
|
|
1263
|
+
* Regenerate with `pnpm run build:mono-font`
|
|
1264
|
+
* (scripts/build-mono-font-subset.ts), which subsets
|
|
1265
|
+
* third_party/fonts/jetbrains-mono-nl/JetBrainsMonoNL-Regular.ttf down to
|
|
1266
|
+
* Basic Latin + Latin-1 Supplement — the self-hosted, embeddable
|
|
1267
|
+
* `@font-face` packages/core/src/theme.ts's `buildStyleBlock()` inlines
|
|
1268
|
+
* into an SVG's own `<style>` for its `.mono` rule (class-diagram method
|
|
1269
|
+
* signatures, ER-diagram attribute types), replacing a Google Fonts CDN
|
|
1270
|
+
* `@import` this library's SVG output previously depended on by default
|
|
1271
|
+
* (#1061). See that script's header comment for why this subset is
|
|
1272
|
+
* deliberately narrower than the site's own (demo/components/generated/
|
|
1273
|
+
* mono-font-subset.ts).
|
|
1274
|
+
*/
|
|
1275
|
+
/** The self-hosted family name every consumer's font stack should list
|
|
1276
|
+
* first — matches this subset's own `name` table entry. */
|
|
1277
|
+
var MONO_FONT_FAMILY = "JetBrains Mono NL";
|
|
1278
|
+
/** The `@font-face` rule embedding the subset as a base64 `woff2` data
|
|
1279
|
+
* URI — no separate network request, no page-depth-relative path to get
|
|
1280
|
+
* wrong (see the script header comment for why data-URI over a static
|
|
1281
|
+
* asset file). */
|
|
1282
|
+
var MONO_FONT_FACE_CSS = `@font-face {
|
|
1283
|
+
font-family: 'JetBrains Mono NL';
|
|
1284
|
+
font-style: normal;
|
|
1285
|
+
font-weight: 400;
|
|
1286
|
+
font-display: swap;
|
|
1287
|
+
src: url(data:font/woff2;base64,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) format('woff2');
|
|
1288
|
+
unicode-range: U+0020-007E, U+00A0-00FF;
|
|
1289
|
+
}`;
|
|
1290
|
+
//#endregion
|
|
1240
1291
|
//#region packages/core/src/theme.ts
|
|
1241
1292
|
/** Default bg/fg when no colors are provided (zinc light) */
|
|
1242
1293
|
var DEFAULTS$1 = {
|
|
@@ -1484,11 +1535,26 @@ function styleOpenTag(nonce) {
|
|
|
1484
1535
|
*
|
|
1485
1536
|
* Any other value is treated as a literal font name: sanitized and quoted
|
|
1486
1537
|
* exactly as before.
|
|
1538
|
+
*
|
|
1539
|
+
* `hasMonoFont`'s `.mono` rule (class-diagram method signatures, ER-diagram
|
|
1540
|
+
* attribute types) never reaches out to Google Fonts at all — it used to
|
|
1541
|
+
* `@import` 'JetBrains Mono' from Google's CDN, a third-party network
|
|
1542
|
+
* dependency a published library's default SVG output had no business
|
|
1543
|
+
* making (#1061, filed as a follow-up from #1059's ASCII-side self-hosting
|
|
1544
|
+
* fix). It now embeds {@link SVG_MONO_FONT_FACE_CSS} — a small, subsetted
|
|
1545
|
+
* (Basic Latin + Latin-1 Supplement), self-hosted `@font-face` as a base64
|
|
1546
|
+
* `woff2` data URI, generated by scripts/build-mono-font-subset.ts into
|
|
1547
|
+
* ./generated/mono-font-subset.ts — directly in the SVG's own `<style>`
|
|
1548
|
+
* block. Unlike `font` above, this isn't caller-configurable: it's the same
|
|
1549
|
+
* embed for every consumer, on by default, with no network request either
|
|
1550
|
+
* way — a strict improvement over the dead-or-alive CDN fetch it replaces,
|
|
1551
|
+
* and the only way a *standalone* SVG (no surrounding host page providing
|
|
1552
|
+
* its own copy of the font) renders `.mono` text correctly out of the box.
|
|
1487
1553
|
*/
|
|
1488
1554
|
function buildStyleBlock(font, hasMonoFont, nonce) {
|
|
1489
1555
|
const isVarReference = isSafeCssVarReference(font);
|
|
1490
1556
|
const safeFont = isVarReference ? font.trim() : font.replace(/[<>{};'"]/g, "");
|
|
1491
|
-
const fontImports =
|
|
1557
|
+
const fontImports = isVarReference || isFontStackOrGenericFamily(font) ? [] : [`@import url('https://fonts.googleapis.com/css2?family=${encodeURIComponent(safeFont)}:wght@400;500;600;700&display=swap');`];
|
|
1492
1558
|
const derivedVars = `
|
|
1493
1559
|
/* Derived from --bg and --fg (overridable via --line, --accent, etc.) */
|
|
1494
1560
|
--_text: var(--fg);
|
|
@@ -1506,8 +1572,9 @@ function buildStyleBlock(font, hasMonoFont, nonce) {
|
|
|
1506
1572
|
return [
|
|
1507
1573
|
styleOpenTag(nonce),
|
|
1508
1574
|
...fontImports.length > 0 ? [` ${fontImports.join("\n ")}`] : [],
|
|
1575
|
+
...hasMonoFont ? [` ${MONO_FONT_FACE_CSS}`] : [],
|
|
1509
1576
|
` text { font-family: ${isVarReference ? safeFont : `'${safeFont}'`}, system-ui, sans-serif; }`,
|
|
1510
|
-
...hasMonoFont ? [` .mono { font-family: '
|
|
1577
|
+
...hasMonoFont ? [` .mono { font-family: '${MONO_FONT_FAMILY}', 'SF Mono', 'Fira Code', ui-monospace, monospace; }`] : [],
|
|
1511
1578
|
` svg {${derivedVars}`,
|
|
1512
1579
|
` }`,
|
|
1513
1580
|
"</style>"
|
|
@@ -2697,7 +2764,7 @@ function parseXYChart(lines) {
|
|
|
2697
2764
|
const xCatMatch = line.match(/^x-axis\s+(?:"([^"]*)"\s*)?\[([^\]]+)\]/);
|
|
2698
2765
|
if (xCatMatch) {
|
|
2699
2766
|
if (xCatMatch[1]) xAxis.title = xCatMatch[1];
|
|
2700
|
-
xAxis.categories = xCatMatch[2]
|
|
2767
|
+
xAxis.categories = splitCategoryList(xCatMatch[2]).map((s) => unquote(s.trim()));
|
|
2701
2768
|
continue;
|
|
2702
2769
|
}
|
|
2703
2770
|
const xRangeMatch = line.match(/^x-axis\s+(?:"([^"]*)"\s+)?(-?\d+(?:\.\d+)?)\s*-->\s*(-?\d+(?:\.\d+)?)/);
|
|
@@ -2768,6 +2835,39 @@ function parseXYChart(lines) {
|
|
|
2768
2835
|
};
|
|
2769
2836
|
}
|
|
2770
2837
|
/**
|
|
2838
|
+
* Split a categorical x-axis bracket's comma-separated contents into raw
|
|
2839
|
+
* item strings, treating a comma *inside* a double-quoted item as literal
|
|
2840
|
+
* text rather than a delimiter. A naive `.split(',')` would break
|
|
2841
|
+
* `["Jan, Feb", "Mar"]` into three pieces instead of two — quoting a value
|
|
2842
|
+
* that itself contains a comma is exactly the case quoting exists for. Each
|
|
2843
|
+
* returned raw item is still trimmed and unquoted by the caller.
|
|
2844
|
+
*/
|
|
2845
|
+
function splitCategoryList(str) {
|
|
2846
|
+
const items = [];
|
|
2847
|
+
let current = "";
|
|
2848
|
+
let inQuotes = false;
|
|
2849
|
+
for (const char of str) if (char === "\"") {
|
|
2850
|
+
inQuotes = !inQuotes;
|
|
2851
|
+
current += char;
|
|
2852
|
+
} else if (char === "," && !inQuotes) {
|
|
2853
|
+
items.push(current);
|
|
2854
|
+
current = "";
|
|
2855
|
+
} else current += char;
|
|
2856
|
+
items.push(current);
|
|
2857
|
+
return items;
|
|
2858
|
+
}
|
|
2859
|
+
/**
|
|
2860
|
+
* Strip one matching pair of leading/trailing double quotes from a category
|
|
2861
|
+
* item, if present — mirroring how an axis *title* capture group already
|
|
2862
|
+
* unquotes via its regex. `x-axis [A, B, C]` items are only split and
|
|
2863
|
+
* trimmed, so a quoted item like `"CLI output / logs"` previously kept its
|
|
2864
|
+
* literal quote characters in the rendered label. See issue #1087.
|
|
2865
|
+
*/
|
|
2866
|
+
function unquote(value) {
|
|
2867
|
+
if (value.length >= 2 && value.startsWith("\"") && value.endsWith("\"")) return value.slice(1, -1);
|
|
2868
|
+
return value;
|
|
2869
|
+
}
|
|
2870
|
+
/**
|
|
2771
2871
|
* Parse a `bar`/`line` series' comma-separated bracket contents into
|
|
2772
2872
|
* numbers, throwing on any element that isn't a valid number instead of
|
|
2773
2873
|
* silently coercing it to `NaN` (which serializes as `null` and can
|
|
@@ -9581,10 +9681,10 @@ function canvasToString$1(canvas, options) {
|
|
|
9581
9681
|
var VERTICAL_FLIP_MAP = {
|
|
9582
9682
|
"▲": "▼",
|
|
9583
9683
|
"▼": "▲",
|
|
9584
|
-
"
|
|
9585
|
-
"
|
|
9586
|
-
"
|
|
9587
|
-
"
|
|
9684
|
+
"↖": "↙",
|
|
9685
|
+
"↙": "↖",
|
|
9686
|
+
"↗": "↘",
|
|
9687
|
+
"↘": "↗",
|
|
9588
9688
|
"^": "v",
|
|
9589
9689
|
v: "^",
|
|
9590
9690
|
"┌": "└",
|
|
@@ -13894,51 +13994,74 @@ function markerArrowChar(useAscii, marker) {
|
|
|
13894
13994
|
if (marker === "cross") return useAscii ? "x" : "✕";
|
|
13895
13995
|
}
|
|
13896
13996
|
/**
|
|
13997
|
+
* Maps a direction to its Unicode arrowhead glyph, or `undefined` for a
|
|
13998
|
+
* direction with no arrowhead (`Middle`, or any other value this switch
|
|
13999
|
+
* doesn't recognize) — callers decide the ultimate fallback.
|
|
14000
|
+
*
|
|
14001
|
+
* Uses triangular Unicode symbols (▲▼◄►) for the four orthogonal
|
|
14002
|
+
* directions and diagonal arrow symbols (↗↖↘↙) for the four diagonal ones.
|
|
14003
|
+
* The diagonal glyphs are plain Arrows-block characters (U+2196–U+2199),
|
|
14004
|
+
* not filled triangles like the orthogonal set — JetBrains Mono NL (this
|
|
14005
|
+
* site's self-hosted ASCII font, see scripts/build-mono-font-subset.ts)
|
|
14006
|
+
* has no glyph at all for the filled diagonal triangles (◢◣◤◥, U+25E2–
|
|
14007
|
+
* U+25E5) that would otherwise match the orthogonal style; U+2196–U+2199
|
|
14008
|
+
* are real, correctly-directional glyphs the font does have, which beats
|
|
14009
|
+
* falling back to an unpinned system font for just these four characters.
|
|
14010
|
+
* See issue #1062.
|
|
14011
|
+
*
|
|
14012
|
+
* Exported (alongside {@link asciiArrowChar}) so tests can exercise every
|
|
14013
|
+
* direction directly — the ELK/pathfinder layout this module actually
|
|
14014
|
+
* draws from rarely if ever produces some direction combinations (e.g.
|
|
14015
|
+
* `LowerLeft` only arises from determinePath's rare Case-4 diagonal
|
|
14016
|
+
* fallback, and only for a specific relative source/target position no
|
|
14017
|
+
* existing sample or hand-written diagram happens to trigger), which would
|
|
14018
|
+
* otherwise leave this mapping's correctness for those directions unverified.
|
|
14019
|
+
*/
|
|
14020
|
+
function unicodeArrowChar(dir) {
|
|
14021
|
+
if (dirEquals(dir, Up)) return "▲";
|
|
14022
|
+
if (dirEquals(dir, Down)) return "▼";
|
|
14023
|
+
if (dirEquals(dir, Left)) return "◄";
|
|
14024
|
+
if (dirEquals(dir, Right)) return "►";
|
|
14025
|
+
if (dirEquals(dir, UpperRight)) return "↗";
|
|
14026
|
+
if (dirEquals(dir, UpperLeft)) return "↖";
|
|
14027
|
+
if (dirEquals(dir, LowerRight)) return "↘";
|
|
14028
|
+
if (dirEquals(dir, LowerLeft)) return "↙";
|
|
14029
|
+
}
|
|
14030
|
+
/** ASCII-mode counterpart of {@link unicodeArrowChar} — orthogonal only, no diagonals. */
|
|
14031
|
+
function asciiArrowChar(dir) {
|
|
14032
|
+
if (dirEquals(dir, Up)) return "^";
|
|
14033
|
+
if (dirEquals(dir, Down)) return "v";
|
|
14034
|
+
if (dirEquals(dir, Left)) return "<";
|
|
14035
|
+
if (dirEquals(dir, Right)) return ">";
|
|
14036
|
+
}
|
|
14037
|
+
/**
|
|
13897
14038
|
* Draw the arrowhead at the end of an edge path.
|
|
13898
|
-
* Uses triangular Unicode symbols (▲▼◄►) or ASCII symbols (^v<>).
|
|
13899
14039
|
*
|
|
13900
|
-
* `marker` overrides the directional
|
|
13901
|
-
*
|
|
14040
|
+
* `marker` overrides the directional glyph with a fixed circle/cross glyph
|
|
14041
|
+
* for `--o`/`--x` (flowchart) terminators — those are direction-
|
|
13902
14042
|
* independent, so the direction computed below is only used to place it,
|
|
13903
14043
|
* never to pick which glyph to draw. See issue #330.
|
|
13904
14044
|
*/
|
|
13905
14045
|
function drawArrowHead(graph, lastLine, fallbackDir, marker) {
|
|
13906
14046
|
const canvas = copyCanvas(graph.canvas);
|
|
13907
14047
|
if (lastLine.length === 0) return canvas;
|
|
13908
|
-
const from = lastLine[0];
|
|
13909
14048
|
const lastPos = lastLine[lastLine.length - 1];
|
|
13910
|
-
let dir = determineDirection(
|
|
14049
|
+
let dir = determineDirection(lastLine.length >= 2 ? lastLine[lastLine.length - 2] : lastLine[0], lastPos);
|
|
13911
14050
|
if (lastLine.length === 1 || dirEquals(dir, Middle)) dir = fallbackDir;
|
|
13912
|
-
let char;
|
|
13913
14051
|
const markerChar = markerArrowChar(graph.config.useAscii, marker);
|
|
14052
|
+
let char;
|
|
13914
14053
|
if (markerChar !== void 0) char = markerChar;
|
|
13915
14054
|
else if (!graph.config.useAscii) {
|
|
13916
|
-
|
|
13917
|
-
|
|
13918
|
-
|
|
13919
|
-
else
|
|
13920
|
-
|
|
13921
|
-
|
|
13922
|
-
|
|
13923
|
-
|
|
13924
|
-
else
|
|
13925
|
-
|
|
13926
|
-
else if (dirEquals(fallbackDir, Left)) char = "◄";
|
|
13927
|
-
else if (dirEquals(fallbackDir, Right)) char = "►";
|
|
13928
|
-
else if (dirEquals(fallbackDir, UpperRight)) char = "◥";
|
|
13929
|
-
else if (dirEquals(fallbackDir, UpperLeft)) char = "◤";
|
|
13930
|
-
else if (dirEquals(fallbackDir, LowerRight)) char = "◢";
|
|
13931
|
-
else if (dirEquals(fallbackDir, LowerLeft)) char = "◣";
|
|
13932
|
-
else char = "●";
|
|
13933
|
-
} else if (dirEquals(dir, Up)) char = "^";
|
|
13934
|
-
else if (dirEquals(dir, Down)) char = "v";
|
|
13935
|
-
else if (dirEquals(dir, Left)) char = "<";
|
|
13936
|
-
else if (dirEquals(dir, Right)) char = ">";
|
|
13937
|
-
else if (dirEquals(fallbackDir, Up)) char = "^";
|
|
13938
|
-
else if (dirEquals(fallbackDir, Down)) char = "v";
|
|
13939
|
-
else if (dirEquals(fallbackDir, Left)) char = "<";
|
|
13940
|
-
else if (dirEquals(fallbackDir, Right)) char = ">";
|
|
13941
|
-
else char = "*";
|
|
14055
|
+
const resolved = unicodeArrowChar(dir);
|
|
14056
|
+
/* v8 ignore else */
|
|
14057
|
+
if (resolved !== void 0) char = resolved;
|
|
14058
|
+
else char = unicodeArrowChar(fallbackDir) ?? "●";
|
|
14059
|
+
} else {
|
|
14060
|
+
const resolved = asciiArrowChar(dir);
|
|
14061
|
+
/* v8 ignore else */
|
|
14062
|
+
if (resolved !== void 0) char = resolved;
|
|
14063
|
+
else char = asciiArrowChar(fallbackDir) ?? "*";
|
|
14064
|
+
}
|
|
13942
14065
|
write(canvas, lastPos.x, lastPos.y, char);
|
|
13943
14066
|
return canvas;
|
|
13944
14067
|
}
|
|
@@ -15581,21 +15704,29 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
15581
15704
|
}
|
|
15582
15705
|
const leftNoteWidth = new Array(diagram.actors.length).fill(0);
|
|
15583
15706
|
const rightNoteWidth = new Array(diagram.actors.length).fill(0);
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15707
|
+
const overNoteHalfLeft = new Array(diagram.actors.length).fill(0);
|
|
15708
|
+
const overNoteHalfRight = new Array(diagram.actors.length).fill(0);
|
|
15709
|
+
for (const note of diagram.notes) if (note.position === "left" || note.position === "right") {
|
|
15710
|
+
const aIdx = actorIndexOf(note.actorIds[0]);
|
|
15587
15711
|
const w = noteBoxWidth(note);
|
|
15588
15712
|
if (note.position === "left") leftNoteWidth[aIdx] = Math.max(leftNoteWidth[aIdx], w);
|
|
15589
15713
|
else rightNoteWidth[aIdx] = Math.max(rightNoteWidth[aIdx], w);
|
|
15714
|
+
} else if (note.position === "over" && note.actorIds.length === 1) {
|
|
15715
|
+
const aIdx = actorIndexOf(note.actorIds[0]);
|
|
15716
|
+
const w = noteBoxWidth(note);
|
|
15717
|
+
const halfLeft = Math.floor(w / 2);
|
|
15718
|
+
const halfRight = w - halfLeft;
|
|
15719
|
+
overNoteHalfLeft[aIdx] = Math.max(overNoteHalfLeft[aIdx], halfLeft);
|
|
15720
|
+
overNoteHalfRight[aIdx] = Math.max(overNoteHalfRight[aIdx], halfRight);
|
|
15590
15721
|
}
|
|
15591
15722
|
const minLifelineGap = paddingOffset(config.paddingX, 5, 10, 4);
|
|
15592
|
-
const llX = [Math.max(halfBox[0] + (boxSpanOf[0] === -1 ? 0 : 2), leftNoteWidth[0] + 1)];
|
|
15723
|
+
const llX = [Math.max(halfBox[0] + (boxSpanOf[0] === -1 ? 0 : 2), leftNoteWidth[0] + 1, overNoteHalfLeft[0])];
|
|
15593
15724
|
for (let i = 1; i < diagram.actors.length; i++) {
|
|
15594
15725
|
const prevSpan = boxSpanOf[i - 1];
|
|
15595
15726
|
const thisSpan = boxSpanOf[i];
|
|
15596
15727
|
const walls = prevSpan === thisSpan ? 0 : (prevSpan === -1 ? 0 : 1) + (thisSpan === -1 ? 0 : 1);
|
|
15597
15728
|
const wallExtra = walls === 0 ? 0 : walls * 2 - 1;
|
|
15598
|
-
const noteGapNeed = Math.max(rightNoteWidth[i - 1], leftNoteWidth[i]) + 2;
|
|
15729
|
+
const noteGapNeed = Math.max(rightNoteWidth[i - 1], leftNoteWidth[i], overNoteHalfRight[i - 1], overNoteHalfLeft[i]) + 2;
|
|
15599
15730
|
const gap = Math.max(halfBox[i - 1] + halfBox[i] + 2 + wallExtra, adjMaxWidth[i - 1] + 2, noteGapNeed, minLifelineGap);
|
|
15600
15731
|
llX[i] = llX[i - 1] + gap;
|
|
15601
15732
|
}
|
|
@@ -16400,27 +16531,90 @@ function renderClassAscii(text, config, colorMode, theme, options = {}) {
|
|
|
16400
16531
|
for (let lv = 0; lv <= maxLevel; lv++) {
|
|
16401
16532
|
const group = levelGroups[lv];
|
|
16402
16533
|
if (group.length === 0) continue;
|
|
16534
|
+
const blockKeyOf = (id) => {
|
|
16535
|
+
const pset = parents.get(id);
|
|
16536
|
+
return pset && pset.size > 0 ? [...pset].sort().join(",") : `root:${id}`;
|
|
16537
|
+
};
|
|
16538
|
+
const blockMembers = /* @__PURE__ */ new Map();
|
|
16539
|
+
for (const id of group) {
|
|
16540
|
+
const key = blockKeyOf(id);
|
|
16541
|
+
let members = blockMembers.get(key);
|
|
16542
|
+
if (!members) {
|
|
16543
|
+
members = [];
|
|
16544
|
+
blockMembers.set(key, members);
|
|
16545
|
+
}
|
|
16546
|
+
members.push(id);
|
|
16547
|
+
}
|
|
16548
|
+
const baselineSlotLeft = /* @__PURE__ */ new Map();
|
|
16549
|
+
const slotWidthOf = /* @__PURE__ */ new Map();
|
|
16550
|
+
const leftPadOf = /* @__PURE__ */ new Map();
|
|
16551
|
+
{
|
|
16552
|
+
let x = 0;
|
|
16553
|
+
for (const id of group) {
|
|
16554
|
+
const w = classBoxW.get(id);
|
|
16555
|
+
const reach = columnReach.get(id);
|
|
16556
|
+
const leftPad = Math.max(0, reach.left - Math.floor(w / 2));
|
|
16557
|
+
const rightPad = Math.max(0, reach.right - (w - 1 - Math.floor(w / 2)));
|
|
16558
|
+
baselineSlotLeft.set(id, x);
|
|
16559
|
+
slotWidthOf.set(id, leftPad + w + rightPad);
|
|
16560
|
+
leftPadOf.set(id, leftPad);
|
|
16561
|
+
x += leftPad + w + rightPad + hGap;
|
|
16562
|
+
}
|
|
16563
|
+
}
|
|
16403
16564
|
let currentX = 0;
|
|
16404
16565
|
let maxH = 0;
|
|
16405
|
-
|
|
16566
|
+
/** Places one class's box at slot-left `slotLeft`, advancing `currentX`/`maxH`/`maxSlotEnd`. */
|
|
16567
|
+
const placeAt = (id, slotLeft) => {
|
|
16406
16568
|
const cls = classById.get(id);
|
|
16407
16569
|
const w = classBoxW.get(id);
|
|
16408
16570
|
const h = classBoxH.get(id);
|
|
16409
|
-
const
|
|
16410
|
-
const
|
|
16411
|
-
const rightPad = Math.max(0, reach.right - (w - 1 - Math.floor(w / 2)));
|
|
16571
|
+
const leftPad = leftPadOf.get(id);
|
|
16572
|
+
const slotWidth = slotWidthOf.get(id);
|
|
16412
16573
|
placed.set(id, {
|
|
16413
16574
|
cls,
|
|
16414
16575
|
sections: classSections.get(id),
|
|
16415
|
-
x:
|
|
16576
|
+
x: slotLeft + leftPad,
|
|
16416
16577
|
y: currentY,
|
|
16417
16578
|
width: w,
|
|
16418
16579
|
height: h
|
|
16419
16580
|
});
|
|
16420
|
-
|
|
16421
|
-
|
|
16422
|
-
currentX += slotWidth + hGap;
|
|
16581
|
+
maxSlotEnd = Math.max(maxSlotEnd, slotLeft + slotWidth);
|
|
16582
|
+
currentX = slotLeft + slotWidth + hGap;
|
|
16423
16583
|
maxH = Math.max(maxH, h);
|
|
16584
|
+
};
|
|
16585
|
+
const placedThisLevel = /* @__PURE__ */ new Set();
|
|
16586
|
+
for (const id of group) {
|
|
16587
|
+
if (placedThisLevel.has(id)) continue;
|
|
16588
|
+
const key = blockKeyOf(id);
|
|
16589
|
+
const pset = parents.get(id);
|
|
16590
|
+
const qualifying = pset ? [...pset].filter((pid) => level.get(pid) < lv) : [];
|
|
16591
|
+
if (qualifying.length === 0) {
|
|
16592
|
+
placeAt(id, Math.max(baselineSlotLeft.get(id), currentX, 0));
|
|
16593
|
+
placedThisLevel.add(id);
|
|
16594
|
+
continue;
|
|
16595
|
+
}
|
|
16596
|
+
const members = blockMembers.get(key);
|
|
16597
|
+
const parentCenters = qualifying.map((pid) => {
|
|
16598
|
+
const p = placed.get(pid);
|
|
16599
|
+
return p.x + Math.floor(p.width / 2);
|
|
16600
|
+
});
|
|
16601
|
+
const desiredCenter = Math.round(parentCenters.reduce((sum, c) => sum + c, 0) / parentCenters.length);
|
|
16602
|
+
let totalWidth = 0;
|
|
16603
|
+
for (let i = 0; i < members.length; i++) {
|
|
16604
|
+
totalWidth += slotWidthOf.get(members[i]);
|
|
16605
|
+
if (i < members.length - 1) totalWidth += hGap;
|
|
16606
|
+
}
|
|
16607
|
+
const firstLeftPad = leftPadOf.get(members[0]);
|
|
16608
|
+
const lastMember = members[members.length - 1];
|
|
16609
|
+
const lastRightPad = slotWidthOf.get(lastMember) - leftPadOf.get(lastMember) - classBoxW.get(lastMember);
|
|
16610
|
+
const visibleWidth = totalWidth - firstLeftPad - lastRightPad;
|
|
16611
|
+
const blockDesiredLeft = desiredCenter - firstLeftPad - Math.floor(visibleWidth / 2);
|
|
16612
|
+
let memberX = Math.max(blockDesiredLeft, currentX, 0);
|
|
16613
|
+
for (const memberId of members) {
|
|
16614
|
+
placeAt(memberId, memberX);
|
|
16615
|
+
placedThisLevel.add(memberId);
|
|
16616
|
+
memberX = currentX;
|
|
16617
|
+
}
|
|
16424
16618
|
}
|
|
16425
16619
|
currentY += maxH + vGap;
|
|
16426
16620
|
}
|