@particle-academy/fancy-flow 0.32.0 → 0.33.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.d.cts CHANGED
@@ -391,6 +391,17 @@ type ConfigFieldRendererProps = {
391
391
  field: ConfigField;
392
392
  value: unknown;
393
393
  onChange: (value: unknown) => void;
394
+ /**
395
+ * DOM id for the control this renders.
396
+ *
397
+ * Supplied by the caller rather than generated here so the panel's `<label>`
398
+ * can point at it — a label beside a control is not a label attached to one.
399
+ * Also the agent-facing handle: the Human+ contract asks that every
400
+ * interactive element have a stable identity so an agent targets it instead
401
+ * of guessing at the DOM, and until this existed nothing in the editor had
402
+ * one.
403
+ */
404
+ id?: string;
394
405
  renderCredentialField?: (props: {
395
406
  credentialType: string;
396
407
  value: unknown;
@@ -410,14 +421,17 @@ type ConfigFieldRendererProps = {
410
421
  }) => ReactNode;
411
422
  };
412
423
  /**
413
- * ConfigFieldRenderer — dispatches to the right input element per field
414
- * type. Plain HTML inputs styled via the package's CSS so the package
415
- * stays standalone (no react-fancy import required).
424
+ * ConfigFieldRenderer — dispatches to the right input element per field type.
425
+ *
426
+ * Plain HTML inputs styled via the package's CSS so the package stays
427
+ * standalone (no react-fancy import required) and every surface stays themeable
428
+ * through the `--ff-*` token layer. Hosts that want react-fancy form components
429
+ * can supply their own field renderers via the kind's `renderPanel`.
416
430
  *
417
- * Hosts that want to use react-fancy form components can supply their
418
- * own field renderers via the kind's `renderPanel`.
431
+ * Each control carries the caller's `id` and a `data-ff-field` handle keyed by
432
+ * the field, so a label can point at it and an agent can find it by name.
419
433
  */
420
- declare function ConfigFieldRenderer({ field, value, onChange, renderCredentialField, renderDocumentField, }: ConfigFieldRendererProps): react.JSX.Element | null;
434
+ declare function ConfigFieldRenderer({ field, value, onChange, id, renderCredentialField, renderDocumentField, }: ConfigFieldRendererProps): react.JSX.Element | null;
421
435
 
422
436
  /** Entry-point node — outputs only, no inputs. */
423
437
  declare function TriggerNodeInner(props: NodeProps<FlowNode>): react.JSX.Element;
package/dist/index.d.ts CHANGED
@@ -391,6 +391,17 @@ type ConfigFieldRendererProps = {
391
391
  field: ConfigField;
392
392
  value: unknown;
393
393
  onChange: (value: unknown) => void;
394
+ /**
395
+ * DOM id for the control this renders.
396
+ *
397
+ * Supplied by the caller rather than generated here so the panel's `<label>`
398
+ * can point at it — a label beside a control is not a label attached to one.
399
+ * Also the agent-facing handle: the Human+ contract asks that every
400
+ * interactive element have a stable identity so an agent targets it instead
401
+ * of guessing at the DOM, and until this existed nothing in the editor had
402
+ * one.
403
+ */
404
+ id?: string;
394
405
  renderCredentialField?: (props: {
395
406
  credentialType: string;
396
407
  value: unknown;
@@ -410,14 +421,17 @@ type ConfigFieldRendererProps = {
410
421
  }) => ReactNode;
411
422
  };
412
423
  /**
413
- * ConfigFieldRenderer — dispatches to the right input element per field
414
- * type. Plain HTML inputs styled via the package's CSS so the package
415
- * stays standalone (no react-fancy import required).
424
+ * ConfigFieldRenderer — dispatches to the right input element per field type.
425
+ *
426
+ * Plain HTML inputs styled via the package's CSS so the package stays
427
+ * standalone (no react-fancy import required) and every surface stays themeable
428
+ * through the `--ff-*` token layer. Hosts that want react-fancy form components
429
+ * can supply their own field renderers via the kind's `renderPanel`.
416
430
  *
417
- * Hosts that want to use react-fancy form components can supply their
418
- * own field renderers via the kind's `renderPanel`.
431
+ * Each control carries the caller's `id` and a `data-ff-field` handle keyed by
432
+ * the field, so a label can point at it and an agent can find it by name.
419
433
  */
420
- declare function ConfigFieldRenderer({ field, value, onChange, renderCredentialField, renderDocumentField, }: ConfigFieldRendererProps): react.JSX.Element | null;
434
+ declare function ConfigFieldRenderer({ field, value, onChange, id, renderCredentialField, renderDocumentField, }: ConfigFieldRendererProps): react.JSX.Element | null;
421
435
 
422
436
  /** Entry-point node — outputs only, no inputs. */
423
437
  declare function TriggerNodeInner(props: NodeProps<FlowNode>): react.JSX.Element;
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import { onNodeKindsChanged, listNodeKinds, getNodeKind, defaultConfigFor, valid
14
14
  export { categoryAccent, defaultConfigFor, getNodeKind, listNodeKinds, onNodeKindsChanged, registerNodeKind, validateConfig } from './chunk-Q6IPK64P.js';
15
15
  import { getRichInputAdapter } from './chunk-F5RPRB7A.js';
16
16
  export { RichInputPreview, getRichInputAdapter, isRichInputEnabled, onRichInputAdapterChanged, registerRichInputAdapter } from './chunk-F5RPRB7A.js';
17
- import { memo, forwardRef, useState, useMemo, useEffect, useCallback, useRef, useImperativeHandle } from 'react';
17
+ import { memo, forwardRef, useState, useMemo, useEffect, useCallback, useRef, useImperativeHandle, useId } from 'react';
18
18
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
19
19
 
20
20
  function NodeShellInner({
@@ -565,9 +565,11 @@ function ConfigFieldRenderer({
565
565
  field,
566
566
  value,
567
567
  onChange,
568
+ id,
568
569
  renderCredentialField,
569
570
  renderDocumentField
570
571
  }) {
572
+ const handle = { id, "data-ff-field": field.key };
571
573
  switch (field.type) {
572
574
  case "text": {
573
575
  if (field.choices?.length) {
@@ -576,6 +578,7 @@ function ConfigFieldRenderer({
576
578
  return /* @__PURE__ */ jsx(
577
579
  "input",
578
580
  {
581
+ ...handle,
579
582
  className: "ff-panel__input",
580
583
  type: "text",
581
584
  value: value ?? "",
@@ -588,6 +591,7 @@ function ConfigFieldRenderer({
588
591
  return /* @__PURE__ */ jsx(
589
592
  "textarea",
590
593
  {
594
+ ...handle,
591
595
  className: "ff-panel__input ff-panel__input--textarea",
592
596
  rows: field.rows ?? 4,
593
597
  value: value ?? "",
@@ -599,6 +603,7 @@ function ConfigFieldRenderer({
599
603
  return /* @__PURE__ */ jsx(
600
604
  "input",
601
605
  {
606
+ ...handle,
602
607
  className: "ff-panel__input",
603
608
  type: "number",
604
609
  value: value ?? "",
@@ -613,6 +618,7 @@ function ConfigFieldRenderer({
613
618
  /* @__PURE__ */ jsx(
614
619
  "input",
615
620
  {
621
+ ...handle,
616
622
  type: "checkbox",
617
623
  checked: !!value,
618
624
  onChange: (e) => onChange(e.target.checked)
@@ -624,6 +630,7 @@ function ConfigFieldRenderer({
624
630
  return /* @__PURE__ */ jsxs(
625
631
  "select",
626
632
  {
633
+ ...handle,
627
634
  className: "ff-panel__input",
628
635
  value: value ?? "",
629
636
  onChange: (e) => onChange(e.target.value),
@@ -639,6 +646,7 @@ function ConfigFieldRenderer({
639
646
  return /* @__PURE__ */ jsx(
640
647
  "textarea",
641
648
  {
649
+ ...handle,
642
650
  className: "ff-panel__input ff-panel__input--expression",
643
651
  rows: 2,
644
652
  value: value ?? "",
@@ -939,6 +947,8 @@ function NodeConfigPanel({
939
947
  className,
940
948
  style
941
949
  }) {
950
+ const uid = useId();
951
+ const fieldId = (key) => `${uid}-${key}`;
942
952
  if (!node) {
943
953
  return /* @__PURE__ */ jsxs("aside", { className: ["ff-panel", "ff-panel--empty", className ?? ""].filter(Boolean).join(" "), style, children: [
944
954
  header,
@@ -988,10 +998,12 @@ function NodeConfigPanel({
988
998
  ] }),
989
999
  kind.description && /* @__PURE__ */ jsx("p", { className: "ff-panel__kind-desc", children: kind.description }),
990
1000
  /* @__PURE__ */ jsxs("div", { className: "ff-panel__field", children: [
991
- /* @__PURE__ */ jsx("label", { className: "ff-panel__label", children: "Label" }),
1001
+ /* @__PURE__ */ jsx("label", { className: "ff-panel__label", htmlFor: fieldId("label"), children: "Label" }),
992
1002
  /* @__PURE__ */ jsx(
993
1003
  "input",
994
1004
  {
1005
+ id: fieldId("label"),
1006
+ "data-ff-field": "label",
995
1007
  className: "ff-panel__input",
996
1008
  value: node.data.label ?? "",
997
1009
  onChange: (e) => setLabel(e.target.value),
@@ -1000,10 +1012,12 @@ function NodeConfigPanel({
1000
1012
  )
1001
1013
  ] }),
1002
1014
  /* @__PURE__ */ jsxs("div", { className: "ff-panel__field", children: [
1003
- /* @__PURE__ */ jsx("label", { className: "ff-panel__label", children: "Description" }),
1015
+ /* @__PURE__ */ jsx("label", { className: "ff-panel__label", htmlFor: fieldId("description"), children: "Description" }),
1004
1016
  /* @__PURE__ */ jsx(
1005
1017
  "textarea",
1006
1018
  {
1019
+ id: fieldId("description"),
1020
+ "data-ff-field": "description",
1007
1021
  className: "ff-panel__input ff-panel__input--textarea",
1008
1022
  rows: 2,
1009
1023
  value: node.data.description ?? "",
@@ -1018,7 +1032,7 @@ function NodeConfigPanel({
1018
1032
  }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1019
1033
  (kind.configSchema ?? []).length > 0 && /* @__PURE__ */ jsx("hr", { className: "ff-panel__divider" }),
1020
1034
  (kind.configSchema ?? []).map((field) => /* @__PURE__ */ jsxs("div", { className: "ff-panel__field", children: [
1021
- /* @__PURE__ */ jsxs("label", { className: "ff-panel__label", children: [
1035
+ /* @__PURE__ */ jsxs("label", { className: "ff-panel__label", htmlFor: fieldId(field.key), children: [
1022
1036
  field.label,
1023
1037
  field.required && /* @__PURE__ */ jsx("span", { className: "ff-panel__required", "aria-hidden": true, children: " *" })
1024
1038
  ] }),
@@ -1026,6 +1040,7 @@ function NodeConfigPanel({
1026
1040
  /* @__PURE__ */ jsx(
1027
1041
  ConfigFieldRenderer,
1028
1042
  {
1043
+ id: fieldId(field.key),
1029
1044
  field,
1030
1045
  value: config[field.key],
1031
1046
  onChange: (v) => setConfigValue(field.key, v),