@octaviaflow/core 3.0.18-beta.9 → 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 (170) hide show
  1. package/dist/chunk-2ZIOFEIS.js +3001 -0
  2. package/dist/chunk-2ZIOFEIS.js.map +1 -0
  3. package/dist/chunk-3DWB2PUF.js +2991 -0
  4. package/dist/chunk-3DWB2PUF.js.map +1 -0
  5. package/dist/chunk-4ZALUTZS.js +2936 -0
  6. package/dist/chunk-4ZALUTZS.js.map +1 -0
  7. package/dist/chunk-5ARKSRED.js +2951 -0
  8. package/dist/chunk-5ARKSRED.js.map +1 -0
  9. package/dist/chunk-5OSGSJMM.js +2981 -0
  10. package/dist/chunk-5OSGSJMM.js.map +1 -0
  11. package/dist/chunk-5YQQMEF3.js +2981 -0
  12. package/dist/chunk-5YQQMEF3.js.map +1 -0
  13. package/dist/chunk-6QCJK7H7.js +2991 -0
  14. package/dist/chunk-6QCJK7H7.js.map +1 -0
  15. package/dist/chunk-6QDYERZD.js +2944 -0
  16. package/dist/chunk-6QDYERZD.js.map +1 -0
  17. package/dist/chunk-76TP67ED.js +2984 -0
  18. package/dist/chunk-76TP67ED.js.map +1 -0
  19. package/dist/chunk-A6KMO4JV.js +2949 -0
  20. package/dist/chunk-A6KMO4JV.js.map +1 -0
  21. package/dist/chunk-B7FTWSTM.js +2938 -0
  22. package/dist/chunk-B7FTWSTM.js.map +1 -0
  23. package/dist/chunk-BCO6M26F.js +2940 -0
  24. package/dist/chunk-BCO6M26F.js.map +1 -0
  25. package/dist/chunk-DBNSBJO7.js +2993 -0
  26. package/dist/chunk-DBNSBJO7.js.map +1 -0
  27. package/dist/chunk-ECIHUVU7.js +2986 -0
  28. package/dist/chunk-ECIHUVU7.js.map +1 -0
  29. package/dist/chunk-HDOTOZNA.js +2936 -0
  30. package/dist/chunk-HDOTOZNA.js.map +1 -0
  31. package/dist/chunk-IUIICQU5.js +2946 -0
  32. package/dist/chunk-IUIICQU5.js.map +1 -0
  33. package/dist/chunk-J2UYZI6D.js +2946 -0
  34. package/dist/chunk-J2UYZI6D.js.map +1 -0
  35. package/dist/chunk-K2H7JLQW.js +2952 -0
  36. package/dist/chunk-K2H7JLQW.js.map +1 -0
  37. package/dist/chunk-KOE4VS4I.js +2945 -0
  38. package/dist/chunk-KOE4VS4I.js.map +1 -0
  39. package/dist/chunk-KUXYBP66.js +2953 -0
  40. package/dist/chunk-KUXYBP66.js.map +1 -0
  41. package/dist/chunk-M2QXKYL2.js +2945 -0
  42. package/dist/chunk-M2QXKYL2.js.map +1 -0
  43. package/dist/chunk-MMXL343D.js +2974 -0
  44. package/dist/chunk-MMXL343D.js.map +1 -0
  45. package/dist/chunk-MXJL3EPE.js +2986 -0
  46. package/dist/chunk-MXJL3EPE.js.map +1 -0
  47. package/dist/chunk-MYZ25B2R.js +2995 -0
  48. package/dist/chunk-MYZ25B2R.js.map +1 -0
  49. package/dist/chunk-NTMEYB7B.js +2949 -0
  50. package/dist/chunk-NTMEYB7B.js.map +1 -0
  51. package/dist/chunk-Q6ERDPQR.js +2981 -0
  52. package/dist/chunk-Q6ERDPQR.js.map +1 -0
  53. package/dist/chunk-SLVDAZSX.js +2946 -0
  54. package/dist/chunk-SLVDAZSX.js.map +1 -0
  55. package/dist/chunk-UQBPYONV.js +2991 -0
  56. package/dist/chunk-UQBPYONV.js.map +1 -0
  57. package/dist/chunk-UXMNBS22.js +2955 -0
  58. package/dist/chunk-UXMNBS22.js.map +1 -0
  59. package/dist/chunk-WEGED7TA.js +2991 -0
  60. package/dist/chunk-WEGED7TA.js.map +1 -0
  61. package/dist/chunk-XSRJM2TR.js +2944 -0
  62. package/dist/chunk-XSRJM2TR.js.map +1 -0
  63. package/dist/chunk-ZAMJEU42.js +2992 -0
  64. package/dist/chunk-ZAMJEU42.js.map +1 -0
  65. package/dist/chunk-ZRAM6FXB.js +2949 -0
  66. package/dist/chunk-ZRAM6FXB.js.map +1 -0
  67. package/dist/components/ChatAttachment/ChatAttachment.d.ts +20 -0
  68. package/dist/components/ChatAttachment/ChatAttachment.d.ts.map +1 -0
  69. package/dist/components/ChatAttachment/index.d.ts +2 -0
  70. package/dist/components/ChatAttachment/index.d.ts.map +1 -0
  71. package/dist/components/ChatBubble/ChatBubble.d.ts +11 -1
  72. package/dist/components/ChatBubble/ChatBubble.d.ts.map +1 -1
  73. package/dist/components/ChatCodeBlock/ChatCodeBlock.d.ts +19 -0
  74. package/dist/components/ChatCodeBlock/ChatCodeBlock.d.ts.map +1 -0
  75. package/dist/components/ChatCodeBlock/index.d.ts +2 -0
  76. package/dist/components/ChatCodeBlock/index.d.ts.map +1 -0
  77. package/dist/components/ChatComposer/ChatComposer.d.ts +51 -0
  78. package/dist/components/ChatComposer/ChatComposer.d.ts.map +1 -0
  79. package/dist/components/ChatComposer/index.d.ts +2 -0
  80. package/dist/components/ChatComposer/index.d.ts.map +1 -0
  81. package/dist/components/ChatDayDivider/ChatDayDivider.d.ts +11 -0
  82. package/dist/components/ChatDayDivider/ChatDayDivider.d.ts.map +1 -0
  83. package/dist/components/ChatDayDivider/index.d.ts +2 -0
  84. package/dist/components/ChatDayDivider/index.d.ts.map +1 -0
  85. package/dist/components/ChatFeedback/ChatFeedback.d.ts +22 -0
  86. package/dist/components/ChatFeedback/ChatFeedback.d.ts.map +1 -0
  87. package/dist/components/ChatFeedback/index.d.ts +2 -0
  88. package/dist/components/ChatFeedback/index.d.ts.map +1 -0
  89. package/dist/components/ChatPanel/ChatPanel.d.ts +34 -0
  90. package/dist/components/ChatPanel/ChatPanel.d.ts.map +1 -0
  91. package/dist/components/ChatPanel/index.d.ts +2 -0
  92. package/dist/components/ChatPanel/index.d.ts.map +1 -0
  93. package/dist/components/ChatScrollToBottom/ChatScrollToBottom.d.ts +12 -0
  94. package/dist/components/ChatScrollToBottom/ChatScrollToBottom.d.ts.map +1 -0
  95. package/dist/components/ChatScrollToBottom/index.d.ts +2 -0
  96. package/dist/components/ChatScrollToBottom/index.d.ts.map +1 -0
  97. package/dist/components/ChatSources/ChatSources.d.ts +34 -0
  98. package/dist/components/ChatSources/ChatSources.d.ts.map +1 -0
  99. package/dist/components/ChatSources/index.d.ts +2 -0
  100. package/dist/components/ChatSources/index.d.ts.map +1 -0
  101. package/dist/components/ChatSuggestions/ChatSuggestions.d.ts +30 -0
  102. package/dist/components/ChatSuggestions/ChatSuggestions.d.ts.map +1 -0
  103. package/dist/components/ChatSuggestions/index.d.ts +2 -0
  104. package/dist/components/ChatSuggestions/index.d.ts.map +1 -0
  105. package/dist/components/ChatThinking/ChatThinking.d.ts +29 -0
  106. package/dist/components/ChatThinking/ChatThinking.d.ts.map +1 -0
  107. package/dist/components/ChatThinking/index.d.ts +2 -0
  108. package/dist/components/ChatThinking/index.d.ts.map +1 -0
  109. package/dist/components/ChatThread/ChatThread.d.ts +25 -0
  110. package/dist/components/ChatThread/ChatThread.d.ts.map +1 -0
  111. package/dist/components/ChatThread/index.d.ts +2 -0
  112. package/dist/components/ChatThread/index.d.ts.map +1 -0
  113. package/dist/components/ChatToolTrace/ChatToolTrace.d.ts +27 -0
  114. package/dist/components/ChatToolTrace/ChatToolTrace.d.ts.map +1 -0
  115. package/dist/components/ChatToolTrace/index.d.ts +2 -0
  116. package/dist/components/ChatToolTrace/index.d.ts.map +1 -0
  117. package/dist/components/ConditionBuilder/ConditionBuilder.d.ts +6 -0
  118. package/dist/components/ConditionBuilder/ConditionBuilder.d.ts.map +1 -1
  119. package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
  120. package/dist/components/ExecutionConsole/ExecutionConsole.d.ts +1 -1
  121. package/dist/components/ExecutionConsole/ExecutionConsole.d.ts.map +1 -1
  122. package/dist/components/FlowMinimap/FlowMinimap.d.ts.map +1 -1
  123. package/dist/components/FlowToolbar/FlowToolbar.d.ts.map +1 -1
  124. package/dist/components/Input/Input.d.ts +1 -1
  125. package/dist/components/Input/Input.d.ts.map +1 -1
  126. package/dist/components/MultiSelect/MultiSelect.d.ts +7 -0
  127. package/dist/components/MultiSelect/MultiSelect.d.ts.map +1 -1
  128. package/dist/components/NumberInput/NumberInput.d.ts +7 -0
  129. package/dist/components/NumberInput/NumberInput.d.ts.map +1 -1
  130. package/dist/components/PageHeader/PageHeader.d.ts +5 -1
  131. package/dist/components/PageHeader/PageHeader.d.ts.map +1 -1
  132. package/dist/components/Select/Select.d.ts.map +1 -1
  133. package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
  134. package/dist/index.cjs +5197 -3852
  135. package/dist/index.cjs.map +1 -1
  136. package/dist/index.d.ts +13 -0
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +4866 -3511
  139. package/dist/index.js.map +1 -1
  140. package/dist/styles.css +1 -1
  141. package/dist/utils/useAnchoredPopover.d.ts +63 -0
  142. package/dist/utils/useAnchoredPopover.d.ts.map +1 -0
  143. package/dist/workflow/components/ConfigPanel/ConfigPanel.d.ts +27 -9
  144. package/dist/workflow/components/ConfigPanel/ConfigPanel.d.ts.map +1 -1
  145. package/dist/workflow/components/FlowCanvas/FlowCanvas.d.ts +8 -0
  146. package/dist/workflow/components/FlowCanvas/FlowCanvas.d.ts.map +1 -1
  147. package/dist/workflow/components/FlowEdge/FlowEdge.d.ts.map +1 -1
  148. package/dist/workflow/components/FxPanel/DataRefButton.d.ts +9 -0
  149. package/dist/workflow/components/FxPanel/DataRefButton.d.ts.map +1 -0
  150. package/dist/workflow/components/FxPanel/FxPanel.d.ts +74 -0
  151. package/dist/workflow/components/FxPanel/FxPanel.d.ts.map +1 -0
  152. package/dist/workflow/components/FxPanel/FxToggleButton.d.ts +9 -0
  153. package/dist/workflow/components/FxPanel/FxToggleButton.d.ts.map +1 -0
  154. package/dist/workflow/components/Handle/Handle.d.ts +9 -1
  155. package/dist/workflow/components/Handle/Handle.d.ts.map +1 -1
  156. package/dist/workflow/components/Handle/handleRegistry.d.ts +19 -0
  157. package/dist/workflow/components/Handle/handleRegistry.d.ts.map +1 -1
  158. package/dist/workflow/components/kinds/index.d.ts.map +1 -1
  159. package/dist/workflow/index.d.ts +2 -0
  160. package/dist/workflow/index.d.ts.map +1 -1
  161. package/dist/workflow/layout/elk.d.ts.map +1 -1
  162. package/dist/workflow/types.d.ts +108 -0
  163. package/dist/workflow/types.d.ts.map +1 -1
  164. package/dist/workflow/utils/parenting.d.ts +5 -3
  165. package/dist/workflow/utils/parenting.d.ts.map +1 -1
  166. package/dist/workflow.cjs +678 -196
  167. package/dist/workflow.cjs.map +1 -1
  168. package/dist/workflow.js +539 -64
  169. package/dist/workflow.js.map +1 -1
  170. package/package.json +2 -2
package/dist/workflow.js CHANGED
@@ -50,7 +50,7 @@ import {
50
50
  useSelection,
51
51
  useViewport,
52
52
  useViewportOrNull
53
- } from "./chunk-XG2OYFX6.js";
53
+ } from "./chunk-KOE4VS4I.js";
54
54
  import {
55
55
  cn
56
56
  } from "./chunk-ZAUUGK2Y.js";
@@ -299,6 +299,9 @@ function createElkEngine(elk, defaults = {}) {
299
299
  if (list) list.push(n);
300
300
  else childrenByParent.set(key, [n]);
301
301
  }
302
+ const engineOptions = Object.fromEntries(
303
+ Object.entries(options?.engine ?? {}).map(([k, v]) => [k, String(v)])
304
+ );
302
305
  const buildChildren = (parentId) => {
303
306
  const list = childrenByParent.get(parentId) ?? [];
304
307
  return list.map((n) => ({
@@ -310,7 +313,8 @@ function createElkEngine(elk, defaults = {}) {
310
313
  "elk.algorithm": algorithm,
311
314
  "elk.direction": ELK_DIRECTION[direction],
312
315
  "elk.spacing.nodeNode": String(nodeSpacing),
313
- "elk.layered.spacing.nodeNodeBetweenLayers": String(rankSpacing)
316
+ "elk.layered.spacing.nodeNodeBetweenLayers": String(rankSpacing),
317
+ ...engineOptions
314
318
  } : void 0
315
319
  }));
316
320
  };
@@ -358,7 +362,7 @@ import {
358
362
  useRef as useRef3,
359
363
  useState as useState3
360
364
  } from "react";
361
- import { jsx, jsxs } from "react/jsx-runtime";
365
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
362
366
  var SAVE_STATE_LABEL = {
363
367
  clean: "Saved",
364
368
  dirty: "Unsaved changes",
@@ -376,26 +380,27 @@ var SAVE_STATE_DOT = {
376
380
  function ConfigPanel({
377
381
  open = true,
378
382
  title,
379
- kindLabel,
380
383
  icon,
384
+ headerActions,
381
385
  banner,
382
386
  tabs,
383
387
  activeTab: controlledActive,
384
388
  defaultActiveTab,
385
389
  onTabChange,
386
- saveState = "clean",
390
+ saveState,
387
391
  onTitleChange,
388
392
  onClose,
389
393
  onSave,
390
394
  onCancel,
395
+ padding = 12,
391
396
  resizable = true,
392
- defaultWidth = 360,
393
- minWidth = 280,
397
+ defaultWidth = 400,
398
+ minWidth = 400,
394
399
  maxWidth = 720,
395
400
  maxWidthContainerInset = 80,
396
401
  variant = "pinned",
397
402
  children,
398
- footer,
403
+ footerActions,
399
404
  className,
400
405
  style
401
406
  }) {
@@ -492,55 +497,53 @@ function ConfigPanel({
492
497
  "aria-hidden": "true"
493
498
  }
494
499
  ),
495
- /* @__PURE__ */ jsxs("header", { className: "ods-flow-config-panel__header", children: [
496
- kindLabel && /* @__PURE__ */ jsx("span", { className: "ods-flow-config-panel__breadcrumb", children: kindLabel }),
497
- /* @__PURE__ */ jsxs("div", { className: "ods-flow-config-panel__title-row", children: [
498
- icon && /* @__PURE__ */ jsx("span", { className: "ods-flow-config-panel__icon", children: icon }),
499
- editingTitle ? /* @__PURE__ */ jsx(
500
- "input",
501
- {
502
- className: "ods-flow-config-panel__title-input",
503
- value: draftTitle,
504
- autoFocus: true,
505
- onChange: (e) => setDraftTitle(e.target.value),
506
- onBlur: () => commitTitle(draftTitle),
507
- onKeyDown: titleKeyDown,
508
- "aria-label": "Edit title"
509
- }
510
- ) : /* @__PURE__ */ jsx(
511
- "button",
512
- {
513
- type: "button",
514
- className: "ods-flow-config-panel__title",
515
- onDoubleClick: () => {
516
- if (!onTitleChange) return;
517
- setDraftTitle(title ?? "");
518
- setEditingTitle(true);
519
- },
520
- title: onTitleChange ? "Double-click to rename" : void 0,
521
- children: title ?? "Untitled"
522
- }
523
- ),
524
- onClose && /* @__PURE__ */ jsx(
525
- "button",
526
- {
527
- type: "button",
528
- className: "ods-flow-config-panel__close",
529
- onClick: onClose,
530
- "aria-label": "Close panel",
531
- children: /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
532
- "path",
533
- {
534
- d: "M3 3l8 8M11 3l-8 8",
535
- stroke: "currentColor",
536
- strokeWidth: "1.5",
537
- strokeLinecap: "round"
538
- }
539
- ) })
540
- }
541
- )
542
- ] })
543
- ] }),
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
+ ] }) }),
544
547
  tabs && tabs.length > 1 && /* @__PURE__ */ jsx("nav", { className: "ods-flow-config-panel__tabs", role: "tablist", children: tabs.map((t) => /* @__PURE__ */ jsxs(
545
548
  "button",
546
549
  {
@@ -561,9 +564,17 @@ function ConfigPanel({
561
564
  t.id
562
565
  )) }),
563
566
  banner && /* @__PURE__ */ jsx("div", { className: "ods-flow-config-panel__banner", children: banner }),
564
- /* @__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
+ ),
565
576
  /* @__PURE__ */ jsxs("footer", { className: "ods-flow-config-panel__footer", children: [
566
- /* @__PURE__ */ jsxs(
577
+ saveState && /* @__PURE__ */ jsxs(
567
578
  "span",
568
579
  {
569
580
  className: cn(
@@ -583,7 +594,7 @@ function ConfigPanel({
583
594
  ]
584
595
  }
585
596
  ),
586
- 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: [
587
598
  onCancel && /* @__PURE__ */ jsx(
588
599
  "button",
589
600
  {
@@ -599,19 +610,481 @@ function ConfigPanel({
599
610
  type: "button",
600
611
  className: "ods-flow-config-panel__btn ods-flow-config-panel__btn--primary",
601
612
  onClick: onSave,
602
- disabled: saveState === "saving" || saveState === "clean",
613
+ disabled: saveState === "saving",
603
614
  children: saveState === "saving" ? "Saving\u2026" : "Save"
604
615
  }
605
616
  )
617
+ ] }) })
618
+ ] })
619
+ ]
620
+ }
621
+ );
622
+ }
623
+
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
699
+ }) {
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);
706
+ };
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);
724
+ }
725
+ }
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;
734
+ });
735
+ };
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);
744
+ }
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";
772
+ }
773
+ };
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;
830
+ }
831
+ };
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
+ ] })
606
891
  ] })
892
+ ]
893
+ },
894
+ item.id
895
+ );
896
+ };
897
+ return /* @__PURE__ */ jsxs2(
898
+ "aside",
899
+ {
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
+ })
607
1043
  ] })
608
1044
  ]
609
1045
  }
610
1046
  );
611
1047
  }
612
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
+ );
1084
+ }
1085
+
613
1086
  // src/workflow/components/NodeToolbar/NodeToolbar.tsx
614
- import { jsx as jsx2 } from "react/jsx-runtime";
1087
+ import { jsx as jsx4 } from "react/jsx-runtime";
615
1088
  function NodeToolbar({
616
1089
  isVisible,
617
1090
  position = "top",
@@ -626,7 +1099,7 @@ function NodeToolbar({
626
1099
  const show = isVisible ?? node.selected;
627
1100
  if (!show) return null;
628
1101
  const inverseScale = 1 / viewport.zoom;
629
- return /* @__PURE__ */ jsx2(
1102
+ return /* @__PURE__ */ jsx4(
630
1103
  "div",
631
1104
  {
632
1105
  className: cn("ods-node-toolbar", `ods-node-toolbar--${position}`, className),
@@ -638,7 +1111,7 @@ function NodeToolbar({
638
1111
  onPointerDown: (e) => e.stopPropagation(),
639
1112
  onMouseDown: (e) => e.stopPropagation(),
640
1113
  onClick: (e) => e.stopPropagation(),
641
- children: /* @__PURE__ */ jsx2(
1114
+ children: /* @__PURE__ */ jsx4(
642
1115
  "div",
643
1116
  {
644
1117
  className: "ods-node-toolbar__inner",
@@ -786,11 +1259,13 @@ export {
786
1259
  DEFAULT_NODE_HEIGHT,
787
1260
  DEFAULT_NODE_KINDS,
788
1261
  DEFAULT_NODE_WIDTH,
1262
+ DataRefButton,
789
1263
  ErrorNode,
790
1264
  FlowCanvas,
791
1265
  FlowEdge,
792
1266
  FlowNode,
793
1267
  ForEachNode,
1268
+ FxPanel,
794
1269
  GroupNode,
795
1270
  Handle,
796
1271
  HttpRequestNode,