@zenuml/core 4.2.1 → 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 +849 -583
- package/dist/parser/index.cjs +2 -2
- package/dist/parser/index.mjs +1181 -1255
- package/dist/zenuml.esm.mjs +18288 -20531
- package/dist/zenuml.js +630 -753
- package/package.json +1 -13
- package/types/index.d.ts +26 -5
- package/types/svg.d.ts +0 -4
package/dist/cli/zenuml.mjs
CHANGED
|
@@ -9075,7 +9075,7 @@ ToCollector.enterRet = function (ctx) {
|
|
|
9075
9075
|
returnTo && participants.Add(returnTo);
|
|
9076
9076
|
};
|
|
9077
9077
|
|
|
9078
|
-
const walker$
|
|
9078
|
+
const walker$1 = antlr4.tree.ParseTreeWalker.DEFAULT;
|
|
9079
9079
|
|
|
9080
9080
|
// The walk is a pure function of the (immutable) parse tree and consumers only
|
|
9081
9081
|
// read the returned Participants, so results are cached per context node.
|
|
@@ -9086,80 +9086,11 @@ ToCollector.getParticipants = function (context) {
|
|
|
9086
9086
|
const cached = participantsCache.get(context);
|
|
9087
9087
|
if (cached) return cached;
|
|
9088
9088
|
participants = new Participants$1();
|
|
9089
|
-
walker$
|
|
9089
|
+
walker$1.walk(this, context);
|
|
9090
9090
|
participantsCache.set(context, participants);
|
|
9091
9091
|
return participants;
|
|
9092
9092
|
};
|
|
9093
9093
|
|
|
9094
|
-
const walker$1 = antlr4.tree.ParseTreeWalker.DEFAULT;
|
|
9095
|
-
|
|
9096
|
-
let ChildFragmentDetector = new sequenceParserListener();
|
|
9097
|
-
|
|
9098
|
-
let cursor = 0;
|
|
9099
|
-
let max = 0;
|
|
9100
|
-
|
|
9101
|
-
ChildFragmentDetector.enterTcf = function () {
|
|
9102
|
-
cursor++;
|
|
9103
|
-
};
|
|
9104
|
-
ChildFragmentDetector.enterOpt = function () {
|
|
9105
|
-
cursor++;
|
|
9106
|
-
};
|
|
9107
|
-
ChildFragmentDetector.enterPar = function () {
|
|
9108
|
-
cursor++;
|
|
9109
|
-
};
|
|
9110
|
-
ChildFragmentDetector.enterAlt = function () {
|
|
9111
|
-
cursor++;
|
|
9112
|
-
};
|
|
9113
|
-
ChildFragmentDetector.enterLoop = function () {
|
|
9114
|
-
cursor++;
|
|
9115
|
-
};
|
|
9116
|
-
ChildFragmentDetector.enterSection = function () {
|
|
9117
|
-
cursor++;
|
|
9118
|
-
};
|
|
9119
|
-
ChildFragmentDetector.enterCritical = function () {
|
|
9120
|
-
cursor++;
|
|
9121
|
-
};
|
|
9122
|
-
|
|
9123
|
-
ChildFragmentDetector.exitTcf = function () {
|
|
9124
|
-
max = Math.max(max, cursor);
|
|
9125
|
-
cursor--;
|
|
9126
|
-
};
|
|
9127
|
-
ChildFragmentDetector.exitOpt = function () {
|
|
9128
|
-
max = Math.max(max, cursor);
|
|
9129
|
-
cursor--;
|
|
9130
|
-
};
|
|
9131
|
-
ChildFragmentDetector.exitPar = function () {
|
|
9132
|
-
max = Math.max(max, cursor);
|
|
9133
|
-
cursor--;
|
|
9134
|
-
};
|
|
9135
|
-
ChildFragmentDetector.exitAlt = function () {
|
|
9136
|
-
max = Math.max(max, cursor);
|
|
9137
|
-
cursor--;
|
|
9138
|
-
};
|
|
9139
|
-
ChildFragmentDetector.exitLoop = function () {
|
|
9140
|
-
max = Math.max(max, cursor);
|
|
9141
|
-
cursor--;
|
|
9142
|
-
};
|
|
9143
|
-
ChildFragmentDetector.exitSection = function () {
|
|
9144
|
-
max = Math.max(max, cursor);
|
|
9145
|
-
cursor--;
|
|
9146
|
-
};
|
|
9147
|
-
ChildFragmentDetector.exitCritical = function () {
|
|
9148
|
-
max = Math.max(max, cursor);
|
|
9149
|
-
cursor--;
|
|
9150
|
-
};
|
|
9151
|
-
|
|
9152
|
-
ChildFragmentDetector.depth = function (me) {
|
|
9153
|
-
return function (context) {
|
|
9154
|
-
cursor = 0;
|
|
9155
|
-
max = 0;
|
|
9156
|
-
context.children.map(function (child) {
|
|
9157
|
-
walker$1.walk(me, child);
|
|
9158
|
-
});
|
|
9159
|
-
return max;
|
|
9160
|
-
};
|
|
9161
|
-
};
|
|
9162
|
-
|
|
9163
9094
|
const seqParser$9 = sequenceParser;
|
|
9164
9095
|
const TitleContext = seqParser$9.TitleContext;
|
|
9165
9096
|
TitleContext.prototype.content = function () {
|
|
@@ -9227,10 +9158,6 @@ CreationContext$6.prototype.Constructor = function () {
|
|
|
9227
9158
|
return this.creationBody()?.construct()?.getFormattedText();
|
|
9228
9159
|
};
|
|
9229
9160
|
|
|
9230
|
-
CreationContext$6.prototype.To = function () {
|
|
9231
|
-
return this.Constructor();
|
|
9232
|
-
};
|
|
9233
|
-
|
|
9234
9161
|
CreationContext$6.prototype.Owner = function () {
|
|
9235
9162
|
if (!this.Constructor()) {
|
|
9236
9163
|
return "Missing Constructor";
|
|
@@ -9245,10 +9172,6 @@ MessageContext$6.prototype.To = function () {
|
|
|
9245
9172
|
return toCtx?.name?.()?.getFormattedText() || toCtx?.getFormattedText();
|
|
9246
9173
|
};
|
|
9247
9174
|
|
|
9248
|
-
MessageContext$6.prototype.Owner = function () {
|
|
9249
|
-
return this.To() || getOwnerFromAncestor(this.parentCtx);
|
|
9250
|
-
};
|
|
9251
|
-
|
|
9252
9175
|
function getOwnerFromAncestor(ctx) {
|
|
9253
9176
|
while (ctx) {
|
|
9254
9177
|
if (ctx instanceof CreationContext$6 || ctx instanceof MessageContext$6) {
|
|
@@ -9259,32 +9182,39 @@ function getOwnerFromAncestor(ctx) {
|
|
|
9259
9182
|
return undefined;
|
|
9260
9183
|
}
|
|
9261
9184
|
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9185
|
+
// MessageContext, AsyncMessageContext, ReturnAsyncMessageContext and
|
|
9186
|
+
// RetContext all resolve Owner the same way: prefer their own To(), else
|
|
9187
|
+
// look up the nearest CreationContext/MessageContext ancestor. Assigning
|
|
9188
|
+
// the same function to each prototype keeps the body single-sourced while
|
|
9189
|
+
// each context class still gets its own Owner property.
|
|
9190
|
+
function ownerFromToOrAncestor() {
|
|
9268
9191
|
return this.To() || getOwnerFromAncestor(this.parentCtx);
|
|
9269
|
-
}
|
|
9192
|
+
}
|
|
9193
|
+
|
|
9194
|
+
[
|
|
9195
|
+
MessageContext$6,
|
|
9196
|
+
AsyncMessageContext$2,
|
|
9197
|
+
ReturnAsyncMessageContext$2,
|
|
9198
|
+
RetMessageContext,
|
|
9199
|
+
].forEach((ContextClass) => {
|
|
9200
|
+
ContextClass.prototype.Owner = ownerFromToOrAncestor;
|
|
9201
|
+
});
|
|
9270
9202
|
|
|
9271
|
-
ReturnAsyncMessageContext
|
|
9203
|
+
// AsyncMessageContext and ReturnAsyncMessageContext resolve To() identically
|
|
9204
|
+
// (unlike MessageContext, which reaches its `to` node through
|
|
9205
|
+
// messageBody()?.fromTo(), and RetContext, which delegates to ReturnTo()).
|
|
9206
|
+
function toFromDirectTo() {
|
|
9272
9207
|
const toCtx = this.to();
|
|
9273
9208
|
return toCtx?.name?.()?.getFormattedText() || toCtx?.getFormattedText();
|
|
9274
|
-
}
|
|
9209
|
+
}
|
|
9275
9210
|
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
};
|
|
9211
|
+
AsyncMessageContext$2.prototype.To = toFromDirectTo;
|
|
9212
|
+
ReturnAsyncMessageContext$2.prototype.To = toFromDirectTo;
|
|
9279
9213
|
|
|
9280
9214
|
RetMessageContext.prototype.To = function () {
|
|
9281
9215
|
return this.ReturnTo();
|
|
9282
9216
|
};
|
|
9283
9217
|
|
|
9284
|
-
RetMessageContext.prototype.Owner = function () {
|
|
9285
|
-
return this.To() || getOwnerFromAncestor(this.parentCtx);
|
|
9286
|
-
};
|
|
9287
|
-
|
|
9288
9218
|
const ProgContext$2 = sequenceParser.ProgContext;
|
|
9289
9219
|
|
|
9290
9220
|
/**
|
|
@@ -9397,11 +9327,7 @@ const MessageContext$4 = sequenceParser.MessageContext;
|
|
|
9397
9327
|
const CreationContext$4 = sequenceParser.CreationContext;
|
|
9398
9328
|
|
|
9399
9329
|
ParserRuleContext$1.prototype.Origin = function () {
|
|
9400
|
-
|
|
9401
|
-
while (ctx && !(ctx instanceof StatContext$2 || ctx instanceof ProgContext)) {
|
|
9402
|
-
ctx = this.parentCtx;
|
|
9403
|
-
}
|
|
9404
|
-
return ctx.Origin();
|
|
9330
|
+
return this.ClosestAncestorStat()?.Origin();
|
|
9405
9331
|
};
|
|
9406
9332
|
// Origin is essentially the 'from' of a message.
|
|
9407
9333
|
// For example, in `S -> A.m1 {B.m2 {C.m3}}`,
|
|
@@ -9482,9 +9408,6 @@ CreationContext$3.prototype.SignatureText = function() {
|
|
|
9482
9408
|
const text = this.ParametersText() || "create";
|
|
9483
9409
|
return `«${text}»`;
|
|
9484
9410
|
};
|
|
9485
|
-
CreationContext$3.prototype.isParamValid = function() {
|
|
9486
|
-
return (this.creationBody().parameters()?.parameter()?.length ?? 0) > 0;
|
|
9487
|
-
};
|
|
9488
9411
|
RetContext$1.prototype.SignatureText = function() {
|
|
9489
9412
|
return this.asyncMessage()?.content()?.getFormattedText() ?? this.returnAsyncMessage()?.content()?.getFormattedText() ?? this.expr()?.getFormattedText() ?? "";
|
|
9490
9413
|
};
|
|
@@ -9575,27 +9498,22 @@ CreationContext.prototype.From = function() {
|
|
|
9575
9498
|
}
|
|
9576
9499
|
return void 0;
|
|
9577
9500
|
};
|
|
9501
|
+
function fromProvidedOrOrigin() {
|
|
9502
|
+
return this.ProvidedFrom() || this.ClosestAncestorStat()?.Origin();
|
|
9503
|
+
}
|
|
9578
9504
|
MessageContext.prototype.ProvidedFrom = function() {
|
|
9579
9505
|
const fromCtx = this.messageBody()?.fromTo?.()?.from?.();
|
|
9580
9506
|
return fromCtx?.name?.()?.getFormattedText?.() || fromCtx?.getFormattedText?.();
|
|
9581
9507
|
};
|
|
9582
|
-
MessageContext.prototype.From =
|
|
9583
|
-
|
|
9584
|
-
};
|
|
9585
|
-
AsyncMessageContext.prototype.ProvidedFrom = function() {
|
|
9586
|
-
const fromCtx = this.from();
|
|
9587
|
-
return fromCtx?.name?.()?.getFormattedText?.() || fromCtx?.getFormattedText?.();
|
|
9588
|
-
};
|
|
9589
|
-
AsyncMessageContext.prototype.From = function() {
|
|
9590
|
-
return this.ProvidedFrom() || this.ClosestAncestorStat()?.Origin();
|
|
9591
|
-
};
|
|
9592
|
-
ReturnAsyncMessageContext.prototype.ProvidedFrom = function() {
|
|
9508
|
+
MessageContext.prototype.From = fromProvidedOrOrigin;
|
|
9509
|
+
function providedFromDirectFrom() {
|
|
9593
9510
|
const fromCtx = this.from();
|
|
9594
9511
|
return fromCtx?.name?.()?.getFormattedText?.() || fromCtx?.getFormattedText?.();
|
|
9595
|
-
}
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9512
|
+
}
|
|
9513
|
+
AsyncMessageContext.prototype.ProvidedFrom = providedFromDirectFrom;
|
|
9514
|
+
AsyncMessageContext.prototype.From = fromProvidedOrOrigin;
|
|
9515
|
+
ReturnAsyncMessageContext.prototype.ProvidedFrom = providedFromDirectFrom;
|
|
9516
|
+
ReturnAsyncMessageContext.prototype.From = fromProvidedOrOrigin;
|
|
9599
9517
|
RetContext.prototype.From = function() {
|
|
9600
9518
|
return this.asyncMessage()?.From?.() || this.returnAsyncMessage()?.From?.() || this.ClosestAncestorStat()?.Origin();
|
|
9601
9519
|
};
|
|
@@ -9616,14 +9534,6 @@ ParserRuleContext.prototype.ClosestAncestorStat = function() {
|
|
|
9616
9534
|
}
|
|
9617
9535
|
return void 0;
|
|
9618
9536
|
};
|
|
9619
|
-
ParserRuleContext.prototype.ClosestAncestorBlock = function() {
|
|
9620
|
-
const parentCtx = this.ClosestAncestorStat()?.parentCtx;
|
|
9621
|
-
if (parentCtx instanceof seqParser.BlockContext) {
|
|
9622
|
-
return parentCtx;
|
|
9623
|
-
}
|
|
9624
|
-
console.warn("Cannot find closest ancestor block for context:", this);
|
|
9625
|
-
return void 0;
|
|
9626
|
-
};
|
|
9627
9537
|
|
|
9628
9538
|
antlr4.ParserRuleContext.prototype.getAncestors = function(predicate) {
|
|
9629
9539
|
const ancestors = [];
|
|
@@ -9642,11 +9552,9 @@ antlr4.ParserRuleContext.prototype.getAncestors = function(predicate) {
|
|
|
9642
9552
|
|
|
9643
9553
|
const formatText = (text) => text.replace(/[\n\r]/g, " ").replace(/\s+/g, " ").replace(/\s*([,;.])\s*/g, "$1").replace(/\s*(\()\s*|\s*(\))/g, "$1$2").replace(/\s+$/g, "").replace(/^"(.*)"$/, "$1");
|
|
9644
9554
|
|
|
9645
|
-
const errors = [];
|
|
9646
9555
|
const errorDetails = [];
|
|
9647
9556
|
class SeqErrorListener extends antlr4.error.ErrorListener {
|
|
9648
9557
|
syntaxError(recognizer, offendingSymbol, line, column, msg) {
|
|
9649
|
-
errors.push(`${offendingSymbol} line ${line}, col ${column}: ${msg}`);
|
|
9650
9558
|
errorDetails.push({
|
|
9651
9559
|
line,
|
|
9652
9560
|
column,
|
|
@@ -9656,7 +9564,7 @@ class SeqErrorListener extends antlr4.error.ErrorListener {
|
|
|
9656
9564
|
}
|
|
9657
9565
|
|
|
9658
9566
|
// Reentrant error listener: collects syntax errors into its OWN instance array
|
|
9659
|
-
// instead of the shared module-level `
|
|
9567
|
+
// instead of the shared module-level `errorDetails`. This is what lets
|
|
9660
9568
|
// validate()/parse() below run repeatedly or concurrently (e.g. server-side)
|
|
9661
9569
|
// without the clear-before/clear-after dance the legacy globals require.
|
|
9662
9570
|
class CollectingErrorListener extends antlr4.error.ErrorListener {
|
|
@@ -9716,7 +9624,7 @@ function rootContext(code) {
|
|
|
9716
9624
|
|
|
9717
9625
|
const parser = createParser(code);
|
|
9718
9626
|
parser.addErrorListener(new SeqErrorListener());
|
|
9719
|
-
return parser.
|
|
9627
|
+
return parser.prog();
|
|
9720
9628
|
}
|
|
9721
9629
|
|
|
9722
9630
|
antlr4.ParserRuleContext.prototype.getFormattedText = function () {
|
|
@@ -9747,7 +9655,7 @@ antlr4.ParserRuleContext.prototype.getComment = function () {
|
|
|
9747
9655
|
/**
|
|
9748
9656
|
* Parse ZenUML DSL with a per-call (reentrant) error listener and return both
|
|
9749
9657
|
* the error-recovered parse tree and a structured error list. Safe to call
|
|
9750
|
-
* repeatedly/concurrently — it does not touch the module-level
|
|
9658
|
+
* repeatedly/concurrently — it does not touch the module-level ErrorDetails.
|
|
9751
9659
|
*
|
|
9752
9660
|
* @param {string} code ZenUML DSL source
|
|
9753
9661
|
* @returns {{ rootContext: object, pass: boolean, errorDetails: { line: number, column: number, msg: string }[] }}
|
|
@@ -9801,15 +9709,7 @@ const Parser = {
|
|
|
9801
9709
|
},
|
|
9802
9710
|
parse,
|
|
9803
9711
|
validate,
|
|
9804
|
-
Errors: errors,
|
|
9805
9712
|
ErrorDetails: errorDetails,
|
|
9806
|
-
/**
|
|
9807
|
-
* @return {number} how many levels of embedded fragments
|
|
9808
|
-
*/
|
|
9809
|
-
Depth: function (ctx) {
|
|
9810
|
-
const childFragmentDetector = ChildFragmentDetector;
|
|
9811
|
-
return childFragmentDetector.depth(childFragmentDetector)(ctx);
|
|
9812
|
-
},
|
|
9813
9713
|
};
|
|
9814
9714
|
|
|
9815
9715
|
const FRAGMENT_PADDING_X = 10;
|
|
@@ -9823,6 +9723,7 @@ const PARTICIPANT_ICON_WIDTH = 40;
|
|
|
9823
9723
|
const LIFELINE_WIDTH = 1;
|
|
9824
9724
|
const PARTICIPANT_TOP_SPACE_FOR_GROUP = 20;
|
|
9825
9725
|
const OCCURRENCE_EMPTY_HEIGHT = 24;
|
|
9726
|
+
const DIAGRAM_HEADER_HEIGHT = 24 + 4 * 2 + 1;
|
|
9826
9727
|
|
|
9827
9728
|
var TextType = /* @__PURE__ */ ((TextType2) => {
|
|
9828
9729
|
TextType2[TextType2["MessageContent"] = 0] = "MessageContent";
|
|
@@ -9956,39 +9857,6 @@ function OrderedParticipants(rootContext) {
|
|
|
9956
9857
|
return result;
|
|
9957
9858
|
}
|
|
9958
9859
|
|
|
9959
|
-
const LEVELS = ["log", "trace", "debug", "info", "warn", "error"];
|
|
9960
|
-
const LEVEL_WEIGHT = {
|
|
9961
|
-
trace: 10,
|
|
9962
|
-
debug: 20,
|
|
9963
|
-
log: 30,
|
|
9964
|
-
info: 30,
|
|
9965
|
-
warn: 40,
|
|
9966
|
-
error: 50
|
|
9967
|
-
};
|
|
9968
|
-
const THRESHOLD = LEVEL_WEIGHT.warn;
|
|
9969
|
-
function consoleFn(level) {
|
|
9970
|
-
const fn = console[level];
|
|
9971
|
-
return typeof fn === "function" ? fn.bind(console) : console.log.bind(console);
|
|
9972
|
-
}
|
|
9973
|
-
function createLogger(prefix) {
|
|
9974
|
-
const logger = {};
|
|
9975
|
-
for (const level of LEVELS) {
|
|
9976
|
-
const fn = consoleFn(level);
|
|
9977
|
-
logger[level] = (...args) => {
|
|
9978
|
-
if (LEVEL_WEIGHT[level] >= THRESHOLD) {
|
|
9979
|
-
if (prefix) {
|
|
9980
|
-
fn(prefix[0], prefix[1], ...args);
|
|
9981
|
-
} else {
|
|
9982
|
-
fn(...args);
|
|
9983
|
-
}
|
|
9984
|
-
}
|
|
9985
|
-
};
|
|
9986
|
-
}
|
|
9987
|
-
logger.child = (opts) => createLogger(["%c" + (opts.name || ""), "color: #00f"]);
|
|
9988
|
-
return logger;
|
|
9989
|
-
}
|
|
9990
|
-
const rootLogger = createLogger();
|
|
9991
|
-
|
|
9992
9860
|
function Dual(p, v) {
|
|
9993
9861
|
return { position: p, velocity: v };
|
|
9994
9862
|
}
|
|
@@ -10129,7 +9997,6 @@ class Coordinates {
|
|
|
10129
9997
|
}
|
|
10130
9998
|
const leftGap = this.getParticipantGap(this.participantModels[0]);
|
|
10131
9999
|
const position = leftGap + this.positionsCache[pIndex];
|
|
10132
|
-
rootLogger.debug(`Position of ${participantName} is ${position}`);
|
|
10133
10000
|
return position;
|
|
10134
10001
|
}
|
|
10135
10002
|
walkThrough() {
|
|
@@ -10176,17 +10043,11 @@ class Coordinates {
|
|
|
10176
10043
|
}
|
|
10177
10044
|
const leftIndex = Math.min(indexFrom, indexTo);
|
|
10178
10045
|
const rightIndex = Math.max(indexFrom, indexTo);
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
);
|
|
10185
|
-
} catch {
|
|
10186
|
-
console.warn(
|
|
10187
|
-
`Could not set message gap between ${from} and ${message.to}`
|
|
10188
|
-
);
|
|
10189
|
-
}
|
|
10046
|
+
const messageWidth = this.getMessageWidth(message);
|
|
10047
|
+
this.m[leftIndex][rightIndex] = Math.max(
|
|
10048
|
+
messageWidth + ARROW_HEAD_WIDTH + OCCURRENCE_WIDTH,
|
|
10049
|
+
this.m[leftIndex][rightIndex]
|
|
10050
|
+
);
|
|
10190
10051
|
}
|
|
10191
10052
|
}
|
|
10192
10053
|
withParticipantGaps(participantModels) {
|
|
@@ -10216,9 +10077,6 @@ class Coordinates {
|
|
|
10216
10077
|
);
|
|
10217
10078
|
const participantWidth = Math.max(labelWidth + iconWidth + emojiWidth, MIN_PARTICIPANT_WIDTH) + MARGIN;
|
|
10218
10079
|
this.participantWidthCache.set(participant.name, participantWidth);
|
|
10219
|
-
rootLogger.debug(
|
|
10220
|
-
`Width of ${participant.name} is ${participantWidth}; labelWidth: ${labelWidth}`
|
|
10221
|
-
);
|
|
10222
10080
|
return participantWidth;
|
|
10223
10081
|
}
|
|
10224
10082
|
}
|
|
@@ -10247,6 +10105,24 @@ class NodeVM {
|
|
|
10247
10105
|
}
|
|
10248
10106
|
}
|
|
10249
10107
|
|
|
10108
|
+
const SPACING_UNIT = 4;
|
|
10109
|
+
const tw = (value) => value * SPACING_UNIT;
|
|
10110
|
+
const DEFAULT_LAYOUT_METRICS = {
|
|
10111
|
+
statementMarginY: tw(4),
|
|
10112
|
+
// .statement-container .my-4
|
|
10113
|
+
fragmentHeaderHeight: 25,
|
|
10114
|
+
// .fragment .leading-4 => line-height: 1rem
|
|
10115
|
+
fragmentPaddingBottom: 10
|
|
10116
|
+
// .zenuml .fragment =>padding-bottom: 10px
|
|
10117
|
+
};
|
|
10118
|
+
const MESSAGE_HEIGHT = 16;
|
|
10119
|
+
const SELF_MESSAGE_HEIGHT = 30;
|
|
10120
|
+
const ASYNC_SELF_MESSAGE_HEIGHT = 44;
|
|
10121
|
+
const CREATION_MESSAGE_HEIGHT = 40;
|
|
10122
|
+
const COMMENT_LINE_HEIGHT = tw(5);
|
|
10123
|
+
const FRAGMENT_BORDER_WIDTH = 1;
|
|
10124
|
+
const FRAGMENT_GUARD_HEIGHT = 28;
|
|
10125
|
+
|
|
10250
10126
|
function getLocalParticipantNames(ctx) {
|
|
10251
10127
|
if (!ctx) return [];
|
|
10252
10128
|
return [ctx.Origin() || _STARTER_, ...Participants(ctx).Names()];
|
|
@@ -10677,7 +10553,7 @@ class MarkdownMeasurer {
|
|
|
10677
10553
|
return 0;
|
|
10678
10554
|
}
|
|
10679
10555
|
const lines = _text.trim().split("\n");
|
|
10680
|
-
return lines.length *
|
|
10556
|
+
return lines.length * COMMENT_LINE_HEIGHT;
|
|
10681
10557
|
}
|
|
10682
10558
|
}
|
|
10683
10559
|
|
|
@@ -10689,57 +10565,12 @@ class StatementVM extends NodeVM {
|
|
|
10689
10565
|
if (!context?.getComment || !context.getComment()) return 0;
|
|
10690
10566
|
return new MarkdownMeasurer().measure(context.getComment());
|
|
10691
10567
|
}
|
|
10692
|
-
resolveFragmentOrigin(fallbackOrigin) {
|
|
10693
|
-
try {
|
|
10694
|
-
const participants = getLocalParticipantNames(this.context) || [];
|
|
10695
|
-
return participants[0] || fallbackOrigin;
|
|
10696
|
-
} catch (error) {
|
|
10697
|
-
console.warn("Failed to resolve fragment origin", error);
|
|
10698
|
-
return fallbackOrigin;
|
|
10699
|
-
}
|
|
10700
|
-
}
|
|
10701
10568
|
findLeftParticipant(ctx, fallbackOrigin) {
|
|
10702
10569
|
if (!ctx) return void 0;
|
|
10703
10570
|
const local = getLocalParticipantNames(ctx) || [];
|
|
10704
10571
|
const ordered = this.runtime.participants;
|
|
10705
10572
|
return ordered.find((name) => local.includes(name)) || local[0] || fallbackOrigin || void 0;
|
|
10706
10573
|
}
|
|
10707
|
-
measureOccurrence(context, top, participant, minHeight = 24, contentInset = 1) {
|
|
10708
|
-
const block = context?.braceBlock?.()?.block?.();
|
|
10709
|
-
if (!block) {
|
|
10710
|
-
return minHeight;
|
|
10711
|
-
}
|
|
10712
|
-
const offset = this.metrics.statementMarginY - contentInset;
|
|
10713
|
-
const blockStart = top - offset;
|
|
10714
|
-
const blockEnd = this.layoutBlock(
|
|
10715
|
-
block,
|
|
10716
|
-
participant || this.runtime.originParticipant,
|
|
10717
|
-
blockStart
|
|
10718
|
-
);
|
|
10719
|
-
const height = blockEnd - blockStart - offset;
|
|
10720
|
-
return Math.max(minHeight, height);
|
|
10721
|
-
}
|
|
10722
|
-
isRootLevelStatement(statCtx) {
|
|
10723
|
-
const block = statCtx?.parentCtx;
|
|
10724
|
-
return block === this.runtime.rootBlock;
|
|
10725
|
-
}
|
|
10726
|
-
isFirstStatement(statCtx) {
|
|
10727
|
-
const block = statCtx?.parentCtx;
|
|
10728
|
-
const statements = block?.stat?.() || [];
|
|
10729
|
-
return statements.length > 0 && statements[0] === statCtx;
|
|
10730
|
-
}
|
|
10731
|
-
altHasMultipleBranches(ctx) {
|
|
10732
|
-
if (typeof ctx?.alt !== "function") {
|
|
10733
|
-
return false;
|
|
10734
|
-
}
|
|
10735
|
-
const altContext = ctx.alt();
|
|
10736
|
-
if (!altContext) {
|
|
10737
|
-
return false;
|
|
10738
|
-
}
|
|
10739
|
-
const elseIfBlocks = altContext?.elseIfBlock?.() || [];
|
|
10740
|
-
const hasElse = Boolean(altContext?.elseBlock?.());
|
|
10741
|
-
return elseIfBlocks.length > 0 || hasElse;
|
|
10742
|
-
}
|
|
10743
10574
|
}
|
|
10744
10575
|
|
|
10745
10576
|
class AsyncMessageStatementVM extends StatementVM {
|
|
@@ -10754,7 +10585,7 @@ class AsyncMessageStatementVM extends StatementVM {
|
|
|
10754
10585
|
const source = asyncContext?.From?.() || asyncContext?.ProvidedFrom?.() || asyncContext?.Origin?.() || origin;
|
|
10755
10586
|
const target = asyncContext?.Owner?.() || asyncContext?.to?.()?.getFormattedText?.() || source;
|
|
10756
10587
|
const isSelf = source === target;
|
|
10757
|
-
const messageHeight = isSelf ?
|
|
10588
|
+
const messageHeight = isSelf ? ASYNC_SELF_MESSAGE_HEIGHT : MESSAGE_HEIGHT;
|
|
10758
10589
|
return {
|
|
10759
10590
|
top,
|
|
10760
10591
|
height: commentHeight + messageHeight,
|
|
@@ -10763,7 +10594,6 @@ class AsyncMessageStatementVM extends StatementVM {
|
|
|
10763
10594
|
}
|
|
10764
10595
|
}
|
|
10765
10596
|
|
|
10766
|
-
const CREATION_MESSAGE_HEIGHT = 40;
|
|
10767
10597
|
class CreationStatementVM extends StatementVM {
|
|
10768
10598
|
constructor(statement, creation, runtime) {
|
|
10769
10599
|
super(statement, runtime);
|
|
@@ -10787,7 +10617,6 @@ class CreationStatementVM extends StatementVM {
|
|
|
10787
10617
|
}
|
|
10788
10618
|
const assignment = this.creation?.Assignment?.();
|
|
10789
10619
|
if (assignment) {
|
|
10790
|
-
rootLogger.info(`creation::assignment::${participant}`);
|
|
10791
10620
|
cursor += 12;
|
|
10792
10621
|
}
|
|
10793
10622
|
const height = cursor - top;
|
|
@@ -10813,26 +10642,7 @@ class EmptyStatementVM extends StatementVM {
|
|
|
10813
10642
|
}
|
|
10814
10643
|
}
|
|
10815
10644
|
|
|
10816
|
-
class
|
|
10817
|
-
beginFragment(context, top) {
|
|
10818
|
-
const commentHeight = this.measureComment(context);
|
|
10819
|
-
const headerHeight = this.metrics.fragmentHeaderHeight;
|
|
10820
|
-
return {
|
|
10821
|
-
cursor: top + commentHeight + headerHeight,
|
|
10822
|
-
commentHeight,
|
|
10823
|
-
headerHeight
|
|
10824
|
-
};
|
|
10825
|
-
}
|
|
10826
|
-
finalizeFragment(top, cursor) {
|
|
10827
|
-
cursor += this.metrics.fragmentPaddingBottom;
|
|
10828
|
-
return {
|
|
10829
|
-
top,
|
|
10830
|
-
height: cursor - top
|
|
10831
|
-
};
|
|
10832
|
-
}
|
|
10833
|
-
}
|
|
10834
|
-
|
|
10835
|
-
class FragmentAltVM extends FragmentVM {
|
|
10645
|
+
class FragmentAltVM extends StatementVM {
|
|
10836
10646
|
constructor(statement, alt, runtime) {
|
|
10837
10647
|
super(statement, runtime);
|
|
10838
10648
|
this.alt = alt;
|
|
@@ -10844,7 +10654,7 @@ class FragmentAltVM extends FragmentVM {
|
|
|
10844
10654
|
let cursor = top + 1 + this.metrics.fragmentHeaderHeight + commentHeight;
|
|
10845
10655
|
const ifBlock = this.alt?.ifBlock?.();
|
|
10846
10656
|
if (ifBlock) {
|
|
10847
|
-
cursor +=
|
|
10657
|
+
cursor += FRAGMENT_GUARD_HEIGHT;
|
|
10848
10658
|
cursor = this.layoutBlock(
|
|
10849
10659
|
ifBlock.braceBlock()?.block(),
|
|
10850
10660
|
leftParticipant,
|
|
@@ -10852,7 +10662,7 @@ class FragmentAltVM extends FragmentVM {
|
|
|
10852
10662
|
);
|
|
10853
10663
|
}
|
|
10854
10664
|
this.alt?.elseIfBlock?.()?.forEach((block) => {
|
|
10855
|
-
cursor +=
|
|
10665
|
+
cursor += FRAGMENT_GUARD_HEIGHT;
|
|
10856
10666
|
cursor += 8;
|
|
10857
10667
|
cursor += 1;
|
|
10858
10668
|
cursor = this.layoutBlock(
|
|
@@ -10863,7 +10673,7 @@ class FragmentAltVM extends FragmentVM {
|
|
|
10863
10673
|
});
|
|
10864
10674
|
const elseBlock = this.alt?.elseBlock?.()?.braceBlock?.()?.block?.();
|
|
10865
10675
|
if (elseBlock) {
|
|
10866
|
-
cursor +=
|
|
10676
|
+
cursor += FRAGMENT_GUARD_HEIGHT;
|
|
10867
10677
|
cursor += 8;
|
|
10868
10678
|
cursor += 1;
|
|
10869
10679
|
cursor = this.layoutBlock(elseBlock, leftParticipant, cursor);
|
|
@@ -10877,7 +10687,7 @@ class FragmentAltVM extends FragmentVM {
|
|
|
10877
10687
|
}
|
|
10878
10688
|
}
|
|
10879
10689
|
|
|
10880
|
-
class FragmentRefVM extends
|
|
10690
|
+
class FragmentRefVM extends StatementVM {
|
|
10881
10691
|
kind = "ref";
|
|
10882
10692
|
measure(top) {
|
|
10883
10693
|
const context = this.context?.ref?.() || this.context;
|
|
@@ -10888,7 +10698,7 @@ class FragmentRefVM extends FragmentVM {
|
|
|
10888
10698
|
}
|
|
10889
10699
|
}
|
|
10890
10700
|
|
|
10891
|
-
class FragmentSingleBlockVM extends
|
|
10701
|
+
class FragmentSingleBlockVM extends StatementVM {
|
|
10892
10702
|
constructor(statement, fragment, runtime, kind) {
|
|
10893
10703
|
super(statement, runtime);
|
|
10894
10704
|
this.fragment = fragment;
|
|
@@ -10896,15 +10706,15 @@ class FragmentSingleBlockVM extends FragmentVM {
|
|
|
10896
10706
|
}
|
|
10897
10707
|
measure(top, origin) {
|
|
10898
10708
|
const commentHeight = this.measureComment(this.fragment);
|
|
10899
|
-
let cursor = top +
|
|
10709
|
+
let cursor = top + FRAGMENT_BORDER_WIDTH + this.metrics.fragmentHeaderHeight + commentHeight;
|
|
10900
10710
|
const hasCondition = Boolean(this.fragment?.parExpr?.()?.condition?.());
|
|
10901
|
-
if (hasCondition) cursor +=
|
|
10711
|
+
if (hasCondition) cursor += FRAGMENT_GUARD_HEIGHT;
|
|
10902
10712
|
const block = this.fragment?.braceBlock?.()?.block?.();
|
|
10903
10713
|
if (block) {
|
|
10904
10714
|
const fragmentOrigin = this.findLeftParticipant(this.fragment, origin) || origin;
|
|
10905
10715
|
cursor = this.layoutBlock(block, fragmentOrigin, cursor, this.kind);
|
|
10906
10716
|
}
|
|
10907
|
-
cursor += this.metrics.fragmentPaddingBottom +
|
|
10717
|
+
cursor += this.metrics.fragmentPaddingBottom + FRAGMENT_BORDER_WIDTH;
|
|
10908
10718
|
return {
|
|
10909
10719
|
top,
|
|
10910
10720
|
height: cursor - top,
|
|
@@ -10913,7 +10723,7 @@ class FragmentSingleBlockVM extends FragmentVM {
|
|
|
10913
10723
|
}
|
|
10914
10724
|
}
|
|
10915
10725
|
|
|
10916
|
-
class FragmentTryCatchVM extends
|
|
10726
|
+
class FragmentTryCatchVM extends StatementVM {
|
|
10917
10727
|
constructor(statement, tcf, runtime) {
|
|
10918
10728
|
super(statement, runtime);
|
|
10919
10729
|
this.tcf = tcf;
|
|
@@ -10992,6 +10802,14 @@ class ReturnStatementVM extends StatementVM {
|
|
|
10992
10802
|
}
|
|
10993
10803
|
}
|
|
10994
10804
|
|
|
10805
|
+
const SINGLE_BLOCK_FRAGMENT_KINDS = [
|
|
10806
|
+
"loop",
|
|
10807
|
+
"opt",
|
|
10808
|
+
"par",
|
|
10809
|
+
"section",
|
|
10810
|
+
"critical"
|
|
10811
|
+
];
|
|
10812
|
+
|
|
10995
10813
|
class SyncMessageStatementVM extends StatementVM {
|
|
10996
10814
|
constructor(statement, message, runtime) {
|
|
10997
10815
|
super(statement, runtime);
|
|
@@ -11006,7 +10824,7 @@ class SyncMessageStatementVM extends StatementVM {
|
|
|
11006
10824
|
const block = this.message.braceBlock?.()?.block?.();
|
|
11007
10825
|
const assignee = this.message.Assignment?.();
|
|
11008
10826
|
const isSelf = source === target;
|
|
11009
|
-
const messageHeight = isSelf ?
|
|
10827
|
+
const messageHeight = isSelf ? SELF_MESSAGE_HEIGHT : MESSAGE_HEIGHT;
|
|
11010
10828
|
cursor += messageHeight;
|
|
11011
10829
|
if (block) {
|
|
11012
10830
|
const fragmentOrigin = this.findLeftParticipant(this.message, origin) || origin;
|
|
@@ -11042,7 +10860,7 @@ const createStatementVM = (statement, runtime) => {
|
|
|
11042
10860
|
if (statement.divider?.()) {
|
|
11043
10861
|
return new DividerStatementVM(statement, runtime);
|
|
11044
10862
|
}
|
|
11045
|
-
for (const kind of
|
|
10863
|
+
for (const kind of SINGLE_BLOCK_FRAGMENT_KINDS) {
|
|
11046
10864
|
const fragment = statement[kind]?.();
|
|
11047
10865
|
if (fragment) {
|
|
11048
10866
|
return new FragmentSingleBlockVM(statement, fragment, runtime, kind);
|
|
@@ -11083,17 +10901,6 @@ class BlockVM extends NodeVM {
|
|
|
11083
10901
|
}
|
|
11084
10902
|
}
|
|
11085
10903
|
|
|
11086
|
-
const SPACING_UNIT = 4;
|
|
11087
|
-
const tw = (value) => value * SPACING_UNIT;
|
|
11088
|
-
const DEFAULT_LAYOUT_METRICS = {
|
|
11089
|
-
statementMarginY: tw(4),
|
|
11090
|
-
// .statement-container .my-4
|
|
11091
|
-
fragmentHeaderHeight: 25,
|
|
11092
|
-
// .fragment .leading-4 => line-height: 1rem
|
|
11093
|
-
fragmentPaddingBottom: 10
|
|
11094
|
-
// .zenuml .fragment =>padding-bottom: 10px
|
|
11095
|
-
};
|
|
11096
|
-
|
|
11097
10904
|
class VerticalCoordinates {
|
|
11098
10905
|
statementMap = /* @__PURE__ */ new Map();
|
|
11099
10906
|
creationTops = /* @__PURE__ */ new Map();
|
|
@@ -11105,16 +10912,13 @@ class VerticalCoordinates {
|
|
|
11105
10912
|
const originParticipant = messages.length === 0 ? _STARTER_ : messages[0].from || _STARTER_;
|
|
11106
10913
|
const runtime = {
|
|
11107
10914
|
metrics: DEFAULT_LAYOUT_METRICS,
|
|
11108
|
-
rootBlock,
|
|
11109
10915
|
participants,
|
|
11110
|
-
originParticipant,
|
|
11111
10916
|
recordCoordinate: (statement, coordinate) => {
|
|
11112
10917
|
const key = createStatementKey(statement);
|
|
11113
10918
|
this.statementMap.set(key, coordinate);
|
|
11114
10919
|
},
|
|
11115
10920
|
updateCreationTop: (participant, top) => {
|
|
11116
10921
|
const paddingTop = top - 8;
|
|
11117
|
-
rootLogger.info(`[VerticalCoordinates] updateCreationTop participant="${participant}" raw=${top} paddingTop=${paddingTop}`);
|
|
11118
10922
|
this.creationTops.set(participant, paddingTop);
|
|
11119
10923
|
}
|
|
11120
10924
|
};
|
|
@@ -11127,19 +10931,13 @@ class VerticalCoordinates {
|
|
|
11127
10931
|
getStatementCoordinate(key) {
|
|
11128
10932
|
return this.statementMap.get(key);
|
|
11129
10933
|
}
|
|
11130
|
-
getStatementCoordinateFor(statement) {
|
|
11131
|
-
return this.statementMap.get(createStatementKey(statement));
|
|
11132
|
-
}
|
|
11133
|
-
entries() {
|
|
11134
|
-
return Array.from(this.statementMap.entries());
|
|
11135
|
-
}
|
|
11136
10934
|
getTotalHeight() {
|
|
11137
10935
|
return this.totalHeight;
|
|
11138
10936
|
}
|
|
11139
10937
|
}
|
|
11140
10938
|
|
|
11141
10939
|
let dic = {};
|
|
11142
|
-
|
|
10940
|
+
let persistDic = {};
|
|
11143
10941
|
const getCache = (key) => {
|
|
11144
10942
|
if (key != null) {
|
|
11145
10943
|
const cacheValue = dic[key] ?? persistDic[key];
|
|
@@ -11153,78 +10951,92 @@ const setCache = (key, value, persist = false) => {
|
|
|
11153
10951
|
persistDic[key] = value;
|
|
11154
10952
|
}
|
|
11155
10953
|
};
|
|
10954
|
+
const clearPersistentCache = () => {
|
|
10955
|
+
persistDic = {};
|
|
10956
|
+
dic = {};
|
|
10957
|
+
};
|
|
11156
10958
|
|
|
11157
10959
|
const FONT_FAMILY = "Helvetica, Verdana, serif";
|
|
11158
|
-
const FONT_SIZE_PARTICIPANT = "
|
|
10960
|
+
const FONT_SIZE_PARTICIPANT = "14px";
|
|
10961
|
+
const FONT_SIZE_MESSAGE = "15px";
|
|
11159
10962
|
const FONT_SIZE_FRAGMENT = "14px";
|
|
11160
|
-
function
|
|
11161
|
-
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}`;
|
|
11162
10968
|
}
|
|
11163
10969
|
let canvasCtx = null;
|
|
11164
10970
|
function setCanvasContext(ctx) {
|
|
10971
|
+
if (ctx === canvasCtx) return;
|
|
11165
10972
|
canvasCtx = ctx;
|
|
10973
|
+
clearPersistentCache();
|
|
11166
10974
|
}
|
|
11167
10975
|
function getCanvasContext() {
|
|
11168
10976
|
if (canvasCtx) return canvasCtx;
|
|
11169
10977
|
try {
|
|
11170
10978
|
if (typeof OffscreenCanvas !== "undefined") {
|
|
11171
10979
|
canvasCtx = new OffscreenCanvas(1, 1).getContext("2d");
|
|
11172
|
-
rootLogger.debug("[ZenUML] WidthProviderOnCanvas: using OffscreenCanvas");
|
|
11173
10980
|
} else if (typeof document !== "undefined") {
|
|
11174
10981
|
canvasCtx = document.createElement("canvas").getContext("2d");
|
|
11175
|
-
rootLogger.debug("[ZenUML] WidthProviderOnCanvas: using <canvas> element");
|
|
11176
|
-
} else {
|
|
11177
|
-
rootLogger.debug("[ZenUML] WidthProviderOnCanvas: no canvas available, using character estimate fallback");
|
|
11178
10982
|
}
|
|
11179
10983
|
} catch {
|
|
11180
|
-
rootLogger.debug("[ZenUML] WidthProviderOnCanvas: canvas creation failed, using character estimate fallback");
|
|
11181
10984
|
}
|
|
11182
10985
|
return canvasCtx;
|
|
11183
10986
|
}
|
|
11184
10987
|
function WidthProviderOnCanvas(text, type) {
|
|
11185
10988
|
const measured = text.trim();
|
|
11186
|
-
const cacheKey = `WidthProviderOnCanvas_${measured}_${type}`;
|
|
10989
|
+
const cacheKey = `WidthProviderOnCanvas_${getFontSpec(type)}_${measured}_${type}`;
|
|
11187
10990
|
const cacheValue = getCache(cacheKey);
|
|
11188
10991
|
if (cacheValue != null) {
|
|
11189
10992
|
return cacheValue;
|
|
11190
10993
|
}
|
|
11191
10994
|
const ctx = getCanvasContext();
|
|
11192
10995
|
if (!ctx) {
|
|
11193
|
-
const width2 = Math.ceil(
|
|
11194
|
-
|
|
10996
|
+
const width2 = Math.ceil(
|
|
10997
|
+
measured.length * Number.parseFloat(getFontSize(type)) * 0.6
|
|
10998
|
+
);
|
|
10999
|
+
setCache(cacheKey, width2);
|
|
11195
11000
|
return width2;
|
|
11196
11001
|
}
|
|
11197
|
-
ctx.font = getFontSpec();
|
|
11002
|
+
ctx.font = getFontSpec(type);
|
|
11003
|
+
ctx.fontVariantCaps = "normal";
|
|
11198
11004
|
const width = Math.round(ctx.measureText(measured).width);
|
|
11199
11005
|
setCache(cacheKey, width, true);
|
|
11200
11006
|
return width;
|
|
11201
11007
|
}
|
|
11202
|
-
const EMOJI_PATTERN =
|
|
11203
|
-
|
|
11008
|
+
const EMOJI_PATTERN = (
|
|
11009
|
+
// eslint-disable-next-line no-misleading-character-class
|
|
11010
|
+
/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE00}-\u{FE0F}\u{200D}]/u
|
|
11011
|
+
);
|
|
11012
|
+
function measureWithSvg(text, fontSize, caps = "normal") {
|
|
11204
11013
|
if (typeof document === "undefined") return null;
|
|
11205
11014
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
11206
11015
|
svg.setAttribute("style", "position:absolute;left:-9999px;top:-9999px");
|
|
11207
11016
|
const textEl = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
11208
11017
|
textEl.setAttribute("font-family", FONT_FAMILY);
|
|
11209
11018
|
textEl.setAttribute("font-size", fontSize);
|
|
11019
|
+
textEl.style.fontVariantCaps = caps;
|
|
11020
|
+
if (typeof textEl.getBBox !== "function") return null;
|
|
11210
11021
|
textEl.textContent = text;
|
|
11211
11022
|
svg.appendChild(textEl);
|
|
11212
11023
|
document.body.appendChild(svg);
|
|
11213
11024
|
const width = textEl.getBBox().width;
|
|
11214
11025
|
document.body.removeChild(svg);
|
|
11215
|
-
return width;
|
|
11026
|
+
return Number.isFinite(width) && width > 0 ? width : null;
|
|
11216
11027
|
}
|
|
11217
|
-
function measureTextWithFont(text, fontSize) {
|
|
11028
|
+
function measureTextWithFont(text, fontSize, caps = "normal") {
|
|
11218
11029
|
const measured = text.trim();
|
|
11219
11030
|
if (!measured) return 0;
|
|
11220
11031
|
const hasEmoji = EMOJI_PATTERN.test(measured);
|
|
11221
|
-
const
|
|
11032
|
+
const font = `${fontSize} ${FONT_FAMILY}`;
|
|
11033
|
+
const cacheKey = (hasEmoji ? `measureTextWithFont_svg_${font}_${measured}` : `measureTextWithFont_${font}_${measured}`) + `_${caps}`;
|
|
11222
11034
|
const cacheValue = getCache(cacheKey);
|
|
11223
11035
|
if (cacheValue != null) {
|
|
11224
11036
|
return cacheValue;
|
|
11225
11037
|
}
|
|
11226
|
-
if (hasEmoji) {
|
|
11227
|
-
const svgWidth = measureWithSvg(measured, fontSize);
|
|
11038
|
+
if (hasEmoji || caps !== "normal") {
|
|
11039
|
+
const svgWidth = measureWithSvg(measured, fontSize, caps);
|
|
11228
11040
|
if (svgWidth != null) {
|
|
11229
11041
|
setCache(cacheKey, svgWidth, true);
|
|
11230
11042
|
return svgWidth;
|
|
@@ -11234,11 +11046,19 @@ function measureTextWithFont(text, fontSize) {
|
|
|
11234
11046
|
if (!ctx) {
|
|
11235
11047
|
const px = Number.parseFloat(fontSize) || 14;
|
|
11236
11048
|
const width2 = Math.ceil(measured.length * px * 0.6);
|
|
11237
|
-
setCache(cacheKey, width2
|
|
11049
|
+
setCache(cacheKey, width2);
|
|
11238
11050
|
return width2;
|
|
11239
11051
|
}
|
|
11240
|
-
ctx.font =
|
|
11241
|
-
|
|
11052
|
+
ctx.font = font;
|
|
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
|
+
}
|
|
11242
11062
|
setCache(cacheKey, width, true);
|
|
11243
11063
|
return width;
|
|
11244
11064
|
}
|
|
@@ -11249,6 +11069,11 @@ function measureSvgParticipantLabelWidth(text) {
|
|
|
11249
11069
|
return measureTextWithFont(text, FONT_SIZE_PARTICIPANT);
|
|
11250
11070
|
}
|
|
11251
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
|
+
|
|
11252
11077
|
const walker = antlr4.tree.ParseTreeWalker.DEFAULT;
|
|
11253
11078
|
class FrameBuilder extends sequenceParserListener {
|
|
11254
11079
|
_orderedParticipants;
|
|
@@ -11358,22 +11183,59 @@ function FrameBorder(frame) {
|
|
|
11358
11183
|
return { left: 0, right: 0 };
|
|
11359
11184
|
}
|
|
11360
11185
|
return {
|
|
11361
|
-
type: frame.type,
|
|
11362
11186
|
left: FRAGMENT_PADDING_X * longestPath(frame, "LEFT" /* LEFT */),
|
|
11363
11187
|
right: FRAGMENT_PADDING_X * longestPath(frame, "RIGHT" /* RIGHT */)
|
|
11364
11188
|
};
|
|
11365
11189
|
}
|
|
11366
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
|
+
|
|
11208
|
+
function TotalWidth(ctx, coordinates) {
|
|
11209
|
+
const allParticipants = coordinates.orderedParticipantNames();
|
|
11210
|
+
if (!ctx || allParticipants.length === 0) return 0;
|
|
11211
|
+
const localParticipants = getLocalParticipantNames(ctx);
|
|
11212
|
+
const leftParticipant = allParticipants.find((p) => localParticipants.includes(p)) || "";
|
|
11213
|
+
const rightParticipant = allParticipants.slice().reverse().find((p) => localParticipants.includes(p)) || "";
|
|
11214
|
+
const frameBuilder = new FrameBuilder(allParticipants);
|
|
11215
|
+
const frame = frameBuilder.getFrame(ctx);
|
|
11216
|
+
const border = FrameBorder(frame);
|
|
11217
|
+
const extraWidth = extraWidthDueToSelfMessage(
|
|
11218
|
+
ctx,
|
|
11219
|
+
rightParticipant,
|
|
11220
|
+
coordinates
|
|
11221
|
+
);
|
|
11222
|
+
const participantWidth = coordinates.distance(leftParticipant, rightParticipant) + coordinates.half(leftParticipant) + coordinates.half(rightParticipant);
|
|
11223
|
+
return Math.max(participantWidth, FRAGMENT_MIN_WIDTH) + border.left + border.right + extraWidth;
|
|
11224
|
+
}
|
|
11225
|
+
function extraWidthDueToSelfMessage(ctx, rightParticipant, coordinates) {
|
|
11226
|
+
const allMessages = AllMessages(ctx);
|
|
11227
|
+
const widths = allMessages.filter((m) => m.from === m.to).map(
|
|
11228
|
+
(m) => coordinates.getMessageWidth(m) - coordinates.distance(m.from || _STARTER_, rightParticipant) - coordinates.half(rightParticipant)
|
|
11229
|
+
);
|
|
11230
|
+
return Math.max.apply(null, [0, ...widths]);
|
|
11231
|
+
}
|
|
11232
|
+
|
|
11367
11233
|
const PARTICIPANT_TOP_SPACE = PARTICIPANT_TOP_SPACE_FOR_GROUP + 8;
|
|
11368
11234
|
const PARTICIPANT_BOX_PADDING = 16;
|
|
11369
|
-
const PARTICIPANT_BOX_PADDING_ASSIGNEE = 16;
|
|
11370
11235
|
const PARTICIPANT_ICON_ROW_WIDTH = 28;
|
|
11371
11236
|
const PARTICIPANT_EMOJI_WIDTH = 20;
|
|
11372
11237
|
const PARTICIPANT_VISUAL_HEIGHT = 40;
|
|
11373
11238
|
const PARTICIPANT_MAX_WIDTH = 250;
|
|
11374
|
-
function snapX$1(x) {
|
|
11375
|
-
return x;
|
|
11376
|
-
}
|
|
11377
11239
|
function cssToSvgStyle(css) {
|
|
11378
11240
|
const result = {};
|
|
11379
11241
|
let hasKeys = false;
|
|
@@ -11392,24 +11254,32 @@ function cssToSvgStyle(css) {
|
|
|
11392
11254
|
|
|
11393
11255
|
function buildParticipants(models, coordinates, verticalCoordinates, measureText) {
|
|
11394
11256
|
return models.map((m) => {
|
|
11395
|
-
const centerX =
|
|
11257
|
+
const centerX = coordinates.getPosition(m.name);
|
|
11396
11258
|
const halfWidth = coordinates.half(m.name);
|
|
11397
|
-
const isAssignee = m.name.includes(":") && m.getDisplayName() === m.name;
|
|
11398
11259
|
let width;
|
|
11399
11260
|
let measuredTextWidth;
|
|
11400
11261
|
let measuredStereotypeWidth;
|
|
11401
11262
|
if (measureText && m.name !== _STARTER_) {
|
|
11402
11263
|
const textWidth = measureSvgParticipantLabelWidth(m.getDisplayName());
|
|
11403
11264
|
measuredTextWidth = textWidth;
|
|
11404
|
-
const padding =
|
|
11265
|
+
const padding = PARTICIPANT_BOX_PADDING;
|
|
11405
11266
|
const iconWidth = m.hasIcon() ? PARTICIPANT_ICON_ROW_WIDTH : 0;
|
|
11406
11267
|
const emojiWidth = m.emoji ? PARTICIPANT_EMOJI_WIDTH : 0;
|
|
11407
11268
|
const STEREOTYPE_BOX_OVERHEAD = 8;
|
|
11408
11269
|
if (m.stereotype) {
|
|
11409
|
-
measuredStereotypeWidth = measureSvgParticipantLabelWidth(
|
|
11270
|
+
measuredStereotypeWidth = measureSvgParticipantLabelWidth(
|
|
11271
|
+
`«${m.stereotype}»`
|
|
11272
|
+
);
|
|
11410
11273
|
}
|
|
11411
11274
|
const stereotypeBoxWidth = measuredStereotypeWidth != null ? measuredStereotypeWidth + STEREOTYPE_BOX_OVERHEAD : 0;
|
|
11412
|
-
width = Math.min(
|
|
11275
|
+
width = Math.min(
|
|
11276
|
+
Math.max(
|
|
11277
|
+
textWidth + padding + iconWidth + emojiWidth,
|
|
11278
|
+
stereotypeBoxWidth,
|
|
11279
|
+
MIN_PARTICIPANT_WIDTH
|
|
11280
|
+
),
|
|
11281
|
+
PARTICIPANT_MAX_WIDTH
|
|
11282
|
+
);
|
|
11413
11283
|
} else {
|
|
11414
11284
|
width = Math.min(halfWidth * 2 - MARGIN, PARTICIPANT_MAX_WIDTH);
|
|
11415
11285
|
}
|
|
@@ -11488,39 +11358,49 @@ function buildGroups(participants, diagramHeight) {
|
|
|
11488
11358
|
return groups;
|
|
11489
11359
|
}
|
|
11490
11360
|
|
|
11491
|
-
|
|
11492
|
-
|
|
11361
|
+
class Anchor2 {
|
|
11362
|
+
constructor(position, layers) {
|
|
11363
|
+
this.position = position;
|
|
11364
|
+
this.layers = layers;
|
|
11365
|
+
}
|
|
11366
|
+
/**
|
|
11367
|
+
* centerToEdge is used for translateX calculations.
|
|
11368
|
+
*/
|
|
11369
|
+
centerToEdge(other) {
|
|
11370
|
+
return other.rightEdgeOfRightWall() - this.centerOfRightWall();
|
|
11371
|
+
}
|
|
11372
|
+
centerToCenter(other) {
|
|
11373
|
+
return other.centerOfRightWall() - this.centerOfRightWall();
|
|
11374
|
+
}
|
|
11375
|
+
/**
|
|
11376
|
+
* edgeOffset is used for interactionWidth calculations.
|
|
11377
|
+
*/
|
|
11378
|
+
edgeOffset(other) {
|
|
11379
|
+
const isRightToLeft = other.position < this.position;
|
|
11380
|
+
const rightEdgeOfLeftAnchor = isRightToLeft ? other.rightEdgeOfRightWall() : this.rightEdgeOfRightWall();
|
|
11381
|
+
const leftEdgeOfRightAnchor = isRightToLeft ? this.leftEdgeOfRightWall() : other.leftEdgeOfRightWall();
|
|
11382
|
+
const distance = leftEdgeOfRightAnchor - rightEdgeOfLeftAnchor - LIFELINE_WIDTH;
|
|
11383
|
+
return isRightToLeft ? distance * -1 : distance;
|
|
11384
|
+
}
|
|
11385
|
+
centerOfRightWall() {
|
|
11386
|
+
return this.layers <= 1 ? this.position : this.position + OCCURRENCE_BAR_SIDE_WIDTH * (this.layers - 1);
|
|
11387
|
+
}
|
|
11388
|
+
rightEdgeOfRightWall() {
|
|
11389
|
+
return this.position + OCCURRENCE_BAR_SIDE_WIDTH * this.layers;
|
|
11390
|
+
}
|
|
11391
|
+
leftEdgeOfRightWall() {
|
|
11392
|
+
return this.layers === 0 ? this.position : this.centerOfRightWall() - OCCURRENCE_BAR_SIDE_WIDTH;
|
|
11393
|
+
}
|
|
11493
11394
|
}
|
|
11395
|
+
|
|
11494
11396
|
const COMMENT_FONT_ASCENT = 15;
|
|
11495
11397
|
function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, allParticipants, _measureText, commentHeight = 0, commentText, commentStyle, commentYBase) {
|
|
11496
11398
|
const statNode = info.statNode;
|
|
11497
|
-
if (!statNode) return null;
|
|
11498
11399
|
const fragmentCtx = findFragmentContext(statNode);
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
label: info.fragmentLabel || "",
|
|
11504
|
-
labelWidth: info.fragmentLabel ? measureSvgFragmentLabelWidth(resolveEmojiInText(info.fragmentLabel)) : void 0,
|
|
11505
|
-
x: 0,
|
|
11506
|
-
y: coord.top,
|
|
11507
|
-
width: coordinates.getWidth(),
|
|
11508
|
-
height: coord.height,
|
|
11509
|
-
headerY: coord.top + 1 + commentHeight,
|
|
11510
|
-
sections: [],
|
|
11511
|
-
number: info.number,
|
|
11512
|
-
depth: info.depth
|
|
11513
|
-
},
|
|
11514
|
-
comment: commentText && commentYBase != null ? {
|
|
11515
|
-
x: 1,
|
|
11516
|
-
y: commentYBase + 1 + COMMENT_FONT_ASCENT,
|
|
11517
|
-
text: commentText,
|
|
11518
|
-
style: commentStyle,
|
|
11519
|
-
fragmentComment: true
|
|
11520
|
-
} : void 0
|
|
11521
|
-
};
|
|
11522
|
-
}
|
|
11523
|
-
const localNames = getDeepParticipantNames(fragmentCtx, info.fragmentSections);
|
|
11400
|
+
const localNames = getDeepParticipantNames(
|
|
11401
|
+
fragmentCtx,
|
|
11402
|
+
info.fragmentSections
|
|
11403
|
+
);
|
|
11524
11404
|
const leftParticipant = allParticipants.find((p) => localNames.includes(p)) || "";
|
|
11525
11405
|
const rightParticipant = allParticipants.slice().reverse().find((p) => localNames.includes(p)) || "";
|
|
11526
11406
|
const frameBuilder = new FrameBuilder(allParticipants);
|
|
@@ -11529,14 +11409,8 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11529
11409
|
let fragWidth;
|
|
11530
11410
|
let fragX;
|
|
11531
11411
|
if (leftParticipant && rightParticipant) {
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
const extraWidths = selfMessages.map(
|
|
11535
|
-
(m) => coordinates.getMessageWidth(m) - coordinates.distance(m.from || _STARTER_, rightParticipant) - coordinates.half(rightParticipant)
|
|
11536
|
-
);
|
|
11537
|
-
const extraWidth = Math.max(0, ...extraWidths);
|
|
11538
|
-
fragWidth = Math.max(participantWidth, FRAGMENT_MIN_WIDTH) + fragBorder.left + fragBorder.right + extraWidth;
|
|
11539
|
-
fragX = snapX(coordinates.getPosition(leftParticipant)) - coordinates.half(leftParticipant);
|
|
11412
|
+
fragWidth = TotalWidth(statNode, coordinates);
|
|
11413
|
+
fragX = coordinates.getPosition(leftParticipant) - coordinates.half(leftParticipant);
|
|
11540
11414
|
} else {
|
|
11541
11415
|
fragWidth = Math.max(FRAGMENT_MIN_WIDTH, coordinates.getWidth());
|
|
11542
11416
|
fragX = 0;
|
|
@@ -11551,11 +11425,12 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11551
11425
|
if (sectionBlock) {
|
|
11552
11426
|
const innerStats = sectionBlock.stat?.() || [];
|
|
11553
11427
|
if (innerStats.length > 0) {
|
|
11554
|
-
const firstStatKey =
|
|
11428
|
+
const firstStatKey = createStatementKey(innerStats[0]);
|
|
11555
11429
|
if (firstStatKey) {
|
|
11556
11430
|
const innerCoord = verticalCoordinates.getStatementCoordinate(firstStatKey);
|
|
11557
11431
|
if (innerCoord) {
|
|
11558
|
-
|
|
11432
|
+
const labelHeight = info.fragmentKind === "alt" ? FRAGMENT_GUARD_HEIGHT : 20;
|
|
11433
|
+
sectionY = innerCoord.top - labelHeight - 8 - 8 - 1;
|
|
11559
11434
|
}
|
|
11560
11435
|
}
|
|
11561
11436
|
}
|
|
@@ -11564,11 +11439,16 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11564
11439
|
const sectionHeight = i < info.fragmentSections.length - 1 ? 0 : coord.top + coord.height - sectionY;
|
|
11565
11440
|
sections.push({
|
|
11566
11441
|
label: section.label,
|
|
11442
|
+
guardKeyword: section.guardKeyword,
|
|
11567
11443
|
y: sectionY,
|
|
11568
11444
|
height: sectionHeight,
|
|
11569
11445
|
labelWidth: section.label ? measureSvgFragmentLabelWidth(resolveEmojiInText(section.label)) : void 0,
|
|
11570
|
-
innerLabel: /^\[\s*.*\s*\]$/.test(section.label) ? section.label.slice(1, -1).trim() : void 0,
|
|
11571
|
-
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(
|
|
11450
|
+
resolveEmojiInText(section.label.slice(1, -1).trim())
|
|
11451
|
+
) : void 0,
|
|
11572
11452
|
keyword: (() => {
|
|
11573
11453
|
const spaceIdx = section.label.indexOf(" ");
|
|
11574
11454
|
if (spaceIdx > 0 && !section.label.startsWith("finally") && !section.label.startsWith("[")) {
|
|
@@ -11579,7 +11459,9 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11579
11459
|
keywordWidth: (() => {
|
|
11580
11460
|
const spaceIdx = section.label.indexOf(" ");
|
|
11581
11461
|
if (spaceIdx > 0 && !section.label.startsWith("finally") && !section.label.startsWith("[")) {
|
|
11582
|
-
return measureSvgFragmentLabelWidth(
|
|
11462
|
+
return measureSvgFragmentLabelWidth(
|
|
11463
|
+
section.label.substring(0, spaceIdx)
|
|
11464
|
+
);
|
|
11583
11465
|
}
|
|
11584
11466
|
return void 0;
|
|
11585
11467
|
})(),
|
|
@@ -11593,7 +11475,9 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11593
11475
|
detailWidth: (() => {
|
|
11594
11476
|
const spaceIdx = section.label.indexOf(" ");
|
|
11595
11477
|
if (spaceIdx > 0 && !section.label.startsWith("finally") && !section.label.startsWith("[")) {
|
|
11596
|
-
return measureSvgFragmentLabelWidth(
|
|
11478
|
+
return measureSvgFragmentLabelWidth(
|
|
11479
|
+
section.label.substring(spaceIdx + 1)
|
|
11480
|
+
);
|
|
11597
11481
|
}
|
|
11598
11482
|
return void 0;
|
|
11599
11483
|
})()
|
|
@@ -11606,7 +11490,7 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11606
11490
|
if (innerStats.length > 1) {
|
|
11607
11491
|
sections.push({ label: "", y: coord.top, height: 0 });
|
|
11608
11492
|
for (let i = 1; i < innerStats.length; i++) {
|
|
11609
|
-
const statKey =
|
|
11493
|
+
const statKey = createStatementKey(innerStats[i]);
|
|
11610
11494
|
if (!statKey) continue;
|
|
11611
11495
|
const innerCoord = verticalCoordinates.getStatementCoordinate(statKey);
|
|
11612
11496
|
if (!innerCoord) continue;
|
|
@@ -11633,13 +11517,17 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11633
11517
|
return {
|
|
11634
11518
|
fragment: {
|
|
11635
11519
|
kind: info.fragmentKind,
|
|
11520
|
+
headerLabel: info.fragmentHeaderLabel,
|
|
11636
11521
|
label: info.fragmentLabel || "",
|
|
11522
|
+
guardKeyword: info.guardKeyword,
|
|
11637
11523
|
labelWidth: info.fragmentLabel ? measureSvgFragmentLabelWidth(resolveEmojiInText(info.fragmentLabel)) : void 0,
|
|
11638
11524
|
x: fragX,
|
|
11639
11525
|
y: coord.top,
|
|
11640
11526
|
width: fragWidth,
|
|
11641
11527
|
height: coord.height,
|
|
11642
|
-
|
|
11528
|
+
// The header sits just inside the fragment's 1px top border, below any
|
|
11529
|
+
// inline comment — the same offset FragmentSingleBlockVM.measure() applies.
|
|
11530
|
+
headerY: coord.top + FRAGMENT_BORDER_WIDTH + commentHeight,
|
|
11643
11531
|
sections,
|
|
11644
11532
|
number: info.number,
|
|
11645
11533
|
depth: info.depth
|
|
@@ -11648,7 +11536,7 @@ function buildFragmentGeometry(info, coord, coordinates, verticalCoordinates, al
|
|
|
11648
11536
|
};
|
|
11649
11537
|
}
|
|
11650
11538
|
function findFragmentContext(stat) {
|
|
11651
|
-
for (const kind of
|
|
11539
|
+
for (const kind of SINGLE_BLOCK_FRAGMENT_KINDS) {
|
|
11652
11540
|
const frag = stat[kind]?.();
|
|
11653
11541
|
if (frag) return frag;
|
|
11654
11542
|
}
|
|
@@ -11660,10 +11548,6 @@ function findFragmentContext(stat) {
|
|
|
11660
11548
|
if (ref) return ref;
|
|
11661
11549
|
return null;
|
|
11662
11550
|
}
|
|
11663
|
-
function createStatementKeyFromStat(statement) {
|
|
11664
|
-
if (!statement?.start || !statement?.stop) return "";
|
|
11665
|
-
return `${statement.start.start}-${statement.stop.stop}`;
|
|
11666
|
-
}
|
|
11667
11551
|
function getDeepParticipantNames(fragmentCtx, sections) {
|
|
11668
11552
|
const names = new Set(getLocalParticipantNames(fragmentCtx));
|
|
11669
11553
|
if (sections) {
|
|
@@ -11701,11 +11585,37 @@ function walkBlock(block, currentOrigin, activeOccurrences, parentNumber, depth,
|
|
|
11701
11585
|
const to = message.Owner?.() || _STARTER_;
|
|
11702
11586
|
const label = normalizeLabel(message.SignatureText?.() || "");
|
|
11703
11587
|
const nestedBlock = message.braceBlock?.()?.block?.();
|
|
11704
|
-
results.push({
|
|
11588
|
+
results.push({
|
|
11589
|
+
key,
|
|
11590
|
+
kind: "sync",
|
|
11591
|
+
from,
|
|
11592
|
+
to,
|
|
11593
|
+
label,
|
|
11594
|
+
isSelf: from === to,
|
|
11595
|
+
hasBlock: !!nestedBlock,
|
|
11596
|
+
comment,
|
|
11597
|
+
statNode: stat,
|
|
11598
|
+
senderOccurrenceDepth: activeOccurrences.get(from) || 0,
|
|
11599
|
+
targetHasOccurrence: activeOccurrences.has(to),
|
|
11600
|
+
targetOccurrenceDepth: activeOccurrences.get(to) || 0,
|
|
11601
|
+
number,
|
|
11602
|
+
depth,
|
|
11603
|
+
parentBlockKind
|
|
11604
|
+
});
|
|
11705
11605
|
if (nestedBlock) {
|
|
11706
11606
|
const innerOccs = new Map(activeOccurrences);
|
|
11707
11607
|
innerOccs.set(to, (innerOccs.get(to) || 0) + 1);
|
|
11708
|
-
results.push(
|
|
11608
|
+
results.push(
|
|
11609
|
+
...walkBlock(
|
|
11610
|
+
nestedBlock,
|
|
11611
|
+
to,
|
|
11612
|
+
innerOccs,
|
|
11613
|
+
number,
|
|
11614
|
+
depth + 1,
|
|
11615
|
+
"sync",
|
|
11616
|
+
0
|
|
11617
|
+
)
|
|
11618
|
+
);
|
|
11709
11619
|
}
|
|
11710
11620
|
continue;
|
|
11711
11621
|
}
|
|
@@ -11713,8 +11623,23 @@ function walkBlock(block, currentOrigin, activeOccurrences, parentNumber, depth,
|
|
|
11713
11623
|
if (asyncMsg) {
|
|
11714
11624
|
const from = asyncMsg.From?.() || asyncMsg.ProvidedFrom?.() || asyncMsg.Origin?.() || currentOrigin;
|
|
11715
11625
|
const to = asyncMsg.Owner?.() || asyncMsg.to?.()?.getFormattedText?.() || from;
|
|
11716
|
-
const label = normalizeLabel(
|
|
11717
|
-
|
|
11626
|
+
const label = normalizeLabel(
|
|
11627
|
+
asyncMsg.content?.()?.getText?.() || asyncMsg.SignatureText?.() || ""
|
|
11628
|
+
);
|
|
11629
|
+
results.push({
|
|
11630
|
+
key,
|
|
11631
|
+
kind: "async",
|
|
11632
|
+
from,
|
|
11633
|
+
to,
|
|
11634
|
+
label,
|
|
11635
|
+
isSelf: from === to,
|
|
11636
|
+
hasBlock: false,
|
|
11637
|
+
comment,
|
|
11638
|
+
senderOccurrenceDepth: activeOccurrences.get(from) || 0,
|
|
11639
|
+
targetHasOccurrence: activeOccurrences.has(to),
|
|
11640
|
+
number,
|
|
11641
|
+
depth
|
|
11642
|
+
});
|
|
11718
11643
|
continue;
|
|
11719
11644
|
}
|
|
11720
11645
|
const creation = stat.creation?.();
|
|
@@ -11723,11 +11648,36 @@ function walkBlock(block, currentOrigin, activeOccurrences, parentNumber, depth,
|
|
|
11723
11648
|
const to = creation.Owner?.() || "";
|
|
11724
11649
|
const label = normalizeLabel(creation.SignatureText?.() || "«create»");
|
|
11725
11650
|
const creationBlock = creation.braceBlock?.()?.block?.();
|
|
11726
|
-
results.push({
|
|
11651
|
+
results.push({
|
|
11652
|
+
key,
|
|
11653
|
+
kind: "creation",
|
|
11654
|
+
from,
|
|
11655
|
+
to,
|
|
11656
|
+
label,
|
|
11657
|
+
isSelf: false,
|
|
11658
|
+
hasBlock: !!creationBlock,
|
|
11659
|
+
comment,
|
|
11660
|
+
statNode: stat,
|
|
11661
|
+
senderOccurrenceDepth: activeOccurrences.get(from) || 0,
|
|
11662
|
+
targetHasOccurrence: activeOccurrences.has(to),
|
|
11663
|
+
number,
|
|
11664
|
+
depth,
|
|
11665
|
+
parentBlockKind
|
|
11666
|
+
});
|
|
11727
11667
|
if (creationBlock) {
|
|
11728
11668
|
const innerOccs = new Map(activeOccurrences);
|
|
11729
11669
|
innerOccs.set(to, (innerOccs.get(to) || 0) + 1);
|
|
11730
|
-
results.push(
|
|
11670
|
+
results.push(
|
|
11671
|
+
...walkBlock(
|
|
11672
|
+
creationBlock,
|
|
11673
|
+
to || currentOrigin,
|
|
11674
|
+
innerOccs,
|
|
11675
|
+
number,
|
|
11676
|
+
depth + 1,
|
|
11677
|
+
"creation",
|
|
11678
|
+
0
|
|
11679
|
+
)
|
|
11680
|
+
);
|
|
11731
11681
|
}
|
|
11732
11682
|
continue;
|
|
11733
11683
|
}
|
|
@@ -11737,13 +11687,40 @@ function walkBlock(block, currentOrigin, activeOccurrences, parentNumber, depth,
|
|
|
11737
11687
|
const asyncMessage = ret?.asyncMessage?.();
|
|
11738
11688
|
const from = asyncMessage?.From?.() || ret?.From?.() || currentOrigin;
|
|
11739
11689
|
const to = asyncMessage?.to?.()?.getFormattedText?.() || ret?.ReturnTo?.() || _STARTER_;
|
|
11740
|
-
results.push({
|
|
11690
|
+
results.push({
|
|
11691
|
+
key,
|
|
11692
|
+
kind: "return",
|
|
11693
|
+
from,
|
|
11694
|
+
to,
|
|
11695
|
+
label,
|
|
11696
|
+
isSelf: from === to,
|
|
11697
|
+
hasBlock: false,
|
|
11698
|
+
comment,
|
|
11699
|
+
senderOccurrenceDepth: activeOccurrences.get(from) || 0,
|
|
11700
|
+
targetOccurrenceDepth: activeOccurrences.get(to) || 0,
|
|
11701
|
+
number,
|
|
11702
|
+
depth,
|
|
11703
|
+
parentBlockKind
|
|
11704
|
+
});
|
|
11741
11705
|
continue;
|
|
11742
11706
|
}
|
|
11743
11707
|
const divider = stat.divider?.();
|
|
11744
11708
|
if (divider) {
|
|
11745
|
-
const label = normalizeLabel(
|
|
11746
|
-
|
|
11709
|
+
const label = normalizeLabel(
|
|
11710
|
+
divider.getFormattedText?.() || divider.getText?.() || ""
|
|
11711
|
+
);
|
|
11712
|
+
results.push({
|
|
11713
|
+
key,
|
|
11714
|
+
kind: "divider",
|
|
11715
|
+
from: "",
|
|
11716
|
+
to: "",
|
|
11717
|
+
label,
|
|
11718
|
+
isSelf: false,
|
|
11719
|
+
hasBlock: false,
|
|
11720
|
+
senderOccurrenceDepth: 0,
|
|
11721
|
+
number,
|
|
11722
|
+
depth
|
|
11723
|
+
});
|
|
11747
11724
|
continue;
|
|
11748
11725
|
}
|
|
11749
11726
|
const fragmentInfo = extractFragmentInfo(stat);
|
|
@@ -11758,6 +11735,8 @@ function walkBlock(block, currentOrigin, activeOccurrences, parentNumber, depth,
|
|
|
11758
11735
|
hasBlock: false,
|
|
11759
11736
|
fragmentKind: fragmentInfo.fragmentKind,
|
|
11760
11737
|
fragmentLabel: fragmentInfo.label,
|
|
11738
|
+
fragmentHeaderLabel: fragmentInfo.headerLabel,
|
|
11739
|
+
guardKeyword: fragmentInfo.guardKeyword,
|
|
11761
11740
|
fragmentSections: fragmentInfo.sections,
|
|
11762
11741
|
comment,
|
|
11763
11742
|
statNode: stat,
|
|
@@ -11765,19 +11744,28 @@ function walkBlock(block, currentOrigin, activeOccurrences, parentNumber, depth,
|
|
|
11765
11744
|
number,
|
|
11766
11745
|
depth
|
|
11767
11746
|
});
|
|
11768
|
-
walkFragmentBlocks(
|
|
11747
|
+
walkFragmentBlocks(
|
|
11748
|
+
stat,
|
|
11749
|
+
currentOrigin,
|
|
11750
|
+
results,
|
|
11751
|
+
activeOccurrences,
|
|
11752
|
+
number,
|
|
11753
|
+
depth
|
|
11754
|
+
);
|
|
11769
11755
|
}
|
|
11770
11756
|
return results;
|
|
11771
11757
|
}
|
|
11772
11758
|
function extractFragmentInfo(stat) {
|
|
11773
|
-
for (const kind of
|
|
11759
|
+
for (const kind of SINGLE_BLOCK_FRAGMENT_KINDS) {
|
|
11774
11760
|
const frag = stat[kind]?.();
|
|
11775
11761
|
if (frag) {
|
|
11776
11762
|
const condition = frag.parExpr?.()?.condition?.();
|
|
11777
11763
|
const label = condition?.getFormattedText?.() || "";
|
|
11778
11764
|
return {
|
|
11779
11765
|
fragmentKind: kind,
|
|
11766
|
+
headerLabel: frag.atom?.()?.getFormattedText?.(),
|
|
11780
11767
|
label,
|
|
11768
|
+
guardKeyword: kind === "loop" ? frag.WHILE?.()?.getText?.() : void 0,
|
|
11781
11769
|
sections: [{ label, blockNode: frag.braceBlock?.()?.block?.() }]
|
|
11782
11770
|
};
|
|
11783
11771
|
}
|
|
@@ -11787,16 +11775,30 @@ function extractFragmentInfo(stat) {
|
|
|
11787
11775
|
const sections = [];
|
|
11788
11776
|
const ifBlock = alt.ifBlock?.();
|
|
11789
11777
|
if (ifBlock) {
|
|
11790
|
-
sections.push({
|
|
11778
|
+
sections.push({
|
|
11779
|
+
label: "Alt",
|
|
11780
|
+
guardKeyword: "if",
|
|
11781
|
+
condition: ifBlock.parExpr?.()?.condition?.()?.getFormattedText?.() || "",
|
|
11782
|
+
blockNode: ifBlock.braceBlock?.()?.block?.()
|
|
11783
|
+
});
|
|
11791
11784
|
}
|
|
11792
11785
|
for (const elseIf of alt.elseIfBlock?.() || []) {
|
|
11793
11786
|
const condition = elseIf.parExpr?.()?.condition?.();
|
|
11794
11787
|
const label = condition?.getFormattedText?.() || "";
|
|
11795
|
-
sections.push({
|
|
11788
|
+
sections.push({
|
|
11789
|
+
label: `else if ${label}`,
|
|
11790
|
+
guardKeyword: "else if",
|
|
11791
|
+
condition: label,
|
|
11792
|
+
blockNode: elseIf.braceBlock?.()?.block?.()
|
|
11793
|
+
});
|
|
11796
11794
|
}
|
|
11797
11795
|
const elseBlock = alt.elseBlock?.();
|
|
11798
11796
|
if (elseBlock) {
|
|
11799
|
-
sections.push({
|
|
11797
|
+
sections.push({
|
|
11798
|
+
label: "else",
|
|
11799
|
+
guardKeyword: "else",
|
|
11800
|
+
blockNode: elseBlock.braceBlock?.()?.block?.()
|
|
11801
|
+
});
|
|
11800
11802
|
}
|
|
11801
11803
|
const ifCondition = alt.ifBlock?.()?.parExpr?.()?.condition?.();
|
|
11802
11804
|
const firstLabel = ifCondition?.getFormattedText?.() || "";
|
|
@@ -11807,15 +11809,24 @@ function extractFragmentInfo(stat) {
|
|
|
11807
11809
|
const sections = [];
|
|
11808
11810
|
const tryBlock = tcf.tryBlock?.();
|
|
11809
11811
|
if (tryBlock) {
|
|
11810
|
-
sections.push({
|
|
11812
|
+
sections.push({
|
|
11813
|
+
label: "Try",
|
|
11814
|
+
blockNode: tryBlock.braceBlock?.()?.block?.()
|
|
11815
|
+
});
|
|
11811
11816
|
}
|
|
11812
11817
|
for (const catchBlock of tcf.catchBlock?.() || []) {
|
|
11813
11818
|
const exception = catchBlock.invocation?.()?.parameters?.()?.getFormattedText?.() || "";
|
|
11814
|
-
sections.push({
|
|
11819
|
+
sections.push({
|
|
11820
|
+
label: `catch ${exception}`,
|
|
11821
|
+
blockNode: catchBlock.braceBlock?.()?.block?.()
|
|
11822
|
+
});
|
|
11815
11823
|
}
|
|
11816
11824
|
const finallyBlock = tcf.finallyBlock?.();
|
|
11817
11825
|
if (finallyBlock) {
|
|
11818
|
-
sections.push({
|
|
11826
|
+
sections.push({
|
|
11827
|
+
label: "finally",
|
|
11828
|
+
blockNode: finallyBlock.braceBlock?.()?.block?.()
|
|
11829
|
+
});
|
|
11819
11830
|
}
|
|
11820
11831
|
return { fragmentKind: "tcf", label: "", sections };
|
|
11821
11832
|
}
|
|
@@ -11827,14 +11838,27 @@ function extractFragmentInfo(stat) {
|
|
|
11827
11838
|
return null;
|
|
11828
11839
|
}
|
|
11829
11840
|
function blockLength(block) {
|
|
11830
|
-
return (block?.stat?.() || []).filter(
|
|
11841
|
+
return (block?.stat?.() || []).filter(
|
|
11842
|
+
(stat) => !!createStatementKey(stat)
|
|
11843
|
+
).length;
|
|
11831
11844
|
}
|
|
11832
11845
|
function walkFragmentBlocks(stat, origin, results, activeOccurrences, parentNumber, depth) {
|
|
11833
|
-
for (const kind of
|
|
11846
|
+
for (const kind of SINGLE_BLOCK_FRAGMENT_KINDS) {
|
|
11834
11847
|
const frag = stat[kind]?.();
|
|
11835
11848
|
if (frag) {
|
|
11836
11849
|
const block = frag.braceBlock?.()?.block?.();
|
|
11837
|
-
if (block)
|
|
11850
|
+
if (block)
|
|
11851
|
+
results.push(
|
|
11852
|
+
...walkBlock(
|
|
11853
|
+
block,
|
|
11854
|
+
origin,
|
|
11855
|
+
activeOccurrences,
|
|
11856
|
+
parentNumber,
|
|
11857
|
+
depth + 1,
|
|
11858
|
+
null,
|
|
11859
|
+
0
|
|
11860
|
+
)
|
|
11861
|
+
);
|
|
11838
11862
|
return;
|
|
11839
11863
|
}
|
|
11840
11864
|
}
|
|
@@ -11845,14 +11869,32 @@ function walkFragmentBlocks(stat, origin, results, activeOccurrences, parentNumb
|
|
|
11845
11869
|
if (ifBlock) {
|
|
11846
11870
|
const block = ifBlock.braceBlock?.()?.block?.();
|
|
11847
11871
|
if (block) {
|
|
11848
|
-
results.push(
|
|
11872
|
+
results.push(
|
|
11873
|
+
...walkBlock(
|
|
11874
|
+
block,
|
|
11875
|
+
origin,
|
|
11876
|
+
activeOccurrences,
|
|
11877
|
+
parentNumber,
|
|
11878
|
+
depth + 1,
|
|
11879
|
+
null,
|
|
11880
|
+
sectionOffset
|
|
11881
|
+
)
|
|
11882
|
+
);
|
|
11849
11883
|
sectionOffset += blockLength(block);
|
|
11850
11884
|
}
|
|
11851
11885
|
}
|
|
11852
11886
|
for (const elseIf of alt.elseIfBlock?.() || []) {
|
|
11853
11887
|
const block = elseIf.braceBlock?.()?.block?.();
|
|
11854
11888
|
if (block) {
|
|
11855
|
-
const sectionStmts = walkBlock(
|
|
11889
|
+
const sectionStmts = walkBlock(
|
|
11890
|
+
block,
|
|
11891
|
+
origin,
|
|
11892
|
+
activeOccurrences,
|
|
11893
|
+
parentNumber,
|
|
11894
|
+
depth + 1,
|
|
11895
|
+
null,
|
|
11896
|
+
sectionOffset
|
|
11897
|
+
);
|
|
11856
11898
|
if (sectionStmts.length > 0) sectionStmts[0].sectionReset = true;
|
|
11857
11899
|
results.push(...sectionStmts);
|
|
11858
11900
|
sectionOffset += blockLength(block);
|
|
@@ -11862,7 +11904,15 @@ function walkFragmentBlocks(stat, origin, results, activeOccurrences, parentNumb
|
|
|
11862
11904
|
if (elseBlock) {
|
|
11863
11905
|
const block = elseBlock.braceBlock?.()?.block?.();
|
|
11864
11906
|
if (block) {
|
|
11865
|
-
const sectionStmts = walkBlock(
|
|
11907
|
+
const sectionStmts = walkBlock(
|
|
11908
|
+
block,
|
|
11909
|
+
origin,
|
|
11910
|
+
activeOccurrences,
|
|
11911
|
+
parentNumber,
|
|
11912
|
+
depth + 1,
|
|
11913
|
+
null,
|
|
11914
|
+
sectionOffset
|
|
11915
|
+
);
|
|
11866
11916
|
if (sectionStmts.length > 0) sectionStmts[0].sectionReset = true;
|
|
11867
11917
|
results.push(...sectionStmts);
|
|
11868
11918
|
}
|
|
@@ -11876,14 +11926,32 @@ function walkFragmentBlocks(stat, origin, results, activeOccurrences, parentNumb
|
|
|
11876
11926
|
if (tryBlock) {
|
|
11877
11927
|
const block = tryBlock.braceBlock?.()?.block?.();
|
|
11878
11928
|
if (block) {
|
|
11879
|
-
results.push(
|
|
11929
|
+
results.push(
|
|
11930
|
+
...walkBlock(
|
|
11931
|
+
block,
|
|
11932
|
+
origin,
|
|
11933
|
+
activeOccurrences,
|
|
11934
|
+
parentNumber,
|
|
11935
|
+
depth + 1,
|
|
11936
|
+
null,
|
|
11937
|
+
sectionOffset
|
|
11938
|
+
)
|
|
11939
|
+
);
|
|
11880
11940
|
sectionOffset += blockLength(block);
|
|
11881
11941
|
}
|
|
11882
11942
|
}
|
|
11883
11943
|
for (const catchBlock of tcf.catchBlock?.() || []) {
|
|
11884
11944
|
const block = catchBlock.braceBlock?.()?.block?.();
|
|
11885
11945
|
if (block) {
|
|
11886
|
-
const sectionStmts = walkBlock(
|
|
11946
|
+
const sectionStmts = walkBlock(
|
|
11947
|
+
block,
|
|
11948
|
+
origin,
|
|
11949
|
+
activeOccurrences,
|
|
11950
|
+
parentNumber,
|
|
11951
|
+
depth + 1,
|
|
11952
|
+
null,
|
|
11953
|
+
sectionOffset
|
|
11954
|
+
);
|
|
11887
11955
|
if (sectionStmts.length > 0) sectionStmts[0].sectionReset = true;
|
|
11888
11956
|
results.push(...sectionStmts);
|
|
11889
11957
|
sectionOffset += blockLength(block);
|
|
@@ -11893,7 +11961,15 @@ function walkFragmentBlocks(stat, origin, results, activeOccurrences, parentNumb
|
|
|
11893
11961
|
if (finallyBlock) {
|
|
11894
11962
|
const block = finallyBlock.braceBlock?.()?.block?.();
|
|
11895
11963
|
if (block) {
|
|
11896
|
-
const sectionStmts = walkBlock(
|
|
11964
|
+
const sectionStmts = walkBlock(
|
|
11965
|
+
block,
|
|
11966
|
+
origin,
|
|
11967
|
+
activeOccurrences,
|
|
11968
|
+
parentNumber,
|
|
11969
|
+
depth + 1,
|
|
11970
|
+
null,
|
|
11971
|
+
sectionOffset
|
|
11972
|
+
);
|
|
11897
11973
|
if (sectionStmts.length > 0) sectionStmts[0].sectionReset = true;
|
|
11898
11974
|
results.push(...sectionStmts);
|
|
11899
11975
|
}
|
|
@@ -11925,10 +12001,6 @@ function computeReturnDebt(statements, verticalCoordinates, returnHeight) {
|
|
|
11925
12001
|
if (ownerKey && occInnerDebt > 0) {
|
|
11926
12002
|
result.set(`inner:${ownerKey}`, occInnerDebt);
|
|
11927
12003
|
}
|
|
11928
|
-
const hasMixed = hasNonReturnChild[maxDepth] || false;
|
|
11929
|
-
if (ownerKey && hasMixed) {
|
|
11930
|
-
result.set(`mixed:${ownerKey}`, 1);
|
|
11931
|
-
}
|
|
11932
12004
|
const hasAssign = blockHasAssignment[maxDepth] || false;
|
|
11933
12005
|
debtByDepth.pop();
|
|
11934
12006
|
directDebtByDepth.pop();
|
|
@@ -12012,10 +12084,6 @@ function computeReturnDebt(statements, verticalCoordinates, returnHeight) {
|
|
|
12012
12084
|
if (ownerKey && occInnerDebtEnd > 0) {
|
|
12013
12085
|
result.set(`inner:${ownerKey}`, occInnerDebtEnd);
|
|
12014
12086
|
}
|
|
12015
|
-
const hasMixedEnd = hasNonReturnChild[maxDepth] || false;
|
|
12016
|
-
if (ownerKey && hasMixedEnd) {
|
|
12017
|
-
result.set(`mixed:${ownerKey}`, 1);
|
|
12018
|
-
}
|
|
12019
12087
|
const hasAssignEnd = blockHasAssignment[maxDepth] || false;
|
|
12020
12088
|
debtByDepth.pop();
|
|
12021
12089
|
directDebtByDepth.pop();
|
|
@@ -12031,7 +12099,6 @@ function computeReturnDebt(statements, verticalCoordinates, returnHeight) {
|
|
|
12031
12099
|
}
|
|
12032
12100
|
}
|
|
12033
12101
|
}
|
|
12034
|
-
result.set("__totalDebt__", debtByDepth[0] || 0);
|
|
12035
12102
|
return result;
|
|
12036
12103
|
}
|
|
12037
12104
|
|
|
@@ -12059,7 +12126,11 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12059
12126
|
}
|
|
12060
12127
|
}
|
|
12061
12128
|
const RETURN_VISUAL_HEIGHT = 16;
|
|
12062
|
-
const adjustMap = computeReturnDebt(
|
|
12129
|
+
const adjustMap = computeReturnDebt(
|
|
12130
|
+
statements,
|
|
12131
|
+
verticalCoordinates,
|
|
12132
|
+
RETURN_VISUAL_HEIGHT
|
|
12133
|
+
);
|
|
12063
12134
|
for (const info of statements) {
|
|
12064
12135
|
const coord = verticalCoordinates.getStatementCoordinate(info.key);
|
|
12065
12136
|
if (!coord) continue;
|
|
@@ -12075,7 +12146,7 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12075
12146
|
const tX = coordinates.getPosition(info.to);
|
|
12076
12147
|
if (fX > tX) commentParticipant = info.to;
|
|
12077
12148
|
}
|
|
12078
|
-
const commentX = commentParticipant ?
|
|
12149
|
+
const commentX = commentParticipant ? coordinates.getPosition(commentParticipant) : 10;
|
|
12079
12150
|
const COMMENT_FONT_ASCENT = 15;
|
|
12080
12151
|
const commentXOffset = info.senderOccurrenceDepth >= 1 ? OCCURRENCE_BAR_SIDE_WIDTH + 1 : 1;
|
|
12081
12152
|
const codeSpanPadding = commentObj.text.trimStart().startsWith("`") ? 2 : 0;
|
|
@@ -12088,30 +12159,31 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12088
12159
|
}
|
|
12089
12160
|
const messageStyle = commentObj ? cssToSvgStyle(commentObj.messageStyle) : void 0;
|
|
12090
12161
|
if (info.kind === "sync" || info.kind === "async") {
|
|
12091
|
-
let fromX =
|
|
12092
|
-
const toX =
|
|
12093
|
-
const
|
|
12094
|
-
const
|
|
12162
|
+
let fromX = coordinates.getPosition(info.from);
|
|
12163
|
+
const toX = coordinates.getPosition(info.to);
|
|
12164
|
+
const isAsync = info.kind === "async";
|
|
12165
|
+
const messageHeight = !info.isSelf ? MESSAGE_HEIGHT : isAsync ? ASYNC_SELF_MESSAGE_HEIGHT : SELF_MESSAGE_HEIGHT;
|
|
12166
|
+
const msgCommentHeight = commentObj?.text ? (info.comment?.trim().split("\n").length || 0) * COMMENT_LINE_HEIGHT : 0;
|
|
12095
12167
|
const messageY = coord.top + adjust + msgCommentHeight + messageHeight - 0.5;
|
|
12096
12168
|
if (info.senderOccurrenceDepth >= 1 && !info.isSelf) {
|
|
12097
|
-
const
|
|
12098
|
-
const occOffset = depth * OCCURRENCE_BAR_SIDE_WIDTH;
|
|
12169
|
+
const senderAnchor = new Anchor2(fromX, info.senderOccurrenceDepth);
|
|
12099
12170
|
const isLTR = fromX < toX;
|
|
12100
|
-
fromX = isLTR ?
|
|
12171
|
+
fromX = isLTR ? senderAnchor.rightEdgeOfRightWall() : senderAnchor.leftEdgeOfRightWall();
|
|
12101
12172
|
}
|
|
12102
12173
|
const targetDepth = info.targetOccurrenceDepth || (info.targetHasOccurrence ? 1 : 0);
|
|
12103
12174
|
const nestingOffset = targetDepth * OCCURRENCE_BAR_SIDE_WIDTH;
|
|
12104
12175
|
if (info.isSelf) {
|
|
12105
|
-
const isAsync = info.kind === "async";
|
|
12106
12176
|
const selfYOffset = 0;
|
|
12107
12177
|
const selfWidth = isAsync ? 28 : OCCURRENCE_WIDTH;
|
|
12108
|
-
const
|
|
12109
|
-
|
|
12178
|
+
const selfX = new Anchor2(
|
|
12179
|
+
fromX,
|
|
12180
|
+
info.senderOccurrenceDepth
|
|
12181
|
+
).rightEdgeOfRightWall();
|
|
12110
12182
|
selfCalls.push({
|
|
12111
12183
|
x: selfX,
|
|
12112
12184
|
y: coord.top + selfYOffset + msgCommentHeight,
|
|
12113
12185
|
width: selfWidth,
|
|
12114
|
-
height:
|
|
12186
|
+
height: messageHeight,
|
|
12115
12187
|
label: info.label,
|
|
12116
12188
|
arrowStyle: isAsync ? "open" : "solid",
|
|
12117
12189
|
number: info.number,
|
|
@@ -12183,16 +12255,15 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12183
12255
|
continue;
|
|
12184
12256
|
}
|
|
12185
12257
|
if (info.kind === "creation") {
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
const toX = snapX$1(coordinates.getPosition(info.to));
|
|
12258
|
+
let fromX = coordinates.getPosition(info.from);
|
|
12259
|
+
const toX = coordinates.getPosition(info.to);
|
|
12189
12260
|
if (info.senderOccurrenceDepth >= 1) {
|
|
12190
12261
|
const occOffset = OCCURRENCE_BAR_SIDE_WIDTH + (info.senderOccurrenceDepth - 1) * OCCURRENCE_BAR_SIDE_WIDTH;
|
|
12191
12262
|
const isLTR = fromX < toX;
|
|
12192
12263
|
fromX = isLTR ? fromX + occOffset : fromX - occOffset;
|
|
12193
12264
|
}
|
|
12194
12265
|
const targetParticipant = participants.find((p) => p.name === info.to);
|
|
12195
|
-
const messageY = targetParticipant ? targetParticipant.y + PARTICIPANT_VISUAL_HEIGHT / 2 : coord.top +
|
|
12266
|
+
const messageY = targetParticipant ? targetParticipant.y + PARTICIPANT_VISUAL_HEIGHT / 2 : coord.top + CREATION_MESSAGE_HEIGHT / 2;
|
|
12196
12267
|
if (targetParticipant) {
|
|
12197
12268
|
creations.push({
|
|
12198
12269
|
participant: targetParticipant,
|
|
@@ -12211,7 +12282,7 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12211
12282
|
});
|
|
12212
12283
|
}
|
|
12213
12284
|
const occX = toX - OCCURRENCE_BAR_SIDE_WIDTH;
|
|
12214
|
-
const occY = targetParticipant ? targetParticipant.y + PARTICIPANT_VISUAL_HEIGHT - 2 : coord.top +
|
|
12285
|
+
const occY = targetParticipant ? targetParticipant.y + PARTICIPANT_VISUAL_HEIGHT - 2 : coord.top + CREATION_MESSAGE_HEIGHT - 2;
|
|
12215
12286
|
const occHeight = Math.max(
|
|
12216
12287
|
coord.top + coord.height - occY,
|
|
12217
12288
|
OCCURRENCE_EMPTY_HEIGHT
|
|
@@ -12228,7 +12299,7 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12228
12299
|
const creationAssign = creationCtx?.Assignment?.();
|
|
12229
12300
|
if (creationAssign?.assignee) {
|
|
12230
12301
|
const occBottom = occY + occHeight - 2;
|
|
12231
|
-
const rawFromX =
|
|
12302
|
+
const rawFromX = coordinates.getPosition(info.from);
|
|
12232
12303
|
const isLTR = rawFromX < toX;
|
|
12233
12304
|
const occHalf = OCCURRENCE_BAR_SIDE_WIDTH;
|
|
12234
12305
|
const senderNest = Math.max(info.senderOccurrenceDepth - 1, 0) * OCCURRENCE_BAR_SIDE_WIDTH;
|
|
@@ -12250,7 +12321,7 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12250
12321
|
}
|
|
12251
12322
|
if (info.kind === "fragment" && info.fragmentKind) {
|
|
12252
12323
|
const adjustedCoord = { top: coord.top + adjust, height: coord.height };
|
|
12253
|
-
const fragmentCommentHeight = commentObj?.text ? (info.comment?.trim().split("\n").length || 0) *
|
|
12324
|
+
const fragmentCommentHeight = commentObj?.text ? (info.comment?.trim().split("\n").length || 0) * COMMENT_LINE_HEIGHT : 0;
|
|
12254
12325
|
const fragmentResult = buildFragmentGeometry(
|
|
12255
12326
|
info,
|
|
12256
12327
|
adjustedCoord,
|
|
@@ -12272,26 +12343,14 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12272
12343
|
continue;
|
|
12273
12344
|
}
|
|
12274
12345
|
if (info.kind === "return") {
|
|
12275
|
-
const rawFromX =
|
|
12276
|
-
const rawToX =
|
|
12346
|
+
const rawFromX = coordinates.getPosition(info.from);
|
|
12347
|
+
const rawToX = coordinates.getPosition(info.to);
|
|
12277
12348
|
const isReverse = rawToX < rawFromX;
|
|
12278
|
-
const
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
fromX = rawFromX + OCCURRENCE_BAR_SIDE_WIDTH * fromLayers + 1;
|
|
12284
|
-
}
|
|
12285
|
-
const toLayers = info.targetOccurrenceDepth || 0;
|
|
12286
|
-
let toX;
|
|
12287
|
-
if (isReverse) {
|
|
12288
|
-
toX = rawToX + OCCURRENCE_BAR_SIDE_WIDTH * toLayers;
|
|
12289
|
-
} else {
|
|
12290
|
-
toX = toLayers === 0 ? rawToX : (toLayers <= 1 ? rawToX : rawToX + OCCURRENCE_BAR_SIDE_WIDTH * (toLayers - 1)) - OCCURRENCE_BAR_SIDE_WIDTH;
|
|
12291
|
-
}
|
|
12292
|
-
if (isReverse) {
|
|
12293
|
-
toX += LIFELINE_WIDTH;
|
|
12294
|
-
}
|
|
12349
|
+
const fromAnchor = new Anchor2(rawFromX, info.senderOccurrenceDepth);
|
|
12350
|
+
const toAnchor = new Anchor2(rawToX, info.targetOccurrenceDepth || 0);
|
|
12351
|
+
const RETURN_STROKE_OFFSET = 1;
|
|
12352
|
+
const fromX = isReverse ? fromAnchor.leftEdgeOfRightWall() : fromAnchor.rightEdgeOfRightWall() + RETURN_STROKE_OFFSET;
|
|
12353
|
+
const toX = isReverse ? toAnchor.rightEdgeOfRightWall() + LIFELINE_WIDTH : toAnchor.leftEdgeOfRightWall();
|
|
12295
12354
|
const returnOffset = coord.height === 0 ? 16.5 : 15.5;
|
|
12296
12355
|
const returnY = coord.top + adjust + returnOffset;
|
|
12297
12356
|
returns.push({
|
|
@@ -12313,7 +12372,9 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12313
12372
|
y: coord.top + adjust + coord.height / 2,
|
|
12314
12373
|
width: diagramWidth,
|
|
12315
12374
|
label: info.label,
|
|
12316
|
-
labelWidth: measureSvgFragmentLabelWidth(
|
|
12375
|
+
labelWidth: measureSvgFragmentLabelWidth(
|
|
12376
|
+
resolveEmojiInText(cleanLabel)
|
|
12377
|
+
)
|
|
12317
12378
|
});
|
|
12318
12379
|
continue;
|
|
12319
12380
|
}
|
|
@@ -12336,11 +12397,23 @@ function buildMessages(rootContext, coordinates, verticalCoordinates, participan
|
|
|
12336
12397
|
}
|
|
12337
12398
|
}
|
|
12338
12399
|
if (depth > 0) {
|
|
12339
|
-
occurrences[i] = {
|
|
12400
|
+
occurrences[i] = {
|
|
12401
|
+
...inner,
|
|
12402
|
+
x: inner.x + depth * OCCURRENCE_BAR_SIDE_WIDTH
|
|
12403
|
+
};
|
|
12340
12404
|
}
|
|
12341
12405
|
}
|
|
12342
|
-
|
|
12343
|
-
|
|
12406
|
+
return {
|
|
12407
|
+
messages,
|
|
12408
|
+
selfCalls,
|
|
12409
|
+
occurrences,
|
|
12410
|
+
creations,
|
|
12411
|
+
fragments,
|
|
12412
|
+
returns,
|
|
12413
|
+
dividers,
|
|
12414
|
+
comments,
|
|
12415
|
+
maxReturnBottom
|
|
12416
|
+
};
|
|
12344
12417
|
}
|
|
12345
12418
|
|
|
12346
12419
|
function buildGeometry(input) {
|
|
@@ -12364,13 +12437,36 @@ function buildGeometry(input) {
|
|
|
12364
12437
|
participants,
|
|
12365
12438
|
measureText
|
|
12366
12439
|
);
|
|
12367
|
-
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
|
+
}
|
|
12368
12462
|
let maxOccBottom = 0;
|
|
12369
12463
|
let maxOtherY = 0;
|
|
12370
|
-
for (const o of occurrences)
|
|
12464
|
+
for (const o of occurrences)
|
|
12465
|
+
maxOccBottom = Math.max(maxOccBottom, o.y + o.height);
|
|
12371
12466
|
for (const m of messages) maxOtherY = Math.max(maxOtherY, m.y);
|
|
12372
12467
|
for (const s of selfCalls) maxOtherY = Math.max(maxOtherY, s.y + s.height);
|
|
12373
|
-
for (const c of creations)
|
|
12468
|
+
for (const c of creations)
|
|
12469
|
+
maxOtherY = Math.max(maxOtherY, c.message.y + PARTICIPANT_VISUAL_HEIGHT);
|
|
12374
12470
|
for (const f of fragments) maxOtherY = Math.max(maxOtherY, f.y + f.height);
|
|
12375
12471
|
for (const d of dividers) maxOtherY = Math.max(maxOtherY, d.y);
|
|
12376
12472
|
const diagramHeight = Math.max(
|
|
@@ -12380,21 +12476,8 @@ function buildGeometry(input) {
|
|
|
12380
12476
|
buildResult.maxReturnBottom
|
|
12381
12477
|
);
|
|
12382
12478
|
const lifelineBottom = diagramHeight + PARTICIPANT_VISUAL_HEIGHT - 28;
|
|
12383
|
-
const lifelines = buildLifelines(
|
|
12384
|
-
|
|
12385
|
-
lifelineBottom
|
|
12386
|
-
);
|
|
12387
|
-
const localParticipants = getLocalParticipantNames(rootContext);
|
|
12388
|
-
const orderedNames = coordinates.orderedParticipantNames();
|
|
12389
|
-
const leftParticipant = orderedNames.find((p) => localParticipants.includes(p)) || "";
|
|
12390
|
-
const rightParticipant = orderedNames.slice().reverse().find((p) => localParticipants.includes(p)) || "";
|
|
12391
|
-
const participantWidth = coordinates.distance(leftParticipant, rightParticipant) + coordinates.half(leftParticipant) + coordinates.half(rightParticipant);
|
|
12392
|
-
const selfMessages = AllMessages(rootContext).filter((m) => m.from === m.to);
|
|
12393
|
-
const extraWidths = selfMessages.map(
|
|
12394
|
-
(m) => coordinates.getMessageWidth(m) - coordinates.distance(m.from || _STARTER_, rightParticipant) - coordinates.half(rightParticipant)
|
|
12395
|
-
);
|
|
12396
|
-
const extraWidthDueToSelfMessage = Math.max(0, ...extraWidths);
|
|
12397
|
-
let diagramWidth = Math.max(participantWidth, FRAGMENT_MIN_WIDTH) + extraWidthDueToSelfMessage;
|
|
12479
|
+
const lifelines = buildLifelines(participants, lifelineBottom);
|
|
12480
|
+
let diagramWidth = TotalWidth(rootContext, coordinates) - frameBorder.left - frameBorder.right;
|
|
12398
12481
|
if (measureText) {
|
|
12399
12482
|
const LABEL_PAD = 10;
|
|
12400
12483
|
for (const msg of messages) {
|
|
@@ -12445,6 +12528,42 @@ function buildGeometry(input) {
|
|
|
12445
12528
|
f.width = targetRight - f.x;
|
|
12446
12529
|
}
|
|
12447
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
|
+
}
|
|
12448
12567
|
const groups = buildGroups(participants, diagramHeight);
|
|
12449
12568
|
return {
|
|
12450
12569
|
width: diagramWidth,
|
|
@@ -12543,7 +12662,7 @@ function styleToAttr(style) {
|
|
|
12543
12662
|
return Object.entries(style).map(([k, v]) => `${esc(k)}: ${esc(v)}`).join("; ");
|
|
12544
12663
|
}
|
|
12545
12664
|
|
|
12546
|
-
const STROKE_WIDTH$1 =
|
|
12665
|
+
const STROKE_WIDTH$1 = 1;
|
|
12547
12666
|
const HALF_STROKE$1 = STROKE_WIDTH$1 / 2;
|
|
12548
12667
|
const ICON_SIZE = 24;
|
|
12549
12668
|
const ICON_MARGIN_RIGHT = 4;
|
|
@@ -12551,12 +12670,12 @@ const ICON_PAINT_OFFSET_X = 4;
|
|
|
12551
12670
|
const LABEL_PAD_LEFT = 8;
|
|
12552
12671
|
const LABEL_HORIZONTAL_PADDING = 16;
|
|
12553
12672
|
const STEREOTYPE_VERTICAL_OFFSET = 8;
|
|
12554
|
-
const STEREOTYPE_FONT_SIZE =
|
|
12673
|
+
const STEREOTYPE_FONT_SIZE = 14;
|
|
12555
12674
|
const BOUNDARY_ICON_VERTICAL_TWEAK = 2.75;
|
|
12556
12675
|
const PARTICIPANT_TEXT_FILL = "#222";
|
|
12557
12676
|
function renderParticipant(p) {
|
|
12558
12677
|
if (p.isStarter) return renderStarterParticipant(p);
|
|
12559
|
-
const rx =
|
|
12678
|
+
const rx = 4 - HALF_STROKE$1;
|
|
12560
12679
|
const x = p.x - p.width / 2 + HALF_STROKE$1;
|
|
12561
12680
|
const rectY = p.y + HALF_STROKE$1;
|
|
12562
12681
|
const rectW = p.width - STROKE_WIDTH$1;
|
|
@@ -12625,7 +12744,7 @@ function renderStarterParticipant(p) {
|
|
|
12625
12744
|
if (!icon) {
|
|
12626
12745
|
return "";
|
|
12627
12746
|
}
|
|
12628
|
-
const rx =
|
|
12747
|
+
const rx = 4 - HALF_STROKE$1;
|
|
12629
12748
|
const boxX = p.x - p.width / 2 + HALF_STROKE$1;
|
|
12630
12749
|
const rectY = p.y + HALF_STROKE$1;
|
|
12631
12750
|
const rectW = p.width - STROKE_WIDTH$1;
|
|
@@ -12665,21 +12784,31 @@ function renderLifeline(l) {
|
|
|
12665
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"/>`;
|
|
12666
12785
|
}
|
|
12667
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
|
+
|
|
12668
12796
|
function renderMessage(m) {
|
|
12669
12797
|
const isLTR = m.fromX < m.toX;
|
|
12670
12798
|
const fromX = isLTR ? m.fromX + 1 : m.fromX;
|
|
12671
12799
|
const toX = isLTR ? m.toX : m.toX + 1;
|
|
12672
12800
|
const direction = Math.sign(m.toX - m.fromX);
|
|
12673
12801
|
const labelX = (m.fromX + m.toX) / 2 - direction * 3.5 + 0.5;
|
|
12674
|
-
const labelY = m.y -
|
|
12802
|
+
const labelY = m.y - 2.5;
|
|
12675
12803
|
const dashAttr = m.arrowStyle === "dashed" ? ' stroke-dasharray="6,4"' : "";
|
|
12676
12804
|
const styleAttr = m.style ? ` style="${styleToAttr(m.style)}"` : "";
|
|
12677
12805
|
const numberX = Math.min(fromX, toX) - 4;
|
|
12678
|
-
const numberSvg = m.number
|
|
12679
|
-
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;
|
|
12680
12809
|
return `<g class="message">
|
|
12681
12810
|
<line x1="${fromX}" y1="${lineY}" x2="${toX}" y2="${lineY}" class="message-line"${dashAttr}/>
|
|
12682
|
-
${renderArrowHead(toX,
|
|
12811
|
+
${renderArrowHead(toX, headCenterY, m.isReverse, m.arrowStyle)}
|
|
12683
12812
|
<text x="${labelX}" y="${labelY}" text-anchor="middle" class="message-label"${styleAttr}>${esc(resolveEmojiInText(m.label))}</text>
|
|
12684
12813
|
${numberSvg}
|
|
12685
12814
|
</g>`;
|
|
@@ -12690,7 +12819,7 @@ function renderSelfCall(s) {
|
|
|
12690
12819
|
const labelX = x1 + 6;
|
|
12691
12820
|
const labelY = s.y + (isAsync ? 15 : 12);
|
|
12692
12821
|
const numberY = s.y + 12;
|
|
12693
|
-
const numberSvg = s.number
|
|
12822
|
+
const numberSvg = renderMessageNumber(s.number, x1 - 3, numberY);
|
|
12694
12823
|
const svgX = x1 + 1;
|
|
12695
12824
|
const svgY = s.y + (isAsync ? 20 : 14);
|
|
12696
12825
|
const pathEnd = isAsync ? "L1,15" : "L14,15";
|
|
@@ -12699,11 +12828,11 @@ function renderSelfCall(s) {
|
|
|
12699
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";
|
|
12700
12829
|
return `<g class="message self-call">
|
|
12701
12830
|
<svg x="${svgX}" y="${svgY}" width="30" height="24">
|
|
12702
|
-
<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"/>
|
|
12703
12832
|
<g transform="translate(${arrowTx}, 10)">
|
|
12704
12833
|
<svg height="10" width="7" viewBox="0 0 7 9">
|
|
12705
12834
|
<g transform="scale(-1, 1) translate(-7, 0)">
|
|
12706
|
-
<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"/>
|
|
12707
12836
|
</g>
|
|
12708
12837
|
</svg>
|
|
12709
12838
|
</g>
|
|
@@ -12721,96 +12850,122 @@ function renderArrowHead(tipX, tipY, pointsLeft, style) {
|
|
|
12721
12850
|
const rtlTransform = pointsLeft ? ' transform="scale(-1, 1) translate(-7, 0)"' : "";
|
|
12722
12851
|
return `<svg x="${svgX}" y="${svgY}" width="7" height="10" viewBox="0 0 7 9" overflow="visible" class="arrow-head${isFilled ? "" : " arrow-open"}">
|
|
12723
12852
|
<g${rtlTransform}>
|
|
12724
|
-
<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}"/>
|
|
12725
12854
|
</g>
|
|
12726
12855
|
</svg>`;
|
|
12727
12856
|
}
|
|
12728
12857
|
|
|
12729
12858
|
function renderOccurrence(o) {
|
|
12730
|
-
const strokeHalf =
|
|
12859
|
+
const strokeHalf = 0.5;
|
|
12731
12860
|
const rx = o.x + strokeHalf;
|
|
12732
|
-
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)}"/>`;
|
|
12733
12862
|
}
|
|
12734
12863
|
|
|
12735
|
-
const HEADER_HEIGHT =
|
|
12736
|
-
const BRACKET_WIDTH = 3.89;
|
|
12864
|
+
const HEADER_HEIGHT = DEFAULT_LAYOUT_METRICS.fragmentHeaderHeight;
|
|
12737
12865
|
const TEXT_PAD_X = 4;
|
|
12866
|
+
const NUMBER_PAD_X = 4;
|
|
12738
12867
|
const STROKE_WIDTH = 1;
|
|
12739
12868
|
const HALF_STROKE = STROKE_WIDTH / 2;
|
|
12740
12869
|
function renderFragment(f) {
|
|
12741
12870
|
const parts = [];
|
|
12871
|
+
const kindLabel = f.headerLabel ?? fragmentHeaderLabel(f.kind);
|
|
12872
|
+
const width = Math.max(f.width, fragmentHeaderWidth(kindLabel, f.number));
|
|
12742
12873
|
parts.push(
|
|
12743
|
-
`<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"/>`
|
|
12744
12875
|
);
|
|
12745
12876
|
const headerX = f.x + 1;
|
|
12746
12877
|
const headerY = f.headerY;
|
|
12747
|
-
const headerW =
|
|
12878
|
+
const headerW = width - 2;
|
|
12748
12879
|
parts.push(
|
|
12749
|
-
`<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)"'}/>`
|
|
12750
12881
|
);
|
|
12751
|
-
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;
|
|
12752
12894
|
const iconY = headerY;
|
|
12753
12895
|
parts.push(getFragmentIcon(f.kind, iconX, iconY));
|
|
12754
|
-
const
|
|
12755
|
-
const labelX = headerX + 26;
|
|
12896
|
+
const labelX = headerX + 26 + numberInset;
|
|
12756
12897
|
parts.push(
|
|
12757
12898
|
`<text x="${labelX}" y="${headerY + HEADER_HEIGHT / 2 - 0.5}" dominant-baseline="central" class="fragment-label">${esc(kindLabel)}</text>`
|
|
12758
12899
|
);
|
|
12759
|
-
if (f.
|
|
12900
|
+
if (f.label) {
|
|
12760
12901
|
parts.push(
|
|
12761
|
-
|
|
12902
|
+
renderGuardRow(
|
|
12903
|
+
headerX,
|
|
12904
|
+
headerY + HEADER_HEIGHT,
|
|
12905
|
+
f.label,
|
|
12906
|
+
fragmentGuardKeyword(f.kind, f.guardKeyword)
|
|
12907
|
+
)
|
|
12762
12908
|
);
|
|
12763
12909
|
}
|
|
12764
|
-
if (f.label) {
|
|
12765
|
-
const condY = headerY + HEADER_HEIGHT + 15;
|
|
12766
|
-
parts.push(renderBracketedLabel(headerX, condY, f.label, f.labelWidth, "fragment-condition"));
|
|
12767
|
-
}
|
|
12768
12910
|
if (f.sections.length > 1) {
|
|
12769
12911
|
for (let i = 1; i < f.sections.length; i++) {
|
|
12770
12912
|
const section = f.sections[i];
|
|
12771
12913
|
const lineY = section.y;
|
|
12772
12914
|
const separatorY = lineY + HALF_STROKE;
|
|
12773
12915
|
const sepX1 = f.x + 1 + (section.contentInsetLeft ?? 0);
|
|
12774
|
-
const sepX2 = f.x +
|
|
12916
|
+
const sepX2 = f.x + width - 1;
|
|
12775
12917
|
parts.push(
|
|
12776
12918
|
`<line x1="${sepX1}" y1="${separatorY}" x2="${sepX2}" y2="${separatorY}" class="fragment-separator"/>`
|
|
12777
12919
|
);
|
|
12778
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
|
+
}
|
|
12779
12932
|
const labelY = lineY + 16;
|
|
12780
12933
|
const isFinally = section.label.startsWith("finally");
|
|
12781
12934
|
const isBracketed = !!section.innerLabel && section.label !== "[else]";
|
|
12782
12935
|
if (isBracketed) {
|
|
12783
12936
|
const labelX2 = f.x + 1;
|
|
12784
|
-
parts.push(
|
|
12785
|
-
renderBracketedLabel(labelX2, labelY, section.innerLabel, section.innerLabelWidth, "fragment-section-label")
|
|
12786
|
-
);
|
|
12937
|
+
parts.push(renderGuardRow(labelX2, lineY + 1, section.innerLabel));
|
|
12787
12938
|
continue;
|
|
12788
12939
|
}
|
|
12789
12940
|
const spaceIdx = section.label.indexOf(" ");
|
|
12790
12941
|
if (spaceIdx > 0 && !isFinally) {
|
|
12791
12942
|
const keyword = section.keyword || section.label.substring(0, spaceIdx);
|
|
12792
12943
|
const condition = section.detail || section.label.substring(spaceIdx + 1);
|
|
12793
|
-
const keywordWidth =
|
|
12944
|
+
const keywordWidth = measureTextWithFont(
|
|
12945
|
+
keyword,
|
|
12946
|
+
"14px",
|
|
12947
|
+
"all-small-caps"
|
|
12948
|
+
);
|
|
12794
12949
|
const keywordX = f.x + 5;
|
|
12795
12950
|
const conditionX = keywordX + keywordWidth + TEXT_PAD_X * 2;
|
|
12796
|
-
const bgWidth =
|
|
12951
|
+
const bgWidth = keywordWidth + (section.detailWidth ?? condition.length * 7) + TEXT_PAD_X * 4;
|
|
12797
12952
|
parts.push(
|
|
12798
|
-
`<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>`
|
|
12799
12954
|
);
|
|
12800
12955
|
} else {
|
|
12801
12956
|
const finallyX = f.x + 5;
|
|
12802
12957
|
const finallyY = labelY;
|
|
12803
12958
|
if (section.label === "[else]") {
|
|
12804
12959
|
parts.push(
|
|
12805
|
-
`<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>`
|
|
12806
12961
|
);
|
|
12807
12962
|
continue;
|
|
12808
12963
|
}
|
|
12809
|
-
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;
|
|
12810
12965
|
const bgY = lineY + 1;
|
|
12811
12966
|
const bgHeight = 20;
|
|
12812
12967
|
parts.push(
|
|
12813
|
-
`<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>`
|
|
12814
12969
|
);
|
|
12815
12970
|
}
|
|
12816
12971
|
}
|
|
@@ -12820,33 +12975,11 @@ function renderFragment(f) {
|
|
|
12820
12975
|
${parts.join("\n ")}
|
|
12821
12976
|
</g>`;
|
|
12822
12977
|
}
|
|
12823
|
-
function
|
|
12824
|
-
const
|
|
12825
|
-
const
|
|
12826
|
-
const
|
|
12827
|
-
return `<g
|
|
12828
|
-
}
|
|
12829
|
-
function getKindLabel(kind) {
|
|
12830
|
-
switch (kind) {
|
|
12831
|
-
case "alt":
|
|
12832
|
-
return "Alt";
|
|
12833
|
-
case "loop":
|
|
12834
|
-
return "Loop";
|
|
12835
|
-
case "opt":
|
|
12836
|
-
return "Opt";
|
|
12837
|
-
case "par":
|
|
12838
|
-
return "Par";
|
|
12839
|
-
case "critical":
|
|
12840
|
-
return "Critical";
|
|
12841
|
-
case "section":
|
|
12842
|
-
return "Section";
|
|
12843
|
-
case "tcf":
|
|
12844
|
-
return "Try";
|
|
12845
|
-
case "ref":
|
|
12846
|
-
return "Ref";
|
|
12847
|
-
default:
|
|
12848
|
-
return kind.charAt(0).toUpperCase() + kind.slice(1);
|
|
12849
|
-
}
|
|
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>`;
|
|
12850
12983
|
}
|
|
12851
12984
|
function getFragmentIcon(kind, x, y) {
|
|
12852
12985
|
const w = 20;
|
|
@@ -12930,7 +13063,7 @@ function renderOpenArrow(tipX, tipY, pointsLeft) {
|
|
|
12930
13063
|
const x1 = tipX + dir * w;
|
|
12931
13064
|
const y1 = tipY - halfH;
|
|
12932
13065
|
const y2 = tipY + halfH;
|
|
12933
|
-
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"/>`;
|
|
12934
13067
|
}
|
|
12935
13068
|
|
|
12936
13069
|
function renderReturn(r) {
|
|
@@ -12940,8 +13073,9 @@ function renderReturn(r) {
|
|
|
12940
13073
|
const minX = Math.min(r.fromX, r.toX);
|
|
12941
13074
|
const ARROW_PADDING_HALF = 3.5;
|
|
12942
13075
|
const labelX = minX + Math.abs(r.toX - r.fromX) / 2 + (r.isReverse ? ARROW_PADDING_HALF : -ARROW_PADDING_HALF);
|
|
12943
|
-
const
|
|
12944
|
-
const
|
|
13076
|
+
const snappedY = Math.floor(r.y);
|
|
13077
|
+
const lineY = snappedY + 0.5;
|
|
13078
|
+
const labelY = snappedY - 3;
|
|
12945
13079
|
const arrowTipX = r.toX;
|
|
12946
13080
|
const w = 5.15;
|
|
12947
13081
|
const halfH = 3.25;
|
|
@@ -12950,10 +13084,10 @@ function renderReturn(r) {
|
|
|
12950
13084
|
const ay1 = lineY - halfH;
|
|
12951
13085
|
const ay2 = lineY + halfH;
|
|
12952
13086
|
const numberX = Math.min(r.fromX, r.toX) - 4;
|
|
12953
|
-
const numberSvg = r.number
|
|
13087
|
+
const numberSvg = renderMessageNumber(r.number, numberX, labelY);
|
|
12954
13088
|
return `<g class="return">
|
|
12955
13089
|
<line x1="${r.fromX}" y1="${lineY}" x2="${r.toX}" y2="${lineY}" class="return-line"/>
|
|
12956
|
-
<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"/>
|
|
12957
13091
|
<text x="${labelX}" y="${labelY}" text-anchor="middle" class="return-label">${esc(resolveEmojiInText(r.label))}</text>
|
|
12958
13092
|
${numberSvg}
|
|
12959
13093
|
</g>`;
|
|
@@ -13093,47 +13227,157 @@ function renderGroup(g) {
|
|
|
13093
13227
|
</g>`;
|
|
13094
13228
|
}
|
|
13095
13229
|
|
|
13096
|
-
const
|
|
13097
|
-
const
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13230
|
+
const HELVETICA = "Helvetica, Verdana, serif";
|
|
13231
|
+
const DEFAULT_PALETTE = {
|
|
13232
|
+
frameBorder: "#666",
|
|
13233
|
+
frameBg: "#ffffff",
|
|
13234
|
+
titleText: "#222",
|
|
13235
|
+
participantBg: "#ffffff",
|
|
13236
|
+
participantBorder: "#666",
|
|
13237
|
+
participantText: "#222",
|
|
13238
|
+
messageArrow: "#000",
|
|
13239
|
+
messageText: "#222",
|
|
13240
|
+
occurrenceBg: "#dedede",
|
|
13241
|
+
occurrenceBorder: "#666",
|
|
13242
|
+
fragmentBorder: "#666",
|
|
13243
|
+
fragmentHeaderBg: "#dedede",
|
|
13244
|
+
fragmentHeaderOpacity: "0.498",
|
|
13245
|
+
fragmentText: "#000",
|
|
13246
|
+
fragmentSeparator: "#e5e7eb",
|
|
13247
|
+
commentText: "#333",
|
|
13248
|
+
fontFamily: HELVETICA
|
|
13249
|
+
};
|
|
13250
|
+
const THEME_PALETTES = {
|
|
13251
|
+
"theme-default": DEFAULT_PALETTE,
|
|
13252
|
+
// `.theme-mermaid` sets only base/text/border/occurrence, and hides the
|
|
13253
|
+
// footer — chrome the SVG output does not draw. Every slot it leaves unset
|
|
13254
|
+
// matches theme-default, so the two render identically.
|
|
13255
|
+
"theme-mermaid": DEFAULT_PALETTE,
|
|
13256
|
+
// src/assets/tailwind.css `.theme-clean-light`.
|
|
13257
|
+
"theme-clean-light": {
|
|
13258
|
+
frameBorder: "#e5e9f0",
|
|
13259
|
+
frameBg: "#ffffff",
|
|
13260
|
+
titleText: "#353748",
|
|
13261
|
+
participantBg: "#edf4fe",
|
|
13262
|
+
participantBorder: "#368eff",
|
|
13263
|
+
participantText: "#368eff",
|
|
13264
|
+
messageArrow: "#368eff",
|
|
13265
|
+
messageText: "#368eff",
|
|
13266
|
+
occurrenceBg: "#ffffff",
|
|
13267
|
+
occurrenceBorder: "#368eff",
|
|
13268
|
+
fragmentBorder: "#c8c9c9",
|
|
13269
|
+
fragmentHeaderBg: "#edf4fe",
|
|
13270
|
+
fragmentHeaderOpacity: "1",
|
|
13271
|
+
fragmentText: "#368eff",
|
|
13272
|
+
fragmentSeparator: "#e5e9f0",
|
|
13273
|
+
commentText: "#353748",
|
|
13274
|
+
fontFamily: HELVETICA
|
|
13275
|
+
},
|
|
13276
|
+
// src/assets/tailwind.css `.theme-clean-dark`.
|
|
13277
|
+
"theme-clean-dark": {
|
|
13278
|
+
frameBorder: "#cecfd2",
|
|
13279
|
+
frameBg: "#111628",
|
|
13280
|
+
titleText: "#cecfd2",
|
|
13281
|
+
participantBg: "#5964f2",
|
|
13282
|
+
participantBorder: "#cecfd2",
|
|
13283
|
+
participantText: "#536fff",
|
|
13284
|
+
messageArrow: "#536fff",
|
|
13285
|
+
messageText: "#cecfd2",
|
|
13286
|
+
occurrenceBg: "#5964f2",
|
|
13287
|
+
occurrenceBorder: "#cecfd2",
|
|
13288
|
+
fragmentBorder: "#cecfd2",
|
|
13289
|
+
fragmentHeaderBg: "#5964f2",
|
|
13290
|
+
fragmentHeaderOpacity: "1",
|
|
13291
|
+
fragmentText: "#cecfd2",
|
|
13292
|
+
fragmentSeparator: "#cecfd2",
|
|
13293
|
+
commentText: "#cecfd2",
|
|
13294
|
+
fontFamily: HELVETICA
|
|
13295
|
+
},
|
|
13296
|
+
// src/assets/tailwind.css `.theme-neon`. The DOM theme also swaps in the
|
|
13297
|
+
// MS Sans Serif webfont (`.zenuml .theme-neon`); the SVG names it first and
|
|
13298
|
+
// keeps the Helvetica stack behind it, since a consumer embedding the SVG
|
|
13299
|
+
// will not have that @font-face rule.
|
|
13300
|
+
"theme-neon": {
|
|
13301
|
+
frameBorder: "#60ff33",
|
|
13302
|
+
frameBg: "#000000",
|
|
13303
|
+
titleText: "#8ffc5b",
|
|
13304
|
+
participantBg: "#000000",
|
|
13305
|
+
participantBorder: "#8ffc5b",
|
|
13306
|
+
participantText: "#8ffc5b",
|
|
13307
|
+
messageArrow: "#8ffc5b",
|
|
13308
|
+
messageText: "#8ffc5b",
|
|
13309
|
+
occurrenceBg: "#8ffc5b",
|
|
13310
|
+
occurrenceBorder: "#8ffc5b",
|
|
13311
|
+
fragmentBorder: "#60ff33",
|
|
13312
|
+
fragmentHeaderBg: "#000000",
|
|
13313
|
+
fragmentHeaderOpacity: "1",
|
|
13314
|
+
fragmentText: "#8ffc5b",
|
|
13315
|
+
fragmentSeparator: "#60ff33",
|
|
13316
|
+
commentText: "#8ffc5b",
|
|
13317
|
+
fontFamily: `"MS Sans Serif", ${HELVETICA}`
|
|
13318
|
+
}
|
|
13319
|
+
};
|
|
13320
|
+
function resolvePalette(theme) {
|
|
13321
|
+
if (theme && theme in THEME_PALETTES) {
|
|
13322
|
+
return THEME_PALETTES[theme];
|
|
13323
|
+
}
|
|
13324
|
+
return DEFAULT_PALETTE;
|
|
13325
|
+
}
|
|
13326
|
+
function buildThemeStyles(p) {
|
|
13327
|
+
return `
|
|
13328
|
+
.frame-border-outer { fill: ${p.frameBorder}; }
|
|
13329
|
+
.frame-border-inner { fill: ${p.frameBg}; }
|
|
13330
|
+
.frame-header-bg { fill: ${p.frameBg}; }
|
|
13331
|
+
.frame-header-line { stroke: ${p.frameBorder}; stroke-width: 1; shape-rendering: crispEdges; }
|
|
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}; }
|
|
13335
|
+
.participant-icon { color: ${p.participantText}; }
|
|
13336
|
+
.participant-icon [fill="currentColor"]:not([stroke]) { stroke: ${p.participantBorder}; stroke-width: 1; }
|
|
13337
|
+
.lifeline { stroke: ${p.participantBorder}; stroke-width: 1; }
|
|
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; }
|
|
13112
13341
|
.arrow-open { fill: none; }
|
|
13113
|
-
.occurrence { fill:
|
|
13114
|
-
.fragment-border { fill: none; stroke:
|
|
13115
|
-
.fragment-header { fill:
|
|
13116
|
-
.fragment-
|
|
13117
|
-
.
|
|
13118
|
-
.fragment-
|
|
13119
|
-
.fragment-
|
|
13120
|
-
.
|
|
13121
|
-
.
|
|
13122
|
-
.
|
|
13123
|
-
.return-
|
|
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; }
|
|
13344
|
+
.fragment-header { fill: ${p.fragmentHeaderBg}; fill-opacity: ${p.fragmentHeaderOpacity}; stroke: none; shape-rendering: crispEdges; }
|
|
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}; }
|
|
13348
|
+
.fragment-condition { font-family: ${p.fontFamily}; font-size: 14px; fill: ${p.fragmentText}; }
|
|
13349
|
+
.fragment-separator { stroke: ${p.fragmentSeparator}; stroke-width: 1; shape-rendering: crispEdges; }
|
|
13350
|
+
.fragment-section-keyword { font-variant-caps: all-small-caps; }
|
|
13351
|
+
.fragment-section-label { font-family: ${p.fontFamily}; font-size: 14px; fill: ${p.fragmentText}; }
|
|
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}; }
|
|
13355
|
+
.return-icon { fill: ${p.messageText}; }
|
|
13124
13356
|
.divider-line { stroke: #aaaa33; stroke-width: 1; }
|
|
13125
13357
|
.divider-bg { fill: #fff5ad; stroke: #aaaa33; stroke-width: 1; }
|
|
13126
|
-
.divider-label { font-family:
|
|
13127
|
-
.comment-text { font-family:
|
|
13128
|
-
.
|
|
13129
|
-
.
|
|
13130
|
-
.group-
|
|
13131
|
-
.group-title-
|
|
13358
|
+
.divider-label { font-family: ${p.fontFamily}; font-size: 14px; fill: #333; }
|
|
13359
|
+
.comment-text { font-family: ${p.fontFamily}; font-size: 14px; fill: ${p.commentText}; opacity: 0.5; }
|
|
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; }
|
|
13362
|
+
.group-outline { fill: none; stroke: ${p.participantBorder}; }
|
|
13363
|
+
.group-title-bg { fill: ${p.frameBg}; stroke: none; }
|
|
13364
|
+
.group-title-text { font-family: ${p.fontFamily}; font-size: 13px; font-weight: 400; fill: ${p.participantText}; }
|
|
13132
13365
|
`;
|
|
13366
|
+
}
|
|
13367
|
+
|
|
13368
|
+
const FRAME_VIEWPORT_HEADER_ALLOWANCE = 28;
|
|
13369
|
+
const FRAME_BORDER_RADIUS = 4;
|
|
13133
13370
|
function renderToSvg(code, options) {
|
|
13134
13371
|
const rootContext = RootContext(code);
|
|
13135
13372
|
if (!rootContext) {
|
|
13136
|
-
return {
|
|
13373
|
+
return {
|
|
13374
|
+
svg: "<svg></svg>",
|
|
13375
|
+
innerSvg: "",
|
|
13376
|
+
width: 0,
|
|
13377
|
+
height: 0,
|
|
13378
|
+
viewBox: "0 0 0 0",
|
|
13379
|
+
geometry: void 0
|
|
13380
|
+
};
|
|
13137
13381
|
}
|
|
13138
13382
|
const coordinates = new Coordinates(rootContext, WidthProviderOnCanvas);
|
|
13139
13383
|
const verticalCoordinates = new VerticalCoordinates(rootContext);
|
|
@@ -13145,14 +13389,41 @@ function renderToSvg(code, options) {
|
|
|
13145
13389
|
coordinates,
|
|
13146
13390
|
verticalCoordinates,
|
|
13147
13391
|
title,
|
|
13148
|
-
measureText: WidthProviderOnCanvas
|
|
13392
|
+
measureText: WidthProviderOnCanvas,
|
|
13393
|
+
enableNumbering: options?.enableNumbering
|
|
13149
13394
|
});
|
|
13150
|
-
return { ...composeSvg(geometry), geometry };
|
|
13395
|
+
return { ...composeSvg(geometry, options), geometry };
|
|
13151
13396
|
}
|
|
13152
13397
|
function composeSvg(g, options) {
|
|
13153
13398
|
const padding = 10;
|
|
13154
|
-
const headerH =
|
|
13155
|
-
|
|
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);
|
|
13156
13427
|
const viewWidth = g.width + contentLeftMargin + padding + g.frameBorderRight + 1;
|
|
13157
13428
|
const viewHeight = g.height + padding * 2 + headerH - 1;
|
|
13158
13429
|
const parts = [];
|
|
@@ -13202,14 +13473,13 @@ function composeSvg(g, options) {
|
|
|
13202
13473
|
`<rect class="frame-border-outer" x="0" y="0" width="${viewWidth}" height="${viewHeight}" rx="${r}" fill="#666"/>`,
|
|
13203
13474
|
`<rect class="frame-border-inner" x="1" y="1" width="${viewWidth - 2}" height="${viewHeight - 2}" rx="${Math.max(0, r - 1)}" fill="#fff"/>`
|
|
13204
13475
|
].join("\n");
|
|
13205
|
-
const
|
|
13206
|
-
const headerLineY = headerH + contentPaddingTop;
|
|
13476
|
+
const headerLineY = 1 + DIAGRAM_HEADER_HEIGHT;
|
|
13207
13477
|
const headerLineDrawY = headerLineY - 0.5;
|
|
13208
13478
|
const headerLineSvg = `<line class="frame-header-line" x1="1" y1="${headerLineDrawY}" x2="${viewWidth - 1}" y2="${headerLineDrawY}"/>`;
|
|
13209
|
-
const titleSvg = g.title ? `<text x="5" y="${headerLineDrawY / 2}" dominant-baseline="central" class="frame-title">${
|
|
13479
|
+
const titleSvg = g.title ? `<text x="5" y="${headerLineDrawY / 2}" dominant-baseline="central" class="frame-title">${esc(g.title)}</text>` : "";
|
|
13210
13480
|
const viewBox = `0 0 ${viewWidth} ${viewHeight}`;
|
|
13211
13481
|
const defs = `<defs>
|
|
13212
|
-
<style>${
|
|
13482
|
+
<style>${buildThemeStyles(resolvePalette(options?.theme))}</style>
|
|
13213
13483
|
</defs>`;
|
|
13214
13484
|
const frame = `${frameSvg}
|
|
13215
13485
|
${headerLineSvg}
|
|
@@ -13225,9 +13495,6 @@ ${innerSvg}
|
|
|
13225
13495
|
</svg>`;
|
|
13226
13496
|
return { svg, innerSvg, width: viewWidth, height: viewHeight, viewBox };
|
|
13227
13497
|
}
|
|
13228
|
-
function escXml(s) {
|
|
13229
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
13230
|
-
}
|
|
13231
13498
|
|
|
13232
13499
|
function readVersion() {
|
|
13233
13500
|
const pkgPath = resolve(import.meta.dir, "../../package.json");
|
|
@@ -13249,7 +13516,8 @@ Rendering:
|
|
|
13249
13516
|
-o, --output <file> Output file (use "-" for stdout; default: <input>.svg)
|
|
13250
13517
|
-e, --outputFormat <format> Output format: "svg" (default) or "png"
|
|
13251
13518
|
-s, --scale <factor> Pixel scale factor for PNG (default: 2; ignored for SVG)
|
|
13252
|
-
-t, --theme <name> Theme
|
|
13519
|
+
-t, --theme <name> Theme: default, mermaid, clean-light, clean-dark,
|
|
13520
|
+
neon (prefix "theme-"; unknown names use default)
|
|
13253
13521
|
-c, --configFile <file> JSON config file with { theme, scale, outputFormat }
|
|
13254
13522
|
--md Markdown mode: render zenuml code blocks and produce output Markdown
|
|
13255
13523
|
|
|
@@ -13610,7 +13878,6 @@ async function checkOne(input) {
|
|
|
13610
13878
|
errors: [{ line: 0, column: 0, msg: err.message }]
|
|
13611
13879
|
};
|
|
13612
13880
|
}
|
|
13613
|
-
Parser.Errors.length = 0;
|
|
13614
13881
|
Parser.ErrorDetails.length = 0;
|
|
13615
13882
|
Parser.RootContext(code);
|
|
13616
13883
|
const errors = Parser.ErrorDetails.map((e) => ({
|
|
@@ -13776,7 +14043,6 @@ async function main() {
|
|
|
13776
14043
|
`);
|
|
13777
14044
|
process.exit(1);
|
|
13778
14045
|
}
|
|
13779
|
-
Parser.Errors.length = 0;
|
|
13780
14046
|
Parser.ErrorDetails.length = 0;
|
|
13781
14047
|
const tree = Parser.RootContext(code);
|
|
13782
14048
|
if (Parser.ErrorDetails.length > 0) {
|