@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.cjs
CHANGED
|
@@ -169,6 +169,7 @@ var init_participant_inference = __esm({
|
|
|
169
169
|
var parser_exports = {};
|
|
170
170
|
__export(parser_exports, {
|
|
171
171
|
isSequenceBlock: () => isSequenceBlock,
|
|
172
|
+
isSequenceNote: () => isSequenceNote,
|
|
172
173
|
isSequenceSection: () => isSequenceSection,
|
|
173
174
|
looksLikeSequence: () => looksLikeSequence,
|
|
174
175
|
parseSequenceDgmo: () => parseSequenceDgmo
|
|
@@ -179,6 +180,9 @@ function isSequenceBlock(el) {
|
|
|
179
180
|
function isSequenceSection(el) {
|
|
180
181
|
return "kind" in el && el.kind === "section";
|
|
181
182
|
}
|
|
183
|
+
function isSequenceNote(el) {
|
|
184
|
+
return "kind" in el && el.kind === "note";
|
|
185
|
+
}
|
|
182
186
|
function parseReturnLabel(rawLabel) {
|
|
183
187
|
if (!rawLabel) return { label: "" };
|
|
184
188
|
const arrowReturn = rawLabel.match(ARROW_RETURN_PATTERN);
|
|
@@ -189,19 +193,22 @@ function parseReturnLabel(rawLabel) {
|
|
|
189
193
|
if (umlReturn) {
|
|
190
194
|
return { label: umlReturn[1].trim(), returnLabel: umlReturn[2].trim() };
|
|
191
195
|
}
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
const lastColon = rawLabel.lastIndexOf(":");
|
|
197
|
+
if (lastColon > 0 && lastColon < rawLabel.length - 1) {
|
|
198
|
+
const afterColon = rawLabel.substring(lastColon + 1);
|
|
199
|
+
if (!afterColon.startsWith("//")) {
|
|
200
|
+
const reqPart = rawLabel.substring(0, lastColon).trim();
|
|
201
|
+
const resPart = afterColon.trim();
|
|
202
|
+
if (reqPart && resPart) {
|
|
203
|
+
return { label: reqPart, returnLabel: resPart };
|
|
204
|
+
}
|
|
198
205
|
}
|
|
199
206
|
}
|
|
200
207
|
return { label: rawLabel };
|
|
201
208
|
}
|
|
202
|
-
function measureIndent(
|
|
209
|
+
function measureIndent(line3) {
|
|
203
210
|
let indent = 0;
|
|
204
|
-
for (const ch of
|
|
211
|
+
for (const ch of line3) {
|
|
205
212
|
if (ch === " ") indent++;
|
|
206
213
|
else if (ch === " ") indent += 4;
|
|
207
214
|
else break;
|
|
@@ -225,13 +232,17 @@ function parseSequenceDgmo(content) {
|
|
|
225
232
|
}
|
|
226
233
|
const lines = content.split("\n");
|
|
227
234
|
let hasExplicitChart = false;
|
|
235
|
+
let contentStarted = false;
|
|
228
236
|
let activeGroup = null;
|
|
237
|
+
const participantGroupMap = /* @__PURE__ */ new Map();
|
|
229
238
|
const blockStack = [];
|
|
230
239
|
const currentContainer = () => {
|
|
231
240
|
if (blockStack.length === 0) return result.elements;
|
|
232
241
|
const top = blockStack[blockStack.length - 1];
|
|
242
|
+
if (top.activeElseIfBranch) return top.activeElseIfBranch.children;
|
|
233
243
|
return top.inElse ? top.block.elseChildren : top.block.children;
|
|
234
244
|
};
|
|
245
|
+
let lastMsgFrom = null;
|
|
235
246
|
for (let i = 0; i < lines.length; i++) {
|
|
236
247
|
const raw = lines[i];
|
|
237
248
|
const trimmed = raw.trim();
|
|
@@ -242,9 +253,15 @@ function parseSequenceDgmo(content) {
|
|
|
242
253
|
}
|
|
243
254
|
const groupMatch = trimmed.match(GROUP_HEADING_PATTERN);
|
|
244
255
|
if (groupMatch) {
|
|
256
|
+
const groupColor = groupMatch[2]?.trim();
|
|
257
|
+
if (groupColor && groupColor.startsWith("#")) {
|
|
258
|
+
result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
contentStarted = true;
|
|
245
262
|
activeGroup = {
|
|
246
|
-
name: groupMatch[1],
|
|
247
|
-
color:
|
|
263
|
+
name: groupMatch[1].trim(),
|
|
264
|
+
color: groupColor || void 0,
|
|
248
265
|
participantIds: [],
|
|
249
266
|
lineNumber
|
|
250
267
|
};
|
|
@@ -254,7 +271,11 @@ function parseSequenceDgmo(content) {
|
|
|
254
271
|
if (activeGroup && measureIndent(raw) === 0) {
|
|
255
272
|
activeGroup = null;
|
|
256
273
|
}
|
|
257
|
-
if (trimmed.startsWith("
|
|
274
|
+
if (trimmed.startsWith("//")) continue;
|
|
275
|
+
if (trimmed.startsWith("#") && !trimmed.startsWith("##")) {
|
|
276
|
+
result.error = `Line ${lineNumber}: Use // for comments. # is reserved for group headings (##)`;
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
258
279
|
const sectionMatch = trimmed.match(SECTION_PATTERN);
|
|
259
280
|
if (sectionMatch) {
|
|
260
281
|
const sectionIndent = measureIndent(raw);
|
|
@@ -264,11 +285,16 @@ function parseSequenceDgmo(content) {
|
|
|
264
285
|
blockStack.pop();
|
|
265
286
|
}
|
|
266
287
|
const labelRaw = sectionMatch[1].trim();
|
|
267
|
-
const colorMatch = labelRaw.match(/^(.+?)\((
|
|
288
|
+
const colorMatch = labelRaw.match(/^(.+?)\(([^)]+)\)$/);
|
|
289
|
+
if (colorMatch && colorMatch[2].trim().startsWith("#")) {
|
|
290
|
+
result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
contentStarted = true;
|
|
268
294
|
const section = {
|
|
269
295
|
kind: "section",
|
|
270
296
|
label: colorMatch ? colorMatch[1].trim() : labelRaw,
|
|
271
|
-
color: colorMatch ? colorMatch[2] : void 0,
|
|
297
|
+
color: colorMatch ? colorMatch[2].trim() : void 0,
|
|
272
298
|
lineNumber
|
|
273
299
|
};
|
|
274
300
|
result.sections.push(section);
|
|
@@ -278,24 +304,32 @@ function parseSequenceDgmo(content) {
|
|
|
278
304
|
const colonIndex = trimmed.indexOf(":");
|
|
279
305
|
if (colonIndex > 0 && !trimmed.includes("->") && !trimmed.includes("~>")) {
|
|
280
306
|
const key = trimmed.substring(0, colonIndex).trim().toLowerCase();
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (
|
|
285
|
-
|
|
307
|
+
if (key === "note" || key.startsWith("note ")) {
|
|
308
|
+
} else {
|
|
309
|
+
const value = trimmed.substring(colonIndex + 1).trim();
|
|
310
|
+
if (key === "chart") {
|
|
311
|
+
hasExplicitChart = true;
|
|
312
|
+
if (value.toLowerCase() !== "sequence") {
|
|
313
|
+
result.error = `Expected chart type "sequence", got "${value}"`;
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (contentStarted) {
|
|
319
|
+
result.error = `Line ${lineNumber}: Options like '${key}: ${value}' must appear before the first message or declaration`;
|
|
286
320
|
return result;
|
|
287
321
|
}
|
|
322
|
+
if (key === "title") {
|
|
323
|
+
result.title = value;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
result.options[key] = value;
|
|
288
327
|
continue;
|
|
289
328
|
}
|
|
290
|
-
if (key === "title") {
|
|
291
|
-
result.title = value;
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
result.options[key] = value;
|
|
295
|
-
continue;
|
|
296
329
|
}
|
|
297
330
|
const isAMatch = trimmed.match(IS_A_PATTERN);
|
|
298
331
|
if (isAMatch) {
|
|
332
|
+
contentStarted = true;
|
|
299
333
|
const id = isAMatch[1];
|
|
300
334
|
const typeStr = isAMatch[2].toLowerCase();
|
|
301
335
|
const remainder = isAMatch[3]?.trim() || "";
|
|
@@ -318,12 +352,19 @@ function parseSequenceDgmo(content) {
|
|
|
318
352
|
});
|
|
319
353
|
}
|
|
320
354
|
if (activeGroup && !activeGroup.participantIds.includes(id)) {
|
|
355
|
+
const existingGroup = participantGroupMap.get(id);
|
|
356
|
+
if (existingGroup) {
|
|
357
|
+
result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
321
360
|
activeGroup.participantIds.push(id);
|
|
361
|
+
participantGroupMap.set(id, activeGroup.name);
|
|
322
362
|
}
|
|
323
363
|
continue;
|
|
324
364
|
}
|
|
325
365
|
const posOnlyMatch = trimmed.match(POSITION_ONLY_PATTERN);
|
|
326
366
|
if (posOnlyMatch) {
|
|
367
|
+
contentStarted = true;
|
|
327
368
|
const id = posOnlyMatch[1];
|
|
328
369
|
const position = parseInt(posOnlyMatch[2], 10);
|
|
329
370
|
if (!result.participants.some((p) => p.id === id)) {
|
|
@@ -336,11 +377,18 @@ function parseSequenceDgmo(content) {
|
|
|
336
377
|
});
|
|
337
378
|
}
|
|
338
379
|
if (activeGroup && !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
|
+
}
|
|
339
385
|
activeGroup.participantIds.push(id);
|
|
386
|
+
participantGroupMap.set(id, activeGroup.name);
|
|
340
387
|
}
|
|
341
388
|
continue;
|
|
342
389
|
}
|
|
343
390
|
if (activeGroup && measureIndent(raw) > 0 && /^\S+$/.test(trimmed)) {
|
|
391
|
+
contentStarted = true;
|
|
344
392
|
const id = trimmed;
|
|
345
393
|
if (!result.participants.some((p) => p.id === id)) {
|
|
346
394
|
result.participants.push({
|
|
@@ -351,7 +399,13 @@ function parseSequenceDgmo(content) {
|
|
|
351
399
|
});
|
|
352
400
|
}
|
|
353
401
|
if (!activeGroup.participantIds.includes(id)) {
|
|
402
|
+
const existingGroup = participantGroupMap.get(id);
|
|
403
|
+
if (existingGroup) {
|
|
404
|
+
result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
354
407
|
activeGroup.participantIds.push(id);
|
|
408
|
+
participantGroupMap.set(id, activeGroup.name);
|
|
355
409
|
}
|
|
356
410
|
continue;
|
|
357
411
|
}
|
|
@@ -359,28 +413,31 @@ function parseSequenceDgmo(content) {
|
|
|
359
413
|
while (blockStack.length > 0) {
|
|
360
414
|
const top = blockStack[blockStack.length - 1];
|
|
361
415
|
if (indent > top.indent) break;
|
|
362
|
-
if (indent === top.indent &&
|
|
363
|
-
|
|
416
|
+
if (indent === top.indent && (top.block.type === "if" || top.block.type === "parallel")) {
|
|
417
|
+
const lower = trimmed.toLowerCase();
|
|
418
|
+
if (lower === "else" || lower.startsWith("else if ")) break;
|
|
419
|
+
}
|
|
364
420
|
blockStack.pop();
|
|
365
421
|
}
|
|
366
|
-
let isAsync = false;
|
|
367
|
-
let arrowLine = trimmed;
|
|
368
422
|
const asyncPrefixMatch = trimmed.match(/^async\s+(.+)$/i);
|
|
369
|
-
if (asyncPrefixMatch) {
|
|
370
|
-
|
|
371
|
-
|
|
423
|
+
if (asyncPrefixMatch && ARROW_PATTERN.test(asyncPrefixMatch[1])) {
|
|
424
|
+
result.error = `Line ${lineNumber}: Use ~> for async messages: A ~> B: message`;
|
|
425
|
+
return result;
|
|
372
426
|
}
|
|
373
|
-
|
|
427
|
+
let isAsync = false;
|
|
428
|
+
const asyncArrowMatch = trimmed.match(
|
|
374
429
|
/^(\S+)\s*~>\s*([^\s:]+)\s*(?::\s*(.+))?$/
|
|
375
430
|
);
|
|
376
|
-
const syncArrowMatch =
|
|
431
|
+
const syncArrowMatch = trimmed.match(
|
|
377
432
|
/^(\S+)\s*->\s*([^\s:]+)\s*(?::\s*(.+))?$/
|
|
378
433
|
);
|
|
379
434
|
const arrowMatch = asyncArrowMatch || syncArrowMatch;
|
|
380
435
|
if (asyncArrowMatch) isAsync = true;
|
|
381
436
|
if (arrowMatch) {
|
|
437
|
+
contentStarted = true;
|
|
382
438
|
const from = arrowMatch[1];
|
|
383
439
|
const to = arrowMatch[2];
|
|
440
|
+
lastMsgFrom = from;
|
|
384
441
|
const rawLabel = arrowMatch[3]?.trim() || "";
|
|
385
442
|
const { label, returnLabel } = isAsync ? { label: rawLabel, returnLabel: void 0 } : parseReturnLabel(rawLabel);
|
|
386
443
|
const msg = {
|
|
@@ -413,6 +470,7 @@ function parseSequenceDgmo(content) {
|
|
|
413
470
|
}
|
|
414
471
|
const ifMatch = trimmed.match(/^if\s+(.+)$/i);
|
|
415
472
|
if (ifMatch) {
|
|
473
|
+
contentStarted = true;
|
|
416
474
|
const block = {
|
|
417
475
|
kind: "block",
|
|
418
476
|
type: "if",
|
|
@@ -427,6 +485,7 @@ function parseSequenceDgmo(content) {
|
|
|
427
485
|
}
|
|
428
486
|
const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
|
|
429
487
|
if (loopMatch) {
|
|
488
|
+
contentStarted = true;
|
|
430
489
|
const block = {
|
|
431
490
|
kind: "block",
|
|
432
491
|
type: "loop",
|
|
@@ -441,6 +500,7 @@ function parseSequenceDgmo(content) {
|
|
|
441
500
|
}
|
|
442
501
|
const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
|
|
443
502
|
if (parallelMatch) {
|
|
503
|
+
contentStarted = true;
|
|
444
504
|
const block = {
|
|
445
505
|
kind: "block",
|
|
446
506
|
type: "parallel",
|
|
@@ -453,15 +513,97 @@ function parseSequenceDgmo(content) {
|
|
|
453
513
|
blockStack.push({ block, indent, inElse: false });
|
|
454
514
|
continue;
|
|
455
515
|
}
|
|
516
|
+
const elseIfMatch = trimmed.match(/^else\s+if\s+(.+)$/i);
|
|
517
|
+
if (elseIfMatch) {
|
|
518
|
+
if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
|
|
519
|
+
const top = blockStack[blockStack.length - 1];
|
|
520
|
+
if (top.block.type === "parallel") {
|
|
521
|
+
result.error = `Line ${lineNumber}: parallel blocks don't support else if \u2014 list all concurrent messages directly inside the block`;
|
|
522
|
+
return result;
|
|
523
|
+
}
|
|
524
|
+
if (top.block.type === "if") {
|
|
525
|
+
const branch = { label: elseIfMatch[1].trim(), children: [] };
|
|
526
|
+
if (!top.block.elseIfBranches) top.block.elseIfBranches = [];
|
|
527
|
+
top.block.elseIfBranches.push(branch);
|
|
528
|
+
top.activeElseIfBranch = branch;
|
|
529
|
+
top.inElse = false;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
456
534
|
if (trimmed.toLowerCase() === "else") {
|
|
457
|
-
if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent
|
|
458
|
-
blockStack[blockStack.length - 1]
|
|
535
|
+
if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
|
|
536
|
+
const top = blockStack[blockStack.length - 1];
|
|
537
|
+
if (top.block.type === "parallel") {
|
|
538
|
+
result.error = `Line ${lineNumber}: parallel blocks don't support else \u2014 list all concurrent messages directly inside the block`;
|
|
539
|
+
return result;
|
|
540
|
+
}
|
|
541
|
+
if (top.block.type === "if") {
|
|
542
|
+
top.inElse = true;
|
|
543
|
+
top.activeElseIfBranch = void 0;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
const noteSingleMatch = trimmed.match(NOTE_SINGLE);
|
|
549
|
+
if (noteSingleMatch) {
|
|
550
|
+
const notePosition = noteSingleMatch[1]?.toLowerCase() || "right";
|
|
551
|
+
let noteParticipant = noteSingleMatch[2] || null;
|
|
552
|
+
if (!noteParticipant) {
|
|
553
|
+
if (!lastMsgFrom) continue;
|
|
554
|
+
noteParticipant = lastMsgFrom;
|
|
459
555
|
}
|
|
556
|
+
if (!result.participants.some((p) => p.id === noteParticipant)) {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
const note = {
|
|
560
|
+
kind: "note",
|
|
561
|
+
text: noteSingleMatch[3].trim(),
|
|
562
|
+
position: notePosition,
|
|
563
|
+
participantId: noteParticipant,
|
|
564
|
+
lineNumber,
|
|
565
|
+
endLineNumber: lineNumber
|
|
566
|
+
};
|
|
567
|
+
currentContainer().push(note);
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
const noteMultiMatch = trimmed.match(NOTE_MULTI);
|
|
571
|
+
if (noteMultiMatch) {
|
|
572
|
+
const notePosition = noteMultiMatch[1]?.toLowerCase() || "right";
|
|
573
|
+
let noteParticipant = noteMultiMatch[2] || null;
|
|
574
|
+
if (!noteParticipant) {
|
|
575
|
+
if (!lastMsgFrom) continue;
|
|
576
|
+
noteParticipant = lastMsgFrom;
|
|
577
|
+
}
|
|
578
|
+
if (!result.participants.some((p) => p.id === noteParticipant)) {
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
const noteLines = [];
|
|
582
|
+
while (i + 1 < lines.length) {
|
|
583
|
+
const nextRaw = lines[i + 1];
|
|
584
|
+
const nextTrimmed = nextRaw.trim();
|
|
585
|
+
if (!nextTrimmed) break;
|
|
586
|
+
const nextIndent = measureIndent(nextRaw);
|
|
587
|
+
if (nextIndent <= indent) break;
|
|
588
|
+
noteLines.push(nextTrimmed);
|
|
589
|
+
i++;
|
|
590
|
+
}
|
|
591
|
+
if (noteLines.length === 0) continue;
|
|
592
|
+
const note = {
|
|
593
|
+
kind: "note",
|
|
594
|
+
text: noteLines.join("\n"),
|
|
595
|
+
position: notePosition,
|
|
596
|
+
participantId: noteParticipant,
|
|
597
|
+
lineNumber,
|
|
598
|
+
endLineNumber: i + 1
|
|
599
|
+
// i has advanced past the body lines (1-based)
|
|
600
|
+
};
|
|
601
|
+
currentContainer().push(note);
|
|
460
602
|
continue;
|
|
461
603
|
}
|
|
462
604
|
}
|
|
463
605
|
if (!hasExplicitChart && result.messages.length === 0) {
|
|
464
|
-
const hasArrows = lines.some((
|
|
606
|
+
const hasArrows = lines.some((line3) => ARROW_PATTERN.test(line3.trim()));
|
|
465
607
|
if (!hasArrows) {
|
|
466
608
|
result.error = 'No "chart: sequence" header and no sequence content detected';
|
|
467
609
|
return result;
|
|
@@ -472,13 +614,13 @@ function parseSequenceDgmo(content) {
|
|
|
472
614
|
function looksLikeSequence(content) {
|
|
473
615
|
if (!content) return false;
|
|
474
616
|
const lines = content.split("\n");
|
|
475
|
-
return lines.some((
|
|
476
|
-
const trimmed =
|
|
477
|
-
if (trimmed.startsWith("
|
|
617
|
+
return lines.some((line3) => {
|
|
618
|
+
const trimmed = line3.trim();
|
|
619
|
+
if (trimmed.startsWith("//")) return false;
|
|
478
620
|
return ARROW_PATTERN.test(trimmed);
|
|
479
621
|
});
|
|
480
622
|
}
|
|
481
|
-
var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN;
|
|
623
|
+
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;
|
|
482
624
|
var init_parser = __esm({
|
|
483
625
|
"src/sequence/parser.ts"() {
|
|
484
626
|
"use strict";
|
|
@@ -496,11 +638,13 @@ var init_parser = __esm({
|
|
|
496
638
|
]);
|
|
497
639
|
IS_A_PATTERN = /^(\S+)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
|
|
498
640
|
POSITION_ONLY_PATTERN = /^(\S+)\s+position\s+(-?\d+)$/i;
|
|
499
|
-
GROUP_HEADING_PATTERN = /^##\s+(
|
|
500
|
-
SECTION_PATTERN = /^==\s+(.+?)
|
|
641
|
+
GROUP_HEADING_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
|
|
642
|
+
SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
|
|
501
643
|
ARROW_PATTERN = /\S+\s*(?:->|~>)\s*\S+/;
|
|
502
644
|
ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
|
|
503
645
|
UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
|
|
646
|
+
NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
|
|
647
|
+
NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+([^\s:]+))?\s*:?\s*$/i;
|
|
504
648
|
}
|
|
505
649
|
});
|
|
506
650
|
|
|
@@ -579,6 +723,369 @@ var init_colors = __esm({
|
|
|
579
723
|
}
|
|
580
724
|
});
|
|
581
725
|
|
|
726
|
+
// src/graph/flowchart-parser.ts
|
|
727
|
+
var flowchart_parser_exports = {};
|
|
728
|
+
__export(flowchart_parser_exports, {
|
|
729
|
+
looksLikeFlowchart: () => looksLikeFlowchart,
|
|
730
|
+
parseFlowchart: () => parseFlowchart
|
|
731
|
+
});
|
|
732
|
+
function measureIndent2(line3) {
|
|
733
|
+
let indent = 0;
|
|
734
|
+
for (const ch of line3) {
|
|
735
|
+
if (ch === " ") indent++;
|
|
736
|
+
else if (ch === " ") indent += 4;
|
|
737
|
+
else break;
|
|
738
|
+
}
|
|
739
|
+
return indent;
|
|
740
|
+
}
|
|
741
|
+
function nodeId(shape, label) {
|
|
742
|
+
return `${shape}:${label.toLowerCase().trim()}`;
|
|
743
|
+
}
|
|
744
|
+
function extractColor(label, palette) {
|
|
745
|
+
const m = label.match(COLOR_SUFFIX_RE);
|
|
746
|
+
if (!m) return { label };
|
|
747
|
+
const colorName = m[1].trim();
|
|
748
|
+
return {
|
|
749
|
+
label: label.substring(0, m.index).trim(),
|
|
750
|
+
color: resolveColor(colorName, palette)
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function parseNodeRef(text, palette) {
|
|
754
|
+
const t = text.trim();
|
|
755
|
+
if (!t) return null;
|
|
756
|
+
let m = t.match(/^\[\[([^\]]+)\]\]$/);
|
|
757
|
+
if (m) {
|
|
758
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
759
|
+
return { id: nodeId("subroutine", label), label, shape: "subroutine", color };
|
|
760
|
+
}
|
|
761
|
+
m = t.match(/^\[([^\]]+)~\]$/);
|
|
762
|
+
if (m) {
|
|
763
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
764
|
+
return { id: nodeId("document", label), label, shape: "document", color };
|
|
765
|
+
}
|
|
766
|
+
m = t.match(/^\[([^\]]+)\]$/);
|
|
767
|
+
if (m) {
|
|
768
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
769
|
+
return { id: nodeId("process", label), label, shape: "process", color };
|
|
770
|
+
}
|
|
771
|
+
m = t.match(/^\((.+)\)$/);
|
|
772
|
+
if (m) {
|
|
773
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
774
|
+
return { id: nodeId("terminal", label), label, shape: "terminal", color };
|
|
775
|
+
}
|
|
776
|
+
m = t.match(/^<([^>]+)>$/);
|
|
777
|
+
if (m) {
|
|
778
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
779
|
+
return { id: nodeId("decision", label), label, shape: "decision", color };
|
|
780
|
+
}
|
|
781
|
+
m = t.match(/^\/([^/]+)\/$/);
|
|
782
|
+
if (m) {
|
|
783
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
784
|
+
return { id: nodeId("io", label), label, shape: "io", color };
|
|
785
|
+
}
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
function splitArrows(line3) {
|
|
789
|
+
const segments = [];
|
|
790
|
+
const arrowRe = /(?:^|\s)-([^>\s(][^(>]*?)?\s*(?:\(([^)]+)\))?\s*->|(?:^|\s)->/g;
|
|
791
|
+
let lastIndex = 0;
|
|
792
|
+
const arrowPositions = [];
|
|
793
|
+
let searchFrom = 0;
|
|
794
|
+
while (searchFrom < line3.length) {
|
|
795
|
+
const idx = line3.indexOf("->", searchFrom);
|
|
796
|
+
if (idx === -1) break;
|
|
797
|
+
let arrowStart = idx;
|
|
798
|
+
let label;
|
|
799
|
+
let color;
|
|
800
|
+
if (idx > 0 && line3[idx - 1] !== " " && line3[idx - 1] !== " ") {
|
|
801
|
+
let scanBack = idx - 1;
|
|
802
|
+
while (scanBack > 0 && line3[scanBack] !== "-") {
|
|
803
|
+
scanBack--;
|
|
804
|
+
}
|
|
805
|
+
if (line3[scanBack] === "-" && (scanBack === 0 || /\s/.test(line3[scanBack - 1]))) {
|
|
806
|
+
let arrowContent = line3.substring(scanBack + 1, idx);
|
|
807
|
+
if (arrowContent.endsWith("-")) arrowContent = arrowContent.slice(0, -1);
|
|
808
|
+
const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
|
|
809
|
+
if (colorMatch) {
|
|
810
|
+
color = colorMatch[1].trim();
|
|
811
|
+
const labelPart = arrowContent.substring(0, colorMatch.index).trim();
|
|
812
|
+
if (labelPart) label = labelPart;
|
|
813
|
+
} else {
|
|
814
|
+
const labelPart = arrowContent.trim();
|
|
815
|
+
if (labelPart) label = labelPart;
|
|
816
|
+
}
|
|
817
|
+
arrowStart = scanBack;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
|
|
821
|
+
searchFrom = idx + 2;
|
|
822
|
+
}
|
|
823
|
+
if (arrowPositions.length === 0) {
|
|
824
|
+
return [line3];
|
|
825
|
+
}
|
|
826
|
+
for (let i = 0; i < arrowPositions.length; i++) {
|
|
827
|
+
const arrow = arrowPositions[i];
|
|
828
|
+
const beforeText = line3.substring(lastIndex, arrow.start).trim();
|
|
829
|
+
if (beforeText || i === 0) {
|
|
830
|
+
segments.push(beforeText);
|
|
831
|
+
}
|
|
832
|
+
let arrowToken = "->";
|
|
833
|
+
if (arrow.label && arrow.color) arrowToken = `-${arrow.label}(${arrow.color})->`;
|
|
834
|
+
else if (arrow.label) arrowToken = `-${arrow.label}->`;
|
|
835
|
+
else if (arrow.color) arrowToken = `-(${arrow.color})->`;
|
|
836
|
+
segments.push(arrowToken);
|
|
837
|
+
lastIndex = arrow.end;
|
|
838
|
+
}
|
|
839
|
+
const remaining = line3.substring(lastIndex).trim();
|
|
840
|
+
if (remaining) {
|
|
841
|
+
segments.push(remaining);
|
|
842
|
+
}
|
|
843
|
+
return segments;
|
|
844
|
+
}
|
|
845
|
+
function parseArrowToken(token, palette) {
|
|
846
|
+
if (token === "->") return {};
|
|
847
|
+
const colorOnly = token.match(/^-\(([^)]+)\)->$/);
|
|
848
|
+
if (colorOnly) {
|
|
849
|
+
return { color: resolveColor(colorOnly[1].trim(), palette) };
|
|
850
|
+
}
|
|
851
|
+
const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
|
|
852
|
+
if (m) {
|
|
853
|
+
const label = m[1]?.trim() || void 0;
|
|
854
|
+
const color = m[2] ? resolveColor(m[2].trim(), palette) : void 0;
|
|
855
|
+
return { label, color };
|
|
856
|
+
}
|
|
857
|
+
return {};
|
|
858
|
+
}
|
|
859
|
+
function parseFlowchart(content, palette) {
|
|
860
|
+
const lines = content.split("\n");
|
|
861
|
+
const result = {
|
|
862
|
+
type: "flowchart",
|
|
863
|
+
direction: "TB",
|
|
864
|
+
nodes: [],
|
|
865
|
+
edges: []
|
|
866
|
+
};
|
|
867
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
868
|
+
const indentStack = [];
|
|
869
|
+
let currentGroup = null;
|
|
870
|
+
const groups = [];
|
|
871
|
+
let contentStarted = false;
|
|
872
|
+
function getOrCreateNode(ref, lineNumber) {
|
|
873
|
+
const existing = nodeMap.get(ref.id);
|
|
874
|
+
if (existing) return existing;
|
|
875
|
+
const node = {
|
|
876
|
+
id: ref.id,
|
|
877
|
+
label: ref.label,
|
|
878
|
+
shape: ref.shape,
|
|
879
|
+
lineNumber,
|
|
880
|
+
...ref.color && { color: ref.color },
|
|
881
|
+
...currentGroup && { group: currentGroup.id }
|
|
882
|
+
};
|
|
883
|
+
nodeMap.set(ref.id, node);
|
|
884
|
+
result.nodes.push(node);
|
|
885
|
+
if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
|
|
886
|
+
currentGroup.nodeIds.push(ref.id);
|
|
887
|
+
}
|
|
888
|
+
return node;
|
|
889
|
+
}
|
|
890
|
+
function addEdge(sourceId, targetId, lineNumber, label, color) {
|
|
891
|
+
const edge = {
|
|
892
|
+
source: sourceId,
|
|
893
|
+
target: targetId,
|
|
894
|
+
lineNumber,
|
|
895
|
+
...label && { label },
|
|
896
|
+
...color && { color }
|
|
897
|
+
};
|
|
898
|
+
result.edges.push(edge);
|
|
899
|
+
}
|
|
900
|
+
function processContentLine(trimmed, lineNumber, indent) {
|
|
901
|
+
contentStarted = true;
|
|
902
|
+
while (indentStack.length > 0) {
|
|
903
|
+
const top = indentStack[indentStack.length - 1];
|
|
904
|
+
if (top.indent >= indent) {
|
|
905
|
+
indentStack.pop();
|
|
906
|
+
} else {
|
|
907
|
+
break;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
const implicitSourceId = indentStack.length > 0 ? indentStack[indentStack.length - 1].nodeId : null;
|
|
911
|
+
const segments = splitArrows(trimmed);
|
|
912
|
+
if (segments.length === 1) {
|
|
913
|
+
const ref = parseNodeRef(segments[0], palette);
|
|
914
|
+
if (ref) {
|
|
915
|
+
const node = getOrCreateNode(ref, lineNumber);
|
|
916
|
+
indentStack.push({ nodeId: node.id, indent });
|
|
917
|
+
return node.id;
|
|
918
|
+
}
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
let lastNodeId = null;
|
|
922
|
+
let pendingArrow = null;
|
|
923
|
+
for (let i = 0; i < segments.length; i++) {
|
|
924
|
+
const seg = segments[i];
|
|
925
|
+
if (seg === "->" || /^-.+->$/.test(seg)) {
|
|
926
|
+
pendingArrow = parseArrowToken(seg, palette);
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
const ref = parseNodeRef(seg, palette);
|
|
930
|
+
if (!ref) continue;
|
|
931
|
+
const node = getOrCreateNode(ref, lineNumber);
|
|
932
|
+
if (pendingArrow !== null) {
|
|
933
|
+
const sourceId = lastNodeId ?? implicitSourceId;
|
|
934
|
+
if (sourceId) {
|
|
935
|
+
addEdge(
|
|
936
|
+
sourceId,
|
|
937
|
+
node.id,
|
|
938
|
+
lineNumber,
|
|
939
|
+
pendingArrow.label,
|
|
940
|
+
pendingArrow.color
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
pendingArrow = null;
|
|
944
|
+
} else if (lastNodeId === null && implicitSourceId === null) {
|
|
945
|
+
}
|
|
946
|
+
lastNodeId = node.id;
|
|
947
|
+
}
|
|
948
|
+
if (pendingArrow !== null && lastNodeId === null && implicitSourceId) {
|
|
949
|
+
}
|
|
950
|
+
if (segments.length >= 2 && segments[0] === "" && implicitSourceId && lastNodeId) {
|
|
951
|
+
}
|
|
952
|
+
if (lastNodeId) {
|
|
953
|
+
indentStack.push({ nodeId: lastNodeId, indent });
|
|
954
|
+
}
|
|
955
|
+
return lastNodeId;
|
|
956
|
+
}
|
|
957
|
+
for (let i = 0; i < lines.length; i++) {
|
|
958
|
+
const raw = lines[i];
|
|
959
|
+
const trimmed = raw.trim();
|
|
960
|
+
const lineNumber = i + 1;
|
|
961
|
+
const indent = measureIndent2(raw);
|
|
962
|
+
if (!trimmed) continue;
|
|
963
|
+
if (trimmed.startsWith("//")) continue;
|
|
964
|
+
const groupMatch = trimmed.match(GROUP_HEADING_RE);
|
|
965
|
+
if (groupMatch) {
|
|
966
|
+
const groupLabel = groupMatch[1].trim();
|
|
967
|
+
const groupColorName = groupMatch[2]?.trim();
|
|
968
|
+
const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
|
|
969
|
+
currentGroup = {
|
|
970
|
+
id: `group:${groupLabel.toLowerCase()}`,
|
|
971
|
+
label: groupLabel,
|
|
972
|
+
nodeIds: [],
|
|
973
|
+
lineNumber,
|
|
974
|
+
...groupColor && { color: groupColor }
|
|
975
|
+
};
|
|
976
|
+
groups.push(currentGroup);
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (!contentStarted && trimmed.includes(":") && !trimmed.includes("->")) {
|
|
980
|
+
const colonIdx = trimmed.indexOf(":");
|
|
981
|
+
const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
|
|
982
|
+
const value = trimmed.substring(colonIdx + 1).trim();
|
|
983
|
+
if (key === "chart") {
|
|
984
|
+
if (value.toLowerCase() !== "flowchart") {
|
|
985
|
+
result.error = `Line ${lineNumber}: Expected chart type "flowchart", got "${value}"`;
|
|
986
|
+
return result;
|
|
987
|
+
}
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
if (key === "title") {
|
|
991
|
+
result.title = value;
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
if (key === "direction") {
|
|
995
|
+
const dir = value.toUpperCase();
|
|
996
|
+
if (dir === "TB" || dir === "LR") {
|
|
997
|
+
result.direction = dir;
|
|
998
|
+
}
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
processContentLine(trimmed, lineNumber, indent);
|
|
1004
|
+
}
|
|
1005
|
+
if (groups.length > 0) result.groups = groups;
|
|
1006
|
+
if (result.nodes.length === 0 && !result.error) {
|
|
1007
|
+
result.error = "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).";
|
|
1008
|
+
}
|
|
1009
|
+
return result;
|
|
1010
|
+
}
|
|
1011
|
+
function looksLikeFlowchart(content) {
|
|
1012
|
+
if (!content.includes("->")) return false;
|
|
1013
|
+
const hasShapeDelimiter = /\[[^\]]+\]/.test(content) || /\([^)]+\)/.test(content) || /<[^>]+>/.test(content) || /\/[^/]+\//.test(content);
|
|
1014
|
+
if (!hasShapeDelimiter) return false;
|
|
1015
|
+
const shapeNearArrow = /[\])][ \t]*-.*->/.test(content) || // shape ] or ) followed by arrow
|
|
1016
|
+
/->[ \t]*[\[(<\/]/.test(content);
|
|
1017
|
+
return shapeNearArrow;
|
|
1018
|
+
}
|
|
1019
|
+
var COLOR_SUFFIX_RE, GROUP_HEADING_RE;
|
|
1020
|
+
var init_flowchart_parser = __esm({
|
|
1021
|
+
"src/graph/flowchart-parser.ts"() {
|
|
1022
|
+
"use strict";
|
|
1023
|
+
init_colors();
|
|
1024
|
+
COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
|
|
1025
|
+
GROUP_HEADING_RE = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
// src/dgmo-router.ts
|
|
1030
|
+
var dgmo_router_exports = {};
|
|
1031
|
+
__export(dgmo_router_exports, {
|
|
1032
|
+
DGMO_CHART_TYPE_MAP: () => DGMO_CHART_TYPE_MAP,
|
|
1033
|
+
getDgmoFramework: () => getDgmoFramework,
|
|
1034
|
+
parseDgmoChartType: () => parseDgmoChartType
|
|
1035
|
+
});
|
|
1036
|
+
function getDgmoFramework(chartType) {
|
|
1037
|
+
return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
|
|
1038
|
+
}
|
|
1039
|
+
function parseDgmoChartType(content) {
|
|
1040
|
+
const lines = content.split("\n");
|
|
1041
|
+
for (const line3 of lines) {
|
|
1042
|
+
const trimmed = line3.trim();
|
|
1043
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
|
|
1044
|
+
continue;
|
|
1045
|
+
const match = trimmed.match(/^chart\s*:\s*(.+)/i);
|
|
1046
|
+
if (match) return match[1].trim().toLowerCase();
|
|
1047
|
+
}
|
|
1048
|
+
if (looksLikeSequence(content)) return "sequence";
|
|
1049
|
+
if (looksLikeFlowchart(content)) return "flowchart";
|
|
1050
|
+
return null;
|
|
1051
|
+
}
|
|
1052
|
+
var DGMO_CHART_TYPE_MAP;
|
|
1053
|
+
var init_dgmo_router = __esm({
|
|
1054
|
+
"src/dgmo-router.ts"() {
|
|
1055
|
+
"use strict";
|
|
1056
|
+
init_parser();
|
|
1057
|
+
init_flowchart_parser();
|
|
1058
|
+
DGMO_CHART_TYPE_MAP = {
|
|
1059
|
+
// Standard charts (via ECharts)
|
|
1060
|
+
bar: "echart",
|
|
1061
|
+
line: "echart",
|
|
1062
|
+
"multi-line": "echart",
|
|
1063
|
+
area: "echart",
|
|
1064
|
+
pie: "echart",
|
|
1065
|
+
doughnut: "echart",
|
|
1066
|
+
radar: "echart",
|
|
1067
|
+
"polar-area": "echart",
|
|
1068
|
+
"bar-stacked": "echart",
|
|
1069
|
+
// ECharts
|
|
1070
|
+
scatter: "echart",
|
|
1071
|
+
sankey: "echart",
|
|
1072
|
+
chord: "echart",
|
|
1073
|
+
function: "echart",
|
|
1074
|
+
heatmap: "echart",
|
|
1075
|
+
funnel: "echart",
|
|
1076
|
+
// D3
|
|
1077
|
+
slope: "d3",
|
|
1078
|
+
wordcloud: "d3",
|
|
1079
|
+
arc: "d3",
|
|
1080
|
+
timeline: "d3",
|
|
1081
|
+
venn: "d3",
|
|
1082
|
+
quadrant: "d3",
|
|
1083
|
+
sequence: "d3",
|
|
1084
|
+
flowchart: "d3"
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
|
|
582
1089
|
// src/fonts.ts
|
|
583
1090
|
var FONT_FAMILY;
|
|
584
1091
|
var init_fonts = __esm({
|
|
@@ -1634,6 +2141,409 @@ var init_palettes = __esm({
|
|
|
1634
2141
|
}
|
|
1635
2142
|
});
|
|
1636
2143
|
|
|
2144
|
+
// src/graph/layout.ts
|
|
2145
|
+
var layout_exports = {};
|
|
2146
|
+
__export(layout_exports, {
|
|
2147
|
+
layoutGraph: () => layoutGraph
|
|
2148
|
+
});
|
|
2149
|
+
function computeNodeWidth(label, shape) {
|
|
2150
|
+
const base = Math.max(120, label.length * 9 + 40);
|
|
2151
|
+
if (shape === "subroutine") return base + 10;
|
|
2152
|
+
return base;
|
|
2153
|
+
}
|
|
2154
|
+
function computeNodeHeight(shape) {
|
|
2155
|
+
return shape === "decision" ? 60 : 50;
|
|
2156
|
+
}
|
|
2157
|
+
function layoutGraph(graph) {
|
|
2158
|
+
if (graph.nodes.length === 0) {
|
|
2159
|
+
return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
|
|
2160
|
+
}
|
|
2161
|
+
const g = new import_dagre.default.graphlib.Graph({ compound: true });
|
|
2162
|
+
g.setGraph({
|
|
2163
|
+
rankdir: graph.direction,
|
|
2164
|
+
nodesep: 50,
|
|
2165
|
+
ranksep: 60,
|
|
2166
|
+
edgesep: 20
|
|
2167
|
+
});
|
|
2168
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
2169
|
+
const nodeDataMap = /* @__PURE__ */ new Map();
|
|
2170
|
+
for (const node of graph.nodes) {
|
|
2171
|
+
nodeDataMap.set(node.id, node);
|
|
2172
|
+
}
|
|
2173
|
+
if (graph.groups) {
|
|
2174
|
+
for (const group of graph.groups) {
|
|
2175
|
+
g.setNode(group.id, {
|
|
2176
|
+
label: group.label,
|
|
2177
|
+
clusterLabelPos: "top"
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
for (const node of graph.nodes) {
|
|
2182
|
+
const width = computeNodeWidth(node.label, node.shape);
|
|
2183
|
+
const height = computeNodeHeight(node.shape);
|
|
2184
|
+
g.setNode(node.id, { label: node.label, width, height });
|
|
2185
|
+
if (node.group && graph.groups?.some((gr) => gr.id === node.group)) {
|
|
2186
|
+
g.setParent(node.id, node.group);
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
const edgeDataMap = /* @__PURE__ */ new Map();
|
|
2190
|
+
for (const edge of graph.edges) {
|
|
2191
|
+
const key = `${edge.source}->${edge.target}`;
|
|
2192
|
+
edgeDataMap.set(key, edge);
|
|
2193
|
+
g.setEdge(edge.source, edge.target, {
|
|
2194
|
+
label: edge.label ?? ""
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
import_dagre.default.layout(g);
|
|
2198
|
+
const layoutNodes = graph.nodes.map((node) => {
|
|
2199
|
+
const pos = g.node(node.id);
|
|
2200
|
+
return {
|
|
2201
|
+
id: node.id,
|
|
2202
|
+
label: node.label,
|
|
2203
|
+
shape: node.shape,
|
|
2204
|
+
color: node.color,
|
|
2205
|
+
group: node.group,
|
|
2206
|
+
lineNumber: node.lineNumber,
|
|
2207
|
+
x: pos.x,
|
|
2208
|
+
y: pos.y,
|
|
2209
|
+
width: pos.width,
|
|
2210
|
+
height: pos.height
|
|
2211
|
+
};
|
|
2212
|
+
});
|
|
2213
|
+
const layoutEdges = graph.edges.map((edge) => {
|
|
2214
|
+
const edgeData = g.edge(edge.source, edge.target);
|
|
2215
|
+
return {
|
|
2216
|
+
source: edge.source,
|
|
2217
|
+
target: edge.target,
|
|
2218
|
+
points: edgeData?.points ?? [],
|
|
2219
|
+
label: edge.label,
|
|
2220
|
+
color: edge.color,
|
|
2221
|
+
lineNumber: edge.lineNumber
|
|
2222
|
+
};
|
|
2223
|
+
});
|
|
2224
|
+
const layoutGroups = [];
|
|
2225
|
+
if (graph.groups) {
|
|
2226
|
+
const nodeMap = new Map(layoutNodes.map((n) => [n.id, n]));
|
|
2227
|
+
for (const group of graph.groups) {
|
|
2228
|
+
const members = group.nodeIds.map((id) => nodeMap.get(id)).filter((n) => n !== void 0);
|
|
2229
|
+
if (members.length === 0) {
|
|
2230
|
+
layoutGroups.push({
|
|
2231
|
+
id: group.id,
|
|
2232
|
+
label: group.label,
|
|
2233
|
+
color: group.color,
|
|
2234
|
+
x: 0,
|
|
2235
|
+
y: 0,
|
|
2236
|
+
width: 0,
|
|
2237
|
+
height: 0
|
|
2238
|
+
});
|
|
2239
|
+
continue;
|
|
2240
|
+
}
|
|
2241
|
+
let minX = Infinity;
|
|
2242
|
+
let minY = Infinity;
|
|
2243
|
+
let maxX = -Infinity;
|
|
2244
|
+
let maxY = -Infinity;
|
|
2245
|
+
for (const member of members) {
|
|
2246
|
+
const left = member.x - member.width / 2;
|
|
2247
|
+
const right = member.x + member.width / 2;
|
|
2248
|
+
const top = member.y - member.height / 2;
|
|
2249
|
+
const bottom = member.y + member.height / 2;
|
|
2250
|
+
if (left < minX) minX = left;
|
|
2251
|
+
if (right > maxX) maxX = right;
|
|
2252
|
+
if (top < minY) minY = top;
|
|
2253
|
+
if (bottom > maxY) maxY = bottom;
|
|
2254
|
+
}
|
|
2255
|
+
layoutGroups.push({
|
|
2256
|
+
id: group.id,
|
|
2257
|
+
label: group.label,
|
|
2258
|
+
color: group.color,
|
|
2259
|
+
x: minX - GROUP_PADDING,
|
|
2260
|
+
y: minY - GROUP_PADDING,
|
|
2261
|
+
width: maxX - minX + GROUP_PADDING * 2,
|
|
2262
|
+
height: maxY - minY + GROUP_PADDING * 2
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
let totalWidth = 0;
|
|
2267
|
+
let totalHeight = 0;
|
|
2268
|
+
for (const node of layoutNodes) {
|
|
2269
|
+
const right = node.x + node.width / 2;
|
|
2270
|
+
const bottom = node.y + node.height / 2;
|
|
2271
|
+
if (right > totalWidth) totalWidth = right;
|
|
2272
|
+
if (bottom > totalHeight) totalHeight = bottom;
|
|
2273
|
+
}
|
|
2274
|
+
for (const group of layoutGroups) {
|
|
2275
|
+
const right = group.x + group.width;
|
|
2276
|
+
const bottom = group.y + group.height;
|
|
2277
|
+
if (right > totalWidth) totalWidth = right;
|
|
2278
|
+
if (bottom > totalHeight) totalHeight = bottom;
|
|
2279
|
+
}
|
|
2280
|
+
totalWidth += 40;
|
|
2281
|
+
totalHeight += 40;
|
|
2282
|
+
return {
|
|
2283
|
+
nodes: layoutNodes,
|
|
2284
|
+
edges: layoutEdges,
|
|
2285
|
+
groups: layoutGroups,
|
|
2286
|
+
width: totalWidth,
|
|
2287
|
+
height: totalHeight
|
|
2288
|
+
};
|
|
2289
|
+
}
|
|
2290
|
+
var import_dagre, GROUP_PADDING;
|
|
2291
|
+
var init_layout = __esm({
|
|
2292
|
+
"src/graph/layout.ts"() {
|
|
2293
|
+
"use strict";
|
|
2294
|
+
import_dagre = __toESM(require("@dagrejs/dagre"), 1);
|
|
2295
|
+
GROUP_PADDING = 20;
|
|
2296
|
+
}
|
|
2297
|
+
});
|
|
2298
|
+
|
|
2299
|
+
// src/graph/flowchart-renderer.ts
|
|
2300
|
+
var flowchart_renderer_exports = {};
|
|
2301
|
+
__export(flowchart_renderer_exports, {
|
|
2302
|
+
renderFlowchart: () => renderFlowchart,
|
|
2303
|
+
renderFlowchartForExport: () => renderFlowchartForExport
|
|
2304
|
+
});
|
|
2305
|
+
function mix(a, b, pct) {
|
|
2306
|
+
const parse = (h) => {
|
|
2307
|
+
const r = h.replace("#", "");
|
|
2308
|
+
const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
|
|
2309
|
+
return [parseInt(f.substring(0, 2), 16), parseInt(f.substring(2, 4), 16), parseInt(f.substring(4, 6), 16)];
|
|
2310
|
+
};
|
|
2311
|
+
const [ar, ag, ab] = parse(a), [br, bg, bb] = parse(b), t = pct / 100;
|
|
2312
|
+
const c = (x, y) => Math.round(x * t + y * (1 - t)).toString(16).padStart(2, "0");
|
|
2313
|
+
return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
|
|
2314
|
+
}
|
|
2315
|
+
function nodeFill(palette, isDark, nodeColor) {
|
|
2316
|
+
if (nodeColor) {
|
|
2317
|
+
return mix(nodeColor, isDark ? palette.surface : palette.bg, 25);
|
|
2318
|
+
}
|
|
2319
|
+
return mix(palette.primary, isDark ? palette.surface : palette.bg, 15);
|
|
2320
|
+
}
|
|
2321
|
+
function nodeStroke(palette, nodeColor) {
|
|
2322
|
+
return nodeColor ?? palette.textMuted;
|
|
2323
|
+
}
|
|
2324
|
+
function renderTerminal(g, node, palette, isDark) {
|
|
2325
|
+
const w = node.width;
|
|
2326
|
+
const h = node.height;
|
|
2327
|
+
const rx = h / 2;
|
|
2328
|
+
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);
|
|
2329
|
+
}
|
|
2330
|
+
function renderProcess(g, node, palette, isDark) {
|
|
2331
|
+
const w = node.width;
|
|
2332
|
+
const h = node.height;
|
|
2333
|
+
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);
|
|
2334
|
+
}
|
|
2335
|
+
function renderDecision(g, node, palette, isDark) {
|
|
2336
|
+
const w = node.width / 2;
|
|
2337
|
+
const h = node.height / 2;
|
|
2338
|
+
const points = [
|
|
2339
|
+
`${0},${-h}`,
|
|
2340
|
+
// top
|
|
2341
|
+
`${w},${0}`,
|
|
2342
|
+
// right
|
|
2343
|
+
`${0},${h}`,
|
|
2344
|
+
// bottom
|
|
2345
|
+
`${-w},${0}`
|
|
2346
|
+
// left
|
|
2347
|
+
].join(" ");
|
|
2348
|
+
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);
|
|
2349
|
+
}
|
|
2350
|
+
function renderIO(g, node, palette, isDark) {
|
|
2351
|
+
const w = node.width / 2;
|
|
2352
|
+
const h = node.height / 2;
|
|
2353
|
+
const sk = IO_SKEW;
|
|
2354
|
+
const points = [
|
|
2355
|
+
`${-w + sk},${-h}`,
|
|
2356
|
+
// top-left (shifted right)
|
|
2357
|
+
`${w + sk},${-h}`,
|
|
2358
|
+
// top-right (shifted right)
|
|
2359
|
+
`${w - sk},${h}`,
|
|
2360
|
+
// bottom-right (shifted left)
|
|
2361
|
+
`${-w - sk},${h}`
|
|
2362
|
+
// bottom-left (shifted left)
|
|
2363
|
+
].join(" ");
|
|
2364
|
+
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);
|
|
2365
|
+
}
|
|
2366
|
+
function renderSubroutine(g, node, palette, isDark) {
|
|
2367
|
+
const w = node.width;
|
|
2368
|
+
const h = node.height;
|
|
2369
|
+
const s = nodeStroke(palette, node.color);
|
|
2370
|
+
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);
|
|
2371
|
+
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);
|
|
2372
|
+
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);
|
|
2373
|
+
}
|
|
2374
|
+
function renderDocument(g, node, palette, isDark) {
|
|
2375
|
+
const w = node.width;
|
|
2376
|
+
const h = node.height;
|
|
2377
|
+
const waveH = DOC_WAVE_HEIGHT;
|
|
2378
|
+
const left = -w / 2;
|
|
2379
|
+
const right = w / 2;
|
|
2380
|
+
const top = -h / 2;
|
|
2381
|
+
const bottom = h / 2 - waveH;
|
|
2382
|
+
const d = [
|
|
2383
|
+
`M ${left} ${top}`,
|
|
2384
|
+
`L ${right} ${top}`,
|
|
2385
|
+
`L ${right} ${bottom}`,
|
|
2386
|
+
`C ${right - w * 0.25} ${bottom + waveH * 2}, ${left + w * 0.25} ${bottom - waveH}, ${left} ${bottom}`,
|
|
2387
|
+
"Z"
|
|
2388
|
+
].join(" ");
|
|
2389
|
+
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);
|
|
2390
|
+
}
|
|
2391
|
+
function renderNodeShape(g, node, palette, isDark) {
|
|
2392
|
+
switch (node.shape) {
|
|
2393
|
+
case "terminal":
|
|
2394
|
+
renderTerminal(g, node, palette, isDark);
|
|
2395
|
+
break;
|
|
2396
|
+
case "process":
|
|
2397
|
+
renderProcess(g, node, palette, isDark);
|
|
2398
|
+
break;
|
|
2399
|
+
case "decision":
|
|
2400
|
+
renderDecision(g, node, palette, isDark);
|
|
2401
|
+
break;
|
|
2402
|
+
case "io":
|
|
2403
|
+
renderIO(g, node, palette, isDark);
|
|
2404
|
+
break;
|
|
2405
|
+
case "subroutine":
|
|
2406
|
+
renderSubroutine(g, node, palette, isDark);
|
|
2407
|
+
break;
|
|
2408
|
+
case "document":
|
|
2409
|
+
renderDocument(g, node, palette, isDark);
|
|
2410
|
+
break;
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
function renderFlowchart(container, graph, layout, palette, isDark, onClickItem, exportDims) {
|
|
2414
|
+
d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
2415
|
+
const width = exportDims?.width ?? container.clientWidth;
|
|
2416
|
+
const height = exportDims?.height ?? container.clientHeight;
|
|
2417
|
+
if (width <= 0 || height <= 0) return;
|
|
2418
|
+
const titleOffset = graph.title ? TITLE_HEIGHT : 0;
|
|
2419
|
+
const diagramW = layout.width;
|
|
2420
|
+
const diagramH = layout.height + titleOffset;
|
|
2421
|
+
const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
|
|
2422
|
+
const scaleY = (height - DIAGRAM_PADDING * 2) / diagramH;
|
|
2423
|
+
const scale = Math.min(1, scaleX, scaleY);
|
|
2424
|
+
const scaledW = diagramW * scale;
|
|
2425
|
+
const scaledH = diagramH * scale;
|
|
2426
|
+
const offsetX = (width - scaledW) / 2;
|
|
2427
|
+
const offsetY = (height - scaledH) / 2;
|
|
2428
|
+
const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("background", palette.bg).style("font-family", FONT_FAMILY);
|
|
2429
|
+
const defs = svg.append("defs");
|
|
2430
|
+
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);
|
|
2431
|
+
const edgeColors = /* @__PURE__ */ new Set();
|
|
2432
|
+
for (const edge of layout.edges) {
|
|
2433
|
+
if (edge.color) edgeColors.add(edge.color);
|
|
2434
|
+
}
|
|
2435
|
+
for (const color of edgeColors) {
|
|
2436
|
+
const id = `fc-arrow-${color.replace("#", "")}`;
|
|
2437
|
+
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);
|
|
2438
|
+
}
|
|
2439
|
+
const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
|
|
2440
|
+
if (graph.title) {
|
|
2441
|
+
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);
|
|
2442
|
+
}
|
|
2443
|
+
const contentG = mainG.append("g").attr("transform", `translate(0, ${titleOffset})`);
|
|
2444
|
+
for (const group of layout.groups) {
|
|
2445
|
+
if (group.width === 0 && group.height === 0) continue;
|
|
2446
|
+
const gx = group.x - GROUP_EXTRA_PADDING;
|
|
2447
|
+
const gy = group.y - GROUP_EXTRA_PADDING - GROUP_LABEL_FONT_SIZE - 4;
|
|
2448
|
+
const gw = group.width + GROUP_EXTRA_PADDING * 2;
|
|
2449
|
+
const gh = group.height + GROUP_EXTRA_PADDING * 2 + GROUP_LABEL_FONT_SIZE + 4;
|
|
2450
|
+
const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
|
|
2451
|
+
const strokeColor = group.color ?? palette.textMuted;
|
|
2452
|
+
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");
|
|
2453
|
+
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);
|
|
2454
|
+
}
|
|
2455
|
+
for (const edge of layout.edges) {
|
|
2456
|
+
if (edge.points.length < 2) continue;
|
|
2457
|
+
const edgeG = contentG.append("g").attr("class", "fc-edge-group").attr("data-line-number", String(edge.lineNumber));
|
|
2458
|
+
const edgeColor = edge.color ?? palette.textMuted;
|
|
2459
|
+
const markerId = edge.color ? `fc-arrow-${edge.color.replace("#", "")}` : "fc-arrow";
|
|
2460
|
+
const pathD = lineGenerator(edge.points);
|
|
2461
|
+
if (pathD) {
|
|
2462
|
+
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");
|
|
2463
|
+
}
|
|
2464
|
+
if (edge.label) {
|
|
2465
|
+
const midIdx = Math.floor(edge.points.length / 2);
|
|
2466
|
+
const midPt = edge.points[midIdx];
|
|
2467
|
+
const labelLen = edge.label.length;
|
|
2468
|
+
const bgW = labelLen * 7 + 8;
|
|
2469
|
+
const bgH = 16;
|
|
2470
|
+
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");
|
|
2471
|
+
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);
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
for (const node of layout.nodes) {
|
|
2475
|
+
const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber));
|
|
2476
|
+
if (onClickItem) {
|
|
2477
|
+
nodeG.style("cursor", "pointer").on("click", () => {
|
|
2478
|
+
onClickItem(node.lineNumber);
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2481
|
+
renderNodeShape(nodeG, node, palette, isDark);
|
|
2482
|
+
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);
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
function renderFlowchartForExport(content, theme, palette) {
|
|
2486
|
+
const parsed = parseFlowchart(content, palette);
|
|
2487
|
+
if (parsed.error || parsed.nodes.length === 0) return "";
|
|
2488
|
+
const layout = layoutGraph(parsed);
|
|
2489
|
+
const isDark = theme === "dark";
|
|
2490
|
+
const container = document.createElement("div");
|
|
2491
|
+
container.style.width = `${layout.width + DIAGRAM_PADDING * 2}px`;
|
|
2492
|
+
container.style.height = `${layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0)}px`;
|
|
2493
|
+
container.style.position = "absolute";
|
|
2494
|
+
container.style.left = "-9999px";
|
|
2495
|
+
document.body.appendChild(container);
|
|
2496
|
+
const exportWidth = layout.width + DIAGRAM_PADDING * 2;
|
|
2497
|
+
const exportHeight = layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0);
|
|
2498
|
+
try {
|
|
2499
|
+
renderFlowchart(
|
|
2500
|
+
container,
|
|
2501
|
+
parsed,
|
|
2502
|
+
layout,
|
|
2503
|
+
palette,
|
|
2504
|
+
isDark,
|
|
2505
|
+
void 0,
|
|
2506
|
+
{ width: exportWidth, height: exportHeight }
|
|
2507
|
+
);
|
|
2508
|
+
const svgEl = container.querySelector("svg");
|
|
2509
|
+
if (!svgEl) return "";
|
|
2510
|
+
if (theme === "transparent") {
|
|
2511
|
+
svgEl.style.background = "none";
|
|
2512
|
+
}
|
|
2513
|
+
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
2514
|
+
svgEl.style.fontFamily = FONT_FAMILY;
|
|
2515
|
+
return svgEl.outerHTML;
|
|
2516
|
+
} finally {
|
|
2517
|
+
document.body.removeChild(container);
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
var d3Selection, d3Shape, 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;
|
|
2521
|
+
var init_flowchart_renderer = __esm({
|
|
2522
|
+
"src/graph/flowchart-renderer.ts"() {
|
|
2523
|
+
"use strict";
|
|
2524
|
+
d3Selection = __toESM(require("d3-selection"), 1);
|
|
2525
|
+
d3Shape = __toESM(require("d3-shape"), 1);
|
|
2526
|
+
init_fonts();
|
|
2527
|
+
init_flowchart_parser();
|
|
2528
|
+
init_layout();
|
|
2529
|
+
DIAGRAM_PADDING = 20;
|
|
2530
|
+
TITLE_HEIGHT = 30;
|
|
2531
|
+
TITLE_FONT_SIZE = 18;
|
|
2532
|
+
NODE_FONT_SIZE = 13;
|
|
2533
|
+
EDGE_LABEL_FONT_SIZE = 11;
|
|
2534
|
+
GROUP_LABEL_FONT_SIZE = 11;
|
|
2535
|
+
EDGE_STROKE_WIDTH = 1.5;
|
|
2536
|
+
NODE_STROKE_WIDTH = 1.5;
|
|
2537
|
+
ARROWHEAD_W = 10;
|
|
2538
|
+
ARROWHEAD_H = 7;
|
|
2539
|
+
IO_SKEW = 15;
|
|
2540
|
+
SUBROUTINE_INSET = 8;
|
|
2541
|
+
DOC_WAVE_HEIGHT = 10;
|
|
2542
|
+
GROUP_EXTRA_PADDING = 12;
|
|
2543
|
+
lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
|
|
2544
|
+
}
|
|
2545
|
+
});
|
|
2546
|
+
|
|
1637
2547
|
// src/sequence/renderer.ts
|
|
1638
2548
|
var renderer_exports = {};
|
|
1639
2549
|
__export(renderer_exports, {
|
|
@@ -1644,7 +2554,44 @@ __export(renderer_exports, {
|
|
|
1644
2554
|
groupMessagesBySection: () => groupMessagesBySection,
|
|
1645
2555
|
renderSequenceDiagram: () => renderSequenceDiagram
|
|
1646
2556
|
});
|
|
1647
|
-
function
|
|
2557
|
+
function parseInlineMarkdown(text) {
|
|
2558
|
+
const spans = [];
|
|
2559
|
+
const regex = /\*\*(.+?)\*\*|__(.+?)__|\*(.+?)\*|_(.+?)_|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*_`[]+)/g;
|
|
2560
|
+
let match;
|
|
2561
|
+
while ((match = regex.exec(text)) !== null) {
|
|
2562
|
+
if (match[1]) spans.push({ text: match[1], bold: true });
|
|
2563
|
+
else if (match[2]) spans.push({ text: match[2], bold: true });
|
|
2564
|
+
else if (match[3]) spans.push({ text: match[3], italic: true });
|
|
2565
|
+
else if (match[4]) spans.push({ text: match[4], italic: true });
|
|
2566
|
+
else if (match[5]) spans.push({ text: match[5], code: true });
|
|
2567
|
+
else if (match[6]) spans.push({ text: match[6], href: match[7] });
|
|
2568
|
+
else if (match[8]) spans.push({ text: match[8] });
|
|
2569
|
+
}
|
|
2570
|
+
return spans;
|
|
2571
|
+
}
|
|
2572
|
+
function wrapTextLines(text, maxChars) {
|
|
2573
|
+
const rawLines = text.split("\n");
|
|
2574
|
+
const wrapped = [];
|
|
2575
|
+
for (const line3 of rawLines) {
|
|
2576
|
+
if (line3.length <= maxChars) {
|
|
2577
|
+
wrapped.push(line3);
|
|
2578
|
+
} else {
|
|
2579
|
+
const words = line3.split(" ");
|
|
2580
|
+
let current = "";
|
|
2581
|
+
for (const word of words) {
|
|
2582
|
+
if (current && (current + " " + word).length > maxChars) {
|
|
2583
|
+
wrapped.push(current);
|
|
2584
|
+
current = word;
|
|
2585
|
+
} else {
|
|
2586
|
+
current = current ? current + " " + word : word;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
if (current) wrapped.push(current);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
return wrapped;
|
|
2593
|
+
}
|
|
2594
|
+
function mix2(a, b, pct) {
|
|
1648
2595
|
const parse = (h) => {
|
|
1649
2596
|
const r = h.replace("#", "");
|
|
1650
2597
|
const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
|
|
@@ -1750,7 +2697,12 @@ function groupMessagesBySection(elements, messages) {
|
|
|
1750
2697
|
...collectIndices(el.children),
|
|
1751
2698
|
...collectIndices(el.elseChildren)
|
|
1752
2699
|
);
|
|
1753
|
-
|
|
2700
|
+
if (el.elseIfBranches) {
|
|
2701
|
+
for (const branch of el.elseIfBranches) {
|
|
2702
|
+
indices.push(...collectIndices(branch.children));
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
} else if (isSequenceSection(el) || isSequenceNote(el)) {
|
|
1754
2706
|
continue;
|
|
1755
2707
|
} else {
|
|
1756
2708
|
const idx = messages.indexOf(el);
|
|
@@ -1766,7 +2718,7 @@ function groupMessagesBySection(elements, messages) {
|
|
|
1766
2718
|
} else if (currentGroup) {
|
|
1767
2719
|
if (isSequenceBlock(el)) {
|
|
1768
2720
|
currentGroup.messageIndices.push(...collectIndices([el]));
|
|
1769
|
-
} else {
|
|
2721
|
+
} else if (!isSequenceNote(el)) {
|
|
1770
2722
|
const idx = messages.indexOf(el);
|
|
1771
2723
|
if (idx >= 0) currentGroup.messageIndices.push(idx);
|
|
1772
2724
|
}
|
|
@@ -1923,7 +2875,7 @@ function applyGroupOrdering(participants, groups) {
|
|
|
1923
2875
|
return result;
|
|
1924
2876
|
}
|
|
1925
2877
|
function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
|
|
1926
|
-
|
|
2878
|
+
d3Selection2.select(container).selectAll("*").remove();
|
|
1927
2879
|
const { title, messages, elements, groups, options: parsedOptions } = parsed;
|
|
1928
2880
|
const collapsedSections = options?.collapsedSections;
|
|
1929
2881
|
const participants = applyPositionOverrides(
|
|
@@ -1964,7 +2916,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1964
2916
|
if (isSequenceBlock(el)) {
|
|
1965
2917
|
const idx = findFirstMsgIndex(el.children);
|
|
1966
2918
|
if (idx >= 0) return idx;
|
|
1967
|
-
|
|
2919
|
+
if (el.elseIfBranches) {
|
|
2920
|
+
for (const branch of el.elseIfBranches) {
|
|
2921
|
+
const branchIdx = findFirstMsgIndex(branch.children);
|
|
2922
|
+
if (branchIdx >= 0) return branchIdx;
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
const elseIdx = findFirstMsgIndex(el.elseChildren);
|
|
2926
|
+
if (elseIdx >= 0) return elseIdx;
|
|
2927
|
+
} else if (!isSequenceSection(el) && !isSequenceNote(el)) {
|
|
1968
2928
|
const idx = messages.indexOf(el);
|
|
1969
2929
|
if (idx >= 0 && !hiddenMsgIndices.has(idx)) return idx;
|
|
1970
2930
|
}
|
|
@@ -1984,6 +2944,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1984
2944
|
if (!isSequenceBlock(el)) continue;
|
|
1985
2945
|
const firstIdx = findFirstMsgIndex(el.children);
|
|
1986
2946
|
if (firstIdx >= 0) addExtra(firstIdx, BLOCK_HEADER_SPACE);
|
|
2947
|
+
if (el.elseIfBranches) {
|
|
2948
|
+
for (const branch of el.elseIfBranches) {
|
|
2949
|
+
const firstBranchIdx = findFirstMsgIndex(branch.children);
|
|
2950
|
+
if (firstBranchIdx >= 0) addExtra(firstBranchIdx, BLOCK_HEADER_SPACE);
|
|
2951
|
+
markBlockSpacing(branch.children);
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
1987
2954
|
const firstElseIdx = findFirstMsgIndex(el.elseChildren);
|
|
1988
2955
|
if (firstElseIdx >= 0) addExtra(firstElseIdx, BLOCK_HEADER_SPACE);
|
|
1989
2956
|
markBlockSpacing(el.children);
|
|
@@ -1997,6 +2964,34 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1997
2964
|
if (elements && elements.length > 0) {
|
|
1998
2965
|
markBlockSpacing(elements);
|
|
1999
2966
|
}
|
|
2967
|
+
const NOTE_OFFSET_BELOW = 16;
|
|
2968
|
+
const computeNoteHeight = (text) => {
|
|
2969
|
+
const lines = wrapTextLines(text, NOTE_CHARS_PER_LINE);
|
|
2970
|
+
return lines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
|
|
2971
|
+
};
|
|
2972
|
+
const markNoteSpacing = (els) => {
|
|
2973
|
+
for (let i = 0; i < els.length; i++) {
|
|
2974
|
+
const el = els[i];
|
|
2975
|
+
if (isSequenceNote(el)) {
|
|
2976
|
+
const noteH = computeNoteHeight(el.text);
|
|
2977
|
+
const nextIdx = i + 1 < els.length ? findFirstMsgIndex([els[i + 1]]) : -1;
|
|
2978
|
+
if (nextIdx >= 0) {
|
|
2979
|
+
addExtra(nextIdx, noteH + NOTE_OFFSET_BELOW);
|
|
2980
|
+
}
|
|
2981
|
+
} else if (isSequenceBlock(el)) {
|
|
2982
|
+
markNoteSpacing(el.children);
|
|
2983
|
+
if (el.elseIfBranches) {
|
|
2984
|
+
for (const branch of el.elseIfBranches) {
|
|
2985
|
+
markNoteSpacing(branch.children);
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
markNoteSpacing(el.elseChildren);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
};
|
|
2992
|
+
if (elements && elements.length > 0) {
|
|
2993
|
+
markNoteSpacing(elements);
|
|
2994
|
+
}
|
|
2000
2995
|
const preSectionMsgIndices = [];
|
|
2001
2996
|
const sectionRegions = [];
|
|
2002
2997
|
{
|
|
@@ -2005,15 +3000,27 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2005
3000
|
for (const child of block.children) {
|
|
2006
3001
|
if (isSequenceBlock(child)) {
|
|
2007
3002
|
indices.push(...collectMsgIndicesFromBlock(child));
|
|
2008
|
-
} else if (!isSequenceSection(child)) {
|
|
3003
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
2009
3004
|
const idx = messages.indexOf(child);
|
|
2010
3005
|
if (idx >= 0) indices.push(idx);
|
|
2011
3006
|
}
|
|
2012
3007
|
}
|
|
3008
|
+
if (block.elseIfBranches) {
|
|
3009
|
+
for (const branch of block.elseIfBranches) {
|
|
3010
|
+
for (const child of branch.children) {
|
|
3011
|
+
if (isSequenceBlock(child)) {
|
|
3012
|
+
indices.push(...collectMsgIndicesFromBlock(child));
|
|
3013
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
3014
|
+
const idx = messages.indexOf(child);
|
|
3015
|
+
if (idx >= 0) indices.push(idx);
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
2013
3020
|
for (const child of block.elseChildren) {
|
|
2014
3021
|
if (isSequenceBlock(child)) {
|
|
2015
3022
|
indices.push(...collectMsgIndicesFromBlock(child));
|
|
2016
|
-
} else if (!isSequenceSection(child)) {
|
|
3023
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
2017
3024
|
const idx = messages.indexOf(child);
|
|
2018
3025
|
if (idx >= 0) indices.push(idx);
|
|
2019
3026
|
}
|
|
@@ -2088,7 +3095,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2088
3095
|
const GROUP_PADDING_TOP = 22;
|
|
2089
3096
|
const GROUP_PADDING_BOTTOM = 8;
|
|
2090
3097
|
const GROUP_LABEL_SIZE = 11;
|
|
2091
|
-
const titleOffset = title ?
|
|
3098
|
+
const titleOffset = title ? TITLE_HEIGHT2 : 0;
|
|
2092
3099
|
const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
|
|
2093
3100
|
const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
|
|
2094
3101
|
const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
|
|
@@ -2142,7 +3149,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2142
3149
|
participants.forEach((p, i) => {
|
|
2143
3150
|
participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
|
|
2144
3151
|
});
|
|
2145
|
-
const svg =
|
|
3152
|
+
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);
|
|
2146
3153
|
const defs = svg.append("defs");
|
|
2147
3154
|
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(
|
|
2148
3155
|
"points",
|
|
@@ -2168,7 +3175,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2168
3175
|
const boxY = participantStartY - GROUP_PADDING_TOP;
|
|
2169
3176
|
const boxH = PARTICIPANT_BOX_HEIGHT + GROUP_PADDING_TOP + GROUP_PADDING_BOTTOM;
|
|
2170
3177
|
const resolvedGroupColor = group.color ? resolveColor(group.color, palette) : void 0;
|
|
2171
|
-
const fillColor = resolvedGroupColor ?
|
|
3178
|
+
const fillColor = resolvedGroupColor ? mix2(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
|
|
2172
3179
|
const strokeColor = resolvedGroupColor || palette.textMuted;
|
|
2173
3180
|
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));
|
|
2174
3181
|
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);
|
|
@@ -2193,7 +3200,12 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2193
3200
|
...collectMsgIndices(el.children),
|
|
2194
3201
|
...collectMsgIndices(el.elseChildren)
|
|
2195
3202
|
);
|
|
2196
|
-
|
|
3203
|
+
if (el.elseIfBranches) {
|
|
3204
|
+
for (const branch of el.elseIfBranches) {
|
|
3205
|
+
indices.push(...collectMsgIndices(branch.children));
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
} else if (!isSequenceSection(el) && !isSequenceNote(el)) {
|
|
2197
3209
|
const idx = messages.indexOf(el);
|
|
2198
3210
|
if (idx >= 0) indices.push(idx);
|
|
2199
3211
|
}
|
|
@@ -2206,8 +3218,21 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2206
3218
|
for (const el of els) {
|
|
2207
3219
|
if (!isSequenceBlock(el)) continue;
|
|
2208
3220
|
const ifIndices = collectMsgIndices(el.children);
|
|
3221
|
+
const elseIfBranchData = [];
|
|
3222
|
+
if (el.elseIfBranches) {
|
|
3223
|
+
for (const branch of el.elseIfBranches) {
|
|
3224
|
+
elseIfBranchData.push({
|
|
3225
|
+
label: branch.label,
|
|
3226
|
+
indices: collectMsgIndices(branch.children)
|
|
3227
|
+
});
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
2209
3230
|
const elseIndices = collectMsgIndices(el.elseChildren);
|
|
2210
|
-
const allIndices = [
|
|
3231
|
+
const allIndices = [
|
|
3232
|
+
...ifIndices,
|
|
3233
|
+
...elseIfBranchData.flatMap((b) => b.indices),
|
|
3234
|
+
...elseIndices
|
|
3235
|
+
];
|
|
2211
3236
|
if (allIndices.length === 0) continue;
|
|
2212
3237
|
let minStep = Infinity;
|
|
2213
3238
|
let maxStep = -Infinity;
|
|
@@ -2245,6 +3270,32 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2245
3270
|
italic: false,
|
|
2246
3271
|
blockLine: el.lineNumber
|
|
2247
3272
|
});
|
|
3273
|
+
for (const branchData of elseIfBranchData) {
|
|
3274
|
+
if (branchData.indices.length > 0) {
|
|
3275
|
+
let firstBranchStep = Infinity;
|
|
3276
|
+
for (const mi of branchData.indices) {
|
|
3277
|
+
const first = msgToFirstStep.get(mi);
|
|
3278
|
+
if (first !== void 0)
|
|
3279
|
+
firstBranchStep = Math.min(firstBranchStep, first);
|
|
3280
|
+
}
|
|
3281
|
+
if (firstBranchStep < Infinity) {
|
|
3282
|
+
const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
|
|
3283
|
+
deferredLines.push({
|
|
3284
|
+
x1: frameX,
|
|
3285
|
+
y1: dividerY,
|
|
3286
|
+
x2: frameX + frameW,
|
|
3287
|
+
y2: dividerY
|
|
3288
|
+
});
|
|
3289
|
+
deferredLabels.push({
|
|
3290
|
+
x: frameX + 6,
|
|
3291
|
+
y: dividerY + 14,
|
|
3292
|
+
text: `else if ${branchData.label}`,
|
|
3293
|
+
bold: false,
|
|
3294
|
+
italic: true
|
|
3295
|
+
});
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
2248
3299
|
if (elseIndices.length > 0) {
|
|
2249
3300
|
let firstElseStep = Infinity;
|
|
2250
3301
|
for (const mi of elseIndices) {
|
|
@@ -2270,6 +3321,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2270
3321
|
}
|
|
2271
3322
|
}
|
|
2272
3323
|
renderBlockFrames(el.children, depth + 1);
|
|
3324
|
+
if (el.elseIfBranches) {
|
|
3325
|
+
for (const branch of el.elseIfBranches) {
|
|
3326
|
+
renderBlockFrames(branch.children, depth + 1);
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
2273
3329
|
renderBlockFrames(el.elseChildren, depth + 1);
|
|
2274
3330
|
}
|
|
2275
3331
|
};
|
|
@@ -2291,7 +3347,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2291
3347
|
if (msg) coveredLines.push(msg.lineNumber);
|
|
2292
3348
|
}
|
|
2293
3349
|
svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
|
|
2294
|
-
const actFill =
|
|
3350
|
+
const actFill = mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
|
|
2295
3351
|
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");
|
|
2296
3352
|
});
|
|
2297
3353
|
for (const ln of deferredLines) {
|
|
@@ -2417,6 +3473,94 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2417
3473
|
}
|
|
2418
3474
|
}
|
|
2419
3475
|
});
|
|
3476
|
+
const noteFill = isDark ? mix2(palette.surface, palette.bg, 50) : mix2(palette.bg, palette.surface, 15);
|
|
3477
|
+
const findAssociatedStep = (note) => {
|
|
3478
|
+
let bestMsgIndex = -1;
|
|
3479
|
+
let bestLine = -1;
|
|
3480
|
+
for (let mi = 0; mi < messages.length; mi++) {
|
|
3481
|
+
if (messages[mi].lineNumber < note.lineNumber && messages[mi].lineNumber > bestLine && !hiddenMsgIndices.has(mi)) {
|
|
3482
|
+
bestLine = messages[mi].lineNumber;
|
|
3483
|
+
bestMsgIndex = mi;
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
if (bestMsgIndex < 0) return -1;
|
|
3487
|
+
return msgToFirstStep.get(bestMsgIndex) ?? -1;
|
|
3488
|
+
};
|
|
3489
|
+
const renderNoteElements = (els) => {
|
|
3490
|
+
for (const el of els) {
|
|
3491
|
+
if (isSequenceNote(el)) {
|
|
3492
|
+
const px = participantX.get(el.participantId);
|
|
3493
|
+
if (px === void 0) continue;
|
|
3494
|
+
const si = findAssociatedStep(el);
|
|
3495
|
+
if (si < 0) continue;
|
|
3496
|
+
const noteY = stepY(si);
|
|
3497
|
+
const wrappedLines = wrapTextLines(el.text, NOTE_CHARS_PER_LINE);
|
|
3498
|
+
const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
|
|
3499
|
+
const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
|
|
3500
|
+
const noteW = Math.min(
|
|
3501
|
+
NOTE_MAX_W,
|
|
3502
|
+
Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
|
|
3503
|
+
);
|
|
3504
|
+
const isRight = el.position === "right";
|
|
3505
|
+
const noteX = isRight ? px + ACTIVATION_WIDTH + NOTE_GAP : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
|
|
3506
|
+
const noteTopY = noteY + NOTE_OFFSET_BELOW;
|
|
3507
|
+
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));
|
|
3508
|
+
noteG.append("path").attr(
|
|
3509
|
+
"d",
|
|
3510
|
+
[
|
|
3511
|
+
`M ${noteX} ${noteTopY}`,
|
|
3512
|
+
`L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
|
|
3513
|
+
`L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
|
|
3514
|
+
`L ${noteX + noteW} ${noteTopY + noteH}`,
|
|
3515
|
+
`L ${noteX} ${noteTopY + noteH}`,
|
|
3516
|
+
"Z"
|
|
3517
|
+
].join(" ")
|
|
3518
|
+
).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
|
|
3519
|
+
noteG.append("path").attr(
|
|
3520
|
+
"d",
|
|
3521
|
+
[
|
|
3522
|
+
`M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
|
|
3523
|
+
`L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
|
|
3524
|
+
`L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
|
|
3525
|
+
].join(" ")
|
|
3526
|
+
).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
|
|
3527
|
+
wrappedLines.forEach((line3, li) => {
|
|
3528
|
+
const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
|
|
3529
|
+
const isBullet = line3.startsWith("- ");
|
|
3530
|
+
const bulletIndent = isBullet ? 10 : 0;
|
|
3531
|
+
const displayLine = isBullet ? line3.slice(2) : line3;
|
|
3532
|
+
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");
|
|
3533
|
+
if (isBullet) {
|
|
3534
|
+
noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).text("\u2022");
|
|
3535
|
+
}
|
|
3536
|
+
const spans = parseInlineMarkdown(displayLine);
|
|
3537
|
+
for (const span of spans) {
|
|
3538
|
+
if (span.href) {
|
|
3539
|
+
const a = textEl.append("a").attr("href", span.href);
|
|
3540
|
+
a.append("tspan").text(span.text).attr("fill", palette.primary).style("text-decoration", "underline");
|
|
3541
|
+
} else {
|
|
3542
|
+
const tspan = textEl.append("tspan").text(span.text);
|
|
3543
|
+
if (span.bold) tspan.attr("font-weight", "bold");
|
|
3544
|
+
if (span.italic) tspan.attr("font-style", "italic");
|
|
3545
|
+
if (span.code)
|
|
3546
|
+
tspan.attr("font-family", "monospace").attr("font-size", NOTE_FONT_SIZE - 1);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
});
|
|
3550
|
+
} else if (isSequenceBlock(el)) {
|
|
3551
|
+
renderNoteElements(el.children);
|
|
3552
|
+
if (el.elseIfBranches) {
|
|
3553
|
+
for (const branch of el.elseIfBranches) {
|
|
3554
|
+
renderNoteElements(branch.children);
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
renderNoteElements(el.elseChildren);
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
};
|
|
3561
|
+
if (elements && elements.length > 0) {
|
|
3562
|
+
renderNoteElements(elements);
|
|
3563
|
+
}
|
|
2420
3564
|
}
|
|
2421
3565
|
function renderParticipant(svg, participant, cx, cy, palette, isDark) {
|
|
2422
3566
|
const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
|
|
@@ -2458,11 +3602,11 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark) {
|
|
|
2458
3602
|
isActor ? PARTICIPANT_BOX_HEIGHT + 14 : PARTICIPANT_BOX_HEIGHT / 2 + 5
|
|
2459
3603
|
).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 13).attr("font-weight", 500).text(participant.label);
|
|
2460
3604
|
}
|
|
2461
|
-
var
|
|
3605
|
+
var d3Selection2, 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;
|
|
2462
3606
|
var init_renderer = __esm({
|
|
2463
3607
|
"src/sequence/renderer.ts"() {
|
|
2464
3608
|
"use strict";
|
|
2465
|
-
|
|
3609
|
+
d3Selection2 = __toESM(require("d3-selection"), 1);
|
|
2466
3610
|
init_colors();
|
|
2467
3611
|
init_fonts();
|
|
2468
3612
|
init_parser();
|
|
@@ -2470,13 +3614,22 @@ var init_renderer = __esm({
|
|
|
2470
3614
|
PARTICIPANT_BOX_WIDTH = 120;
|
|
2471
3615
|
PARTICIPANT_BOX_HEIGHT = 50;
|
|
2472
3616
|
TOP_MARGIN = 20;
|
|
2473
|
-
|
|
3617
|
+
TITLE_HEIGHT2 = 30;
|
|
2474
3618
|
PARTICIPANT_Y_OFFSET = 10;
|
|
2475
3619
|
SERVICE_BORDER_RADIUS = 10;
|
|
2476
3620
|
MESSAGE_START_OFFSET = 30;
|
|
2477
3621
|
LIFELINE_TAIL = 30;
|
|
2478
3622
|
ARROWHEAD_SIZE = 8;
|
|
2479
|
-
|
|
3623
|
+
NOTE_MAX_W = 200;
|
|
3624
|
+
NOTE_FOLD = 10;
|
|
3625
|
+
NOTE_PAD_H = 8;
|
|
3626
|
+
NOTE_PAD_V = 6;
|
|
3627
|
+
NOTE_FONT_SIZE = 10;
|
|
3628
|
+
NOTE_LINE_H = 14;
|
|
3629
|
+
NOTE_GAP = 15;
|
|
3630
|
+
NOTE_CHAR_W = 6;
|
|
3631
|
+
NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
|
|
3632
|
+
fill = (palette, isDark) => mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
|
|
2480
3633
|
stroke = (palette) => palette.textMuted;
|
|
2481
3634
|
SW = 1.5;
|
|
2482
3635
|
W = PARTICIPANT_BOX_WIDTH;
|
|
@@ -2516,7 +3669,10 @@ __export(index_exports, {
|
|
|
2516
3669
|
hslToHex: () => hslToHex,
|
|
2517
3670
|
inferParticipantType: () => inferParticipantType,
|
|
2518
3671
|
isSequenceBlock: () => isSequenceBlock,
|
|
3672
|
+
isSequenceNote: () => isSequenceNote,
|
|
2519
3673
|
isValidHex: () => isValidHex,
|
|
3674
|
+
layoutGraph: () => layoutGraph,
|
|
3675
|
+
looksLikeFlowchart: () => looksLikeFlowchart,
|
|
2520
3676
|
looksLikeSequence: () => looksLikeSequence,
|
|
2521
3677
|
mute: () => mute,
|
|
2522
3678
|
nord: () => nord,
|
|
@@ -2527,6 +3683,7 @@ __export(index_exports, {
|
|
|
2527
3683
|
parseD3: () => parseD3,
|
|
2528
3684
|
parseDgmoChartType: () => parseDgmoChartType,
|
|
2529
3685
|
parseEChart: () => parseEChart,
|
|
3686
|
+
parseFlowchart: () => parseFlowchart,
|
|
2530
3687
|
parseQuadrant: () => parseQuadrant,
|
|
2531
3688
|
parseSequenceDgmo: () => parseSequenceDgmo,
|
|
2532
3689
|
parseTimelineDate: () => parseTimelineDate,
|
|
@@ -2534,6 +3691,8 @@ __export(index_exports, {
|
|
|
2534
3691
|
renderArcDiagram: () => renderArcDiagram,
|
|
2535
3692
|
renderD3ForExport: () => renderD3ForExport,
|
|
2536
3693
|
renderEChartsForExport: () => renderEChartsForExport,
|
|
3694
|
+
renderFlowchart: () => renderFlowchart,
|
|
3695
|
+
renderFlowchartForExport: () => renderFlowchartForExport,
|
|
2537
3696
|
renderQuadrant: () => renderQuadrant,
|
|
2538
3697
|
renderSequenceDiagram: () => renderSequenceDiagram,
|
|
2539
3698
|
renderSlopeChart: () => renderSlopeChart,
|
|
@@ -2549,51 +3708,7 @@ __export(index_exports, {
|
|
|
2549
3708
|
tokyoNightPalette: () => tokyoNightPalette
|
|
2550
3709
|
});
|
|
2551
3710
|
module.exports = __toCommonJS(index_exports);
|
|
2552
|
-
|
|
2553
|
-
// src/dgmo-router.ts
|
|
2554
|
-
init_parser();
|
|
2555
|
-
var DGMO_CHART_TYPE_MAP = {
|
|
2556
|
-
// Standard charts (via ECharts)
|
|
2557
|
-
bar: "echart",
|
|
2558
|
-
line: "echart",
|
|
2559
|
-
"multi-line": "echart",
|
|
2560
|
-
area: "echart",
|
|
2561
|
-
pie: "echart",
|
|
2562
|
-
doughnut: "echart",
|
|
2563
|
-
radar: "echart",
|
|
2564
|
-
"polar-area": "echart",
|
|
2565
|
-
"bar-stacked": "echart",
|
|
2566
|
-
// ECharts
|
|
2567
|
-
scatter: "echart",
|
|
2568
|
-
sankey: "echart",
|
|
2569
|
-
chord: "echart",
|
|
2570
|
-
function: "echart",
|
|
2571
|
-
heatmap: "echart",
|
|
2572
|
-
funnel: "echart",
|
|
2573
|
-
// D3
|
|
2574
|
-
slope: "d3",
|
|
2575
|
-
wordcloud: "d3",
|
|
2576
|
-
arc: "d3",
|
|
2577
|
-
timeline: "d3",
|
|
2578
|
-
venn: "d3",
|
|
2579
|
-
quadrant: "d3",
|
|
2580
|
-
sequence: "d3"
|
|
2581
|
-
};
|
|
2582
|
-
function getDgmoFramework(chartType) {
|
|
2583
|
-
return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
|
|
2584
|
-
}
|
|
2585
|
-
function parseDgmoChartType(content) {
|
|
2586
|
-
const lines = content.split("\n");
|
|
2587
|
-
for (const line2 of lines) {
|
|
2588
|
-
const trimmed = line2.trim();
|
|
2589
|
-
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
|
|
2590
|
-
continue;
|
|
2591
|
-
const match = trimmed.match(/^chart\s*:\s*(.+)/i);
|
|
2592
|
-
if (match) return match[1].trim().toLowerCase();
|
|
2593
|
-
}
|
|
2594
|
-
if (looksLikeSequence(content)) return "sequence";
|
|
2595
|
-
return null;
|
|
2596
|
-
}
|
|
3711
|
+
init_dgmo_router();
|
|
2597
3712
|
|
|
2598
3713
|
// src/chart.ts
|
|
2599
3714
|
init_colors();
|
|
@@ -2955,7 +4070,7 @@ function parseEChart(content, palette) {
|
|
|
2955
4070
|
function buildEChartsOption(parsed, palette, isDark) {
|
|
2956
4071
|
const textColor = palette.text;
|
|
2957
4072
|
const axisLineColor = palette.border;
|
|
2958
|
-
const gridOpacity = isDark ? 0.7 : 0.
|
|
4073
|
+
const gridOpacity = isDark ? 0.7 : 0.55;
|
|
2959
4074
|
const colors = getSeriesColors(palette);
|
|
2960
4075
|
if (parsed.error) {
|
|
2961
4076
|
return {};
|
|
@@ -3661,7 +4776,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
|
|
|
3661
4776
|
const textColor = palette.text;
|
|
3662
4777
|
const axisLineColor = palette.border;
|
|
3663
4778
|
const splitLineColor = palette.border;
|
|
3664
|
-
const gridOpacity = isDark ? 0.7 : 0.
|
|
4779
|
+
const gridOpacity = isDark ? 0.7 : 0.55;
|
|
3665
4780
|
const colors = getSeriesColors(palette);
|
|
3666
4781
|
const titleConfig = parsed.title ? {
|
|
3667
4782
|
text: parsed.title,
|
|
@@ -4074,8 +5189,8 @@ async function renderEChartsForExport(content, theme, palette) {
|
|
|
4074
5189
|
|
|
4075
5190
|
// src/d3.ts
|
|
4076
5191
|
var d3Scale = __toESM(require("d3-scale"), 1);
|
|
4077
|
-
var
|
|
4078
|
-
var
|
|
5192
|
+
var d3Selection3 = __toESM(require("d3-selection"), 1);
|
|
5193
|
+
var d3Shape2 = __toESM(require("d3-shape"), 1);
|
|
4079
5194
|
var d3Array = __toESM(require("d3-array"), 1);
|
|
4080
5195
|
var import_d3_cloud = __toESM(require("d3-cloud"), 1);
|
|
4081
5196
|
init_fonts();
|
|
@@ -4182,10 +5297,10 @@ function parseD3(content, palette) {
|
|
|
4182
5297
|
let currentArcGroup = null;
|
|
4183
5298
|
let currentTimelineGroup = null;
|
|
4184
5299
|
for (let i = 0; i < lines.length; i++) {
|
|
4185
|
-
const
|
|
5300
|
+
const line3 = lines[i].trim();
|
|
4186
5301
|
const lineNumber = i + 1;
|
|
4187
|
-
if (!
|
|
4188
|
-
const sectionMatch =
|
|
5302
|
+
if (!line3) continue;
|
|
5303
|
+
const sectionMatch = line3.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
|
|
4189
5304
|
if (sectionMatch) {
|
|
4190
5305
|
if (result.type === "arc") {
|
|
4191
5306
|
const name = sectionMatch[1].trim();
|
|
@@ -4200,11 +5315,11 @@ function parseD3(content, palette) {
|
|
|
4200
5315
|
}
|
|
4201
5316
|
continue;
|
|
4202
5317
|
}
|
|
4203
|
-
if (
|
|
5318
|
+
if (line3.startsWith("#") || line3.startsWith("//")) {
|
|
4204
5319
|
continue;
|
|
4205
5320
|
}
|
|
4206
5321
|
if (result.type === "arc") {
|
|
4207
|
-
const linkMatch =
|
|
5322
|
+
const linkMatch = line3.match(
|
|
4208
5323
|
/^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?::\s*(\d+(?:\.\d+)?))?$/
|
|
4209
5324
|
);
|
|
4210
5325
|
if (linkMatch) {
|
|
@@ -4234,7 +5349,7 @@ function parseD3(content, palette) {
|
|
|
4234
5349
|
}
|
|
4235
5350
|
}
|
|
4236
5351
|
if (result.type === "timeline") {
|
|
4237
|
-
const eraMatch =
|
|
5352
|
+
const eraMatch = line3.match(
|
|
4238
5353
|
/^era\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
4239
5354
|
);
|
|
4240
5355
|
if (eraMatch) {
|
|
@@ -4247,7 +5362,7 @@ function parseD3(content, palette) {
|
|
|
4247
5362
|
});
|
|
4248
5363
|
continue;
|
|
4249
5364
|
}
|
|
4250
|
-
const markerMatch =
|
|
5365
|
+
const markerMatch = line3.match(
|
|
4251
5366
|
/^marker\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
4252
5367
|
);
|
|
4253
5368
|
if (markerMatch) {
|
|
@@ -4262,7 +5377,7 @@ function parseD3(content, palette) {
|
|
|
4262
5377
|
}
|
|
4263
5378
|
}
|
|
4264
5379
|
if (result.type === "timeline") {
|
|
4265
|
-
const durationMatch =
|
|
5380
|
+
const durationMatch = line3.match(
|
|
4266
5381
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d+(?:\.\d{1,2})?)([dwmy])\s*:\s*(.+)$/
|
|
4267
5382
|
);
|
|
4268
5383
|
if (durationMatch) {
|
|
@@ -4281,7 +5396,7 @@ function parseD3(content, palette) {
|
|
|
4281
5396
|
});
|
|
4282
5397
|
continue;
|
|
4283
5398
|
}
|
|
4284
|
-
const rangeMatch =
|
|
5399
|
+
const rangeMatch = line3.match(
|
|
4285
5400
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
4286
5401
|
);
|
|
4287
5402
|
if (rangeMatch) {
|
|
@@ -4295,7 +5410,7 @@ function parseD3(content, palette) {
|
|
|
4295
5410
|
});
|
|
4296
5411
|
continue;
|
|
4297
5412
|
}
|
|
4298
|
-
const pointMatch =
|
|
5413
|
+
const pointMatch = line3.match(
|
|
4299
5414
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
4300
5415
|
);
|
|
4301
5416
|
if (pointMatch) {
|
|
@@ -4310,7 +5425,7 @@ function parseD3(content, palette) {
|
|
|
4310
5425
|
}
|
|
4311
5426
|
}
|
|
4312
5427
|
if (result.type === "venn") {
|
|
4313
|
-
const overlapMatch =
|
|
5428
|
+
const overlapMatch = line3.match(
|
|
4314
5429
|
/^(.+?&.+?)\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
|
|
4315
5430
|
);
|
|
4316
5431
|
if (overlapMatch) {
|
|
@@ -4320,7 +5435,7 @@ function parseD3(content, palette) {
|
|
|
4320
5435
|
result.vennOverlaps.push({ sets, size, label, lineNumber });
|
|
4321
5436
|
continue;
|
|
4322
5437
|
}
|
|
4323
|
-
const setMatch =
|
|
5438
|
+
const setMatch = line3.match(
|
|
4324
5439
|
/^(.+?)(?:\(([^)]+)\))?\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
|
|
4325
5440
|
);
|
|
4326
5441
|
if (setMatch) {
|
|
@@ -4333,7 +5448,7 @@ function parseD3(content, palette) {
|
|
|
4333
5448
|
}
|
|
4334
5449
|
}
|
|
4335
5450
|
if (result.type === "quadrant") {
|
|
4336
|
-
const xAxisMatch =
|
|
5451
|
+
const xAxisMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
|
|
4337
5452
|
if (xAxisMatch) {
|
|
4338
5453
|
const parts = xAxisMatch[1].split(",").map((s) => s.trim());
|
|
4339
5454
|
if (parts.length >= 2) {
|
|
@@ -4342,7 +5457,7 @@ function parseD3(content, palette) {
|
|
|
4342
5457
|
}
|
|
4343
5458
|
continue;
|
|
4344
5459
|
}
|
|
4345
|
-
const yAxisMatch =
|
|
5460
|
+
const yAxisMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
|
|
4346
5461
|
if (yAxisMatch) {
|
|
4347
5462
|
const parts = yAxisMatch[1].split(",").map((s) => s.trim());
|
|
4348
5463
|
if (parts.length >= 2) {
|
|
@@ -4352,7 +5467,7 @@ function parseD3(content, palette) {
|
|
|
4352
5467
|
continue;
|
|
4353
5468
|
}
|
|
4354
5469
|
const quadrantLabelRe = /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i;
|
|
4355
|
-
const quadrantMatch =
|
|
5470
|
+
const quadrantMatch = line3.match(quadrantLabelRe);
|
|
4356
5471
|
if (quadrantMatch) {
|
|
4357
5472
|
const position = quadrantMatch[1].toLowerCase();
|
|
4358
5473
|
const labelPart = quadrantMatch[2].trim();
|
|
@@ -4368,7 +5483,7 @@ function parseD3(content, palette) {
|
|
|
4368
5483
|
result.quadrantLabels.bottomRight = label;
|
|
4369
5484
|
continue;
|
|
4370
5485
|
}
|
|
4371
|
-
const pointMatch =
|
|
5486
|
+
const pointMatch = line3.match(
|
|
4372
5487
|
/^(.+?):\s*([0-9]*\.?[0-9]+)\s*,\s*([0-9]*\.?[0-9]+)\s*$/
|
|
4373
5488
|
);
|
|
4374
5489
|
if (pointMatch) {
|
|
@@ -4385,13 +5500,13 @@ function parseD3(content, palette) {
|
|
|
4385
5500
|
continue;
|
|
4386
5501
|
}
|
|
4387
5502
|
}
|
|
4388
|
-
const colonIndex =
|
|
5503
|
+
const colonIndex = line3.indexOf(":");
|
|
4389
5504
|
if (colonIndex !== -1) {
|
|
4390
|
-
const rawKey =
|
|
5505
|
+
const rawKey = line3.substring(0, colonIndex).trim();
|
|
4391
5506
|
const key = rawKey.toLowerCase();
|
|
4392
5507
|
const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
|
|
4393
5508
|
if (key === "chart") {
|
|
4394
|
-
const value =
|
|
5509
|
+
const value = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4395
5510
|
if (value === "slope" || value === "wordcloud" || value === "arc" || value === "timeline" || value === "venn" || value === "quadrant" || value === "sequence") {
|
|
4396
5511
|
result.type = value;
|
|
4397
5512
|
} else {
|
|
@@ -4401,35 +5516,35 @@ function parseD3(content, palette) {
|
|
|
4401
5516
|
continue;
|
|
4402
5517
|
}
|
|
4403
5518
|
if (key === "title") {
|
|
4404
|
-
result.title =
|
|
5519
|
+
result.title = line3.substring(colonIndex + 1).trim();
|
|
4405
5520
|
if (result.type === "quadrant") {
|
|
4406
5521
|
result.quadrantTitleLineNumber = lineNumber;
|
|
4407
5522
|
}
|
|
4408
5523
|
continue;
|
|
4409
5524
|
}
|
|
4410
5525
|
if (key === "orientation") {
|
|
4411
|
-
const v =
|
|
5526
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4412
5527
|
if (v === "horizontal" || v === "vertical") {
|
|
4413
5528
|
result.orientation = v;
|
|
4414
5529
|
}
|
|
4415
5530
|
continue;
|
|
4416
5531
|
}
|
|
4417
5532
|
if (key === "order") {
|
|
4418
|
-
const v =
|
|
5533
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4419
5534
|
if (v === "name" || v === "group" || v === "degree") {
|
|
4420
5535
|
result.arcOrder = v;
|
|
4421
5536
|
}
|
|
4422
5537
|
continue;
|
|
4423
5538
|
}
|
|
4424
5539
|
if (key === "sort") {
|
|
4425
|
-
const v =
|
|
5540
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4426
5541
|
if (v === "time" || v === "group") {
|
|
4427
5542
|
result.timelineSort = v;
|
|
4428
5543
|
}
|
|
4429
5544
|
continue;
|
|
4430
5545
|
}
|
|
4431
5546
|
if (key === "swimlanes") {
|
|
4432
|
-
const v =
|
|
5547
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4433
5548
|
if (v === "on") {
|
|
4434
5549
|
result.timelineSwimlanes = true;
|
|
4435
5550
|
} else if (v === "off") {
|
|
@@ -4438,7 +5553,7 @@ function parseD3(content, palette) {
|
|
|
4438
5553
|
continue;
|
|
4439
5554
|
}
|
|
4440
5555
|
if (key === "values") {
|
|
4441
|
-
const v =
|
|
5556
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4442
5557
|
if (v === "off") {
|
|
4443
5558
|
result.vennShowValues = false;
|
|
4444
5559
|
} else if (v === "on") {
|
|
@@ -4447,21 +5562,21 @@ function parseD3(content, palette) {
|
|
|
4447
5562
|
continue;
|
|
4448
5563
|
}
|
|
4449
5564
|
if (key === "rotate") {
|
|
4450
|
-
const v =
|
|
5565
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4451
5566
|
if (v === "none" || v === "mixed" || v === "angled") {
|
|
4452
5567
|
result.cloudOptions.rotate = v;
|
|
4453
5568
|
}
|
|
4454
5569
|
continue;
|
|
4455
5570
|
}
|
|
4456
5571
|
if (key === "max") {
|
|
4457
|
-
const v = parseInt(
|
|
5572
|
+
const v = parseInt(line3.substring(colonIndex + 1).trim(), 10);
|
|
4458
5573
|
if (!isNaN(v) && v > 0) {
|
|
4459
5574
|
result.cloudOptions.max = v;
|
|
4460
5575
|
}
|
|
4461
5576
|
continue;
|
|
4462
5577
|
}
|
|
4463
5578
|
if (key === "size") {
|
|
4464
|
-
const v =
|
|
5579
|
+
const v = line3.substring(colonIndex + 1).trim();
|
|
4465
5580
|
const parts = v.split(",").map((s) => parseInt(s.trim(), 10));
|
|
4466
5581
|
if (parts.length === 2 && parts.every((n) => !isNaN(n) && n > 0) && parts[0] < parts[1]) {
|
|
4467
5582
|
result.cloudOptions.minSize = parts[0];
|
|
@@ -4471,7 +5586,7 @@ function parseD3(content, palette) {
|
|
|
4471
5586
|
}
|
|
4472
5587
|
const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
|
|
4473
5588
|
const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
|
|
4474
|
-
const valuePart =
|
|
5589
|
+
const valuePart = line3.substring(colonIndex + 1).trim();
|
|
4475
5590
|
const values = valuePart.split(",").map((v) => v.trim());
|
|
4476
5591
|
const numericValues = [];
|
|
4477
5592
|
let allNumeric = true;
|
|
@@ -4502,15 +5617,15 @@ function parseD3(content, palette) {
|
|
|
4502
5617
|
}
|
|
4503
5618
|
}
|
|
4504
5619
|
if (result.type === "wordcloud") {
|
|
4505
|
-
if (colonIndex === -1 && !
|
|
4506
|
-
result.words.push({ text:
|
|
5620
|
+
if (colonIndex === -1 && !line3.includes(" ")) {
|
|
5621
|
+
result.words.push({ text: line3, weight: 10, lineNumber });
|
|
4507
5622
|
} else {
|
|
4508
|
-
freeformLines.push(
|
|
5623
|
+
freeformLines.push(line3);
|
|
4509
5624
|
}
|
|
4510
5625
|
continue;
|
|
4511
5626
|
}
|
|
4512
|
-
if (result.periods.length === 0 &&
|
|
4513
|
-
const periods =
|
|
5627
|
+
if (result.periods.length === 0 && line3.includes(",") && !line3.includes(":")) {
|
|
5628
|
+
const periods = line3.split(",").map((p) => p.trim()).filter(Boolean);
|
|
4514
5629
|
if (periods.length >= 2) {
|
|
4515
5630
|
result.periods = periods;
|
|
4516
5631
|
continue;
|
|
@@ -4733,7 +5848,7 @@ var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
|
|
|
4733
5848
|
var SLOPE_LABEL_FONT_SIZE = 14;
|
|
4734
5849
|
var SLOPE_CHAR_WIDTH = 8;
|
|
4735
5850
|
function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
4736
|
-
|
|
5851
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
4737
5852
|
const { periods, data, title } = parsed;
|
|
4738
5853
|
if (data.length === 0 || periods.length < 2) return;
|
|
4739
5854
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -4760,7 +5875,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4760
5875
|
const valuePadding = (maxVal - minVal) * 0.1 || 1;
|
|
4761
5876
|
const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
|
|
4762
5877
|
const xScale = d3Scale.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
|
|
4763
|
-
const svg =
|
|
5878
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
4764
5879
|
const g = svg.append("g").attr("transform", `translate(${SLOPE_MARGIN.left},${SLOPE_MARGIN.top})`);
|
|
4765
5880
|
const tooltip = createTooltip(container, palette, isDark);
|
|
4766
5881
|
if (title) {
|
|
@@ -4771,7 +5886,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4771
5886
|
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);
|
|
4772
5887
|
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");
|
|
4773
5888
|
}
|
|
4774
|
-
const lineGen =
|
|
5889
|
+
const lineGen = d3Shape2.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
|
|
4775
5890
|
data.forEach((item, idx) => {
|
|
4776
5891
|
const color = item.color ?? colors[idx % colors.length];
|
|
4777
5892
|
const firstVal = item.values[0];
|
|
@@ -4834,11 +5949,11 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4834
5949
|
const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
|
|
4835
5950
|
const totalHeight = (lines.length - 1) * lineHeight;
|
|
4836
5951
|
const startDy = -totalHeight / 2;
|
|
4837
|
-
lines.forEach((
|
|
5952
|
+
lines.forEach((line3, li) => {
|
|
4838
5953
|
labelEl.append("tspan").attr("x", lastX + 10).attr(
|
|
4839
5954
|
"dy",
|
|
4840
5955
|
li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
|
|
4841
|
-
).text(
|
|
5956
|
+
).text(line3);
|
|
4842
5957
|
});
|
|
4843
5958
|
}
|
|
4844
5959
|
});
|
|
@@ -4933,7 +6048,7 @@ function orderArcNodes(links, order, groups) {
|
|
|
4933
6048
|
}
|
|
4934
6049
|
var ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
|
|
4935
6050
|
function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
4936
|
-
|
|
6051
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
4937
6052
|
const { links, title, orientation, arcOrder, arcNodeGroups } = parsed;
|
|
4938
6053
|
if (links.length === 0) return;
|
|
4939
6054
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -4970,7 +6085,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4970
6085
|
const values = links.map((l) => l.value);
|
|
4971
6086
|
const [minVal, maxVal] = d3Array.extent(values);
|
|
4972
6087
|
const strokeScale = d3Scale.scaleLinear().domain([minVal, maxVal]).range([1.5, 6]);
|
|
4973
|
-
const svg =
|
|
6088
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
4974
6089
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
4975
6090
|
if (title) {
|
|
4976
6091
|
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);
|
|
@@ -4985,14 +6100,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4985
6100
|
function handleMouseEnter(hovered) {
|
|
4986
6101
|
const connected = neighbors.get(hovered);
|
|
4987
6102
|
g.selectAll(".arc-link").each(function() {
|
|
4988
|
-
const el =
|
|
6103
|
+
const el = d3Selection3.select(this);
|
|
4989
6104
|
const src = el.attr("data-source");
|
|
4990
6105
|
const tgt = el.attr("data-target");
|
|
4991
6106
|
const isRelated = src === hovered || tgt === hovered;
|
|
4992
6107
|
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
4993
6108
|
});
|
|
4994
6109
|
g.selectAll(".arc-node").each(function() {
|
|
4995
|
-
const el =
|
|
6110
|
+
const el = d3Selection3.select(this);
|
|
4996
6111
|
const name = el.attr("data-node");
|
|
4997
6112
|
const isRelated = name === hovered || connected.has(name);
|
|
4998
6113
|
el.attr("opacity", isRelated ? 1 : FADE_OPACITY);
|
|
@@ -5017,23 +6132,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
5017
6132
|
const members = groupNodeSets.get(groupName);
|
|
5018
6133
|
if (!members) return;
|
|
5019
6134
|
g.selectAll(".arc-link").each(function() {
|
|
5020
|
-
const el =
|
|
6135
|
+
const el = d3Selection3.select(this);
|
|
5021
6136
|
const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
|
|
5022
6137
|
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
5023
6138
|
});
|
|
5024
6139
|
g.selectAll(".arc-node").each(function() {
|
|
5025
|
-
const el =
|
|
6140
|
+
const el = d3Selection3.select(this);
|
|
5026
6141
|
el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY);
|
|
5027
6142
|
});
|
|
5028
6143
|
g.selectAll(".arc-group-band").each(function() {
|
|
5029
|
-
const el =
|
|
6144
|
+
const el = d3Selection3.select(this);
|
|
5030
6145
|
el.attr(
|
|
5031
6146
|
"fill-opacity",
|
|
5032
6147
|
el.attr("data-group") === groupName ? 0.18 : 0.03
|
|
5033
6148
|
);
|
|
5034
6149
|
});
|
|
5035
6150
|
g.selectAll(".arc-group-label").each(function() {
|
|
5036
|
-
const el =
|
|
6151
|
+
const el = d3Selection3.select(this);
|
|
5037
6152
|
el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
|
|
5038
6153
|
});
|
|
5039
6154
|
}
|
|
@@ -5236,7 +6351,14 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
|
|
|
5236
6351
|
const firstYear = Math.ceil(domainMin);
|
|
5237
6352
|
const lastYear = Math.floor(domainMax);
|
|
5238
6353
|
if (lastYear >= firstYear + 1) {
|
|
5239
|
-
|
|
6354
|
+
const yearSpan = lastYear - firstYear;
|
|
6355
|
+
let step = 1;
|
|
6356
|
+
if (yearSpan > 80) step = 20;
|
|
6357
|
+
else if (yearSpan > 40) step = 10;
|
|
6358
|
+
else if (yearSpan > 20) step = 5;
|
|
6359
|
+
else if (yearSpan > 10) step = 2;
|
|
6360
|
+
const alignedFirst = Math.ceil(firstYear / step) * step;
|
|
6361
|
+
for (let y = alignedFirst; y <= lastYear; y += step) {
|
|
5240
6362
|
ticks.push({ pos: scale(y), label: String(y) });
|
|
5241
6363
|
}
|
|
5242
6364
|
} else if (span > 0.25) {
|
|
@@ -5336,7 +6458,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
|
|
|
5336
6458
|
function hideEventDatesOnScale(g) {
|
|
5337
6459
|
g.selectAll(".tl-event-date").remove();
|
|
5338
6460
|
g.selectAll(".tl-scale-tick").each(function() {
|
|
5339
|
-
const el =
|
|
6461
|
+
const el = d3Selection3.select(this);
|
|
5340
6462
|
const isDashed = el.attr("stroke-dasharray");
|
|
5341
6463
|
el.attr("opacity", isDashed ? 0.15 : 0.4);
|
|
5342
6464
|
});
|
|
@@ -5394,7 +6516,7 @@ function buildEraTooltipHtml(era) {
|
|
|
5394
6516
|
return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
|
|
5395
6517
|
}
|
|
5396
6518
|
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
5397
|
-
|
|
6519
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
5398
6520
|
const {
|
|
5399
6521
|
timelineEvents,
|
|
5400
6522
|
timelineGroups,
|
|
@@ -5446,13 +6568,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5446
6568
|
const FADE_OPACITY = 0.1;
|
|
5447
6569
|
function fadeToGroup(g, groupName) {
|
|
5448
6570
|
g.selectAll(".tl-event").each(function() {
|
|
5449
|
-
const el =
|
|
6571
|
+
const el = d3Selection3.select(this);
|
|
5450
6572
|
const evGroup = el.attr("data-group");
|
|
5451
6573
|
el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY);
|
|
5452
6574
|
});
|
|
5453
6575
|
g.selectAll(".tl-legend-item, .tl-lane-header").each(
|
|
5454
6576
|
function() {
|
|
5455
|
-
const el =
|
|
6577
|
+
const el = d3Selection3.select(this);
|
|
5456
6578
|
const name = el.attr("data-group");
|
|
5457
6579
|
el.attr("opacity", name === groupName ? 1 : FADE_OPACITY);
|
|
5458
6580
|
}
|
|
@@ -5464,7 +6586,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5464
6586
|
}
|
|
5465
6587
|
function fadeToEra(g, eraStart, eraEnd) {
|
|
5466
6588
|
g.selectAll(".tl-event").each(function() {
|
|
5467
|
-
const el =
|
|
6589
|
+
const el = d3Selection3.select(this);
|
|
5468
6590
|
const date = parseFloat(el.attr("data-date"));
|
|
5469
6591
|
const endDate = el.attr("data-end-date");
|
|
5470
6592
|
const evEnd = endDate ? parseFloat(endDate) : date;
|
|
@@ -5476,14 +6598,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5476
6598
|
FADE_OPACITY
|
|
5477
6599
|
);
|
|
5478
6600
|
g.selectAll(".tl-era").each(function() {
|
|
5479
|
-
const el =
|
|
6601
|
+
const el = d3Selection3.select(this);
|
|
5480
6602
|
const s = parseFloat(el.attr("data-era-start"));
|
|
5481
6603
|
const e = parseFloat(el.attr("data-era-end"));
|
|
5482
6604
|
const isSelf = s === eraStart && e === eraEnd;
|
|
5483
6605
|
el.attr("opacity", isSelf ? 1 : FADE_OPACITY);
|
|
5484
6606
|
});
|
|
5485
6607
|
g.selectAll(".tl-marker").each(function() {
|
|
5486
|
-
const el =
|
|
6608
|
+
const el = d3Selection3.select(this);
|
|
5487
6609
|
const date = parseFloat(el.attr("data-marker-date"));
|
|
5488
6610
|
const inside = date >= eraStart && date <= eraEnd;
|
|
5489
6611
|
el.attr("opacity", inside ? 1 : FADE_OPACITY);
|
|
@@ -5515,7 +6637,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5515
6637
|
const innerHeight = height - margin.top - margin.bottom;
|
|
5516
6638
|
const laneWidth = innerWidth / laneCount;
|
|
5517
6639
|
const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
|
|
5518
|
-
const svg =
|
|
6640
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5519
6641
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5520
6642
|
if (title) {
|
|
5521
6643
|
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);
|
|
@@ -5609,7 +6731,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5609
6731
|
const axisX = 20;
|
|
5610
6732
|
const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
|
|
5611
6733
|
const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
|
|
5612
|
-
const svg =
|
|
6734
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5613
6735
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5614
6736
|
if (title) {
|
|
5615
6737
|
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);
|
|
@@ -5732,7 +6854,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5732
6854
|
const totalGaps = (lanes.length - 1) * GROUP_GAP;
|
|
5733
6855
|
const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
|
|
5734
6856
|
const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
5735
|
-
const svg =
|
|
6857
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5736
6858
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5737
6859
|
if (title) {
|
|
5738
6860
|
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);
|
|
@@ -5836,7 +6958,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5836
6958
|
if (ev.uncertain) {
|
|
5837
6959
|
const gradientId = `uncertain-${ev.lineNumber}`;
|
|
5838
6960
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
5839
|
-
|
|
6961
|
+
d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
|
|
5840
6962
|
{ offset: "0%", opacity: 1 },
|
|
5841
6963
|
{ offset: "80%", opacity: 1 },
|
|
5842
6964
|
{ offset: "100%", opacity: 0 }
|
|
@@ -5877,7 +6999,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5877
6999
|
const innerHeight = height - margin.top - margin.bottom;
|
|
5878
7000
|
const rowH = Math.min(28, innerHeight / sorted.length);
|
|
5879
7001
|
const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
5880
|
-
const svg =
|
|
7002
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5881
7003
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5882
7004
|
if (title) {
|
|
5883
7005
|
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);
|
|
@@ -5975,7 +7097,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5975
7097
|
if (ev.uncertain) {
|
|
5976
7098
|
const gradientId = `uncertain-ts-${ev.lineNumber}`;
|
|
5977
7099
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
5978
|
-
|
|
7100
|
+
d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
|
|
5979
7101
|
{ offset: "0%", opacity: 1 },
|
|
5980
7102
|
{ offset: "80%", opacity: 1 },
|
|
5981
7103
|
{ offset: "100%", opacity: 0 }
|
|
@@ -6008,7 +7130,7 @@ function getRotateFn(mode) {
|
|
|
6008
7130
|
return () => 0;
|
|
6009
7131
|
}
|
|
6010
7132
|
function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
6011
|
-
|
|
7133
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6012
7134
|
const { words, title, cloudOptions } = parsed;
|
|
6013
7135
|
if (words.length === 0) return;
|
|
6014
7136
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -6029,7 +7151,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
|
|
|
6029
7151
|
return minSize + t * (maxSize - minSize);
|
|
6030
7152
|
};
|
|
6031
7153
|
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
6032
|
-
const svg =
|
|
7154
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6033
7155
|
if (title) {
|
|
6034
7156
|
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);
|
|
6035
7157
|
}
|
|
@@ -6052,7 +7174,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
|
|
|
6052
7174
|
}
|
|
6053
7175
|
function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
|
|
6054
7176
|
return new Promise((resolve) => {
|
|
6055
|
-
|
|
7177
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6056
7178
|
const { words, title, cloudOptions } = parsed;
|
|
6057
7179
|
if (words.length === 0) {
|
|
6058
7180
|
resolve();
|
|
@@ -6079,7 +7201,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
|
|
|
6079
7201
|
return minSize + t * (maxSize - minSize);
|
|
6080
7202
|
};
|
|
6081
7203
|
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
6082
|
-
const svg =
|
|
7204
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6083
7205
|
if (title) {
|
|
6084
7206
|
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);
|
|
6085
7207
|
}
|
|
@@ -6223,7 +7345,7 @@ function circlePathD(cx, cy, r) {
|
|
|
6223
7345
|
return `M${cx - r},${cy} A${r},${r} 0 1,0 ${cx + r},${cy} A${r},${r} 0 1,0 ${cx - r},${cy} Z`;
|
|
6224
7346
|
}
|
|
6225
7347
|
function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
6226
|
-
|
|
7348
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6227
7349
|
const { vennSets, vennOverlaps, vennShowValues, title } = parsed;
|
|
6228
7350
|
if (vennSets.length < 2) return;
|
|
6229
7351
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -6288,7 +7410,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
|
|
|
6288
7410
|
const setColors = vennSets.map(
|
|
6289
7411
|
(s, i) => s.color ?? colors[i % colors.length]
|
|
6290
7412
|
);
|
|
6291
|
-
const svg =
|
|
7413
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6292
7414
|
const tooltip = createTooltip(container, palette, isDark);
|
|
6293
7415
|
if (title) {
|
|
6294
7416
|
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);
|
|
@@ -6434,7 +7556,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
|
|
|
6434
7556
|
});
|
|
6435
7557
|
}
|
|
6436
7558
|
function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
6437
|
-
|
|
7559
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6438
7560
|
const {
|
|
6439
7561
|
title,
|
|
6440
7562
|
quadrantLabels,
|
|
@@ -6466,7 +7588,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6466
7588
|
const chartHeight = height - margin.top - margin.bottom;
|
|
6467
7589
|
const xScale = d3Scale.scaleLinear().domain([0, 1]).range([0, chartWidth]);
|
|
6468
7590
|
const yScale = d3Scale.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
|
|
6469
|
-
const svg =
|
|
7591
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6470
7592
|
const tooltip = createTooltip(container, palette, isDark);
|
|
6471
7593
|
if (title) {
|
|
6472
7594
|
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(
|
|
@@ -6475,9 +7597,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6475
7597
|
).text(title);
|
|
6476
7598
|
if (onClickItem && quadrantTitleLineNumber) {
|
|
6477
7599
|
titleText.on("click", () => onClickItem(quadrantTitleLineNumber)).on("mouseenter", function() {
|
|
6478
|
-
|
|
7600
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6479
7601
|
}).on("mouseleave", function() {
|
|
6480
|
-
|
|
7602
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6481
7603
|
});
|
|
6482
7604
|
}
|
|
6483
7605
|
}
|
|
@@ -6560,9 +7682,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6560
7682
|
quadrantLabelTexts.on("click", (_, d) => {
|
|
6561
7683
|
if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
|
|
6562
7684
|
}).on("mouseenter", function() {
|
|
6563
|
-
|
|
7685
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6564
7686
|
}).on("mouseleave", function() {
|
|
6565
|
-
|
|
7687
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6566
7688
|
});
|
|
6567
7689
|
}
|
|
6568
7690
|
if (quadrantXAxis) {
|
|
@@ -6577,9 +7699,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6577
7699
|
if (onClickItem && quadrantXAxisLineNumber) {
|
|
6578
7700
|
[xLowLabel, xHighLabel].forEach((label) => {
|
|
6579
7701
|
label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
|
|
6580
|
-
|
|
7702
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6581
7703
|
}).on("mouseleave", function() {
|
|
6582
|
-
|
|
7704
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6583
7705
|
});
|
|
6584
7706
|
});
|
|
6585
7707
|
}
|
|
@@ -6598,9 +7720,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6598
7720
|
if (onClickItem && quadrantYAxisLineNumber) {
|
|
6599
7721
|
[yLowLabel, yHighLabel].forEach((label) => {
|
|
6600
7722
|
label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
|
|
6601
|
-
|
|
7723
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6602
7724
|
}).on("mouseleave", function() {
|
|
6603
|
-
|
|
7725
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6604
7726
|
});
|
|
6605
7727
|
});
|
|
6606
7728
|
}
|
|
@@ -6648,7 +7770,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6648
7770
|
pointsG.selectAll("g.point-group").each(function(_2, i) {
|
|
6649
7771
|
const pt = quadrantPoints[i];
|
|
6650
7772
|
const ptQuad = getPointQuadrant(pt.x, pt.y);
|
|
6651
|
-
|
|
7773
|
+
d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
|
|
6652
7774
|
});
|
|
6653
7775
|
}).on("mouseleave", () => {
|
|
6654
7776
|
quadrantRects.attr("opacity", 1);
|
|
@@ -6663,6 +7785,43 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6663
7785
|
var EXPORT_WIDTH = 1200;
|
|
6664
7786
|
var EXPORT_HEIGHT = 800;
|
|
6665
7787
|
async function renderD3ForExport(content, theme, palette) {
|
|
7788
|
+
const { parseDgmoChartType: parseDgmoChartType2 } = await Promise.resolve().then(() => (init_dgmo_router(), dgmo_router_exports));
|
|
7789
|
+
const detectedType = parseDgmoChartType2(content);
|
|
7790
|
+
if (detectedType === "flowchart") {
|
|
7791
|
+
const { parseFlowchart: parseFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_parser(), flowchart_parser_exports));
|
|
7792
|
+
const { layoutGraph: layoutGraph2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
|
|
7793
|
+
const { renderFlowchart: renderFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_renderer(), flowchart_renderer_exports));
|
|
7794
|
+
const isDark2 = theme === "dark";
|
|
7795
|
+
const { getPalette: getPalette3 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
|
|
7796
|
+
const effectivePalette2 = palette ?? (isDark2 ? getPalette3("nord").dark : getPalette3("nord").light);
|
|
7797
|
+
const fcParsed = parseFlowchart2(content, effectivePalette2);
|
|
7798
|
+
if (fcParsed.error || fcParsed.nodes.length === 0) return "";
|
|
7799
|
+
const layout = layoutGraph2(fcParsed);
|
|
7800
|
+
const container2 = document.createElement("div");
|
|
7801
|
+
container2.style.width = `${EXPORT_WIDTH}px`;
|
|
7802
|
+
container2.style.height = `${EXPORT_HEIGHT}px`;
|
|
7803
|
+
container2.style.position = "absolute";
|
|
7804
|
+
container2.style.left = "-9999px";
|
|
7805
|
+
document.body.appendChild(container2);
|
|
7806
|
+
try {
|
|
7807
|
+
renderFlowchart2(container2, fcParsed, layout, effectivePalette2, isDark2, void 0, {
|
|
7808
|
+
width: EXPORT_WIDTH,
|
|
7809
|
+
height: EXPORT_HEIGHT
|
|
7810
|
+
});
|
|
7811
|
+
const svgEl = container2.querySelector("svg");
|
|
7812
|
+
if (!svgEl) return "";
|
|
7813
|
+
if (theme === "transparent") {
|
|
7814
|
+
svgEl.style.background = "none";
|
|
7815
|
+
} else if (!svgEl.style.background) {
|
|
7816
|
+
svgEl.style.background = effectivePalette2.bg;
|
|
7817
|
+
}
|
|
7818
|
+
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
7819
|
+
svgEl.style.fontFamily = FONT_FAMILY;
|
|
7820
|
+
return svgEl.outerHTML;
|
|
7821
|
+
} finally {
|
|
7822
|
+
document.body.removeChild(container2);
|
|
7823
|
+
}
|
|
7824
|
+
}
|
|
6666
7825
|
const parsed = parseD3(content, palette);
|
|
6667
7826
|
if (parsed.error && parsed.type !== "sequence") {
|
|
6668
7827
|
const looksLikeSequence2 = /->|~>|<-/.test(content);
|
|
@@ -6757,17 +7916,17 @@ function parseQuadrant(content) {
|
|
|
6757
7916
|
};
|
|
6758
7917
|
const lines = content.split("\n");
|
|
6759
7918
|
for (let i = 0; i < lines.length; i++) {
|
|
6760
|
-
const
|
|
7919
|
+
const line3 = lines[i].trim();
|
|
6761
7920
|
const lineNumber = i + 1;
|
|
6762
|
-
if (!
|
|
6763
|
-
if (/^chart\s*:/i.test(
|
|
6764
|
-
const titleMatch =
|
|
7921
|
+
if (!line3 || line3.startsWith("#") || line3.startsWith("//")) continue;
|
|
7922
|
+
if (/^chart\s*:/i.test(line3)) continue;
|
|
7923
|
+
const titleMatch = line3.match(/^title\s*:\s*(.+)/i);
|
|
6765
7924
|
if (titleMatch) {
|
|
6766
7925
|
result.title = titleMatch[1].trim();
|
|
6767
7926
|
result.titleLineNumber = lineNumber;
|
|
6768
7927
|
continue;
|
|
6769
7928
|
}
|
|
6770
|
-
const xMatch =
|
|
7929
|
+
const xMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
|
|
6771
7930
|
if (xMatch) {
|
|
6772
7931
|
const parts = xMatch[1].split(",").map((s) => s.trim());
|
|
6773
7932
|
if (parts.length >= 2) {
|
|
@@ -6776,7 +7935,7 @@ function parseQuadrant(content) {
|
|
|
6776
7935
|
}
|
|
6777
7936
|
continue;
|
|
6778
7937
|
}
|
|
6779
|
-
const yMatch =
|
|
7938
|
+
const yMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
|
|
6780
7939
|
if (yMatch) {
|
|
6781
7940
|
const parts = yMatch[1].split(",").map((s) => s.trim());
|
|
6782
7941
|
if (parts.length >= 2) {
|
|
@@ -6785,7 +7944,7 @@ function parseQuadrant(content) {
|
|
|
6785
7944
|
}
|
|
6786
7945
|
continue;
|
|
6787
7946
|
}
|
|
6788
|
-
const posMatch =
|
|
7947
|
+
const posMatch = line3.match(
|
|
6789
7948
|
/^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i
|
|
6790
7949
|
);
|
|
6791
7950
|
if (posMatch) {
|
|
@@ -6806,7 +7965,7 @@ function parseQuadrant(content) {
|
|
|
6806
7965
|
}
|
|
6807
7966
|
continue;
|
|
6808
7967
|
}
|
|
6809
|
-
const pointMatch =
|
|
7968
|
+
const pointMatch = line3.match(DATA_POINT_RE);
|
|
6810
7969
|
if (pointMatch) {
|
|
6811
7970
|
const key = pointMatch[1].trim().toLowerCase();
|
|
6812
7971
|
if (!QUADRANT_POSITIONS.has(key)) {
|
|
@@ -6880,6 +8039,9 @@ function buildMermaidQuadrant(parsed, options = {}) {
|
|
|
6880
8039
|
}
|
|
6881
8040
|
|
|
6882
8041
|
// src/index.ts
|
|
8042
|
+
init_flowchart_parser();
|
|
8043
|
+
init_layout();
|
|
8044
|
+
init_flowchart_renderer();
|
|
6883
8045
|
init_renderer();
|
|
6884
8046
|
init_colors();
|
|
6885
8047
|
init_palettes();
|
|
@@ -6914,7 +8076,10 @@ init_palettes();
|
|
|
6914
8076
|
hslToHex,
|
|
6915
8077
|
inferParticipantType,
|
|
6916
8078
|
isSequenceBlock,
|
|
8079
|
+
isSequenceNote,
|
|
6917
8080
|
isValidHex,
|
|
8081
|
+
layoutGraph,
|
|
8082
|
+
looksLikeFlowchart,
|
|
6918
8083
|
looksLikeSequence,
|
|
6919
8084
|
mute,
|
|
6920
8085
|
nord,
|
|
@@ -6925,6 +8090,7 @@ init_palettes();
|
|
|
6925
8090
|
parseD3,
|
|
6926
8091
|
parseDgmoChartType,
|
|
6927
8092
|
parseEChart,
|
|
8093
|
+
parseFlowchart,
|
|
6928
8094
|
parseQuadrant,
|
|
6929
8095
|
parseSequenceDgmo,
|
|
6930
8096
|
parseTimelineDate,
|
|
@@ -6932,6 +8098,8 @@ init_palettes();
|
|
|
6932
8098
|
renderArcDiagram,
|
|
6933
8099
|
renderD3ForExport,
|
|
6934
8100
|
renderEChartsForExport,
|
|
8101
|
+
renderFlowchart,
|
|
8102
|
+
renderFlowchartForExport,
|
|
6935
8103
|
renderQuadrant,
|
|
6936
8104
|
renderSequenceDiagram,
|
|
6937
8105
|
renderSlopeChart,
|