@elixpo/lixsketch 5.4.7 → 5.5.1
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-M4LMJ66J.js +1105 -0
- package/dist/react/AIRenderer-M4LMJ66J.js.map +7 -0
- package/dist/react/Arrow-BJDAXPMH.js +8 -0
- package/dist/react/Arrow-BJDAXPMH.js.map +7 -0
- package/dist/react/Circle-KPG7TKIX.js +8 -0
- package/dist/react/Circle-KPG7TKIX.js.map +7 -0
- package/dist/react/CodeShape-K4DIZAIV.js +8 -0
- package/dist/react/CodeShape-K4DIZAIV.js.map +7 -0
- package/dist/react/CopyPaste-4CKJRHJ3.js +569 -0
- package/dist/react/CopyPaste-4CKJRHJ3.js.map +7 -0
- package/dist/react/EraserTrail-PA6IQLQN.js +20 -0
- package/dist/react/EraserTrail-PA6IQLQN.js.map +7 -0
- package/dist/react/EventDispatcher-SRFH6LQZ.js +463 -0
- package/dist/react/EventDispatcher-SRFH6LQZ.js.map +7 -0
- package/dist/react/Frame-6UR5XPAX.js +10 -0
- package/dist/react/Frame-6UR5XPAX.js.map +7 -0
- package/dist/react/FreehandStroke-CK24NUT3.js +8 -0
- package/dist/react/FreehandStroke-CK24NUT3.js.map +7 -0
- package/dist/react/GraphEngine-QE5B2PMR.js +340 -0
- package/dist/react/GraphEngine-QE5B2PMR.js.map +7 -0
- package/dist/react/GraphMathParser-2WMTY4IE.js +10 -0
- package/dist/react/GraphMathParser-2WMTY4IE.js.map +7 -0
- package/dist/react/IconShape-KMIA6T2H.js +10 -0
- package/dist/react/IconShape-KMIA6T2H.js.map +7 -0
- package/dist/react/ImageShape-CHHGBXUQ.js +10 -0
- package/dist/react/ImageShape-CHHGBXUQ.js.map +7 -0
- package/dist/react/LayerOrder-XCHF4MSS.js +66 -0
- package/dist/react/LayerOrder-XCHF4MSS.js.map +7 -0
- package/dist/react/Line-2KOQEEGA.js +10 -0
- package/dist/react/Line-2KOQEEGA.js.map +7 -0
- package/dist/react/LixScriptParser-JYWJX375.js +1033 -0
- package/dist/react/LixScriptParser-JYWJX375.js.map +7 -0
- package/dist/react/MermaidFlowchartRenderer-4QBAZ5MB.js +412 -0
- package/dist/react/MermaidFlowchartRenderer-4QBAZ5MB.js.map +7 -0
- package/dist/react/MermaidSequenceParser-UKG2NSZY.js +8 -0
- package/dist/react/MermaidSequenceParser-UKG2NSZY.js.map +7 -0
- package/dist/react/MermaidSequenceRenderer-HPLHY5II.js +387 -0
- package/dist/react/MermaidSequenceRenderer-HPLHY5II.js.map +7 -0
- package/dist/react/Rectangle-6FE7RN6W.js +8 -0
- package/dist/react/Rectangle-6FE7RN6W.js.map +7 -0
- package/dist/react/ResizeCode-WAMDTKSW.js +2 -0
- package/dist/react/ResizeCode-WAMDTKSW.js.map +7 -0
- package/dist/react/ResizeShapes-LKEQTITP.js +250 -0
- package/dist/react/ResizeShapes-LKEQTITP.js.map +7 -0
- package/dist/react/SceneSerializer-4U4B24JM.js +739 -0
- package/dist/react/SceneSerializer-4U4B24JM.js.map +7 -0
- package/dist/react/Selection-VVUHY5YJ.js +34 -0
- package/dist/react/Selection-VVUHY5YJ.js.map +7 -0
- package/dist/react/SketchEngine-YA4RG3KX.js +413 -0
- package/dist/react/SketchEngine-YA4RG3KX.js.map +7 -0
- package/dist/react/TextShape-54ZJ66E3.js +8 -0
- package/dist/react/TextShape-54ZJ66E3.js.map +7 -0
- package/dist/react/UndoRedo-M2JZUBKN.js +30 -0
- package/dist/react/UndoRedo-M2JZUBKN.js.map +7 -0
- package/dist/react/ZoomPan-U3TXC5AJ.js +186 -0
- package/dist/react/ZoomPan-U3TXC5AJ.js.map +7 -0
- package/dist/react/arrowTool-CWEMIRXK.js +17 -0
- package/dist/react/arrowTool-CWEMIRXK.js.map +7 -0
- package/dist/react/boxicons-5JNJQPUP.woff +0 -0
- package/dist/react/boxicons-6NVWQSCQ.eot +0 -0
- package/dist/react/boxicons-AQ5N6WP7.woff2 +0 -0
- package/dist/react/boxicons-GDR4JU3X.ttf +0 -0
- package/dist/react/boxicons-NNFHLMOR.svg +1660 -0
- package/dist/react/chunk-2OQXI7QB.js +564 -0
- package/dist/react/chunk-2OQXI7QB.js.map +7 -0
- package/dist/react/chunk-5OUBTHHO.js +187 -0
- package/dist/react/chunk-5OUBTHHO.js.map +7 -0
- package/dist/react/chunk-6PFXZKL2.js +628 -0
- package/dist/react/chunk-6PFXZKL2.js.map +7 -0
- package/dist/react/chunk-6SFNWGGZ.js +453 -0
- package/dist/react/chunk-6SFNWGGZ.js.map +7 -0
- package/dist/react/chunk-7TZSIR6V.js +210 -0
- package/dist/react/chunk-7TZSIR6V.js.map +7 -0
- package/dist/react/chunk-AJAZC37E.js +662 -0
- package/dist/react/chunk-AJAZC37E.js.map +7 -0
- package/dist/react/chunk-BU4D2WYQ.js +1213 -0
- package/dist/react/chunk-BU4D2WYQ.js.map +7 -0
- package/dist/react/chunk-BZE4CEBG.js +747 -0
- package/dist/react/chunk-BZE4CEBG.js.map +7 -0
- package/dist/react/chunk-CB2RANCB.js +212 -0
- package/dist/react/chunk-CB2RANCB.js.map +7 -0
- package/dist/react/chunk-DNAG7ML6.js +128 -0
- package/dist/react/chunk-DNAG7ML6.js.map +7 -0
- package/dist/react/chunk-HZWLPXFN.js +198 -0
- package/dist/react/chunk-HZWLPXFN.js.map +7 -0
- package/dist/react/chunk-I6HCCIXB.js +572 -0
- package/dist/react/chunk-I6HCCIXB.js.map +7 -0
- package/dist/react/chunk-IZ5OGWGW.js +235 -0
- package/dist/react/chunk-IZ5OGWGW.js.map +7 -0
- package/dist/react/chunk-JJCJNLNN.js +1490 -0
- package/dist/react/chunk-JJCJNLNN.js.map +7 -0
- package/dist/react/chunk-JQ3IRFDP.js +222 -0
- package/dist/react/chunk-JQ3IRFDP.js.map +7 -0
- package/dist/react/chunk-L5OARZJ7.js +1134 -0
- package/dist/react/chunk-L5OARZJ7.js.map +7 -0
- package/dist/react/chunk-LHRTXNVE.js +1600 -0
- package/dist/react/chunk-LHRTXNVE.js.map +7 -0
- package/dist/react/chunk-Q2GA4MBZ.js +1653 -0
- package/dist/react/chunk-Q2GA4MBZ.js.map +7 -0
- package/dist/react/chunk-QNSE5BAY.js +519 -0
- package/dist/react/chunk-QNSE5BAY.js.map +7 -0
- package/dist/react/chunk-QXFBG7OM.js +191 -0
- package/dist/react/chunk-QXFBG7OM.js.map +7 -0
- package/dist/react/chunk-T6ZVV5ZO.js +714 -0
- package/dist/react/chunk-T6ZVV5ZO.js.map +7 -0
- package/dist/react/chunk-URB6MY46.js +974 -0
- package/dist/react/chunk-URB6MY46.js.map +7 -0
- package/dist/react/chunk-VN62KLOC.js +931 -0
- package/dist/react/chunk-VN62KLOC.js.map +7 -0
- package/dist/react/chunk-VQVEPJSA.js +158 -0
- package/dist/react/chunk-VQVEPJSA.js.map +7 -0
- package/dist/react/chunk-XISEAGBV.js +1632 -0
- package/dist/react/chunk-XISEAGBV.js.map +7 -0
- package/dist/react/chunk-YKJUBFHE.js +575 -0
- package/dist/react/chunk-YKJUBFHE.js.map +7 -0
- package/dist/react/circleTool-NUATSEI2.js +14 -0
- package/dist/react/circleTool-NUATSEI2.js.map +7 -0
- package/dist/react/codeTool-MHCB2FNB.js +38 -0
- package/dist/react/codeTool-MHCB2FNB.js.map +7 -0
- package/dist/react/eraserTool-2GS2LQCS.js +103 -0
- package/dist/react/eraserTool-2GS2LQCS.js.map +7 -0
- package/dist/react/frameTool-PWVKKXSP.js +14 -0
- package/dist/react/frameTool-PWVKKXSP.js.map +7 -0
- package/dist/react/freehandTool-SLYGASVP.js +14 -0
- package/dist/react/freehandTool-SLYGASVP.js.map +7 -0
- package/dist/react/iconTool-GPZ7O7NH.js +18 -0
- package/dist/react/iconTool-GPZ7O7NH.js.map +7 -0
- package/dist/react/imageTool-D5R4XGEU.js +15 -0
- package/dist/react/imageTool-D5R4XGEU.js.map +7 -0
- package/dist/react/index.js +5349 -0
- package/dist/react/index.js.map +7 -0
- package/dist/react/laserTool-XVMV63AV.js +239 -0
- package/dist/react/laserTool-XVMV63AV.js.map +7 -0
- package/dist/react/lineTool-WNI5EMZ5.js +14 -0
- package/dist/react/lineTool-WNI5EMZ5.js.map +7 -0
- package/dist/react/lixCode-OJMVUXXI.ttf +0 -0
- package/dist/react/lixDefault-JEAPAUJL.ttf +0 -0
- package/dist/react/lixFancy-BPXYM5CQ.ttf +0 -0
- package/dist/react/lixFont-CPKEUDVM.woff2 +0 -0
- package/dist/react/rectangleTool-U7DZ6ZTA.js +14 -0
- package/dist/react/rectangleTool-U7DZ6ZTA.js.map +7 -0
- package/dist/react/styles.css +6627 -0
- package/dist/react/textTool-M7S6F3VC.js +21 -0
- package/dist/react/textTool-M7S6F3VC.js.map +7 -0
- package/package.json +39 -5
- package/src/EngineShortcuts.js +83 -27
- package/src/core/AIRenderer.js +30 -18
- package/src/core/GraphEngine.js +64 -41
- package/src/core/MermaidFlowchartRenderer.js +153 -88
- package/src/core/MermaidSequenceRenderer.js +168 -93
- package/src/core/SceneSerializer.js +86 -2
- package/src/core/Selection.js +119 -19
- package/src/core/UndoRedo.js +82 -15
- package/src/core/ZoomPan.js +15 -5
- package/src/index.js +14 -1
- package/src/react/LixSketchCanvas.jsx +347 -0
- package/src/react/components/AppMenu.jsx +482 -0
- package/src/react/components/Footer.jsx +85 -0
- package/src/react/components/Toolbar.jsx +140 -0
- package/src/react/components/canvas/CanvasLoadingOverlay.jsx +76 -0
- package/src/react/components/canvas/ContextMenu.jsx +466 -0
- package/src/react/components/canvas/FindBar.jsx +177 -0
- package/src/react/components/canvas/ImageSourcePicker.jsx +133 -0
- package/src/react/components/canvas/MultiSelectActions.jsx +120 -0
- package/src/react/components/canvas/SVGCanvas.jsx +145 -0
- package/src/react/components/canvas/SaveStatusIndicator.jsx +29 -0
- package/src/react/components/modals/CanvasPropertiesModal.jsx +358 -0
- package/src/react/components/modals/CommandPalette.jsx +307 -0
- package/src/react/components/modals/ExportImageModal.jsx +287 -0
- package/src/react/components/modals/GraphModal.jsx +174 -0
- package/src/react/components/modals/HelpModal.jsx +208 -0
- package/src/react/components/modals/LixScriptModal.jsx +130 -0
- package/src/react/components/modals/ShortcutsModal.jsx +125 -0
- package/src/react/components/sidebars/ArrowSidebar.jsx +173 -0
- package/src/react/components/sidebars/CircleSidebar.jsx +134 -0
- package/src/react/components/sidebars/FrameSidebar.jsx +263 -0
- package/src/react/components/sidebars/IconSidebar.jsx +279 -0
- package/src/react/components/sidebars/ImageSidebar.jsx +69 -0
- package/src/react/components/sidebars/LineSidebar.jsx +109 -0
- package/src/react/components/sidebars/PaintbrushSidebar.jsx +119 -0
- package/src/react/components/sidebars/RectangleSidebar.jsx +165 -0
- package/src/react/components/sidebars/ShapeSidebar.jsx +130 -0
- package/src/react/components/sidebars/TextSidebar.jsx +174 -0
- package/src/react/hooks/inertStores.js +58 -0
- package/src/react/hooks/useSketchEngine.js +76 -0
- package/src/react/hooks/useTranslation.js +92 -0
- package/src/react/index.js +41 -0
- package/src/react/store/useSketchStore.js +234 -0
- package/src/react/store/useUIStore.js +196 -0
- package/src/react/styles/arrowSidebar.css +263 -0
- package/src/react/styles/canvasStroke.css +47 -0
- package/src/react/styles/canvasTools.css +769 -0
- package/src/react/styles/circleSidebar.css +206 -0
- package/src/react/styles/fonts/fonts.css +29 -0
- package/src/react/styles/fonts/lixCode.ttf +0 -0
- package/src/react/styles/fonts/lixDefault.ttf +0 -0
- package/src/react/styles/fonts/lixDocs.ttf +0 -0
- package/src/react/styles/fonts/lixFancy.ttf +0 -0
- package/src/react/styles/fonts/lixFont.woff2 +0 -0
- package/src/react/styles/lineSidebar.css +207 -0
- package/src/react/styles/paintbrushSidebar.css +247 -0
- package/src/react/styles/squareSidebar.css +206 -0
- package/src/react/styles/writeText.css +149 -0
- package/src/react/styles.css +175 -0
- package/src/react/toolsEnum.js +20 -0
- package/src/react/utils/toast.js +18 -0
- package/src/shapes/Frame.js +7 -1
- package/src/shapes/Line.js +11 -5
- package/src/tools/arrowTool.js +30 -4
- package/src/tools/circleTool.js +33 -9
- package/src/tools/codeTool.js +5 -3
- package/src/tools/frameTool.js +47 -7
- package/src/tools/freehandTool.js +12 -4
- package/src/tools/iconTool.js +7 -1
- package/src/tools/imageTool.js +7 -1
- package/src/tools/lineTool.js +27 -3
- package/src/tools/rectangleTool.js +45 -8
- package/src/tools/textTool.js +23 -6
|
@@ -359,117 +359,182 @@ export function renderFlowchartPreviewSVG(diagram) {
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
/**
|
|
362
|
-
* Render a flowchart diagram onto the canvas
|
|
362
|
+
* Render a flowchart diagram onto the canvas as a real engine `Frame`
|
|
363
|
+
* containing independent shapes — Rectangle nodes (with embedded labels)
|
|
364
|
+
* joined by Arrow edges. Each child is fully independent for click /
|
|
365
|
+
* drag / resize / colour-change, exactly like a user-drawn shape; the
|
|
366
|
+
* Frame's `_diagramType` marker means deleting it pulls every child with
|
|
367
|
+
* it (so the diagram behaves as one logical unit when you're done with
|
|
368
|
+
* it) — see Frame.destroy().
|
|
369
|
+
*
|
|
370
|
+
* Issue #34 bug #3 (follow-up to #22 phase 5): drops the shared `groupId`
|
|
371
|
+
* glue that made selecting one shape select the whole diagram; the Frame
|
|
372
|
+
* is the explicit container instead.
|
|
363
373
|
*/
|
|
364
374
|
export function renderFlowchartOnCanvas(diagram) {
|
|
365
375
|
if (!diagram || !diagram.nodes || diagram.nodes.length === 0) return false;
|
|
366
|
-
if (!window.svg || !window.Frame) {
|
|
376
|
+
if (!window.svg || !window.Rectangle || !window.Frame) {
|
|
367
377
|
console.error('[FlowchartRenderer] Engine not initialized');
|
|
368
378
|
return false;
|
|
369
379
|
}
|
|
370
380
|
|
|
371
|
-
|
|
372
|
-
const
|
|
373
|
-
if (!svgMarkup) return false;
|
|
374
|
-
|
|
375
|
-
// Parse SVG to get dimensions
|
|
376
|
-
const parser = new DOMParser();
|
|
377
|
-
const doc = parser.parseFromString(svgMarkup, 'image/svg+xml');
|
|
378
|
-
const svgEl = doc.querySelector('svg');
|
|
379
|
-
if (!svgEl) return false;
|
|
381
|
+
const nodes = diagram.nodes;
|
|
382
|
+
const edges = diagram.edges || [];
|
|
380
383
|
|
|
381
|
-
|
|
382
|
-
|
|
384
|
+
// Diagram bounds (in the renderer's natural coordinate space)
|
|
385
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
386
|
+
nodes.forEach((n) => {
|
|
387
|
+
const nw = n.width || 140;
|
|
388
|
+
const nh = n.height || 60;
|
|
389
|
+
minX = Math.min(minX, n.x);
|
|
390
|
+
minY = Math.min(minY, n.y);
|
|
391
|
+
maxX = Math.max(maxX, n.x + nw);
|
|
392
|
+
maxY = Math.max(maxY, n.y + nh);
|
|
393
|
+
});
|
|
394
|
+
const dw = (maxX - minX) || 1;
|
|
395
|
+
const dh = (maxY - minY) || 1;
|
|
383
396
|
|
|
384
|
-
//
|
|
397
|
+
// Center on the current viewport
|
|
385
398
|
const vb = window.currentViewBox || { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight };
|
|
386
399
|
const vcx = vb.x + vb.width / 2;
|
|
387
400
|
const vcy = vb.y + vb.height / 2;
|
|
401
|
+
const ox = vcx - dw / 2 - minX;
|
|
402
|
+
const oy = vcy - dh / 2 - minY;
|
|
403
|
+
|
|
404
|
+
// ── Wrapper frame ──────────────────────────────────────────────────
|
|
405
|
+
// Created up-front so we can call addShapeToFrame as each child is
|
|
406
|
+
// built. _diagramType marks it so Frame.destroy() takes the children
|
|
407
|
+
// along on delete (issue #34 bug #3).
|
|
408
|
+
const PADDING = 40;
|
|
409
|
+
const frameTitle = diagram.title || 'Mermaid diagram';
|
|
410
|
+
const frame = new window.Frame(
|
|
411
|
+
vcx - dw / 2 - PADDING,
|
|
412
|
+
vcy - dh / 2 - PADDING,
|
|
413
|
+
dw + PADDING * 2,
|
|
414
|
+
dh + PADDING * 2,
|
|
415
|
+
{
|
|
416
|
+
stroke: '#888',
|
|
417
|
+
strokeWidth: 1,
|
|
418
|
+
fill: 'transparent',
|
|
419
|
+
opacity: 0.7,
|
|
420
|
+
frameName: frameTitle,
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
frame._diagramType = 'mermaid-flowchart';
|
|
424
|
+
window.shapes.push(frame);
|
|
425
|
+
if (window.pushCreateAction) window.pushCreateAction(frame);
|
|
426
|
+
|
|
427
|
+
const nodeMap = new Map(); // id → shape
|
|
428
|
+
|
|
429
|
+
// ── Nodes ──────────────────────────────────────────────────────────
|
|
430
|
+
for (const n of nodes) {
|
|
431
|
+
const nw = n.width || 140;
|
|
432
|
+
const nh = n.height || 60;
|
|
433
|
+
const nx = n.x + ox;
|
|
434
|
+
const ny = n.y + oy;
|
|
435
|
+
const cx = nx + nw / 2;
|
|
436
|
+
const cy = ny + nh / 2;
|
|
437
|
+
|
|
438
|
+
const opts = {
|
|
439
|
+
stroke: n.stroke || '#fff',
|
|
440
|
+
strokeWidth: n.strokeWidth ?? 1.5,
|
|
441
|
+
fill: n.fill || 'transparent',
|
|
442
|
+
fillStyle: n.fill && n.fill !== 'transparent' ? 'solid' : 'none',
|
|
443
|
+
roughness: 1,
|
|
444
|
+
label: n.label || '',
|
|
445
|
+
};
|
|
388
446
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
447
|
+
let shape = null;
|
|
448
|
+
try {
|
|
449
|
+
if (n.type === 'circle' && window.Circle) {
|
|
450
|
+
shape = new window.Circle(cx, cy, nw / 2, nh / 2, opts);
|
|
451
|
+
} else if (n.type === 'diamond') {
|
|
452
|
+
const sz = Math.max(nw, nh) * 0.75;
|
|
453
|
+
shape = new window.Rectangle(cx - sz / 2, cy - sz / 2, sz, sz, opts);
|
|
454
|
+
shape.rotation = 45;
|
|
455
|
+
if (typeof shape.draw === 'function') shape.draw();
|
|
456
|
+
} else if (n.type === 'roundrect') {
|
|
457
|
+
shape = new window.Rectangle(nx, ny, nw, nh, { ...opts, cornerRadius: Math.min(nw, nh) * 0.2 });
|
|
458
|
+
} else {
|
|
459
|
+
shape = new window.Rectangle(nx, ny, nw, nh, opts);
|
|
460
|
+
}
|
|
461
|
+
} catch (err) {
|
|
462
|
+
console.warn('[FlowchartRenderer] Node creation failed:', n.id, err);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
if (!shape) continue;
|
|
396
466
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
frame = new window.Frame(frameX, frameY, frameW, frameH, {
|
|
401
|
-
stroke: '#888', strokeWidth: 2, fill: 'transparent', opacity: 1,
|
|
402
|
-
frameName: title,
|
|
403
|
-
});
|
|
404
|
-
frame._diagramType = 'flowchart';
|
|
405
|
-
frame._diagramData = diagram;
|
|
467
|
+
window.shapes.push(shape);
|
|
468
|
+
if (window.pushCreateAction) window.pushCreateAction(shape);
|
|
469
|
+
frame.addShapeToFrame(shape);
|
|
406
470
|
|
|
407
|
-
|
|
408
|
-
if (window.pushCreateAction) window.pushCreateAction(frame);
|
|
409
|
-
} catch (err) {
|
|
410
|
-
console.error('[FlowchartRenderer] Frame creation failed:', err);
|
|
411
|
-
return false;
|
|
471
|
+
nodeMap.set(n.id, { shape, x: nx, y: ny, width: nw, height: nh, centerX: cx, centerY: cy });
|
|
412
472
|
}
|
|
413
473
|
|
|
414
|
-
//
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
graphGroup.setAttribute('transform', `translate(${frameX + framePad}, ${frameY + framePad})`);
|
|
420
|
-
|
|
421
|
-
// Copy defs
|
|
422
|
-
const defs = svgEl.querySelector('defs');
|
|
423
|
-
if (defs) {
|
|
424
|
-
let mainDefs = window.svg.querySelector('defs');
|
|
425
|
-
if (!mainDefs) {
|
|
426
|
-
mainDefs = document.createElementNS(NS, 'defs');
|
|
427
|
-
window.svg.insertBefore(mainDefs, window.svg.firstChild);
|
|
428
|
-
}
|
|
429
|
-
while (defs.firstChild) {
|
|
430
|
-
mainDefs.appendChild(defs.firstChild);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
474
|
+
// ── Edges ──────────────────────────────────────────────────────────
|
|
475
|
+
for (const e of edges) {
|
|
476
|
+
const fromNode = nodeMap.get(e.from);
|
|
477
|
+
const toNode = nodeMap.get(e.to);
|
|
478
|
+
if (!fromNode || !toNode) continue;
|
|
433
479
|
|
|
434
|
-
//
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
svgEl.removeChild(child);
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
graphGroup.appendChild(child);
|
|
442
|
-
}
|
|
480
|
+
// Connect from the source center to the target center — autoAttach
|
|
481
|
+
// will snap each endpoint to the appropriate edge of the shape.
|
|
482
|
+
const sp = { x: fromNode.centerX, y: fromNode.centerY };
|
|
483
|
+
const ep = { x: toNode.centerX, y: toNode.centerY };
|
|
443
484
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
move(dx, dy) {
|
|
456
|
-
this.x += dx;
|
|
457
|
-
this.y += dy;
|
|
458
|
-
this.group.setAttribute('transform', `translate(${this.x}, ${this.y})`);
|
|
459
|
-
},
|
|
460
|
-
updateAttachedArrows() {},
|
|
485
|
+
const directed = e.directed !== false;
|
|
486
|
+
const style = e.style || 'normal';
|
|
487
|
+
const isThick = style === 'thick';
|
|
488
|
+
const isDotted = style === 'dotted';
|
|
489
|
+
|
|
490
|
+
const opts = {
|
|
491
|
+
stroke: e.stroke || '#fff',
|
|
492
|
+
strokeWidth: isThick ? 3 : 1.5,
|
|
493
|
+
roughness: 1,
|
|
494
|
+
strokeDasharray: isDotted ? '5 3' : '',
|
|
495
|
+
label: e.label || '',
|
|
461
496
|
};
|
|
462
497
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
498
|
+
let connector = null;
|
|
499
|
+
try {
|
|
500
|
+
if (directed && window.Arrow) {
|
|
501
|
+
connector = new window.Arrow(sp, ep, opts);
|
|
502
|
+
} else if (window.Line) {
|
|
503
|
+
connector = new window.Line(sp, ep, opts);
|
|
504
|
+
} else if (window.Arrow) {
|
|
505
|
+
connector = new window.Arrow(sp, ep, opts);
|
|
506
|
+
}
|
|
507
|
+
} catch (err) {
|
|
508
|
+
console.warn('[FlowchartRenderer] Edge creation failed:', e, err);
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
if (!connector) continue;
|
|
512
|
+
|
|
513
|
+
window.shapes.push(connector);
|
|
514
|
+
if (window.pushCreateAction) window.pushCreateAction(connector);
|
|
515
|
+
frame.addShapeToFrame(connector);
|
|
516
|
+
|
|
517
|
+
// Wire arrow endpoints into the source/target shapes so moving a
|
|
518
|
+
// node drags its connections along. window.__autoAttach is set up
|
|
519
|
+
// by AIRenderer.initAIRenderer() during engine init.
|
|
520
|
+
if (directed && connector.shapeName === 'arrow' && typeof window.__autoAttach === 'function') {
|
|
521
|
+
try {
|
|
522
|
+
window.__autoAttach(connector, fromNode.shape, true, sp);
|
|
523
|
+
window.__autoAttach(connector, toNode.shape, false, ep);
|
|
524
|
+
} catch (err) {
|
|
525
|
+
console.warn('[FlowchartRenderer] autoAttach failed:', err);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
467
528
|
}
|
|
468
529
|
|
|
469
|
-
// Select the
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
530
|
+
// Select the first node so the user has feedback that something
|
|
531
|
+
// landed. Selecting a child (not the frame) reinforces the
|
|
532
|
+
// "independent shapes inside a frame" model.
|
|
533
|
+
const first = nodeMap.values().next().value;
|
|
534
|
+
if (first) {
|
|
535
|
+
window.currentShape = first.shape;
|
|
536
|
+
if (typeof first.shape.selectShape === 'function') first.shape.selectShape();
|
|
537
|
+
}
|
|
473
538
|
|
|
474
539
|
return true;
|
|
475
540
|
}
|
|
@@ -361,119 +361,194 @@ export function parseAndRenderSequence(src) {
|
|
|
361
361
|
}
|
|
362
362
|
|
|
363
363
|
/**
|
|
364
|
-
* Render a sequence diagram onto the canvas
|
|
365
|
-
*
|
|
366
|
-
*
|
|
364
|
+
* Render a sequence diagram onto the canvas as a real engine `Frame`
|
|
365
|
+
* containing independent shapes — top + bottom participant boxes,
|
|
366
|
+
* dashed lifelines, and a real Arrow (or Line for `--x`/`-x`) per
|
|
367
|
+
* message. Each child is fully independent for click / drag / resize;
|
|
368
|
+
* the Frame's `_diagramType` marker makes Frame.destroy() pull the
|
|
369
|
+
* children along on delete, so the diagram still behaves as one
|
|
370
|
+
* logical unit when discarded.
|
|
371
|
+
*
|
|
372
|
+
* Issue #34 bug #3 (follow-up to #24 per-actor split): drops the shared
|
|
373
|
+
* `groupId` glue so clicking one shape selects only that shape.
|
|
374
|
+
*
|
|
375
|
+
* Notes and block-frames (alt/opt/loop) are skipped for now — they'd
|
|
376
|
+
* either need their own shape types or a richer label model. Self-
|
|
377
|
+
* messages are also skipped (would need a curved arrow).
|
|
367
378
|
*/
|
|
368
379
|
export function renderSequenceOnCanvas(diagram) {
|
|
369
380
|
if (!diagram || diagram.type !== 'sequenceDiagram') return false;
|
|
370
|
-
if (!window.svg || !window.
|
|
371
|
-
console.error('[SequenceRenderer] Engine not initialized');
|
|
381
|
+
if (!window.svg || !window.Rectangle || !window.Line || !window.Arrow) {
|
|
382
|
+
console.error('[SequenceRenderer] Engine not initialized (Rectangle / Line / Arrow missing)');
|
|
372
383
|
return false;
|
|
373
384
|
}
|
|
374
385
|
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
if (
|
|
386
|
+
const participants = diagram.participants || [];
|
|
387
|
+
const messages = diagram.messages || [];
|
|
388
|
+
if (participants.length === 0) return false;
|
|
378
389
|
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
|
|
390
|
+
// Mirror the layout math from renderSequenceSVG so the canvas layout
|
|
391
|
+
// matches the modal preview.
|
|
392
|
+
const pCount = participants.length;
|
|
393
|
+
const contentWidth = (pCount - 1) * PARTICIPANT_GAP + PARTICIPANT_W;
|
|
394
|
+
const totalWidth = Math.max(contentWidth + SIDE_MARGIN * 2, 400);
|
|
395
|
+
const startX = (totalWidth - contentWidth) / 2;
|
|
396
|
+
const pCenters = participants.map((_, i) => startX + i * PARTICIPANT_GAP + PARTICIPANT_W / 2);
|
|
384
397
|
|
|
385
|
-
const
|
|
386
|
-
const
|
|
398
|
+
const topBoxBottom = TOP_MARGIN + PARTICIPANT_H;
|
|
399
|
+
const msgYPositions = [];
|
|
400
|
+
let currentY = topBoxBottom + 30;
|
|
401
|
+
for (let mi = 0; mi < messages.length; mi++) {
|
|
402
|
+
msgYPositions.push(currentY);
|
|
403
|
+
currentY += MSG_ROW_HEIGHT;
|
|
404
|
+
}
|
|
405
|
+
const bottomBoxTop = currentY + 20;
|
|
406
|
+
const totalHeight = bottomBoxTop + PARTICIPANT_H + BOTTOM_MARGIN;
|
|
387
407
|
|
|
388
|
-
//
|
|
408
|
+
// Centre the diagram on the current viewport so the user sees it
|
|
409
|
+
// right where they invoked the renderer.
|
|
389
410
|
const vb = window.currentViewBox || { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight };
|
|
390
|
-
const
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
// Create frame
|
|
402
|
-
let frame;
|
|
403
|
-
try {
|
|
404
|
-
frame = new window.Frame(frameX, frameY, frameW, frameH, {
|
|
405
|
-
stroke: '#888', strokeWidth: 2, fill: 'transparent', opacity: 1,
|
|
406
|
-
frameName: title,
|
|
407
|
-
});
|
|
408
|
-
frame._diagramType = 'sequence';
|
|
409
|
-
frame._diagramData = diagram;
|
|
410
|
-
|
|
411
|
-
window.shapes.push(frame);
|
|
412
|
-
if (window.pushCreateAction) window.pushCreateAction(frame);
|
|
413
|
-
} catch (err) {
|
|
414
|
-
console.error('[SequenceRenderer] Frame creation failed:', err);
|
|
411
|
+
const ox = vb.x + vb.width / 2 - totalWidth / 2;
|
|
412
|
+
const oy = vb.y + vb.height / 2 - totalHeight / 2;
|
|
413
|
+
|
|
414
|
+
// ── Wrapper frame ──────────────────────────────────────────────────
|
|
415
|
+
// Issue #34 bug #3: drop the shared groupId glue. Each child is
|
|
416
|
+
// independent for click / drag / resize. The Frame's `_diagramType`
|
|
417
|
+
// marker makes Frame.destroy() pull the children along on delete,
|
|
418
|
+
// so the diagram still behaves as one logical unit when discarded.
|
|
419
|
+
if (!window.Frame) {
|
|
420
|
+
console.error('[SequenceRenderer] window.Frame missing — cannot wrap diagram');
|
|
415
421
|
return false;
|
|
416
422
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
mainDefs = document.createElementNS(NS, 'defs');
|
|
431
|
-
window.svg.insertBefore(mainDefs, window.svg.firstChild);
|
|
432
|
-
}
|
|
433
|
-
while (defs.firstChild) {
|
|
434
|
-
mainDefs.appendChild(defs.firstChild);
|
|
435
|
-
}
|
|
423
|
+
const PADDING = 24;
|
|
424
|
+
const frameTitle = diagram.title || 'Sequence diagram';
|
|
425
|
+
const frame = new window.Frame(
|
|
426
|
+
ox - PADDING,
|
|
427
|
+
oy - PADDING,
|
|
428
|
+
totalWidth + PADDING * 2,
|
|
429
|
+
totalHeight + PADDING * 2,
|
|
430
|
+
{
|
|
431
|
+
stroke: '#888',
|
|
432
|
+
strokeWidth: 1,
|
|
433
|
+
fill: 'transparent',
|
|
434
|
+
opacity: 0.7,
|
|
435
|
+
frameName: frameTitle,
|
|
436
436
|
}
|
|
437
|
+
);
|
|
438
|
+
frame._diagramType = 'mermaid-sequence';
|
|
439
|
+
window.shapes.push(frame);
|
|
440
|
+
if (window.pushCreateAction) window.pushCreateAction(frame);
|
|
437
441
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
442
|
+
const created = [];
|
|
443
|
+
|
|
444
|
+
// ── Participants: top box + lifeline (and bottom box) ─────────────
|
|
445
|
+
const pIndex = new Map();
|
|
446
|
+
for (let pi = 0; pi < pCount; pi++) {
|
|
447
|
+
const p = participants[pi];
|
|
448
|
+
pIndex.set(p.name, pi);
|
|
449
|
+
const cx = pCenters[pi] + ox;
|
|
450
|
+
const bx = cx - PARTICIPANT_W / 2;
|
|
451
|
+
|
|
452
|
+
try {
|
|
453
|
+
// Top participant box
|
|
454
|
+
const topBox = new window.Rectangle(bx, TOP_MARGIN + oy, PARTICIPANT_W, PARTICIPANT_H, {
|
|
455
|
+
stroke: THEME.participantBorder,
|
|
456
|
+
strokeWidth: 1.5,
|
|
457
|
+
fill: THEME.participantBg,
|
|
458
|
+
fillStyle: 'solid',
|
|
459
|
+
roughness: 1,
|
|
460
|
+
label: p.name,
|
|
461
|
+
});
|
|
462
|
+
window.shapes.push(topBox);
|
|
463
|
+
if (window.pushCreateAction) window.pushCreateAction(topBox);
|
|
464
|
+
frame.addShapeToFrame(topBox);
|
|
465
|
+
created.push(topBox);
|
|
466
|
+
|
|
467
|
+
// Lifeline (dashed vertical line spanning the diagram height)
|
|
468
|
+
const lifeline = new window.Line(
|
|
469
|
+
{ x: cx, y: topBoxBottom + oy },
|
|
470
|
+
{ x: cx, y: bottomBoxTop + oy },
|
|
471
|
+
{
|
|
472
|
+
stroke: THEME.lifeline,
|
|
473
|
+
strokeWidth: 1,
|
|
474
|
+
strokeDasharray: '6 4',
|
|
475
|
+
roughness: 0,
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
window.shapes.push(lifeline);
|
|
479
|
+
if (window.pushCreateAction) window.pushCreateAction(lifeline);
|
|
480
|
+
frame.addShapeToFrame(lifeline);
|
|
481
|
+
created.push(lifeline);
|
|
482
|
+
|
|
483
|
+
// Bottom participant box (mirrors top — Mermaid convention)
|
|
484
|
+
const bottomBox = new window.Rectangle(bx, bottomBoxTop + oy, PARTICIPANT_W, PARTICIPANT_H, {
|
|
485
|
+
stroke: THEME.participantBorder,
|
|
486
|
+
strokeWidth: 1.5,
|
|
487
|
+
fill: THEME.participantBg,
|
|
488
|
+
fillStyle: 'solid',
|
|
489
|
+
roughness: 1,
|
|
490
|
+
label: p.name,
|
|
491
|
+
});
|
|
492
|
+
window.shapes.push(bottomBox);
|
|
493
|
+
if (window.pushCreateAction) window.pushCreateAction(bottomBox);
|
|
494
|
+
frame.addShapeToFrame(bottomBox);
|
|
495
|
+
created.push(bottomBox);
|
|
496
|
+
} catch (err) {
|
|
497
|
+
console.warn('[SequenceRenderer] Participant creation failed:', p.name, err);
|
|
446
498
|
}
|
|
499
|
+
}
|
|
447
500
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
501
|
+
// ── Messages: arrow (or line for --x style) per row ───────────────
|
|
502
|
+
for (let mi = 0; mi < messages.length; mi++) {
|
|
503
|
+
const m = messages[mi];
|
|
504
|
+
const fromI = pIndex.get(m.from);
|
|
505
|
+
const toI = pIndex.get(m.to);
|
|
506
|
+
if (fromI == null || toI == null) continue;
|
|
507
|
+
if (fromI === toI) continue; // skip self-messages (v1 limitation)
|
|
508
|
+
|
|
509
|
+
const fromCx = pCenters[fromI] + ox;
|
|
510
|
+
const toCx = pCenters[toI] + ox;
|
|
511
|
+
const y = msgYPositions[mi] + oy;
|
|
512
|
+
|
|
513
|
+
const labelText = m.number ? `${m.number}. ${m.text}` : m.text;
|
|
514
|
+
|
|
515
|
+
// Solid vs dashed (sync vs async response). `cross` style (-x)
|
|
516
|
+
// would render as a line with an X at the head — fall back to a
|
|
517
|
+
// line for that, arrow otherwise.
|
|
518
|
+
const isCross = !!m.cross && m.arrowHead === 'cross';
|
|
519
|
+
const opts = {
|
|
520
|
+
stroke: THEME.messageLine,
|
|
521
|
+
strokeWidth: 1.5,
|
|
522
|
+
roughness: 0,
|
|
523
|
+
strokeDasharray: m.solid ? '' : '6 4',
|
|
524
|
+
label: labelText || '',
|
|
465
525
|
};
|
|
466
526
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
527
|
+
try {
|
|
528
|
+
const sp = { x: fromCx, y };
|
|
529
|
+
const ep = { x: toCx, y };
|
|
530
|
+
const connector = isCross
|
|
531
|
+
? new window.Line(sp, ep, opts)
|
|
532
|
+
: new window.Arrow(sp, ep, opts);
|
|
533
|
+
window.shapes.push(connector);
|
|
534
|
+
if (window.pushCreateAction) window.pushCreateAction(connector);
|
|
535
|
+
frame.addShapeToFrame(connector);
|
|
536
|
+
created.push(connector);
|
|
537
|
+
} catch (err) {
|
|
538
|
+
console.warn('[SequenceRenderer] Message creation failed:', m, err);
|
|
539
|
+
}
|
|
471
540
|
}
|
|
472
541
|
|
|
473
|
-
//
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
542
|
+
// Auto-select the first node so the user has feedback that the
|
|
543
|
+
// diagram landed. Selecting a child (not the frame) reinforces the
|
|
544
|
+
// "independent shapes inside a frame" model from issue #34 bug #3.
|
|
545
|
+
const first = created[0];
|
|
546
|
+
if (first) {
|
|
547
|
+
window.currentShape = first;
|
|
548
|
+
if (typeof first.selectShape === 'function') first.selectShape();
|
|
549
|
+
}
|
|
477
550
|
|
|
551
|
+
console.log(`[SequenceRenderer] Done: ${pCount} participants, ${messages.length} messages`);
|
|
478
552
|
return true;
|
|
479
553
|
}
|
|
554
|
+
|