@umodeapporg/ui 0.30.1 → 0.31.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.
- package/dist/assets/umode-ui.css +32 -0
- package/dist/components/UI/BulkActionBar.js +1 -1
- package/dist/components/UI/BulkActionBar.js.map +1 -1
- package/dist/components/UI/Flow/FlowAnnotationDraftPreview.d.ts +6 -0
- package/dist/components/UI/Flow/FlowAnnotationDraftPreview.js +65 -0
- package/dist/components/UI/Flow/FlowAnnotationDraftPreview.js.map +1 -0
- package/dist/components/UI/Flow/FlowAnnotationNode.d.ts +34 -0
- package/dist/components/UI/Flow/FlowAnnotationNode.js +231 -0
- package/dist/components/UI/Flow/FlowAnnotationNode.js.map +1 -0
- package/dist/components/UI/Flow/FlowAnnotationToolbar.d.ts +32 -0
- package/dist/components/UI/Flow/FlowAnnotationToolbar.js +298 -0
- package/dist/components/UI/Flow/FlowAnnotationToolbar.js.map +1 -0
- package/dist/components/UI/Flow/FlowCanvas.d.ts +2 -0
- package/dist/components/UI/Flow/FlowCanvas.js +278 -20
- package/dist/components/UI/Flow/FlowCanvas.js.map +1 -1
- package/dist/components/UI/Flow/FlowInspector.js +6 -5
- package/dist/components/UI/Flow/FlowInspector.js.map +1 -1
- package/dist/components/UI/Flow/FlowSelectionBar.d.ts +10 -0
- package/dist/components/UI/Flow/FlowSelectionBar.js +56 -0
- package/dist/components/UI/Flow/FlowSelectionBar.js.map +1 -0
- package/dist/components/UI/Flow/flowAnnotations.d.ts +38 -0
- package/dist/components/UI/Flow/flowAnnotations.js +123 -0
- package/dist/components/UI/Flow/flowAnnotations.js.map +1 -0
- package/dist/components/UI/Flow/flowEditorStore.d.ts +29 -5
- package/dist/components/UI/Flow/flowEditorStore.js +123 -23
- package/dist/components/UI/Flow/flowEditorStore.js.map +1 -1
- package/dist/components/UI/Flow/flowTypes.d.ts +30 -0
- package/dist/components/UI/Flow/index.d.ts +2 -1
- package/dist/components/UI/Flow/index.js +5 -1
- package/dist/components/UI/Flow/index.js.map +1 -1
- package/dist/components/UI/Flow/useFlowAnnotationDrawing.d.ts +25 -0
- package/dist/components/UI/Flow/useFlowAnnotationDrawing.js +117 -0
- package/dist/components/UI/Flow/useFlowAnnotationDrawing.js.map +1 -0
- package/dist/locales/en/ui.json.d.ts +41 -0
- package/dist/locales/en/ui.json.js +1 -1
- package/dist/locales/index.d.ts +82 -0
- package/dist/locales/pt/ui.json.d.ts +41 -0
- package/dist/locales/pt/ui.json.js +1 -1
- package/package.json +1 -1
- package/src/assets/fonts/LICENSE-Virgil.txt +45 -0
- package/src/assets/fonts/virgil.woff2 +0 -0
- package/src/assets/styles/base.css +10 -0
|
@@ -1,26 +1,45 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useState, useRef,
|
|
2
|
+
import { useMemo, useState, useRef, useCallback, useEffect } from "react";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { mergeClasses } from "../../../helpers/mergeClasses.js";
|
|
5
|
-
import { useReactFlow, useStore, MarkerType, ReactFlow, Background, BackgroundVariant, MiniMap } from "@xyflow/react";
|
|
5
|
+
import { useReactFlow, useStore, MarkerType, ReactFlow, SelectionMode, Background, BackgroundVariant, MiniMap, Panel } from "@xyflow/react";
|
|
6
|
+
import FlowAnnotationDraftPreview from "./FlowAnnotationDraftPreview.js";
|
|
7
|
+
import FlowAnnotationNode from "./FlowAnnotationNode.js";
|
|
8
|
+
import { scaleAnnotationPoints, FLOW_ANNOTATION_MIN_SIZE, FLOW_TEXT_CARET_OFFSET } from "./flowAnnotations.js";
|
|
9
|
+
import FlowAnnotationToolbar, { FlowAnnotationLauncher } from "./FlowAnnotationToolbar.js";
|
|
6
10
|
import FlowBlockNode, { FLOW_INPUT_HANDLE_ID } from "./FlowBlockNode.js";
|
|
7
11
|
import { flowOutputsOf } from "./flowBlockRegistry.js";
|
|
8
12
|
import FlowConnectionEdge from "./FlowConnectionEdge.js";
|
|
9
13
|
import { useFlowBlockRegistry, useFlowEditorStore, useFlowEditor } from "./FlowEditorProvider.js";
|
|
10
|
-
import { canConnect, layoutFlowByLevels } from "./flowGraph.js";
|
|
14
|
+
import { canConnect, isFlowNodeRemovable, layoutFlowByLevels } from "./flowGraph.js";
|
|
15
|
+
import FlowSelectionBar from "./FlowSelectionBar.js";
|
|
11
16
|
import { boundsOfRects, viewportToReveal } from "./flowViewport.js";
|
|
12
17
|
import FlowZoomControls from "./FlowZoomControls.js";
|
|
18
|
+
import { useFlowAnnotationDrawing } from "./useFlowAnnotationDrawing.js";
|
|
13
19
|
import { useFlowLabels } from "./useFlowLabels.js";
|
|
14
20
|
import "@xyflow/react/dist/base.css";
|
|
15
21
|
/* empty css */
|
|
16
22
|
const PRO_OPTIONS = { hideAttribution: true };
|
|
17
|
-
const NODE_TYPES = { flowBlock: FlowBlockNode };
|
|
23
|
+
const NODE_TYPES = { flowBlock: FlowBlockNode, flowAnnotation: FlowAnnotationNode };
|
|
18
24
|
const EDGE_TYPES = { flowConnection: FlowConnectionEdge };
|
|
19
25
|
const MINIMAP_SIZE = { width: 160, height: 104 };
|
|
20
26
|
const MINIMAP_MIN_NODES = 5;
|
|
21
|
-
const
|
|
27
|
+
const BLOCK_GRID = 16;
|
|
22
28
|
const PORTS = ["next", "true", "false", "error"];
|
|
23
29
|
const REVEAL_DURATION_MS = 200;
|
|
30
|
+
const PAN_ON_DRAG_BUTTONS = [1];
|
|
31
|
+
const MULTI_SELECTION_KEYS = ["Shift", "Meta", "Control"];
|
|
32
|
+
const TOOL_BY_KEY = {
|
|
33
|
+
v: "select",
|
|
34
|
+
p: "drawing",
|
|
35
|
+
l: "line",
|
|
36
|
+
r: "rectangle",
|
|
37
|
+
o: "ellipse",
|
|
38
|
+
a: "arrow",
|
|
39
|
+
t: "text"
|
|
40
|
+
};
|
|
41
|
+
const SHAPE_TOOLS = ["line", "arrow", "rectangle", "ellipse", "step"];
|
|
42
|
+
const ANNOTATION_Z_INDEX = -1;
|
|
24
43
|
const isPortId = (value) => PORTS.includes(value);
|
|
25
44
|
const worstSeverity = (issues) => {
|
|
26
45
|
if (issues.some((issue) => issue.severity === "error")) return "error";
|
|
@@ -35,7 +54,8 @@ const FlowCanvas = ({
|
|
|
35
54
|
ariaLabel,
|
|
36
55
|
className,
|
|
37
56
|
controlsPosition,
|
|
38
|
-
background = "dots"
|
|
57
|
+
background = "dots",
|
|
58
|
+
annotationTools = false
|
|
39
59
|
}) => {
|
|
40
60
|
const { t } = useTranslation("ui");
|
|
41
61
|
const labels = useFlowLabels();
|
|
@@ -44,11 +64,72 @@ const FlowCanvas = ({
|
|
|
44
64
|
const graph = useFlowEditor((state) => state.graph);
|
|
45
65
|
const selectedNodeIds = useFlowEditor((state) => state.selectedNodeIds);
|
|
46
66
|
const selectedEdgeIds = useFlowEditor((state) => state.selectedEdgeIds);
|
|
67
|
+
const selectedAnnotationIds = useFlowEditor((state) => state.selectedAnnotationIds);
|
|
68
|
+
const isAreaSelecting = useFlowEditor((state) => state.isAreaSelecting);
|
|
47
69
|
const canUndo = useFlowEditor((state) => state.past.length > 0);
|
|
48
70
|
const canRedo = useFlowEditor((state) => state.future.length > 0);
|
|
49
71
|
const safeIssues = useMemo(() => Array.isArray(issues) ? issues : [], [issues]);
|
|
50
72
|
const [measuredSizes, setMeasuredSizes] = useState({});
|
|
51
|
-
const { getViewport, setViewport } = useReactFlow();
|
|
73
|
+
const { getViewport, setViewport, screenToFlowPosition } = useReactFlow();
|
|
74
|
+
const containerRef = useRef(null);
|
|
75
|
+
const [tool, setTool] = useState("select");
|
|
76
|
+
const [shapeTool, setShapeTool] = useState("rectangle");
|
|
77
|
+
const [toolsOpen, setToolsOpen] = useState(false);
|
|
78
|
+
const [annotationTone, setAnnotationTone] = useState("danger");
|
|
79
|
+
const [annotationStroke, setAnnotationStroke] = useState("medium");
|
|
80
|
+
const [editingAnnotationId, setEditingAnnotationId] = useState(null);
|
|
81
|
+
const canAnnotate = annotationTools && !readOnly;
|
|
82
|
+
const isDrawing = canAnnotate && tool !== "select";
|
|
83
|
+
const annotations = useMemo(() => graph.annotations ?? [], [graph.annotations]);
|
|
84
|
+
const changeTool = useCallback((nextTool) => {
|
|
85
|
+
setTool(nextTool);
|
|
86
|
+
setToolsOpen(true);
|
|
87
|
+
if (SHAPE_TOOLS.includes(nextTool)) setShapeTool(nextTool);
|
|
88
|
+
}, []);
|
|
89
|
+
const closeTools = useCallback(() => {
|
|
90
|
+
setTool("select");
|
|
91
|
+
setToolsOpen(false);
|
|
92
|
+
}, []);
|
|
93
|
+
const handleAnnotationCreated = useCallback(
|
|
94
|
+
(annotationId, kind) => {
|
|
95
|
+
if (kind === "drawing" || kind === "step") {
|
|
96
|
+
store.getState().setSelection({ annotationIds: [] });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
setTool("select");
|
|
100
|
+
if (kind === "text") setEditingAnnotationId(annotationId);
|
|
101
|
+
},
|
|
102
|
+
[store]
|
|
103
|
+
);
|
|
104
|
+
const draft = useFlowAnnotationDrawing({
|
|
105
|
+
containerRef,
|
|
106
|
+
tool,
|
|
107
|
+
tone: annotationTone,
|
|
108
|
+
stroke: annotationStroke,
|
|
109
|
+
enabled: canAnnotate,
|
|
110
|
+
onCreated: handleAnnotationCreated
|
|
111
|
+
});
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (!canAnnotate) return void 0;
|
|
114
|
+
const pane = containerRef.current?.querySelector(".react-flow__pane");
|
|
115
|
+
if (!pane) return void 0;
|
|
116
|
+
const handleDoubleClick = (event) => {
|
|
117
|
+
if (event.target !== pane) return;
|
|
118
|
+
const point = screenToFlowPosition({ x: event.clientX, y: event.clientY }, { snapToGrid: false });
|
|
119
|
+
const annotationId = store.getState().addAnnotation({
|
|
120
|
+
kind: "text",
|
|
121
|
+
tone: annotationTone,
|
|
122
|
+
stroke: annotationStroke,
|
|
123
|
+
position: { x: point.x, y: point.y - FLOW_TEXT_CARET_OFFSET },
|
|
124
|
+
size: { width: FLOW_ANNOTATION_MIN_SIZE, height: FLOW_ANNOTATION_MIN_SIZE },
|
|
125
|
+
text: ""
|
|
126
|
+
});
|
|
127
|
+
setTool("select");
|
|
128
|
+
setEditingAnnotationId(annotationId);
|
|
129
|
+
};
|
|
130
|
+
pane.addEventListener("dblclick", handleDoubleClick);
|
|
131
|
+
return () => pane.removeEventListener("dblclick", handleDoubleClick);
|
|
132
|
+
}, [canAnnotate, annotationTone, annotationStroke, screenToFlowPosition, store]);
|
|
52
133
|
const canvasWidth = useStore((state) => state.width);
|
|
53
134
|
const canvasHeight = useStore((state) => state.height);
|
|
54
135
|
const [revealNodeIds, setRevealNodeIds] = useState([]);
|
|
@@ -85,6 +166,18 @@ const FlowCanvas = ({
|
|
|
85
166
|
pendingViewportRef.current = { viewport: target, settlesAt: Date.now() + REVEAL_DURATION_MS };
|
|
86
167
|
void setViewport(target, { duration: REVEAL_DURATION_MS });
|
|
87
168
|
}, [revealNodeIds, graph.nodes, measuredSizes, canvasWidth, canvasHeight, getViewport, setViewport]);
|
|
169
|
+
const handleAreaSelectionStart = useCallback(() => {
|
|
170
|
+
store.getState().setAreaSelecting(true);
|
|
171
|
+
const finishAreaSelection = () => {
|
|
172
|
+
store.getState().setAreaSelecting(false);
|
|
173
|
+
window.removeEventListener("pointerup", finishAreaSelection);
|
|
174
|
+
window.removeEventListener("pointercancel", finishAreaSelection);
|
|
175
|
+
window.removeEventListener("blur", finishAreaSelection);
|
|
176
|
+
};
|
|
177
|
+
window.addEventListener("pointerup", finishAreaSelection);
|
|
178
|
+
window.addEventListener("pointercancel", finishAreaSelection);
|
|
179
|
+
window.addEventListener("blur", finishAreaSelection);
|
|
180
|
+
}, [store]);
|
|
88
181
|
const stopRevealing = useCallback(() => {
|
|
89
182
|
pendingViewportRef.current = null;
|
|
90
183
|
setRevealNodeIds((current) => current.length > 0 ? [] : current);
|
|
@@ -102,7 +195,7 @@ const FlowCanvas = ({
|
|
|
102
195
|
}),
|
|
103
196
|
[addableBlocks, registry]
|
|
104
197
|
);
|
|
105
|
-
const
|
|
198
|
+
const blockNodes = useMemo(
|
|
106
199
|
() => graph.nodes.map((node) => {
|
|
107
200
|
const definition = registry.get(node.type);
|
|
108
201
|
const nodeIssues = safeIssues.filter((issue) => issue.nodeId === node.nodeId);
|
|
@@ -146,6 +239,70 @@ const FlowCanvas = ({
|
|
|
146
239
|
store
|
|
147
240
|
]
|
|
148
241
|
);
|
|
242
|
+
const resizeAnnotation = useCallback(
|
|
243
|
+
(annotationId, geometry) => {
|
|
244
|
+
const annotation = (store.getState().graph.annotations ?? []).find(
|
|
245
|
+
(candidate) => candidate.annotationId === annotationId
|
|
246
|
+
);
|
|
247
|
+
if (!annotation || !geometry.size) return;
|
|
248
|
+
store.getState().setAnnotationGeometry({
|
|
249
|
+
[annotationId]: {
|
|
250
|
+
...geometry,
|
|
251
|
+
points: scaleAnnotationPoints(annotation.points, annotation.size, geometry.size)
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
[store]
|
|
256
|
+
);
|
|
257
|
+
const finishTextEdit = useCallback(
|
|
258
|
+
(annotationId) => {
|
|
259
|
+
setEditingAnnotationId((current) => current === annotationId ? null : current);
|
|
260
|
+
const annotation = (store.getState().graph.annotations ?? []).find(
|
|
261
|
+
(candidate) => candidate.annotationId === annotationId
|
|
262
|
+
);
|
|
263
|
+
if (annotation && !annotation.text?.trim()) store.getState().removeAnnotations([annotationId]);
|
|
264
|
+
window.setTimeout(() => {
|
|
265
|
+
if (!document.activeElement || document.activeElement === document.body)
|
|
266
|
+
containerRef.current?.focus({ preventScroll: true });
|
|
267
|
+
}, 0);
|
|
268
|
+
},
|
|
269
|
+
[store]
|
|
270
|
+
);
|
|
271
|
+
const annotationNodes = useMemo(
|
|
272
|
+
() => annotations.map((annotation) => {
|
|
273
|
+
const selected = selectedAnnotationIds.includes(annotation.annotationId);
|
|
274
|
+
const editing = editingAnnotationId === annotation.annotationId;
|
|
275
|
+
return {
|
|
276
|
+
id: annotation.annotationId,
|
|
277
|
+
type: "flowAnnotation",
|
|
278
|
+
position: annotation.position,
|
|
279
|
+
// A text note takes the size of what is written in it; every other kind keeps its box.
|
|
280
|
+
...annotation.kind === "text" ? {} : { width: annotation.size.width, height: annotation.size.height },
|
|
281
|
+
selected,
|
|
282
|
+
draggable: !readOnly && !editing,
|
|
283
|
+
connectable: false,
|
|
284
|
+
zIndex: ANNOTATION_Z_INDEX,
|
|
285
|
+
// A shape is only grabbed by its stroke until it is selected; text is grabbed anywhere.
|
|
286
|
+
style: { pointerEvents: annotation.kind === "text" || selected ? "all" : "none" },
|
|
287
|
+
data: {
|
|
288
|
+
annotation,
|
|
289
|
+
readOnly,
|
|
290
|
+
editing,
|
|
291
|
+
placeholder: t("flow.annotations.textPlaceholder"),
|
|
292
|
+
onTextChange: (text) => store.getState().updateAnnotations([annotation.annotationId], { text }),
|
|
293
|
+
onEditStart: () => setEditingAnnotationId(annotation.annotationId),
|
|
294
|
+
onEditEnd: () => finishTextEdit(annotation.annotationId),
|
|
295
|
+
onResizeStart: () => store.getState().recordHistory(),
|
|
296
|
+
onResize: (geometry) => resizeAnnotation(annotation.annotationId, geometry)
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}),
|
|
300
|
+
[annotations, selectedAnnotationIds, editingAnnotationId, readOnly, t, store, finishTextEdit, resizeAnnotation]
|
|
301
|
+
);
|
|
302
|
+
const nodes = useMemo(
|
|
303
|
+
() => [...annotationNodes, ...blockNodes],
|
|
304
|
+
[annotationNodes, blockNodes]
|
|
305
|
+
);
|
|
149
306
|
const edges = useMemo(() => {
|
|
150
307
|
const traversed = new Set(runOverlay?.traversedEdgeIds ?? []);
|
|
151
308
|
return graph.edges.map((edge) => ({
|
|
@@ -168,20 +325,42 @@ const FlowCanvas = ({
|
|
|
168
325
|
}, [graph.edges, selectedEdgeIds, runOverlay, labels, safeIssues, insertableBlocks, readOnly, store]);
|
|
169
326
|
const handleNodesChange = useCallback(
|
|
170
327
|
(changes) => {
|
|
328
|
+
const state = store.getState();
|
|
329
|
+
const annotationIds = new Set((state.graph.annotations ?? []).map((annotation) => annotation.annotationId));
|
|
171
330
|
const positions = {};
|
|
331
|
+
const annotationGeometry = {};
|
|
172
332
|
const sizes = {};
|
|
173
|
-
let
|
|
333
|
+
let nodeSelection = null;
|
|
334
|
+
let annotationSelection = null;
|
|
335
|
+
const toggled = (current, id, selected) => selected ? [.../* @__PURE__ */ new Set([...current, id])] : current.filter((candidate) => candidate !== id);
|
|
174
336
|
for (const change of changes) {
|
|
175
337
|
if (change.type === "dimensions" && change.dimensions) sizes[change.id] = change.dimensions;
|
|
176
|
-
if (change.type === "position" && change.position && !readOnly)
|
|
338
|
+
if (change.type === "position" && change.position && !readOnly) {
|
|
339
|
+
if (annotationIds.has(change.id)) annotationGeometry[change.id] = { position: change.position };
|
|
340
|
+
else
|
|
341
|
+
positions[change.id] = {
|
|
342
|
+
x: Math.round(change.position.x / BLOCK_GRID) * BLOCK_GRID,
|
|
343
|
+
y: Math.round(change.position.y / BLOCK_GRID) * BLOCK_GRID
|
|
344
|
+
};
|
|
345
|
+
}
|
|
177
346
|
if (change.type === "select") {
|
|
178
|
-
|
|
179
|
-
|
|
347
|
+
if (annotationIds.has(change.id))
|
|
348
|
+
annotationSelection = toggled(
|
|
349
|
+
annotationSelection ?? state.selectedAnnotationIds,
|
|
350
|
+
change.id,
|
|
351
|
+
change.selected
|
|
352
|
+
);
|
|
353
|
+
else nodeSelection = toggled(nodeSelection ?? state.selectedNodeIds, change.id, change.selected);
|
|
180
354
|
}
|
|
181
355
|
}
|
|
182
356
|
if (Object.keys(sizes).length > 0) setMeasuredSizes((current) => ({ ...current, ...sizes }));
|
|
183
|
-
if (Object.keys(positions).length > 0)
|
|
184
|
-
if (
|
|
357
|
+
if (Object.keys(positions).length > 0) state.moveNodes(positions);
|
|
358
|
+
if (Object.keys(annotationGeometry).length > 0) state.setAnnotationGeometry(annotationGeometry);
|
|
359
|
+
if (nodeSelection || annotationSelection)
|
|
360
|
+
state.setSelection({
|
|
361
|
+
...nodeSelection ? { nodeIds: nodeSelection } : {},
|
|
362
|
+
...annotationSelection ? { annotationIds: annotationSelection } : {}
|
|
363
|
+
});
|
|
185
364
|
},
|
|
186
365
|
[store, readOnly]
|
|
187
366
|
);
|
|
@@ -219,6 +398,14 @@ const FlowCanvas = ({
|
|
|
219
398
|
const withModifier = event.metaKey || event.ctrlKey;
|
|
220
399
|
const key = event.key.toLowerCase();
|
|
221
400
|
if (event.key === "Delete" || event.key === "Backspace") state.removeSelected();
|
|
401
|
+
else if (event.key === "Escape" && isDrawing) setTool("select");
|
|
402
|
+
else if (event.key === "Escape") state.setSelection({ nodeIds: [], edgeIds: [], annotationIds: [] });
|
|
403
|
+
else if (withModifier && key === "a")
|
|
404
|
+
state.setSelection({
|
|
405
|
+
nodeIds: state.graph.nodes.map((node) => node.nodeId),
|
|
406
|
+
annotationIds: (state.graph.annotations ?? []).map((annotation) => annotation.annotationId)
|
|
407
|
+
});
|
|
408
|
+
else if (canAnnotate && !withModifier && !event.altKey && TOOL_BY_KEY[key]) changeTool(TOOL_BY_KEY[key]);
|
|
222
409
|
else if (withModifier && key === "z" && event.shiftKey) state.redo();
|
|
223
410
|
else if (withModifier && key === "z") state.undo();
|
|
224
411
|
else if (withModifier && key === "y") state.redo();
|
|
@@ -228,8 +415,37 @@ const FlowCanvas = ({
|
|
|
228
415
|
else return;
|
|
229
416
|
event.preventDefault();
|
|
230
417
|
},
|
|
231
|
-
[store, readOnly]
|
|
418
|
+
[store, readOnly, isDrawing, canAnnotate, changeTool]
|
|
419
|
+
);
|
|
420
|
+
const canDeleteSelection = useMemo(
|
|
421
|
+
() => selectedAnnotationIds.length > 0 || graph.nodes.some((node) => selectedNodeIds.includes(node.nodeId) && isFlowNodeRemovable(node, registry)),
|
|
422
|
+
[graph.nodes, selectedNodeIds, selectedAnnotationIds, registry]
|
|
423
|
+
);
|
|
424
|
+
const selectedAnnotations = useMemo(
|
|
425
|
+
() => annotations.filter((annotation) => selectedAnnotationIds.includes(annotation.annotationId)),
|
|
426
|
+
[annotations, selectedAnnotationIds]
|
|
232
427
|
);
|
|
428
|
+
const sharedSelectedValue = (valueOf) => {
|
|
429
|
+
const values = new Set(selectedAnnotations.map(valueOf));
|
|
430
|
+
return values.size === 1 ? [...values][0] ?? null : null;
|
|
431
|
+
};
|
|
432
|
+
const shownTone = sharedSelectedValue((annotation) => annotation.tone) ?? annotationTone;
|
|
433
|
+
const shownStroke = sharedSelectedValue((annotation) => annotation.stroke ?? "medium") ?? annotationStroke;
|
|
434
|
+
const changeAnnotationTone = (tone) => {
|
|
435
|
+
setAnnotationTone(tone);
|
|
436
|
+
if (selectedAnnotations.length > 0) store.getState().updateAnnotations(selectedAnnotationIds, { tone });
|
|
437
|
+
};
|
|
438
|
+
const changeAnnotationStroke = (stroke) => {
|
|
439
|
+
setAnnotationStroke(stroke);
|
|
440
|
+
if (selectedAnnotations.length > 0) store.getState().updateAnnotations(selectedAnnotationIds, { stroke });
|
|
441
|
+
};
|
|
442
|
+
const selectedCount = selectedNodeIds.length + selectedAnnotationIds.length;
|
|
443
|
+
const showsSelectionBar = !readOnly && !isAreaSelecting && editingAnnotationId === null && selectedCount > 1;
|
|
444
|
+
const clearSelection = () => store.getState().setSelection({ nodeIds: [], edgeIds: [], annotationIds: [] });
|
|
445
|
+
const handlePaneClick = () => {
|
|
446
|
+
if (editingAnnotationId) finishTextEdit(editingAnnotationId);
|
|
447
|
+
clearSelection();
|
|
448
|
+
};
|
|
233
449
|
const handleLayout = useCallback(() => {
|
|
234
450
|
const state = store.getState();
|
|
235
451
|
state.recordHistory();
|
|
@@ -240,10 +456,12 @@ const FlowCanvas = ({
|
|
|
240
456
|
{
|
|
241
457
|
role: "region",
|
|
242
458
|
"aria-label": ariaLabel ?? t("flow.canvas"),
|
|
459
|
+
ref: containerRef,
|
|
243
460
|
tabIndex: 0,
|
|
244
461
|
onKeyDown: handleKeyDown,
|
|
245
462
|
className: mergeClasses(
|
|
246
|
-
"umode-flow relative h-full min-h-[24rem] w-full rounded-xl bg-canvas focus:outline-none",
|
|
463
|
+
"umode-flow relative h-full min-h-[24rem] w-full select-none rounded-xl bg-canvas focus:outline-none",
|
|
464
|
+
isDrawing && "umode-flow--drawing",
|
|
247
465
|
className
|
|
248
466
|
),
|
|
249
467
|
children: /* @__PURE__ */ jsxs(
|
|
@@ -264,12 +482,20 @@ const FlowCanvas = ({
|
|
|
264
482
|
onMoveStart: (event) => {
|
|
265
483
|
if (event) stopRevealing();
|
|
266
484
|
},
|
|
267
|
-
onPaneClick:
|
|
485
|
+
onPaneClick: handlePaneClick,
|
|
486
|
+
onSelectionStart: handleAreaSelectionStart,
|
|
487
|
+
onSelectionEnd: () => store.getState().setAreaSelecting(false),
|
|
268
488
|
deleteKeyCode: null,
|
|
489
|
+
selectionKeyCode: null,
|
|
490
|
+
multiSelectionKeyCode: MULTI_SELECTION_KEYS,
|
|
491
|
+
selectionOnDrag: !isDrawing,
|
|
492
|
+
selectionMode: SelectionMode.Partial,
|
|
493
|
+
panOnDrag: PAN_ON_DRAG_BUTTONS,
|
|
494
|
+
panOnScroll: true,
|
|
495
|
+
panOnScrollSpeed: 1,
|
|
496
|
+
zoomOnDoubleClick: false,
|
|
269
497
|
nodesDraggable: !readOnly,
|
|
270
498
|
nodesConnectable: !readOnly,
|
|
271
|
-
snapToGrid: true,
|
|
272
|
-
snapGrid: SNAP_GRID,
|
|
273
499
|
minZoom: 0.25,
|
|
274
500
|
maxZoom: 2,
|
|
275
501
|
fitView: true,
|
|
@@ -277,7 +503,39 @@ const FlowCanvas = ({
|
|
|
277
503
|
proOptions: PRO_OPTIONS,
|
|
278
504
|
children: [
|
|
279
505
|
background === "dots" && /* @__PURE__ */ jsx(Background, { variant: BackgroundVariant.Dots, gap: 16, size: 1 }),
|
|
280
|
-
|
|
506
|
+
draft && /* @__PURE__ */ jsx(FlowAnnotationDraftPreview, { draft }),
|
|
507
|
+
graph.nodes.length >= MINIMAP_MIN_NODES && /* @__PURE__ */ jsx(MiniMap, { pannable: true, zoomable: true, position: "bottom-right", style: MINIMAP_SIZE }),
|
|
508
|
+
(showsSelectionBar || canAnnotate) && /* @__PURE__ */ jsxs(Panel, { position: "bottom-center", className: "flex flex-col items-center gap-y-2", children: [
|
|
509
|
+
showsSelectionBar && /* @__PURE__ */ jsx(
|
|
510
|
+
FlowSelectionBar,
|
|
511
|
+
{
|
|
512
|
+
selectedCount,
|
|
513
|
+
canDelete: canDeleteSelection,
|
|
514
|
+
onDuplicate: () => store.getState().duplicateSelected(),
|
|
515
|
+
onDelete: () => store.getState().removeSelected(),
|
|
516
|
+
onClear: clearSelection
|
|
517
|
+
}
|
|
518
|
+
),
|
|
519
|
+
canAnnotate && (toolsOpen ? /* @__PURE__ */ jsx(
|
|
520
|
+
FlowAnnotationToolbar,
|
|
521
|
+
{
|
|
522
|
+
tool,
|
|
523
|
+
shapeTool,
|
|
524
|
+
onToolChange: changeTool,
|
|
525
|
+
tone: shownTone,
|
|
526
|
+
onToneChange: changeAnnotationTone,
|
|
527
|
+
stroke: shownStroke,
|
|
528
|
+
onStrokeChange: changeAnnotationStroke,
|
|
529
|
+
canUndo,
|
|
530
|
+
canRedo,
|
|
531
|
+
onUndo: () => store.getState().undo(),
|
|
532
|
+
onRedo: () => store.getState().redo(),
|
|
533
|
+
canDelete: canDeleteSelection,
|
|
534
|
+
onDelete: () => store.getState().removeSelected(),
|
|
535
|
+
onClose: closeTools
|
|
536
|
+
}
|
|
537
|
+
) : /* @__PURE__ */ jsx(FlowAnnotationLauncher, { onOpen: () => setToolsOpen(true) }))
|
|
538
|
+
] }),
|
|
281
539
|
/* @__PURE__ */ jsx(
|
|
282
540
|
FlowZoomControls,
|
|
283
541
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlowCanvas.js","sources":["../../../../src/components/UI/Flow/FlowCanvas.tsx"],"sourcesContent":["import { KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport { mergeClasses } from '@/helpers/mergeClasses';\nimport {\n Background,\n BackgroundVariant,\n type Connection,\n type EdgeChange,\n MarkerType,\n MiniMap,\n type NodeChange,\n type PanelPosition,\n type ProOptions,\n ReactFlow,\n useReactFlow,\n useStore as useReactFlowStore,\n} from '@xyflow/react';\n\nimport FlowBlockNode, { FLOW_INPUT_HANDLE_ID, type IFlowAddableBlock, type TFlowBlockNode } from './FlowBlockNode';\nimport { flowOutputsOf } from './flowBlockRegistry';\nimport FlowConnectionEdge, { type TFlowConnectionEdge } from './FlowConnectionEdge';\nimport { useFlowBlockRegistry, useFlowEditor, useFlowEditorStore } from './FlowEditorProvider';\nimport { canConnect, layoutFlowByLevels } from './flowGraph';\nimport type { IFlowIssue, IFlowPosition, IFlowRunOverlay, TFlowIssueSeverity, TFlowPortId } from './flowTypes';\nimport { boundsOfRects, type IFlowRect, type IFlowViewport, viewportToReveal } from './flowViewport';\nimport FlowZoomControls from './FlowZoomControls';\nimport { useFlowLabels } from './useFlowLabels';\n\nimport '@xyflow/react/dist/base.css';\nimport '../../../styles/flow.css';\n\nconst PRO_OPTIONS: ProOptions = { hideAttribution: true };\nconst NODE_TYPES = { flowBlock: FlowBlockNode };\nconst EDGE_TYPES = { flowConnection: FlowConnectionEdge };\nconst MINIMAP_SIZE = { width: 160, height: 104 };\n/** A small flow fits on screen whole, where a minimap only covers blocks. */\nconst MINIMAP_MIN_NODES = 5;\nconst SNAP_GRID: [number, number] = [16, 16];\nconst PORTS: Array<TFlowPortId> = ['next', 'true', 'false', 'error'];\nconst REVEAL_DURATION_MS = 200;\n\nexport interface IFlowCanvasProps {\n issues?: Array<IFlowIssue>;\n runOverlay?: IFlowRunOverlay | null;\n readOnly?: boolean;\n /** Accessible name of the canvas region, from `t()`. Defaults to the library label. */\n ariaLabel?: string;\n className?: string;\n /** Where the zoom and history controls sit. */\n controlsPosition?: PanelPosition;\n /** The dot grid behind the blocks. Blocks still snap to the grid when it is hidden. */\n background?: 'dots' | 'none';\n}\n\nconst isPortId = (value: string | null | undefined): value is TFlowPortId => PORTS.includes(value as TFlowPortId);\n\nconst worstSeverity = (issues: Array<IFlowIssue>): TFlowIssueSeverity | undefined => {\n if (issues.some((issue) => issue.severity === 'error')) return 'error';\n if (issues.some((issue) => issue.severity === 'warning')) return 'warning';\n return undefined;\n};\n\nconst isTypingTarget = (target: EventTarget | null): boolean =>\n target instanceof HTMLElement &&\n (target.isContentEditable || ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName));\n\n/**\n * The editing surface. Renders the graph held by `FlowEditorProvider` and turns every\n * gesture (drag, connect, select, delete, shortcuts) into a store action, so undo\n * and the dirty flag see all of them.\n */\nconst FlowCanvas: React.FC<IFlowCanvasProps> = ({\n issues = [],\n runOverlay,\n readOnly = false,\n ariaLabel,\n className,\n controlsPosition,\n background = 'dots',\n}) => {\n const { t } = useTranslation('ui');\n const labels = useFlowLabels();\n const registry = useFlowBlockRegistry();\n const store = useFlowEditorStore();\n\n const graph = useFlowEditor((state) => state.graph);\n const selectedNodeIds = useFlowEditor((state) => state.selectedNodeIds);\n const selectedEdgeIds = useFlowEditor((state) => state.selectedEdgeIds);\n const canUndo = useFlowEditor((state) => state.past.length > 0);\n const canRedo = useFlowEditor((state) => state.future.length > 0);\n\n const safeIssues = useMemo(() => (Array.isArray(issues) ? issues : []), [issues]);\n // Measured sizes are view state, not part of the flow: kept here so the minimap can draw the blocks.\n const [measuredSizes, setMeasuredSizes] = useState<Record<string, { width: number; height: number }>>({});\n\n const { getViewport, setViewport } = useReactFlow<TFlowBlockNode, TFlowConnectionEdge>();\n const canvasWidth = useReactFlowStore((state) => state.width);\n const canvasHeight = useReactFlowStore((state) => state.height);\n // Blocks just added and selected: kept in view until the user moves the view or the selection, so\n // the host panel that opens on selection (and narrows the canvas) cannot hide them.\n const [revealNodeIds, setRevealNodeIds] = useState<Array<string>>([]);\n const knownNodeIdsRef = useRef<Set<string> | null>(null);\n const pendingViewportRef = useRef<{ viewport: IFlowViewport; settlesAt: number } | null>(null);\n\n useEffect(() => {\n const currentIds = new Set(graph.nodes.map((node) => node.nodeId));\n const knownIds = knownNodeIdsRef.current;\n knownNodeIdsRef.current = currentIds;\n const addedAndSelected = knownIds ? selectedNodeIds.filter((id) => currentIds.has(id) && !knownIds.has(id)) : [];\n if (addedAndSelected.length > 0) {\n setRevealNodeIds(addedAndSelected);\n return;\n }\n setRevealNodeIds((current) =>\n current.length > 0 && !current.every((id) => selectedNodeIds.includes(id)) ? [] : current,\n );\n }, [graph.nodes, selectedNodeIds]);\n\n useEffect(() => {\n if (revealNodeIds.length === 0) return;\n const rects: Array<IFlowRect> = [];\n for (const nodeId of revealNodeIds) {\n const node = graph.nodes.find((candidate) => candidate.nodeId === nodeId);\n const size = measuredSizes[nodeId];\n if (!node || !size) return;\n rects.push({ ...node.position, ...size });\n }\n const bounds = boundsOfRects(rects);\n if (!bounds) return;\n // A pan still animating reports an in-between viewport; build on where it is heading instead.\n const pending = pendingViewportRef.current;\n const baseViewport = pending && pending.settlesAt > Date.now() ? pending.viewport : getViewport();\n const target = viewportToReveal(baseViewport, bounds, { width: canvasWidth, height: canvasHeight });\n if (!target) return;\n pendingViewportRef.current = { viewport: target, settlesAt: Date.now() + REVEAL_DURATION_MS };\n void setViewport(target, { duration: REVEAL_DURATION_MS });\n }, [revealNodeIds, graph.nodes, measuredSizes, canvasWidth, canvasHeight, getViewport, setViewport]);\n\n const stopRevealing = useCallback(() => {\n pendingViewportRef.current = null;\n setRevealNodeIds((current) => (current.length > 0 ? [] : current));\n }, []);\n\n const addableBlocks = useMemo<Array<IFlowAddableBlock>>(() => {\n if (readOnly) return [];\n return registry.list\n .filter((definition) => definition.category !== 'trigger')\n .filter(\n (definition) =>\n definition.maxInstances === undefined ||\n graph.nodes.filter((node) => node.type === definition.type).length < definition.maxInstances,\n )\n .map((definition) => ({ type: definition.type, label: definition.label, icon: definition.icon }));\n }, [registry, graph.nodes, readOnly]);\n\n // A block in the middle of an edge needs an output to carry the flow on to the old target.\n const insertableBlocks = useMemo<Array<IFlowAddableBlock>>(\n () =>\n addableBlocks.filter((block) => {\n const definition = registry.get(block.type);\n return definition ? flowOutputsOf(definition, definition.createDefaultConfig()).length > 0 : false;\n }),\n [addableBlocks, registry],\n );\n\n const nodes = useMemo<Array<TFlowBlockNode>>(\n () =>\n graph.nodes.map((node) => {\n const definition = registry.get(node.type);\n const nodeIssues = safeIssues.filter((issue) => issue.nodeId === node.nodeId);\n return {\n id: node.nodeId,\n type: 'flowBlock',\n position: node.position,\n measured: measuredSizes[node.nodeId],\n selected: selectedNodeIds.includes(node.nodeId),\n draggable: !readOnly,\n connectable: !readOnly,\n data: {\n icon: definition?.resolveIcon?.(node.config) ?? definition?.icon ?? 'help',\n title: node.label ?? definition?.resolveLabel?.(node.config) ?? definition?.label ?? node.type,\n categoryLabel: definition ? labels.categories[definition.category] : '',\n summary: definition\n ? definition.renderSummary(node.config)\n : labels.issueMessage({ code: 'unknownBlock', severity: 'error' }),\n hasInput: definition ? definition.category !== 'trigger' : true,\n outputs: flowOutputsOf(definition, node.config),\n connectedPorts: graph.edges.filter((edge) => edge.source === node.nodeId).map((edge) => edge.sourcePort),\n portLabels: labels.ports,\n addableBlocks,\n onAddFromPort: readOnly\n ? undefined\n : (port, type) => store.getState().addNodeFromPort(type, node.nodeId, port),\n disabled: node.disabled,\n issueSeverity: worstSeverity(nodeIssues),\n issueCount: nodeIssues.length,\n runStatus: runOverlay?.steps?.[node.nodeId],\n },\n };\n }),\n [\n graph.nodes,\n graph.edges,\n registry,\n safeIssues,\n selectedNodeIds,\n readOnly,\n labels,\n runOverlay,\n measuredSizes,\n addableBlocks,\n store,\n ],\n );\n\n const edges = useMemo<Array<TFlowConnectionEdge>>(() => {\n const traversed = new Set(runOverlay?.traversedEdgeIds ?? []);\n return graph.edges.map((edge) => ({\n id: edge.edgeId,\n type: 'flowConnection',\n source: edge.source,\n sourceHandle: edge.sourcePort,\n target: edge.target,\n targetHandle: FLOW_INPUT_HANDLE_ID,\n selected: selectedEdgeIds.includes(edge.edgeId),\n markerEnd: { type: MarkerType.ArrowClosed, width: 16, height: 16 },\n data: {\n label: edge.sourcePort === 'next' ? undefined : labels.ports[edge.sourcePort],\n traversed: traversed.has(edge.edgeId),\n issueSeverity: worstSeverity(safeIssues.filter((issue) => issue.edgeId === edge.edgeId)),\n insertableBlocks,\n onInsert: readOnly ? undefined : (type: string) => store.getState().insertNodeOnEdge(type, edge.edgeId),\n },\n }));\n }, [graph.edges, selectedEdgeIds, runOverlay, labels, safeIssues, insertableBlocks, readOnly, store]);\n\n const handleNodesChange = useCallback(\n (changes: Array<NodeChange<TFlowBlockNode>>) => {\n const positions: Record<string, IFlowPosition> = {};\n const sizes: Record<string, { width: number; height: number }> = {};\n let selection: Array<string> | null = null;\n for (const change of changes) {\n if (change.type === 'dimensions' && change.dimensions) sizes[change.id] = change.dimensions;\n if (change.type === 'position' && change.position && !readOnly) positions[change.id] = change.position;\n if (change.type === 'select') {\n const current: Array<string> = selection ?? store.getState().selectedNodeIds;\n selection = change.selected\n ? [...new Set([...current, change.id])]\n : current.filter((id) => id !== change.id);\n }\n }\n if (Object.keys(sizes).length > 0) setMeasuredSizes((current) => ({ ...current, ...sizes }));\n if (Object.keys(positions).length > 0) store.getState().moveNodes(positions);\n if (selection) store.getState().setSelection({ nodeIds: selection });\n },\n [store, readOnly],\n );\n\n const handleEdgesChange = useCallback(\n (changes: Array<EdgeChange<TFlowConnectionEdge>>) => {\n let selection: Array<string> | null = null;\n for (const change of changes) {\n if (change.type !== 'select') continue;\n const current: Array<string> = selection ?? store.getState().selectedEdgeIds;\n selection = change.selected ? [...new Set([...current, change.id])] : current.filter((id) => id !== change.id);\n }\n if (selection) store.getState().setSelection({ edgeIds: selection });\n },\n [store],\n );\n\n const handleConnect = useCallback(\n (connection: Connection) => {\n if (readOnly || !isPortId(connection.sourceHandle)) return;\n store.getState().connect(connection.source, connection.sourceHandle, connection.target);\n },\n [store, readOnly],\n );\n\n const isValidConnection = useCallback(\n (connection: Connection | TFlowConnectionEdge) =>\n isPortId(connection.sourceHandle) &&\n canConnect(store.getState().graph, registry, {\n source: connection.source,\n sourcePort: connection.sourceHandle,\n target: connection.target,\n }),\n [store, registry],\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n if (readOnly || isTypingTarget(event.target)) return;\n const state = store.getState();\n const withModifier = event.metaKey || event.ctrlKey;\n const key = event.key.toLowerCase();\n\n if (event.key === 'Delete' || event.key === 'Backspace') state.removeSelected();\n else if (withModifier && key === 'z' && event.shiftKey) state.redo();\n else if (withModifier && key === 'z') state.undo();\n else if (withModifier && key === 'y') state.redo();\n else if (withModifier && key === 'c') state.copySelected();\n else if (withModifier && key === 'v') state.paste();\n else if (withModifier && key === 'd') state.duplicateSelected();\n else return;\n event.preventDefault();\n },\n [store, readOnly],\n );\n\n const handleLayout = useCallback(() => {\n const state = store.getState();\n state.recordHistory();\n state.moveNodes(layoutFlowByLevels(state.graph, registry));\n }, [store, registry]);\n\n return (\n <div\n role=\"region\"\n aria-label={ariaLabel ?? t('flow.canvas')}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n className={mergeClasses(\n 'umode-flow relative h-full min-h-[24rem] w-full rounded-xl bg-canvas focus:outline-none',\n className,\n )}\n >\n <ReactFlow<TFlowBlockNode, TFlowConnectionEdge>\n nodes={nodes}\n edges={edges}\n nodeTypes={NODE_TYPES}\n edgeTypes={EDGE_TYPES}\n onNodesChange={handleNodesChange}\n onEdgesChange={handleEdgesChange}\n onConnect={handleConnect}\n isValidConnection={isValidConnection}\n onNodeDragStart={() => {\n stopRevealing();\n store.getState().recordHistory();\n }}\n onMoveStart={(event) => {\n if (event) stopRevealing();\n }}\n onPaneClick={() => store.getState().setSelection({ nodeIds: [], edgeIds: [] })}\n deleteKeyCode={null}\n nodesDraggable={!readOnly}\n nodesConnectable={!readOnly}\n snapToGrid\n snapGrid={SNAP_GRID}\n minZoom={0.25}\n maxZoom={2}\n fitView\n fitViewOptions={{ padding: 0.2, maxZoom: 1 }}\n proOptions={PRO_OPTIONS}\n >\n {background === 'dots' && <Background variant={BackgroundVariant.Dots} gap={16} size={1} />}\n {nodes.length >= MINIMAP_MIN_NODES && (\n <MiniMap pannable zoomable position=\"bottom-right\" style={MINIMAP_SIZE} />\n )}\n <FlowZoomControls\n onUndo={readOnly ? undefined : () => store.getState().undo()}\n onRedo={readOnly ? undefined : () => store.getState().redo()}\n canUndo={canUndo}\n canRedo={canRedo}\n onLayout={readOnly ? undefined : handleLayout}\n position={controlsPosition}\n />\n </ReactFlow>\n </div>\n );\n};\n\nexport default FlowCanvas;\n"],"names":["useReactFlowStore"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FlowCanvas.js","sources":["../../../../src/components/UI/Flow/FlowCanvas.tsx"],"sourcesContent":["import { KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport { mergeClasses } from '@/helpers/mergeClasses';\nimport {\n Background,\n BackgroundVariant,\n type Connection,\n type EdgeChange,\n MarkerType,\n MiniMap,\n type NodeChange,\n Panel,\n type PanelPosition,\n type ProOptions,\n ReactFlow,\n SelectionMode,\n useReactFlow,\n useStore as useReactFlowStore,\n} from '@xyflow/react';\n\nimport FlowAnnotationDraftPreview from './FlowAnnotationDraftPreview';\nimport FlowAnnotationNode, { type TFlowAnnotationNode } from './FlowAnnotationNode';\nimport { FLOW_ANNOTATION_MIN_SIZE, FLOW_TEXT_CARET_OFFSET, scaleAnnotationPoints } from './flowAnnotations';\nimport FlowAnnotationToolbar, { FlowAnnotationLauncher } from './FlowAnnotationToolbar';\nimport FlowBlockNode, { FLOW_INPUT_HANDLE_ID, type IFlowAddableBlock, type TFlowBlockNode } from './FlowBlockNode';\nimport { flowOutputsOf } from './flowBlockRegistry';\nimport FlowConnectionEdge, { type TFlowConnectionEdge } from './FlowConnectionEdge';\nimport { useFlowBlockRegistry, useFlowEditor, useFlowEditorStore } from './FlowEditorProvider';\nimport type { IFlowAnnotationGeometry } from './flowEditorStore';\nimport { canConnect, isFlowNodeRemovable, layoutFlowByLevels } from './flowGraph';\nimport FlowSelectionBar from './FlowSelectionBar';\nimport type {\n IFlowAnnotation,\n IFlowIssue,\n IFlowPosition,\n IFlowRunOverlay,\n TFlowAnnotationKind,\n TFlowAnnotationStroke,\n TFlowAnnotationTone,\n TFlowCanvasTool,\n TFlowIssueSeverity,\n TFlowPortId,\n TFlowShapeTool,\n} from './flowTypes';\nimport { boundsOfRects, type IFlowRect, type IFlowViewport, viewportToReveal } from './flowViewport';\nimport FlowZoomControls from './FlowZoomControls';\nimport { useFlowAnnotationDrawing } from './useFlowAnnotationDrawing';\nimport { useFlowLabels } from './useFlowLabels';\n\nimport '@xyflow/react/dist/base.css';\nimport '../../../styles/flow.css';\n\nconst PRO_OPTIONS: ProOptions = { hideAttribution: true };\nconst NODE_TYPES = { flowBlock: FlowBlockNode, flowAnnotation: FlowAnnotationNode };\nconst EDGE_TYPES = { flowConnection: FlowConnectionEdge };\nconst MINIMAP_SIZE = { width: 160, height: 104 };\n/** A small flow fits on screen whole, where a minimap only covers blocks. */\nconst MINIMAP_MIN_NODES = 5;\n/** Blocks snap to this grid; annotations move freely, so the snap is applied here and not by xyflow. */\nconst BLOCK_GRID = 16;\nconst PORTS: Array<TFlowPortId> = ['next', 'true', 'false', 'error'];\nconst REVEAL_DURATION_MS = 200;\n/**\n * Navigation reads like Figma: two fingers on a trackpad (or the wheel) pan, a pinch or Cmd/Ctrl + wheel\n * zooms, Space + drag or the middle button pans, and a drag on the empty canvas draws a selection.\n */\nconst PAN_ON_DRAG_BUTTONS = [1];\nconst MULTI_SELECTION_KEYS = ['Shift', 'Meta', 'Control'];\nconst TOOL_BY_KEY: Record<string, TFlowCanvasTool> = {\n v: 'select',\n p: 'drawing',\n l: 'line',\n r: 'rectangle',\n o: 'ellipse',\n a: 'arrow',\n t: 'text',\n};\nconst SHAPE_TOOLS: Array<TFlowShapeTool> = ['line', 'arrow', 'rectangle', 'ellipse', 'step'];\n/** Annotations sit under the blocks: a frame drawn around them never covers what it points at. */\nconst ANNOTATION_Z_INDEX = -1;\n\ntype TFlowCanvasNode = TFlowBlockNode | TFlowAnnotationNode;\n\nexport interface IFlowCanvasProps {\n issues?: Array<IFlowIssue>;\n runOverlay?: IFlowRunOverlay | null;\n readOnly?: boolean;\n /** Accessible name of the canvas region, from `t()`. Defaults to the library label. */\n ariaLabel?: string;\n className?: string;\n /** Where the zoom and history controls sit. */\n controlsPosition?: PanelPosition;\n /** The dot grid behind the blocks. Blocks still snap to the grid when it is hidden. */\n background?: 'dots' | 'none';\n /** Shows the drawing toolbar (pen, shapes, text). Annotations already in the graph render either way. */\n annotationTools?: boolean;\n}\n\nconst isPortId = (value: string | null | undefined): value is TFlowPortId => PORTS.includes(value as TFlowPortId);\n\nconst worstSeverity = (issues: Array<IFlowIssue>): TFlowIssueSeverity | undefined => {\n if (issues.some((issue) => issue.severity === 'error')) return 'error';\n if (issues.some((issue) => issue.severity === 'warning')) return 'warning';\n return undefined;\n};\n\nconst isTypingTarget = (target: EventTarget | null): boolean =>\n target instanceof HTMLElement &&\n (target.isContentEditable || ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName));\n\n/**\n * The editing surface. Renders the graph held by `FlowEditorProvider` and turns every\n * gesture (drag, connect, select, delete, shortcuts) into a store action, so undo\n * and the dirty flag see all of them.\n */\nconst FlowCanvas: React.FC<IFlowCanvasProps> = ({\n issues = [],\n runOverlay,\n readOnly = false,\n ariaLabel,\n className,\n controlsPosition,\n background = 'dots',\n annotationTools = false,\n}) => {\n const { t } = useTranslation('ui');\n const labels = useFlowLabels();\n const registry = useFlowBlockRegistry();\n const store = useFlowEditorStore();\n\n const graph = useFlowEditor((state) => state.graph);\n const selectedNodeIds = useFlowEditor((state) => state.selectedNodeIds);\n const selectedEdgeIds = useFlowEditor((state) => state.selectedEdgeIds);\n const selectedAnnotationIds = useFlowEditor((state) => state.selectedAnnotationIds);\n const isAreaSelecting = useFlowEditor((state) => state.isAreaSelecting);\n const canUndo = useFlowEditor((state) => state.past.length > 0);\n const canRedo = useFlowEditor((state) => state.future.length > 0);\n\n const safeIssues = useMemo(() => (Array.isArray(issues) ? issues : []), [issues]);\n // Measured sizes are view state, not part of the flow: kept here so the minimap can draw the blocks.\n const [measuredSizes, setMeasuredSizes] = useState<Record<string, { width: number; height: number }>>({});\n\n const { getViewport, setViewport, screenToFlowPosition } = useReactFlow<TFlowCanvasNode, TFlowConnectionEdge>();\n const containerRef = useRef<HTMLDivElement>(null);\n const [tool, setTool] = useState<TFlowCanvasTool>('select');\n const [shapeTool, setShapeTool] = useState<TFlowShapeTool>('rectangle');\n const [toolsOpen, setToolsOpen] = useState(false);\n const [annotationTone, setAnnotationTone] = useState<TFlowAnnotationTone>('danger');\n const [annotationStroke, setAnnotationStroke] = useState<TFlowAnnotationStroke>('medium');\n const [editingAnnotationId, setEditingAnnotationId] = useState<string | null>(null);\n const canAnnotate = annotationTools && !readOnly;\n const isDrawing = canAnnotate && tool !== 'select';\n const annotations = useMemo(() => graph.annotations ?? [], [graph.annotations]);\n\n const changeTool = useCallback((nextTool: TFlowCanvasTool) => {\n setTool(nextTool);\n setToolsOpen(true);\n if (SHAPE_TOOLS.includes(nextTool as TFlowShapeTool)) setShapeTool(nextTool as TFlowShapeTool);\n }, []);\n\n const closeTools = useCallback(() => {\n setTool('select');\n setToolsOpen(false);\n }, []);\n\n // The pen and the steps keep going (stroke after stroke, 1, 2, 3...); every other tool hands back the pointer.\n const handleAnnotationCreated = useCallback(\n (annotationId: string, kind: TFlowAnnotationKind) => {\n if (kind === 'drawing' || kind === 'step') {\n store.getState().setSelection({ annotationIds: [] });\n return;\n }\n setTool('select');\n if (kind === 'text') setEditingAnnotationId(annotationId);\n },\n [store],\n );\n\n const draft = useFlowAnnotationDrawing({\n containerRef,\n tool,\n tone: annotationTone,\n stroke: annotationStroke,\n enabled: canAnnotate,\n onCreated: handleAnnotationCreated,\n });\n // As in Excalidraw: a double click on the empty canvas starts a note right there, ready to type.\n useEffect(() => {\n if (!canAnnotate) return undefined;\n const pane = containerRef.current?.querySelector<HTMLElement>('.react-flow__pane');\n if (!pane) return undefined;\n const handleDoubleClick = (event: MouseEvent) => {\n if (event.target !== pane) return;\n const point = screenToFlowPosition({ x: event.clientX, y: event.clientY }, { snapToGrid: false });\n const annotationId = store.getState().addAnnotation({\n kind: 'text',\n tone: annotationTone,\n stroke: annotationStroke,\n position: { x: point.x, y: point.y - FLOW_TEXT_CARET_OFFSET },\n size: { width: FLOW_ANNOTATION_MIN_SIZE, height: FLOW_ANNOTATION_MIN_SIZE },\n text: '',\n });\n setTool('select');\n setEditingAnnotationId(annotationId);\n };\n pane.addEventListener('dblclick', handleDoubleClick);\n return () => pane.removeEventListener('dblclick', handleDoubleClick);\n }, [canAnnotate, annotationTone, annotationStroke, screenToFlowPosition, store]);\n\n const canvasWidth = useReactFlowStore((state) => state.width);\n const canvasHeight = useReactFlowStore((state) => state.height);\n // Blocks just added and selected: kept in view until the user moves the view or the selection, so\n // the host panel that opens on selection (and narrows the canvas) cannot hide them.\n const [revealNodeIds, setRevealNodeIds] = useState<Array<string>>([]);\n const knownNodeIdsRef = useRef<Set<string> | null>(null);\n const pendingViewportRef = useRef<{ viewport: IFlowViewport; settlesAt: number } | null>(null);\n\n useEffect(() => {\n const currentIds = new Set(graph.nodes.map((node) => node.nodeId));\n const knownIds = knownNodeIdsRef.current;\n knownNodeIdsRef.current = currentIds;\n const addedAndSelected = knownIds ? selectedNodeIds.filter((id) => currentIds.has(id) && !knownIds.has(id)) : [];\n if (addedAndSelected.length > 0) {\n setRevealNodeIds(addedAndSelected);\n return;\n }\n setRevealNodeIds((current) =>\n current.length > 0 && !current.every((id) => selectedNodeIds.includes(id)) ? [] : current,\n );\n }, [graph.nodes, selectedNodeIds]);\n\n useEffect(() => {\n if (revealNodeIds.length === 0) return;\n const rects: Array<IFlowRect> = [];\n for (const nodeId of revealNodeIds) {\n const node = graph.nodes.find((candidate) => candidate.nodeId === nodeId);\n const size = measuredSizes[nodeId];\n if (!node || !size) return;\n rects.push({ ...node.position, ...size });\n }\n const bounds = boundsOfRects(rects);\n if (!bounds) return;\n // A pan still animating reports an in-between viewport; build on where it is heading instead.\n const pending = pendingViewportRef.current;\n const baseViewport = pending && pending.settlesAt > Date.now() ? pending.viewport : getViewport();\n const target = viewportToReveal(baseViewport, bounds, { width: canvasWidth, height: canvasHeight });\n if (!target) return;\n pendingViewportRef.current = { viewport: target, settlesAt: Date.now() + REVEAL_DURATION_MS };\n void setViewport(target, { duration: REVEAL_DURATION_MS });\n }, [revealNodeIds, graph.nodes, measuredSizes, canvasWidth, canvasHeight, getViewport, setViewport]);\n\n // xyflow reports no end for a cancelled pointer, so the release is also heard on the window.\n const handleAreaSelectionStart = useCallback(() => {\n store.getState().setAreaSelecting(true);\n const finishAreaSelection = () => {\n store.getState().setAreaSelecting(false);\n window.removeEventListener('pointerup', finishAreaSelection);\n window.removeEventListener('pointercancel', finishAreaSelection);\n window.removeEventListener('blur', finishAreaSelection);\n };\n window.addEventListener('pointerup', finishAreaSelection);\n window.addEventListener('pointercancel', finishAreaSelection);\n window.addEventListener('blur', finishAreaSelection);\n }, [store]);\n\n const stopRevealing = useCallback(() => {\n pendingViewportRef.current = null;\n setRevealNodeIds((current) => (current.length > 0 ? [] : current));\n }, []);\n\n const addableBlocks = useMemo<Array<IFlowAddableBlock>>(() => {\n if (readOnly) return [];\n return registry.list\n .filter((definition) => definition.category !== 'trigger')\n .filter(\n (definition) =>\n definition.maxInstances === undefined ||\n graph.nodes.filter((node) => node.type === definition.type).length < definition.maxInstances,\n )\n .map((definition) => ({ type: definition.type, label: definition.label, icon: definition.icon }));\n }, [registry, graph.nodes, readOnly]);\n\n // A block in the middle of an edge needs an output to carry the flow on to the old target.\n const insertableBlocks = useMemo<Array<IFlowAddableBlock>>(\n () =>\n addableBlocks.filter((block) => {\n const definition = registry.get(block.type);\n return definition ? flowOutputsOf(definition, definition.createDefaultConfig()).length > 0 : false;\n }),\n [addableBlocks, registry],\n );\n\n const blockNodes = useMemo<Array<TFlowBlockNode>>(\n () =>\n graph.nodes.map((node) => {\n const definition = registry.get(node.type);\n const nodeIssues = safeIssues.filter((issue) => issue.nodeId === node.nodeId);\n return {\n id: node.nodeId,\n type: 'flowBlock',\n position: node.position,\n measured: measuredSizes[node.nodeId],\n selected: selectedNodeIds.includes(node.nodeId),\n draggable: !readOnly,\n connectable: !readOnly,\n data: {\n icon: definition?.resolveIcon?.(node.config) ?? definition?.icon ?? 'help',\n title: node.label ?? definition?.resolveLabel?.(node.config) ?? definition?.label ?? node.type,\n categoryLabel: definition ? labels.categories[definition.category] : '',\n summary: definition\n ? definition.renderSummary(node.config)\n : labels.issueMessage({ code: 'unknownBlock', severity: 'error' }),\n hasInput: definition ? definition.category !== 'trigger' : true,\n outputs: flowOutputsOf(definition, node.config),\n connectedPorts: graph.edges.filter((edge) => edge.source === node.nodeId).map((edge) => edge.sourcePort),\n portLabels: labels.ports,\n addableBlocks,\n onAddFromPort: readOnly\n ? undefined\n : (port, type) => store.getState().addNodeFromPort(type, node.nodeId, port),\n disabled: node.disabled,\n issueSeverity: worstSeverity(nodeIssues),\n issueCount: nodeIssues.length,\n runStatus: runOverlay?.steps?.[node.nodeId],\n },\n };\n }),\n [\n graph.nodes,\n graph.edges,\n registry,\n safeIssues,\n selectedNodeIds,\n readOnly,\n labels,\n runOverlay,\n measuredSizes,\n addableBlocks,\n store,\n ],\n );\n\n const resizeAnnotation = useCallback(\n (annotationId: string, geometry: IFlowAnnotationGeometry) => {\n const annotation = (store.getState().graph.annotations ?? []).find(\n (candidate) => candidate.annotationId === annotationId,\n );\n if (!annotation || !geometry.size) return;\n store.getState().setAnnotationGeometry({\n [annotationId]: {\n ...geometry,\n points: scaleAnnotationPoints(annotation.points, annotation.size, geometry.size),\n },\n });\n },\n [store],\n );\n\n const finishTextEdit = useCallback(\n (annotationId: string) => {\n setEditingAnnotationId((current) => (current === annotationId ? null : current));\n const annotation = (store.getState().graph.annotations ?? []).find(\n (candidate) => candidate.annotationId === annotationId,\n );\n if (annotation && !annotation.text?.trim()) store.getState().removeAnnotations([annotationId]);\n // Escape leaves the focus nowhere; give it back to the canvas so the tool keys keep working.\n window.setTimeout(() => {\n if (!document.activeElement || document.activeElement === document.body)\n containerRef.current?.focus({ preventScroll: true });\n }, 0);\n },\n [store],\n );\n\n const annotationNodes = useMemo<Array<TFlowAnnotationNode>>(\n () =>\n annotations.map((annotation) => {\n const selected = selectedAnnotationIds.includes(annotation.annotationId);\n const editing = editingAnnotationId === annotation.annotationId;\n return {\n id: annotation.annotationId,\n type: 'flowAnnotation',\n position: annotation.position,\n // A text note takes the size of what is written in it; every other kind keeps its box.\n ...(annotation.kind === 'text' ? {} : { width: annotation.size.width, height: annotation.size.height }),\n selected,\n draggable: !readOnly && !editing,\n connectable: false,\n zIndex: ANNOTATION_Z_INDEX,\n // A shape is only grabbed by its stroke until it is selected; text is grabbed anywhere.\n style: { pointerEvents: annotation.kind === 'text' || selected ? 'all' : 'none' },\n data: {\n annotation,\n readOnly,\n editing,\n placeholder: t('flow.annotations.textPlaceholder'),\n onTextChange: (text: string) => store.getState().updateAnnotations([annotation.annotationId], { text }),\n onEditStart: () => setEditingAnnotationId(annotation.annotationId),\n onEditEnd: () => finishTextEdit(annotation.annotationId),\n onResizeStart: () => store.getState().recordHistory(),\n onResize: (geometry: IFlowAnnotationGeometry) => resizeAnnotation(annotation.annotationId, geometry),\n },\n };\n }),\n [annotations, selectedAnnotationIds, editingAnnotationId, readOnly, t, store, finishTextEdit, resizeAnnotation],\n );\n\n const nodes = useMemo<Array<TFlowCanvasNode>>(\n () => [...annotationNodes, ...blockNodes],\n [annotationNodes, blockNodes],\n );\n\n const edges = useMemo<Array<TFlowConnectionEdge>>(() => {\n const traversed = new Set(runOverlay?.traversedEdgeIds ?? []);\n return graph.edges.map((edge) => ({\n id: edge.edgeId,\n type: 'flowConnection',\n source: edge.source,\n sourceHandle: edge.sourcePort,\n target: edge.target,\n targetHandle: FLOW_INPUT_HANDLE_ID,\n selected: selectedEdgeIds.includes(edge.edgeId),\n markerEnd: { type: MarkerType.ArrowClosed, width: 16, height: 16 },\n data: {\n label: edge.sourcePort === 'next' ? undefined : labels.ports[edge.sourcePort],\n traversed: traversed.has(edge.edgeId),\n issueSeverity: worstSeverity(safeIssues.filter((issue) => issue.edgeId === edge.edgeId)),\n insertableBlocks,\n onInsert: readOnly ? undefined : (type: string) => store.getState().insertNodeOnEdge(type, edge.edgeId),\n },\n }));\n }, [graph.edges, selectedEdgeIds, runOverlay, labels, safeIssues, insertableBlocks, readOnly, store]);\n\n const handleNodesChange = useCallback(\n (changes: Array<NodeChange<TFlowCanvasNode>>) => {\n const state = store.getState();\n const annotationIds = new Set((state.graph.annotations ?? []).map((annotation) => annotation.annotationId));\n const positions: Record<string, IFlowPosition> = {};\n const annotationGeometry: Record<string, IFlowAnnotationGeometry> = {};\n const sizes: Record<string, { width: number; height: number }> = {};\n let nodeSelection: Array<string> | null = null;\n let annotationSelection: Array<string> | null = null;\n const toggled = (current: Array<string>, id: string, selected: boolean): Array<string> =>\n selected ? [...new Set([...current, id])] : current.filter((candidate) => candidate !== id);\n\n for (const change of changes) {\n if (change.type === 'dimensions' && change.dimensions) sizes[change.id] = change.dimensions;\n if (change.type === 'position' && change.position && !readOnly) {\n if (annotationIds.has(change.id)) annotationGeometry[change.id] = { position: change.position };\n else\n positions[change.id] = {\n x: Math.round(change.position.x / BLOCK_GRID) * BLOCK_GRID,\n y: Math.round(change.position.y / BLOCK_GRID) * BLOCK_GRID,\n };\n }\n if (change.type === 'select') {\n if (annotationIds.has(change.id))\n annotationSelection = toggled(\n annotationSelection ?? state.selectedAnnotationIds,\n change.id,\n change.selected,\n );\n else nodeSelection = toggled(nodeSelection ?? state.selectedNodeIds, change.id, change.selected);\n }\n }\n if (Object.keys(sizes).length > 0) setMeasuredSizes((current) => ({ ...current, ...sizes }));\n if (Object.keys(positions).length > 0) state.moveNodes(positions);\n if (Object.keys(annotationGeometry).length > 0) state.setAnnotationGeometry(annotationGeometry);\n if (nodeSelection || annotationSelection)\n state.setSelection({\n ...(nodeSelection ? { nodeIds: nodeSelection } : {}),\n ...(annotationSelection ? { annotationIds: annotationSelection } : {}),\n });\n },\n [store, readOnly],\n );\n\n const handleEdgesChange = useCallback(\n (changes: Array<EdgeChange<TFlowConnectionEdge>>) => {\n let selection: Array<string> | null = null;\n for (const change of changes) {\n if (change.type !== 'select') continue;\n const current: Array<string> = selection ?? store.getState().selectedEdgeIds;\n selection = change.selected ? [...new Set([...current, change.id])] : current.filter((id) => id !== change.id);\n }\n if (selection) store.getState().setSelection({ edgeIds: selection });\n },\n [store],\n );\n\n const handleConnect = useCallback(\n (connection: Connection) => {\n if (readOnly || !isPortId(connection.sourceHandle)) return;\n store.getState().connect(connection.source, connection.sourceHandle, connection.target);\n },\n [store, readOnly],\n );\n\n const isValidConnection = useCallback(\n (connection: Connection | TFlowConnectionEdge) =>\n isPortId(connection.sourceHandle) &&\n canConnect(store.getState().graph, registry, {\n source: connection.source,\n sourcePort: connection.sourceHandle,\n target: connection.target,\n }),\n [store, registry],\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n if (readOnly || isTypingTarget(event.target)) return;\n const state = store.getState();\n const withModifier = event.metaKey || event.ctrlKey;\n const key = event.key.toLowerCase();\n\n if (event.key === 'Delete' || event.key === 'Backspace') state.removeSelected();\n else if (event.key === 'Escape' && isDrawing) setTool('select');\n else if (event.key === 'Escape') state.setSelection({ nodeIds: [], edgeIds: [], annotationIds: [] });\n else if (withModifier && key === 'a')\n state.setSelection({\n nodeIds: state.graph.nodes.map((node) => node.nodeId),\n annotationIds: (state.graph.annotations ?? []).map((annotation) => annotation.annotationId),\n });\n else if (canAnnotate && !withModifier && !event.altKey && TOOL_BY_KEY[key]) changeTool(TOOL_BY_KEY[key]);\n else if (withModifier && key === 'z' && event.shiftKey) state.redo();\n else if (withModifier && key === 'z') state.undo();\n else if (withModifier && key === 'y') state.redo();\n else if (withModifier && key === 'c') state.copySelected();\n else if (withModifier && key === 'v') state.paste();\n else if (withModifier && key === 'd') state.duplicateSelected();\n else return;\n event.preventDefault();\n },\n [store, readOnly, isDrawing, canAnnotate, changeTool],\n );\n\n const canDeleteSelection = useMemo(\n () =>\n selectedAnnotationIds.length > 0 ||\n graph.nodes.some((node) => selectedNodeIds.includes(node.nodeId) && isFlowNodeRemovable(node, registry)),\n [graph.nodes, selectedNodeIds, selectedAnnotationIds, registry],\n );\n\n // The toolbar shows the colour and weight of what is selected, when it all shares one, and changing\n // them restyles the selection as well as what is drawn next.\n const selectedAnnotations = useMemo(\n () => annotations.filter((annotation) => selectedAnnotationIds.includes(annotation.annotationId)),\n [annotations, selectedAnnotationIds],\n );\n const sharedSelectedValue = <TValue,>(valueOf: (annotation: IFlowAnnotation) => TValue): TValue | null => {\n const values = new Set(selectedAnnotations.map(valueOf));\n return values.size === 1 ? ([...values][0] ?? null) : null;\n };\n const shownTone = sharedSelectedValue((annotation) => annotation.tone) ?? annotationTone;\n const shownStroke = sharedSelectedValue((annotation) => annotation.stroke ?? 'medium') ?? annotationStroke;\n const changeAnnotationTone = (tone: TFlowAnnotationTone) => {\n setAnnotationTone(tone);\n if (selectedAnnotations.length > 0) store.getState().updateAnnotations(selectedAnnotationIds, { tone });\n };\n const changeAnnotationStroke = (stroke: TFlowAnnotationStroke) => {\n setAnnotationStroke(stroke);\n if (selectedAnnotations.length > 0) store.getState().updateAnnotations(selectedAnnotationIds, { stroke });\n };\n\n const selectedCount = selectedNodeIds.length + selectedAnnotationIds.length;\n const showsSelectionBar = !readOnly && !isAreaSelecting && editingAnnotationId === null && selectedCount > 1;\n const clearSelection = () => store.getState().setSelection({ nodeIds: [], edgeIds: [], annotationIds: [] });\n const handlePaneClick = () => {\n if (editingAnnotationId) finishTextEdit(editingAnnotationId);\n clearSelection();\n };\n\n const handleLayout = useCallback(() => {\n const state = store.getState();\n state.recordHistory();\n state.moveNodes(layoutFlowByLevels(state.graph, registry));\n }, [store, registry]);\n\n return (\n <div\n role=\"region\"\n aria-label={ariaLabel ?? t('flow.canvas')}\n ref={containerRef}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n className={mergeClasses(\n 'umode-flow relative h-full min-h-[24rem] w-full select-none rounded-xl bg-canvas focus:outline-none',\n isDrawing && 'umode-flow--drawing',\n className,\n )}\n >\n <ReactFlow<TFlowCanvasNode, TFlowConnectionEdge>\n nodes={nodes}\n edges={edges}\n nodeTypes={NODE_TYPES}\n edgeTypes={EDGE_TYPES}\n onNodesChange={handleNodesChange}\n onEdgesChange={handleEdgesChange}\n onConnect={handleConnect}\n isValidConnection={isValidConnection}\n onNodeDragStart={() => {\n stopRevealing();\n store.getState().recordHistory();\n }}\n onMoveStart={(event) => {\n if (event) stopRevealing();\n }}\n onPaneClick={handlePaneClick}\n onSelectionStart={handleAreaSelectionStart}\n onSelectionEnd={() => store.getState().setAreaSelecting(false)}\n deleteKeyCode={null}\n selectionKeyCode={null}\n multiSelectionKeyCode={MULTI_SELECTION_KEYS}\n selectionOnDrag={!isDrawing}\n selectionMode={SelectionMode.Partial}\n panOnDrag={PAN_ON_DRAG_BUTTONS}\n panOnScroll\n panOnScrollSpeed={1}\n zoomOnDoubleClick={false}\n nodesDraggable={!readOnly}\n nodesConnectable={!readOnly}\n minZoom={0.25}\n maxZoom={2}\n fitView\n fitViewOptions={{ padding: 0.2, maxZoom: 1 }}\n proOptions={PRO_OPTIONS}\n >\n {background === 'dots' && <Background variant={BackgroundVariant.Dots} gap={16} size={1} />}\n {draft && <FlowAnnotationDraftPreview draft={draft} />}\n {graph.nodes.length >= MINIMAP_MIN_NODES && (\n <MiniMap pannable zoomable position=\"bottom-right\" style={MINIMAP_SIZE} />\n )}\n {(showsSelectionBar || canAnnotate) && (\n <Panel position=\"bottom-center\" className=\"flex flex-col items-center gap-y-2\">\n {showsSelectionBar && (\n <FlowSelectionBar\n selectedCount={selectedCount}\n canDelete={canDeleteSelection}\n onDuplicate={() => store.getState().duplicateSelected()}\n onDelete={() => store.getState().removeSelected()}\n onClear={clearSelection}\n />\n )}\n {canAnnotate &&\n (toolsOpen ? (\n <FlowAnnotationToolbar\n tool={tool}\n shapeTool={shapeTool}\n onToolChange={changeTool}\n tone={shownTone}\n onToneChange={changeAnnotationTone}\n stroke={shownStroke}\n onStrokeChange={changeAnnotationStroke}\n canUndo={canUndo}\n canRedo={canRedo}\n onUndo={() => store.getState().undo()}\n onRedo={() => store.getState().redo()}\n canDelete={canDeleteSelection}\n onDelete={() => store.getState().removeSelected()}\n onClose={closeTools}\n />\n ) : (\n <FlowAnnotationLauncher onOpen={() => setToolsOpen(true)} />\n ))}\n </Panel>\n )}\n <FlowZoomControls\n onUndo={readOnly ? undefined : () => store.getState().undo()}\n onRedo={readOnly ? undefined : () => store.getState().redo()}\n canUndo={canUndo}\n canRedo={canRedo}\n onLayout={readOnly ? undefined : handleLayout}\n position={controlsPosition}\n />\n </ReactFlow>\n </div>\n );\n};\n\nexport default FlowCanvas;\n"],"names":["useReactFlowStore"],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ import { MaterialSymbol } from "../MaterialSymbol/index.js";
|
|
|
7
7
|
import NoDataFound from "../NoDataFound/index.js";
|
|
8
8
|
import { mergeClasses } from "../../../helpers/mergeClasses.js";
|
|
9
9
|
import { useFlowBlockRegistry, useFlowEditorStore, useFlowEditor } from "./FlowEditorProvider.js";
|
|
10
|
+
import { selectFlowInspectedNodeId } from "./flowEditorStore.js";
|
|
10
11
|
import { isFlowNodeRemovable } from "./flowGraph.js";
|
|
11
12
|
import { useFlowLabels } from "./useFlowLabels.js";
|
|
12
13
|
const FlowInspector = ({
|
|
@@ -19,15 +20,15 @@ const FlowInspector = ({
|
|
|
19
20
|
const labels = useFlowLabels();
|
|
20
21
|
const registry = useFlowBlockRegistry();
|
|
21
22
|
const store = useFlowEditorStore();
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
);
|
|
23
|
+
const selectedNode = useFlowEditor((state) => {
|
|
24
|
+
const inspectedNodeId = selectFlowInspectedNodeId(state);
|
|
25
|
+
return inspectedNodeId ? state.graph.nodes.find((node) => node.nodeId === inspectedNodeId) : void 0;
|
|
26
|
+
});
|
|
26
27
|
const shellClass = mergeClasses(
|
|
27
28
|
"flex h-full min-h-0 flex-col gap-y-3 rounded-xl border border-border bg-surface p-4",
|
|
28
29
|
className
|
|
29
30
|
);
|
|
30
|
-
if (!selectedNode
|
|
31
|
+
if (!selectedNode) {
|
|
31
32
|
if (hideWhenEmpty) return null;
|
|
32
33
|
return /* @__PURE__ */ jsx("aside", { className: shellClass, "aria-label": t("flow.inspector.title"), children: /* @__PURE__ */ jsx(NoDataFound, { size: "sm", variant: "waiting", message: t("flow.inspector.empty") }) });
|
|
33
34
|
}
|