@likec4/diagram 1.23.1 → 1.24.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.
Files changed (118) hide show
  1. package/dist/LikeC4Diagram.css.d.ts +5 -0
  2. package/dist/LikeC4Diagram.css.js +1 -0
  3. package/dist/LikeC4Diagram.d.ts +1 -1
  4. package/dist/LikeC4Diagram.js +6 -2
  5. package/dist/LikeC4Diagram.props.d.ts +6 -0
  6. package/dist/StaticLikeC4Diagram.js +2 -1
  7. package/dist/base/BaseXYFlow.d.ts +1 -1
  8. package/dist/base/BaseXYFlow.js +12 -3
  9. package/dist/base/const.d.ts +1 -1
  10. package/dist/base/const.js +1 -1
  11. package/dist/base/primitives/compound/CompoundActionButton.css.js +2 -0
  12. package/dist/base/primitives/compound/CompoundDetailsButton.css.js +2 -0
  13. package/dist/base/primitives/compound/CompoundNodeContainer.css.d.ts +0 -1
  14. package/dist/base/primitives/compound/CompoundNodeContainer.css.js +4 -4
  15. package/dist/base/primitives/compound/CompoundNodeContainer.js +35 -22
  16. package/dist/base/primitives/compound/CompoundTitle.css.js +3 -1
  17. package/dist/base/primitives/customNode.js +1 -1
  18. package/dist/base/primitives/edge/EdgeContainer.js +5 -4
  19. package/dist/base/primitives/edge/EdgeLabel.css.d.ts +0 -1
  20. package/dist/base/primitives/edge/EdgeLabel.css.js +2 -5
  21. package/dist/base/primitives/edge/EdgeLabel.js +5 -5
  22. package/dist/base/primitives/edge/edge.css.d.ts +1 -1
  23. package/dist/base/primitives/edge/edge.css.js +4 -5
  24. package/dist/base/primitives/element/ElementActionButtons.css.js +2 -0
  25. package/dist/base/primitives/element/ElementActionButtons.js +4 -3
  26. package/dist/base/primitives/element/ElementDetailsButton.css.js +2 -0
  27. package/dist/base/primitives/element/ElementNodeContainer.css.js +2 -0
  28. package/dist/base/primitives/element/ElementNodeContainer.js +3 -1
  29. package/dist/base/primitives/element/ElementShape.css.js +4 -2
  30. package/dist/base/primitives/element/ElementShape.js +2 -3
  31. package/dist/base/primitives/element/ElementTitle.css.js +2 -0
  32. package/dist/base/primitives/element/styles.css.js +2 -0
  33. package/dist/components/SearchControl.css.js +2 -0
  34. package/dist/context/FramerMotionConfig.d.ts +9 -1
  35. package/dist/context/FramerMotionConfig.js +5 -2
  36. package/dist/context/RootContainer.js +13 -1
  37. package/dist/diagram.css +1 -1
  38. package/dist/globals.css.d.ts +2 -0
  39. package/dist/hooks/safeContext.js +1 -0
  40. package/dist/hooks/useDiagram.d.ts +415 -693
  41. package/dist/hooks/useDiagram.js +1 -0
  42. package/dist/hooks/useIsReducedGraphics.d.ts +4 -0
  43. package/dist/hooks/useIsReducedGraphics.js +8 -0
  44. package/dist/hooks/useXYFlow.js +1 -1
  45. package/dist/likec4diagram/DiagramUI.d.ts +2 -1
  46. package/dist/likec4diagram/DiagramUI.js +5 -3
  47. package/dist/likec4diagram/custom/nodes/nodeTypes.js +2 -2
  48. package/dist/likec4diagram/ui/controls/styles.css.js +2 -0
  49. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.css.js +2 -0
  50. package/dist/likec4diagram/ui/diagram-title/DiagramTitlePanel.js +3 -4
  51. package/dist/likec4diagram/ui/search/LikeC4Search.css.js +2 -0
  52. package/dist/overlays/element-details/ElementDetailsCard.js +12 -8
  53. package/dist/overlays/element-details/actor.d.ts +9084 -2569
  54. package/dist/overlays/overlay/Overlay.js +6 -3
  55. package/dist/overlays/relationship-details/RelationshipDetails.js +31 -17
  56. package/dist/overlays/relationship-details/actor.d.ts +228 -3
  57. package/dist/overlays/relationship-details/actor.js +86 -14
  58. package/dist/overlays/relationship-details/hooks.d.ts +3 -0
  59. package/dist/overlays/relationship-details/hooks.js +6 -2
  60. package/dist/overlays/relationships-browser/-useRelationshipsView.js +12 -3
  61. package/dist/overlays/relationships-browser/RelationshipsBrowser.js +12 -3
  62. package/dist/overlays/relationships-browser/actor.d.ts +271 -54
  63. package/dist/overlays/relationships-browser/actor.js +86 -23
  64. package/dist/state/DiagramActorProvider.js +12 -2
  65. package/dist/state/diagram-machine.d.ts +426 -694
  66. package/dist/state/diagram-machine.js +89 -68
  67. package/dist/state/persistence.d.ts +5 -0
  68. package/dist/state/persistence.js +23 -0
  69. package/package.json +9 -9
  70. package/src/LikeC4Diagram.css.ts +13 -5
  71. package/src/LikeC4Diagram.props.ts +7 -0
  72. package/src/LikeC4Diagram.tsx +33 -24
  73. package/src/StaticLikeC4Diagram.tsx +2 -1
  74. package/src/base/BaseXYFlow.tsx +14 -9
  75. package/src/base/const.ts +1 -1
  76. package/src/base/primitives/compound/CompoundActionButton.css.ts +6 -0
  77. package/src/base/primitives/compound/CompoundDetailsButton.css.ts +4 -0
  78. package/src/base/primitives/compound/CompoundNodeContainer.css.ts +41 -59
  79. package/src/base/primitives/compound/CompoundNodeContainer.tsx +42 -22
  80. package/src/base/primitives/compound/CompoundTitle.css.ts +14 -13
  81. package/src/base/primitives/customNode.ts +3 -2
  82. package/src/base/primitives/edge/EdgeContainer.tsx +4 -3
  83. package/src/base/primitives/edge/EdgeLabel.css.ts +17 -11
  84. package/src/base/primitives/edge/EdgeLabel.tsx +4 -4
  85. package/src/base/primitives/edge/edge.css.ts +47 -27
  86. package/src/base/primitives/element/ElementActionButtons.css.ts +4 -0
  87. package/src/base/primitives/element/ElementActionButtons.tsx +4 -2
  88. package/src/base/primitives/element/ElementDetailsButton.css.ts +6 -8
  89. package/src/base/primitives/element/ElementNodeContainer.css.ts +18 -21
  90. package/src/base/primitives/element/ElementNodeContainer.tsx +3 -1
  91. package/src/base/primitives/element/ElementShape.css.ts +10 -6
  92. package/src/base/primitives/element/ElementShape.tsx +2 -2
  93. package/src/components/SearchControl.css.ts +11 -4
  94. package/src/context/FramerMotionConfig.tsx +13 -2
  95. package/src/context/RootContainer.tsx +9 -2
  96. package/src/globals.css.ts +3 -0
  97. package/src/hooks/safeContext.tsx +2 -1
  98. package/src/hooks/useDiagram.ts +1 -0
  99. package/src/hooks/useIsReducedGraphics.ts +5 -0
  100. package/src/hooks/useXYFlow.ts +1 -1
  101. package/src/likec4diagram/DiagramUI.tsx +5 -3
  102. package/src/likec4diagram/custom/nodes/nodeTypes.tsx +2 -2
  103. package/src/likec4diagram/ui/controls/styles.css.ts +11 -1
  104. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.css.ts +10 -3
  105. package/src/likec4diagram/ui/diagram-title/DiagramTitlePanel.tsx +6 -3
  106. package/src/likec4diagram/ui/search/LikeC4Search.css.ts +1 -0
  107. package/src/overlays/element-details/ElementDetailsCard.tsx +238 -231
  108. package/src/overlays/overlay/Overlay.css.ts +1 -9
  109. package/src/overlays/overlay/Overlay.tsx +6 -3
  110. package/src/overlays/relationship-details/RelationshipDetails.tsx +83 -66
  111. package/src/overlays/relationship-details/actor.ts +106 -13
  112. package/src/overlays/relationship-details/hooks.ts +6 -2
  113. package/src/overlays/relationships-browser/-useRelationshipsView.ts +10 -0
  114. package/src/overlays/relationships-browser/RelationshipsBrowser.tsx +16 -8
  115. package/src/overlays/relationships-browser/actor.ts +92 -22
  116. package/src/state/DiagramActorProvider.tsx +13 -2
  117. package/src/state/diagram-machine.ts +88 -65
  118. package/src/state/persistence.ts +22 -0
@@ -62,8 +62,9 @@ import {
62
62
  updateNodeData,
63
63
  } from './assign'
64
64
  import { type HotKeyEvent, hotkeyActorLogic } from './hotkeyActor'
65
+ import { DiagramToggledFeaturesPersistence } from './persistence'
65
66
  import { type Events as SyncLayoutEvents, syncManualLayoutActorLogic } from './syncManualLayoutActor'
66
- import { focusedBounds, typedSystem } from './utils'
67
+ import { findDiagramNode, focusedBounds, typedSystem } from './utils'
67
68
 
68
69
  type XYStoreApi = ReturnType<typeof useStoreApi<Types.Node, Types.Edge>>
69
70
 
@@ -197,7 +198,7 @@ export const diagramMachine = setup({
197
198
  }
198
199
  nonexhaustive(event.type)
199
200
  },
200
- 'click: selected node ': ({ event }) => {
201
+ 'click: selected node': ({ event }) => {
201
202
  assertEvent(event, 'xyflow.nodeClick')
202
203
  return event.node.selected === true
203
204
  },
@@ -209,6 +210,10 @@ export const diagramMachine = setup({
209
210
  assertEvent(event, 'xyflow.nodeClick')
210
211
  return context.focusedNode === event.node.id
211
212
  },
213
+ 'click: node has connections': ({ context, event }) => {
214
+ assertEvent(event, 'xyflow.nodeClick')
215
+ return context.xyedges.some(e => e.source === event.node.id || e.target === event.node.id)
216
+ },
212
217
  },
213
218
  actions: {
214
219
  'trigger:NavigateTo': (_, _params: { viewId: ViewId }) => {
@@ -220,6 +225,26 @@ export const diagramMachine = setup({
220
225
  'trigger:OpenSource': (_, _params: OpenSourceParams) => {
221
226
  },
222
227
 
228
+ 'assign lastClickedNode': assign(({ context, event }) => {
229
+ assertEvent(event, 'xyflow.nodeClick')
230
+ return {
231
+ lastClickedNode: lastClickedNode({ context, event }),
232
+ }
233
+ }),
234
+
235
+ 'open source of focused or last clicked node': enqueueActions(({ context, enqueue }) => {
236
+ const nodeId = context.focusedNode ?? context.lastClickedNode?.id
237
+ if (!nodeId || !context.features.enableVscode) return
238
+ const diagramNode = findDiagramNode(context, nodeId)
239
+ if (!diagramNode) return
240
+
241
+ if (DiagramNode.deploymentRef(diagramNode)) {
242
+ enqueue.raise({ type: 'open.source', deployment: DiagramNode.deploymentRef(diagramNode)! })
243
+ } else if (DiagramNode.modelRef(diagramNode)) {
244
+ enqueue.raise({ type: 'open.source', element: DiagramNode.modelRef(diagramNode)! })
245
+ }
246
+ }),
247
+
223
248
  'xyflow:fitDiagram': ({ context }, params?: { duration?: number; bounds?: BBox }) => {
224
249
  const {
225
250
  bounds = context.view.bounds,
@@ -340,7 +365,7 @@ export const diagramMachine = setup({
340
365
  context: ({ input, self, spawn }) => ({
341
366
  ...input,
342
367
  features: { ...AllDisabled },
343
- toggledFeatures: {
368
+ toggledFeatures: DiagramToggledFeaturesPersistence.read() ?? {
344
369
  enableReadOnly: true,
345
370
  },
346
371
  initialized: false,
@@ -398,10 +423,11 @@ export const diagramMachine = setup({
398
423
  {
399
424
  // TODO: xstate fails to infer the type of the guard
400
425
  guard: and([
401
- 'enabled: FocusMode' as const,
426
+ 'enabled: FocusMode',
427
+ 'click: node has connections',
402
428
  or([
403
- 'click: same node' as const,
404
- 'click: selected node ' as const,
429
+ 'click: same node',
430
+ 'click: selected node',
405
431
  ]),
406
432
  ]) as any,
407
433
  actions: [
@@ -413,9 +439,10 @@ export const diagramMachine = setup({
413
439
  target: 'focused',
414
440
  },
415
441
  {
416
- actions: assign({
417
- lastClickedNode,
418
- }),
442
+ actions: [
443
+ 'assign lastClickedNode',
444
+ 'open source of focused or last clicked node',
445
+ ],
419
446
  },
420
447
  ],
421
448
  'xyflow.paneClick': {
@@ -458,6 +485,7 @@ export const diagramMachine = setup({
458
485
  ...focusNodesEdges(s),
459
486
  viewportBeforeFocus: { ...s.context.viewport },
460
487
  })),
488
+ 'open source of focused or last clicked node',
461
489
  spawnChild('hotkeyActorLogic', { id: 'hotkey' }),
462
490
  {
463
491
  type: 'xyflow:fitDiagram',
@@ -490,6 +518,7 @@ export const diagramMachine = setup({
490
518
  focusedNode: ({ event }) => event.node.id as NodeId,
491
519
  }),
492
520
  assign(focusNodesEdges),
521
+ 'open source of focused or last clicked node',
493
522
  {
494
523
  type: 'xyflow:fitDiagram',
495
524
  params: focusedBounds,
@@ -503,6 +532,7 @@ export const diagramMachine = setup({
503
532
  focusedNode: ({ event }) => event.nodeId,
504
533
  }),
505
534
  assign(focusNodesEdges),
535
+ 'open source of focused or last clicked node',
506
536
  {
507
537
  type: 'xyflow:fitDiagram',
508
538
  params: focusedBounds,
@@ -534,57 +564,16 @@ export const diagramMachine = setup({
534
564
  },
535
565
  // Navigating to another view (after `navigateTo` event)
536
566
  navigating: {
537
- initial: 'pending',
538
- type: 'compound',
539
- states: {
540
- pending: {
541
- entry: enqueueActions(({ enqueue }) => {
542
- enqueue('closeAllOverlays')
543
- enqueue('stopSyncLayout')
544
- enqueue({
545
- type: 'trigger:NavigateTo',
546
- params: ({ context }) => ({
547
- viewId: nonNullable(context.lastOnNavigate, 'Invalid state, lastOnNavigate is null').toView,
548
- }),
549
- })
567
+ entry: [
568
+ 'closeAllOverlays',
569
+ 'stopSyncLayout',
570
+ {
571
+ type: 'trigger:NavigateTo',
572
+ params: ({ context }) => ({
573
+ viewId: nonNullable(context.lastOnNavigate, 'Invalid state, lastOnNavigate is null').toView,
550
574
  }),
551
- on: {
552
- 'update.view': {
553
- actions: enqueueActions(({ enqueue, context, event }) => {
554
- const { fromNode, toNode } = findCorrespondingNode(context, event)
555
- if (fromNode && toNode) {
556
- enqueue({
557
- type: 'xyflow:alignNodeFromToAfterNavigate',
558
- params: {
559
- fromNode: fromNode.id,
560
- toPosition: {
561
- x: toNode.position[0],
562
- y: toNode.position[1],
563
- },
564
- },
565
- })
566
- enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 80 })
567
- } else {
568
- enqueue({
569
- type: 'xyflow:setViewportCenter',
570
- params: getBBoxCenter(event.view.bounds),
571
- })
572
- enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
573
- }
574
- enqueue.assign(updateNavigationHistory)
575
- enqueue.assign(mergeXYNodesEdges)
576
- }),
577
- target: 'done',
578
- },
579
- },
580
575
  },
581
- done: {
582
- type: 'final',
583
- },
584
- },
585
- onDone: {
586
- target: 'idle',
587
- },
576
+ ],
588
577
  exit: assign({
589
578
  syncLayoutActorRef: ({ self, context, spawn }) =>
590
579
  spawn('syncManualLayoutActorLogic', {
@@ -592,12 +581,44 @@ export const diagramMachine = setup({
592
581
  systemId: 'syncLayout',
593
582
  input: { parent: self, viewId: context.view.id },
594
583
  }),
595
- lastOnNavigate: null,
584
+ // lastOnNavigate: null,
596
585
  }),
586
+ on: {
587
+ 'update.view': {
588
+ actions: enqueueActions(({ enqueue, context, event }) => {
589
+ const { fromNode, toNode } = findCorrespondingNode(context, event)
590
+ if (fromNode && toNode) {
591
+ enqueue({
592
+ type: 'xyflow:alignNodeFromToAfterNavigate',
593
+ params: {
594
+ fromNode: fromNode.id,
595
+ toPosition: {
596
+ x: toNode.position[0],
597
+ y: toNode.position[1],
598
+ },
599
+ },
600
+ })
601
+ enqueue.raise({ type: 'fitDiagram' }, { id: 'fitDiagram', delay: 80 })
602
+ } else {
603
+ enqueue({
604
+ type: 'xyflow:setViewportCenter',
605
+ params: getBBoxCenter(event.view.bounds),
606
+ })
607
+ enqueue.raise({ type: 'fitDiagram', duration: 200 }, { id: 'fitDiagram', delay: 25 })
608
+ }
609
+ enqueue.assign(updateNavigationHistory)
610
+ enqueue.assign(mergeXYNodesEdges)
611
+ enqueue.assign({
612
+ lastOnNavigate: null,
613
+ })
614
+ }),
615
+ target: '#idle',
616
+ },
617
+ },
597
618
  },
598
619
  walkthrough: {
599
620
  entry: [
600
- // spawnChild('hotkeyActor', { id: 'hotkey' }),
621
+ spawnChild('hotkeyActorLogic', { id: 'hotkey' }),
601
622
  assign({
602
623
  viewportBeforeFocus: ({ context }) => context.viewport,
603
624
  activeWalkthrough: ({ context, event }) => {
@@ -863,11 +884,13 @@ export const diagramMachine = setup({
863
884
  },
864
885
  'toggle.feature': {
865
886
  actions: assign({
866
- toggledFeatures: ({ context, event }) => ({
867
- ...context.toggledFeatures,
868
- [`enable${event.feature}`]: event.forceValue ??
869
- !(context.toggledFeatures[`enable${event.feature}`] ?? context.features[`enable${event.feature}`]),
870
- }),
887
+ toggledFeatures: ({ context, event }) => {
888
+ return DiagramToggledFeaturesPersistence.write({
889
+ ...context.toggledFeatures,
890
+ [`enable${event.feature}`]: event.forceValue ??
891
+ !(context.toggledFeatures[`enable${event.feature}`] ?? context.features[`enable${event.feature}`]),
892
+ })
893
+ },
871
894
  }),
872
895
  },
873
896
  },
@@ -0,0 +1,22 @@
1
+ import { isBoolean, pickBy } from 'remeda'
2
+ import type { EnabledFeatures } from '../context/DiagramFeatures'
3
+
4
+ const key = `likec4:diagram:toggledFeatures`
5
+ export const DiagramToggledFeaturesPersistence = {
6
+ read() {
7
+ try {
8
+ let fromStorage = sessionStorage.getItem(key)
9
+ if (fromStorage) {
10
+ return JSON.parse(fromStorage) as Partial<EnabledFeatures>
11
+ }
12
+ throw new Error(`Workspace ${key} not found`)
13
+ } catch (e) {
14
+ console.error(`Error reading fromStorage ${key}:`, e)
15
+ return null
16
+ }
17
+ },
18
+ write<F extends Partial<EnabledFeatures>>(toggledFeatures: F): F {
19
+ sessionStorage.setItem(key, JSON.stringify(pickBy(toggledFeatures, isBoolean)))
20
+ return toggledFeatures
21
+ },
22
+ }