@particle-academy/fancy-flow 0.24.0 → 0.25.0

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 (41) hide show
  1. package/dist/{chunk-QAA4CA22.js → chunk-ATQXZEVD.js} +117 -6
  2. package/dist/chunk-ATQXZEVD.js.map +1 -0
  3. package/dist/{chunk-OHVSEHDC.js → chunk-N5ICD2WZ.js} +3 -3
  4. package/dist/{chunk-OHVSEHDC.js.map → chunk-N5ICD2WZ.js.map} +1 -1
  5. package/dist/{chunk-ZS6DVGB3.js → chunk-OFW7GN3Y.js} +4 -4
  6. package/dist/{chunk-ZS6DVGB3.js.map → chunk-OFW7GN3Y.js.map} +1 -1
  7. package/dist/{chunk-YXRLIV7A.js → chunk-Q6IPK64P.js} +4 -2
  8. package/dist/chunk-Q6IPK64P.js.map +1 -0
  9. package/dist/{chunk-KDHLKBL2.js → chunk-U2VJB7HR.js} +7 -5
  10. package/dist/chunk-U2VJB7HR.js.map +1 -0
  11. package/dist/engine.cjs +4 -2
  12. package/dist/engine.cjs.map +1 -1
  13. package/dist/engine.js +3 -3
  14. package/dist/index.cjs +119 -23
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +3 -8
  17. package/dist/index.d.ts +3 -8
  18. package/dist/index.js +13 -32
  19. package/dist/index.js.map +1 -1
  20. package/dist/registry/index.d.cts +2 -2
  21. package/dist/registry/index.d.ts +2 -2
  22. package/dist/registry.cjs +110 -2
  23. package/dist/registry.cjs.map +1 -1
  24. package/dist/registry.js +3 -3
  25. package/dist/runtime.cjs +4 -2
  26. package/dist/runtime.cjs.map +1 -1
  27. package/dist/runtime.js +3 -3
  28. package/dist/schema.cjs.map +1 -1
  29. package/dist/schema.js +2 -2
  30. package/dist/styles.css +60 -0
  31. package/dist/styles.css.map +1 -1
  32. package/dist/{types-DwRpXXNy.d.ts → types-BGtR3k9J.d.ts} +1 -1
  33. package/dist/{types-BekE5JTG.d.cts → types-B_pxRqfw.d.cts} +1 -1
  34. package/dist/ux.cjs.map +1 -1
  35. package/dist/ux.d.cts +1 -1
  36. package/dist/ux.d.ts +1 -1
  37. package/dist/ux.js +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-KDHLKBL2.js.map +0 -1
  40. package/dist/chunk-QAA4CA22.js.map +0 -1
  41. package/dist/chunk-YXRLIV7A.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1870,6 +1870,8 @@ function categoryAccent(category) {
1870
1870
  return "#a855f7";
1871
1871
  case "layout":
1872
1872
  return "#64748b";
1873
+ case "annotation":
1874
+ return "#eab308";
1873
1875
  default:
1874
1876
  return "#71717a";
1875
1877
  }
@@ -10976,6 +10978,88 @@ function LaneNodeInner(props) {
10976
10978
  );
10977
10979
  }
10978
10980
  var LaneNode = ReactExports.memo(LaneNodeInner);
10981
+ var FlowEditorContext = ReactExports.createContext(null);
10982
+ var FlowEditorProvider = FlowEditorContext.Provider;
10983
+ function useFlowEditor() {
10984
+ const api = ReactExports.useContext(FlowEditorContext);
10985
+ if (api === null) {
10986
+ throw new Error("useFlowEditor() must be called inside <FlowEditor>.");
10987
+ }
10988
+ return api;
10989
+ }
10990
+ function useFlowEditorOptional() {
10991
+ return ReactExports.useContext(FlowEditorContext);
10992
+ }
10993
+ var NOTE_COLORS = {
10994
+ amber: { bg: "rgba(234, 179, 8, 0.14)", border: "rgba(234, 179, 8, 0.55)", title: "#a16207" },
10995
+ sky: { bg: "rgba(14, 165, 233, 0.12)", border: "rgba(14, 165, 233, 0.5)", title: "#0369a1" },
10996
+ violet: { bg: "rgba(139, 92, 246, 0.13)", border: "rgba(139, 92, 246, 0.5)", title: "#6d28d9" },
10997
+ emerald: { bg: "rgba(16, 185, 129, 0.13)", border: "rgba(16, 185, 129, 0.5)", title: "#047857" },
10998
+ rose: { bg: "rgba(244, 63, 94, 0.12)", border: "rgba(244, 63, 94, 0.5)", title: "#be123c" },
10999
+ slate: { bg: "rgba(100, 116, 139, 0.12)", border: "rgba(100, 116, 139, 0.5)", title: "#475569" }
11000
+ };
11001
+ function coerceColor(v2) {
11002
+ return typeof v2 === "string" && v2 in NOTE_COLORS ? v2 : "amber";
11003
+ }
11004
+ function NoteNodeInner(props) {
11005
+ const data = props.data;
11006
+ const config = data.config ?? {};
11007
+ const api = useFlowEditorOptional();
11008
+ const hasConfigText = typeof config.text === "string" && config.text.length > 0;
11009
+ const text = hasConfigText ? String(config.text) : typeof data.body === "string" ? data.body : typeof data.text === "string" ? data.text : "";
11010
+ const title = typeof config.title === "string" && config.title || (!hasConfigText && typeof data.body === "string" && typeof data.label === "string" ? data.label : "");
11011
+ const skin = NOTE_COLORS[coerceColor(config.color)];
11012
+ const [editing, setEditing] = ReactExports.useState(false);
11013
+ const [draft, setDraft] = ReactExports.useState(text);
11014
+ const areaRef = ReactExports.useRef(null);
11015
+ ReactExports.useEffect(() => {
11016
+ if (editing && areaRef.current) {
11017
+ const el = areaRef.current;
11018
+ el.focus();
11019
+ el.setSelectionRange(el.value.length, el.value.length);
11020
+ }
11021
+ }, [editing]);
11022
+ const commit = () => {
11023
+ setEditing(false);
11024
+ if (!api || draft === text) return;
11025
+ const current = api.nodes.find((n) => n.id === props.id);
11026
+ if (!current) return;
11027
+ api.updateNode({
11028
+ ...current,
11029
+ data: { ...current.data, config: { ...config, text: draft } }
11030
+ });
11031
+ };
11032
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11033
+ "div",
11034
+ {
11035
+ className: ["ff-note", props.selected ? "ff-note--selected" : ""].filter(Boolean).join(" "),
11036
+ style: { background: skin.bg, borderColor: skin.border },
11037
+ onDoubleClick: api ? () => {
11038
+ setDraft(text);
11039
+ setEditing(true);
11040
+ } : void 0,
11041
+ children: [
11042
+ /* @__PURE__ */ jsxRuntime.jsx(NodeResizer, { isVisible: props.selected ?? false, color: skin.border, minWidth: 140, minHeight: 80 }),
11043
+ title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-note__title", style: { color: skin.title }, children: title }),
11044
+ editing ? /* @__PURE__ */ jsxRuntime.jsx(
11045
+ "textarea",
11046
+ {
11047
+ ref: areaRef,
11048
+ className: "ff-note__input nodrag nowheel",
11049
+ value: draft,
11050
+ onChange: (e) => setDraft(e.target.value),
11051
+ onBlur: commit,
11052
+ onKeyDown: (e) => {
11053
+ if (e.key === "Escape") setEditing(false);
11054
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commit();
11055
+ }
11056
+ }
11057
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-note__body", children: text || /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-note__placeholder", children: api ? "Double-click to write a note\u2026" : "Note" }) })
11058
+ ]
11059
+ }
11060
+ );
11061
+ }
11062
+ var NoteNode = ReactExports.memo(NoteNodeInner);
10979
11063
 
10980
11064
  // src/registry/capabilities.ts
10981
11065
  var llmClient = null;
@@ -11091,12 +11175,14 @@ async function runFlow(graph, executors, onEvent = () => {
11091
11175
  }
11092
11176
  }
11093
11177
  const visualKind = getNodeKind(node.type ?? "");
11094
- if (node.type === "note" || visualKind?.category === "layout") {
11178
+ const isLayout = visualKind?.category === "layout";
11179
+ const isAnnotation = node.type === "note" || visualKind?.category === "annotation";
11180
+ if (isLayout || isAnnotation) {
11095
11181
  onEvent({
11096
11182
  type: "node-status",
11097
11183
  nodeId: node.id,
11098
11184
  status: "idle",
11099
- text: visualKind?.category === "layout" ? "lane" : "annotation"
11185
+ text: isLayout ? "lane" : "annotation"
11100
11186
  });
11101
11187
  continue;
11102
11188
  }
@@ -12053,6 +12139,35 @@ var KINDS = [
12053
12139
  defaultConfig: { title: "Lane", orientation: "horizontal" },
12054
12140
  resizable: { minWidth: 160, minHeight: 72 },
12055
12141
  component: LaneNode
12142
+ },
12143
+ {
12144
+ // Note — a sticky-note annotation. Portless + visual-only: the runtime skips
12145
+ // the `annotation` category, so a note's text NEVER reaches a runner — it
12146
+ // rides in the document purely for people, editors, and MCP tools. Uses its
12147
+ // own renderer (NoteNode); double-click on the canvas to edit in place.
12148
+ name: "@particle-academy/note",
12149
+ aliases: ["note", "@fancy/note"],
12150
+ category: "annotation",
12151
+ label: "Note",
12152
+ description: "A sticky note that documents the canvas. Never runs \u2014 editor + agent only.",
12153
+ icon: "\u{1F5D2}",
12154
+ inputs: [],
12155
+ outputs: [],
12156
+ configSchema: [
12157
+ { type: "text", key: "title", label: "Title", placeholder: "Optional heading" },
12158
+ { type: "textarea", key: "text", label: "Note", rows: 5, placeholder: "What does this part of the flow do?" },
12159
+ { type: "select", key: "color", label: "Color", default: "amber", options: [
12160
+ { value: "amber", label: "Amber" },
12161
+ { value: "sky", label: "Sky" },
12162
+ { value: "violet", label: "Violet" },
12163
+ { value: "emerald", label: "Emerald" },
12164
+ { value: "rose", label: "Rose" },
12165
+ { value: "slate", label: "Slate" }
12166
+ ] }
12167
+ ],
12168
+ defaultConfig: { text: "", color: "amber" },
12169
+ resizable: { minWidth: 140, minHeight: 80 },
12170
+ component: NoteNode
12056
12171
  }
12057
12172
  ];
12058
12173
  function registerBuiltinKinds() {
@@ -12151,14 +12266,6 @@ function OutputNodeInner(props) {
12151
12266
  return /* @__PURE__ */ jsxRuntime.jsx(NodeShell, { node: props, accent: "#a855f7", tag: "OUTPUT", icon: "\u25CF", showOutputs: false });
12152
12267
  }
12153
12268
  var OutputNode2 = ReactExports.memo(OutputNodeInner);
12154
- function NoteNodeInner({ data, selected: selected2 }) {
12155
- const noteData = data;
12156
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `ff-note ${selected2 ? "ff-note--selected" : ""}`, children: [
12157
- noteData.label && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-note__title", children: noteData.label }),
12158
- noteData.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-note__body", children: noteData.body })
12159
- ] });
12160
- }
12161
- var NoteNode = ReactExports.memo(NoteNodeInner);
12162
12269
  function SubgraphNodeInner(props) {
12163
12270
  const data = props.data;
12164
12271
  const childCount = data.childIds?.length ?? 0;
@@ -12545,7 +12652,7 @@ function FlowCanvas({
12545
12652
  }
12546
12653
  );
12547
12654
  }
12548
- var CATEGORY_ORDER = ["trigger", "logic", "data", "ai", "io", "human", "output", "layout", "custom"];
12655
+ var CATEGORY_ORDER = ["trigger", "logic", "data", "ai", "io", "human", "output", "layout", "annotation", "custom"];
12549
12656
  var CATEGORY_LABELS = {
12550
12657
  trigger: "Triggers",
12551
12658
  logic: "Logic",
@@ -12555,6 +12662,7 @@ var CATEGORY_LABELS = {
12555
12662
  human: "Human",
12556
12663
  output: "Output",
12557
12664
  layout: "Layout",
12665
+ annotation: "Notes",
12558
12666
  custom: "Custom"
12559
12667
  };
12560
12668
  function NodePalette({ categories, onPick, className, style: style2 }) {
@@ -13694,18 +13802,6 @@ function buildNodeTypes() {
13694
13802
  }
13695
13803
  return map;
13696
13804
  }
13697
- var FlowEditorContext = ReactExports.createContext(null);
13698
- var FlowEditorProvider = FlowEditorContext.Provider;
13699
- function useFlowEditor() {
13700
- const api = ReactExports.useContext(FlowEditorContext);
13701
- if (api === null) {
13702
- throw new Error("useFlowEditor() must be called inside <FlowEditor>.");
13703
- }
13704
- return api;
13705
- }
13706
- function useFlowEditorOptional() {
13707
- return ReactExports.useContext(FlowEditorContext);
13708
- }
13709
13805
  var FlowEditor = ReactExports.forwardRef(function FlowEditor2(props, ref) {
13710
13806
  return /* @__PURE__ */ jsxRuntime.jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
13711
13807
  "div",