@octaviaflow/core 3.0.18 → 3.1.0-beta.52

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 (251) hide show
  1. package/dist/chunk-2NGC7AI3.js +2637 -0
  2. package/dist/chunk-2NGC7AI3.js.map +1 -0
  3. package/dist/chunk-2O6K5PLY.js +2637 -0
  4. package/dist/chunk-2O6K5PLY.js.map +1 -0
  5. package/dist/chunk-2ZIOFEIS.js +3001 -0
  6. package/dist/chunk-2ZIOFEIS.js.map +1 -0
  7. package/dist/chunk-3DWB2PUF.js +2991 -0
  8. package/dist/chunk-3DWB2PUF.js.map +1 -0
  9. package/dist/chunk-4ZALUTZS.js +2936 -0
  10. package/dist/chunk-4ZALUTZS.js.map +1 -0
  11. package/dist/chunk-5ARKSRED.js +2951 -0
  12. package/dist/chunk-5ARKSRED.js.map +1 -0
  13. package/dist/chunk-5OSGSJMM.js +2981 -0
  14. package/dist/chunk-5OSGSJMM.js.map +1 -0
  15. package/dist/chunk-5YQQMEF3.js +2981 -0
  16. package/dist/chunk-5YQQMEF3.js.map +1 -0
  17. package/dist/chunk-6QCJK7H7.js +2991 -0
  18. package/dist/chunk-6QCJK7H7.js.map +1 -0
  19. package/dist/chunk-6QDYERZD.js +2944 -0
  20. package/dist/chunk-6QDYERZD.js.map +1 -0
  21. package/dist/chunk-76TP67ED.js +2984 -0
  22. package/dist/chunk-76TP67ED.js.map +1 -0
  23. package/dist/chunk-A6KMO4JV.js +2949 -0
  24. package/dist/chunk-A6KMO4JV.js.map +1 -0
  25. package/dist/chunk-B7FTWSTM.js +2938 -0
  26. package/dist/chunk-B7FTWSTM.js.map +1 -0
  27. package/dist/chunk-BCO6M26F.js +2940 -0
  28. package/dist/chunk-BCO6M26F.js.map +1 -0
  29. package/dist/chunk-C3UD2AZ5.js +2637 -0
  30. package/dist/chunk-C3UD2AZ5.js.map +1 -0
  31. package/dist/chunk-CEUP4NK2.js +2850 -0
  32. package/dist/chunk-CEUP4NK2.js.map +1 -0
  33. package/dist/chunk-DBNSBJO7.js +2993 -0
  34. package/dist/chunk-DBNSBJO7.js.map +1 -0
  35. package/dist/chunk-ECIHUVU7.js +2986 -0
  36. package/dist/chunk-ECIHUVU7.js.map +1 -0
  37. package/dist/chunk-EERNYLFL.js +2860 -0
  38. package/dist/chunk-EERNYLFL.js.map +1 -0
  39. package/dist/chunk-EKFDJX4G.js +2872 -0
  40. package/dist/chunk-EKFDJX4G.js.map +1 -0
  41. package/dist/chunk-GJA3GJUZ.js +2844 -0
  42. package/dist/chunk-GJA3GJUZ.js.map +1 -0
  43. package/dist/chunk-HDOTOZNA.js +2936 -0
  44. package/dist/chunk-HDOTOZNA.js.map +1 -0
  45. package/dist/chunk-IOKUV7FD.js +2658 -0
  46. package/dist/chunk-IOKUV7FD.js.map +1 -0
  47. package/dist/chunk-IUIICQU5.js +2946 -0
  48. package/dist/chunk-IUIICQU5.js.map +1 -0
  49. package/dist/chunk-J2UYZI6D.js +2946 -0
  50. package/dist/chunk-J2UYZI6D.js.map +1 -0
  51. package/dist/chunk-J7YASALS.js +2859 -0
  52. package/dist/chunk-J7YASALS.js.map +1 -0
  53. package/dist/chunk-JIEUYBQT.js +2658 -0
  54. package/dist/chunk-JIEUYBQT.js.map +1 -0
  55. package/dist/chunk-K2H7JLQW.js +2952 -0
  56. package/dist/chunk-K2H7JLQW.js.map +1 -0
  57. package/dist/chunk-KOE4VS4I.js +2945 -0
  58. package/dist/chunk-KOE4VS4I.js.map +1 -0
  59. package/dist/chunk-KUXYBP66.js +2953 -0
  60. package/dist/chunk-KUXYBP66.js.map +1 -0
  61. package/dist/chunk-KYMYNYFV.js +2656 -0
  62. package/dist/chunk-KYMYNYFV.js.map +1 -0
  63. package/dist/chunk-M2QXKYL2.js +2945 -0
  64. package/dist/chunk-M2QXKYL2.js.map +1 -0
  65. package/dist/chunk-MMXL343D.js +2974 -0
  66. package/dist/chunk-MMXL343D.js.map +1 -0
  67. package/dist/chunk-MXJL3EPE.js +2986 -0
  68. package/dist/chunk-MXJL3EPE.js.map +1 -0
  69. package/dist/chunk-MYZ25B2R.js +2995 -0
  70. package/dist/chunk-MYZ25B2R.js.map +1 -0
  71. package/dist/chunk-NTMEYB7B.js +2949 -0
  72. package/dist/chunk-NTMEYB7B.js.map +1 -0
  73. package/dist/chunk-PVJXX6GP.js +2640 -0
  74. package/dist/chunk-PVJXX6GP.js.map +1 -0
  75. package/dist/chunk-Q6ERDPQR.js +2981 -0
  76. package/dist/chunk-Q6ERDPQR.js.map +1 -0
  77. package/dist/chunk-S2SSBMWJ.js +2658 -0
  78. package/dist/chunk-S2SSBMWJ.js.map +1 -0
  79. package/dist/chunk-SLVDAZSX.js +2946 -0
  80. package/dist/chunk-SLVDAZSX.js.map +1 -0
  81. package/dist/chunk-UQBPYONV.js +2991 -0
  82. package/dist/chunk-UQBPYONV.js.map +1 -0
  83. package/dist/chunk-UXMNBS22.js +2955 -0
  84. package/dist/chunk-UXMNBS22.js.map +1 -0
  85. package/dist/chunk-WEGED7TA.js +2991 -0
  86. package/dist/chunk-WEGED7TA.js.map +1 -0
  87. package/dist/chunk-WEPTBLWX.js +2847 -0
  88. package/dist/chunk-WEPTBLWX.js.map +1 -0
  89. package/dist/chunk-WG4ZQMPS.js +2844 -0
  90. package/dist/chunk-WG4ZQMPS.js.map +1 -0
  91. package/dist/chunk-XEPEBHAW.js +2808 -0
  92. package/dist/chunk-XEPEBHAW.js.map +1 -0
  93. package/dist/chunk-XG2OYFX6.js +2925 -0
  94. package/dist/chunk-XG2OYFX6.js.map +1 -0
  95. package/dist/chunk-XSRJM2TR.js +2944 -0
  96. package/dist/chunk-XSRJM2TR.js.map +1 -0
  97. package/dist/chunk-ZAMJEU42.js +2992 -0
  98. package/dist/chunk-ZAMJEU42.js.map +1 -0
  99. package/dist/chunk-ZRAM6FXB.js +2949 -0
  100. package/dist/chunk-ZRAM6FXB.js.map +1 -0
  101. package/dist/components/ChatAttachment/ChatAttachment.d.ts +20 -0
  102. package/dist/components/ChatAttachment/ChatAttachment.d.ts.map +1 -0
  103. package/dist/components/ChatAttachment/index.d.ts +2 -0
  104. package/dist/components/ChatAttachment/index.d.ts.map +1 -0
  105. package/dist/components/ChatBubble/ChatBubble.d.ts +11 -1
  106. package/dist/components/ChatBubble/ChatBubble.d.ts.map +1 -1
  107. package/dist/components/ChatCodeBlock/ChatCodeBlock.d.ts +19 -0
  108. package/dist/components/ChatCodeBlock/ChatCodeBlock.d.ts.map +1 -0
  109. package/dist/components/ChatCodeBlock/index.d.ts +2 -0
  110. package/dist/components/ChatCodeBlock/index.d.ts.map +1 -0
  111. package/dist/components/ChatComposer/ChatComposer.d.ts +51 -0
  112. package/dist/components/ChatComposer/ChatComposer.d.ts.map +1 -0
  113. package/dist/components/ChatComposer/index.d.ts +2 -0
  114. package/dist/components/ChatComposer/index.d.ts.map +1 -0
  115. package/dist/components/ChatDayDivider/ChatDayDivider.d.ts +11 -0
  116. package/dist/components/ChatDayDivider/ChatDayDivider.d.ts.map +1 -0
  117. package/dist/components/ChatDayDivider/index.d.ts +2 -0
  118. package/dist/components/ChatDayDivider/index.d.ts.map +1 -0
  119. package/dist/components/ChatFeedback/ChatFeedback.d.ts +22 -0
  120. package/dist/components/ChatFeedback/ChatFeedback.d.ts.map +1 -0
  121. package/dist/components/ChatFeedback/index.d.ts +2 -0
  122. package/dist/components/ChatFeedback/index.d.ts.map +1 -0
  123. package/dist/components/ChatPanel/ChatPanel.d.ts +34 -0
  124. package/dist/components/ChatPanel/ChatPanel.d.ts.map +1 -0
  125. package/dist/components/ChatPanel/index.d.ts +2 -0
  126. package/dist/components/ChatPanel/index.d.ts.map +1 -0
  127. package/dist/components/ChatScrollToBottom/ChatScrollToBottom.d.ts +12 -0
  128. package/dist/components/ChatScrollToBottom/ChatScrollToBottom.d.ts.map +1 -0
  129. package/dist/components/ChatScrollToBottom/index.d.ts +2 -0
  130. package/dist/components/ChatScrollToBottom/index.d.ts.map +1 -0
  131. package/dist/components/ChatSources/ChatSources.d.ts +34 -0
  132. package/dist/components/ChatSources/ChatSources.d.ts.map +1 -0
  133. package/dist/components/ChatSources/index.d.ts +2 -0
  134. package/dist/components/ChatSources/index.d.ts.map +1 -0
  135. package/dist/components/ChatSuggestions/ChatSuggestions.d.ts +30 -0
  136. package/dist/components/ChatSuggestions/ChatSuggestions.d.ts.map +1 -0
  137. package/dist/components/ChatSuggestions/index.d.ts +2 -0
  138. package/dist/components/ChatSuggestions/index.d.ts.map +1 -0
  139. package/dist/components/ChatThinking/ChatThinking.d.ts +29 -0
  140. package/dist/components/ChatThinking/ChatThinking.d.ts.map +1 -0
  141. package/dist/components/ChatThinking/index.d.ts +2 -0
  142. package/dist/components/ChatThinking/index.d.ts.map +1 -0
  143. package/dist/components/ChatThread/ChatThread.d.ts +25 -0
  144. package/dist/components/ChatThread/ChatThread.d.ts.map +1 -0
  145. package/dist/components/ChatThread/index.d.ts +2 -0
  146. package/dist/components/ChatThread/index.d.ts.map +1 -0
  147. package/dist/components/ChatToolTrace/ChatToolTrace.d.ts +27 -0
  148. package/dist/components/ChatToolTrace/ChatToolTrace.d.ts.map +1 -0
  149. package/dist/components/ChatToolTrace/index.d.ts +2 -0
  150. package/dist/components/ChatToolTrace/index.d.ts.map +1 -0
  151. package/dist/components/ConditionBuilder/ConditionBuilder.d.ts +6 -0
  152. package/dist/components/ConditionBuilder/ConditionBuilder.d.ts.map +1 -1
  153. package/dist/components/CsvViewer/CsvViewer.d.ts +51 -0
  154. package/dist/components/CsvViewer/CsvViewer.d.ts.map +1 -0
  155. package/dist/components/CsvViewer/index.d.ts +2 -0
  156. package/dist/components/CsvViewer/index.d.ts.map +1 -0
  157. package/dist/components/DataTable/DataTable.d.ts +19 -1
  158. package/dist/components/DataTable/DataTable.d.ts.map +1 -1
  159. package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
  160. package/dist/components/DropdownMenu/DropdownMenu.d.ts +12 -2
  161. package/dist/components/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  162. package/dist/components/ExecutionConsole/ExecutionConsole.d.ts +8 -2
  163. package/dist/components/ExecutionConsole/ExecutionConsole.d.ts.map +1 -1
  164. package/dist/components/FlowMinimap/FlowMinimap.d.ts +17 -1
  165. package/dist/components/FlowMinimap/FlowMinimap.d.ts.map +1 -1
  166. package/dist/components/FlowToolbar/FlowToolbar.d.ts +16 -10
  167. package/dist/components/FlowToolbar/FlowToolbar.d.ts.map +1 -1
  168. package/dist/components/Input/Input.d.ts +1 -1
  169. package/dist/components/Input/Input.d.ts.map +1 -1
  170. package/dist/components/JsonViewer/JsonViewer.d.ts +42 -7
  171. package/dist/components/JsonViewer/JsonViewer.d.ts.map +1 -1
  172. package/dist/components/JsonViewer/index.d.ts +1 -1
  173. package/dist/components/JsonViewer/index.d.ts.map +1 -1
  174. package/dist/components/MultiSelect/MultiSelect.d.ts +7 -0
  175. package/dist/components/MultiSelect/MultiSelect.d.ts.map +1 -1
  176. package/dist/components/NumberInput/NumberInput.d.ts +7 -0
  177. package/dist/components/NumberInput/NumberInput.d.ts.map +1 -1
  178. package/dist/components/PageHeader/PageHeader.d.ts +5 -1
  179. package/dist/components/PageHeader/PageHeader.d.ts.map +1 -1
  180. package/dist/components/Select/Select.d.ts.map +1 -1
  181. package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
  182. package/dist/components/WorkflowHeader/WorkflowHeader.d.ts +130 -0
  183. package/dist/components/WorkflowHeader/WorkflowHeader.d.ts.map +1 -0
  184. package/dist/components/WorkflowHeader/WorkflowHeaderExpanded.d.ts +69 -0
  185. package/dist/components/WorkflowHeader/WorkflowHeaderExpanded.d.ts.map +1 -0
  186. package/dist/components/WorkflowHeader/index.d.ts +3 -0
  187. package/dist/components/WorkflowHeader/index.d.ts.map +1 -0
  188. package/dist/components/WorkflowHeader/misc/WorkflowHeaderCentered.d.ts +40 -0
  189. package/dist/components/WorkflowHeader/misc/WorkflowHeaderCentered.d.ts.map +1 -0
  190. package/dist/components/WorkflowHeader/misc/WorkflowHeaderCommand.d.ts +39 -0
  191. package/dist/components/WorkflowHeader/misc/WorkflowHeaderCommand.d.ts.map +1 -0
  192. package/dist/components/WorkflowHeader/misc/WorkflowHeaderMinimal.d.ts +44 -0
  193. package/dist/components/WorkflowHeader/misc/WorkflowHeaderMinimal.d.ts.map +1 -0
  194. package/dist/components/WorkflowHeader/misc/WorkflowHeaderRail.d.ts +45 -0
  195. package/dist/components/WorkflowHeader/misc/WorkflowHeaderRail.d.ts.map +1 -0
  196. package/dist/components/WorkflowHeader/misc/WorkflowHeaderStudio.d.ts +48 -0
  197. package/dist/components/WorkflowHeader/misc/WorkflowHeaderStudio.d.ts.map +1 -0
  198. package/dist/components/WorkflowHeader/misc/WorkflowHeaderTiered.d.ts +52 -0
  199. package/dist/components/WorkflowHeader/misc/WorkflowHeaderTiered.d.ts.map +1 -0
  200. package/dist/components/XmlViewer/XmlViewer.d.ts +26 -1
  201. package/dist/components/XmlViewer/XmlViewer.d.ts.map +1 -1
  202. package/dist/components/XmlViewer/index.d.ts +1 -1
  203. package/dist/components/XmlViewer/index.d.ts.map +1 -1
  204. package/dist/components/YamlViewer/YamlViewer.d.ts +26 -1
  205. package/dist/components/YamlViewer/YamlViewer.d.ts.map +1 -1
  206. package/dist/components/YamlViewer/index.d.ts +1 -1
  207. package/dist/components/YamlViewer/index.d.ts.map +1 -1
  208. package/dist/hooks/useRelativeTime.d.ts +28 -0
  209. package/dist/hooks/useRelativeTime.d.ts.map +1 -0
  210. package/dist/hooks/useWorkflowRuntime.d.ts +20 -0
  211. package/dist/hooks/useWorkflowRuntime.d.ts.map +1 -0
  212. package/dist/index.cjs +7100 -4449
  213. package/dist/index.cjs.map +1 -1
  214. package/dist/index.d.ts +20 -3
  215. package/dist/index.d.ts.map +1 -1
  216. package/dist/index.js +7265 -4898
  217. package/dist/index.js.map +1 -1
  218. package/dist/styles.css +1 -1
  219. package/dist/utils/useAnchoredPopover.d.ts +63 -0
  220. package/dist/utils/useAnchoredPopover.d.ts.map +1 -0
  221. package/dist/workflow/components/ConfigPanel/ConfigPanel.d.ts +27 -9
  222. package/dist/workflow/components/ConfigPanel/ConfigPanel.d.ts.map +1 -1
  223. package/dist/workflow/components/FlowCanvas/FlowCanvas.d.ts +49 -1
  224. package/dist/workflow/components/FlowCanvas/FlowCanvas.d.ts.map +1 -1
  225. package/dist/workflow/components/FlowEdge/FlowEdge.d.ts.map +1 -1
  226. package/dist/workflow/components/FxPanel/DataRefButton.d.ts +9 -0
  227. package/dist/workflow/components/FxPanel/DataRefButton.d.ts.map +1 -0
  228. package/dist/workflow/components/FxPanel/FxPanel.d.ts +74 -0
  229. package/dist/workflow/components/FxPanel/FxPanel.d.ts.map +1 -0
  230. package/dist/workflow/components/FxPanel/FxToggleButton.d.ts +9 -0
  231. package/dist/workflow/components/FxPanel/FxToggleButton.d.ts.map +1 -0
  232. package/dist/workflow/components/Handle/Handle.d.ts +9 -1
  233. package/dist/workflow/components/Handle/Handle.d.ts.map +1 -1
  234. package/dist/workflow/components/Handle/handleRegistry.d.ts +19 -0
  235. package/dist/workflow/components/Handle/handleRegistry.d.ts.map +1 -1
  236. package/dist/workflow/components/kinds/index.d.ts +4 -0
  237. package/dist/workflow/components/kinds/index.d.ts.map +1 -1
  238. package/dist/workflow/index.d.ts +3 -1
  239. package/dist/workflow/index.d.ts.map +1 -1
  240. package/dist/workflow/layout/elk.d.ts.map +1 -1
  241. package/dist/workflow/store/selectors.d.ts +12 -0
  242. package/dist/workflow/store/selectors.d.ts.map +1 -1
  243. package/dist/workflow/types.d.ts +108 -0
  244. package/dist/workflow/types.d.ts.map +1 -1
  245. package/dist/workflow/utils/parenting.d.ts +5 -3
  246. package/dist/workflow/utils/parenting.d.ts.map +1 -1
  247. package/dist/workflow.cjs +1114 -473
  248. package/dist/workflow.cjs.map +1 -1
  249. package/dist/workflow.js +540 -251
  250. package/dist/workflow.js.map +1 -1
  251. package/package.json +7 -3
package/dist/workflow.js CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  GroupNode,
13
13
  Handle,
14
14
  HttpRequestNode,
15
+ NodeResizer,
15
16
  OutputNode,
16
17
  ParallelNode,
17
18
  StickyNode,
@@ -35,10 +36,21 @@ import {
35
36
  smoothStepPath,
36
37
  stepPath,
37
38
  straightPath,
39
+ useConnection,
40
+ useEdgeById,
41
+ useEdges,
38
42
  useFlow,
39
43
  useFlowNodeContext,
40
- useFlowStore
41
- } from "./chunk-2RD5OERK.js";
44
+ useFlowSelector,
45
+ useIsEdgeSelected,
46
+ useIsNodeSelected,
47
+ useNodeById,
48
+ useNodeData,
49
+ useNodes,
50
+ useSelection,
51
+ useViewport,
52
+ useViewportOrNull
53
+ } from "./chunk-KOE4VS4I.js";
42
54
  import {
43
55
  cn
44
56
  } from "./chunk-ZAUUGK2Y.js";
@@ -287,6 +299,9 @@ function createElkEngine(elk, defaults = {}) {
287
299
  if (list) list.push(n);
288
300
  else childrenByParent.set(key, [n]);
289
301
  }
302
+ const engineOptions = Object.fromEntries(
303
+ Object.entries(options?.engine ?? {}).map(([k, v]) => [k, String(v)])
304
+ );
290
305
  const buildChildren = (parentId) => {
291
306
  const list = childrenByParent.get(parentId) ?? [];
292
307
  return list.map((n) => ({
@@ -298,7 +313,8 @@ function createElkEngine(elk, defaults = {}) {
298
313
  "elk.algorithm": algorithm,
299
314
  "elk.direction": ELK_DIRECTION[direction],
300
315
  "elk.spacing.nodeNode": String(nodeSpacing),
301
- "elk.layered.spacing.nodeNodeBetweenLayers": String(rankSpacing)
316
+ "elk.layered.spacing.nodeNodeBetweenLayers": String(rankSpacing),
317
+ ...engineOptions
302
318
  } : void 0
303
319
  }));
304
320
  };
@@ -339,60 +355,6 @@ function createElkEngine(elk, defaults = {}) {
339
355
  };
340
356
  }
341
357
 
342
- // src/workflow/store/selectors.ts
343
- import { useMemo as useMemo2, useSyncExternalStore } from "react";
344
- function useFlowSelector(selector, isEqual = Object.is) {
345
- void isEqual;
346
- const store = useFlowStore();
347
- return useSyncExternalStore(
348
- store.subscribe,
349
- () => selector(store.getSnapshot()),
350
- () => selector(store.getSnapshot())
351
- );
352
- }
353
- function useNodes() {
354
- return useFlowSelector((s) => s.nodes);
355
- }
356
- function useEdges() {
357
- return useFlowSelector((s) => s.edges);
358
- }
359
- function useViewport() {
360
- return useFlowSelector((s) => s.viewport);
361
- }
362
- function useNodeById(id) {
363
- return useFlowSelector((s) => s.nodes.find((n) => n.id === id));
364
- }
365
- function useNodeData(id) {
366
- return useFlowSelector(
367
- (s) => s.nodes.find((n) => n.id === id)?.data ?? void 0
368
- );
369
- }
370
- function useEdgeById(id) {
371
- return useFlowSelector((s) => s.edges.find((e) => e.id === id));
372
- }
373
- function useIsNodeSelected(id) {
374
- return useFlowSelector((s) => s.selectedNodeIds.has(id));
375
- }
376
- function useIsEdgeSelected(id) {
377
- return useFlowSelector((s) => s.selectedEdgeIds.has(id));
378
- }
379
- function useConnection() {
380
- return useFlowSelector((s) => s.connection);
381
- }
382
- function useSelection() {
383
- const nodes = useNodes();
384
- const edges = useEdges();
385
- const selectedNodeIds = useFlowSelector((s) => s.selectedNodeIds);
386
- const selectedEdgeIds = useFlowSelector((s) => s.selectedEdgeIds);
387
- return useMemo2(
388
- () => ({
389
- nodes: nodes.filter((n) => selectedNodeIds.has(n.id)),
390
- edges: edges.filter((e) => selectedEdgeIds.has(e.id))
391
- }),
392
- [nodes, edges, selectedNodeIds, selectedEdgeIds]
393
- );
394
- }
395
-
396
358
  // src/workflow/components/ConfigPanel/ConfigPanel.tsx
397
359
  import {
398
360
  useCallback as useCallback3,
@@ -400,7 +362,7 @@ import {
400
362
  useRef as useRef3,
401
363
  useState as useState3
402
364
  } from "react";
403
- import { jsx, jsxs } from "react/jsx-runtime";
365
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
404
366
  var SAVE_STATE_LABEL = {
405
367
  clean: "Saved",
406
368
  dirty: "Unsaved changes",
@@ -418,26 +380,27 @@ var SAVE_STATE_DOT = {
418
380
  function ConfigPanel({
419
381
  open = true,
420
382
  title,
421
- kindLabel,
422
383
  icon,
384
+ headerActions,
423
385
  banner,
424
386
  tabs,
425
387
  activeTab: controlledActive,
426
388
  defaultActiveTab,
427
389
  onTabChange,
428
- saveState = "clean",
390
+ saveState,
429
391
  onTitleChange,
430
392
  onClose,
431
393
  onSave,
432
394
  onCancel,
395
+ padding = 12,
433
396
  resizable = true,
434
- defaultWidth = 360,
435
- minWidth = 280,
397
+ defaultWidth = 400,
398
+ minWidth = 400,
436
399
  maxWidth = 720,
437
400
  maxWidthContainerInset = 80,
438
401
  variant = "pinned",
439
402
  children,
440
- footer,
403
+ footerActions,
441
404
  className,
442
405
  style
443
406
  }) {
@@ -534,55 +497,53 @@ function ConfigPanel({
534
497
  "aria-hidden": "true"
535
498
  }
536
499
  ),
537
- /* @__PURE__ */ jsxs("header", { className: "ods-flow-config-panel__header", children: [
538
- kindLabel && /* @__PURE__ */ jsx("span", { className: "ods-flow-config-panel__breadcrumb", children: kindLabel }),
539
- /* @__PURE__ */ jsxs("div", { className: "ods-flow-config-panel__title-row", children: [
540
- icon && /* @__PURE__ */ jsx("span", { className: "ods-flow-config-panel__icon", children: icon }),
541
- editingTitle ? /* @__PURE__ */ jsx(
542
- "input",
543
- {
544
- className: "ods-flow-config-panel__title-input",
545
- value: draftTitle,
546
- autoFocus: true,
547
- onChange: (e) => setDraftTitle(e.target.value),
548
- onBlur: () => commitTitle(draftTitle),
549
- onKeyDown: titleKeyDown,
550
- "aria-label": "Edit title"
551
- }
552
- ) : /* @__PURE__ */ jsx(
553
- "button",
554
- {
555
- type: "button",
556
- className: "ods-flow-config-panel__title",
557
- onDoubleClick: () => {
558
- if (!onTitleChange) return;
559
- setDraftTitle(title ?? "");
560
- setEditingTitle(true);
561
- },
562
- title: onTitleChange ? "Double-click to rename" : void 0,
563
- children: title ?? "Untitled"
564
- }
565
- ),
566
- onClose && /* @__PURE__ */ jsx(
567
- "button",
568
- {
569
- type: "button",
570
- className: "ods-flow-config-panel__close",
571
- onClick: onClose,
572
- "aria-label": "Close panel",
573
- children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
574
- "path",
575
- {
576
- d: "M3 3l8 8M11 3l-8 8",
577
- stroke: "currentColor",
578
- strokeWidth: "1.5",
579
- strokeLinecap: "round"
580
- }
581
- ) })
582
- }
583
- )
584
- ] })
585
- ] }),
500
+ /* @__PURE__ */ jsx("header", { className: "ods-flow-config-panel__header", children: /* @__PURE__ */ jsxs("div", { className: "ods-flow-config-panel__title-row", children: [
501
+ icon && /* @__PURE__ */ jsx("span", { className: "ods-flow-config-panel__icon", children: icon }),
502
+ editingTitle ? /* @__PURE__ */ jsx(
503
+ "input",
504
+ {
505
+ className: "ods-flow-config-panel__title-input",
506
+ value: draftTitle,
507
+ autoFocus: true,
508
+ onChange: (e) => setDraftTitle(e.target.value),
509
+ onBlur: () => commitTitle(draftTitle),
510
+ onKeyDown: titleKeyDown,
511
+ "aria-label": "Edit title"
512
+ }
513
+ ) : /* @__PURE__ */ jsx(
514
+ "button",
515
+ {
516
+ type: "button",
517
+ className: "ods-flow-config-panel__title",
518
+ onDoubleClick: () => {
519
+ if (!onTitleChange) return;
520
+ setDraftTitle(title ?? "");
521
+ setEditingTitle(true);
522
+ },
523
+ title: onTitleChange ? "Double-click to rename" : void 0,
524
+ children: title ?? "Untitled"
525
+ }
526
+ ),
527
+ headerActions && /* @__PURE__ */ jsx("div", { className: "ods-flow-config-panel__header-actions", children: headerActions }),
528
+ onClose && /* @__PURE__ */ jsx(
529
+ "button",
530
+ {
531
+ type: "button",
532
+ className: "ods-flow-config-panel__close",
533
+ onClick: onClose,
534
+ "aria-label": "Close panel",
535
+ children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
536
+ "path",
537
+ {
538
+ d: "M3 3l8 8M11 3l-8 8",
539
+ stroke: "currentColor",
540
+ strokeWidth: "1.5",
541
+ strokeLinecap: "round"
542
+ }
543
+ ) })
544
+ }
545
+ )
546
+ ] }) }),
586
547
  tabs && tabs.length > 1 && /* @__PURE__ */ jsx("nav", { className: "ods-flow-config-panel__tabs", role: "tablist", children: tabs.map((t) => /* @__PURE__ */ jsxs(
587
548
  "button",
588
549
  {
@@ -603,9 +564,17 @@ function ConfigPanel({
603
564
  t.id
604
565
  )) }),
605
566
  banner && /* @__PURE__ */ jsx("div", { className: "ods-flow-config-panel__banner", children: banner }),
606
- /* @__PURE__ */ jsx("div", { className: "ods-flow-config-panel__body", role: "tabpanel", children: tabs ? activeTab?.content : children }),
567
+ /* @__PURE__ */ jsx(
568
+ "div",
569
+ {
570
+ className: "ods-flow-config-panel__body",
571
+ role: "tabpanel",
572
+ style: { padding },
573
+ children: tabs ? activeTab?.content : children
574
+ }
575
+ ),
607
576
  /* @__PURE__ */ jsxs("footer", { className: "ods-flow-config-panel__footer", children: [
608
- /* @__PURE__ */ jsxs(
577
+ saveState && /* @__PURE__ */ jsxs(
609
578
  "span",
610
579
  {
611
580
  className: cn(
@@ -625,7 +594,7 @@ function ConfigPanel({
625
594
  ]
626
595
  }
627
596
  ),
628
- footer ?? /* @__PURE__ */ jsxs("div", { className: "ods-flow-config-panel__footer-actions", children: [
597
+ /* @__PURE__ */ jsx("div", { className: "ods-flow-config-panel__footer-actions", children: footerActions ?? /* @__PURE__ */ jsxs(Fragment, { children: [
629
598
  onCancel && /* @__PURE__ */ jsx(
630
599
  "button",
631
600
  {
@@ -641,164 +610,481 @@ function ConfigPanel({
641
610
  type: "button",
642
611
  className: "ods-flow-config-panel__btn ods-flow-config-panel__btn--primary",
643
612
  onClick: onSave,
644
- disabled: saveState === "saving" || saveState === "clean",
613
+ disabled: saveState === "saving",
645
614
  children: saveState === "saving" ? "Saving\u2026" : "Save"
646
615
  }
647
616
  )
648
- ] })
617
+ ] }) })
649
618
  ] })
650
619
  ]
651
620
  }
652
621
  );
653
622
  }
654
623
 
655
- // src/workflow/components/NodeResizer/NodeResizer.tsx
656
- import { useRef as useRef4 } from "react";
657
- import { jsx as jsx2 } from "react/jsx-runtime";
658
- function NodeResizer({
659
- isVisible,
660
- minWidth = 80,
661
- minHeight = 60,
662
- maxWidth,
663
- maxHeight,
664
- keepAspectRatio = false,
665
- onResize,
666
- onResizeEnd,
667
- color
624
+ // src/workflow/components/FxPanel/FxPanel.tsx
625
+ import {
626
+ useMemo as useMemo2,
627
+ useRef as useRef4,
628
+ useState as useState4
629
+ } from "react";
630
+ import {
631
+ ChevronDownIcon,
632
+ ChevronRightIcon,
633
+ CloseIcon,
634
+ CollapseAllIcon,
635
+ DraggableIcon,
636
+ ExpandAllIcon,
637
+ FunctionMathIcon,
638
+ SearchIcon,
639
+ ValueVariableIcon
640
+ } from "@octaviaflow/icons";
641
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
642
+ var KIND_ICON = {
643
+ function: FunctionMathIcon,
644
+ variable: ValueVariableIcon
645
+ };
646
+ function countLeaves(items) {
647
+ let n = 0;
648
+ for (const it of items) {
649
+ if (it.children && it.children.length > 0) n += countLeaves(it.children);
650
+ else n += 1;
651
+ }
652
+ return n;
653
+ }
654
+ function collectBranchIds(items, out) {
655
+ for (const it of items) {
656
+ if (it.children && it.children.length > 0) {
657
+ out.add(it.id);
658
+ collectBranchIds(it.children, out);
659
+ }
660
+ }
661
+ }
662
+ function flattenTree(items, depth, expandedItems, out) {
663
+ for (const it of items) {
664
+ const hasChildren = !!it.children && it.children.length > 0;
665
+ const expanded = hasChildren && expandedItems.has(it.id);
666
+ out.push({ item: it, depth, hasChildren, expanded });
667
+ if (expanded) flattenTree(it.children, depth + 1, expandedItems, out);
668
+ }
669
+ }
670
+ function collectHits(items, ancestors, q, out) {
671
+ for (const it of items) {
672
+ if (it.children && it.children.length > 0) {
673
+ collectHits(it.children, [...ancestors, it.label], q, out);
674
+ } else {
675
+ const path = [...ancestors, it.label].join(" / ");
676
+ const hay = `${it.label} ${it.description ?? ""} ${path} ${it.insertValue ?? ""}`.toLowerCase();
677
+ if (hay.includes(q)) out.push({ item: it, ancestors });
678
+ }
679
+ }
680
+ }
681
+ function FxPanel({
682
+ categories,
683
+ title = "Data References",
684
+ hint = "Drag onto a field, or click an item to insert it.",
685
+ search: controlledSearch,
686
+ defaultSearch = "",
687
+ onSearchChange,
688
+ expandedCategory: controlledExpanded,
689
+ defaultExpandedCategory,
690
+ onExpandedCategoryChange,
691
+ onClose,
692
+ onItemDragStart,
693
+ onItemSelect,
694
+ width = 300,
695
+ emptyLabel = "No matches",
696
+ searchPlaceholder = "Search fields, functions & variables\u2026",
697
+ className,
698
+ style
668
699
  }) {
669
- const { node, selected } = useFlowNodeContext();
670
- const viewport = useViewport();
671
- const flow = useFlow();
672
- const dragRef = useRef4(null);
673
- const show = isVisible ?? selected;
674
- if (!show) return null;
675
- const beginResize = (e, corner) => {
676
- e.preventDefault();
677
- e.stopPropagation();
678
- e.target.setPointerCapture(e.pointerId);
679
- const w = node.width ?? DEFAULT_NODE_WIDTH;
680
- const h = node.height ?? DEFAULT_NODE_HEIGHT;
681
- dragRef.current = {
682
- pointerId: e.pointerId,
683
- corner,
684
- startClientX: e.clientX,
685
- startClientY: e.clientY,
686
- startWidth: w,
687
- startHeight: h,
688
- startX: node.position.x,
689
- startY: node.position.y,
690
- aspect: w / Math.max(1, h)
691
- };
700
+ const [internalSearch, setInternalSearch] = useState4(defaultSearch);
701
+ const search = controlledSearch ?? internalSearch;
702
+ const query = search.trim().toLowerCase();
703
+ const setSearch = (next) => {
704
+ if (controlledSearch === void 0) setInternalSearch(next);
705
+ onSearchChange?.(next);
692
706
  };
693
- const onMove = (e) => {
694
- const drag = dragRef.current;
695
- if (!drag || drag.pointerId !== e.pointerId) return;
696
- const dx = (e.clientX - drag.startClientX) / viewport.zoom;
697
- const dy = (e.clientY - drag.startClientY) / viewport.zoom;
698
- let nextW = drag.startWidth;
699
- let nextH = drag.startHeight;
700
- let nextX = drag.startX;
701
- let nextY = drag.startY;
702
- switch (drag.corner) {
703
- case "se":
704
- nextW = drag.startWidth + dx;
705
- nextH = drag.startHeight + dy;
706
- break;
707
- case "sw":
708
- nextW = drag.startWidth - dx;
709
- nextH = drag.startHeight + dy;
710
- nextX = drag.startX + dx;
711
- break;
712
- case "ne":
713
- nextW = drag.startWidth + dx;
714
- nextH = drag.startHeight - dy;
715
- nextY = drag.startY + dy;
716
- break;
717
- case "nw":
718
- nextW = drag.startWidth - dx;
719
- nextH = drag.startHeight - dy;
720
- nextX = drag.startX + dx;
721
- nextY = drag.startY + dy;
722
- break;
723
- }
724
- if (keepAspectRatio) {
725
- nextH = nextW / drag.aspect;
726
- if (drag.corner === "nw" || drag.corner === "ne") {
727
- nextY = drag.startY + (drag.startHeight - nextH);
707
+ const visibleCategories = useMemo2(
708
+ () => categories.filter((c) => c.items.length > 0),
709
+ [categories]
710
+ );
711
+ const [internalCat, setInternalCat] = useState4(
712
+ defaultExpandedCategory !== void 0 ? defaultExpandedCategory : visibleCategories[0]?.id ?? null
713
+ );
714
+ const openCategoryId = controlledExpanded ?? internalCat;
715
+ const setOpenCategory = (id) => {
716
+ if (controlledExpanded === void 0) setInternalCat(id);
717
+ onExpandedCategoryChange?.(id);
718
+ };
719
+ const [expandedItems, setExpandedItems] = useState4(() => {
720
+ const ids = /* @__PURE__ */ new Set();
721
+ for (const cat of categories) {
722
+ for (const item of cat.items) {
723
+ if (item.children && item.children.length > 0) ids.add(item.id);
728
724
  }
729
725
  }
730
- nextW = Math.max(minWidth, maxWidth ? Math.min(maxWidth, nextW) : nextW);
731
- nextH = Math.max(minHeight, maxHeight ? Math.min(maxHeight, nextH) : nextH);
732
- flow.updateNode(node.id, {
733
- width: nextW,
734
- height: nextH,
735
- position: { x: nextX, y: nextY }
726
+ return ids;
727
+ });
728
+ const toggleItem = (id) => {
729
+ setExpandedItems((prev) => {
730
+ const next = new Set(prev);
731
+ if (next.has(id)) next.delete(id);
732
+ else next.add(id);
733
+ return next;
736
734
  });
737
- onResize?.({ width: nextW, height: nextH });
738
735
  };
739
- const onUp = (e) => {
740
- if (dragRef.current?.pointerId === e.pointerId) {
741
- const cur = flow.getNode(node.id);
742
- if (cur) {
743
- onResizeEnd?.({
744
- width: cur.width ?? DEFAULT_NODE_WIDTH,
745
- height: cur.height ?? DEFAULT_NODE_HEIGHT
746
- });
736
+ const setCategoryExpansion = (cat, expand) => {
737
+ const branchIds = /* @__PURE__ */ new Set();
738
+ collectBranchIds(cat.items, branchIds);
739
+ setExpandedItems((prev) => {
740
+ const next = new Set(prev);
741
+ for (const id of branchIds) {
742
+ if (expand) next.add(id);
743
+ else next.delete(id);
747
744
  }
748
- dragRef.current = null;
745
+ return next;
746
+ });
747
+ };
748
+ const [draggingId, setDraggingId] = useState4(null);
749
+ const [focusedId, setFocusedId] = useState4(null);
750
+ const hits = useMemo2(() => {
751
+ if (!query) return null;
752
+ const all = [];
753
+ for (const cat of visibleCategories) {
754
+ const out = [];
755
+ collectHits(cat.items, [], query, out);
756
+ for (const hit of out) all.push({ category: cat, hit });
757
+ }
758
+ return all;
759
+ }, [query, visibleCategories]);
760
+ const handleDragStart = (item, category) => (e) => {
761
+ setDraggingId(item.id);
762
+ if (onItemDragStart) {
763
+ onItemDragStart(item, category, e);
764
+ } else if (item.insertValue) {
765
+ e.dataTransfer.setData("text/plain", item.insertValue);
766
+ e.dataTransfer.setData("application/x-fx-insert", item.insertValue);
767
+ e.dataTransfer.setData(
768
+ "application/x-fx-type",
769
+ category.kind ?? "function"
770
+ );
771
+ e.dataTransfer.effectAllowed = "copy";
749
772
  }
750
773
  };
751
- const handleColor = color ?? "var(--ods-accent)";
752
- const handleStyle = (corner) => {
753
- const base = {
754
- position: "absolute",
755
- width: 12,
756
- height: 12,
757
- background: "var(--ods-surface-canvas)",
758
- border: `2px solid ${handleColor}`,
759
- borderRadius: 2,
760
- cursor: cursorFor(corner),
761
- touchAction: "none",
762
- // Place each handle so its CENTRE sits on the corresponding corner.
763
- transform: "translate(-50%, -50%)"
764
- };
765
- switch (corner) {
766
- case "nw":
767
- return { ...base, top: 0, left: 0 };
768
- case "ne":
769
- return { ...base, top: 0, left: "100%" };
770
- case "sw":
771
- return { ...base, top: "100%", left: 0 };
772
- case "se":
773
- return { ...base, top: "100%", left: "100%" };
774
+ const listRef = useRef4(null);
775
+ const focusRow = (id) => {
776
+ setFocusedId(id);
777
+ requestAnimationFrame(() => {
778
+ listRef.current?.querySelector(`[data-fx-row="${CSS.escape(id)}"]`)?.focus();
779
+ });
780
+ };
781
+ const onRowKeyDown = (e, rows, row, category) => {
782
+ const idx = rows.findIndex((r) => r.item.id === row.item.id);
783
+ if (idx < 0) return;
784
+ switch (e.key) {
785
+ case "ArrowDown":
786
+ e.preventDefault();
787
+ if (idx < rows.length - 1) focusRow(rows[idx + 1].item.id);
788
+ break;
789
+ case "ArrowUp":
790
+ e.preventDefault();
791
+ if (idx > 0) focusRow(rows[idx - 1].item.id);
792
+ break;
793
+ case "ArrowRight":
794
+ if (row.hasChildren) {
795
+ e.preventDefault();
796
+ if (!row.expanded) toggleItem(row.item.id);
797
+ else if (idx < rows.length - 1) focusRow(rows[idx + 1].item.id);
798
+ }
799
+ break;
800
+ case "ArrowLeft":
801
+ if (row.hasChildren && row.expanded) {
802
+ e.preventDefault();
803
+ toggleItem(row.item.id);
804
+ } else {
805
+ for (let i = idx - 1; i >= 0; i--) {
806
+ if (rows[i].depth < row.depth) {
807
+ e.preventDefault();
808
+ focusRow(rows[i].item.id);
809
+ break;
810
+ }
811
+ }
812
+ }
813
+ break;
814
+ case "Home":
815
+ e.preventDefault();
816
+ if (rows.length) focusRow(rows[0].item.id);
817
+ break;
818
+ case "End":
819
+ e.preventDefault();
820
+ if (rows.length) focusRow(rows[rows.length - 1].item.id);
821
+ break;
822
+ case "Enter":
823
+ case " ":
824
+ e.preventDefault();
825
+ if (row.hasChildren) toggleItem(row.item.id);
826
+ else onItemSelect?.(row.item, category);
827
+ break;
828
+ default:
829
+ break;
774
830
  }
775
831
  };
776
- return /* @__PURE__ */ jsx2("div", { className: cn("ods-node-resizer"), "data-flow-no-drag": "true", children: ["nw", "ne", "sw", "se"].map((corner) => /* @__PURE__ */ jsx2(
777
- "div",
832
+ const searchRows = useMemo2(
833
+ () => hits ? hits.map(({ hit }) => ({
834
+ item: hit.item,
835
+ depth: 0,
836
+ hasChildren: false,
837
+ expanded: false
838
+ })) : [],
839
+ [hits]
840
+ );
841
+ const renderRow = (row, category, rows, crumb) => {
842
+ const { item, depth, hasChildren, expanded } = row;
843
+ const mono = category.kind === "function";
844
+ const draggable = !!item.insertValue;
845
+ const isFocused = focusedId === item.id;
846
+ return /* @__PURE__ */ jsxs2(
847
+ "div",
848
+ {
849
+ "data-fx-row": item.id,
850
+ className: cn(
851
+ "ods-flow-fx-panel__row",
852
+ hasChildren ? "ods-flow-fx-panel__row--branch" : "ods-flow-fx-panel__row--leaf",
853
+ draggingId === item.id && "ods-flow-fx-panel__row--dragging",
854
+ isFocused && "ods-flow-fx-panel__row--focused"
855
+ ),
856
+ role: "treeitem",
857
+ "aria-expanded": hasChildren ? expanded : void 0,
858
+ "aria-level": depth + 1,
859
+ tabIndex: isFocused || focusedId === null && rows[0]?.item.id === item.id ? 0 : -1,
860
+ draggable,
861
+ onDragStart: draggable ? handleDragStart(item, category) : void 0,
862
+ onDragEnd: () => setDraggingId(null),
863
+ onFocus: () => setFocusedId(item.id),
864
+ onKeyDown: (e) => onRowKeyDown(e, rows, row, category),
865
+ onClick: () => {
866
+ if (hasChildren) toggleItem(item.id);
867
+ else onItemSelect?.(item, category);
868
+ },
869
+ title: item.insertValue,
870
+ children: [
871
+ Array.from({ length: depth }, (_, i) => /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__indent", "aria-hidden": "true" }, i)),
872
+ hasChildren ? /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__twisty", "aria-hidden": "true", children: expanded ? /* @__PURE__ */ jsx2(ChevronDownIcon, { size: "sm" }) : /* @__PURE__ */ jsx2(ChevronRightIcon, { size: "sm" }) }) : /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__grip", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(DraggableIcon, { size: "sm" }) }),
873
+ /* @__PURE__ */ jsxs2("span", { className: "ods-flow-fx-panel__row-main", children: [
874
+ /* @__PURE__ */ jsxs2("span", { className: "ods-flow-fx-panel__row-line", children: [
875
+ mono ? /* @__PURE__ */ jsx2("code", { className: "ods-flow-fx-panel__row-label ods-flow-fx-panel__row-label--mono", children: item.label }) : /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__row-label", children: item.label }),
876
+ item.valueType && /* @__PURE__ */ jsx2(
877
+ "span",
878
+ {
879
+ className: cn(
880
+ "ods-flow-fx-panel__type",
881
+ `ods-flow-fx-panel__type--${item.valueType}`
882
+ ),
883
+ children: item.valueType
884
+ }
885
+ )
886
+ ] }),
887
+ (item.description || crumb) && /* @__PURE__ */ jsxs2("span", { className: "ods-flow-fx-panel__row-sub", children: [
888
+ crumb && /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__crumb", children: crumb }),
889
+ item.description
890
+ ] })
891
+ ] })
892
+ ]
893
+ },
894
+ item.id
895
+ );
896
+ };
897
+ return /* @__PURE__ */ jsxs2(
898
+ "aside",
778
899
  {
779
- style: handleStyle(corner),
780
- onPointerDown: (e) => beginResize(e, corner),
781
- onPointerMove: onMove,
782
- onPointerUp: onUp,
783
- onPointerCancel: onUp,
784
- "aria-label": `Resize ${corner}`
785
- },
786
- corner
787
- )) });
900
+ className: cn("ods-flow-fx-panel", className),
901
+ style: { width, ...style },
902
+ "aria-label": typeof title === "string" ? title : "Data References",
903
+ children: [
904
+ /* @__PURE__ */ jsxs2("header", { className: "ods-flow-fx-panel__header", children: [
905
+ /* @__PURE__ */ jsx2("h3", { className: "ods-flow-fx-panel__title", children: title }),
906
+ onClose && /* @__PURE__ */ jsx2(
907
+ "button",
908
+ {
909
+ type: "button",
910
+ className: "ods-flow-fx-panel__close",
911
+ onClick: onClose,
912
+ "aria-label": "Close data references",
913
+ children: /* @__PURE__ */ jsx2(CloseIcon, { size: "sm" })
914
+ }
915
+ )
916
+ ] }),
917
+ /* @__PURE__ */ jsxs2("div", { className: "ods-flow-fx-panel__search", children: [
918
+ /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__search-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(SearchIcon, { size: "sm" }) }),
919
+ /* @__PURE__ */ jsx2(
920
+ "input",
921
+ {
922
+ type: "text",
923
+ className: "ods-flow-fx-panel__search-input",
924
+ placeholder: searchPlaceholder,
925
+ "aria-label": "Search fields, functions and variables",
926
+ value: search,
927
+ onChange: (e) => setSearch(e.target.value)
928
+ }
929
+ ),
930
+ search && /* @__PURE__ */ jsx2(
931
+ "button",
932
+ {
933
+ type: "button",
934
+ className: "ods-flow-fx-panel__search-clear",
935
+ onClick: () => setSearch(""),
936
+ "aria-label": "Clear search",
937
+ children: /* @__PURE__ */ jsx2(CloseIcon, { size: "sm" })
938
+ }
939
+ )
940
+ ] }),
941
+ hint && !query && /* @__PURE__ */ jsx2("p", { className: "ods-flow-fx-panel__hint", children: hint }),
942
+ /* @__PURE__ */ jsxs2("div", { className: "ods-flow-fx-panel__list", ref: listRef, role: "tree", children: [
943
+ query && hits && (hits.length > 0 ? /* @__PURE__ */ jsxs2("div", { className: "ods-flow-fx-panel__results", children: [
944
+ /* @__PURE__ */ jsxs2("div", { className: "ods-flow-fx-panel__results-count", children: [
945
+ hits.length,
946
+ " ",
947
+ hits.length === 1 ? "match" : "matches"
948
+ ] }),
949
+ hits.map(
950
+ ({ category, hit }, i) => renderRow(
951
+ searchRows[i],
952
+ category,
953
+ searchRows,
954
+ hit.ancestors.length ? hit.ancestors.join(" / ") : category.label
955
+ )
956
+ )
957
+ ] }) : /* @__PURE__ */ jsx2("div", { className: "ods-flow-fx-panel__empty", children: emptyLabel })),
958
+ !query && visibleCategories.map((cat) => {
959
+ const isOpen = openCategoryId === cat.id;
960
+ const kind = cat.kind ?? "function";
961
+ const KindIcon = KIND_ICON[kind];
962
+ const branchIds = /* @__PURE__ */ new Set();
963
+ collectBranchIds(cat.items, branchIds);
964
+ const hasTree = branchIds.size > 0;
965
+ const rows = [];
966
+ if (isOpen) flattenTree(cat.items, 0, expandedItems, rows);
967
+ const allExpanded = hasTree && [...branchIds].every((id) => expandedItems.has(id));
968
+ return /* @__PURE__ */ jsxs2("div", { className: "ods-flow-fx-panel__category", children: [
969
+ /* @__PURE__ */ jsxs2(
970
+ "div",
971
+ {
972
+ className: cn(
973
+ "ods-flow-fx-panel__cat-header",
974
+ isOpen && "ods-flow-fx-panel__cat-header--open"
975
+ ),
976
+ children: [
977
+ /* @__PURE__ */ jsxs2(
978
+ "button",
979
+ {
980
+ type: "button",
981
+ className: "ods-flow-fx-panel__cat-toggle",
982
+ "aria-expanded": isOpen,
983
+ onClick: () => setOpenCategory(isOpen ? null : cat.id),
984
+ children: [
985
+ /* @__PURE__ */ jsx2(
986
+ ChevronRightIcon,
987
+ {
988
+ size: "sm",
989
+ className: cn(
990
+ "ods-flow-fx-panel__chevron",
991
+ isOpen && "ods-flow-fx-panel__chevron--open"
992
+ )
993
+ }
994
+ ),
995
+ /* @__PURE__ */ jsxs2(
996
+ "span",
997
+ {
998
+ className: cn(
999
+ "ods-flow-fx-panel__badge",
1000
+ `ods-flow-fx-panel__badge--${kind}`
1001
+ ),
1002
+ children: [
1003
+ /* @__PURE__ */ jsx2(
1004
+ KindIcon,
1005
+ {
1006
+ size: "sm",
1007
+ className: "ods-flow-fx-panel__badge-icon"
1008
+ }
1009
+ ),
1010
+ cat.label
1011
+ ]
1012
+ }
1013
+ ),
1014
+ /* @__PURE__ */ jsx2("span", { className: "ods-flow-fx-panel__count", children: countLeaves(cat.items) })
1015
+ ]
1016
+ }
1017
+ ),
1018
+ isOpen && hasTree && /* @__PURE__ */ jsx2(
1019
+ "button",
1020
+ {
1021
+ type: "button",
1022
+ className: "ods-flow-fx-panel__cat-action",
1023
+ "aria-label": allExpanded ? "Collapse all" : "Expand all",
1024
+ title: allExpanded ? "Collapse all" : "Expand all",
1025
+ onClick: () => setCategoryExpansion(cat, !allExpanded),
1026
+ children: allExpanded ? /* @__PURE__ */ jsx2(CollapseAllIcon, { size: "sm" }) : /* @__PURE__ */ jsx2(ExpandAllIcon, { size: "sm" })
1027
+ }
1028
+ )
1029
+ ]
1030
+ }
1031
+ ),
1032
+ isOpen && /* @__PURE__ */ jsx2(
1033
+ "div",
1034
+ {
1035
+ className: "ods-flow-fx-panel__rows",
1036
+ role: "group",
1037
+ "aria-label": cat.label,
1038
+ children: rows.map((row) => renderRow(row, cat, rows))
1039
+ }
1040
+ )
1041
+ ] }, cat.id);
1042
+ })
1043
+ ] })
1044
+ ]
1045
+ }
1046
+ );
788
1047
  }
789
- function cursorFor(corner) {
790
- switch (corner) {
791
- case "nw":
792
- case "se":
793
- return "nwse-resize";
794
- case "ne":
795
- case "sw":
796
- return "nesw-resize";
797
- }
1048
+
1049
+ // src/workflow/components/FxPanel/DataRefButton.tsx
1050
+ import { DataReferenceIcon } from "@octaviaflow/icons";
1051
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1052
+ function DataRefButton({
1053
+ active = false,
1054
+ label = "Data ref",
1055
+ className,
1056
+ type = "button",
1057
+ ...rest
1058
+ }) {
1059
+ return /* @__PURE__ */ jsxs3(
1060
+ "button",
1061
+ {
1062
+ ...rest,
1063
+ type,
1064
+ className: cn(
1065
+ "ods-flow-dataref-button",
1066
+ active && "ods-flow-dataref-button--active",
1067
+ label == null && "ods-flow-dataref-button--icon-only",
1068
+ className
1069
+ ),
1070
+ "aria-pressed": active,
1071
+ title: active ? "Hide data references" : "Show data references",
1072
+ children: [
1073
+ /* @__PURE__ */ jsx3(
1074
+ DataReferenceIcon,
1075
+ {
1076
+ size: "sm",
1077
+ className: "ods-flow-dataref-button__icon"
1078
+ }
1079
+ ),
1080
+ label != null && /* @__PURE__ */ jsx3("span", { className: "ods-flow-dataref-button__label", children: label })
1081
+ ]
1082
+ }
1083
+ );
798
1084
  }
799
1085
 
800
1086
  // src/workflow/components/NodeToolbar/NodeToolbar.tsx
801
- import { jsx as jsx3 } from "react/jsx-runtime";
1087
+ import { jsx as jsx4 } from "react/jsx-runtime";
802
1088
  function NodeToolbar({
803
1089
  isVisible,
804
1090
  position = "top",
@@ -813,7 +1099,7 @@ function NodeToolbar({
813
1099
  const show = isVisible ?? node.selected;
814
1100
  if (!show) return null;
815
1101
  const inverseScale = 1 / viewport.zoom;
816
- return /* @__PURE__ */ jsx3(
1102
+ return /* @__PURE__ */ jsx4(
817
1103
  "div",
818
1104
  {
819
1105
  className: cn("ods-node-toolbar", `ods-node-toolbar--${position}`, className),
@@ -825,7 +1111,7 @@ function NodeToolbar({
825
1111
  onPointerDown: (e) => e.stopPropagation(),
826
1112
  onMouseDown: (e) => e.stopPropagation(),
827
1113
  onClick: (e) => e.stopPropagation(),
828
- children: /* @__PURE__ */ jsx3(
1114
+ children: /* @__PURE__ */ jsx4(
829
1115
  "div",
830
1116
  {
831
1117
  className: "ods-node-toolbar__inner",
@@ -973,11 +1259,13 @@ export {
973
1259
  DEFAULT_NODE_HEIGHT,
974
1260
  DEFAULT_NODE_KINDS,
975
1261
  DEFAULT_NODE_WIDTH,
1262
+ DataRefButton,
976
1263
  ErrorNode,
977
1264
  FlowCanvas,
978
1265
  FlowEdge,
979
1266
  FlowNode,
980
1267
  ForEachNode,
1268
+ FxPanel,
981
1269
  GroupNode,
982
1270
  Handle,
983
1271
  HttpRequestNode,
@@ -1024,6 +1312,7 @@ export {
1024
1312
  useNodeData,
1025
1313
  useNodes,
1026
1314
  useSelection,
1027
- useViewport
1315
+ useViewport,
1316
+ useViewportOrNull
1028
1317
  };
1029
1318
  //# sourceMappingURL=workflow.js.map