@burdenoff/fluidgrids-fe-sdk 2026.624.2 → 2026.626.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.
@@ -1,7 +1,7 @@
1
- import { registerWorkflowNodeTypes, ensureNodeConfigsLoaded, onNodeTypesUpdate, NODE_TYPES, getNodeMetadata, useNodeExecution, AVAILABLE_NODES, createNodeWithDefaultData, NODES_METADATA, isNodeConfigsInitialized, HeaderWithIcon, useInstalledNodes, useInstallNode, useRefreshAvailableNodes, getNodeDetail, NODES, NodePropertyPanel, NODE_CONFIGS, ConnectionSelector } from './chunk-2YP4QVEZ.js';
2
- import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, Input as Input$1 } from './chunk-7VEOUEIN.js';
1
+ import { registerWorkflowNodeTypes, ensureNodeConfigsLoaded, onNodeTypesUpdate, NODE_TYPES, getNodeMetadata, useNodeExecution, getNodeDetail, AVAILABLE_NODES, createNodeWithDefaultData, NODES_METADATA, NODE_CONFIGS, isNodeConfigsInitialized, HeaderWithIcon, useInstalledNodes, useInstallNode, useRefreshAvailableNodes, NODES, NodePropertyPanel, ConnectionSelector } from './chunk-KBHRFZXR.js';
2
+ import { Dialog as Dialog$1, DialogContent as DialogContent$1, DialogHeader as DialogHeader$1, DialogTitle as DialogTitle$1, DialogDescription as DialogDescription$1, DialogFooter, Input as Input$1 } from './chunk-7VEOUEIN.js';
3
3
  import { cn, Badge as Badge$1, Button } from './chunk-Z54K22LP.js';
4
- import { useFlowStore } from './chunk-Z2ZEVYDE.js';
4
+ import { useFlowStore } from './chunk-KU64CCSV.js';
5
5
  import { useLazyWorkflowDefinition, useCreateWorkflow, useWorkflowVersions, useRestoreWorkflowVersion, useActivateWorkflow, useDeactivateWorkflow } from './chunk-4FFPGQD3.js';
6
6
  import { NodeSchemaIntrospectionDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, NodeUsageStatusDocument, RemoveNodeFromWorkflowsDocument } from './chunk-FGYVIXQ7.js';
7
7
  import { BrowseWorkflowTemplatesDocument, BrowseWorkflowNodesDocument, UninstallWorkflowNodeDocument, CreateStoreOrderDocument } from './chunk-LUGJJ7LL.js';
@@ -17,7 +17,7 @@ import React3__default, { forwardRef, lazy, memo, createContext, useContext, use
17
17
  import { Handle, Position, getBezierPath, MarkerType, ReactFlowProvider, useReactFlow, ReactFlow, Background, useOnSelectionChange, useStore, MiniMap, getSmoothStepPath, EdgeLabelRenderer, getOutgoers, getConnectedEdges, useNodes, useNodesState, useEdgesState, addEdge, Controls, Panel } from '@xyflow/react';
18
18
  import { produce } from 'immer';
19
19
  import { cn as cn$1 } from '@burdenoff/fe-libs/utils';
20
- import { Card as Card$1, Badge, CardContent as CardContent$1, Button as Button$1, Input, Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, Popover, PopoverTrigger, PopoverContent, Toaster, Tabs, TabsList, TabsTrigger, TabsContent, CardHeader as CardHeader$1, CardTitle as CardTitle$1, CardDescription as CardDescription$1, ScrollArea, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, Label, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Textarea } from '@burdenoff/fe-libs/ui';
20
+ import { Card as Card$1, Badge, CardContent as CardContent$1, Button as Button$1, Input, Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Popover, PopoverTrigger, PopoverContent, Toaster, Tabs, TabsList, TabsTrigger, TabsContent, CardHeader as CardHeader$1, CardTitle as CardTitle$1, CardDescription as CardDescription$1, ScrollArea, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, Label, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuItem, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Textarea } from '@burdenoff/fe-libs/ui';
21
21
  import { isEmpty } from 'radash';
22
22
  import { Icon } from '@iconify/react';
23
23
  import { gql } from '@apollo/client';
@@ -2573,18 +2573,18 @@ function TemplatesModal({
2573
2573
  const handleCloseDetailPanel = () => {
2574
2574
  setSelectedTemplate(null);
2575
2575
  };
2576
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
2577
- DialogContent,
2576
+ return /* @__PURE__ */ jsx(Dialog$1, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
2577
+ DialogContent$1,
2578
2578
  {
2579
2579
  size: "xl",
2580
2580
  className: "!flex !flex-col !h-[75vh] !max-h-[75vh] min-h-[500px] p-0 gap-0 overflow-hidden",
2581
2581
  children: [
2582
- /* @__PURE__ */ jsxs(DialogHeader, { className: "px-6 pt-6 pb-4 border-b shrink-0", children: [
2582
+ /* @__PURE__ */ jsxs(DialogHeader$1, { className: "px-6 pt-6 pb-4 border-b shrink-0", children: [
2583
2583
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2584
2584
  /* @__PURE__ */ jsx(Sparkles, { className: "size-5 text-primary" }),
2585
- /* @__PURE__ */ jsx(DialogTitle, { className: "text-2xl", children: "Workflow Templates" })
2585
+ /* @__PURE__ */ jsx(DialogTitle$1, { className: "text-2xl", children: "Workflow Templates" })
2586
2586
  ] }),
2587
- /* @__PURE__ */ jsx(DialogDescription, { children: "Browse and use pre-built workflow templates to get started quickly" }),
2587
+ /* @__PURE__ */ jsx(DialogDescription$1, { children: "Browse and use pre-built workflow templates to get started quickly" }),
2588
2588
  /* @__PURE__ */ jsx("div", { className: "pt-4", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2589
2589
  /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" }),
2590
2590
  /* @__PURE__ */ jsx(
@@ -2692,7 +2692,7 @@ function DesktopSidebarFragment({
2692
2692
  const handleUseTemplate = useCallback(
2693
2693
  async (template) => {
2694
2694
  try {
2695
- const { useImportWorkflowTemplate: useImportWorkflowTemplate2 } = await import('./useImportWorkflowTemplate-PNE2ANBV.js');
2695
+ const { useImportWorkflowTemplate: useImportWorkflowTemplate2 } = await import('./useImportWorkflowTemplate-UTPGK45L.js');
2696
2696
  const { useTemplateDetail: useTemplateDetail2 } = await import('./useTemplateDetail-NK63V3PC.js');
2697
2697
  toast.loading("Loading template...", { id: "template-load" });
2698
2698
  const { getGlobalStoreClient } = await import('./global-client-OW43ZRDR.js');
@@ -9929,9 +9929,9 @@ function TriggerDataDialog({
9929
9929
  setSubmitted(true);
9930
9930
  onExecute({});
9931
9931
  }, [onExecute]);
9932
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", children: [
9933
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
9934
- /* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
9932
+ return /* @__PURE__ */ jsx(Dialog$1, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent$1, { className: "sm:max-w-lg", children: [
9933
+ /* @__PURE__ */ jsxs(DialogHeader$1, { children: [
9934
+ /* @__PURE__ */ jsxs(DialogTitle$1, { className: "flex items-center gap-2", children: [
9935
9935
  /* @__PURE__ */ jsx(
9936
9936
  Icon,
9937
9937
  {
@@ -9941,7 +9941,7 @@ function TriggerDataDialog({
9941
9941
  ),
9942
9942
  "Execute Workflow"
9943
9943
  ] }),
9944
- /* @__PURE__ */ jsx(DialogDescription, { children: "Provide the trigger data (JSON) that will be passed to the first node in the workflow. This is the input your workflow will process." })
9944
+ /* @__PURE__ */ jsx(DialogDescription$1, { children: "Provide the trigger data (JSON) that will be passed to the first node in the workflow. This is the input your workflow will process." })
9945
9945
  ] }),
9946
9946
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
9947
9947
  /* @__PURE__ */ jsx(
@@ -10710,6 +10710,714 @@ var ChatPanel = () => {
10710
10710
  }
10711
10711
  );
10712
10712
  };
10713
+
10714
+ // src/builder/lib/field-schema.ts
10715
+ function isRecord(value) {
10716
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10717
+ }
10718
+ function isIdentifier(key) {
10719
+ return /^[A-Za-z_$][\w$]*$/.test(key);
10720
+ }
10721
+ function joinPath(base, key) {
10722
+ if (isIdentifier(key)) return base ? `${base}.${key}` : key;
10723
+ const bracket = `[${JSON.stringify(key)}]`;
10724
+ return base ? `${base}${bracket}` : bracket;
10725
+ }
10726
+ function pathToExpression(path) {
10727
+ if (!path) return "${output}";
10728
+ const accessor = path.startsWith("[") ? `output${path}` : `output.${path}`;
10729
+ return "${" + accessor + "}";
10730
+ }
10731
+ function fieldFromSpec(key, path, spec) {
10732
+ if (typeof spec === "string") {
10733
+ return { key, path, type: spec };
10734
+ }
10735
+ if (isRecord(spec)) {
10736
+ const declaredType = typeof spec.type === "string" ? spec.type : void 0;
10737
+ const description = typeof spec.description === "string" ? spec.description : void 0;
10738
+ const required = spec.required === true;
10739
+ if (isRecord(spec.properties)) {
10740
+ const children = Object.entries(spec.properties).map(
10741
+ ([k, v]) => fieldFromSpec(k, joinPath(path, k), v)
10742
+ );
10743
+ return { key, path, type: "object", required, description, children };
10744
+ }
10745
+ if (spec.items !== void 0) {
10746
+ const element = fieldFromSpec("0", `${path}[0]`, spec.items);
10747
+ return {
10748
+ key,
10749
+ path,
10750
+ type: "array",
10751
+ required,
10752
+ description,
10753
+ children: [element]
10754
+ };
10755
+ }
10756
+ return { key, path, type: declaredType ?? "any", required, description };
10757
+ }
10758
+ return { key, path, type: "any" };
10759
+ }
10760
+ function fieldsFromIO(io, root = "") {
10761
+ if (!isRecord(io)) return [];
10762
+ return Object.entries(io).map(
10763
+ ([key, spec]) => fieldFromSpec(key, joinPath(root, key), spec)
10764
+ );
10765
+ }
10766
+ function normalizeOperations(raw) {
10767
+ if (Array.isArray(raw)) return raw;
10768
+ if (isRecord(raw) && Array.isArray(raw.operations)) {
10769
+ return raw.operations;
10770
+ }
10771
+ return [];
10772
+ }
10773
+ function resolveNodeOperation(data, fallbackOperations2) {
10774
+ const nodeData = isRecord(data?.nodeData) ? data?.nodeData : void 0;
10775
+ let operations = normalizeOperations(nodeData?.operations);
10776
+ if (operations.length === 0) {
10777
+ operations = normalizeOperations(data?.operations);
10778
+ }
10779
+ if (operations.length === 0 && fallbackOperations2 !== void 0) {
10780
+ operations = normalizeOperations(fallbackOperations2);
10781
+ }
10782
+ const opName = typeof data?.operation_name === "string" ? data.operation_name : void 0;
10783
+ const selected = operations.find((op) => op.name === opName) ?? operations[0];
10784
+ return { operations, selected };
10785
+ }
10786
+ var FIELD_PATH_MIME = "application/x-fg-field-path";
10787
+ var TYPE_ICON = {
10788
+ string: "mdi:format-text",
10789
+ number: "mdi:numeric",
10790
+ integer: "mdi:numeric",
10791
+ boolean: "mdi:toggle-switch-outline",
10792
+ array: "mdi:code-brackets",
10793
+ object: "mdi:code-braces",
10794
+ any: "mdi:help-circle-outline"
10795
+ };
10796
+ function FieldRow({ field, depth, draggable }) {
10797
+ const [open, setOpen] = useState(depth < 1);
10798
+ const hasChildren = !!field.children && field.children.length > 0;
10799
+ const handleDragStart = (e) => {
10800
+ e.dataTransfer.setData(FIELD_PATH_MIME, field.path);
10801
+ e.dataTransfer.setData("text/plain", pathToExpression(field.path));
10802
+ e.dataTransfer.effectAllowed = "copy";
10803
+ };
10804
+ return /* @__PURE__ */ jsxs("div", { children: [
10805
+ /* @__PURE__ */ jsxs(
10806
+ "div",
10807
+ {
10808
+ className: `group flex items-center gap-1.5 rounded-md py-1 pr-2 text-xs hover:bg-muted ${draggable ? "cursor-grab active:cursor-grabbing" : ""}`,
10809
+ style: { paddingLeft: `${depth * 14 + 6}px` },
10810
+ draggable,
10811
+ onDragStart: draggable ? handleDragStart : void 0,
10812
+ "data-field-path": field.path,
10813
+ title: field.description || field.path,
10814
+ children: [
10815
+ hasChildren ? /* @__PURE__ */ jsx(
10816
+ "button",
10817
+ {
10818
+ type: "button",
10819
+ onClick: () => setOpen((v) => !v),
10820
+ className: "flex size-4 items-center justify-center text-muted-foreground hover:text-foreground",
10821
+ children: /* @__PURE__ */ jsx(
10822
+ Icon,
10823
+ {
10824
+ icon: open ? "mdi:chevron-down" : "mdi:chevron-right",
10825
+ className: "size-3.5"
10826
+ }
10827
+ )
10828
+ }
10829
+ ) : /* @__PURE__ */ jsx("span", { className: "inline-block size-4" }),
10830
+ /* @__PURE__ */ jsx(
10831
+ Icon,
10832
+ {
10833
+ icon: TYPE_ICON[field.type] ?? TYPE_ICON.any,
10834
+ className: "size-3.5 shrink-0 text-primary/70"
10835
+ }
10836
+ ),
10837
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: field.key }),
10838
+ /* @__PURE__ */ jsxs("span", { className: "ml-auto shrink-0 text-[10px] lowercase tracking-wide text-muted-foreground", children: [
10839
+ field.type,
10840
+ field.required ? " *" : ""
10841
+ ] })
10842
+ ]
10843
+ }
10844
+ ),
10845
+ hasChildren && open && /* @__PURE__ */ jsx("div", { children: field.children.map((child) => /* @__PURE__ */ jsx(
10846
+ FieldRow,
10847
+ {
10848
+ field: child,
10849
+ depth: depth + 1,
10850
+ draggable
10851
+ },
10852
+ child.path
10853
+ )) })
10854
+ ] });
10855
+ }
10856
+ function FieldTree({
10857
+ fields,
10858
+ draggable = false,
10859
+ emptyMessage = "No fields available."
10860
+ }) {
10861
+ if (fields.length === 0) {
10862
+ return /* @__PURE__ */ jsx("div", { className: "px-2 py-4 text-xs text-card-foreground/50", children: emptyMessage });
10863
+ }
10864
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5", children: fields.map((field) => /* @__PURE__ */ jsx(
10865
+ FieldRow,
10866
+ {
10867
+ field,
10868
+ depth: 0,
10869
+ draggable
10870
+ },
10871
+ field.path
10872
+ )) });
10873
+ }
10874
+ function coerceNumber(type, raw) {
10875
+ if (raw.trim() === "") return void 0;
10876
+ const n = type === "integer" ? parseInt(raw, 10) : parseFloat(raw);
10877
+ return Number.isNaN(n) ? void 0 : n;
10878
+ }
10879
+ function asRecord(value) {
10880
+ return typeof value === "object" && value !== null ? value : void 0;
10881
+ }
10882
+ function fallbackOperations(type) {
10883
+ if (!type) return void 0;
10884
+ return NODE_CONFIGS.find((c) => c.type === type)?.operations;
10885
+ }
10886
+ function selectedOperationOf(data, type) {
10887
+ return resolveNodeOperation(data, fallbackOperations(type)).selected;
10888
+ }
10889
+ function paramsFromOperation(op) {
10890
+ const io = asRecord(op?.input_fields);
10891
+ if (!io) return [];
10892
+ return Object.entries(io).map(([name, spec]) => {
10893
+ const rec = asRecord(spec);
10894
+ const optsRaw = rec?.options ?? rec?.enum;
10895
+ const options = Array.isArray(optsRaw) ? optsRaw.map(
10896
+ (o) => typeof o === "string" ? o : asRecord(o)?.value ?? asRecord(o)?.name
10897
+ ).filter((v) => typeof v === "string") : void 0;
10898
+ return {
10899
+ name,
10900
+ type: typeof spec === "string" ? spec : typeof rec?.type === "string" ? rec.type : "any",
10901
+ required: rec?.required === true,
10902
+ description: typeof rec?.description === "string" ? rec.description : void 0,
10903
+ options,
10904
+ defaultValue: rec?.default
10905
+ };
10906
+ });
10907
+ }
10908
+ function nodeTitle(node) {
10909
+ if (!node) return "Node";
10910
+ const data = asRecord(node.data);
10911
+ const label = typeof data?.label === "string" ? data.label : "";
10912
+ return label || getNodeDetail(node.type).title || node.id;
10913
+ }
10914
+ function nodeSubtitle(node) {
10915
+ const data = asRecord(node?.data);
10916
+ return typeof data?.operation_name === "string" ? data.operation_name : void 0;
10917
+ }
10918
+ var SECTION_LABEL = "text-[11px] font-semibold uppercase tracking-[0.12em] text-muted-foreground";
10919
+ function NodeDetailView() {
10920
+ const ndv = useFlowStore((s) => s.workflow.ndv);
10921
+ const nodes = useFlowStore((s) => s.workflow.nodes);
10922
+ const edges = useFlowStore((s) => s.workflow.edges);
10923
+ const closeNdv = useFlowStore((s) => s.actions.ndv.close);
10924
+ const showNodePropertiesOf = useFlowStore(
10925
+ (s) => s.actions.sidebar.showNodePropertiesOf
10926
+ );
10927
+ const open = !!ndv;
10928
+ const node = useMemo(
10929
+ () => ndv ? nodes.find((n) => n.id === ndv.nodeId) : void 0,
10930
+ [ndv, nodes]
10931
+ );
10932
+ const incoming = useMemo(
10933
+ () => ndv ? edges.filter((e) => e.target === ndv.nodeId) : [],
10934
+ [edges, ndv]
10935
+ );
10936
+ const sourceIds = useMemo(() => incoming.map((e) => e.source), [incoming]);
10937
+ const sourceKey = sourceIds.join(",");
10938
+ const [selectedSource, setSelectedSource] = useState();
10939
+ const [tab, setTab] = useState("parameters");
10940
+ const [outputView, setOutputView] = useState("schema");
10941
+ const [dragField, setDragField] = useState(null);
10942
+ const [modeByField, setModeByField] = useState({});
10943
+ const panesRef = useRef(null);
10944
+ const dragCleanupRef = useRef(null);
10945
+ const [leftPct, setLeftPct] = useState(30);
10946
+ const [rightPct, setRightPct] = useState(30);
10947
+ const startDrag = (which) => (e) => {
10948
+ e.preventDefault();
10949
+ const el = panesRef.current;
10950
+ if (!el) return;
10951
+ const rect = el.getBoundingClientRect();
10952
+ const onMove = (ev) => {
10953
+ const pct = (ev.clientX - rect.left) / rect.width * 100;
10954
+ if (which === "left") setLeftPct(Math.min(55, Math.max(15, pct)));
10955
+ else setRightPct(Math.min(55, Math.max(15, 100 - pct)));
10956
+ };
10957
+ const cleanup = () => {
10958
+ window.removeEventListener("mousemove", onMove);
10959
+ window.removeEventListener("mouseup", cleanup);
10960
+ dragCleanupRef.current = null;
10961
+ };
10962
+ dragCleanupRef.current = cleanup;
10963
+ window.addEventListener("mousemove", onMove);
10964
+ window.addEventListener("mouseup", cleanup);
10965
+ };
10966
+ useEffect(() => () => dragCleanupRef.current?.(), []);
10967
+ useEffect(() => {
10968
+ setSelectedSource(
10969
+ (prev) => prev && sourceIds.includes(prev) ? prev : sourceIds[0]
10970
+ );
10971
+ }, [ndv?.nodeId, sourceKey, sourceIds]);
10972
+ useEffect(() => {
10973
+ if (open) setTab("parameters");
10974
+ }, [open, ndv?.nodeId]);
10975
+ useEffect(() => {
10976
+ setModeByField({});
10977
+ }, [ndv?.nodeId, selectedSource]);
10978
+ const sourceNode = useMemo(
10979
+ () => nodes.find((n) => n.id === selectedSource),
10980
+ [nodes, selectedSource]
10981
+ );
10982
+ const currentEdge = useMemo(
10983
+ () => incoming.find((e) => e.source === selectedSource),
10984
+ [incoming, selectedSource]
10985
+ );
10986
+ const currentOp = useMemo(
10987
+ () => selectedOperationOf(asRecord(node?.data), node?.type),
10988
+ [node]
10989
+ );
10990
+ const sourceOp = useMemo(
10991
+ () => selectedOperationOf(asRecord(sourceNode?.data), sourceNode?.type),
10992
+ [sourceNode]
10993
+ );
10994
+ const inputFields = useMemo(
10995
+ () => fieldsFromIO(sourceOp?.output_fields),
10996
+ [sourceOp]
10997
+ );
10998
+ const outputFields = useMemo(
10999
+ () => fieldsFromIO(currentOp?.output_fields),
11000
+ [currentOp]
11001
+ );
11002
+ const params = useMemo(() => paramsFromOperation(currentOp), [currentOp]);
11003
+ const mapping = currentEdge?.mapping?.definition ?? {};
11004
+ const argsState = useMemo(() => {
11005
+ const data = asRecord(node?.data);
11006
+ const args = asRecord(data?.args);
11007
+ return asRecord(args?.state) ?? {};
11008
+ }, [node]);
11009
+ const writeMapping = (field, value) => {
11010
+ if (!currentEdge) return;
11011
+ const store = useFlowStore.getState();
11012
+ const freshEdges = store.workflow.edges;
11013
+ const next = freshEdges.map((e) => {
11014
+ if (e.id !== currentEdge.id) return e;
11015
+ const def = { ...e.mapping?.definition ?? {} };
11016
+ if (value.trim()) def[field] = value;
11017
+ else delete def[field];
11018
+ return {
11019
+ ...e,
11020
+ mapping: {
11021
+ definition: def,
11022
+ comment: e.mapping?.comment ?? `Map ${selectedSource} \u2192 ${ndv?.nodeId ?? ""}`
11023
+ }
11024
+ };
11025
+ });
11026
+ store.actions.edges.setEdges(next);
11027
+ };
11028
+ const clearMappingFromAllIncoming = (field) => {
11029
+ if (!ndv) return;
11030
+ const store = useFlowStore.getState();
11031
+ const fresh = store.workflow.edges;
11032
+ let changed = false;
11033
+ const next = fresh.map((e) => {
11034
+ if (e.target !== ndv.nodeId || !e.mapping?.definition) return e;
11035
+ if (!(field in e.mapping.definition)) return e;
11036
+ const def = { ...e.mapping.definition };
11037
+ delete def[field];
11038
+ changed = true;
11039
+ return { ...e, mapping: { ...e.mapping, definition: def } };
11040
+ });
11041
+ if (changed) store.actions.edges.setEdges(next);
11042
+ };
11043
+ const writeFixed = (field, value) => {
11044
+ if (!ndv) return;
11045
+ const store = useFlowStore.getState();
11046
+ const next = store.workflow.nodes.map((n) => {
11047
+ if (n.id !== ndv.nodeId) return n;
11048
+ const data = { ...asRecord(n.data) ?? {} };
11049
+ const args = { ...asRecord(data.args) ?? {} };
11050
+ const state = { ...asRecord(args.state) ?? {} };
11051
+ if (value === void 0 || value === null || value === "") {
11052
+ delete state[field];
11053
+ } else {
11054
+ state[field] = value;
11055
+ }
11056
+ args.state = state;
11057
+ data.args = args;
11058
+ return { ...n, data };
11059
+ });
11060
+ store.actions.nodes.setNodes(next);
11061
+ clearMappingFromAllIncoming(field);
11062
+ };
11063
+ const toggleMode = (field, current) => {
11064
+ if (current === "expression") {
11065
+ clearMappingFromAllIncoming(field);
11066
+ }
11067
+ setModeByField((m) => ({
11068
+ ...m,
11069
+ [field]: current === "expression" ? "fixed" : "expression"
11070
+ }));
11071
+ };
11072
+ const handleDropOnParam = (field) => (e) => {
11073
+ e.preventDefault();
11074
+ setDragField(null);
11075
+ const path = e.dataTransfer.getData(FIELD_PATH_MIME);
11076
+ if (path) {
11077
+ writeMapping(field, pathToExpression(path));
11078
+ setModeByField((m) => ({ ...m, [field]: "expression" }));
11079
+ }
11080
+ };
11081
+ const openClassicPanel = () => {
11082
+ if (!ndv) return;
11083
+ showNodePropertiesOf({
11084
+ id: ndv.nodeId,
11085
+ type: ndv.type
11086
+ });
11087
+ closeNdv();
11088
+ };
11089
+ const headerIcon = getNodeDetail(node?.type).icon;
11090
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (o) => !o && closeNdv(), children: /* @__PURE__ */ jsxs(
11091
+ DialogContent,
11092
+ {
11093
+ style: {
11094
+ width: "94vw",
11095
+ height: "92vh",
11096
+ maxWidth: "none",
11097
+ maxHeight: "92vh",
11098
+ // Above the app shell navbar so the header (title/close) isn't occluded.
11099
+ zIndex: 1e3
11100
+ },
11101
+ className: "flex flex-col gap-0 overflow-hidden border-border bg-background p-0",
11102
+ "data-testid": "ndv-modal",
11103
+ children: [
11104
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "flex shrink-0 flex-row items-center gap-3 space-y-0 border-b border-border px-4 py-3", children: [
11105
+ /* @__PURE__ */ jsx("div", { className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-muted", children: /* @__PURE__ */ jsx(Icon, { icon: headerIcon, className: "size-4 text-foreground" }) }),
11106
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
11107
+ /* @__PURE__ */ jsx(DialogTitle, { className: "truncate text-sm font-semibold leading-tight", children: nodeTitle(node) }),
11108
+ nodeSubtitle(node) && /* @__PURE__ */ jsx(DialogDescription, { className: "truncate text-xs leading-tight", children: nodeSubtitle(node) })
11109
+ ] }),
11110
+ /* @__PURE__ */ jsx(
11111
+ "button",
11112
+ {
11113
+ type: "button",
11114
+ onClick: openClassicPanel,
11115
+ className: "rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
11116
+ children: "Side panel"
11117
+ }
11118
+ ),
11119
+ /* @__PURE__ */ jsx(
11120
+ "button",
11121
+ {
11122
+ type: "button",
11123
+ "aria-label": "Close",
11124
+ onClick: closeNdv,
11125
+ className: "rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground",
11126
+ children: /* @__PURE__ */ jsx(Icon, { icon: "mdi:close", className: "size-4" })
11127
+ }
11128
+ )
11129
+ ] }),
11130
+ /* @__PURE__ */ jsxs("div", { ref: panesRef, className: "flex min-h-0 flex-1", children: [
11131
+ /* @__PURE__ */ jsxs(
11132
+ "section",
11133
+ {
11134
+ style: { width: `${leftPct}%` },
11135
+ className: "flex min-h-0 min-w-[180px] flex-col bg-card/40",
11136
+ children: [
11137
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-2.5", children: [
11138
+ /* @__PURE__ */ jsx("span", { className: SECTION_LABEL, children: "Input" }),
11139
+ sourceIds.length > 0 && /* @__PURE__ */ jsx(
11140
+ "select",
11141
+ {
11142
+ className: "ml-auto max-w-[60%] truncate rounded-md border border-input bg-background px-2 py-1 text-xs text-foreground outline-none focus:border-primary",
11143
+ value: selectedSource ?? "",
11144
+ onChange: (e) => setSelectedSource(e.target.value),
11145
+ "data-testid": "ndv-source-selector",
11146
+ children: sourceIds.map((sid) => {
11147
+ const sn = nodes.find((n) => n.id === sid);
11148
+ return /* @__PURE__ */ jsx("option", { value: sid, children: nodeTitle(sn) }, sid);
11149
+ })
11150
+ }
11151
+ )
11152
+ ] }),
11153
+ /* @__PURE__ */ jsx(
11154
+ "div",
11155
+ {
11156
+ className: "min-h-0 flex-1 overflow-auto px-2 pb-3",
11157
+ "data-testid": "ndv-input-tree",
11158
+ children: inputFields.length > 0 ? /* @__PURE__ */ jsx(FieldTree, { fields: inputFields, draggable: true }) : /* @__PURE__ */ jsx(
11159
+ EmptyState,
11160
+ {
11161
+ icon: "mdi:tray-arrow-down",
11162
+ title: "No input data",
11163
+ hint: sourceIds.length === 0 ? "Connect an upstream node to map its fields." : "The selected source has no declared output fields."
11164
+ }
11165
+ )
11166
+ }
11167
+ )
11168
+ ]
11169
+ }
11170
+ ),
11171
+ /* @__PURE__ */ jsx(ResizeHandle, { onStart: startDrag("left") }),
11172
+ /* @__PURE__ */ jsxs("section", { className: "flex min-h-0 min-w-[280px] flex-1 flex-col", children: [
11173
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-4 border-b border-border px-4", children: [
11174
+ /* @__PURE__ */ jsx(
11175
+ TabButton,
11176
+ {
11177
+ active: tab === "parameters",
11178
+ onClick: () => setTab("parameters"),
11179
+ children: "Parameters"
11180
+ }
11181
+ ),
11182
+ /* @__PURE__ */ jsx(
11183
+ TabButton,
11184
+ {
11185
+ active: tab === "settings",
11186
+ onClick: () => setTab("settings"),
11187
+ children: "Settings"
11188
+ }
11189
+ )
11190
+ ] }),
11191
+ tab === "parameters" ? /* @__PURE__ */ jsxs(
11192
+ "div",
11193
+ {
11194
+ className: "min-h-0 flex-1 space-y-4 overflow-auto p-4",
11195
+ "data-testid": "ndv-params",
11196
+ children: [
11197
+ !currentEdge && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: "No upstream node connected \u2014 set fixed values below, or connect a node to map fields with expressions." }),
11198
+ params.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-6 text-center text-xs text-muted-foreground", children: "This operation has no input parameters." }) : params.map((p) => {
11199
+ const mode = modeByField[p.name] ?? (mapping[p.name] !== void 0 ? "expression" : "fixed");
11200
+ const exprValue = mapping[p.name] ?? "";
11201
+ const fixedValue = argsState[p.name];
11202
+ const highlight = dragField === p.name;
11203
+ const inputCx = cn$1(
11204
+ "w-full rounded-md border bg-background px-2.5 py-1.5 text-sm outline-none transition-colors disabled:opacity-50",
11205
+ highlight ? "border-primary ring-1 ring-primary" : "border-input focus:border-primary"
11206
+ );
11207
+ return /* @__PURE__ */ jsxs(
11208
+ "div",
11209
+ {
11210
+ className: "flex flex-col gap-1.5",
11211
+ onDragOver: (e) => e.preventDefault(),
11212
+ onDragEnter: () => setDragField(p.name),
11213
+ onDragLeave: () => setDragField((f) => f === p.name ? null : f),
11214
+ onDrop: handleDropOnParam(p.name),
11215
+ children: [
11216
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
11217
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-foreground", children: p.name }),
11218
+ p.required && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "*" }),
11219
+ /* @__PURE__ */ jsx("span", { className: "ml-auto text-[10px] uppercase tracking-wide text-muted-foreground", children: p.type }),
11220
+ /* @__PURE__ */ jsx(
11221
+ "button",
11222
+ {
11223
+ type: "button",
11224
+ onClick: () => toggleMode(p.name, mode),
11225
+ title: mode === "expression" ? "Switch to a fixed value" : "Switch to an expression",
11226
+ "data-testid": `ndv-mode-${p.name}`,
11227
+ className: cn$1(
11228
+ "ml-1 rounded p-0.5 hover:text-foreground",
11229
+ mode === "expression" ? "text-primary" : "text-muted-foreground"
11230
+ ),
11231
+ children: /* @__PURE__ */ jsx(
11232
+ Icon,
11233
+ {
11234
+ icon: mode === "expression" ? "mdi:variable" : "mdi:form-textbox",
11235
+ className: "size-3.5"
11236
+ }
11237
+ )
11238
+ }
11239
+ )
11240
+ ] }),
11241
+ mode === "expression" ? /* @__PURE__ */ jsx(
11242
+ "input",
11243
+ {
11244
+ type: "text",
11245
+ value: exprValue,
11246
+ placeholder: currentEdge ? "Drop a field or type ${output.\u2026}" : "Connect an upstream node to use expressions",
11247
+ disabled: !currentEdge,
11248
+ onChange: (e) => writeMapping(p.name, e.target.value),
11249
+ "data-testid": `ndv-param-${p.name}`,
11250
+ className: cn$1(inputCx, "font-mono text-primary")
11251
+ }
11252
+ ) : p.type === "boolean" ? /* @__PURE__ */ jsxs("label", { className: "flex w-fit cursor-pointer items-center gap-2 text-sm", children: [
11253
+ /* @__PURE__ */ jsx(
11254
+ "input",
11255
+ {
11256
+ type: "checkbox",
11257
+ checked: fixedValue === true,
11258
+ onChange: (e) => writeFixed(p.name, e.target.checked),
11259
+ "data-testid": `ndv-param-${p.name}`,
11260
+ className: "size-4"
11261
+ }
11262
+ ),
11263
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: fixedValue === void 0 ? "Not set (default)" : fixedValue === true ? "true" : "false" })
11264
+ ] }) : p.options && p.options.length > 0 ? /* @__PURE__ */ jsxs(
11265
+ "select",
11266
+ {
11267
+ value: typeof fixedValue === "string" ? fixedValue : "",
11268
+ onChange: (e) => writeFixed(p.name, e.target.value),
11269
+ "data-testid": `ndv-param-${p.name}`,
11270
+ className: inputCx,
11271
+ children: [
11272
+ /* @__PURE__ */ jsx("option", { value: "", children: p.defaultValue !== void 0 ? `Default (${String(p.defaultValue)})` : "Select\u2026" }),
11273
+ p.options.map((o) => /* @__PURE__ */ jsx("option", { value: o, children: o }, o))
11274
+ ]
11275
+ }
11276
+ ) : p.type === "integer" || p.type === "number" ? /* @__PURE__ */ jsx(
11277
+ "input",
11278
+ {
11279
+ type: "number",
11280
+ step: p.type === "integer" ? "1" : "any",
11281
+ value: typeof fixedValue === "number" ? String(fixedValue) : "",
11282
+ placeholder: p.defaultValue !== void 0 ? `Default: ${String(p.defaultValue)}` : "Enter a number",
11283
+ onChange: (e) => writeFixed(
11284
+ p.name,
11285
+ coerceNumber(p.type, e.target.value)
11286
+ ),
11287
+ "data-testid": `ndv-param-${p.name}`,
11288
+ className: inputCx
11289
+ }
11290
+ ) : /* @__PURE__ */ jsx(
11291
+ "input",
11292
+ {
11293
+ type: "text",
11294
+ value: fixedValue === void 0 || fixedValue === null ? "" : String(fixedValue),
11295
+ placeholder: p.defaultValue !== void 0 ? `Default: ${String(p.defaultValue)}` : "Enter a value",
11296
+ onChange: (e) => writeFixed(p.name, e.target.value),
11297
+ "data-testid": `ndv-param-${p.name}`,
11298
+ className: inputCx
11299
+ }
11300
+ ),
11301
+ p.description && /* @__PURE__ */ jsx("span", { className: "text-[11px] leading-snug text-muted-foreground", children: p.description })
11302
+ ]
11303
+ },
11304
+ p.name
11305
+ );
11306
+ })
11307
+ ]
11308
+ }
11309
+ ) : /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto p-4 text-xs text-muted-foreground", children: "No additional settings for this node." })
11310
+ ] }),
11311
+ /* @__PURE__ */ jsx(ResizeHandle, { onStart: startDrag("right") }),
11312
+ /* @__PURE__ */ jsxs(
11313
+ "section",
11314
+ {
11315
+ style: { width: `${rightPct}%` },
11316
+ className: "flex min-h-0 min-w-[180px] flex-col bg-card/40",
11317
+ children: [
11318
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-2.5", children: [
11319
+ /* @__PURE__ */ jsx("span", { className: SECTION_LABEL, children: "Output" }),
11320
+ outputFields.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
11321
+ /* @__PURE__ */ jsx(
11322
+ ToggleChip,
11323
+ {
11324
+ active: outputView === "schema",
11325
+ onClick: () => setOutputView("schema"),
11326
+ children: "Schema"
11327
+ }
11328
+ ),
11329
+ /* @__PURE__ */ jsx(
11330
+ ToggleChip,
11331
+ {
11332
+ active: outputView === "json",
11333
+ onClick: () => setOutputView("json"),
11334
+ children: "JSON"
11335
+ }
11336
+ )
11337
+ ] })
11338
+ ] }),
11339
+ /* @__PURE__ */ jsx(
11340
+ "div",
11341
+ {
11342
+ className: "min-h-0 flex-1 overflow-auto px-2 pb-3",
11343
+ "data-testid": "ndv-output-tree",
11344
+ children: outputFields.length === 0 ? /* @__PURE__ */ jsx(
11345
+ EmptyState,
11346
+ {
11347
+ icon: "mdi:tray-arrow-up",
11348
+ title: "No output data",
11349
+ hint: "This operation has no declared output fields."
11350
+ }
11351
+ ) : outputView === "schema" ? /* @__PURE__ */ jsx(FieldTree, { fields: outputFields }) : /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap break-words px-2 text-[11px] leading-relaxed text-muted-foreground", children: JSON.stringify(currentOp?.output_fields ?? {}, null, 2) })
11352
+ }
11353
+ )
11354
+ ]
11355
+ }
11356
+ )
11357
+ ] })
11358
+ ]
11359
+ }
11360
+ ) });
11361
+ }
11362
+ function ResizeHandle({ onStart }) {
11363
+ return /* @__PURE__ */ jsx(
11364
+ "div",
11365
+ {
11366
+ onMouseDown: onStart,
11367
+ role: "separator",
11368
+ "aria-orientation": "vertical",
11369
+ className: "group flex w-1.5 shrink-0 cursor-col-resize items-center justify-center bg-border/60 hover:bg-primary/40",
11370
+ children: /* @__PURE__ */ jsx("div", { className: "h-8 w-0.5 rounded bg-muted-foreground/40 group-hover:bg-primary" })
11371
+ }
11372
+ );
11373
+ }
11374
+ function TabButton({
11375
+ active,
11376
+ onClick,
11377
+ children
11378
+ }) {
11379
+ return /* @__PURE__ */ jsx(
11380
+ "button",
11381
+ {
11382
+ type: "button",
11383
+ onClick,
11384
+ className: cn$1(
11385
+ "-mb-px border-b-2 py-2.5 text-sm transition-colors",
11386
+ active ? "border-primary font-medium text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
11387
+ ),
11388
+ children
11389
+ }
11390
+ );
11391
+ }
11392
+ function ToggleChip({
11393
+ active,
11394
+ onClick,
11395
+ children
11396
+ }) {
11397
+ return /* @__PURE__ */ jsx(
11398
+ "button",
11399
+ {
11400
+ type: "button",
11401
+ onClick,
11402
+ className: cn$1(
11403
+ "rounded px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide transition-colors",
11404
+ active ? "bg-primary/10 text-primary" : "text-muted-foreground hover:text-foreground"
11405
+ ),
11406
+ children
11407
+ }
11408
+ );
11409
+ }
11410
+ function EmptyState({
11411
+ icon,
11412
+ title,
11413
+ hint
11414
+ }) {
11415
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-2 px-6 text-center", children: [
11416
+ /* @__PURE__ */ jsx(Icon, { icon, className: "size-7 text-muted-foreground/50" }),
11417
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-foreground/80", children: title }),
11418
+ /* @__PURE__ */ jsx("div", { className: "max-w-[14rem] text-xs text-muted-foreground", children: hint })
11419
+ ] });
11420
+ }
10713
11421
  var STATUS_STYLES = {
10714
11422
  DRAFT: { label: "Draft", variant: "secondary" },
10715
11423
  ACTIVE: { label: "Active", variant: "default" },
@@ -11168,6 +11876,7 @@ var FlowBuilder = () => {
11168
11876
  ),
11169
11877
  /* @__PURE__ */ jsx(ChatPanel, {}),
11170
11878
  /* @__PURE__ */ jsx(LogsPanel, {}),
11879
+ /* @__PURE__ */ jsx(NodeDetailView, {}),
11171
11880
  /* @__PURE__ */ jsx(
11172
11881
  "div",
11173
11882
  {
@@ -11421,5 +12130,5 @@ function WorkflowBuilder() {
11421
12130
  }
11422
12131
 
11423
12132
  export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, TemplateCard, WorkflowBuilder, useWorkflowTemplates };
11424
- //# sourceMappingURL=chunk-EVUQS7DK.js.map
11425
- //# sourceMappingURL=chunk-EVUQS7DK.js.map
12133
+ //# sourceMappingURL=chunk-QANP2YKX.js.map
12134
+ //# sourceMappingURL=chunk-QANP2YKX.js.map