@particle-academy/fancy-flow 0.42.0 → 0.43.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.
package/dist/index.cjs CHANGED
@@ -11033,11 +11033,11 @@ function NoteNodeInner(props) {
11033
11033
  const config = data.config ?? {};
11034
11034
  const api = useFlowEditorOptional();
11035
11035
  const hasConfigText = typeof config.text === "string" && config.text.length > 0;
11036
- const text = hasConfigText ? String(config.text) : typeof data.body === "string" ? data.body : typeof data.text === "string" ? data.text : "";
11036
+ const text2 = hasConfigText ? String(config.text) : typeof data.body === "string" ? data.body : typeof data.text === "string" ? data.text : "";
11037
11037
  const title = typeof config.title === "string" && config.title || (!hasConfigText && typeof data.body === "string" && typeof data.label === "string" ? data.label : "");
11038
11038
  const skin = NOTE_COLORS[coerceColor(config.color)];
11039
11039
  const [editing, setEditing] = ReactExports.useState(false);
11040
- const [draft, setDraft] = ReactExports.useState(text);
11040
+ const [draft, setDraft] = ReactExports.useState(text2);
11041
11041
  const areaRef = ReactExports.useRef(null);
11042
11042
  ReactExports.useEffect(() => {
11043
11043
  if (editing && areaRef.current) {
@@ -11048,7 +11048,7 @@ function NoteNodeInner(props) {
11048
11048
  }, [editing]);
11049
11049
  const commit = () => {
11050
11050
  setEditing(false);
11051
- if (!api || draft === text) return;
11051
+ if (!api || draft === text2) return;
11052
11052
  const current = api.nodes.find((n) => n.id === props.id);
11053
11053
  if (!current) return;
11054
11054
  api.updateNode({
@@ -11062,7 +11062,7 @@ function NoteNodeInner(props) {
11062
11062
  className: ["ff-note", props.selected ? "ff-note--selected" : ""].filter(Boolean).join(" "),
11063
11063
  style: { background: skin.bg, borderColor: skin.border },
11064
11064
  onDoubleClick: api ? () => {
11065
- setDraft(text);
11065
+ setDraft(text2);
11066
11066
  setEditing(true);
11067
11067
  } : void 0,
11068
11068
  children: [
@@ -11081,7 +11081,7 @@ function NoteNodeInner(props) {
11081
11081
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commit();
11082
11082
  }
11083
11083
  }
11084
- ) : /* @__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" }) })
11084
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-note__body", children: text2 || /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-note__placeholder", children: api ? "Double-click to write a note\u2026" : "Note" }) })
11085
11085
  ]
11086
11086
  }
11087
11087
  );
@@ -12898,8 +12898,8 @@ function describeExpressionGrammar() {
12898
12898
  note: "Dot-paths only \u2014 no arithmetic, comparisons or function calls; anything else resolves to nothing. A field that is exactly one expression keeps the resolved value's type; mixed text interpolates as a string. fancy-flow's own runFlow does not interpolate: it hands config to your executor verbatim, and the fancy-flow-php runtime is what resolves these."
12899
12899
  };
12900
12900
  }
12901
- function findTrigger(text, caret) {
12902
- const before = text.slice(0, caret);
12901
+ function findTrigger(text2, caret) {
12902
+ const before = text2.slice(0, caret);
12903
12903
  const open = before.lastIndexOf("{{");
12904
12904
  if (open === -1) return null;
12905
12905
  if (before.indexOf("}}", open) !== -1) return null;
@@ -12910,9 +12910,9 @@ function filterVariables(vars, query) {
12910
12910
  if (q2 === "") return vars;
12911
12911
  return vars.filter((v2) => v2.path.toLowerCase().includes(q2));
12912
12912
  }
12913
- function applyCompletion(text, caret, trigger, variable) {
12914
- const head = text.slice(0, trigger.open);
12915
- const tail = text.slice(caret);
12913
+ function applyCompletion(text2, caret, trigger, variable) {
12914
+ const head = text2.slice(0, trigger.open);
12915
+ const tail = text2.slice(caret);
12916
12916
  return { value: `${head}${variable.expression}${tail}`, caret: head.length + variable.expression.length };
12917
12917
  }
12918
12918
  function ExpressionField({
@@ -13436,7 +13436,7 @@ function JsonField({
13436
13436
  return "";
13437
13437
  }
13438
13438
  }, [value]);
13439
- const [text, setText] = ReactExports.useState(serialized);
13439
+ const [text2, setText] = ReactExports.useState(serialized);
13440
13440
  const [error, setError] = ReactExports.useState(null);
13441
13441
  const [touched, setTouched] = ReactExports.useState(false);
13442
13442
  const lastProps = ReactExports.useRef(serialized);
@@ -13474,7 +13474,7 @@ function JsonField({
13474
13474
  ...handle,
13475
13475
  className: "ff-panel__input ff-panel__input--json",
13476
13476
  rows: rows ?? 6,
13477
- value: text,
13477
+ value: text2,
13478
13478
  spellCheck: false,
13479
13479
  "aria-invalid": showError || void 0,
13480
13480
  "aria-describedby": showError ? errorId : void 0,
@@ -14903,8 +14903,8 @@ function FlowEditorInner({
14903
14903
  x: labelEdit.x,
14904
14904
  y: labelEdit.y,
14905
14905
  initial: flow.edges.find((e) => e.id === labelEdit.edgeId)?.label ?? "",
14906
- onCommit: (text) => {
14907
- api.setEdgeLabel(labelEdit.edgeId, text);
14906
+ onCommit: (text2) => {
14907
+ api.setEdgeLabel(labelEdit.edgeId, text2);
14908
14908
  setLabelEdit(null);
14909
14909
  },
14910
14910
  onCancel: () => setLabelEdit(null)
@@ -15009,7 +15009,7 @@ function EdgeLabelEditor({
15009
15009
  onCommit,
15010
15010
  onCancel
15011
15011
  }) {
15012
- const [text, setText] = ReactExports.useState(initial);
15012
+ const [text2, setText] = ReactExports.useState(initial);
15013
15013
  const ref = ReactExports.useRef(null);
15014
15014
  ReactExports.useEffect(() => {
15015
15015
  ref.current?.focus();
@@ -15026,17 +15026,17 @@ function EdgeLabelEditor({
15026
15026
  {
15027
15027
  ref,
15028
15028
  className: "ff-panel__input",
15029
- value: text,
15029
+ value: text2,
15030
15030
  placeholder: "Label this connection",
15031
15031
  "aria-label": "Connection label",
15032
15032
  "data-action": "edge-label-input",
15033
15033
  onChange: (e) => setText(e.target.value),
15034
15034
  onKeyDown: (e) => {
15035
15035
  e.stopPropagation();
15036
- if (e.key === "Enter") onCommit(text);
15036
+ if (e.key === "Enter") onCommit(text2);
15037
15037
  if (e.key === "Escape") onCancel();
15038
15038
  },
15039
- onBlur: () => onCommit(text)
15039
+ onBlur: () => onCommit(text2)
15040
15040
  }
15041
15041
  )
15042
15042
  }
@@ -15271,6 +15271,67 @@ var flowKeys = {
15271
15271
  run: (runId) => ["flow", "runs", runId]
15272
15272
  };
15273
15273
 
15274
+ // src/expressions/expr.ts
15275
+ var WHOLE = /^\{\{([\s\S]*?)\}\}$/;
15276
+ var EACH = /\{\{([\s\S]*?)\}\}/g;
15277
+ var FALSY_STRINGS = /* @__PURE__ */ new Set(["", "0", "false", "no", "off", "null"]);
15278
+ function resolvePath(path, context) {
15279
+ const trimmed = path.trim();
15280
+ if (trimmed === "") return null;
15281
+ const segments = trimmed.split(".");
15282
+ let cursor;
15283
+ const head = segments[0];
15284
+ if (head === "$json" || head === "$input") {
15285
+ cursor = context !== null && typeof context === "object" && "in" in context ? context.in : context;
15286
+ segments.shift();
15287
+ } else {
15288
+ cursor = context;
15289
+ }
15290
+ for (const segment of segments) {
15291
+ if (cursor === null || cursor === void 0) return null;
15292
+ if (typeof cursor !== "object") return null;
15293
+ const next = cursor[segment];
15294
+ if (next === void 0) return null;
15295
+ cursor = next;
15296
+ }
15297
+ return cursor === void 0 ? null : cursor;
15298
+ }
15299
+ function evaluateExpression(template, context) {
15300
+ if (typeof template !== "string") return template;
15301
+ const whole = WHOLE.exec(template.trim());
15302
+ if (whole) return resolvePath(whole[1] ?? "", context);
15303
+ return template.replace(EACH, (_m, path) => stringify(resolvePath(path, context)));
15304
+ }
15305
+ function truthy(value) {
15306
+ if (typeof value === "boolean") return value;
15307
+ if (value === null || value === void 0) return false;
15308
+ if (typeof value === "string") return !FALSY_STRINGS.has(value.trim().toLowerCase());
15309
+ if (Array.isArray(value)) return value.length > 0;
15310
+ if (typeof value === "number") return value !== 0;
15311
+ return Boolean(value);
15312
+ }
15313
+ function text(value) {
15314
+ return stringify(value);
15315
+ }
15316
+ function stringify(value) {
15317
+ if (typeof value === "string") return value;
15318
+ if (typeof value === "boolean") return value ? "true" : "false";
15319
+ if (value === null || value === void 0) return "";
15320
+ if (typeof value === "number" || typeof value === "bigint") return String(value);
15321
+ try {
15322
+ return JSON.stringify(value) ?? "";
15323
+ } catch {
15324
+ return "";
15325
+ }
15326
+ }
15327
+ function evaluateConfig(config, context) {
15328
+ const out = {};
15329
+ for (const [key, value] of Object.entries(config)) {
15330
+ out[key] = Array.isArray(value) ? value.map((v2) => evaluateExpression(v2, context)) : value !== null && typeof value === "object" ? evaluateConfig(value, context) : evaluateExpression(value, context);
15331
+ }
15332
+ return out;
15333
+ }
15334
+
15274
15335
  // src/index.ts
15275
15336
  registerBuiltinKinds();
15276
15337
  /*! Bundled license information:
@@ -15324,6 +15385,8 @@ exports.defineNode = defineNode;
15324
15385
  exports.describeExpressionGrammar = describeExpressionGrammar;
15325
15386
  exports.distributeNodes = distributeNodes;
15326
15387
  exports.encodePause = encodePause;
15388
+ exports.evaluateConfig = evaluateConfig;
15389
+ exports.evaluateExpression = evaluateExpression;
15327
15390
  exports.exportWorkflow = exportWorkflow;
15328
15391
  exports.flowKeys = flowKeys;
15329
15392
  exports.flowLive = flowLive;
@@ -15345,9 +15408,12 @@ exports.registerBuiltinKinds = registerBuiltinKinds;
15345
15408
  exports.registerNodeKind = registerNodeKind;
15346
15409
  exports.registerRichInputAdapter = registerRichInputAdapter;
15347
15410
  exports.resolveNodePorts = resolveNodePorts;
15411
+ exports.resolvePath = resolvePath;
15348
15412
  exports.resolvePortSpec = resolvePortSpec;
15349
15413
  exports.runCohort = runCohort;
15350
15414
  exports.runFlow = runFlow;
15415
+ exports.text = text;
15416
+ exports.truthy = truthy;
15351
15417
  exports.useFlowEditor = useFlowEditor;
15352
15418
  exports.useFlowEditorOptional = useFlowEditorOptional;
15353
15419
  exports.useFlowHistory = useFlowHistory;