@pixerate/editor-react 0.40.0 → 0.42.0

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.
@@ -2532,6 +2532,217 @@ import {
2532
2532
  angleToSvgLinearCoords,
2533
2533
  gradientToSvgDef as gradientToSvgDef2
2534
2534
  } from "@pixerate/editor/canvas";
2535
+
2536
+ // src/canvas/useMermaidDiagram.ts
2537
+ import { useEffect as useEffect10, useMemo as useMemo8, useRef as useRef15, useState as useState8, useSyncExternalStore as useSyncExternalStore4 } from "react";
2538
+ import {
2539
+ DiagramController
2540
+ } from "@pixerate/editor/diagram";
2541
+ import {
2542
+ DiagramStream,
2543
+ blendDiagramLayout,
2544
+ easeDiagramTween,
2545
+ mermaidMovesFromCanvas,
2546
+ mermaidToCanvas
2547
+ } from "@pixerate/editor/canvas";
2548
+ function useDiagramState(controller) {
2549
+ return useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
2550
+ }
2551
+ function useMermaidDiagram(controllerOrOptions = {}) {
2552
+ const own = useRef15(null);
2553
+ let controller;
2554
+ if (controllerOrOptions instanceof DiagramController) controller = controllerOrOptions;
2555
+ else {
2556
+ if (!own.current) own.current = new DiagramController(controllerOrOptions);
2557
+ controller = own.current;
2558
+ }
2559
+ const layoutOptions = controllerOrOptions instanceof DiagramController ? void 0 : controllerOrOptions.layoutOptions;
2560
+ const state = useDiagramState(controller);
2561
+ const canvas = useMemo8(
2562
+ () => mermaidToCanvas(state.document, { ...layoutOptions, editable: state.editable }),
2563
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2564
+ [state.document, state.editable]
2565
+ );
2566
+ const layoutRef = useRef15(canvas.layout);
2567
+ layoutRef.current = canvas.layout;
2568
+ const actions = useMemo8(
2569
+ () => ({
2570
+ apply: (ops, label) => controller.apply(ops, label),
2571
+ load: (source, options) => controller.load(source, options),
2572
+ moveNodes: (moved, all = moved) => {
2573
+ const op = mermaidMovesFromCanvas(layoutRef.current, moved, all, { stored: controller.getDocument().meta.layout ?? {} });
2574
+ return op ? controller.apply(op, "Move") : null;
2575
+ },
2576
+ connect: (source, target, label) => controller.apply({ type: "addEdge", edge: { source, target, ...label ? { label } : {} } }, "Connect"),
2577
+ remove: (ids) => {
2578
+ const graph = controller.getDocument().graph;
2579
+ const edgeIds = new Set(graph.edges.map((e) => e.id));
2580
+ const edges = ids.filter((id) => edgeIds.has(id));
2581
+ const nodes = ids.filter((id) => !edgeIds.has(id));
2582
+ const ops = [];
2583
+ if (edges.length) ops.push({ type: "removeEdges", ids: edges });
2584
+ if (nodes.length) ops.push({ type: "removeNodes", ids: nodes });
2585
+ return controller.apply(ops, "Delete");
2586
+ },
2587
+ rename: (id, label) => controller.apply(
2588
+ controller.getDocument().graph.groups.some((g) => g.id === id) ? { type: "renameGroup", id, label } : { type: "renameNode", id, label },
2589
+ "Rename"
2590
+ ),
2591
+ select: (ids) => controller.select(ids),
2592
+ undo: () => void controller.undo(),
2593
+ redo: () => void controller.redo()
2594
+ }),
2595
+ [controller]
2596
+ );
2597
+ return { controller, state, document: state.document, source: state.source, ...canvas, ...actions };
2598
+ }
2599
+ function useMermaidStream(source, options = {}) {
2600
+ const { complete = false, duration = 300, ...layoutOptions } = options;
2601
+ const stream = useRef15(null);
2602
+ if (!stream.current) stream.current = new DiagramStream(layoutOptions);
2603
+ const result = useMemo8(() => stream.current.update(source, complete), [source, complete]);
2604
+ const [shown, setShown] = useState8({ layout: result.layout, settled: true });
2605
+ const shownRef = useRef15(shown.layout);
2606
+ shownRef.current = shown.layout;
2607
+ useEffect10(() => {
2608
+ const target = result.layout;
2609
+ if (!target) return setShown({ layout: null, settled: true });
2610
+ const from = shownRef.current;
2611
+ if (!from || duration <= 0 || typeof requestAnimationFrame !== "function") return setShown({ layout: target, settled: true });
2612
+ if (from === target) return;
2613
+ let frame = 0;
2614
+ const start = performance.now();
2615
+ const step = () => {
2616
+ const t = Math.min(1, (performance.now() - start) / duration);
2617
+ setShown({ layout: blendDiagramLayout(from, target, easeDiagramTween(t)), settled: t >= 1 });
2618
+ if (t < 1) frame = requestAnimationFrame(step);
2619
+ };
2620
+ frame = requestAnimationFrame(step);
2621
+ return () => cancelAnimationFrame(frame);
2622
+ }, [result.layout, duration]);
2623
+ const doc = result.document.tier === "editable" ? result.document : null;
2624
+ const canvas = useMemo8(() => {
2625
+ if (!doc || !shown.layout) return { nodes: [], edges: [] };
2626
+ const placed = new Set(Object.keys(shown.layout.positions));
2627
+ const graph = {
2628
+ ...doc.graph,
2629
+ nodes: doc.graph.nodes.filter((n) => placed.has(n.id)),
2630
+ groups: doc.graph.groups.filter((g) => placed.has(g.id)),
2631
+ edges: doc.graph.edges.filter((e) => placed.has(e.source) && placed.has(e.target))
2632
+ };
2633
+ return mermaidToCanvas({ ...doc, graph }, { layout: shown.layout });
2634
+ }, [doc, shown.layout]);
2635
+ return { document: doc, ...canvas, layout: shown.layout, settled: shown.settled };
2636
+ }
2637
+
2638
+ // src/canvas/MermaidDiagramNode.tsx
2639
+ import { MERMAID_SHAPE_PAINT, mermaidLabelLines, mermaidNodeMembers, mermaidNodePaint, mermaidShapePaths } from "@pixerate/editor/canvas";
2640
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2641
+ function mermaidNodeStyle(styles = {}, selected = false) {
2642
+ const paint = mermaidNodePaint(styles, selected);
2643
+ const { color, "stroke-width": strokeWidth, "stroke-dasharray": strokeDasharray, ...vars } = paint;
2644
+ return { ...vars, color, strokeWidth, strokeDasharray };
2645
+ }
2646
+ function MermaidDiagramNode({ data, width = 160, height = 56, selected, className = "", children }) {
2647
+ const node = data.node;
2648
+ if (!node) return null;
2649
+ const members = mermaidNodeMembers(node.data);
2650
+ return /* @__PURE__ */ jsxs8(
2651
+ "div",
2652
+ {
2653
+ className: `pixerate-diagram-node ${className}`.trim(),
2654
+ "data-shape": node.shape,
2655
+ "data-selected": selected ? "true" : void 0,
2656
+ style: { position: "relative", width: "100%", height: "100%", ...mermaidNodeStyle(data.resolvedStyles, selected) },
2657
+ children: [
2658
+ /* @__PURE__ */ jsx8("svg", { viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "none", width: "100%", height: "100%", style: { position: "absolute", inset: 0, overflow: "visible" }, "aria-hidden": "true", children: mermaidShapePaths(node.shape, width, height).map((path, i) => /* @__PURE__ */ jsx8("path", { d: path.d, style: { ...MERMAID_SHAPE_PAINT[path.paint], vectorEffect: "non-scaling-stroke" } }, i)) }),
2659
+ /* @__PURE__ */ jsxs8(
2660
+ "div",
2661
+ {
2662
+ style: { position: "relative", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: members.length ? "flex-start" : "center", height: "100%", padding: "6px 10px", boxSizing: "border-box", fontSize: 13, lineHeight: 1.3, textAlign: "center", pointerEvents: "none" },
2663
+ children: [
2664
+ mermaidLabelLines(node.label).map((line, i) => /* @__PURE__ */ jsx8("span", { style: { fontWeight: members.length ? 600 : void 0 }, children: line }, i)),
2665
+ members.length > 0 && /* @__PURE__ */ jsx8("div", { style: { alignSelf: "stretch", marginTop: 4, paddingTop: 4, borderTop: "1px solid var(--pixerate-diagram-node-border)", textAlign: "left", fontSize: 12 }, children: members.map((m, i) => /* @__PURE__ */ jsx8("div", { children: m }, i)) })
2666
+ ]
2667
+ }
2668
+ ),
2669
+ children
2670
+ ]
2671
+ }
2672
+ );
2673
+ }
2674
+ function MermaidDiagramGroup({ data, selected, className = "", children }) {
2675
+ const group = data.group;
2676
+ if (!group) return null;
2677
+ return /* @__PURE__ */ jsxs8(
2678
+ "div",
2679
+ {
2680
+ className: `pixerate-diagram-group ${className}`.trim(),
2681
+ "data-selected": selected ? "true" : void 0,
2682
+ style: {
2683
+ width: "100%",
2684
+ height: "100%",
2685
+ boxSizing: "border-box",
2686
+ borderRadius: 8,
2687
+ border: `1px ${selected ? "solid" : "dashed"} ${selected ? "var(--primary, #2563eb)" : data.resolvedStyles?.stroke ?? "var(--border, #cbd5e1)"}`,
2688
+ background: data.resolvedStyles?.fill ?? "var(--muted, rgba(148, 163, 184, 0.08))",
2689
+ color: data.resolvedStyles?.color ?? "var(--muted-foreground, #475569)"
2690
+ },
2691
+ children: [
2692
+ /* @__PURE__ */ jsx8("div", { style: { padding: "6px 10px", fontSize: 12, fontWeight: 600 }, children: mermaidLabelLines(group.label).join(" ") }),
2693
+ children
2694
+ ]
2695
+ }
2696
+ );
2697
+ }
2698
+
2699
+ // src/canvas/MermaidThumbnail.tsx
2700
+ import { useMemo as useMemo9 } from "react";
2701
+ import { parseMermaid } from "@pixerate/editor/diagram";
2702
+ import { mermaidThumbnailSvg } from "@pixerate/editor/canvas";
2703
+ import { Fragment as Fragment2, jsx as jsx9 } from "react/jsx-runtime";
2704
+ function MermaidThumbnail({ source, className = "", fallback = null, ...options }) {
2705
+ const doc = useMemo9(() => typeof source === "string" ? parseMermaid(source) : source, [source]);
2706
+ const key = JSON.stringify(options);
2707
+ const drawn = useMemo9(
2708
+ () => doc.tier === "editable" && doc.graph.nodes.length ? mermaidThumbnailSvg(doc, options) : null,
2709
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2710
+ [doc, key]
2711
+ );
2712
+ if (!drawn) return /* @__PURE__ */ jsx9(Fragment2, { children: fallback });
2713
+ return /* @__PURE__ */ jsx9("span", { className: `pixerate-diagram-thumbnail ${className}`.trim(), style: { display: "inline-block", lineHeight: 0 }, dangerouslySetInnerHTML: { __html: drawn.svg } });
2714
+ }
2715
+
2716
+ // src/canvas/useDiagramDirector.ts
2717
+ import { useRef as useRef16, useSyncExternalStore as useSyncExternalStore5 } from "react";
2718
+ import { DiagramDirector, PresenceStore } from "@pixerate/editor/diagram";
2719
+ function useDiagramDirector(controller, options) {
2720
+ const placement = useRef16(options.placement);
2721
+ placement.current = options.placement;
2722
+ const ref = useRef16(null);
2723
+ if (!ref.current) {
2724
+ const presence = new PresenceStore();
2725
+ ref.current = {
2726
+ presence,
2727
+ director: new DiagramDirector(controller, presence, { placement: () => placement.current(), speed: options.speed, clock: options.clock, linger: options.linger })
2728
+ };
2729
+ }
2730
+ return ref.current;
2731
+ }
2732
+ function usePresenceState(presence) {
2733
+ return useSyncExternalStore5(presence.subscribe, presence.getState, presence.getState);
2734
+ }
2735
+
2736
+ // src/canvas/DiagramPresenceLayer.tsx
2737
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2738
+ function DiagramPresenceLayer({ presence, viewport, className = "" }) {
2739
+ const { actors } = usePresenceState(presence);
2740
+ if (!actors.length) return null;
2741
+ return /* @__PURE__ */ jsx10("div", { className: `pixerate-diagram-presence ${className}`.trim(), "aria-hidden": "true", style: { position: "absolute", inset: 0, pointerEvents: "none", overflow: "hidden", zIndex: 50 }, children: actors.map((a) => /* @__PURE__ */ jsxs9("div", { "data-actor": a.id, style: { position: "absolute", left: 0, top: 0, transform: `translate(${a.x * viewport.zoom + viewport.x}px, ${a.y * viewport.zoom + viewport.y}px)` }, children: [
2742
+ /* @__PURE__ */ jsx10("svg", { width: "18", height: "20", viewBox: "0 0 12 18", style: { display: "block", transform: a.pressed ? "scale(0.85)" : void 0, transformOrigin: "0 0" }, children: /* @__PURE__ */ jsx10("path", { d: "M0,0 L0,14 L4,10.5 L7,17 L9.5,16 L6.5,9.5 L11.5,9.5 Z", style: { fill: a.color, stroke: "#ffffff", strokeWidth: 1 } }) }),
2743
+ /* @__PURE__ */ jsx10("span", { style: { position: "absolute", left: 14, top: 16, whiteSpace: "nowrap", borderRadius: 4, padding: "1px 6px", fontSize: 11, fontWeight: 600, color: "#ffffff", background: a.color }, children: a.message ?? a.name })
2744
+ ] }, a.id)) });
2745
+ }
2535
2746
  export {
2536
2747
  ANIMATABLE_PROPERTIES,
2537
2748
  ANIMATION_DATA_KEY,
@@ -2556,8 +2767,12 @@ export {
2556
2767
  CanvasInstanceInspector,
2557
2768
  CanvasScenesStore2 as CanvasScenesStore,
2558
2769
  DEFAULT_ARRANGE_SHORTCUTS,
2770
+ DiagramPresenceLayer,
2559
2771
  MAX_KEYFRAME_REFS,
2560
2772
  ManualClock,
2773
+ MermaidDiagramGroup,
2774
+ MermaidDiagramNode,
2775
+ MermaidThumbnail,
2561
2776
  RESIZE_HANDLES,
2562
2777
  SHAPE_DATA_KEY,
2563
2778
  SHAPE_KINDS,
@@ -2738,6 +2953,7 @@ export {
2738
2953
  measureSelection2 as measureSelection,
2739
2954
  measureToLine,
2740
2955
  mergeNodeUpdates,
2956
+ mermaidNodeStyle,
2741
2957
  mixColorsOklab,
2742
2958
  moveKeyframes,
2743
2959
  moveLayoutChild,
@@ -2855,6 +3071,11 @@ export {
2855
3071
  useCanvasScenes,
2856
3072
  useCanvasShortcuts,
2857
3073
  useCanvasTokens,
3074
+ useDiagramDirector,
3075
+ useDiagramState,
3076
+ useMermaidDiagram,
3077
+ useMermaidStream,
3078
+ usePresenceState,
2858
3079
  useStreamingCanvas,
2859
3080
  withNodeAnimation,
2860
3081
  withStyleSize,