@likec4/diagram 1.28.0 → 1.29.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.
Files changed (162) hide show
  1. package/bundle/index.js +1040 -1190
  2. package/dist/LikeC4CustomColors.js +2 -2
  3. package/dist/LikeC4Diagram.js +2 -2
  4. package/dist/LikeC4ModelProvider.js +1 -1
  5. package/dist/_virtual/index.js +5 -0
  6. package/dist/_virtual/index2.js +3 -0
  7. package/dist/_virtual/react.production.js +3 -0
  8. package/dist/base/BaseXYFlow.js +5 -5
  9. package/dist/base/primitives/compound/CompoundActionButton.js +1 -1
  10. package/dist/base/primitives/compound/CompoundDetailsButton.js +1 -1
  11. package/dist/base/primitives/compound/CompoundNodeContainer.d.ts +1 -1
  12. package/dist/base/primitives/compound/CompoundNodeContainer.js +2 -2
  13. package/dist/base/primitives/customEdge.d.ts +1 -1
  14. package/dist/base/primitives/customEdge.js +3 -3
  15. package/dist/base/primitives/customNode.js +2 -2
  16. package/dist/base/primitives/edge/EdgeContainer.js +1 -1
  17. package/dist/base/primitives/edge/EdgeLabel.css.d.ts +3 -0
  18. package/dist/base/primitives/edge/EdgeLabel.css.js +3 -0
  19. package/dist/base/primitives/edge/EdgeLabel.d.ts +1 -1
  20. package/dist/base/primitives/edge/EdgeLabel.js +3 -2
  21. package/dist/base/primitives/edge/EdgeLabelContainer.js +1 -1
  22. package/dist/base/primitives/edge/EdgePath.d.ts +10 -42
  23. package/dist/base/primitives/edge/EdgePath.js +23 -16
  24. package/dist/base/primitives/edge/edge.css.js +1 -1
  25. package/dist/base/primitives/element/ElementActionButtons.js +1 -2
  26. package/dist/base/primitives/element/ElementDetailsButton.js +1 -2
  27. package/dist/base/primitives/element/ElementNodeContainer.js +3 -3
  28. package/dist/base/updateEdges.js +7 -2
  29. package/dist/base/updateNodes.js +11 -3
  30. package/dist/bundle/LikeC4View.js +3 -3
  31. package/dist/bundle/ReactLikeC4.js +1 -1
  32. package/dist/bundle/ShadowRoot.js +3 -3
  33. package/dist/bundle/styles.css.js +3 -3
  34. package/dist/context/ControlsCustomLayout.js +1 -1
  35. package/dist/context/DiagramEventHandlers.js +5 -5
  36. package/dist/context/DiagramFeatures.js +7 -7
  37. package/dist/context/EnsureMantine.js +2 -2
  38. package/dist/context/FramerMotionConfig.js +2 -2
  39. package/dist/context/IconRenderer.js +4 -4
  40. package/dist/context/ReduceGraphics.js +2 -2
  41. package/dist/context/RootContainer.js +5 -9
  42. package/dist/hooks/safeContext.d.ts +2 -5
  43. package/dist/hooks/safeContext.js +5 -4
  44. package/dist/hooks/useCurrentViewId.js +1 -1
  45. package/dist/hooks/useDiagram.d.ts +715 -150
  46. package/dist/hooks/useDiagram.js +2 -2
  47. package/dist/hooks/useMantinePortalProps.js +2 -2
  48. package/dist/hooks/useSetState.js +3 -3
  49. package/dist/hooks/useUpdateEffect.js +1 -1
  50. package/dist/hooks/useXYFlow.d.ts +2 -2
  51. package/dist/likec4diagram/DiagramUI.js +2 -2
  52. package/dist/likec4diagram/DiagramXYFlow.js +2 -2
  53. package/dist/likec4diagram/custom/edges/NotePopover.js +2 -2
  54. package/dist/likec4diagram/custom/edges/RelationshipEdge.d.ts +2 -2
  55. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +7 -7
  56. package/dist/likec4diagram/custom/edges/RelationshipsDropdownMenu.js +6 -6
  57. package/dist/likec4diagram/custom/index.d.ts +1 -2
  58. package/dist/likec4diagram/custom/nodes/toolbar/ColorButton.js +2 -2
  59. package/dist/likec4diagram/custom/nodes/toolbar/CompoundToolbar.js +3 -3
  60. package/dist/likec4diagram/custom/nodes/toolbar/_shared.js +2 -2
  61. package/dist/likec4diagram/ui/controls/ChangeAutoLayoutButton.js +4 -4
  62. package/dist/likec4diagram/ui/controls/Controls.js +9 -9
  63. package/dist/likec4diagram/ui/controls/NavigationButtons.js +1 -1
  64. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +1 -1
  65. package/dist/likec4diagram/ui/notation/NotationPanel.js +4 -4
  66. package/dist/likec4diagram/ui/search/ElementsColumn.js +88 -77
  67. package/dist/likec4diagram/ui/search/LikeC4Search.d.ts +2 -1
  68. package/dist/likec4diagram/ui/search/LikeC4Search.js +27 -24
  69. package/dist/likec4diagram/ui/search/SearchInput.js +1 -1
  70. package/dist/likec4diagram/ui/search/ViewsColum.js +3 -2
  71. package/dist/likec4diagram/ui/search/state.js +11 -9
  72. package/dist/likec4diagram/ui/walkthrough/DynamicViewWalkthrough.js +1 -1
  73. package/dist/likec4diagram/useLayoutConstraints.js +3 -3
  74. package/dist/likec4model/LikeC4ModelContext.js +2 -2
  75. package/dist/likec4model/useLikeC4ElementsTree.js +2 -2
  76. package/dist/likec4model/useLikeC4Model.js +2 -2
  77. package/dist/node_modules/.pnpm/react@19.0.0/node_modules/react/cjs/react.production.js +548 -0
  78. package/dist/node_modules/.pnpm/react@19.0.0/node_modules/react/index.js +15 -0
  79. package/dist/overlays/Overlays.js +6 -6
  80. package/dist/overlays/element-details/ElementDetails.js +1 -1
  81. package/dist/overlays/element-details/ElementDetailsCard.js +5 -5
  82. package/dist/overlays/element-details/TabPanelDeployments.js +7 -4
  83. package/dist/overlays/element-details/TabPanelRelationships.js +2 -2
  84. package/dist/overlays/element-details/TabPanelStructure.js +6 -3
  85. package/dist/overlays/element-details/actor.d.ts +2 -2
  86. package/dist/overlays/element-details/actorContext.js +3 -3
  87. package/dist/overlays/overlay/Overlay.js +8 -7
  88. package/dist/overlays/relationship-details/RelationshipDetails.d.ts +1 -1
  89. package/dist/overlays/relationship-details/RelationshipDetails.js +28 -23
  90. package/dist/overlays/relationship-details/SelectEdge.js +3 -6
  91. package/dist/overlays/relationship-details/actor.d.ts +4 -1
  92. package/dist/overlays/relationship-details/custom/edgeTypes.d.ts +1 -1
  93. package/dist/overlays/relationship-details/custom/edgeTypes.js +1 -1
  94. package/dist/overlays/relationship-details/hooks.d.ts +4 -1
  95. package/dist/overlays/relationship-details/hooks.js +4 -4
  96. package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +1 -1
  97. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +9 -11
  98. package/dist/overlays/relationships-browser/SelectElement.js +8 -5
  99. package/dist/overlays/relationships-browser/actor.d.ts +2 -1
  100. package/dist/overlays/relationships-browser/actor.js +6 -12
  101. package/dist/overlays/relationships-browser/const.d.ts +2 -1
  102. package/dist/overlays/relationships-browser/const.js +6 -1
  103. package/dist/overlays/relationships-browser/custom/edgeTypes.d.ts +1 -1
  104. package/dist/overlays/relationships-browser/custom/edgeTypes.js +3 -4
  105. package/dist/overlays/relationships-browser/custom/nodeTypes.js +1 -1
  106. package/dist/overlays/relationships-browser/hooks.d.ts +2 -1
  107. package/dist/overlays/relationships-browser/hooks.js +4 -4
  108. package/dist/overlays/relationships-browser/layout.js +2 -2
  109. package/dist/overlays/relationships-browser/useViewToNodesEdges.js +1 -2
  110. package/dist/state/DiagramActorProvider.js +7 -5
  111. package/dist/state/diagram-machine.d.ts +719 -146
  112. package/dist/state/diagram-machine.js +51 -10
  113. package/dist/styles.css.js +1 -1
  114. package/package.json +20 -16
  115. package/src/LikeC4Diagram.tsx +0 -1
  116. package/src/base/BaseXYFlow.tsx +2 -2
  117. package/src/base/primitives/compound/CompoundActionButton.tsx +1 -1
  118. package/src/base/primitives/compound/CompoundDetailsButton.tsx +1 -1
  119. package/src/base/primitives/compound/CompoundNodeContainer.tsx +1 -1
  120. package/src/base/primitives/customEdge.ts +1 -0
  121. package/src/base/primitives/edge/EdgeLabel.css.ts +3 -0
  122. package/src/base/primitives/edge/EdgeLabel.tsx +2 -1
  123. package/src/base/primitives/edge/EdgePath.tsx +22 -14
  124. package/src/base/primitives/edge/edge.css.ts +1 -1
  125. package/src/base/primitives/element/ElementActionButtons.tsx +2 -2
  126. package/src/base/primitives/element/ElementDetailsButton.tsx +2 -2
  127. package/src/base/primitives/element/ElementNodeContainer.tsx +1 -1
  128. package/src/base/updateEdges.ts +6 -9
  129. package/src/base/updateNodes.ts +10 -14
  130. package/src/context/FramerMotionConfig.tsx +1 -1
  131. package/src/context/RootContainer.tsx +0 -4
  132. package/src/hooks/safeContext.tsx +3 -1
  133. package/src/likec4diagram/custom/edges/RelationshipEdge.tsx +2 -2
  134. package/src/likec4diagram/custom/edges/RelationshipsDropdownMenu.tsx +1 -1
  135. package/src/likec4diagram/ui/controls/Controls.tsx +56 -58
  136. package/src/likec4diagram/ui/controls/NavigationButtons.tsx +1 -1
  137. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +1 -1
  138. package/src/likec4diagram/ui/notation/NotationPanel.tsx +1 -1
  139. package/src/likec4diagram/ui/search/ElementsColumn.tsx +94 -71
  140. package/src/likec4diagram/ui/search/LikeC4Search.tsx +48 -55
  141. package/src/likec4diagram/ui/search/ViewsColum.tsx +2 -2
  142. package/src/likec4diagram/ui/search/state.ts +8 -6
  143. package/src/likec4diagram/ui/walkthrough/DynamicViewWalkthrough.tsx +1 -1
  144. package/src/overlays/Overlays.tsx +2 -2
  145. package/src/overlays/element-details/ElementDetails.tsx +2 -2
  146. package/src/overlays/element-details/ElementDetailsCard.tsx +1 -1
  147. package/src/overlays/element-details/TabPanelDeployments.tsx +2 -0
  148. package/src/overlays/element-details/TabPanelStructure.tsx +2 -1
  149. package/src/overlays/overlay/Overlay.tsx +2 -1
  150. package/src/overlays/relationship-details/RelationshipDetails.tsx +25 -16
  151. package/src/overlays/relationship-details/SelectEdge.tsx +0 -14
  152. package/src/overlays/relationship-details/actor.ts +2 -1
  153. package/src/overlays/relationship-details/custom/edgeTypes.tsx +1 -1
  154. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +1 -3
  155. package/src/overlays/relationships-browser/SelectElement.tsx +2 -0
  156. package/src/overlays/relationships-browser/actor.ts +8 -13
  157. package/src/overlays/relationships-browser/const.ts +8 -1
  158. package/src/overlays/relationships-browser/custom/edgeTypes.tsx +2 -3
  159. package/src/overlays/relationships-browser/custom/nodeTypes.tsx +1 -1
  160. package/src/overlays/relationships-browser/useViewToNodesEdges.ts +1 -2
  161. package/src/state/DiagramActorProvider.tsx +7 -4
  162. package/src/state/diagram-machine.ts +50 -8
@@ -1,4 +1,4 @@
1
- import { type DiagramEdge, type DiagramView, ifind, invariant, isAncestor, nonNullable } from '@likec4/core'
1
+ import { type DiagramEdge, type DiagramView, invariant, isAncestor, nonNullable } from '@likec4/core'
2
2
  import { ActionIcon, Group } from '@mantine/core'
3
3
  import { useCallbackRef, useStateHistory } from '@mantine/hooks'
4
4
  import { IconChevronLeft, IconChevronRight, IconX } from '@tabler/icons-react'
@@ -6,9 +6,9 @@ import { useSelector } from '@xstate/react'
6
6
  import { Panel, ReactFlowProvider, useReactFlow, useStoreApi } from '@xyflow/react'
7
7
  import clsx from 'clsx'
8
8
  import { deepEqual } from 'fast-equals'
9
- import { AnimatePresence, LayoutGroup, m } from 'framer-motion'
9
+ import { AnimatePresence, LayoutGroup, m } from 'motion/react'
10
10
  import { memo, useEffect, useMemo, useRef } from 'react'
11
- import { first } from 'remeda'
11
+ import { find, isTruthy } from 'remeda'
12
12
  import { BaseXYFlow } from '../../base/BaseXYFlow'
13
13
  import { useLikeC4Model } from '../../likec4model/useLikeC4Model'
14
14
  import type { RelationshipDetailsTypes, RelationshipDetailsTypes as Types } from './_types'
@@ -66,10 +66,10 @@ export function RelationshipDetails({ actorRef }: RelationshipDetailsProps) {
66
66
  <ReactFlowProvider {...initialRef.current}>
67
67
  <LayoutGroup id={actorRef.sessionId} inherit={false}>
68
68
  <AnimatePresence>
69
- <RelationshipDetailsInner />
69
+ <RelationshipDetailsInner key="xyflow" />
70
+ <SyncRelationshipDetailsXYFlow key="sync" />
70
71
  </AnimatePresence>
71
72
  </LayoutGroup>
72
- <SyncRelationshipDetailsXYFlow />
73
73
  </ReactFlowProvider>
74
74
  </RelationshipDetailsActorContext.Provider>
75
75
  )
@@ -80,22 +80,24 @@ const selectSubject = (state: RelationshipDetailsSnapshot) => ({
80
80
  viewId: state.context.viewId,
81
81
  })
82
82
 
83
- function SyncRelationshipDetailsXYFlow() {
83
+ const SyncRelationshipDetailsXYFlow = memo(() => {
84
84
  const actor = useRelationshipDetailsActor()
85
85
  const subject = useSelector(actor, selectSubject, deepEqual)
86
86
  const likec4model = useLikeC4Model(true)
87
87
  const view = likec4model.findView(subject.viewId) ?? null
88
88
  const data = useMemo(() => {
89
89
  let data: RelationshipDetailsViewData
90
- if ('edgeId' in subject) {
90
+ if ('edgeId' in subject && isTruthy(subject.edgeId)) {
91
91
  invariant(view, `view ${subject.viewId} not found`)
92
92
  const edge = nonNullable(view.findEdge(subject.edgeId), `edge ${subject.edgeId} not found in ${subject.viewId}`)
93
93
  data = computeEdgeDetailsViewData([edge.id], view)
94
- } else {
94
+ } else if (!!subject.source && !!subject.target) {
95
95
  data = computeRelationshipDetailsViewData({
96
96
  source: likec4model.element(subject.source),
97
97
  target: likec4model.element(subject.target),
98
98
  })
99
+ } else {
100
+ return null
99
101
  }
100
102
  return layoutRelationshipDetails(data, view)
101
103
  }, [
@@ -114,11 +116,13 @@ function SyncRelationshipDetailsXYFlow() {
114
116
  }, [store, instance.viewportInitialized, actor])
115
117
 
116
118
  useEffect(() => {
117
- actor.send({ type: 'update.layoutData', data })
119
+ if (data !== null) {
120
+ actor.send({ type: 'update.layoutData', data })
121
+ }
118
122
  }, [data, actor])
119
123
 
120
124
  return null
121
- }
125
+ })
122
126
 
123
127
  const selector = ({ context }: RelationshipDetailsSnapshot) => ({
124
128
  // subject: context.subject,
@@ -227,13 +231,18 @@ const TopLeftPanel = memo(() => {
227
231
  return null
228
232
  }
229
233
 
230
- let edge = ('edgeId' in subject)
231
- ? view.findEdge(subject.edgeId)
232
- : ifind(view.edges(), (e) => e.source.element?.id === subject.source && e.target.element?.id === subject.target)
233
- ?? ifind(view.edges(), (e) => {
234
- return (e.source.element?.id === subject.source || isAncestor(e.source.element?.id ?? '--', subject.source)) &&
235
- (e.target.element?.id === subject.target || isAncestor(e.target.element?.id ?? '', subject.target))
234
+ const edges = [...view.edges()]
235
+
236
+ let edge = ('edgeId' in subject && isTruthy(subject.edgeId))
237
+ ? edges.find(e => e.id === subject.edgeId)
238
+ : (
239
+ find(edges, (e) => e.source.element?.id === subject.source && e.target.element?.id === subject.target)
240
+ || find(edges, (e) => {
241
+ return (e.source.element?.id === subject.source ||
242
+ isAncestor(e.source.element?.id ?? '--', subject.source ?? '__')) &&
243
+ (e.target.element?.id === subject.target || isAncestor(e.target.element?.id ?? '', subject.target ?? '__'))
236
244
  })
245
+ )
237
246
  if (!edge) {
238
247
  return null
239
248
  }
@@ -26,20 +26,6 @@ export const SelectEdge = ({
26
26
  }) => {
27
27
  const browser = useRelationshipDetails()
28
28
  const viewport = useRef<HTMLDivElement>(null)
29
- const portalProps = useMantinePortalProps()
30
- // const data = useLikeC4ElementsTree(scope === 'view' ? viewId : undefined)
31
- // const tree = useTree({
32
- // multiple: false
33
- // })
34
-
35
- // useEffect(() => {
36
- // ancestorsFqn(subject.id).reverse().forEach(id => {
37
- // tree.expand(id)
38
- // })
39
- // tree.select(subject.id)
40
- // }, [subject.id])
41
-
42
- const theme = useComputedColorScheme()
43
29
 
44
30
  const edgeSource = view.nodes.find(n => n.id === edge.source)
45
31
  const edgeTarget = view.nodes.find(n => n.id === edge.target)
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  type BBox,
3
- type DiagramView,
4
3
  type EdgeId,
5
4
  type ExclusiveUnion,
6
5
  type Fqn,
@@ -55,6 +54,8 @@ export type Input = ExclusiveUnion<{
55
54
 
56
55
  type Subject = {
57
56
  edgeId: EdgeId
57
+ source?: never
58
+ target?: never
58
59
  // relationships: null
59
60
  } | {
60
61
  source: Fqn
@@ -22,7 +22,7 @@ export const RelationshipEdge = customEdge<RelationshipDetailsTypes.EdgeData>((p
22
22
  const diagram = useDiagram()
23
23
  return (
24
24
  <EdgeContainer {...props}>
25
- <EdgePath {...props} svgPath={svgPath} />
25
+ <EdgePath edgeProps={props} svgPath={svgPath} />
26
26
  <EdgeLabelContainer
27
27
  edgeProps={props}
28
28
  labelPosition={{
@@ -4,13 +4,12 @@ import { useCallbackRef, useStateHistory } from '@mantine/hooks'
4
4
  import { IconChevronLeft, IconChevronRight, IconX } from '@tabler/icons-react'
5
5
  import { Panel, ReactFlowProvider, useReactFlow, useStoreApi } from '@xyflow/react'
6
6
  import { shallowEqual } from 'fast-equals'
7
- import { AnimatePresence, LayoutGroup, m } from 'framer-motion'
7
+ import { AnimatePresence, LayoutGroup, m } from 'motion/react'
8
8
  import { memo, useEffect, useRef } from 'react'
9
9
  import type { SnapshotFrom } from 'xstate'
10
10
  import { BaseXYFlow } from '../../base/BaseXYFlow'
11
11
  import type { RelationshipsBrowserTypes, RelationshipsBrowserTypes as Types } from './_types'
12
12
  import type { RelationshipsBrowserActorRef } from './actor'
13
- import { ViewPadding } from './const'
14
13
  import { CompoundNode, ElementNode, EmptyNode, RelationshipEdge } from './custom'
15
14
  import {
16
15
  RelationshipsBrowserActorContext,
@@ -101,7 +100,6 @@ const RelationshipsBrowserXYFlow = memo(() => {
101
100
  nodeTypes={nodeTypes}
102
101
  edgeTypes={edgeTypes}
103
102
  fitView={false}
104
- fitViewPadding={ViewPadding}
105
103
  onNodeClick={useCallbackRef((e, node) => {
106
104
  browser.send({ type: 'xyflow.nodeClick', node })
107
105
  })}
@@ -33,6 +33,7 @@ const selector2 = (state: RelationshipsBrowserSnapshot) => {
33
33
  })
34
34
  }
35
35
 
36
+ const setHoveredNode = () => {}
36
37
  export const SelectElement = memo(() => {
37
38
  const browser = useRelationshipsBrowser()
38
39
  const {
@@ -52,6 +53,7 @@ export const SelectElement = memo(() => {
52
53
  const tree = useTree({
53
54
  multiple: false,
54
55
  })
56
+ tree.setHoveredNode = setHoveredNode
55
57
 
56
58
  useEffect(() => {
57
59
  ancestorsFqn(subjectId).reverse().forEach(id => {
@@ -7,7 +7,7 @@ import {
7
7
  applyNodeChanges,
8
8
  useStoreApi,
9
9
  } from '@xyflow/react'
10
- import { type InternalNodeUpdate, getNodeDimensions, getViewportForBounds } from '@xyflow/system'
10
+ import { type InternalNodeUpdate, getViewportForBounds } from '@xyflow/system'
11
11
  import { isNullish, omit, prop } from 'remeda'
12
12
  import {
13
13
  type ActorLogicFrom,
@@ -109,8 +109,8 @@ export const layouter = fromPromise<{
109
109
  const currentSubjectCenter = centerXYInternalNode(currentSubjectInternalNode)
110
110
 
111
111
  // Move to center of existing node
112
- const existingInternalNode = xyflow.getInternalNode(existingNode.id)!
113
- const existingDimensions = getNodeDimensions(existingInternalNode)
112
+ // const existingInternalNode = xyflow.getInternalNode(existingNode.id)!
113
+ // const existingDimensions = getNodeDimensions(existingInternalNode)
114
114
 
115
115
  // Dim all nodes except the existing node
116
116
  // Hide nested nodes
@@ -146,8 +146,8 @@ export const layouter = fromPromise<{
146
146
  return {
147
147
  ...omit(n, ['parentId']),
148
148
  position: {
149
- x: currentSubjectCenter.x - existingDimensions.width / 2,
150
- y: currentSubjectCenter.y - existingDimensions.height / 2,
149
+ x: currentSubjectCenter.x - n.initialWidth / 2,
150
+ y: currentSubjectCenter.y - n.initialHeight / 2,
151
151
  },
152
152
  zIndex: ZIndexes.Max,
153
153
  hidden: false,
@@ -164,19 +164,14 @@ export const layouter = fromPromise<{
164
164
  xyedges: [],
165
165
  })
166
166
 
167
- // Pick the smaller zoom level
168
- zoom = Math.min(
169
- zoom,
170
- nextviewport.zoom,
171
- )
172
167
  // allow framer to render
173
- await delay(175)
168
+ await delay(120)
174
169
  next.xynodes = next.xynodes.map(Base.setDimmed(false))
175
170
 
176
171
  if (signal.aborted) {
177
172
  return updateXYData()
178
173
  }
179
- await xyflow.setCenter(currentSubjectCenter.x, currentSubjectCenter.y, { zoom, duration: 350 })
174
+ await xyflow.setCenter(currentSubjectCenter.x, currentSubjectCenter.y, { zoom, duration: 300 })
180
175
  await xyflow.setCenter(nextSubjectCenter.x, nextSubjectCenter.y, { zoom })
181
176
  return updateXYData()
182
177
  })
@@ -551,7 +546,7 @@ export const relationshipsBrowserLogic = setup({
551
546
  xyedges: event.output.xyedges,
552
547
  navigateFromNode: null,
553
548
  })
554
- enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 50 })
549
+ enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 50 })
555
550
  for (let i = 0; i < 6; i++) {
556
551
  enqueue.raise({ type: 'xyflow.updateNodeInternals' }, { delay: 100 + i * 100 })
557
552
  }
@@ -1 +1,8 @@
1
- export const ViewPadding = 0.1
1
+ import type { Padding } from '@xyflow/system'
2
+
3
+ export const ViewPadding: Padding = {
4
+ top: '40px',
5
+ bottom: '16px',
6
+ left: '16px',
7
+ right: '16px',
8
+ }
@@ -77,10 +77,9 @@ export const RelationshipEdge = customEdge<RelationshipsBrowserTypes.EdgeData>((
77
77
  }
78
78
 
79
79
  return (
80
- <EdgeContainer key={id} {...edgeProps}>
80
+ <EdgeContainer {...edgeProps}>
81
81
  <EdgePath
82
- key={id}
83
- {...edgeProps}
82
+ edgeProps={edgeProps}
84
83
  svgPath={svgPath}
85
84
  {...markOrange && {
86
85
  strokeWidth: 5,
@@ -1,6 +1,6 @@
1
1
  import { Handle } from '@xyflow/react'
2
2
  import { Position } from '@xyflow/system'
3
- import { m } from 'framer-motion'
3
+ import * as m from 'motion/react-m'
4
4
  import type { NodeProps } from '../../../base'
5
5
  import {
6
6
  CompoundNodeContainer,
@@ -70,7 +70,6 @@ export function viewToNodesEdge(
70
70
  draggable: false,
71
71
  selectable: true,
72
72
  focusable: true,
73
- deletable: false,
74
73
  position,
75
74
  zIndex: isCompound ? ZIndexes.Compound : ZIndexes.Element,
76
75
  style: {
@@ -181,7 +180,7 @@ export function viewToNodesEdge(
181
180
  zIndex: ZIndexes.Edge,
182
181
  // selectable: selectable,
183
182
  // hidden: !visiblePredicate(edge),
184
- deletable: false,
183
+ // deletable: false,
185
184
  data: {
186
185
  sourceFqn: edge.sourceFqn,
187
186
  targetFqn: edge.targetFqn,
@@ -6,7 +6,7 @@ import { shallowEqual } from 'fast-equals'
6
6
  import { type PropsWithChildren, useEffect } from 'react'
7
7
  import { useDiagramEventHandlersRef } from '../context/DiagramEventHandlers'
8
8
  import { DiagramFeatures, useEnabledFeatures } from '../context/DiagramFeatures'
9
- import { DiagramActorSafeContext } from '../hooks/safeContext'
9
+ import { DiagramActorContextProvider } from '../hooks/safeContext'
10
10
  import { useUpdateEffect } from '../hooks/useUpdateEffect'
11
11
  import type { Types } from '../likec4diagram/types'
12
12
  import { useViewToNodesEdges } from '../likec4diagram/useViewToNodesEdges'
@@ -34,7 +34,10 @@ export function DiagramActorProvider({
34
34
  diagramMachine.provide({
35
35
  actions: {
36
36
  'trigger:NavigateTo': ((_, { viewId }) => {
37
- handlersRef.current.onNavigateTo?.(viewId as ViewId)
37
+ // this delay allows tap animations to finish
38
+ setTimeout(() => {
39
+ handlersRef.current.onNavigateTo?.(viewId as ViewId)
40
+ }, 30)
38
41
  }),
39
42
 
40
43
  'trigger:OnChange': ((_, params) => {
@@ -89,10 +92,10 @@ export function DiagramActorProvider({
89
92
  const toggledFeatures = useSelector(actorRef, selectToggledFeatures, shallowEqual)
90
93
 
91
94
  return (
92
- <DiagramActorSafeContext value={actorRef}>
95
+ <DiagramActorContextProvider value={actorRef}>
93
96
  <DiagramFeatures overrides={toggledFeatures}>
94
97
  {children}
95
98
  </DiagramFeatures>
96
- </DiagramActorSafeContext>
99
+ </DiagramActorContextProvider>
97
100
  )
98
101
  }
@@ -13,6 +13,7 @@ import {
13
13
  getBBoxCenter,
14
14
  getParallelStepsPrefix,
15
15
  invariant,
16
+ isStepEdgeId,
16
17
  nonexhaustive,
17
18
  nonNullable,
18
19
  } from '@likec4/core'
@@ -24,7 +25,7 @@ import {
24
25
  useStoreApi,
25
26
  } from '@xyflow/react'
26
27
  import { type EdgeChange, type NodeChange, type Rect, type Viewport, nodeToRect } from '@xyflow/system'
27
- import { clamp, first, hasAtLeast, prop, reduce } from 'remeda'
28
+ import { clamp, first, hasAtLeast, prop } from 'remeda'
28
29
  import {
29
30
  type ActorLogicFrom,
30
31
  type ActorRef,
@@ -199,6 +200,7 @@ export const diagramMachine = setup({
199
200
  'enabled: FitView': ({ context }) => context.features.enableFitView,
200
201
  'enabled: FocusMode': ({ context }) => context.features.enableFocusMode,
201
202
  'enabled: Readonly': ({ context }) => context.features.enableReadOnly,
203
+ 'enabled: RelationshipDetails': ({ context }) => context.features.enableRelationshipDetails,
202
204
  'not readonly': ({ context }) => !context.features.enableReadOnly,
203
205
  'is dynamic view': ({ context }) => context.view.__ === 'dynamic',
204
206
  'is another view': ({ context, event }) => {
@@ -227,6 +229,10 @@ export const diagramMachine = setup({
227
229
  assertEvent(event, 'xyflow.nodeClick')
228
230
  return context.xyedges.some(e => e.source === event.node.id || e.target === event.node.id)
229
231
  },
232
+ 'click: selected edge': ({ event }) => {
233
+ assertEvent(event, 'xyflow.edgeClick')
234
+ return event.edge.selected === true || event.edge.data.active === true
235
+ },
230
236
  },
231
237
  actions: {
232
238
  'trigger:NavigateTo': (_, _params: { viewId: ViewId }) => {
@@ -529,7 +535,6 @@ export const diagramMachine = setup({
529
535
  type: 'xyflow:fitDiagram',
530
536
  params: { duration: 0 },
531
537
  },
532
- 'startSyncLayout',
533
538
  assign(({ context }) => ({
534
539
  navigationHistory: {
535
540
  currentIndex: 0,
@@ -540,6 +545,7 @@ export const diagramMachine = setup({
540
545
  }],
541
546
  },
542
547
  })),
548
+ 'startSyncLayout',
543
549
  ],
544
550
  target: 'ready',
545
551
  }, {
@@ -767,6 +773,26 @@ export const diagramMachine = setup({
767
773
  }),
768
774
  target: 'focused',
769
775
  },
776
+ 'xyflow.edgeClick': [{
777
+ guard: and([
778
+ 'is dynamic view',
779
+ 'click: selected edge',
780
+ ]) as any,
781
+ actions: raise(({ event }) => ({
782
+ type: 'walkthrough.start',
783
+ stepId: event.edge.id as StepEdgeId,
784
+ })),
785
+ }, {
786
+ guard: and([
787
+ 'enabled: RelationshipDetails',
788
+ 'click: selected edge',
789
+ ]) as any,
790
+ actions: sendTo(({ system }) => typedSystem(system).overlaysActorRef!, ({ context, event }) => ({
791
+ type: 'open.relationshipDetails',
792
+ viewId: context.view.id,
793
+ edgeId: event.edge.id as EdgeId,
794
+ })),
795
+ }],
770
796
  },
771
797
  },
772
798
  focused: {
@@ -779,18 +805,18 @@ export const diagramMachine = setup({
779
805
  spawnChild('hotkeyActorLogic', { id: 'hotkey' }),
780
806
  'xyflow:fitFocusedBounds',
781
807
  ],
782
- exit: enqueueActions(({ enqueue, context }) => {
808
+ exit: enqueueActions(({ enqueue, context, event }) => {
783
809
  enqueue.stopChild('hotkey')
784
810
  if (context.viewportBeforeFocus) {
785
811
  enqueue({ type: 'xyflow:setViewport', params: { viewport: context.viewportBeforeFocus } })
786
812
  } else {
787
813
  enqueue({ type: 'xyflow:fitDiagram' })
788
814
  }
789
- enqueue.assign((s) => ({
790
- ...unfocusNodesEdges(s),
815
+ enqueue.assign({
816
+ ...unfocusNodesEdges({ context, event }),
791
817
  viewportBeforeFocus: null,
792
818
  focusedNode: null,
793
- }))
819
+ })
794
820
  }),
795
821
  on: {
796
822
  'xyflow.nodeClick': [
@@ -885,6 +911,23 @@ export const diagramMachine = setup({
885
911
  enqueue('xyflow:fitFocusedBounds')
886
912
  }),
887
913
  },
914
+ 'xyflow.edgeClick': {
915
+ actions: [
916
+ assign(({ event, context }) => {
917
+ if (!isStepEdgeId(event.edge.id) || event.edge.id === context.activeWalkthrough?.stepId) {
918
+ return {}
919
+ }
920
+ return {
921
+ activeWalkthrough: {
922
+ stepId: event.edge.id,
923
+ parallelPrefix: getParallelStepsPrefix(event.edge.id),
924
+ },
925
+ }
926
+ }),
927
+ assign(updateActiveWalkthrough),
928
+ 'xyflow:fitFocusedBounds',
929
+ ],
930
+ },
888
931
  'notations.unhighlight': {
889
932
  actions: assign(s => ({
890
933
  ...updateActiveWalkthrough(s),
@@ -947,13 +990,11 @@ export const diagramMachine = setup({
947
990
  },
948
991
  },
949
992
  })
950
- enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 80 })
951
993
  } else {
952
994
  enqueue({
953
995
  type: 'xyflow:setViewportCenter',
954
996
  params: getBBoxCenter(event.view.bounds),
955
997
  })
956
- enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
957
998
  }
958
999
  enqueue.assign(updateNavigationHistory)
959
1000
  enqueue.assign({
@@ -961,6 +1002,7 @@ export const diagramMachine = setup({
961
1002
  lastOnNavigate: null,
962
1003
  })
963
1004
  enqueue('startSyncLayout')
1005
+ enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 25 })
964
1006
  }),
965
1007
  target: '#idle',
966
1008
  },