@diagrammo/dgmo 0.2.6 → 0.2.8
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 +213 -57
- package/dist/cli.cjs +91 -85
- package/dist/index.cjs +1362 -194
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -2
- package/dist/index.d.ts +108 -2
- package/dist/index.js +1357 -193
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/src/d3.ts +59 -1
- package/src/dgmo-router.ts +5 -1
- package/src/echarts.ts +2 -2
- package/src/graph/flowchart-parser.ts +499 -0
- package/src/graph/flowchart-renderer.ts +503 -0
- package/src/graph/layout.ts +222 -0
- package/src/graph/types.ts +44 -0
- package/src/index.ts +24 -0
- package/src/sequence/parser.ts +221 -37
- package/src/sequence/renderer.ts +342 -16
package/dist/index.js
CHANGED
|
@@ -147,6 +147,7 @@ var init_participant_inference = __esm({
|
|
|
147
147
|
var parser_exports = {};
|
|
148
148
|
__export(parser_exports, {
|
|
149
149
|
isSequenceBlock: () => isSequenceBlock,
|
|
150
|
+
isSequenceNote: () => isSequenceNote,
|
|
150
151
|
isSequenceSection: () => isSequenceSection,
|
|
151
152
|
looksLikeSequence: () => looksLikeSequence,
|
|
152
153
|
parseSequenceDgmo: () => parseSequenceDgmo
|
|
@@ -157,6 +158,9 @@ function isSequenceBlock(el) {
|
|
|
157
158
|
function isSequenceSection(el) {
|
|
158
159
|
return "kind" in el && el.kind === "section";
|
|
159
160
|
}
|
|
161
|
+
function isSequenceNote(el) {
|
|
162
|
+
return "kind" in el && el.kind === "note";
|
|
163
|
+
}
|
|
160
164
|
function parseReturnLabel(rawLabel) {
|
|
161
165
|
if (!rawLabel) return { label: "" };
|
|
162
166
|
const arrowReturn = rawLabel.match(ARROW_RETURN_PATTERN);
|
|
@@ -167,19 +171,22 @@ function parseReturnLabel(rawLabel) {
|
|
|
167
171
|
if (umlReturn) {
|
|
168
172
|
return { label: umlReturn[1].trim(), returnLabel: umlReturn[2].trim() };
|
|
169
173
|
}
|
|
170
|
-
const
|
|
171
|
-
if (
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
const lastColon = rawLabel.lastIndexOf(":");
|
|
175
|
+
if (lastColon > 0 && lastColon < rawLabel.length - 1) {
|
|
176
|
+
const afterColon = rawLabel.substring(lastColon + 1);
|
|
177
|
+
if (!afterColon.startsWith("//")) {
|
|
178
|
+
const reqPart = rawLabel.substring(0, lastColon).trim();
|
|
179
|
+
const resPart = afterColon.trim();
|
|
180
|
+
if (reqPart && resPart) {
|
|
181
|
+
return { label: reqPart, returnLabel: resPart };
|
|
182
|
+
}
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
185
|
return { label: rawLabel };
|
|
179
186
|
}
|
|
180
|
-
function measureIndent(
|
|
187
|
+
function measureIndent(line3) {
|
|
181
188
|
let indent = 0;
|
|
182
|
-
for (const ch of
|
|
189
|
+
for (const ch of line3) {
|
|
183
190
|
if (ch === " ") indent++;
|
|
184
191
|
else if (ch === " ") indent += 4;
|
|
185
192
|
else break;
|
|
@@ -203,13 +210,17 @@ function parseSequenceDgmo(content) {
|
|
|
203
210
|
}
|
|
204
211
|
const lines = content.split("\n");
|
|
205
212
|
let hasExplicitChart = false;
|
|
213
|
+
let contentStarted = false;
|
|
206
214
|
let activeGroup = null;
|
|
215
|
+
const participantGroupMap = /* @__PURE__ */ new Map();
|
|
207
216
|
const blockStack = [];
|
|
208
217
|
const currentContainer = () => {
|
|
209
218
|
if (blockStack.length === 0) return result.elements;
|
|
210
219
|
const top = blockStack[blockStack.length - 1];
|
|
220
|
+
if (top.activeElseIfBranch) return top.activeElseIfBranch.children;
|
|
211
221
|
return top.inElse ? top.block.elseChildren : top.block.children;
|
|
212
222
|
};
|
|
223
|
+
let lastMsgFrom = null;
|
|
213
224
|
for (let i = 0; i < lines.length; i++) {
|
|
214
225
|
const raw = lines[i];
|
|
215
226
|
const trimmed = raw.trim();
|
|
@@ -220,9 +231,15 @@ function parseSequenceDgmo(content) {
|
|
|
220
231
|
}
|
|
221
232
|
const groupMatch = trimmed.match(GROUP_HEADING_PATTERN);
|
|
222
233
|
if (groupMatch) {
|
|
234
|
+
const groupColor = groupMatch[2]?.trim();
|
|
235
|
+
if (groupColor && groupColor.startsWith("#")) {
|
|
236
|
+
result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
contentStarted = true;
|
|
223
240
|
activeGroup = {
|
|
224
|
-
name: groupMatch[1],
|
|
225
|
-
color:
|
|
241
|
+
name: groupMatch[1].trim(),
|
|
242
|
+
color: groupColor || void 0,
|
|
226
243
|
participantIds: [],
|
|
227
244
|
lineNumber
|
|
228
245
|
};
|
|
@@ -232,7 +249,11 @@ function parseSequenceDgmo(content) {
|
|
|
232
249
|
if (activeGroup && measureIndent(raw) === 0) {
|
|
233
250
|
activeGroup = null;
|
|
234
251
|
}
|
|
235
|
-
if (trimmed.startsWith("
|
|
252
|
+
if (trimmed.startsWith("//")) continue;
|
|
253
|
+
if (trimmed.startsWith("#") && !trimmed.startsWith("##")) {
|
|
254
|
+
result.error = `Line ${lineNumber}: Use // for comments. # is reserved for group headings (##)`;
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
236
257
|
const sectionMatch = trimmed.match(SECTION_PATTERN);
|
|
237
258
|
if (sectionMatch) {
|
|
238
259
|
const sectionIndent = measureIndent(raw);
|
|
@@ -242,11 +263,16 @@ function parseSequenceDgmo(content) {
|
|
|
242
263
|
blockStack.pop();
|
|
243
264
|
}
|
|
244
265
|
const labelRaw = sectionMatch[1].trim();
|
|
245
|
-
const colorMatch = labelRaw.match(/^(.+?)\((
|
|
266
|
+
const colorMatch = labelRaw.match(/^(.+?)\(([^)]+)\)$/);
|
|
267
|
+
if (colorMatch && colorMatch[2].trim().startsWith("#")) {
|
|
268
|
+
result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
contentStarted = true;
|
|
246
272
|
const section = {
|
|
247
273
|
kind: "section",
|
|
248
274
|
label: colorMatch ? colorMatch[1].trim() : labelRaw,
|
|
249
|
-
color: colorMatch ? colorMatch[2] : void 0,
|
|
275
|
+
color: colorMatch ? colorMatch[2].trim() : void 0,
|
|
250
276
|
lineNumber
|
|
251
277
|
};
|
|
252
278
|
result.sections.push(section);
|
|
@@ -256,24 +282,32 @@ function parseSequenceDgmo(content) {
|
|
|
256
282
|
const colonIndex = trimmed.indexOf(":");
|
|
257
283
|
if (colonIndex > 0 && !trimmed.includes("->") && !trimmed.includes("~>")) {
|
|
258
284
|
const key = trimmed.substring(0, colonIndex).trim().toLowerCase();
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (
|
|
263
|
-
|
|
285
|
+
if (key === "note" || key.startsWith("note ")) {
|
|
286
|
+
} else {
|
|
287
|
+
const value = trimmed.substring(colonIndex + 1).trim();
|
|
288
|
+
if (key === "chart") {
|
|
289
|
+
hasExplicitChart = true;
|
|
290
|
+
if (value.toLowerCase() !== "sequence") {
|
|
291
|
+
result.error = `Expected chart type "sequence", got "${value}"`;
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (contentStarted) {
|
|
297
|
+
result.error = `Line ${lineNumber}: Options like '${key}: ${value}' must appear before the first message or declaration`;
|
|
264
298
|
return result;
|
|
265
299
|
}
|
|
300
|
+
if (key === "title") {
|
|
301
|
+
result.title = value;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
result.options[key] = value;
|
|
266
305
|
continue;
|
|
267
306
|
}
|
|
268
|
-
if (key === "title") {
|
|
269
|
-
result.title = value;
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
result.options[key] = value;
|
|
273
|
-
continue;
|
|
274
307
|
}
|
|
275
308
|
const isAMatch = trimmed.match(IS_A_PATTERN);
|
|
276
309
|
if (isAMatch) {
|
|
310
|
+
contentStarted = true;
|
|
277
311
|
const id = isAMatch[1];
|
|
278
312
|
const typeStr = isAMatch[2].toLowerCase();
|
|
279
313
|
const remainder = isAMatch[3]?.trim() || "";
|
|
@@ -296,12 +330,19 @@ function parseSequenceDgmo(content) {
|
|
|
296
330
|
});
|
|
297
331
|
}
|
|
298
332
|
if (activeGroup && !activeGroup.participantIds.includes(id)) {
|
|
333
|
+
const existingGroup = participantGroupMap.get(id);
|
|
334
|
+
if (existingGroup) {
|
|
335
|
+
result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
299
338
|
activeGroup.participantIds.push(id);
|
|
339
|
+
participantGroupMap.set(id, activeGroup.name);
|
|
300
340
|
}
|
|
301
341
|
continue;
|
|
302
342
|
}
|
|
303
343
|
const posOnlyMatch = trimmed.match(POSITION_ONLY_PATTERN);
|
|
304
344
|
if (posOnlyMatch) {
|
|
345
|
+
contentStarted = true;
|
|
305
346
|
const id = posOnlyMatch[1];
|
|
306
347
|
const position = parseInt(posOnlyMatch[2], 10);
|
|
307
348
|
if (!result.participants.some((p) => p.id === id)) {
|
|
@@ -314,11 +355,18 @@ function parseSequenceDgmo(content) {
|
|
|
314
355
|
});
|
|
315
356
|
}
|
|
316
357
|
if (activeGroup && !activeGroup.participantIds.includes(id)) {
|
|
358
|
+
const existingGroup = participantGroupMap.get(id);
|
|
359
|
+
if (existingGroup) {
|
|
360
|
+
result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
317
363
|
activeGroup.participantIds.push(id);
|
|
364
|
+
participantGroupMap.set(id, activeGroup.name);
|
|
318
365
|
}
|
|
319
366
|
continue;
|
|
320
367
|
}
|
|
321
368
|
if (activeGroup && measureIndent(raw) > 0 && /^\S+$/.test(trimmed)) {
|
|
369
|
+
contentStarted = true;
|
|
322
370
|
const id = trimmed;
|
|
323
371
|
if (!result.participants.some((p) => p.id === id)) {
|
|
324
372
|
result.participants.push({
|
|
@@ -329,7 +377,13 @@ function parseSequenceDgmo(content) {
|
|
|
329
377
|
});
|
|
330
378
|
}
|
|
331
379
|
if (!activeGroup.participantIds.includes(id)) {
|
|
380
|
+
const existingGroup = participantGroupMap.get(id);
|
|
381
|
+
if (existingGroup) {
|
|
382
|
+
result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
332
385
|
activeGroup.participantIds.push(id);
|
|
386
|
+
participantGroupMap.set(id, activeGroup.name);
|
|
333
387
|
}
|
|
334
388
|
continue;
|
|
335
389
|
}
|
|
@@ -337,28 +391,31 @@ function parseSequenceDgmo(content) {
|
|
|
337
391
|
while (blockStack.length > 0) {
|
|
338
392
|
const top = blockStack[blockStack.length - 1];
|
|
339
393
|
if (indent > top.indent) break;
|
|
340
|
-
if (indent === top.indent &&
|
|
341
|
-
|
|
394
|
+
if (indent === top.indent && (top.block.type === "if" || top.block.type === "parallel")) {
|
|
395
|
+
const lower = trimmed.toLowerCase();
|
|
396
|
+
if (lower === "else" || lower.startsWith("else if ")) break;
|
|
397
|
+
}
|
|
342
398
|
blockStack.pop();
|
|
343
399
|
}
|
|
344
|
-
let isAsync = false;
|
|
345
|
-
let arrowLine = trimmed;
|
|
346
400
|
const asyncPrefixMatch = trimmed.match(/^async\s+(.+)$/i);
|
|
347
|
-
if (asyncPrefixMatch) {
|
|
348
|
-
|
|
349
|
-
|
|
401
|
+
if (asyncPrefixMatch && ARROW_PATTERN.test(asyncPrefixMatch[1])) {
|
|
402
|
+
result.error = `Line ${lineNumber}: Use ~> for async messages: A ~> B: message`;
|
|
403
|
+
return result;
|
|
350
404
|
}
|
|
351
|
-
|
|
405
|
+
let isAsync = false;
|
|
406
|
+
const asyncArrowMatch = trimmed.match(
|
|
352
407
|
/^(\S+)\s*~>\s*([^\s:]+)\s*(?::\s*(.+))?$/
|
|
353
408
|
);
|
|
354
|
-
const syncArrowMatch =
|
|
409
|
+
const syncArrowMatch = trimmed.match(
|
|
355
410
|
/^(\S+)\s*->\s*([^\s:]+)\s*(?::\s*(.+))?$/
|
|
356
411
|
);
|
|
357
412
|
const arrowMatch = asyncArrowMatch || syncArrowMatch;
|
|
358
413
|
if (asyncArrowMatch) isAsync = true;
|
|
359
414
|
if (arrowMatch) {
|
|
415
|
+
contentStarted = true;
|
|
360
416
|
const from = arrowMatch[1];
|
|
361
417
|
const to = arrowMatch[2];
|
|
418
|
+
lastMsgFrom = from;
|
|
362
419
|
const rawLabel = arrowMatch[3]?.trim() || "";
|
|
363
420
|
const { label, returnLabel } = isAsync ? { label: rawLabel, returnLabel: void 0 } : parseReturnLabel(rawLabel);
|
|
364
421
|
const msg = {
|
|
@@ -391,6 +448,7 @@ function parseSequenceDgmo(content) {
|
|
|
391
448
|
}
|
|
392
449
|
const ifMatch = trimmed.match(/^if\s+(.+)$/i);
|
|
393
450
|
if (ifMatch) {
|
|
451
|
+
contentStarted = true;
|
|
394
452
|
const block = {
|
|
395
453
|
kind: "block",
|
|
396
454
|
type: "if",
|
|
@@ -405,6 +463,7 @@ function parseSequenceDgmo(content) {
|
|
|
405
463
|
}
|
|
406
464
|
const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
|
|
407
465
|
if (loopMatch) {
|
|
466
|
+
contentStarted = true;
|
|
408
467
|
const block = {
|
|
409
468
|
kind: "block",
|
|
410
469
|
type: "loop",
|
|
@@ -419,6 +478,7 @@ function parseSequenceDgmo(content) {
|
|
|
419
478
|
}
|
|
420
479
|
const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
|
|
421
480
|
if (parallelMatch) {
|
|
481
|
+
contentStarted = true;
|
|
422
482
|
const block = {
|
|
423
483
|
kind: "block",
|
|
424
484
|
type: "parallel",
|
|
@@ -431,15 +491,97 @@ function parseSequenceDgmo(content) {
|
|
|
431
491
|
blockStack.push({ block, indent, inElse: false });
|
|
432
492
|
continue;
|
|
433
493
|
}
|
|
494
|
+
const elseIfMatch = trimmed.match(/^else\s+if\s+(.+)$/i);
|
|
495
|
+
if (elseIfMatch) {
|
|
496
|
+
if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
|
|
497
|
+
const top = blockStack[blockStack.length - 1];
|
|
498
|
+
if (top.block.type === "parallel") {
|
|
499
|
+
result.error = `Line ${lineNumber}: parallel blocks don't support else if \u2014 list all concurrent messages directly inside the block`;
|
|
500
|
+
return result;
|
|
501
|
+
}
|
|
502
|
+
if (top.block.type === "if") {
|
|
503
|
+
const branch = { label: elseIfMatch[1].trim(), children: [] };
|
|
504
|
+
if (!top.block.elseIfBranches) top.block.elseIfBranches = [];
|
|
505
|
+
top.block.elseIfBranches.push(branch);
|
|
506
|
+
top.activeElseIfBranch = branch;
|
|
507
|
+
top.inElse = false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
434
512
|
if (trimmed.toLowerCase() === "else") {
|
|
435
|
-
if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent
|
|
436
|
-
blockStack[blockStack.length - 1]
|
|
513
|
+
if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
|
|
514
|
+
const top = blockStack[blockStack.length - 1];
|
|
515
|
+
if (top.block.type === "parallel") {
|
|
516
|
+
result.error = `Line ${lineNumber}: parallel blocks don't support else \u2014 list all concurrent messages directly inside the block`;
|
|
517
|
+
return result;
|
|
518
|
+
}
|
|
519
|
+
if (top.block.type === "if") {
|
|
520
|
+
top.inElse = true;
|
|
521
|
+
top.activeElseIfBranch = void 0;
|
|
522
|
+
}
|
|
437
523
|
}
|
|
438
524
|
continue;
|
|
439
525
|
}
|
|
526
|
+
const noteSingleMatch = trimmed.match(NOTE_SINGLE);
|
|
527
|
+
if (noteSingleMatch) {
|
|
528
|
+
const notePosition = noteSingleMatch[1]?.toLowerCase() || "right";
|
|
529
|
+
let noteParticipant = noteSingleMatch[2] || null;
|
|
530
|
+
if (!noteParticipant) {
|
|
531
|
+
if (!lastMsgFrom) continue;
|
|
532
|
+
noteParticipant = lastMsgFrom;
|
|
533
|
+
}
|
|
534
|
+
if (!result.participants.some((p) => p.id === noteParticipant)) {
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
const note = {
|
|
538
|
+
kind: "note",
|
|
539
|
+
text: noteSingleMatch[3].trim(),
|
|
540
|
+
position: notePosition,
|
|
541
|
+
participantId: noteParticipant,
|
|
542
|
+
lineNumber,
|
|
543
|
+
endLineNumber: lineNumber
|
|
544
|
+
};
|
|
545
|
+
currentContainer().push(note);
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
const noteMultiMatch = trimmed.match(NOTE_MULTI);
|
|
549
|
+
if (noteMultiMatch) {
|
|
550
|
+
const notePosition = noteMultiMatch[1]?.toLowerCase() || "right";
|
|
551
|
+
let noteParticipant = noteMultiMatch[2] || null;
|
|
552
|
+
if (!noteParticipant) {
|
|
553
|
+
if (!lastMsgFrom) continue;
|
|
554
|
+
noteParticipant = lastMsgFrom;
|
|
555
|
+
}
|
|
556
|
+
if (!result.participants.some((p) => p.id === noteParticipant)) {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
const noteLines = [];
|
|
560
|
+
while (i + 1 < lines.length) {
|
|
561
|
+
const nextRaw = lines[i + 1];
|
|
562
|
+
const nextTrimmed = nextRaw.trim();
|
|
563
|
+
if (!nextTrimmed) break;
|
|
564
|
+
const nextIndent = measureIndent(nextRaw);
|
|
565
|
+
if (nextIndent <= indent) break;
|
|
566
|
+
noteLines.push(nextTrimmed);
|
|
567
|
+
i++;
|
|
568
|
+
}
|
|
569
|
+
if (noteLines.length === 0) continue;
|
|
570
|
+
const note = {
|
|
571
|
+
kind: "note",
|
|
572
|
+
text: noteLines.join("\n"),
|
|
573
|
+
position: notePosition,
|
|
574
|
+
participantId: noteParticipant,
|
|
575
|
+
lineNumber,
|
|
576
|
+
endLineNumber: i + 1
|
|
577
|
+
// i has advanced past the body lines (1-based)
|
|
578
|
+
};
|
|
579
|
+
currentContainer().push(note);
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
440
582
|
}
|
|
441
583
|
if (!hasExplicitChart && result.messages.length === 0) {
|
|
442
|
-
const hasArrows = lines.some((
|
|
584
|
+
const hasArrows = lines.some((line3) => ARROW_PATTERN.test(line3.trim()));
|
|
443
585
|
if (!hasArrows) {
|
|
444
586
|
result.error = 'No "chart: sequence" header and no sequence content detected';
|
|
445
587
|
return result;
|
|
@@ -450,13 +592,13 @@ function parseSequenceDgmo(content) {
|
|
|
450
592
|
function looksLikeSequence(content) {
|
|
451
593
|
if (!content) return false;
|
|
452
594
|
const lines = content.split("\n");
|
|
453
|
-
return lines.some((
|
|
454
|
-
const trimmed =
|
|
455
|
-
if (trimmed.startsWith("
|
|
595
|
+
return lines.some((line3) => {
|
|
596
|
+
const trimmed = line3.trim();
|
|
597
|
+
if (trimmed.startsWith("//")) return false;
|
|
456
598
|
return ARROW_PATTERN.test(trimmed);
|
|
457
599
|
});
|
|
458
600
|
}
|
|
459
|
-
var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN;
|
|
601
|
+
var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN, NOTE_SINGLE, NOTE_MULTI;
|
|
460
602
|
var init_parser = __esm({
|
|
461
603
|
"src/sequence/parser.ts"() {
|
|
462
604
|
"use strict";
|
|
@@ -474,11 +616,13 @@ var init_parser = __esm({
|
|
|
474
616
|
]);
|
|
475
617
|
IS_A_PATTERN = /^(\S+)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
|
|
476
618
|
POSITION_ONLY_PATTERN = /^(\S+)\s+position\s+(-?\d+)$/i;
|
|
477
|
-
GROUP_HEADING_PATTERN = /^##\s+(
|
|
478
|
-
SECTION_PATTERN = /^==\s+(.+?)
|
|
619
|
+
GROUP_HEADING_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
|
|
620
|
+
SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
|
|
479
621
|
ARROW_PATTERN = /\S+\s*(?:->|~>)\s*\S+/;
|
|
480
622
|
ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
|
|
481
623
|
UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
|
|
624
|
+
NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
|
|
625
|
+
NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+([^\s:]+))?\s*:?\s*$/i;
|
|
482
626
|
}
|
|
483
627
|
});
|
|
484
628
|
|
|
@@ -557,6 +701,369 @@ var init_colors = __esm({
|
|
|
557
701
|
}
|
|
558
702
|
});
|
|
559
703
|
|
|
704
|
+
// src/graph/flowchart-parser.ts
|
|
705
|
+
var flowchart_parser_exports = {};
|
|
706
|
+
__export(flowchart_parser_exports, {
|
|
707
|
+
looksLikeFlowchart: () => looksLikeFlowchart,
|
|
708
|
+
parseFlowchart: () => parseFlowchart
|
|
709
|
+
});
|
|
710
|
+
function measureIndent2(line3) {
|
|
711
|
+
let indent = 0;
|
|
712
|
+
for (const ch of line3) {
|
|
713
|
+
if (ch === " ") indent++;
|
|
714
|
+
else if (ch === " ") indent += 4;
|
|
715
|
+
else break;
|
|
716
|
+
}
|
|
717
|
+
return indent;
|
|
718
|
+
}
|
|
719
|
+
function nodeId(shape, label) {
|
|
720
|
+
return `${shape}:${label.toLowerCase().trim()}`;
|
|
721
|
+
}
|
|
722
|
+
function extractColor(label, palette) {
|
|
723
|
+
const m = label.match(COLOR_SUFFIX_RE);
|
|
724
|
+
if (!m) return { label };
|
|
725
|
+
const colorName = m[1].trim();
|
|
726
|
+
return {
|
|
727
|
+
label: label.substring(0, m.index).trim(),
|
|
728
|
+
color: resolveColor(colorName, palette)
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function parseNodeRef(text, palette) {
|
|
732
|
+
const t = text.trim();
|
|
733
|
+
if (!t) return null;
|
|
734
|
+
let m = t.match(/^\[\[([^\]]+)\]\]$/);
|
|
735
|
+
if (m) {
|
|
736
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
737
|
+
return { id: nodeId("subroutine", label), label, shape: "subroutine", color };
|
|
738
|
+
}
|
|
739
|
+
m = t.match(/^\[([^\]]+)~\]$/);
|
|
740
|
+
if (m) {
|
|
741
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
742
|
+
return { id: nodeId("document", label), label, shape: "document", color };
|
|
743
|
+
}
|
|
744
|
+
m = t.match(/^\[([^\]]+)\]$/);
|
|
745
|
+
if (m) {
|
|
746
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
747
|
+
return { id: nodeId("process", label), label, shape: "process", color };
|
|
748
|
+
}
|
|
749
|
+
m = t.match(/^\((.+)\)$/);
|
|
750
|
+
if (m) {
|
|
751
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
752
|
+
return { id: nodeId("terminal", label), label, shape: "terminal", color };
|
|
753
|
+
}
|
|
754
|
+
m = t.match(/^<([^>]+)>$/);
|
|
755
|
+
if (m) {
|
|
756
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
757
|
+
return { id: nodeId("decision", label), label, shape: "decision", color };
|
|
758
|
+
}
|
|
759
|
+
m = t.match(/^\/([^/]+)\/$/);
|
|
760
|
+
if (m) {
|
|
761
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
762
|
+
return { id: nodeId("io", label), label, shape: "io", color };
|
|
763
|
+
}
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
function splitArrows(line3) {
|
|
767
|
+
const segments = [];
|
|
768
|
+
const arrowRe = /(?:^|\s)-([^>\s(][^(>]*?)?\s*(?:\(([^)]+)\))?\s*->|(?:^|\s)->/g;
|
|
769
|
+
let lastIndex = 0;
|
|
770
|
+
const arrowPositions = [];
|
|
771
|
+
let searchFrom = 0;
|
|
772
|
+
while (searchFrom < line3.length) {
|
|
773
|
+
const idx = line3.indexOf("->", searchFrom);
|
|
774
|
+
if (idx === -1) break;
|
|
775
|
+
let arrowStart = idx;
|
|
776
|
+
let label;
|
|
777
|
+
let color;
|
|
778
|
+
if (idx > 0 && line3[idx - 1] !== " " && line3[idx - 1] !== " ") {
|
|
779
|
+
let scanBack = idx - 1;
|
|
780
|
+
while (scanBack > 0 && line3[scanBack] !== "-") {
|
|
781
|
+
scanBack--;
|
|
782
|
+
}
|
|
783
|
+
if (line3[scanBack] === "-" && (scanBack === 0 || /\s/.test(line3[scanBack - 1]))) {
|
|
784
|
+
let arrowContent = line3.substring(scanBack + 1, idx);
|
|
785
|
+
if (arrowContent.endsWith("-")) arrowContent = arrowContent.slice(0, -1);
|
|
786
|
+
const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
|
|
787
|
+
if (colorMatch) {
|
|
788
|
+
color = colorMatch[1].trim();
|
|
789
|
+
const labelPart = arrowContent.substring(0, colorMatch.index).trim();
|
|
790
|
+
if (labelPart) label = labelPart;
|
|
791
|
+
} else {
|
|
792
|
+
const labelPart = arrowContent.trim();
|
|
793
|
+
if (labelPart) label = labelPart;
|
|
794
|
+
}
|
|
795
|
+
arrowStart = scanBack;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
|
|
799
|
+
searchFrom = idx + 2;
|
|
800
|
+
}
|
|
801
|
+
if (arrowPositions.length === 0) {
|
|
802
|
+
return [line3];
|
|
803
|
+
}
|
|
804
|
+
for (let i = 0; i < arrowPositions.length; i++) {
|
|
805
|
+
const arrow = arrowPositions[i];
|
|
806
|
+
const beforeText = line3.substring(lastIndex, arrow.start).trim();
|
|
807
|
+
if (beforeText || i === 0) {
|
|
808
|
+
segments.push(beforeText);
|
|
809
|
+
}
|
|
810
|
+
let arrowToken = "->";
|
|
811
|
+
if (arrow.label && arrow.color) arrowToken = `-${arrow.label}(${arrow.color})->`;
|
|
812
|
+
else if (arrow.label) arrowToken = `-${arrow.label}->`;
|
|
813
|
+
else if (arrow.color) arrowToken = `-(${arrow.color})->`;
|
|
814
|
+
segments.push(arrowToken);
|
|
815
|
+
lastIndex = arrow.end;
|
|
816
|
+
}
|
|
817
|
+
const remaining = line3.substring(lastIndex).trim();
|
|
818
|
+
if (remaining) {
|
|
819
|
+
segments.push(remaining);
|
|
820
|
+
}
|
|
821
|
+
return segments;
|
|
822
|
+
}
|
|
823
|
+
function parseArrowToken(token, palette) {
|
|
824
|
+
if (token === "->") return {};
|
|
825
|
+
const colorOnly = token.match(/^-\(([^)]+)\)->$/);
|
|
826
|
+
if (colorOnly) {
|
|
827
|
+
return { color: resolveColor(colorOnly[1].trim(), palette) };
|
|
828
|
+
}
|
|
829
|
+
const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
|
|
830
|
+
if (m) {
|
|
831
|
+
const label = m[1]?.trim() || void 0;
|
|
832
|
+
const color = m[2] ? resolveColor(m[2].trim(), palette) : void 0;
|
|
833
|
+
return { label, color };
|
|
834
|
+
}
|
|
835
|
+
return {};
|
|
836
|
+
}
|
|
837
|
+
function parseFlowchart(content, palette) {
|
|
838
|
+
const lines = content.split("\n");
|
|
839
|
+
const result = {
|
|
840
|
+
type: "flowchart",
|
|
841
|
+
direction: "TB",
|
|
842
|
+
nodes: [],
|
|
843
|
+
edges: []
|
|
844
|
+
};
|
|
845
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
846
|
+
const indentStack = [];
|
|
847
|
+
let currentGroup = null;
|
|
848
|
+
const groups = [];
|
|
849
|
+
let contentStarted = false;
|
|
850
|
+
function getOrCreateNode(ref, lineNumber) {
|
|
851
|
+
const existing = nodeMap.get(ref.id);
|
|
852
|
+
if (existing) return existing;
|
|
853
|
+
const node = {
|
|
854
|
+
id: ref.id,
|
|
855
|
+
label: ref.label,
|
|
856
|
+
shape: ref.shape,
|
|
857
|
+
lineNumber,
|
|
858
|
+
...ref.color && { color: ref.color },
|
|
859
|
+
...currentGroup && { group: currentGroup.id }
|
|
860
|
+
};
|
|
861
|
+
nodeMap.set(ref.id, node);
|
|
862
|
+
result.nodes.push(node);
|
|
863
|
+
if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
|
|
864
|
+
currentGroup.nodeIds.push(ref.id);
|
|
865
|
+
}
|
|
866
|
+
return node;
|
|
867
|
+
}
|
|
868
|
+
function addEdge(sourceId, targetId, lineNumber, label, color) {
|
|
869
|
+
const edge = {
|
|
870
|
+
source: sourceId,
|
|
871
|
+
target: targetId,
|
|
872
|
+
lineNumber,
|
|
873
|
+
...label && { label },
|
|
874
|
+
...color && { color }
|
|
875
|
+
};
|
|
876
|
+
result.edges.push(edge);
|
|
877
|
+
}
|
|
878
|
+
function processContentLine(trimmed, lineNumber, indent) {
|
|
879
|
+
contentStarted = true;
|
|
880
|
+
while (indentStack.length > 0) {
|
|
881
|
+
const top = indentStack[indentStack.length - 1];
|
|
882
|
+
if (top.indent >= indent) {
|
|
883
|
+
indentStack.pop();
|
|
884
|
+
} else {
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
const implicitSourceId = indentStack.length > 0 ? indentStack[indentStack.length - 1].nodeId : null;
|
|
889
|
+
const segments = splitArrows(trimmed);
|
|
890
|
+
if (segments.length === 1) {
|
|
891
|
+
const ref = parseNodeRef(segments[0], palette);
|
|
892
|
+
if (ref) {
|
|
893
|
+
const node = getOrCreateNode(ref, lineNumber);
|
|
894
|
+
indentStack.push({ nodeId: node.id, indent });
|
|
895
|
+
return node.id;
|
|
896
|
+
}
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
let lastNodeId = null;
|
|
900
|
+
let pendingArrow = null;
|
|
901
|
+
for (let i = 0; i < segments.length; i++) {
|
|
902
|
+
const seg = segments[i];
|
|
903
|
+
if (seg === "->" || /^-.+->$/.test(seg)) {
|
|
904
|
+
pendingArrow = parseArrowToken(seg, palette);
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
const ref = parseNodeRef(seg, palette);
|
|
908
|
+
if (!ref) continue;
|
|
909
|
+
const node = getOrCreateNode(ref, lineNumber);
|
|
910
|
+
if (pendingArrow !== null) {
|
|
911
|
+
const sourceId = lastNodeId ?? implicitSourceId;
|
|
912
|
+
if (sourceId) {
|
|
913
|
+
addEdge(
|
|
914
|
+
sourceId,
|
|
915
|
+
node.id,
|
|
916
|
+
lineNumber,
|
|
917
|
+
pendingArrow.label,
|
|
918
|
+
pendingArrow.color
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
pendingArrow = null;
|
|
922
|
+
} else if (lastNodeId === null && implicitSourceId === null) {
|
|
923
|
+
}
|
|
924
|
+
lastNodeId = node.id;
|
|
925
|
+
}
|
|
926
|
+
if (pendingArrow !== null && lastNodeId === null && implicitSourceId) {
|
|
927
|
+
}
|
|
928
|
+
if (segments.length >= 2 && segments[0] === "" && implicitSourceId && lastNodeId) {
|
|
929
|
+
}
|
|
930
|
+
if (lastNodeId) {
|
|
931
|
+
indentStack.push({ nodeId: lastNodeId, indent });
|
|
932
|
+
}
|
|
933
|
+
return lastNodeId;
|
|
934
|
+
}
|
|
935
|
+
for (let i = 0; i < lines.length; i++) {
|
|
936
|
+
const raw = lines[i];
|
|
937
|
+
const trimmed = raw.trim();
|
|
938
|
+
const lineNumber = i + 1;
|
|
939
|
+
const indent = measureIndent2(raw);
|
|
940
|
+
if (!trimmed) continue;
|
|
941
|
+
if (trimmed.startsWith("//")) continue;
|
|
942
|
+
const groupMatch = trimmed.match(GROUP_HEADING_RE);
|
|
943
|
+
if (groupMatch) {
|
|
944
|
+
const groupLabel = groupMatch[1].trim();
|
|
945
|
+
const groupColorName = groupMatch[2]?.trim();
|
|
946
|
+
const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
|
|
947
|
+
currentGroup = {
|
|
948
|
+
id: `group:${groupLabel.toLowerCase()}`,
|
|
949
|
+
label: groupLabel,
|
|
950
|
+
nodeIds: [],
|
|
951
|
+
lineNumber,
|
|
952
|
+
...groupColor && { color: groupColor }
|
|
953
|
+
};
|
|
954
|
+
groups.push(currentGroup);
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
if (!contentStarted && trimmed.includes(":") && !trimmed.includes("->")) {
|
|
958
|
+
const colonIdx = trimmed.indexOf(":");
|
|
959
|
+
const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
|
|
960
|
+
const value = trimmed.substring(colonIdx + 1).trim();
|
|
961
|
+
if (key === "chart") {
|
|
962
|
+
if (value.toLowerCase() !== "flowchart") {
|
|
963
|
+
result.error = `Line ${lineNumber}: Expected chart type "flowchart", got "${value}"`;
|
|
964
|
+
return result;
|
|
965
|
+
}
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
if (key === "title") {
|
|
969
|
+
result.title = value;
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (key === "direction") {
|
|
973
|
+
const dir = value.toUpperCase();
|
|
974
|
+
if (dir === "TB" || dir === "LR") {
|
|
975
|
+
result.direction = dir;
|
|
976
|
+
}
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
processContentLine(trimmed, lineNumber, indent);
|
|
982
|
+
}
|
|
983
|
+
if (groups.length > 0) result.groups = groups;
|
|
984
|
+
if (result.nodes.length === 0 && !result.error) {
|
|
985
|
+
result.error = "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).";
|
|
986
|
+
}
|
|
987
|
+
return result;
|
|
988
|
+
}
|
|
989
|
+
function looksLikeFlowchart(content) {
|
|
990
|
+
if (!content.includes("->")) return false;
|
|
991
|
+
const hasShapeDelimiter = /\[[^\]]+\]/.test(content) || /\([^)]+\)/.test(content) || /<[^>]+>/.test(content) || /\/[^/]+\//.test(content);
|
|
992
|
+
if (!hasShapeDelimiter) return false;
|
|
993
|
+
const shapeNearArrow = /[\])][ \t]*-.*->/.test(content) || // shape ] or ) followed by arrow
|
|
994
|
+
/->[ \t]*[\[(<\/]/.test(content);
|
|
995
|
+
return shapeNearArrow;
|
|
996
|
+
}
|
|
997
|
+
var COLOR_SUFFIX_RE, GROUP_HEADING_RE;
|
|
998
|
+
var init_flowchart_parser = __esm({
|
|
999
|
+
"src/graph/flowchart-parser.ts"() {
|
|
1000
|
+
"use strict";
|
|
1001
|
+
init_colors();
|
|
1002
|
+
COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
|
|
1003
|
+
GROUP_HEADING_RE = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
// src/dgmo-router.ts
|
|
1008
|
+
var dgmo_router_exports = {};
|
|
1009
|
+
__export(dgmo_router_exports, {
|
|
1010
|
+
DGMO_CHART_TYPE_MAP: () => DGMO_CHART_TYPE_MAP,
|
|
1011
|
+
getDgmoFramework: () => getDgmoFramework,
|
|
1012
|
+
parseDgmoChartType: () => parseDgmoChartType
|
|
1013
|
+
});
|
|
1014
|
+
function getDgmoFramework(chartType) {
|
|
1015
|
+
return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
|
|
1016
|
+
}
|
|
1017
|
+
function parseDgmoChartType(content) {
|
|
1018
|
+
const lines = content.split("\n");
|
|
1019
|
+
for (const line3 of lines) {
|
|
1020
|
+
const trimmed = line3.trim();
|
|
1021
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
|
|
1022
|
+
continue;
|
|
1023
|
+
const match = trimmed.match(/^chart\s*:\s*(.+)/i);
|
|
1024
|
+
if (match) return match[1].trim().toLowerCase();
|
|
1025
|
+
}
|
|
1026
|
+
if (looksLikeSequence(content)) return "sequence";
|
|
1027
|
+
if (looksLikeFlowchart(content)) return "flowchart";
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
var DGMO_CHART_TYPE_MAP;
|
|
1031
|
+
var init_dgmo_router = __esm({
|
|
1032
|
+
"src/dgmo-router.ts"() {
|
|
1033
|
+
"use strict";
|
|
1034
|
+
init_parser();
|
|
1035
|
+
init_flowchart_parser();
|
|
1036
|
+
DGMO_CHART_TYPE_MAP = {
|
|
1037
|
+
// Standard charts (via ECharts)
|
|
1038
|
+
bar: "echart",
|
|
1039
|
+
line: "echart",
|
|
1040
|
+
"multi-line": "echart",
|
|
1041
|
+
area: "echart",
|
|
1042
|
+
pie: "echart",
|
|
1043
|
+
doughnut: "echart",
|
|
1044
|
+
radar: "echart",
|
|
1045
|
+
"polar-area": "echart",
|
|
1046
|
+
"bar-stacked": "echart",
|
|
1047
|
+
// ECharts
|
|
1048
|
+
scatter: "echart",
|
|
1049
|
+
sankey: "echart",
|
|
1050
|
+
chord: "echart",
|
|
1051
|
+
function: "echart",
|
|
1052
|
+
heatmap: "echart",
|
|
1053
|
+
funnel: "echart",
|
|
1054
|
+
// D3
|
|
1055
|
+
slope: "d3",
|
|
1056
|
+
wordcloud: "d3",
|
|
1057
|
+
arc: "d3",
|
|
1058
|
+
timeline: "d3",
|
|
1059
|
+
venn: "d3",
|
|
1060
|
+
quadrant: "d3",
|
|
1061
|
+
sequence: "d3",
|
|
1062
|
+
flowchart: "d3"
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
|
|
560
1067
|
// src/fonts.ts
|
|
561
1068
|
var FONT_FAMILY;
|
|
562
1069
|
var init_fonts = __esm({
|
|
@@ -1612,6 +2119,409 @@ var init_palettes = __esm({
|
|
|
1612
2119
|
}
|
|
1613
2120
|
});
|
|
1614
2121
|
|
|
2122
|
+
// src/graph/layout.ts
|
|
2123
|
+
var layout_exports = {};
|
|
2124
|
+
__export(layout_exports, {
|
|
2125
|
+
layoutGraph: () => layoutGraph
|
|
2126
|
+
});
|
|
2127
|
+
import dagre from "@dagrejs/dagre";
|
|
2128
|
+
function computeNodeWidth(label, shape) {
|
|
2129
|
+
const base = Math.max(120, label.length * 9 + 40);
|
|
2130
|
+
if (shape === "subroutine") return base + 10;
|
|
2131
|
+
return base;
|
|
2132
|
+
}
|
|
2133
|
+
function computeNodeHeight(shape) {
|
|
2134
|
+
return shape === "decision" ? 60 : 50;
|
|
2135
|
+
}
|
|
2136
|
+
function layoutGraph(graph) {
|
|
2137
|
+
if (graph.nodes.length === 0) {
|
|
2138
|
+
return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
|
|
2139
|
+
}
|
|
2140
|
+
const g = new dagre.graphlib.Graph({ compound: true });
|
|
2141
|
+
g.setGraph({
|
|
2142
|
+
rankdir: graph.direction,
|
|
2143
|
+
nodesep: 50,
|
|
2144
|
+
ranksep: 60,
|
|
2145
|
+
edgesep: 20
|
|
2146
|
+
});
|
|
2147
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
2148
|
+
const nodeDataMap = /* @__PURE__ */ new Map();
|
|
2149
|
+
for (const node of graph.nodes) {
|
|
2150
|
+
nodeDataMap.set(node.id, node);
|
|
2151
|
+
}
|
|
2152
|
+
if (graph.groups) {
|
|
2153
|
+
for (const group of graph.groups) {
|
|
2154
|
+
g.setNode(group.id, {
|
|
2155
|
+
label: group.label,
|
|
2156
|
+
clusterLabelPos: "top"
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
for (const node of graph.nodes) {
|
|
2161
|
+
const width = computeNodeWidth(node.label, node.shape);
|
|
2162
|
+
const height = computeNodeHeight(node.shape);
|
|
2163
|
+
g.setNode(node.id, { label: node.label, width, height });
|
|
2164
|
+
if (node.group && graph.groups?.some((gr) => gr.id === node.group)) {
|
|
2165
|
+
g.setParent(node.id, node.group);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
const edgeDataMap = /* @__PURE__ */ new Map();
|
|
2169
|
+
for (const edge of graph.edges) {
|
|
2170
|
+
const key = `${edge.source}->${edge.target}`;
|
|
2171
|
+
edgeDataMap.set(key, edge);
|
|
2172
|
+
g.setEdge(edge.source, edge.target, {
|
|
2173
|
+
label: edge.label ?? ""
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
dagre.layout(g);
|
|
2177
|
+
const layoutNodes = graph.nodes.map((node) => {
|
|
2178
|
+
const pos = g.node(node.id);
|
|
2179
|
+
return {
|
|
2180
|
+
id: node.id,
|
|
2181
|
+
label: node.label,
|
|
2182
|
+
shape: node.shape,
|
|
2183
|
+
color: node.color,
|
|
2184
|
+
group: node.group,
|
|
2185
|
+
lineNumber: node.lineNumber,
|
|
2186
|
+
x: pos.x,
|
|
2187
|
+
y: pos.y,
|
|
2188
|
+
width: pos.width,
|
|
2189
|
+
height: pos.height
|
|
2190
|
+
};
|
|
2191
|
+
});
|
|
2192
|
+
const layoutEdges = graph.edges.map((edge) => {
|
|
2193
|
+
const edgeData = g.edge(edge.source, edge.target);
|
|
2194
|
+
return {
|
|
2195
|
+
source: edge.source,
|
|
2196
|
+
target: edge.target,
|
|
2197
|
+
points: edgeData?.points ?? [],
|
|
2198
|
+
label: edge.label,
|
|
2199
|
+
color: edge.color,
|
|
2200
|
+
lineNumber: edge.lineNumber
|
|
2201
|
+
};
|
|
2202
|
+
});
|
|
2203
|
+
const layoutGroups = [];
|
|
2204
|
+
if (graph.groups) {
|
|
2205
|
+
const nodeMap = new Map(layoutNodes.map((n) => [n.id, n]));
|
|
2206
|
+
for (const group of graph.groups) {
|
|
2207
|
+
const members = group.nodeIds.map((id) => nodeMap.get(id)).filter((n) => n !== void 0);
|
|
2208
|
+
if (members.length === 0) {
|
|
2209
|
+
layoutGroups.push({
|
|
2210
|
+
id: group.id,
|
|
2211
|
+
label: group.label,
|
|
2212
|
+
color: group.color,
|
|
2213
|
+
x: 0,
|
|
2214
|
+
y: 0,
|
|
2215
|
+
width: 0,
|
|
2216
|
+
height: 0
|
|
2217
|
+
});
|
|
2218
|
+
continue;
|
|
2219
|
+
}
|
|
2220
|
+
let minX = Infinity;
|
|
2221
|
+
let minY = Infinity;
|
|
2222
|
+
let maxX = -Infinity;
|
|
2223
|
+
let maxY = -Infinity;
|
|
2224
|
+
for (const member of members) {
|
|
2225
|
+
const left = member.x - member.width / 2;
|
|
2226
|
+
const right = member.x + member.width / 2;
|
|
2227
|
+
const top = member.y - member.height / 2;
|
|
2228
|
+
const bottom = member.y + member.height / 2;
|
|
2229
|
+
if (left < minX) minX = left;
|
|
2230
|
+
if (right > maxX) maxX = right;
|
|
2231
|
+
if (top < minY) minY = top;
|
|
2232
|
+
if (bottom > maxY) maxY = bottom;
|
|
2233
|
+
}
|
|
2234
|
+
layoutGroups.push({
|
|
2235
|
+
id: group.id,
|
|
2236
|
+
label: group.label,
|
|
2237
|
+
color: group.color,
|
|
2238
|
+
x: minX - GROUP_PADDING,
|
|
2239
|
+
y: minY - GROUP_PADDING,
|
|
2240
|
+
width: maxX - minX + GROUP_PADDING * 2,
|
|
2241
|
+
height: maxY - minY + GROUP_PADDING * 2
|
|
2242
|
+
});
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
let totalWidth = 0;
|
|
2246
|
+
let totalHeight = 0;
|
|
2247
|
+
for (const node of layoutNodes) {
|
|
2248
|
+
const right = node.x + node.width / 2;
|
|
2249
|
+
const bottom = node.y + node.height / 2;
|
|
2250
|
+
if (right > totalWidth) totalWidth = right;
|
|
2251
|
+
if (bottom > totalHeight) totalHeight = bottom;
|
|
2252
|
+
}
|
|
2253
|
+
for (const group of layoutGroups) {
|
|
2254
|
+
const right = group.x + group.width;
|
|
2255
|
+
const bottom = group.y + group.height;
|
|
2256
|
+
if (right > totalWidth) totalWidth = right;
|
|
2257
|
+
if (bottom > totalHeight) totalHeight = bottom;
|
|
2258
|
+
}
|
|
2259
|
+
totalWidth += 40;
|
|
2260
|
+
totalHeight += 40;
|
|
2261
|
+
return {
|
|
2262
|
+
nodes: layoutNodes,
|
|
2263
|
+
edges: layoutEdges,
|
|
2264
|
+
groups: layoutGroups,
|
|
2265
|
+
width: totalWidth,
|
|
2266
|
+
height: totalHeight
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
var GROUP_PADDING;
|
|
2270
|
+
var init_layout = __esm({
|
|
2271
|
+
"src/graph/layout.ts"() {
|
|
2272
|
+
"use strict";
|
|
2273
|
+
GROUP_PADDING = 20;
|
|
2274
|
+
}
|
|
2275
|
+
});
|
|
2276
|
+
|
|
2277
|
+
// src/graph/flowchart-renderer.ts
|
|
2278
|
+
var flowchart_renderer_exports = {};
|
|
2279
|
+
__export(flowchart_renderer_exports, {
|
|
2280
|
+
renderFlowchart: () => renderFlowchart,
|
|
2281
|
+
renderFlowchartForExport: () => renderFlowchartForExport
|
|
2282
|
+
});
|
|
2283
|
+
import * as d3Selection from "d3-selection";
|
|
2284
|
+
import * as d3Shape from "d3-shape";
|
|
2285
|
+
function mix(a, b, pct) {
|
|
2286
|
+
const parse = (h) => {
|
|
2287
|
+
const r = h.replace("#", "");
|
|
2288
|
+
const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
|
|
2289
|
+
return [parseInt(f.substring(0, 2), 16), parseInt(f.substring(2, 4), 16), parseInt(f.substring(4, 6), 16)];
|
|
2290
|
+
};
|
|
2291
|
+
const [ar, ag, ab] = parse(a), [br, bg, bb] = parse(b), t = pct / 100;
|
|
2292
|
+
const c = (x, y) => Math.round(x * t + y * (1 - t)).toString(16).padStart(2, "0");
|
|
2293
|
+
return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
|
|
2294
|
+
}
|
|
2295
|
+
function nodeFill(palette, isDark, nodeColor) {
|
|
2296
|
+
if (nodeColor) {
|
|
2297
|
+
return mix(nodeColor, isDark ? palette.surface : palette.bg, 25);
|
|
2298
|
+
}
|
|
2299
|
+
return mix(palette.primary, isDark ? palette.surface : palette.bg, 15);
|
|
2300
|
+
}
|
|
2301
|
+
function nodeStroke(palette, nodeColor) {
|
|
2302
|
+
return nodeColor ?? palette.textMuted;
|
|
2303
|
+
}
|
|
2304
|
+
function renderTerminal(g, node, palette, isDark) {
|
|
2305
|
+
const w = node.width;
|
|
2306
|
+
const h = node.height;
|
|
2307
|
+
const rx = h / 2;
|
|
2308
|
+
g.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", rx).attr("ry", rx).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
|
|
2309
|
+
}
|
|
2310
|
+
function renderProcess(g, node, palette, isDark) {
|
|
2311
|
+
const w = node.width;
|
|
2312
|
+
const h = node.height;
|
|
2313
|
+
g.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", 3).attr("ry", 3).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
|
|
2314
|
+
}
|
|
2315
|
+
function renderDecision(g, node, palette, isDark) {
|
|
2316
|
+
const w = node.width / 2;
|
|
2317
|
+
const h = node.height / 2;
|
|
2318
|
+
const points = [
|
|
2319
|
+
`${0},${-h}`,
|
|
2320
|
+
// top
|
|
2321
|
+
`${w},${0}`,
|
|
2322
|
+
// right
|
|
2323
|
+
`${0},${h}`,
|
|
2324
|
+
// bottom
|
|
2325
|
+
`${-w},${0}`
|
|
2326
|
+
// left
|
|
2327
|
+
].join(" ");
|
|
2328
|
+
g.append("polygon").attr("points", points).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
|
|
2329
|
+
}
|
|
2330
|
+
function renderIO(g, node, palette, isDark) {
|
|
2331
|
+
const w = node.width / 2;
|
|
2332
|
+
const h = node.height / 2;
|
|
2333
|
+
const sk = IO_SKEW;
|
|
2334
|
+
const points = [
|
|
2335
|
+
`${-w + sk},${-h}`,
|
|
2336
|
+
// top-left (shifted right)
|
|
2337
|
+
`${w + sk},${-h}`,
|
|
2338
|
+
// top-right (shifted right)
|
|
2339
|
+
`${w - sk},${h}`,
|
|
2340
|
+
// bottom-right (shifted left)
|
|
2341
|
+
`${-w - sk},${h}`
|
|
2342
|
+
// bottom-left (shifted left)
|
|
2343
|
+
].join(" ");
|
|
2344
|
+
g.append("polygon").attr("points", points).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
|
|
2345
|
+
}
|
|
2346
|
+
function renderSubroutine(g, node, palette, isDark) {
|
|
2347
|
+
const w = node.width;
|
|
2348
|
+
const h = node.height;
|
|
2349
|
+
const s = nodeStroke(palette, node.color);
|
|
2350
|
+
g.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", 3).attr("ry", 3).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", s).attr("stroke-width", NODE_STROKE_WIDTH);
|
|
2351
|
+
g.append("line").attr("x1", -w / 2 + SUBROUTINE_INSET).attr("y1", -h / 2).attr("x2", -w / 2 + SUBROUTINE_INSET).attr("y2", h / 2).attr("stroke", s).attr("stroke-width", NODE_STROKE_WIDTH);
|
|
2352
|
+
g.append("line").attr("x1", w / 2 - SUBROUTINE_INSET).attr("y1", -h / 2).attr("x2", w / 2 - SUBROUTINE_INSET).attr("y2", h / 2).attr("stroke", s).attr("stroke-width", NODE_STROKE_WIDTH);
|
|
2353
|
+
}
|
|
2354
|
+
function renderDocument(g, node, palette, isDark) {
|
|
2355
|
+
const w = node.width;
|
|
2356
|
+
const h = node.height;
|
|
2357
|
+
const waveH = DOC_WAVE_HEIGHT;
|
|
2358
|
+
const left = -w / 2;
|
|
2359
|
+
const right = w / 2;
|
|
2360
|
+
const top = -h / 2;
|
|
2361
|
+
const bottom = h / 2 - waveH;
|
|
2362
|
+
const d = [
|
|
2363
|
+
`M ${left} ${top}`,
|
|
2364
|
+
`L ${right} ${top}`,
|
|
2365
|
+
`L ${right} ${bottom}`,
|
|
2366
|
+
`C ${right - w * 0.25} ${bottom + waveH * 2}, ${left + w * 0.25} ${bottom - waveH}, ${left} ${bottom}`,
|
|
2367
|
+
"Z"
|
|
2368
|
+
].join(" ");
|
|
2369
|
+
g.append("path").attr("d", d).attr("fill", nodeFill(palette, isDark, node.color)).attr("stroke", nodeStroke(palette, node.color)).attr("stroke-width", NODE_STROKE_WIDTH);
|
|
2370
|
+
}
|
|
2371
|
+
function renderNodeShape(g, node, palette, isDark) {
|
|
2372
|
+
switch (node.shape) {
|
|
2373
|
+
case "terminal":
|
|
2374
|
+
renderTerminal(g, node, palette, isDark);
|
|
2375
|
+
break;
|
|
2376
|
+
case "process":
|
|
2377
|
+
renderProcess(g, node, palette, isDark);
|
|
2378
|
+
break;
|
|
2379
|
+
case "decision":
|
|
2380
|
+
renderDecision(g, node, palette, isDark);
|
|
2381
|
+
break;
|
|
2382
|
+
case "io":
|
|
2383
|
+
renderIO(g, node, palette, isDark);
|
|
2384
|
+
break;
|
|
2385
|
+
case "subroutine":
|
|
2386
|
+
renderSubroutine(g, node, palette, isDark);
|
|
2387
|
+
break;
|
|
2388
|
+
case "document":
|
|
2389
|
+
renderDocument(g, node, palette, isDark);
|
|
2390
|
+
break;
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
function renderFlowchart(container, graph, layout, palette, isDark, onClickItem, exportDims) {
|
|
2394
|
+
d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
2395
|
+
const width = exportDims?.width ?? container.clientWidth;
|
|
2396
|
+
const height = exportDims?.height ?? container.clientHeight;
|
|
2397
|
+
if (width <= 0 || height <= 0) return;
|
|
2398
|
+
const titleOffset = graph.title ? TITLE_HEIGHT : 0;
|
|
2399
|
+
const diagramW = layout.width;
|
|
2400
|
+
const diagramH = layout.height + titleOffset;
|
|
2401
|
+
const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
|
|
2402
|
+
const scaleY = (height - DIAGRAM_PADDING * 2) / diagramH;
|
|
2403
|
+
const scale = Math.min(1, scaleX, scaleY);
|
|
2404
|
+
const scaledW = diagramW * scale;
|
|
2405
|
+
const scaledH = diagramH * scale;
|
|
2406
|
+
const offsetX = (width - scaledW) / 2;
|
|
2407
|
+
const offsetY = (height - scaledH) / 2;
|
|
2408
|
+
const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("background", palette.bg).style("font-family", FONT_FAMILY);
|
|
2409
|
+
const defs = svg.append("defs");
|
|
2410
|
+
defs.append("marker").attr("id", "fc-arrow").attr("viewBox", `0 0 ${ARROWHEAD_W} ${ARROWHEAD_H}`).attr("refX", ARROWHEAD_W).attr("refY", ARROWHEAD_H / 2).attr("markerWidth", ARROWHEAD_W).attr("markerHeight", ARROWHEAD_H).attr("orient", "auto").append("polygon").attr("points", `0,0 ${ARROWHEAD_W},${ARROWHEAD_H / 2} 0,${ARROWHEAD_H}`).attr("fill", palette.textMuted);
|
|
2411
|
+
const edgeColors = /* @__PURE__ */ new Set();
|
|
2412
|
+
for (const edge of layout.edges) {
|
|
2413
|
+
if (edge.color) edgeColors.add(edge.color);
|
|
2414
|
+
}
|
|
2415
|
+
for (const color of edgeColors) {
|
|
2416
|
+
const id = `fc-arrow-${color.replace("#", "")}`;
|
|
2417
|
+
defs.append("marker").attr("id", id).attr("viewBox", `0 0 ${ARROWHEAD_W} ${ARROWHEAD_H}`).attr("refX", ARROWHEAD_W).attr("refY", ARROWHEAD_H / 2).attr("markerWidth", ARROWHEAD_W).attr("markerHeight", ARROWHEAD_H).attr("orient", "auto").append("polygon").attr("points", `0,0 ${ARROWHEAD_W},${ARROWHEAD_H / 2} 0,${ARROWHEAD_H}`).attr("fill", color);
|
|
2418
|
+
}
|
|
2419
|
+
const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
|
|
2420
|
+
if (graph.title) {
|
|
2421
|
+
mainG.append("text").attr("x", diagramW / 2).attr("y", TITLE_FONT_SIZE).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", "bold").attr("class", "fc-title").text(graph.title);
|
|
2422
|
+
}
|
|
2423
|
+
const contentG = mainG.append("g").attr("transform", `translate(0, ${titleOffset})`);
|
|
2424
|
+
for (const group of layout.groups) {
|
|
2425
|
+
if (group.width === 0 && group.height === 0) continue;
|
|
2426
|
+
const gx = group.x - GROUP_EXTRA_PADDING;
|
|
2427
|
+
const gy = group.y - GROUP_EXTRA_PADDING - GROUP_LABEL_FONT_SIZE - 4;
|
|
2428
|
+
const gw = group.width + GROUP_EXTRA_PADDING * 2;
|
|
2429
|
+
const gh = group.height + GROUP_EXTRA_PADDING * 2 + GROUP_LABEL_FONT_SIZE + 4;
|
|
2430
|
+
const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
|
|
2431
|
+
const strokeColor = group.color ?? palette.textMuted;
|
|
2432
|
+
contentG.append("rect").attr("x", gx).attr("y", gy).attr("width", gw).attr("height", gh).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "fc-group");
|
|
2433
|
+
contentG.append("text").attr("x", gx + 8).attr("y", gy + GROUP_LABEL_FONT_SIZE + 4).attr("fill", strokeColor).attr("font-size", GROUP_LABEL_FONT_SIZE).attr("font-weight", "bold").attr("opacity", 0.7).attr("class", "fc-group-label").text(group.label);
|
|
2434
|
+
}
|
|
2435
|
+
for (const edge of layout.edges) {
|
|
2436
|
+
if (edge.points.length < 2) continue;
|
|
2437
|
+
const edgeG = contentG.append("g").attr("class", "fc-edge-group").attr("data-line-number", String(edge.lineNumber));
|
|
2438
|
+
const edgeColor = edge.color ?? palette.textMuted;
|
|
2439
|
+
const markerId = edge.color ? `fc-arrow-${edge.color.replace("#", "")}` : "fc-arrow";
|
|
2440
|
+
const pathD = lineGenerator(edge.points);
|
|
2441
|
+
if (pathD) {
|
|
2442
|
+
edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor).attr("stroke-width", EDGE_STROKE_WIDTH).attr("marker-end", `url(#${markerId})`).attr("class", "fc-edge");
|
|
2443
|
+
}
|
|
2444
|
+
if (edge.label) {
|
|
2445
|
+
const midIdx = Math.floor(edge.points.length / 2);
|
|
2446
|
+
const midPt = edge.points[midIdx];
|
|
2447
|
+
const labelLen = edge.label.length;
|
|
2448
|
+
const bgW = labelLen * 7 + 8;
|
|
2449
|
+
const bgH = 16;
|
|
2450
|
+
edgeG.append("rect").attr("x", midPt.x - bgW / 2).attr("y", midPt.y - bgH / 2 - 1).attr("width", bgW).attr("height", bgH).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85).attr("class", "fc-edge-label-bg");
|
|
2451
|
+
edgeG.append("text").attr("x", midPt.x).attr("y", midPt.y + 4).attr("text-anchor", "middle").attr("fill", edgeColor).attr("font-size", EDGE_LABEL_FONT_SIZE).attr("class", "fc-edge-label").text(edge.label);
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
for (const node of layout.nodes) {
|
|
2455
|
+
const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber));
|
|
2456
|
+
if (onClickItem) {
|
|
2457
|
+
nodeG.style("cursor", "pointer").on("click", () => {
|
|
2458
|
+
onClickItem(node.lineNumber);
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
renderNodeShape(nodeG, node, palette, isDark);
|
|
2462
|
+
nodeG.append("text").attr("x", 0).attr("y", 0).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", palette.text).attr("font-size", NODE_FONT_SIZE).text(node.label);
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
function renderFlowchartForExport(content, theme, palette) {
|
|
2466
|
+
const parsed = parseFlowchart(content, palette);
|
|
2467
|
+
if (parsed.error || parsed.nodes.length === 0) return "";
|
|
2468
|
+
const layout = layoutGraph(parsed);
|
|
2469
|
+
const isDark = theme === "dark";
|
|
2470
|
+
const container = document.createElement("div");
|
|
2471
|
+
container.style.width = `${layout.width + DIAGRAM_PADDING * 2}px`;
|
|
2472
|
+
container.style.height = `${layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0)}px`;
|
|
2473
|
+
container.style.position = "absolute";
|
|
2474
|
+
container.style.left = "-9999px";
|
|
2475
|
+
document.body.appendChild(container);
|
|
2476
|
+
const exportWidth = layout.width + DIAGRAM_PADDING * 2;
|
|
2477
|
+
const exportHeight = layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0);
|
|
2478
|
+
try {
|
|
2479
|
+
renderFlowchart(
|
|
2480
|
+
container,
|
|
2481
|
+
parsed,
|
|
2482
|
+
layout,
|
|
2483
|
+
palette,
|
|
2484
|
+
isDark,
|
|
2485
|
+
void 0,
|
|
2486
|
+
{ width: exportWidth, height: exportHeight }
|
|
2487
|
+
);
|
|
2488
|
+
const svgEl = container.querySelector("svg");
|
|
2489
|
+
if (!svgEl) return "";
|
|
2490
|
+
if (theme === "transparent") {
|
|
2491
|
+
svgEl.style.background = "none";
|
|
2492
|
+
}
|
|
2493
|
+
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
2494
|
+
svgEl.style.fontFamily = FONT_FAMILY;
|
|
2495
|
+
return svgEl.outerHTML;
|
|
2496
|
+
} finally {
|
|
2497
|
+
document.body.removeChild(container);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
var DIAGRAM_PADDING, TITLE_HEIGHT, TITLE_FONT_SIZE, NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE, GROUP_LABEL_FONT_SIZE, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, ARROWHEAD_W, ARROWHEAD_H, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT, GROUP_EXTRA_PADDING, lineGenerator;
|
|
2501
|
+
var init_flowchart_renderer = __esm({
|
|
2502
|
+
"src/graph/flowchart-renderer.ts"() {
|
|
2503
|
+
"use strict";
|
|
2504
|
+
init_fonts();
|
|
2505
|
+
init_flowchart_parser();
|
|
2506
|
+
init_layout();
|
|
2507
|
+
DIAGRAM_PADDING = 20;
|
|
2508
|
+
TITLE_HEIGHT = 30;
|
|
2509
|
+
TITLE_FONT_SIZE = 18;
|
|
2510
|
+
NODE_FONT_SIZE = 13;
|
|
2511
|
+
EDGE_LABEL_FONT_SIZE = 11;
|
|
2512
|
+
GROUP_LABEL_FONT_SIZE = 11;
|
|
2513
|
+
EDGE_STROKE_WIDTH = 1.5;
|
|
2514
|
+
NODE_STROKE_WIDTH = 1.5;
|
|
2515
|
+
ARROWHEAD_W = 10;
|
|
2516
|
+
ARROWHEAD_H = 7;
|
|
2517
|
+
IO_SKEW = 15;
|
|
2518
|
+
SUBROUTINE_INSET = 8;
|
|
2519
|
+
DOC_WAVE_HEIGHT = 10;
|
|
2520
|
+
GROUP_EXTRA_PADDING = 12;
|
|
2521
|
+
lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
|
|
2522
|
+
}
|
|
2523
|
+
});
|
|
2524
|
+
|
|
1615
2525
|
// src/sequence/renderer.ts
|
|
1616
2526
|
var renderer_exports = {};
|
|
1617
2527
|
__export(renderer_exports, {
|
|
@@ -1622,8 +2532,45 @@ __export(renderer_exports, {
|
|
|
1622
2532
|
groupMessagesBySection: () => groupMessagesBySection,
|
|
1623
2533
|
renderSequenceDiagram: () => renderSequenceDiagram
|
|
1624
2534
|
});
|
|
1625
|
-
import * as
|
|
1626
|
-
function
|
|
2535
|
+
import * as d3Selection2 from "d3-selection";
|
|
2536
|
+
function parseInlineMarkdown(text) {
|
|
2537
|
+
const spans = [];
|
|
2538
|
+
const regex = /\*\*(.+?)\*\*|__(.+?)__|\*(.+?)\*|_(.+?)_|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*_`[]+)/g;
|
|
2539
|
+
let match;
|
|
2540
|
+
while ((match = regex.exec(text)) !== null) {
|
|
2541
|
+
if (match[1]) spans.push({ text: match[1], bold: true });
|
|
2542
|
+
else if (match[2]) spans.push({ text: match[2], bold: true });
|
|
2543
|
+
else if (match[3]) spans.push({ text: match[3], italic: true });
|
|
2544
|
+
else if (match[4]) spans.push({ text: match[4], italic: true });
|
|
2545
|
+
else if (match[5]) spans.push({ text: match[5], code: true });
|
|
2546
|
+
else if (match[6]) spans.push({ text: match[6], href: match[7] });
|
|
2547
|
+
else if (match[8]) spans.push({ text: match[8] });
|
|
2548
|
+
}
|
|
2549
|
+
return spans;
|
|
2550
|
+
}
|
|
2551
|
+
function wrapTextLines(text, maxChars) {
|
|
2552
|
+
const rawLines = text.split("\n");
|
|
2553
|
+
const wrapped = [];
|
|
2554
|
+
for (const line3 of rawLines) {
|
|
2555
|
+
if (line3.length <= maxChars) {
|
|
2556
|
+
wrapped.push(line3);
|
|
2557
|
+
} else {
|
|
2558
|
+
const words = line3.split(" ");
|
|
2559
|
+
let current = "";
|
|
2560
|
+
for (const word of words) {
|
|
2561
|
+
if (current && (current + " " + word).length > maxChars) {
|
|
2562
|
+
wrapped.push(current);
|
|
2563
|
+
current = word;
|
|
2564
|
+
} else {
|
|
2565
|
+
current = current ? current + " " + word : word;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
if (current) wrapped.push(current);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
return wrapped;
|
|
2572
|
+
}
|
|
2573
|
+
function mix2(a, b, pct) {
|
|
1627
2574
|
const parse = (h) => {
|
|
1628
2575
|
const r = h.replace("#", "");
|
|
1629
2576
|
const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
|
|
@@ -1729,7 +2676,12 @@ function groupMessagesBySection(elements, messages) {
|
|
|
1729
2676
|
...collectIndices(el.children),
|
|
1730
2677
|
...collectIndices(el.elseChildren)
|
|
1731
2678
|
);
|
|
1732
|
-
|
|
2679
|
+
if (el.elseIfBranches) {
|
|
2680
|
+
for (const branch of el.elseIfBranches) {
|
|
2681
|
+
indices.push(...collectIndices(branch.children));
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
} else if (isSequenceSection(el) || isSequenceNote(el)) {
|
|
1733
2685
|
continue;
|
|
1734
2686
|
} else {
|
|
1735
2687
|
const idx = messages.indexOf(el);
|
|
@@ -1745,7 +2697,7 @@ function groupMessagesBySection(elements, messages) {
|
|
|
1745
2697
|
} else if (currentGroup) {
|
|
1746
2698
|
if (isSequenceBlock(el)) {
|
|
1747
2699
|
currentGroup.messageIndices.push(...collectIndices([el]));
|
|
1748
|
-
} else {
|
|
2700
|
+
} else if (!isSequenceNote(el)) {
|
|
1749
2701
|
const idx = messages.indexOf(el);
|
|
1750
2702
|
if (idx >= 0) currentGroup.messageIndices.push(idx);
|
|
1751
2703
|
}
|
|
@@ -1902,7 +2854,7 @@ function applyGroupOrdering(participants, groups) {
|
|
|
1902
2854
|
return result;
|
|
1903
2855
|
}
|
|
1904
2856
|
function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
|
|
1905
|
-
|
|
2857
|
+
d3Selection2.select(container).selectAll("*").remove();
|
|
1906
2858
|
const { title, messages, elements, groups, options: parsedOptions } = parsed;
|
|
1907
2859
|
const collapsedSections = options?.collapsedSections;
|
|
1908
2860
|
const participants = applyPositionOverrides(
|
|
@@ -1943,7 +2895,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1943
2895
|
if (isSequenceBlock(el)) {
|
|
1944
2896
|
const idx = findFirstMsgIndex(el.children);
|
|
1945
2897
|
if (idx >= 0) return idx;
|
|
1946
|
-
|
|
2898
|
+
if (el.elseIfBranches) {
|
|
2899
|
+
for (const branch of el.elseIfBranches) {
|
|
2900
|
+
const branchIdx = findFirstMsgIndex(branch.children);
|
|
2901
|
+
if (branchIdx >= 0) return branchIdx;
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
const elseIdx = findFirstMsgIndex(el.elseChildren);
|
|
2905
|
+
if (elseIdx >= 0) return elseIdx;
|
|
2906
|
+
} else if (!isSequenceSection(el) && !isSequenceNote(el)) {
|
|
1947
2907
|
const idx = messages.indexOf(el);
|
|
1948
2908
|
if (idx >= 0 && !hiddenMsgIndices.has(idx)) return idx;
|
|
1949
2909
|
}
|
|
@@ -1963,6 +2923,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1963
2923
|
if (!isSequenceBlock(el)) continue;
|
|
1964
2924
|
const firstIdx = findFirstMsgIndex(el.children);
|
|
1965
2925
|
if (firstIdx >= 0) addExtra(firstIdx, BLOCK_HEADER_SPACE);
|
|
2926
|
+
if (el.elseIfBranches) {
|
|
2927
|
+
for (const branch of el.elseIfBranches) {
|
|
2928
|
+
const firstBranchIdx = findFirstMsgIndex(branch.children);
|
|
2929
|
+
if (firstBranchIdx >= 0) addExtra(firstBranchIdx, BLOCK_HEADER_SPACE);
|
|
2930
|
+
markBlockSpacing(branch.children);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
1966
2933
|
const firstElseIdx = findFirstMsgIndex(el.elseChildren);
|
|
1967
2934
|
if (firstElseIdx >= 0) addExtra(firstElseIdx, BLOCK_HEADER_SPACE);
|
|
1968
2935
|
markBlockSpacing(el.children);
|
|
@@ -1976,6 +2943,34 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1976
2943
|
if (elements && elements.length > 0) {
|
|
1977
2944
|
markBlockSpacing(elements);
|
|
1978
2945
|
}
|
|
2946
|
+
const NOTE_OFFSET_BELOW = 16;
|
|
2947
|
+
const computeNoteHeight = (text) => {
|
|
2948
|
+
const lines = wrapTextLines(text, NOTE_CHARS_PER_LINE);
|
|
2949
|
+
return lines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
|
|
2950
|
+
};
|
|
2951
|
+
const markNoteSpacing = (els) => {
|
|
2952
|
+
for (let i = 0; i < els.length; i++) {
|
|
2953
|
+
const el = els[i];
|
|
2954
|
+
if (isSequenceNote(el)) {
|
|
2955
|
+
const noteH = computeNoteHeight(el.text);
|
|
2956
|
+
const nextIdx = i + 1 < els.length ? findFirstMsgIndex([els[i + 1]]) : -1;
|
|
2957
|
+
if (nextIdx >= 0) {
|
|
2958
|
+
addExtra(nextIdx, noteH + NOTE_OFFSET_BELOW);
|
|
2959
|
+
}
|
|
2960
|
+
} else if (isSequenceBlock(el)) {
|
|
2961
|
+
markNoteSpacing(el.children);
|
|
2962
|
+
if (el.elseIfBranches) {
|
|
2963
|
+
for (const branch of el.elseIfBranches) {
|
|
2964
|
+
markNoteSpacing(branch.children);
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
markNoteSpacing(el.elseChildren);
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
};
|
|
2971
|
+
if (elements && elements.length > 0) {
|
|
2972
|
+
markNoteSpacing(elements);
|
|
2973
|
+
}
|
|
1979
2974
|
const preSectionMsgIndices = [];
|
|
1980
2975
|
const sectionRegions = [];
|
|
1981
2976
|
{
|
|
@@ -1984,15 +2979,27 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1984
2979
|
for (const child of block.children) {
|
|
1985
2980
|
if (isSequenceBlock(child)) {
|
|
1986
2981
|
indices.push(...collectMsgIndicesFromBlock(child));
|
|
1987
|
-
} else if (!isSequenceSection(child)) {
|
|
2982
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
1988
2983
|
const idx = messages.indexOf(child);
|
|
1989
2984
|
if (idx >= 0) indices.push(idx);
|
|
1990
2985
|
}
|
|
1991
2986
|
}
|
|
2987
|
+
if (block.elseIfBranches) {
|
|
2988
|
+
for (const branch of block.elseIfBranches) {
|
|
2989
|
+
for (const child of branch.children) {
|
|
2990
|
+
if (isSequenceBlock(child)) {
|
|
2991
|
+
indices.push(...collectMsgIndicesFromBlock(child));
|
|
2992
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
2993
|
+
const idx = messages.indexOf(child);
|
|
2994
|
+
if (idx >= 0) indices.push(idx);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
1992
2999
|
for (const child of block.elseChildren) {
|
|
1993
3000
|
if (isSequenceBlock(child)) {
|
|
1994
3001
|
indices.push(...collectMsgIndicesFromBlock(child));
|
|
1995
|
-
} else if (!isSequenceSection(child)) {
|
|
3002
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
1996
3003
|
const idx = messages.indexOf(child);
|
|
1997
3004
|
if (idx >= 0) indices.push(idx);
|
|
1998
3005
|
}
|
|
@@ -2067,7 +3074,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2067
3074
|
const GROUP_PADDING_TOP = 22;
|
|
2068
3075
|
const GROUP_PADDING_BOTTOM = 8;
|
|
2069
3076
|
const GROUP_LABEL_SIZE = 11;
|
|
2070
|
-
const titleOffset = title ?
|
|
3077
|
+
const titleOffset = title ? TITLE_HEIGHT2 : 0;
|
|
2071
3078
|
const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
|
|
2072
3079
|
const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
|
|
2073
3080
|
const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
|
|
@@ -2121,7 +3128,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2121
3128
|
participants.forEach((p, i) => {
|
|
2122
3129
|
participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
|
|
2123
3130
|
});
|
|
2124
|
-
const svg =
|
|
3131
|
+
const svg = d3Selection2.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
|
|
2125
3132
|
const defs = svg.append("defs");
|
|
2126
3133
|
defs.append("marker").attr("id", "seq-arrowhead").attr("viewBox", `0 0 ${ARROWHEAD_SIZE} ${ARROWHEAD_SIZE}`).attr("refX", ARROWHEAD_SIZE).attr("refY", ARROWHEAD_SIZE / 2).attr("markerWidth", ARROWHEAD_SIZE).attr("markerHeight", ARROWHEAD_SIZE).attr("orient", "auto").append("polygon").attr(
|
|
2127
3134
|
"points",
|
|
@@ -2147,7 +3154,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2147
3154
|
const boxY = participantStartY - GROUP_PADDING_TOP;
|
|
2148
3155
|
const boxH = PARTICIPANT_BOX_HEIGHT + GROUP_PADDING_TOP + GROUP_PADDING_BOTTOM;
|
|
2149
3156
|
const resolvedGroupColor = group.color ? resolveColor(group.color, palette) : void 0;
|
|
2150
|
-
const fillColor = resolvedGroupColor ?
|
|
3157
|
+
const fillColor = resolvedGroupColor ? mix2(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
|
|
2151
3158
|
const strokeColor = resolvedGroupColor || palette.textMuted;
|
|
2152
3159
|
svg.append("rect").attr("x", minX).attr("y", boxY).attr("width", maxX - minX).attr("height", boxH).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "group-box").attr("data-group-line", String(group.lineNumber));
|
|
2153
3160
|
svg.append("text").attr("x", minX + 8).attr("y", boxY + GROUP_LABEL_SIZE + 4).attr("fill", strokeColor).attr("font-size", GROUP_LABEL_SIZE).attr("font-weight", "bold").attr("opacity", 0.7).attr("class", "group-label").attr("data-group-line", String(group.lineNumber)).text(group.name);
|
|
@@ -2172,7 +3179,12 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2172
3179
|
...collectMsgIndices(el.children),
|
|
2173
3180
|
...collectMsgIndices(el.elseChildren)
|
|
2174
3181
|
);
|
|
2175
|
-
|
|
3182
|
+
if (el.elseIfBranches) {
|
|
3183
|
+
for (const branch of el.elseIfBranches) {
|
|
3184
|
+
indices.push(...collectMsgIndices(branch.children));
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
} else if (!isSequenceSection(el) && !isSequenceNote(el)) {
|
|
2176
3188
|
const idx = messages.indexOf(el);
|
|
2177
3189
|
if (idx >= 0) indices.push(idx);
|
|
2178
3190
|
}
|
|
@@ -2185,8 +3197,21 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2185
3197
|
for (const el of els) {
|
|
2186
3198
|
if (!isSequenceBlock(el)) continue;
|
|
2187
3199
|
const ifIndices = collectMsgIndices(el.children);
|
|
3200
|
+
const elseIfBranchData = [];
|
|
3201
|
+
if (el.elseIfBranches) {
|
|
3202
|
+
for (const branch of el.elseIfBranches) {
|
|
3203
|
+
elseIfBranchData.push({
|
|
3204
|
+
label: branch.label,
|
|
3205
|
+
indices: collectMsgIndices(branch.children)
|
|
3206
|
+
});
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
2188
3209
|
const elseIndices = collectMsgIndices(el.elseChildren);
|
|
2189
|
-
const allIndices = [
|
|
3210
|
+
const allIndices = [
|
|
3211
|
+
...ifIndices,
|
|
3212
|
+
...elseIfBranchData.flatMap((b) => b.indices),
|
|
3213
|
+
...elseIndices
|
|
3214
|
+
];
|
|
2190
3215
|
if (allIndices.length === 0) continue;
|
|
2191
3216
|
let minStep = Infinity;
|
|
2192
3217
|
let maxStep = -Infinity;
|
|
@@ -2224,6 +3249,32 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2224
3249
|
italic: false,
|
|
2225
3250
|
blockLine: el.lineNumber
|
|
2226
3251
|
});
|
|
3252
|
+
for (const branchData of elseIfBranchData) {
|
|
3253
|
+
if (branchData.indices.length > 0) {
|
|
3254
|
+
let firstBranchStep = Infinity;
|
|
3255
|
+
for (const mi of branchData.indices) {
|
|
3256
|
+
const first = msgToFirstStep.get(mi);
|
|
3257
|
+
if (first !== void 0)
|
|
3258
|
+
firstBranchStep = Math.min(firstBranchStep, first);
|
|
3259
|
+
}
|
|
3260
|
+
if (firstBranchStep < Infinity) {
|
|
3261
|
+
const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
|
|
3262
|
+
deferredLines.push({
|
|
3263
|
+
x1: frameX,
|
|
3264
|
+
y1: dividerY,
|
|
3265
|
+
x2: frameX + frameW,
|
|
3266
|
+
y2: dividerY
|
|
3267
|
+
});
|
|
3268
|
+
deferredLabels.push({
|
|
3269
|
+
x: frameX + 6,
|
|
3270
|
+
y: dividerY + 14,
|
|
3271
|
+
text: `else if ${branchData.label}`,
|
|
3272
|
+
bold: false,
|
|
3273
|
+
italic: true
|
|
3274
|
+
});
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
2227
3278
|
if (elseIndices.length > 0) {
|
|
2228
3279
|
let firstElseStep = Infinity;
|
|
2229
3280
|
for (const mi of elseIndices) {
|
|
@@ -2249,6 +3300,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2249
3300
|
}
|
|
2250
3301
|
}
|
|
2251
3302
|
renderBlockFrames(el.children, depth + 1);
|
|
3303
|
+
if (el.elseIfBranches) {
|
|
3304
|
+
for (const branch of el.elseIfBranches) {
|
|
3305
|
+
renderBlockFrames(branch.children, depth + 1);
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
2252
3308
|
renderBlockFrames(el.elseChildren, depth + 1);
|
|
2253
3309
|
}
|
|
2254
3310
|
};
|
|
@@ -2270,7 +3326,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2270
3326
|
if (msg) coveredLines.push(msg.lineNumber);
|
|
2271
3327
|
}
|
|
2272
3328
|
svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
|
|
2273
|
-
const actFill =
|
|
3329
|
+
const actFill = mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
|
|
2274
3330
|
svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", palette.primary).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("class", "activation");
|
|
2275
3331
|
});
|
|
2276
3332
|
for (const ln of deferredLines) {
|
|
@@ -2396,6 +3452,94 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2396
3452
|
}
|
|
2397
3453
|
}
|
|
2398
3454
|
});
|
|
3455
|
+
const noteFill = isDark ? mix2(palette.surface, palette.bg, 50) : mix2(palette.bg, palette.surface, 15);
|
|
3456
|
+
const findAssociatedStep = (note) => {
|
|
3457
|
+
let bestMsgIndex = -1;
|
|
3458
|
+
let bestLine = -1;
|
|
3459
|
+
for (let mi = 0; mi < messages.length; mi++) {
|
|
3460
|
+
if (messages[mi].lineNumber < note.lineNumber && messages[mi].lineNumber > bestLine && !hiddenMsgIndices.has(mi)) {
|
|
3461
|
+
bestLine = messages[mi].lineNumber;
|
|
3462
|
+
bestMsgIndex = mi;
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
if (bestMsgIndex < 0) return -1;
|
|
3466
|
+
return msgToFirstStep.get(bestMsgIndex) ?? -1;
|
|
3467
|
+
};
|
|
3468
|
+
const renderNoteElements = (els) => {
|
|
3469
|
+
for (const el of els) {
|
|
3470
|
+
if (isSequenceNote(el)) {
|
|
3471
|
+
const px = participantX.get(el.participantId);
|
|
3472
|
+
if (px === void 0) continue;
|
|
3473
|
+
const si = findAssociatedStep(el);
|
|
3474
|
+
if (si < 0) continue;
|
|
3475
|
+
const noteY = stepY(si);
|
|
3476
|
+
const wrappedLines = wrapTextLines(el.text, NOTE_CHARS_PER_LINE);
|
|
3477
|
+
const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
|
|
3478
|
+
const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
|
|
3479
|
+
const noteW = Math.min(
|
|
3480
|
+
NOTE_MAX_W,
|
|
3481
|
+
Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
|
|
3482
|
+
);
|
|
3483
|
+
const isRight = el.position === "right";
|
|
3484
|
+
const noteX = isRight ? px + ACTIVATION_WIDTH + NOTE_GAP : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
|
|
3485
|
+
const noteTopY = noteY + NOTE_OFFSET_BELOW;
|
|
3486
|
+
const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber)).attr("data-line-end", String(el.endLineNumber));
|
|
3487
|
+
noteG.append("path").attr(
|
|
3488
|
+
"d",
|
|
3489
|
+
[
|
|
3490
|
+
`M ${noteX} ${noteTopY}`,
|
|
3491
|
+
`L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
|
|
3492
|
+
`L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
|
|
3493
|
+
`L ${noteX + noteW} ${noteTopY + noteH}`,
|
|
3494
|
+
`L ${noteX} ${noteTopY + noteH}`,
|
|
3495
|
+
"Z"
|
|
3496
|
+
].join(" ")
|
|
3497
|
+
).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
|
|
3498
|
+
noteG.append("path").attr(
|
|
3499
|
+
"d",
|
|
3500
|
+
[
|
|
3501
|
+
`M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
|
|
3502
|
+
`L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
|
|
3503
|
+
`L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
|
|
3504
|
+
].join(" ")
|
|
3505
|
+
).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
|
|
3506
|
+
wrappedLines.forEach((line3, li) => {
|
|
3507
|
+
const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
|
|
3508
|
+
const isBullet = line3.startsWith("- ");
|
|
3509
|
+
const bulletIndent = isBullet ? 10 : 0;
|
|
3510
|
+
const displayLine = isBullet ? line3.slice(2) : line3;
|
|
3511
|
+
const textEl = noteG.append("text").attr("x", noteX + NOTE_PAD_H + bulletIndent).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).attr("class", "note-text");
|
|
3512
|
+
if (isBullet) {
|
|
3513
|
+
noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).text("\u2022");
|
|
3514
|
+
}
|
|
3515
|
+
const spans = parseInlineMarkdown(displayLine);
|
|
3516
|
+
for (const span of spans) {
|
|
3517
|
+
if (span.href) {
|
|
3518
|
+
const a = textEl.append("a").attr("href", span.href);
|
|
3519
|
+
a.append("tspan").text(span.text).attr("fill", palette.primary).style("text-decoration", "underline");
|
|
3520
|
+
} else {
|
|
3521
|
+
const tspan = textEl.append("tspan").text(span.text);
|
|
3522
|
+
if (span.bold) tspan.attr("font-weight", "bold");
|
|
3523
|
+
if (span.italic) tspan.attr("font-style", "italic");
|
|
3524
|
+
if (span.code)
|
|
3525
|
+
tspan.attr("font-family", "monospace").attr("font-size", NOTE_FONT_SIZE - 1);
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
});
|
|
3529
|
+
} else if (isSequenceBlock(el)) {
|
|
3530
|
+
renderNoteElements(el.children);
|
|
3531
|
+
if (el.elseIfBranches) {
|
|
3532
|
+
for (const branch of el.elseIfBranches) {
|
|
3533
|
+
renderNoteElements(branch.children);
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
renderNoteElements(el.elseChildren);
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
};
|
|
3540
|
+
if (elements && elements.length > 0) {
|
|
3541
|
+
renderNoteElements(elements);
|
|
3542
|
+
}
|
|
2399
3543
|
}
|
|
2400
3544
|
function renderParticipant(svg, participant, cx, cy, palette, isDark) {
|
|
2401
3545
|
const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
|
|
@@ -2437,7 +3581,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark) {
|
|
|
2437
3581
|
isActor ? PARTICIPANT_BOX_HEIGHT + 14 : PARTICIPANT_BOX_HEIGHT / 2 + 5
|
|
2438
3582
|
).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 13).attr("font-weight", 500).text(participant.label);
|
|
2439
3583
|
}
|
|
2440
|
-
var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN,
|
|
3584
|
+
var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT2, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, fill, stroke, SW, W, H;
|
|
2441
3585
|
var init_renderer = __esm({
|
|
2442
3586
|
"src/sequence/renderer.ts"() {
|
|
2443
3587
|
"use strict";
|
|
@@ -2448,13 +3592,22 @@ var init_renderer = __esm({
|
|
|
2448
3592
|
PARTICIPANT_BOX_WIDTH = 120;
|
|
2449
3593
|
PARTICIPANT_BOX_HEIGHT = 50;
|
|
2450
3594
|
TOP_MARGIN = 20;
|
|
2451
|
-
|
|
3595
|
+
TITLE_HEIGHT2 = 30;
|
|
2452
3596
|
PARTICIPANT_Y_OFFSET = 10;
|
|
2453
3597
|
SERVICE_BORDER_RADIUS = 10;
|
|
2454
3598
|
MESSAGE_START_OFFSET = 30;
|
|
2455
3599
|
LIFELINE_TAIL = 30;
|
|
2456
3600
|
ARROWHEAD_SIZE = 8;
|
|
2457
|
-
|
|
3601
|
+
NOTE_MAX_W = 200;
|
|
3602
|
+
NOTE_FOLD = 10;
|
|
3603
|
+
NOTE_PAD_H = 8;
|
|
3604
|
+
NOTE_PAD_V = 6;
|
|
3605
|
+
NOTE_FONT_SIZE = 10;
|
|
3606
|
+
NOTE_LINE_H = 14;
|
|
3607
|
+
NOTE_GAP = 15;
|
|
3608
|
+
NOTE_CHAR_W = 6;
|
|
3609
|
+
NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
|
|
3610
|
+
fill = (palette, isDark) => mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
|
|
2458
3611
|
stroke = (palette) => palette.textMuted;
|
|
2459
3612
|
SW = 1.5;
|
|
2460
3613
|
W = PARTICIPANT_BOX_WIDTH;
|
|
@@ -2462,50 +3615,8 @@ var init_renderer = __esm({
|
|
|
2462
3615
|
}
|
|
2463
3616
|
});
|
|
2464
3617
|
|
|
2465
|
-
// src/
|
|
2466
|
-
|
|
2467
|
-
var DGMO_CHART_TYPE_MAP = {
|
|
2468
|
-
// Standard charts (via ECharts)
|
|
2469
|
-
bar: "echart",
|
|
2470
|
-
line: "echart",
|
|
2471
|
-
"multi-line": "echart",
|
|
2472
|
-
area: "echart",
|
|
2473
|
-
pie: "echart",
|
|
2474
|
-
doughnut: "echart",
|
|
2475
|
-
radar: "echart",
|
|
2476
|
-
"polar-area": "echart",
|
|
2477
|
-
"bar-stacked": "echart",
|
|
2478
|
-
// ECharts
|
|
2479
|
-
scatter: "echart",
|
|
2480
|
-
sankey: "echart",
|
|
2481
|
-
chord: "echart",
|
|
2482
|
-
function: "echart",
|
|
2483
|
-
heatmap: "echart",
|
|
2484
|
-
funnel: "echart",
|
|
2485
|
-
// D3
|
|
2486
|
-
slope: "d3",
|
|
2487
|
-
wordcloud: "d3",
|
|
2488
|
-
arc: "d3",
|
|
2489
|
-
timeline: "d3",
|
|
2490
|
-
venn: "d3",
|
|
2491
|
-
quadrant: "d3",
|
|
2492
|
-
sequence: "d3"
|
|
2493
|
-
};
|
|
2494
|
-
function getDgmoFramework(chartType) {
|
|
2495
|
-
return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
|
|
2496
|
-
}
|
|
2497
|
-
function parseDgmoChartType(content) {
|
|
2498
|
-
const lines = content.split("\n");
|
|
2499
|
-
for (const line2 of lines) {
|
|
2500
|
-
const trimmed = line2.trim();
|
|
2501
|
-
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
|
|
2502
|
-
continue;
|
|
2503
|
-
const match = trimmed.match(/^chart\s*:\s*(.+)/i);
|
|
2504
|
-
if (match) return match[1].trim().toLowerCase();
|
|
2505
|
-
}
|
|
2506
|
-
if (looksLikeSequence(content)) return "sequence";
|
|
2507
|
-
return null;
|
|
2508
|
-
}
|
|
3618
|
+
// src/index.ts
|
|
3619
|
+
init_dgmo_router();
|
|
2509
3620
|
|
|
2510
3621
|
// src/chart.ts
|
|
2511
3622
|
init_colors();
|
|
@@ -2867,7 +3978,7 @@ function parseEChart(content, palette) {
|
|
|
2867
3978
|
function buildEChartsOption(parsed, palette, isDark) {
|
|
2868
3979
|
const textColor = palette.text;
|
|
2869
3980
|
const axisLineColor = palette.border;
|
|
2870
|
-
const gridOpacity = isDark ? 0.7 : 0.
|
|
3981
|
+
const gridOpacity = isDark ? 0.7 : 0.55;
|
|
2871
3982
|
const colors = getSeriesColors(palette);
|
|
2872
3983
|
if (parsed.error) {
|
|
2873
3984
|
return {};
|
|
@@ -3573,7 +4684,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
|
|
|
3573
4684
|
const textColor = palette.text;
|
|
3574
4685
|
const axisLineColor = palette.border;
|
|
3575
4686
|
const splitLineColor = palette.border;
|
|
3576
|
-
const gridOpacity = isDark ? 0.7 : 0.
|
|
4687
|
+
const gridOpacity = isDark ? 0.7 : 0.55;
|
|
3577
4688
|
const colors = getSeriesColors(palette);
|
|
3578
4689
|
const titleConfig = parsed.title ? {
|
|
3579
4690
|
text: parsed.title,
|
|
@@ -3989,8 +5100,8 @@ init_fonts();
|
|
|
3989
5100
|
init_colors();
|
|
3990
5101
|
init_palettes();
|
|
3991
5102
|
import * as d3Scale from "d3-scale";
|
|
3992
|
-
import * as
|
|
3993
|
-
import * as
|
|
5103
|
+
import * as d3Selection3 from "d3-selection";
|
|
5104
|
+
import * as d3Shape2 from "d3-shape";
|
|
3994
5105
|
import * as d3Array from "d3-array";
|
|
3995
5106
|
import cloud from "d3-cloud";
|
|
3996
5107
|
var DEFAULT_CLOUD_OPTIONS = {
|
|
@@ -4094,10 +5205,10 @@ function parseD3(content, palette) {
|
|
|
4094
5205
|
let currentArcGroup = null;
|
|
4095
5206
|
let currentTimelineGroup = null;
|
|
4096
5207
|
for (let i = 0; i < lines.length; i++) {
|
|
4097
|
-
const
|
|
5208
|
+
const line3 = lines[i].trim();
|
|
4098
5209
|
const lineNumber = i + 1;
|
|
4099
|
-
if (!
|
|
4100
|
-
const sectionMatch =
|
|
5210
|
+
if (!line3) continue;
|
|
5211
|
+
const sectionMatch = line3.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
|
|
4101
5212
|
if (sectionMatch) {
|
|
4102
5213
|
if (result.type === "arc") {
|
|
4103
5214
|
const name = sectionMatch[1].trim();
|
|
@@ -4112,11 +5223,11 @@ function parseD3(content, palette) {
|
|
|
4112
5223
|
}
|
|
4113
5224
|
continue;
|
|
4114
5225
|
}
|
|
4115
|
-
if (
|
|
5226
|
+
if (line3.startsWith("#") || line3.startsWith("//")) {
|
|
4116
5227
|
continue;
|
|
4117
5228
|
}
|
|
4118
5229
|
if (result.type === "arc") {
|
|
4119
|
-
const linkMatch =
|
|
5230
|
+
const linkMatch = line3.match(
|
|
4120
5231
|
/^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?::\s*(\d+(?:\.\d+)?))?$/
|
|
4121
5232
|
);
|
|
4122
5233
|
if (linkMatch) {
|
|
@@ -4146,7 +5257,7 @@ function parseD3(content, palette) {
|
|
|
4146
5257
|
}
|
|
4147
5258
|
}
|
|
4148
5259
|
if (result.type === "timeline") {
|
|
4149
|
-
const eraMatch =
|
|
5260
|
+
const eraMatch = line3.match(
|
|
4150
5261
|
/^era\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
4151
5262
|
);
|
|
4152
5263
|
if (eraMatch) {
|
|
@@ -4159,7 +5270,7 @@ function parseD3(content, palette) {
|
|
|
4159
5270
|
});
|
|
4160
5271
|
continue;
|
|
4161
5272
|
}
|
|
4162
|
-
const markerMatch =
|
|
5273
|
+
const markerMatch = line3.match(
|
|
4163
5274
|
/^marker\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
4164
5275
|
);
|
|
4165
5276
|
if (markerMatch) {
|
|
@@ -4174,7 +5285,7 @@ function parseD3(content, palette) {
|
|
|
4174
5285
|
}
|
|
4175
5286
|
}
|
|
4176
5287
|
if (result.type === "timeline") {
|
|
4177
|
-
const durationMatch =
|
|
5288
|
+
const durationMatch = line3.match(
|
|
4178
5289
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d+(?:\.\d{1,2})?)([dwmy])\s*:\s*(.+)$/
|
|
4179
5290
|
);
|
|
4180
5291
|
if (durationMatch) {
|
|
@@ -4193,7 +5304,7 @@ function parseD3(content, palette) {
|
|
|
4193
5304
|
});
|
|
4194
5305
|
continue;
|
|
4195
5306
|
}
|
|
4196
|
-
const rangeMatch =
|
|
5307
|
+
const rangeMatch = line3.match(
|
|
4197
5308
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
4198
5309
|
);
|
|
4199
5310
|
if (rangeMatch) {
|
|
@@ -4207,7 +5318,7 @@ function parseD3(content, palette) {
|
|
|
4207
5318
|
});
|
|
4208
5319
|
continue;
|
|
4209
5320
|
}
|
|
4210
|
-
const pointMatch =
|
|
5321
|
+
const pointMatch = line3.match(
|
|
4211
5322
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
4212
5323
|
);
|
|
4213
5324
|
if (pointMatch) {
|
|
@@ -4222,7 +5333,7 @@ function parseD3(content, palette) {
|
|
|
4222
5333
|
}
|
|
4223
5334
|
}
|
|
4224
5335
|
if (result.type === "venn") {
|
|
4225
|
-
const overlapMatch =
|
|
5336
|
+
const overlapMatch = line3.match(
|
|
4226
5337
|
/^(.+?&.+?)\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
|
|
4227
5338
|
);
|
|
4228
5339
|
if (overlapMatch) {
|
|
@@ -4232,7 +5343,7 @@ function parseD3(content, palette) {
|
|
|
4232
5343
|
result.vennOverlaps.push({ sets, size, label, lineNumber });
|
|
4233
5344
|
continue;
|
|
4234
5345
|
}
|
|
4235
|
-
const setMatch =
|
|
5346
|
+
const setMatch = line3.match(
|
|
4236
5347
|
/^(.+?)(?:\(([^)]+)\))?\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
|
|
4237
5348
|
);
|
|
4238
5349
|
if (setMatch) {
|
|
@@ -4245,7 +5356,7 @@ function parseD3(content, palette) {
|
|
|
4245
5356
|
}
|
|
4246
5357
|
}
|
|
4247
5358
|
if (result.type === "quadrant") {
|
|
4248
|
-
const xAxisMatch =
|
|
5359
|
+
const xAxisMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
|
|
4249
5360
|
if (xAxisMatch) {
|
|
4250
5361
|
const parts = xAxisMatch[1].split(",").map((s) => s.trim());
|
|
4251
5362
|
if (parts.length >= 2) {
|
|
@@ -4254,7 +5365,7 @@ function parseD3(content, palette) {
|
|
|
4254
5365
|
}
|
|
4255
5366
|
continue;
|
|
4256
5367
|
}
|
|
4257
|
-
const yAxisMatch =
|
|
5368
|
+
const yAxisMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
|
|
4258
5369
|
if (yAxisMatch) {
|
|
4259
5370
|
const parts = yAxisMatch[1].split(",").map((s) => s.trim());
|
|
4260
5371
|
if (parts.length >= 2) {
|
|
@@ -4264,7 +5375,7 @@ function parseD3(content, palette) {
|
|
|
4264
5375
|
continue;
|
|
4265
5376
|
}
|
|
4266
5377
|
const quadrantLabelRe = /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i;
|
|
4267
|
-
const quadrantMatch =
|
|
5378
|
+
const quadrantMatch = line3.match(quadrantLabelRe);
|
|
4268
5379
|
if (quadrantMatch) {
|
|
4269
5380
|
const position = quadrantMatch[1].toLowerCase();
|
|
4270
5381
|
const labelPart = quadrantMatch[2].trim();
|
|
@@ -4280,7 +5391,7 @@ function parseD3(content, palette) {
|
|
|
4280
5391
|
result.quadrantLabels.bottomRight = label;
|
|
4281
5392
|
continue;
|
|
4282
5393
|
}
|
|
4283
|
-
const pointMatch =
|
|
5394
|
+
const pointMatch = line3.match(
|
|
4284
5395
|
/^(.+?):\s*([0-9]*\.?[0-9]+)\s*,\s*([0-9]*\.?[0-9]+)\s*$/
|
|
4285
5396
|
);
|
|
4286
5397
|
if (pointMatch) {
|
|
@@ -4297,13 +5408,13 @@ function parseD3(content, palette) {
|
|
|
4297
5408
|
continue;
|
|
4298
5409
|
}
|
|
4299
5410
|
}
|
|
4300
|
-
const colonIndex =
|
|
5411
|
+
const colonIndex = line3.indexOf(":");
|
|
4301
5412
|
if (colonIndex !== -1) {
|
|
4302
|
-
const rawKey =
|
|
5413
|
+
const rawKey = line3.substring(0, colonIndex).trim();
|
|
4303
5414
|
const key = rawKey.toLowerCase();
|
|
4304
5415
|
const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
|
|
4305
5416
|
if (key === "chart") {
|
|
4306
|
-
const value =
|
|
5417
|
+
const value = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4307
5418
|
if (value === "slope" || value === "wordcloud" || value === "arc" || value === "timeline" || value === "venn" || value === "quadrant" || value === "sequence") {
|
|
4308
5419
|
result.type = value;
|
|
4309
5420
|
} else {
|
|
@@ -4313,35 +5424,35 @@ function parseD3(content, palette) {
|
|
|
4313
5424
|
continue;
|
|
4314
5425
|
}
|
|
4315
5426
|
if (key === "title") {
|
|
4316
|
-
result.title =
|
|
5427
|
+
result.title = line3.substring(colonIndex + 1).trim();
|
|
4317
5428
|
if (result.type === "quadrant") {
|
|
4318
5429
|
result.quadrantTitleLineNumber = lineNumber;
|
|
4319
5430
|
}
|
|
4320
5431
|
continue;
|
|
4321
5432
|
}
|
|
4322
5433
|
if (key === "orientation") {
|
|
4323
|
-
const v =
|
|
5434
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4324
5435
|
if (v === "horizontal" || v === "vertical") {
|
|
4325
5436
|
result.orientation = v;
|
|
4326
5437
|
}
|
|
4327
5438
|
continue;
|
|
4328
5439
|
}
|
|
4329
5440
|
if (key === "order") {
|
|
4330
|
-
const v =
|
|
5441
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4331
5442
|
if (v === "name" || v === "group" || v === "degree") {
|
|
4332
5443
|
result.arcOrder = v;
|
|
4333
5444
|
}
|
|
4334
5445
|
continue;
|
|
4335
5446
|
}
|
|
4336
5447
|
if (key === "sort") {
|
|
4337
|
-
const v =
|
|
5448
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4338
5449
|
if (v === "time" || v === "group") {
|
|
4339
5450
|
result.timelineSort = v;
|
|
4340
5451
|
}
|
|
4341
5452
|
continue;
|
|
4342
5453
|
}
|
|
4343
5454
|
if (key === "swimlanes") {
|
|
4344
|
-
const v =
|
|
5455
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4345
5456
|
if (v === "on") {
|
|
4346
5457
|
result.timelineSwimlanes = true;
|
|
4347
5458
|
} else if (v === "off") {
|
|
@@ -4350,7 +5461,7 @@ function parseD3(content, palette) {
|
|
|
4350
5461
|
continue;
|
|
4351
5462
|
}
|
|
4352
5463
|
if (key === "values") {
|
|
4353
|
-
const v =
|
|
5464
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4354
5465
|
if (v === "off") {
|
|
4355
5466
|
result.vennShowValues = false;
|
|
4356
5467
|
} else if (v === "on") {
|
|
@@ -4359,21 +5470,21 @@ function parseD3(content, palette) {
|
|
|
4359
5470
|
continue;
|
|
4360
5471
|
}
|
|
4361
5472
|
if (key === "rotate") {
|
|
4362
|
-
const v =
|
|
5473
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4363
5474
|
if (v === "none" || v === "mixed" || v === "angled") {
|
|
4364
5475
|
result.cloudOptions.rotate = v;
|
|
4365
5476
|
}
|
|
4366
5477
|
continue;
|
|
4367
5478
|
}
|
|
4368
5479
|
if (key === "max") {
|
|
4369
|
-
const v = parseInt(
|
|
5480
|
+
const v = parseInt(line3.substring(colonIndex + 1).trim(), 10);
|
|
4370
5481
|
if (!isNaN(v) && v > 0) {
|
|
4371
5482
|
result.cloudOptions.max = v;
|
|
4372
5483
|
}
|
|
4373
5484
|
continue;
|
|
4374
5485
|
}
|
|
4375
5486
|
if (key === "size") {
|
|
4376
|
-
const v =
|
|
5487
|
+
const v = line3.substring(colonIndex + 1).trim();
|
|
4377
5488
|
const parts = v.split(",").map((s) => parseInt(s.trim(), 10));
|
|
4378
5489
|
if (parts.length === 2 && parts.every((n) => !isNaN(n) && n > 0) && parts[0] < parts[1]) {
|
|
4379
5490
|
result.cloudOptions.minSize = parts[0];
|
|
@@ -4383,7 +5494,7 @@ function parseD3(content, palette) {
|
|
|
4383
5494
|
}
|
|
4384
5495
|
const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
|
|
4385
5496
|
const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
|
|
4386
|
-
const valuePart =
|
|
5497
|
+
const valuePart = line3.substring(colonIndex + 1).trim();
|
|
4387
5498
|
const values = valuePart.split(",").map((v) => v.trim());
|
|
4388
5499
|
const numericValues = [];
|
|
4389
5500
|
let allNumeric = true;
|
|
@@ -4414,15 +5525,15 @@ function parseD3(content, palette) {
|
|
|
4414
5525
|
}
|
|
4415
5526
|
}
|
|
4416
5527
|
if (result.type === "wordcloud") {
|
|
4417
|
-
if (colonIndex === -1 && !
|
|
4418
|
-
result.words.push({ text:
|
|
5528
|
+
if (colonIndex === -1 && !line3.includes(" ")) {
|
|
5529
|
+
result.words.push({ text: line3, weight: 10, lineNumber });
|
|
4419
5530
|
} else {
|
|
4420
|
-
freeformLines.push(
|
|
5531
|
+
freeformLines.push(line3);
|
|
4421
5532
|
}
|
|
4422
5533
|
continue;
|
|
4423
5534
|
}
|
|
4424
|
-
if (result.periods.length === 0 &&
|
|
4425
|
-
const periods =
|
|
5535
|
+
if (result.periods.length === 0 && line3.includes(",") && !line3.includes(":")) {
|
|
5536
|
+
const periods = line3.split(",").map((p) => p.trim()).filter(Boolean);
|
|
4426
5537
|
if (periods.length >= 2) {
|
|
4427
5538
|
result.periods = periods;
|
|
4428
5539
|
continue;
|
|
@@ -4645,7 +5756,7 @@ var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
|
|
|
4645
5756
|
var SLOPE_LABEL_FONT_SIZE = 14;
|
|
4646
5757
|
var SLOPE_CHAR_WIDTH = 8;
|
|
4647
5758
|
function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
4648
|
-
|
|
5759
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
4649
5760
|
const { periods, data, title } = parsed;
|
|
4650
5761
|
if (data.length === 0 || periods.length < 2) return;
|
|
4651
5762
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -4672,7 +5783,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4672
5783
|
const valuePadding = (maxVal - minVal) * 0.1 || 1;
|
|
4673
5784
|
const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
|
|
4674
5785
|
const xScale = d3Scale.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
|
|
4675
|
-
const svg =
|
|
5786
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
4676
5787
|
const g = svg.append("g").attr("transform", `translate(${SLOPE_MARGIN.left},${SLOPE_MARGIN.top})`);
|
|
4677
5788
|
const tooltip = createTooltip(container, palette, isDark);
|
|
4678
5789
|
if (title) {
|
|
@@ -4683,7 +5794,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4683
5794
|
g.append("text").attr("x", x).attr("y", -15).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "18px").attr("font-weight", "600").text(period);
|
|
4684
5795
|
g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", "4,4");
|
|
4685
5796
|
}
|
|
4686
|
-
const lineGen =
|
|
5797
|
+
const lineGen = d3Shape2.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
|
|
4687
5798
|
data.forEach((item, idx) => {
|
|
4688
5799
|
const color = item.color ?? colors[idx % colors.length];
|
|
4689
5800
|
const firstVal = item.values[0];
|
|
@@ -4746,11 +5857,11 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4746
5857
|
const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
|
|
4747
5858
|
const totalHeight = (lines.length - 1) * lineHeight;
|
|
4748
5859
|
const startDy = -totalHeight / 2;
|
|
4749
|
-
lines.forEach((
|
|
5860
|
+
lines.forEach((line3, li) => {
|
|
4750
5861
|
labelEl.append("tspan").attr("x", lastX + 10).attr(
|
|
4751
5862
|
"dy",
|
|
4752
5863
|
li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
|
|
4753
|
-
).text(
|
|
5864
|
+
).text(line3);
|
|
4754
5865
|
});
|
|
4755
5866
|
}
|
|
4756
5867
|
});
|
|
@@ -4845,7 +5956,7 @@ function orderArcNodes(links, order, groups) {
|
|
|
4845
5956
|
}
|
|
4846
5957
|
var ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
|
|
4847
5958
|
function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
4848
|
-
|
|
5959
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
4849
5960
|
const { links, title, orientation, arcOrder, arcNodeGroups } = parsed;
|
|
4850
5961
|
if (links.length === 0) return;
|
|
4851
5962
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -4882,7 +5993,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4882
5993
|
const values = links.map((l) => l.value);
|
|
4883
5994
|
const [minVal, maxVal] = d3Array.extent(values);
|
|
4884
5995
|
const strokeScale = d3Scale.scaleLinear().domain([minVal, maxVal]).range([1.5, 6]);
|
|
4885
|
-
const svg =
|
|
5996
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
4886
5997
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
4887
5998
|
if (title) {
|
|
4888
5999
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -4897,14 +6008,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4897
6008
|
function handleMouseEnter(hovered) {
|
|
4898
6009
|
const connected = neighbors.get(hovered);
|
|
4899
6010
|
g.selectAll(".arc-link").each(function() {
|
|
4900
|
-
const el =
|
|
6011
|
+
const el = d3Selection3.select(this);
|
|
4901
6012
|
const src = el.attr("data-source");
|
|
4902
6013
|
const tgt = el.attr("data-target");
|
|
4903
6014
|
const isRelated = src === hovered || tgt === hovered;
|
|
4904
6015
|
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
4905
6016
|
});
|
|
4906
6017
|
g.selectAll(".arc-node").each(function() {
|
|
4907
|
-
const el =
|
|
6018
|
+
const el = d3Selection3.select(this);
|
|
4908
6019
|
const name = el.attr("data-node");
|
|
4909
6020
|
const isRelated = name === hovered || connected.has(name);
|
|
4910
6021
|
el.attr("opacity", isRelated ? 1 : FADE_OPACITY);
|
|
@@ -4929,23 +6040,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4929
6040
|
const members = groupNodeSets.get(groupName);
|
|
4930
6041
|
if (!members) return;
|
|
4931
6042
|
g.selectAll(".arc-link").each(function() {
|
|
4932
|
-
const el =
|
|
6043
|
+
const el = d3Selection3.select(this);
|
|
4933
6044
|
const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
|
|
4934
6045
|
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
4935
6046
|
});
|
|
4936
6047
|
g.selectAll(".arc-node").each(function() {
|
|
4937
|
-
const el =
|
|
6048
|
+
const el = d3Selection3.select(this);
|
|
4938
6049
|
el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY);
|
|
4939
6050
|
});
|
|
4940
6051
|
g.selectAll(".arc-group-band").each(function() {
|
|
4941
|
-
const el =
|
|
6052
|
+
const el = d3Selection3.select(this);
|
|
4942
6053
|
el.attr(
|
|
4943
6054
|
"fill-opacity",
|
|
4944
6055
|
el.attr("data-group") === groupName ? 0.18 : 0.03
|
|
4945
6056
|
);
|
|
4946
6057
|
});
|
|
4947
6058
|
g.selectAll(".arc-group-label").each(function() {
|
|
4948
|
-
const el =
|
|
6059
|
+
const el = d3Selection3.select(this);
|
|
4949
6060
|
el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
|
|
4950
6061
|
});
|
|
4951
6062
|
}
|
|
@@ -5148,7 +6259,14 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
|
|
|
5148
6259
|
const firstYear = Math.ceil(domainMin);
|
|
5149
6260
|
const lastYear = Math.floor(domainMax);
|
|
5150
6261
|
if (lastYear >= firstYear + 1) {
|
|
5151
|
-
|
|
6262
|
+
const yearSpan = lastYear - firstYear;
|
|
6263
|
+
let step = 1;
|
|
6264
|
+
if (yearSpan > 80) step = 20;
|
|
6265
|
+
else if (yearSpan > 40) step = 10;
|
|
6266
|
+
else if (yearSpan > 20) step = 5;
|
|
6267
|
+
else if (yearSpan > 10) step = 2;
|
|
6268
|
+
const alignedFirst = Math.ceil(firstYear / step) * step;
|
|
6269
|
+
for (let y = alignedFirst; y <= lastYear; y += step) {
|
|
5152
6270
|
ticks.push({ pos: scale(y), label: String(y) });
|
|
5153
6271
|
}
|
|
5154
6272
|
} else if (span > 0.25) {
|
|
@@ -5248,7 +6366,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
|
|
|
5248
6366
|
function hideEventDatesOnScale(g) {
|
|
5249
6367
|
g.selectAll(".tl-event-date").remove();
|
|
5250
6368
|
g.selectAll(".tl-scale-tick").each(function() {
|
|
5251
|
-
const el =
|
|
6369
|
+
const el = d3Selection3.select(this);
|
|
5252
6370
|
const isDashed = el.attr("stroke-dasharray");
|
|
5253
6371
|
el.attr("opacity", isDashed ? 0.15 : 0.4);
|
|
5254
6372
|
});
|
|
@@ -5306,7 +6424,7 @@ function buildEraTooltipHtml(era) {
|
|
|
5306
6424
|
return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
|
|
5307
6425
|
}
|
|
5308
6426
|
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
5309
|
-
|
|
6427
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
5310
6428
|
const {
|
|
5311
6429
|
timelineEvents,
|
|
5312
6430
|
timelineGroups,
|
|
@@ -5358,13 +6476,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5358
6476
|
const FADE_OPACITY = 0.1;
|
|
5359
6477
|
function fadeToGroup(g, groupName) {
|
|
5360
6478
|
g.selectAll(".tl-event").each(function() {
|
|
5361
|
-
const el =
|
|
6479
|
+
const el = d3Selection3.select(this);
|
|
5362
6480
|
const evGroup = el.attr("data-group");
|
|
5363
6481
|
el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY);
|
|
5364
6482
|
});
|
|
5365
6483
|
g.selectAll(".tl-legend-item, .tl-lane-header").each(
|
|
5366
6484
|
function() {
|
|
5367
|
-
const el =
|
|
6485
|
+
const el = d3Selection3.select(this);
|
|
5368
6486
|
const name = el.attr("data-group");
|
|
5369
6487
|
el.attr("opacity", name === groupName ? 1 : FADE_OPACITY);
|
|
5370
6488
|
}
|
|
@@ -5376,7 +6494,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5376
6494
|
}
|
|
5377
6495
|
function fadeToEra(g, eraStart, eraEnd) {
|
|
5378
6496
|
g.selectAll(".tl-event").each(function() {
|
|
5379
|
-
const el =
|
|
6497
|
+
const el = d3Selection3.select(this);
|
|
5380
6498
|
const date = parseFloat(el.attr("data-date"));
|
|
5381
6499
|
const endDate = el.attr("data-end-date");
|
|
5382
6500
|
const evEnd = endDate ? parseFloat(endDate) : date;
|
|
@@ -5388,14 +6506,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5388
6506
|
FADE_OPACITY
|
|
5389
6507
|
);
|
|
5390
6508
|
g.selectAll(".tl-era").each(function() {
|
|
5391
|
-
const el =
|
|
6509
|
+
const el = d3Selection3.select(this);
|
|
5392
6510
|
const s = parseFloat(el.attr("data-era-start"));
|
|
5393
6511
|
const e = parseFloat(el.attr("data-era-end"));
|
|
5394
6512
|
const isSelf = s === eraStart && e === eraEnd;
|
|
5395
6513
|
el.attr("opacity", isSelf ? 1 : FADE_OPACITY);
|
|
5396
6514
|
});
|
|
5397
6515
|
g.selectAll(".tl-marker").each(function() {
|
|
5398
|
-
const el =
|
|
6516
|
+
const el = d3Selection3.select(this);
|
|
5399
6517
|
const date = parseFloat(el.attr("data-marker-date"));
|
|
5400
6518
|
const inside = date >= eraStart && date <= eraEnd;
|
|
5401
6519
|
el.attr("opacity", inside ? 1 : FADE_OPACITY);
|
|
@@ -5427,7 +6545,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5427
6545
|
const innerHeight = height - margin.top - margin.bottom;
|
|
5428
6546
|
const laneWidth = innerWidth / laneCount;
|
|
5429
6547
|
const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
|
|
5430
|
-
const svg =
|
|
6548
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5431
6549
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5432
6550
|
if (title) {
|
|
5433
6551
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -5521,7 +6639,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5521
6639
|
const axisX = 20;
|
|
5522
6640
|
const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
|
|
5523
6641
|
const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
|
|
5524
|
-
const svg =
|
|
6642
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5525
6643
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5526
6644
|
if (title) {
|
|
5527
6645
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -5644,7 +6762,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5644
6762
|
const totalGaps = (lanes.length - 1) * GROUP_GAP;
|
|
5645
6763
|
const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
|
|
5646
6764
|
const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
5647
|
-
const svg =
|
|
6765
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5648
6766
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5649
6767
|
if (title) {
|
|
5650
6768
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -5748,7 +6866,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5748
6866
|
if (ev.uncertain) {
|
|
5749
6867
|
const gradientId = `uncertain-${ev.lineNumber}`;
|
|
5750
6868
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
5751
|
-
|
|
6869
|
+
d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
|
|
5752
6870
|
{ offset: "0%", opacity: 1 },
|
|
5753
6871
|
{ offset: "80%", opacity: 1 },
|
|
5754
6872
|
{ offset: "100%", opacity: 0 }
|
|
@@ -5789,7 +6907,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5789
6907
|
const innerHeight = height - margin.top - margin.bottom;
|
|
5790
6908
|
const rowH = Math.min(28, innerHeight / sorted.length);
|
|
5791
6909
|
const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
5792
|
-
const svg =
|
|
6910
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5793
6911
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5794
6912
|
if (title) {
|
|
5795
6913
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -5887,7 +7005,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5887
7005
|
if (ev.uncertain) {
|
|
5888
7006
|
const gradientId = `uncertain-ts-${ev.lineNumber}`;
|
|
5889
7007
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
5890
|
-
|
|
7008
|
+
d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
|
|
5891
7009
|
{ offset: "0%", opacity: 1 },
|
|
5892
7010
|
{ offset: "80%", opacity: 1 },
|
|
5893
7011
|
{ offset: "100%", opacity: 0 }
|
|
@@ -5920,7 +7038,7 @@ function getRotateFn(mode) {
|
|
|
5920
7038
|
return () => 0;
|
|
5921
7039
|
}
|
|
5922
7040
|
function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
5923
|
-
|
|
7041
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
5924
7042
|
const { words, title, cloudOptions } = parsed;
|
|
5925
7043
|
if (words.length === 0) return;
|
|
5926
7044
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -5941,7 +7059,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
|
|
|
5941
7059
|
return minSize + t * (maxSize - minSize);
|
|
5942
7060
|
};
|
|
5943
7061
|
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
5944
|
-
const svg =
|
|
7062
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5945
7063
|
if (title) {
|
|
5946
7064
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
5947
7065
|
}
|
|
@@ -5964,7 +7082,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
|
|
|
5964
7082
|
}
|
|
5965
7083
|
function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
|
|
5966
7084
|
return new Promise((resolve) => {
|
|
5967
|
-
|
|
7085
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
5968
7086
|
const { words, title, cloudOptions } = parsed;
|
|
5969
7087
|
if (words.length === 0) {
|
|
5970
7088
|
resolve();
|
|
@@ -5991,7 +7109,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
|
|
|
5991
7109
|
return minSize + t * (maxSize - minSize);
|
|
5992
7110
|
};
|
|
5993
7111
|
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
5994
|
-
const svg =
|
|
7112
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5995
7113
|
if (title) {
|
|
5996
7114
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
5997
7115
|
}
|
|
@@ -6135,7 +7253,7 @@ function circlePathD(cx, cy, r) {
|
|
|
6135
7253
|
return `M${cx - r},${cy} A${r},${r} 0 1,0 ${cx + r},${cy} A${r},${r} 0 1,0 ${cx - r},${cy} Z`;
|
|
6136
7254
|
}
|
|
6137
7255
|
function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
6138
|
-
|
|
7256
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6139
7257
|
const { vennSets, vennOverlaps, vennShowValues, title } = parsed;
|
|
6140
7258
|
if (vennSets.length < 2) return;
|
|
6141
7259
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -6200,7 +7318,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
|
|
|
6200
7318
|
const setColors = vennSets.map(
|
|
6201
7319
|
(s, i) => s.color ?? colors[i % colors.length]
|
|
6202
7320
|
);
|
|
6203
|
-
const svg =
|
|
7321
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6204
7322
|
const tooltip = createTooltip(container, palette, isDark);
|
|
6205
7323
|
if (title) {
|
|
6206
7324
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -6346,7 +7464,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
|
|
|
6346
7464
|
});
|
|
6347
7465
|
}
|
|
6348
7466
|
function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
6349
|
-
|
|
7467
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6350
7468
|
const {
|
|
6351
7469
|
title,
|
|
6352
7470
|
quadrantLabels,
|
|
@@ -6378,7 +7496,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6378
7496
|
const chartHeight = height - margin.top - margin.bottom;
|
|
6379
7497
|
const xScale = d3Scale.scaleLinear().domain([0, 1]).range([0, chartWidth]);
|
|
6380
7498
|
const yScale = d3Scale.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
|
|
6381
|
-
const svg =
|
|
7499
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6382
7500
|
const tooltip = createTooltip(container, palette, isDark);
|
|
6383
7501
|
if (title) {
|
|
6384
7502
|
const titleText = svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style(
|
|
@@ -6387,9 +7505,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6387
7505
|
).text(title);
|
|
6388
7506
|
if (onClickItem && quadrantTitleLineNumber) {
|
|
6389
7507
|
titleText.on("click", () => onClickItem(quadrantTitleLineNumber)).on("mouseenter", function() {
|
|
6390
|
-
|
|
7508
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6391
7509
|
}).on("mouseleave", function() {
|
|
6392
|
-
|
|
7510
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6393
7511
|
});
|
|
6394
7512
|
}
|
|
6395
7513
|
}
|
|
@@ -6472,9 +7590,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6472
7590
|
quadrantLabelTexts.on("click", (_, d) => {
|
|
6473
7591
|
if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
|
|
6474
7592
|
}).on("mouseenter", function() {
|
|
6475
|
-
|
|
7593
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6476
7594
|
}).on("mouseleave", function() {
|
|
6477
|
-
|
|
7595
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6478
7596
|
});
|
|
6479
7597
|
}
|
|
6480
7598
|
if (quadrantXAxis) {
|
|
@@ -6489,9 +7607,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6489
7607
|
if (onClickItem && quadrantXAxisLineNumber) {
|
|
6490
7608
|
[xLowLabel, xHighLabel].forEach((label) => {
|
|
6491
7609
|
label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
|
|
6492
|
-
|
|
7610
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6493
7611
|
}).on("mouseleave", function() {
|
|
6494
|
-
|
|
7612
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6495
7613
|
});
|
|
6496
7614
|
});
|
|
6497
7615
|
}
|
|
@@ -6510,9 +7628,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6510
7628
|
if (onClickItem && quadrantYAxisLineNumber) {
|
|
6511
7629
|
[yLowLabel, yHighLabel].forEach((label) => {
|
|
6512
7630
|
label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
|
|
6513
|
-
|
|
7631
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6514
7632
|
}).on("mouseleave", function() {
|
|
6515
|
-
|
|
7633
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6516
7634
|
});
|
|
6517
7635
|
});
|
|
6518
7636
|
}
|
|
@@ -6560,7 +7678,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6560
7678
|
pointsG.selectAll("g.point-group").each(function(_2, i) {
|
|
6561
7679
|
const pt = quadrantPoints[i];
|
|
6562
7680
|
const ptQuad = getPointQuadrant(pt.x, pt.y);
|
|
6563
|
-
|
|
7681
|
+
d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
|
|
6564
7682
|
});
|
|
6565
7683
|
}).on("mouseleave", () => {
|
|
6566
7684
|
quadrantRects.attr("opacity", 1);
|
|
@@ -6575,6 +7693,43 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6575
7693
|
var EXPORT_WIDTH = 1200;
|
|
6576
7694
|
var EXPORT_HEIGHT = 800;
|
|
6577
7695
|
async function renderD3ForExport(content, theme, palette) {
|
|
7696
|
+
const { parseDgmoChartType: parseDgmoChartType2 } = await Promise.resolve().then(() => (init_dgmo_router(), dgmo_router_exports));
|
|
7697
|
+
const detectedType = parseDgmoChartType2(content);
|
|
7698
|
+
if (detectedType === "flowchart") {
|
|
7699
|
+
const { parseFlowchart: parseFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_parser(), flowchart_parser_exports));
|
|
7700
|
+
const { layoutGraph: layoutGraph2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
|
|
7701
|
+
const { renderFlowchart: renderFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_renderer(), flowchart_renderer_exports));
|
|
7702
|
+
const isDark2 = theme === "dark";
|
|
7703
|
+
const { getPalette: getPalette3 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
|
|
7704
|
+
const effectivePalette2 = palette ?? (isDark2 ? getPalette3("nord").dark : getPalette3("nord").light);
|
|
7705
|
+
const fcParsed = parseFlowchart2(content, effectivePalette2);
|
|
7706
|
+
if (fcParsed.error || fcParsed.nodes.length === 0) return "";
|
|
7707
|
+
const layout = layoutGraph2(fcParsed);
|
|
7708
|
+
const container2 = document.createElement("div");
|
|
7709
|
+
container2.style.width = `${EXPORT_WIDTH}px`;
|
|
7710
|
+
container2.style.height = `${EXPORT_HEIGHT}px`;
|
|
7711
|
+
container2.style.position = "absolute";
|
|
7712
|
+
container2.style.left = "-9999px";
|
|
7713
|
+
document.body.appendChild(container2);
|
|
7714
|
+
try {
|
|
7715
|
+
renderFlowchart2(container2, fcParsed, layout, effectivePalette2, isDark2, void 0, {
|
|
7716
|
+
width: EXPORT_WIDTH,
|
|
7717
|
+
height: EXPORT_HEIGHT
|
|
7718
|
+
});
|
|
7719
|
+
const svgEl = container2.querySelector("svg");
|
|
7720
|
+
if (!svgEl) return "";
|
|
7721
|
+
if (theme === "transparent") {
|
|
7722
|
+
svgEl.style.background = "none";
|
|
7723
|
+
} else if (!svgEl.style.background) {
|
|
7724
|
+
svgEl.style.background = effectivePalette2.bg;
|
|
7725
|
+
}
|
|
7726
|
+
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
7727
|
+
svgEl.style.fontFamily = FONT_FAMILY;
|
|
7728
|
+
return svgEl.outerHTML;
|
|
7729
|
+
} finally {
|
|
7730
|
+
document.body.removeChild(container2);
|
|
7731
|
+
}
|
|
7732
|
+
}
|
|
6578
7733
|
const parsed = parseD3(content, palette);
|
|
6579
7734
|
if (parsed.error && parsed.type !== "sequence") {
|
|
6580
7735
|
const looksLikeSequence2 = /->|~>|<-/.test(content);
|
|
@@ -6669,17 +7824,17 @@ function parseQuadrant(content) {
|
|
|
6669
7824
|
};
|
|
6670
7825
|
const lines = content.split("\n");
|
|
6671
7826
|
for (let i = 0; i < lines.length; i++) {
|
|
6672
|
-
const
|
|
7827
|
+
const line3 = lines[i].trim();
|
|
6673
7828
|
const lineNumber = i + 1;
|
|
6674
|
-
if (!
|
|
6675
|
-
if (/^chart\s*:/i.test(
|
|
6676
|
-
const titleMatch =
|
|
7829
|
+
if (!line3 || line3.startsWith("#") || line3.startsWith("//")) continue;
|
|
7830
|
+
if (/^chart\s*:/i.test(line3)) continue;
|
|
7831
|
+
const titleMatch = line3.match(/^title\s*:\s*(.+)/i);
|
|
6677
7832
|
if (titleMatch) {
|
|
6678
7833
|
result.title = titleMatch[1].trim();
|
|
6679
7834
|
result.titleLineNumber = lineNumber;
|
|
6680
7835
|
continue;
|
|
6681
7836
|
}
|
|
6682
|
-
const xMatch =
|
|
7837
|
+
const xMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
|
|
6683
7838
|
if (xMatch) {
|
|
6684
7839
|
const parts = xMatch[1].split(",").map((s) => s.trim());
|
|
6685
7840
|
if (parts.length >= 2) {
|
|
@@ -6688,7 +7843,7 @@ function parseQuadrant(content) {
|
|
|
6688
7843
|
}
|
|
6689
7844
|
continue;
|
|
6690
7845
|
}
|
|
6691
|
-
const yMatch =
|
|
7846
|
+
const yMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
|
|
6692
7847
|
if (yMatch) {
|
|
6693
7848
|
const parts = yMatch[1].split(",").map((s) => s.trim());
|
|
6694
7849
|
if (parts.length >= 2) {
|
|
@@ -6697,7 +7852,7 @@ function parseQuadrant(content) {
|
|
|
6697
7852
|
}
|
|
6698
7853
|
continue;
|
|
6699
7854
|
}
|
|
6700
|
-
const posMatch =
|
|
7855
|
+
const posMatch = line3.match(
|
|
6701
7856
|
/^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i
|
|
6702
7857
|
);
|
|
6703
7858
|
if (posMatch) {
|
|
@@ -6718,7 +7873,7 @@ function parseQuadrant(content) {
|
|
|
6718
7873
|
}
|
|
6719
7874
|
continue;
|
|
6720
7875
|
}
|
|
6721
|
-
const pointMatch =
|
|
7876
|
+
const pointMatch = line3.match(DATA_POINT_RE);
|
|
6722
7877
|
if (pointMatch) {
|
|
6723
7878
|
const key = pointMatch[1].trim().toLowerCase();
|
|
6724
7879
|
if (!QUADRANT_POSITIONS.has(key)) {
|
|
@@ -6792,6 +7947,9 @@ function buildMermaidQuadrant(parsed, options = {}) {
|
|
|
6792
7947
|
}
|
|
6793
7948
|
|
|
6794
7949
|
// src/index.ts
|
|
7950
|
+
init_flowchart_parser();
|
|
7951
|
+
init_layout();
|
|
7952
|
+
init_flowchart_renderer();
|
|
6795
7953
|
init_renderer();
|
|
6796
7954
|
init_colors();
|
|
6797
7955
|
init_palettes();
|
|
@@ -6825,7 +7983,10 @@ export {
|
|
|
6825
7983
|
hslToHex,
|
|
6826
7984
|
inferParticipantType,
|
|
6827
7985
|
isSequenceBlock,
|
|
7986
|
+
isSequenceNote,
|
|
6828
7987
|
isValidHex,
|
|
7988
|
+
layoutGraph,
|
|
7989
|
+
looksLikeFlowchart,
|
|
6829
7990
|
looksLikeSequence,
|
|
6830
7991
|
mute,
|
|
6831
7992
|
nord,
|
|
@@ -6836,6 +7997,7 @@ export {
|
|
|
6836
7997
|
parseD3,
|
|
6837
7998
|
parseDgmoChartType,
|
|
6838
7999
|
parseEChart,
|
|
8000
|
+
parseFlowchart,
|
|
6839
8001
|
parseQuadrant,
|
|
6840
8002
|
parseSequenceDgmo,
|
|
6841
8003
|
parseTimelineDate,
|
|
@@ -6843,6 +8005,8 @@ export {
|
|
|
6843
8005
|
renderArcDiagram,
|
|
6844
8006
|
renderD3ForExport,
|
|
6845
8007
|
renderEChartsForExport,
|
|
8008
|
+
renderFlowchart,
|
|
8009
|
+
renderFlowchartForExport,
|
|
6846
8010
|
renderQuadrant,
|
|
6847
8011
|
renderSequenceDiagram,
|
|
6848
8012
|
renderSlopeChart,
|