schematex 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -7
- package/dist/ai/ai-sdk.cjs +18 -12
- package/dist/ai/ai-sdk.cjs.map +1 -1
- package/dist/ai/ai-sdk.d.cts +4 -3
- package/dist/ai/ai-sdk.d.ts +4 -3
- package/dist/ai/ai-sdk.js +14 -8
- package/dist/ai/ai-sdk.js.map +1 -1
- package/dist/ai/index.cjs +15 -11
- package/dist/ai/index.d.cts +20 -153
- package/dist/ai/index.d.ts +20 -153
- package/dist/ai/index.js +3 -3
- package/dist/api-C5SECOxZ.d.cts +69 -0
- package/dist/api-Cr_MxttI.d.ts +69 -0
- package/dist/browser.cjs +23 -5
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +12 -3
- package/dist/browser.d.ts +12 -3
- package/dist/browser.js +13 -5
- package/dist/browser.js.map +1 -1
- package/dist/{chunk-3YUUC6RN.cjs → chunk-4QPDZJAL.cjs} +1421 -37
- package/dist/chunk-4QPDZJAL.cjs.map +1 -0
- package/dist/{chunk-NWPCY65Z.cjs → chunk-HK56GQQP.cjs} +544 -61
- package/dist/chunk-HK56GQQP.cjs.map +1 -0
- package/dist/{chunk-GTDQAN2Z.js → chunk-OK5ZS3LU.js} +1419 -38
- package/dist/chunk-OK5ZS3LU.js.map +1 -0
- package/dist/{chunk-XRCY75UV.cjs → chunk-QMTWG6JL.cjs} +947 -916
- package/dist/chunk-QMTWG6JL.cjs.map +1 -0
- package/dist/{chunk-HUPDIRBX.js → chunk-VCH7RI5H.js} +947 -916
- package/dist/chunk-VCH7RI5H.js.map +1 -0
- package/dist/{chunk-IM4RCUHA.js → chunk-VKPCR7BG.js} +544 -62
- package/dist/chunk-VKPCR7BG.js.map +1 -0
- package/dist/diagrams/blockdiagram/index.d.cts +1 -1
- package/dist/diagrams/blockdiagram/index.d.ts +1 -1
- package/dist/diagrams/circuit/index.cjs +7 -7
- package/dist/diagrams/circuit/index.d.cts +1 -1
- package/dist/diagrams/circuit/index.d.ts +1 -1
- package/dist/diagrams/circuit/index.js +1 -1
- package/dist/diagrams/ecomap/index.d.cts +1 -1
- package/dist/diagrams/ecomap/index.d.ts +1 -1
- package/dist/diagrams/entity/index.d.cts +1 -1
- package/dist/diagrams/entity/index.d.ts +1 -1
- package/dist/diagrams/fishbone/index.d.cts +1 -1
- package/dist/diagrams/fishbone/index.d.ts +1 -1
- package/dist/diagrams/flowchart/index.d.cts +2 -2
- package/dist/diagrams/flowchart/index.d.ts +2 -2
- package/dist/diagrams/genogram/index.d.cts +1 -1
- package/dist/diagrams/genogram/index.d.ts +1 -1
- package/dist/diagrams/ladder/index.d.cts +1 -1
- package/dist/diagrams/ladder/index.d.ts +1 -1
- package/dist/diagrams/logic/index.d.cts +1 -1
- package/dist/diagrams/logic/index.d.ts +1 -1
- package/dist/diagrams/orgchart/index.d.cts +1 -1
- package/dist/diagrams/orgchart/index.d.ts +1 -1
- package/dist/diagrams/pedigree/index.d.cts +1 -1
- package/dist/diagrams/pedigree/index.d.ts +1 -1
- package/dist/diagrams/phylo/index.d.cts +1 -1
- package/dist/diagrams/phylo/index.d.ts +1 -1
- package/dist/diagrams/sld/index.d.cts +1 -1
- package/dist/diagrams/sld/index.d.ts +1 -1
- package/dist/diagrams/sociogram/index.d.cts +1 -1
- package/dist/diagrams/sociogram/index.d.ts +1 -1
- package/dist/diagrams/timing/index.d.cts +1 -1
- package/dist/diagrams/timing/index.d.ts +1 -1
- package/dist/diagrams/venn/index.d.cts +1 -1
- package/dist/diagrams/venn/index.d.ts +1 -1
- package/dist/{index-C9A0h-CB.d.cts → index-BD2yDfQM.d.cts} +1 -1
- package/dist/{index-CJai_TEZ.d.ts → index-C30zQWZI.d.ts} +1 -1
- package/dist/index.cjs +24 -12
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/react.cjs +7 -9
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +3 -2
- package/dist/react.d.ts +3 -2
- package/dist/react.js +7 -9
- package/dist/react.js.map +1 -1
- package/dist/tools-BHWaJPOl.d.ts +153 -0
- package/dist/tools-DlpuE76u.d.cts +153 -0
- package/dist/{types-BOAsqHoU.d.cts → types-WTr9W5Ud.d.cts} +2 -2
- package/dist/{types-BOAsqHoU.d.ts → types-WTr9W5Ud.d.ts} +2 -2
- package/package.json +2 -2
- package/dist/api-C5UcmT7n.d.cts +0 -22
- package/dist/api-C5UcmT7n.d.ts +0 -22
- package/dist/chunk-3YUUC6RN.cjs.map +0 -1
- package/dist/chunk-GTDQAN2Z.js.map +0 -1
- package/dist/chunk-HUPDIRBX.js.map +0 -1
- package/dist/chunk-IM4RCUHA.js.map +0 -1
- package/dist/chunk-NWPCY65Z.cjs.map +0 -1
- package/dist/chunk-XRCY75UV.cjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { orgchart } from './chunk-I55HO32M.js';
|
|
2
|
-
import { circuit } from './chunk-
|
|
2
|
+
import { circuit } from './chunk-VCH7RI5H.js';
|
|
3
3
|
import { blockdiagram } from './chunk-EPKIJEH7.js';
|
|
4
4
|
import { ladder } from './chunk-IBRW3UOA.js';
|
|
5
5
|
import { sld } from './chunk-6OSUNBZY.js';
|
|
@@ -16,7 +16,7 @@ import { sociogram } from './chunk-ZX74KJPM.js';
|
|
|
16
16
|
import { timing } from './chunk-6URNSB6G.js';
|
|
17
17
|
import { logic } from './chunk-ZYRCPSBU.js';
|
|
18
18
|
import { resolveBaseTheme, resolveTimelineTheme, cssCustomProperties, resolveMindmapTheme } from './chunk-2VNMKOUO.js';
|
|
19
|
-
import { matchQuotedTitle } from './chunk-5IKOLUWK.js';
|
|
19
|
+
import { matchQuotedTitle, stripQuotes } from './chunk-5IKOLUWK.js';
|
|
20
20
|
import { el, escapeXml, title, desc, text, path, rect, group, svgRoot, defs, circle, polygon, line, multilineText } from './chunk-SYYBKDL7.js';
|
|
21
21
|
|
|
22
22
|
// src/diagrams/decisiontree/parser.ts
|
|
@@ -3935,7 +3935,7 @@ function renderLayout(layout) {
|
|
|
3935
3935
|
var state = {
|
|
3936
3936
|
type: "state",
|
|
3937
3937
|
detect(text2) {
|
|
3938
|
-
return /^\s*state\b/i.test(text2);
|
|
3938
|
+
return /^\s*(?:state\b|stateDiagram(?:-v2)?\b)/i.test(text2);
|
|
3939
3939
|
},
|
|
3940
3940
|
parse: parseStateDiagram,
|
|
3941
3941
|
render(text2, config) {
|
|
@@ -7184,7 +7184,7 @@ function parseHeader(state2) {
|
|
|
7184
7184
|
if (!m) break;
|
|
7185
7185
|
const key = m[1].toLowerCase();
|
|
7186
7186
|
const valueRaw = m[2].trim();
|
|
7187
|
-
const value =
|
|
7187
|
+
const value = stripQuotes2(valueRaw);
|
|
7188
7188
|
if (key === "title") state2.ast.title = value;
|
|
7189
7189
|
else if (key === "system") state2.ast.system = value;
|
|
7190
7190
|
else if (key === "direction") {
|
|
@@ -7202,7 +7202,7 @@ function parseHeader(state2) {
|
|
|
7202
7202
|
state2.i++;
|
|
7203
7203
|
}
|
|
7204
7204
|
}
|
|
7205
|
-
function
|
|
7205
|
+
function stripQuotes2(s) {
|
|
7206
7206
|
const t = s.trim();
|
|
7207
7207
|
if (t.startsWith('"') && t.endsWith('"') && t.length >= 2) return t.slice(1, -1);
|
|
7208
7208
|
return t;
|
|
@@ -7239,7 +7239,7 @@ function parseActorDecl(ln, state2) {
|
|
|
7239
7239
|
id = asMatch[1];
|
|
7240
7240
|
rest = rest.slice(0, asMatch.index).trim();
|
|
7241
7241
|
}
|
|
7242
|
-
const name =
|
|
7242
|
+
const name = stripQuotes2(rest);
|
|
7243
7243
|
if (!name) throw new UsecaseParseError(`actor declaration missing name`, ln.line);
|
|
7244
7244
|
const actorId = id ?? defaultIdFor(name);
|
|
7245
7245
|
declareId(state2, actorId, "actor", ln.line);
|
|
@@ -7275,7 +7275,7 @@ function parseUsecaseDecl(ln, state2) {
|
|
|
7275
7275
|
id = asMatch[1];
|
|
7276
7276
|
rest = rest.slice(0, asMatch.index).trim();
|
|
7277
7277
|
}
|
|
7278
|
-
const name =
|
|
7278
|
+
const name = stripQuotes2(rest);
|
|
7279
7279
|
if (!name) throw new UsecaseParseError(`usecase declaration missing name`, ln.line);
|
|
7280
7280
|
const ucId = id ?? defaultIdFor(name);
|
|
7281
7281
|
declareId(state2, ucId, "usecase", ln.line);
|
|
@@ -8030,13 +8030,13 @@ function layoutUsecase(ast) {
|
|
|
8030
8030
|
pb = perimeter(r.target, a.cx, a.cy);
|
|
8031
8031
|
}
|
|
8032
8032
|
const dashed = r.kind === "include" || r.kind === "extend";
|
|
8033
|
-
let
|
|
8034
|
-
if (r.kind === "directed" || r.kind === "include" || r.kind === "extend")
|
|
8035
|
-
else if (r.kind === "generalization")
|
|
8033
|
+
let arrowKind2 = "none";
|
|
8034
|
+
if (r.kind === "directed" || r.kind === "include" || r.kind === "extend") arrowKind2 = "open";
|
|
8035
|
+
else if (r.kind === "generalization") arrowKind2 = "hollow";
|
|
8036
8036
|
const edge = {
|
|
8037
8037
|
relation: r,
|
|
8038
8038
|
d: `M ${round(pa.x)} ${round(pa.y)} L ${round(pb.x)} ${round(pb.y)}`,
|
|
8039
|
-
arrowKind,
|
|
8039
|
+
arrowKind: arrowKind2,
|
|
8040
8040
|
dashed
|
|
8041
8041
|
};
|
|
8042
8042
|
if (r.kind === "include" || r.kind === "extend") {
|
|
@@ -8519,7 +8519,7 @@ function preprocess7(src) {
|
|
|
8519
8519
|
}
|
|
8520
8520
|
return out;
|
|
8521
8521
|
}
|
|
8522
|
-
function
|
|
8522
|
+
function stripQuotes3(s) {
|
|
8523
8523
|
const t = s.trim();
|
|
8524
8524
|
if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
|
|
8525
8525
|
return t.slice(1, -1);
|
|
@@ -8537,7 +8537,7 @@ function parseHeaderLine(ln, ast) {
|
|
|
8537
8537
|
if (!m) return false;
|
|
8538
8538
|
const key = m[1].toLowerCase();
|
|
8539
8539
|
const valueRaw = m[2].trim();
|
|
8540
|
-
const value =
|
|
8540
|
+
const value = stripQuotes3(valueRaw);
|
|
8541
8541
|
switch (key) {
|
|
8542
8542
|
case "title":
|
|
8543
8543
|
ast.title = value;
|
|
@@ -8666,23 +8666,23 @@ function parseTaskLine(ln, ast) {
|
|
|
8666
8666
|
milestone = true;
|
|
8667
8667
|
return "";
|
|
8668
8668
|
}).trim();
|
|
8669
|
-
const
|
|
8669
|
+
const markers4 = [];
|
|
8670
8670
|
KEY_RE.lastIndex = 0;
|
|
8671
8671
|
let mm;
|
|
8672
8672
|
while ((mm = KEY_RE.exec(rest)) !== null) {
|
|
8673
|
-
|
|
8673
|
+
markers4.push({ key: mm[1].toLowerCase(), start: mm.index, valStart: mm.index + mm[0].length });
|
|
8674
8674
|
}
|
|
8675
8675
|
const values = {};
|
|
8676
|
-
for (let i = 0; i <
|
|
8677
|
-
const cur =
|
|
8678
|
-
const nextStart = i + 1 <
|
|
8676
|
+
for (let i = 0; i < markers4.length; i++) {
|
|
8677
|
+
const cur = markers4[i];
|
|
8678
|
+
const nextStart = i + 1 < markers4.length ? markers4[i + 1].start : rest.length;
|
|
8679
8679
|
const val = rest.slice(cur.valStart, nextStart).trim();
|
|
8680
8680
|
if (cur.key in values) {
|
|
8681
8681
|
throw new PertParseError(`duplicate '${cur.key}:' on task '${id}'`, ln.line);
|
|
8682
8682
|
}
|
|
8683
8683
|
values[cur.key] = val;
|
|
8684
8684
|
}
|
|
8685
|
-
const preamble = (
|
|
8685
|
+
const preamble = (markers4.length ? rest.slice(0, markers4[0].start) : rest).trim();
|
|
8686
8686
|
if (preamble) {
|
|
8687
8687
|
throw new PertParseError(`unexpected text '${preamble}' in task '${id}'`, ln.line);
|
|
8688
8688
|
}
|
|
@@ -8710,7 +8710,7 @@ function parseTaskLine(ln, ast) {
|
|
|
8710
8710
|
}
|
|
8711
8711
|
const tags = values.tags ? values.tags.split(",").map((t) => t.trim()).filter(Boolean) : [];
|
|
8712
8712
|
const className = values.class ? values.class.trim() : void 0;
|
|
8713
|
-
const lane = values.lane ?
|
|
8713
|
+
const lane = values.lane ? stripQuotes3(values.lane) : void 0;
|
|
8714
8714
|
const task = {
|
|
8715
8715
|
id,
|
|
8716
8716
|
label,
|
|
@@ -10265,6 +10265,155 @@ var pert = {
|
|
|
10265
10265
|
}
|
|
10266
10266
|
};
|
|
10267
10267
|
|
|
10268
|
+
// src/core/diagnostics.ts
|
|
10269
|
+
var ENGINE_BUG_NAMES = /* @__PURE__ */ new Set([
|
|
10270
|
+
"ReferenceError",
|
|
10271
|
+
"TypeError",
|
|
10272
|
+
"RangeError"
|
|
10273
|
+
]);
|
|
10274
|
+
function diagnosticFromError(err) {
|
|
10275
|
+
if (err instanceof Error) {
|
|
10276
|
+
const anyErr = err;
|
|
10277
|
+
const hasParseFields = typeof anyErr.line === "number";
|
|
10278
|
+
const isEngineBug = !hasParseFields && ENGINE_BUG_NAMES.has(err.name);
|
|
10279
|
+
const source = typeof anyErr.source === "string" ? anyErr.source : isEngineBug ? firstStackFrame(err.stack) : void 0;
|
|
10280
|
+
return {
|
|
10281
|
+
severity: "error",
|
|
10282
|
+
code: isEngineBug ? "ENGINE_BUG" : "DSL_INVALID",
|
|
10283
|
+
line: typeof anyErr.line === "number" ? anyErr.line : void 0,
|
|
10284
|
+
column: typeof anyErr.column === "number" ? anyErr.column : void 0,
|
|
10285
|
+
source,
|
|
10286
|
+
message: isEngineBug ? `[engine bug: ${err.name}] ${err.message}` : err.message,
|
|
10287
|
+
hint: typeof anyErr.hint === "string" ? anyErr.hint : isEngineBug ? "This looks like a Schematex internal error rather than a DSL syntax problem. Keep the failing DSL and file an issue." : void 0,
|
|
10288
|
+
fatal: true
|
|
10289
|
+
};
|
|
10290
|
+
}
|
|
10291
|
+
return {
|
|
10292
|
+
severity: "error",
|
|
10293
|
+
code: "UNKNOWN_THROW",
|
|
10294
|
+
message: String(err),
|
|
10295
|
+
fatal: true
|
|
10296
|
+
};
|
|
10297
|
+
}
|
|
10298
|
+
function renderDiagnosticSvg(diagnostics, type, config = {}) {
|
|
10299
|
+
const headline = type ? `${type} preview could not be rendered` : "Diagram preview could not be rendered";
|
|
10300
|
+
const detail = diagnostics[0]?.message ?? "Schematex could not parse this DSL.";
|
|
10301
|
+
const lines = wrapText3(detail, 88).slice(0, 5);
|
|
10302
|
+
const source = diagnostics[0]?.source ? wrapText3(`Source: ${diagnostics[0].source}`, 88).slice(0, 2) : [];
|
|
10303
|
+
const foot = "Strict validation failed before a diagram could be drawn.";
|
|
10304
|
+
const width = 760;
|
|
10305
|
+
const lineHeight = 20;
|
|
10306
|
+
const height = 174 + (lines.length + source.length) * lineHeight;
|
|
10307
|
+
const fontFamily = config.fontFamily ?? "system-ui, -apple-system, sans-serif";
|
|
10308
|
+
const children = [
|
|
10309
|
+
title(headline),
|
|
10310
|
+
desc(`${headline}. ${detail}`),
|
|
10311
|
+
el(
|
|
10312
|
+
"style",
|
|
10313
|
+
{},
|
|
10314
|
+
`
|
|
10315
|
+
.schematex-preview-error { font-family: ${escapeXml(fontFamily)}; }
|
|
10316
|
+
.schematex-preview-error-frame { fill: #fff7ed; stroke: #c2410c; stroke-width: 1.5; }
|
|
10317
|
+
.schematex-preview-error-mark { fill: #c2410c; }
|
|
10318
|
+
.schematex-preview-error-title { fill: #7c2d12; font-size: 18px; font-weight: 600; }
|
|
10319
|
+
.schematex-preview-error-copy { fill: #431407; font-size: 13px; }
|
|
10320
|
+
.schematex-preview-error-muted { fill: #9a3412; font-size: 12px; }
|
|
10321
|
+
`
|
|
10322
|
+
),
|
|
10323
|
+
el("g", { class: "schematex-preview-error" }, [
|
|
10324
|
+
rect({
|
|
10325
|
+
x: 1,
|
|
10326
|
+
y: 1,
|
|
10327
|
+
width: width - 2,
|
|
10328
|
+
height: height - 2,
|
|
10329
|
+
rx: 6,
|
|
10330
|
+
class: "schematex-preview-error-frame"
|
|
10331
|
+
}),
|
|
10332
|
+
rect({
|
|
10333
|
+
x: 28,
|
|
10334
|
+
y: 30,
|
|
10335
|
+
width: 8,
|
|
10336
|
+
height: 34,
|
|
10337
|
+
rx: 4,
|
|
10338
|
+
class: "schematex-preview-error-mark"
|
|
10339
|
+
}),
|
|
10340
|
+
rect({
|
|
10341
|
+
x: 28,
|
|
10342
|
+
y: 72,
|
|
10343
|
+
width: 8,
|
|
10344
|
+
height: 8,
|
|
10345
|
+
rx: 4,
|
|
10346
|
+
class: "schematex-preview-error-mark"
|
|
10347
|
+
}),
|
|
10348
|
+
text(
|
|
10349
|
+
{ x: 58, y: 49, class: "schematex-preview-error-title" },
|
|
10350
|
+
headline
|
|
10351
|
+
),
|
|
10352
|
+
text(
|
|
10353
|
+
{ x: 58, y: 76, class: "schematex-preview-error-muted" },
|
|
10354
|
+
"The DSL is still available to repair or retry."
|
|
10355
|
+
),
|
|
10356
|
+
...renderRows(lines, 58, 112, lineHeight, "schematex-preview-error-copy"),
|
|
10357
|
+
...renderRows(
|
|
10358
|
+
source,
|
|
10359
|
+
58,
|
|
10360
|
+
112 + lines.length * lineHeight + 10,
|
|
10361
|
+
lineHeight,
|
|
10362
|
+
"schematex-preview-error-muted"
|
|
10363
|
+
),
|
|
10364
|
+
text(
|
|
10365
|
+
{ x: 58, y: height - 28, class: "schematex-preview-error-muted" },
|
|
10366
|
+
foot
|
|
10367
|
+
)
|
|
10368
|
+
])
|
|
10369
|
+
];
|
|
10370
|
+
return svgRoot(
|
|
10371
|
+
{
|
|
10372
|
+
width,
|
|
10373
|
+
height,
|
|
10374
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
10375
|
+
role: "img",
|
|
10376
|
+
"aria-label": headline,
|
|
10377
|
+
"data-schematex-status": "invalid"
|
|
10378
|
+
},
|
|
10379
|
+
children
|
|
10380
|
+
);
|
|
10381
|
+
}
|
|
10382
|
+
function renderRows(rows, x, y, lineHeight, className) {
|
|
10383
|
+
return rows.map(
|
|
10384
|
+
(row, idx) => text({ x, y: y + idx * lineHeight, class: className }, row)
|
|
10385
|
+
);
|
|
10386
|
+
}
|
|
10387
|
+
function wrapText3(value, max) {
|
|
10388
|
+
const words = value.replace(/\s+/g, " ").trim().split(" ");
|
|
10389
|
+
const rows = [];
|
|
10390
|
+
let current = "";
|
|
10391
|
+
for (const word of words) {
|
|
10392
|
+
if (!current) {
|
|
10393
|
+
current = word;
|
|
10394
|
+
continue;
|
|
10395
|
+
}
|
|
10396
|
+
if (current.length + word.length + 1 <= max) {
|
|
10397
|
+
current += ` ${word}`;
|
|
10398
|
+
continue;
|
|
10399
|
+
}
|
|
10400
|
+
rows.push(current);
|
|
10401
|
+
current = word;
|
|
10402
|
+
}
|
|
10403
|
+
if (current) rows.push(current);
|
|
10404
|
+
return rows.length > 0 ? rows : [value];
|
|
10405
|
+
}
|
|
10406
|
+
function firstStackFrame(stack) {
|
|
10407
|
+
if (!stack) return void 0;
|
|
10408
|
+
for (const line2 of stack.split("\n")) {
|
|
10409
|
+
const trimmed = line2.trim();
|
|
10410
|
+
if (trimmed.startsWith("at ")) {
|
|
10411
|
+
return trimmed.replace(/\((?:.*\/)?([^/]+)\)/, "($1)");
|
|
10412
|
+
}
|
|
10413
|
+
}
|
|
10414
|
+
return void 0;
|
|
10415
|
+
}
|
|
10416
|
+
|
|
10268
10417
|
// src/diagrams/mindmap/inline.ts
|
|
10269
10418
|
var RE_TASK = /^\[( |x|X)\]\s+(.*)$/;
|
|
10270
10419
|
function tokenizeInline(raw) {
|
|
@@ -11234,7 +11383,7 @@ function newAST() {
|
|
|
11234
11383
|
config: { ...DEFAULT_CONFIG }
|
|
11235
11384
|
};
|
|
11236
11385
|
}
|
|
11237
|
-
function
|
|
11386
|
+
function stripQuotes4(s) {
|
|
11238
11387
|
const t = s.trim();
|
|
11239
11388
|
if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) {
|
|
11240
11389
|
return t.slice(1, -1);
|
|
@@ -11270,7 +11419,7 @@ function parseAxis(raw) {
|
|
|
11270
11419
|
function parseNumberList2(raw) {
|
|
11271
11420
|
const t = raw.trim();
|
|
11272
11421
|
const inner = t.startsWith("[") && t.endsWith("]") ? t.slice(1, -1) : t;
|
|
11273
|
-
return inner.split(",").map((s) =>
|
|
11422
|
+
return inner.split(",").map((s) => stripQuotes4(s.trim())).filter((s) => s.length > 0);
|
|
11274
11423
|
}
|
|
11275
11424
|
function parseProperties2(raw, point) {
|
|
11276
11425
|
let i = 0;
|
|
@@ -11385,7 +11534,7 @@ function parseHeader2(line2, ast) {
|
|
|
11385
11534
|
ast.cols = Number(heatMatch[1]);
|
|
11386
11535
|
ast.rows = Number(heatMatch[2]);
|
|
11387
11536
|
const title2 = heatMatch[3].trim();
|
|
11388
|
-
if (title2) ast.title =
|
|
11537
|
+
if (title2) ast.title = stripQuotes4(title2);
|
|
11389
11538
|
return void 0;
|
|
11390
11539
|
}
|
|
11391
11540
|
const corrMatch = rest.match(/^correlation\s*(?:(\d+)\s*x\s*(\d+))?\s*(.*)$/i);
|
|
@@ -11397,7 +11546,7 @@ function parseHeader2(line2, ast) {
|
|
|
11397
11546
|
ast.rows = Number(corrMatch[2]);
|
|
11398
11547
|
}
|
|
11399
11548
|
const title2 = corrMatch[3].trim();
|
|
11400
|
-
if (title2) ast.title =
|
|
11549
|
+
if (title2) ast.title = stripQuotes4(title2);
|
|
11401
11550
|
return void 0;
|
|
11402
11551
|
}
|
|
11403
11552
|
const tokenMatch = rest.match(/^([a-zA-Z0-9_-]+)\s*(.*)$/);
|
|
@@ -11405,13 +11554,13 @@ function parseHeader2(line2, ast) {
|
|
|
11405
11554
|
const tok = tokenMatch[1].toLowerCase();
|
|
11406
11555
|
const remainder = tokenMatch[2].trim();
|
|
11407
11556
|
if (TEMPLATE_NAMES.has(tok)) {
|
|
11408
|
-
if (remainder) ast.title =
|
|
11557
|
+
if (remainder) ast.title = stripQuotes4(remainder);
|
|
11409
11558
|
return tok;
|
|
11410
11559
|
}
|
|
11411
11560
|
if (rest.startsWith('"') || rest.startsWith("'")) {
|
|
11412
|
-
ast.title =
|
|
11561
|
+
ast.title = stripQuotes4(rest);
|
|
11413
11562
|
} else if (rest.length > 0) {
|
|
11414
|
-
ast.title =
|
|
11563
|
+
ast.title = stripQuotes4(rest);
|
|
11415
11564
|
}
|
|
11416
11565
|
}
|
|
11417
11566
|
return void 0;
|
|
@@ -11447,7 +11596,7 @@ function parseMatrix(text2) {
|
|
|
11447
11596
|
inConfig = false;
|
|
11448
11597
|
}
|
|
11449
11598
|
if (/^title\s*:/i.test(line2)) {
|
|
11450
|
-
st.ast.title =
|
|
11599
|
+
st.ast.title = stripQuotes4(line2.replace(/^title\s*:\s*/i, ""));
|
|
11451
11600
|
continue;
|
|
11452
11601
|
}
|
|
11453
11602
|
if (/^x-axis\s*:/i.test(line2)) {
|
|
@@ -11532,7 +11681,7 @@ function parseMatrix(text2) {
|
|
|
11532
11681
|
if (qShort) {
|
|
11533
11682
|
const q = Number(qShort[1]);
|
|
11534
11683
|
const labelRaw = qShort[2].trim();
|
|
11535
|
-
const label =
|
|
11684
|
+
const label = stripQuotes4(labelRaw);
|
|
11536
11685
|
const cell = quadrantToCell(q);
|
|
11537
11686
|
st.ast.cellLabels.push({ col: cell.col, row: cell.row, label });
|
|
11538
11687
|
continue;
|
|
@@ -13420,10 +13569,10 @@ function renderEntity(e) {
|
|
|
13420
13569
|
a.name
|
|
13421
13570
|
)
|
|
13422
13571
|
);
|
|
13423
|
-
const
|
|
13572
|
+
const markers4 = attrMarkers(a);
|
|
13424
13573
|
const markerW = 26;
|
|
13425
13574
|
const markerGap = 4;
|
|
13426
|
-
const markersBlockW =
|
|
13575
|
+
const markersBlockW = markers4.length * markerW + (markers4.length - 1) * markerGap;
|
|
13427
13576
|
const markersStartX = e.x + e.width - C2.ENTITY_PADDING_X - markersBlockW;
|
|
13428
13577
|
if (a.type) {
|
|
13429
13578
|
body.push(
|
|
@@ -13438,8 +13587,8 @@ function renderEntity(e) {
|
|
|
13438
13587
|
)
|
|
13439
13588
|
);
|
|
13440
13589
|
}
|
|
13441
|
-
for (let j = 0; j <
|
|
13442
|
-
const m =
|
|
13590
|
+
for (let j = 0; j < markers4.length; j++) {
|
|
13591
|
+
const m = markers4[j];
|
|
13443
13592
|
const px = markersStartX + j * (markerW + markerGap);
|
|
13444
13593
|
body.push(
|
|
13445
13594
|
rect({
|
|
@@ -18613,6 +18762,1178 @@ var sfc = {
|
|
|
18613
18762
|
}
|
|
18614
18763
|
};
|
|
18615
18764
|
|
|
18765
|
+
// src/diagrams/sequence/parser.ts
|
|
18766
|
+
var SequenceParseError = class extends Error {
|
|
18767
|
+
line;
|
|
18768
|
+
constructor(message, line2) {
|
|
18769
|
+
super(line2 ? `${message} (line ${line2})` : message);
|
|
18770
|
+
this.name = "SequenceParseError";
|
|
18771
|
+
this.line = line2;
|
|
18772
|
+
}
|
|
18773
|
+
};
|
|
18774
|
+
var KINDS = /* @__PURE__ */ new Set([
|
|
18775
|
+
"participant",
|
|
18776
|
+
"actor",
|
|
18777
|
+
"boundary",
|
|
18778
|
+
"control",
|
|
18779
|
+
"entity",
|
|
18780
|
+
"database",
|
|
18781
|
+
"collections",
|
|
18782
|
+
"queue"
|
|
18783
|
+
]);
|
|
18784
|
+
var SIMPLE_FRAG = /* @__PURE__ */ new Set([
|
|
18785
|
+
"opt",
|
|
18786
|
+
"loop",
|
|
18787
|
+
"break",
|
|
18788
|
+
"critical",
|
|
18789
|
+
"neg",
|
|
18790
|
+
"ignore",
|
|
18791
|
+
"consider",
|
|
18792
|
+
"assert"
|
|
18793
|
+
]);
|
|
18794
|
+
var MULTI_FRAG = /* @__PURE__ */ new Set(["par", "seq", "strict"]);
|
|
18795
|
+
var MSGSET_FRAG = /* @__PURE__ */ new Set(["ignore", "consider"]);
|
|
18796
|
+
var ALL_FRAG = /* @__PURE__ */ new Set([
|
|
18797
|
+
"alt",
|
|
18798
|
+
...SIMPLE_FRAG,
|
|
18799
|
+
...MULTI_FRAG
|
|
18800
|
+
]);
|
|
18801
|
+
var ARROW_RE = /(-->|->>|o->|-x|->)/;
|
|
18802
|
+
function arrowKind(token) {
|
|
18803
|
+
switch (token) {
|
|
18804
|
+
case "-->":
|
|
18805
|
+
return "reply";
|
|
18806
|
+
case "->>":
|
|
18807
|
+
return "async";
|
|
18808
|
+
case "o->":
|
|
18809
|
+
return "found";
|
|
18810
|
+
case "-x":
|
|
18811
|
+
return "lost";
|
|
18812
|
+
default:
|
|
18813
|
+
return "sync";
|
|
18814
|
+
}
|
|
18815
|
+
}
|
|
18816
|
+
function firstWord(s) {
|
|
18817
|
+
const m = /^(\S+)/.exec(s);
|
|
18818
|
+
return m ? m[1] : "";
|
|
18819
|
+
}
|
|
18820
|
+
function extractGuard(rest) {
|
|
18821
|
+
const t = rest.trim();
|
|
18822
|
+
if (!t) return void 0;
|
|
18823
|
+
if (t.startsWith("[") && t.endsWith("]")) return t.slice(1, -1).trim();
|
|
18824
|
+
if (t.startsWith("(") && t.endsWith(")")) return t.slice(1, -1).trim();
|
|
18825
|
+
return t;
|
|
18826
|
+
}
|
|
18827
|
+
var SequenceParser = class {
|
|
18828
|
+
lines;
|
|
18829
|
+
i = 0;
|
|
18830
|
+
order = [];
|
|
18831
|
+
byId = /* @__PURE__ */ new Map();
|
|
18832
|
+
warnings = [];
|
|
18833
|
+
constructor(source) {
|
|
18834
|
+
this.lines = source.split(/\r?\n/).map((raw, idx) => ({
|
|
18835
|
+
text: raw.trim(),
|
|
18836
|
+
n: idx + 1
|
|
18837
|
+
}));
|
|
18838
|
+
}
|
|
18839
|
+
parse() {
|
|
18840
|
+
const header = this.consumeHeader();
|
|
18841
|
+
const ast = {
|
|
18842
|
+
type: "sequence",
|
|
18843
|
+
participants: [],
|
|
18844
|
+
statements: [],
|
|
18845
|
+
warnings: this.warnings
|
|
18846
|
+
};
|
|
18847
|
+
if (header.title) ast.title = header.title;
|
|
18848
|
+
const statements = this.parseBlock();
|
|
18849
|
+
const leftover = this.peek();
|
|
18850
|
+
if (leftover) {
|
|
18851
|
+
const fw = firstWord(leftover.text).toLowerCase();
|
|
18852
|
+
throw new SequenceParseError(
|
|
18853
|
+
`'${fw}' without a matching combined fragment`,
|
|
18854
|
+
leftover.n
|
|
18855
|
+
);
|
|
18856
|
+
}
|
|
18857
|
+
if (this.autonumberSpec) ast.autonumber = this.autonumberSpec;
|
|
18858
|
+
ast.statements = statements;
|
|
18859
|
+
ast.participants = this.order.map((id) => this.byId.get(id));
|
|
18860
|
+
return ast;
|
|
18861
|
+
}
|
|
18862
|
+
autonumberSpec;
|
|
18863
|
+
// ── line cursor ──────────────────────────────────────────────
|
|
18864
|
+
peek() {
|
|
18865
|
+
while (this.i < this.lines.length) {
|
|
18866
|
+
const ln = this.lines[this.i];
|
|
18867
|
+
if (ln.text === "" || ln.text.startsWith("#") || ln.text.startsWith("//")) {
|
|
18868
|
+
this.i++;
|
|
18869
|
+
continue;
|
|
18870
|
+
}
|
|
18871
|
+
return ln;
|
|
18872
|
+
}
|
|
18873
|
+
return null;
|
|
18874
|
+
}
|
|
18875
|
+
next() {
|
|
18876
|
+
const ln = this.peek();
|
|
18877
|
+
if (ln) this.i++;
|
|
18878
|
+
return ln;
|
|
18879
|
+
}
|
|
18880
|
+
// ── header ───────────────────────────────────────────────────
|
|
18881
|
+
consumeHeader() {
|
|
18882
|
+
const ln = this.next();
|
|
18883
|
+
if (!ln || !/^sequence\b/i.test(ln.text)) {
|
|
18884
|
+
throw new SequenceParseError(
|
|
18885
|
+
"A sequence diagram must start with the keyword 'sequence'",
|
|
18886
|
+
ln?.n
|
|
18887
|
+
);
|
|
18888
|
+
}
|
|
18889
|
+
const title2 = matchQuotedTitle(ln.text);
|
|
18890
|
+
return title2 ? { title: title2 } : {};
|
|
18891
|
+
}
|
|
18892
|
+
// ── participants ─────────────────────────────────────────────
|
|
18893
|
+
ensure(id, kind = "participant", line2) {
|
|
18894
|
+
let p = this.byId.get(id);
|
|
18895
|
+
if (!p) {
|
|
18896
|
+
p = { id, name: id, kind, line: line2 };
|
|
18897
|
+
this.byId.set(id, p);
|
|
18898
|
+
this.order.push(id);
|
|
18899
|
+
}
|
|
18900
|
+
return p;
|
|
18901
|
+
}
|
|
18902
|
+
declare(line2) {
|
|
18903
|
+
const fw = firstWord(line2.text);
|
|
18904
|
+
const kind = fw.toLowerCase();
|
|
18905
|
+
let rest = line2.text.slice(fw.length).trim();
|
|
18906
|
+
let stereotype;
|
|
18907
|
+
const stereoMatch = /«([^»]*)»|<<([^>]*)>>/.exec(rest);
|
|
18908
|
+
if (stereoMatch) {
|
|
18909
|
+
stereotype = (stereoMatch[1] ?? stereoMatch[2] ?? "").trim();
|
|
18910
|
+
rest = (rest.slice(0, stereoMatch.index) + rest.slice(stereoMatch.index + stereoMatch[0].length)).trim();
|
|
18911
|
+
}
|
|
18912
|
+
let id;
|
|
18913
|
+
let name;
|
|
18914
|
+
const asMatch = /\sas\s/i.exec(rest);
|
|
18915
|
+
if (asMatch) {
|
|
18916
|
+
id = stripQuotes(rest.slice(0, asMatch.index).trim());
|
|
18917
|
+
name = stripQuotes(rest.slice(asMatch.index + asMatch[0].length).trim());
|
|
18918
|
+
} else {
|
|
18919
|
+
id = stripQuotes(rest);
|
|
18920
|
+
name = id;
|
|
18921
|
+
}
|
|
18922
|
+
if (!id) {
|
|
18923
|
+
throw new SequenceParseError(`'${fw}' declaration needs a name`, line2.n);
|
|
18924
|
+
}
|
|
18925
|
+
const existing = this.byId.get(id);
|
|
18926
|
+
if (existing) {
|
|
18927
|
+
existing.kind = kind;
|
|
18928
|
+
existing.name = name;
|
|
18929
|
+
if (stereotype) existing.stereotype = stereotype;
|
|
18930
|
+
} else {
|
|
18931
|
+
const p = { id, name, kind, line: line2.n };
|
|
18932
|
+
if (stereotype) p.stereotype = stereotype;
|
|
18933
|
+
this.byId.set(id, p);
|
|
18934
|
+
this.order.push(id);
|
|
18935
|
+
}
|
|
18936
|
+
}
|
|
18937
|
+
// ── block / statement dispatch ───────────────────────────────
|
|
18938
|
+
/** Parse statements until a terminator (`end`/`else`/`and`) or EOF. Terminators are left unconsumed. */
|
|
18939
|
+
parseBlock() {
|
|
18940
|
+
const out = [];
|
|
18941
|
+
for (; ; ) {
|
|
18942
|
+
const ln = this.peek();
|
|
18943
|
+
if (!ln) break;
|
|
18944
|
+
const fw = firstWord(ln.text).toLowerCase();
|
|
18945
|
+
if (fw === "end" || fw === "else" || fw === "and") break;
|
|
18946
|
+
if (KINDS.has(fw)) {
|
|
18947
|
+
this.i++;
|
|
18948
|
+
this.declare(ln);
|
|
18949
|
+
continue;
|
|
18950
|
+
}
|
|
18951
|
+
if (ALL_FRAG.has(fw)) {
|
|
18952
|
+
out.push(this.parseFragment(ln));
|
|
18953
|
+
continue;
|
|
18954
|
+
}
|
|
18955
|
+
this.i++;
|
|
18956
|
+
const stmt = this.parseSimpleStatement(ln);
|
|
18957
|
+
if (stmt) out.push(stmt);
|
|
18958
|
+
}
|
|
18959
|
+
return out;
|
|
18960
|
+
}
|
|
18961
|
+
parseFragment(open) {
|
|
18962
|
+
const fw = firstWord(open.text).toLowerCase();
|
|
18963
|
+
this.i++;
|
|
18964
|
+
let rest = open.text.slice(fw.length);
|
|
18965
|
+
let messageSet;
|
|
18966
|
+
if (MSGSET_FRAG.has(fw)) {
|
|
18967
|
+
const bm = /\{([^}]*)\}/.exec(rest);
|
|
18968
|
+
if (bm) {
|
|
18969
|
+
messageSet = bm[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
18970
|
+
rest = rest.slice(0, bm.index) + rest.slice(bm.index + bm[0].length);
|
|
18971
|
+
}
|
|
18972
|
+
}
|
|
18973
|
+
const firstGuard = extractGuard(rest);
|
|
18974
|
+
const operands = [
|
|
18975
|
+
{ guard: firstGuard, statements: this.parseBlock() }
|
|
18976
|
+
];
|
|
18977
|
+
for (; ; ) {
|
|
18978
|
+
const ln = this.peek();
|
|
18979
|
+
if (!ln) {
|
|
18980
|
+
throw new SequenceParseError(`Unterminated '${fw}' fragment`, open.n);
|
|
18981
|
+
}
|
|
18982
|
+
const term = firstWord(ln.text).toLowerCase();
|
|
18983
|
+
if (term === "end") {
|
|
18984
|
+
this.i++;
|
|
18985
|
+
break;
|
|
18986
|
+
}
|
|
18987
|
+
if (term === "else") {
|
|
18988
|
+
if (fw !== "alt") {
|
|
18989
|
+
throw new SequenceParseError(
|
|
18990
|
+
`'else' is only valid inside an 'alt' fragment, not '${fw}'`,
|
|
18991
|
+
ln.n
|
|
18992
|
+
);
|
|
18993
|
+
}
|
|
18994
|
+
this.i++;
|
|
18995
|
+
operands.push({
|
|
18996
|
+
guard: extractGuard(ln.text.slice(4)),
|
|
18997
|
+
statements: this.parseBlock()
|
|
18998
|
+
});
|
|
18999
|
+
continue;
|
|
19000
|
+
}
|
|
19001
|
+
if (term === "and") {
|
|
19002
|
+
if (!MULTI_FRAG.has(fw)) {
|
|
19003
|
+
throw new SequenceParseError(
|
|
19004
|
+
`'and' is only valid inside par/seq/strict, not '${fw}'`,
|
|
19005
|
+
ln.n
|
|
19006
|
+
);
|
|
19007
|
+
}
|
|
19008
|
+
this.i++;
|
|
19009
|
+
const label = ln.text.slice(3).trim();
|
|
19010
|
+
operands.push({
|
|
19011
|
+
guard: label ? extractGuard(label) : void 0,
|
|
19012
|
+
statements: this.parseBlock()
|
|
19013
|
+
});
|
|
19014
|
+
continue;
|
|
19015
|
+
}
|
|
19016
|
+
throw new SequenceParseError(`Unexpected '${term}' in '${fw}'`, ln.n);
|
|
19017
|
+
}
|
|
19018
|
+
const frag = { kind: "fragment", op: fw, operands, line: open.n };
|
|
19019
|
+
if (messageSet && messageSet.length) frag.messageSet = messageSet;
|
|
19020
|
+
return frag;
|
|
19021
|
+
}
|
|
19022
|
+
/** Non-fragment, non-participant statements. */
|
|
19023
|
+
parseSimpleStatement(ln) {
|
|
19024
|
+
const text2 = ln.text;
|
|
19025
|
+
const fw = firstWord(text2).toLowerCase();
|
|
19026
|
+
if (fw === "activate" || fw === "deactivate") {
|
|
19027
|
+
const m = /^(?:de)?activate\s+(\S+)$/i.exec(text2);
|
|
19028
|
+
if (!m) throw new SequenceParseError(`Malformed ${fw} statement`, ln.n);
|
|
19029
|
+
this.ensure(m[1], "participant", ln.n);
|
|
19030
|
+
return { kind: fw, id: m[1], line: ln.n };
|
|
19031
|
+
}
|
|
19032
|
+
if (fw === "note") {
|
|
19033
|
+
return this.parseNote(ln);
|
|
19034
|
+
}
|
|
19035
|
+
if (fw === "ref") {
|
|
19036
|
+
const m = /^ref\s+over\s+(.+?)\s*:\s*(.*)$/i.exec(text2);
|
|
19037
|
+
if (!m) throw new SequenceParseError("Malformed ref (expected: ref over A, B : text)", ln.n);
|
|
19038
|
+
const ids = m[1].split(",").map((s) => stripQuotes(s.trim())).filter(Boolean);
|
|
19039
|
+
ids.forEach((id) => this.ensure(id, "participant", ln.n));
|
|
19040
|
+
return { kind: "ref", ids, text: m[2].trim(), line: ln.n };
|
|
19041
|
+
}
|
|
19042
|
+
if (text2.startsWith("==")) {
|
|
19043
|
+
const m = /^==\s*(.*?)\s*==\s*$/.exec(text2);
|
|
19044
|
+
return { kind: "divider", text: m ? m[1].trim() : "", line: ln.n };
|
|
19045
|
+
}
|
|
19046
|
+
if (fw === "state") {
|
|
19047
|
+
const m = /^state\s+(\S+)\s*:\s*(.*)$/i.exec(text2);
|
|
19048
|
+
if (!m) throw new SequenceParseError("Malformed state invariant (expected: state A : text)", ln.n);
|
|
19049
|
+
this.ensure(m[1], "participant", ln.n);
|
|
19050
|
+
return { kind: "invariant", id: m[1], text: m[2].trim(), line: ln.n };
|
|
19051
|
+
}
|
|
19052
|
+
if (fw === "destroy") {
|
|
19053
|
+
const m = /^destroy\s+(\S+)$/i.exec(text2);
|
|
19054
|
+
if (!m) throw new SequenceParseError("Malformed destroy statement", ln.n);
|
|
19055
|
+
this.ensure(m[1], "participant", ln.n);
|
|
19056
|
+
return { kind: "destroy", id: m[1], line: ln.n };
|
|
19057
|
+
}
|
|
19058
|
+
if (fw === "autonumber") {
|
|
19059
|
+
const m = /^autonumber(?:\s+(\d+))?(?:\s+(\d+))?$/i.exec(text2);
|
|
19060
|
+
const start = m && m[1] ? parseInt(m[1], 10) : 1;
|
|
19061
|
+
const step = m && m[2] ? parseInt(m[2], 10) : 1;
|
|
19062
|
+
this.autonumberSpec = { start, step };
|
|
19063
|
+
return null;
|
|
19064
|
+
}
|
|
19065
|
+
return this.parseMessage(ln);
|
|
19066
|
+
}
|
|
19067
|
+
parseNote(ln) {
|
|
19068
|
+
const m = /^note\s+(over|left of|right of)\s+(.+?)\s*:\s*(.*)$/i.exec(ln.text);
|
|
19069
|
+
if (!m) {
|
|
19070
|
+
throw new SequenceParseError(
|
|
19071
|
+
"Malformed note (expected: note over|left of|right of A[, B] : text)",
|
|
19072
|
+
ln.n
|
|
19073
|
+
);
|
|
19074
|
+
}
|
|
19075
|
+
const placementRaw = m[1].toLowerCase();
|
|
19076
|
+
const placement = placementRaw === "over" ? "over" : placementRaw.startsWith("left") ? "left" : "right";
|
|
19077
|
+
const ids = m[2].split(",").map((s) => stripQuotes(s.trim())).filter(Boolean);
|
|
19078
|
+
ids.forEach((id) => this.ensure(id, "participant", ln.n));
|
|
19079
|
+
return { kind: "note", placement, ids, text: m[3].trim(), line: ln.n };
|
|
19080
|
+
}
|
|
19081
|
+
parseMessage(ln) {
|
|
19082
|
+
const text2 = ln.text;
|
|
19083
|
+
const arrowMatch = ARROW_RE.exec(text2);
|
|
19084
|
+
if (!arrowMatch) {
|
|
19085
|
+
throw new SequenceParseError(`Cannot parse statement: "${text2}"`, ln.n);
|
|
19086
|
+
}
|
|
19087
|
+
const token = arrowMatch[1];
|
|
19088
|
+
const at = arrowMatch.index;
|
|
19089
|
+
const leftRaw = text2.slice(0, at);
|
|
19090
|
+
let rightRaw = text2.slice(at + token.length);
|
|
19091
|
+
let label;
|
|
19092
|
+
const colon = rightRaw.indexOf(":");
|
|
19093
|
+
if (colon >= 0) {
|
|
19094
|
+
label = rightRaw.slice(colon + 1).trim();
|
|
19095
|
+
rightRaw = rightRaw.slice(0, colon);
|
|
19096
|
+
}
|
|
19097
|
+
let activateTarget = false;
|
|
19098
|
+
let deactivateSource = false;
|
|
19099
|
+
let left = leftRaw.trim();
|
|
19100
|
+
if (left.endsWith("+")) {
|
|
19101
|
+
activateTarget = true;
|
|
19102
|
+
left = left.slice(0, -1).trim();
|
|
19103
|
+
} else if (left.endsWith("-")) {
|
|
19104
|
+
deactivateSource = true;
|
|
19105
|
+
left = left.slice(0, -1).trim();
|
|
19106
|
+
}
|
|
19107
|
+
let right = rightRaw.trim();
|
|
19108
|
+
if (right.startsWith("+")) {
|
|
19109
|
+
activateTarget = true;
|
|
19110
|
+
right = right.slice(1).trim();
|
|
19111
|
+
} else if (right.startsWith("-")) {
|
|
19112
|
+
deactivateSource = true;
|
|
19113
|
+
right = right.slice(1).trim();
|
|
19114
|
+
}
|
|
19115
|
+
const arrow = arrowKind(token);
|
|
19116
|
+
let create = false;
|
|
19117
|
+
if (right.startsWith("*")) {
|
|
19118
|
+
create = true;
|
|
19119
|
+
right = right.slice(1).trim();
|
|
19120
|
+
}
|
|
19121
|
+
const from = arrow === "found" ? "" : stripQuotes(left);
|
|
19122
|
+
const to = arrow === "lost" ? "" : stripQuotes(right);
|
|
19123
|
+
if (from) this.ensure(from, "participant", ln.n);
|
|
19124
|
+
if (to) {
|
|
19125
|
+
const p = this.ensure(to, "participant", ln.n);
|
|
19126
|
+
if (create) p.createdInline = true;
|
|
19127
|
+
}
|
|
19128
|
+
const msg = {
|
|
19129
|
+
kind: "message",
|
|
19130
|
+
from,
|
|
19131
|
+
to,
|
|
19132
|
+
arrow,
|
|
19133
|
+
line: ln.n
|
|
19134
|
+
};
|
|
19135
|
+
if (label) msg.label = label;
|
|
19136
|
+
if (activateTarget) msg.activateTarget = true;
|
|
19137
|
+
if (deactivateSource) msg.deactivateSource = true;
|
|
19138
|
+
if (create) msg.create = true;
|
|
19139
|
+
return msg;
|
|
19140
|
+
}
|
|
19141
|
+
};
|
|
19142
|
+
function parseSequence(text2) {
|
|
19143
|
+
return new SequenceParser(text2).parse();
|
|
19144
|
+
}
|
|
19145
|
+
|
|
19146
|
+
// src/diagrams/sequence/layout.ts
|
|
19147
|
+
var SEQ_CONST = {
|
|
19148
|
+
HEAD_W_MIN: 90,
|
|
19149
|
+
HEAD_H: 38,
|
|
19150
|
+
HEAD_PAD_X: 14,
|
|
19151
|
+
LIFELINE_GAP: 140,
|
|
19152
|
+
LEFT_MARGIN: 24,
|
|
19153
|
+
RIGHT_PAD: 28,
|
|
19154
|
+
TOP_PAD: 16,
|
|
19155
|
+
HEAD_TO_FIRST: 30,
|
|
19156
|
+
EVENT_GAP: 38,
|
|
19157
|
+
BOTTOM_PAD: 30,
|
|
19158
|
+
SELF_GAP: 30,
|
|
19159
|
+
SELF_LOOP_W: 48,
|
|
19160
|
+
ACT_W: 10,
|
|
19161
|
+
ACT_NEST_DX: 6,
|
|
19162
|
+
ACT_STEP: 30,
|
|
19163
|
+
FRAG_PAD_X: 10,
|
|
19164
|
+
FRAG_LABEL_H: 18,
|
|
19165
|
+
FRAG_PAD_TOP: 16,
|
|
19166
|
+
FRAG_PAD_BOTTOM: 12,
|
|
19167
|
+
FRAG_GAP_BEFORE: 12,
|
|
19168
|
+
FRAG_OPERAND_GAP: 18,
|
|
19169
|
+
FRAG_NEST_INSET: 8,
|
|
19170
|
+
REF_H: 40,
|
|
19171
|
+
REF_GAP_BEFORE: 12,
|
|
19172
|
+
NOTE_GAP_BEFORE: 10,
|
|
19173
|
+
NOTE_PAD: 8,
|
|
19174
|
+
NOTE_MIN_W: 70,
|
|
19175
|
+
NOTE_LINE_H: 15,
|
|
19176
|
+
NOTE_SIDE_GAP: 16,
|
|
19177
|
+
DIV_H: 26,
|
|
19178
|
+
DIV_GAP_BEFORE: 8,
|
|
19179
|
+
INV_GAP_BEFORE: 10,
|
|
19180
|
+
INV_PAD: 10,
|
|
19181
|
+
INV_H: 24,
|
|
19182
|
+
LABEL_GAP_PAD: 26,
|
|
19183
|
+
LOST_LEN: 72
|
|
19184
|
+
};
|
|
19185
|
+
var LABEL_SIZE = 11;
|
|
19186
|
+
var HEAD_TEXT_SIZE = 12.5;
|
|
19187
|
+
function textWidth(s, size) {
|
|
19188
|
+
let w = 0;
|
|
19189
|
+
for (const ch of s) {
|
|
19190
|
+
w += /[⺀-]/.test(ch) ? size : size * 0.55;
|
|
19191
|
+
}
|
|
19192
|
+
return w;
|
|
19193
|
+
}
|
|
19194
|
+
var SequenceLayout = class {
|
|
19195
|
+
ast;
|
|
19196
|
+
lifelines = [];
|
|
19197
|
+
colOf = /* @__PURE__ */ new Map();
|
|
19198
|
+
llById = /* @__PURE__ */ new Map();
|
|
19199
|
+
x = [];
|
|
19200
|
+
headW = [];
|
|
19201
|
+
y = 0;
|
|
19202
|
+
open = /* @__PURE__ */ new Map();
|
|
19203
|
+
colAccStack = [];
|
|
19204
|
+
messages = [];
|
|
19205
|
+
activations = [];
|
|
19206
|
+
fragments = [];
|
|
19207
|
+
refs = [];
|
|
19208
|
+
notes = [];
|
|
19209
|
+
dividers = [];
|
|
19210
|
+
invariants = [];
|
|
19211
|
+
destroys = [];
|
|
19212
|
+
warnings = [];
|
|
19213
|
+
autoCounter = null;
|
|
19214
|
+
contentRight = 0;
|
|
19215
|
+
constructor(ast) {
|
|
19216
|
+
this.ast = ast;
|
|
19217
|
+
}
|
|
19218
|
+
run() {
|
|
19219
|
+
this.buildColumns();
|
|
19220
|
+
if (this.ast.autonumber) this.autoCounter = this.ast.autonumber.start;
|
|
19221
|
+
const headBottom = SEQ_CONST.TOP_PAD + SEQ_CONST.HEAD_H;
|
|
19222
|
+
this.y = headBottom + SEQ_CONST.HEAD_TO_FIRST - SEQ_CONST.EVENT_GAP;
|
|
19223
|
+
this.walk(this.ast.statements, 0);
|
|
19224
|
+
const bottomY = this.y + SEQ_CONST.EVENT_GAP;
|
|
19225
|
+
for (const [id, stack] of this.open) {
|
|
19226
|
+
while (stack.length) {
|
|
19227
|
+
const b = stack.pop();
|
|
19228
|
+
this.activations.push({ id, x: b.x, yTop: b.yTop, yBottom: bottomY, level: b.level });
|
|
19229
|
+
}
|
|
19230
|
+
}
|
|
19231
|
+
const bodyBottom = bottomY + SEQ_CONST.BOTTOM_PAD;
|
|
19232
|
+
for (const ll of this.lifelines) {
|
|
19233
|
+
if (!ll.destroyed) ll.axisBottom = bodyBottom;
|
|
19234
|
+
}
|
|
19235
|
+
const lastCenter = this.x[this.x.length - 1] ?? SEQ_CONST.LEFT_MARGIN;
|
|
19236
|
+
const lastHalf = (this.headW[this.headW.length - 1] ?? SEQ_CONST.HEAD_W_MIN) / 2;
|
|
19237
|
+
this.contentRight = Math.max(this.contentRight, lastCenter + lastHalf);
|
|
19238
|
+
const width = Math.round(this.contentRight + SEQ_CONST.RIGHT_PAD);
|
|
19239
|
+
const height = Math.round(bodyBottom);
|
|
19240
|
+
for (const d of this.dividers) d.width = width;
|
|
19241
|
+
const result = {
|
|
19242
|
+
width,
|
|
19243
|
+
height,
|
|
19244
|
+
lifelines: this.lifelines,
|
|
19245
|
+
messages: this.messages,
|
|
19246
|
+
activations: this.activations,
|
|
19247
|
+
fragments: this.fragments,
|
|
19248
|
+
refs: this.refs,
|
|
19249
|
+
notes: this.notes,
|
|
19250
|
+
dividers: this.dividers,
|
|
19251
|
+
invariants: this.invariants,
|
|
19252
|
+
destroys: this.destroys,
|
|
19253
|
+
warnings: this.warnings.concat(this.ast.warnings),
|
|
19254
|
+
ast: this.ast
|
|
19255
|
+
};
|
|
19256
|
+
if (this.ast.title) result.title = this.ast.title;
|
|
19257
|
+
return result;
|
|
19258
|
+
}
|
|
19259
|
+
// ── horizontal pass ──────────────────────────────────────────
|
|
19260
|
+
buildColumns() {
|
|
19261
|
+
const ps = this.ast.participants;
|
|
19262
|
+
this.headW = ps.map(
|
|
19263
|
+
(p) => Math.max(SEQ_CONST.HEAD_W_MIN, Math.ceil(textWidth(p.name, HEAD_TEXT_SIZE)) + SEQ_CONST.HEAD_PAD_X * 2)
|
|
19264
|
+
);
|
|
19265
|
+
ps.forEach((p, i) => this.colOf.set(p.id, i));
|
|
19266
|
+
const n = ps.length;
|
|
19267
|
+
const gaps = new Array(Math.max(0, n - 1)).fill(SEQ_CONST.LIFELINE_GAP);
|
|
19268
|
+
for (let k = 0; k < gaps.length; k++) {
|
|
19269
|
+
gaps[k] = Math.max(gaps[k], this.headW[k] / 2 + this.headW[k + 1] / 2 + 28);
|
|
19270
|
+
}
|
|
19271
|
+
const msgs = collectMessages(this.ast.statements);
|
|
19272
|
+
for (const m of msgs) {
|
|
19273
|
+
const label = m.label ?? "";
|
|
19274
|
+
const w = textWidth(label, LABEL_SIZE) + SEQ_CONST.LABEL_GAP_PAD;
|
|
19275
|
+
const cf = this.colOf.get(m.from);
|
|
19276
|
+
const ct = this.colOf.get(m.to);
|
|
19277
|
+
if (cf === void 0 && ct === void 0) continue;
|
|
19278
|
+
if (cf === void 0 || ct === void 0) continue;
|
|
19279
|
+
if (cf === ct) {
|
|
19280
|
+
if (cf < gaps.length) gaps[cf] = Math.max(gaps[cf], SEQ_CONST.SELF_LOOP_W + w);
|
|
19281
|
+
continue;
|
|
19282
|
+
}
|
|
19283
|
+
const i = Math.min(cf, ct);
|
|
19284
|
+
const j = Math.max(cf, ct);
|
|
19285
|
+
if (j === i + 1) {
|
|
19286
|
+
gaps[i] = Math.max(gaps[i], w);
|
|
19287
|
+
} else {
|
|
19288
|
+
let span = 0;
|
|
19289
|
+
for (let k = i; k < j; k++) span += gaps[k];
|
|
19290
|
+
if (span < w) {
|
|
19291
|
+
const add = (w - span) / (j - i);
|
|
19292
|
+
for (let k = i; k < j; k++) gaps[k] += add;
|
|
19293
|
+
}
|
|
19294
|
+
}
|
|
19295
|
+
}
|
|
19296
|
+
this.x = new Array(n);
|
|
19297
|
+
let cx = SEQ_CONST.LEFT_MARGIN + (this.headW[0] ?? SEQ_CONST.HEAD_W_MIN) / 2;
|
|
19298
|
+
for (let i = 0; i < n; i++) {
|
|
19299
|
+
this.x[i] = cx;
|
|
19300
|
+
if (i < gaps.length) cx += gaps[i];
|
|
19301
|
+
}
|
|
19302
|
+
ps.forEach((p, i) => {
|
|
19303
|
+
const created = p.createdInline === true;
|
|
19304
|
+
const headY = created ? -1 : SEQ_CONST.TOP_PAD;
|
|
19305
|
+
const ll = {
|
|
19306
|
+
participant: p,
|
|
19307
|
+
index: i,
|
|
19308
|
+
x: this.x[i],
|
|
19309
|
+
headX: this.x[i] - this.headW[i] / 2,
|
|
19310
|
+
headY,
|
|
19311
|
+
headW: this.headW[i],
|
|
19312
|
+
headH: SEQ_CONST.HEAD_H,
|
|
19313
|
+
axisTop: created ? -1 : SEQ_CONST.TOP_PAD + SEQ_CONST.HEAD_H,
|
|
19314
|
+
axisBottom: 0,
|
|
19315
|
+
destroyed: false
|
|
19316
|
+
};
|
|
19317
|
+
this.lifelines.push(ll);
|
|
19318
|
+
this.llById.set(p.id, ll);
|
|
19319
|
+
});
|
|
19320
|
+
}
|
|
19321
|
+
// ── column accounting (for fragment extents) ─────────────────
|
|
19322
|
+
touch(col) {
|
|
19323
|
+
if (col === void 0) return;
|
|
19324
|
+
for (const acc of this.colAccStack) {
|
|
19325
|
+
if (col < acc.min) acc.min = col;
|
|
19326
|
+
if (col > acc.max) acc.max = col;
|
|
19327
|
+
}
|
|
19328
|
+
}
|
|
19329
|
+
noteRight(x, w) {
|
|
19330
|
+
this.contentRight = Math.max(this.contentRight, x + w);
|
|
19331
|
+
}
|
|
19332
|
+
// ── activation bars ──────────────────────────────────────────
|
|
19333
|
+
openBar(id) {
|
|
19334
|
+
const col = this.colOf.get(id);
|
|
19335
|
+
if (col === void 0) return;
|
|
19336
|
+
const stack = this.open.get(id) ?? [];
|
|
19337
|
+
const level = stack.length;
|
|
19338
|
+
const x = this.x[col] - SEQ_CONST.ACT_W / 2 + level * SEQ_CONST.ACT_NEST_DX;
|
|
19339
|
+
stack.push({ x, yTop: this.y, level });
|
|
19340
|
+
this.open.set(id, stack);
|
|
19341
|
+
}
|
|
19342
|
+
closeBar(id) {
|
|
19343
|
+
const stack = this.open.get(id);
|
|
19344
|
+
if (!stack || stack.length === 0) {
|
|
19345
|
+
this.warnings.push(`deactivate '${id}' with no matching activation`);
|
|
19346
|
+
return;
|
|
19347
|
+
}
|
|
19348
|
+
const b = stack.pop();
|
|
19349
|
+
this.activations.push({ id, x: b.x, yTop: b.yTop, yBottom: this.y, level: b.level });
|
|
19350
|
+
}
|
|
19351
|
+
faceEdge(id, col, towardRight, extraLevel = 0) {
|
|
19352
|
+
const stack = this.open.get(id) ?? [];
|
|
19353
|
+
const levels = stack.length + extraLevel;
|
|
19354
|
+
if (levels <= 0) return this.x[col];
|
|
19355
|
+
if (!towardRight) return this.x[col] - SEQ_CONST.ACT_W / 2;
|
|
19356
|
+
return this.x[col] + SEQ_CONST.ACT_W / 2 + (levels - 1) * SEQ_CONST.ACT_NEST_DX;
|
|
19357
|
+
}
|
|
19358
|
+
// ── vertical walk ────────────────────────────────────────────
|
|
19359
|
+
walk(stmts, depth) {
|
|
19360
|
+
for (const s of stmts) {
|
|
19361
|
+
switch (s.kind) {
|
|
19362
|
+
case "message":
|
|
19363
|
+
this.placeMessage(s);
|
|
19364
|
+
break;
|
|
19365
|
+
case "activate":
|
|
19366
|
+
this.y += SEQ_CONST.ACT_STEP;
|
|
19367
|
+
this.openBar(s.id);
|
|
19368
|
+
this.touch(this.colOf.get(s.id));
|
|
19369
|
+
break;
|
|
19370
|
+
case "deactivate":
|
|
19371
|
+
this.y += SEQ_CONST.ACT_STEP;
|
|
19372
|
+
this.closeBar(s.id);
|
|
19373
|
+
this.touch(this.colOf.get(s.id));
|
|
19374
|
+
break;
|
|
19375
|
+
case "note":
|
|
19376
|
+
this.placeNote(s);
|
|
19377
|
+
break;
|
|
19378
|
+
case "ref":
|
|
19379
|
+
this.placeRef(s.ids, s.text);
|
|
19380
|
+
break;
|
|
19381
|
+
case "divider":
|
|
19382
|
+
this.y += SEQ_CONST.DIV_GAP_BEFORE;
|
|
19383
|
+
this.dividers.push({ text: s.text, y: this.y + SEQ_CONST.DIV_H / 2, width: 0 });
|
|
19384
|
+
this.y += SEQ_CONST.DIV_H;
|
|
19385
|
+
break;
|
|
19386
|
+
case "invariant":
|
|
19387
|
+
this.placeInvariant(s.id, s.text);
|
|
19388
|
+
break;
|
|
19389
|
+
case "destroy":
|
|
19390
|
+
this.placeDestroy(s.id);
|
|
19391
|
+
break;
|
|
19392
|
+
case "fragment":
|
|
19393
|
+
this.placeFragment(s.op, s.operands, depth, s.messageSet);
|
|
19394
|
+
break;
|
|
19395
|
+
}
|
|
19396
|
+
}
|
|
19397
|
+
}
|
|
19398
|
+
nextNumber() {
|
|
19399
|
+
if (this.autoCounter === null || !this.ast.autonumber) return void 0;
|
|
19400
|
+
const n = this.autoCounter;
|
|
19401
|
+
this.autoCounter += this.ast.autonumber.step;
|
|
19402
|
+
return n;
|
|
19403
|
+
}
|
|
19404
|
+
placeMessage(m) {
|
|
19405
|
+
this.y += SEQ_CONST.EVENT_GAP;
|
|
19406
|
+
const y = this.y;
|
|
19407
|
+
const cf = this.colOf.get(m.from);
|
|
19408
|
+
const ct = this.colOf.get(m.to);
|
|
19409
|
+
const self = m.from !== "" && m.from === m.to;
|
|
19410
|
+
let x1;
|
|
19411
|
+
let x2;
|
|
19412
|
+
if (m.arrow === "found") {
|
|
19413
|
+
const c = ct;
|
|
19414
|
+
x2 = this.faceEdge(m.to, c, false);
|
|
19415
|
+
x1 = x2 - SEQ_CONST.LOST_LEN;
|
|
19416
|
+
} else if (m.arrow === "lost") {
|
|
19417
|
+
const c = cf;
|
|
19418
|
+
x1 = this.faceEdge(m.from, c, true);
|
|
19419
|
+
x2 = x1 + SEQ_CONST.LOST_LEN;
|
|
19420
|
+
} else if (self) {
|
|
19421
|
+
const c = cf;
|
|
19422
|
+
x1 = this.faceEdge(m.from, c, true);
|
|
19423
|
+
x2 = x1 + SEQ_CONST.SELF_LOOP_W;
|
|
19424
|
+
} else {
|
|
19425
|
+
const fromRight = (ct ?? 0) > (cf ?? 0);
|
|
19426
|
+
x1 = this.faceEdge(m.from, cf, fromRight);
|
|
19427
|
+
if (m.create) {
|
|
19428
|
+
const halfW = this.headW[ct] / 2;
|
|
19429
|
+
x2 = fromRight ? this.x[ct] - halfW : this.x[ct] + halfW;
|
|
19430
|
+
} else {
|
|
19431
|
+
x2 = this.faceEdge(m.to, ct, !fromRight, m.activateTarget ? 1 : 0);
|
|
19432
|
+
}
|
|
19433
|
+
}
|
|
19434
|
+
const row = { message: m, y, x1, x2, self };
|
|
19435
|
+
const num = this.nextNumber();
|
|
19436
|
+
if (num !== void 0) row.number = num;
|
|
19437
|
+
if (self) {
|
|
19438
|
+
row.selfBottomY = y + SEQ_CONST.SELF_GAP;
|
|
19439
|
+
this.y = row.selfBottomY;
|
|
19440
|
+
}
|
|
19441
|
+
this.messages.push(row);
|
|
19442
|
+
this.contentRight = Math.max(this.contentRight, x1, x2);
|
|
19443
|
+
this.touch(cf);
|
|
19444
|
+
this.touch(ct);
|
|
19445
|
+
if (m.create && ct !== void 0) {
|
|
19446
|
+
const ll = this.llById.get(m.to);
|
|
19447
|
+
ll.headY = y - SEQ_CONST.HEAD_H / 2;
|
|
19448
|
+
ll.axisTop = ll.headY + SEQ_CONST.HEAD_H;
|
|
19449
|
+
}
|
|
19450
|
+
if (m.activateTarget && ct !== void 0) this.openBar(m.to);
|
|
19451
|
+
if (m.deactivateSource && cf !== void 0) this.closeBar(m.from);
|
|
19452
|
+
}
|
|
19453
|
+
placeNote(note) {
|
|
19454
|
+
this.y += SEQ_CONST.NOTE_GAP_BEFORE;
|
|
19455
|
+
const lines = note.text.split(/<br\s*\/?>|\\n/);
|
|
19456
|
+
const textW = lines.reduce((mx, l) => Math.max(mx, textWidth(l, LABEL_SIZE)), 0);
|
|
19457
|
+
const w = Math.max(SEQ_CONST.NOTE_MIN_W, Math.ceil(textW) + SEQ_CONST.NOTE_PAD * 2);
|
|
19458
|
+
const h = lines.length * SEQ_CONST.NOTE_LINE_H + SEQ_CONST.NOTE_PAD * 2;
|
|
19459
|
+
const top = this.y;
|
|
19460
|
+
const cols = note.ids.map((id) => this.colOf.get(id)).filter((c) => c !== void 0);
|
|
19461
|
+
cols.forEach((c) => this.touch(c));
|
|
19462
|
+
let x;
|
|
19463
|
+
let boxW = w;
|
|
19464
|
+
if (note.placement === "over" && cols.length >= 2) {
|
|
19465
|
+
const a = this.x[Math.min(...cols)];
|
|
19466
|
+
const b = this.x[Math.max(...cols)];
|
|
19467
|
+
const span = b - a + this.headW[Math.min(...cols)] / 2 + this.headW[Math.max(...cols)] / 2;
|
|
19468
|
+
boxW = Math.max(w, span);
|
|
19469
|
+
x = (a + b) / 2 - boxW / 2;
|
|
19470
|
+
} else if (note.placement === "left" && cols.length) {
|
|
19471
|
+
x = this.x[cols[0]] - SEQ_CONST.NOTE_SIDE_GAP - w;
|
|
19472
|
+
} else if (note.placement === "right" && cols.length) {
|
|
19473
|
+
x = this.x[cols[0]] + SEQ_CONST.NOTE_SIDE_GAP;
|
|
19474
|
+
} else {
|
|
19475
|
+
const c = cols[0] ?? 0;
|
|
19476
|
+
x = this.x[c] - w / 2;
|
|
19477
|
+
}
|
|
19478
|
+
this.notes.push({ note, x, y: top, width: boxW, height: h });
|
|
19479
|
+
this.noteRight(x, boxW);
|
|
19480
|
+
this.y = top + h;
|
|
19481
|
+
}
|
|
19482
|
+
placeRef(ids, text2) {
|
|
19483
|
+
this.y += SEQ_CONST.REF_GAP_BEFORE;
|
|
19484
|
+
const cols = ids.map((id) => this.colOf.get(id)).filter((c) => c !== void 0);
|
|
19485
|
+
cols.forEach((c) => this.touch(c));
|
|
19486
|
+
const minC = cols.length ? Math.min(...cols) : 0;
|
|
19487
|
+
const maxC = cols.length ? Math.max(...cols) : 0;
|
|
19488
|
+
const left = this.x[minC] - SEQ_CONST.FRAG_PAD_X;
|
|
19489
|
+
const right = this.x[maxC] + SEQ_CONST.FRAG_PAD_X;
|
|
19490
|
+
const top = this.y;
|
|
19491
|
+
this.refs.push({ text: text2, x: left, y: top, width: right - left, height: SEQ_CONST.REF_H });
|
|
19492
|
+
this.noteRight(left, right - left);
|
|
19493
|
+
this.y = top + SEQ_CONST.REF_H;
|
|
19494
|
+
}
|
|
19495
|
+
placeInvariant(id, text2) {
|
|
19496
|
+
this.y += SEQ_CONST.INV_GAP_BEFORE;
|
|
19497
|
+
const col = this.colOf.get(id);
|
|
19498
|
+
this.touch(col);
|
|
19499
|
+
const w = Math.ceil(textWidth(text2, LABEL_SIZE)) + SEQ_CONST.INV_PAD * 2;
|
|
19500
|
+
const cx = col !== void 0 ? this.x[col] : SEQ_CONST.LEFT_MARGIN;
|
|
19501
|
+
const top = this.y;
|
|
19502
|
+
this.invariants.push({ text: text2, cx, y: top, width: w, height: SEQ_CONST.INV_H });
|
|
19503
|
+
this.noteRight(cx - w / 2, w);
|
|
19504
|
+
this.y = top + SEQ_CONST.INV_H;
|
|
19505
|
+
}
|
|
19506
|
+
placeDestroy(id) {
|
|
19507
|
+
this.y += SEQ_CONST.EVENT_GAP / 2;
|
|
19508
|
+
const ll = this.llById.get(id);
|
|
19509
|
+
const col = this.colOf.get(id);
|
|
19510
|
+
if (ll && col !== void 0) {
|
|
19511
|
+
ll.destroyed = true;
|
|
19512
|
+
ll.axisBottom = this.y;
|
|
19513
|
+
this.destroys.push({ x: this.x[col], y: this.y });
|
|
19514
|
+
this.touch(col);
|
|
19515
|
+
}
|
|
19516
|
+
}
|
|
19517
|
+
placeFragment(op, operands, depth, messageSet) {
|
|
19518
|
+
const frameTop = this.y + SEQ_CONST.FRAG_GAP_BEFORE;
|
|
19519
|
+
const acc = { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY };
|
|
19520
|
+
this.colAccStack.push(acc);
|
|
19521
|
+
const operandGeom = [];
|
|
19522
|
+
this.y = frameTop + SEQ_CONST.FRAG_LABEL_H + SEQ_CONST.FRAG_PAD_TOP - SEQ_CONST.EVENT_GAP;
|
|
19523
|
+
const firstGeom = {};
|
|
19524
|
+
if (operands[0]?.guard) firstGeom.guard = operands[0].guard;
|
|
19525
|
+
operandGeom.push(firstGeom);
|
|
19526
|
+
this.walk(operands[0]?.statements ?? [], depth + 1);
|
|
19527
|
+
for (let oi = 1; oi < operands.length; oi++) {
|
|
19528
|
+
const sepY = this.y + SEQ_CONST.FRAG_OPERAND_GAP;
|
|
19529
|
+
const geom = { sepY };
|
|
19530
|
+
if (operands[oi].guard) geom.guard = operands[oi].guard;
|
|
19531
|
+
operandGeom.push(geom);
|
|
19532
|
+
this.y = sepY + SEQ_CONST.FRAG_PAD_TOP - SEQ_CONST.EVENT_GAP;
|
|
19533
|
+
this.walk(operands[oi].statements, depth + 1);
|
|
19534
|
+
}
|
|
19535
|
+
const frameBottom = this.y + SEQ_CONST.FRAG_PAD_BOTTOM;
|
|
19536
|
+
this.colAccStack.pop();
|
|
19537
|
+
let minC = acc.min;
|
|
19538
|
+
let maxC = acc.max;
|
|
19539
|
+
if (!isFinite(minC)) {
|
|
19540
|
+
minC = 0;
|
|
19541
|
+
maxC = Math.max(0, this.lifelines.length - 1);
|
|
19542
|
+
}
|
|
19543
|
+
const inset = depth * SEQ_CONST.FRAG_NEST_INSET;
|
|
19544
|
+
let left = this.x[minC] - SEQ_CONST.FRAG_PAD_X + inset;
|
|
19545
|
+
let right = this.x[maxC] + SEQ_CONST.FRAG_PAD_X - inset;
|
|
19546
|
+
if (right - left < 40) {
|
|
19547
|
+
const mid = (left + right) / 2;
|
|
19548
|
+
left = mid - 20;
|
|
19549
|
+
right = mid + 20;
|
|
19550
|
+
}
|
|
19551
|
+
const frame = {
|
|
19552
|
+
op,
|
|
19553
|
+
x: left,
|
|
19554
|
+
y: frameTop,
|
|
19555
|
+
width: right - left,
|
|
19556
|
+
height: frameBottom - frameTop,
|
|
19557
|
+
operands: operandGeom
|
|
19558
|
+
};
|
|
19559
|
+
if (messageSet && messageSet.length) frame.messageSet = messageSet;
|
|
19560
|
+
this.fragments.push(frame);
|
|
19561
|
+
this.noteRight(left, right - left);
|
|
19562
|
+
this.y = frameBottom;
|
|
19563
|
+
}
|
|
19564
|
+
};
|
|
19565
|
+
function collectMessages(stmts) {
|
|
19566
|
+
const out = [];
|
|
19567
|
+
for (const s of stmts) {
|
|
19568
|
+
if (s.kind === "message") out.push(s);
|
|
19569
|
+
else if (s.kind === "fragment") {
|
|
19570
|
+
for (const op of s.operands) out.push(...collectMessages(op.statements));
|
|
19571
|
+
}
|
|
19572
|
+
}
|
|
19573
|
+
return out;
|
|
19574
|
+
}
|
|
19575
|
+
function layoutSequence(ast) {
|
|
19576
|
+
return new SequenceLayout(ast).run();
|
|
19577
|
+
}
|
|
19578
|
+
|
|
19579
|
+
// src/diagrams/sequence/renderer.ts
|
|
19580
|
+
var HEAD = "#e8f0fb";
|
|
19581
|
+
var HEAD_STROKE = "#5b85c0";
|
|
19582
|
+
function buildCss7(t) {
|
|
19583
|
+
return `
|
|
19584
|
+
.sx-seq { font-family: system-ui, -apple-system, sans-serif; }
|
|
19585
|
+
.sx-seq-axis { stroke: ${t.neutral}; stroke-width: 1; stroke-dasharray: 4 4; }
|
|
19586
|
+
.sx-seq-head rect, .sx-seq-head path { fill: ${HEAD}; stroke: ${HEAD_STROKE}; stroke-width: 1.4; }
|
|
19587
|
+
.sx-seq-head-name { font: 600 12.5px sans-serif; fill: ${t.text}; }
|
|
19588
|
+
.sx-seq-head-stereo { font: italic 10px sans-serif; fill: ${t.textMuted}; }
|
|
19589
|
+
.sx-seq-actor line, .sx-seq-actor circle { stroke: ${t.stroke}; stroke-width: 1.6; fill: none; stroke-linecap: round; }
|
|
19590
|
+
.sx-seq-actor-head { fill: ${t.bg}; }
|
|
19591
|
+
.sx-seq-icon { fill: ${t.bg}; stroke: ${HEAD_STROKE}; stroke-width: 1.6; }
|
|
19592
|
+
.sx-seq-icon-line { stroke: ${HEAD_STROKE}; stroke-width: 1.6; fill: none; stroke-linecap: round; }
|
|
19593
|
+
.sx-seq-icon-fill { fill: ${HEAD_STROKE}; stroke: none; }
|
|
19594
|
+
.sx-seq-act { fill: ${t.bg}; stroke: ${HEAD_STROKE}; stroke-width: 1.2; }
|
|
19595
|
+
.sx-seq-msg { stroke: ${t.stroke}; stroke-width: 1.5; fill: none; }
|
|
19596
|
+
.sx-seq-msg-reply { stroke: ${t.stroke}; stroke-width: 1.4; fill: none; stroke-dasharray: 6 4; }
|
|
19597
|
+
.sx-seq-msg-label { font: 11px sans-serif; fill: ${t.text}; }
|
|
19598
|
+
.sx-seq-msg-num { font: 600 10px sans-serif; fill: ${t.textMuted}; }
|
|
19599
|
+
.sx-seq-endpoint { fill: ${t.stroke}; }
|
|
19600
|
+
.sx-seq-frame { fill: none; stroke: ${t.neutral}; stroke-width: 1.2; }
|
|
19601
|
+
.sx-seq-frame-neg { fill: ${t.negative}; fill-opacity: 0.06; }
|
|
19602
|
+
.sx-seq-frame-sep { stroke: ${t.neutral}; stroke-width: 1; stroke-dasharray: 5 4; }
|
|
19603
|
+
.sx-seq-frame-tab { fill: ${t.fillMuted}; stroke: ${t.neutral}; stroke-width: 1.2; }
|
|
19604
|
+
.sx-seq-frame-op { font: 700 11px sans-serif; fill: ${t.text}; }
|
|
19605
|
+
.sx-seq-guard { font: italic 10.5px sans-serif; fill: ${t.textMuted}; }
|
|
19606
|
+
.sx-seq-ref-name { font: 600 12px sans-serif; fill: ${t.text}; }
|
|
19607
|
+
.sx-seq-note rect { fill: #fdf6da; stroke: #d9c97e; stroke-width: 1.1; }
|
|
19608
|
+
.sx-seq-note path { fill: #efe2a6; stroke: #d9c97e; stroke-width: 1.1; }
|
|
19609
|
+
.sx-seq-note-text { font: 11px sans-serif; fill: ${t.text}; }
|
|
19610
|
+
.sx-seq-div line { stroke: ${t.neutral}; stroke-width: 1; }
|
|
19611
|
+
.sx-seq-div rect { fill: ${t.fillMuted}; stroke: ${t.neutral}; stroke-width: 1; }
|
|
19612
|
+
.sx-seq-div-text { font: 600 11px sans-serif; fill: ${t.textMuted}; }
|
|
19613
|
+
.sx-seq-inv rect { fill: ${t.bg}; stroke: ${t.neutral}; stroke-width: 1.1; }
|
|
19614
|
+
.sx-seq-inv-text { font: italic 10.5px sans-serif; fill: ${t.textMuted}; }
|
|
19615
|
+
.sx-seq-destroy { stroke: ${t.negative}; stroke-width: 2; }
|
|
19616
|
+
.sx-seq-title { font: 700 16px sans-serif; fill: ${t.text}; }
|
|
19617
|
+
`.trim();
|
|
19618
|
+
}
|
|
19619
|
+
function markers3(t) {
|
|
19620
|
+
return defs([
|
|
19621
|
+
el(
|
|
19622
|
+
"marker",
|
|
19623
|
+
{ id: "sx-seq-filled", viewBox: "0 0 12 10", refX: 10, refY: 5, markerWidth: 11, markerHeight: 9, orient: "auto" },
|
|
19624
|
+
[el("polygon", { points: "0,0 11,5 0,10", fill: t.stroke })]
|
|
19625
|
+
),
|
|
19626
|
+
el(
|
|
19627
|
+
"marker",
|
|
19628
|
+
{ id: "sx-seq-open", viewBox: "0 0 12 10", refX: 10, refY: 5, markerWidth: 12, markerHeight: 10, orient: "auto" },
|
|
19629
|
+
[el("polyline", { points: "0,0 10,5 0,10", fill: "none", stroke: t.stroke, "stroke-width": 1.5 })]
|
|
19630
|
+
)
|
|
19631
|
+
]);
|
|
19632
|
+
}
|
|
19633
|
+
var CHAR_W2 = 6;
|
|
19634
|
+
function renderLifeline(ll) {
|
|
19635
|
+
const parts = [];
|
|
19636
|
+
parts.push(line({ class: "sx-seq-axis", x1: ll.x, y1: ll.axisTop, x2: ll.x, y2: ll.axisBottom }));
|
|
19637
|
+
parts.push(renderHead(ll));
|
|
19638
|
+
return group({ class: "sx-seq-lifeline", "data-id": ll.participant.id, "data-kind": ll.participant.kind }, parts);
|
|
19639
|
+
}
|
|
19640
|
+
function renderHead(ll) {
|
|
19641
|
+
const p = ll.participant;
|
|
19642
|
+
const cx = ll.x;
|
|
19643
|
+
const stereo = p.stereotype ? `\xAB${p.stereotype}\xBB` : null;
|
|
19644
|
+
if (p.kind === "actor") {
|
|
19645
|
+
const topY = ll.headY;
|
|
19646
|
+
const fig = [];
|
|
19647
|
+
if (stereo) {
|
|
19648
|
+
fig.push(text({ class: "sx-seq-head-stereo", x: cx, y: topY - 4, "text-anchor": "middle" }, stereo));
|
|
19649
|
+
}
|
|
19650
|
+
fig.push(
|
|
19651
|
+
circle({ class: "sx-seq-actor-head", cx, cy: topY + 6, r: 4.5 }),
|
|
19652
|
+
line({ x1: cx, y1: topY + 10, x2: cx, y2: topY + 22 }),
|
|
19653
|
+
line({ x1: cx - 8, y1: topY + 14, x2: cx + 8, y2: topY + 14 }),
|
|
19654
|
+
line({ x1: cx, y1: topY + 22, x2: cx - 7, y2: topY + 32 }),
|
|
19655
|
+
line({ x1: cx, y1: topY + 22, x2: cx + 7, y2: topY + 32 }),
|
|
19656
|
+
text({ class: "sx-seq-head-name", x: cx, y: topY + 44, "text-anchor": "middle" }, p.name)
|
|
19657
|
+
);
|
|
19658
|
+
return group({ class: "sx-seq-actor sx-seq-head" }, fig);
|
|
19659
|
+
}
|
|
19660
|
+
if (p.kind === "boundary" || p.kind === "control" || p.kind === "entity") {
|
|
19661
|
+
return renderRobustnessIcon(ll, stereo);
|
|
19662
|
+
}
|
|
19663
|
+
if (p.kind === "database") {
|
|
19664
|
+
const w = ll.headW;
|
|
19665
|
+
const h = ll.headH;
|
|
19666
|
+
const x = ll.headX;
|
|
19667
|
+
const y = ll.headY;
|
|
19668
|
+
const ry = 5;
|
|
19669
|
+
const d = `M ${x} ${y + ry} a ${w / 2} ${ry} 0 0 1 ${w} 0 v ${h - 2 * ry} a ${w / 2} ${ry} 0 0 1 ${-w} 0 Z M ${x} ${y + ry} a ${w / 2} ${ry} 0 0 0 ${w} 0`;
|
|
19670
|
+
return group({ class: "sx-seq-head" }, [
|
|
19671
|
+
path({ d }),
|
|
19672
|
+
text({ class: "sx-seq-head-name", x: cx, y: y + h / 2 + 6, "text-anchor": "middle" }, p.name)
|
|
19673
|
+
]);
|
|
19674
|
+
}
|
|
19675
|
+
const parts = [
|
|
19676
|
+
rect({ x: ll.headX, y: ll.headY, width: ll.headW, height: ll.headH, rx: 3, ry: 3 })
|
|
19677
|
+
];
|
|
19678
|
+
const stereoText = stereo ?? (p.kind === "collections" || p.kind === "queue" ? `\xAB${p.kind}\xBB` : null);
|
|
19679
|
+
if (stereoText) {
|
|
19680
|
+
parts.push(
|
|
19681
|
+
text({ class: "sx-seq-head-stereo", x: cx, y: ll.headY + 14, "text-anchor": "middle" }, stereoText)
|
|
19682
|
+
);
|
|
19683
|
+
parts.push(
|
|
19684
|
+
text({ class: "sx-seq-head-name", x: cx, y: ll.headY + 28, "text-anchor": "middle" }, p.name)
|
|
19685
|
+
);
|
|
19686
|
+
} else {
|
|
19687
|
+
parts.push(
|
|
19688
|
+
text({ class: "sx-seq-head-name", x: cx, y: ll.headY + ll.headH / 2 + 5, "text-anchor": "middle" }, p.name)
|
|
19689
|
+
);
|
|
19690
|
+
}
|
|
19691
|
+
return group({ class: "sx-seq-head" }, parts);
|
|
19692
|
+
}
|
|
19693
|
+
function renderRobustnessIcon(ll, stereo) {
|
|
19694
|
+
const p = ll.participant;
|
|
19695
|
+
const cx = ll.x;
|
|
19696
|
+
const R = 11;
|
|
19697
|
+
const cy = ll.headY + 14;
|
|
19698
|
+
const parts = [];
|
|
19699
|
+
if (stereo) {
|
|
19700
|
+
parts.push(text({ class: "sx-seq-head-stereo", x: cx, y: ll.headY - 2, "text-anchor": "middle" }, stereo));
|
|
19701
|
+
}
|
|
19702
|
+
parts.push(circle({ class: "sx-seq-icon", cx, cy, r: R }));
|
|
19703
|
+
if (p.kind === "boundary") {
|
|
19704
|
+
const bx = cx - R - 9;
|
|
19705
|
+
parts.push(line({ class: "sx-seq-icon-line", x1: bx, y1: cy - R, x2: bx, y2: cy + R }));
|
|
19706
|
+
parts.push(line({ class: "sx-seq-icon-line", x1: bx, y1: cy, x2: cx - R, y2: cy }));
|
|
19707
|
+
} else if (p.kind === "entity") {
|
|
19708
|
+
parts.push(line({ class: "sx-seq-icon-line", x1: cx - R - 2, y1: cy + R + 1, x2: cx + R + 2, y2: cy + R + 1 }));
|
|
19709
|
+
} else {
|
|
19710
|
+
parts.push(
|
|
19711
|
+
polygon({ class: "sx-seq-icon-fill", points: `${cx - 2},${cy - R - 6} ${cx + 5},${cy - R - 1} ${cx - 3},${cy - R + 2}` })
|
|
19712
|
+
);
|
|
19713
|
+
}
|
|
19714
|
+
parts.push(text({ class: "sx-seq-head-name", x: cx, y: ll.headY + 44, "text-anchor": "middle" }, p.name));
|
|
19715
|
+
return group({ class: "sx-seq-head sx-seq-icon-head", "data-icon": p.kind }, parts);
|
|
19716
|
+
}
|
|
19717
|
+
function renderActivation(a) {
|
|
19718
|
+
const h = Math.max(6, a.yBottom - a.yTop);
|
|
19719
|
+
return rect({
|
|
19720
|
+
class: "sx-seq-act",
|
|
19721
|
+
"data-id": a.id,
|
|
19722
|
+
x: a.x,
|
|
19723
|
+
y: a.yTop,
|
|
19724
|
+
width: 10,
|
|
19725
|
+
height: h
|
|
19726
|
+
});
|
|
19727
|
+
}
|
|
19728
|
+
function renderMessage(m) {
|
|
19729
|
+
const k = m.message.arrow;
|
|
19730
|
+
const lineClass2 = k === "reply" ? "sx-seq-msg-reply" : "sx-seq-msg";
|
|
19731
|
+
const markerEnd = k === "async" || k === "reply" ? "url(#sx-seq-open)" : "url(#sx-seq-filled)";
|
|
19732
|
+
const parts = [];
|
|
19733
|
+
if (m.self) {
|
|
19734
|
+
const bottom = m.selfBottomY ?? m.y + 28;
|
|
19735
|
+
const right = m.x2;
|
|
19736
|
+
const d = `M ${m.x1} ${m.y} H ${right} V ${bottom} H ${m.x1}`;
|
|
19737
|
+
parts.push(path({ class: lineClass2, d, "marker-end": markerEnd }));
|
|
19738
|
+
if (m.message.label) {
|
|
19739
|
+
parts.push(
|
|
19740
|
+
text(
|
|
19741
|
+
{ class: "sx-seq-msg-label", x: right + 6, y: (m.y + bottom) / 2 + 3 },
|
|
19742
|
+
numbered(m, m.message.label)
|
|
19743
|
+
)
|
|
19744
|
+
);
|
|
19745
|
+
}
|
|
19746
|
+
} else {
|
|
19747
|
+
parts.push(line({ class: lineClass2, x1: m.x1, y1: m.y, x2: m.x2, y2: m.y, "marker-end": markerEnd }));
|
|
19748
|
+
if (k === "lost") parts.push(circle({ class: "sx-seq-endpoint", cx: m.x2, cy: m.y, r: 4 }));
|
|
19749
|
+
if (k === "found") parts.push(circle({ class: "sx-seq-endpoint", cx: m.x1, cy: m.y, r: 4 }));
|
|
19750
|
+
if (m.message.label) {
|
|
19751
|
+
const mid = (m.x1 + m.x2) / 2;
|
|
19752
|
+
parts.push(
|
|
19753
|
+
text(
|
|
19754
|
+
{ class: "sx-seq-msg-label", x: mid, y: m.y - 6, "text-anchor": "middle" },
|
|
19755
|
+
numbered(m, m.message.label)
|
|
19756
|
+
)
|
|
19757
|
+
);
|
|
19758
|
+
}
|
|
19759
|
+
}
|
|
19760
|
+
return group(
|
|
19761
|
+
{
|
|
19762
|
+
class: "sx-seq-message",
|
|
19763
|
+
"data-kind": k,
|
|
19764
|
+
"data-from": m.message.from,
|
|
19765
|
+
"data-to": m.message.to
|
|
19766
|
+
},
|
|
19767
|
+
parts
|
|
19768
|
+
);
|
|
19769
|
+
}
|
|
19770
|
+
function numbered(m, label) {
|
|
19771
|
+
return m.number !== void 0 ? `${m.number}. ${label}` : label;
|
|
19772
|
+
}
|
|
19773
|
+
function tabWidth(f) {
|
|
19774
|
+
return Math.max(40, f.op.length * 8 + 22);
|
|
19775
|
+
}
|
|
19776
|
+
function renderFragmentBox(f) {
|
|
19777
|
+
const frameClass = f.op === "neg" ? "sx-seq-frame sx-seq-frame-neg" : "sx-seq-frame";
|
|
19778
|
+
const parts = [
|
|
19779
|
+
rect({ class: frameClass, x: f.x, y: f.y, width: f.width, height: f.height, rx: 2, ry: 2 })
|
|
19780
|
+
];
|
|
19781
|
+
for (const op of f.operands) {
|
|
19782
|
+
if (op.sepY !== void 0) {
|
|
19783
|
+
parts.push(line({ class: "sx-seq-frame-sep", x1: f.x, y1: op.sepY, x2: f.x + f.width, y2: op.sepY }));
|
|
19784
|
+
}
|
|
19785
|
+
}
|
|
19786
|
+
return group({ class: "sx-seq-fragment", "data-op": f.op }, parts);
|
|
19787
|
+
}
|
|
19788
|
+
function renderFragmentTab(f) {
|
|
19789
|
+
const tabW = tabWidth(f);
|
|
19790
|
+
const tabH = 18;
|
|
19791
|
+
const fold = 6;
|
|
19792
|
+
const tx = f.x;
|
|
19793
|
+
const ty = f.y;
|
|
19794
|
+
const tab = `M ${tx} ${ty} H ${tx + tabW} V ${ty + tabH - fold} L ${tx + tabW - fold} ${ty + tabH} H ${tx} Z`;
|
|
19795
|
+
const parts = [
|
|
19796
|
+
path({ class: "sx-seq-frame-tab", d: tab }),
|
|
19797
|
+
text({ class: "sx-seq-frame-op", x: tx + 8, y: ty + 13 }, f.op)
|
|
19798
|
+
];
|
|
19799
|
+
if (f.messageSet && f.messageSet.length) {
|
|
19800
|
+
parts.push(
|
|
19801
|
+
text({ class: "sx-seq-guard", x: tx + tabW + 8, y: ty + 13 }, `{${f.messageSet.join(", ")}}`)
|
|
19802
|
+
);
|
|
19803
|
+
}
|
|
19804
|
+
f.operands.forEach((op, i) => {
|
|
19805
|
+
if (!op.guard) return;
|
|
19806
|
+
const gx = i === 0 ? f.x + tabW + 8 : f.x + 8;
|
|
19807
|
+
const gy = i === 0 ? f.y + 13 : (op.sepY ?? f.y) + 14;
|
|
19808
|
+
parts.push(text({ class: "sx-seq-guard", x: gx, y: gy }, `[${op.guard}]`));
|
|
19809
|
+
});
|
|
19810
|
+
return group({ class: "sx-seq-fragment-tab-g", "data-op": f.op }, parts);
|
|
19811
|
+
}
|
|
19812
|
+
function renderRef(r) {
|
|
19813
|
+
const tabW = 40;
|
|
19814
|
+
const tabH = 18;
|
|
19815
|
+
const fold = 6;
|
|
19816
|
+
const tab = `M ${r.x} ${r.y} H ${r.x + tabW} V ${r.y + tabH - fold} L ${r.x + tabW - fold} ${r.y + tabH} H ${r.x} Z`;
|
|
19817
|
+
return group({ class: "sx-seq-fragment", "data-op": "ref" }, [
|
|
19818
|
+
rect({ class: "sx-seq-frame", x: r.x, y: r.y, width: r.width, height: r.height, rx: 2, ry: 2 }),
|
|
19819
|
+
path({ class: "sx-seq-frame-tab", d: tab }),
|
|
19820
|
+
text({ class: "sx-seq-frame-op", x: r.x + 8, y: r.y + 13 }, "ref"),
|
|
19821
|
+
text(
|
|
19822
|
+
{ class: "sx-seq-ref-name", x: r.x + r.width / 2, y: r.y + r.height / 2 + 8, "text-anchor": "middle" },
|
|
19823
|
+
r.text
|
|
19824
|
+
)
|
|
19825
|
+
]);
|
|
19826
|
+
}
|
|
19827
|
+
function renderNote2(nb) {
|
|
19828
|
+
const fold = 8;
|
|
19829
|
+
const x = nb.x;
|
|
19830
|
+
const y = nb.y;
|
|
19831
|
+
const w = nb.width;
|
|
19832
|
+
const h = nb.height;
|
|
19833
|
+
const body = `M ${x} ${y} H ${x + w - fold} L ${x + w} ${y + fold} V ${y + h} H ${x} Z`;
|
|
19834
|
+
const corner = `M ${x + w - fold} ${y} V ${y + fold} H ${x + w} Z`;
|
|
19835
|
+
const lines = nb.note.text.split(/<br\s*\/?>|\\n/);
|
|
19836
|
+
const startY = y + h / 2 - (lines.length - 1) * 15 / 2 + 4;
|
|
19837
|
+
const texts = lines.map(
|
|
19838
|
+
(l, i) => text({ class: "sx-seq-note-text", x: x + w / 2, y: startY + i * 15, "text-anchor": "middle" }, l)
|
|
19839
|
+
);
|
|
19840
|
+
return group({ class: "sx-seq-note" }, [path({ d: body }), path({ d: corner }), ...texts]);
|
|
19841
|
+
}
|
|
19842
|
+
function renderDivider(d) {
|
|
19843
|
+
const labelW = Math.max(60, d.text.length * CHAR_W2 + 24);
|
|
19844
|
+
const cx = d.width / 2;
|
|
19845
|
+
const parts = [
|
|
19846
|
+
line({ x1: 8, y1: d.y, x2: cx - labelW / 2, y2: d.y }),
|
|
19847
|
+
line({ x1: cx + labelW / 2, y1: d.y, x2: d.width - 8, y2: d.y }),
|
|
19848
|
+
rect({ x: cx - labelW / 2, y: d.y - 11, width: labelW, height: 22, rx: 4, ry: 4 }),
|
|
19849
|
+
text({ class: "sx-seq-div-text", x: cx, y: d.y + 4, "text-anchor": "middle" }, d.text)
|
|
19850
|
+
];
|
|
19851
|
+
return group({ class: "sx-seq-div" }, parts);
|
|
19852
|
+
}
|
|
19853
|
+
function renderInvariant(iv) {
|
|
19854
|
+
return group({ class: "sx-seq-inv" }, [
|
|
19855
|
+
rect({ x: iv.cx - iv.width / 2, y: iv.y, width: iv.width, height: iv.height, rx: iv.height / 2, ry: iv.height / 2 }),
|
|
19856
|
+
text({ class: "sx-seq-inv-text", x: iv.cx, y: iv.y + iv.height / 2 + 4, "text-anchor": "middle" }, `{${iv.text}}`)
|
|
19857
|
+
]);
|
|
19858
|
+
}
|
|
19859
|
+
function renderDestroy(d) {
|
|
19860
|
+
const r = 7;
|
|
19861
|
+
return group({ class: "sx-seq-destroy-g" }, [
|
|
19862
|
+
line({ class: "sx-seq-destroy", x1: d.x - r, y1: d.y - r, x2: d.x + r, y2: d.y + r }),
|
|
19863
|
+
line({ class: "sx-seq-destroy", x1: d.x - r, y1: d.y + r, x2: d.x + r, y2: d.y - r })
|
|
19864
|
+
]);
|
|
19865
|
+
}
|
|
19866
|
+
function renderSequenceLayout(layout, config) {
|
|
19867
|
+
const t = resolveBaseTheme(config?.theme ?? "default");
|
|
19868
|
+
const children = [];
|
|
19869
|
+
const nMsg = layout.messages.length;
|
|
19870
|
+
const nFrag = layout.fragments.length;
|
|
19871
|
+
children.push(title(`Sequence Diagram${layout.title ? " \u2014 " + layout.title : ""}`));
|
|
19872
|
+
children.push(
|
|
19873
|
+
desc(
|
|
19874
|
+
`${layout.lifelines.length} participants, ${nMsg} messages, ${nFrag} combined fragments.`
|
|
19875
|
+
)
|
|
19876
|
+
);
|
|
19877
|
+
children.push(el("style", {}, buildCss7(t)));
|
|
19878
|
+
children.push(markers3(t));
|
|
19879
|
+
const titleBand = layout.title ? 32 : 0;
|
|
19880
|
+
if (layout.title) {
|
|
19881
|
+
children.push(
|
|
19882
|
+
text({ x: layout.width / 2, y: 22, class: "sx-seq-title", "text-anchor": "middle" }, layout.title)
|
|
19883
|
+
);
|
|
19884
|
+
}
|
|
19885
|
+
const body = [];
|
|
19886
|
+
body.push(group({ class: "sx-seq-lifelines" }, layout.lifelines.map(renderLifeline)));
|
|
19887
|
+
body.push(group({ class: "sx-seq-frames" }, layout.fragments.map(renderFragmentBox)));
|
|
19888
|
+
body.push(group({ class: "sx-seq-acts" }, layout.activations.map(renderActivation)));
|
|
19889
|
+
body.push(group({ class: "sx-seq-frame-tabs" }, layout.fragments.map(renderFragmentTab)));
|
|
19890
|
+
body.push(group({ class: "sx-seq-refs" }, layout.refs.map(renderRef)));
|
|
19891
|
+
body.push(group({ class: "sx-seq-messages" }, layout.messages.map(renderMessage)));
|
|
19892
|
+
body.push(group({ class: "sx-seq-notes" }, layout.notes.map(renderNote2)));
|
|
19893
|
+
body.push(group({ class: "sx-seq-divs" }, layout.dividers.map(renderDivider)));
|
|
19894
|
+
body.push(group({ class: "sx-seq-invs" }, layout.invariants.map(renderInvariant)));
|
|
19895
|
+
body.push(group({ class: "sx-seq-destroys" }, layout.destroys.map(renderDestroy)));
|
|
19896
|
+
children.push(
|
|
19897
|
+
titleBand ? group({ transform: `translate(0, ${titleBand})` }, body) : group({}, body)
|
|
19898
|
+
);
|
|
19899
|
+
const height = layout.height + titleBand;
|
|
19900
|
+
return svgRoot(
|
|
19901
|
+
{
|
|
19902
|
+
class: "sx-seq",
|
|
19903
|
+
role: "img",
|
|
19904
|
+
"aria-label": escapeXml(layout.title ?? "UML sequence diagram"),
|
|
19905
|
+
width: layout.width,
|
|
19906
|
+
height,
|
|
19907
|
+
viewBox: `0 0 ${layout.width} ${height}`,
|
|
19908
|
+
"data-diagram-type": "sequence"
|
|
19909
|
+
},
|
|
19910
|
+
children
|
|
19911
|
+
);
|
|
19912
|
+
}
|
|
19913
|
+
function renderSequence(textOrAst, config) {
|
|
19914
|
+
const ast = typeof textOrAst === "string" ? parseSequence(textOrAst) : textOrAst;
|
|
19915
|
+
const layout = layoutSequence(ast);
|
|
19916
|
+
return renderSequenceLayout(layout, config);
|
|
19917
|
+
}
|
|
19918
|
+
|
|
19919
|
+
// src/diagrams/sequence/index.ts
|
|
19920
|
+
var sequence = {
|
|
19921
|
+
type: "sequence",
|
|
19922
|
+
detect(text2) {
|
|
19923
|
+
for (const raw of text2.split(/\r?\n/)) {
|
|
19924
|
+
const t = raw.trim();
|
|
19925
|
+
if (!t) continue;
|
|
19926
|
+
if (t.startsWith("#") || t.startsWith("//")) continue;
|
|
19927
|
+
return /^sequence\b/i.test(t);
|
|
19928
|
+
}
|
|
19929
|
+
return false;
|
|
19930
|
+
},
|
|
19931
|
+
parse: parseSequence,
|
|
19932
|
+
render(text2, config) {
|
|
19933
|
+
return renderSequence(text2, config);
|
|
19934
|
+
}
|
|
19935
|
+
};
|
|
19936
|
+
|
|
18616
19937
|
// src/core/api.ts
|
|
18617
19938
|
var plugins = [
|
|
18618
19939
|
genogram,
|
|
@@ -18644,7 +19965,8 @@ var plugins = [
|
|
|
18644
19965
|
sfc,
|
|
18645
19966
|
prisma,
|
|
18646
19967
|
usecase,
|
|
18647
|
-
pert
|
|
19968
|
+
pert,
|
|
19969
|
+
sequence
|
|
18648
19970
|
];
|
|
18649
19971
|
function detectPlugin(text2, config) {
|
|
18650
19972
|
if (config?.type) {
|
|
@@ -18655,7 +19977,7 @@ function detectPlugin(text2, config) {
|
|
|
18655
19977
|
if (plugin.detect(text2)) return plugin;
|
|
18656
19978
|
}
|
|
18657
19979
|
throw new Error(
|
|
18658
|
-
"Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', or '
|
|
19980
|
+
"Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', 'pert', or 'sequence'."
|
|
18659
19981
|
);
|
|
18660
19982
|
}
|
|
18661
19983
|
function preprocess8(text2) {
|
|
@@ -18680,9 +20002,68 @@ function parse(text2, config) {
|
|
|
18680
20002
|
`Diagram type '${plugin.type}' does not yet expose a parse() method.`
|
|
18681
20003
|
);
|
|
18682
20004
|
}
|
|
20005
|
+
function parseResult(text2, config) {
|
|
20006
|
+
let plugin;
|
|
20007
|
+
try {
|
|
20008
|
+
const prepared = preprocess8(text2);
|
|
20009
|
+
plugin = detectPlugin(prepared, config);
|
|
20010
|
+
if (!plugin.parse) {
|
|
20011
|
+
throw new Error(
|
|
20012
|
+
`Diagram type '${plugin.type}' does not yet expose a parse() method.`
|
|
20013
|
+
);
|
|
20014
|
+
}
|
|
20015
|
+
return {
|
|
20016
|
+
ok: true,
|
|
20017
|
+
status: "valid",
|
|
20018
|
+
type: plugin.type,
|
|
20019
|
+
ast: plugin.parse(prepared),
|
|
20020
|
+
diagnostics: []
|
|
20021
|
+
};
|
|
20022
|
+
} catch (err) {
|
|
20023
|
+
return {
|
|
20024
|
+
ok: false,
|
|
20025
|
+
status: "invalid",
|
|
20026
|
+
type: plugin?.type ?? config?.type ?? null,
|
|
20027
|
+
diagnostics: [diagnosticFromError(err)]
|
|
20028
|
+
};
|
|
20029
|
+
}
|
|
20030
|
+
}
|
|
18683
20031
|
function render(text2, config) {
|
|
20032
|
+
if (config?.mode === "preview") return renderResult(text2, config).svg;
|
|
18684
20033
|
const prepared = preprocess8(text2);
|
|
18685
20034
|
const plugin = detectPlugin(prepared, config);
|
|
20035
|
+
return renderWithPlugin(prepared, plugin, config);
|
|
20036
|
+
}
|
|
20037
|
+
function renderResult(text2, config) {
|
|
20038
|
+
let plugin;
|
|
20039
|
+
try {
|
|
20040
|
+
const prepared = preprocess8(text2);
|
|
20041
|
+
plugin = detectPlugin(prepared, config);
|
|
20042
|
+
return {
|
|
20043
|
+
ok: true,
|
|
20044
|
+
status: "valid",
|
|
20045
|
+
type: plugin.type,
|
|
20046
|
+
svg: renderWithPlugin(prepared, plugin, config),
|
|
20047
|
+
diagnostics: []
|
|
20048
|
+
};
|
|
20049
|
+
} catch (err) {
|
|
20050
|
+
const type = plugin?.type ?? config?.type ?? null;
|
|
20051
|
+
const diagnostics = [diagnosticFromError(err)];
|
|
20052
|
+
return {
|
|
20053
|
+
ok: false,
|
|
20054
|
+
status: "invalid",
|
|
20055
|
+
type,
|
|
20056
|
+
svg: renderDiagnosticSvg(diagnostics, type, {
|
|
20057
|
+
fontFamily: config?.fontFamily
|
|
20058
|
+
}),
|
|
20059
|
+
diagnostics
|
|
20060
|
+
};
|
|
20061
|
+
}
|
|
20062
|
+
}
|
|
20063
|
+
function renderPreview(text2, config) {
|
|
20064
|
+
return renderResult(text2, config).svg;
|
|
20065
|
+
}
|
|
20066
|
+
function renderWithPlugin(prepared, plugin, config) {
|
|
18686
20067
|
const renderConfig = {
|
|
18687
20068
|
fontFamily: config?.fontFamily ?? "system-ui, -apple-system, sans-serif",
|
|
18688
20069
|
fontSize: 12,
|
|
@@ -18692,6 +20073,6 @@ function render(text2, config) {
|
|
|
18692
20073
|
return plugin.render(prepared, renderConfig);
|
|
18693
20074
|
}
|
|
18694
20075
|
|
|
18695
|
-
export { decisiontree, parse, pert, pid, prisma, render, state, timeline, usecase };
|
|
18696
|
-
//# sourceMappingURL=chunk-
|
|
18697
|
-
//# sourceMappingURL=chunk-
|
|
20076
|
+
export { decisiontree, parse, parseResult, pert, pid, prisma, render, renderPreview, renderResult, state, timeline, usecase };
|
|
20077
|
+
//# sourceMappingURL=chunk-OK5ZS3LU.js.map
|
|
20078
|
+
//# sourceMappingURL=chunk-OK5ZS3LU.js.map
|