@burdenoff/fluidgrids-fe-sdk 2026.1111.12 → 2026.1111.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{MissingNodesInstallModal-YI57ImRx.js → MissingNodesInstallModal-Ca4f--lw.js} +1 -1
- package/dist/{TemplateConfigurationModal-BimyvVH6.js → TemplateConfigurationModal-ChQsXf0-.js} +1 -1
- package/dist/{TemplateDetailPanel-CkwB6vrW.js → TemplateDetailPanel-C0YiVdub.js} +1 -1
- package/dist/{TemplatesPage-ZK6cMWur.js → TemplatesPage-QXtq6mI3.js} +1 -1
- package/dist/{WorkflowSearchRedirect-BHtdF64q.js → WorkflowSearchRedirect-B4nHALJg.js} +2 -2
- package/dist/{agentTools-Dgrdql-W.js → agentTools-C0g2OWvI.js} +1 -1
- package/dist/canvasNoteTools-BUWY18XK.js +136 -0
- package/dist/{flow-builder-CukxgO65.js → flow-builder-BvCQAGDR.js} +475 -10
- package/dist/{index-BES5GtJJ.d.ts → index-CLqh2u28.d.ts} +73 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +428 -7
- package/dist/{node-detail-view-DRk8Ln5L.js → node-detail-view-CPD_NSZc.js} +1 -1
- package/dist/observability.d.ts +1 -1
- package/dist/{quick-config-panel-40_1utiL.js → quick-config-panel-DHkYGaCL.js} +1 -1
- package/dist/templates.js +1 -1
- package/dist/workflow-builder.js +1 -1
- package/package.json +1 -1
|
@@ -21,7 +21,7 @@ import { Link, useNavigate, useParams } from "react-router";
|
|
|
21
21
|
import { useMutation, useQuery } from "@apollo/client/react";
|
|
22
22
|
import { gql } from "@apollo/client/core";
|
|
23
23
|
import { create } from "zustand";
|
|
24
|
-
import { Background, Controls, EdgeLabelRenderer, Handle, MarkerType, MiniMap, Panel, Position, ReactFlow, ReactFlowProvider, addEdge, getBezierPath, useEdgesState, useNodes, useNodesState, useOnSelectionChange, useReactFlow, useStore } from "@xyflow/react";
|
|
24
|
+
import { Background, Controls, EdgeLabelRenderer, Handle, MarkerType, MiniMap, NodeToolbar, Panel, Position, ReactFlow, ReactFlowProvider, ViewportPortal, addEdge, getBezierPath, useEdgesState, useInternalNode, useNodes, useNodesState, useOnSelectionChange, useReactFlow, useStore } from "@xyflow/react";
|
|
25
25
|
import { Icon } from "@iconify/react";
|
|
26
26
|
import ReactMarkdown from "react-markdown";
|
|
27
27
|
import { cn } from "@burdenoff/fe-libs/utils";
|
|
@@ -10582,7 +10582,7 @@ function buildNodeCheck(requiredNodeTypes, installedAppIds) {
|
|
|
10582
10582
|
}
|
|
10583
10583
|
|
|
10584
10584
|
// src/hooks/store/useTemplateImportFlow.tsx
|
|
10585
|
-
const LazyTemplateConfigurationModal = lazy(() => import("./TemplateConfigurationModal-
|
|
10585
|
+
const LazyTemplateConfigurationModal = lazy(() => import("./TemplateConfigurationModal-ChQsXf0-.js").then((m) => ({ default: m.TemplateConfigurationModal })));
|
|
10586
10586
|
/**
|
|
10587
10587
|
* "Use this template", the same everywhere it is offered: the builder's
|
|
10588
10588
|
* template panel, the Templates page and the template preview page
|
|
@@ -12759,7 +12759,7 @@ function TemplatesGrid({ templates, loading, hasMore, onLoadMore, onTemplateClic
|
|
|
12759
12759
|
}
|
|
12760
12760
|
|
|
12761
12761
|
// src/builder/components/flow-builder/components/templates/TemplatesModal.tsx
|
|
12762
|
-
const LazyTemplateDetailPanel = lazy(() => import("./TemplateDetailPanel-
|
|
12762
|
+
const LazyTemplateDetailPanel = lazy(() => import("./TemplateDetailPanel-C0YiVdub.js").then((m) => ({ default: m.TemplateDetailPanel })));
|
|
12763
12763
|
/**
|
|
12764
12764
|
* Modal for browsing and selecting workflow templates
|
|
12765
12765
|
* Features:
|
|
@@ -20702,6 +20702,448 @@ function WebhookRunFollower() {
|
|
|
20702
20702
|
return null;
|
|
20703
20703
|
}
|
|
20704
20704
|
|
|
20705
|
+
// src/agentMarks/plainText.ts
|
|
20706
|
+
/**
|
|
20707
|
+
* ★ BOFF-11584: the text of a callout or note is model output, and the model
|
|
20708
|
+
* has read workflow content people wrote. It is shown as plain text only:
|
|
20709
|
+
* React renders it as a text node (never `dangerouslySetInnerHTML`, never a
|
|
20710
|
+
* markdown renderer), so markup, links and images in it stay inert characters.
|
|
20711
|
+
*
|
|
20712
|
+
* This keeps it short and printable as well: control characters (other than
|
|
20713
|
+
* line breaks) and bidirectional controls, which can disguise what a line
|
|
20714
|
+
* says, are removed; runs of blank lines collapse; the result is clamped.
|
|
20715
|
+
*/
|
|
20716
|
+
const BIDI_CONTROLS = /* @__PURE__ */ new Set([
|
|
20717
|
+
1564,
|
|
20718
|
+
8206,
|
|
20719
|
+
8207,
|
|
20720
|
+
8234,
|
|
20721
|
+
8235,
|
|
20722
|
+
8236,
|
|
20723
|
+
8237,
|
|
20724
|
+
8238,
|
|
20725
|
+
8294,
|
|
20726
|
+
8295,
|
|
20727
|
+
8296,
|
|
20728
|
+
8297
|
|
20729
|
+
]);
|
|
20730
|
+
function isDroppedCharacter(code) {
|
|
20731
|
+
if (code === 10) return false;
|
|
20732
|
+
if (code <= 31) return true;
|
|
20733
|
+
if (code >= 127 && code <= 159) return true;
|
|
20734
|
+
return BIDI_CONTROLS.has(code);
|
|
20735
|
+
}
|
|
20736
|
+
function toPlainText(text, max) {
|
|
20737
|
+
let kept = "";
|
|
20738
|
+
for (const character of text.replace(/\r\n?/g, "\n").replace(/\t/g, " ")) if (!isDroppedCharacter(character.codePointAt(0) ?? 0)) kept += character;
|
|
20739
|
+
const cleaned = kept.replace(/ {2,}/g, " ").replace(/ *\n */g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
20740
|
+
if (cleaned.length <= max) return cleaned;
|
|
20741
|
+
return `${cleaned.slice(0, max - 1).trimEnd()}…`;
|
|
20742
|
+
}
|
|
20743
|
+
|
|
20744
|
+
// src/agentMarks/marksStore.ts
|
|
20745
|
+
/** The text of a mark is model output: kept as plain text, never markup. */
|
|
20746
|
+
const CALLOUT_LABEL_MAX = 200;
|
|
20747
|
+
const NOTE_MESSAGE_MAX = 280;
|
|
20748
|
+
/** A workflow holds at most this many notes, resolved ones included. */
|
|
20749
|
+
const MAX_NOTES_PER_WORKFLOW = 20;
|
|
20750
|
+
let sequence = 0;
|
|
20751
|
+
const nextId = (prefix) => {
|
|
20752
|
+
sequence += 1;
|
|
20753
|
+
return `${prefix}${sequence}`;
|
|
20754
|
+
};
|
|
20755
|
+
const useAgentMarksStore = create()((set, get) => ({
|
|
20756
|
+
callouts: [],
|
|
20757
|
+
notes: [],
|
|
20758
|
+
nextNumber: {},
|
|
20759
|
+
openNoteId: null,
|
|
20760
|
+
flash: null,
|
|
20761
|
+
actions: {
|
|
20762
|
+
addCallout: ({ label, target, element, route }) => {
|
|
20763
|
+
const callout = {
|
|
20764
|
+
id: nextId("c"),
|
|
20765
|
+
label: toPlainText(label, 200),
|
|
20766
|
+
target,
|
|
20767
|
+
route,
|
|
20768
|
+
element: element ? new WeakRef(element) : null
|
|
20769
|
+
};
|
|
20770
|
+
set((state) => ({ callouts: [...state.callouts, callout].slice(-3) }));
|
|
20771
|
+
return callout;
|
|
20772
|
+
},
|
|
20773
|
+
rebindCallout: (id, element) => set((state) => ({ callouts: state.callouts.map((callout) => callout.id === id ? {
|
|
20774
|
+
...callout,
|
|
20775
|
+
element: new WeakRef(element)
|
|
20776
|
+
} : callout) })),
|
|
20777
|
+
removeCallout: (id) => set((state) => ({ callouts: state.callouts.filter((callout) => callout.id !== id) })),
|
|
20778
|
+
clearCallouts: () => {
|
|
20779
|
+
const count = get().callouts.length;
|
|
20780
|
+
if (count > 0) set({ callouts: [] });
|
|
20781
|
+
return count;
|
|
20782
|
+
},
|
|
20783
|
+
dropCalloutsOffRoute: (route) => {
|
|
20784
|
+
const { callouts } = get();
|
|
20785
|
+
const kept = callouts.filter((callout) => callout.route === route);
|
|
20786
|
+
if (kept.length !== callouts.length) set({ callouts: kept });
|
|
20787
|
+
return callouts.length - kept.length;
|
|
20788
|
+
},
|
|
20789
|
+
addNote: (input) => {
|
|
20790
|
+
const { notes, nextNumber } = get();
|
|
20791
|
+
if (notes.filter((note) => note.workflowKey === input.workflowKey).length >= 20) return null;
|
|
20792
|
+
const number = nextNumber[input.workflowKey] ?? 1;
|
|
20793
|
+
const note = {
|
|
20794
|
+
id: nextId("n"),
|
|
20795
|
+
number,
|
|
20796
|
+
workflowKey: input.workflowKey,
|
|
20797
|
+
nodeId: input.nodeId,
|
|
20798
|
+
nodeLabel: toPlainText(input.nodeLabel, 80),
|
|
20799
|
+
paramKey: input.paramKey ?? null,
|
|
20800
|
+
kind: input.kind,
|
|
20801
|
+
message: toPlainText(input.message, 280),
|
|
20802
|
+
resolved: false
|
|
20803
|
+
};
|
|
20804
|
+
set({
|
|
20805
|
+
notes: [...notes, note],
|
|
20806
|
+
nextNumber: {
|
|
20807
|
+
...nextNumber,
|
|
20808
|
+
[input.workflowKey]: number + 1
|
|
20809
|
+
}
|
|
20810
|
+
});
|
|
20811
|
+
return note;
|
|
20812
|
+
},
|
|
20813
|
+
setResolved: (id, resolved) => set((state) => ({ notes: state.notes.map((note) => note.id === id ? {
|
|
20814
|
+
...note,
|
|
20815
|
+
resolved
|
|
20816
|
+
} : note) })),
|
|
20817
|
+
removeNote: (id) => set((state) => ({
|
|
20818
|
+
notes: state.notes.filter((note) => note.id !== id),
|
|
20819
|
+
openNoteId: state.openNoteId === id ? null : state.openNoteId
|
|
20820
|
+
})),
|
|
20821
|
+
clearNotes: (workflowKey) => {
|
|
20822
|
+
const { notes, openNoteId } = get();
|
|
20823
|
+
const kept = workflowKey === void 0 ? [] : notes.filter((note) => note.workflowKey !== workflowKey);
|
|
20824
|
+
const removed = notes.length - kept.length;
|
|
20825
|
+
if (removed > 0) set({
|
|
20826
|
+
notes: kept,
|
|
20827
|
+
openNoteId: kept.some((note) => note.id === openNoteId) ? openNoteId : null
|
|
20828
|
+
});
|
|
20829
|
+
return removed;
|
|
20830
|
+
},
|
|
20831
|
+
openNote: (id) => set({ openNoteId: id }),
|
|
20832
|
+
flashNode: (nodeId) => {
|
|
20833
|
+
sequence += 1;
|
|
20834
|
+
set({ flash: {
|
|
20835
|
+
nodeId,
|
|
20836
|
+
token: sequence
|
|
20837
|
+
} });
|
|
20838
|
+
}
|
|
20839
|
+
}
|
|
20840
|
+
}));
|
|
20841
|
+
/** Reset everything (tests, and sign-out if a host wants it). */
|
|
20842
|
+
function resetAgentMarks() {
|
|
20843
|
+
useAgentMarksStore.setState({
|
|
20844
|
+
callouts: [],
|
|
20845
|
+
notes: [],
|
|
20846
|
+
nextNumber: {},
|
|
20847
|
+
openNoteId: null,
|
|
20848
|
+
flash: null
|
|
20849
|
+
});
|
|
20850
|
+
}
|
|
20851
|
+
|
|
20852
|
+
// src/builder/agentNotes/notesKey.ts
|
|
20853
|
+
/**
|
|
20854
|
+
* ★ BOFF-11584: which workflow a canvas note belongs to. A saved workflow is
|
|
20855
|
+
* known by its key across versions; a new one has no key until its first
|
|
20856
|
+
* save, so its in-memory id stands in.
|
|
20857
|
+
*/
|
|
20858
|
+
function notesKeyOf(workflow) {
|
|
20859
|
+
return workflow.workflowKey || workflow.id || "unsaved";
|
|
20860
|
+
}
|
|
20861
|
+
|
|
20862
|
+
// src/builder/agentNotes/CanvasNotesLayer.tsx
|
|
20863
|
+
/**
|
|
20864
|
+
* ★ BOFF-11584: the assistant's notes on the builder canvas
|
|
20865
|
+
* (`fg_canvas_annotate`). Rendered as a child of `<ReactFlow>`, outside every
|
|
20866
|
+
* node component: the nodes know nothing about notes.
|
|
20867
|
+
*
|
|
20868
|
+
* - Numbered pins sit above their node (`NodeToolbar`), so they follow it
|
|
20869
|
+
* through drags, pans and zooms. A pin opens the note's card: the message,
|
|
20870
|
+
* the parameter it is about, "Open node" and "Mark resolved".
|
|
20871
|
+
* - A dashed ring marks each node with an open note (`ViewportPortal`, in
|
|
20872
|
+
* canvas coordinates), and a brief ring shows the node the assistant just
|
|
20873
|
+
* brought into view.
|
|
20874
|
+
* - The notes list (top left; the FluidGrids mark holds the top right)
|
|
20875
|
+
* holds every note, including orphaned ones whose node was deleted: those
|
|
20876
|
+
* are never moved onto another node.
|
|
20877
|
+
*
|
|
20878
|
+
* Everything here is `data-agent="deny"`: an agent cannot read or press its
|
|
20879
|
+
* own notes. Messages render as React text, never as markup.
|
|
20880
|
+
*/
|
|
20881
|
+
const FLASH_MS = 1800;
|
|
20882
|
+
const RING_PAD = 8;
|
|
20883
|
+
function pinClass(note, open) {
|
|
20884
|
+
return cn("flex size-6 items-center justify-center rounded-full text-xs font-semibold shadow-md transition-transform focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1", note.resolved ? "bg-muted text-muted-foreground" : note.kind === "fix" ? "bg-destructive text-destructive-foreground" : "bg-primary text-primary-foreground", open && "scale-110 ring-2 ring-ring ring-offset-1 ring-offset-background");
|
|
20885
|
+
}
|
|
20886
|
+
/** A ring around a node, drawn in canvas coordinates. */
|
|
20887
|
+
const NodeRing = ({ nodeId, tone }) => {
|
|
20888
|
+
const node = useInternalNode(nodeId);
|
|
20889
|
+
const width = node?.measured.width ?? 0;
|
|
20890
|
+
const height = node?.measured.height ?? 0;
|
|
20891
|
+
if (!node || width === 0 || height === 0) return null;
|
|
20892
|
+
const { x, y } = node.internals.positionAbsolute;
|
|
20893
|
+
return /* @__PURE__ */ jsx("div", {
|
|
20894
|
+
"aria-hidden": "true",
|
|
20895
|
+
"data-testid": `fg-canvas-note-ring-${tone}`,
|
|
20896
|
+
className: cn("pointer-events-none absolute left-0 top-0 rounded-2xl border-2", tone === "fix" && "border-dashed border-destructive", tone === "info" && "border-dashed border-primary", tone === "flash" && "border-primary ring-4 ring-primary/30 motion-safe:animate-pulse"),
|
|
20897
|
+
style: {
|
|
20898
|
+
transform: `translate(${x - RING_PAD}px, ${y - RING_PAD}px)`,
|
|
20899
|
+
width: width + 16,
|
|
20900
|
+
height: height + 16
|
|
20901
|
+
}
|
|
20902
|
+
});
|
|
20903
|
+
};
|
|
20904
|
+
/** The node the assistant just brought into view, for a moment. */
|
|
20905
|
+
const FlashRing = () => {
|
|
20906
|
+
const flash = useAgentMarksStore((state) => state.flash);
|
|
20907
|
+
const [shown, setShown] = useState(null);
|
|
20908
|
+
useEffect(() => {
|
|
20909
|
+
if (!flash) return;
|
|
20910
|
+
setShown(flash.token);
|
|
20911
|
+
const timer = setTimeout(() => setShown(null), FLASH_MS);
|
|
20912
|
+
return () => clearTimeout(timer);
|
|
20913
|
+
}, [flash]);
|
|
20914
|
+
if (!flash || shown !== flash.token) return null;
|
|
20915
|
+
return /* @__PURE__ */ jsx(NodeRing, {
|
|
20916
|
+
nodeId: flash.nodeId,
|
|
20917
|
+
tone: "flash"
|
|
20918
|
+
});
|
|
20919
|
+
};
|
|
20920
|
+
const CanvasNoteCard = ({ note, nodeOnCanvas }) => {
|
|
20921
|
+
const tr = useTr();
|
|
20922
|
+
const { openNote, setResolved } = useAgentMarksStore((state) => state.actions);
|
|
20923
|
+
const openNodePanel = () => {
|
|
20924
|
+
const { workflow, actions } = useFlowStore.getState();
|
|
20925
|
+
const node = workflow.nodes.find((candidate) => candidate.id === note.nodeId);
|
|
20926
|
+
if (!node?.type) return;
|
|
20927
|
+
actions.ndv.open({
|
|
20928
|
+
nodeId: node.id,
|
|
20929
|
+
type: node.type
|
|
20930
|
+
});
|
|
20931
|
+
openNote(null);
|
|
20932
|
+
};
|
|
20933
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
20934
|
+
"data-testid": "fg-canvas-note-card",
|
|
20935
|
+
className: "nowheel w-64 cursor-default rounded-lg border border-border bg-popover p-3 text-left text-xs text-foreground shadow-lg",
|
|
20936
|
+
children: [
|
|
20937
|
+
/* @__PURE__ */ jsxs("div", {
|
|
20938
|
+
className: "mb-1.5 flex items-center justify-between gap-2",
|
|
20939
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
20940
|
+
className: "flex items-center gap-1.5 font-semibold",
|
|
20941
|
+
children: [/* @__PURE__ */ jsx(Sparkles, {
|
|
20942
|
+
"aria-hidden": "true",
|
|
20943
|
+
className: "size-3.5 text-primary"
|
|
20944
|
+
}), note.kind === "fix" ? tr("fluidgrids.agentMarks.noteFixTitle", "Note {{number}} · To fix", { number: note.number }) : tr("fluidgrids.agentMarks.noteInfoTitle", "Note {{number}}", { number: note.number })]
|
|
20945
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
20946
|
+
type: "button",
|
|
20947
|
+
"aria-label": tr("fluidgrids.agentMarks.closeNote", "Close note"),
|
|
20948
|
+
onClick: () => openNote(null),
|
|
20949
|
+
className: "-m-1 rounded p-1 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
20950
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
20951
|
+
"aria-hidden": "true",
|
|
20952
|
+
className: "size-3.5"
|
|
20953
|
+
})
|
|
20954
|
+
})]
|
|
20955
|
+
}),
|
|
20956
|
+
/* @__PURE__ */ jsxs("p", {
|
|
20957
|
+
className: "mb-1 truncate text-muted-foreground",
|
|
20958
|
+
children: [note.nodeLabel, note.paramKey ? ` · ${tr("fluidgrids.agentMarks.noteParam", "Field: {{param}}", { param: note.paramKey })}` : ""]
|
|
20959
|
+
}),
|
|
20960
|
+
/* @__PURE__ */ jsx("p", {
|
|
20961
|
+
"data-testid": "fg-canvas-note-message",
|
|
20962
|
+
className: "whitespace-pre-line break-words text-sm text-foreground",
|
|
20963
|
+
children: note.message
|
|
20964
|
+
}),
|
|
20965
|
+
!nodeOnCanvas && /* @__PURE__ */ jsx("p", {
|
|
20966
|
+
className: "mt-1.5 text-muted-foreground",
|
|
20967
|
+
children: tr("fluidgrids.agentMarks.noteOrphaned", "This node was removed from the workflow.")
|
|
20968
|
+
}),
|
|
20969
|
+
/* @__PURE__ */ jsxs("div", {
|
|
20970
|
+
className: "mt-2.5 flex justify-end gap-2",
|
|
20971
|
+
children: [nodeOnCanvas && /* @__PURE__ */ jsx("button", {
|
|
20972
|
+
type: "button",
|
|
20973
|
+
onClick: openNodePanel,
|
|
20974
|
+
className: "rounded-md border border-border px-2 py-1 font-medium hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
20975
|
+
children: tr("fluidgrids.agentMarks.openNode", "Open node")
|
|
20976
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
20977
|
+
type: "button",
|
|
20978
|
+
"data-testid": "fg-canvas-note-resolve",
|
|
20979
|
+
onClick: () => setResolved(note.id, !note.resolved),
|
|
20980
|
+
className: "rounded-md bg-primary px-2 py-1 font-medium text-primary-foreground hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
20981
|
+
children: note.resolved ? tr("fluidgrids.agentMarks.reopenNote", "Reopen") : tr("fluidgrids.agentMarks.resolveNote", "Mark resolved")
|
|
20982
|
+
})]
|
|
20983
|
+
})
|
|
20984
|
+
]
|
|
20985
|
+
});
|
|
20986
|
+
};
|
|
20987
|
+
/** The pins above one node, and the open card. */
|
|
20988
|
+
const NodePins = ({ nodeId, notes }) => {
|
|
20989
|
+
const tr = useTr();
|
|
20990
|
+
const openNoteId = useAgentMarksStore((state) => state.openNoteId);
|
|
20991
|
+
const openNote = useAgentMarksStore((state) => state.actions.openNote);
|
|
20992
|
+
const open = notes.find((note) => note.id === openNoteId);
|
|
20993
|
+
return /* @__PURE__ */ jsx(NodeToolbar, {
|
|
20994
|
+
nodeId,
|
|
20995
|
+
isVisible: true,
|
|
20996
|
+
position: Position.Top,
|
|
20997
|
+
align: "end",
|
|
20998
|
+
offset: 6,
|
|
20999
|
+
"data-agent": "deny",
|
|
21000
|
+
"data-testid": "fg-canvas-note-pins",
|
|
21001
|
+
className: "nodrag nopan",
|
|
21002
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
21003
|
+
className: "flex flex-col items-end gap-1.5",
|
|
21004
|
+
children: [open && /* @__PURE__ */ jsx(CanvasNoteCard, {
|
|
21005
|
+
note: open,
|
|
21006
|
+
nodeOnCanvas: true
|
|
21007
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
21008
|
+
className: "flex gap-1",
|
|
21009
|
+
children: notes.map((note) => /* @__PURE__ */ jsx("button", {
|
|
21010
|
+
type: "button",
|
|
21011
|
+
"data-testid": "fg-canvas-note-pin",
|
|
21012
|
+
"aria-expanded": note.id === openNoteId,
|
|
21013
|
+
"aria-label": tr("fluidgrids.agentMarks.notePin", "Assistant note {{number}}", { number: note.number }),
|
|
21014
|
+
onClick: () => openNote(note.id === openNoteId ? null : note.id),
|
|
21015
|
+
className: pinClass(note, note.id === openNoteId),
|
|
21016
|
+
children: note.resolved ? /* @__PURE__ */ jsx(Check, {
|
|
21017
|
+
"aria-hidden": "true",
|
|
21018
|
+
className: "size-3.5"
|
|
21019
|
+
}) : note.number
|
|
21020
|
+
}, note.id))
|
|
21021
|
+
})]
|
|
21022
|
+
})
|
|
21023
|
+
});
|
|
21024
|
+
};
|
|
21025
|
+
/** Every note on this workflow, orphaned ones included. */
|
|
21026
|
+
const NotesList = ({ notes, live }) => {
|
|
21027
|
+
const tr = useTr();
|
|
21028
|
+
const [expanded, setExpanded] = useState(false);
|
|
21029
|
+
const { fitView } = useReactFlow();
|
|
21030
|
+
const { openNote, removeNote, flashNode } = useAgentMarksStore((state) => state.actions);
|
|
21031
|
+
const openCount = notes.filter((note) => !note.resolved).length;
|
|
21032
|
+
const orphaned = notes.filter((note) => !live.has(note.nodeId)).length;
|
|
21033
|
+
const show = (note) => {
|
|
21034
|
+
if (!live.has(note.nodeId)) return;
|
|
21035
|
+
fitView({
|
|
21036
|
+
nodes: [{ id: note.nodeId }],
|
|
21037
|
+
duration: 400,
|
|
21038
|
+
padding: .6,
|
|
21039
|
+
maxZoom: 1.25
|
|
21040
|
+
}).then((done) => {
|
|
21041
|
+
if (done) flashNode(note.nodeId);
|
|
21042
|
+
});
|
|
21043
|
+
openNote(note.id);
|
|
21044
|
+
};
|
|
21045
|
+
return /* @__PURE__ */ jsx(Panel, {
|
|
21046
|
+
position: "top-left",
|
|
21047
|
+
"data-agent": "deny",
|
|
21048
|
+
"data-testid": "fg-canvas-notes-list",
|
|
21049
|
+
className: "nodrag nopan nowheel",
|
|
21050
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
21051
|
+
className: "w-64 rounded-lg border border-border bg-popover text-xs text-foreground shadow-md",
|
|
21052
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
21053
|
+
type: "button",
|
|
21054
|
+
"aria-expanded": expanded,
|
|
21055
|
+
onClick: () => setExpanded((value) => !value),
|
|
21056
|
+
className: "flex w-full items-center justify-between gap-2 rounded-lg px-3 py-2 font-medium hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
21057
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
21058
|
+
className: "flex items-center gap-1.5",
|
|
21059
|
+
children: [/* @__PURE__ */ jsx(Sparkles, {
|
|
21060
|
+
"aria-hidden": "true",
|
|
21061
|
+
className: "size-3.5 text-primary"
|
|
21062
|
+
}), tr("fluidgrids.agentMarks.notesHeading", "Assistant notes")]
|
|
21063
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
21064
|
+
className: "text-muted-foreground",
|
|
21065
|
+
children: [tr("fluidgrids.agentMarks.notesOpenCount", "{{count}} open", { count: openCount }), orphaned > 0 ? ` · ${tr("fluidgrids.agentMarks.notesOrphanedCount", "{{count}} removed", { count: orphaned })}` : ""]
|
|
21066
|
+
})]
|
|
21067
|
+
}), expanded && /* @__PURE__ */ jsx("ul", {
|
|
21068
|
+
className: "max-h-64 overflow-y-auto border-t border-border py-1",
|
|
21069
|
+
children: notes.map((note) => {
|
|
21070
|
+
const onCanvas = live.has(note.nodeId);
|
|
21071
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
21072
|
+
"data-testid": "fg-canvas-notes-item",
|
|
21073
|
+
className: "flex items-start gap-2 px-3 py-1.5",
|
|
21074
|
+
children: [
|
|
21075
|
+
/* @__PURE__ */ jsx("span", {
|
|
21076
|
+
className: cn(pinClass(note, false), "size-5 shrink-0"),
|
|
21077
|
+
children: note.resolved ? /* @__PURE__ */ jsx(Check, {
|
|
21078
|
+
"aria-hidden": "true",
|
|
21079
|
+
className: "size-3"
|
|
21080
|
+
}) : note.number
|
|
21081
|
+
}),
|
|
21082
|
+
/* @__PURE__ */ jsxs("button", {
|
|
21083
|
+
type: "button",
|
|
21084
|
+
disabled: !onCanvas,
|
|
21085
|
+
onClick: () => show(note),
|
|
21086
|
+
className: "min-w-0 flex-1 text-left disabled:cursor-default",
|
|
21087
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
21088
|
+
className: "block truncate font-medium",
|
|
21089
|
+
children: note.nodeLabel
|
|
21090
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
21091
|
+
className: cn("block truncate text-muted-foreground", note.resolved && "line-through"),
|
|
21092
|
+
children: onCanvas ? note.message : tr("fluidgrids.agentMarks.noteOrphaned", "This node was removed from the workflow.")
|
|
21093
|
+
})]
|
|
21094
|
+
}),
|
|
21095
|
+
!onCanvas && /* @__PURE__ */ jsx("button", {
|
|
21096
|
+
type: "button",
|
|
21097
|
+
"aria-label": tr("fluidgrids.agentMarks.removeNote", "Remove note"),
|
|
21098
|
+
onClick: () => removeNote(note.id),
|
|
21099
|
+
className: "shrink-0 rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
21100
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
21101
|
+
"aria-hidden": "true",
|
|
21102
|
+
className: "size-3.5"
|
|
21103
|
+
})
|
|
21104
|
+
})
|
|
21105
|
+
]
|
|
21106
|
+
}, note.id);
|
|
21107
|
+
})
|
|
21108
|
+
})]
|
|
21109
|
+
})
|
|
21110
|
+
});
|
|
21111
|
+
};
|
|
21112
|
+
const CanvasNotesLayer = () => {
|
|
21113
|
+
const workflowKey = useFlowStore((state) => notesKeyOf(state.workflow));
|
|
21114
|
+
const nodeIds = useFlowStore(useShallow((state) => state.workflow.nodes.map((node) => node.id)));
|
|
21115
|
+
const allNotes = useAgentMarksStore((state) => state.notes);
|
|
21116
|
+
const notes = useMemo(() => allNotes.filter((note) => note.workflowKey === workflowKey), [allNotes, workflowKey]);
|
|
21117
|
+
const live = useMemo(() => new Set(nodeIds), [nodeIds]);
|
|
21118
|
+
const byNode = useMemo(() => {
|
|
21119
|
+
const groups = /* @__PURE__ */ new Map();
|
|
21120
|
+
for (const note of notes) {
|
|
21121
|
+
if (!live.has(note.nodeId)) continue;
|
|
21122
|
+
groups.set(note.nodeId, [...groups.get(note.nodeId) ?? [], note]);
|
|
21123
|
+
}
|
|
21124
|
+
return [...groups.entries()];
|
|
21125
|
+
}, [notes, live]);
|
|
21126
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21127
|
+
/* @__PURE__ */ jsxs(ViewportPortal, { children: [byNode.map(([nodeId, nodeNotes]) => {
|
|
21128
|
+
const openNotes = nodeNotes.filter((note) => !note.resolved);
|
|
21129
|
+
if (openNotes.length === 0) return null;
|
|
21130
|
+
const tone = openNotes.some((note) => note.kind === "fix") ? "fix" : "info";
|
|
21131
|
+
return /* @__PURE__ */ jsx(NodeRing, {
|
|
21132
|
+
nodeId,
|
|
21133
|
+
tone
|
|
21134
|
+
}, nodeId);
|
|
21135
|
+
}), /* @__PURE__ */ jsx(FlashRing, {})] }),
|
|
21136
|
+
byNode.map(([nodeId, nodeNotes]) => /* @__PURE__ */ jsx(NodePins, {
|
|
21137
|
+
nodeId,
|
|
21138
|
+
notes: nodeNotes
|
|
21139
|
+
}, nodeId)),
|
|
21140
|
+
notes.length > 0 && /* @__PURE__ */ jsx(NotesList, {
|
|
21141
|
+
notes,
|
|
21142
|
+
live
|
|
21143
|
+
})
|
|
21144
|
+
] });
|
|
21145
|
+
};
|
|
21146
|
+
|
|
20705
21147
|
// src/builder/lib/autosave-runner.ts
|
|
20706
21148
|
function createAutosaveRunner(deps) {
|
|
20707
21149
|
let timer;
|
|
@@ -27143,8 +27585,8 @@ function EditLeaseBanner() {
|
|
|
27143
27585
|
}
|
|
27144
27586
|
|
|
27145
27587
|
// src/builder/components/flow-builder/flow-builder.tsx
|
|
27146
|
-
const LazyNodeDetailView = lazy(() => import("./node-detail-view-
|
|
27147
|
-
const LazyQuickConfigPanel = lazy(() => import("./quick-config-panel-
|
|
27588
|
+
const LazyNodeDetailView = lazy(() => import("./node-detail-view-CPD_NSZc.js").then((m) => ({ default: m.NodeDetailView })));
|
|
27589
|
+
const LazyQuickConfigPanel = lazy(() => import("./quick-config-panel-DHkYGaCL.js").then((m) => ({ default: m.QuickConfigPanel })));
|
|
27148
27590
|
const edgeTypes = { deletable: CustomDeletableEdge };
|
|
27149
27591
|
const edgeOptions = {
|
|
27150
27592
|
type: "deletable",
|
|
@@ -27204,7 +27646,7 @@ const FlowBuilder = ({ readOnly = false }) => {
|
|
|
27204
27646
|
const [agentTools, setAgentTools] = useState([]);
|
|
27205
27647
|
useEffect(() => {
|
|
27206
27648
|
let cancelled = false;
|
|
27207
|
-
import("./agentTools-
|
|
27649
|
+
import("./agentTools-C0g2OWvI.js").then(({ createBuilderAgentTools }) => {
|
|
27208
27650
|
if (cancelled) return;
|
|
27209
27651
|
setAgentTools(createBuilderAgentTools({ durableFunctionsEnabled: () => paletteGatesRef.current }));
|
|
27210
27652
|
}).catch((error) => {
|
|
@@ -27251,7 +27693,30 @@ const FlowBuilder = ({ readOnly = false }) => {
|
|
|
27251
27693
|
}
|
|
27252
27694
|
return merged;
|
|
27253
27695
|
}, [nodeTypesVersion, nodes]);
|
|
27254
|
-
const { getNodes } = useReactFlow();
|
|
27696
|
+
const { getNodes, fitView } = useReactFlow();
|
|
27697
|
+
const fitViewRef = useRef(fitView);
|
|
27698
|
+
useEffect(() => {
|
|
27699
|
+
fitViewRef.current = fitView;
|
|
27700
|
+
}, [fitView]);
|
|
27701
|
+
const [noteTools, setNoteTools] = useState([]);
|
|
27702
|
+
useEffect(() => {
|
|
27703
|
+
let cancelled = false;
|
|
27704
|
+
import("./canvasNoteTools-BUWY18XK.js").then(({ createCanvasNoteTools }) => {
|
|
27705
|
+
if (cancelled) return;
|
|
27706
|
+
setNoteTools(createCanvasNoteTools({ focusNode: (nodeId) => fitViewRef.current({
|
|
27707
|
+
nodes: [{ id: nodeId }],
|
|
27708
|
+
duration: 400,
|
|
27709
|
+
padding: .6,
|
|
27710
|
+
maxZoom: 1.25
|
|
27711
|
+
}) }));
|
|
27712
|
+
}).catch((error) => {
|
|
27713
|
+
console.error("[FlowBuilder] Assistant note tools failed to load:", error);
|
|
27714
|
+
});
|
|
27715
|
+
return () => {
|
|
27716
|
+
cancelled = true;
|
|
27717
|
+
};
|
|
27718
|
+
}, []);
|
|
27719
|
+
useAgentTools(noteTools);
|
|
27255
27720
|
const { handleOnEdgeDropConnectEnd, floatingMenuWrapperRef, handleAddConnectedNode } = useAddNodeOnEdgeDrop();
|
|
27256
27721
|
const deleteKeyCode = useDeleteKeyCode();
|
|
27257
27722
|
useNodeSelectedEvent();
|
|
@@ -27321,7 +27786,7 @@ const FlowBuilder = ({ readOnly = false }) => {
|
|
|
27321
27786
|
className: "relative flex-1 rounded-b-xl border border-card-foreground/10 border-t-0 overflow-hidden mx-4 mb-1",
|
|
27322
27787
|
children: [
|
|
27323
27788
|
/* @__PURE__ */ jsx(CanvasControls, {}),
|
|
27324
|
-
/* @__PURE__ */
|
|
27789
|
+
/* @__PURE__ */ jsxs(ReactFlow, {
|
|
27325
27790
|
proOptions: { hideAttribution: true },
|
|
27326
27791
|
onInit: ({ fitView }) => fitView({
|
|
27327
27792
|
minZoom: 1,
|
|
@@ -27354,7 +27819,7 @@ const FlowBuilder = ({ readOnly = false }) => {
|
|
|
27354
27819
|
maxZoom: 1
|
|
27355
27820
|
},
|
|
27356
27821
|
defaultEdgeOptions: edgeOptions,
|
|
27357
|
-
children: /* @__PURE__ */ jsx(Background, { gap: 24 })
|
|
27822
|
+
children: [/* @__PURE__ */ jsx(Background, { gap: 24 }), /* @__PURE__ */ jsx(CanvasNotesLayer, {})]
|
|
27358
27823
|
}),
|
|
27359
27824
|
/* @__PURE__ */ jsx(FluidGridsMark, {}),
|
|
27360
27825
|
nodes.length === 0 && !editingBlocked && /* @__PURE__ */ jsx("div", {
|
|
@@ -27544,4 +28009,4 @@ const FlowBuilderPage = ({ workflow, readOnly = false }) => {
|
|
|
27544
28009
|
] });
|
|
27545
28010
|
};
|
|
27546
28011
|
|
|
27547
|
-
export {
|
|
28012
|
+
export { useGlobalStoreClient as $, DialogFooter$1 as A, useImportWorkflowTemplate as B, templatePreview as C, DURABLE_FUNCTION_NODE_TYPES as Ct, DialogClose as D, isDurableFunctionNodeType as Dt, Dialog$1 as E, filterPaletteNodes as Et, DialogTrigger$1 as F, NodeSettingsPanel as G, useInstalledNodes as H, computeAttachPosition as I, RUNNER_INSTALL_SCRIPT_URL as J, RunOnSection as K, useTemplateImportFlow as L, DialogOverlay as M, DialogPortal as N, DialogContent$1 as O, isNodeTypeHiddenFromPalette as Ot, DialogTitle$1 as P, NodeParamsForm as Q, buildNodeCheck as R, TemplateCard as S, Input$1 as St, badgeVariants as T, WORKFLOW_TEST_NODE_TYPES as Tt, NodeExecutionActions as U, useWorkflowTemplates as V, NodeEditorTabBody as W, useRunnerActions as X, runnerInstallCommand as Y, useRunners as Z, resetAgentMarks as _, CardContent$1 as _t, useAiWorkflowTemplates as a, ConnectionSelector as at, JsonView as b, CardHeader$1 as bt, RunsUsageMeter as c, useNodeEditorTab as ct, readRunsQuota as d, NodeEditorTabs as dt, NDV_Z_INDEX as et, runsQuotaLevel as f, TabButton as ft, notesKeyOf as g, Card$1 as gt, useWorkflowReloadStore as h, FluidGridsMark as ht, enrichDraftNodes as i, CodeEditorField as it, DialogHeader$1 as j, DialogDescription$1 as k, isRunControlNodeType as kt, RunsUsageMeterView as l, SCHEDULE_NODE_TYPE as lt, releaseLeaseKeptForReload as m, useNodeSelectedEvent as mt, AI_DRAFT_MAX_PROMPT_LENGTH as n, PopoverContent$1 as nt, BUILDER_FULLSCREEN_ATTRIBUTE as o, ConnectionFormDialog as ot, summarizeRunsQuota as p, NodeInputFields as pt, RunnerStatusPill as q, draftToCanvas as r, PopoverTrigger$1 as rt, BUILDER_FULLSCREEN_EVENT as s, ReadOnlyFieldset as st, FlowBuilderPage as t, Popover$1 as tt, useRunsQuota as u, SchedulePanel as ut, useAgentMarksStore as v, CardDescription$1 as vt, Badge$1 as w, RUN_CONTROL_NODE_TYPES as wt, TableView as x, CardTitle$1 as xt, toPlainText as y, CardFooter as yt, buildRequiredNodeTypes as z };
|