@elixpo/lixsketch 5.5.10 → 5.5.12
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/dist/react/{AIRenderer-46WIFF2B.js → AIRenderer-PWWLWI6U.js} +84 -16
- package/dist/react/AIRenderer-PWWLWI6U.js.map +7 -0
- package/dist/react/{MermaidFlowchartRenderer-A26B2SM3.js → MermaidFlowchartRenderer-4YEMAYQC.js} +18 -3
- package/dist/react/MermaidFlowchartRenderer-4YEMAYQC.js.map +7 -0
- package/dist/react/{MermaidSequenceRenderer-OF4JK77D.js → MermaidSequenceRenderer-KCUHO7UI.js} +106 -4
- package/dist/react/MermaidSequenceRenderer-KCUHO7UI.js.map +7 -0
- package/dist/react/MermaidStructuredRenderer-GGOOGON5.js +365 -0
- package/dist/react/MermaidStructuredRenderer-GGOOGON5.js.map +7 -0
- package/dist/react/{SketchEngine-BCRJ62CV.js → SketchEngine-NPF2XN6Z.js} +2 -2
- package/dist/react/index.js +61 -127
- package/dist/react/index.js.map +3 -3
- package/package.json +1 -1
- package/src/core/AIRenderer.js +95 -11
- package/src/core/MermaidFlowchartRenderer.js +18 -3
- package/src/core/MermaidSequenceRenderer.js +116 -7
- package/src/core/MermaidStructuredRenderer.js +363 -0
- package/src/react/LixSketchCanvas.jsx +1 -1
- package/src/react/components/canvas/FindBar.jsx +1 -1
- package/src/react/components/modals/CanvasPropertiesModal.jsx +1 -1
- package/src/react/components/modals/LixScriptModal.jsx +22 -96
- package/src/react/components/sidebars/ArrowSidebar.jsx +6 -6
- package/src/react/components/sidebars/CircleSidebar.jsx +3 -3
- package/src/react/components/sidebars/FrameSidebar.jsx +1 -1
- package/src/react/components/sidebars/LineSidebar.jsx +4 -4
- package/src/react/components/sidebars/PaintbrushSidebar.jsx +5 -5
- package/src/react/components/sidebars/RectangleSidebar.jsx +4 -4
- package/src/react/components/sidebars/TextSidebar.jsx +5 -5
- package/src/react/store/useUIStore.js +1 -1
- package/dist/react/AIRenderer-46WIFF2B.js.map +0 -7
- package/dist/react/MermaidFlowchartRenderer-A26B2SM3.js.map +0 -7
- package/dist/react/MermaidSequenceRenderer-OF4JK77D.js.map +0 -7
- /package/dist/react/{SketchEngine-BCRJ62CV.js.map → SketchEngine-NPF2XN6Z.js.map} +0 -0
package/dist/react/{MermaidSequenceRenderer-OF4JK77D.js → MermaidSequenceRenderer-KCUHO7UI.js}
RENAMED
|
@@ -16,7 +16,7 @@ var SIDE_MARGIN = 40;
|
|
|
16
16
|
var FONT_FAMILY = "lixFont, sans-serif";
|
|
17
17
|
function themeColors() {
|
|
18
18
|
const isDark = typeof document !== "undefined" && document.body && document.body.classList.contains("theme-dark");
|
|
19
|
-
if (isDark) return
|
|
19
|
+
if (isDark) return DARK_THEME;
|
|
20
20
|
return {
|
|
21
21
|
bg: "#fbfaf6",
|
|
22
22
|
participantBg: "#ffffff",
|
|
@@ -35,7 +35,7 @@ function themeColors() {
|
|
|
35
35
|
crossColor: "#c2483a"
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
var
|
|
38
|
+
var DARK_THEME = {
|
|
39
39
|
bg: "#1e1e28",
|
|
40
40
|
participantBg: "#232329",
|
|
41
41
|
participantBorder: "#555",
|
|
@@ -80,6 +80,7 @@ function wrapText(text, fontSize, maxWidth) {
|
|
|
80
80
|
}
|
|
81
81
|
function renderSequenceSVG(diagram, opts = {}) {
|
|
82
82
|
if (!diagram || diagram.type !== "sequenceDiagram") return "";
|
|
83
|
+
const THEME = themeColors();
|
|
83
84
|
const participants = diagram.participants;
|
|
84
85
|
const messages = diagram.messages;
|
|
85
86
|
const notes = diagram.notes;
|
|
@@ -271,6 +272,8 @@ function renderSequenceOnCanvas(diagram) {
|
|
|
271
272
|
}
|
|
272
273
|
const participants = diagram.participants || [];
|
|
273
274
|
const messages = diagram.messages || [];
|
|
275
|
+
const notes = diagram.notes || [];
|
|
276
|
+
const blocks = diagram.blocks || [];
|
|
274
277
|
if (participants.length === 0) return false;
|
|
275
278
|
const pCount = participants.length;
|
|
276
279
|
const contentWidth = (pCount - 1) * PARTICIPANT_GAP + PARTICIPANT_W;
|
|
@@ -278,12 +281,23 @@ function renderSequenceOnCanvas(diagram) {
|
|
|
278
281
|
const startX = (totalWidth - contentWidth) / 2;
|
|
279
282
|
const pCenters = participants.map((_, i) => startX + i * PARTICIPANT_GAP + PARTICIPANT_W / 2);
|
|
280
283
|
const topBoxBottom = TOP_MARGIN + PARTICIPANT_H;
|
|
284
|
+
const noteAtMsg = /* @__PURE__ */ new Map();
|
|
285
|
+
notes.forEach((note) => {
|
|
286
|
+
const lines = wrapText(note.text, 11, NOTE_MAX_W - NOTE_PAD * 2);
|
|
287
|
+
const height = lines.length * 15 + NOTE_PAD * 2;
|
|
288
|
+
if (!noteAtMsg.has(note.atMessage)) noteAtMsg.set(note.atMessage, []);
|
|
289
|
+
noteAtMsg.get(note.atMessage).push({ ...note, lines, height });
|
|
290
|
+
});
|
|
281
291
|
const msgYPositions = [];
|
|
282
292
|
let currentY = topBoxBottom + 30;
|
|
283
293
|
for (let mi = 0; mi < messages.length; mi++) {
|
|
294
|
+
const before = noteAtMsg.get(mi);
|
|
295
|
+
if (before?.length) currentY += Math.max(...before.map((note) => note.height)) + 10;
|
|
284
296
|
msgYPositions.push(currentY);
|
|
285
297
|
currentY += MSG_ROW_HEIGHT;
|
|
286
298
|
}
|
|
299
|
+
const trailingNotes = noteAtMsg.get(messages.length);
|
|
300
|
+
if (trailingNotes?.length) currentY += Math.max(...trailingNotes.map((note) => note.height)) + 10;
|
|
287
301
|
const bottomBoxTop = currentY + 20;
|
|
288
302
|
const totalHeight = bottomBoxTop + PARTICIPANT_H + BOTTOM_MARGIN;
|
|
289
303
|
const vb = window.currentViewBox || { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight };
|
|
@@ -364,12 +378,40 @@ function renderSequenceOnCanvas(diagram) {
|
|
|
364
378
|
console.warn("[SequenceRenderer] Participant creation failed:", p.name, err);
|
|
365
379
|
}
|
|
366
380
|
}
|
|
381
|
+
for (const block of blocks) {
|
|
382
|
+
const startY = block.startMsg < msgYPositions.length ? msgYPositions[block.startMsg] - 22 : topBoxBottom + 15;
|
|
383
|
+
const endY = block.endMsg < msgYPositions.length ? msgYPositions[block.endMsg] - 8 : currentY;
|
|
384
|
+
try {
|
|
385
|
+
const blockShape = new window.Rectangle(
|
|
386
|
+
startX - 18 + ox,
|
|
387
|
+
startY + oy,
|
|
388
|
+
contentWidth + 36,
|
|
389
|
+
Math.max(36, endY - startY),
|
|
390
|
+
{
|
|
391
|
+
stroke: TK.blockBorder,
|
|
392
|
+
strokeWidth: 1,
|
|
393
|
+
strokeDasharray: "5 3",
|
|
394
|
+
fill: "transparent",
|
|
395
|
+
fillStyle: "none",
|
|
396
|
+
roughness: 0.5,
|
|
397
|
+
label: [block.type, block.label].filter(Boolean).join(": "),
|
|
398
|
+
labelColor: TK.blockLabel,
|
|
399
|
+
labelFontSize: 10
|
|
400
|
+
}
|
|
401
|
+
);
|
|
402
|
+
window.shapes.push(blockShape);
|
|
403
|
+
if (window.pushCreateAction) window.pushCreateAction(blockShape);
|
|
404
|
+
frame.addShapeToFrame(blockShape);
|
|
405
|
+
created.push(blockShape);
|
|
406
|
+
} catch (err) {
|
|
407
|
+
console.warn("[SequenceRenderer] Block creation failed:", block, err);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
367
410
|
for (let mi = 0; mi < messages.length; mi++) {
|
|
368
411
|
const m = messages[mi];
|
|
369
412
|
const fromI = pIndex.get(m.from);
|
|
370
413
|
const toI = pIndex.get(m.to);
|
|
371
414
|
if (fromI == null || toI == null) continue;
|
|
372
|
-
if (fromI === toI) continue;
|
|
373
415
|
const fromCx = pCenters[fromI] + ox;
|
|
374
416
|
const toCx = pCenters[toI] + ox;
|
|
375
417
|
const y = msgYPositions[mi] + oy;
|
|
@@ -386,6 +428,26 @@ function renderSequenceOnCanvas(diagram) {
|
|
|
386
428
|
try {
|
|
387
429
|
const sp = { x: fromCx, y };
|
|
388
430
|
const ep = { x: toCx, y };
|
|
431
|
+
if (fromI === toI) {
|
|
432
|
+
const loopWidth = 46;
|
|
433
|
+
const loopHeight = 28;
|
|
434
|
+
const a = { x: fromCx, y };
|
|
435
|
+
const b = { x: fromCx + loopWidth, y };
|
|
436
|
+
const c = { x: fromCx + loopWidth, y: y + loopHeight };
|
|
437
|
+
const d = { x: fromCx + 5, y: y + loopHeight };
|
|
438
|
+
const segments = [
|
|
439
|
+
new window.Line(a, b, opts),
|
|
440
|
+
new window.Line(b, c, opts),
|
|
441
|
+
new window.Arrow(c, d, opts)
|
|
442
|
+
];
|
|
443
|
+
segments.forEach((segment) => {
|
|
444
|
+
window.shapes.push(segment);
|
|
445
|
+
if (window.pushCreateAction) window.pushCreateAction(segment);
|
|
446
|
+
frame.addShapeToFrame(segment);
|
|
447
|
+
created.push(segment);
|
|
448
|
+
});
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
389
451
|
const connector = isCross ? new window.Line(sp, ep, opts) : new window.Arrow(sp, ep, opts);
|
|
390
452
|
window.shapes.push(connector);
|
|
391
453
|
if (window.pushCreateAction) window.pushCreateAction(connector);
|
|
@@ -395,6 +457,46 @@ function renderSequenceOnCanvas(diagram) {
|
|
|
395
457
|
console.warn("[SequenceRenderer] Message creation failed:", m, err);
|
|
396
458
|
}
|
|
397
459
|
}
|
|
460
|
+
for (const [messageIndex, noteGroup] of noteAtMsg.entries()) {
|
|
461
|
+
const baseY = messageIndex < msgYPositions.length ? msgYPositions[messageIndex] - 15 : messageIndex > 0 ? msgYPositions[messageIndex - 1] + MSG_ROW_HEIGHT - 15 : topBoxBottom + 30;
|
|
462
|
+
noteGroup.forEach((note, noteIndex) => {
|
|
463
|
+
const indexes = note.targets.map((target) => pIndex.get(target)).filter((index) => index !== void 0);
|
|
464
|
+
if (!indexes.length) return;
|
|
465
|
+
const noteWidth = NOTE_MAX_W;
|
|
466
|
+
let centerX = pCenters[indexes[0]];
|
|
467
|
+
if (note.position === "over" && indexes.length > 1) {
|
|
468
|
+
centerX = (pCenters[Math.min(...indexes)] + pCenters[Math.max(...indexes)]) / 2;
|
|
469
|
+
} else if (note.position === "left of") {
|
|
470
|
+
centerX -= PARTICIPANT_W / 2 + noteWidth / 2 + 8;
|
|
471
|
+
} else if (note.position === "right of") {
|
|
472
|
+
centerX += PARTICIPANT_W / 2 + noteWidth / 2 + 8;
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
const noteShape = new window.Rectangle(
|
|
476
|
+
centerX - noteWidth / 2 + ox,
|
|
477
|
+
baseY - note.height - noteIndex * (note.height + 6) + oy,
|
|
478
|
+
noteWidth,
|
|
479
|
+
note.height,
|
|
480
|
+
{
|
|
481
|
+
stroke: TK.noteBorder,
|
|
482
|
+
strokeWidth: 1,
|
|
483
|
+
fill: TK.noteBg,
|
|
484
|
+
fillStyle: "solid",
|
|
485
|
+
roughness: 0.7,
|
|
486
|
+
label: note.text,
|
|
487
|
+
labelColor: TK.noteText,
|
|
488
|
+
labelFontSize: 11
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
window.shapes.push(noteShape);
|
|
492
|
+
if (window.pushCreateAction) window.pushCreateAction(noteShape);
|
|
493
|
+
frame.addShapeToFrame(noteShape);
|
|
494
|
+
created.push(noteShape);
|
|
495
|
+
} catch (err) {
|
|
496
|
+
console.warn("[SequenceRenderer] Note creation failed:", note, err);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
}
|
|
398
500
|
const first = created[0];
|
|
399
501
|
if (first) {
|
|
400
502
|
window.currentShape = first;
|
|
@@ -409,4 +511,4 @@ export {
|
|
|
409
511
|
renderSequencePreviewSVG,
|
|
410
512
|
renderSequenceSVG
|
|
411
513
|
};
|
|
412
|
-
//# sourceMappingURL=MermaidSequenceRenderer-
|
|
514
|
+
//# sourceMappingURL=MermaidSequenceRenderer-KCUHO7UI.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/MermaidSequenceRenderer.js"],
|
|
4
|
+
"sourcesContent": ["/* eslint-disable */\n/**\n * MermaidSequenceRenderer - Renders parsed sequence diagrams as high-quality SVG.\n *\n * One renderer for both preview and canvas \u2014 ensures they always match.\n * The canvas renderer inserts the SVG into a frame; elements inside are\n * individually editable (each participant box, message label, note, etc.\n * is its own SVG group with data attributes for the editor).\n *\n * Design follows the reference image: dark theme, clean lines,\n * participant boxes at top and bottom, dashed lifelines, arrow styles.\n */\n\nimport { parseSequenceDiagram } from './MermaidSequenceParser.js';\n\n// Layout constants\nconst PARTICIPANT_W = 100;\nconst PARTICIPANT_H = 36;\nconst PARTICIPANT_GAP = 140;\nconst MSG_ROW_HEIGHT = 50;\nconst NOTE_PAD = 10;\nconst NOTE_MAX_W = 160;\nconst TOP_MARGIN = 30;\nconst BOTTOM_MARGIN = 30;\nconst SIDE_MARGIN = 40;\nconst FONT_FAMILY = 'lixFont, sans-serif';\nconst CODE_FONT = 'lixCode, monospace';\n\n// Issue #38 follow-up: theme-aware palette. The on-canvas renderer reads\n// `themeColors()` at draw time so a single render call gets whichever\n// palette is active. The dark THEME object below is kept for the SVG-\n// string preview path (`renderSequenceSVG`), which is rendered inside\n// the modal's preview pane.\nfunction themeColors() {\n const isDark = typeof document !== 'undefined'\n && document.body\n && document.body.classList.contains('theme-dark');\n if (isDark) return DARK_THEME;\n return {\n bg: '#fbfaf6',\n participantBg: '#ffffff',\n participantBorder: '#9c9c9c',\n participantText: '#38384e',\n lifeline: '#b0b0b8',\n messageLine: '#62627a',\n messageDash: '#888',\n messageText: '#38384e',\n noteBg: '#fffce0',\n noteBorder: '#c0b870',\n noteText: '#5e5230',\n blockBg: 'rgba(80,80,120,0.08)',\n blockBorder: '#9c9c9c',\n blockLabel: '#62627a',\n crossColor: '#c2483a',\n };\n}\n\n// Theme colors (dark theme \u2014 preview/SVG-string path).\nconst DARK_THEME = {\n bg: '#1e1e28',\n participantBg: '#232329',\n participantBorder: '#555',\n participantText: '#e8e8ee',\n lifeline: '#444',\n messageLine: '#888',\n messageDash: '#666',\n messageText: '#e0e0e0',\n noteBg: '#3a3520',\n noteBorder: '#665e30',\n noteText: '#d4c870',\n blockBg: 'rgba(80,80,120,0.12)',\n blockBorder: '#555',\n blockLabel: '#a0a0b0',\n crossColor: '#e74c3c',\n};\n\nfunction escapeXml(str) {\n return String(str)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\n/**\n * Measure approximate text width (since we can't use DOM measurement in pure SVG generation).\n * Uses a rough character-width heuristic.\n */\nfunction measureText(text, fontSize) {\n const avgCharWidth = fontSize * 0.55;\n return text.length * avgCharWidth;\n}\n\n/**\n * Word-wrap text into lines that fit within maxWidth.\n */\nfunction wrapText(text, fontSize, maxWidth) {\n // Handle <br/> tags\n const segments = text.split(/<br\\s*\\/?>/i);\n const lines = [];\n for (const segment of segments) {\n const words = segment.split(/\\s+/);\n let currentLine = '';\n for (const word of words) {\n const testLine = currentLine ? currentLine + ' ' + word : word;\n if (measureText(testLine, fontSize) > maxWidth && currentLine) {\n lines.push(currentLine);\n currentLine = word;\n } else {\n currentLine = testLine;\n }\n }\n if (currentLine) lines.push(currentLine);\n }\n return lines.length > 0 ? lines : [''];\n}\n\n/**\n * Render a parsed sequence diagram to SVG markup.\n *\n * @param {Object} diagram - Parsed from parseSequenceDiagram()\n * @param {Object} opts - { width?, fitToContent? }\n * @returns {string} SVG markup string\n */\nexport function renderSequenceSVG(diagram, opts = {}) {\n if (!diagram || diagram.type !== 'sequenceDiagram') return '';\n\n const THEME = themeColors();\n\n const participants = diagram.participants;\n const messages = diagram.messages;\n const notes = diagram.notes;\n const blocks = diagram.blocks || [];\n\n const pCount = participants.length;\n if (pCount === 0) return '';\n\n // Build participant index map\n const pIndex = new Map();\n participants.forEach((p, i) => pIndex.set(p.name, i));\n\n // Calculate total width\n const contentWidth = (pCount - 1) * PARTICIPANT_GAP + PARTICIPANT_W;\n const totalWidth = opts.width || Math.max(contentWidth + SIDE_MARGIN * 2, 400);\n const startX = (totalWidth - contentWidth) / 2;\n\n // Participant X centers\n const pCenters = participants.map((_, i) => startX + i * PARTICIPANT_GAP + PARTICIPANT_W / 2);\n\n // Pre-calculate note heights to account for row expansion\n const noteAtMsg = new Map(); // msgIndex -> [{note, height}]\n for (const note of notes) {\n const fontSize = 11;\n const lines = wrapText(note.text, fontSize, NOTE_MAX_W - NOTE_PAD * 2);\n const h = lines.length * (fontSize + 4) + NOTE_PAD * 2;\n if (!noteAtMsg.has(note.atMessage)) noteAtMsg.set(note.atMessage, []);\n noteAtMsg.get(note.atMessage).push({ ...note, lines, height: h });\n }\n\n // Calculate message Y positions (accounting for notes that expand rows)\n const topBoxBottom = TOP_MARGIN + PARTICIPANT_H;\n let currentY = topBoxBottom + 30; // gap after top participant boxes\n\n const msgYPositions = [];\n for (let mi = 0; mi < messages.length; mi++) {\n // Check if notes appear before this message\n const notesBefore = noteAtMsg.get(mi);\n if (notesBefore) {\n const maxNoteH = Math.max(...notesBefore.map(n => n.height));\n currentY += maxNoteH + 10;\n }\n msgYPositions.push(currentY);\n currentY += MSG_ROW_HEIGHT;\n }\n\n // Notes after last message\n const notesAfterLast = noteAtMsg.get(messages.length);\n if (notesAfterLast) {\n const maxNoteH = Math.max(...notesAfterLast.map(n => n.height));\n currentY += maxNoteH + 10;\n }\n\n const bottomBoxTop = currentY + 20;\n const totalHeight = bottomBoxTop + PARTICIPANT_H + BOTTOM_MARGIN;\n\n // Start building SVG\n let svg = '';\n const defs = [];\n\n // Arrow markers\n defs.push(`<marker id=\"seq-arrow-open\" markerWidth=\"10\" markerHeight=\"7\" refX=\"9\" refY=\"3.5\" orient=\"auto\">\n <polyline points=\"1,1 9,3.5 1,6\" fill=\"none\" stroke=\"${THEME.messageLine}\" stroke-width=\"1.5\" stroke-linejoin=\"round\" />\n </marker>`);\n defs.push(`<marker id=\"seq-arrow-filled\" markerWidth=\"10\" markerHeight=\"7\" refX=\"9\" refY=\"3.5\" orient=\"auto\">\n <polygon points=\"1,1 9,3.5 1,6\" fill=\"${THEME.messageLine}\" stroke=\"none\" />\n </marker>`);\n\n // Background\n svg += `<rect x=\"0\" y=\"0\" width=\"${totalWidth}\" height=\"${totalHeight}\" fill=\"${THEME.bg}\" rx=\"8\" />`;\n\n // Title\n if (diagram.title) {\n svg += `<text x=\"${totalWidth / 2}\" y=\"${TOP_MARGIN - 8}\" text-anchor=\"middle\" fill=\"${THEME.participantText}\" font-size=\"14\" font-family=\"${FONT_FAMILY}\" font-weight=\"600\">${escapeXml(diagram.title)}</text>`;\n }\n\n // --- Lifelines (dashed vertical lines) ---\n for (let pi = 0; pi < pCount; pi++) {\n const cx = pCenters[pi];\n svg += `<line x1=\"${cx}\" y1=\"${topBoxBottom}\" x2=\"${cx}\" y2=\"${bottomBoxTop}\" stroke=\"${THEME.lifeline}\" stroke-width=\"1\" stroke-dasharray=\"6 4\" />`;\n }\n\n // --- Participant boxes (top) ---\n for (let pi = 0; pi < pCount; pi++) {\n const cx = pCenters[pi];\n const bx = cx - PARTICIPANT_W / 2;\n const by = TOP_MARGIN;\n svg += `<g data-seq-type=\"participant\" data-seq-id=\"${escapeXml(participants[pi].name)}\" data-seq-pos=\"top\">`;\n svg += `<rect x=\"${bx}\" y=\"${by}\" width=\"${PARTICIPANT_W}\" height=\"${PARTICIPANT_H}\" rx=\"4\" fill=\"${THEME.participantBg}\" stroke=\"${THEME.participantBorder}\" stroke-width=\"1.5\" />`;\n svg += `<text x=\"${cx}\" y=\"${by + PARTICIPANT_H / 2 + 1}\" text-anchor=\"middle\" dominant-baseline=\"central\" fill=\"${THEME.participantText}\" font-size=\"13\" font-family=\"${FONT_FAMILY}\">${escapeXml(participants[pi].name)}</text>`;\n svg += `</g>`;\n }\n\n // --- Participant boxes (bottom) ---\n for (let pi = 0; pi < pCount; pi++) {\n const cx = pCenters[pi];\n const bx = cx - PARTICIPANT_W / 2;\n const by = bottomBoxTop;\n svg += `<g data-seq-type=\"participant\" data-seq-id=\"${escapeXml(participants[pi].name)}\" data-seq-pos=\"bottom\">`;\n svg += `<rect x=\"${bx}\" y=\"${by}\" width=\"${PARTICIPANT_W}\" height=\"${PARTICIPANT_H}\" rx=\"4\" fill=\"${THEME.participantBg}\" stroke=\"${THEME.participantBorder}\" stroke-width=\"1.5\" />`;\n svg += `<text x=\"${cx}\" y=\"${by + PARTICIPANT_H / 2 + 1}\" text-anchor=\"middle\" dominant-baseline=\"central\" fill=\"${THEME.participantText}\" font-size=\"13\" font-family=\"${FONT_FAMILY}\">${escapeXml(participants[pi].name)}</text>`;\n svg += `</g>`;\n }\n\n // --- Blocks (alt/loop/opt etc.) ---\n for (const block of blocks) {\n const startY = block.startMsg < msgYPositions.length\n ? msgYPositions[block.startMsg] - 20\n : topBoxBottom + 20;\n const endY = block.endMsg <= msgYPositions.length\n ? (block.endMsg < msgYPositions.length ? msgYPositions[block.endMsg] - 10 : currentY)\n : currentY;\n\n const blockX = startX - 15;\n const blockW = contentWidth + 30;\n\n svg += `<g data-seq-type=\"block\" data-block-type=\"${block.type}\">`;\n svg += `<rect x=\"${blockX}\" y=\"${startY}\" width=\"${blockW}\" height=\"${endY - startY}\" rx=\"4\" fill=\"${THEME.blockBg}\" stroke=\"${THEME.blockBorder}\" stroke-width=\"1\" stroke-dasharray=\"4 2\" />`;\n // Type label in top-left corner\n svg += `<rect x=\"${blockX}\" y=\"${startY}\" width=\"${measureText(block.type, 10) + 12}\" height=\"18\" rx=\"3\" fill=\"${THEME.blockBorder}\" />`;\n svg += `<text x=\"${blockX + 6}\" y=\"${startY + 12}\" fill=\"${THEME.bg}\" font-size=\"10\" font-family=\"${FONT_FAMILY}\" font-weight=\"600\">${escapeXml(block.type)}</text>`;\n // Condition label\n if (block.label) {\n svg += `<text x=\"${blockX + measureText(block.type, 10) + 20}\" y=\"${startY + 12}\" fill=\"${THEME.blockLabel}\" font-size=\"10\" font-family=\"${FONT_FAMILY}\" font-style=\"italic\">[${escapeXml(block.label)}]</text>`;\n }\n\n // Section dividers (else sections)\n for (let si = 1; si < block.sections.length; si++) {\n const section = block.sections[si];\n const secY = section.startMsg < msgYPositions.length\n ? msgYPositions[section.startMsg] - 15\n : endY - 10;\n svg += `<line x1=\"${blockX}\" y1=\"${secY}\" x2=\"${blockX + blockW}\" y2=\"${secY}\" stroke=\"${THEME.blockBorder}\" stroke-width=\"1\" stroke-dasharray=\"4 2\" />`;\n if (section.label) {\n svg += `<text x=\"${blockX + 8}\" y=\"${secY + 13}\" fill=\"${THEME.blockLabel}\" font-size=\"10\" font-family=\"${FONT_FAMILY}\" font-style=\"italic\">[${escapeXml(section.label)}]</text>`;\n }\n }\n svg += `</g>`;\n }\n\n // --- Messages ---\n for (let mi = 0; mi < messages.length; mi++) {\n const msg = messages[mi];\n const y = msgYPositions[mi];\n const fromIdx = pIndex.get(msg.from);\n const toIdx = pIndex.get(msg.to);\n if (fromIdx === undefined || toIdx === undefined) continue;\n\n const fromX = pCenters[fromIdx];\n const toX = pCenters[toIdx];\n const isSelf = fromIdx === toIdx;\n\n svg += `<g data-seq-type=\"message\" data-seq-idx=\"${mi}\">`;\n\n if (isSelf) {\n // Self-message: loop arrow to the right\n const loopW = 40;\n const loopH = 25;\n const dash = msg.solid ? '' : ` stroke-dasharray=\"6 3\"`;\n svg += `<path d=\"M ${fromX} ${y} L ${fromX + loopW} ${y} L ${fromX + loopW} ${y + loopH} L ${fromX + 4} ${y + loopH}\" fill=\"none\" stroke=\"${THEME.messageLine}\" stroke-width=\"1.5\"${dash} marker-end=\"url(#seq-arrow-open)\" />`;\n if (msg.text) {\n svg += `<text x=\"${fromX + loopW + 6}\" y=\"${y + loopH / 2 + 1}\" dominant-baseline=\"central\" fill=\"${THEME.messageText}\" font-size=\"12\" font-family=\"${FONT_FAMILY}\">${escapeXml(msg.text)}</text>`;\n }\n } else {\n const isLeft = toX < fromX;\n const lineEndX = isLeft ? toX + 4 : toX - 4;\n const dash = msg.solid ? '' : ` stroke-dasharray=\"6 3\"`;\n\n // Arrow line\n const markerId = msg.arrowHead === 'filled' ? 'seq-arrow-filled' : 'seq-arrow-open';\n\n if (msg.cross) {\n // Cross at end (lost message)\n svg += `<line x1=\"${fromX}\" y1=\"${y}\" x2=\"${lineEndX}\" y2=\"${y}\" stroke=\"${THEME.messageLine}\" stroke-width=\"1.5\"${dash} />`;\n // X mark\n const xSize = 6;\n svg += `<line x1=\"${toX - xSize}\" y1=\"${y - xSize}\" x2=\"${toX + xSize}\" y2=\"${y + xSize}\" stroke=\"${THEME.crossColor}\" stroke-width=\"2\" />`;\n svg += `<line x1=\"${toX + xSize}\" y1=\"${y - xSize}\" x2=\"${toX - xSize}\" y2=\"${y + xSize}\" stroke=\"${THEME.crossColor}\" stroke-width=\"2\" />`;\n } else {\n svg += `<line x1=\"${fromX}\" y1=\"${y}\" x2=\"${lineEndX}\" y2=\"${y}\" stroke=\"${THEME.messageLine}\" stroke-width=\"1.5\"${dash} marker-end=\"url(#${markerId})\" />`;\n }\n\n // Message text (centered above the line)\n if (msg.text) {\n const midX = (fromX + toX) / 2;\n const textContent = msg.number ? `${msg.number}. ${msg.text}` : msg.text;\n svg += `<text x=\"${midX}\" y=\"${y - 8}\" text-anchor=\"middle\" fill=\"${THEME.messageText}\" font-size=\"12\" font-family=\"${FONT_FAMILY}\">${escapeXml(textContent)}</text>`;\n }\n }\n\n svg += `</g>`;\n }\n\n // --- Notes ---\n for (const [msgIdx, noteGroup] of noteAtMsg.entries()) {\n const baseY = msgIdx < msgYPositions.length\n ? msgYPositions[msgIdx] - 15\n : (msgIdx > 0 ? msgYPositions[msgIdx - 1] + MSG_ROW_HEIGHT - 15 : topBoxBottom + 30);\n\n for (const note of noteGroup) {\n const targetIdxs = note.targets.map(t => pIndex.get(t)).filter(i => i !== undefined);\n if (targetIdxs.length === 0) continue;\n\n const fontSize = 11;\n const lineH = fontSize + 4;\n const noteH = note.lines.length * lineH + NOTE_PAD * 2;\n const noteW = Math.min(\n NOTE_MAX_W,\n Math.max(...note.lines.map(l => measureText(l, fontSize))) + NOTE_PAD * 2 + 10\n );\n\n let noteX;\n if (note.position === 'left of') {\n const px = pCenters[targetIdxs[0]];\n noteX = px - PARTICIPANT_W / 2 - noteW - 8;\n } else if (note.position === 'right of') {\n const px = pCenters[targetIdxs[0]];\n noteX = px + PARTICIPANT_W / 2 + 8;\n } else {\n // 'over' - center between targets\n if (targetIdxs.length >= 2) {\n const minP = Math.min(...targetIdxs);\n const maxP = Math.max(...targetIdxs);\n const center = (pCenters[minP] + pCenters[maxP]) / 2;\n noteX = center - noteW / 2;\n } else {\n noteX = pCenters[targetIdxs[0]] - noteW / 2;\n }\n }\n\n const noteY = baseY - noteH;\n\n svg += `<g data-seq-type=\"note\">`;\n svg += `<rect x=\"${noteX}\" y=\"${noteY}\" width=\"${noteW}\" height=\"${noteH}\" rx=\"3\" fill=\"${THEME.noteBg}\" stroke=\"${THEME.noteBorder}\" stroke-width=\"1\" />`;\n // Render wrapped text lines\n note.lines.forEach((line, li) => {\n svg += `<text x=\"${noteX + NOTE_PAD}\" y=\"${noteY + NOTE_PAD + li * lineH + fontSize}\" fill=\"${THEME.noteText}\" font-size=\"${fontSize}\" font-family=\"${FONT_FAMILY}\">${escapeXml(line)}</text>`;\n });\n svg += `</g>`;\n }\n }\n\n // Build final SVG\n const defsStr = defs.length > 0 ? `<defs>${defs.join('')}</defs>` : '';\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${totalWidth}\" height=\"${totalHeight}\" viewBox=\"0 0 ${totalWidth} ${totalHeight}\">${defsStr}${svg}</svg>`;\n}\n\n/**\n * Generate preview SVG for the modal (fixed width).\n */\nexport function renderSequencePreviewSVG(diagram) {\n return renderSequenceSVG(diagram, { width: 620 });\n}\n\n/**\n * Parse raw mermaid source and render sequence SVG.\n * Returns SVG string or empty string if not a sequence diagram.\n */\nexport function parseAndRenderSequence(src) {\n const diagram = parseSequenceDiagram(src);\n if (!diagram) return '';\n return renderSequenceSVG(diagram);\n}\n\n/**\n * Render a sequence diagram onto the canvas as a real engine `Frame`\n * containing independent shapes \u2014 top + bottom participant boxes,\n * dashed lifelines, and a real Arrow (or Line for `--x`/`-x`) per\n * message. Each child is fully independent for click / drag / resize;\n * the Frame's `_diagramType` marker makes Frame.destroy() pull the\n * children along on delete, so the diagram still behaves as one\n * logical unit when discarded.\n *\n * Issue #34 bug #3 (follow-up to #24 per-actor split): drops the shared\n * `groupId` glue so clicking one shape selects only that shape.\n *\n * Notes and block frames are ordinary labelled rectangles. Self messages\n * use three line segments plus an arrow, keeping every visible component\n * editable with the same primitives users draw manually.\n */\nexport function renderSequenceOnCanvas(diagram) {\n if (!diagram || diagram.type !== 'sequenceDiagram') return false;\n if (!window.svg || !window.Rectangle || !window.Line || !window.Arrow) {\n console.error('[SequenceRenderer] Engine not initialized (Rectangle / Line / Arrow missing)');\n return false;\n }\n\n const participants = diagram.participants || [];\n const messages = diagram.messages || [];\n const notes = diagram.notes || [];\n const blocks = diagram.blocks || [];\n if (participants.length === 0) return false;\n\n // Mirror the layout math from renderSequenceSVG so the canvas layout\n // matches the modal preview.\n const pCount = participants.length;\n const contentWidth = (pCount - 1) * PARTICIPANT_GAP + PARTICIPANT_W;\n const totalWidth = Math.max(contentWidth + SIDE_MARGIN * 2, 400);\n const startX = (totalWidth - contentWidth) / 2;\n const pCenters = participants.map((_, i) => startX + i * PARTICIPANT_GAP + PARTICIPANT_W / 2);\n\n const topBoxBottom = TOP_MARGIN + PARTICIPANT_H;\n const noteAtMsg = new Map();\n notes.forEach((note) => {\n const lines = wrapText(note.text, 11, NOTE_MAX_W - NOTE_PAD * 2);\n const height = lines.length * 15 + NOTE_PAD * 2;\n if (!noteAtMsg.has(note.atMessage)) noteAtMsg.set(note.atMessage, []);\n noteAtMsg.get(note.atMessage).push({ ...note, lines, height });\n });\n const msgYPositions = [];\n let currentY = topBoxBottom + 30;\n for (let mi = 0; mi < messages.length; mi++) {\n const before = noteAtMsg.get(mi);\n if (before?.length) currentY += Math.max(...before.map(note => note.height)) + 10;\n msgYPositions.push(currentY);\n currentY += MSG_ROW_HEIGHT;\n }\n const trailingNotes = noteAtMsg.get(messages.length);\n if (trailingNotes?.length) currentY += Math.max(...trailingNotes.map(note => note.height)) + 10;\n const bottomBoxTop = currentY + 20;\n const totalHeight = bottomBoxTop + PARTICIPANT_H + BOTTOM_MARGIN;\n\n // Centre the diagram on the current viewport so the user sees it\n // right where they invoked the renderer.\n const vb = window.currentViewBox || { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight };\n const ox = vb.x + vb.width / 2 - totalWidth / 2;\n const oy = vb.y + vb.height / 2 - totalHeight / 2;\n\n // \u2500\u2500 Wrapper frame \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Issue #34 bug #3: drop the shared groupId glue. Each child is\n // independent for click / drag / resize. The Frame's `_diagramType`\n // marker makes Frame.destroy() pull the children along on delete,\n // so the diagram still behaves as one logical unit when discarded.\n if (!window.Frame) {\n console.error('[SequenceRenderer] window.Frame missing \u2014 cannot wrap diagram');\n return false;\n }\n // Padding bumped 24 \u2192 60 so participant labels + message text near\n // the frame's edges stay inside on the new wider light canvas.\n const PADDING = 60;\n const TK = themeColors();\n const frameTitle = diagram.title || 'Sequence diagram';\n const frame = new window.Frame(\n ox - PADDING,\n oy - PADDING,\n totalWidth + PADDING * 2,\n totalHeight + PADDING * 2,\n {\n stroke: TK.blockBorder,\n strokeWidth: 1,\n fill: 'transparent',\n opacity: 0.7,\n frameName: frameTitle,\n }\n );\n frame._diagramType = 'mermaid-sequence';\n window.shapes.push(frame);\n if (window.pushCreateAction) window.pushCreateAction(frame);\n\n const created = [];\n\n // \u2500\u2500 Participants: top box + lifeline (and bottom box) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const pIndex = new Map();\n for (let pi = 0; pi < pCount; pi++) {\n const p = participants[pi];\n pIndex.set(p.name, pi);\n const cx = pCenters[pi] + ox;\n const bx = cx - PARTICIPANT_W / 2;\n\n try {\n // Top participant box\n const topBox = new window.Rectangle(bx, TOP_MARGIN + oy, PARTICIPANT_W, PARTICIPANT_H, {\n stroke: TK.participantBorder,\n strokeWidth: 1.5,\n fill: TK.participantBg,\n fillStyle: 'solid',\n roughness: 1,\n label: p.name,\n labelColor: TK.participantText,\n });\n window.shapes.push(topBox);\n if (window.pushCreateAction) window.pushCreateAction(topBox);\n frame.addShapeToFrame(topBox);\n created.push(topBox);\n\n // Lifeline (dashed vertical line spanning the diagram height)\n const lifeline = new window.Line(\n { x: cx, y: topBoxBottom + oy },\n { x: cx, y: bottomBoxTop + oy },\n {\n stroke: TK.lifeline,\n strokeWidth: 1,\n strokeDasharray: '6 4',\n roughness: 0,\n }\n );\n window.shapes.push(lifeline);\n if (window.pushCreateAction) window.pushCreateAction(lifeline);\n frame.addShapeToFrame(lifeline);\n created.push(lifeline);\n\n // Bottom participant box (mirrors top \u2014 Mermaid convention)\n const bottomBox = new window.Rectangle(bx, bottomBoxTop + oy, PARTICIPANT_W, PARTICIPANT_H, {\n stroke: TK.participantBorder,\n strokeWidth: 1.5,\n fill: TK.participantBg,\n fillStyle: 'solid',\n roughness: 1,\n label: p.name,\n labelColor: TK.participantText,\n });\n window.shapes.push(bottomBox);\n if (window.pushCreateAction) window.pushCreateAction(bottomBox);\n frame.addShapeToFrame(bottomBox);\n created.push(bottomBox);\n } catch (err) {\n console.warn('[SequenceRenderer] Participant creation failed:', p.name, err);\n }\n }\n\n // \u2500\u2500 Blocks: editable containers behind message rows \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n for (const block of blocks) {\n const startY = block.startMsg < msgYPositions.length ? msgYPositions[block.startMsg] - 22 : topBoxBottom + 15;\n const endY = block.endMsg < msgYPositions.length ? msgYPositions[block.endMsg] - 8 : currentY;\n try {\n const blockShape = new window.Rectangle(\n startX - 18 + ox,\n startY + oy,\n contentWidth + 36,\n Math.max(36, endY - startY),\n {\n stroke: TK.blockBorder,\n strokeWidth: 1,\n strokeDasharray: '5 3',\n fill: 'transparent',\n fillStyle: 'none',\n roughness: .5,\n label: [block.type, block.label].filter(Boolean).join(': '),\n labelColor: TK.blockLabel,\n labelFontSize: 10,\n }\n );\n window.shapes.push(blockShape);\n if (window.pushCreateAction) window.pushCreateAction(blockShape);\n frame.addShapeToFrame(blockShape);\n created.push(blockShape);\n } catch (err) {\n console.warn('[SequenceRenderer] Block creation failed:', block, err);\n }\n }\n\n // \u2500\u2500 Messages: arrow (or line for --x style) per row \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n for (let mi = 0; mi < messages.length; mi++) {\n const m = messages[mi];\n const fromI = pIndex.get(m.from);\n const toI = pIndex.get(m.to);\n if (fromI == null || toI == null) continue;\n\n const fromCx = pCenters[fromI] + ox;\n const toCx = pCenters[toI] + ox;\n const y = msgYPositions[mi] + oy;\n\n const labelText = m.number ? `${m.number}. ${m.text}` : m.text;\n\n // Solid vs dashed (sync vs async response). `cross` style (-x)\n // would render as a line with an X at the head \u2014 fall back to a\n // line for that, arrow otherwise.\n const isCross = !!m.cross && m.arrowHead === 'cross';\n const opts = {\n stroke: TK.messageLine,\n strokeWidth: 1.5,\n roughness: 0,\n strokeDasharray: m.solid ? '' : '6 4',\n label: labelText || '',\n labelColor: TK.messageText,\n };\n\n try {\n const sp = { x: fromCx, y };\n const ep = { x: toCx, y };\n if (fromI === toI) {\n const loopWidth = 46;\n const loopHeight = 28;\n const a = { x: fromCx, y };\n const b = { x: fromCx + loopWidth, y };\n const c = { x: fromCx + loopWidth, y: y + loopHeight };\n const d = { x: fromCx + 5, y: y + loopHeight };\n const segments = [\n new window.Line(a, b, opts),\n new window.Line(b, c, opts),\n new window.Arrow(c, d, opts),\n ];\n segments.forEach((segment) => {\n window.shapes.push(segment);\n if (window.pushCreateAction) window.pushCreateAction(segment);\n frame.addShapeToFrame(segment);\n created.push(segment);\n });\n continue;\n }\n const connector = isCross\n ? new window.Line(sp, ep, opts)\n : new window.Arrow(sp, ep, opts);\n window.shapes.push(connector);\n if (window.pushCreateAction) window.pushCreateAction(connector);\n frame.addShapeToFrame(connector);\n created.push(connector);\n } catch (err) {\n console.warn('[SequenceRenderer] Message creation failed:', m, err);\n }\n }\n\n\n // \u2500\u2500 Notes: one independently editable rectangle per note \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n for (const [messageIndex, noteGroup] of noteAtMsg.entries()) {\n const baseY = messageIndex < msgYPositions.length\n ? msgYPositions[messageIndex] - 15\n : (messageIndex > 0 ? msgYPositions[messageIndex - 1] + MSG_ROW_HEIGHT - 15 : topBoxBottom + 30);\n noteGroup.forEach((note, noteIndex) => {\n const indexes = note.targets.map(target => pIndex.get(target)).filter(index => index !== undefined);\n if (!indexes.length) return;\n const noteWidth = NOTE_MAX_W;\n let centerX = pCenters[indexes[0]];\n if (note.position === 'over' && indexes.length > 1) {\n centerX = (pCenters[Math.min(...indexes)] + pCenters[Math.max(...indexes)]) / 2;\n } else if (note.position === 'left of') {\n centerX -= PARTICIPANT_W / 2 + noteWidth / 2 + 8;\n } else if (note.position === 'right of') {\n centerX += PARTICIPANT_W / 2 + noteWidth / 2 + 8;\n }\n try {\n const noteShape = new window.Rectangle(\n centerX - noteWidth / 2 + ox,\n baseY - note.height - noteIndex * (note.height + 6) + oy,\n noteWidth,\n note.height,\n {\n stroke: TK.noteBorder,\n strokeWidth: 1,\n fill: TK.noteBg,\n fillStyle: 'solid',\n roughness: .7,\n label: note.text,\n labelColor: TK.noteText,\n labelFontSize: 11,\n }\n );\n window.shapes.push(noteShape);\n if (window.pushCreateAction) window.pushCreateAction(noteShape);\n frame.addShapeToFrame(noteShape);\n created.push(noteShape);\n } catch (err) {\n console.warn('[SequenceRenderer] Note creation failed:', note, err);\n }\n });\n }\n\n // Auto-select the first node so the user has feedback that the\n // diagram landed. Selecting a child (not the frame) reinforces the\n // \"independent shapes inside a frame\" model from issue #34 bug #3.\n const first = created[0];\n if (first) {\n window.currentShape = first;\n if (typeof first.selectShape === 'function') first.selectShape();\n }\n\n console.log(`[SequenceRenderer] Done: ${pCount} participants, ${messages.length} messages`);\n return true;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/core/MermaidStructuredRenderer.js
|
|
4
|
+
var NS = "http://www.w3.org/2000/svg";
|
|
5
|
+
var PALETTE = [
|
|
6
|
+
{ fill: "#dfeee4", stroke: "#5f836c" },
|
|
7
|
+
{ fill: "#f4e3d4", stroke: "#a97852" },
|
|
8
|
+
{ fill: "#e9e1ef", stroke: "#7e6b91" },
|
|
9
|
+
{ fill: "#f3edc9", stroke: "#9a8745" },
|
|
10
|
+
{ fill: "#dcebed", stroke: "#5d7f82" },
|
|
11
|
+
{ fill: "#f1dedc", stroke: "#9a6863" }
|
|
12
|
+
];
|
|
13
|
+
function escapeXml(value) {
|
|
14
|
+
return String(value ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
15
|
+
}
|
|
16
|
+
function isDark() {
|
|
17
|
+
return typeof document !== "undefined" && document.body?.classList.contains("theme-dark");
|
|
18
|
+
}
|
|
19
|
+
function theme() {
|
|
20
|
+
return isDark() ? { bg: "#20232a", text: "#f0eee8", line: "#aaa89f", frame: "#77766f", panel: "#292d34" } : { bg: "#fbfaf6", text: "#343832", line: "#6e746c", frame: "#b9b7ac", panel: "#f5f2ea" };
|
|
21
|
+
}
|
|
22
|
+
function sourceLines(src) {
|
|
23
|
+
return src.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("%%"));
|
|
24
|
+
}
|
|
25
|
+
function detectStructuredMermaid(src) {
|
|
26
|
+
const header = sourceLines(src)[0]?.toLowerCase() || "";
|
|
27
|
+
if (header === "erdiagram") return "er";
|
|
28
|
+
if (/^pie(?:\s|$)/.test(header) || /^xychart(?:-beta)?(?:\s|$)/.test(header)) return "chart";
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function parseERDiagram(src) {
|
|
32
|
+
const lines = sourceLines(src);
|
|
33
|
+
if (lines[0]?.toLowerCase() !== "erdiagram") return null;
|
|
34
|
+
const entityMap = /* @__PURE__ */ new Map();
|
|
35
|
+
const relationships = [];
|
|
36
|
+
let current = null;
|
|
37
|
+
const ensureEntity = (name) => {
|
|
38
|
+
if (!entityMap.has(name)) entityMap.set(name, { name, attributes: [] });
|
|
39
|
+
return entityMap.get(name);
|
|
40
|
+
};
|
|
41
|
+
for (let index = 1; index < lines.length; index++) {
|
|
42
|
+
const line = lines[index];
|
|
43
|
+
const entityStart = line.match(/^([\w-]+)\s*\{$/);
|
|
44
|
+
if (entityStart) {
|
|
45
|
+
current = ensureEntity(entityStart[1]);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (line === "}") {
|
|
49
|
+
current = null;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (current) {
|
|
53
|
+
const attribute = line.match(/^(\S+)\s+(\S+)(?:\s+(.+))?$/);
|
|
54
|
+
if (attribute) {
|
|
55
|
+
current.attributes.push({
|
|
56
|
+
type: attribute[1],
|
|
57
|
+
name: attribute[2],
|
|
58
|
+
key: attribute[3] || ""
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const relation = line.match(/^([\w-]+)\s+([|o}{.]+)(?:--|\.\.)([|o}{.]+)\s+([\w-]+)\s*:\s*(.+)$/);
|
|
64
|
+
if (relation) {
|
|
65
|
+
ensureEntity(relation[1]);
|
|
66
|
+
ensureEntity(relation[4]);
|
|
67
|
+
relationships.push({
|
|
68
|
+
from: relation[1],
|
|
69
|
+
fromCardinality: relation[2],
|
|
70
|
+
toCardinality: relation[3],
|
|
71
|
+
to: relation[4],
|
|
72
|
+
label: relation[5]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (entityMap.size === 0) return null;
|
|
77
|
+
return { type: "erDiagram", title: "Entity relationship diagram", entities: [...entityMap.values()], relationships };
|
|
78
|
+
}
|
|
79
|
+
function layoutER(diagram) {
|
|
80
|
+
const width = 230;
|
|
81
|
+
const headerHeight = 44;
|
|
82
|
+
const rowHeight = 30;
|
|
83
|
+
const columns = Math.max(1, Math.ceil(Math.sqrt(diagram.entities.length)));
|
|
84
|
+
return diagram.entities.map((entity, index) => ({
|
|
85
|
+
...entity,
|
|
86
|
+
x: 60 + index % columns * 330,
|
|
87
|
+
y: 55 + Math.floor(index / columns) * 270,
|
|
88
|
+
width,
|
|
89
|
+
headerHeight,
|
|
90
|
+
rowHeight,
|
|
91
|
+
height: headerHeight + Math.max(1, entity.attributes.length) * rowHeight,
|
|
92
|
+
color: PALETTE[index % PALETTE.length]
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
function renderERPreviewSVG(diagram) {
|
|
96
|
+
if (!diagram?.entities?.length) return "";
|
|
97
|
+
const TK = theme();
|
|
98
|
+
const entities = layoutER(diagram);
|
|
99
|
+
const byName = new Map(entities.map((entity) => [entity.name, entity]));
|
|
100
|
+
const maxX = Math.max(...entities.map((entity) => entity.x + entity.width)) + 60;
|
|
101
|
+
const maxY = Math.max(...entities.map((entity) => entity.y + entity.height)) + 55;
|
|
102
|
+
let content = `<rect width="${maxX}" height="${maxY}" rx="10" fill="${TK.bg}"/>`;
|
|
103
|
+
content += `<defs><marker id="er-arrow" markerWidth="9" markerHeight="7" refX="8" refY="3.5" orient="auto"><path d="M1 1 L8 3.5 L1 6" fill="none" stroke="${TK.line}" stroke-width="1.4"/></marker></defs>`;
|
|
104
|
+
for (const relation of diagram.relationships) {
|
|
105
|
+
const from = byName.get(relation.from);
|
|
106
|
+
const to = byName.get(relation.to);
|
|
107
|
+
if (!from || !to) continue;
|
|
108
|
+
const x1 = from.x + from.width / 2;
|
|
109
|
+
const y1 = from.y + from.height / 2;
|
|
110
|
+
const x2 = to.x + to.width / 2;
|
|
111
|
+
const y2 = to.y + to.height / 2;
|
|
112
|
+
const mx = (x1 + x2) / 2;
|
|
113
|
+
const my = (y1 + y2) / 2;
|
|
114
|
+
content += `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${TK.line}" stroke-width="1.6" marker-end="url(#er-arrow)"/>`;
|
|
115
|
+
content += `<text x="${mx}" y="${my - 8}" text-anchor="middle" fill="${TK.text}" font-size="12" font-family="lixFont">${escapeXml(`${relation.fromCardinality} ${relation.label} ${relation.toCardinality}`)}</text>`;
|
|
116
|
+
}
|
|
117
|
+
for (const entity of entities) {
|
|
118
|
+
content += `<g><rect x="${entity.x}" y="${entity.y}" width="${entity.width}" height="${entity.height}" rx="8" fill="${TK.panel}" stroke="${entity.color.stroke}" stroke-width="1.5"/>`;
|
|
119
|
+
content += `<rect x="${entity.x}" y="${entity.y}" width="${entity.width}" height="${entity.headerHeight}" rx="8" fill="${entity.color.fill}" stroke="${entity.color.stroke}" stroke-width="1.5"/>`;
|
|
120
|
+
content += `<text x="${entity.x + entity.width / 2}" y="${entity.y + 27}" text-anchor="middle" fill="#343832" font-size="14" font-weight="600" font-family="lixFont">${escapeXml(entity.name)}</text>`;
|
|
121
|
+
const attributes = entity.attributes.length ? entity.attributes : [{ type: "", name: "No attributes", key: "" }];
|
|
122
|
+
attributes.forEach((attribute, row) => {
|
|
123
|
+
const y = entity.y + entity.headerHeight + row * entity.rowHeight;
|
|
124
|
+
content += `<line x1="${entity.x}" y1="${y}" x2="${entity.x + entity.width}" y2="${y}" stroke="${TK.frame}" stroke-width="0.8"/>`;
|
|
125
|
+
content += `<text x="${entity.x + 10}" y="${y + 20}" fill="${TK.text}" font-size="11" font-family="lixCode">${escapeXml([attribute.type, attribute.name, attribute.key].filter(Boolean).join(" "))}</text>`;
|
|
126
|
+
});
|
|
127
|
+
content += "</g>";
|
|
128
|
+
}
|
|
129
|
+
return `<svg xmlns="${NS}" width="600" height="450" viewBox="0 0 ${maxX} ${maxY}">${content}</svg>`;
|
|
130
|
+
}
|
|
131
|
+
function numberList(value) {
|
|
132
|
+
return value.replace(/^\[|\]$/g, "").split(",").map((item) => Number(item.trim())).filter(Number.isFinite);
|
|
133
|
+
}
|
|
134
|
+
function parseChartDiagram(src) {
|
|
135
|
+
const lines = sourceLines(src);
|
|
136
|
+
const header = lines[0]?.toLowerCase() || "";
|
|
137
|
+
if (/^pie(?:\s|$)/.test(header)) {
|
|
138
|
+
let title2 = lines[0].replace(/^pie(?:\s+showData)?\s*/i, "").replace(/^title\s+/i, "").trim() || "Pie chart";
|
|
139
|
+
const values = [];
|
|
140
|
+
for (let index = 1; index < lines.length; index++) {
|
|
141
|
+
const titleMatch = lines[index].match(/^title\s+(.+)$/i);
|
|
142
|
+
if (titleMatch) {
|
|
143
|
+
title2 = titleMatch[1];
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const item = lines[index].match(/^"?(.+?)"?\s*:\s*(-?\d+(?:\.\d+)?)$/);
|
|
147
|
+
if (item) values.push({ label: item[1], value: Number(item[2]) });
|
|
148
|
+
}
|
|
149
|
+
return values.length ? { type: "chart", kind: "pie", title: title2, categories: values.map((v) => v.label), series: [{ name: title2, values: values.map((v) => v.value) }] } : null;
|
|
150
|
+
}
|
|
151
|
+
if (!/^xychart(?:-beta)?(?:\s|$)/.test(header)) return null;
|
|
152
|
+
let title = "Chart";
|
|
153
|
+
let categories = [];
|
|
154
|
+
const series = [];
|
|
155
|
+
for (let index = 1; index < lines.length; index++) {
|
|
156
|
+
const titleMatch = lines[index].match(/^title\s+"?(.+?)"?$/i);
|
|
157
|
+
const axisMatch = lines[index].match(/^x-axis(?:\s+".*?")?\s+(\[.+\])/i);
|
|
158
|
+
const seriesMatch = lines[index].match(/^(bar|line)(?:\s+"?([^"\[]+)"?)?\s+(\[.+\])$/i);
|
|
159
|
+
if (titleMatch) title = titleMatch[1];
|
|
160
|
+
else if (axisMatch) categories = axisMatch[1].replace(/^\[|\]$/g, "").split(",").map((v) => v.trim().replace(/^"|"$/g, ""));
|
|
161
|
+
else if (seriesMatch) series.push({ kind: seriesMatch[1].toLowerCase(), name: seriesMatch[2]?.trim() || seriesMatch[1], values: numberList(seriesMatch[3]) });
|
|
162
|
+
}
|
|
163
|
+
const maxItems = Math.max(0, ...series.map((item) => item.values.length));
|
|
164
|
+
if (!categories.length) categories = Array.from({ length: maxItems }, (_, index) => String(index + 1));
|
|
165
|
+
return series.length ? { type: "chart", kind: "xy", title, categories, series } : null;
|
|
166
|
+
}
|
|
167
|
+
function renderChartPreviewSVG(chart) {
|
|
168
|
+
if (!chart?.series?.length) return "";
|
|
169
|
+
const TK = theme();
|
|
170
|
+
const width = 720;
|
|
171
|
+
const height = 450;
|
|
172
|
+
const left = 70;
|
|
173
|
+
const top = 60;
|
|
174
|
+
const plotWidth = 580;
|
|
175
|
+
const plotHeight = 300;
|
|
176
|
+
const values = chart.series.flatMap((series) => series.values);
|
|
177
|
+
const maximum = Math.max(1, ...values.map(Math.abs));
|
|
178
|
+
const categoryCount = Math.max(1, chart.categories.length);
|
|
179
|
+
const slot = plotWidth / categoryCount;
|
|
180
|
+
let content = `<rect width="${width}" height="${height}" rx="10" fill="${TK.bg}"/><text x="${width / 2}" y="32" text-anchor="middle" fill="${TK.text}" font-size="18" font-family="lixFont">${escapeXml(chart.title)}</text>`;
|
|
181
|
+
content += `<line x1="${left}" y1="${top}" x2="${left}" y2="${top + plotHeight}" stroke="${TK.line}"/><line x1="${left}" y1="${top + plotHeight}" x2="${left + plotWidth}" y2="${top + plotHeight}" stroke="${TK.line}"/>`;
|
|
182
|
+
chart.categories.forEach((category, index) => {
|
|
183
|
+
content += `<text x="${left + slot * (index + 0.5)}" y="${top + plotHeight + 24}" text-anchor="middle" fill="${TK.text}" font-size="11" font-family="lixFont">${escapeXml(category)}</text>`;
|
|
184
|
+
});
|
|
185
|
+
chart.series.forEach((series, seriesIndex) => {
|
|
186
|
+
const color = PALETTE[seriesIndex % PALETTE.length];
|
|
187
|
+
if (series.kind === "line") {
|
|
188
|
+
const points = series.values.map((value, index) => `${left + slot * (index + 0.5)},${top + plotHeight - Math.abs(value) / maximum * plotHeight}`).join(" ");
|
|
189
|
+
content += `<polyline points="${points}" fill="none" stroke="${color.stroke}" stroke-width="3"/>`;
|
|
190
|
+
series.values.forEach((value, index) => content += `<circle cx="${left + slot * (index + 0.5)}" cy="${top + plotHeight - Math.abs(value) / maximum * plotHeight}" r="5" fill="${color.fill}" stroke="${color.stroke}" stroke-width="2"/>`);
|
|
191
|
+
} else {
|
|
192
|
+
const barWidth = Math.max(12, slot * 0.7 / chart.series.length);
|
|
193
|
+
series.values.forEach((value, index) => {
|
|
194
|
+
const barHeight = Math.abs(value) / maximum * plotHeight;
|
|
195
|
+
const x = left + slot * index + slot * 0.15 + seriesIndex * barWidth;
|
|
196
|
+
content += `<rect x="${x}" y="${top + plotHeight - barHeight}" width="${barWidth}" height="${barHeight}" rx="4" fill="${color.fill}" stroke="${color.stroke}" stroke-width="1.5"/><text x="${x + barWidth / 2}" y="${top + plotHeight - barHeight - 7}" text-anchor="middle" fill="${TK.text}" font-size="10" font-family="lixFont">${value}</text>`;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
return `<svg xmlns="${NS}" width="600" height="450" viewBox="0 0 ${width} ${height}">${content}</svg>`;
|
|
201
|
+
}
|
|
202
|
+
function push(shape, frame) {
|
|
203
|
+
if (!shape) return null;
|
|
204
|
+
window.shapes.push(shape);
|
|
205
|
+
if (window.pushCreateAction) window.pushCreateAction(shape);
|
|
206
|
+
if (frame?.addShapeToFrame) frame.addShapeToFrame(shape);
|
|
207
|
+
return shape;
|
|
208
|
+
}
|
|
209
|
+
function canvasOrigin(width, height) {
|
|
210
|
+
const vb = window.currentViewBox || { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight };
|
|
211
|
+
return { x: vb.x + vb.width / 2 - width / 2, y: vb.y + vb.height / 2 - height / 2 };
|
|
212
|
+
}
|
|
213
|
+
function createFrame(x, y, width, height, name, type) {
|
|
214
|
+
const TK = theme();
|
|
215
|
+
const frame = new window.Frame(x - 45, y - 45, width + 90, height + 90, {
|
|
216
|
+
stroke: TK.frame,
|
|
217
|
+
strokeWidth: 1,
|
|
218
|
+
fill: "transparent",
|
|
219
|
+
opacity: 0.75,
|
|
220
|
+
frameName: name
|
|
221
|
+
});
|
|
222
|
+
frame._diagramType = type;
|
|
223
|
+
push(frame);
|
|
224
|
+
return frame;
|
|
225
|
+
}
|
|
226
|
+
function renderEROnCanvas(diagram) {
|
|
227
|
+
if (!diagram?.entities?.length || !window.Rectangle || !window.Arrow || !window.Frame) return false;
|
|
228
|
+
const TK = theme();
|
|
229
|
+
const entities = layoutER(diagram);
|
|
230
|
+
const width = Math.max(...entities.map((entity) => entity.x + entity.width)) + 40;
|
|
231
|
+
const height = Math.max(...entities.map((entity) => entity.y + entity.height)) + 40;
|
|
232
|
+
const origin = canvasOrigin(width, height);
|
|
233
|
+
const frame = createFrame(origin.x, origin.y, width, height, diagram.title, "mermaid-er");
|
|
234
|
+
const entityShapes = /* @__PURE__ */ new Map();
|
|
235
|
+
let first = null;
|
|
236
|
+
for (const entity of entities) {
|
|
237
|
+
const x = origin.x + entity.x;
|
|
238
|
+
const y = origin.y + entity.y;
|
|
239
|
+
const header = push(new window.Rectangle(x, y, entity.width, entity.headerHeight, {
|
|
240
|
+
stroke: entity.color.stroke,
|
|
241
|
+
strokeWidth: 1.5,
|
|
242
|
+
fill: entity.color.fill,
|
|
243
|
+
fillStyle: "solid",
|
|
244
|
+
roughness: 1,
|
|
245
|
+
label: entity.name,
|
|
246
|
+
labelColor: "#343832",
|
|
247
|
+
labelFontSize: 15
|
|
248
|
+
}), frame);
|
|
249
|
+
if (!first) first = header;
|
|
250
|
+
entityShapes.set(entity.name, { shape: header, x, y, width: entity.width, height: entity.headerHeight });
|
|
251
|
+
const attributes = entity.attributes.length ? entity.attributes : [{ type: "", name: "No attributes", key: "" }];
|
|
252
|
+
attributes.forEach((attribute, row) => push(new window.Rectangle(
|
|
253
|
+
x,
|
|
254
|
+
y + entity.headerHeight + row * entity.rowHeight,
|
|
255
|
+
entity.width,
|
|
256
|
+
entity.rowHeight,
|
|
257
|
+
{
|
|
258
|
+
stroke: TK.frame,
|
|
259
|
+
strokeWidth: 1,
|
|
260
|
+
fill: TK.panel,
|
|
261
|
+
fillStyle: "solid",
|
|
262
|
+
roughness: 0.4,
|
|
263
|
+
label: [attribute.type, attribute.name, attribute.key].filter(Boolean).join(" "),
|
|
264
|
+
labelColor: TK.text,
|
|
265
|
+
labelFontSize: 11
|
|
266
|
+
}
|
|
267
|
+
), frame));
|
|
268
|
+
}
|
|
269
|
+
for (const relation of diagram.relationships) {
|
|
270
|
+
const from = entityShapes.get(relation.from);
|
|
271
|
+
const to = entityShapes.get(relation.to);
|
|
272
|
+
if (!from || !to) continue;
|
|
273
|
+
const start = { x: from.x + from.width / 2, y: from.y + from.height / 2 };
|
|
274
|
+
const end = { x: to.x + to.width / 2, y: to.y + to.height / 2 };
|
|
275
|
+
const arrow = push(new window.Arrow(start, end, {
|
|
276
|
+
stroke: TK.line,
|
|
277
|
+
strokeWidth: 1.5,
|
|
278
|
+
roughness: 1,
|
|
279
|
+
label: `${relation.fromCardinality} ${relation.label} ${relation.toCardinality}`,
|
|
280
|
+
labelColor: TK.text
|
|
281
|
+
}), frame);
|
|
282
|
+
if (arrow && window.__autoAttach) {
|
|
283
|
+
window.__autoAttach(arrow, from.shape, true, start);
|
|
284
|
+
window.__autoAttach(arrow, to.shape, false, end);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (first?.selectShape) {
|
|
288
|
+
window.currentShape = first;
|
|
289
|
+
first.selectShape();
|
|
290
|
+
}
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
function renderChartOnCanvas(chart) {
|
|
294
|
+
if (!chart?.series?.length || !window.Rectangle || !window.Circle || !window.Line || !window.Frame) return false;
|
|
295
|
+
const TK = theme();
|
|
296
|
+
const width = 720;
|
|
297
|
+
const height = 450;
|
|
298
|
+
const origin = canvasOrigin(width, height);
|
|
299
|
+
const frame = createFrame(origin.x, origin.y, width, height, chart.title, "mermaid-chart");
|
|
300
|
+
const left = origin.x + 70;
|
|
301
|
+
const top = origin.y + 65;
|
|
302
|
+
const plotWidth = 580;
|
|
303
|
+
const plotHeight = 300;
|
|
304
|
+
const values = chart.series.flatMap((series) => series.values);
|
|
305
|
+
const maximum = Math.max(1, ...values.map(Math.abs));
|
|
306
|
+
const slot = plotWidth / Math.max(1, chart.categories.length);
|
|
307
|
+
let first = null;
|
|
308
|
+
push(new window.Line({ x: left, y: top }, { x: left, y: top + plotHeight }, { stroke: TK.line, strokeWidth: 1.5, roughness: 0 }), frame);
|
|
309
|
+
push(new window.Line({ x: left, y: top + plotHeight }, { x: left + plotWidth, y: top + plotHeight }, { stroke: TK.line, strokeWidth: 1.5, roughness: 0 }), frame);
|
|
310
|
+
chart.series.forEach((series, seriesIndex) => {
|
|
311
|
+
const color = PALETTE[seriesIndex % PALETTE.length];
|
|
312
|
+
if (series.kind === "line") {
|
|
313
|
+
let previous = null;
|
|
314
|
+
series.values.forEach((value, index) => {
|
|
315
|
+
const point = { x: left + slot * (index + 0.5), y: top + plotHeight - Math.abs(value) / maximum * plotHeight };
|
|
316
|
+
const dot = push(new window.Circle(point.x, point.y, 8, 8, {
|
|
317
|
+
stroke: color.stroke,
|
|
318
|
+
strokeWidth: 2,
|
|
319
|
+
fill: color.fill,
|
|
320
|
+
fillStyle: "solid",
|
|
321
|
+
roughness: 0.5,
|
|
322
|
+
label: String(value),
|
|
323
|
+
labelColor: TK.text,
|
|
324
|
+
labelFontSize: 9
|
|
325
|
+
}), frame);
|
|
326
|
+
if (!first) first = dot;
|
|
327
|
+
if (previous) push(new window.Line(previous, point, { stroke: color.stroke, strokeWidth: 3, roughness: 0.5 }), frame);
|
|
328
|
+
previous = point;
|
|
329
|
+
});
|
|
330
|
+
} else {
|
|
331
|
+
const barWidth = Math.max(18, slot * 0.7 / chart.series.length);
|
|
332
|
+
series.values.forEach((value, index) => {
|
|
333
|
+
const barHeight = Math.max(8, Math.abs(value) / maximum * plotHeight);
|
|
334
|
+
const x = left + slot * index + slot * 0.15 + seriesIndex * barWidth;
|
|
335
|
+
const bar = push(new window.Rectangle(x, top + plotHeight - barHeight, barWidth, barHeight, {
|
|
336
|
+
stroke: color.stroke,
|
|
337
|
+
strokeWidth: 1.5,
|
|
338
|
+
fill: color.fill,
|
|
339
|
+
fillStyle: "solid",
|
|
340
|
+
roughness: 0.7,
|
|
341
|
+
label: `${chart.categories[index] || index + 1}
|
|
342
|
+
${value}`,
|
|
343
|
+
labelColor: "#343832",
|
|
344
|
+
labelFontSize: 11
|
|
345
|
+
}), frame);
|
|
346
|
+
if (!first) first = bar;
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
if (first?.selectShape) {
|
|
351
|
+
window.currentShape = first;
|
|
352
|
+
first.selectShape();
|
|
353
|
+
}
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
export {
|
|
357
|
+
detectStructuredMermaid,
|
|
358
|
+
parseChartDiagram,
|
|
359
|
+
parseERDiagram,
|
|
360
|
+
renderChartOnCanvas,
|
|
361
|
+
renderChartPreviewSVG,
|
|
362
|
+
renderEROnCanvas,
|
|
363
|
+
renderERPreviewSVG
|
|
364
|
+
};
|
|
365
|
+
//# sourceMappingURL=MermaidStructuredRenderer-GGOOGON5.js.map
|