@likec4/diagram 1.59.2 → 1.59.3

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.
Files changed (73) hide show
  1. package/README.md +53 -0
  2. package/dist/LikeC4Styles.js +2 -1
  3. package/dist/base/BaseXYFlow.js +8 -1
  4. package/dist/base-primitives/compound/CompoundNodeContainer.js +2 -3
  5. package/dist/base-primitives/compound/CompoundTitle.js +5 -4
  6. package/dist/base-primitives/element/ElementData.js +6 -1
  7. package/dist/base-primitives/element/ElementShape.js +9 -10
  8. package/dist/context/CurrentViewModelProvider.js +2 -1
  9. package/dist/context/DiagramEventHandlers.js +2 -1
  10. package/dist/context/IconRenderer.js +42 -26
  11. package/dist/context/TagStylesContext.d.ts +2 -1
  12. package/dist/context/TagStylesContext.js +32 -20
  13. package/dist/custom/index.js +1 -1
  14. package/dist/editor/LikeC4EditorCallbacks.d.ts +20 -1
  15. package/dist/editor/LikeC4EditorCallbacks.js +4 -1
  16. package/dist/editor/actor/setup.d.ts +1 -0
  17. package/dist/editor/actor/setup.js +17 -12
  18. package/dist/editor/actor/state.sync-queue.js +1 -0
  19. package/dist/editor/applyChangesToManualLayout.js +1 -1
  20. package/dist/editor/index.d.ts +1 -1
  21. package/dist/editor/useEditorActorLogic.js +10 -3
  22. package/dist/hooks/useDiagram.js +4 -2
  23. package/dist/hooks/useDiagramCompareLayout.d.ts +13 -3
  24. package/dist/hooks/useDiagramCompareLayout.js +6 -1
  25. package/dist/hooks/useUpdateEffect.js +2 -1
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.js +3 -2
  28. package/dist/likec4diagram/DiagramUI.js +4 -3
  29. package/dist/likec4diagram/DiagramXYFlow.d.ts +1 -0
  30. package/dist/likec4diagram/DiagramXYFlow.js +14 -7
  31. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +1 -3
  32. package/dist/likec4diagram/custom/edges/SequenceStepEdge.js +1 -3
  33. package/dist/likec4diagram/custom/edges/useRelationshipEdgePath.js +3 -2
  34. package/dist/likec4diagram/custom/nodes/nodes.js +6 -3
  35. package/dist/likec4diagram/custom/nodes/toolbar/Toolbar.js +4 -3
  36. package/dist/likec4diagram/relationship-popover/RelationshipPopover.js +1 -2
  37. package/dist/likec4diagram/state/assign.js +1 -1
  38. package/dist/likec4diagram/state/createViewChange.js +1 -6
  39. package/dist/likec4diagram/state/machine.actions.js +6 -2
  40. package/dist/likec4diagram/state/machine.setup.js +9 -8
  41. package/dist/likec4diagram/state/machine.state.ready.walkthrough.js +1 -1
  42. package/dist/likec4diagram/state/utils.js +7 -3
  43. package/dist/likec4diagram/ui/FloatingSequenceActors.js +2 -1
  44. package/dist/likec4diagram/ui/sequence-outline/SequenceOutlinePanel.js +4 -2
  45. package/dist/likec4diagram/useLayoutConstraints.js +19 -4
  46. package/dist/likec4diagram/xyflow-sequence/layouter.js +8 -10
  47. package/dist/navigationpanel/NavigationPanelDropdown.js +2 -1
  48. package/dist/navigationpanel/controls/DetailsControls.js +2 -1
  49. package/dist/navigationpanel/editorpanel/ApplySemanticLayout.js +2 -1
  50. package/dist/navigationpanel/editorpanel/ChangeAutoLayoutButton.js +2 -1
  51. package/dist/navigationpanel/editorpanel/ManualLayoutToolsButton.d.ts +2 -0
  52. package/dist/navigationpanel/editorpanel/ManualLayoutToolsButton.js +11 -3
  53. package/dist/navigationpanel/walkthrough/DynamicViewControls.js +1 -3
  54. package/dist/overlays/element-details/ElementDetailsCard.js +2 -2
  55. package/dist/overlays/overlaysActor.js +16 -17
  56. package/dist/overlays/relationship-details/RelationshipDetails.js +2 -1
  57. package/dist/overlays/relationship-details/hooks.js +2 -1
  58. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +4 -2
  59. package/dist/overlays/relationships-browser/actor.js +2 -1
  60. package/dist/overlays/relationships-browser/custom/ElementActions.js +39 -38
  61. package/dist/overlays/relationships-browser/hooks.js +2 -1
  62. package/dist/projects-overview/ProjectsOverview.js +8 -7
  63. package/dist/projects-overview/hooks.js +2 -1
  64. package/dist/search/components/ViewsColum.js +10 -8
  65. package/dist/search/hooks.js +2 -1
  66. package/dist/shadowroot/ShadowRoot.js +4 -2
  67. package/dist/styles2.js +1 -1
  68. package/dist/utils/index.js +2 -2
  69. package/package.json +26 -26
  70. package/panda.buildinfo.json +2 -1
  71. package/styles-min.css +1 -1
  72. package/styles-xyflow.css +1 -1
  73. package/styles.css +1 -1
@@ -13,7 +13,8 @@ var depsShallowEqual = (d1, d2) => {
13
13
  return true;
14
14
  };
15
15
  function useUpdateEffect$1(callback, deps, equalityFn, effectHook) {
16
- useCustomCompareEffect(useFirstMountState() ? noop : callback, deps, equalityFn ?? depsShallowEqual, effectHook);
16
+ const isFirstMount = useFirstMountState();
17
+ useCustomCompareEffect(isFirstMount ? noop : callback, deps, equalityFn ?? depsShallowEqual, effectHook);
17
18
  }
18
19
  __name(useUpdateEffect$1, "useUpdateEffect");
19
20
  //#endregion
package/dist/index.d.ts CHANGED
@@ -3,9 +3,10 @@ export { LikeC4Diagram, type LikeC4DiagramProps, } from './LikeC4Diagram';
3
3
  export type { ElementIconRenderer, ElementIconRendererProps, LikeC4ColorScheme, NodeRenderers, OnCanvasClick, OnEdgeClick, OnInitialized, OnNavigateTo, OnNodeClick, OnOpenSource, PaddingWithUnit, ViewPadding, ViewPaddings, WhereOperator, } from './LikeC4Diagram.props';
4
4
  export { LikeC4ModelProvider, type LikeC4ModelProviderProps, } from './LikeC4ModelProvider';
5
5
  export { LikeC4EditorProvider, type LikeC4EditorProviderProps, } from './editor/LikeC4EditorProvider';
6
- export { createLikeC4Editor, type LikeC4EditorCallbacks, } from './editor/LikeC4EditorCallbacks';
6
+ export { createLikeC4Editor, type LikeC4EditorCallbacks, type LikeC4EditorChangeResult, } from './editor/LikeC4EditorCallbacks';
7
7
  export { LikeC4ProjectsProvider, type LikeC4ProjectsProviderProps, } from './LikeC4ProjectsProvider';
8
8
  export { type DiagramApi, type DiagramContext, selectDiagramContext, selectDiagramSnapshot, useDiagram, useDiagramContext, useDiagramSelector, useOnDiagramEvent, } from './hooks/useDiagram';
9
+ export { type DiagramCompareLayoutOps, type DiagramCompareLayoutState, useDiagramCompareLayout, } from './hooks/useDiagramCompareLayout';
9
10
  export { useLikeC4Model, useLikeC4Specification, useLikeC4ViewModel, } from './hooks/useLikeC4Model';
10
11
  export { type CurrentViewModel, useCurrentViewModel, useOptionalCurrentViewModel, } from './hooks/useCurrentViewModel';
11
12
  export { useChangeLikeC4Project, useHasProjects, useLikeC4Project, useLikeC4ProjectId, useLikeC4Projects, useLikeC4ProjectsContext, } from './hooks/useLikeC4Project';
package/dist/index.js CHANGED
@@ -7,12 +7,13 @@ import { useDiagramContext, useOnDiagramEvent } from "./hooks/useDiagram.js";
7
7
  import { useCurrentView, useCurrentViewId } from "./hooks/useCurrentView.js";
8
8
  import { useOptionalCurrentViewModel } from "./context/LikeC4ModelContext.js";
9
9
  import { useLikeC4Model, useLikeC4Specification, useLikeC4ViewModel } from "./hooks/useLikeC4Model.js";
10
+ import { useLikeC4Styles } from "./hooks/useLikeC4Styles.js";
10
11
  import { createLikeC4Editor } from "./editor/LikeC4EditorCallbacks.js";
11
12
  import { LikeC4EditorProvider } from "./editor/LikeC4EditorProvider.js";
12
13
  import { useCurrentViewModel } from "./hooks/useCurrentViewModel.js";
13
14
  import { useChangeLikeC4Project, useHasProjects, useLikeC4Project, useLikeC4ProjectId, useLikeC4Projects, useLikeC4ProjectsContext } from "./hooks/useLikeC4Project.js";
14
- import { useLikeC4Styles } from "./hooks/useLikeC4Styles.js";
15
15
  import { pickViewBounds } from "./utils/view-bounds.js";
16
+ import { useDiagramCompareLayout } from "./hooks/useDiagramCompareLayout.js";
16
17
  import { SearchContext, normalizeSearch, useSearchContext } from "./search/SearchContext.js";
17
18
  import { SearchPanelContent, bodyCss, dialogCss } from "./search/Search.js";
18
19
  import { LikeC4Diagram } from "./LikeC4Diagram.js";
@@ -23,4 +24,4 @@ import { LikeC4View } from "./LikeC4View.js";
23
24
  import { ReactLikeC4 } from "./ReactLikeC4.js";
24
25
  import { LikeC4ProjectsOverview } from "./LikeC4ProjectsOverview.js";
25
26
  import { SearchControl } from "./components/SearchControl.js";
26
- export { DefaultMantineProvider, FramerMotionConfig, IconRendererProvider, LikeC4Diagram, LikeC4EditorProvider, LikeC4ModelProvider, LikeC4ProjectsOverview, LikeC4ProjectsProvider, LikeC4View, ReactLikeC4, SearchContext, SearchControl, SearchPanelContent, StaticLikeC4Diagram, createLikeC4Editor, normalizeSearch, pickViewBounds, bodyCss as searchBodyCss, dialogCss as searchDialogCss, selectDiagramContext, selectDiagramSnapshot, useChangeLikeC4Project, useCurrentView, useCurrentViewId, useCurrentViewModel, useDiagram, useDiagramContext, useDiagramSelector, useHasProjects, useLikeC4Model, useLikeC4Project, useLikeC4ProjectId, useLikeC4Projects, useLikeC4ProjectsContext, useLikeC4Specification, useLikeC4Styles, useLikeC4ViewModel, useOnDiagramEvent, useOptionalCurrentViewModel, useSearchContext, useUpdateEffect };
27
+ export { DefaultMantineProvider, FramerMotionConfig, IconRendererProvider, LikeC4Diagram, LikeC4EditorProvider, LikeC4ModelProvider, LikeC4ProjectsOverview, LikeC4ProjectsProvider, LikeC4View, ReactLikeC4, SearchContext, SearchControl, SearchPanelContent, StaticLikeC4Diagram, createLikeC4Editor, normalizeSearch, pickViewBounds, bodyCss as searchBodyCss, dialogCss as searchDialogCss, selectDiagramContext, selectDiagramSnapshot, useChangeLikeC4Project, useCurrentView, useCurrentViewId, useCurrentViewModel, useDiagram, useDiagramCompareLayout, useDiagramContext, useDiagramSelector, useHasProjects, useLikeC4Model, useLikeC4Project, useLikeC4ProjectId, useLikeC4Projects, useLikeC4ProjectsContext, useLikeC4Specification, useLikeC4Styles, useLikeC4ViewModel, useOnDiagramEvent, useOptionalCurrentViewModel, useSearchContext, useUpdateEffect };
@@ -32,10 +32,11 @@ var LikeC4DiagramUI = memo(() => {
32
32
  const { enableControls, enableNotations, enableSearch, enableRelationshipDetails, enableReadOnly, enableCompareWithLatest } = useEnabledFeatures();
33
33
  const rerender = useRerender();
34
34
  const { isSequenceView, isActiveWalkthrough, ...actors } = useDiagramSelector(selectChildren);
35
+ const handleReset = useCallback(() => {
36
+ rerender();
37
+ }, []);
35
38
  return /* @__PURE__ */ jsxs(ErrorBoundary, {
36
- onReset: useCallback(() => {
37
- rerender();
38
- }, []),
39
+ onReset: handleReset,
39
40
  children: [
40
41
  isSequenceView && /* @__PURE__ */ jsx(FloatingSequenceActors, { isActiveWalkthrough }),
41
42
  isActiveWalkthrough && /* @__PURE__ */ jsx(SequenceOutlinePanel, {}),
@@ -2,5 +2,6 @@ import { PropsWithChildren } from 'react';
2
2
  import { JSX } from 'react/jsx-runtime';
3
3
  import { Simplify } from 'type-fest';
4
4
  import { LikeC4DiagramProperties } from '../LikeC4Diagram.props';
5
+ export declare function resolveInteractionEnabled(enabled: boolean, override: boolean | undefined, isEditorBusy: boolean): boolean;
5
6
  export type LikeC4DiagramXYFlowProps = PropsWithChildren<Simplify<Pick<LikeC4DiagramProperties<any>, 'background' | 'reactFlowProps' | 'renderNodes'>>>;
6
7
  export declare function LikeC4DiagramXYFlow({ background, reactFlowProps, children, renderNodes, }: LikeC4DiagramXYFlowProps): JSX.Element;
@@ -8,6 +8,7 @@ import { useDiagramEventHandlers } from "../context/DiagramEventHandlers.js";
8
8
  import { selectDiagramSnapshot, useDiagram, useDiagramSelector } from "../hooks/safeContext.js";
9
9
  import { viewBounds } from "./state/utils.js";
10
10
  import { deriveToggledFeatures } from "./state/machine.setup.js";
11
+ import { useEditorActorStateHasTag } from "../hooks/useEditorActor.js";
11
12
  import { BuiltinEdges, BuiltinNodes } from "./custom/index.js";
12
13
  import { useLayoutConstraints } from "./useLayoutConstraints.js";
13
14
  import { cx } from "@likec4/styles/css";
@@ -54,12 +55,14 @@ var viewportToTopLeft = (ctx) => {
54
55
  zoom: 1
55
56
  };
56
57
  };
58
+ function resolveInteractionEnabled(enabled, override, isEditorBusy) {
59
+ return (override ?? enabled) && !isEditorBusy;
60
+ }
57
61
  var selectXYProps = selectDiagramSnapshot(({ context: ctx, children }) => {
58
62
  const { enableReadOnly } = deriveToggledFeatures(ctx);
59
63
  const editorSnapshot = enableReadOnly ? null : children.editor?.getSnapshot();
60
64
  const isNotEditingEdge = enableReadOnly || editorSnapshot?.context.editing?.subject !== "edge";
61
- const isEditorBusy = editorSnapshot?.hasTag("busy") ?? false;
62
- let nodesDraggable = !enableReadOnly && ctx.nodesDraggable && !isEditorBusy;
65
+ let nodesDraggable = !enableReadOnly && ctx.nodesDraggable;
63
66
  if (ctx.dynamicViewVariant === "sequence" && ctx.view._type === "dynamic") nodesDraggable = false;
64
67
  return {
65
68
  enableReadOnly,
@@ -69,7 +72,7 @@ var selectXYProps = selectDiagramSnapshot(({ context: ctx, children }) => {
69
72
  pannable: ctx.pannable,
70
73
  zoomable: ctx.zoomable,
71
74
  nodesDraggable,
72
- nodesSelectable: ctx.nodesSelectable && isNotEditingEdge && !isEditorBusy,
75
+ nodesSelectable: ctx.nodesSelectable && isNotEditingEdge,
73
76
  fitViewPadding: ctx.fitViewPadding,
74
77
  enableFitView: ctx.features.enableFitView,
75
78
  enableControls: ctx.features.enableControls && ctx.features.enableFitView,
@@ -79,8 +82,12 @@ var selectXYProps = selectDiagramSnapshot(({ context: ctx, children }) => {
79
82
  return shallowEqual(a, b) && shallowEqual(aNodes, bNodes) && shallowEqual(aEdges, bEdges);
80
83
  });
81
84
  function LikeC4DiagramXYFlow({ background = "dots", reactFlowProps = {}, children, renderNodes }) {
85
+ const { nodesDraggable: nodesDraggableOverride, elementsSelectable: nodesSelectableOverride, ...safeReactFlowProps } = reactFlowProps;
82
86
  const diagram = useDiagram();
83
87
  let { enableReadOnly, initialized, nodes, edges, enableFitView, nodesDraggable, nodesSelectable, enableControls, ...props } = useDiagramSelector(selectXYProps);
88
+ const isEditorBusy = useEditorActorStateHasTag("busy");
89
+ nodesDraggable = resolveInteractionEnabled(nodesDraggable, nodesDraggableOverride, isEditorBusy);
90
+ nodesSelectable = resolveInteractionEnabled(nodesSelectable, nodesSelectableOverride, isEditorBusy);
84
91
  const { onNodeContextMenu, onCanvasContextMenu, onEdgeContextMenu, onNodeClick, onEdgeClick, onCanvasClick, onCanvasDblClick } = useDiagramEventHandlers();
85
92
  const { reducedGraphics, $panning } = useRootContainer();
86
93
  const isReducedGraphics = reducedGraphics;
@@ -234,15 +241,15 @@ function LikeC4DiagramXYFlow({ background = "dots", reactFlowProps = {}, childre
234
241
  }),
235
242
  ...onCanvasContextMenu && { onPaneContextMenu: onCanvasContextMenu },
236
243
  ...enableFitView && { onViewportResize },
237
- nodesDraggable,
238
- nodesSelectable,
239
244
  nodesFocusable: true,
240
245
  edgesFocusable: true,
241
246
  elevateEdgesOnSelect: !enableReadOnly,
242
247
  zIndexMode: "manual",
243
248
  ...nodesDraggable && layoutConstraints,
244
249
  ...props,
245
- ...reactFlowProps,
250
+ ...safeReactFlowProps,
251
+ nodesDraggable,
252
+ nodesSelectable,
246
253
  children: [enableControls && /* @__PURE__ */ jsx(Controls$1, { padding: props.fitViewPadding }), children]
247
254
  });
248
255
  }
@@ -257,4 +264,4 @@ var Controls$1 = /* @__PURE__ */ __name(({ padding }) => /* @__PURE__ */ jsx(Con
257
264
  position: "bottom-left"
258
265
  }), "Controls");
259
266
  //#endregion
260
- export { LikeC4DiagramXYFlow };
267
+ export { LikeC4DiagramXYFlow, resolveInteractionEnabled };
@@ -362,9 +362,7 @@ function ControlPoints({ isControlPointDragging, edgeProps, controlPoints, onMov
362
362
  addSelectedEdges([edgeId]);
363
363
  onLmbControlPointerDown(index, e, domNode);
364
364
  break;
365
- case 2:
366
- onRmbControlPointerDown(index, e);
367
- break;
365
+ case 2: onRmbControlPointerDown(index, e);
368
366
  }
369
367
  });
370
368
  const onControlPointerDblClick = useCallbackRef((e) => {
@@ -37,9 +37,7 @@ function SequenceStepEdge(props) {
37
37
  case isBack:
38
38
  labelX = props.sourceX - LABEL_OFFSET;
39
39
  break;
40
- default:
41
- labelX = props.sourceX + LABEL_OFFSET;
42
- break;
40
+ default: labelX = props.sourceX + LABEL_OFFSET;
43
41
  }
44
42
  const { state, stepnum } = props.data;
45
43
  return /* @__PURE__ */ jsxs(EdgeContainer, {
@@ -38,7 +38,7 @@ function useRelationshipEdgePath({ props: { sourceX, sourceY, source, target, ta
38
38
  height: targetNodeHeight
39
39
  };
40
40
  const nodeMargin = 6;
41
- return nonNullable(curve(data.dir === "back" ? [
41
+ const points = data.dir === "back" ? [
42
42
  targetCenterPos,
43
43
  getNodeIntersectionFromCenterToPoint(targetNd, first(controlPoints) ?? sourceCenterPos, nodeMargin),
44
44
  ...controlPoints,
@@ -50,7 +50,8 @@ function useRelationshipEdgePath({ props: { sourceX, sourceY, source, target, ta
50
50
  ...controlPoints,
51
51
  getNodeIntersectionFromCenterToPoint(targetNd, last(controlPoints) ?? sourceCenterPos, nodeMargin),
52
52
  targetCenterPos
53
- ]));
53
+ ];
54
+ return nonNullable(curve(points));
54
55
  }
55
56
  //#endregion
56
57
  export { useRelationshipEdgePath };
@@ -116,8 +116,9 @@ var hasDrifts = (props) => {
116
116
  };
117
117
  function CompoundElementNode(props) {
118
118
  const { enableElementDetails, enableReadOnly, enableCompareWithLatest } = useEnabledFeatures();
119
+ const showDrifts = enableCompareWithLatest && hasDrifts(props);
119
120
  return /* @__PURE__ */ jsxs(CompoundNodeContainer, {
120
- className: enableCompareWithLatest && hasDrifts(props) ? compoundHasDrifts : void 0,
121
+ className: showDrifts ? compoundHasDrifts : void 0,
121
122
  nodeProps: props,
122
123
  children: [
123
124
  enableCompareWithLatest && /* @__PURE__ */ jsx(NodeDrifts, { nodeProps: props }),
@@ -134,8 +135,9 @@ function CompoundElementNode(props) {
134
135
  }
135
136
  function CompoundDeploymentNode(props) {
136
137
  const { enableElementDetails, enableReadOnly, enableCompareWithLatest } = useEnabledFeatures();
138
+ const showDrifts = enableCompareWithLatest && hasDrifts(props);
137
139
  return /* @__PURE__ */ jsxs(CompoundNodeContainer, {
138
- className: enableCompareWithLatest && hasDrifts(props) ? compoundHasDrifts : void 0,
140
+ className: showDrifts ? compoundHasDrifts : void 0,
139
141
  nodeProps: props,
140
142
  children: [
141
143
  enableCompareWithLatest && /* @__PURE__ */ jsx(NodeDrifts, { nodeProps: props }),
@@ -152,8 +154,9 @@ function CompoundDeploymentNode(props) {
152
154
  }
153
155
  function ViewGroupNode(props) {
154
156
  const { enableCompareWithLatest } = useEnabledFeatures();
157
+ const showDrifts = enableCompareWithLatest && hasDrifts(props);
155
158
  return /* @__PURE__ */ jsxs(CompoundNodeContainer, {
156
- className: enableCompareWithLatest && hasDrifts(props) ? compoundHasDrifts : void 0,
159
+ className: showDrifts ? compoundHasDrifts : void 0,
157
160
  nodeProps: props,
158
161
  children: [
159
162
  enableCompareWithLatest && /* @__PURE__ */ jsx(NodeDrifts, { nodeProps: props }),
@@ -20,9 +20,10 @@ function Toolbar({ title, children, nodeProps, ...props }) {
20
20
  const { selected = false, dragging = false, data: { hovered = false } } = nodeProps;
21
21
  const _isToolbarVisible = hovered && selectedNodesCount === 0 || selected && selectedNodesCount === 1;
22
22
  let delay = 150;
23
- if (_isToolbarVisible) if (selected) delay = 100;
24
- else delay = 1e3;
25
- else if (selectedNodesCount > 0) delay = 50;
23
+ if (_isToolbarVisible) {
24
+ if (selected) delay = 100;
25
+ else delay = 1e3;
26
+ } else if (selectedNodesCount > 0) delay = 50;
26
27
  const [isToolbarVisible] = useDebouncedValue(_isToolbarVisible, delay);
27
28
  if (!isToolbarVisible) return null;
28
29
  return /* @__PURE__ */ jsx(NodeToolbar, {
@@ -134,7 +134,6 @@ var RelationshipPopover = memo(() => {
134
134
  var getEdgeLabelElement = (edgeId, container) => {
135
135
  return container?.querySelector(`.likec4-edge-label[data-edge-id="${edgeId}"]`) ?? container?.querySelector(`.likec4-edge-middle-point[data-edge-id="${edgeId}"]`) ?? null;
136
136
  };
137
- var POPOVER_PADDING = 8;
138
137
  var RelationshipPopoverInternal = ({ viewId, diagramEdge, direct, nested, sourceNode, targetNode, onMouseEnter, onMouseLeave }) => {
139
138
  const ref = useRef(null);
140
139
  const { enableNavigateTo, enableVscode } = useEnabledFeatures();
@@ -180,7 +179,7 @@ var RelationshipPopoverInternal = ({ viewId, diagramEdge, direct, nested, source
180
179
  })}px`
181
180
  });
182
181
  } }),
183
- hide({ padding: POPOVER_PADDING * 2 })
182
+ hide({ padding: 16 })
184
183
  ]
185
184
  }).then(({ x, y, middlewareData }) => {
186
185
  if (wasCanceled) return;
@@ -86,7 +86,7 @@ function resetEdgeControlPoints(nodeLookup, edge) {
86
86
  if (source === target) {
87
87
  const loopSize = 80;
88
88
  const centerOfTopBoundary = vector(0, source.height || 0).multiply(-.5).add(sourceCenter);
89
- return [centerOfTopBoundary.add(vector(-80 / 2.5, -80)).round().toObject(), centerOfTopBoundary.add(vector(loopSize / 2.5, -80)).round().toObject()];
89
+ return [centerOfTopBoundary.add(vector(-32, -80)).round().toObject(), centerOfTopBoundary.add(vector(loopSize / 2.5, -80)).round().toObject()];
90
90
  }
91
91
  const sourceToTargetVector = targetCenter.subtract(sourceCenter);
92
92
  const sourceBorderPoint = getBorderPointOnVector(source, sourceCenter, sourceToTargetVector);
@@ -1,5 +1,4 @@
1
1
  import "../../_virtual/_rolldown/runtime.js";
2
- import { bezierControlPoints, isSamePoint } from "../../utils/xyflow.js";
3
2
  import { calcViewBounds } from "../../utils/view-bounds.js";
4
3
  import { hasAtLeast, map } from "remeda";
5
4
  import { getNodeDimensions } from "@xyflow/system";
@@ -7,7 +6,6 @@ import { getNodeDimensions } from "@xyflow/system";
7
6
  function createViewChange(parentContext) {
8
7
  const { view: { drifts: _1, _layout: _2, ...view }, xynodes, xystore } = parentContext;
9
8
  const { nodeLookup, edgeLookup } = xystore.getState();
10
- const movedNodes = /* @__PURE__ */ new Set();
11
9
  const nodes = map(view.nodes, (node) => {
12
10
  const internal = nodeLookup.get(node.id);
13
11
  if (!internal) {
@@ -17,7 +15,6 @@ function createViewChange(parentContext) {
17
15
  const xynodedata = xynodes.find((n) => n.data.id === node.id)?.data ?? internal.data;
18
16
  const position = internal.internals.positionAbsolute;
19
17
  const { width, height } = getNodeDimensions(internal);
20
- if (!isSamePoint(position, node) || node.width !== width || node.height !== height) movedNodes.add(node.id);
21
18
  return {
22
19
  ...node,
23
20
  shape: xynodedata.shape,
@@ -36,9 +33,7 @@ function createViewChange(parentContext) {
36
33
  return edge;
37
34
  }
38
35
  const data = xyedge.data;
39
- let controlPoints = data.controlPoints ?? [];
40
- const sourceOrTargetMoved = movedNodes.has(xyedge.source) || movedNodes.has(xyedge.target);
41
- if (controlPoints.length === 0 && sourceOrTargetMoved) controlPoints = bezierControlPoints(data.points);
36
+ const controlPoints = data.controlPoints ?? [];
42
37
  const _updated = {
43
38
  ...edge,
44
39
  points: data.points
@@ -250,7 +250,8 @@ var layoutAlign = (params) => machine.createAction(({ context, event }) => {
250
250
  }
251
251
  const xystore = nonNullable(context.xystore, "xystore is not initialized");
252
252
  const { nodeLookup, parentLookup } = xystore.getState();
253
- const nodesToAlign = [...difference$1(new Set(nodeLookup.values().filter((n) => n.selected).map((n) => n.id)), new Set(parentLookup.keys()))];
253
+ const selectedNodes = new Set(nodeLookup.values().filter((n) => n.selected).map((n) => n.id));
254
+ const nodesToAlign = [...difference$1(selectedNodes, new Set(parentLookup.keys()))];
254
255
  if (!hasAtLeast(nodesToAlign, 2)) {
255
256
  console.warn("At least 2 nodes must be selected to align");
256
257
  return;
@@ -737,7 +738,10 @@ var updateView = () => machine.enqueueActions(({ enqueue, event, context }) => {
737
738
  let recenter = !context.viewportChangedManually && !context.focusedNode && !context.activeWalkthrough;
738
739
  recenter = recenter || context.toggledFeatures.enableCompareWithLatest === true && !!nextView._layout && context.view._layout !== nextView._layout;
739
740
  if (nextView._type === "dynamic" && context.view._type === "dynamic") {
740
- if (nextView.variant === context.view.variant && nextView.variant !== context.dynamicViewVariant) enqueue(setViewportCenter(BBox$1.center(viewBounds(context, nextView))));
741
+ if (nextView.variant === context.view.variant && nextView.variant !== context.dynamicViewVariant) {
742
+ const nextCenter = BBox$1.center(viewBounds(context, nextView));
743
+ enqueue(setViewportCenter(nextCenter));
744
+ }
741
745
  }
742
746
  if (recenter) {
743
747
  enqueue(cancelFitDiagram());
@@ -67,6 +67,14 @@ var isReadOnly = (context) => deriveToggledFeatures(context).enableReadOnly;
67
67
  function isActiveSequenceWalkthrough(context) {
68
68
  return isDynamicView(context.view) && context.dynamicViewVariant === "sequence" && !!context.activeWalkthrough && hasProp(context.view, "flow");
69
69
  }
70
+ var actors = defineActors({
71
+ hotkey: hotkeyActorLogic,
72
+ overlays: overlaysActorLogic,
73
+ search: searchActorLogic,
74
+ mediaPrint: mediaPrintActorLogic,
75
+ editor: editorActorLogic,
76
+ navigationPanel: navigationPanelActorLogic
77
+ });
70
78
  var machine = setup({
71
79
  types: {
72
80
  context: {},
@@ -75,14 +83,7 @@ var machine = setup({
75
83
  events: {},
76
84
  emitted: {}
77
85
  },
78
- actors: defineActors({
79
- hotkey: hotkeyActorLogic,
80
- overlays: overlaysActorLogic,
81
- search: searchActorLogic,
82
- mediaPrint: mediaPrintActorLogic,
83
- editor: editorActorLogic,
84
- navigationPanel: navigationPanelActorLogic
85
- }),
86
+ actors,
86
87
  guards: {
87
88
  "isReady": ({ context }) => context.initialized.xydata && context.initialized.xyflow,
88
89
  "enabled: Editor": ({ context }) => context.features.enableEditor,
@@ -172,9 +172,9 @@ var walkthrough = machine.createStateConfig({
172
172
  actions: [fitFocusedBounds(), emitEdgeClick()]
173
173
  }, { actions: [
174
174
  assign$1(({ event, context }) => {
175
- invariant(event.edge.type === "seq-step", `Expected seq-step edge, but got "${event.edge.type}"`);
176
175
  invariant(!!context.activeWalkthrough);
177
176
  const stepId = event.edge.data.id;
177
+ invariant(isStepPath$1(stepId), `Expected step edge, but got "${stepId}"`);
178
178
  return { activeWalkthrough: {
179
179
  ...context.activeWalkthrough,
180
180
  stepId,
@@ -56,7 +56,8 @@ function findDiagramEdge(ctx, xyedgeId) {
56
56
  * If {@link nextView} is provided, returns the bounds of the next view.
57
57
  */
58
58
  function viewBounds(ctx, nextView) {
59
- return pickViewBounds(nextView ?? ctx.view, ctx.dynamicViewVariant);
59
+ const view = nextView ?? ctx.view;
60
+ return pickViewBounds(view, ctx.dynamicViewVariant);
60
61
  }
61
62
  function focusedBounds(params) {
62
63
  const b = params.context.xynodes.reduce((acc, node) => {
@@ -107,7 +108,9 @@ function activeSequenceBounds(params) {
107
108
  })
108
109
  };
109
110
  }
110
- const actorsBounds = getNodesBounds([nonNullable(xystate.nodeLookup.get(stepEdge.source)), nonNullable(xystate.nodeLookup.get(stepEdge.target))], xystate);
111
+ const sourceNode = nonNullable(xystate.nodeLookup.get(stepEdge.source));
112
+ const targetNode = nonNullable(xystate.nodeLookup.get(stepEdge.target));
113
+ const actorsBounds = getNodesBounds([sourceNode, targetNode], xystate);
111
114
  let stepBounds = edgeBounds ? BBox.merge(edgeBounds, actorsBounds) : actorsBounds;
112
115
  return {
113
116
  duration: 500,
@@ -157,7 +160,8 @@ function findCorrespondingNode(context, event) {
157
160
  }
158
161
  function calcViewportForBounds(context, bounds) {
159
162
  let { width, height, transform } = context.xystore.getState();
160
- return getViewportForBounds(bounds, width, height, MinZoom, Math.max(transform[2], 1), context.fitViewPadding);
163
+ const maxZoom = Math.max(transform[2], 1);
164
+ return getViewportForBounds(bounds, width, height, MinZoom, maxZoom, context.fitViewPadding);
161
165
  }
162
166
  //#endregion
163
167
  export { activeSequenceBounds, calcViewportForBounds, findCorrespondingNode, findDiagramEdge, findDiagramNode, findNodeByModelFqn, focusedBounds, getEdgeBounds, nodeRef, typedSystem, viewBounds };
@@ -39,9 +39,10 @@ var selectActorNodes = selectXYStore((state) => {
39
39
  * Shows sequence actors at the top of the diagram, if current viewport does not fit them
40
40
  */
41
41
  function FloatingSequenceActors({ isActiveWalkthrough }) {
42
+ const ids = useXYStore(selectActorNodes);
42
43
  return /* @__PURE__ */ jsx(LayoutGroup, { children: /* @__PURE__ */ jsx(AnimatePresence, {
43
44
  anchorY: "top",
44
- children: useXYStore(selectActorNodes).map((id) => /* @__PURE__ */ jsx(SequenceActor, {
45
+ children: ids.map((id) => /* @__PURE__ */ jsx(SequenceActor, {
45
46
  id,
46
47
  top: isActiveWalkthrough ? 24 : 64
47
48
  }, id))
@@ -169,14 +169,16 @@ var leadingIcons = css.raw({
169
169
  gap: "2",
170
170
  height: rowLeadingHeight
171
171
  });
172
- var MultilineLabelSpan = styled("span", { base: css.raw({
172
+ /** Multiline label: preserves explicit newlines, wraps long lines, first line aligned with the badge. */
173
+ var multilineLabelText = css.raw({
173
174
  flex: "1",
174
175
  minWidth: "0",
175
176
  fontSize: "xs",
176
177
  lineHeight: rowLeadingHeight,
177
178
  whiteSpace: "pre-line",
178
179
  overflowWrap: "anywhere"
179
- }) });
180
+ });
181
+ var MultilineLabelSpan = styled("span", { base: multilineLabelText });
180
182
  var selectFlow = selectDiagramContext((s) => {
181
183
  const title = extractViewTitleFromPath(s.view.title ?? "Untitled");
182
184
  const description = s.view.description;
@@ -1,6 +1,6 @@
1
1
  import "../_virtual/_rolldown/runtime.js";
2
2
  import { useXYStoreApi } from "../hooks/useXYFlow.js";
3
- import { bezierControlPoints, nodeToRect as nodeToRect$1 } from "../utils/xyflow.js";
3
+ import { nodeToRect as nodeToRect$1 } from "../utils/xyflow.js";
4
4
  import { useDiagram } from "../hooks/safeContext.js";
5
5
  import { vector as vector$1 } from "../utils/index.js";
6
6
  import { useMemo, useRef } from "react";
@@ -139,7 +139,7 @@ function makeEdgeModifier(edge, anchor) {
139
139
  * Creates a modifier function that moves edge points when one of its nodes is moved.
140
140
  */
141
141
  function makeRelativeEdgeModifier(edge, movingRect, anchorNode, staticNode) {
142
- const controlPoints = edge.data.controlPoints ?? bezierControlPoints(edge.data.points);
142
+ const controlPoints = edge.data.controlPoints;
143
143
  const anchorV = vector$1(BBox$1.center(anchorNode));
144
144
  const staticV = vector$1(BBox$1.center(staticNode));
145
145
  const staticToAnchor = anchorV.subtract(staticV);
@@ -158,8 +158,13 @@ function makeRelativeEdgeModifier(edge, movingRect, anchorNode, staticNode) {
158
158
  };
159
159
  const d = vector$1(dx, dy);
160
160
  const relativePoint = (pt) => {
161
+ if (staticToAnchorLength === 0) return {
162
+ x: pt.x,
163
+ y: pt.y
164
+ };
161
165
  const point = vector$1(pt);
162
- const coeff = clamp(point.subtract(staticV).dot(staticToAnchor) / staticToAnchorLength ** 2, {
166
+ const projLength = point.subtract(staticV).dot(staticToAnchor);
167
+ const coeff = clamp(projLength / staticToAnchorLength ** 2, {
163
168
  min: -1,
164
169
  max: 1
165
170
  });
@@ -173,7 +178,17 @@ function makeRelativeEdgeModifier(edge, movingRect, anchorNode, staticNode) {
173
178
  id: edge.id,
174
179
  type: "replace",
175
180
  item: produce(current, (draft) => {
176
- draft.data.controlPoints = controlPoints.map(relativePoint);
181
+ if (controlPoints) draft.data.controlPoints = controlPoints.map(relativePoint);
182
+ else {
183
+ draft.data.points = map(edge.data.points, ([x, y]) => {
184
+ const point = relativePoint({
185
+ x,
186
+ y
187
+ });
188
+ return [point.x, point.y];
189
+ });
190
+ draft.data.controlPoints = controlPoints;
191
+ }
177
192
  if (edge.data.labelBBox) {
178
193
  draft.data.labelBBox ??= edge.data.labelBBox;
179
194
  const { x, y } = relativePoint(edge.data.labelBBox);
@@ -198,8 +198,10 @@ var SequenceViewLayouter = class {
198
198
  target.maxRow = Math.max(target.maxRow, step.to.row);
199
199
  const [left, right] = source.column <= target.column ? [source, target] : [target, source];
200
200
  const width = (step.label?.width ?? 100) + 50;
201
- if (Math.abs(target.column - source.column) <= 1) if (left !== right) this.constraint(left.centerX.plus(width), "<=", right.centerX);
202
- else this.constraint(left.centerX.plus(width), "<=", left.offset.right);
201
+ if (Math.abs(target.column - source.column) <= 1) {
202
+ if (left !== right) this.constraint(left.centerX.plus(width), "<=", right.centerX);
203
+ else this.constraint(left.centerX.plus(width), "<=", left.offset.right);
204
+ }
203
205
  let height = step.label?.height ? step.label.height + 50 + 24 : 100;
204
206
  height = Math.max(height, 100) + step.offset;
205
207
  this.ensureRow(step.from.row, height);
@@ -262,7 +264,6 @@ var SequenceViewLayouter = class {
262
264
  this.put(offset.bottom).before(y2, PADDING);
263
265
  }
264
266
  this.put(y2).before(bottom);
265
- break;
266
267
  }
267
268
  for (var col = from.column; col <= to.column; col++) {
268
269
  const offset = this.actorBox(col).offset;
@@ -400,7 +401,8 @@ var SequenceViewLayouter = class {
400
401
  }
401
402
  };
402
403
  const createSubflowArea = (subflow) => {
403
- const rect = rectFromSteps(selectSteps(subflow.id));
404
+ const steps = selectSteps(subflow.id);
405
+ const rect = rectFromSteps(steps);
404
406
  let space;
405
407
  switch (true) {
406
408
  case flowGuards.isAltOrTry(subflow):
@@ -420,9 +422,7 @@ var SequenceViewLayouter = class {
420
422
  bottom: 0
421
423
  };
422
424
  break;
423
- default:
424
- space = spacing;
425
- break;
425
+ default: space = spacing;
426
426
  }
427
427
  const area = {
428
428
  ...this.wrapAroundRect(rect, space),
@@ -507,9 +507,7 @@ var SequenceViewLayouter = class {
507
507
  this.constraint(left, "==", right, Strength.weak);
508
508
  break;
509
509
  case "<= 0":
510
- case ">= 0":
511
- this.constraint(left, "== 0", void 0, Strength.weak);
512
- break;
510
+ case ">= 0": this.constraint(left, "== 0", void 0, Strength.weak);
513
511
  }
514
512
  return constraint;
515
513
  }
@@ -84,7 +84,8 @@ var compare = compareNaturalHierarchically(VIEW_FOLDERS_SEPARATOR);
84
84
  var SearchResults = memo(() => {
85
85
  const likec4model = useLikeC4Model();
86
86
  const actor = useNavigationActor();
87
- const deferredSearchQuery = useDeferredValue(useSelector(actor.actorRef, selectSearchQuery));
87
+ const searchQuery = useSelector(actor.actorRef, selectSearchQuery);
88
+ const deferredSearchQuery = useDeferredValue(searchQuery);
88
89
  const isSearchByPath = deferredSearchQuery.includes(VIEW_FOLDERS_SEPARATOR);
89
90
  const highlight = isSearchByPath ? deferredSearchQuery.split(VIEW_FOLDERS_SEPARATOR) : deferredSearchQuery;
90
91
  const [found, setFound] = useState([]);
@@ -30,6 +30,7 @@ var selector = selectNavigationContext((context) => {
30
30
  var DetailsControls = (props) => {
31
31
  const [opened, setOpened] = useState(false);
32
32
  const data = useNavigationActorSelector(selector);
33
+ const portalProps = useMantinePortalProps();
33
34
  return /* @__PURE__ */ jsxs(Popover, {
34
35
  position: "bottom-end",
35
36
  shadow: "xl",
@@ -41,7 +42,7 @@ var DetailsControls = (props) => {
41
42
  offset: { mainAxis: 4 },
42
43
  opened,
43
44
  onChange: setOpened,
44
- ...useMantinePortalProps(),
45
+ ...portalProps,
45
46
  ...props,
46
47
  children: [/* @__PURE__ */ jsx(ViewDetailsCardTrigger, {
47
48
  linksCount: data.links.length,
@@ -9,10 +9,11 @@ import { jsx } from "react/jsx-runtime";
9
9
  //#region src/navigationpanel/editorpanel/ApplySemanticLayout.tsx
10
10
  var ApplySemanticLayout = memo(({ visible = true }) => {
11
11
  const diagram = useDiagram();
12
+ const isBusy = useEditorActorStateHasTag("ai-semantic-layout");
12
13
  return /* @__PURE__ */ jsx(Tooltip, {
13
14
  label: "Semantic Layout with AI",
14
15
  children: /* @__PURE__ */ jsx(PanelActionIcon, {
15
- loading: useEditorActorStateHasTag("ai-semantic-layout"),
16
+ loading: isBusy,
16
17
  initial: { display: visible ? "block" : "none" },
17
18
  animate: {
18
19
  display: visible ? "block" : "none",
@@ -154,8 +154,9 @@ var SpacingSliders = forwardRef(({ isVertical, nodeSep, rankSep, onChange }, _re
154
154
  let nodeSepValue = Math.round(value.x * MAX_SPACING);
155
155
  let rankSepValue = Math.round(value.y * MAX_SPACING);
156
156
  if (!isVertical) [nodeSepValue, rankSepValue] = [rankSepValue, nodeSepValue];
157
+ const mergedRef = useMergedRef(ref, _ref);
157
158
  return /* @__PURE__ */ jsxs(Box, {
158
- ref: useMergedRef(ref, _ref),
159
+ ref: mergedRef,
159
160
  className: spacingSliderBody,
160
161
  pt: "100%",
161
162
  children: [/* @__PURE__ */ jsx(Box, {
@@ -1,2 +1,4 @@
1
+ import { TooltipProps } from '@mantine/core';
1
2
  import { MemoExoticComponent } from 'react';
3
+ export declare function manualLayoutTooltipProps(isPopoverOpen: boolean): Pick<TooltipProps, 'opened'>;
2
4
  export declare const ManualLayoutToolsButton: MemoExoticComponent<() => import("react").JSX.Element>;
@@ -5,7 +5,7 @@ import { PanelActionIcon } from "../_common.js";
5
5
  import { Tooltip as Tooltip$1 } from "./_common.js";
6
6
  import { css } from "@likec4/styles/css";
7
7
  import { IconLayoutAlignBottom, IconLayoutAlignCenter, IconLayoutAlignLeft, IconLayoutAlignMiddle, IconLayoutAlignRight, IconLayoutAlignTop, IconLayoutBoardSplit, IconLayoutCollage, IconRouteOff } from "@tabler/icons-react";
8
- import { memo } from "react";
8
+ import { memo, useState } from "react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { Popover, PopoverDropdown, PopoverTarget, TooltipGroup } from "@mantine/core";
11
11
  import { hstack } from "@likec4/styles/patterns";
@@ -26,15 +26,23 @@ var Action = ({ label, icon, onClick }) => /* @__PURE__ */ jsx(Tooltip$1, {
26
26
  children: icon
27
27
  })
28
28
  });
29
+ function manualLayoutTooltipProps(isPopoverOpen) {
30
+ return isPopoverOpen ? { opened: false } : {};
31
+ }
29
32
  var ManualLayoutToolsButton = memo(() => {
30
33
  const diagram = useDiagram();
34
+ const portalProps = useMantinePortalProps();
35
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
31
36
  return /* @__PURE__ */ jsxs(Popover, {
37
+ opened: isPopoverOpen,
38
+ onChange: setIsPopoverOpen,
32
39
  position: "right",
33
40
  offset: { mainAxis: 12 },
34
41
  clickOutsideEvents: ["pointerdown"],
35
- ...useMantinePortalProps(),
42
+ ...portalProps,
36
43
  children: [/* @__PURE__ */ jsx(PopoverTarget, { children: /* @__PURE__ */ jsx(Tooltip$1, {
37
44
  label: "Manual layouting tools",
45
+ ...manualLayoutTooltipProps(isPopoverOpen),
38
46
  children: /* @__PURE__ */ jsx(PanelActionIcon, { children: /* @__PURE__ */ jsx(IconLayoutCollage, {}) })
39
47
  }) }), /* @__PURE__ */ jsx(PopoverDropdown, {
40
48
  className: hstack({
@@ -122,4 +130,4 @@ var ManualLayoutToolsButton = memo(() => {
122
130
  });
123
131
  ManualLayoutToolsButton.displayName = "ManualLayoutToolsButton";
124
132
  //#endregion
125
- export { ManualLayoutToolsButton };
133
+ export { ManualLayoutToolsButton, manualLayoutTooltipProps };