@zenuml/core 4.3.0 → 4.3.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/cli/zenuml.mjs +274 -117
- package/dist/zenuml.esm.mjs +9248 -9026
- package/dist/zenuml.js +555 -550
- package/package.json +1 -1
- package/types/index.d.ts +2 -0
package/dist/cli/zenuml.mjs
CHANGED
|
@@ -9723,6 +9723,7 @@ const PARTICIPANT_ICON_WIDTH = 40;
|
|
|
9723
9723
|
const LIFELINE_WIDTH = 1;
|
|
9724
9724
|
const PARTICIPANT_TOP_SPACE_FOR_GROUP = 20;
|
|
9725
9725
|
const OCCURRENCE_EMPTY_HEIGHT = 24;
|
|
9726
|
+
const DIAGRAM_HEADER_HEIGHT = 24 + 4 * 2 + 1;
|
|
9726
9727
|
|
|
9727
9728
|
var TextType = /* @__PURE__ */ ((TextType2) => {
|
|
9728
9729
|
TextType2[TextType2["MessageContent"] = 0] = "MessageContent";
|
|
@@ -10120,6 +10121,7 @@ const ASYNC_SELF_MESSAGE_HEIGHT = 44;
|
|
|
10120
10121
|
const CREATION_MESSAGE_HEIGHT = 40;
|
|
10121
10122
|
const COMMENT_LINE_HEIGHT = tw(5);
|
|
10122
10123
|
const FRAGMENT_BORDER_WIDTH = 1;
|
|
10124
|
+
const FRAGMENT_GUARD_HEIGHT = 28;
|
|
10123
10125
|
|
|
10124
10126
|
function getLocalParticipantNames(ctx) {
|
|
10125
10127
|
if (!ctx) return [];
|
|
@@ -10652,7 +10654,7 @@ class FragmentAltVM extends StatementVM {
|
|
|
10652
10654
|
let cursor = top + 1 + this.metrics.fragmentHeaderHeight + commentHeight;
|
|
10653
10655
|
const ifBlock = this.alt?.ifBlock?.();
|
|
10654
10656
|
if (ifBlock) {
|
|
10655
|
-
cursor +=
|
|
10657
|
+
cursor += FRAGMENT_GUARD_HEIGHT;
|
|
10656
10658
|
cursor = this.layoutBlock(
|
|
10657
10659
|
ifBlock.braceBlock()?.block(),
|
|
10658
10660
|
leftParticipant,
|
|
@@ -10660,7 +10662,7 @@ class FragmentAltVM extends StatementVM {
|
|
|
10660
10662
|
);
|
|
10661
10663
|
}
|
|
10662
10664
|
this.alt?.elseIfBlock?.()?.forEach((block) => {
|
|
10663
|
-
cursor +=
|
|
10665
|
+
cursor += FRAGMENT_GUARD_HEIGHT;
|
|
10664
10666
|
cursor += 8;
|
|
10665
10667
|
cursor += 1;
|
|
10666
10668
|
cursor = this.layoutBlock(
|
|
@@ -10671,7 +10673,7 @@ class FragmentAltVM extends StatementVM {
|
|
|
10671
10673
|
});
|
|
10672
10674
|
const elseBlock = this.alt?.elseBlock?.()?.braceBlock?.()?.block?.();
|
|
10673
10675
|
if (elseBlock) {
|
|
10674
|
-
cursor +=
|
|
10676
|
+
cursor += FRAGMENT_GUARD_HEIGHT;
|
|
10675
10677
|
cursor += 8;
|
|
10676
10678
|
cursor += 1;
|
|
10677
10679
|
cursor = this.layoutBlock(elseBlock, leftParticipant, cursor);
|
|
@@ -10706,7 +10708,7 @@ class FragmentSingleBlockVM extends StatementVM {
|
|
|
10706
10708
|
const commentHeight = this.measureComment(this.fragment);
|
|
10707
10709
|
let cursor = top + FRAGMENT_BORDER_WIDTH + this.metrics.fragmentHeaderHeight + commentHeight;
|
|
10708
10710
|
const hasCondition = Boolean(this.fragment?.parExpr?.()?.condition?.());
|
|
10709
|
-
if (hasCondition) cursor +=
|
|
10711
|
+
if (hasCondition) cursor += FRAGMENT_GUARD_HEIGHT;
|
|
10710
10712
|
const block = this.fragment?.braceBlock?.()?.block?.();
|
|
10711
10713
|
if (block) {
|
|
10712
10714
|
const fragmentOrigin = this.findLeftParticipant(this.fragment, origin) || origin;
|
|
@@ -10955,10 +10957,14 @@ const clearPersistentCache = () => {
|
|
|
10955
10957
|
};
|
|
10956
10958
|
|
|
10957
10959
|
const FONT_FAMILY = "Helvetica, Verdana, serif";
|
|
10958
|
-
const FONT_SIZE_PARTICIPANT = "
|
|
10960
|
+
const FONT_SIZE_PARTICIPANT = "14px";
|
|
10961
|
+
const FONT_SIZE_MESSAGE = "15px";
|
|
10959
10962
|
const FONT_SIZE_FRAGMENT = "14px";
|
|
10960
|
-
function
|
|
10961
|
-
return
|
|
10963
|
+
function getFontSize(type) {
|
|
10964
|
+
return type === TextType.MessageContent ? FONT_SIZE_MESSAGE : FONT_SIZE_PARTICIPANT;
|
|
10965
|
+
}
|
|
10966
|
+
function getFontSpec(type) {
|
|
10967
|
+
return `${getFontSize(type)} ${FONT_FAMILY}`;
|
|
10962
10968
|
}
|
|
10963
10969
|
let canvasCtx = null;
|
|
10964
10970
|
function setCanvasContext(ctx) {
|
|
@@ -10980,18 +10986,21 @@ function getCanvasContext() {
|
|
|
10980
10986
|
}
|
|
10981
10987
|
function WidthProviderOnCanvas(text, type) {
|
|
10982
10988
|
const measured = text.trim();
|
|
10983
|
-
const cacheKey = `WidthProviderOnCanvas_${getFontSpec()}_${measured}_${type}`;
|
|
10989
|
+
const cacheKey = `WidthProviderOnCanvas_${getFontSpec(type)}_${measured}_${type}`;
|
|
10984
10990
|
const cacheValue = getCache(cacheKey);
|
|
10985
10991
|
if (cacheValue != null) {
|
|
10986
10992
|
return cacheValue;
|
|
10987
10993
|
}
|
|
10988
10994
|
const ctx = getCanvasContext();
|
|
10989
10995
|
if (!ctx) {
|
|
10990
|
-
const width2 = Math.ceil(
|
|
10996
|
+
const width2 = Math.ceil(
|
|
10997
|
+
measured.length * Number.parseFloat(getFontSize(type)) * 0.6
|
|
10998
|
+
);
|
|
10991
10999
|
setCache(cacheKey, width2);
|
|
10992
11000
|
return width2;
|
|
10993
11001
|
}
|
|
10994
|
-
ctx.font = getFontSpec();
|
|
11002
|
+
ctx.font = getFontSpec(type);
|
|
11003
|
+
ctx.fontVariantCaps = "normal";
|
|
10995
11004
|
const width = Math.round(ctx.measureText(measured).width);
|
|
10996
11005
|
setCache(cacheKey, width, true);
|
|
10997
11006
|
return width;
|
|
@@ -11000,32 +11009,34 @@ const EMOJI_PATTERN = (
|
|
|
11000
11009
|
// eslint-disable-next-line no-misleading-character-class
|
|
11001
11010
|
/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE00}-\u{FE0F}\u{200D}]/u
|
|
11002
11011
|
);
|
|
11003
|
-
function measureWithSvg(text, fontSize) {
|
|
11012
|
+
function measureWithSvg(text, fontSize, caps = "normal") {
|
|
11004
11013
|
if (typeof document === "undefined") return null;
|
|
11005
11014
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
11006
11015
|
svg.setAttribute("style", "position:absolute;left:-9999px;top:-9999px");
|
|
11007
11016
|
const textEl = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
11008
11017
|
textEl.setAttribute("font-family", FONT_FAMILY);
|
|
11009
11018
|
textEl.setAttribute("font-size", fontSize);
|
|
11019
|
+
textEl.style.fontVariantCaps = caps;
|
|
11020
|
+
if (typeof textEl.getBBox !== "function") return null;
|
|
11010
11021
|
textEl.textContent = text;
|
|
11011
11022
|
svg.appendChild(textEl);
|
|
11012
11023
|
document.body.appendChild(svg);
|
|
11013
11024
|
const width = textEl.getBBox().width;
|
|
11014
11025
|
document.body.removeChild(svg);
|
|
11015
|
-
return width;
|
|
11026
|
+
return Number.isFinite(width) && width > 0 ? width : null;
|
|
11016
11027
|
}
|
|
11017
|
-
function measureTextWithFont(text, fontSize) {
|
|
11028
|
+
function measureTextWithFont(text, fontSize, caps = "normal") {
|
|
11018
11029
|
const measured = text.trim();
|
|
11019
11030
|
if (!measured) return 0;
|
|
11020
11031
|
const hasEmoji = EMOJI_PATTERN.test(measured);
|
|
11021
11032
|
const font = `${fontSize} ${FONT_FAMILY}`;
|
|
11022
|
-
const cacheKey = hasEmoji ? `measureTextWithFont_svg_${font}_${measured}` : `measureTextWithFont_${font}_${measured}`;
|
|
11033
|
+
const cacheKey = (hasEmoji ? `measureTextWithFont_svg_${font}_${measured}` : `measureTextWithFont_${font}_${measured}`) + `_${caps}`;
|
|
11023
11034
|
const cacheValue = getCache(cacheKey);
|
|
11024
11035
|
if (cacheValue != null) {
|
|
11025
11036
|
return cacheValue;
|
|
11026
11037
|
}
|
|
11027
|
-
if (hasEmoji) {
|
|
11028
|
-
const svgWidth = measureWithSvg(measured, fontSize);
|
|
11038
|
+
if (hasEmoji || caps !== "normal") {
|
|
11039
|
+
const svgWidth = measureWithSvg(measured, fontSize, caps);
|
|
11029
11040
|
if (svgWidth != null) {
|
|
11030
11041
|
setCache(cacheKey, svgWidth, true);
|
|
11031
11042
|
return svgWidth;
|
|
@@ -11039,7 +11050,15 @@ function measureTextWithFont(text, fontSize) {
|
|
|
11039
11050
|
return width2;
|
|
11040
11051
|
}
|
|
11041
11052
|
ctx.font = font;
|
|
11042
|
-
|
|
11053
|
+
ctx.fontVariantCaps = "normal";
|
|
11054
|
+
const normalWidth = ctx.measureText(measured).width;
|
|
11055
|
+
let width = normalWidth;
|
|
11056
|
+
if (caps !== "normal") {
|
|
11057
|
+
const normalProbe = ctx.measureText("iiii").width;
|
|
11058
|
+
ctx.fontVariantCaps = caps;
|
|
11059
|
+
width = Math.abs(ctx.measureText("iiii").width - normalProbe) > 0.01 ? ctx.measureText(measured).width : Math.max(normalWidth, ctx.measureText(measured.toUpperCase()).width);
|
|
11060
|
+
ctx.fontVariantCaps = "normal";
|
|
11061
|
+
}
|
|
11043
11062
|
setCache(cacheKey, width, true);
|
|
11044
11063
|
return width;
|
|
11045
11064
|
}
|
|
@@ -11050,6 +11069,11 @@ function measureSvgParticipantLabelWidth(text) {
|
|
|
11050
11069
|
return measureTextWithFont(text, FONT_SIZE_PARTICIPANT);
|
|
11051
11070
|
}
|
|
11052
11071
|
|
|
11072
|
+
const fragmentGuardKeyword = (kind, sourceKeyword) => kind === "alt" || kind === "opt" ? "if" : kind === "loop" ? sourceKeyword ?? "loop" : void 0;
|
|
11073
|
+
const fragmentGuardWidth = (condition, keyword) => Math.ceil(
|
|
11074
|
+
10 + measureTextWithFont(resolveEmojiInText(condition), "14px") + (keyword ? measureTextWithFont(keyword, "12px", "all-small-caps") + (condition ? 4 : 0) : 0)
|
|
11075
|
+
);
|
|
11076
|
+
|
|
11053
11077
|
const walker = antlr4.tree.ParseTreeWalker.DEFAULT;
|
|
11054
11078
|
class FrameBuilder extends sequenceParserListener {
|
|
11055
11079
|
_orderedParticipants;
|
|
@@ -11164,6 +11188,23 @@ function FrameBorder(frame) {
|
|
|
11164
11188
|
};
|
|
11165
11189
|
}
|
|
11166
11190
|
|
|
11191
|
+
const fragmentHeaderLabel = (kind) => {
|
|
11192
|
+
const labels = {
|
|
11193
|
+
alt: "Alt",
|
|
11194
|
+
loop: "Loop",
|
|
11195
|
+
opt: "Opt",
|
|
11196
|
+
par: "Par",
|
|
11197
|
+
critical: "Critical",
|
|
11198
|
+
section: "Section",
|
|
11199
|
+
tcf: "Try",
|
|
11200
|
+
ref: "Ref"
|
|
11201
|
+
};
|
|
11202
|
+
return labels[kind] ?? kind.charAt(0).toUpperCase() + kind.slice(1);
|
|
11203
|
+
};
|
|
11204
|
+
const fragmentHeaderWidth = (label, number) => Math.ceil(
|
|
11205
|
+
32 + measureTextWithFont(label, "14px", "all-small-caps") * 1.1 + (number ? measureTextWithFont(number, "12px") + 15 : 0)
|
|
11206
|
+
);
|
|
11207
|
+
|
|
11167
11208
|
function TotalWidth(ctx, coordinates) {
|
|
11168
11209
|
const allParticipants = coordinates.orderedParticipantNames();
|
|
11169
11210
|
if (!ctx || allParticipants.length === 0) return 0;
|
|
@@ -11388,7 +11429,8 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11388
11429
|
if (firstStatKey) {
|
|
11389
11430
|
const innerCoord = verticalCoordinates.getStatementCoordinate(firstStatKey);
|
|
11390
11431
|
if (innerCoord) {
|
|
11391
|
-
|
|
11432
|
+
const labelHeight = info.fragmentKind === "alt" ? FRAGMENT_GUARD_HEIGHT : 20;
|
|
11433
|
+
sectionY = innerCoord.top - labelHeight - 8 - 8 - 1;
|
|
11392
11434
|
}
|
|
11393
11435
|
}
|
|
11394
11436
|
}
|
|
@@ -11397,11 +11439,14 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11397
11439
|
const sectionHeight = i < info.fragmentSections.length - 1 ? 0 : coord.top + coord.height - sectionY;
|
|
11398
11440
|
sections.push({
|
|
11399
11441
|
label: section.label,
|
|
11442
|
+
guardKeyword: section.guardKeyword,
|
|
11400
11443
|
y: sectionY,
|
|
11401
11444
|
height: sectionHeight,
|
|
11402
11445
|
labelWidth: section.label ? measureSvgFragmentLabelWidth(resolveEmojiInText(section.label)) : void 0,
|
|
11403
|
-
innerLabel: /^\[\s*.*\s*\]$/.test(section.label) ? section.label.slice(1, -1).trim() : void 0,
|
|
11404
|
-
innerLabelWidth:
|
|
11446
|
+
innerLabel: section.condition ?? (/^\[\s*.*\s*\]$/.test(section.label) ? section.label.slice(1, -1).trim() : void 0),
|
|
11447
|
+
innerLabelWidth: section.condition !== void 0 ? measureSvgFragmentLabelWidth(
|
|
11448
|
+
resolveEmojiInText(section.condition)
|
|
11449
|
+
) : /^\[\s*.*\s*\]$/.test(section.label) ? measureSvgFragmentLabelWidth(
|
|
11405
11450
|
resolveEmojiInText(section.label.slice(1, -1).trim())
|
|
11406
11451
|
) : void 0,
|
|
11407
11452
|
keyword: (() => {
|
|
@@ -11472,7 +11517,9 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11472
11517
|
return {
|
|
11473
11518
|
fragment: {
|
|
11474
11519
|
kind: info.fragmentKind,
|
|
11520
|
+
headerLabel: info.fragmentHeaderLabel,
|
|
11475
11521
|
label: info.fragmentLabel || "",
|
|
11522
|
+
guardKeyword: info.guardKeyword,
|
|
11476
11523
|
labelWidth: info.fragmentLabel ? measureSvgFragmentLabelWidth(resolveEmojiInText(info.fragmentLabel)) : void 0,
|
|
11477
11524
|
x: fragX,
|
|
11478
11525
|
y: coord.top,
|
|
@@ -11688,6 +11735,8 @@ function walkBlock(block, currentOrigin, activeOccurrences, parentNumber, depth,
|
|
|
11688
11735
|
hasBlock: false,
|
|
11689
11736
|
fragmentKind: fragmentInfo.fragmentKind,
|
|
11690
11737
|
fragmentLabel: fragmentInfo.label,
|
|
11738
|
+
fragmentHeaderLabel: fragmentInfo.headerLabel,
|
|
11739
|
+
guardKeyword: fragmentInfo.guardKeyword,
|
|
11691
11740
|
fragmentSections: fragmentInfo.sections,
|
|
11692
11741
|
comment,
|
|
11693
11742
|
statNode: stat,
|
|
@@ -11714,7 +11763,9 @@ function extractFragmentInfo(stat) {
|
|
|
11714
11763
|
const label = condition?.getFormattedText?.() || "";
|
|
11715
11764
|
return {
|
|
11716
11765
|
fragmentKind: kind,
|
|
11766
|
+
headerLabel: frag.atom?.()?.getFormattedText?.(),
|
|
11717
11767
|
label,
|
|
11768
|
+
guardKeyword: kind === "loop" ? frag.WHILE?.()?.getText?.() : void 0,
|
|
11718
11769
|
sections: [{ label, blockNode: frag.braceBlock?.()?.block?.() }]
|
|
11719
11770
|
};
|
|
11720
11771
|
}
|
|
@@ -11726,6 +11777,8 @@ function extractFragmentInfo(stat) {
|
|
|
11726
11777
|
if (ifBlock) {
|
|
11727
11778
|
sections.push({
|
|
11728
11779
|
label: "Alt",
|
|
11780
|
+
guardKeyword: "if",
|
|
11781
|
+
condition: ifBlock.parExpr?.()?.condition?.()?.getFormattedText?.() || "",
|
|
11729
11782
|
blockNode: ifBlock.braceBlock?.()?.block?.()
|
|
11730
11783
|
});
|
|
11731
11784
|
}
|
|
@@ -11733,14 +11786,17 @@ function extractFragmentInfo(stat) {
|
|
|
11733
11786
|
const condition = elseIf.parExpr?.()?.condition?.();
|
|
11734
11787
|
const label = condition?.getFormattedText?.() || "";
|
|
11735
11788
|
sections.push({
|
|
11736
|
-
label: `
|
|
11789
|
+
label: `else if ${label}`,
|
|
11790
|
+
guardKeyword: "else if",
|
|
11791
|
+
condition: label,
|
|
11737
11792
|
blockNode: elseIf.braceBlock?.()?.block?.()
|
|
11738
11793
|
});
|
|
11739
11794
|
}
|
|
11740
11795
|
const elseBlock = alt.elseBlock?.();
|
|
11741
11796
|
if (elseBlock) {
|
|
11742
11797
|
sections.push({
|
|
11743
|
-
label: "
|
|
11798
|
+
label: "else",
|
|
11799
|
+
guardKeyword: "else",
|
|
11744
11800
|
blockNode: elseBlock.braceBlock?.()?.block?.()
|
|
11745
11801
|
});
|
|
11746
11802
|
}
|
|
@@ -12381,13 +12437,36 @@ function buildGeometry(input) {
|
|
|
12381
12437
|
participants,
|
|
12382
12438
|
measureText
|
|
12383
12439
|
);
|
|
12384
|
-
const {
|
|
12440
|
+
const {
|
|
12441
|
+
messages,
|
|
12442
|
+
selfCalls,
|
|
12443
|
+
occurrences,
|
|
12444
|
+
creations,
|
|
12445
|
+
fragments,
|
|
12446
|
+
returns,
|
|
12447
|
+
dividers,
|
|
12448
|
+
comments
|
|
12449
|
+
} = buildResult;
|
|
12450
|
+
if (input.enableNumbering === false) {
|
|
12451
|
+
const numberedItems = [
|
|
12452
|
+
...messages,
|
|
12453
|
+
...selfCalls,
|
|
12454
|
+
...fragments,
|
|
12455
|
+
...returns,
|
|
12456
|
+
...creations.map((creation) => creation.message)
|
|
12457
|
+
];
|
|
12458
|
+
for (const numbered of numberedItems) {
|
|
12459
|
+
numbered.number = void 0;
|
|
12460
|
+
}
|
|
12461
|
+
}
|
|
12385
12462
|
let maxOccBottom = 0;
|
|
12386
12463
|
let maxOtherY = 0;
|
|
12387
|
-
for (const o of occurrences)
|
|
12464
|
+
for (const o of occurrences)
|
|
12465
|
+
maxOccBottom = Math.max(maxOccBottom, o.y + o.height);
|
|
12388
12466
|
for (const m of messages) maxOtherY = Math.max(maxOtherY, m.y);
|
|
12389
12467
|
for (const s of selfCalls) maxOtherY = Math.max(maxOtherY, s.y + s.height);
|
|
12390
|
-
for (const c of creations)
|
|
12468
|
+
for (const c of creations)
|
|
12469
|
+
maxOtherY = Math.max(maxOtherY, c.message.y + PARTICIPANT_VISUAL_HEIGHT);
|
|
12391
12470
|
for (const f of fragments) maxOtherY = Math.max(maxOtherY, f.y + f.height);
|
|
12392
12471
|
for (const d of dividers) maxOtherY = Math.max(maxOtherY, d.y);
|
|
12393
12472
|
const diagramHeight = Math.max(
|
|
@@ -12397,10 +12476,7 @@ function buildGeometry(input) {
|
|
|
12397
12476
|
buildResult.maxReturnBottom
|
|
12398
12477
|
);
|
|
12399
12478
|
const lifelineBottom = diagramHeight + PARTICIPANT_VISUAL_HEIGHT - 28;
|
|
12400
|
-
const lifelines = buildLifelines(
|
|
12401
|
-
participants,
|
|
12402
|
-
lifelineBottom
|
|
12403
|
-
);
|
|
12479
|
+
const lifelines = buildLifelines(participants, lifelineBottom);
|
|
12404
12480
|
let diagramWidth = TotalWidth(rootContext, coordinates) - frameBorder.left - frameBorder.right;
|
|
12405
12481
|
if (measureText) {
|
|
12406
12482
|
const LABEL_PAD = 10;
|
|
@@ -12452,6 +12528,42 @@ function buildGeometry(input) {
|
|
|
12452
12528
|
f.width = targetRight - f.x;
|
|
12453
12529
|
}
|
|
12454
12530
|
}
|
|
12531
|
+
for (const inner of [...fragments].sort((a, b) => a.height - b.height)) {
|
|
12532
|
+
inner.width = Math.max(
|
|
12533
|
+
inner.width,
|
|
12534
|
+
fragmentHeaderWidth(
|
|
12535
|
+
inner.headerLabel ?? fragmentHeaderLabel(inner.kind),
|
|
12536
|
+
inner.number
|
|
12537
|
+
)
|
|
12538
|
+
);
|
|
12539
|
+
if (inner.label)
|
|
12540
|
+
inner.width = Math.max(
|
|
12541
|
+
inner.width,
|
|
12542
|
+
fragmentGuardWidth(
|
|
12543
|
+
inner.label,
|
|
12544
|
+
fragmentGuardKeyword(inner.kind, inner.guardKeyword)
|
|
12545
|
+
)
|
|
12546
|
+
);
|
|
12547
|
+
for (const section of inner.sections) {
|
|
12548
|
+
if (section.guardKeyword)
|
|
12549
|
+
inner.width = Math.max(
|
|
12550
|
+
inner.width,
|
|
12551
|
+
fragmentGuardWidth(section.innerLabel ?? "", section.guardKeyword)
|
|
12552
|
+
);
|
|
12553
|
+
}
|
|
12554
|
+
for (const outer of fragments) {
|
|
12555
|
+
if (outer.y < inner.y && outer.y + outer.height >= inner.y + inner.height && outer.x <= inner.x) {
|
|
12556
|
+
outer.width = Math.max(
|
|
12557
|
+
outer.width,
|
|
12558
|
+
inner.x + inner.width + FRAGMENT_PADDING_X - outer.x
|
|
12559
|
+
);
|
|
12560
|
+
}
|
|
12561
|
+
}
|
|
12562
|
+
diagramWidth = Math.max(
|
|
12563
|
+
diagramWidth,
|
|
12564
|
+
inner.x + inner.width - frameBorder.right
|
|
12565
|
+
);
|
|
12566
|
+
}
|
|
12455
12567
|
const groups = buildGroups(participants, diagramHeight);
|
|
12456
12568
|
return {
|
|
12457
12569
|
width: diagramWidth,
|
|
@@ -12550,7 +12662,7 @@ function styleToAttr(style) {
|
|
|
12550
12662
|
return Object.entries(style).map(([k, v]) => `${esc(k)}: ${esc(v)}`).join("; ");
|
|
12551
12663
|
}
|
|
12552
12664
|
|
|
12553
|
-
const STROKE_WIDTH$1 =
|
|
12665
|
+
const STROKE_WIDTH$1 = 1;
|
|
12554
12666
|
const HALF_STROKE$1 = STROKE_WIDTH$1 / 2;
|
|
12555
12667
|
const ICON_SIZE = 24;
|
|
12556
12668
|
const ICON_MARGIN_RIGHT = 4;
|
|
@@ -12558,12 +12670,12 @@ const ICON_PAINT_OFFSET_X = 4;
|
|
|
12558
12670
|
const LABEL_PAD_LEFT = 8;
|
|
12559
12671
|
const LABEL_HORIZONTAL_PADDING = 16;
|
|
12560
12672
|
const STEREOTYPE_VERTICAL_OFFSET = 8;
|
|
12561
|
-
const STEREOTYPE_FONT_SIZE =
|
|
12673
|
+
const STEREOTYPE_FONT_SIZE = 14;
|
|
12562
12674
|
const BOUNDARY_ICON_VERTICAL_TWEAK = 2.75;
|
|
12563
12675
|
const PARTICIPANT_TEXT_FILL = "#222";
|
|
12564
12676
|
function renderParticipant(p) {
|
|
12565
12677
|
if (p.isStarter) return renderStarterParticipant(p);
|
|
12566
|
-
const rx =
|
|
12678
|
+
const rx = 4 - HALF_STROKE$1;
|
|
12567
12679
|
const x = p.x - p.width / 2 + HALF_STROKE$1;
|
|
12568
12680
|
const rectY = p.y + HALF_STROKE$1;
|
|
12569
12681
|
const rectW = p.width - STROKE_WIDTH$1;
|
|
@@ -12632,7 +12744,7 @@ function renderStarterParticipant(p) {
|
|
|
12632
12744
|
if (!icon) {
|
|
12633
12745
|
return "";
|
|
12634
12746
|
}
|
|
12635
|
-
const rx =
|
|
12747
|
+
const rx = 4 - HALF_STROKE$1;
|
|
12636
12748
|
const boxX = p.x - p.width / 2 + HALF_STROKE$1;
|
|
12637
12749
|
const rectY = p.y + HALF_STROKE$1;
|
|
12638
12750
|
const rectW = p.width - STROKE_WIDTH$1;
|
|
@@ -12672,21 +12784,31 @@ function renderLifeline(l) {
|
|
|
12672
12784
|
return `<line x1="${x}" y1="${l.topY}" x2="${x}" y2="${l.bottomY}" class="lifeline" stroke-dasharray="${l.dashed ? "5,5" : "none"}" stroke-dashoffset="${l.dashed ? "5" : "0"}" shape-rendering="crispEdges"/>`;
|
|
12673
12785
|
}
|
|
12674
12786
|
|
|
12787
|
+
function messageNumberWidth(number) {
|
|
12788
|
+
return measureTextWithFont(number, "12px") + 8;
|
|
12789
|
+
}
|
|
12790
|
+
function renderMessageNumber(number, right, baseline) {
|
|
12791
|
+
if (!number) return "";
|
|
12792
|
+
return `<rect x="${right - messageNumberWidth(number)}" y="${baseline - 12}" width="${messageNumberWidth(number)}" height="16" rx="2" class="message-number-bg"/>
|
|
12793
|
+
<text x="${right - 4}" y="${baseline}" text-anchor="end" class="seq-number">${esc(number)}</text>`;
|
|
12794
|
+
}
|
|
12795
|
+
|
|
12675
12796
|
function renderMessage(m) {
|
|
12676
12797
|
const isLTR = m.fromX < m.toX;
|
|
12677
12798
|
const fromX = isLTR ? m.fromX + 1 : m.fromX;
|
|
12678
12799
|
const toX = isLTR ? m.toX : m.toX + 1;
|
|
12679
12800
|
const direction = Math.sign(m.toX - m.fromX);
|
|
12680
12801
|
const labelX = (m.fromX + m.toX) / 2 - direction * 3.5 + 0.5;
|
|
12681
|
-
const labelY = m.y -
|
|
12802
|
+
const labelY = m.y - 2.5;
|
|
12682
12803
|
const dashAttr = m.arrowStyle === "dashed" ? ' stroke-dasharray="6,4"' : "";
|
|
12683
12804
|
const styleAttr = m.style ? ` style="${styleToAttr(m.style)}"` : "";
|
|
12684
12805
|
const numberX = Math.min(fromX, toX) - 4;
|
|
12685
|
-
const numberSvg = m.number
|
|
12686
|
-
const
|
|
12806
|
+
const numberSvg = renderMessageNumber(m.number, numberX, m.y - 3.5);
|
|
12807
|
+
const headCenterY = m.y - 0.5;
|
|
12808
|
+
const lineY = headCenterY + 0.5;
|
|
12687
12809
|
return `<g class="message">
|
|
12688
12810
|
<line x1="${fromX}" y1="${lineY}" x2="${toX}" y2="${lineY}" class="message-line"${dashAttr}/>
|
|
12689
|
-
${renderArrowHead(toX,
|
|
12811
|
+
${renderArrowHead(toX, headCenterY, m.isReverse, m.arrowStyle)}
|
|
12690
12812
|
<text x="${labelX}" y="${labelY}" text-anchor="middle" class="message-label"${styleAttr}>${esc(resolveEmojiInText(m.label))}</text>
|
|
12691
12813
|
${numberSvg}
|
|
12692
12814
|
</g>`;
|
|
@@ -12697,7 +12819,7 @@ function renderSelfCall(s) {
|
|
|
12697
12819
|
const labelX = x1 + 6;
|
|
12698
12820
|
const labelY = s.y + (isAsync ? 15 : 12);
|
|
12699
12821
|
const numberY = s.y + 12;
|
|
12700
|
-
const numberSvg = s.number
|
|
12822
|
+
const numberSvg = renderMessageNumber(s.number, x1 - 3, numberY);
|
|
12701
12823
|
const svgX = x1 + 1;
|
|
12702
12824
|
const svgY = s.y + (isAsync ? 20 : 14);
|
|
12703
12825
|
const pathEnd = isAsync ? "L1,15" : "L14,15";
|
|
@@ -12706,11 +12828,11 @@ function renderSelfCall(s) {
|
|
|
12706
12828
|
const arrowPath = isAsync ? "M1 1.25 L6.15 4.5 L1 7.75" : "M1 1.25 L6.15 4.5 L1 7.75 Z";
|
|
12707
12829
|
return `<g class="message self-call">
|
|
12708
12830
|
<svg x="${svgX}" y="${svgY}" width="30" height="24">
|
|
12709
|
-
<path d="M0,2 L26,2 Q28,2 28,4 L28,13 Q28,15 26,15 ${pathEnd}" fill="none" stroke="#000" stroke-width="
|
|
12831
|
+
<path d="M0,2 L26,2 Q28,2 28,4 L28,13 Q28,15 26,15 ${pathEnd}" fill="none" stroke="#000" stroke-width="1"/>
|
|
12710
12832
|
<g transform="translate(${arrowTx}, 10)">
|
|
12711
12833
|
<svg height="10" width="7" viewBox="0 0 7 9">
|
|
12712
12834
|
<g transform="scale(-1, 1) translate(-7, 0)">
|
|
12713
|
-
<path d="${arrowPath}" stroke="#000" stroke-linecap="round" fill="${arrowFill}" stroke-width="
|
|
12835
|
+
<path d="${arrowPath}" stroke="#000" stroke-linecap="round" fill="${arrowFill}" stroke-width="1"/>
|
|
12714
12836
|
</g>
|
|
12715
12837
|
</svg>
|
|
12716
12838
|
</g>
|
|
@@ -12728,96 +12850,122 @@ function renderArrowHead(tipX, tipY, pointsLeft, style) {
|
|
|
12728
12850
|
const rtlTransform = pointsLeft ? ' transform="scale(-1, 1) translate(-7, 0)"' : "";
|
|
12729
12851
|
return `<svg x="${svgX}" y="${svgY}" width="7" height="10" viewBox="0 0 7 9" overflow="visible" class="arrow-head${isFilled ? "" : " arrow-open"}">
|
|
12730
12852
|
<g${rtlTransform}>
|
|
12731
|
-
<path d="${pathD}" stroke="#000" stroke-linecap="round" stroke-width="
|
|
12853
|
+
<path d="${pathD}" transform="translate(1 0.5)" stroke="#000" stroke-linecap="round" stroke-width="1" fill="${fillAttr}"/>
|
|
12732
12854
|
</g>
|
|
12733
12855
|
</svg>`;
|
|
12734
12856
|
}
|
|
12735
12857
|
|
|
12736
12858
|
function renderOccurrence(o) {
|
|
12737
|
-
const strokeHalf =
|
|
12859
|
+
const strokeHalf = 0.5;
|
|
12738
12860
|
const rx = o.x + strokeHalf;
|
|
12739
|
-
return `<rect x="${rx}" y="${o.y + strokeHalf}" width="${o.width - strokeHalf * 2}" height="${o.height - strokeHalf * 2}" rx="1" class="occurrence" data-participant="${esc(o.participantName)}"/>`;
|
|
12861
|
+
return `<rect x="${rx}" y="${o.y + strokeHalf}" width="${o.width - strokeHalf * 2}" height="${o.height - strokeHalf * 2}" rx="1.5" class="occurrence" data-participant="${esc(o.participantName)}"/>`;
|
|
12740
12862
|
}
|
|
12741
12863
|
|
|
12742
12864
|
const HEADER_HEIGHT = DEFAULT_LAYOUT_METRICS.fragmentHeaderHeight;
|
|
12743
|
-
const BRACKET_WIDTH = 3.89;
|
|
12744
12865
|
const TEXT_PAD_X = 4;
|
|
12866
|
+
const NUMBER_PAD_X = 4;
|
|
12745
12867
|
const STROKE_WIDTH = 1;
|
|
12746
12868
|
const HALF_STROKE = STROKE_WIDTH / 2;
|
|
12747
12869
|
function renderFragment(f) {
|
|
12748
12870
|
const parts = [];
|
|
12871
|
+
const kindLabel = f.headerLabel ?? fragmentHeaderLabel(f.kind);
|
|
12872
|
+
const width = Math.max(f.width, fragmentHeaderWidth(kindLabel, f.number));
|
|
12749
12873
|
parts.push(
|
|
12750
|
-
`<rect x="${f.x + HALF_STROKE}" y="${f.y + HALF_STROKE}" width="${
|
|
12874
|
+
`<rect x="${f.x + HALF_STROKE}" y="${f.y + HALF_STROKE}" width="${width - STROKE_WIDTH}" height="${f.height - STROKE_WIDTH}" rx="${4 - HALF_STROKE}" class="fragment-border"/>`
|
|
12751
12875
|
);
|
|
12752
12876
|
const headerX = f.x + 1;
|
|
12753
12877
|
const headerY = f.headerY;
|
|
12754
|
-
const headerW =
|
|
12878
|
+
const headerW = width - 2;
|
|
12755
12879
|
parts.push(
|
|
12756
|
-
`<rect x="${headerX}" y="${headerY}" width="${headerW}" height="${HEADER_HEIGHT}" class="fragment-header"/>`
|
|
12880
|
+
`<rect x="${headerX}" y="${headerY}" width="${headerW}" height="${HEADER_HEIGHT}" class="fragment-header"${f.kind === "opt" ? "" : ' style="clip-path: inset(0 round 4px 4px 0 0)"'}/>`
|
|
12757
12881
|
);
|
|
12758
|
-
const
|
|
12882
|
+
const numberWidth = f.number ? Math.ceil(measureTextWithFont(f.number, "12px") * 64) / 64 : 0;
|
|
12883
|
+
const numberInset = f.number ? numberWidth + 14 : 0;
|
|
12884
|
+
const titleY = headerY + HEADER_HEIGHT / 2 - 0.5;
|
|
12885
|
+
if (f.number) {
|
|
12886
|
+
const numberBoxX = headerX + 4;
|
|
12887
|
+
const numberX = numberBoxX + NUMBER_PAD_X;
|
|
12888
|
+
parts.push(
|
|
12889
|
+
`<rect x="${numberBoxX}" y="${headerY + (HEADER_HEIGHT - 1 - 16) / 2}" width="${numberWidth + NUMBER_PAD_X * 2}" height="16" rx="2" class="fragment-number-bg"/>`,
|
|
12890
|
+
`<text x="${numberX}" y="${titleY}" dominant-baseline="central" class="seq-number">${esc(f.number)}</text>`
|
|
12891
|
+
);
|
|
12892
|
+
}
|
|
12893
|
+
const iconX = headerX + 4 + numberInset;
|
|
12759
12894
|
const iconY = headerY;
|
|
12760
12895
|
parts.push(getFragmentIcon(f.kind, iconX, iconY));
|
|
12761
|
-
const
|
|
12762
|
-
const labelX = headerX + 26;
|
|
12896
|
+
const labelX = headerX + 26 + numberInset;
|
|
12763
12897
|
parts.push(
|
|
12764
12898
|
`<text x="${labelX}" y="${headerY + HEADER_HEIGHT / 2 - 0.5}" dominant-baseline="central" class="fragment-label">${esc(kindLabel)}</text>`
|
|
12765
12899
|
);
|
|
12766
|
-
if (f.
|
|
12900
|
+
if (f.label) {
|
|
12767
12901
|
parts.push(
|
|
12768
|
-
|
|
12902
|
+
renderGuardRow(
|
|
12903
|
+
headerX,
|
|
12904
|
+
headerY + HEADER_HEIGHT,
|
|
12905
|
+
f.label,
|
|
12906
|
+
fragmentGuardKeyword(f.kind, f.guardKeyword)
|
|
12907
|
+
)
|
|
12769
12908
|
);
|
|
12770
12909
|
}
|
|
12771
|
-
if (f.label) {
|
|
12772
|
-
const condY = headerY + HEADER_HEIGHT + 15;
|
|
12773
|
-
parts.push(renderBracketedLabel(headerX, condY, f.label, f.labelWidth, "fragment-condition"));
|
|
12774
|
-
}
|
|
12775
12910
|
if (f.sections.length > 1) {
|
|
12776
12911
|
for (let i = 1; i < f.sections.length; i++) {
|
|
12777
12912
|
const section = f.sections[i];
|
|
12778
12913
|
const lineY = section.y;
|
|
12779
12914
|
const separatorY = lineY + HALF_STROKE;
|
|
12780
12915
|
const sepX1 = f.x + 1 + (section.contentInsetLeft ?? 0);
|
|
12781
|
-
const sepX2 = f.x +
|
|
12916
|
+
const sepX2 = f.x + width - 1;
|
|
12782
12917
|
parts.push(
|
|
12783
12918
|
`<line x1="${sepX1}" y1="${separatorY}" x2="${sepX2}" y2="${separatorY}" class="fragment-separator"/>`
|
|
12784
12919
|
);
|
|
12785
12920
|
if (section.label) {
|
|
12921
|
+
if (f.kind === "alt") {
|
|
12922
|
+
parts.push(
|
|
12923
|
+
renderGuardRow(
|
|
12924
|
+
f.x + 1,
|
|
12925
|
+
lineY + 1,
|
|
12926
|
+
section.innerLabel ?? "",
|
|
12927
|
+
section.guardKeyword
|
|
12928
|
+
)
|
|
12929
|
+
);
|
|
12930
|
+
continue;
|
|
12931
|
+
}
|
|
12786
12932
|
const labelY = lineY + 16;
|
|
12787
12933
|
const isFinally = section.label.startsWith("finally");
|
|
12788
12934
|
const isBracketed = !!section.innerLabel && section.label !== "[else]";
|
|
12789
12935
|
if (isBracketed) {
|
|
12790
12936
|
const labelX2 = f.x + 1;
|
|
12791
|
-
parts.push(
|
|
12792
|
-
renderBracketedLabel(labelX2, labelY, section.innerLabel, section.innerLabelWidth, "fragment-section-label")
|
|
12793
|
-
);
|
|
12937
|
+
parts.push(renderGuardRow(labelX2, lineY + 1, section.innerLabel));
|
|
12794
12938
|
continue;
|
|
12795
12939
|
}
|
|
12796
12940
|
const spaceIdx = section.label.indexOf(" ");
|
|
12797
12941
|
if (spaceIdx > 0 && !isFinally) {
|
|
12798
12942
|
const keyword = section.keyword || section.label.substring(0, spaceIdx);
|
|
12799
12943
|
const condition = section.detail || section.label.substring(spaceIdx + 1);
|
|
12800
|
-
const keywordWidth =
|
|
12944
|
+
const keywordWidth = measureTextWithFont(
|
|
12945
|
+
keyword,
|
|
12946
|
+
"14px",
|
|
12947
|
+
"all-small-caps"
|
|
12948
|
+
);
|
|
12801
12949
|
const keywordX = f.x + 5;
|
|
12802
12950
|
const conditionX = keywordX + keywordWidth + TEXT_PAD_X * 2;
|
|
12803
|
-
const bgWidth =
|
|
12951
|
+
const bgWidth = keywordWidth + (section.detailWidth ?? condition.length * 7) + TEXT_PAD_X * 4;
|
|
12804
12952
|
parts.push(
|
|
12805
|
-
`<g opacity="0.65"><rect x="${keywordX - TEXT_PAD_X}" y="${lineY + 1}" width="${bgWidth}" height="20" fill="#fff"/><text x="${keywordX}" y="${labelY}" class="fragment-section-label" fill="#222">${esc(keyword)}</text><text x="${conditionX}" y="${labelY}" class="fragment-section-label" fill="#222">${esc(condition)}</text></g>`
|
|
12953
|
+
`<g opacity="0.65"><rect x="${keywordX - TEXT_PAD_X}" y="${lineY + 1}" width="${bgWidth}" height="20" fill="#fff"/><text x="${keywordX}" y="${labelY}" class="fragment-section-label fragment-section-keyword" fill="#222">${esc(keyword)}</text><text x="${conditionX}" y="${labelY}" class="fragment-section-label" fill="#222">${esc(condition)}</text></g>`
|
|
12806
12954
|
);
|
|
12807
12955
|
} else {
|
|
12808
12956
|
const finallyX = f.x + 5;
|
|
12809
12957
|
const finallyY = labelY;
|
|
12810
12958
|
if (section.label === "[else]") {
|
|
12811
12959
|
parts.push(
|
|
12812
|
-
`<text x="${finallyX}" y="${finallyY}" class="fragment-section-label">${esc(section.label)}</text>`
|
|
12960
|
+
`<text x="${finallyX}" y="${finallyY}" class="fragment-section-label fragment-section-keyword">${esc(section.label)}</text>`
|
|
12813
12961
|
);
|
|
12814
12962
|
continue;
|
|
12815
12963
|
}
|
|
12816
|
-
const bgWidth = (section.labelWidth ?? section.label.length * 7) + TEXT_PAD_X * 2;
|
|
12964
|
+
const bgWidth = (isFinally ? measureTextWithFont(section.label, "14px", "all-small-caps") : section.labelWidth ?? section.label.length * 7) + TEXT_PAD_X * 2;
|
|
12817
12965
|
const bgY = lineY + 1;
|
|
12818
12966
|
const bgHeight = 20;
|
|
12819
12967
|
parts.push(
|
|
12820
|
-
`<g opacity="0.65"><rect x="${finallyX - TEXT_PAD_X}" y="${bgY}" width="${bgWidth}" height="${bgHeight}" fill="#fff"/><text x="${finallyX}" y="${finallyY}" class="fragment-section-label">${esc(section.label)}</text></g>`
|
|
12968
|
+
`<g opacity="0.65"><rect x="${finallyX - TEXT_PAD_X}" y="${bgY}" width="${bgWidth}" height="${bgHeight}" fill="#fff"/><text x="${finallyX}" y="${finallyY}" class="fragment-section-label fragment-section-keyword">${esc(section.label)}</text></g>`
|
|
12821
12969
|
);
|
|
12822
12970
|
}
|
|
12823
12971
|
}
|
|
@@ -12827,33 +12975,11 @@ function renderFragment(f) {
|
|
|
12827
12975
|
${parts.join("\n ")}
|
|
12828
12976
|
</g>`;
|
|
12829
12977
|
}
|
|
12830
|
-
function
|
|
12831
|
-
const
|
|
12832
|
-
const
|
|
12833
|
-
const
|
|
12834
|
-
return `<g
|
|
12835
|
-
}
|
|
12836
|
-
function getKindLabel(kind) {
|
|
12837
|
-
switch (kind) {
|
|
12838
|
-
case "alt":
|
|
12839
|
-
return "Alt";
|
|
12840
|
-
case "loop":
|
|
12841
|
-
return "Loop";
|
|
12842
|
-
case "opt":
|
|
12843
|
-
return "Opt";
|
|
12844
|
-
case "par":
|
|
12845
|
-
return "Par";
|
|
12846
|
-
case "critical":
|
|
12847
|
-
return "Critical";
|
|
12848
|
-
case "section":
|
|
12849
|
-
return "Section";
|
|
12850
|
-
case "tcf":
|
|
12851
|
-
return "Try";
|
|
12852
|
-
case "ref":
|
|
12853
|
-
return "Ref";
|
|
12854
|
-
default:
|
|
12855
|
-
return kind.charAt(0).toUpperCase() + kind.slice(1);
|
|
12856
|
-
}
|
|
12978
|
+
function renderGuardRow(x, rowY, innerText, keyword) {
|
|
12979
|
+
const baseline = rowY + 19;
|
|
12980
|
+
const textX = x + 4;
|
|
12981
|
+
const conditionX = textX + (keyword ? measureTextWithFont(keyword, "12px", "all-small-caps") + 4 : 0);
|
|
12982
|
+
return `<g class="guard-row">` + (keyword ? `<text x="${textX}" y="${baseline}" class="guard-keyword">${esc(keyword)}</text>` : "") + (innerText ? `<text x="${conditionX}" y="${baseline}" class="fragment-condition" opacity="0.65">${esc(resolveEmojiInText(innerText))}</text>` : "") + `</g>`;
|
|
12857
12983
|
}
|
|
12858
12984
|
function getFragmentIcon(kind, x, y) {
|
|
12859
12985
|
const w = 20;
|
|
@@ -12937,7 +13063,7 @@ function renderOpenArrow(tipX, tipY, pointsLeft) {
|
|
|
12937
13063
|
const x1 = tipX + dir * w;
|
|
12938
13064
|
const y1 = tipY - halfH;
|
|
12939
13065
|
const y2 = tipY + halfH;
|
|
12940
|
-
return `<polyline points="${x1},${y1} ${tipX},${tipY} ${x1},${y2}" fill="none" stroke-linecap="round" class="arrow-head arrow-open"/>`;
|
|
13066
|
+
return `<polyline points="${x1},${y1} ${tipX},${tipY} ${x1},${y2}" transform="translate(${pointsLeft ? -1 : 1} 0)" fill="none" stroke-linecap="round" class="arrow-head arrow-open"/>`;
|
|
12941
13067
|
}
|
|
12942
13068
|
|
|
12943
13069
|
function renderReturn(r) {
|
|
@@ -12947,8 +13073,9 @@ function renderReturn(r) {
|
|
|
12947
13073
|
const minX = Math.min(r.fromX, r.toX);
|
|
12948
13074
|
const ARROW_PADDING_HALF = 3.5;
|
|
12949
13075
|
const labelX = minX + Math.abs(r.toX - r.fromX) / 2 + (r.isReverse ? ARROW_PADDING_HALF : -ARROW_PADDING_HALF);
|
|
12950
|
-
const
|
|
12951
|
-
const
|
|
13076
|
+
const snappedY = Math.floor(r.y);
|
|
13077
|
+
const lineY = snappedY + 0.5;
|
|
13078
|
+
const labelY = snappedY - 3;
|
|
12952
13079
|
const arrowTipX = r.toX;
|
|
12953
13080
|
const w = 5.15;
|
|
12954
13081
|
const halfH = 3.25;
|
|
@@ -12957,10 +13084,10 @@ function renderReturn(r) {
|
|
|
12957
13084
|
const ay1 = lineY - halfH;
|
|
12958
13085
|
const ay2 = lineY + halfH;
|
|
12959
13086
|
const numberX = Math.min(r.fromX, r.toX) - 4;
|
|
12960
|
-
const numberSvg = r.number
|
|
13087
|
+
const numberSvg = renderMessageNumber(r.number, numberX, labelY);
|
|
12961
13088
|
return `<g class="return">
|
|
12962
13089
|
<line x1="${r.fromX}" y1="${lineY}" x2="${r.toX}" y2="${lineY}" class="return-line"/>
|
|
12963
|
-
<polyline points="${ax1},${ay1} ${arrowTipX},${lineY} ${ax1},${ay2}" fill="none" stroke-linecap="round" class="return-arrow"/>
|
|
13090
|
+
<polyline points="${ax1},${ay1} ${arrowTipX},${lineY} ${ax1},${ay2}" transform="translate(${dir} 0)" fill="none" stroke-linecap="round" class="return-arrow"/>
|
|
12964
13091
|
<text x="${labelX}" y="${labelY}" text-anchor="middle" class="return-label">${esc(resolveEmojiInText(r.label))}</text>
|
|
12965
13092
|
${numberSvg}
|
|
12966
13093
|
</g>`;
|
|
@@ -13202,39 +13329,43 @@ function buildThemeStyles(p) {
|
|
|
13202
13329
|
.frame-border-inner { fill: ${p.frameBg}; }
|
|
13203
13330
|
.frame-header-bg { fill: ${p.frameBg}; }
|
|
13204
13331
|
.frame-header-line { stroke: ${p.frameBorder}; stroke-width: 1; shape-rendering: crispEdges; }
|
|
13205
|
-
.frame-title { font-family: ${p.fontFamily}; font-size:
|
|
13206
|
-
.participant-box { fill: ${p.participantBg}; stroke: ${p.participantBorder}; stroke-width:
|
|
13207
|
-
.participant-label { font-family: ${p.fontFamily}; font-size:
|
|
13332
|
+
.frame-title { font-family: ${p.fontFamily}; font-size: 14px; font-weight: 400; fill: ${p.titleText}; }
|
|
13333
|
+
.participant-box { fill: ${p.participantBg}; stroke: ${p.participantBorder}; stroke-width: 1; }
|
|
13334
|
+
.participant-label { font-family: ${p.fontFamily}; font-size: 14px; fill: ${p.participantText}; }
|
|
13208
13335
|
.participant-icon { color: ${p.participantText}; }
|
|
13209
13336
|
.participant-icon [fill="currentColor"]:not([stroke]) { stroke: ${p.participantBorder}; stroke-width: 1; }
|
|
13210
13337
|
.lifeline { stroke: ${p.participantBorder}; stroke-width: 1; }
|
|
13211
|
-
.message-line { stroke: ${p.messageArrow}; stroke-width:
|
|
13212
|
-
.message-label { font-family: ${p.fontFamily}; font-size:
|
|
13213
|
-
.arrow-head { fill: ${p.messageArrow}; stroke: ${p.messageArrow}; stroke-width:
|
|
13338
|
+
.message-line { stroke: ${p.messageArrow}; stroke-width: 1; shape-rendering: crispEdges; }
|
|
13339
|
+
.message-label { font-family: ${p.fontFamily}; font-size: 15px; fill: ${p.messageText}; }
|
|
13340
|
+
.arrow-head { fill: ${p.messageArrow}; stroke: ${p.messageArrow}; stroke-width: 1; }
|
|
13214
13341
|
.arrow-open { fill: none; }
|
|
13215
|
-
.occurrence { fill: ${p.occurrenceBg}; stroke: ${p.occurrenceBorder}; stroke-width:
|
|
13216
|
-
.fragment-border { fill: none; stroke: ${p.fragmentBorder}; stroke-width: 1;
|
|
13342
|
+
.occurrence { fill: ${p.occurrenceBg}; stroke: ${p.occurrenceBorder}; stroke-width: 1; shape-rendering: crispEdges; }
|
|
13343
|
+
.fragment-border { fill: none; stroke: ${p.fragmentBorder}; stroke-width: 1; }
|
|
13217
13344
|
.fragment-header { fill: ${p.fragmentHeaderBg}; fill-opacity: ${p.fragmentHeaderOpacity}; stroke: none; shape-rendering: crispEdges; }
|
|
13218
|
-
.fragment-
|
|
13345
|
+
.fragment-number-bg { fill: #6b7280; fill-opacity: 0.1; stroke: none; }
|
|
13346
|
+
.message-number-bg { fill: #6b7280; fill-opacity: 0.1; stroke: none; }
|
|
13347
|
+
.fragment-label { font-family: ${p.fontFamily}; font-size: 14px; font-weight: 400; font-variant-caps: all-small-caps; fill: ${p.fragmentText}; }
|
|
13219
13348
|
.fragment-condition { font-family: ${p.fontFamily}; font-size: 14px; fill: ${p.fragmentText}; }
|
|
13220
13349
|
.fragment-separator { stroke: ${p.fragmentSeparator}; stroke-width: 1; shape-rendering: crispEdges; }
|
|
13350
|
+
.fragment-section-keyword { font-variant-caps: all-small-caps; }
|
|
13221
13351
|
.fragment-section-label { font-family: ${p.fontFamily}; font-size: 14px; fill: ${p.fragmentText}; }
|
|
13222
|
-
.return-line { stroke: ${p.messageArrow}; stroke-width:
|
|
13223
|
-
.return-arrow { stroke: ${p.messageArrow}; stroke-width:
|
|
13224
|
-
.return-label { font-family: ${p.fontFamily}; font-size:
|
|
13352
|
+
.return-line { stroke: ${p.messageArrow}; stroke-width: 1; stroke-dasharray: 6,4; shape-rendering: crispEdges; }
|
|
13353
|
+
.return-arrow { stroke: ${p.messageArrow}; stroke-width: 1; fill: none; }
|
|
13354
|
+
.return-label { font-family: ${p.fontFamily}; font-size: 15px; fill: ${p.messageText}; }
|
|
13225
13355
|
.return-icon { fill: ${p.messageText}; }
|
|
13226
13356
|
.divider-line { stroke: #aaaa33; stroke-width: 1; }
|
|
13227
13357
|
.divider-bg { fill: #fff5ad; stroke: #aaaa33; stroke-width: 1; }
|
|
13228
13358
|
.divider-label { font-family: ${p.fontFamily}; font-size: 14px; fill: #333; }
|
|
13229
13359
|
.comment-text { font-family: ${p.fontFamily}; font-size: 14px; fill: ${p.commentText}; opacity: 0.5; }
|
|
13230
|
-
.
|
|
13360
|
+
.guard-keyword { font-family: ${p.fontFamily}; font-size: 12px; font-weight: 400; font-variant-caps: all-small-caps; fill: #6b7280; }
|
|
13361
|
+
.seq-number { font-family: ${p.fontFamily}; font-size: 12px; font-weight: 400; fill: #6b7280; }
|
|
13231
13362
|
.group-outline { fill: none; stroke: ${p.participantBorder}; }
|
|
13232
13363
|
.group-title-bg { fill: ${p.frameBg}; stroke: none; }
|
|
13233
13364
|
.group-title-text { font-family: ${p.fontFamily}; font-size: 13px; font-weight: 400; fill: ${p.participantText}; }
|
|
13234
13365
|
`;
|
|
13235
13366
|
}
|
|
13236
13367
|
|
|
13237
|
-
const
|
|
13368
|
+
const FRAME_VIEWPORT_HEADER_ALLOWANCE = 28;
|
|
13238
13369
|
const FRAME_BORDER_RADIUS = 4;
|
|
13239
13370
|
function renderToSvg(code, options) {
|
|
13240
13371
|
const rootContext = RootContext(code);
|
|
@@ -13258,14 +13389,41 @@ function renderToSvg(code, options) {
|
|
|
13258
13389
|
coordinates,
|
|
13259
13390
|
verticalCoordinates,
|
|
13260
13391
|
title,
|
|
13261
|
-
measureText: WidthProviderOnCanvas
|
|
13392
|
+
measureText: WidthProviderOnCanvas,
|
|
13393
|
+
enableNumbering: options?.enableNumbering
|
|
13262
13394
|
});
|
|
13263
13395
|
return { ...composeSvg(geometry, options), geometry };
|
|
13264
13396
|
}
|
|
13265
13397
|
function composeSvg(g, options) {
|
|
13266
13398
|
const padding = 10;
|
|
13267
|
-
const headerH =
|
|
13268
|
-
|
|
13399
|
+
const headerH = FRAME_VIEWPORT_HEADER_ALLOWANCE;
|
|
13400
|
+
let contentLeftMargin = 1 + padding + g.frameBorderLeft;
|
|
13401
|
+
let numberLeft = 0;
|
|
13402
|
+
for (const message of [
|
|
13403
|
+
...g.messages,
|
|
13404
|
+
...g.creations.map((creation) => creation.message)
|
|
13405
|
+
]) {
|
|
13406
|
+
if (!message.number) continue;
|
|
13407
|
+
numberLeft = Math.min(
|
|
13408
|
+
numberLeft,
|
|
13409
|
+
Math.min(message.fromX, message.toX) + 1 - 4 - messageNumberWidth(message.number)
|
|
13410
|
+
);
|
|
13411
|
+
}
|
|
13412
|
+
for (const call of g.selfCalls) {
|
|
13413
|
+
if (call.number)
|
|
13414
|
+
numberLeft = Math.min(
|
|
13415
|
+
numberLeft,
|
|
13416
|
+
call.x - 3 - messageNumberWidth(call.number)
|
|
13417
|
+
);
|
|
13418
|
+
}
|
|
13419
|
+
for (const returned of g.returns) {
|
|
13420
|
+
if (returned.number && !returned.isSelf)
|
|
13421
|
+
numberLeft = Math.min(
|
|
13422
|
+
numberLeft,
|
|
13423
|
+
Math.min(returned.fromX, returned.toX) - 4 - messageNumberWidth(returned.number)
|
|
13424
|
+
);
|
|
13425
|
+
}
|
|
13426
|
+
contentLeftMargin += Math.max(0, padding - contentLeftMargin - numberLeft);
|
|
13269
13427
|
const viewWidth = g.width + contentLeftMargin + padding + g.frameBorderRight + 1;
|
|
13270
13428
|
const viewHeight = g.height + padding * 2 + headerH - 1;
|
|
13271
13429
|
const parts = [];
|
|
@@ -13315,8 +13473,7 @@ function composeSvg(g, options) {
|
|
|
13315
13473
|
`<rect class="frame-border-outer" x="0" y="0" width="${viewWidth}" height="${viewHeight}" rx="${r}" fill="#666"/>`,
|
|
13316
13474
|
`<rect class="frame-border-inner" x="1" y="1" width="${viewWidth - 2}" height="${viewHeight - 2}" rx="${Math.max(0, r - 1)}" fill="#fff"/>`
|
|
13317
13475
|
].join("\n");
|
|
13318
|
-
const
|
|
13319
|
-
const headerLineY = headerH + contentPaddingTop;
|
|
13476
|
+
const headerLineY = 1 + DIAGRAM_HEADER_HEIGHT;
|
|
13320
13477
|
const headerLineDrawY = headerLineY - 0.5;
|
|
13321
13478
|
const headerLineSvg = `<line class="frame-header-line" x1="1" y1="${headerLineDrawY}" x2="${viewWidth - 1}" y2="${headerLineDrawY}"/>`;
|
|
13322
13479
|
const titleSvg = g.title ? `<text x="5" y="${headerLineDrawY / 2}" dominant-baseline="central" class="frame-title">${esc(g.title)}</text>` : "";
|