@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.js
CHANGED
|
@@ -147,6 +147,7 @@ var init_participant_inference = __esm({
|
|
|
147
147
|
var parser_exports = {};
|
|
148
148
|
__export(parser_exports, {
|
|
149
149
|
isSequenceBlock: () => isSequenceBlock,
|
|
150
|
+
isSequenceNote: () => isSequenceNote,
|
|
150
151
|
isSequenceSection: () => isSequenceSection,
|
|
151
152
|
looksLikeSequence: () => looksLikeSequence,
|
|
152
153
|
parseSequenceDgmo: () => parseSequenceDgmo
|
|
@@ -157,6 +158,9 @@ function isSequenceBlock(el) {
|
|
|
157
158
|
function isSequenceSection(el) {
|
|
158
159
|
return "kind" in el && el.kind === "section";
|
|
159
160
|
}
|
|
161
|
+
function isSequenceNote(el) {
|
|
162
|
+
return "kind" in el && el.kind === "note";
|
|
163
|
+
}
|
|
160
164
|
function parseReturnLabel(rawLabel) {
|
|
161
165
|
if (!rawLabel) return { label: "" };
|
|
162
166
|
const arrowReturn = rawLabel.match(ARROW_RETURN_PATTERN);
|
|
@@ -167,19 +171,22 @@ function parseReturnLabel(rawLabel) {
|
|
|
167
171
|
if (umlReturn) {
|
|
168
172
|
return { label: umlReturn[1].trim(), returnLabel: umlReturn[2].trim() };
|
|
169
173
|
}
|
|
170
|
-
const
|
|
171
|
-
if (
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
const lastColon = rawLabel.lastIndexOf(":");
|
|
175
|
+
if (lastColon > 0 && lastColon < rawLabel.length - 1) {
|
|
176
|
+
const afterColon = rawLabel.substring(lastColon + 1);
|
|
177
|
+
if (!afterColon.startsWith("//")) {
|
|
178
|
+
const reqPart = rawLabel.substring(0, lastColon).trim();
|
|
179
|
+
const resPart = afterColon.trim();
|
|
180
|
+
if (reqPart && resPart) {
|
|
181
|
+
return { label: reqPart, returnLabel: resPart };
|
|
182
|
+
}
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
185
|
return { label: rawLabel };
|
|
179
186
|
}
|
|
180
|
-
function measureIndent(
|
|
187
|
+
function measureIndent(line3) {
|
|
181
188
|
let indent = 0;
|
|
182
|
-
for (const ch of
|
|
189
|
+
for (const ch of line3) {
|
|
183
190
|
if (ch === " ") indent++;
|
|
184
191
|
else if (ch === " ") indent += 4;
|
|
185
192
|
else break;
|
|
@@ -203,13 +210,17 @@ function parseSequenceDgmo(content) {
|
|
|
203
210
|
}
|
|
204
211
|
const lines = content.split("\n");
|
|
205
212
|
let hasExplicitChart = false;
|
|
213
|
+
let contentStarted = false;
|
|
206
214
|
let activeGroup = null;
|
|
215
|
+
const participantGroupMap = /* @__PURE__ */ new Map();
|
|
207
216
|
const blockStack = [];
|
|
208
217
|
const currentContainer = () => {
|
|
209
218
|
if (blockStack.length === 0) return result.elements;
|
|
210
219
|
const top = blockStack[blockStack.length - 1];
|
|
220
|
+
if (top.activeElseIfBranch) return top.activeElseIfBranch.children;
|
|
211
221
|
return top.inElse ? top.block.elseChildren : top.block.children;
|
|
212
222
|
};
|
|
223
|
+
let lastMsgFrom = null;
|
|
213
224
|
for (let i = 0; i < lines.length; i++) {
|
|
214
225
|
const raw = lines[i];
|
|
215
226
|
const trimmed = raw.trim();
|
|
@@ -220,9 +231,15 @@ function parseSequenceDgmo(content) {
|
|
|
220
231
|
}
|
|
221
232
|
const groupMatch = trimmed.match(GROUP_HEADING_PATTERN);
|
|
222
233
|
if (groupMatch) {
|
|
234
|
+
const groupColor = groupMatch[2]?.trim();
|
|
235
|
+
if (groupColor && groupColor.startsWith("#")) {
|
|
236
|
+
result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
contentStarted = true;
|
|
223
240
|
activeGroup = {
|
|
224
|
-
name: groupMatch[1],
|
|
225
|
-
color:
|
|
241
|
+
name: groupMatch[1].trim(),
|
|
242
|
+
color: groupColor || void 0,
|
|
226
243
|
participantIds: [],
|
|
227
244
|
lineNumber
|
|
228
245
|
};
|
|
@@ -232,7 +249,11 @@ function parseSequenceDgmo(content) {
|
|
|
232
249
|
if (activeGroup && measureIndent(raw) === 0) {
|
|
233
250
|
activeGroup = null;
|
|
234
251
|
}
|
|
235
|
-
if (trimmed.startsWith("
|
|
252
|
+
if (trimmed.startsWith("//")) continue;
|
|
253
|
+
if (trimmed.startsWith("#") && !trimmed.startsWith("##")) {
|
|
254
|
+
result.error = `Line ${lineNumber}: Use // for comments. # is reserved for group headings (##)`;
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
236
257
|
const sectionMatch = trimmed.match(SECTION_PATTERN);
|
|
237
258
|
if (sectionMatch) {
|
|
238
259
|
const sectionIndent = measureIndent(raw);
|
|
@@ -242,11 +263,16 @@ function parseSequenceDgmo(content) {
|
|
|
242
263
|
blockStack.pop();
|
|
243
264
|
}
|
|
244
265
|
const labelRaw = sectionMatch[1].trim();
|
|
245
|
-
const colorMatch = labelRaw.match(/^(.+?)\((
|
|
266
|
+
const colorMatch = labelRaw.match(/^(.+?)\(([^)]+)\)$/);
|
|
267
|
+
if (colorMatch && colorMatch[2].trim().startsWith("#")) {
|
|
268
|
+
result.error = `Line ${lineNumber}: Use a named color instead of hex (e.g., blue, red, teal)`;
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
contentStarted = true;
|
|
246
272
|
const section = {
|
|
247
273
|
kind: "section",
|
|
248
274
|
label: colorMatch ? colorMatch[1].trim() : labelRaw,
|
|
249
|
-
color: colorMatch ? colorMatch[2] : void 0,
|
|
275
|
+
color: colorMatch ? colorMatch[2].trim() : void 0,
|
|
250
276
|
lineNumber
|
|
251
277
|
};
|
|
252
278
|
result.sections.push(section);
|
|
@@ -256,24 +282,32 @@ function parseSequenceDgmo(content) {
|
|
|
256
282
|
const colonIndex = trimmed.indexOf(":");
|
|
257
283
|
if (colonIndex > 0 && !trimmed.includes("->") && !trimmed.includes("~>")) {
|
|
258
284
|
const key = trimmed.substring(0, colonIndex).trim().toLowerCase();
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (
|
|
263
|
-
|
|
285
|
+
if (key === "note" || key.startsWith("note ")) {
|
|
286
|
+
} else {
|
|
287
|
+
const value = trimmed.substring(colonIndex + 1).trim();
|
|
288
|
+
if (key === "chart") {
|
|
289
|
+
hasExplicitChart = true;
|
|
290
|
+
if (value.toLowerCase() !== "sequence") {
|
|
291
|
+
result.error = `Expected chart type "sequence", got "${value}"`;
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (contentStarted) {
|
|
297
|
+
result.error = `Line ${lineNumber}: Options like '${key}: ${value}' must appear before the first message or declaration`;
|
|
264
298
|
return result;
|
|
265
299
|
}
|
|
300
|
+
if (key === "title") {
|
|
301
|
+
result.title = value;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
result.options[key] = value;
|
|
266
305
|
continue;
|
|
267
306
|
}
|
|
268
|
-
if (key === "title") {
|
|
269
|
-
result.title = value;
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
result.options[key] = value;
|
|
273
|
-
continue;
|
|
274
307
|
}
|
|
275
308
|
const isAMatch = trimmed.match(IS_A_PATTERN);
|
|
276
309
|
if (isAMatch) {
|
|
310
|
+
contentStarted = true;
|
|
277
311
|
const id = isAMatch[1];
|
|
278
312
|
const typeStr = isAMatch[2].toLowerCase();
|
|
279
313
|
const remainder = isAMatch[3]?.trim() || "";
|
|
@@ -296,12 +330,19 @@ function parseSequenceDgmo(content) {
|
|
|
296
330
|
});
|
|
297
331
|
}
|
|
298
332
|
if (activeGroup && !activeGroup.participantIds.includes(id)) {
|
|
333
|
+
const existingGroup = participantGroupMap.get(id);
|
|
334
|
+
if (existingGroup) {
|
|
335
|
+
result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
299
338
|
activeGroup.participantIds.push(id);
|
|
339
|
+
participantGroupMap.set(id, activeGroup.name);
|
|
300
340
|
}
|
|
301
341
|
continue;
|
|
302
342
|
}
|
|
303
343
|
const posOnlyMatch = trimmed.match(POSITION_ONLY_PATTERN);
|
|
304
344
|
if (posOnlyMatch) {
|
|
345
|
+
contentStarted = true;
|
|
305
346
|
const id = posOnlyMatch[1];
|
|
306
347
|
const position = parseInt(posOnlyMatch[2], 10);
|
|
307
348
|
if (!result.participants.some((p) => p.id === id)) {
|
|
@@ -314,11 +355,18 @@ function parseSequenceDgmo(content) {
|
|
|
314
355
|
});
|
|
315
356
|
}
|
|
316
357
|
if (activeGroup && !activeGroup.participantIds.includes(id)) {
|
|
358
|
+
const existingGroup = participantGroupMap.get(id);
|
|
359
|
+
if (existingGroup) {
|
|
360
|
+
result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
317
363
|
activeGroup.participantIds.push(id);
|
|
364
|
+
participantGroupMap.set(id, activeGroup.name);
|
|
318
365
|
}
|
|
319
366
|
continue;
|
|
320
367
|
}
|
|
321
368
|
if (activeGroup && measureIndent(raw) > 0 && /^\S+$/.test(trimmed)) {
|
|
369
|
+
contentStarted = true;
|
|
322
370
|
const id = trimmed;
|
|
323
371
|
if (!result.participants.some((p) => p.id === id)) {
|
|
324
372
|
result.participants.push({
|
|
@@ -329,7 +377,13 @@ function parseSequenceDgmo(content) {
|
|
|
329
377
|
});
|
|
330
378
|
}
|
|
331
379
|
if (!activeGroup.participantIds.includes(id)) {
|
|
380
|
+
const existingGroup = participantGroupMap.get(id);
|
|
381
|
+
if (existingGroup) {
|
|
382
|
+
result.error = `Line ${lineNumber}: Participant '${id}' is already in group '${existingGroup}' \u2014 participants can only belong to one group`;
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
332
385
|
activeGroup.participantIds.push(id);
|
|
386
|
+
participantGroupMap.set(id, activeGroup.name);
|
|
333
387
|
}
|
|
334
388
|
continue;
|
|
335
389
|
}
|
|
@@ -337,28 +391,31 @@ function parseSequenceDgmo(content) {
|
|
|
337
391
|
while (blockStack.length > 0) {
|
|
338
392
|
const top = blockStack[blockStack.length - 1];
|
|
339
393
|
if (indent > top.indent) break;
|
|
340
|
-
if (indent === top.indent &&
|
|
341
|
-
|
|
394
|
+
if (indent === top.indent && (top.block.type === "if" || top.block.type === "parallel")) {
|
|
395
|
+
const lower = trimmed.toLowerCase();
|
|
396
|
+
if (lower === "else" || lower.startsWith("else if ")) break;
|
|
397
|
+
}
|
|
342
398
|
blockStack.pop();
|
|
343
399
|
}
|
|
344
|
-
let isAsync = false;
|
|
345
|
-
let arrowLine = trimmed;
|
|
346
400
|
const asyncPrefixMatch = trimmed.match(/^async\s+(.+)$/i);
|
|
347
|
-
if (asyncPrefixMatch) {
|
|
348
|
-
|
|
349
|
-
|
|
401
|
+
if (asyncPrefixMatch && ARROW_PATTERN.test(asyncPrefixMatch[1])) {
|
|
402
|
+
result.error = `Line ${lineNumber}: Use ~> for async messages: A ~> B: message`;
|
|
403
|
+
return result;
|
|
350
404
|
}
|
|
351
|
-
|
|
405
|
+
let isAsync = false;
|
|
406
|
+
const asyncArrowMatch = trimmed.match(
|
|
352
407
|
/^(\S+)\s*~>\s*([^\s:]+)\s*(?::\s*(.+))?$/
|
|
353
408
|
);
|
|
354
|
-
const syncArrowMatch =
|
|
409
|
+
const syncArrowMatch = trimmed.match(
|
|
355
410
|
/^(\S+)\s*->\s*([^\s:]+)\s*(?::\s*(.+))?$/
|
|
356
411
|
);
|
|
357
412
|
const arrowMatch = asyncArrowMatch || syncArrowMatch;
|
|
358
413
|
if (asyncArrowMatch) isAsync = true;
|
|
359
414
|
if (arrowMatch) {
|
|
415
|
+
contentStarted = true;
|
|
360
416
|
const from = arrowMatch[1];
|
|
361
417
|
const to = arrowMatch[2];
|
|
418
|
+
lastMsgFrom = from;
|
|
362
419
|
const rawLabel = arrowMatch[3]?.trim() || "";
|
|
363
420
|
const { label, returnLabel } = isAsync ? { label: rawLabel, returnLabel: void 0 } : parseReturnLabel(rawLabel);
|
|
364
421
|
const msg = {
|
|
@@ -391,6 +448,7 @@ function parseSequenceDgmo(content) {
|
|
|
391
448
|
}
|
|
392
449
|
const ifMatch = trimmed.match(/^if\s+(.+)$/i);
|
|
393
450
|
if (ifMatch) {
|
|
451
|
+
contentStarted = true;
|
|
394
452
|
const block = {
|
|
395
453
|
kind: "block",
|
|
396
454
|
type: "if",
|
|
@@ -405,6 +463,7 @@ function parseSequenceDgmo(content) {
|
|
|
405
463
|
}
|
|
406
464
|
const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
|
|
407
465
|
if (loopMatch) {
|
|
466
|
+
contentStarted = true;
|
|
408
467
|
const block = {
|
|
409
468
|
kind: "block",
|
|
410
469
|
type: "loop",
|
|
@@ -419,6 +478,7 @@ function parseSequenceDgmo(content) {
|
|
|
419
478
|
}
|
|
420
479
|
const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
|
|
421
480
|
if (parallelMatch) {
|
|
481
|
+
contentStarted = true;
|
|
422
482
|
const block = {
|
|
423
483
|
kind: "block",
|
|
424
484
|
type: "parallel",
|
|
@@ -431,15 +491,105 @@ function parseSequenceDgmo(content) {
|
|
|
431
491
|
blockStack.push({ block, indent, inElse: false });
|
|
432
492
|
continue;
|
|
433
493
|
}
|
|
494
|
+
const elseIfMatch = trimmed.match(/^else\s+if\s+(.+)$/i);
|
|
495
|
+
if (elseIfMatch) {
|
|
496
|
+
if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
|
|
497
|
+
const top = blockStack[blockStack.length - 1];
|
|
498
|
+
if (top.block.type === "parallel") {
|
|
499
|
+
result.error = `Line ${lineNumber}: parallel blocks don't support else if \u2014 list all concurrent messages directly inside the block`;
|
|
500
|
+
return result;
|
|
501
|
+
}
|
|
502
|
+
if (top.block.type === "if") {
|
|
503
|
+
const branch = { label: elseIfMatch[1].trim(), children: [] };
|
|
504
|
+
if (!top.block.elseIfBranches) top.block.elseIfBranches = [];
|
|
505
|
+
top.block.elseIfBranches.push(branch);
|
|
506
|
+
top.activeElseIfBranch = branch;
|
|
507
|
+
top.inElse = false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
434
512
|
if (trimmed.toLowerCase() === "else") {
|
|
435
|
-
if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent
|
|
436
|
-
blockStack[blockStack.length - 1]
|
|
513
|
+
if (blockStack.length > 0 && blockStack[blockStack.length - 1].indent === indent) {
|
|
514
|
+
const top = blockStack[blockStack.length - 1];
|
|
515
|
+
if (top.block.type === "parallel") {
|
|
516
|
+
result.error = `Line ${lineNumber}: parallel blocks don't support else \u2014 list all concurrent messages directly inside the block`;
|
|
517
|
+
return result;
|
|
518
|
+
}
|
|
519
|
+
if (top.block.type === "if") {
|
|
520
|
+
top.inElse = true;
|
|
521
|
+
top.activeElseIfBranch = void 0;
|
|
522
|
+
}
|
|
437
523
|
}
|
|
438
524
|
continue;
|
|
439
525
|
}
|
|
526
|
+
const noteSingleMatch = trimmed.match(NOTE_SINGLE);
|
|
527
|
+
if (noteSingleMatch) {
|
|
528
|
+
const notePosition = noteSingleMatch[1]?.toLowerCase() || "right";
|
|
529
|
+
let noteParticipant = noteSingleMatch[2] || null;
|
|
530
|
+
if (!noteParticipant) {
|
|
531
|
+
if (!lastMsgFrom) {
|
|
532
|
+
result.error = `Line ${lineNumber}: note requires a preceding message`;
|
|
533
|
+
return result;
|
|
534
|
+
}
|
|
535
|
+
noteParticipant = lastMsgFrom;
|
|
536
|
+
}
|
|
537
|
+
if (!result.participants.some((p) => p.id === noteParticipant)) {
|
|
538
|
+
result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
|
|
539
|
+
return result;
|
|
540
|
+
}
|
|
541
|
+
const note = {
|
|
542
|
+
kind: "note",
|
|
543
|
+
text: noteSingleMatch[3].trim(),
|
|
544
|
+
position: notePosition,
|
|
545
|
+
participantId: noteParticipant,
|
|
546
|
+
lineNumber
|
|
547
|
+
};
|
|
548
|
+
currentContainer().push(note);
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
const noteMultiMatch = trimmed.match(NOTE_MULTI);
|
|
552
|
+
if (noteMultiMatch) {
|
|
553
|
+
const notePosition = noteMultiMatch[1]?.toLowerCase() || "right";
|
|
554
|
+
let noteParticipant = noteMultiMatch[2] || null;
|
|
555
|
+
if (!noteParticipant) {
|
|
556
|
+
if (!lastMsgFrom) {
|
|
557
|
+
result.error = `Line ${lineNumber}: note requires a preceding message`;
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
noteParticipant = lastMsgFrom;
|
|
561
|
+
}
|
|
562
|
+
if (!result.participants.some((p) => p.id === noteParticipant)) {
|
|
563
|
+
result.error = `Line ${lineNumber}: note references unknown participant '${noteParticipant}'`;
|
|
564
|
+
return result;
|
|
565
|
+
}
|
|
566
|
+
const noteLines = [];
|
|
567
|
+
while (i + 1 < lines.length) {
|
|
568
|
+
const nextRaw = lines[i + 1];
|
|
569
|
+
const nextTrimmed = nextRaw.trim();
|
|
570
|
+
if (!nextTrimmed) break;
|
|
571
|
+
const nextIndent = measureIndent(nextRaw);
|
|
572
|
+
if (nextIndent <= indent) break;
|
|
573
|
+
noteLines.push(nextTrimmed);
|
|
574
|
+
i++;
|
|
575
|
+
}
|
|
576
|
+
if (noteLines.length === 0) {
|
|
577
|
+
result.error = `Line ${lineNumber}: multi-line note has no content \u2014 add indented lines or use 'note: text'`;
|
|
578
|
+
return result;
|
|
579
|
+
}
|
|
580
|
+
const note = {
|
|
581
|
+
kind: "note",
|
|
582
|
+
text: noteLines.join("\n"),
|
|
583
|
+
position: notePosition,
|
|
584
|
+
participantId: noteParticipant,
|
|
585
|
+
lineNumber
|
|
586
|
+
};
|
|
587
|
+
currentContainer().push(note);
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
440
590
|
}
|
|
441
591
|
if (!hasExplicitChart && result.messages.length === 0) {
|
|
442
|
-
const hasArrows = lines.some((
|
|
592
|
+
const hasArrows = lines.some((line3) => ARROW_PATTERN.test(line3.trim()));
|
|
443
593
|
if (!hasArrows) {
|
|
444
594
|
result.error = 'No "chart: sequence" header and no sequence content detected';
|
|
445
595
|
return result;
|
|
@@ -450,13 +600,13 @@ function parseSequenceDgmo(content) {
|
|
|
450
600
|
function looksLikeSequence(content) {
|
|
451
601
|
if (!content) return false;
|
|
452
602
|
const lines = content.split("\n");
|
|
453
|
-
return lines.some((
|
|
454
|
-
const trimmed =
|
|
455
|
-
if (trimmed.startsWith("
|
|
603
|
+
return lines.some((line3) => {
|
|
604
|
+
const trimmed = line3.trim();
|
|
605
|
+
if (trimmed.startsWith("//")) return false;
|
|
456
606
|
return ARROW_PATTERN.test(trimmed);
|
|
457
607
|
});
|
|
458
608
|
}
|
|
459
|
-
var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN;
|
|
609
|
+
var VALID_PARTICIPANT_TYPES, IS_A_PATTERN, POSITION_ONLY_PATTERN, GROUP_HEADING_PATTERN, SECTION_PATTERN, ARROW_PATTERN, ARROW_RETURN_PATTERN, UML_RETURN_PATTERN, NOTE_SINGLE, NOTE_MULTI;
|
|
460
610
|
var init_parser = __esm({
|
|
461
611
|
"src/sequence/parser.ts"() {
|
|
462
612
|
"use strict";
|
|
@@ -474,11 +624,13 @@ var init_parser = __esm({
|
|
|
474
624
|
]);
|
|
475
625
|
IS_A_PATTERN = /^(\S+)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
|
|
476
626
|
POSITION_ONLY_PATTERN = /^(\S+)\s+position\s+(-?\d+)$/i;
|
|
477
|
-
GROUP_HEADING_PATTERN = /^##\s+(
|
|
478
|
-
SECTION_PATTERN = /^==\s+(.+?)
|
|
627
|
+
GROUP_HEADING_PATTERN = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
|
|
628
|
+
SECTION_PATTERN = /^==\s+(.+?)(?:\s*==)?\s*$/;
|
|
479
629
|
ARROW_PATTERN = /\S+\s*(?:->|~>)\s*\S+/;
|
|
480
630
|
ARROW_RETURN_PATTERN = /^(.+?)\s*<-\s*(.+)$/;
|
|
481
631
|
UML_RETURN_PATTERN = /^(\w+\([^)]*\))\s*:\s*(.+)$/;
|
|
632
|
+
NOTE_SINGLE = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*:\s*(.+)$/i;
|
|
633
|
+
NOTE_MULTI = /^note(?:\s+(right|left)\s+of\s+(\S+))?\s*$/i;
|
|
482
634
|
}
|
|
483
635
|
});
|
|
484
636
|
|
|
@@ -557,6 +709,369 @@ var init_colors = __esm({
|
|
|
557
709
|
}
|
|
558
710
|
});
|
|
559
711
|
|
|
712
|
+
// src/graph/flowchart-parser.ts
|
|
713
|
+
var flowchart_parser_exports = {};
|
|
714
|
+
__export(flowchart_parser_exports, {
|
|
715
|
+
looksLikeFlowchart: () => looksLikeFlowchart,
|
|
716
|
+
parseFlowchart: () => parseFlowchart
|
|
717
|
+
});
|
|
718
|
+
function measureIndent2(line3) {
|
|
719
|
+
let indent = 0;
|
|
720
|
+
for (const ch of line3) {
|
|
721
|
+
if (ch === " ") indent++;
|
|
722
|
+
else if (ch === " ") indent += 4;
|
|
723
|
+
else break;
|
|
724
|
+
}
|
|
725
|
+
return indent;
|
|
726
|
+
}
|
|
727
|
+
function nodeId(shape, label) {
|
|
728
|
+
return `${shape}:${label.toLowerCase().trim()}`;
|
|
729
|
+
}
|
|
730
|
+
function extractColor(label, palette) {
|
|
731
|
+
const m = label.match(COLOR_SUFFIX_RE);
|
|
732
|
+
if (!m) return { label };
|
|
733
|
+
const colorName = m[1].trim();
|
|
734
|
+
return {
|
|
735
|
+
label: label.substring(0, m.index).trim(),
|
|
736
|
+
color: resolveColor(colorName, palette)
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
function parseNodeRef(text, palette) {
|
|
740
|
+
const t = text.trim();
|
|
741
|
+
if (!t) return null;
|
|
742
|
+
let m = t.match(/^\[\[([^\]]+)\]\]$/);
|
|
743
|
+
if (m) {
|
|
744
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
745
|
+
return { id: nodeId("subroutine", label), label, shape: "subroutine", color };
|
|
746
|
+
}
|
|
747
|
+
m = t.match(/^\[([^\]]+)~\]$/);
|
|
748
|
+
if (m) {
|
|
749
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
750
|
+
return { id: nodeId("document", label), label, shape: "document", color };
|
|
751
|
+
}
|
|
752
|
+
m = t.match(/^\[([^\]]+)\]$/);
|
|
753
|
+
if (m) {
|
|
754
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
755
|
+
return { id: nodeId("process", label), label, shape: "process", color };
|
|
756
|
+
}
|
|
757
|
+
m = t.match(/^\((.+)\)$/);
|
|
758
|
+
if (m) {
|
|
759
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
760
|
+
return { id: nodeId("terminal", label), label, shape: "terminal", color };
|
|
761
|
+
}
|
|
762
|
+
m = t.match(/^<([^>]+)>$/);
|
|
763
|
+
if (m) {
|
|
764
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
765
|
+
return { id: nodeId("decision", label), label, shape: "decision", color };
|
|
766
|
+
}
|
|
767
|
+
m = t.match(/^\/([^/]+)\/$/);
|
|
768
|
+
if (m) {
|
|
769
|
+
const { label, color } = extractColor(m[1].trim(), palette);
|
|
770
|
+
return { id: nodeId("io", label), label, shape: "io", color };
|
|
771
|
+
}
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
function splitArrows(line3) {
|
|
775
|
+
const segments = [];
|
|
776
|
+
const arrowRe = /(?:^|\s)-([^>\s(][^(>]*?)?\s*(?:\(([^)]+)\))?\s*->|(?:^|\s)->/g;
|
|
777
|
+
let lastIndex = 0;
|
|
778
|
+
const arrowPositions = [];
|
|
779
|
+
let searchFrom = 0;
|
|
780
|
+
while (searchFrom < line3.length) {
|
|
781
|
+
const idx = line3.indexOf("->", searchFrom);
|
|
782
|
+
if (idx === -1) break;
|
|
783
|
+
let arrowStart = idx;
|
|
784
|
+
let label;
|
|
785
|
+
let color;
|
|
786
|
+
if (idx > 0 && line3[idx - 1] !== " " && line3[idx - 1] !== " ") {
|
|
787
|
+
let scanBack = idx - 1;
|
|
788
|
+
while (scanBack > 0 && line3[scanBack] !== "-") {
|
|
789
|
+
scanBack--;
|
|
790
|
+
}
|
|
791
|
+
if (line3[scanBack] === "-" && (scanBack === 0 || /\s/.test(line3[scanBack - 1]))) {
|
|
792
|
+
let arrowContent = line3.substring(scanBack + 1, idx);
|
|
793
|
+
if (arrowContent.endsWith("-")) arrowContent = arrowContent.slice(0, -1);
|
|
794
|
+
const colorMatch = arrowContent.match(/\(([^)]+)\)\s*$/);
|
|
795
|
+
if (colorMatch) {
|
|
796
|
+
color = colorMatch[1].trim();
|
|
797
|
+
const labelPart = arrowContent.substring(0, colorMatch.index).trim();
|
|
798
|
+
if (labelPart) label = labelPart;
|
|
799
|
+
} else {
|
|
800
|
+
const labelPart = arrowContent.trim();
|
|
801
|
+
if (labelPart) label = labelPart;
|
|
802
|
+
}
|
|
803
|
+
arrowStart = scanBack;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
arrowPositions.push({ start: arrowStart, end: idx + 2, label, color });
|
|
807
|
+
searchFrom = idx + 2;
|
|
808
|
+
}
|
|
809
|
+
if (arrowPositions.length === 0) {
|
|
810
|
+
return [line3];
|
|
811
|
+
}
|
|
812
|
+
for (let i = 0; i < arrowPositions.length; i++) {
|
|
813
|
+
const arrow = arrowPositions[i];
|
|
814
|
+
const beforeText = line3.substring(lastIndex, arrow.start).trim();
|
|
815
|
+
if (beforeText || i === 0) {
|
|
816
|
+
segments.push(beforeText);
|
|
817
|
+
}
|
|
818
|
+
let arrowToken = "->";
|
|
819
|
+
if (arrow.label && arrow.color) arrowToken = `-${arrow.label}(${arrow.color})->`;
|
|
820
|
+
else if (arrow.label) arrowToken = `-${arrow.label}->`;
|
|
821
|
+
else if (arrow.color) arrowToken = `-(${arrow.color})->`;
|
|
822
|
+
segments.push(arrowToken);
|
|
823
|
+
lastIndex = arrow.end;
|
|
824
|
+
}
|
|
825
|
+
const remaining = line3.substring(lastIndex).trim();
|
|
826
|
+
if (remaining) {
|
|
827
|
+
segments.push(remaining);
|
|
828
|
+
}
|
|
829
|
+
return segments;
|
|
830
|
+
}
|
|
831
|
+
function parseArrowToken(token, palette) {
|
|
832
|
+
if (token === "->") return {};
|
|
833
|
+
const colorOnly = token.match(/^-\(([^)]+)\)->$/);
|
|
834
|
+
if (colorOnly) {
|
|
835
|
+
return { color: resolveColor(colorOnly[1].trim(), palette) };
|
|
836
|
+
}
|
|
837
|
+
const m = token.match(/^-(.+?)(?:\(([^)]+)\))?->$/);
|
|
838
|
+
if (m) {
|
|
839
|
+
const label = m[1]?.trim() || void 0;
|
|
840
|
+
const color = m[2] ? resolveColor(m[2].trim(), palette) : void 0;
|
|
841
|
+
return { label, color };
|
|
842
|
+
}
|
|
843
|
+
return {};
|
|
844
|
+
}
|
|
845
|
+
function parseFlowchart(content, palette) {
|
|
846
|
+
const lines = content.split("\n");
|
|
847
|
+
const result = {
|
|
848
|
+
type: "flowchart",
|
|
849
|
+
direction: "TB",
|
|
850
|
+
nodes: [],
|
|
851
|
+
edges: []
|
|
852
|
+
};
|
|
853
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
854
|
+
const indentStack = [];
|
|
855
|
+
let currentGroup = null;
|
|
856
|
+
const groups = [];
|
|
857
|
+
let contentStarted = false;
|
|
858
|
+
function getOrCreateNode(ref, lineNumber) {
|
|
859
|
+
const existing = nodeMap.get(ref.id);
|
|
860
|
+
if (existing) return existing;
|
|
861
|
+
const node = {
|
|
862
|
+
id: ref.id,
|
|
863
|
+
label: ref.label,
|
|
864
|
+
shape: ref.shape,
|
|
865
|
+
lineNumber,
|
|
866
|
+
...ref.color && { color: ref.color },
|
|
867
|
+
...currentGroup && { group: currentGroup.id }
|
|
868
|
+
};
|
|
869
|
+
nodeMap.set(ref.id, node);
|
|
870
|
+
result.nodes.push(node);
|
|
871
|
+
if (currentGroup && !currentGroup.nodeIds.includes(ref.id)) {
|
|
872
|
+
currentGroup.nodeIds.push(ref.id);
|
|
873
|
+
}
|
|
874
|
+
return node;
|
|
875
|
+
}
|
|
876
|
+
function addEdge(sourceId, targetId, lineNumber, label, color) {
|
|
877
|
+
const edge = {
|
|
878
|
+
source: sourceId,
|
|
879
|
+
target: targetId,
|
|
880
|
+
lineNumber,
|
|
881
|
+
...label && { label },
|
|
882
|
+
...color && { color }
|
|
883
|
+
};
|
|
884
|
+
result.edges.push(edge);
|
|
885
|
+
}
|
|
886
|
+
function processContentLine(trimmed, lineNumber, indent) {
|
|
887
|
+
contentStarted = true;
|
|
888
|
+
while (indentStack.length > 0) {
|
|
889
|
+
const top = indentStack[indentStack.length - 1];
|
|
890
|
+
if (top.indent >= indent) {
|
|
891
|
+
indentStack.pop();
|
|
892
|
+
} else {
|
|
893
|
+
break;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
const implicitSourceId = indentStack.length > 0 ? indentStack[indentStack.length - 1].nodeId : null;
|
|
897
|
+
const segments = splitArrows(trimmed);
|
|
898
|
+
if (segments.length === 1) {
|
|
899
|
+
const ref = parseNodeRef(segments[0], palette);
|
|
900
|
+
if (ref) {
|
|
901
|
+
const node = getOrCreateNode(ref, lineNumber);
|
|
902
|
+
indentStack.push({ nodeId: node.id, indent });
|
|
903
|
+
return node.id;
|
|
904
|
+
}
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
let lastNodeId = null;
|
|
908
|
+
let pendingArrow = null;
|
|
909
|
+
for (let i = 0; i < segments.length; i++) {
|
|
910
|
+
const seg = segments[i];
|
|
911
|
+
if (seg === "->" || /^-.+->$/.test(seg)) {
|
|
912
|
+
pendingArrow = parseArrowToken(seg, palette);
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
const ref = parseNodeRef(seg, palette);
|
|
916
|
+
if (!ref) continue;
|
|
917
|
+
const node = getOrCreateNode(ref, lineNumber);
|
|
918
|
+
if (pendingArrow !== null) {
|
|
919
|
+
const sourceId = lastNodeId ?? implicitSourceId;
|
|
920
|
+
if (sourceId) {
|
|
921
|
+
addEdge(
|
|
922
|
+
sourceId,
|
|
923
|
+
node.id,
|
|
924
|
+
lineNumber,
|
|
925
|
+
pendingArrow.label,
|
|
926
|
+
pendingArrow.color
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
pendingArrow = null;
|
|
930
|
+
} else if (lastNodeId === null && implicitSourceId === null) {
|
|
931
|
+
}
|
|
932
|
+
lastNodeId = node.id;
|
|
933
|
+
}
|
|
934
|
+
if (pendingArrow !== null && lastNodeId === null && implicitSourceId) {
|
|
935
|
+
}
|
|
936
|
+
if (segments.length >= 2 && segments[0] === "" && implicitSourceId && lastNodeId) {
|
|
937
|
+
}
|
|
938
|
+
if (lastNodeId) {
|
|
939
|
+
indentStack.push({ nodeId: lastNodeId, indent });
|
|
940
|
+
}
|
|
941
|
+
return lastNodeId;
|
|
942
|
+
}
|
|
943
|
+
for (let i = 0; i < lines.length; i++) {
|
|
944
|
+
const raw = lines[i];
|
|
945
|
+
const trimmed = raw.trim();
|
|
946
|
+
const lineNumber = i + 1;
|
|
947
|
+
const indent = measureIndent2(raw);
|
|
948
|
+
if (!trimmed) continue;
|
|
949
|
+
if (trimmed.startsWith("//")) continue;
|
|
950
|
+
const groupMatch = trimmed.match(GROUP_HEADING_RE);
|
|
951
|
+
if (groupMatch) {
|
|
952
|
+
const groupLabel = groupMatch[1].trim();
|
|
953
|
+
const groupColorName = groupMatch[2]?.trim();
|
|
954
|
+
const groupColor = groupColorName ? resolveColor(groupColorName, palette) : void 0;
|
|
955
|
+
currentGroup = {
|
|
956
|
+
id: `group:${groupLabel.toLowerCase()}`,
|
|
957
|
+
label: groupLabel,
|
|
958
|
+
nodeIds: [],
|
|
959
|
+
lineNumber,
|
|
960
|
+
...groupColor && { color: groupColor }
|
|
961
|
+
};
|
|
962
|
+
groups.push(currentGroup);
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
if (!contentStarted && trimmed.includes(":") && !trimmed.includes("->")) {
|
|
966
|
+
const colonIdx = trimmed.indexOf(":");
|
|
967
|
+
const key = trimmed.substring(0, colonIdx).trim().toLowerCase();
|
|
968
|
+
const value = trimmed.substring(colonIdx + 1).trim();
|
|
969
|
+
if (key === "chart") {
|
|
970
|
+
if (value.toLowerCase() !== "flowchart") {
|
|
971
|
+
result.error = `Line ${lineNumber}: Expected chart type "flowchart", got "${value}"`;
|
|
972
|
+
return result;
|
|
973
|
+
}
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
if (key === "title") {
|
|
977
|
+
result.title = value;
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
if (key === "direction") {
|
|
981
|
+
const dir = value.toUpperCase();
|
|
982
|
+
if (dir === "TB" || dir === "LR") {
|
|
983
|
+
result.direction = dir;
|
|
984
|
+
}
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
processContentLine(trimmed, lineNumber, indent);
|
|
990
|
+
}
|
|
991
|
+
if (groups.length > 0) result.groups = groups;
|
|
992
|
+
if (result.nodes.length === 0 && !result.error) {
|
|
993
|
+
result.error = "No nodes found. Add flowchart content with shape syntax like [Process] or (Start).";
|
|
994
|
+
}
|
|
995
|
+
return result;
|
|
996
|
+
}
|
|
997
|
+
function looksLikeFlowchart(content) {
|
|
998
|
+
if (!content.includes("->")) return false;
|
|
999
|
+
const hasShapeDelimiter = /\[[^\]]+\]/.test(content) || /\([^)]+\)/.test(content) || /<[^>]+>/.test(content) || /\/[^/]+\//.test(content);
|
|
1000
|
+
if (!hasShapeDelimiter) return false;
|
|
1001
|
+
const shapeNearArrow = /[\])][ \t]*-.*->/.test(content) || // shape ] or ) followed by arrow
|
|
1002
|
+
/->[ \t]*[\[(<\/]/.test(content);
|
|
1003
|
+
return shapeNearArrow;
|
|
1004
|
+
}
|
|
1005
|
+
var COLOR_SUFFIX_RE, GROUP_HEADING_RE;
|
|
1006
|
+
var init_flowchart_parser = __esm({
|
|
1007
|
+
"src/graph/flowchart-parser.ts"() {
|
|
1008
|
+
"use strict";
|
|
1009
|
+
init_colors();
|
|
1010
|
+
COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
|
|
1011
|
+
GROUP_HEADING_RE = /^##\s+(.+?)(?:\(([^)]+)\))?\s*$/;
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
// src/dgmo-router.ts
|
|
1016
|
+
var dgmo_router_exports = {};
|
|
1017
|
+
__export(dgmo_router_exports, {
|
|
1018
|
+
DGMO_CHART_TYPE_MAP: () => DGMO_CHART_TYPE_MAP,
|
|
1019
|
+
getDgmoFramework: () => getDgmoFramework,
|
|
1020
|
+
parseDgmoChartType: () => parseDgmoChartType
|
|
1021
|
+
});
|
|
1022
|
+
function getDgmoFramework(chartType) {
|
|
1023
|
+
return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
|
|
1024
|
+
}
|
|
1025
|
+
function parseDgmoChartType(content) {
|
|
1026
|
+
const lines = content.split("\n");
|
|
1027
|
+
for (const line3 of lines) {
|
|
1028
|
+
const trimmed = line3.trim();
|
|
1029
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
|
|
1030
|
+
continue;
|
|
1031
|
+
const match = trimmed.match(/^chart\s*:\s*(.+)/i);
|
|
1032
|
+
if (match) return match[1].trim().toLowerCase();
|
|
1033
|
+
}
|
|
1034
|
+
if (looksLikeSequence(content)) return "sequence";
|
|
1035
|
+
if (looksLikeFlowchart(content)) return "flowchart";
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1038
|
+
var DGMO_CHART_TYPE_MAP;
|
|
1039
|
+
var init_dgmo_router = __esm({
|
|
1040
|
+
"src/dgmo-router.ts"() {
|
|
1041
|
+
"use strict";
|
|
1042
|
+
init_parser();
|
|
1043
|
+
init_flowchart_parser();
|
|
1044
|
+
DGMO_CHART_TYPE_MAP = {
|
|
1045
|
+
// Standard charts (via ECharts)
|
|
1046
|
+
bar: "echart",
|
|
1047
|
+
line: "echart",
|
|
1048
|
+
"multi-line": "echart",
|
|
1049
|
+
area: "echart",
|
|
1050
|
+
pie: "echart",
|
|
1051
|
+
doughnut: "echart",
|
|
1052
|
+
radar: "echart",
|
|
1053
|
+
"polar-area": "echart",
|
|
1054
|
+
"bar-stacked": "echart",
|
|
1055
|
+
// ECharts
|
|
1056
|
+
scatter: "echart",
|
|
1057
|
+
sankey: "echart",
|
|
1058
|
+
chord: "echart",
|
|
1059
|
+
function: "echart",
|
|
1060
|
+
heatmap: "echart",
|
|
1061
|
+
funnel: "echart",
|
|
1062
|
+
// D3
|
|
1063
|
+
slope: "d3",
|
|
1064
|
+
wordcloud: "d3",
|
|
1065
|
+
arc: "d3",
|
|
1066
|
+
timeline: "d3",
|
|
1067
|
+
venn: "d3",
|
|
1068
|
+
quadrant: "d3",
|
|
1069
|
+
sequence: "d3",
|
|
1070
|
+
flowchart: "d3"
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
|
|
560
1075
|
// src/fonts.ts
|
|
561
1076
|
var FONT_FAMILY;
|
|
562
1077
|
var init_fonts = __esm({
|
|
@@ -1612,6 +2127,409 @@ var init_palettes = __esm({
|
|
|
1612
2127
|
}
|
|
1613
2128
|
});
|
|
1614
2129
|
|
|
2130
|
+
// src/graph/layout.ts
|
|
2131
|
+
var layout_exports = {};
|
|
2132
|
+
__export(layout_exports, {
|
|
2133
|
+
layoutGraph: () => layoutGraph
|
|
2134
|
+
});
|
|
2135
|
+
import dagre from "@dagrejs/dagre";
|
|
2136
|
+
function computeNodeWidth(label, shape) {
|
|
2137
|
+
const base = Math.max(120, label.length * 9 + 40);
|
|
2138
|
+
if (shape === "subroutine") return base + 10;
|
|
2139
|
+
return base;
|
|
2140
|
+
}
|
|
2141
|
+
function computeNodeHeight(shape) {
|
|
2142
|
+
return shape === "decision" ? 60 : 50;
|
|
2143
|
+
}
|
|
2144
|
+
function layoutGraph(graph) {
|
|
2145
|
+
if (graph.nodes.length === 0) {
|
|
2146
|
+
return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
|
|
2147
|
+
}
|
|
2148
|
+
const g = new dagre.graphlib.Graph({ compound: true });
|
|
2149
|
+
g.setGraph({
|
|
2150
|
+
rankdir: graph.direction,
|
|
2151
|
+
nodesep: 50,
|
|
2152
|
+
ranksep: 60,
|
|
2153
|
+
edgesep: 20
|
|
2154
|
+
});
|
|
2155
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
2156
|
+
const nodeDataMap = /* @__PURE__ */ new Map();
|
|
2157
|
+
for (const node of graph.nodes) {
|
|
2158
|
+
nodeDataMap.set(node.id, node);
|
|
2159
|
+
}
|
|
2160
|
+
if (graph.groups) {
|
|
2161
|
+
for (const group of graph.groups) {
|
|
2162
|
+
g.setNode(group.id, {
|
|
2163
|
+
label: group.label,
|
|
2164
|
+
clusterLabelPos: "top"
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
for (const node of graph.nodes) {
|
|
2169
|
+
const width = computeNodeWidth(node.label, node.shape);
|
|
2170
|
+
const height = computeNodeHeight(node.shape);
|
|
2171
|
+
g.setNode(node.id, { label: node.label, width, height });
|
|
2172
|
+
if (node.group && graph.groups?.some((gr) => gr.id === node.group)) {
|
|
2173
|
+
g.setParent(node.id, node.group);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
const edgeDataMap = /* @__PURE__ */ new Map();
|
|
2177
|
+
for (const edge of graph.edges) {
|
|
2178
|
+
const key = `${edge.source}->${edge.target}`;
|
|
2179
|
+
edgeDataMap.set(key, edge);
|
|
2180
|
+
g.setEdge(edge.source, edge.target, {
|
|
2181
|
+
label: edge.label ?? ""
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
dagre.layout(g);
|
|
2185
|
+
const layoutNodes = graph.nodes.map((node) => {
|
|
2186
|
+
const pos = g.node(node.id);
|
|
2187
|
+
return {
|
|
2188
|
+
id: node.id,
|
|
2189
|
+
label: node.label,
|
|
2190
|
+
shape: node.shape,
|
|
2191
|
+
color: node.color,
|
|
2192
|
+
group: node.group,
|
|
2193
|
+
lineNumber: node.lineNumber,
|
|
2194
|
+
x: pos.x,
|
|
2195
|
+
y: pos.y,
|
|
2196
|
+
width: pos.width,
|
|
2197
|
+
height: pos.height
|
|
2198
|
+
};
|
|
2199
|
+
});
|
|
2200
|
+
const layoutEdges = graph.edges.map((edge) => {
|
|
2201
|
+
const edgeData = g.edge(edge.source, edge.target);
|
|
2202
|
+
return {
|
|
2203
|
+
source: edge.source,
|
|
2204
|
+
target: edge.target,
|
|
2205
|
+
points: edgeData?.points ?? [],
|
|
2206
|
+
label: edge.label,
|
|
2207
|
+
color: edge.color,
|
|
2208
|
+
lineNumber: edge.lineNumber
|
|
2209
|
+
};
|
|
2210
|
+
});
|
|
2211
|
+
const layoutGroups = [];
|
|
2212
|
+
if (graph.groups) {
|
|
2213
|
+
const nodeMap = new Map(layoutNodes.map((n) => [n.id, n]));
|
|
2214
|
+
for (const group of graph.groups) {
|
|
2215
|
+
const members = group.nodeIds.map((id) => nodeMap.get(id)).filter((n) => n !== void 0);
|
|
2216
|
+
if (members.length === 0) {
|
|
2217
|
+
layoutGroups.push({
|
|
2218
|
+
id: group.id,
|
|
2219
|
+
label: group.label,
|
|
2220
|
+
color: group.color,
|
|
2221
|
+
x: 0,
|
|
2222
|
+
y: 0,
|
|
2223
|
+
width: 0,
|
|
2224
|
+
height: 0
|
|
2225
|
+
});
|
|
2226
|
+
continue;
|
|
2227
|
+
}
|
|
2228
|
+
let minX = Infinity;
|
|
2229
|
+
let minY = Infinity;
|
|
2230
|
+
let maxX = -Infinity;
|
|
2231
|
+
let maxY = -Infinity;
|
|
2232
|
+
for (const member of members) {
|
|
2233
|
+
const left = member.x - member.width / 2;
|
|
2234
|
+
const right = member.x + member.width / 2;
|
|
2235
|
+
const top = member.y - member.height / 2;
|
|
2236
|
+
const bottom = member.y + member.height / 2;
|
|
2237
|
+
if (left < minX) minX = left;
|
|
2238
|
+
if (right > maxX) maxX = right;
|
|
2239
|
+
if (top < minY) minY = top;
|
|
2240
|
+
if (bottom > maxY) maxY = bottom;
|
|
2241
|
+
}
|
|
2242
|
+
layoutGroups.push({
|
|
2243
|
+
id: group.id,
|
|
2244
|
+
label: group.label,
|
|
2245
|
+
color: group.color,
|
|
2246
|
+
x: minX - GROUP_PADDING,
|
|
2247
|
+
y: minY - GROUP_PADDING,
|
|
2248
|
+
width: maxX - minX + GROUP_PADDING * 2,
|
|
2249
|
+
height: maxY - minY + GROUP_PADDING * 2
|
|
2250
|
+
});
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
let totalWidth = 0;
|
|
2254
|
+
let totalHeight = 0;
|
|
2255
|
+
for (const node of layoutNodes) {
|
|
2256
|
+
const right = node.x + node.width / 2;
|
|
2257
|
+
const bottom = node.y + node.height / 2;
|
|
2258
|
+
if (right > totalWidth) totalWidth = right;
|
|
2259
|
+
if (bottom > totalHeight) totalHeight = bottom;
|
|
2260
|
+
}
|
|
2261
|
+
for (const group of layoutGroups) {
|
|
2262
|
+
const right = group.x + group.width;
|
|
2263
|
+
const bottom = group.y + group.height;
|
|
2264
|
+
if (right > totalWidth) totalWidth = right;
|
|
2265
|
+
if (bottom > totalHeight) totalHeight = bottom;
|
|
2266
|
+
}
|
|
2267
|
+
totalWidth += 40;
|
|
2268
|
+
totalHeight += 40;
|
|
2269
|
+
return {
|
|
2270
|
+
nodes: layoutNodes,
|
|
2271
|
+
edges: layoutEdges,
|
|
2272
|
+
groups: layoutGroups,
|
|
2273
|
+
width: totalWidth,
|
|
2274
|
+
height: totalHeight
|
|
2275
|
+
};
|
|
2276
|
+
}
|
|
2277
|
+
var GROUP_PADDING;
|
|
2278
|
+
var init_layout = __esm({
|
|
2279
|
+
"src/graph/layout.ts"() {
|
|
2280
|
+
"use strict";
|
|
2281
|
+
GROUP_PADDING = 20;
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
|
|
2285
|
+
// src/graph/flowchart-renderer.ts
|
|
2286
|
+
var flowchart_renderer_exports = {};
|
|
2287
|
+
__export(flowchart_renderer_exports, {
|
|
2288
|
+
renderFlowchart: () => renderFlowchart,
|
|
2289
|
+
renderFlowchartForExport: () => renderFlowchartForExport
|
|
2290
|
+
});
|
|
2291
|
+
import * as d3Selection from "d3-selection";
|
|
2292
|
+
import * as d3Shape from "d3-shape";
|
|
2293
|
+
function mix(a, b, pct) {
|
|
2294
|
+
const parse = (h) => {
|
|
2295
|
+
const r = h.replace("#", "");
|
|
2296
|
+
const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
|
|
2297
|
+
return [parseInt(f.substring(0, 2), 16), parseInt(f.substring(2, 4), 16), parseInt(f.substring(4, 6), 16)];
|
|
2298
|
+
};
|
|
2299
|
+
const [ar, ag, ab] = parse(a), [br, bg, bb] = parse(b), t = pct / 100;
|
|
2300
|
+
const c = (x, y) => Math.round(x * t + y * (1 - t)).toString(16).padStart(2, "0");
|
|
2301
|
+
return `#${c(ar, br)}${c(ag, bg)}${c(ab, bb)}`;
|
|
2302
|
+
}
|
|
2303
|
+
function nodeFill(palette, isDark, nodeColor) {
|
|
2304
|
+
if (nodeColor) {
|
|
2305
|
+
return mix(nodeColor, isDark ? palette.surface : palette.bg, 25);
|
|
2306
|
+
}
|
|
2307
|
+
return mix(palette.primary, isDark ? palette.surface : palette.bg, 15);
|
|
2308
|
+
}
|
|
2309
|
+
function nodeStroke(palette, nodeColor) {
|
|
2310
|
+
return nodeColor ?? palette.textMuted;
|
|
2311
|
+
}
|
|
2312
|
+
function renderTerminal(g, node, palette, isDark) {
|
|
2313
|
+
const w = node.width;
|
|
2314
|
+
const h = node.height;
|
|
2315
|
+
const rx = h / 2;
|
|
2316
|
+
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);
|
|
2317
|
+
}
|
|
2318
|
+
function renderProcess(g, node, palette, isDark) {
|
|
2319
|
+
const w = node.width;
|
|
2320
|
+
const h = node.height;
|
|
2321
|
+
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);
|
|
2322
|
+
}
|
|
2323
|
+
function renderDecision(g, node, palette, isDark) {
|
|
2324
|
+
const w = node.width / 2;
|
|
2325
|
+
const h = node.height / 2;
|
|
2326
|
+
const points = [
|
|
2327
|
+
`${0},${-h}`,
|
|
2328
|
+
// top
|
|
2329
|
+
`${w},${0}`,
|
|
2330
|
+
// right
|
|
2331
|
+
`${0},${h}`,
|
|
2332
|
+
// bottom
|
|
2333
|
+
`${-w},${0}`
|
|
2334
|
+
// left
|
|
2335
|
+
].join(" ");
|
|
2336
|
+
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);
|
|
2337
|
+
}
|
|
2338
|
+
function renderIO(g, node, palette, isDark) {
|
|
2339
|
+
const w = node.width / 2;
|
|
2340
|
+
const h = node.height / 2;
|
|
2341
|
+
const sk = IO_SKEW;
|
|
2342
|
+
const points = [
|
|
2343
|
+
`${-w + sk},${-h}`,
|
|
2344
|
+
// top-left (shifted right)
|
|
2345
|
+
`${w + sk},${-h}`,
|
|
2346
|
+
// top-right (shifted right)
|
|
2347
|
+
`${w - sk},${h}`,
|
|
2348
|
+
// bottom-right (shifted left)
|
|
2349
|
+
`${-w - sk},${h}`
|
|
2350
|
+
// bottom-left (shifted left)
|
|
2351
|
+
].join(" ");
|
|
2352
|
+
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);
|
|
2353
|
+
}
|
|
2354
|
+
function renderSubroutine(g, node, palette, isDark) {
|
|
2355
|
+
const w = node.width;
|
|
2356
|
+
const h = node.height;
|
|
2357
|
+
const s = nodeStroke(palette, node.color);
|
|
2358
|
+
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);
|
|
2359
|
+
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);
|
|
2360
|
+
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);
|
|
2361
|
+
}
|
|
2362
|
+
function renderDocument(g, node, palette, isDark) {
|
|
2363
|
+
const w = node.width;
|
|
2364
|
+
const h = node.height;
|
|
2365
|
+
const waveH = DOC_WAVE_HEIGHT;
|
|
2366
|
+
const left = -w / 2;
|
|
2367
|
+
const right = w / 2;
|
|
2368
|
+
const top = -h / 2;
|
|
2369
|
+
const bottom = h / 2 - waveH;
|
|
2370
|
+
const d = [
|
|
2371
|
+
`M ${left} ${top}`,
|
|
2372
|
+
`L ${right} ${top}`,
|
|
2373
|
+
`L ${right} ${bottom}`,
|
|
2374
|
+
`C ${right - w * 0.25} ${bottom + waveH * 2}, ${left + w * 0.25} ${bottom - waveH}, ${left} ${bottom}`,
|
|
2375
|
+
"Z"
|
|
2376
|
+
].join(" ");
|
|
2377
|
+
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);
|
|
2378
|
+
}
|
|
2379
|
+
function renderNodeShape(g, node, palette, isDark) {
|
|
2380
|
+
switch (node.shape) {
|
|
2381
|
+
case "terminal":
|
|
2382
|
+
renderTerminal(g, node, palette, isDark);
|
|
2383
|
+
break;
|
|
2384
|
+
case "process":
|
|
2385
|
+
renderProcess(g, node, palette, isDark);
|
|
2386
|
+
break;
|
|
2387
|
+
case "decision":
|
|
2388
|
+
renderDecision(g, node, palette, isDark);
|
|
2389
|
+
break;
|
|
2390
|
+
case "io":
|
|
2391
|
+
renderIO(g, node, palette, isDark);
|
|
2392
|
+
break;
|
|
2393
|
+
case "subroutine":
|
|
2394
|
+
renderSubroutine(g, node, palette, isDark);
|
|
2395
|
+
break;
|
|
2396
|
+
case "document":
|
|
2397
|
+
renderDocument(g, node, palette, isDark);
|
|
2398
|
+
break;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
function renderFlowchart(container, graph, layout, palette, isDark, onClickItem, exportDims) {
|
|
2402
|
+
d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
2403
|
+
const width = exportDims?.width ?? container.clientWidth;
|
|
2404
|
+
const height = exportDims?.height ?? container.clientHeight;
|
|
2405
|
+
if (width <= 0 || height <= 0) return;
|
|
2406
|
+
const titleOffset = graph.title ? TITLE_HEIGHT : 0;
|
|
2407
|
+
const diagramW = layout.width;
|
|
2408
|
+
const diagramH = layout.height + titleOffset;
|
|
2409
|
+
const scaleX = (width - DIAGRAM_PADDING * 2) / diagramW;
|
|
2410
|
+
const scaleY = (height - DIAGRAM_PADDING * 2) / diagramH;
|
|
2411
|
+
const scale = Math.min(1, scaleX, scaleY);
|
|
2412
|
+
const scaledW = diagramW * scale;
|
|
2413
|
+
const scaledH = diagramH * scale;
|
|
2414
|
+
const offsetX = (width - scaledW) / 2;
|
|
2415
|
+
const offsetY = (height - scaledH) / 2;
|
|
2416
|
+
const svg = d3Selection.select(container).append("svg").attr("width", width).attr("height", height).style("background", palette.bg).style("font-family", FONT_FAMILY);
|
|
2417
|
+
const defs = svg.append("defs");
|
|
2418
|
+
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);
|
|
2419
|
+
const edgeColors = /* @__PURE__ */ new Set();
|
|
2420
|
+
for (const edge of layout.edges) {
|
|
2421
|
+
if (edge.color) edgeColors.add(edge.color);
|
|
2422
|
+
}
|
|
2423
|
+
for (const color of edgeColors) {
|
|
2424
|
+
const id = `fc-arrow-${color.replace("#", "")}`;
|
|
2425
|
+
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);
|
|
2426
|
+
}
|
|
2427
|
+
const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY}) scale(${scale})`);
|
|
2428
|
+
if (graph.title) {
|
|
2429
|
+
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);
|
|
2430
|
+
}
|
|
2431
|
+
const contentG = mainG.append("g").attr("transform", `translate(0, ${titleOffset})`);
|
|
2432
|
+
for (const group of layout.groups) {
|
|
2433
|
+
if (group.width === 0 && group.height === 0) continue;
|
|
2434
|
+
const gx = group.x - GROUP_EXTRA_PADDING;
|
|
2435
|
+
const gy = group.y - GROUP_EXTRA_PADDING - GROUP_LABEL_FONT_SIZE - 4;
|
|
2436
|
+
const gw = group.width + GROUP_EXTRA_PADDING * 2;
|
|
2437
|
+
const gh = group.height + GROUP_EXTRA_PADDING * 2 + GROUP_LABEL_FONT_SIZE + 4;
|
|
2438
|
+
const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
|
|
2439
|
+
const strokeColor = group.color ?? palette.textMuted;
|
|
2440
|
+
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");
|
|
2441
|
+
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);
|
|
2442
|
+
}
|
|
2443
|
+
for (const edge of layout.edges) {
|
|
2444
|
+
if (edge.points.length < 2) continue;
|
|
2445
|
+
const edgeG = contentG.append("g").attr("class", "fc-edge-group").attr("data-line-number", String(edge.lineNumber));
|
|
2446
|
+
const edgeColor = edge.color ?? palette.textMuted;
|
|
2447
|
+
const markerId = edge.color ? `fc-arrow-${edge.color.replace("#", "")}` : "fc-arrow";
|
|
2448
|
+
const pathD = lineGenerator(edge.points);
|
|
2449
|
+
if (pathD) {
|
|
2450
|
+
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");
|
|
2451
|
+
}
|
|
2452
|
+
if (edge.label) {
|
|
2453
|
+
const midIdx = Math.floor(edge.points.length / 2);
|
|
2454
|
+
const midPt = edge.points[midIdx];
|
|
2455
|
+
const labelLen = edge.label.length;
|
|
2456
|
+
const bgW = labelLen * 7 + 8;
|
|
2457
|
+
const bgH = 16;
|
|
2458
|
+
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");
|
|
2459
|
+
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);
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
for (const node of layout.nodes) {
|
|
2463
|
+
const nodeG = contentG.append("g").attr("transform", `translate(${node.x}, ${node.y})`).attr("class", "fc-node").attr("data-line-number", String(node.lineNumber));
|
|
2464
|
+
if (onClickItem) {
|
|
2465
|
+
nodeG.style("cursor", "pointer").on("click", () => {
|
|
2466
|
+
onClickItem(node.lineNumber);
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
renderNodeShape(nodeG, node, palette, isDark);
|
|
2470
|
+
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);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
function renderFlowchartForExport(content, theme, palette) {
|
|
2474
|
+
const parsed = parseFlowchart(content, palette);
|
|
2475
|
+
if (parsed.error || parsed.nodes.length === 0) return "";
|
|
2476
|
+
const layout = layoutGraph(parsed);
|
|
2477
|
+
const isDark = theme === "dark";
|
|
2478
|
+
const container = document.createElement("div");
|
|
2479
|
+
container.style.width = `${layout.width + DIAGRAM_PADDING * 2}px`;
|
|
2480
|
+
container.style.height = `${layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0)}px`;
|
|
2481
|
+
container.style.position = "absolute";
|
|
2482
|
+
container.style.left = "-9999px";
|
|
2483
|
+
document.body.appendChild(container);
|
|
2484
|
+
const exportWidth = layout.width + DIAGRAM_PADDING * 2;
|
|
2485
|
+
const exportHeight = layout.height + DIAGRAM_PADDING * 2 + (parsed.title ? TITLE_HEIGHT : 0);
|
|
2486
|
+
try {
|
|
2487
|
+
renderFlowchart(
|
|
2488
|
+
container,
|
|
2489
|
+
parsed,
|
|
2490
|
+
layout,
|
|
2491
|
+
palette,
|
|
2492
|
+
isDark,
|
|
2493
|
+
void 0,
|
|
2494
|
+
{ width: exportWidth, height: exportHeight }
|
|
2495
|
+
);
|
|
2496
|
+
const svgEl = container.querySelector("svg");
|
|
2497
|
+
if (!svgEl) return "";
|
|
2498
|
+
if (theme === "transparent") {
|
|
2499
|
+
svgEl.style.background = "none";
|
|
2500
|
+
}
|
|
2501
|
+
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
2502
|
+
svgEl.style.fontFamily = FONT_FAMILY;
|
|
2503
|
+
return svgEl.outerHTML;
|
|
2504
|
+
} finally {
|
|
2505
|
+
document.body.removeChild(container);
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
var DIAGRAM_PADDING, TITLE_HEIGHT, TITLE_FONT_SIZE, NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE, GROUP_LABEL_FONT_SIZE, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, ARROWHEAD_W, ARROWHEAD_H, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT, GROUP_EXTRA_PADDING, lineGenerator;
|
|
2509
|
+
var init_flowchart_renderer = __esm({
|
|
2510
|
+
"src/graph/flowchart-renderer.ts"() {
|
|
2511
|
+
"use strict";
|
|
2512
|
+
init_fonts();
|
|
2513
|
+
init_flowchart_parser();
|
|
2514
|
+
init_layout();
|
|
2515
|
+
DIAGRAM_PADDING = 20;
|
|
2516
|
+
TITLE_HEIGHT = 30;
|
|
2517
|
+
TITLE_FONT_SIZE = 18;
|
|
2518
|
+
NODE_FONT_SIZE = 13;
|
|
2519
|
+
EDGE_LABEL_FONT_SIZE = 11;
|
|
2520
|
+
GROUP_LABEL_FONT_SIZE = 11;
|
|
2521
|
+
EDGE_STROKE_WIDTH = 1.5;
|
|
2522
|
+
NODE_STROKE_WIDTH = 1.5;
|
|
2523
|
+
ARROWHEAD_W = 10;
|
|
2524
|
+
ARROWHEAD_H = 7;
|
|
2525
|
+
IO_SKEW = 15;
|
|
2526
|
+
SUBROUTINE_INSET = 8;
|
|
2527
|
+
DOC_WAVE_HEIGHT = 10;
|
|
2528
|
+
GROUP_EXTRA_PADDING = 12;
|
|
2529
|
+
lineGenerator = d3Shape.line().x((d) => d.x).y((d) => d.y).curve(d3Shape.curveBasis);
|
|
2530
|
+
}
|
|
2531
|
+
});
|
|
2532
|
+
|
|
1615
2533
|
// src/sequence/renderer.ts
|
|
1616
2534
|
var renderer_exports = {};
|
|
1617
2535
|
__export(renderer_exports, {
|
|
@@ -1622,8 +2540,43 @@ __export(renderer_exports, {
|
|
|
1622
2540
|
groupMessagesBySection: () => groupMessagesBySection,
|
|
1623
2541
|
renderSequenceDiagram: () => renderSequenceDiagram
|
|
1624
2542
|
});
|
|
1625
|
-
import * as
|
|
1626
|
-
function
|
|
2543
|
+
import * as d3Selection2 from "d3-selection";
|
|
2544
|
+
function parseInlineMarkdown(text) {
|
|
2545
|
+
const spans = [];
|
|
2546
|
+
const regex = /\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|\[(.+?)\]\((.+?)\)|([^*`[]+)/g;
|
|
2547
|
+
let match;
|
|
2548
|
+
while ((match = regex.exec(text)) !== null) {
|
|
2549
|
+
if (match[1]) spans.push({ text: match[1], bold: true });
|
|
2550
|
+
else if (match[2]) spans.push({ text: match[2], italic: true });
|
|
2551
|
+
else if (match[3]) spans.push({ text: match[3], code: true });
|
|
2552
|
+
else if (match[4]) spans.push({ text: match[4], href: match[5] });
|
|
2553
|
+
else if (match[6]) spans.push({ text: match[6] });
|
|
2554
|
+
}
|
|
2555
|
+
return spans;
|
|
2556
|
+
}
|
|
2557
|
+
function wrapTextLines(text, maxChars) {
|
|
2558
|
+
const rawLines = text.split("\n");
|
|
2559
|
+
const wrapped = [];
|
|
2560
|
+
for (const line3 of rawLines) {
|
|
2561
|
+
if (line3.length <= maxChars) {
|
|
2562
|
+
wrapped.push(line3);
|
|
2563
|
+
} else {
|
|
2564
|
+
const words = line3.split(" ");
|
|
2565
|
+
let current = "";
|
|
2566
|
+
for (const word of words) {
|
|
2567
|
+
if (current && (current + " " + word).length > maxChars) {
|
|
2568
|
+
wrapped.push(current);
|
|
2569
|
+
current = word;
|
|
2570
|
+
} else {
|
|
2571
|
+
current = current ? current + " " + word : word;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
if (current) wrapped.push(current);
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
return wrapped;
|
|
2578
|
+
}
|
|
2579
|
+
function mix2(a, b, pct) {
|
|
1627
2580
|
const parse = (h) => {
|
|
1628
2581
|
const r = h.replace("#", "");
|
|
1629
2582
|
const f = r.length === 3 ? r[0] + r[0] + r[1] + r[1] + r[2] + r[2] : r;
|
|
@@ -1729,7 +2682,12 @@ function groupMessagesBySection(elements, messages) {
|
|
|
1729
2682
|
...collectIndices(el.children),
|
|
1730
2683
|
...collectIndices(el.elseChildren)
|
|
1731
2684
|
);
|
|
1732
|
-
|
|
2685
|
+
if (el.elseIfBranches) {
|
|
2686
|
+
for (const branch of el.elseIfBranches) {
|
|
2687
|
+
indices.push(...collectIndices(branch.children));
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
} else if (isSequenceSection(el) || isSequenceNote(el)) {
|
|
1733
2691
|
continue;
|
|
1734
2692
|
} else {
|
|
1735
2693
|
const idx = messages.indexOf(el);
|
|
@@ -1745,7 +2703,7 @@ function groupMessagesBySection(elements, messages) {
|
|
|
1745
2703
|
} else if (currentGroup) {
|
|
1746
2704
|
if (isSequenceBlock(el)) {
|
|
1747
2705
|
currentGroup.messageIndices.push(...collectIndices([el]));
|
|
1748
|
-
} else {
|
|
2706
|
+
} else if (!isSequenceNote(el)) {
|
|
1749
2707
|
const idx = messages.indexOf(el);
|
|
1750
2708
|
if (idx >= 0) currentGroup.messageIndices.push(idx);
|
|
1751
2709
|
}
|
|
@@ -1902,7 +2860,7 @@ function applyGroupOrdering(participants, groups) {
|
|
|
1902
2860
|
return result;
|
|
1903
2861
|
}
|
|
1904
2862
|
function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
|
|
1905
|
-
|
|
2863
|
+
d3Selection2.select(container).selectAll("*").remove();
|
|
1906
2864
|
const { title, messages, elements, groups, options: parsedOptions } = parsed;
|
|
1907
2865
|
const collapsedSections = options?.collapsedSections;
|
|
1908
2866
|
const participants = applyPositionOverrides(
|
|
@@ -1943,7 +2901,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1943
2901
|
if (isSequenceBlock(el)) {
|
|
1944
2902
|
const idx = findFirstMsgIndex(el.children);
|
|
1945
2903
|
if (idx >= 0) return idx;
|
|
1946
|
-
|
|
2904
|
+
if (el.elseIfBranches) {
|
|
2905
|
+
for (const branch of el.elseIfBranches) {
|
|
2906
|
+
const branchIdx = findFirstMsgIndex(branch.children);
|
|
2907
|
+
if (branchIdx >= 0) return branchIdx;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
const elseIdx = findFirstMsgIndex(el.elseChildren);
|
|
2911
|
+
if (elseIdx >= 0) return elseIdx;
|
|
2912
|
+
} else if (!isSequenceSection(el) && !isSequenceNote(el)) {
|
|
1947
2913
|
const idx = messages.indexOf(el);
|
|
1948
2914
|
if (idx >= 0 && !hiddenMsgIndices.has(idx)) return idx;
|
|
1949
2915
|
}
|
|
@@ -1963,6 +2929,13 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1963
2929
|
if (!isSequenceBlock(el)) continue;
|
|
1964
2930
|
const firstIdx = findFirstMsgIndex(el.children);
|
|
1965
2931
|
if (firstIdx >= 0) addExtra(firstIdx, BLOCK_HEADER_SPACE);
|
|
2932
|
+
if (el.elseIfBranches) {
|
|
2933
|
+
for (const branch of el.elseIfBranches) {
|
|
2934
|
+
const firstBranchIdx = findFirstMsgIndex(branch.children);
|
|
2935
|
+
if (firstBranchIdx >= 0) addExtra(firstBranchIdx, BLOCK_HEADER_SPACE);
|
|
2936
|
+
markBlockSpacing(branch.children);
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
1966
2939
|
const firstElseIdx = findFirstMsgIndex(el.elseChildren);
|
|
1967
2940
|
if (firstElseIdx >= 0) addExtra(firstElseIdx, BLOCK_HEADER_SPACE);
|
|
1968
2941
|
markBlockSpacing(el.children);
|
|
@@ -1984,15 +2957,27 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
1984
2957
|
for (const child of block.children) {
|
|
1985
2958
|
if (isSequenceBlock(child)) {
|
|
1986
2959
|
indices.push(...collectMsgIndicesFromBlock(child));
|
|
1987
|
-
} else if (!isSequenceSection(child)) {
|
|
2960
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
1988
2961
|
const idx = messages.indexOf(child);
|
|
1989
2962
|
if (idx >= 0) indices.push(idx);
|
|
1990
2963
|
}
|
|
1991
2964
|
}
|
|
2965
|
+
if (block.elseIfBranches) {
|
|
2966
|
+
for (const branch of block.elseIfBranches) {
|
|
2967
|
+
for (const child of branch.children) {
|
|
2968
|
+
if (isSequenceBlock(child)) {
|
|
2969
|
+
indices.push(...collectMsgIndicesFromBlock(child));
|
|
2970
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
2971
|
+
const idx = messages.indexOf(child);
|
|
2972
|
+
if (idx >= 0) indices.push(idx);
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
1992
2977
|
for (const child of block.elseChildren) {
|
|
1993
2978
|
if (isSequenceBlock(child)) {
|
|
1994
2979
|
indices.push(...collectMsgIndicesFromBlock(child));
|
|
1995
|
-
} else if (!isSequenceSection(child)) {
|
|
2980
|
+
} else if (!isSequenceSection(child) && !isSequenceNote(child)) {
|
|
1996
2981
|
const idx = messages.indexOf(child);
|
|
1997
2982
|
if (idx >= 0) indices.push(idx);
|
|
1998
2983
|
}
|
|
@@ -2067,7 +3052,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2067
3052
|
const GROUP_PADDING_TOP = 22;
|
|
2068
3053
|
const GROUP_PADDING_BOTTOM = 8;
|
|
2069
3054
|
const GROUP_LABEL_SIZE = 11;
|
|
2070
|
-
const titleOffset = title ?
|
|
3055
|
+
const titleOffset = title ? TITLE_HEIGHT2 : 0;
|
|
2071
3056
|
const groupOffset = groups.length > 0 ? GROUP_PADDING_TOP + GROUP_LABEL_SIZE : 0;
|
|
2072
3057
|
const participantStartY = TOP_MARGIN + titleOffset + PARTICIPANT_Y_OFFSET + groupOffset;
|
|
2073
3058
|
const lifelineStartY0 = participantStartY + PARTICIPANT_BOX_HEIGHT;
|
|
@@ -2121,7 +3106,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2121
3106
|
participants.forEach((p, i) => {
|
|
2122
3107
|
participantX.set(p.id, offsetX + i * PARTICIPANT_GAP);
|
|
2123
3108
|
});
|
|
2124
|
-
const svg =
|
|
3109
|
+
const svg = d3Selection2.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
|
|
2125
3110
|
const defs = svg.append("defs");
|
|
2126
3111
|
defs.append("marker").attr("id", "seq-arrowhead").attr("viewBox", `0 0 ${ARROWHEAD_SIZE} ${ARROWHEAD_SIZE}`).attr("refX", ARROWHEAD_SIZE).attr("refY", ARROWHEAD_SIZE / 2).attr("markerWidth", ARROWHEAD_SIZE).attr("markerHeight", ARROWHEAD_SIZE).attr("orient", "auto").append("polygon").attr(
|
|
2127
3112
|
"points",
|
|
@@ -2147,7 +3132,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2147
3132
|
const boxY = participantStartY - GROUP_PADDING_TOP;
|
|
2148
3133
|
const boxH = PARTICIPANT_BOX_HEIGHT + GROUP_PADDING_TOP + GROUP_PADDING_BOTTOM;
|
|
2149
3134
|
const resolvedGroupColor = group.color ? resolveColor(group.color, palette) : void 0;
|
|
2150
|
-
const fillColor = resolvedGroupColor ?
|
|
3135
|
+
const fillColor = resolvedGroupColor ? mix2(resolvedGroupColor, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : palette.bg;
|
|
2151
3136
|
const strokeColor = resolvedGroupColor || palette.textMuted;
|
|
2152
3137
|
svg.append("rect").attr("x", minX).attr("y", boxY).attr("width", maxX - minX).attr("height", boxH).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "group-box").attr("data-group-line", String(group.lineNumber));
|
|
2153
3138
|
svg.append("text").attr("x", minX + 8).attr("y", boxY + GROUP_LABEL_SIZE + 4).attr("fill", strokeColor).attr("font-size", GROUP_LABEL_SIZE).attr("font-weight", "bold").attr("opacity", 0.7).attr("class", "group-label").attr("data-group-line", String(group.lineNumber)).text(group.name);
|
|
@@ -2172,7 +3157,12 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2172
3157
|
...collectMsgIndices(el.children),
|
|
2173
3158
|
...collectMsgIndices(el.elseChildren)
|
|
2174
3159
|
);
|
|
2175
|
-
|
|
3160
|
+
if (el.elseIfBranches) {
|
|
3161
|
+
for (const branch of el.elseIfBranches) {
|
|
3162
|
+
indices.push(...collectMsgIndices(branch.children));
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
} else if (!isSequenceSection(el) && !isSequenceNote(el)) {
|
|
2176
3166
|
const idx = messages.indexOf(el);
|
|
2177
3167
|
if (idx >= 0) indices.push(idx);
|
|
2178
3168
|
}
|
|
@@ -2185,8 +3175,21 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2185
3175
|
for (const el of els) {
|
|
2186
3176
|
if (!isSequenceBlock(el)) continue;
|
|
2187
3177
|
const ifIndices = collectMsgIndices(el.children);
|
|
3178
|
+
const elseIfBranchData = [];
|
|
3179
|
+
if (el.elseIfBranches) {
|
|
3180
|
+
for (const branch of el.elseIfBranches) {
|
|
3181
|
+
elseIfBranchData.push({
|
|
3182
|
+
label: branch.label,
|
|
3183
|
+
indices: collectMsgIndices(branch.children)
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
2188
3187
|
const elseIndices = collectMsgIndices(el.elseChildren);
|
|
2189
|
-
const allIndices = [
|
|
3188
|
+
const allIndices = [
|
|
3189
|
+
...ifIndices,
|
|
3190
|
+
...elseIfBranchData.flatMap((b) => b.indices),
|
|
3191
|
+
...elseIndices
|
|
3192
|
+
];
|
|
2190
3193
|
if (allIndices.length === 0) continue;
|
|
2191
3194
|
let minStep = Infinity;
|
|
2192
3195
|
let maxStep = -Infinity;
|
|
@@ -2224,6 +3227,32 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2224
3227
|
italic: false,
|
|
2225
3228
|
blockLine: el.lineNumber
|
|
2226
3229
|
});
|
|
3230
|
+
for (const branchData of elseIfBranchData) {
|
|
3231
|
+
if (branchData.indices.length > 0) {
|
|
3232
|
+
let firstBranchStep = Infinity;
|
|
3233
|
+
for (const mi of branchData.indices) {
|
|
3234
|
+
const first = msgToFirstStep.get(mi);
|
|
3235
|
+
if (first !== void 0)
|
|
3236
|
+
firstBranchStep = Math.min(firstBranchStep, first);
|
|
3237
|
+
}
|
|
3238
|
+
if (firstBranchStep < Infinity) {
|
|
3239
|
+
const dividerY = stepY(firstBranchStep) - stepSpacing / 2;
|
|
3240
|
+
deferredLines.push({
|
|
3241
|
+
x1: frameX,
|
|
3242
|
+
y1: dividerY,
|
|
3243
|
+
x2: frameX + frameW,
|
|
3244
|
+
y2: dividerY
|
|
3245
|
+
});
|
|
3246
|
+
deferredLabels.push({
|
|
3247
|
+
x: frameX + 6,
|
|
3248
|
+
y: dividerY + 14,
|
|
3249
|
+
text: `else if ${branchData.label}`,
|
|
3250
|
+
bold: false,
|
|
3251
|
+
italic: true
|
|
3252
|
+
});
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
2227
3256
|
if (elseIndices.length > 0) {
|
|
2228
3257
|
let firstElseStep = Infinity;
|
|
2229
3258
|
for (const mi of elseIndices) {
|
|
@@ -2249,6 +3278,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2249
3278
|
}
|
|
2250
3279
|
}
|
|
2251
3280
|
renderBlockFrames(el.children, depth + 1);
|
|
3281
|
+
if (el.elseIfBranches) {
|
|
3282
|
+
for (const branch of el.elseIfBranches) {
|
|
3283
|
+
renderBlockFrames(branch.children, depth + 1);
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
2252
3286
|
renderBlockFrames(el.elseChildren, depth + 1);
|
|
2253
3287
|
}
|
|
2254
3288
|
};
|
|
@@ -2270,7 +3304,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2270
3304
|
if (msg) coveredLines.push(msg.lineNumber);
|
|
2271
3305
|
}
|
|
2272
3306
|
svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
|
|
2273
|
-
const actFill =
|
|
3307
|
+
const actFill = mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
|
|
2274
3308
|
svg.append("rect").attr("x", x).attr("y", y1).attr("width", ACTIVATION_WIDTH).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", palette.primary).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("class", "activation");
|
|
2275
3309
|
});
|
|
2276
3310
|
for (const ln of deferredLines) {
|
|
@@ -2396,6 +3430,91 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
|
|
|
2396
3430
|
}
|
|
2397
3431
|
}
|
|
2398
3432
|
});
|
|
3433
|
+
const noteFill = isDark ? mix2(palette.surface, palette.bg, 50) : mix2(palette.bg, palette.surface, 15);
|
|
3434
|
+
const findAssociatedStep = (note) => {
|
|
3435
|
+
let bestMsgIndex = -1;
|
|
3436
|
+
let bestLine = -1;
|
|
3437
|
+
for (let mi = 0; mi < messages.length; mi++) {
|
|
3438
|
+
if (messages[mi].lineNumber < note.lineNumber && messages[mi].lineNumber > bestLine && !hiddenMsgIndices.has(mi)) {
|
|
3439
|
+
bestLine = messages[mi].lineNumber;
|
|
3440
|
+
bestMsgIndex = mi;
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
if (bestMsgIndex < 0) return -1;
|
|
3444
|
+
return msgToFirstStep.get(bestMsgIndex) ?? -1;
|
|
3445
|
+
};
|
|
3446
|
+
const renderNoteElements = (els) => {
|
|
3447
|
+
for (const el of els) {
|
|
3448
|
+
if (isSequenceNote(el)) {
|
|
3449
|
+
const px = participantX.get(el.participantId);
|
|
3450
|
+
if (px === void 0) continue;
|
|
3451
|
+
const si = findAssociatedStep(el);
|
|
3452
|
+
if (si < 0) continue;
|
|
3453
|
+
const noteY = stepY(si);
|
|
3454
|
+
const wrappedLines = wrapTextLines(el.text, NOTE_CHARS_PER_LINE);
|
|
3455
|
+
const noteH = wrappedLines.length * NOTE_LINE_H + NOTE_PAD_V * 2;
|
|
3456
|
+
const maxLineLen = Math.max(...wrappedLines.map((l) => l.length));
|
|
3457
|
+
const noteW = Math.min(
|
|
3458
|
+
NOTE_MAX_W,
|
|
3459
|
+
Math.max(80, maxLineLen * NOTE_CHAR_W + NOTE_PAD_H * 2 + NOTE_FOLD)
|
|
3460
|
+
);
|
|
3461
|
+
const isRight = el.position === "right";
|
|
3462
|
+
const noteX = isRight ? px + ACTIVATION_WIDTH + NOTE_GAP : px - ACTIVATION_WIDTH - NOTE_GAP - noteW;
|
|
3463
|
+
const noteTopY = noteY - noteH / 2;
|
|
3464
|
+
const noteG = svg.append("g").attr("class", "note").attr("data-note-toggle", "").attr("data-line-number", String(el.lineNumber));
|
|
3465
|
+
noteG.append("path").attr(
|
|
3466
|
+
"d",
|
|
3467
|
+
[
|
|
3468
|
+
`M ${noteX} ${noteTopY}`,
|
|
3469
|
+
`L ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
|
|
3470
|
+
`L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`,
|
|
3471
|
+
`L ${noteX + noteW} ${noteTopY + noteH}`,
|
|
3472
|
+
`L ${noteX} ${noteTopY + noteH}`,
|
|
3473
|
+
"Z"
|
|
3474
|
+
].join(" ")
|
|
3475
|
+
).attr("fill", noteFill).attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-box");
|
|
3476
|
+
noteG.append("path").attr(
|
|
3477
|
+
"d",
|
|
3478
|
+
[
|
|
3479
|
+
`M ${noteX + noteW - NOTE_FOLD} ${noteTopY}`,
|
|
3480
|
+
`L ${noteX + noteW - NOTE_FOLD} ${noteTopY + NOTE_FOLD}`,
|
|
3481
|
+
`L ${noteX + noteW} ${noteTopY + NOTE_FOLD}`
|
|
3482
|
+
].join(" ")
|
|
3483
|
+
).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 0.75).attr("class", "note-fold");
|
|
3484
|
+
const connectorNoteX = isRight ? noteX : noteX + noteW;
|
|
3485
|
+
const connectorLifeX = isRight ? px + ACTIVATION_WIDTH / 2 : px - ACTIVATION_WIDTH / 2;
|
|
3486
|
+
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");
|
|
3487
|
+
wrappedLines.forEach((line3, li) => {
|
|
3488
|
+
const textY = noteTopY + NOTE_PAD_V + (li + 1) * NOTE_LINE_H - 3;
|
|
3489
|
+
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");
|
|
3490
|
+
const spans = parseInlineMarkdown(line3);
|
|
3491
|
+
for (const span of spans) {
|
|
3492
|
+
if (span.href) {
|
|
3493
|
+
const a = textEl.append("a").attr("href", span.href);
|
|
3494
|
+
a.append("tspan").text(span.text).attr("fill", palette.primary).style("text-decoration", "underline");
|
|
3495
|
+
} else {
|
|
3496
|
+
const tspan = textEl.append("tspan").text(span.text);
|
|
3497
|
+
if (span.bold) tspan.attr("font-weight", "bold");
|
|
3498
|
+
if (span.italic) tspan.attr("font-style", "italic");
|
|
3499
|
+
if (span.code)
|
|
3500
|
+
tspan.attr("font-family", "monospace").attr("font-size", NOTE_FONT_SIZE - 1);
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
});
|
|
3504
|
+
} else if (isSequenceBlock(el)) {
|
|
3505
|
+
renderNoteElements(el.children);
|
|
3506
|
+
if (el.elseIfBranches) {
|
|
3507
|
+
for (const branch of el.elseIfBranches) {
|
|
3508
|
+
renderNoteElements(branch.children);
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
renderNoteElements(el.elseChildren);
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
};
|
|
3515
|
+
if (elements && elements.length > 0) {
|
|
3516
|
+
renderNoteElements(elements);
|
|
3517
|
+
}
|
|
2399
3518
|
}
|
|
2400
3519
|
function renderParticipant(svg, participant, cx, cy, palette, isDark) {
|
|
2401
3520
|
const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
|
|
@@ -2437,7 +3556,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark) {
|
|
|
2437
3556
|
isActor ? PARTICIPANT_BOX_HEIGHT + 14 : PARTICIPANT_BOX_HEIGHT / 2 + 5
|
|
2438
3557
|
).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", 13).attr("font-weight", 500).text(participant.label);
|
|
2439
3558
|
}
|
|
2440
|
-
var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN,
|
|
3559
|
+
var PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, TOP_MARGIN, TITLE_HEIGHT2, PARTICIPANT_Y_OFFSET, SERVICE_BORDER_RADIUS, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W, NOTE_FOLD, NOTE_PAD_H, NOTE_PAD_V, NOTE_FONT_SIZE, NOTE_LINE_H, NOTE_GAP, NOTE_CHAR_W, NOTE_CHARS_PER_LINE, fill, stroke, SW, W, H;
|
|
2441
3560
|
var init_renderer = __esm({
|
|
2442
3561
|
"src/sequence/renderer.ts"() {
|
|
2443
3562
|
"use strict";
|
|
@@ -2448,13 +3567,22 @@ var init_renderer = __esm({
|
|
|
2448
3567
|
PARTICIPANT_BOX_WIDTH = 120;
|
|
2449
3568
|
PARTICIPANT_BOX_HEIGHT = 50;
|
|
2450
3569
|
TOP_MARGIN = 20;
|
|
2451
|
-
|
|
3570
|
+
TITLE_HEIGHT2 = 30;
|
|
2452
3571
|
PARTICIPANT_Y_OFFSET = 10;
|
|
2453
3572
|
SERVICE_BORDER_RADIUS = 10;
|
|
2454
3573
|
MESSAGE_START_OFFSET = 30;
|
|
2455
3574
|
LIFELINE_TAIL = 30;
|
|
2456
3575
|
ARROWHEAD_SIZE = 8;
|
|
2457
|
-
|
|
3576
|
+
NOTE_MAX_W = 200;
|
|
3577
|
+
NOTE_FOLD = 10;
|
|
3578
|
+
NOTE_PAD_H = 8;
|
|
3579
|
+
NOTE_PAD_V = 6;
|
|
3580
|
+
NOTE_FONT_SIZE = 10;
|
|
3581
|
+
NOTE_LINE_H = 14;
|
|
3582
|
+
NOTE_GAP = 15;
|
|
3583
|
+
NOTE_CHAR_W = 6;
|
|
3584
|
+
NOTE_CHARS_PER_LINE = Math.floor((NOTE_MAX_W - NOTE_PAD_H * 2 - NOTE_FOLD) / NOTE_CHAR_W);
|
|
3585
|
+
fill = (palette, isDark) => mix2(palette.primary, isDark ? palette.surface : palette.bg, isDark ? 15 : 30);
|
|
2458
3586
|
stroke = (palette) => palette.textMuted;
|
|
2459
3587
|
SW = 1.5;
|
|
2460
3588
|
W = PARTICIPANT_BOX_WIDTH;
|
|
@@ -2462,50 +3590,8 @@ var init_renderer = __esm({
|
|
|
2462
3590
|
}
|
|
2463
3591
|
});
|
|
2464
3592
|
|
|
2465
|
-
// src/
|
|
2466
|
-
|
|
2467
|
-
var DGMO_CHART_TYPE_MAP = {
|
|
2468
|
-
// Standard charts (via ECharts)
|
|
2469
|
-
bar: "echart",
|
|
2470
|
-
line: "echart",
|
|
2471
|
-
"multi-line": "echart",
|
|
2472
|
-
area: "echart",
|
|
2473
|
-
pie: "echart",
|
|
2474
|
-
doughnut: "echart",
|
|
2475
|
-
radar: "echart",
|
|
2476
|
-
"polar-area": "echart",
|
|
2477
|
-
"bar-stacked": "echart",
|
|
2478
|
-
// ECharts
|
|
2479
|
-
scatter: "echart",
|
|
2480
|
-
sankey: "echart",
|
|
2481
|
-
chord: "echart",
|
|
2482
|
-
function: "echart",
|
|
2483
|
-
heatmap: "echart",
|
|
2484
|
-
funnel: "echart",
|
|
2485
|
-
// D3
|
|
2486
|
-
slope: "d3",
|
|
2487
|
-
wordcloud: "d3",
|
|
2488
|
-
arc: "d3",
|
|
2489
|
-
timeline: "d3",
|
|
2490
|
-
venn: "d3",
|
|
2491
|
-
quadrant: "d3",
|
|
2492
|
-
sequence: "d3"
|
|
2493
|
-
};
|
|
2494
|
-
function getDgmoFramework(chartType) {
|
|
2495
|
-
return DGMO_CHART_TYPE_MAP[chartType.toLowerCase()] ?? null;
|
|
2496
|
-
}
|
|
2497
|
-
function parseDgmoChartType(content) {
|
|
2498
|
-
const lines = content.split("\n");
|
|
2499
|
-
for (const line2 of lines) {
|
|
2500
|
-
const trimmed = line2.trim();
|
|
2501
|
-
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//"))
|
|
2502
|
-
continue;
|
|
2503
|
-
const match = trimmed.match(/^chart\s*:\s*(.+)/i);
|
|
2504
|
-
if (match) return match[1].trim().toLowerCase();
|
|
2505
|
-
}
|
|
2506
|
-
if (looksLikeSequence(content)) return "sequence";
|
|
2507
|
-
return null;
|
|
2508
|
-
}
|
|
3593
|
+
// src/index.ts
|
|
3594
|
+
init_dgmo_router();
|
|
2509
3595
|
|
|
2510
3596
|
// src/chart.ts
|
|
2511
3597
|
init_colors();
|
|
@@ -2867,7 +3953,7 @@ function parseEChart(content, palette) {
|
|
|
2867
3953
|
function buildEChartsOption(parsed, palette, isDark) {
|
|
2868
3954
|
const textColor = palette.text;
|
|
2869
3955
|
const axisLineColor = palette.border;
|
|
2870
|
-
const gridOpacity = isDark ? 0.7 : 0.
|
|
3956
|
+
const gridOpacity = isDark ? 0.7 : 0.55;
|
|
2871
3957
|
const colors = getSeriesColors(palette);
|
|
2872
3958
|
if (parsed.error) {
|
|
2873
3959
|
return {};
|
|
@@ -3573,7 +4659,7 @@ function buildEChartsOptionFromChart(parsed, palette, isDark) {
|
|
|
3573
4659
|
const textColor = palette.text;
|
|
3574
4660
|
const axisLineColor = palette.border;
|
|
3575
4661
|
const splitLineColor = palette.border;
|
|
3576
|
-
const gridOpacity = isDark ? 0.7 : 0.
|
|
4662
|
+
const gridOpacity = isDark ? 0.7 : 0.55;
|
|
3577
4663
|
const colors = getSeriesColors(palette);
|
|
3578
4664
|
const titleConfig = parsed.title ? {
|
|
3579
4665
|
text: parsed.title,
|
|
@@ -3989,8 +5075,8 @@ init_fonts();
|
|
|
3989
5075
|
init_colors();
|
|
3990
5076
|
init_palettes();
|
|
3991
5077
|
import * as d3Scale from "d3-scale";
|
|
3992
|
-
import * as
|
|
3993
|
-
import * as
|
|
5078
|
+
import * as d3Selection3 from "d3-selection";
|
|
5079
|
+
import * as d3Shape2 from "d3-shape";
|
|
3994
5080
|
import * as d3Array from "d3-array";
|
|
3995
5081
|
import cloud from "d3-cloud";
|
|
3996
5082
|
var DEFAULT_CLOUD_OPTIONS = {
|
|
@@ -4094,10 +5180,10 @@ function parseD3(content, palette) {
|
|
|
4094
5180
|
let currentArcGroup = null;
|
|
4095
5181
|
let currentTimelineGroup = null;
|
|
4096
5182
|
for (let i = 0; i < lines.length; i++) {
|
|
4097
|
-
const
|
|
5183
|
+
const line3 = lines[i].trim();
|
|
4098
5184
|
const lineNumber = i + 1;
|
|
4099
|
-
if (!
|
|
4100
|
-
const sectionMatch =
|
|
5185
|
+
if (!line3) continue;
|
|
5186
|
+
const sectionMatch = line3.match(/^#{2,}\s+(.+?)(?:\s*\(([^)]+)\))?\s*$/);
|
|
4101
5187
|
if (sectionMatch) {
|
|
4102
5188
|
if (result.type === "arc") {
|
|
4103
5189
|
const name = sectionMatch[1].trim();
|
|
@@ -4112,11 +5198,11 @@ function parseD3(content, palette) {
|
|
|
4112
5198
|
}
|
|
4113
5199
|
continue;
|
|
4114
5200
|
}
|
|
4115
|
-
if (
|
|
5201
|
+
if (line3.startsWith("#") || line3.startsWith("//")) {
|
|
4116
5202
|
continue;
|
|
4117
5203
|
}
|
|
4118
5204
|
if (result.type === "arc") {
|
|
4119
|
-
const linkMatch =
|
|
5205
|
+
const linkMatch = line3.match(
|
|
4120
5206
|
/^(.+?)\s*->\s*(.+?)(?:\(([^)]+)\))?\s*(?::\s*(\d+(?:\.\d+)?))?$/
|
|
4121
5207
|
);
|
|
4122
5208
|
if (linkMatch) {
|
|
@@ -4146,7 +5232,7 @@ function parseD3(content, palette) {
|
|
|
4146
5232
|
}
|
|
4147
5233
|
}
|
|
4148
5234
|
if (result.type === "timeline") {
|
|
4149
|
-
const eraMatch =
|
|
5235
|
+
const eraMatch = line3.match(
|
|
4150
5236
|
/^era\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
4151
5237
|
);
|
|
4152
5238
|
if (eraMatch) {
|
|
@@ -4159,7 +5245,7 @@ function parseD3(content, palette) {
|
|
|
4159
5245
|
});
|
|
4160
5246
|
continue;
|
|
4161
5247
|
}
|
|
4162
|
-
const markerMatch =
|
|
5248
|
+
const markerMatch = line3.match(
|
|
4163
5249
|
/^marker\s+(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+?)(?:\s*\(([^)]+)\))?\s*$/
|
|
4164
5250
|
);
|
|
4165
5251
|
if (markerMatch) {
|
|
@@ -4174,7 +5260,7 @@ function parseD3(content, palette) {
|
|
|
4174
5260
|
}
|
|
4175
5261
|
}
|
|
4176
5262
|
if (result.type === "timeline") {
|
|
4177
|
-
const durationMatch =
|
|
5263
|
+
const durationMatch = line3.match(
|
|
4178
5264
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d+(?:\.\d{1,2})?)([dwmy])\s*:\s*(.+)$/
|
|
4179
5265
|
);
|
|
4180
5266
|
if (durationMatch) {
|
|
@@ -4193,7 +5279,7 @@ function parseD3(content, palette) {
|
|
|
4193
5279
|
});
|
|
4194
5280
|
continue;
|
|
4195
5281
|
}
|
|
4196
|
-
const rangeMatch =
|
|
5282
|
+
const rangeMatch = line3.match(
|
|
4197
5283
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*->\s*(\?)?(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
4198
5284
|
);
|
|
4199
5285
|
if (rangeMatch) {
|
|
@@ -4207,7 +5293,7 @@ function parseD3(content, palette) {
|
|
|
4207
5293
|
});
|
|
4208
5294
|
continue;
|
|
4209
5295
|
}
|
|
4210
|
-
const pointMatch =
|
|
5296
|
+
const pointMatch = line3.match(
|
|
4211
5297
|
/^(\d{4}(?:-\d{2})?(?:-\d{2})?)\s*:\s*(.+)$/
|
|
4212
5298
|
);
|
|
4213
5299
|
if (pointMatch) {
|
|
@@ -4222,7 +5308,7 @@ function parseD3(content, palette) {
|
|
|
4222
5308
|
}
|
|
4223
5309
|
}
|
|
4224
5310
|
if (result.type === "venn") {
|
|
4225
|
-
const overlapMatch =
|
|
5311
|
+
const overlapMatch = line3.match(
|
|
4226
5312
|
/^(.+?&.+?)\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
|
|
4227
5313
|
);
|
|
4228
5314
|
if (overlapMatch) {
|
|
@@ -4232,7 +5318,7 @@ function parseD3(content, palette) {
|
|
|
4232
5318
|
result.vennOverlaps.push({ sets, size, label, lineNumber });
|
|
4233
5319
|
continue;
|
|
4234
5320
|
}
|
|
4235
|
-
const setMatch =
|
|
5321
|
+
const setMatch = line3.match(
|
|
4236
5322
|
/^(.+?)(?:\(([^)]+)\))?\s*:\s*(\d+(?:\.\d+)?)\s*(?:"([^"]*)")?\s*$/
|
|
4237
5323
|
);
|
|
4238
5324
|
if (setMatch) {
|
|
@@ -4245,7 +5331,7 @@ function parseD3(content, palette) {
|
|
|
4245
5331
|
}
|
|
4246
5332
|
}
|
|
4247
5333
|
if (result.type === "quadrant") {
|
|
4248
|
-
const xAxisMatch =
|
|
5334
|
+
const xAxisMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
|
|
4249
5335
|
if (xAxisMatch) {
|
|
4250
5336
|
const parts = xAxisMatch[1].split(",").map((s) => s.trim());
|
|
4251
5337
|
if (parts.length >= 2) {
|
|
@@ -4254,7 +5340,7 @@ function parseD3(content, palette) {
|
|
|
4254
5340
|
}
|
|
4255
5341
|
continue;
|
|
4256
5342
|
}
|
|
4257
|
-
const yAxisMatch =
|
|
5343
|
+
const yAxisMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
|
|
4258
5344
|
if (yAxisMatch) {
|
|
4259
5345
|
const parts = yAxisMatch[1].split(",").map((s) => s.trim());
|
|
4260
5346
|
if (parts.length >= 2) {
|
|
@@ -4264,7 +5350,7 @@ function parseD3(content, palette) {
|
|
|
4264
5350
|
continue;
|
|
4265
5351
|
}
|
|
4266
5352
|
const quadrantLabelRe = /^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i;
|
|
4267
|
-
const quadrantMatch =
|
|
5353
|
+
const quadrantMatch = line3.match(quadrantLabelRe);
|
|
4268
5354
|
if (quadrantMatch) {
|
|
4269
5355
|
const position = quadrantMatch[1].toLowerCase();
|
|
4270
5356
|
const labelPart = quadrantMatch[2].trim();
|
|
@@ -4280,7 +5366,7 @@ function parseD3(content, palette) {
|
|
|
4280
5366
|
result.quadrantLabels.bottomRight = label;
|
|
4281
5367
|
continue;
|
|
4282
5368
|
}
|
|
4283
|
-
const pointMatch =
|
|
5369
|
+
const pointMatch = line3.match(
|
|
4284
5370
|
/^(.+?):\s*([0-9]*\.?[0-9]+)\s*,\s*([0-9]*\.?[0-9]+)\s*$/
|
|
4285
5371
|
);
|
|
4286
5372
|
if (pointMatch) {
|
|
@@ -4297,13 +5383,13 @@ function parseD3(content, palette) {
|
|
|
4297
5383
|
continue;
|
|
4298
5384
|
}
|
|
4299
5385
|
}
|
|
4300
|
-
const colonIndex =
|
|
5386
|
+
const colonIndex = line3.indexOf(":");
|
|
4301
5387
|
if (colonIndex !== -1) {
|
|
4302
|
-
const rawKey =
|
|
5388
|
+
const rawKey = line3.substring(0, colonIndex).trim();
|
|
4303
5389
|
const key = rawKey.toLowerCase();
|
|
4304
5390
|
const colorMatch = rawKey.match(/^(.+?)\(([^)]+)\)\s*$/);
|
|
4305
5391
|
if (key === "chart") {
|
|
4306
|
-
const value =
|
|
5392
|
+
const value = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4307
5393
|
if (value === "slope" || value === "wordcloud" || value === "arc" || value === "timeline" || value === "venn" || value === "quadrant" || value === "sequence") {
|
|
4308
5394
|
result.type = value;
|
|
4309
5395
|
} else {
|
|
@@ -4313,35 +5399,35 @@ function parseD3(content, palette) {
|
|
|
4313
5399
|
continue;
|
|
4314
5400
|
}
|
|
4315
5401
|
if (key === "title") {
|
|
4316
|
-
result.title =
|
|
5402
|
+
result.title = line3.substring(colonIndex + 1).trim();
|
|
4317
5403
|
if (result.type === "quadrant") {
|
|
4318
5404
|
result.quadrantTitleLineNumber = lineNumber;
|
|
4319
5405
|
}
|
|
4320
5406
|
continue;
|
|
4321
5407
|
}
|
|
4322
5408
|
if (key === "orientation") {
|
|
4323
|
-
const v =
|
|
5409
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4324
5410
|
if (v === "horizontal" || v === "vertical") {
|
|
4325
5411
|
result.orientation = v;
|
|
4326
5412
|
}
|
|
4327
5413
|
continue;
|
|
4328
5414
|
}
|
|
4329
5415
|
if (key === "order") {
|
|
4330
|
-
const v =
|
|
5416
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4331
5417
|
if (v === "name" || v === "group" || v === "degree") {
|
|
4332
5418
|
result.arcOrder = v;
|
|
4333
5419
|
}
|
|
4334
5420
|
continue;
|
|
4335
5421
|
}
|
|
4336
5422
|
if (key === "sort") {
|
|
4337
|
-
const v =
|
|
5423
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4338
5424
|
if (v === "time" || v === "group") {
|
|
4339
5425
|
result.timelineSort = v;
|
|
4340
5426
|
}
|
|
4341
5427
|
continue;
|
|
4342
5428
|
}
|
|
4343
5429
|
if (key === "swimlanes") {
|
|
4344
|
-
const v =
|
|
5430
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4345
5431
|
if (v === "on") {
|
|
4346
5432
|
result.timelineSwimlanes = true;
|
|
4347
5433
|
} else if (v === "off") {
|
|
@@ -4350,7 +5436,7 @@ function parseD3(content, palette) {
|
|
|
4350
5436
|
continue;
|
|
4351
5437
|
}
|
|
4352
5438
|
if (key === "values") {
|
|
4353
|
-
const v =
|
|
5439
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4354
5440
|
if (v === "off") {
|
|
4355
5441
|
result.vennShowValues = false;
|
|
4356
5442
|
} else if (v === "on") {
|
|
@@ -4359,21 +5445,21 @@ function parseD3(content, palette) {
|
|
|
4359
5445
|
continue;
|
|
4360
5446
|
}
|
|
4361
5447
|
if (key === "rotate") {
|
|
4362
|
-
const v =
|
|
5448
|
+
const v = line3.substring(colonIndex + 1).trim().toLowerCase();
|
|
4363
5449
|
if (v === "none" || v === "mixed" || v === "angled") {
|
|
4364
5450
|
result.cloudOptions.rotate = v;
|
|
4365
5451
|
}
|
|
4366
5452
|
continue;
|
|
4367
5453
|
}
|
|
4368
5454
|
if (key === "max") {
|
|
4369
|
-
const v = parseInt(
|
|
5455
|
+
const v = parseInt(line3.substring(colonIndex + 1).trim(), 10);
|
|
4370
5456
|
if (!isNaN(v) && v > 0) {
|
|
4371
5457
|
result.cloudOptions.max = v;
|
|
4372
5458
|
}
|
|
4373
5459
|
continue;
|
|
4374
5460
|
}
|
|
4375
5461
|
if (key === "size") {
|
|
4376
|
-
const v =
|
|
5462
|
+
const v = line3.substring(colonIndex + 1).trim();
|
|
4377
5463
|
const parts = v.split(",").map((s) => parseInt(s.trim(), 10));
|
|
4378
5464
|
if (parts.length === 2 && parts.every((n) => !isNaN(n) && n > 0) && parts[0] < parts[1]) {
|
|
4379
5465
|
result.cloudOptions.minSize = parts[0];
|
|
@@ -4383,7 +5469,7 @@ function parseD3(content, palette) {
|
|
|
4383
5469
|
}
|
|
4384
5470
|
const labelPart = colorMatch ? colorMatch[1].trim() : rawKey;
|
|
4385
5471
|
const colorPart = colorMatch ? resolveColor(colorMatch[2].trim(), palette) : null;
|
|
4386
|
-
const valuePart =
|
|
5472
|
+
const valuePart = line3.substring(colonIndex + 1).trim();
|
|
4387
5473
|
const values = valuePart.split(",").map((v) => v.trim());
|
|
4388
5474
|
const numericValues = [];
|
|
4389
5475
|
let allNumeric = true;
|
|
@@ -4414,15 +5500,15 @@ function parseD3(content, palette) {
|
|
|
4414
5500
|
}
|
|
4415
5501
|
}
|
|
4416
5502
|
if (result.type === "wordcloud") {
|
|
4417
|
-
if (colonIndex === -1 && !
|
|
4418
|
-
result.words.push({ text:
|
|
5503
|
+
if (colonIndex === -1 && !line3.includes(" ")) {
|
|
5504
|
+
result.words.push({ text: line3, weight: 10, lineNumber });
|
|
4419
5505
|
} else {
|
|
4420
|
-
freeformLines.push(
|
|
5506
|
+
freeformLines.push(line3);
|
|
4421
5507
|
}
|
|
4422
5508
|
continue;
|
|
4423
5509
|
}
|
|
4424
|
-
if (result.periods.length === 0 &&
|
|
4425
|
-
const periods =
|
|
5510
|
+
if (result.periods.length === 0 && line3.includes(",") && !line3.includes(":")) {
|
|
5511
|
+
const periods = line3.split(",").map((p) => p.trim()).filter(Boolean);
|
|
4426
5512
|
if (periods.length >= 2) {
|
|
4427
5513
|
result.periods = periods;
|
|
4428
5514
|
continue;
|
|
@@ -4645,7 +5731,7 @@ var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
|
|
|
4645
5731
|
var SLOPE_LABEL_FONT_SIZE = 14;
|
|
4646
5732
|
var SLOPE_CHAR_WIDTH = 8;
|
|
4647
5733
|
function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
4648
|
-
|
|
5734
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
4649
5735
|
const { periods, data, title } = parsed;
|
|
4650
5736
|
if (data.length === 0 || periods.length < 2) return;
|
|
4651
5737
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -4672,7 +5758,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4672
5758
|
const valuePadding = (maxVal - minVal) * 0.1 || 1;
|
|
4673
5759
|
const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
|
|
4674
5760
|
const xScale = d3Scale.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
|
|
4675
|
-
const svg =
|
|
5761
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
4676
5762
|
const g = svg.append("g").attr("transform", `translate(${SLOPE_MARGIN.left},${SLOPE_MARGIN.top})`);
|
|
4677
5763
|
const tooltip = createTooltip(container, palette, isDark);
|
|
4678
5764
|
if (title) {
|
|
@@ -4683,7 +5769,7 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4683
5769
|
g.append("text").attr("x", x).attr("y", -15).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "18px").attr("font-weight", "600").text(period);
|
|
4684
5770
|
g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", "4,4");
|
|
4685
5771
|
}
|
|
4686
|
-
const lineGen =
|
|
5772
|
+
const lineGen = d3Shape2.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
|
|
4687
5773
|
data.forEach((item, idx) => {
|
|
4688
5774
|
const color = item.color ?? colors[idx % colors.length];
|
|
4689
5775
|
const firstVal = item.values[0];
|
|
@@ -4746,11 +5832,11 @@ function renderSlopeChart(container, parsed, palette, isDark, onClickItem, expor
|
|
|
4746
5832
|
const lineHeight = SLOPE_LABEL_FONT_SIZE * 1.2;
|
|
4747
5833
|
const totalHeight = (lines.length - 1) * lineHeight;
|
|
4748
5834
|
const startDy = -totalHeight / 2;
|
|
4749
|
-
lines.forEach((
|
|
5835
|
+
lines.forEach((line3, li) => {
|
|
4750
5836
|
labelEl.append("tspan").attr("x", lastX + 10).attr(
|
|
4751
5837
|
"dy",
|
|
4752
5838
|
li === 0 ? `${startDy + SLOPE_LABEL_FONT_SIZE * 0.35}px` : `${lineHeight}px`
|
|
4753
|
-
).text(
|
|
5839
|
+
).text(line3);
|
|
4754
5840
|
});
|
|
4755
5841
|
}
|
|
4756
5842
|
});
|
|
@@ -4845,7 +5931,7 @@ function orderArcNodes(links, order, groups) {
|
|
|
4845
5931
|
}
|
|
4846
5932
|
var ARC_MARGIN = { top: 60, right: 40, bottom: 60, left: 40 };
|
|
4847
5933
|
function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
4848
|
-
|
|
5934
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
4849
5935
|
const { links, title, orientation, arcOrder, arcNodeGroups } = parsed;
|
|
4850
5936
|
if (links.length === 0) return;
|
|
4851
5937
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -4882,7 +5968,7 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4882
5968
|
const values = links.map((l) => l.value);
|
|
4883
5969
|
const [minVal, maxVal] = d3Array.extent(values);
|
|
4884
5970
|
const strokeScale = d3Scale.scaleLinear().domain([minVal, maxVal]).range([1.5, 6]);
|
|
4885
|
-
const svg =
|
|
5971
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
4886
5972
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
4887
5973
|
if (title) {
|
|
4888
5974
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -4897,14 +5983,14 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4897
5983
|
function handleMouseEnter(hovered) {
|
|
4898
5984
|
const connected = neighbors.get(hovered);
|
|
4899
5985
|
g.selectAll(".arc-link").each(function() {
|
|
4900
|
-
const el =
|
|
5986
|
+
const el = d3Selection3.select(this);
|
|
4901
5987
|
const src = el.attr("data-source");
|
|
4902
5988
|
const tgt = el.attr("data-target");
|
|
4903
5989
|
const isRelated = src === hovered || tgt === hovered;
|
|
4904
5990
|
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
4905
5991
|
});
|
|
4906
5992
|
g.selectAll(".arc-node").each(function() {
|
|
4907
|
-
const el =
|
|
5993
|
+
const el = d3Selection3.select(this);
|
|
4908
5994
|
const name = el.attr("data-node");
|
|
4909
5995
|
const isRelated = name === hovered || connected.has(name);
|
|
4910
5996
|
el.attr("opacity", isRelated ? 1 : FADE_OPACITY);
|
|
@@ -4929,23 +6015,23 @@ function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, expo
|
|
|
4929
6015
|
const members = groupNodeSets.get(groupName);
|
|
4930
6016
|
if (!members) return;
|
|
4931
6017
|
g.selectAll(".arc-link").each(function() {
|
|
4932
|
-
const el =
|
|
6018
|
+
const el = d3Selection3.select(this);
|
|
4933
6019
|
const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
|
|
4934
6020
|
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
4935
6021
|
});
|
|
4936
6022
|
g.selectAll(".arc-node").each(function() {
|
|
4937
|
-
const el =
|
|
6023
|
+
const el = d3Selection3.select(this);
|
|
4938
6024
|
el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY);
|
|
4939
6025
|
});
|
|
4940
6026
|
g.selectAll(".arc-group-band").each(function() {
|
|
4941
|
-
const el =
|
|
6027
|
+
const el = d3Selection3.select(this);
|
|
4942
6028
|
el.attr(
|
|
4943
6029
|
"fill-opacity",
|
|
4944
6030
|
el.attr("data-group") === groupName ? 0.18 : 0.03
|
|
4945
6031
|
);
|
|
4946
6032
|
});
|
|
4947
6033
|
g.selectAll(".arc-group-label").each(function() {
|
|
4948
|
-
const el =
|
|
6034
|
+
const el = d3Selection3.select(this);
|
|
4949
6035
|
el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
|
|
4950
6036
|
});
|
|
4951
6037
|
}
|
|
@@ -5148,7 +6234,14 @@ function computeTimeTicks(domainMin, domainMax, scale, boundaryStart, boundaryEn
|
|
|
5148
6234
|
const firstYear = Math.ceil(domainMin);
|
|
5149
6235
|
const lastYear = Math.floor(domainMax);
|
|
5150
6236
|
if (lastYear >= firstYear + 1) {
|
|
5151
|
-
|
|
6237
|
+
const yearSpan = lastYear - firstYear;
|
|
6238
|
+
let step = 1;
|
|
6239
|
+
if (yearSpan > 80) step = 20;
|
|
6240
|
+
else if (yearSpan > 40) step = 10;
|
|
6241
|
+
else if (yearSpan > 20) step = 5;
|
|
6242
|
+
else if (yearSpan > 10) step = 2;
|
|
6243
|
+
const alignedFirst = Math.ceil(firstYear / step) * step;
|
|
6244
|
+
for (let y = alignedFirst; y <= lastYear; y += step) {
|
|
5152
6245
|
ticks.push({ pos: scale(y), label: String(y) });
|
|
5153
6246
|
}
|
|
5154
6247
|
} else if (span > 0.25) {
|
|
@@ -5248,7 +6341,7 @@ function showEventDatesOnScale(g, scale, startDate, endDate, innerHeight, accent
|
|
|
5248
6341
|
function hideEventDatesOnScale(g) {
|
|
5249
6342
|
g.selectAll(".tl-event-date").remove();
|
|
5250
6343
|
g.selectAll(".tl-scale-tick").each(function() {
|
|
5251
|
-
const el =
|
|
6344
|
+
const el = d3Selection3.select(this);
|
|
5252
6345
|
const isDashed = el.attr("stroke-dasharray");
|
|
5253
6346
|
el.attr("opacity", isDashed ? 0.15 : 0.4);
|
|
5254
6347
|
});
|
|
@@ -5306,7 +6399,7 @@ function buildEraTooltipHtml(era) {
|
|
|
5306
6399
|
return `<strong>${era.label}</strong><br>${formatDateLabel(era.startDate)} \u2192 ${formatDateLabel(era.endDate)}`;
|
|
5307
6400
|
}
|
|
5308
6401
|
function renderTimeline(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
5309
|
-
|
|
6402
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
5310
6403
|
const {
|
|
5311
6404
|
timelineEvents,
|
|
5312
6405
|
timelineGroups,
|
|
@@ -5358,13 +6451,13 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5358
6451
|
const FADE_OPACITY = 0.1;
|
|
5359
6452
|
function fadeToGroup(g, groupName) {
|
|
5360
6453
|
g.selectAll(".tl-event").each(function() {
|
|
5361
|
-
const el =
|
|
6454
|
+
const el = d3Selection3.select(this);
|
|
5362
6455
|
const evGroup = el.attr("data-group");
|
|
5363
6456
|
el.attr("opacity", evGroup === groupName ? 1 : FADE_OPACITY);
|
|
5364
6457
|
});
|
|
5365
6458
|
g.selectAll(".tl-legend-item, .tl-lane-header").each(
|
|
5366
6459
|
function() {
|
|
5367
|
-
const el =
|
|
6460
|
+
const el = d3Selection3.select(this);
|
|
5368
6461
|
const name = el.attr("data-group");
|
|
5369
6462
|
el.attr("opacity", name === groupName ? 1 : FADE_OPACITY);
|
|
5370
6463
|
}
|
|
@@ -5376,7 +6469,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5376
6469
|
}
|
|
5377
6470
|
function fadeToEra(g, eraStart, eraEnd) {
|
|
5378
6471
|
g.selectAll(".tl-event").each(function() {
|
|
5379
|
-
const el =
|
|
6472
|
+
const el = d3Selection3.select(this);
|
|
5380
6473
|
const date = parseFloat(el.attr("data-date"));
|
|
5381
6474
|
const endDate = el.attr("data-end-date");
|
|
5382
6475
|
const evEnd = endDate ? parseFloat(endDate) : date;
|
|
@@ -5388,14 +6481,14 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5388
6481
|
FADE_OPACITY
|
|
5389
6482
|
);
|
|
5390
6483
|
g.selectAll(".tl-era").each(function() {
|
|
5391
|
-
const el =
|
|
6484
|
+
const el = d3Selection3.select(this);
|
|
5392
6485
|
const s = parseFloat(el.attr("data-era-start"));
|
|
5393
6486
|
const e = parseFloat(el.attr("data-era-end"));
|
|
5394
6487
|
const isSelf = s === eraStart && e === eraEnd;
|
|
5395
6488
|
el.attr("opacity", isSelf ? 1 : FADE_OPACITY);
|
|
5396
6489
|
});
|
|
5397
6490
|
g.selectAll(".tl-marker").each(function() {
|
|
5398
|
-
const el =
|
|
6491
|
+
const el = d3Selection3.select(this);
|
|
5399
6492
|
const date = parseFloat(el.attr("data-marker-date"));
|
|
5400
6493
|
const inside = date >= eraStart && date <= eraEnd;
|
|
5401
6494
|
el.attr("opacity", inside ? 1 : FADE_OPACITY);
|
|
@@ -5427,7 +6520,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5427
6520
|
const innerHeight = height - margin.top - margin.bottom;
|
|
5428
6521
|
const laneWidth = innerWidth / laneCount;
|
|
5429
6522
|
const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
|
|
5430
|
-
const svg =
|
|
6523
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5431
6524
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5432
6525
|
if (title) {
|
|
5433
6526
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -5521,7 +6614,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5521
6614
|
const axisX = 20;
|
|
5522
6615
|
const yScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerHeight]);
|
|
5523
6616
|
const sorted = timelineEvents.slice().sort((a, b) => parseTimelineDate(a.date) - parseTimelineDate(b.date));
|
|
5524
|
-
const svg =
|
|
6617
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5525
6618
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5526
6619
|
if (title) {
|
|
5527
6620
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -5644,7 +6737,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5644
6737
|
const totalGaps = (lanes.length - 1) * GROUP_GAP;
|
|
5645
6738
|
const rowH = Math.min(28, (innerHeight - totalGaps) / totalEventRows);
|
|
5646
6739
|
const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
5647
|
-
const svg =
|
|
6740
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5648
6741
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5649
6742
|
if (title) {
|
|
5650
6743
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -5748,7 +6841,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5748
6841
|
if (ev.uncertain) {
|
|
5749
6842
|
const gradientId = `uncertain-${ev.lineNumber}`;
|
|
5750
6843
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
5751
|
-
|
|
6844
|
+
d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
|
|
5752
6845
|
{ offset: "0%", opacity: 1 },
|
|
5753
6846
|
{ offset: "80%", opacity: 1 },
|
|
5754
6847
|
{ offset: "100%", opacity: 0 }
|
|
@@ -5789,7 +6882,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5789
6882
|
const innerHeight = height - margin.top - margin.bottom;
|
|
5790
6883
|
const rowH = Math.min(28, innerHeight / sorted.length);
|
|
5791
6884
|
const xScale = d3Scale.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
|
|
5792
|
-
const svg =
|
|
6885
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5793
6886
|
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
5794
6887
|
if (title) {
|
|
5795
6888
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -5887,7 +6980,7 @@ function renderTimeline(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
5887
6980
|
if (ev.uncertain) {
|
|
5888
6981
|
const gradientId = `uncertain-ts-${ev.lineNumber}`;
|
|
5889
6982
|
const defs = svg.select("defs").node() || svg.append("defs").node();
|
|
5890
|
-
|
|
6983
|
+
d3Selection3.select(defs).append("linearGradient").attr("id", gradientId).attr("x1", "0%").attr("y1", "0%").attr("x2", "100%").attr("y2", "0%").selectAll("stop").data([
|
|
5891
6984
|
{ offset: "0%", opacity: 1 },
|
|
5892
6985
|
{ offset: "80%", opacity: 1 },
|
|
5893
6986
|
{ offset: "100%", opacity: 0 }
|
|
@@ -5920,7 +7013,7 @@ function getRotateFn(mode) {
|
|
|
5920
7013
|
return () => 0;
|
|
5921
7014
|
}
|
|
5922
7015
|
function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
5923
|
-
|
|
7016
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
5924
7017
|
const { words, title, cloudOptions } = parsed;
|
|
5925
7018
|
if (words.length === 0) return;
|
|
5926
7019
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -5941,7 +7034,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
|
|
|
5941
7034
|
return minSize + t * (maxSize - minSize);
|
|
5942
7035
|
};
|
|
5943
7036
|
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
5944
|
-
const svg =
|
|
7037
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5945
7038
|
if (title) {
|
|
5946
7039
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
5947
7040
|
}
|
|
@@ -5964,7 +7057,7 @@ function renderWordCloud(container, parsed, palette, _isDark, onClickItem, expor
|
|
|
5964
7057
|
}
|
|
5965
7058
|
function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
|
|
5966
7059
|
return new Promise((resolve) => {
|
|
5967
|
-
|
|
7060
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
5968
7061
|
const { words, title, cloudOptions } = parsed;
|
|
5969
7062
|
if (words.length === 0) {
|
|
5970
7063
|
resolve();
|
|
@@ -5991,7 +7084,7 @@ function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
|
|
|
5991
7084
|
return minSize + t * (maxSize - minSize);
|
|
5992
7085
|
};
|
|
5993
7086
|
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
5994
|
-
const svg =
|
|
7087
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
5995
7088
|
if (title) {
|
|
5996
7089
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
5997
7090
|
}
|
|
@@ -6135,7 +7228,7 @@ function circlePathD(cx, cy, r) {
|
|
|
6135
7228
|
return `M${cx - r},${cy} A${r},${r} 0 1,0 ${cx + r},${cy} A${r},${r} 0 1,0 ${cx - r},${cy} Z`;
|
|
6136
7229
|
}
|
|
6137
7230
|
function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
6138
|
-
|
|
7231
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6139
7232
|
const { vennSets, vennOverlaps, vennShowValues, title } = parsed;
|
|
6140
7233
|
if (vennSets.length < 2) return;
|
|
6141
7234
|
const width = exportDims?.width ?? container.clientWidth;
|
|
@@ -6200,7 +7293,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
|
|
|
6200
7293
|
const setColors = vennSets.map(
|
|
6201
7294
|
(s, i) => s.color ?? colors[i % colors.length]
|
|
6202
7295
|
);
|
|
6203
|
-
const svg =
|
|
7296
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6204
7297
|
const tooltip = createTooltip(container, palette, isDark);
|
|
6205
7298
|
if (title) {
|
|
6206
7299
|
svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").text(title);
|
|
@@ -6346,7 +7439,7 @@ function renderVenn(container, parsed, palette, isDark, onClickItem, exportDims)
|
|
|
6346
7439
|
});
|
|
6347
7440
|
}
|
|
6348
7441
|
function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
6349
|
-
|
|
7442
|
+
d3Selection3.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
6350
7443
|
const {
|
|
6351
7444
|
title,
|
|
6352
7445
|
quadrantLabels,
|
|
@@ -6378,7 +7471,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6378
7471
|
const chartHeight = height - margin.top - margin.bottom;
|
|
6379
7472
|
const xScale = d3Scale.scaleLinear().domain([0, 1]).range([0, chartWidth]);
|
|
6380
7473
|
const yScale = d3Scale.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
|
|
6381
|
-
const svg =
|
|
7474
|
+
const svg = d3Selection3.select(container).append("svg").attr("width", width).attr("height", height).style("background", bgColor);
|
|
6382
7475
|
const tooltip = createTooltip(container, palette, isDark);
|
|
6383
7476
|
if (title) {
|
|
6384
7477
|
const titleText = svg.append("text").attr("x", width / 2).attr("y", 30).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", "20px").attr("font-weight", "700").style(
|
|
@@ -6387,9 +7480,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6387
7480
|
).text(title);
|
|
6388
7481
|
if (onClickItem && quadrantTitleLineNumber) {
|
|
6389
7482
|
titleText.on("click", () => onClickItem(quadrantTitleLineNumber)).on("mouseenter", function() {
|
|
6390
|
-
|
|
7483
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6391
7484
|
}).on("mouseleave", function() {
|
|
6392
|
-
|
|
7485
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6393
7486
|
});
|
|
6394
7487
|
}
|
|
6395
7488
|
}
|
|
@@ -6472,9 +7565,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6472
7565
|
quadrantLabelTexts.on("click", (_, d) => {
|
|
6473
7566
|
if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
|
|
6474
7567
|
}).on("mouseenter", function() {
|
|
6475
|
-
|
|
7568
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6476
7569
|
}).on("mouseleave", function() {
|
|
6477
|
-
|
|
7570
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6478
7571
|
});
|
|
6479
7572
|
}
|
|
6480
7573
|
if (quadrantXAxis) {
|
|
@@ -6489,9 +7582,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6489
7582
|
if (onClickItem && quadrantXAxisLineNumber) {
|
|
6490
7583
|
[xLowLabel, xHighLabel].forEach((label) => {
|
|
6491
7584
|
label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
|
|
6492
|
-
|
|
7585
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6493
7586
|
}).on("mouseleave", function() {
|
|
6494
|
-
|
|
7587
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6495
7588
|
});
|
|
6496
7589
|
});
|
|
6497
7590
|
}
|
|
@@ -6510,9 +7603,9 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6510
7603
|
if (onClickItem && quadrantYAxisLineNumber) {
|
|
6511
7604
|
[yLowLabel, yHighLabel].forEach((label) => {
|
|
6512
7605
|
label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
|
|
6513
|
-
|
|
7606
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
6514
7607
|
}).on("mouseleave", function() {
|
|
6515
|
-
|
|
7608
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
6516
7609
|
});
|
|
6517
7610
|
});
|
|
6518
7611
|
}
|
|
@@ -6560,7 +7653,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6560
7653
|
pointsG.selectAll("g.point-group").each(function(_2, i) {
|
|
6561
7654
|
const pt = quadrantPoints[i];
|
|
6562
7655
|
const ptQuad = getPointQuadrant(pt.x, pt.y);
|
|
6563
|
-
|
|
7656
|
+
d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
|
|
6564
7657
|
});
|
|
6565
7658
|
}).on("mouseleave", () => {
|
|
6566
7659
|
quadrantRects.attr("opacity", 1);
|
|
@@ -6575,6 +7668,43 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
|
|
|
6575
7668
|
var EXPORT_WIDTH = 1200;
|
|
6576
7669
|
var EXPORT_HEIGHT = 800;
|
|
6577
7670
|
async function renderD3ForExport(content, theme, palette) {
|
|
7671
|
+
const { parseDgmoChartType: parseDgmoChartType2 } = await Promise.resolve().then(() => (init_dgmo_router(), dgmo_router_exports));
|
|
7672
|
+
const detectedType = parseDgmoChartType2(content);
|
|
7673
|
+
if (detectedType === "flowchart") {
|
|
7674
|
+
const { parseFlowchart: parseFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_parser(), flowchart_parser_exports));
|
|
7675
|
+
const { layoutGraph: layoutGraph2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
|
|
7676
|
+
const { renderFlowchart: renderFlowchart2 } = await Promise.resolve().then(() => (init_flowchart_renderer(), flowchart_renderer_exports));
|
|
7677
|
+
const isDark2 = theme === "dark";
|
|
7678
|
+
const { getPalette: getPalette3 } = await Promise.resolve().then(() => (init_palettes(), palettes_exports));
|
|
7679
|
+
const effectivePalette2 = palette ?? (isDark2 ? getPalette3("nord").dark : getPalette3("nord").light);
|
|
7680
|
+
const fcParsed = parseFlowchart2(content, effectivePalette2);
|
|
7681
|
+
if (fcParsed.error || fcParsed.nodes.length === 0) return "";
|
|
7682
|
+
const layout = layoutGraph2(fcParsed);
|
|
7683
|
+
const container2 = document.createElement("div");
|
|
7684
|
+
container2.style.width = `${EXPORT_WIDTH}px`;
|
|
7685
|
+
container2.style.height = `${EXPORT_HEIGHT}px`;
|
|
7686
|
+
container2.style.position = "absolute";
|
|
7687
|
+
container2.style.left = "-9999px";
|
|
7688
|
+
document.body.appendChild(container2);
|
|
7689
|
+
try {
|
|
7690
|
+
renderFlowchart2(container2, fcParsed, layout, effectivePalette2, isDark2, void 0, {
|
|
7691
|
+
width: EXPORT_WIDTH,
|
|
7692
|
+
height: EXPORT_HEIGHT
|
|
7693
|
+
});
|
|
7694
|
+
const svgEl = container2.querySelector("svg");
|
|
7695
|
+
if (!svgEl) return "";
|
|
7696
|
+
if (theme === "transparent") {
|
|
7697
|
+
svgEl.style.background = "none";
|
|
7698
|
+
} else if (!svgEl.style.background) {
|
|
7699
|
+
svgEl.style.background = effectivePalette2.bg;
|
|
7700
|
+
}
|
|
7701
|
+
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
7702
|
+
svgEl.style.fontFamily = FONT_FAMILY;
|
|
7703
|
+
return svgEl.outerHTML;
|
|
7704
|
+
} finally {
|
|
7705
|
+
document.body.removeChild(container2);
|
|
7706
|
+
}
|
|
7707
|
+
}
|
|
6578
7708
|
const parsed = parseD3(content, palette);
|
|
6579
7709
|
if (parsed.error && parsed.type !== "sequence") {
|
|
6580
7710
|
const looksLikeSequence2 = /->|~>|<-/.test(content);
|
|
@@ -6669,17 +7799,17 @@ function parseQuadrant(content) {
|
|
|
6669
7799
|
};
|
|
6670
7800
|
const lines = content.split("\n");
|
|
6671
7801
|
for (let i = 0; i < lines.length; i++) {
|
|
6672
|
-
const
|
|
7802
|
+
const line3 = lines[i].trim();
|
|
6673
7803
|
const lineNumber = i + 1;
|
|
6674
|
-
if (!
|
|
6675
|
-
if (/^chart\s*:/i.test(
|
|
6676
|
-
const titleMatch =
|
|
7804
|
+
if (!line3 || line3.startsWith("#") || line3.startsWith("//")) continue;
|
|
7805
|
+
if (/^chart\s*:/i.test(line3)) continue;
|
|
7806
|
+
const titleMatch = line3.match(/^title\s*:\s*(.+)/i);
|
|
6677
7807
|
if (titleMatch) {
|
|
6678
7808
|
result.title = titleMatch[1].trim();
|
|
6679
7809
|
result.titleLineNumber = lineNumber;
|
|
6680
7810
|
continue;
|
|
6681
7811
|
}
|
|
6682
|
-
const xMatch =
|
|
7812
|
+
const xMatch = line3.match(/^x-axis\s*:\s*(.+)/i);
|
|
6683
7813
|
if (xMatch) {
|
|
6684
7814
|
const parts = xMatch[1].split(",").map((s) => s.trim());
|
|
6685
7815
|
if (parts.length >= 2) {
|
|
@@ -6688,7 +7818,7 @@ function parseQuadrant(content) {
|
|
|
6688
7818
|
}
|
|
6689
7819
|
continue;
|
|
6690
7820
|
}
|
|
6691
|
-
const yMatch =
|
|
7821
|
+
const yMatch = line3.match(/^y-axis\s*:\s*(.+)/i);
|
|
6692
7822
|
if (yMatch) {
|
|
6693
7823
|
const parts = yMatch[1].split(",").map((s) => s.trim());
|
|
6694
7824
|
if (parts.length >= 2) {
|
|
@@ -6697,7 +7827,7 @@ function parseQuadrant(content) {
|
|
|
6697
7827
|
}
|
|
6698
7828
|
continue;
|
|
6699
7829
|
}
|
|
6700
|
-
const posMatch =
|
|
7830
|
+
const posMatch = line3.match(
|
|
6701
7831
|
/^(top-right|top-left|bottom-left|bottom-right)\s*:\s*(.+)/i
|
|
6702
7832
|
);
|
|
6703
7833
|
if (posMatch) {
|
|
@@ -6718,7 +7848,7 @@ function parseQuadrant(content) {
|
|
|
6718
7848
|
}
|
|
6719
7849
|
continue;
|
|
6720
7850
|
}
|
|
6721
|
-
const pointMatch =
|
|
7851
|
+
const pointMatch = line3.match(DATA_POINT_RE);
|
|
6722
7852
|
if (pointMatch) {
|
|
6723
7853
|
const key = pointMatch[1].trim().toLowerCase();
|
|
6724
7854
|
if (!QUADRANT_POSITIONS.has(key)) {
|
|
@@ -6792,6 +7922,9 @@ function buildMermaidQuadrant(parsed, options = {}) {
|
|
|
6792
7922
|
}
|
|
6793
7923
|
|
|
6794
7924
|
// src/index.ts
|
|
7925
|
+
init_flowchart_parser();
|
|
7926
|
+
init_layout();
|
|
7927
|
+
init_flowchart_renderer();
|
|
6795
7928
|
init_renderer();
|
|
6796
7929
|
init_colors();
|
|
6797
7930
|
init_palettes();
|
|
@@ -6825,7 +7958,10 @@ export {
|
|
|
6825
7958
|
hslToHex,
|
|
6826
7959
|
inferParticipantType,
|
|
6827
7960
|
isSequenceBlock,
|
|
7961
|
+
isSequenceNote,
|
|
6828
7962
|
isValidHex,
|
|
7963
|
+
layoutGraph,
|
|
7964
|
+
looksLikeFlowchart,
|
|
6829
7965
|
looksLikeSequence,
|
|
6830
7966
|
mute,
|
|
6831
7967
|
nord,
|
|
@@ -6836,6 +7972,7 @@ export {
|
|
|
6836
7972
|
parseD3,
|
|
6837
7973
|
parseDgmoChartType,
|
|
6838
7974
|
parseEChart,
|
|
7975
|
+
parseFlowchart,
|
|
6839
7976
|
parseQuadrant,
|
|
6840
7977
|
parseSequenceDgmo,
|
|
6841
7978
|
parseTimelineDate,
|
|
@@ -6843,6 +7980,8 @@ export {
|
|
|
6843
7980
|
renderArcDiagram,
|
|
6844
7981
|
renderD3ForExport,
|
|
6845
7982
|
renderEChartsForExport,
|
|
7983
|
+
renderFlowchart,
|
|
7984
|
+
renderFlowchartForExport,
|
|
6846
7985
|
renderQuadrant,
|
|
6847
7986
|
renderSequenceDiagram,
|
|
6848
7987
|
renderSlopeChart,
|