@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.
Files changed (42) hide show
  1. package/dist/assets/umode-ui.css +32 -0
  2. package/dist/components/UI/BulkActionBar.js +1 -1
  3. package/dist/components/UI/BulkActionBar.js.map +1 -1
  4. package/dist/components/UI/Flow/FlowAnnotationDraftPreview.d.ts +6 -0
  5. package/dist/components/UI/Flow/FlowAnnotationDraftPreview.js +65 -0
  6. package/dist/components/UI/Flow/FlowAnnotationDraftPreview.js.map +1 -0
  7. package/dist/components/UI/Flow/FlowAnnotationNode.d.ts +34 -0
  8. package/dist/components/UI/Flow/FlowAnnotationNode.js +231 -0
  9. package/dist/components/UI/Flow/FlowAnnotationNode.js.map +1 -0
  10. package/dist/components/UI/Flow/FlowAnnotationToolbar.d.ts +32 -0
  11. package/dist/components/UI/Flow/FlowAnnotationToolbar.js +298 -0
  12. package/dist/components/UI/Flow/FlowAnnotationToolbar.js.map +1 -0
  13. package/dist/components/UI/Flow/FlowCanvas.d.ts +2 -0
  14. package/dist/components/UI/Flow/FlowCanvas.js +278 -20
  15. package/dist/components/UI/Flow/FlowCanvas.js.map +1 -1
  16. package/dist/components/UI/Flow/FlowInspector.js +6 -5
  17. package/dist/components/UI/Flow/FlowInspector.js.map +1 -1
  18. package/dist/components/UI/Flow/FlowSelectionBar.d.ts +10 -0
  19. package/dist/components/UI/Flow/FlowSelectionBar.js +56 -0
  20. package/dist/components/UI/Flow/FlowSelectionBar.js.map +1 -0
  21. package/dist/components/UI/Flow/flowAnnotations.d.ts +38 -0
  22. package/dist/components/UI/Flow/flowAnnotations.js +123 -0
  23. package/dist/components/UI/Flow/flowAnnotations.js.map +1 -0
  24. package/dist/components/UI/Flow/flowEditorStore.d.ts +29 -5
  25. package/dist/components/UI/Flow/flowEditorStore.js +123 -23
  26. package/dist/components/UI/Flow/flowEditorStore.js.map +1 -1
  27. package/dist/components/UI/Flow/flowTypes.d.ts +30 -0
  28. package/dist/components/UI/Flow/index.d.ts +2 -1
  29. package/dist/components/UI/Flow/index.js +5 -1
  30. package/dist/components/UI/Flow/index.js.map +1 -1
  31. package/dist/components/UI/Flow/useFlowAnnotationDrawing.d.ts +25 -0
  32. package/dist/components/UI/Flow/useFlowAnnotationDrawing.js +117 -0
  33. package/dist/components/UI/Flow/useFlowAnnotationDrawing.js.map +1 -0
  34. package/dist/locales/en/ui.json.d.ts +41 -0
  35. package/dist/locales/en/ui.json.js +1 -1
  36. package/dist/locales/index.d.ts +82 -0
  37. package/dist/locales/pt/ui.json.d.ts +41 -0
  38. package/dist/locales/pt/ui.json.js +1 -1
  39. package/package.json +1 -1
  40. package/src/assets/fonts/LICENSE-Virgil.txt +45 -0
  41. package/src/assets/fonts/virgil.woff2 +0 -0
  42. 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, useEffect, useCallback } from "react";
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 SNAP_GRID = [16, 16];
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 nodes = useMemo(
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 selection = null;
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) positions[change.id] = change.position;
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
- const current = selection ?? store.getState().selectedNodeIds;
179
- selection = change.selected ? [.../* @__PURE__ */ new Set([...current, change.id])] : current.filter((id) => id !== change.id);
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) store.getState().moveNodes(positions);
184
- if (selection) store.getState().setSelection({ nodeIds: selection });
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: () => store.getState().setSelection({ nodeIds: [], edgeIds: [] }),
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
- nodes.length >= MINIMAP_MIN_NODES && /* @__PURE__ */ jsx(MiniMap, { pannable: true, zoomable: true, position: "bottom-right", style: MINIMAP_SIZE }),
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 selectedNodeIds = useFlowEditor((state) => state.selectedNodeIds);
23
- const selectedNode = useFlowEditor(
24
- (state) => state.selectedNodeIds.length === 1 ? state.graph.nodes.find((node) => node.nodeId === state.selectedNodeIds[0]) : void 0
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 || selectedNodeIds.length !== 1) {
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
  }