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.js
CHANGED
|
@@ -787,12 +787,31 @@ function isGeometricShapesTextDefault(code) {
|
|
|
787
787
|
return code >= 9632 && code <= 9727;
|
|
788
788
|
}
|
|
789
789
|
/**
|
|
790
|
+
* Arrows-block glyphs that carry Extended_Pictographic (emoji-capable) but
|
|
791
|
+
* no default Emoji_Presentation, and render as a single narrow column in
|
|
792
|
+
* every real terminal — the same misclassification the Geometric Shapes
|
|
793
|
+
* block above hits. Covers:
|
|
794
|
+
*
|
|
795
|
+
* - ↖ U+2196, ↗ U+2197, ↘ U+2198, ↙ U+2199 — drawn by the ASCII renderer
|
|
796
|
+
* itself for diagonal edge-routing arrowheads
|
|
797
|
+
* (packages/ascii-renderer/src/draw-arrows.ts's `drawArrowHead`) in place
|
|
798
|
+
* of the filled triangles (◢◣◤◥) JetBrains Mono NL has no glyph for at
|
|
799
|
+
* all — see issue #1062.
|
|
800
|
+
* - ↔ U+2194, ↕ U+2195, ↩ U+21A9, ↪ U+21AA — not drawn by the renderer
|
|
801
|
+
* itself, but reachable if a diagram author types one into node/edge/note
|
|
802
|
+
* label text, which passes through to the ASCII grid verbatim.
|
|
803
|
+
*/
|
|
804
|
+
function isArrowsBlockTextDefault(code) {
|
|
805
|
+
return code >= 8596 && code <= 8601 || code === 8617 || code === 8618;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
790
808
|
* Check if a character is an emoji (fullwidth)
|
|
791
809
|
*/
|
|
792
810
|
function isEmoji(char) {
|
|
793
811
|
if (EMOJI_PRESENTATION_REGEX.test(char)) return true;
|
|
794
812
|
const code = char.codePointAt(0);
|
|
795
813
|
if (code !== void 0 && isGeometricShapesTextDefault(code)) return false;
|
|
814
|
+
if (code !== void 0 && isArrowsBlockTextDefault(code)) return false;
|
|
796
815
|
return EXTENDED_PICTOGRAPHIC_REGEX.test(char);
|
|
797
816
|
}
|
|
798
817
|
/**
|
|
@@ -1212,6 +1231,38 @@ function sanitizeClassName(className) {
|
|
|
1212
1231
|
return /^-?[a-zA-Z_][a-zA-Z0-9_-]*$/.test(className) ? className : void 0;
|
|
1213
1232
|
}
|
|
1214
1233
|
//#endregion
|
|
1234
|
+
//#region packages/core/src/generated/mono-font-subset.ts
|
|
1235
|
+
/**
|
|
1236
|
+
* GENERATED FILE — do not edit by hand.
|
|
1237
|
+
*
|
|
1238
|
+
* Regenerate with `pnpm run build:mono-font`
|
|
1239
|
+
* (scripts/build-mono-font-subset.ts), which subsets
|
|
1240
|
+
* third_party/fonts/jetbrains-mono-nl/JetBrainsMonoNL-Regular.ttf down to
|
|
1241
|
+
* Basic Latin + Latin-1 Supplement — the self-hosted, embeddable
|
|
1242
|
+
* `@font-face` packages/core/src/theme.ts's `buildStyleBlock()` inlines
|
|
1243
|
+
* into an SVG's own `<style>` for its `.mono` rule (class-diagram method
|
|
1244
|
+
* signatures, ER-diagram attribute types), replacing a Google Fonts CDN
|
|
1245
|
+
* `@import` this library's SVG output previously depended on by default
|
|
1246
|
+
* (#1061). See that script's header comment for why this subset is
|
|
1247
|
+
* deliberately narrower than the site's own (demo/components/generated/
|
|
1248
|
+
* mono-font-subset.ts).
|
|
1249
|
+
*/
|
|
1250
|
+
/** The self-hosted family name every consumer's font stack should list
|
|
1251
|
+
* first — matches this subset's own `name` table entry. */
|
|
1252
|
+
var MONO_FONT_FAMILY = "JetBrains Mono NL";
|
|
1253
|
+
/** The `@font-face` rule embedding the subset as a base64 `woff2` data
|
|
1254
|
+
* URI — no separate network request, no page-depth-relative path to get
|
|
1255
|
+
* wrong (see the script header comment for why data-URI over a static
|
|
1256
|
+
* asset file). */
|
|
1257
|
+
var MONO_FONT_FACE_CSS = `@font-face {
|
|
1258
|
+
font-family: 'JetBrains Mono NL';
|
|
1259
|
+
font-style: normal;
|
|
1260
|
+
font-weight: 400;
|
|
1261
|
+
font-display: swap;
|
|
1262
|
+
src: url(data:font/woff2;base64,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) format('woff2');
|
|
1263
|
+
unicode-range: U+0020-007E, U+00A0-00FF;
|
|
1264
|
+
}`;
|
|
1265
|
+
//#endregion
|
|
1215
1266
|
//#region packages/core/src/theme.ts
|
|
1216
1267
|
/** Default bg/fg when no colors are provided (zinc light) */
|
|
1217
1268
|
var DEFAULTS$1 = {
|
|
@@ -1459,11 +1510,26 @@ function styleOpenTag(nonce) {
|
|
|
1459
1510
|
*
|
|
1460
1511
|
* Any other value is treated as a literal font name: sanitized and quoted
|
|
1461
1512
|
* exactly as before.
|
|
1513
|
+
*
|
|
1514
|
+
* `hasMonoFont`'s `.mono` rule (class-diagram method signatures, ER-diagram
|
|
1515
|
+
* attribute types) never reaches out to Google Fonts at all — it used to
|
|
1516
|
+
* `@import` 'JetBrains Mono' from Google's CDN, a third-party network
|
|
1517
|
+
* dependency a published library's default SVG output had no business
|
|
1518
|
+
* making (#1061, filed as a follow-up from #1059's ASCII-side self-hosting
|
|
1519
|
+
* fix). It now embeds {@link SVG_MONO_FONT_FACE_CSS} — a small, subsetted
|
|
1520
|
+
* (Basic Latin + Latin-1 Supplement), self-hosted `@font-face` as a base64
|
|
1521
|
+
* `woff2` data URI, generated by scripts/build-mono-font-subset.ts into
|
|
1522
|
+
* ./generated/mono-font-subset.ts — directly in the SVG's own `<style>`
|
|
1523
|
+
* block. Unlike `font` above, this isn't caller-configurable: it's the same
|
|
1524
|
+
* embed for every consumer, on by default, with no network request either
|
|
1525
|
+
* way — a strict improvement over the dead-or-alive CDN fetch it replaces,
|
|
1526
|
+
* and the only way a *standalone* SVG (no surrounding host page providing
|
|
1527
|
+
* its own copy of the font) renders `.mono` text correctly out of the box.
|
|
1462
1528
|
*/
|
|
1463
1529
|
function buildStyleBlock(font, hasMonoFont, nonce) {
|
|
1464
1530
|
const isVarReference = isSafeCssVarReference(font);
|
|
1465
1531
|
const safeFont = isVarReference ? font.trim() : font.replace(/[<>{};'"]/g, "");
|
|
1466
|
-
const fontImports =
|
|
1532
|
+
const fontImports = isVarReference || isFontStackOrGenericFamily(font) ? [] : [`@import url('https://fonts.googleapis.com/css2?family=${encodeURIComponent(safeFont)}:wght@400;500;600;700&display=swap');`];
|
|
1467
1533
|
const derivedVars = `
|
|
1468
1534
|
/* Derived from --bg and --fg (overridable via --line, --accent, etc.) */
|
|
1469
1535
|
--_text: var(--fg);
|
|
@@ -1481,8 +1547,9 @@ function buildStyleBlock(font, hasMonoFont, nonce) {
|
|
|
1481
1547
|
return [
|
|
1482
1548
|
styleOpenTag(nonce),
|
|
1483
1549
|
...fontImports.length > 0 ? [` ${fontImports.join("\n ")}`] : [],
|
|
1550
|
+
...hasMonoFont ? [` ${MONO_FONT_FACE_CSS}`] : [],
|
|
1484
1551
|
` text { font-family: ${isVarReference ? safeFont : `'${safeFont}'`}, system-ui, sans-serif; }`,
|
|
1485
|
-
...hasMonoFont ? [` .mono { font-family: '
|
|
1552
|
+
...hasMonoFont ? [` .mono { font-family: '${MONO_FONT_FAMILY}', 'SF Mono', 'Fira Code', ui-monospace, monospace; }`] : [],
|
|
1486
1553
|
` svg {${derivedVars}`,
|
|
1487
1554
|
` }`,
|
|
1488
1555
|
"</style>"
|
|
@@ -2672,7 +2739,7 @@ function parseXYChart(lines) {
|
|
|
2672
2739
|
const xCatMatch = line.match(/^x-axis\s+(?:"([^"]*)"\s*)?\[([^\]]+)\]/);
|
|
2673
2740
|
if (xCatMatch) {
|
|
2674
2741
|
if (xCatMatch[1]) xAxis.title = xCatMatch[1];
|
|
2675
|
-
xAxis.categories = xCatMatch[2]
|
|
2742
|
+
xAxis.categories = splitCategoryList(xCatMatch[2]).map((s) => unquote(s.trim()));
|
|
2676
2743
|
continue;
|
|
2677
2744
|
}
|
|
2678
2745
|
const xRangeMatch = line.match(/^x-axis\s+(?:"([^"]*)"\s+)?(-?\d+(?:\.\d+)?)\s*-->\s*(-?\d+(?:\.\d+)?)/);
|
|
@@ -2743,6 +2810,39 @@ function parseXYChart(lines) {
|
|
|
2743
2810
|
};
|
|
2744
2811
|
}
|
|
2745
2812
|
/**
|
|
2813
|
+
* Split a categorical x-axis bracket's comma-separated contents into raw
|
|
2814
|
+
* item strings, treating a comma *inside* a double-quoted item as literal
|
|
2815
|
+
* text rather than a delimiter. A naive `.split(',')` would break
|
|
2816
|
+
* `["Jan, Feb", "Mar"]` into three pieces instead of two — quoting a value
|
|
2817
|
+
* that itself contains a comma is exactly the case quoting exists for. Each
|
|
2818
|
+
* returned raw item is still trimmed and unquoted by the caller.
|
|
2819
|
+
*/
|
|
2820
|
+
function splitCategoryList(str) {
|
|
2821
|
+
const items = [];
|
|
2822
|
+
let current = "";
|
|
2823
|
+
let inQuotes = false;
|
|
2824
|
+
for (const char of str) if (char === "\"") {
|
|
2825
|
+
inQuotes = !inQuotes;
|
|
2826
|
+
current += char;
|
|
2827
|
+
} else if (char === "," && !inQuotes) {
|
|
2828
|
+
items.push(current);
|
|
2829
|
+
current = "";
|
|
2830
|
+
} else current += char;
|
|
2831
|
+
items.push(current);
|
|
2832
|
+
return items;
|
|
2833
|
+
}
|
|
2834
|
+
/**
|
|
2835
|
+
* Strip one matching pair of leading/trailing double quotes from a category
|
|
2836
|
+
* item, if present — mirroring how an axis *title* capture group already
|
|
2837
|
+
* unquotes via its regex. `x-axis [A, B, C]` items are only split and
|
|
2838
|
+
* trimmed, so a quoted item like `"CLI output / logs"` previously kept its
|
|
2839
|
+
* literal quote characters in the rendered label. See issue #1087.
|
|
2840
|
+
*/
|
|
2841
|
+
function unquote(value) {
|
|
2842
|
+
if (value.length >= 2 && value.startsWith("\"") && value.endsWith("\"")) return value.slice(1, -1);
|
|
2843
|
+
return value;
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2746
2846
|
* Parse a `bar`/`line` series' comma-separated bracket contents into
|
|
2747
2847
|
* numbers, throwing on any element that isn't a valid number instead of
|
|
2748
2848
|
* silently coercing it to `NaN` (which serializes as `null` and can
|
|
@@ -9556,10 +9656,10 @@ function canvasToString$1(canvas, options) {
|
|
|
9556
9656
|
var VERTICAL_FLIP_MAP = {
|
|
9557
9657
|
"▲": "▼",
|
|
9558
9658
|
"▼": "▲",
|
|
9559
|
-
"
|
|
9560
|
-
"
|
|
9561
|
-
"
|
|
9562
|
-
"
|
|
9659
|
+
"↖": "↙",
|
|
9660
|
+
"↙": "↖",
|
|
9661
|
+
"↗": "↘",
|
|
9662
|
+
"↘": "↗",
|
|
9563
9663
|
"^": "v",
|
|
9564
9664
|
v: "^",
|
|
9565
9665
|
"┌": "└",
|
|
@@ -13869,51 +13969,74 @@ function markerArrowChar(useAscii, marker) {
|
|
|
13869
13969
|
if (marker === "cross") return useAscii ? "x" : "✕";
|
|
13870
13970
|
}
|
|
13871
13971
|
/**
|
|
13972
|
+
* Maps a direction to its Unicode arrowhead glyph, or `undefined` for a
|
|
13973
|
+
* direction with no arrowhead (`Middle`, or any other value this switch
|
|
13974
|
+
* doesn't recognize) — callers decide the ultimate fallback.
|
|
13975
|
+
*
|
|
13976
|
+
* Uses triangular Unicode symbols (▲▼◄►) for the four orthogonal
|
|
13977
|
+
* directions and diagonal arrow symbols (↗↖↘↙) for the four diagonal ones.
|
|
13978
|
+
* The diagonal glyphs are plain Arrows-block characters (U+2196–U+2199),
|
|
13979
|
+
* not filled triangles like the orthogonal set — JetBrains Mono NL (this
|
|
13980
|
+
* site's self-hosted ASCII font, see scripts/build-mono-font-subset.ts)
|
|
13981
|
+
* has no glyph at all for the filled diagonal triangles (◢◣◤◥, U+25E2–
|
|
13982
|
+
* U+25E5) that would otherwise match the orthogonal style; U+2196–U+2199
|
|
13983
|
+
* are real, correctly-directional glyphs the font does have, which beats
|
|
13984
|
+
* falling back to an unpinned system font for just these four characters.
|
|
13985
|
+
* See issue #1062.
|
|
13986
|
+
*
|
|
13987
|
+
* Exported (alongside {@link asciiArrowChar}) so tests can exercise every
|
|
13988
|
+
* direction directly — the ELK/pathfinder layout this module actually
|
|
13989
|
+
* draws from rarely if ever produces some direction combinations (e.g.
|
|
13990
|
+
* `LowerLeft` only arises from determinePath's rare Case-4 diagonal
|
|
13991
|
+
* fallback, and only for a specific relative source/target position no
|
|
13992
|
+
* existing sample or hand-written diagram happens to trigger), which would
|
|
13993
|
+
* otherwise leave this mapping's correctness for those directions unverified.
|
|
13994
|
+
*/
|
|
13995
|
+
function unicodeArrowChar(dir) {
|
|
13996
|
+
if (dirEquals(dir, Up)) return "▲";
|
|
13997
|
+
if (dirEquals(dir, Down)) return "▼";
|
|
13998
|
+
if (dirEquals(dir, Left)) return "◄";
|
|
13999
|
+
if (dirEquals(dir, Right)) return "►";
|
|
14000
|
+
if (dirEquals(dir, UpperRight)) return "↗";
|
|
14001
|
+
if (dirEquals(dir, UpperLeft)) return "↖";
|
|
14002
|
+
if (dirEquals(dir, LowerRight)) return "↘";
|
|
14003
|
+
if (dirEquals(dir, LowerLeft)) return "↙";
|
|
14004
|
+
}
|
|
14005
|
+
/** ASCII-mode counterpart of {@link unicodeArrowChar} — orthogonal only, no diagonals. */
|
|
14006
|
+
function asciiArrowChar(dir) {
|
|
14007
|
+
if (dirEquals(dir, Up)) return "^";
|
|
14008
|
+
if (dirEquals(dir, Down)) return "v";
|
|
14009
|
+
if (dirEquals(dir, Left)) return "<";
|
|
14010
|
+
if (dirEquals(dir, Right)) return ">";
|
|
14011
|
+
}
|
|
14012
|
+
/**
|
|
13872
14013
|
* Draw the arrowhead at the end of an edge path.
|
|
13873
|
-
* Uses triangular Unicode symbols (▲▼◄►) or ASCII symbols (^v<>).
|
|
13874
14014
|
*
|
|
13875
|
-
* `marker` overrides the directional
|
|
13876
|
-
*
|
|
14015
|
+
* `marker` overrides the directional glyph with a fixed circle/cross glyph
|
|
14016
|
+
* for `--o`/`--x` (flowchart) terminators — those are direction-
|
|
13877
14017
|
* independent, so the direction computed below is only used to place it,
|
|
13878
14018
|
* never to pick which glyph to draw. See issue #330.
|
|
13879
14019
|
*/
|
|
13880
14020
|
function drawArrowHead(graph, lastLine, fallbackDir, marker) {
|
|
13881
14021
|
const canvas = copyCanvas(graph.canvas);
|
|
13882
14022
|
if (lastLine.length === 0) return canvas;
|
|
13883
|
-
const from = lastLine[0];
|
|
13884
14023
|
const lastPos = lastLine[lastLine.length - 1];
|
|
13885
|
-
let dir = determineDirection(
|
|
14024
|
+
let dir = determineDirection(lastLine.length >= 2 ? lastLine[lastLine.length - 2] : lastLine[0], lastPos);
|
|
13886
14025
|
if (lastLine.length === 1 || dirEquals(dir, Middle)) dir = fallbackDir;
|
|
13887
|
-
let char;
|
|
13888
14026
|
const markerChar = markerArrowChar(graph.config.useAscii, marker);
|
|
14027
|
+
let char;
|
|
13889
14028
|
if (markerChar !== void 0) char = markerChar;
|
|
13890
14029
|
else if (!graph.config.useAscii) {
|
|
13891
|
-
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
else
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
13899
|
-
else
|
|
13900
|
-
|
|
13901
|
-
else if (dirEquals(fallbackDir, Left)) char = "◄";
|
|
13902
|
-
else if (dirEquals(fallbackDir, Right)) char = "►";
|
|
13903
|
-
else if (dirEquals(fallbackDir, UpperRight)) char = "◥";
|
|
13904
|
-
else if (dirEquals(fallbackDir, UpperLeft)) char = "◤";
|
|
13905
|
-
else if (dirEquals(fallbackDir, LowerRight)) char = "◢";
|
|
13906
|
-
else if (dirEquals(fallbackDir, LowerLeft)) char = "◣";
|
|
13907
|
-
else char = "●";
|
|
13908
|
-
} else if (dirEquals(dir, Up)) char = "^";
|
|
13909
|
-
else if (dirEquals(dir, Down)) char = "v";
|
|
13910
|
-
else if (dirEquals(dir, Left)) char = "<";
|
|
13911
|
-
else if (dirEquals(dir, Right)) char = ">";
|
|
13912
|
-
else if (dirEquals(fallbackDir, Up)) char = "^";
|
|
13913
|
-
else if (dirEquals(fallbackDir, Down)) char = "v";
|
|
13914
|
-
else if (dirEquals(fallbackDir, Left)) char = "<";
|
|
13915
|
-
else if (dirEquals(fallbackDir, Right)) char = ">";
|
|
13916
|
-
else char = "*";
|
|
14030
|
+
const resolved = unicodeArrowChar(dir);
|
|
14031
|
+
/* v8 ignore else */
|
|
14032
|
+
if (resolved !== void 0) char = resolved;
|
|
14033
|
+
else char = unicodeArrowChar(fallbackDir) ?? "●";
|
|
14034
|
+
} else {
|
|
14035
|
+
const resolved = asciiArrowChar(dir);
|
|
14036
|
+
/* v8 ignore else */
|
|
14037
|
+
if (resolved !== void 0) char = resolved;
|
|
14038
|
+
else char = asciiArrowChar(fallbackDir) ?? "*";
|
|
14039
|
+
}
|
|
13917
14040
|
write(canvas, lastPos.x, lastPos.y, char);
|
|
13918
14041
|
return canvas;
|
|
13919
14042
|
}
|
|
@@ -15556,21 +15679,29 @@ function renderSequenceAscii(text, config, colorMode, theme) {
|
|
|
15556
15679
|
}
|
|
15557
15680
|
const leftNoteWidth = new Array(diagram.actors.length).fill(0);
|
|
15558
15681
|
const rightNoteWidth = new Array(diagram.actors.length).fill(0);
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
|
|
15682
|
+
const overNoteHalfLeft = new Array(diagram.actors.length).fill(0);
|
|
15683
|
+
const overNoteHalfRight = new Array(diagram.actors.length).fill(0);
|
|
15684
|
+
for (const note of diagram.notes) if (note.position === "left" || note.position === "right") {
|
|
15685
|
+
const aIdx = actorIndexOf(note.actorIds[0]);
|
|
15562
15686
|
const w = noteBoxWidth(note);
|
|
15563
15687
|
if (note.position === "left") leftNoteWidth[aIdx] = Math.max(leftNoteWidth[aIdx], w);
|
|
15564
15688
|
else rightNoteWidth[aIdx] = Math.max(rightNoteWidth[aIdx], w);
|
|
15689
|
+
} else if (note.position === "over" && note.actorIds.length === 1) {
|
|
15690
|
+
const aIdx = actorIndexOf(note.actorIds[0]);
|
|
15691
|
+
const w = noteBoxWidth(note);
|
|
15692
|
+
const halfLeft = Math.floor(w / 2);
|
|
15693
|
+
const halfRight = w - halfLeft;
|
|
15694
|
+
overNoteHalfLeft[aIdx] = Math.max(overNoteHalfLeft[aIdx], halfLeft);
|
|
15695
|
+
overNoteHalfRight[aIdx] = Math.max(overNoteHalfRight[aIdx], halfRight);
|
|
15565
15696
|
}
|
|
15566
15697
|
const minLifelineGap = paddingOffset(config.paddingX, 5, 10, 4);
|
|
15567
|
-
const llX = [Math.max(halfBox[0] + (boxSpanOf[0] === -1 ? 0 : 2), leftNoteWidth[0] + 1)];
|
|
15698
|
+
const llX = [Math.max(halfBox[0] + (boxSpanOf[0] === -1 ? 0 : 2), leftNoteWidth[0] + 1, overNoteHalfLeft[0])];
|
|
15568
15699
|
for (let i = 1; i < diagram.actors.length; i++) {
|
|
15569
15700
|
const prevSpan = boxSpanOf[i - 1];
|
|
15570
15701
|
const thisSpan = boxSpanOf[i];
|
|
15571
15702
|
const walls = prevSpan === thisSpan ? 0 : (prevSpan === -1 ? 0 : 1) + (thisSpan === -1 ? 0 : 1);
|
|
15572
15703
|
const wallExtra = walls === 0 ? 0 : walls * 2 - 1;
|
|
15573
|
-
const noteGapNeed = Math.max(rightNoteWidth[i - 1], leftNoteWidth[i]) + 2;
|
|
15704
|
+
const noteGapNeed = Math.max(rightNoteWidth[i - 1], leftNoteWidth[i], overNoteHalfRight[i - 1], overNoteHalfLeft[i]) + 2;
|
|
15574
15705
|
const gap = Math.max(halfBox[i - 1] + halfBox[i] + 2 + wallExtra, adjMaxWidth[i - 1] + 2, noteGapNeed, minLifelineGap);
|
|
15575
15706
|
llX[i] = llX[i - 1] + gap;
|
|
15576
15707
|
}
|
|
@@ -16375,27 +16506,90 @@ function renderClassAscii(text, config, colorMode, theme, options = {}) {
|
|
|
16375
16506
|
for (let lv = 0; lv <= maxLevel; lv++) {
|
|
16376
16507
|
const group = levelGroups[lv];
|
|
16377
16508
|
if (group.length === 0) continue;
|
|
16509
|
+
const blockKeyOf = (id) => {
|
|
16510
|
+
const pset = parents.get(id);
|
|
16511
|
+
return pset && pset.size > 0 ? [...pset].sort().join(",") : `root:${id}`;
|
|
16512
|
+
};
|
|
16513
|
+
const blockMembers = /* @__PURE__ */ new Map();
|
|
16514
|
+
for (const id of group) {
|
|
16515
|
+
const key = blockKeyOf(id);
|
|
16516
|
+
let members = blockMembers.get(key);
|
|
16517
|
+
if (!members) {
|
|
16518
|
+
members = [];
|
|
16519
|
+
blockMembers.set(key, members);
|
|
16520
|
+
}
|
|
16521
|
+
members.push(id);
|
|
16522
|
+
}
|
|
16523
|
+
const baselineSlotLeft = /* @__PURE__ */ new Map();
|
|
16524
|
+
const slotWidthOf = /* @__PURE__ */ new Map();
|
|
16525
|
+
const leftPadOf = /* @__PURE__ */ new Map();
|
|
16526
|
+
{
|
|
16527
|
+
let x = 0;
|
|
16528
|
+
for (const id of group) {
|
|
16529
|
+
const w = classBoxW.get(id);
|
|
16530
|
+
const reach = columnReach.get(id);
|
|
16531
|
+
const leftPad = Math.max(0, reach.left - Math.floor(w / 2));
|
|
16532
|
+
const rightPad = Math.max(0, reach.right - (w - 1 - Math.floor(w / 2)));
|
|
16533
|
+
baselineSlotLeft.set(id, x);
|
|
16534
|
+
slotWidthOf.set(id, leftPad + w + rightPad);
|
|
16535
|
+
leftPadOf.set(id, leftPad);
|
|
16536
|
+
x += leftPad + w + rightPad + hGap;
|
|
16537
|
+
}
|
|
16538
|
+
}
|
|
16378
16539
|
let currentX = 0;
|
|
16379
16540
|
let maxH = 0;
|
|
16380
|
-
|
|
16541
|
+
/** Places one class's box at slot-left `slotLeft`, advancing `currentX`/`maxH`/`maxSlotEnd`. */
|
|
16542
|
+
const placeAt = (id, slotLeft) => {
|
|
16381
16543
|
const cls = classById.get(id);
|
|
16382
16544
|
const w = classBoxW.get(id);
|
|
16383
16545
|
const h = classBoxH.get(id);
|
|
16384
|
-
const
|
|
16385
|
-
const
|
|
16386
|
-
const rightPad = Math.max(0, reach.right - (w - 1 - Math.floor(w / 2)));
|
|
16546
|
+
const leftPad = leftPadOf.get(id);
|
|
16547
|
+
const slotWidth = slotWidthOf.get(id);
|
|
16387
16548
|
placed.set(id, {
|
|
16388
16549
|
cls,
|
|
16389
16550
|
sections: classSections.get(id),
|
|
16390
|
-
x:
|
|
16551
|
+
x: slotLeft + leftPad,
|
|
16391
16552
|
y: currentY,
|
|
16392
16553
|
width: w,
|
|
16393
16554
|
height: h
|
|
16394
16555
|
});
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
currentX += slotWidth + hGap;
|
|
16556
|
+
maxSlotEnd = Math.max(maxSlotEnd, slotLeft + slotWidth);
|
|
16557
|
+
currentX = slotLeft + slotWidth + hGap;
|
|
16398
16558
|
maxH = Math.max(maxH, h);
|
|
16559
|
+
};
|
|
16560
|
+
const placedThisLevel = /* @__PURE__ */ new Set();
|
|
16561
|
+
for (const id of group) {
|
|
16562
|
+
if (placedThisLevel.has(id)) continue;
|
|
16563
|
+
const key = blockKeyOf(id);
|
|
16564
|
+
const pset = parents.get(id);
|
|
16565
|
+
const qualifying = pset ? [...pset].filter((pid) => level.get(pid) < lv) : [];
|
|
16566
|
+
if (qualifying.length === 0) {
|
|
16567
|
+
placeAt(id, Math.max(baselineSlotLeft.get(id), currentX, 0));
|
|
16568
|
+
placedThisLevel.add(id);
|
|
16569
|
+
continue;
|
|
16570
|
+
}
|
|
16571
|
+
const members = blockMembers.get(key);
|
|
16572
|
+
const parentCenters = qualifying.map((pid) => {
|
|
16573
|
+
const p = placed.get(pid);
|
|
16574
|
+
return p.x + Math.floor(p.width / 2);
|
|
16575
|
+
});
|
|
16576
|
+
const desiredCenter = Math.round(parentCenters.reduce((sum, c) => sum + c, 0) / parentCenters.length);
|
|
16577
|
+
let totalWidth = 0;
|
|
16578
|
+
for (let i = 0; i < members.length; i++) {
|
|
16579
|
+
totalWidth += slotWidthOf.get(members[i]);
|
|
16580
|
+
if (i < members.length - 1) totalWidth += hGap;
|
|
16581
|
+
}
|
|
16582
|
+
const firstLeftPad = leftPadOf.get(members[0]);
|
|
16583
|
+
const lastMember = members[members.length - 1];
|
|
16584
|
+
const lastRightPad = slotWidthOf.get(lastMember) - leftPadOf.get(lastMember) - classBoxW.get(lastMember);
|
|
16585
|
+
const visibleWidth = totalWidth - firstLeftPad - lastRightPad;
|
|
16586
|
+
const blockDesiredLeft = desiredCenter - firstLeftPad - Math.floor(visibleWidth / 2);
|
|
16587
|
+
let memberX = Math.max(blockDesiredLeft, currentX, 0);
|
|
16588
|
+
for (const memberId of members) {
|
|
16589
|
+
placeAt(memberId, memberX);
|
|
16590
|
+
placedThisLevel.add(memberId);
|
|
16591
|
+
memberX = currentX;
|
|
16592
|
+
}
|
|
16399
16593
|
}
|
|
16400
16594
|
currentY += maxH + vGap;
|
|
16401
16595
|
}
|