@likec4/diagram 1.57.0 → 1.58.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 (155) hide show
  1. package/dist/adhoc-editor/EditorPanel.d.ts +1 -1
  2. package/dist/adhoc-editor/ElementsTree.d.ts +1 -1
  3. package/dist/adhoc-editor/LikeC4AdHocViewEditor.d.ts +1 -1
  4. package/dist/adhoc-editor/components/Tree.d.ts +5 -5
  5. package/dist/adhoc-editor/state/ActorProvider.d.ts +1 -1
  6. package/dist/adhoc-editor/state/panel.d.ts +1 -1
  7. package/dist/adhoc-editor/viewrules/ViewRulesPanel.d.ts +1 -1
  8. package/dist/base/BaseXYFlow.d.ts +1 -1
  9. package/dist/base-primitives/Markdown.d.ts +1 -1
  10. package/dist/base-primitives/NodeHandles.d.ts +1 -1
  11. package/dist/base-primitives/compound/CompoundActionButton.d.ts +1 -1
  12. package/dist/base-primitives/compound/CompoundDetailsButton.d.ts +1 -1
  13. package/dist/base-primitives/compound/CompoundNodeContainer.d.ts +1 -1
  14. package/dist/base-primitives/compound/CompoundTitle.d.ts +1 -1
  15. package/dist/base-primitives/compound/actionbtns.css.d.ts +1 -1
  16. package/dist/base-primitives/edge/EdgeActionButton.d.ts +1 -1
  17. package/dist/base-primitives/edge/EdgeContainer.d.ts +1 -1
  18. package/dist/base-primitives/edge/EdgeLabelContainer.d.ts +1 -1
  19. package/dist/base-primitives/edge/EdgeMarkers.d.ts +8 -8
  20. package/dist/base-primitives/element/ElementActionButtons.d.ts +1 -1
  21. package/dist/base-primitives/element/ElementData.d.ts +2 -2
  22. package/dist/base-primitives/element/ElementDetailsButton.d.ts +1 -1
  23. package/dist/base-primitives/element/ElementShape.d.ts +1 -1
  24. package/dist/base-primitives/element/ElementTags.d.ts +1 -1
  25. package/dist/base-primitives/element/ElementTags.js +1 -3
  26. package/dist/components/ErrorFallback.d.ts +2 -2
  27. package/dist/components/NavigationPanel.d.ts +2 -2
  28. package/dist/components/PortalToContainer.d.ts +1 -1
  29. package/dist/components/RootContainer.d.ts +1 -1
  30. package/dist/components/SearchControl.d.ts +2 -2
  31. package/dist/components/ViewNotFound.d.ts +2 -2
  32. package/dist/context/CurrentViewModelProvider.d.ts +1 -1
  33. package/dist/context/DefaultMantineProvider.d.ts +1 -1
  34. package/dist/context/DiagramEventHandlers.d.ts +1 -1
  35. package/dist/context/EnsureMantine.d.ts +1 -1
  36. package/dist/context/FramerMotionConfig.d.ts +3 -3
  37. package/dist/context/FramerMotionConfig.js +2 -2
  38. package/dist/context/IconRenderer.d.ts +2 -2
  39. package/dist/context/IconRenderer.js +32 -12
  40. package/dist/context/LikeC4ModelContext.d.ts +1 -1
  41. package/dist/context/ShadowRootContext.d.ts +3 -0
  42. package/dist/context/ShadowRootContext.js +5 -0
  43. package/dist/context/TagStylesContext.d.ts +2 -1
  44. package/dist/context/TagStylesContext.js +5 -7
  45. package/dist/editor/LikeC4EditorProvider.d.ts +2 -2
  46. package/dist/editor/actor/actions.d.ts +74 -15
  47. package/dist/editor/actor/actions.js +91 -25
  48. package/dist/editor/actor/hotkey.d.ts +1 -1
  49. package/dist/editor/actor/hotkey.js +7 -0
  50. package/dist/editor/actor/machine.d.ts +1 -1
  51. package/dist/editor/actor/machine.js +1 -0
  52. package/dist/editor/actor/setup.d.ts +10 -3
  53. package/dist/editor/actor/setup.js +3 -2
  54. package/dist/editor/actor/state.editor.d.ts +64 -14
  55. package/dist/editor/actor/state.editor.js +12 -11
  56. package/dist/editor/actor/state.sync-queue.d.ts +283 -91
  57. package/dist/editor/actor/state.sync-queue.js +93 -56
  58. package/dist/editor/actor/types.d.ts +6 -1
  59. package/dist/editor/actor/types.js +1 -1
  60. package/dist/editor/useEditorActorLogic.js +14 -2
  61. package/dist/hooks/useEditorActor.d.ts +5 -0
  62. package/dist/hooks/useEditorActor.js +3 -2
  63. package/dist/index.d.ts +2 -0
  64. package/dist/index.js +3 -1
  65. package/dist/likec4diagram/DiagramUI.d.ts +1 -1
  66. package/dist/likec4diagram/DiagramXYFlow.js +15 -3
  67. package/dist/likec4diagram/custom/edges/EdgeDrifts.d.ts +1 -1
  68. package/dist/likec4diagram/custom/edges/RelationshipEdge.js +127 -14
  69. package/dist/likec4diagram/custom/edges/SequenceStepEdge.d.ts +1 -1
  70. package/dist/likec4diagram/custom/edges/edges.css.d.ts +1 -0
  71. package/dist/likec4diagram/custom/edges/edges.css.js +5 -1
  72. package/dist/likec4diagram/custom/nodes/CompoundActions.d.ts +1 -1
  73. package/dist/likec4diagram/custom/nodes/ElementActions.d.ts +2 -2
  74. package/dist/likec4diagram/custom/nodes/NodeDrifts.d.ts +1 -1
  75. package/dist/likec4diagram/custom/nodes/NodeNotes.d.ts +1 -1
  76. package/dist/likec4diagram/custom/nodes/SequenceActorNode.d.ts +2 -2
  77. package/dist/likec4diagram/custom/nodes/nodes.d.ts +7 -7
  78. package/dist/likec4diagram/custom/nodes/toolbar/ColorButton.d.ts +3 -3
  79. package/dist/likec4diagram/custom/nodes/toolbar/CompoundToolbar.d.ts +2 -2
  80. package/dist/likec4diagram/custom/nodes/toolbar/ElementToolbar.d.ts +2 -2
  81. package/dist/likec4diagram/custom/nodes/toolbar/Toolbar.d.ts +1 -1
  82. package/dist/likec4diagram/custom/nodes/toolbar/_shared.d.ts +3 -3
  83. package/dist/likec4diagram/relationship-popover/RelationshipPopover.d.ts +1 -1
  84. package/dist/likec4diagram/relationship-popover/components.d.ts +2 -2
  85. package/dist/likec4diagram/state/DiagramActorProvider.d.ts +1 -1
  86. package/dist/likec4diagram/state/createViewChange.js +1 -0
  87. package/dist/likec4diagram/state/machine.actions.js +3 -2
  88. package/dist/likec4diagram/types.d.ts +2 -0
  89. package/dist/likec4diagram/ui/LayoutDriftFrame.d.ts +1 -1
  90. package/dist/likec4diagram/ui/notation/NotationPanel.d.ts +1 -1
  91. package/dist/likec4diagram/xyflow-diagram/diagram-view.js +4 -2
  92. package/dist/likec4diagram/xyflow-sequence/sequence-layout.js +1 -0
  93. package/dist/navigationpanel/NavigationPanel.d.ts +1 -1
  94. package/dist/navigationpanel/NavigationPanelControls.d.ts +1 -1
  95. package/dist/navigationpanel/NavigationPanelDropdown.d.ts +1 -1
  96. package/dist/navigationpanel/NavigationPanelDropdown.js +2 -2
  97. package/dist/navigationpanel/_common.d.ts +2 -2
  98. package/dist/navigationpanel/comparepanel/CompareActionsMenu.d.ts +1 -1
  99. package/dist/navigationpanel/comparepanel/ComparePanel.d.ts +1 -1
  100. package/dist/navigationpanel/comparepanel/ComparePanelControls.d.ts +1 -1
  101. package/dist/navigationpanel/comparepanel/DriftsSummary.d.ts +1 -1
  102. package/dist/navigationpanel/comparepanel/LayoutTypeSwitcher.d.ts +1 -1
  103. package/dist/navigationpanel/controls/DetailsControls.d.ts +1 -1
  104. package/dist/navigationpanel/controls/LayoutWarning.d.ts +1 -1
  105. package/dist/navigationpanel/controls/LogoButton.d.ts +1 -1
  106. package/dist/navigationpanel/controls/NavigationButtons.d.ts +1 -1
  107. package/dist/navigationpanel/controls/OpenSource.d.ts +1 -1
  108. package/dist/navigationpanel/controls/SearchControl.d.ts +1 -1
  109. package/dist/navigationpanel/controls/ToggleReadonly.d.ts +1 -1
  110. package/dist/navigationpanel/editorpanel/CenterCamera.d.ts +1 -1
  111. package/dist/navigationpanel/editorpanel/ChangeAutoLayoutButton.d.ts +1 -1
  112. package/dist/navigationpanel/editorpanel/EditorPanel.d.ts +1 -1
  113. package/dist/navigationpanel/editorpanel/ManualLayoutToolsButton.d.ts +1 -1
  114. package/dist/navigationpanel/editorpanel/ToggleReadonly.d.ts +1 -1
  115. package/dist/navigationpanel/styles.css.d.ts +1 -1
  116. package/dist/navigationpanel/walkthrough/ActiveWalkthroughControls.d.ts +1 -1
  117. package/dist/navigationpanel/walkthrough/DynamicViewControls.d.ts +1 -1
  118. package/dist/navigationpanel/walkthrough/WalkthroughPanel.d.ts +1 -1
  119. package/dist/overlays/Overlays.d.ts +1 -1
  120. package/dist/overlays/element-details/ElementDetails.d.ts +1 -1
  121. package/dist/overlays/element-details/ElementDetailsCard.d.ts +1 -1
  122. package/dist/overlays/element-details/MetadataValue.d.ts +2 -2
  123. package/dist/overlays/element-details/TabPanelRelationships.d.ts +1 -1
  124. package/dist/overlays/element-details/TabPanelStructure.d.ts +2 -2
  125. package/dist/overlays/relationship-details/RelationshipDetails.d.ts +1 -1
  126. package/dist/overlays/relationship-details/SelectEdge.d.ts +1 -1
  127. package/dist/overlays/relationship-details/custom/ElementActions.d.ts +1 -1
  128. package/dist/overlays/relationship-details/custom/nodeTypes.d.ts +4 -4
  129. package/dist/overlays/relationships-browser/RelationshipsBrowser.d.ts +1 -1
  130. package/dist/overlays/relationships-browser/SelectElement.d.ts +1 -1
  131. package/dist/overlays/relationships-browser/custom/ElementActions.d.ts +1 -1
  132. package/dist/overlays/relationships-browser/custom/EmptyNode.d.ts +1 -1
  133. package/dist/overlays/relationships-browser/custom/nodeTypes.d.ts +4 -4
  134. package/dist/projects-overview/ProjectsOverview.d.ts +1 -1
  135. package/dist/projects-overview/custom/nodeTypes.d.ts +1 -1
  136. package/dist/projects-overview/panel/ProjectsOverviewPanel.d.ts +1 -1
  137. package/dist/search/Search.d.ts +1 -1
  138. package/dist/search/XStateSearchAdapter.d.ts +1 -1
  139. package/dist/search/components/ElementsColumn.d.ts +1 -1
  140. package/dist/search/components/PickView.d.ts +1 -1
  141. package/dist/search/components/SearchByTags.d.ts +1 -1
  142. package/dist/search/components/SearchInput.d.ts +1 -1
  143. package/dist/search/components/ViewsColum.d.ts +3 -3
  144. package/dist/search/components/_shared.css.d.ts +1 -1
  145. package/dist/shadowroot/ShadowRoot.d.ts +1 -2
  146. package/dist/shadowroot/ShadowRoot.js +27 -21
  147. package/dist/shadowroot/index.d.ts +1 -0
  148. package/dist/shadowroot/{styles.css.d.ts → styles.d.ts} +3 -2
  149. package/dist/shadowroot/{styles.css.js → styles.js} +9 -8
  150. package/dist/styles2.js +1 -1
  151. package/package.json +27 -28
  152. package/panda.buildinfo.json +4 -7
  153. package/styles-min.css +1 -1
  154. package/styles-xyflow.css +1 -1
  155. package/styles.css +1 -1
@@ -1,11 +1,11 @@
1
1
  import "../../_virtual/_rolldown/runtime.js";
2
2
  import { machine } from "./setup.js";
3
3
  import { typedSystem } from "../../likec4diagram/state/utils.js";
4
+ import { cancelSync, clearQueue, deleteNodesAndEdges, makeSnapshot, pushToSyncQueue, redo, scheduleSync, undo } from "./actions.js";
4
5
  import { isViewChange } from "./types.js";
5
- import { makeSnapshot, pushToSyncQueue, reschedule, scheduleSync, undo } from "./actions.js";
6
- import { isNot } from "remeda";
6
+ import { findLast } from "remeda";
7
7
  import { invariant } from "@likec4/core";
8
- import { assign, enqueueActions, sendTo } from "xstate";
8
+ import { assign, enqueueActions, log, sendTo } from "xstate";
9
9
  //#region src/editor/actor/state.sync-queue.ts
10
10
  var to = {
11
11
  idle: { target: "#queue-idle" },
@@ -24,6 +24,7 @@ var idle = machine.createStateConfig({
24
24
  ...to.pending
25
25
  },
26
26
  on: {
27
+ "delete.nodes-edges": { actions: deleteNodesAndEdges() },
27
28
  "change.sync-snapshot": {
28
29
  actions: pushToSyncQueue(),
29
30
  ...to.pending
@@ -32,11 +33,13 @@ var idle = machine.createStateConfig({
32
33
  actions: pushToSyncQueue(),
33
34
  ...to.process
34
35
  },
35
- "undo": {
36
- actions: undo(),
37
- reenter: true,
36
+ "undo": { actions: undo() },
37
+ "redo": { actions: redo() },
38
+ "cancel": {
39
+ actions: cancelSync(),
38
40
  ...to.idle
39
- }
41
+ },
42
+ "view.synched": { actions: log("in idle got view.synched") }
40
43
  }
41
44
  });
42
45
  /**
@@ -54,21 +57,32 @@ var pending = machine.createStateConfig({
54
57
  actions: pushToSyncQueue(),
55
58
  ...to.process
56
59
  },
60
+ "delete.nodes-edges": {
61
+ actions: deleteNodesAndEdges(),
62
+ reenter: true,
63
+ ...to.pending
64
+ },
57
65
  "edit.move.start": { ...to.suspended },
58
66
  "undo": {
67
+ reenter: true,
59
68
  actions: undo(),
69
+ ...to.pending
70
+ },
71
+ "redo": {
72
+ reenter: true,
73
+ actions: redo(),
74
+ ...to.pending
75
+ },
76
+ "cancel": {
77
+ actions: clearQueue(),
60
78
  ...to.idle
61
79
  },
62
- "view.synched": {
63
- guard: "has pending",
64
- actions: () => {},
65
- ...to.process
66
- }
80
+ "view.synched": { actions: log("in pending got view.synched") }
67
81
  },
68
82
  after: { "wait-after-edit": [{
69
83
  guard: "has pending",
70
84
  ...to.process
71
- }, { ...to.idle }] }
85
+ }, to.idle] }
72
86
  });
73
87
  /**
74
88
  * When editing starts, we suspend the queue
@@ -76,17 +90,14 @@ var pending = machine.createStateConfig({
76
90
  var suspended = machine.createStateConfig({
77
91
  ...idOf(to.suspended),
78
92
  on: {
79
- "undo": {
80
- actions: undo(),
93
+ "edit.*": to.idle,
94
+ "cancel": {
95
+ actions: [clearQueue(), cancelSync()],
81
96
  ...to.idle
82
- },
83
- "edit.*": { ...to.pending },
84
- "change.*": { actions: reschedule() },
85
- "cancel": { ...to.idle }
97
+ }
86
98
  }
87
99
  });
88
- var peekFromQueue = () => machine.assign(({ system, context: { syncQueue, processing } }) => {
89
- if (processing) return {};
100
+ var peekFromQueue = () => machine.assign(({ system, context: { syncQueue } }) => {
90
101
  let [head, ...tail] = syncQueue;
91
102
  if (head === "sync-snapshot") head = makeSnapshot(system).change;
92
103
  return {
@@ -100,16 +111,12 @@ var clearProcessing = () => ({ processing: null });
100
111
  */
101
112
  var process = machine.createStateConfig({
102
113
  ...idOf(to.process),
103
- initial: "enter",
114
+ initial: "peekFromQueue",
104
115
  tags: ["busy"],
105
116
  states: {
106
- enter: {
117
+ peekFromQueue: {
107
118
  entry: peekFromQueue(),
108
119
  always: [
109
- {
110
- guard: ({ context: { processing } }) => !processing,
111
- ...to.idle
112
- },
113
120
  {
114
121
  guard: ({ context: { processing } }) => processing === "apply-latest-to-manual",
115
122
  target: "applyLatestToManual"
@@ -118,7 +125,19 @@ var process = machine.createStateConfig({
118
125
  guard: ({ context: { processing } }) => processing === "apply-semantic-layout",
119
126
  target: "applySemanticLayout"
120
127
  },
121
- { target: "executeChanges" }
128
+ {
129
+ guard: ({ context: { processing } }) => isViewChange(processing),
130
+ target: "executeChanges"
131
+ },
132
+ {
133
+ guard: "has pending",
134
+ reenter: true,
135
+ ...to.process
136
+ },
137
+ {
138
+ actions: clearQueue(),
139
+ ...to.idle
140
+ }
122
141
  ]
123
142
  },
124
143
  /**
@@ -126,13 +145,15 @@ var process = machine.createStateConfig({
126
145
  */
127
146
  applyLatestToManual: machine.createStateConfig({
128
147
  initial: "call",
148
+ entry: assign(({ system }) => ({ processing: makeSnapshot(system).change })),
129
149
  states: {
130
150
  call: { invoke: {
131
151
  src: "applyLatest",
132
152
  input: ({ context }) => {
133
- const current = context.history?.head.change.layout;
153
+ const current = context.processing;
154
+ invariant(isViewChange(current) && current.op === "save-view-snapshot");
134
155
  return {
135
- current: current?._layout === "manual" ? current : void 0,
156
+ current: current.layout,
136
157
  viewId: context.viewId
137
158
  };
138
159
  },
@@ -140,20 +161,28 @@ var process = machine.createStateConfig({
140
161
  actions: sendTo(typedSystem.diagramActor, ({ event }) => ({
141
162
  type: "update.view",
142
163
  view: event.output.updated
143
- }), { delay: 10 }),
164
+ }), { delay: 20 }),
144
165
  target: "wait"
145
166
  },
146
167
  onError: {
147
- actions: [({ event }) => {
148
- console.error(event.error);
149
- }],
168
+ actions: assign(({ event }) => {
169
+ console.error("applyLatestToManual onError", { error: event.error });
170
+ return {
171
+ processing: null,
172
+ syncQueue: []
173
+ };
174
+ }),
150
175
  ...to.idle
151
176
  }
152
177
  } },
153
178
  wait: {
154
- entry: assign(clearProcessing),
179
+ on: { "*": { actions: log(({ event }) => `wait: ignoring event ${event.type}`) } },
155
180
  after: { "500ms": {
156
- actions: scheduleSync(),
181
+ actions: [
182
+ clearQueue(),
183
+ assign({ redo: null }),
184
+ scheduleSync()
185
+ ],
157
186
  ...to.idle
158
187
  } }
159
188
  }
@@ -169,10 +198,10 @@ var process = machine.createStateConfig({
169
198
  input: ({ context }) => ({ viewId: context.viewId }),
170
199
  onDone: { target: "decideNext" },
171
200
  onError: {
172
- actions: [({ event }) => {
173
- console.error(event.error);
174
- }],
175
- target: "decideNext"
201
+ actions: ({ event }) => {
202
+ console.error("applySemanticLayout onError", { error: event.error });
203
+ },
204
+ target: "failure"
176
205
  }
177
206
  }
178
207
  }),
@@ -190,36 +219,44 @@ var process = machine.createStateConfig({
190
219
  },
191
220
  onDone: {
192
221
  actions: enqueueActions(({ context, event, enqueue }) => {
222
+ const requested = event.output.requested;
193
223
  enqueue.assign({
194
224
  processing: null,
195
- syncQueue: context.syncQueue.filter(isNot(isViewChange))
225
+ syncQueue: context.syncQueue.filter((change) => !isViewChange(change) || !requested.includes(change))
226
+ });
227
+ const lastSyncSnapshot = findLast(event.output.applied, (c) => c.op === "save-view-snapshot");
228
+ if (lastSyncSnapshot) enqueue.sendTo(typedSystem.diagramActor, {
229
+ type: "update.view-bounds",
230
+ bounds: lastSyncSnapshot.layout.bounds
196
231
  });
197
232
  }),
198
- target: "decideNext"
233
+ target: "waitViewSynced"
199
234
  },
200
235
  onError: {
201
- actions: assign(({ event }) => {
236
+ actions: ({ event }) => {
202
237
  console.error("executeChanges onError", { error: event.error });
203
- return {
204
- processing: null,
205
- syncQueue: []
206
- };
207
- }),
208
- ...to.idle
238
+ },
239
+ target: "failure"
209
240
  }
210
241
  } }),
242
+ waitViewSynced: {
243
+ on: { "view.synched": { target: "decideNext" } },
244
+ after: { 2e3: "decideNext" }
245
+ },
211
246
  decideNext: {
212
247
  entry: assign(clearProcessing),
213
248
  always: [{
214
249
  guard: "has pending",
215
- target: "enter"
216
- }, { ...to.idle }]
217
- }
250
+ reenter: true,
251
+ ...to.process
252
+ }, to.idle]
253
+ },
254
+ failure: { always: {
255
+ actions: clearQueue(),
256
+ ...to.idle
257
+ } }
218
258
  },
219
- on: {
220
- "change.*": { actions: reschedule() },
221
- "view.synched": { actions: () => {} }
222
- }
259
+ on: {}
223
260
  });
224
261
  var syncQueue = machine.createStateConfig({
225
262
  initial: "idle",
@@ -10,6 +10,10 @@ export type EditorActorEvent = {
10
10
  type: 'change.latest-to-manual';
11
11
  } | {
12
12
  type: 'change.sync-snapshot';
13
+ } | {
14
+ type: 'delete.nodes-edges';
15
+ nodeIds: t.NodeId[];
16
+ edgeIds: t.EdgeId[];
13
17
  } | {
14
18
  type: 'view.synched';
15
19
  } | {
@@ -36,10 +40,11 @@ export interface EditorActorInput {
36
40
  viewId: t.ViewId;
37
41
  }
38
42
  export type SyncOp = t.ViewChange | 'sync-snapshot' | 'apply-semantic-layout' | 'apply-latest-to-manual';
39
- export declare const isViewChange: (change: SyncOp) => change is t.ViewChange;
43
+ export declare const isViewChange: (change: SyncOp | null) => change is t.ViewChange;
40
44
  export interface EditorActorContext {
41
45
  viewId: t.ViewId;
42
46
  history: LinkedSnapshot | null;
47
+ redo: LinkedSnapshot | null;
43
48
  editing: null | {
44
49
  /**
45
50
  * The subject of the edit
@@ -1,5 +1,5 @@
1
1
  import "../../_virtual/_rolldown/runtime.js";
2
2
  //#region src/editor/actor/types.ts
3
- var isViewChange = (change) => typeof change !== "string";
3
+ var isViewChange = (change) => change !== null && typeof change !== "string";
4
4
  //#endregion
5
5
  export { isViewChange };
@@ -25,8 +25,20 @@ function useEditorActorLogic() {
25
25
  console.error("No editor port available for executing change");
26
26
  throw new Error("No editor port");
27
27
  }
28
- for (const change of input.changes) await promisify(() => port.handleChange(input.viewId, change));
29
- return {};
28
+ const applied = [];
29
+ for (const change of input.changes) try {
30
+ await promisify(() => port.handleChange(input.viewId, change));
31
+ applied.push(change);
32
+ } catch (error) {
33
+ console.error("Failed to execute change", {
34
+ change,
35
+ error
36
+ });
37
+ }
38
+ return {
39
+ requested: input.changes,
40
+ applied
41
+ };
30
42
  });
31
43
  const applySemanticLayout = useCallbackRef(async ({ input }) => {
32
44
  if (!port) {
@@ -2,3 +2,8 @@ import { EditorActorRef } from '../editor/actor/machine';
2
2
  import { EditorActorStateTag } from '../editor/actor/types';
3
3
  export declare function useEditorActorRef(): EditorActorRef | null;
4
4
  export declare function useEditorActorStateHasTag(tag: EditorActorStateTag): boolean;
5
+ /**
6
+ * Check if the editor actor is in the 'busy' state
7
+ * (i.e. syncing with the backend)
8
+ */
9
+ export declare function useEditorIsBusy(): boolean;
@@ -1,6 +1,7 @@
1
1
  import "../_virtual/_rolldown/runtime.js";
2
2
  import { selectDiagramActor, useDiagramSnapshot } from "./useDiagram.js";
3
3
  import { useEffect, useState } from "react";
4
+ import "@mantine/hooks";
4
5
  //#region src/hooks/useEditorActor.ts
5
6
  var select = selectDiagramActor((s) => {
6
7
  return s.children.editor ?? null;
@@ -12,8 +13,8 @@ function useEditorActorStateHasTag(tag) {
12
13
  const editorActor = useEditorActorRef();
13
14
  const [hasTag, setHasTag] = useState(() => editorActor?.getSnapshot().hasTag(tag) ?? false);
14
15
  useEffect(() => {
15
- const subscription = editorActor?.subscribe((snapshot) => {
16
- setHasTag(snapshot.hasTag(tag));
16
+ const subscription = editorActor?.select((s) => s.hasTag(tag)).subscribe((hasTag) => {
17
+ setHasTag(hasTag);
17
18
  });
18
19
  return () => {
19
20
  subscription?.unsubscribe();
package/dist/index.d.ts CHANGED
@@ -22,3 +22,5 @@ export { LikeC4ProjectsOverview, type LikeC4ProjectsOverviewProps, } from './Lik
22
22
  export { normalizeSearch, SearchContext, type SearchContextValue, useSearchContext, } from './search/SearchContext';
23
23
  export { bodyCss as searchBodyCss, dialogCss as searchDialogCss, SearchPanelContent, } from './search/Search';
24
24
  export { SearchControl } from './components/SearchControl';
25
+ export { DefaultMantineProvider } from './context/DefaultMantineProvider';
26
+ export { FramerMotionConfig } from './context/FramerMotionConfig';
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { IconRendererProvider } from "./context/IconRenderer.js";
2
2
  import { useUpdateEffect } from "./hooks/useUpdateEffect.js";
3
+ import { DefaultMantineProvider } from "./context/DefaultMantineProvider.js";
4
+ import { FramerMotionConfig } from "./context/FramerMotionConfig.js";
3
5
  import { useDiagram } from "./hooks/safeContext.js";
4
6
  import { useDiagramContext, useOnDiagramEvent } from "./hooks/useDiagram.js";
5
7
  import { useCurrentView, useCurrentViewId } from "./hooks/useCurrentView.js";
@@ -21,4 +23,4 @@ import { LikeC4View } from "./LikeC4View.js";
21
23
  import { ReactLikeC4 } from "./ReactLikeC4.js";
22
24
  import { LikeC4ProjectsOverview } from "./LikeC4ProjectsOverview.js";
23
25
  import { SearchControl } from "./components/SearchControl.js";
24
- export { IconRendererProvider, LikeC4Diagram, LikeC4EditorProvider, LikeC4ModelProvider, LikeC4ProjectsOverview, LikeC4ProjectsProvider, LikeC4View, ReactLikeC4, SearchContext, SearchControl, SearchPanelContent, StaticLikeC4Diagram, createLikeC4Editor, normalizeSearch, pickViewBounds, bodyCss as searchBodyCss, dialogCss as searchDialogCss, useChangeLikeC4Project, useCurrentView, useCurrentViewId, useCurrentViewModel, useDiagram, useDiagramContext, useHasProjects, useLikeC4Model, useLikeC4Project, useLikeC4ProjectId, useLikeC4Projects, useLikeC4ProjectsContext, useLikeC4Specification, useLikeC4Styles, useLikeC4ViewModel, useOnDiagramEvent, useOptionalCurrentViewModel, useSearchContext, useUpdateEffect };
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, useChangeLikeC4Project, useCurrentView, useCurrentViewId, useCurrentViewModel, useDiagram, useDiagramContext, useHasProjects, useLikeC4Model, useLikeC4Project, useLikeC4ProjectId, useLikeC4Projects, useLikeC4ProjectsContext, useLikeC4Specification, useLikeC4Styles, useLikeC4ViewModel, useOnDiagramEvent, useOptionalCurrentViewModel, useSearchContext, useUpdateEffect };
@@ -1,2 +1,2 @@
1
1
  import { MemoExoticComponent } from 'react';
2
- export declare const LikeC4DiagramUI: MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element>;
2
+ export declare const LikeC4DiagramUI: MemoExoticComponent<() => import("react").JSX.Element>;
@@ -52,8 +52,10 @@ var viewportToTopLeft = (ctx) => {
52
52
  };
53
53
  var selectXYProps = selectDiagramActor(({ context: ctx, children }) => {
54
54
  const { enableReadOnly } = deriveToggledFeatures(ctx);
55
- const isNotEditingEdge = enableReadOnly || children.editor?.getSnapshot().context.editing?.subject !== "edge";
56
- let nodesDraggable = !enableReadOnly && ctx.nodesDraggable;
55
+ const editorSnapshot = enableReadOnly ? null : children.editor?.getSnapshot();
56
+ const isNotEditingEdge = enableReadOnly || editorSnapshot?.context.editing?.subject !== "edge";
57
+ const isEditorBusy = editorSnapshot?.hasTag("busy") ?? false;
58
+ let nodesDraggable = !enableReadOnly && ctx.nodesDraggable && !isEditorBusy;
57
59
  if (ctx.dynamicViewVariant === "sequence" && ctx.view._type === "dynamic") nodesDraggable = false;
58
60
  return {
59
61
  enableReadOnly,
@@ -63,7 +65,7 @@ var selectXYProps = selectDiagramActor(({ context: ctx, children }) => {
63
65
  pannable: ctx.pannable,
64
66
  zoomable: ctx.zoomable,
65
67
  nodesDraggable,
66
- nodesSelectable: ctx.nodesSelectable && isNotEditingEdge,
68
+ nodesSelectable: ctx.nodesSelectable && isNotEditingEdge && !isEditorBusy,
67
69
  fitViewPadding: ctx.fitViewPadding,
68
70
  enableFitView: ctx.features.enableFitView,
69
71
  ...!ctx.features.enableFitView && { viewport: viewportToTopLeft(ctx) }
@@ -128,6 +130,7 @@ function LikeC4DiagramXYFlow({ background = "dots", reactFlowProps = {}, childre
128
130
  background: initialized ? background : "transparent",
129
131
  fitView: false,
130
132
  onNodeClick: useCallbackRef$1((e, node) => {
133
+ if (e.isPropagationStopped()) return;
131
134
  e.stopPropagation();
132
135
  diagram.send({
133
136
  type: "xyflow.nodeClick",
@@ -136,6 +139,7 @@ function LikeC4DiagramXYFlow({ background = "dots", reactFlowProps = {}, childre
136
139
  onNodeClick?.(diagram.findDiagramNode(node.id), e);
137
140
  }),
138
141
  onEdgeClick: useCallbackRef$1((e, edge) => {
142
+ if (e.isPropagationStopped()) return;
139
143
  e.stopPropagation();
140
144
  diagram.send({
141
145
  type: "xyflow.edgeClick",
@@ -144,12 +148,20 @@ function LikeC4DiagramXYFlow({ background = "dots", reactFlowProps = {}, childre
144
148
  onEdgeClick?.(diagram.findDiagramEdge(edge.id), e);
145
149
  }),
146
150
  onEdgeDoubleClick: useCallbackRef$1((e, edge) => {
151
+ if (e.isPropagationStopped()) return;
147
152
  e.stopPropagation();
148
153
  diagram.send({
149
154
  type: "xyflow.edgeDoubleClick",
150
155
  edge
151
156
  });
152
157
  }),
158
+ onDelete: useCallbackRef$1(({ nodes, edges }) => {
159
+ diagram.editorActor().send({
160
+ type: "delete.nodes-edges",
161
+ nodeIds: nodes.map((node) => node.data.id),
162
+ edgeIds: edges.map((edge) => edge.data.id)
163
+ });
164
+ }),
153
165
  onPaneClick: useCallbackRef$1((e) => {
154
166
  e.stopPropagation();
155
167
  diagram.send({ type: "xyflow.paneClick" });
@@ -3,4 +3,4 @@ export type EdgeDriftsProps = {
3
3
  edgeProps: Types.EdgeProps;
4
4
  svgPath: string;
5
5
  };
6
- export declare function EdgeDrifts({ edgeProps: { data }, svgPath, }: EdgeDriftsProps): import("react/jsx-runtime").JSX.Element | null;
6
+ export declare function EdgeDrifts({ edgeProps: { data }, svgPath, }: EdgeDriftsProps): import("react").JSX.Element | null;
@@ -13,7 +13,7 @@ import { EdgeLabelContainer } from "../../../base-primitives/edge/EdgeLabelConta
13
13
  import { EdgePath } from "../../../base-primitives/edge/EdgePath.js";
14
14
  import { memoEdge } from "../../../base-primitives/memoEdge.js";
15
15
  import { EdgeDrifts } from "./EdgeDrifts.js";
16
- import { controlPoint, controlPointsContainer } from "./edges.css.js";
16
+ import { controlPoint, controlPointsContainer, labelDraggable } from "./edges.css.js";
17
17
  import { useControlPoints } from "./useControlPoints.js";
18
18
  import { useRelationshipEdgePath } from "./useRelationshipEdgePath.js";
19
19
  import { css, cx } from "@likec4/styles/css";
@@ -33,7 +33,11 @@ var RelationshipEdge = memoEdge((props) => {
33
33
  const [isControlPointDragging, setIsControlPointDragging] = useState(false);
34
34
  const isControlPointDraggingRef = useRef(isControlPointDragging);
35
35
  isControlPointDraggingRef.current = isControlPointDragging;
36
+ const [isLabelDragging, setIsLabelDragging] = useState(false);
37
+ const isLabelDraggingRef = useRef(isLabelDragging);
38
+ isLabelDraggingRef.current = isLabelDragging;
36
39
  const xyflow = useXYFlow();
40
+ const xyflowStore = useXYStoreApi();
37
41
  const diagram = useDiagram();
38
42
  const { enableNavigateTo, enableReadOnly, enableCompareWithLatest } = useEnabledFeatures();
39
43
  const enabledEditing = !enableReadOnly;
@@ -52,32 +56,58 @@ var RelationshipEdge = memoEdge((props) => {
52
56
  y: labelXY?.y ?? labelY
53
57
  }, isSamePoint);
54
58
  useUpdateEffect$1(() => {
55
- if (isControlPointDraggingRef.current) return;
59
+ if (isControlPointDraggingRef.current || isLabelDraggingRef.current) return;
56
60
  setLabelPos({
57
61
  x: labelX,
58
62
  y: labelY
59
63
  });
60
64
  }, [labelX, labelY]);
61
65
  const svgPathRef = useRef(null);
66
+ const labelOffsetRef = useRef({
67
+ x: 0,
68
+ y: 0
69
+ });
70
+ const captureLabelOffset = useCallbackRef(() => {
71
+ const path = svgPathRef.current;
72
+ if (data.isLabelCustomized && path && labelBBox) {
73
+ const center = getEdgeCenter(path);
74
+ labelOffsetRef.current = {
75
+ x: labelBBox.x - center.x,
76
+ y: labelBBox.y - center.y
77
+ };
78
+ } else labelOffsetRef.current = {
79
+ x: 0,
80
+ y: 0
81
+ };
82
+ });
62
83
  useRafEffect(() => {
63
84
  const path = svgPathRef.current;
64
85
  if (!path || !isControlPointDragging) return;
65
- setLabelPos(getEdgeCenter(path));
86
+ const center = getEdgeCenter(path);
87
+ const offset = labelOffsetRef.current;
88
+ setLabelPos({
89
+ x: center.x + offset.x,
90
+ y: center.y + offset.y
91
+ });
66
92
  }, [edgePath, isControlPointDragging]);
67
93
  const updateEdgeData = useCallbackRef((controlPoints) => {
68
- const point = labelBBox && svgPathRef.current ? getEdgeCenter(svgPathRef.current) : null;
69
- if (point) diagram.updateEdgeData(id, {
70
- controlPoints,
71
- labelBBox: {
72
- ...labelBBox,
73
- ...point
74
- }
75
- });
76
- else diagram.updateEdgeData(id, { controlPoints });
94
+ const center = labelBBox && svgPathRef.current ? getEdgeCenter(svgPathRef.current) : null;
95
+ if (center) {
96
+ const offset = labelOffsetRef.current;
97
+ diagram.updateEdgeData(id, {
98
+ controlPoints,
99
+ labelBBox: {
100
+ ...labelBBox,
101
+ x: center.x + offset.x,
102
+ y: center.y + offset.y
103
+ }
104
+ });
105
+ } else diagram.updateEdgeData(id, { controlPoints });
77
106
  diagram.stopEditing(true);
78
107
  setIsControlPointDragging(false);
79
108
  });
80
109
  const onControlPointerStartMove = useCallbackRef(() => {
110
+ captureLabelOffset();
81
111
  diagram.startEditing("edge");
82
112
  setIsControlPointDragging(true);
83
113
  });
@@ -92,6 +122,7 @@ var RelationshipEdge = memoEdge((props) => {
92
122
  });
93
123
  });
94
124
  const onControlPointerDelete = useCallbackRef((points) => {
125
+ captureLabelOffset();
95
126
  diagram.startEditing("edge");
96
127
  setIsControlPointDragging(true);
97
128
  setControlPoints(points);
@@ -115,6 +146,86 @@ var RelationshipEdge = memoEdge((props) => {
115
146
  diagram.updateEdgeData(id, { controlPoints: newControlPoints });
116
147
  diagram.stopEditing(true);
117
148
  });
149
+ const updateLabelPosition = useCallbackRef((pos) => {
150
+ if (labelBBox) diagram.updateEdgeData(id, {
151
+ labelBBox: {
152
+ ...labelBBox,
153
+ x: pos.x,
154
+ y: pos.y
155
+ },
156
+ isLabelCustomized: true
157
+ });
158
+ diagram.stopEditing(true);
159
+ setIsLabelDragging(false);
160
+ });
161
+ /**
162
+ * Handle pointer down on the edge label to drag it to a custom position.
163
+ * Mirrors the control-point LMB drag.
164
+ */
165
+ const onLabelPointerDown = useCallbackRef((e) => {
166
+ if (e.pointerType !== "mouse" || e.button !== 0 || !labelBBox || isControlPointDraggingRef.current) return;
167
+ const { domNode, edges, addSelectedEdges, unselectNodesAndEdges } = xyflowStore.getState();
168
+ if (!domNode) return;
169
+ e.stopPropagation();
170
+ if (!selected) {
171
+ unselectNodesAndEdges({ edges: edges.filter((ed) => ed.selected && ed.id !== id) });
172
+ addSelectedEdges([id]);
173
+ }
174
+ let hasMoved = false;
175
+ const initialPoint = {
176
+ x: e.clientX,
177
+ y: e.clientY
178
+ };
179
+ const clientPoint = { ...initialPoint };
180
+ const flowStart = xyflow.screenToFlowPosition(initialPoint, { snapToGrid: false });
181
+ const grab = {
182
+ x: flowStart.x - labelBBox.x,
183
+ y: flowStart.y - labelBBox.y
184
+ };
185
+ let pos = {
186
+ x: labelBBox.x,
187
+ y: labelBBox.y
188
+ };
189
+ let animationFrameId = null;
190
+ const onPointerMove = (ev) => {
191
+ clientPoint.x = ev.clientX;
192
+ clientPoint.y = ev.clientY;
193
+ if (!isSamePoint(initialPoint, clientPoint)) {
194
+ if (!hasMoved) {
195
+ hasMoved = true;
196
+ diagram.startEditing("edge");
197
+ setIsLabelDragging(true);
198
+ }
199
+ animationFrameId ??= requestAnimationFrame(() => {
200
+ animationFrameId = null;
201
+ const { x, y } = xyflow.screenToFlowPosition(clientPoint, { snapToGrid: false });
202
+ pos = {
203
+ x: Math.round(x - grab.x),
204
+ y: Math.round(y - grab.y)
205
+ };
206
+ setLabelPos(pos);
207
+ });
208
+ }
209
+ ev.stopPropagation();
210
+ };
211
+ const onPointerUp = (ev) => {
212
+ ev.stopPropagation();
213
+ domNode.removeEventListener("pointermove", onPointerMove, { capture: true });
214
+ if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
215
+ if (hasMoved) {
216
+ domNode.addEventListener("click", stopAndPrevent, {
217
+ capture: true,
218
+ once: true
219
+ });
220
+ updateLabelPosition(pos);
221
+ }
222
+ };
223
+ domNode.addEventListener("pointermove", onPointerMove, { capture: true });
224
+ domNode.addEventListener("pointerup", onPointerUp, {
225
+ once: true,
226
+ capture: true
227
+ });
228
+ });
118
229
  if (isControlPointDragging && !props.data.hovered) props = {
119
230
  ...props,
120
231
  data: {
@@ -139,12 +250,14 @@ var RelationshipEdge = memoEdge((props) => {
139
250
  }),
140
251
  labelBBox && /* @__PURE__ */ jsx(EdgeLabelContainer, {
141
252
  edgeProps: props,
142
- labelPosition: isControlPointDragging ? labelPos : {
253
+ labelPosition: isControlPointDragging || isLabelDragging ? labelPos : {
143
254
  x: labelX,
144
255
  y: labelY
145
256
  },
257
+ ...enabledEditing && { onPointerDown: onLabelPointerDown },
146
258
  children: /* @__PURE__ */ jsx(EdgeLabel, {
147
- pointerEvents: enabledEditing ? "none" : "all",
259
+ pointerEvents: "all",
260
+ className: enabledEditing ? labelDraggable : void 0,
148
261
  edgeProps: props,
149
262
  children: navigateTo && /* @__PURE__ */ jsx(EdgeActionButton, { onClick: (e) => {
150
263
  e.stopPropagation();
@@ -1,2 +1,2 @@
1
1
  import { Types } from '../../types';
2
- export declare function SequenceStepEdge(props: Types.EdgeProps<'seq-step'>): import("react/jsx-runtime").JSX.Element;
2
+ export declare function SequenceStepEdge(props: Types.EdgeProps<'seq-step'>): import("react").JSX.Element;
@@ -1,2 +1,3 @@
1
1
  export declare const controlPointsContainer: string;
2
+ export declare const labelDraggable: string;
2
3
  export declare const controlPoint: string;
@@ -12,6 +12,10 @@ var controlPointsContainer = css({
12
12
  },
13
13
  zIndex: "[100]"
14
14
  });
15
+ var labelDraggable = css({
16
+ cursor: "grab!",
17
+ _active: { cursor: "grabbing!" }
18
+ });
15
19
  var controlPoint = css({
16
20
  fill: `[var(--xy-edge-stroke)]`,
17
21
  stroke: `transparent`,
@@ -41,4 +45,4 @@ var controlPoint = css({
41
45
  _groupActive: { cursor: "grabbing" }
42
46
  });
43
47
  //#endregion
44
- export { controlPoint, controlPointsContainer };
48
+ export { controlPoint, controlPointsContainer, labelDraggable };