@diagrammo/dgmo 0.2.5 → 0.2.7
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 +92 -86
- package/dist/index.cjs +1337 -194
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -2
- package/dist/index.d.ts +107 -2
- package/dist/index.js +1332 -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 +229 -37
- package/src/sequence/renderer.ts +310 -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,105 @@ 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
|
+
}
|
|
459
545
|
}
|
|
460
546
|
continue;
|
|
461
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) {
|
|
554
|
+
result.error = `Line ${lineNumber}: note requires a preceding message`;
|
|
555
|
+
return result;
|
|
556
|
+
}
|
|
557
|
+
noteParticipant = lastMsgFrom;
|
|
558
|
+
}
|
|
559
|
+
if (!result.participants.some((p) => p.id === noteParticipant)) {
|
|
560
|
+
result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
|
|
561
|
+
return result;
|
|
562
|
+
}
|
|
563
|
+
const note = {
|
|
564
|
+
kind: "note",
|
|
565
|
+
text: noteSingleMatch[3].trim(),
|
|
566
|
+
position: notePosition,
|
|
567
|
+
participantId: noteParticipant,
|
|
568
|
+
lineNumber
|
|
569
|
+
};
|
|
570
|
+
currentContainer().push(note);
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const noteMultiMatch = trimmed.match(NOTE_MULTI);
|
|
574
|
+
if (noteMultiMatch) {
|
|
575
|
+
const notePosition = noteMultiMatch[1]?.toLowerCase() || "right";
|
|
576
|
+
let noteParticipant = noteMultiMatch[2] || null;
|
|
577
|
+
if (!noteParticipant) {
|
|
578
|
+
if (!lastMsgFrom) {
|
|
579
|
+
result.error = `Line ${lineNumber}: note requires a preceding message`;
|
|
580
|
+
return result;
|
|
581
|
+
}
|
|
582
|
+
noteParticipant = lastMsgFrom;
|
|
583
|
+
}
|
|
584
|
+
if (!result.participants.some((p) => p.id === noteParticipant)) {
|
|
585
|
+
result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
|
|
586
|
+
return result;
|
|
587
|
+
}
|
|
588
|
+
const noteLines = [];
|
|
589
|
+
while (i + 1 < lines.length) {
|
|
590
|
+
const nextRaw = lines[i + 1];
|
|
591
|
+
const nextTrimmed = nextRaw.trim();
|
|
592
|
+
if (!nextTrimmed) break;
|
|
593
|
+
const nextIndent = measureIndent(nextRaw);
|
|
594
|
+
if (nextIndent <= indent) break;
|
|
595
|
+
noteLines.push(nextTrimmed);
|
|
596
|
+
i++;
|
|
597
|
+
}
|
|
598
|
+
if (noteLines.length === 0) {
|
|
599
|
+
result.error = `Line ${lineNumber}: multi-line note has no content \u2014 add indented lines or use 'note: text'`;
|
|
600
|
+
return result;
|
|
601
|
+
}
|
|
602
|
+
const note = {
|
|
603
|
+
kind: "note",
|
|
604
|
+
text: noteLines.join("\n"),
|
|
605
|
+
position: notePosition,
|
|
606
|
+
participantId: noteParticipant,
|
|
607
|
+
lineNumber
|
|
608
|
+
};
|
|
609
|
+
currentContainer().push(note);
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
462
612
|
}
|
|
463
613
|
if (!hasExplicitChart && result.messages.length === 0) {
|
|
464
|
-
const hasArrows = lines.some((
|
|
614
|
+
const hasArrows = lines.some((line3) => ARROW_PATTERN.test(line3.trim()));
|
|
465
615
|
if (!hasArrows) {
|
|
466
616
|
result.error = 'No "chart: sequence" header and no sequence content detected';
|
|
467
617
|
return result;
|
|
@@ -472,13 +622,13 @@ function parseSequenceDgmo(content) {
|
|
|
472
622
|
function looksLikeSequence(content) {
|
|
473
623
|
if (!content) return false;
|
|
474
624
|
const lines = content.split("\n");
|
|
475
|
-
return lines.some((
|
|
476
|
-
const trimmed =
|
|
477
|
-
if (trimmed.startsWith("
|
|
625
|
+
return lines.some((line3) => {
|
|
626
|
+
const trimmed = line3.trim();
|
|
627
|
+
if (trimmed.startsWith("//")) return false;
|
|
478
628
|
return ARROW_PATTERN.test(trimmed);
|
|
479
629
|
});
|
|
480
630
|
}
|
|
481
|
-
var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN;
|
|
631
|
+
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
632
|
var init_parser = __esm({
|
|
483
633
|
"src/sequence/parser.ts"() {
|
|
484
634
|
"use strict";
|
|
@@ -496,11 +646,13 @@ var init_parser = __esm({
|
|
|
496
646
|
]);
|
|
497
647
|
IS_A_PATTERN = /^(\S+)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
|
|
498
648
|
POSITION_ONLY_PATTERN = /^(\S+)\s+position\s+(-?\d+)$/i;
|
|
499
|
-
GROUP_HEADING_PATTERN = /^##\s+(
|
|
500
|
-
SECTION_PATTERN = /^==\s+(.+?)
|
|
649
|
+
GROUP_HEADING_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
|
|
650
|
+
SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
|
|
501
651
|
ARROW_PATTERN = /\S+\s*(?:->|~>)\s*\S+/;
|
|
502
652
|
ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
|
|
503
653
|
UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
|
|
654
|
+
NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
|
|
655
|
+
NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*$/i;
|
|
504
656
|
}
|
|
505
657
|
});
|
|
506
658
|
|
|
@@ -579,6 +731,369 @@ var init_colors = __esm({
|
|
|
579
731
|
}
|
|
580
732
|
});
|
|
581
733
|
|
|
734
|
+
// src/graph/flowchart-parser.ts
|
|
735
|
+
var flowchart_parser_exports = {};
|
|
736
|
+
__export(flowchart_parser_exports, {
|
|
737
|
+
looksLikeFlowchart: () => looksLikeFlowchart,
|
|
738
|
+
parseFlowchart: () => parseFlowchart
|
|
739
|
+
});
|
|
740
|
+
function measureIndent2(line3) {
|
|
741
|
+
let indent = 0;
|
|
742
|
+
for (const ch of line3) {
|
|
743
|
+
if (ch === " ") indent++;
|
|
744
|
+
else if (ch === " ") indent += 4;
|
|
745
|
+
else break;
|
|
746
|
+
}
|
|
747
|
+
return indent;
|
|
748
|
+
}
|
|
749
|
+
function nodeId(shape, label) {
|
|
750
|
+
return `${shape}:${label.toLowerCase().trim()}`;
|
|
751
|
+
}
|
|
752
|
+
function extractColor(label, palette) {
|
|
753
|
+
const m = label.match(COLOR_SUFFIX_RE);
|
|
754
|
+
if (!m) return { label };
|
|
755
|
+
const colorName = m[1].trim();
|
|
756
|
+
return {
|
|
757
|
+
label: label.substring(0, m.index).trim(),
|
|
758
|
+
color: resolveColor(colorName, palette)
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
function parseNodeRef(text, palette) {
|
|
762
|
+
const t = text.trim();
|
|
763
|
+
if (!t) return null;
|
|
764
|
+
let m = t.match(/^\[\[([^\]]+)\]\]$/);
|
|
765
|
+
if (m) {
|
|
766
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
767
|
+
return { id: nodeId("subroutine", label), label, shape: "subroutine", color };
|
|
768
|
+
}
|
|
769
|
+
m = t.match(/^\[([^\]]+)~\]$/);
|
|
770
|
+
if (m) {
|
|
771
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
772
|
+
return { id: nodeId("document", label), label, shape: "document", color };
|
|
773
|
+
}
|
|
774
|
+
m = t.match(/^\[([^\]]+)\]$/);
|
|
775
|
+
if (m) {
|
|
776
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
777
|
+
return { id: nodeId("process", label), label, shape: "process", color };
|
|
778
|
+
}
|
|
779
|
+
m = t.match(/^\((.+)\)$/);
|
|
780
|
+
if (m) {
|
|
781
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
782
|
+
return { id: nodeId("terminal", label), label, shape: "terminal", color };
|
|
783
|
+
}
|
|
784
|
+
m = t.match(/^<([^>]+)>$/);
|
|
785
|
+
if (m) {
|
|
786
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
787
|
+
return { id: nodeId("decision", label), label, shape: "decision", color };
|
|
788
|
+
}
|
|
789
|
+
m = t.match(/^\/([^/]+)\/$/);
|
|
790
|
+
if (m) {
|
|
791
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
792
|
+
return { id: nodeId("io", label), label, shape: "io", color };
|
|
793
|
+
}
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
function splitArrows(line3) {
|
|
797
|
+
const segments = [];
|
|
798
|
+
const arrowRe = /(?:^|\s)-([^>\s(][^(>]*?)?\s*(?:\(([^)]+)\))?\s*->|(?:^|\s)->/g;
|
|
799
|
+
let lastIndex = 0;
|
|
800
|
+
const arrowPositions = [];
|
|
801
|
+
let searchFrom = 0;
|
|
802
|
+
while (searchFrom < line3.length) {
|
|
803
|
+
const idx = line3.indexOf("->", searchFrom);
|
|
804
|
+
if (idx === -1) break;
|
|
805
|
+
let arrowStart = idx;
|
|
806
|
+
let label;
|
|
807
|
+
let color;
|
|
808
|
+
if (idx > 0 && line3[idx - 1] !== " " && line3[idx - 1] !== " ") {
|
|
809
|
+
let scanBack = idx - 1;
|
|
810
|
+
while (scanBack > 0 && line3[scanBack] !== "-") {
|
|
811
|
+
scanBack--;
|
|
812
|
+
}
|
|
813
|
+
if (line3[scanBack] === "-" && (scanBack === 0 || /\s/.test(line3[scanBack - 1]))) {
|
|
814
|
+
let arrowContent = line3.substring(scanBack + 1, idx);
|
|
815
|
+
if (arrowContent.endsWith("-")) arrowContent = arrowContent.slice(0, -1);
|
|
816
|
+
const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
|
|
817
|
+
if (colorMatch) {
|
|
818
|
+
color = colorMatch[1].trim();
|
|
819
|
+
const labelPart = arrowContent.substring(0, colorMatch.index).trim();
|
|
820
|
+
if (labelPart) label = labelPart;
|
|
821
|
+
} else {
|
|
822
|
+
const labelPart = arrowContent.trim();
|
|
823
|
+
if (labelPart) label = labelPart;
|
|
824
|
+
}
|
|
825
|
+
arrowStart = scanBack;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
|
|
829
|
+
searchFrom = idx + 2;
|
|
830
|
+
}
|
|
831
|
+
if (arrowPositions.length === 0) {
|
|
832
|
+
return [line3];
|
|
833
|
+
}
|
|
834
|
+
for (let i = 0; i < arrowPositions.length; i++) {
|
|
835
|
+
const arrow = arrowPositions[i];
|
|
836
|
+
const beforeText = line3.substring(lastIndex, arrow.start).trim();
|
|
837
|
+
if (beforeText || i === 0) {
|
|
838
|
+
segments.push(beforeText);
|
|
839
|
+
}
|
|
840
|
+
let arrowToken = "->";
|
|
841
|
+
if (arrow.label && arrow.color) arrowToken = `-${arrow.label}(${arrow.color})->`;
|
|
842
|
+
else if (arrow.label) arrowToken = `-${arrow.label}->`;
|
|
843
|
+
else if (arrow.color) arrowToken = `-(${arrow.color})->`;
|
|
844
|
+
segments.push(arrowToken);
|
|
845
|
+
lastIndex = arrow.end;
|
|
846
|
+
}
|
|
847
|
+
const remaining = line3.substring(lastIndex).trim();
|
|
848
|
+
if (remaining) {
|
|
849
|
+
segments.push(remaining);
|
|
850
|
+
}
|
|
851
|
+
return segments;
|
|
852
|
+
}
|
|
853
|
+
function parseArrowToken(token, palette) {
|
|
854
|
+
if (token === "->") return {};
|
|
855
|
+
const colorOnly = token.match(/^-\(([^)]+)\)->$/);
|
|
856
|
+
if (colorOnly) {
|
|
857
|
+
return { color: resolveColor(colorOnly[1].trim(), palette) };
|
|
858
|
+
}
|
|
859
|
+
const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
|
|
860
|
+
if (m) {
|
|
861
|
+
const label = m[1]?.trim() || void 0;
|
|
862
|
+
const color = m[2] ? resolveColor(m[2].trim(), palette) : void 0;
|
|
863
|
+
return { label, color };
|
|
864
|
+
}
|
|
865
|
+
return {};
|
|
866
|
+
}
|
|
867
|
+
function parseFlowchart(content, palette) {
|
|
868
|
+
const lines = content.split("\n");
|
|
869
|
+
const result = {
|
|
870
|
+
type: "flowchart",
|
|
871
|
+
direction: "TB",
|
|
872
|
+
nodes: [],
|
|
873
|
+
edges: []
|
|
874
|
+
};
|
|
875
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
876
|
+
const indentStack = [];
|
|
877
|
+
let currentGroup = null;
|
|
878
|
+
const groups = [];
|
|
879
|
+
let contentStarted = false;
|
|
880
|
+
function getOrCreateNode(ref, lineNumber) {
|
|
881
|
+
const existing = nodeMap.get(ref.id);
|
|
882
|
+
if (existing) return existing;
|
|
883
|
+
const node = {
|
|
884
|
+
id: ref.id,
|
|
885
|
+
label: ref.label,
|
|
886
|
+
shape: ref.shape,
|
|
887
|
+
lineNumber,
|
|
888
|
+
...ref.color && { color: ref.color },
|
|
889
|
+
...currentGroup && { group: currentGroup.id }
|
|
890
|
+
};
|
|
891
|
+
nodeMap.set(ref.id, node);
|
|
892
|
+
result.nodes.push(node);
|
|
893
|
+
if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
|
|
894
|
+
currentGroup.nodeIds.push(ref.id);
|
|
895
|
+
}
|
|
896
|
+
return node;
|
|
897
|
+
}
|
|
898
|
+
function addEdge(sourceId, targetId, lineNumber, label, color) {
|
|
899
|
+
const edge = {
|
|
900
|
+
source: sourceId,
|
|
901
|
+
target: targetId,
|
|
902
|
+
lineNumber,
|
|
903
|
+
...label && { label },
|
|
904
|
+
...color && { color }
|
|
905
|
+
};
|
|
906
|
+
result.edges.push(edge);
|
|
907
|
+
}
|
|
908
|
+
function processContentLine(trimmed, lineNumber, indent) {
|
|
909
|
+
contentStarted = true;
|
|
910
|
+
while (indentStack.length > 0) {
|
|
911
|
+
const top = indentStack[indentStack.length - 1];
|
|
912
|
+
if (top.indent >= indent) {
|
|
913
|
+
indentStack.pop();
|
|
914
|
+
} else {
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
const implicitSourceId = indentStack.length > 0 ? indentStack[indentStack.length - 1].nodeId : null;
|
|
919
|
+
const segments = splitArrows(trimmed);
|
|
920
|
+
if (segments.length === 1) {
|
|
921
|
+
const ref = parseNodeRef(segments[0], palette);
|
|
922
|
+
if (ref) {
|
|
923
|
+
const node = getOrCreateNode(ref, lineNumber);
|
|
924
|
+
indentStack.push({ nodeId: node.id, indent });
|
|
925
|
+
return node.id;
|
|
926
|
+
}
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
let lastNodeId = null;
|
|
930
|
+
let pendingArrow = null;
|
|
931
|
+
for (let i = 0; i < segments.length; i++) {
|
|
932
|
+
const seg = segments[i];
|
|
933
|
+
if (seg === "->" || /^-.+->$/.test(seg)) {
|
|
934
|
+
pendingArrow = parseArrowToken(seg, palette);
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
const ref = parseNodeRef(seg, palette);
|
|
938
|
+
if (!ref) continue;
|
|
939
|
+
const node = getOrCreateNode(ref, lineNumber);
|
|
940
|
+
if (pendingArrow !== null) {
|
|
941
|
+
const sourceId = lastNodeId ?? implicitSourceId;
|
|
942
|
+
if (sourceId) {
|
|
943
|
+
addEdge(
|
|
944
|
+
sourceId,
|
|
945
|
+
node.id,
|
|
946
|
+
lineNumber,
|
|
947
|
+
pendingArrow.label,
|
|
948
|
+
pendingArrow.color
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
pendingArrow = null;
|
|
952
|
+
} else if (lastNodeId === null && implicitSourceId === null) {
|
|
953
|
+
}
|
|
954
|
+
lastNodeId = node.id;
|
|
955
|
+
}
|
|
956
|
+
if (pendingArrow !== null && lastNodeId === null && implicitSourceId) {
|
|
957
|
+
}
|
|
958
|
+
if (segments.length >= 2 && segments[0] === "" && implicitSourceId && lastNodeId) {
|
|
959
|
+
}
|
|
960
|
+
if (lastNodeId) {
|
|
961
|
+
indentStack.push({ nodeId: lastNodeId, indent });
|
|
962
|
+
}
|
|
963
|
+
return lastNodeId;
|
|
964
|
+
}
|
|
965
|
+
for (let i = 0; i < lines.length; i++) {
|
|
966
|
+
const raw = lines[i];
|
|
967
|
+
const trimmed = raw.trim();
|
|
968
|
+
const lineNumber = i + 1;
|
|
969
|
+
const indent = measureIndent2(raw);
|
|
970
|
+
if (!trimmed) continue;
|
|
971
|
+
if (trimmed.startsWith("//")) continue;
|
|
972
|
+
const groupMatch = trimmed.match(GROUP_HEADING_RE);
|
|
973
|
+
if (groupMatch) {
|
|
974
|
+
const groupLabel = groupMatch[1].trim();
|
|
975
|
+
const groupColorName = groupMatch[2]?.trim();
|
|
976
|
+
const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
|
|
977
|
+
currentGroup = {
|
|
978
|
+
id: `group:${groupLabel.toLowerCase()}`,
|
|
979
|
+
label: groupLabel,
|
|
980
|
+
nodeIds: [],
|
|
981
|
+
lineNumber,
|
|
982
|
+
...groupColor && { color: groupColor }
|
|
983
|
+
};
|
|
984
|
+
groups.push(currentGroup);
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
if (!contentStarted && trimmed.includes(":") && !trimmed.includes("->")) {
|
|
988
|
+
const colonIdx = trimmed.indexOf(":");
|
|
989
|
+
const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
|
|
990
|
+
const value = trimmed.substring(colonIdx + 1).trim();
|
|
991
|
+
if (key === "chart") {
|
|
992
|
+
if (value.toLowerCase() !== "flowchart") {
|
|
993
|
+
result.error = `Line ${lineNumber}: Expected chart type "flowchart", got "${value}"`;
|
|
994
|
+
return result;
|
|
995
|
+
}
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
if (key === "title") {
|
|
999
|
+
result.title = value;
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
if (key === "direction") {
|
|
1003
|
+
const dir = value.toUpperCase();
|
|
1004
|
+
if (dir === "TB" || dir === "LR") {
|
|
1005
|
+
result.direction = dir;
|
|
1006
|
+
}
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
processContentLine(trimmed, lineNumber, indent);
|
|
1012
|
+
}
|
|
1013
|
+
if (groups.length > 0) result.groups = groups;
|
|
1014
|
+
if (result.nodes.length === 0 && !result.error) {
|
|
1015
|
+
result.error = "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).";
|
|
1016
|
+
}
|
|
1017
|
+
return result;
|
|
1018
|
+
}
|
|
1019
|
+
function looksLikeFlowchart(content) {
|
|
1020
|
+
if (!content.includes("->")) return false;
|
|
1021
|
+
const hasShapeDelimiter = /\[[^\]]+\]/.test(content) || /\([^)]+\)/.test(content) || /<[^>]+>/.test(content) || /\/[^/]+\//.test(content);
|
|
1022
|
+
if (!hasShapeDelimiter) return false;
|
|
1023
|
+
const shapeNearArrow = /[\])][ \t]*-.*->/.test(content) || // shape ] or ) followed by arrow
|
|
1024
|
+
/->[ \t]*[\[(<\/]/.test(content);
|
|
1025
|
+
return shapeNearArrow;
|
|
1026
|
+
}
|
|
1027
|
+
var COLOR_SUFFIX_RE, GROUP_HEADING_RE;
|
|
1028
|
+
var init_flowchart_parser = __esm({
|
|
1029
|
+
"src/graph/flowchart-parser.ts"() {
|
|
1030
|
+
"use strict";
|
|
1031
|
+
init_colors();
|
|
1032
|
+
COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
|
|
1033
|
+
GROUP_HEADING_RE = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
// src/dgmo-router.ts
|
|
1038
|
+
var dgmo_router_exports = {};
|
|
1039
|
+
__export(dgmo_router_exports, {
|
|
1040
|
+
DGMO_CHART_TYPE_MAP: () => DGMO_CHART_TYPE_MAP,
|
|
1041
|
+
getDgmoFramework: () => getDgmoFramework,
|
|
1042
|
+
parseDgmoChartType: () => parseDgmoChartType
|
|
1043
|
+
});
|
|
1044
|
+
function getDgmoFramework(chartType) {
|
|
1045
|
+
return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
|
|
1046
|
+
}
|
|
1047
|
+
function parseDgmoChartType(content) {
|
|
1048
|
+
const lines = content.split("\n");
|
|
1049
|
+
for (const line3 of lines) {
|
|
1050
|
+
const trimmed = line3.trim();
|
|
1051
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
|
|
1052
|
+
continue;
|
|
1053
|
+
const match = trimmed.match(/^chart\s*:\s*(.+)/i);
|
|
1054
|
+
if (match) return match[1].trim().toLowerCase();
|
|
1055
|
+
}
|
|
1056
|
+
if (looksLikeSequence(content)) return "sequence";
|
|
1057
|
+
if (looksLikeFlowchart(content)) return "flowchart";
|
|
1058
|
+
return null;
|
|
1059
|
+
}
|
|
1060
|
+
var DGMO_CHART_TYPE_MAP;
|
|
1061
|
+
var init_dgmo_router = __esm({
|
|
1062
|
+
"src/dgmo-router.ts"() {
|
|
1063
|
+
"use strict";
|
|
1064
|
+
init_parser();
|
|
1065
|
+
init_flowchart_parser();
|
|
1066
|
+
DGMO_CHART_TYPE_MAP = {
|
|
1067
|
+
// Standard charts (via ECharts)
|
|
1068
|
+
bar: "echart",
|
|
1069
|
+
line: "echart",
|
|
1070
|
+
"multi-line": "echart",
|
|
1071
|
+
area: "echart",
|
|
1072
|
+
pie: "echart",
|
|
1073
|
+
doughnut: "echart",
|
|
1074
|
+
radar: "echart",
|
|
1075
|
+
"polar-area": "echart",
|
|
1076
|
+
"bar-stacked": "echart",
|
|
1077
|
+
// ECharts
|
|
1078
|
+
scatter: "echart",
|
|
1079
|
+
sankey: "echart",
|
|
1080
|
+
chord: "echart",
|
|
1081
|
+
function: "echart",
|
|
1082
|
+
heatmap: "echart",
|
|
1083
|
+
funnel: "echart",
|
|
1084
|
+
// D3
|
|
1085
|
+
slope: "d3",
|
|
1086
|
+
wordcloud: "d3",
|
|
1087
|
+
arc: "d3",
|
|
1088
|
+
timeline: "d3",
|
|
1089
|
+
venn: "d3",
|
|
1090
|
+
quadrant: "d3",
|
|
1091
|
+
sequence: "d3",
|
|
1092
|
+
flowchart: "d3"
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
|
|
582
1097
|
// src/fonts.ts
|
|
583
1098
|
var FONT_FAMILY;
|
|
584
1099
|
var init_fonts = __esm({
|
|
@@ -1634,6 +2149,409 @@ var init_palettes = __esm({
|
|
|
1634
2149
|
}
|
|
1635
2150
|
});
|
|
1636
2151
|
|
|
2152
|
+
// src/graph/layout.ts
|
|
2153
|
+
var layout_exports = {};
|
|
2154
|
+
__export(layout_exports, {
|
|
2155
|
+
layoutGraph: () => layoutGraph
|
|
2156
|
+
});
|
|
2157
|
+
function computeNodeWidth(label, shape) {
|
|
2158
|
+
const base = Math.max(120, label.length * 9 + 40);
|
|
2159
|
+
if (shape === "subroutine") return base + 10;
|
|
2160
|
+
return base;
|
|
2161
|
+
}
|
|
2162
|
+
function computeNodeHeight(shape) {
|
|
2163
|
+
return shape === "decision" ? 60 : 50;
|
|
2164
|
+
}
|
|
2165
|
+
function layoutGraph(graph) {
|
|
2166
|
+
if (graph.nodes.length === 0) {
|
|
2167
|
+
return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
|
|
2168
|
+
}
|
|
2169
|
+
const g = new import_dagre.default.graphlib.Graph({ compound: true });
|
|
2170
|
+
g.setGraph({
|
|
2171
|
+
rankdir: graph.direction,
|
|
2172
|
+
nodesep: 50,
|
|
2173
|
+
ranksep: 60,
|
|
2174
|
+
edgesep: 20
|
|
2175
|
+
});
|
|
2176
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
2177
|
+
const nodeDataMap = /* @__PURE__ */ new Map();
|
|
2178
|
+
for (const node of graph.nodes) {
|
|
2179
|
+
nodeDataMap.set(node.id, node);
|
|
2180
|
+
}
|
|
2181
|
+
if (graph.groups) {
|
|
2182
|
+
for (const group of graph.groups) {
|
|
2183
|
+
g.setNode(group.id, {
|
|
2184
|
+
label: group.label,
|
|
2185
|
+
clusterLabelPos: "top"
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
for (const node of graph.nodes) {
|
|
2190
|
+
const width = computeNodeWidth(node.label, node.shape);
|
|
2191
|
+
const height = computeNodeHeight(node.shape);
|
|
2192
|
+
g.setNode(node.id, { label: node.label, width, height });
|
|
2193
|
+
if (node.group && graph.groups?.some((gr) => gr.id === node.group)) {
|
|
2194
|
+
g.setParent(node.id, node.group);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
const edgeDataMap = /* @__PURE__ */ new Map();
|
|
2198
|
+
for (const edge of graph.edges) {
|
|
2199
|
+
const key = `${edge.source}->${edge.target}`;
|
|
2200
|
+
edgeDataMap.set(key, edge);
|
|
2201
|
+
g.setEdge(edge.source, edge.target, {
|
|
2202
|
+
label: edge.label ?? ""
|
|
2203
|
+
});
|
|
2204
|
+
}
|
|
2205
|
+
import_dagre.default.layout(g);
|
|
2206
|
+
const layoutNodes = graph.nodes.map((node) => {
|
|
2207
|
+
const pos = g.node(node.id);
|
|
2208
|
+
return {
|
|
2209
|
+
id: node.id,
|
|
2210
|
+
label: node.label,
|
|
2211
|
+
shape: node.shape,
|
|
2212
|
+
color: node.color,
|
|
2213
|
+
group: node.group,
|
|
2214
|
+
lineNumber: node.lineNumber,
|
|
2215
|
+
x: pos.x,
|
|
2216
|
+
y: pos.y,
|
|
2217
|
+
width: pos.width,
|
|
2218
|
+
height: pos.height
|
|
2219
|
+
};
|
|
2220
|
+
});
|
|
2221
|
+
const layoutEdges = graph.edges.map((edge) => {
|
|
2222
|
+
const edgeData = g.edge(edge.source, edge.target);
|
|
2223
|
+
return {
|
|
2224
|
+
source: edge.source,
|
|
2225
|
+
target: edge.target,
|
|
2226
|
+
points: edgeData?.points ?? [],
|
|
2227
|
+
label: edge.label,
|
|
2228
|
+
color: edge.color,
|
|
2229
|
+
lineNumber: edge.lineNumber
|
|
2230
|
+
};
|
|
2231
|
+
});
|
|
2232
|
+
const layoutGroups = [];
|
|
2233
|
+
if (graph.groups) {
|
|
2234
|
+
const nodeMap = new Map(layoutNodes.map((n) => [n.id, n]));
|
|
2235
|
+
for (const group of graph.groups) {
|
|
2236
|
+
const members = group.nodeIds.map((id) => nodeMap.get(id)).filter((n) => n !== void 0);
|
|
2237
|
+
if (members.length === 0) {
|
|
2238
|
+
layoutGroups.push({
|
|
2239
|
+
id: group.id,
|
|
2240
|
+
label: group.label,
|
|
2241
|
+
color: group.color,
|
|
2242
|
+
x: 0,
|
|
2243
|
+
y: 0,
|
|
2244
|
+
width: 0,
|
|
2245
|
+
height: 0
|
|
2246
|
+
});
|
|
2247
|
+
continue;
|
|
2248
|
+
}
|
|
2249
|
+
let minX = Infinity;
|
|
2250
|
+
let minY = Infinity;
|
|
2251
|
+
let maxX = -Infinity;
|
|
2252
|
+
let maxY = -Infinity;
|
|
2253
|
+
for (const member of members) {
|
|
2254
|
+
const left = member.x - member.width / 2;
|
|
2255
|
+
const right = member.x + member.width / 2;
|
|
2256
|
+
const top = member.y - member.height / 2;
|
|
2257
|
+
const bottom = member.y + member.height / 2;
|
|
2258
|
+
if (left < minX) minX = left;
|
|
2259
|
+
if (right > maxX) maxX = right;
|
|
2260
|
+
if (top < minY) minY = top;
|
|
2261
|
+
if (bottom > maxY) maxY = bottom;
|
|
2262
|
+
}
|
|
2263
|
+
layoutGroups.push({
|
|
2264
|
+
id: group.id,
|
|
2265
|
+
label: group.label,
|
|
2266
|
+
color: group.color,
|
|
2267
|
+
x: minX - GROUP_PADDING,
|
|
2268
|
+
y: minY - GROUP_PADDING,
|
|
2269
|
+
width: maxX - minX + GROUP_PADDING * 2,
|
|
2270
|
+
height: maxY - minY + GROUP_PADDING * 2
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
let totalWidth = 0;
|
|
2275
|
+
let totalHeight = 0;
|
|
2276
|
+
for (const node of layoutNodes) {
|
|
2277
|
+
const right = node.x + node.width / 2;
|
|
2278
|
+
const bottom = node.y + node.height / 2;
|
|
2279
|
+
if (right > totalWidth) totalWidth = right;
|
|
2280
|
+
if (bottom > totalHeight) totalHeight = bottom;
|
|
2281
|
+
}
|
|
2282
|
+
for (const group of layoutGroups) {
|
|
2283
|
+
const right = group.x + group.width;
|
|
2284
|
+
const bottom = group.y + group.height;
|
|
2285
|
+
if (right > totalWidth) totalWidth = right;
|
|
2286
|
+
if (bottom > totalHeight) totalHeight = bottom;
|
|
2287
|
+
}
|
|
2288
|
+
totalWidth += 40;
|
|
2289
|
+
totalHeight += 40;
|
|
2290
|
+
return {
|
|
2291
|
+
nodes: layoutNodes,
|
|
2292
|
+
edges: layoutEdges,
|
|
2293
|
+
groups: layoutGroups,
|
|
2294
|
+
width: totalWidth,
|
|
2295
|
+
height: totalHeight
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
var import_dagre, GROUP_PADDING;
|
|
2299
|
+
var init_layout = __esm({
|
|
2300
|
+
"src/graph/layout.ts"() {
|
|
2301
|
+
"use strict";
|
|
2302
|
+
import_dagre = __toESM(require("@dagrejs/dagre"), 1);
|
|
2303
|
+
GROUP_PADDING = 20;
|
|
2304
|
+
}
|
|
2305
|
+
});
|
|
2306
|
+
|
|
2307
|
+
// src/graph/flowchart-renderer.ts
|
|
2308
|
+
var flowchart_renderer_exports = {};
|
|
2309
|
+
__export(flowchart_renderer_exports, {
|
|
2310
|
+
renderFlowchart: () => renderFlowchart,
|
|
2311
|
+
renderFlowchartForExport: () => renderFlowchartForExport
|
|
2312
|
+
});
|
|
2313
|
+
function mix(a, b, pct) {
|
|
2314
|
+
const parse = (h) => {
|
|
2315
|
+
const r = h.replace("#", "");
|
|
2316
|
+
const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
|
|
2317
|
+
return [parseInt(f.substring(0, 2), 16), parseInt(f.substring(2, 4), 16), parseInt(f.substring(4, 6), 16)];
|
|
2318
|
+
};
|
|
2319
|
+
const [ar, ag, ab] = parse(a), [br, bg, bb] = parse(b), t = pct / 100;
|
|
2320
|
+
const c = (x, y) => Math.round(x * t + y * (1 - t)).toString(16).padStart(2, "0");
|
|
2321
|
+
return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
|
|
2322
|
+
}
|
|
2323
|
+
function nodeFill(palette, isDark, nodeColor) {
|
|
2324
|
+
if (nodeColor) {
|
|
2325
|
+
return mix(nodeColor, isDark ? palette.surface : palette.bg, 25);
|
|
2326
|
+
}
|
|
2327
|
+
return mix(palette.primary, isDark ? palette.surface : palette.bg, 15);
|
|
2328
|
+
}
|
|
2329
|
+
function nodeStroke(palette, nodeColor) {
|
|
2330
|
+
return nodeColor ?? palette.textMuted;
|
|
2331
|
+
}
|
|
2332
|
+
function renderTerminal(g, node, palette, isDark) {
|
|
2333
|
+
const w = node.width;
|
|
2334
|
+
const h = node.height;
|
|
2335
|
+
const rx = h / 2;
|
|
2336
|
+
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);
|
|
2337
|
+
}
|
|
2338
|
+
function renderProcess(g, node, palette, isDark) {
|
|
2339
|
+
const w = node.width;
|
|
2340
|
+
const h = node.height;
|
|
2341
|
+
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);
|
|
2342
|
+
}
|
|
2343
|
+
function renderDecision(g, node, palette, isDark) {
|
|
2344
|
+
const w = node.width / 2;
|
|
2345
|
+
const h = node.height / 2;
|
|
2346
|
+
const points = [
|
|
2347
|
+
`${0},${-h}`,
|
|
2348
|
+
// top
|
|
2349
|
+
`${w},${0}`,
|
|
2350
|
+
// right
|
|
2351
|
+
`${0},${h}`,
|
|
2352
|
+
// bottom
|
|
2353
|
+
`${-w},${0}`
|
|
2354
|
+
// left
|
|
2355
|
+
].join(" ");
|
|
2356
|
+
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);
|
|
2357
|
+
}
|
|
2358
|
+
function renderIO(g, node, palette, isDark) {
|
|
2359
|
+
const w = node.width / 2;
|
|
2360
|
+
const h = node.height / 2;
|
|
2361
|
+
const sk = IO_SKEW;
|
|
2362
|
+
const points = [
|
|
2363
|
+
`${-w + sk},${-h}`,
|
|
2364
|
+
// top-left (shifted right)
|
|
2365
|
+
`${w + sk},${-h}`,
|
|
2366
|
+
// top-right (shifted right)
|
|
2367
|
+
`${w - sk},${h}`,
|
|
2368
|
+
// bottom-right (shifted left)
|
|
2369
|
+
`${-w - sk},${h}`
|
|
2370
|
+
// bottom-left (shifted left)
|
|
2371
|
+
].join(" ");
|
|
2372
|
+
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);
|
|
2373
|
+
}
|
|
2374
|
+
function renderSubroutine(g, node, palette, isDark) {
|
|
2375
|
+
const w = node.width;
|
|
2376
|
+
const h = node.height;
|
|
2377
|
+
const s = nodeStroke(palette, node.color);
|
|
2378
|
+
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);
|
|
2379
|
+
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);
|
|
2380
|
+
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);
|
|
2381
|
+
}
|
|
2382
|
+
function renderDocument(g, node, palette, isDark) {
|
|
2383
|
+
const w = node.width;
|
|
2384
|
+
const h = node.height;
|
|
2385
|
+
const waveH = DOC_WAVE_HEIGHT;
|
|
2386
|
+
const left = -w / 2;
|
|
2387
|
+
const right = w / 2;
|
|
2388
|
+
const top = -h / 2;
|
|
2389
|
+
const bottom = h / 2 - waveH;
|
|
2390
|
+
const d = [
|
|
2391
|
+
`M ${left} ${top}`,
|
|
2392
|
+
`L ${right} ${top}`,
|
|
2393
|
+
`L ${right} ${bottom}`,
|
|
2394
|
+
`C ${right - w * 0.25} ${bottom + waveH * 2}, ${left + w * 0.25} ${bottom - waveH}, ${left} ${bottom}`,
|
|
2395
|
+
"Z"
|
|
2396
|
+
].join(" ");
|
|
2397
|
+
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);
|
|
2398
|
+
}
|
|
2399
|
+
function renderNodeShape(g, node, palette, isDark) {
|
|
2400
|
+
switch (node.shape) {
|
|
2401
|
+
case "terminal":
|
|
2402
|
+
renderTerminal(g, node, palette, isDark);
|
|
2403
|
+
break;
|
|
2404
|
+
case "process":
|
|
2405
|
+
renderProcess(g, node, palette, isDark);
|
|
2406
|
+
break;
|
|
2407
|
+
case "decision":
|
|
2408
|
+
renderDecision(g, node, palette, isDark);
|
|
2409
|
+
break;
|
|
2410
|
+
case "io":
|
|
2411
|
+
renderIO(g, node, palette, isDark);
|
|
2412
|
+
break;
|
|
2413
|
+
case "subroutine":
|
|
2414
|
+
renderSubroutine(g, node, palette, isDark);
|
|
2415
|
+
break;
|
|
2416
|
+
case "document":
|
|
2417
|
+
renderDocument(g, node, palette, isDark);
|
|
2418
|
+
break;
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
function renderFlowchart(container, graph, layout, palette, isDark, onClickItem, exportDims) {
|
|
2422
|
+
d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
2423
|
+
const width = exportDims?.width ?? container.clientWidth;
|
|
2424
|
+
const height = exportDims?.height ?? container.clientHeight;
|
|
2425
|
+
if (width <= 0 || height <= 0) return;
|
|
2426
|
+
const titleOffset = graph.title ? TITLE_HEIGHT : 0;
|
|
2427
|
+
const diagramW = layout.width;
|
|
2428
|
+
const diagramH = layout.height + titleOffset;
|
|
2429
|
+
const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
|
|
2430
|
+
const scaleY = (height - DIAGRAM_PADDING * 2) / diagramH;
|
|
2431
|
+
const scale = Math.min(1, scaleX, scaleY);
|
|
2432
|
+
const scaledW = diagramW * scale;
|
|
2433
|
+
const scaledH = diagramH * scale;
|
|
2434
|
+
const offsetX = (width - scaledW) / 2;
|
|
2435
|
+
const offsetY = (height - scaledH) / 2;
|
|
2436
|
+
const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("background", palette.bg).style("font-family", FONT_FAMILY);
|
|
2437
|
+
const defs = svg.append("defs");
|
|
2438
|
+
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);
|
|
2439
|
+
const edgeColors = /* @__PURE__ */ new Set();
|
|
2440
|
+
for (const edge of layout.edges) {
|
|
2441
|
+
if (edge.color) edgeColors.add(edge.color);
|
|
2442
|
+
}
|
|
2443
|
+
for (const color of edgeColors) {
|
|
2444
|
+
const id = `fc-arrow-${color.replace("#", "")}`;
|
|
2445
|
+
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);
|
|
2446
|
+
}
|
|
2447
|
+
const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
|
|
2448
|
+
if (graph.title) {
|
|
2449
|
+
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);
|
|
2450
|
+
}
|
|
2451
|
+
const contentG = mainG.append("g").attr("transform", `translate(0, ${titleOffset})`);
|
|
2452
|
+
for (const group of layout.groups) {
|
|
2453
|
+
if (group.width === 0 && group.height === 0) continue;
|
|
2454
|
+
const gx = group.x - GROUP_EXTRA_PADDING;
|
|
2455
|
+
const gy = group.y - GROUP_EXTRA_PADDING - GROUP_LABEL_FONT_SIZE - 4;
|
|
2456
|
+
const gw = group.width + GROUP_EXTRA_PADDING * 2;
|
|
2457
|
+
const gh = group.height + GROUP_EXTRA_PADDING * 2 + GROUP_LABEL_FONT_SIZE + 4;
|
|
2458
|
+
const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
|
|
2459
|
+
const strokeColor = group.color ?? palette.textMuted;
|
|
2460
|
+
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");
|
|
2461
|
+
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);
|
|
2462
|
+
}
|
|
2463
|
+
for (const edge of layout.edges) {
|
|
2464
|
+
if (edge.points.length < 2) continue;
|
|
2465
|
+
const edgeG = contentG.append("g").attr("class", "fc-edge-group").attr("data-line-number", String(edge.lineNumber));
|
|
2466
|
+
const edgeColor = edge.color ?? palette.textMuted;
|
|
2467
|
+
const markerId = edge.color ? `fc-arrow-${edge.color.replace("#", "")}` : "fc-arrow";
|
|
2468
|
+
const pathD = lineGenerator(edge.points);
|
|
2469
|
+
if (pathD) {
|
|
2470
|
+
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");
|
|
2471
|
+
}
|
|
2472
|
+
if (edge.label) {
|
|
2473
|
+
const midIdx = Math.floor(edge.points.length / 2);
|
|
2474
|
+
const midPt = edge.points[midIdx];
|
|
2475
|
+
const labelLen = edge.label.length;
|
|
2476
|
+
const bgW = labelLen * 7 + 8;
|
|
2477
|
+
const bgH = 16;
|
|
2478
|
+
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");
|
|
2479
|
+
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);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
for (const node of layout.nodes) {
|
|
2483
|
+
const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber));
|
|
2484
|
+
if (onClickItem) {
|
|
2485
|
+
nodeG.style("cursor", "pointer").on("click", () => {
|
|
2486
|
+
onClickItem(node.lineNumber);
|
|
2487
|
+
});
|
|
2488
|
+
}
|
|
2489
|
+
renderNodeShape(nodeG, node, palette, isDark);
|
|
2490
|
+
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);
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
function renderFlowchartForExport(content, theme, palette) {
|
|
2494
|
+
const parsed = parseFlowchart(content, palette);
|
|
2495
|
+
if (parsed.error || parsed.nodes.length === 0) return "";
|
|
2496
|
+
const layout = layoutGraph(parsed);
|
|
2497
|
+
const isDark = theme === "dark";
|
|
2498
|
+
const container = document.createElement("div");
|
|
2499
|
+
container.style.width = `${layout.width + DIAGRAM_PADDING * 2}px`;
|
|
2500
|
+
container.style.height = `${layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0)}px`;
|
|
2501
|
+
container.style.position = "absolute";
|
|
2502
|
+
container.style.left = "-9999px";
|
|
2503
|
+
document.body.appendChild(container);
|
|
2504
|
+
const exportWidth = layout.width + DIAGRAM_PADDING * 2;
|
|
2505
|
+
const exportHeight = layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0);
|
|
2506
|
+
try {
|
|
2507
|
+
renderFlowchart(
|
|
2508
|
+
container,
|
|
2509
|
+
parsed,
|
|
2510
|
+
layout,
|
|
2511
|
+
palette,
|
|
2512
|
+
isDark,
|
|
2513
|
+
void 0,
|
|
2514
|
+
{ width: exportWidth, height: exportHeight }
|
|
2515
|
+
);
|
|
2516
|
+
const svgEl = container.querySelector("svg");
|
|
2517
|
+
if (!svgEl) return "";
|
|
2518
|
+
if (theme === "transparent") {
|
|
2519
|
+
svgEl.style.background = "none";
|
|
2520
|
+
}
|
|
2521
|
+
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
2522
|
+
svgEl.style.fontFamily = FONT_FAMILY;
|
|
2523
|
+
return svgEl.outerHTML;
|
|
2524
|
+
} finally {
|
|
2525
|
+
document.body.removeChild(container);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
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;
|
|
2529
|
+
var init_flowchart_renderer = __esm({
|
|
2530
|
+
"src/graph/flowchart-renderer.ts"() {
|
|
2531
|
+
"use strict";
|
|
2532
|
+
d3Selection = __toESM(require("d3-selection"), 1);
|
|
2533
|
+
d3Shape = __toESM(require("d3-shape"), 1);
|
|
2534
|
+
init_fonts();
|
|
2535
|
+
init_flowchart_parser();
|
|
2536
|
+
init_layout();
|
|
2537
|
+
DIAGRAM_PADDING = 20;
|
|
2538
|
+
TITLE_HEIGHT = 30;
|
|
2539
|
+
TITLE_FONT_SIZE = 18;
|
|
2540
|
+
NODE_FONT_SIZE = 13;
|
|
2541
|
+
EDGE_LABEL_FONT_SIZE = 11;
|
|
2542
|
+
GROUP_LABEL_FONT_SIZE = 11;
|
|
2543
|
+
EDGE_STROKE_WIDTH = 1.5;
|
|
2544
|
+
NODE_STROKE_WIDTH = 1.5;
|
|
2545
|
+
ARROWHEAD_W = 10;
|
|
2546
|
+
ARROWHEAD_H = 7;
|
|
2547
|
+
IO_SKEW = 15;
|
|
2548
|
+
SUBROUTINE_INSET = 8;
|
|
2549
|
+
DOC_WAVE_HEIGHT = 10;
|
|
2550
|
+
GROUP_EXTRA_PADDING = 12;
|
|
2551
|
+
lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
|
|
2552
|
+
}
|
|
2553
|
+
});
|
|
2554
|
+
|
|
1637
2555
|
// src/sequence/renderer.ts
|
|
1638
2556
|
var renderer_exports = {};
|
|
1639
2557
|
__export(renderer_exports, {
|
|
@@ -1644,7 +2562,42 @@ __export(renderer_exports, {
|
|
|
1644
2562
|
groupMessagesBySection: () => groupMessagesBySection,
|
|
1645
2563
|
renderSequenceDiagram: () => renderSequenceDiagram
|
|
1646
2564
|
});
|
|
1647
|
-
function
|
|
2565
|
+
function parseInlineMarkdown(text) {
|
|
2566
|
+
const spans = [];
|
|
2567
|
+
const regex = /\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*`[]+)/g;
|
|
2568
|
+
let match;
|
|
2569
|
+
while ((match = regex.exec(text)) !== null) {
|
|
2570
|
+
if (match[1]) spans.push({ text: match[1], bold: true });
|
|
2571
|
+
else if (match[2]) spans.push({ text: match[2], italic: true });
|
|
2572
|
+
else if (match[3]) spans.push({ text: match[3], code: true });
|
|
2573
|
+
else if (match[4]) spans.push({ text: match[4], href: match[5] });
|
|
2574
|
+
else if (match[6]) spans.push({ text: match[6] });
|
|
2575
|
+
}
|
|
2576
|
+
return spans;
|
|
2577
|
+
}
|
|
2578
|
+
function wrapTextLines(text, maxChars) {
|
|
2579
|
+
const rawLines = text.split("\n");
|
|
2580
|
+
const wrapped = [];
|
|
2581
|
+
for (const line3 of rawLines) {
|
|
2582
|
+
if (line3.length <= maxChars) {
|
|
2583
|
+
wrapped.push(line3);
|
|
2584
|
+
} else {
|
|
2585
|
+
const words = line3.split(" ");
|
|
2586
|
+
let current = "";
|
|
2587
|
+
for (const word of words) {
|
|
2588
|
+
if (current && (current + " " + word).length > maxChars) {
|
|
2589
|
+
wrapped.push(current);
|
|
2590
|
+
current = word;
|
|
2591
|
+
} else {
|
|
2592
|
+
current = current ? current + " " + word : word;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
if (current) wrapped.push(current);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
return wrapped;
|
|
2599
|
+
}
|
|
2600
|
+
function mix2(a, b, pct) {
|
|
1648
2601
|
const parse = (h) => {
|
|
1649
2602
|
const r = h.replace("#", "");
|
|
1650
2603
|
const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
|
|
@@ -1750,7 +2703,12 @@ function groupMessagesBySection(elements, messages) {
|
|
|
1750
2703
|
...collectIndices(el.children),
|
|
1751
2704
|
...collectIndices(el.elseChildren)
|
|
1752
2705
|
);
|
|
1753
|
-
|
|
2706
|
+
if (el.elseIfBranches) {
|
|
2707
|
+
for (const branch of el.elseIfBranches) {
|
|
2708
|
+
indices.push(...collectIndices(branch.children));
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
} else if (isSequenceSection(el) || isSequenceNote(el)) {
|
|
1754
2712
|
continue;
|
|
1755
2713
|
} else {
|
|
1756
2714
|
const idx = messages.indexOf(el);
|
|
@@ -1766,7 +2724,7 @@ function groupMessagesBySection(elements, messages) {
|
|
|
1766
2724
|
} else if (currentGroup) {
|
|
1767
2725
|
if (isSequenceBlock(el)) {
|
|
1768
2726
|
currentGroup.messageIndices.push(...collectIndices([el]));
|
|
1769
|
-
} else {
|
|
2727
|
+
} else if (!isSequenceNote(el)) {
|
|
1770
2728
|
const idx = messages.indexOf(el);
|
|
1771
2729
|
if (idx >= 0) currentGroup.messageIndices.push(idx);
|
|
1772
2730
|
}
|
|
@@ -1923,7 +2881,7 @@ function applyGroupOrdering(participants, groups) {
|
|
|
1923
2881
|
return result;
|
|
1924
2882
|
}
|
|
1925
2883
|
function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
|
|
1926
|
-
|
|
2884
|
+
d3Selection2.select(container).selectAll("*").remove();
|
|
1927
2885
|
const { title, messages, elements, groups, options: parsedOptions } = parsed;
|
|
1928
2886
|
const collapsedSections = options?.collapsedSections;
|
|
1929
2887
|
const participants = applyPositionOverrides(
|
|
@@ -1964,7 +2922,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1964
2922
|
if (isSequenceBlock(el)) {
|
|
1965
2923
|
const idx = findFirstMsgIndex(el.children);
|
|
1966
2924
|
if (idx >= 0) return idx;
|
|
1967
|
-
|
|
2925
|
+
if (el.elseIfBranches) {
|
|
2926
|
+
for (const branch of el.elseIfBranches) {
|
|
2927
|
+
const branchIdx = findFirstMsgIndex(branch.children);
|
|
2928
|
+
if (branchIdx >= 0) return branchIdx;
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
const elseIdx = findFirstMsgIndex(el.elseChildren);
|
|
2932
|
+
if (elseIdx >= 0) return elseIdx;
|
|
2933
|
+
} else if (!isSequenceSection(el) && !isSequenceNote(el)) {
|
|
1968
2934
|
const idx = messages.indexOf(el);
|
|
1969
2935
|
if (idx >= 0 && !hiddenMsgIndices.has(idx)) return idx;
|
|
1970
2936
|
}
|
|
@@ -1984,6 +2950,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1984
2950
|
if (!isSequenceBlock(el)) continue;
|
|
1985
2951
|
const firstIdx = findFirstMsgIndex(el.children);
|
|
1986
2952
|
if (firstIdx >= 0) addExtra(firstIdx, BLOCK_HEADER_SPACE);
|
|
2953
|
+
if (el.elseIfBranches) {
|
|
2954
|
+
for (const branch of el.elseIfBranches) {
|
|
2955
|
+
const firstBranchIdx = findFirstMsgIndex(branch.children);
|
|
2956
|
+
if (firstBranchIdx >= 0) addExtra(firstBranchIdx, BLOCK_HEADER_SPACE);
|
|
2957
|
+
markBlockSpacing(branch.children);
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
1987
2960
|
const firstElseIdx = findFirstMsgIndex(el.elseChildren);
|
|
1988
2961
|
if (firstElseIdx >= 0) addExtra(firstElseIdx, BLOCK_HEADER_SPACE);
|
|
1989
2962
|
markBlockSpacing(el.children);
|
|
@@ -2005,15 +2978,27 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2005
2978
|
for (const child of block.children) {
|
|
2006
2979
|
if (isSequenceBlock(child)) {
|
|
2007
2980
|
indices.push(...collectMsgIndicesFromBlock(child));
|
|
2008
|
-
} else if (!isSequenceSection(child)) {
|
|
2981
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
2009
2982
|
const idx = messages.indexOf(child);
|
|
2010
2983
|
if (idx >= 0) indices.push(idx);
|
|
2011
2984
|
}
|
|
2012
2985
|
}
|
|
2986
|
+
if (block.elseIfBranches) {
|
|
2987
|
+
for (const branch of block.elseIfBranches) {
|
|
2988
|
+
for (const child of branch.children) {
|
|
2989
|
+
if (isSequenceBlock(child)) {
|
|
2990
|
+
indices.push(...collectMsgIndicesFromBlock(child));
|
|
2991
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
2992
|
+
const idx = messages.indexOf(child);
|
|
2993
|
+
if (idx >= 0) indices.push(idx);
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2013
2998
|
for (const child of block.elseChildren) {
|
|
2014
2999
|
if (isSequenceBlock(child)) {
|
|
2015
3000
|
indices.push(...collectMsgIndicesFromBlock(child));
|
|
2016
|
-
} else if (!isSequenceSection(child)) {
|
|
3001
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
2017
3002
|
const idx = messages.indexOf(child);
|
|
2018
3003
|
if (idx >= 0) indices.push(idx);
|
|
2019
3004
|
}
|
|
@@ -2088,7 +3073,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2088
3073
|
const GROUP_PADDING_TOP = 22;
|
|
2089
3074
|
const GROUP_PADDING_BOTTOM = 8;
|
|
2090
3075
|
const GROUP_LABEL_SIZE = 11;
|
|
2091
|
-
const titleOffset = title ?
|
|
3076
|
+
const titleOffset = title ? TITLE_HEIGHT2 : 0;
|
|
2092
3077
|
const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
|
|
2093
3078
|
const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
|
|
2094
3079
|
const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
|
|
@@ -2142,7 +3127,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2142
3127
|
participants.forEach((p, i) => {
|
|
2143
3128
|
participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
|
|
2144
3129
|
});
|
|
2145
|
-
const svg =
|
|
3130
|
+
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
3131
|
const defs = svg.append("defs");
|
|
2147
3132
|
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
3133
|
"points",
|
|
@@ -2168,7 +3153,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2168
3153
|
const boxY = participantStartY - GROUP_PADDING_TOP;
|
|
2169
3154
|
const boxH = PARTICIPANT_BOX_HEIGHT + GROUP_PADDING_TOP + GROUP_PADDING_BOTTOM;
|
|
2170
3155
|
const resolvedGroupColor = group.color ? resolveColor(group.color, palette) : void 0;
|
|
2171
|
-
const fillColor = resolvedGroupColor ?
|
|
3156
|
+
const fillColor = resolvedGroupColor ? mix2(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
|
|
2172
3157
|
const strokeColor = resolvedGroupColor || palette.textMuted;
|
|
2173
3158
|
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
3159
|
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 +3178,12 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2193
3178
|
...collectMsgIndices(el.children),
|
|
2194
3179
|
...collectMsgIndices(el.elseChildren)
|
|
2195
3180
|
);
|
|
2196
|
-
|
|
3181
|
+
if (el.elseIfBranches) {
|
|
3182
|
+
for (const branch of el.elseIfBranches) {
|
|
3183
|
+
indices.push(...collectMsgIndices(branch.children));
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
} else if (!isSequenceSection(el) && !isSequenceNote(el)) {
|
|
2197
3187
|
const idx = messages.indexOf(el);
|
|
2198
3188
|
if (idx >= 0) indices.push(idx);
|
|
2199
3189
|
}
|
|
@@ -2206,8 +3196,21 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2206
3196
|
for (const el of els) {
|
|
2207
3197
|
if (!isSequenceBlock(el)) continue;
|
|
2208
3198
|
const ifIndices = collectMsgIndices(el.children);
|
|
3199
|
+
const elseIfBranchData = [];
|
|
3200
|
+
if (el.elseIfBranches) {
|
|
3201
|
+
for (const branch of el.elseIfBranches) {
|
|
3202
|
+
elseIfBranchData.push({
|
|
3203
|
+
label: branch.label,
|
|
3204
|
+
indices: collectMsgIndices(branch.children)
|
|
3205
|
+
});
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
2209
3208
|
const elseIndices = collectMsgIndices(el.elseChildren);
|
|
2210
|
-
const allIndices = [
|
|
3209
|
+
const allIndices = [
|
|
3210
|
+
...ifIndices,
|
|
3211
|
+
...elseIfBranchData.flatMap((b) => b.indices),
|
|
3212
|
+
...elseIndices
|
|
3213
|
+
];
|
|
2211
3214
|
if (allIndices.length === 0) continue;
|
|
2212
3215
|
let minStep = Infinity;
|
|
2213
3216
|
let maxStep = -Infinity;
|
|
@@ -2245,6 +3248,32 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2245
3248
|
italic: false,
|
|
2246
3249
|
blockLine: el.lineNumber
|
|
2247
3250
|
});
|
|
3251
|
+
for (const branchData of elseIfBranchData) {
|
|
3252
|
+
if (branchData.indices.length > 0) {
|
|
3253
|
+
let firstBranchStep = Infinity;
|
|
3254
|
+
for (const mi of branchData.indices) {
|
|
3255
|
+
const first = msgToFirstStep.get(mi);
|
|
3256
|
+
if (first !== void 0)
|
|
3257
|
+
firstBranchStep = Math.min(firstBranchStep, first);
|
|
3258
|
+
}
|
|
3259
|
+
if (firstBranchStep < Infinity) {
|
|
3260
|
+
const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
|
|
3261
|
+
deferredLines.push({
|
|
3262
|
+
x1: frameX,
|
|
3263
|
+
y1: dividerY,
|
|
3264
|
+
x2: frameX + frameW,
|
|
3265
|
+
y2: dividerY
|
|
3266
|
+
});
|
|
3267
|
+
deferredLabels.push({
|
|
3268
|
+
x: frameX + 6,
|
|
3269
|
+
y: dividerY + 14,
|
|
3270
|
+
text: `else if ${branchData.label}`,
|
|
3271
|
+
bold: false,
|
|
3272
|
+
italic: true
|
|
3273
|
+
});
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
2248
3277
|
if (elseIndices.length > 0) {
|
|
2249
3278
|
let firstElseStep = Infinity;
|
|
2250
3279
|
for (const mi of elseIndices) {
|
|
@@ -2270,6 +3299,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2270
3299
|
}
|
|
2271
3300
|
}
|
|
2272
3301
|
renderBlockFrames(el.children, depth + 1);
|
|
3302
|
+
if (el.elseIfBranches) {
|
|
3303
|
+
for (const branch of el.elseIfBranches) {
|
|
3304
|
+
renderBlockFrames(branch.children, depth + 1);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
2273
3307
|
renderBlockFrames(el.elseChildren, depth + 1);
|
|
2274
3308
|
}
|
|
2275
3309
|
};
|
|
@@ -2291,7 +3325,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2291
3325
|
if (msg) coveredLines.push(msg.lineNumber);
|
|
2292
3326
|
}
|
|
2293
3327
|
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 =
|
|
3328
|
+
const actFill = mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
|
|
2295
3329
|
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
3330
|
});
|
|
2297
3331
|
for (const ln of deferredLines) {
|
|
@@ -2417,6 +3451,91 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2417
3451
|
}
|
|
2418
3452
|
}
|
|
2419
3453
|
});
|
|
3454
|
+
const noteFill = isDark ? mix2(palette.surface, palette.bg, 50) : mix2(palette.bg, palette.surface, 15);
|
|
3455
|
+
const findAssociatedStep = (note) => {
|
|
3456
|
+
let bestMsgIndex = -1;
|
|
3457
|
+
let bestLine = -1;
|
|
3458
|
+
for (let mi = 0; mi < messages.length; mi++) {
|
|
3459
|
+
if (messages[mi].lineNumber < note.lineNumber && messages[mi].lineNumber > bestLine && !hiddenMsgIndices.has(mi)) {
|
|
3460
|
+
bestLine = messages[mi].lineNumber;
|
|
3461
|
+
bestMsgIndex = mi;
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
if (bestMsgIndex < 0) return -1;
|
|
3465
|
+
return msgToFirstStep.get(bestMsgIndex) ?? -1;
|
|
3466
|
+
};
|
|
3467
|
+
const renderNoteElements = (els) => {
|
|
3468
|
+
for (const el of els) {
|
|
3469
|
+
if (isSequenceNote(el)) {
|
|
3470
|
+
const px = participantX.get(el.participantId);
|
|
3471
|
+
if (px === void 0) continue;
|
|
3472
|
+
const si = findAssociatedStep(el);
|
|
3473
|
+
if (si < 0) continue;
|
|
3474
|
+
const noteY = stepY(si);
|
|
3475
|
+
const wrappedLines = wrapTextLines(el.text, NOTE_CHARS_PER_LINE);
|
|
3476
|
+
const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
|
|
3477
|
+
const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
|
|
3478
|
+
const noteW = Math.min(
|
|
3479
|
+
NOTE_MAX_W,
|
|
3480
|
+
Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
|
|
3481
|
+
);
|
|
3482
|
+
const isRight = el.position === "right";
|
|
3483
|
+
const noteX = isRight ? px + ACTIVATION_WIDTH + NOTE_GAP : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
|
|
3484
|
+
const noteTopY = noteY - noteH / 2;
|
|
3485
|
+
const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber));
|
|
3486
|
+
noteG.append("path").attr(
|
|
3487
|
+
"d",
|
|
3488
|
+
[
|
|
3489
|
+
`M ${noteX} ${noteTopY}`,
|
|
3490
|
+
`L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
|
|
3491
|
+
`L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
|
|
3492
|
+
`L ${noteX + noteW} ${noteTopY + noteH}`,
|
|
3493
|
+
`L ${noteX} ${noteTopY + noteH}`,
|
|
3494
|
+
"Z"
|
|
3495
|
+
].join(" ")
|
|
3496
|
+
).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
|
|
3497
|
+
noteG.append("path").attr(
|
|
3498
|
+
"d",
|
|
3499
|
+
[
|
|
3500
|
+
`M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
|
|
3501
|
+
`L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
|
|
3502
|
+
`L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
|
|
3503
|
+
].join(" ")
|
|
3504
|
+
).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
|
|
3505
|
+
const connectorNoteX = isRight ? noteX : noteX + noteW;
|
|
3506
|
+
const connectorLifeX = isRight ? px + ACTIVATION_WIDTH / 2 : px - ACTIVATION_WIDTH / 2;
|
|
3507
|
+
noteG.append("line").attr("x1", connectorNoteX).attr("y1", noteY).attr("x2", connectorLifeX).attr("y2", noteY).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("stroke-dasharray", "3 2").attr("class", "note-connector");
|
|
3508
|
+
wrappedLines.forEach((line3, li) => {
|
|
3509
|
+
const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
|
|
3510
|
+
const textEl = noteG.append("text").attr("x", noteX + NOTE_PAD_H).attr("y", textY).attr("fill", palette.text).attr("font-size", NOTE_FONT_SIZE).attr("class", "note-text");
|
|
3511
|
+
const spans = parseInlineMarkdown(line3);
|
|
3512
|
+
for (const span of spans) {
|
|
3513
|
+
if (span.href) {
|
|
3514
|
+
const a = textEl.append("a").attr("href", span.href);
|
|
3515
|
+
a.append("tspan").text(span.text).attr("fill", palette.primary).style("text-decoration", "underline");
|
|
3516
|
+
} else {
|
|
3517
|
+
const tspan = textEl.append("tspan").text(span.text);
|
|
3518
|
+
if (span.bold) tspan.attr("font-weight", "bold");
|
|
3519
|
+
if (span.italic) tspan.attr("font-style", "italic");
|
|
3520
|
+
if (span.code)
|
|
3521
|
+
tspan.attr("font-family", "monospace").attr("font-size", NOTE_FONT_SIZE - 1);
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
});
|
|
3525
|
+
} else if (isSequenceBlock(el)) {
|
|
3526
|
+
renderNoteElements(el.children);
|
|
3527
|
+
if (el.elseIfBranches) {
|
|
3528
|
+
for (const branch of el.elseIfBranches) {
|
|
3529
|
+
renderNoteElements(branch.children);
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
renderNoteElements(el.elseChildren);
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
};
|
|
3536
|
+
if (elements && elements.length > 0) {
|
|
3537
|
+
renderNoteElements(elements);
|
|
3538
|
+
}
|
|
2420
3539
|
}
|
|
2421
3540
|
function renderParticipant(svg, participant, cx, cy, palette, isDark) {
|
|
2422
3541
|
const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
|
|
@@ -2458,11 +3577,11 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark) {
|
|
|
2458
3577
|
isActor ? PARTICIPANT_BOX_HEIGHT + 14 : PARTICIPANT_BOX_HEIGHT / 2 + 5
|
|
2459
3578
|
).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 13).attr("font-weight", 500).text(participant.label);
|
|
2460
3579
|
}
|
|
2461
|
-
var
|
|
3580
|
+
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
3581
|
var init_renderer = __esm({
|
|
2463
3582
|
"src/sequence/renderer.ts"() {
|
|
2464
3583
|
"use strict";
|
|
2465
|
-
|
|
3584
|
+
d3Selection2 = __toESM(require("d3-selection"), 1);
|
|
2466
3585
|
init_colors();
|
|
2467
3586
|
init_fonts();
|
|
2468
3587
|
init_parser();
|
|
@@ -2470,13 +3589,22 @@ var init_renderer = __esm({
|
|
|
2470
3589
|
PARTICIPANT_BOX_WIDTH = 120;
|
|
2471
3590
|
PARTICIPANT_BOX_HEIGHT = 50;
|
|
2472
3591
|
TOP_MARGIN = 20;
|
|
2473
|
-
|
|
3592
|
+
TITLE_HEIGHT2 = 30;
|
|
2474
3593
|
PARTICIPANT_Y_OFFSET = 10;
|
|
2475
3594
|
SERVICE_BORDER_RADIUS = 10;
|
|
2476
3595
|
MESSAGE_START_OFFSET = 30;
|
|
2477
3596
|
LIFELINE_TAIL = 30;
|
|
2478
3597
|
ARROWHEAD_SIZE = 8;
|
|
2479
|
-
|
|
3598
|
+
NOTE_MAX_W = 200;
|
|
3599
|
+
NOTE_FOLD = 10;
|
|
3600
|
+
NOTE_PAD_H = 8;
|
|
3601
|
+
NOTE_PAD_V = 6;
|
|
3602
|
+
NOTE_FONT_SIZE = 10;
|
|
3603
|
+
NOTE_LINE_H = 14;
|
|
3604
|
+
NOTE_GAP = 15;
|
|
3605
|
+
NOTE_CHAR_W = 6;
|
|
3606
|
+
NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
|
|
3607
|
+
fill = (palette, isDark) => mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
|
|
2480
3608
|
stroke = (palette) => palette.textMuted;
|
|
2481
3609
|
SW = 1.5;
|
|
2482
3610
|
W = PARTICIPANT_BOX_WIDTH;
|
|
@@ -2516,7 +3644,10 @@ __export(index_exports, {
|
|
|
2516
3644
|
hslToHex: () => hslToHex,
|
|
2517
3645
|
inferParticipantType: () => inferParticipantType,
|
|
2518
3646
|
isSequenceBlock: () => isSequenceBlock,
|
|
3647
|
+
isSequenceNote: () => isSequenceNote,
|
|
2519
3648
|
isValidHex: () => isValidHex,
|
|
3649
|
+
layoutGraph: () => layoutGraph,
|
|
3650
|
+
looksLikeFlowchart: () => looksLikeFlowchart,
|
|
2520
3651
|
looksLikeSequence: () => looksLikeSequence,
|
|
2521
3652
|
mute: () => mute,
|
|
2522
3653
|
nord: () => nord,
|
|
@@ -2527,6 +3658,7 @@ __export(index_exports, {
|
|
|
2527
3658
|
parseD3: () => parseD3,
|
|
2528
3659
|
parseDgmoChartType: () => parseDgmoChartType,
|
|
2529
3660
|
parseEChart: () => parseEChart,
|
|
3661
|
+
parseFlowchart: () => parseFlowchart,
|
|
2530
3662
|
parseQuadrant: () => parseQuadrant,
|
|
2531
3663
|
parseSequenceDgmo: () => parseSequenceDgmo,
|
|
2532
3664
|
parseTimelineDate: () => parseTimelineDate,
|
|
@@ -2534,6 +3666,8 @@ __export(index_exports, {
|
|
|
2534
3666
|
renderArcDiagram: () => renderArcDiagram,
|
|
2535
3667
|
renderD3ForExport: () => renderD3ForExport,
|
|
2536
3668
|
renderEChartsForExport: () => renderEChartsForExport,
|
|
3669
|
+
renderFlowchart: () => renderFlowchart,
|
|
3670
|
+
renderFlowchartForExport: () => renderFlowchartForExport,
|
|
2537
3671
|
renderQuadrant: () => renderQuadrant,
|
|
2538
3672
|
renderSequenceDiagram: () => renderSequenceDiagram,
|
|
2539
3673
|
renderSlopeChart: () => renderSlopeChart,
|
|
@@ -2549,51 +3683,7 @@ __export(index_exports, {
|
|
|
2549
3683
|
tokyoNightPalette: () => tokyoNightPalette
|
|
2550
3684
|
});
|
|
2551
3685
|
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
|
-
}
|
|
3686
|
+
init_dgmo_router();
|
|
2597
3687
|
|
|
2598
3688
|
// src/chart.ts
|
|
2599
3689
|
init_colors();
|
|
@@ -2955,7 +4045,7 @@ function parseEChart(content, palette) {
|
|
|
2955
4045
|
function buildEChartsOption(parsed, palette, isDark) {
|
|
2956
4046
|
const textColor = palette.text;
|
|
2957
4047
|
const axisLineColor = palette.border;
|
|
2958
|
-
const gridOpacity = isDark ? 0.7 : 0.
|
|
4048
|
+
const gridOpacity = isDark ? 0.7 : 0.55;
|
|
2959
4049
|
const colors = getSeriesColors(palette);
|
|
2960
4050
|
if (parsed.error) {
|
|
2961
4051
|
return {};
|
|
@@ -3661,7 +4751,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
|
|
|
3661
4751
|
const textColor = palette.text;
|
|
3662
4752
|
const axisLineColor = palette.border;
|
|
3663
4753
|
const splitLineColor = palette.border;
|
|
3664
|
-
const gridOpacity = isDark ? 0.7 : 0.
|
|
4754
|
+
const gridOpacity = isDark ? 0.7 : 0.55;
|
|
3665
4755
|
const colors = getSeriesColors(palette);
|
|
3666
4756
|
const titleConfig = parsed.title ? {
|
|
3667
4757
|
text: parsed.title,
|
|
@@ -4074,8 +5164,8 @@ async function renderEChartsForExport(content, theme, palette) {
|
|
|
4074
5164
|
|
|
4075
5165
|
// src/d3.ts
|
|
4076
5166
|
var d3Scale = __toESM(require("d3-scale"), 1);
|
|
4077
|
-
var
|
|
4078
|
-
var
|
|
5167
|
+
var d3Selection3 = __toESM(require("d3-selection"), 1);
|
|
5168
|
+
var d3Shape2 = __toESM(require("d3-shape"), 1);
|
|
4079
5169
|
var d3Array = __toESM(require("d3-array"), 1);
|
|
4080
5170
|
var import_d3_cloud = __toESM(require("d3-cloud"), 1);
|
|
4081
5171
|
init_fonts();
|
|
@@ -4182,10 +5272,10 @@ function parseD3(content, palette) {
|
|
|
4182
5272
|
let currentArcGroup = null;
|
|
4183
5273
|
let currentTimelineGroup = null;
|
|
4184
5274
|
for (let i = 0; i < lines.length; i++) {
|
|
4185
|
-
const
|
|
5275
|
+
const line3 = lines[i].trim();
|
|
4186
5276
|
const lineNumber = i + 1;
|
|
4187
|
-
if (!
|
|
4188
|
-
const sectionMatch =
|
|
5277
|
+
if (!line3) continue;
|
|
5278
|
+
const sectionMatch = line3.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
|
|
4189
5279
|
if (sectionMatch) {
|
|
4190
5280
|
if (result.type === "arc") {
|
|
4191
5281
|
const name = sectionMatch[1].trim();
|
|
@@ -4200,11 +5290,11 @@ function parseD3(content, palette) {
|
|
|
4200
5290
|
}
|
|
4201
5291
|
continue;
|
|
4202
5292
|
}
|
|
4203
|
-
if (
|
|
5293
|
+
if (line3.startsWith("#") || line3.startsWith("//")) {
|
|
4204
5294
|
continue;
|
|
4205
5295
|
}
|
|
4206
5296
|
if (result.type === "arc") {
|
|
4207
|
-
const linkMatch =
|
|
5297
|
+
const linkMatch = line3.match(
|
|
4208
5298
|
/^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?::\s*(\d+(?:\.\d+)?))?$/
|
|
4209
5299
|
);
|
|
4210
5300
|
if (linkMatch) {
|
|
@@ -4234,7 +5324,7 @@ function parseD3(content, palette) {
|
|
|
4234
5324
|
}
|
|
4235
5325
|
}
|
|
4236
5326
|
if (result.type === "timeline") {
|
|
4237
|
-
const eraMatch =
|
|
5327
|
+
const eraMatch = line3.match(
|
|
4238
5328
|
/^era\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
4239
5329
|
);
|
|
4240
5330
|
if (eraMatch) {
|
|
@@ -4247,7 +5337,7 @@ function parseD3(content, palette) {
|
|
|
4247
5337
|
});
|
|
4248
5338
|
continue;
|
|
4249
5339
|
}
|
|
4250
|
-
const markerMatch =
|
|
5340
|
+
const markerMatch = line3.match(
|
|
4251
5341
|
/^marker\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
4252
5342
|
);
|
|
4253
5343
|
if (markerMatch) {
|
|
@@ -4262,7 +5352,7 @@ function parseD3(content, palette) {
|
|
|
4262
5352
|
}
|
|
4263
5353
|
}
|
|
4264
5354
|
if (result.type === "timeline") {
|
|
4265
|
-
const durationMatch =
|
|
5355
|
+
const durationMatch = line3.match(
|
|
4266
5356
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d+(?:\.\d{1,2})?)([dwmy])\s*:\s*(.+)$/
|
|
4267
5357
|
);
|
|
4268
5358
|
if (durationMatch) {
|
|
@@ -4281,7 +5371,7 @@ function parseD3(content, palette) {
|
|
|
4281
5371
|
});
|
|
4282
5372
|
continue;
|
|
4283
5373
|
}
|
|
4284
|
-
const rangeMatch =
|
|
5374
|
+
const rangeMatch = line3.match(
|
|
4285
5375
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
4286
5376
|
);
|
|
4287
5377
|
if (rangeMatch) {
|
|
@@ -4295,7 +5385,7 @@ function parseD3(content, palette) {
|
|
|
4295
5385
|
});
|
|
4296
5386
|
continue;
|
|
4297
5387
|
}
|
|
4298
|
-
const pointMatch =
|
|
5388
|
+
const pointMatch = line3.match(
|
|
4299
5389
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
4300
5390
|
);
|
|
4301
5391
|
if (pointMatch) {
|
|
@@ -4310,7 +5400,7 @@ function parseD3(content, palette) {
|
|
|
4310
5400
|
}
|
|
4311
5401
|
}
|
|
4312
5402
|
if (result.type === "venn") {
|
|
4313
|
-
const overlapMatch =
|
|
5403
|
+
const overlapMatch = line3.match(
|
|
4314
5404
|
/^(.+?&.+?)\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
|
|
4315
5405
|
);
|
|
4316
5406
|
if (overlapMatch) {
|
|
@@ -4320,7 +5410,7 @@ function parseD3(content, palette) {
|
|
|
4320
5410
|
result.vennOverlaps.push({ sets, size, label, lineNumber });
|
|
4321
5411
|
continue;
|
|
4322
5412
|
}
|
|
4323
|
-
const setMatch =
|
|
5413
|
+
const setMatch = line3.match(
|
|
4324
5414
|
/^(.+?)(?:\(([^)]+)\))?\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
|
|
4325
5415
|
);
|
|
4326
5416
|
if (setMatch) {
|
|
@@ -4333,7 +5423,7 @@ function parseD3(content, palette) {
|
|
|
4333
5423
|
}
|
|
4334
5424
|
}
|
|
4335
5425
|
if (result.type === "quadrant") {
|
|
4336
|
-
const xAxisMatch =
|
|
5426
|
+
const xAxisMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
|
|
4337
5427
|
if (xAxisMatch) {
|
|
4338
5428
|
const parts = xAxisMatch[1].split(",").map((s) => s.trim());
|
|
4339
5429
|
if (parts.length >= 2) {
|
|
@@ -4342,7 +5432,7 @@ function parseD3(content, palette) {
|
|
|
4342
5432
|
}
|
|
4343
5433
|
continue;
|
|
4344
5434
|
}
|
|
4345
|
-
const yAxisMatch =
|
|
5435
|
+
const yAxisMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
|
|
4346
5436
|
if (yAxisMatch) {
|
|
4347
5437
|
const parts = yAxisMatch[1].split(",").map((s) => s.trim());
|
|
4348
5438
|
if (parts.length >= 2) {
|
|
@@ -4352,7 +5442,7 @@ function parseD3(content, palette) {
|
|
|
4352
5442
|
continue;
|
|
4353
5443
|
}
|
|
4354
5444
|
const quadrantLabelRe = /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i;
|
|
4355
|
-
const quadrantMatch =
|
|
5445
|
+
const quadrantMatch = line3.match(quadrantLabelRe);
|
|
4356
5446
|
if (quadrantMatch) {
|
|
4357
5447
|
const position = quadrantMatch[1].toLowerCase();
|
|
4358
5448
|
const labelPart = quadrantMatch[2].trim();
|
|
@@ -4368,7 +5458,7 @@ function parseD3(content, palette) {
|
|
|
4368
5458
|
result.quadrantLabels.bottomRight = label;
|
|
4369
5459
|
continue;
|
|
4370
5460
|
}
|
|
4371
|
-
const pointMatch =
|
|
5461
|
+
const pointMatch = line3.match(
|
|
4372
5462
|
/^(.+?):\s*([0-9]*\.?[0-9]+)\s*,\s*([0-9]*\.?[0-9]+)\s*$/
|
|
4373
5463
|
);
|
|
4374
5464
|
if (pointMatch) {
|
|
@@ -4385,13 +5475,13 @@ function parseD3(content, palette) {
|
|
|
4385
5475
|
continue;
|
|
4386
5476
|
}
|
|
4387
5477
|
}
|
|
4388
|
-
const colonIndex =
|
|
5478
|
+
const colonIndex = line3.indexOf(":");
|
|
4389
5479
|
if (colonIndex !== -1) {
|
|
4390
|
-
const rawKey =
|
|
5480
|
+
const rawKey = line3.substring(0, colonIndex).trim();
|
|
4391
5481
|
const key = rawKey.toLowerCase();
|
|
4392
5482
|
const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
|
|
4393
5483
|
if (key === "chart") {
|
|
4394
|
-
const value =
|
|
5484
|
+
const value = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4395
5485
|
if (value === "slope" || value === "wordcloud" || value === "arc" || value === "timeline" || value === "venn" || value === "quadrant" || value === "sequence") {
|
|
4396
5486
|
result.type = value;
|
|
4397
5487
|
} else {
|
|
@@ -4401,35 +5491,35 @@ function parseD3(content, palette) {
|
|
|
4401
5491
|
continue;
|
|
4402
5492
|
}
|
|
4403
5493
|
if (key === "title") {
|
|
4404
|
-
result.title =
|
|
5494
|
+
result.title = line3.substring(colonIndex + 1).trim();
|
|
4405
5495
|
if (result.type === "quadrant") {
|
|
4406
5496
|
result.quadrantTitleLineNumber = lineNumber;
|
|
4407
5497
|
}
|
|
4408
5498
|
continue;
|
|
4409
5499
|
}
|
|
4410
5500
|
if (key === "orientation") {
|
|
4411
|
-
const v =
|
|
5501
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4412
5502
|
if (v === "horizontal" || v === "vertical") {
|
|
4413
5503
|
result.orientation = v;
|
|
4414
5504
|
}
|
|
4415
5505
|
continue;
|
|
4416
5506
|
}
|
|
4417
5507
|
if (key === "order") {
|
|
4418
|
-
const v =
|
|
5508
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4419
5509
|
if (v === "name" || v === "group" || v === "degree") {
|
|
4420
5510
|
result.arcOrder = v;
|
|
4421
5511
|
}
|
|
4422
5512
|
continue;
|
|
4423
5513
|
}
|
|
4424
5514
|
if (key === "sort") {
|
|
4425
|
-
const v =
|
|
5515
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4426
5516
|
if (v === "time" || v === "group") {
|
|
4427
5517
|
result.timelineSort = v;
|
|
4428
5518
|
}
|
|
4429
5519
|
continue;
|
|
4430
5520
|
}
|
|
4431
5521
|
if (key === "swimlanes") {
|
|
4432
|
-
const v =
|
|
5522
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4433
5523
|
if (v === "on") {
|
|
4434
5524
|
result.timelineSwimlanes = true;
|
|
4435
5525
|
} else if (v === "off") {
|
|
@@ -4438,7 +5528,7 @@ function parseD3(content, palette) {
|
|
|
4438
5528
|
continue;
|
|
4439
5529
|
}
|
|
4440
5530
|
if (key === "values") {
|
|
4441
|
-
const v =
|
|
5531
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4442
5532
|
if (v === "off") {
|
|
4443
5533
|
result.vennShowValues = false;
|
|
4444
5534
|
} else if (v === "on") {
|
|
@@ -4447,21 +5537,21 @@ function parseD3(content, palette) {
|
|
|
4447
5537
|
continue;
|
|
4448
5538
|
}
|
|
4449
5539
|
if (key === "rotate") {
|
|
4450
|
-
const v =
|
|
5540
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4451
5541
|
if (v === "none" || v === "mixed" || v === "angled") {
|
|
4452
5542
|
result.cloudOptions.rotate = v;
|
|
4453
5543
|
}
|
|
4454
5544
|
continue;
|
|
4455
5545
|
}
|
|
4456
5546
|
if (key === "max") {
|
|
4457
|
-
const v = parseInt(
|
|
5547
|
+
const v = parseInt(line3.substring(colonIndex + 1).trim(), 10);
|
|
4458
5548
|
if (!isNaN(v) && v > 0) {
|
|
4459
5549
|
result.cloudOptions.max = v;
|
|
4460
5550
|
}
|
|
4461
5551
|
continue;
|
|
4462
5552
|
}
|
|
4463
5553
|
if (key === "size") {
|
|
4464
|
-
const v =
|
|
5554
|
+
const v = line3.substring(colonIndex + 1).trim();
|
|
4465
5555
|
const parts = v.split(",").map((s) => parseInt(s.trim(), 10));
|
|
4466
5556
|
if (parts.length === 2 && parts.every((n) => !isNaN(n) && n > 0) && parts[0] < parts[1]) {
|
|
4467
5557
|
result.cloudOptions.minSize = parts[0];
|
|
@@ -4471,7 +5561,7 @@ function parseD3(content, palette) {
|
|
|
4471
5561
|
}
|
|
4472
5562
|
const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
|
|
4473
5563
|
const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
|
|
4474
|
-
const valuePart =
|
|
5564
|
+
const valuePart = line3.substring(colonIndex + 1).trim();
|
|
4475
5565
|
const values = valuePart.split(",").map((v) => v.trim());
|
|
4476
5566
|
const numericValues = [];
|
|
4477
5567
|
let allNumeric = true;
|
|
@@ -4502,15 +5592,15 @@ function parseD3(content, palette) {
|
|
|
4502
5592
|
}
|
|
4503
5593
|
}
|
|
4504
5594
|
if (result.type === "wordcloud") {
|
|
4505
|
-
if (colonIndex === -1 && !
|
|
4506
|
-
result.words.push({ text:
|
|
5595
|
+
if (colonIndex === -1 && !line3.includes(" ")) {
|
|
5596
|
+
result.words.push({ text: line3, weight: 10, lineNumber });
|
|
4507
5597
|
} else {
|
|
4508
|
-
freeformLines.push(
|
|
5598
|
+
freeformLines.push(line3);
|
|
4509
5599
|
}
|
|
4510
5600
|
continue;
|
|
4511
5601
|
}
|
|
4512
|
-
if (result.periods.length === 0 &&
|
|
4513
|
-
const periods =
|
|
5602
|
+
if (result.periods.length === 0 && line3.includes(",") && !line3.includes(":")) {
|
|
5603
|
+
const periods = line3.split(",").map((p) => p.trim()).filter(Boolean);
|
|
4514
5604
|
if (periods.length >= 2) {
|
|
4515
5605
|
result.periods = periods;
|
|
4516
5606
|
continue;
|
|
@@ -4733,7 +5823,7 @@ var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
|
|
|
4733
5823
|
var SLOPE_LABEL_FONT_SIZE = 14;
|
|
4734
5824
|
var SLOPE_CHAR_WIDTH = 8;
|
|
4735
5825
|
function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
4736
|
-
|
|
5826
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
4737
5827
|
const { periods, data, title } = parsed;
|
|
4738
5828
|
if (data.length === 0 || periods.length < 2) return;
|
|
4739
5829
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -4760,7 +5850,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4760
5850
|
const valuePadding = (maxVal - minVal) * 0.1 || 1;
|
|
4761
5851
|
const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
|
|
4762
5852
|
const xScale = d3Scale.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
|
|
4763
|
-
const svg =
|
|
5853
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
4764
5854
|
const g = svg.append("g").attr("transform", `translate(${SLOPE_MARGIN.left},${SLOPE_MARGIN.top})`);
|
|
4765
5855
|
const tooltip = createTooltip(container, palette, isDark);
|
|
4766
5856
|
if (title) {
|
|
@@ -4771,7 +5861,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4771
5861
|
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
5862
|
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
5863
|
}
|
|
4774
|
-
const lineGen =
|
|
5864
|
+
const lineGen = d3Shape2.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
|
|
4775
5865
|
data.forEach((item, idx) => {
|
|
4776
5866
|
const color = item.color ?? colors[idx % colors.length];
|
|
4777
5867
|
const firstVal = item.values[0];
|
|
@@ -4834,11 +5924,11 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4834
5924
|
const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
|
|
4835
5925
|
const totalHeight = (lines.length - 1) * lineHeight;
|
|
4836
5926
|
const startDy = -totalHeight / 2;
|
|
4837
|
-
lines.forEach((
|
|
5927
|
+
lines.forEach((line3, li) => {
|
|
4838
5928
|
labelEl.append("tspan").attr("x", lastX + 10).attr(
|
|
4839
5929
|
"dy",
|
|
4840
5930
|
li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
|
|
4841
|
-
).text(
|
|
5931
|
+
).text(line3);
|
|
4842
5932
|
});
|
|
4843
5933
|
}
|
|
4844
5934
|
});
|
|
@@ -4933,7 +6023,7 @@ function orderArcNodes(links, order, groups) {
|
|
|
4933
6023
|
}
|
|
4934
6024
|
var ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
|
|
4935
6025
|
function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
4936
|
-
|
|
6026
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
4937
6027
|
const { links, title, orientation, arcOrder, arcNodeGroups } = parsed;
|
|
4938
6028
|
if (links.length === 0) return;
|
|
4939
6029
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -4970,7 +6060,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4970
6060
|
const values = links.map((l) => l.value);
|
|
4971
6061
|
const [minVal, maxVal] = d3Array.extent(values);
|
|
4972
6062
|
const strokeScale = d3Scale.scaleLinear().domain([minVal, maxVal]).range([1.5, 6]);
|
|
4973
|
-
const svg =
|
|
6063
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
4974
6064
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
4975
6065
|
if (title) {
|
|
4976
6066
|
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 +6075,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4985
6075
|
function handleMouseEnter(hovered) {
|
|
4986
6076
|
const connected = neighbors.get(hovered);
|
|
4987
6077
|
g.selectAll(".arc-link").each(function() {
|
|
4988
|
-
const el =
|
|
6078
|
+
const el = d3Selection3.select(this);
|
|
4989
6079
|
const src = el.attr("data-source");
|
|
4990
6080
|
const tgt = el.attr("data-target");
|
|
4991
6081
|
const isRelated = src === hovered || tgt === hovered;
|
|
4992
6082
|
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
4993
6083
|
});
|
|
4994
6084
|
g.selectAll(".arc-node").each(function() {
|
|
4995
|
-
const el =
|
|
6085
|
+
const el = d3Selection3.select(this);
|
|
4996
6086
|
const name = el.attr("data-node");
|
|
4997
6087
|
const isRelated = name === hovered || connected.has(name);
|
|
4998
6088
|
el.attr("opacity", isRelated ? 1 : FADE_OPACITY);
|
|
@@ -5017,23 +6107,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
5017
6107
|
const members = groupNodeSets.get(groupName);
|
|
5018
6108
|
if (!members) return;
|
|
5019
6109
|
g.selectAll(".arc-link").each(function() {
|
|
5020
|
-
const el =
|
|
6110
|
+
const el = d3Selection3.select(this);
|
|
5021
6111
|
const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
|
|
5022
6112
|
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
5023
6113
|
});
|
|
5024
6114
|
g.selectAll(".arc-node").each(function() {
|
|
5025
|
-
const el =
|
|
6115
|
+
const el = d3Selection3.select(this);
|
|
5026
6116
|
el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY);
|
|
5027
6117
|
});
|
|
5028
6118
|
g.selectAll(".arc-group-band").each(function() {
|
|
5029
|
-
const el =
|
|
6119
|
+
const el = d3Selection3.select(this);
|
|
5030
6120
|
el.attr(
|
|
5031
6121
|
"fill-opacity",
|
|
5032
6122
|
el.attr("data-group") === groupName ? 0.18 : 0.03
|
|
5033
6123
|
);
|
|
5034
6124
|
});
|
|
5035
6125
|
g.selectAll(".arc-group-label").each(function() {
|
|
5036
|
-
const el =
|
|
6126
|
+
const el = d3Selection3.select(this);
|
|
5037
6127
|
el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
|
|
5038
6128
|
});
|
|
5039
6129
|
}
|
|
@@ -5236,7 +6326,14 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
|
|
|
5236
6326
|
const firstYear = Math.ceil(domainMin);
|
|
5237
6327
|
const lastYear = Math.floor(domainMax);
|
|
5238
6328
|
if (lastYear >= firstYear + 1) {
|
|
5239
|
-
|
|
6329
|
+
const yearSpan = lastYear - firstYear;
|
|
6330
|
+
let step = 1;
|
|
6331
|
+
if (yearSpan > 80) step = 20;
|
|
6332
|
+
else if (yearSpan > 40) step = 10;
|
|
6333
|
+
else if (yearSpan > 20) step = 5;
|
|
6334
|
+
else if (yearSpan > 10) step = 2;
|
|
6335
|
+
const alignedFirst = Math.ceil(firstYear / step) * step;
|
|
6336
|
+
for (let y = alignedFirst; y <= lastYear; y += step) {
|
|
5240
6337
|
ticks.push({ pos: scale(y), label: String(y) });
|
|
5241
6338
|
}
|
|
5242
6339
|
} else if (span > 0.25) {
|
|
@@ -5336,7 +6433,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
|
|
|
5336
6433
|
function hideEventDatesOnScale(g) {
|
|
5337
6434
|
g.selectAll(".tl-event-date").remove();
|
|
5338
6435
|
g.selectAll(".tl-scale-tick").each(function() {
|
|
5339
|
-
const el =
|
|
6436
|
+
const el = d3Selection3.select(this);
|
|
5340
6437
|
const isDashed = el.attr("stroke-dasharray");
|
|
5341
6438
|
el.attr("opacity", isDashed ? 0.15 : 0.4);
|
|
5342
6439
|
});
|
|
@@ -5394,7 +6491,7 @@ function buildEraTooltipHtml(era) {
|
|
|
5394
6491
|
return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
|
|
5395
6492
|
}
|
|
5396
6493
|
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
5397
|
-
|
|
6494
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
5398
6495
|
const {
|
|
5399
6496
|
timelineEvents,
|
|
5400
6497
|
timelineGroups,
|
|
@@ -5446,13 +6543,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5446
6543
|
const FADE_OPACITY = 0.1;
|
|
5447
6544
|
function fadeToGroup(g, groupName) {
|
|
5448
6545
|
g.selectAll(".tl-event").each(function() {
|
|
5449
|
-
const el =
|
|
6546
|
+
const el = d3Selection3.select(this);
|
|
5450
6547
|
const evGroup = el.attr("data-group");
|
|
5451
6548
|
el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY);
|
|
5452
6549
|
});
|
|
5453
6550
|
g.selectAll(".tl-legend-item, .tl-lane-header").each(
|
|
5454
6551
|
function() {
|
|
5455
|
-
const el =
|
|
6552
|
+
const el = d3Selection3.select(this);
|
|
5456
6553
|
const name = el.attr("data-group");
|
|
5457
6554
|
el.attr("opacity", name === groupName ? 1 : FADE_OPACITY);
|
|
5458
6555
|
}
|
|
@@ -5464,7 +6561,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5464
6561
|
}
|
|
5465
6562
|
function fadeToEra(g, eraStart, eraEnd) {
|
|
5466
6563
|
g.selectAll(".tl-event").each(function() {
|
|
5467
|
-
const el =
|
|
6564
|
+
const el = d3Selection3.select(this);
|
|
5468
6565
|
const date = parseFloat(el.attr("data-date"));
|
|
5469
6566
|
const endDate = el.attr("data-end-date");
|
|
5470
6567
|
const evEnd = endDate ? parseFloat(endDate) : date;
|
|
@@ -5476,14 +6573,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5476
6573
|
FADE_OPACITY
|
|
5477
6574
|
);
|
|
5478
6575
|
g.selectAll(".tl-era").each(function() {
|
|
5479
|
-
const el =
|
|
6576
|
+
const el = d3Selection3.select(this);
|
|
5480
6577
|
const s = parseFloat(el.attr("data-era-start"));
|
|
5481
6578
|
const e = parseFloat(el.attr("data-era-end"));
|
|
5482
6579
|
const isSelf = s === eraStart && e === eraEnd;
|
|
5483
6580
|
el.attr("opacity", isSelf ? 1 : FADE_OPACITY);
|
|
5484
6581
|
});
|
|
5485
6582
|
g.selectAll(".tl-marker").each(function() {
|
|
5486
|
-
const el =
|
|
6583
|
+
const el = d3Selection3.select(this);
|
|
5487
6584
|
const date = parseFloat(el.attr("data-marker-date"));
|
|
5488
6585
|
const inside = date >= eraStart && date <= eraEnd;
|
|
5489
6586
|
el.attr("opacity", inside ? 1 : FADE_OPACITY);
|
|
@@ -5515,7 +6612,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5515
6612
|
const innerHeight = height - margin.top - margin.bottom;
|
|
5516
6613
|
const laneWidth = innerWidth / laneCount;
|
|
5517
6614
|
const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
|
|
5518
|
-
const svg =
|
|
6615
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5519
6616
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5520
6617
|
if (title) {
|
|
5521
6618
|
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 +6706,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5609
6706
|
const axisX = 20;
|
|
5610
6707
|
const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
|
|
5611
6708
|
const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
|
|
5612
|
-
const svg =
|
|
6709
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5613
6710
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5614
6711
|
if (title) {
|
|
5615
6712
|
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 +6829,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5732
6829
|
const totalGaps = (lanes.length - 1) * GROUP_GAP;
|
|
5733
6830
|
const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
|
|
5734
6831
|
const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
5735
|
-
const svg =
|
|
6832
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5736
6833
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5737
6834
|
if (title) {
|
|
5738
6835
|
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 +6933,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5836
6933
|
if (ev.uncertain) {
|
|
5837
6934
|
const gradientId = `uncertain-${ev.lineNumber}`;
|
|
5838
6935
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
5839
|
-
|
|
6936
|
+
d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
|
|
5840
6937
|
{ offset: "0%", opacity: 1 },
|
|
5841
6938
|
{ offset: "80%", opacity: 1 },
|
|
5842
6939
|
{ offset: "100%", opacity: 0 }
|
|
@@ -5877,7 +6974,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5877
6974
|
const innerHeight = height - margin.top - margin.bottom;
|
|
5878
6975
|
const rowH = Math.min(28, innerHeight / sorted.length);
|
|
5879
6976
|
const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
5880
|
-
const svg =
|
|
6977
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5881
6978
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5882
6979
|
if (title) {
|
|
5883
6980
|
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 +7072,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5975
7072
|
if (ev.uncertain) {
|
|
5976
7073
|
const gradientId = `uncertain-ts-${ev.lineNumber}`;
|
|
5977
7074
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
5978
|
-
|
|
7075
|
+
d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
|
|
5979
7076
|
{ offset: "0%", opacity: 1 },
|
|
5980
7077
|
{ offset: "80%", opacity: 1 },
|
|
5981
7078
|
{ offset: "100%", opacity: 0 }
|
|
@@ -6008,7 +7105,7 @@ function getRotateFn(mode) {
|
|
|
6008
7105
|
return () => 0;
|
|
6009
7106
|
}
|
|
6010
7107
|
function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
6011
|
-
|
|
7108
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6012
7109
|
const { words, title, cloudOptions } = parsed;
|
|
6013
7110
|
if (words.length === 0) return;
|
|
6014
7111
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -6029,7 +7126,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
|
|
|
6029
7126
|
return minSize + t * (maxSize - minSize);
|
|
6030
7127
|
};
|
|
6031
7128
|
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
6032
|
-
const svg =
|
|
7129
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6033
7130
|
if (title) {
|
|
6034
7131
|
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
7132
|
}
|
|
@@ -6052,7 +7149,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
|
|
|
6052
7149
|
}
|
|
6053
7150
|
function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
|
|
6054
7151
|
return new Promise((resolve) => {
|
|
6055
|
-
|
|
7152
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6056
7153
|
const { words, title, cloudOptions } = parsed;
|
|
6057
7154
|
if (words.length === 0) {
|
|
6058
7155
|
resolve();
|
|
@@ -6079,7 +7176,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
|
|
|
6079
7176
|
return minSize + t * (maxSize - minSize);
|
|
6080
7177
|
};
|
|
6081
7178
|
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
6082
|
-
const svg =
|
|
7179
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6083
7180
|
if (title) {
|
|
6084
7181
|
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
7182
|
}
|
|
@@ -6223,7 +7320,7 @@ function circlePathD(cx, cy, r) {
|
|
|
6223
7320
|
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
7321
|
}
|
|
6225
7322
|
function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
6226
|
-
|
|
7323
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6227
7324
|
const { vennSets, vennOverlaps, vennShowValues, title } = parsed;
|
|
6228
7325
|
if (vennSets.length < 2) return;
|
|
6229
7326
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -6288,7 +7385,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
|
|
|
6288
7385
|
const setColors = vennSets.map(
|
|
6289
7386
|
(s, i) => s.color ?? colors[i % colors.length]
|
|
6290
7387
|
);
|
|
6291
|
-
const svg =
|
|
7388
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6292
7389
|
const tooltip = createTooltip(container, palette, isDark);
|
|
6293
7390
|
if (title) {
|
|
6294
7391
|
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 +7531,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
|
|
|
6434
7531
|
});
|
|
6435
7532
|
}
|
|
6436
7533
|
function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
6437
|
-
|
|
7534
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6438
7535
|
const {
|
|
6439
7536
|
title,
|
|
6440
7537
|
quadrantLabels,
|
|
@@ -6466,7 +7563,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6466
7563
|
const chartHeight = height - margin.top - margin.bottom;
|
|
6467
7564
|
const xScale = d3Scale.scaleLinear().domain([0, 1]).range([0, chartWidth]);
|
|
6468
7565
|
const yScale = d3Scale.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
|
|
6469
|
-
const svg =
|
|
7566
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6470
7567
|
const tooltip = createTooltip(container, palette, isDark);
|
|
6471
7568
|
if (title) {
|
|
6472
7569
|
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 +7572,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6475
7572
|
).text(title);
|
|
6476
7573
|
if (onClickItem && quadrantTitleLineNumber) {
|
|
6477
7574
|
titleText.on("click", () => onClickItem(quadrantTitleLineNumber)).on("mouseenter", function() {
|
|
6478
|
-
|
|
7575
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6479
7576
|
}).on("mouseleave", function() {
|
|
6480
|
-
|
|
7577
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6481
7578
|
});
|
|
6482
7579
|
}
|
|
6483
7580
|
}
|
|
@@ -6560,9 +7657,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6560
7657
|
quadrantLabelTexts.on("click", (_, d) => {
|
|
6561
7658
|
if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
|
|
6562
7659
|
}).on("mouseenter", function() {
|
|
6563
|
-
|
|
7660
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6564
7661
|
}).on("mouseleave", function() {
|
|
6565
|
-
|
|
7662
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6566
7663
|
});
|
|
6567
7664
|
}
|
|
6568
7665
|
if (quadrantXAxis) {
|
|
@@ -6577,9 +7674,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6577
7674
|
if (onClickItem && quadrantXAxisLineNumber) {
|
|
6578
7675
|
[xLowLabel, xHighLabel].forEach((label) => {
|
|
6579
7676
|
label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
|
|
6580
|
-
|
|
7677
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6581
7678
|
}).on("mouseleave", function() {
|
|
6582
|
-
|
|
7679
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6583
7680
|
});
|
|
6584
7681
|
});
|
|
6585
7682
|
}
|
|
@@ -6598,9 +7695,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6598
7695
|
if (onClickItem && quadrantYAxisLineNumber) {
|
|
6599
7696
|
[yLowLabel, yHighLabel].forEach((label) => {
|
|
6600
7697
|
label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
|
|
6601
|
-
|
|
7698
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6602
7699
|
}).on("mouseleave", function() {
|
|
6603
|
-
|
|
7700
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6604
7701
|
});
|
|
6605
7702
|
});
|
|
6606
7703
|
}
|
|
@@ -6648,7 +7745,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6648
7745
|
pointsG.selectAll("g.point-group").each(function(_2, i) {
|
|
6649
7746
|
const pt = quadrantPoints[i];
|
|
6650
7747
|
const ptQuad = getPointQuadrant(pt.x, pt.y);
|
|
6651
|
-
|
|
7748
|
+
d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
|
|
6652
7749
|
});
|
|
6653
7750
|
}).on("mouseleave", () => {
|
|
6654
7751
|
quadrantRects.attr("opacity", 1);
|
|
@@ -6663,6 +7760,43 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6663
7760
|
var EXPORT_WIDTH = 1200;
|
|
6664
7761
|
var EXPORT_HEIGHT = 800;
|
|
6665
7762
|
async function renderD3ForExport(content, theme, palette) {
|
|
7763
|
+
const { parseDgmoChartType: parseDgmoChartType2 } = await Promise.resolve().then(() => (init_dgmo_router(), dgmo_router_exports));
|
|
7764
|
+
const detectedType = parseDgmoChartType2(content);
|
|
7765
|
+
if (detectedType === "flowchart") {
|
|
7766
|
+
const { parseFlowchart: parseFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_parser(), flowchart_parser_exports));
|
|
7767
|
+
const { layoutGraph: layoutGraph2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
|
|
7768
|
+
const { renderFlowchart: renderFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_renderer(), flowchart_renderer_exports));
|
|
7769
|
+
const isDark2 = theme === "dark";
|
|
7770
|
+
const { getPalette: getPalette3 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
|
|
7771
|
+
const effectivePalette2 = palette ?? (isDark2 ? getPalette3("nord").dark : getPalette3("nord").light);
|
|
7772
|
+
const fcParsed = parseFlowchart2(content, effectivePalette2);
|
|
7773
|
+
if (fcParsed.error || fcParsed.nodes.length === 0) return "";
|
|
7774
|
+
const layout = layoutGraph2(fcParsed);
|
|
7775
|
+
const container2 = document.createElement("div");
|
|
7776
|
+
container2.style.width = `${EXPORT_WIDTH}px`;
|
|
7777
|
+
container2.style.height = `${EXPORT_HEIGHT}px`;
|
|
7778
|
+
container2.style.position = "absolute";
|
|
7779
|
+
container2.style.left = "-9999px";
|
|
7780
|
+
document.body.appendChild(container2);
|
|
7781
|
+
try {
|
|
7782
|
+
renderFlowchart2(container2, fcParsed, layout, effectivePalette2, isDark2, void 0, {
|
|
7783
|
+
width: EXPORT_WIDTH,
|
|
7784
|
+
height: EXPORT_HEIGHT
|
|
7785
|
+
});
|
|
7786
|
+
const svgEl = container2.querySelector("svg");
|
|
7787
|
+
if (!svgEl) return "";
|
|
7788
|
+
if (theme === "transparent") {
|
|
7789
|
+
svgEl.style.background = "none";
|
|
7790
|
+
} else if (!svgEl.style.background) {
|
|
7791
|
+
svgEl.style.background = effectivePalette2.bg;
|
|
7792
|
+
}
|
|
7793
|
+
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
7794
|
+
svgEl.style.fontFamily = FONT_FAMILY;
|
|
7795
|
+
return svgEl.outerHTML;
|
|
7796
|
+
} finally {
|
|
7797
|
+
document.body.removeChild(container2);
|
|
7798
|
+
}
|
|
7799
|
+
}
|
|
6666
7800
|
const parsed = parseD3(content, palette);
|
|
6667
7801
|
if (parsed.error && parsed.type !== "sequence") {
|
|
6668
7802
|
const looksLikeSequence2 = /->|~>|<-/.test(content);
|
|
@@ -6757,17 +7891,17 @@ function parseQuadrant(content) {
|
|
|
6757
7891
|
};
|
|
6758
7892
|
const lines = content.split("\n");
|
|
6759
7893
|
for (let i = 0; i < lines.length; i++) {
|
|
6760
|
-
const
|
|
7894
|
+
const line3 = lines[i].trim();
|
|
6761
7895
|
const lineNumber = i + 1;
|
|
6762
|
-
if (!
|
|
6763
|
-
if (/^chart\s*:/i.test(
|
|
6764
|
-
const titleMatch =
|
|
7896
|
+
if (!line3 || line3.startsWith("#") || line3.startsWith("//")) continue;
|
|
7897
|
+
if (/^chart\s*:/i.test(line3)) continue;
|
|
7898
|
+
const titleMatch = line3.match(/^title\s*:\s*(.+)/i);
|
|
6765
7899
|
if (titleMatch) {
|
|
6766
7900
|
result.title = titleMatch[1].trim();
|
|
6767
7901
|
result.titleLineNumber = lineNumber;
|
|
6768
7902
|
continue;
|
|
6769
7903
|
}
|
|
6770
|
-
const xMatch =
|
|
7904
|
+
const xMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
|
|
6771
7905
|
if (xMatch) {
|
|
6772
7906
|
const parts = xMatch[1].split(",").map((s) => s.trim());
|
|
6773
7907
|
if (parts.length >= 2) {
|
|
@@ -6776,7 +7910,7 @@ function parseQuadrant(content) {
|
|
|
6776
7910
|
}
|
|
6777
7911
|
continue;
|
|
6778
7912
|
}
|
|
6779
|
-
const yMatch =
|
|
7913
|
+
const yMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
|
|
6780
7914
|
if (yMatch) {
|
|
6781
7915
|
const parts = yMatch[1].split(",").map((s) => s.trim());
|
|
6782
7916
|
if (parts.length >= 2) {
|
|
@@ -6785,7 +7919,7 @@ function parseQuadrant(content) {
|
|
|
6785
7919
|
}
|
|
6786
7920
|
continue;
|
|
6787
7921
|
}
|
|
6788
|
-
const posMatch =
|
|
7922
|
+
const posMatch = line3.match(
|
|
6789
7923
|
/^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i
|
|
6790
7924
|
);
|
|
6791
7925
|
if (posMatch) {
|
|
@@ -6806,7 +7940,7 @@ function parseQuadrant(content) {
|
|
|
6806
7940
|
}
|
|
6807
7941
|
continue;
|
|
6808
7942
|
}
|
|
6809
|
-
const pointMatch =
|
|
7943
|
+
const pointMatch = line3.match(DATA_POINT_RE);
|
|
6810
7944
|
if (pointMatch) {
|
|
6811
7945
|
const key = pointMatch[1].trim().toLowerCase();
|
|
6812
7946
|
if (!QUADRANT_POSITIONS.has(key)) {
|
|
@@ -6880,6 +8014,9 @@ function buildMermaidQuadrant(parsed, options = {}) {
|
|
|
6880
8014
|
}
|
|
6881
8015
|
|
|
6882
8016
|
// src/index.ts
|
|
8017
|
+
init_flowchart_parser();
|
|
8018
|
+
init_layout();
|
|
8019
|
+
init_flowchart_renderer();
|
|
6883
8020
|
init_renderer();
|
|
6884
8021
|
init_colors();
|
|
6885
8022
|
init_palettes();
|
|
@@ -6914,7 +8051,10 @@ init_palettes();
|
|
|
6914
8051
|
hslToHex,
|
|
6915
8052
|
inferParticipantType,
|
|
6916
8053
|
isSequenceBlock,
|
|
8054
|
+
isSequenceNote,
|
|
6917
8055
|
isValidHex,
|
|
8056
|
+
layoutGraph,
|
|
8057
|
+
looksLikeFlowchart,
|
|
6918
8058
|
looksLikeSequence,
|
|
6919
8059
|
mute,
|
|
6920
8060
|
nord,
|
|
@@ -6925,6 +8065,7 @@ init_palettes();
|
|
|
6925
8065
|
parseD3,
|
|
6926
8066
|
parseDgmoChartType,
|
|
6927
8067
|
parseEChart,
|
|
8068
|
+
parseFlowchart,
|
|
6928
8069
|
parseQuadrant,
|
|
6929
8070
|
parseSequenceDgmo,
|
|
6930
8071
|
parseTimelineDate,
|
|
@@ -6932,6 +8073,8 @@ init_palettes();
|
|
|
6932
8073
|
renderArcDiagram,
|
|
6933
8074
|
renderD3ForExport,
|
|
6934
8075
|
renderEChartsForExport,
|
|
8076
|
+
renderFlowchart,
|
|
8077
|
+
renderFlowchartForExport,
|
|
6935
8078
|
renderQuadrant,
|
|
6936
8079
|
renderSequenceDiagram,
|
|
6937
8080
|
renderSlopeChart,
|