@octaviaflow/core 3.1.0-beta.102 → 3.1.0-beta.104

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.js CHANGED
@@ -34425,13 +34425,176 @@ import {
34425
34425
  useCallback as useCallback50,
34426
34426
  useEffect as useEffect56,
34427
34427
  useId as useId61,
34428
- useRef as useRef50
34428
+ useRef as useRef50,
34429
+ useState as useState71
34429
34430
  } from "react";
34431
+
34432
+ // src/components/ExpressionField/parseExpression.ts
34433
+ var CALL_NAME_SOURCE = "(?:[A-Z_][A-Z0-9_]*|user\\.[A-Za-z_][A-Za-z0-9_]*)";
34434
+ var CALL_HEAD = new RegExp(`^(${CALL_NAME_SOURCE})\\s*\\(`);
34435
+ function wrappedRegions(value) {
34436
+ const out = [];
34437
+ let i = 0;
34438
+ while (i < value.length) {
34439
+ const open = value.indexOf("{{", i);
34440
+ if (open === -1) break;
34441
+ let depth = 0;
34442
+ let quote = null;
34443
+ let close = -1;
34444
+ for (let j = open; j < value.length; j++) {
34445
+ const ch = value[j];
34446
+ if (quote) {
34447
+ if (ch === quote) quote = null;
34448
+ continue;
34449
+ }
34450
+ if (ch === '"' || ch === "'") {
34451
+ quote = ch;
34452
+ continue;
34453
+ }
34454
+ if (ch === "{" && value[j + 1] === "{") {
34455
+ depth++;
34456
+ j++;
34457
+ continue;
34458
+ }
34459
+ if (ch === "}" && value[j + 1] === "}") {
34460
+ depth--;
34461
+ j++;
34462
+ if (depth === 0) {
34463
+ close = j;
34464
+ break;
34465
+ }
34466
+ }
34467
+ }
34468
+ if (close === -1) break;
34469
+ out.push({ start: open, end: close });
34470
+ i = close + 1;
34471
+ }
34472
+ return out;
34473
+ }
34474
+ function splitArguments(argsText) {
34475
+ const trimmed = argsText.trim();
34476
+ if (!trimmed) return [];
34477
+ const args = [];
34478
+ let current = "";
34479
+ let depth = 0;
34480
+ let braces = 0;
34481
+ let quote = null;
34482
+ for (let i = 0; i < trimmed.length; i++) {
34483
+ const ch = trimmed[i];
34484
+ if (quote) {
34485
+ current += ch;
34486
+ if (ch === quote) quote = null;
34487
+ continue;
34488
+ }
34489
+ if (ch === '"' || ch === "'") {
34490
+ quote = ch;
34491
+ current += ch;
34492
+ continue;
34493
+ }
34494
+ if (ch === "{" && trimmed[i + 1] === "{") {
34495
+ braces++;
34496
+ current += "{{";
34497
+ i++;
34498
+ continue;
34499
+ }
34500
+ if (ch === "}" && trimmed[i + 1] === "}") {
34501
+ braces--;
34502
+ current += "}}";
34503
+ i++;
34504
+ continue;
34505
+ }
34506
+ if (ch === "(") depth++;
34507
+ if (ch === ")") depth--;
34508
+ if (ch === "," && depth === 0 && braces === 0) {
34509
+ args.push(current.trim());
34510
+ current = "";
34511
+ continue;
34512
+ }
34513
+ current += ch;
34514
+ }
34515
+ if (current.trim()) args.push(current.trim());
34516
+ return args;
34517
+ }
34518
+ function parseCall(raw) {
34519
+ const inner = raw.startsWith("{{") && raw.endsWith("}}") ? raw.slice(2, -2).trim() : raw.trim();
34520
+ const head = CALL_HEAD.exec(inner);
34521
+ if (!head) return void 0;
34522
+ const open = inner.indexOf("(");
34523
+ const close = inner.lastIndexOf(")");
34524
+ if (open === -1 || close < open) return void 0;
34525
+ return { name: head[1], args: splitArguments(inner.slice(open + 1, close)) };
34526
+ }
34527
+ function formatCall(name, args) {
34528
+ return `{{${name}(${args.filter((a) => a.trim() !== "").join(", ")})}}`;
34529
+ }
34530
+ function parseExpression(value, pillEveryToken = false) {
34531
+ const segments = [];
34532
+ let cursor = 0;
34533
+ const pushText = (raw) => {
34534
+ if (raw) segments.push({ kind: "text", raw });
34535
+ };
34536
+ for (const { start, end } of wrappedRegions(value)) {
34537
+ pushText(value.slice(cursor, start));
34538
+ const raw = value.slice(start, end + 1);
34539
+ const call = parseCall(raw);
34540
+ if (call) {
34541
+ segments.push({ kind: "call", raw, name: call.name, args: call.args });
34542
+ } else if (raw.startsWith("{{ods://") || pillEveryToken) {
34543
+ segments.push({ kind: "ref", raw });
34544
+ } else {
34545
+ pushText(raw);
34546
+ }
34547
+ cursor = end + 1;
34548
+ }
34549
+ const tail = value.slice(cursor);
34550
+ let last = 0;
34551
+ for (const m of tail.matchAll(/\$\{[^}]+\}/g)) {
34552
+ pushText(tail.slice(last, m.index));
34553
+ segments.push({ kind: "ref", raw: m[0] });
34554
+ last = (m.index ?? 0) + m[0].length;
34555
+ }
34556
+ pushText(tail.slice(last));
34557
+ return segments;
34558
+ }
34559
+ function callAt(raw, path) {
34560
+ let current = parseCall(raw);
34561
+ for (const index of path) {
34562
+ if (!current) return void 0;
34563
+ const arg = current.args[index];
34564
+ if (arg === void 0) return void 0;
34565
+ current = parseCall(arg);
34566
+ }
34567
+ return current;
34568
+ }
34569
+ function formatNestedCall(name, args) {
34570
+ return `${name}(${args.filter((a) => a.trim() !== "").join(", ")})`;
34571
+ }
34572
+ function replaceArgument(raw, path, next) {
34573
+ if (path.length === 0) return next;
34574
+ const [index, ...rest] = path;
34575
+ const call = parseCall(raw);
34576
+ if (!call || index === void 0 || index > call.args.length) return raw;
34577
+ const args = [...call.args];
34578
+ if (args[index] === void 0) args[index] = "";
34579
+ args[index] = rest.length === 0 ? next : replaceArgument(args[index], rest, next);
34580
+ const wrapped = raw.trim().startsWith("{{");
34581
+ return wrapped ? formatCall(call.name, args) : formatNestedCall(call.name, args);
34582
+ }
34583
+ function argumentKind(arg) {
34584
+ const trimmed = arg.trim();
34585
+ if (!trimmed) return "literal";
34586
+ if (parseCall(trimmed)) return "call";
34587
+ if (trimmed.startsWith("{{") || trimmed.startsWith("ods://") || trimmed.startsWith("${")) {
34588
+ return "reference";
34589
+ }
34590
+ return "literal";
34591
+ }
34592
+
34593
+ // src/components/ExpressionField/ExpressionField.tsx
34430
34594
  import { jsx as jsx147, jsxs as jsxs141 } from "react/jsx-runtime";
34431
- var REF_TOKEN = /(\{\{ods:\/\/[^}]+\}\}|\$\{[^}]+\})/g;
34432
- var ANY_TOKEN = /(\{\{[^}]+\}\}|\$\{[^}]+\})/g;
34433
- var CALL_TOKEN = /^\{\{\s*(?:[A-Z_][A-Z0-9_]*|user\.[A-Za-z_][A-Za-z0-9_]*)\s*\(/;
34434
34595
  var ZERO_WIDTH = "\u200B";
34596
+ var PILL = "ods-expression-field__pill";
34597
+ var CHIP = "ods-expression-field__chip";
34435
34598
  function shortReferenceLabel(token) {
34436
34599
  if (token.startsWith("{{ods://")) {
34437
34600
  const path = token.slice(8, -2);
@@ -34453,7 +34616,7 @@ function serialize(el) {
34453
34616
  }
34454
34617
  if (node.nodeType !== Node.ELEMENT_NODE) return;
34455
34618
  const elem = node;
34456
- if (elem.classList.contains("ods-expression-field__pill")) {
34619
+ if (elem.classList.contains(PILL) || elem.classList.contains(CHIP)) {
34457
34620
  out += elem.getAttribute("data-value") || "";
34458
34621
  } else if (elem.tagName === "BR") {
34459
34622
  out += "\n";
@@ -34484,6 +34647,19 @@ function insideCallArguments(textBeforeCaret) {
34484
34647
  }
34485
34648
  return depth > 0;
34486
34649
  }
34650
+ function argSummary(arg, nameOf, depth = 2) {
34651
+ const trimmed = arg.trim();
34652
+ const call = parseCall(trimmed);
34653
+ if (call) {
34654
+ if (depth <= 0) return `${nameOf(call.name)}(\u2026)`;
34655
+ const inner = call.args.map((a) => argSummary(a, nameOf, depth - 1)).filter(Boolean).join(", ");
34656
+ return `${nameOf(call.name)}(${inner})`;
34657
+ }
34658
+ if (trimmed.startsWith("{{") || trimmed.startsWith("ods://") || trimmed.startsWith("${")) {
34659
+ return shortReferenceLabel(trimmed.startsWith("ods://") ? `{{${trimmed}}}` : trimmed);
34660
+ }
34661
+ return trimmed;
34662
+ }
34487
34663
  function ExpressionField({
34488
34664
  value,
34489
34665
  onChange,
@@ -34496,6 +34672,7 @@ function ExpressionField({
34496
34672
  className,
34497
34673
  style,
34498
34674
  resolveLabel,
34675
+ resolveCall,
34499
34676
  pillEveryToken = false,
34500
34677
  "aria-label": ariaLabel
34501
34678
  }) {
@@ -34504,28 +34681,42 @@ function ExpressionField({
34504
34681
  const generatedId = useId61();
34505
34682
  const fieldId = id ?? generatedId;
34506
34683
  const helperId = `${fieldId}-helper`;
34507
- const splitPattern = pillEveryToken ? ANY_TOKEN : REF_TOKEN;
34508
- const isPillToken = useCallback50(
34509
- (part) => {
34510
- if (CALL_TOKEN.test(part)) return false;
34511
- return pillEveryToken ? /^(\{\{[^}]+\}\}|\$\{[^}]+\})$/.test(part) : /^(\{\{ods:\/\/[^}]+\}\}|\$\{[^}]+\})$/.test(part);
34512
- },
34513
- [pillEveryToken]
34514
- );
34684
+ const [editing, setEditing] = useState71(null);
34515
34685
  const labelFor = useCallback50(
34516
34686
  (token) => resolveLabel?.(token) ?? shortReferenceLabel(token),
34517
34687
  [resolveLabel]
34518
34688
  );
34519
- const makePill = useCallback50(
34520
- (token) => {
34521
- const pill = document.createElement("span");
34522
- pill.className = "ods-expression-field__pill";
34523
- pill.contentEditable = "false";
34524
- pill.setAttribute("data-value", token);
34525
- pill.textContent = labelFor(token);
34526
- return pill;
34689
+ const callLabel = useCallback50(
34690
+ (name) => resolveCall?.(name)?.label ?? (name.startsWith("user.") ? name.slice(5) : name),
34691
+ [resolveCall]
34692
+ );
34693
+ const chipText = useCallback50(
34694
+ (name, args) => {
34695
+ const summary = args.map((a) => argSummary(a, callLabel)).filter(Boolean).join(", ");
34696
+ return `${callLabel(name)}(${summary})`;
34527
34697
  },
34528
- [labelFor]
34698
+ [callLabel]
34699
+ );
34700
+ const makeToken = useCallback50(
34701
+ (segment) => {
34702
+ if (segment.kind === "text") return null;
34703
+ const el = document.createElement("span");
34704
+ el.contentEditable = "false";
34705
+ el.setAttribute("data-value", segment.raw);
34706
+ el.tabIndex = -1;
34707
+ if (segment.kind === "ref") {
34708
+ el.className = PILL;
34709
+ el.textContent = labelFor(segment.raw);
34710
+ el.title = segment.raw;
34711
+ } else {
34712
+ el.className = CHIP;
34713
+ el.setAttribute("data-call", segment.name);
34714
+ el.textContent = chipText(segment.name, segment.args);
34715
+ el.title = segment.raw;
34716
+ }
34717
+ return el;
34718
+ },
34719
+ [labelFor, chipText]
34529
34720
  );
34530
34721
  const render = useCallback50(
34531
34722
  (raw) => {
@@ -34533,17 +34724,19 @@ function ExpressionField({
34533
34724
  if (!el) return;
34534
34725
  el.innerHTML = "";
34535
34726
  if (!raw) return;
34536
- raw.split(new RegExp(splitPattern.source, "g")).forEach((part) => {
34537
- if (!part) return;
34538
- if (isPillToken(part)) {
34539
- el.appendChild(makePill(part));
34727
+ for (const segment of parseExpression(raw, pillEveryToken)) {
34728
+ if (segment.kind === "text") {
34729
+ el.appendChild(document.createTextNode(segment.raw));
34730
+ continue;
34731
+ }
34732
+ const token = makeToken(segment);
34733
+ if (token) {
34734
+ el.appendChild(token);
34540
34735
  el.appendChild(document.createTextNode(ZERO_WIDTH));
34541
- } else {
34542
- el.appendChild(document.createTextNode(part));
34543
34736
  }
34544
- });
34737
+ }
34545
34738
  },
34546
- [splitPattern, isPillToken, makePill]
34739
+ [pillEveryToken, makeToken]
34547
34740
  );
34548
34741
  useEffect56(() => {
34549
34742
  if (fromSelf.current) {
@@ -34564,9 +34757,8 @@ function ExpressionField({
34564
34757
  if (!el || !sel || sel.rangeCount === 0 || !el.contains(sel.anchorNode)) return "";
34565
34758
  const range = sel.getRangeAt(0).cloneRange();
34566
34759
  range.setStart(el, 0);
34567
- const fragment = range.cloneContents();
34568
34760
  const holder = document.createElement("div");
34569
- holder.appendChild(fragment);
34761
+ holder.appendChild(range.cloneContents());
34570
34762
  return serialize(holder);
34571
34763
  }, []);
34572
34764
  const insert = useCallback50(
@@ -34576,9 +34768,19 @@ function ExpressionField({
34576
34768
  const sel = window.getSelection();
34577
34769
  const hasCaret = !!sel && sel.rangeCount > 0 && el.contains(sel.anchorNode);
34578
34770
  const asArgument = hasCaret && insideCallArguments(textBeforeCaret());
34579
- const isCall = kind === "function" || /\(\s*\)?\s*$/.test(raw.trim());
34580
- const text = isCall ? raw : asArgument ? raw : raw.startsWith("ods://") ? `{{${raw}}}` : raw;
34581
- const node = !isCall && !asArgument && (text.startsWith("{{") || text.startsWith("${")) ? makePill(text) : document.createTextNode(text);
34771
+ const call = parseCall(raw);
34772
+ const isCall = kind === "function" || !!call;
34773
+ const text = isCall || asArgument ? raw : raw.startsWith("ods://") ? `{{${raw}}}` : raw;
34774
+ let node;
34775
+ if (asArgument) {
34776
+ node = document.createTextNode(text);
34777
+ } else if (isCall && call) {
34778
+ node = makeToken({ kind: "call", raw: text, name: call.name, args: call.args });
34779
+ } else if (text.startsWith("{{") || text.startsWith("${")) {
34780
+ node = makeToken({ kind: "ref", raw: text });
34781
+ } else {
34782
+ node = document.createTextNode(text);
34783
+ }
34582
34784
  const range = hasCaret && sel ? sel.getRangeAt(0) : (() => {
34583
34785
  const r = document.createRange();
34584
34786
  r.selectNodeContents(el);
@@ -34590,27 +34792,113 @@ function ExpressionField({
34590
34792
  const after = document.createTextNode(ZERO_WIDTH);
34591
34793
  range.setStartAfter(node);
34592
34794
  range.insertNode(after);
34593
- if (node.nodeType === Node.TEXT_NODE && isCall) {
34594
- const open = text.lastIndexOf("(");
34595
- const close = text.lastIndexOf(")");
34596
- if (open !== -1 && close === open + 1) {
34597
- range.setStart(node, open + 1);
34598
- range.collapse(true);
34599
- } else {
34600
- range.setStartAfter(after);
34601
- range.collapse(true);
34602
- }
34603
- } else {
34604
- range.setStartAfter(after);
34605
- range.collapse(true);
34606
- }
34795
+ range.setStartAfter(after);
34796
+ range.collapse(true);
34607
34797
  el.focus();
34608
34798
  const selection = window.getSelection();
34609
34799
  selection?.removeAllRanges();
34610
34800
  selection?.addRange(range);
34611
34801
  commit();
34802
+ if (isCall && call && node instanceof HTMLElement && call.args.length === 0) {
34803
+ openEditor(node);
34804
+ }
34805
+ },
34806
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34807
+ [commit, disabled, makeToken, textBeforeCaret]
34808
+ );
34809
+ const selected = useRef50(null);
34810
+ const selectToken = useCallback50((token) => {
34811
+ selected.current = token;
34812
+ const range = document.createRange();
34813
+ range.selectNode(token);
34814
+ const sel = window.getSelection();
34815
+ sel?.removeAllRanges();
34816
+ sel?.addRange(range);
34817
+ fieldRef.current?.focus();
34818
+ }, []);
34819
+ const openEditor = useCallback50((chip) => {
34820
+ const raw = chip.getAttribute("data-value") ?? "";
34821
+ if (!parseCall(raw)) return;
34822
+ const box = chip.getBoundingClientRect();
34823
+ const host = fieldRef.current?.getBoundingClientRect();
34824
+ setEditing({
34825
+ el: chip,
34826
+ raw,
34827
+ rect: {
34828
+ top: box.bottom - (host?.top ?? 0) + 4,
34829
+ left: Math.max(0, box.left - (host?.left ?? 0)),
34830
+ width: Math.max(300, box.width)
34831
+ }
34832
+ });
34833
+ }, []);
34834
+ const applyEditor = useCallback50(
34835
+ (raw) => {
34836
+ if (!editing) return;
34837
+ const call = parseCall(raw);
34838
+ if (!call) return;
34839
+ editing.el.setAttribute("data-value", raw);
34840
+ editing.el.textContent = chipText(call.name, call.args);
34841
+ editing.el.title = raw;
34842
+ setEditing(null);
34843
+ commit();
34844
+ fieldRef.current?.focus();
34845
+ },
34846
+ [editing, chipText, commit]
34847
+ );
34848
+ const handleClick = useCallback50(
34849
+ (e) => {
34850
+ const target = e.target.closest(`.${PILL}, .${CHIP}`);
34851
+ if (!target) return;
34852
+ selectToken(target);
34853
+ if (!disabled && target.classList.contains(CHIP) && e.detail >= 2) {
34854
+ openEditor(target);
34855
+ }
34856
+ },
34857
+ [disabled, openEditor, selectToken]
34858
+ );
34859
+ const handleCopy = useCallback50(
34860
+ (e, cut = false) => {
34861
+ const sel = window.getSelection();
34862
+ if (!sel || sel.rangeCount === 0) return;
34863
+ const holder = document.createElement("div");
34864
+ holder.appendChild(sel.getRangeAt(0).cloneContents());
34865
+ const token = selected.current;
34866
+ const text = serialize(holder) || (token && fieldRef.current?.contains(token) && sel.containsNode(token, true) ? token.getAttribute("data-value") ?? "" : "");
34867
+ if (!text) return;
34868
+ e.preventDefault();
34869
+ e.clipboardData.setData("text/plain", text);
34870
+ if (cut && !disabled) {
34871
+ if (serialize(holder)) sel.getRangeAt(0).deleteContents();
34872
+ else token?.remove();
34873
+ commit();
34874
+ }
34875
+ },
34876
+ [commit, disabled]
34877
+ );
34878
+ const handlePaste = useCallback50(
34879
+ (e) => {
34880
+ e.preventDefault();
34881
+ const text = e.clipboardData.getData("text/plain");
34882
+ if (!text) return;
34883
+ document.execCommand("insertText", false, text);
34884
+ const next = serialize(fieldRef.current);
34885
+ fromSelf.current = false;
34886
+ onChange(next);
34887
+ },
34888
+ [onChange]
34889
+ );
34890
+ const handleKeyDown = useCallback50(
34891
+ (e) => {
34892
+ if (e.key !== "Enter" || !e.metaKey) return;
34893
+ const sel = window.getSelection();
34894
+ const node = sel?.anchorNode;
34895
+ const chip = node instanceof HTMLElement ? node.closest(`.${CHIP}`) : node?.parentElement?.closest(`.${CHIP}`) ?? null;
34896
+ if (chip) {
34897
+ e.preventDefault();
34898
+ openEditor(chip);
34899
+ }
34612
34900
  },
34613
- [commit, disabled, makePill, textBeforeCaret]
34901
+ [openEditor]
34614
34902
  );
34615
34903
  const handleDragOver = useCallback50(
34616
34904
  (e) => {
@@ -34637,42 +34925,244 @@ function ExpressionField({
34637
34925
  },
34638
34926
  [disabled, insert]
34639
34927
  );
34640
- const handlePaste = useCallback50((e) => {
34641
- e.preventDefault();
34642
- const text = e.clipboardData.getData("text/plain");
34643
- document.execCommand("insertText", false, text);
34644
- }, []);
34928
+ const handleBlur = useCallback50(() => {
34929
+ const el = fieldRef.current;
34930
+ if (!el) return;
34931
+ const next = serialize(el);
34932
+ if (next !== value) {
34933
+ fromSelf.current = true;
34934
+ onChange(next);
34935
+ }
34936
+ if (!editing) render(next);
34937
+ }, [editing, onChange, render, value]);
34645
34938
  return /* @__PURE__ */ jsxs141("div", { className: cn("ods-expression-field", className), style, children: [
34646
34939
  label && /* @__PURE__ */ jsx147("label", { className: "ods-expression-field__label", htmlFor: fieldId, children: label }),
34647
- /* @__PURE__ */ jsx147(
34648
- "div",
34649
- {
34650
- id: fieldId,
34651
- ref: fieldRef,
34652
- role: "textbox",
34653
- "aria-multiline": "true",
34654
- "aria-label": ariaLabel ?? (typeof label === "string" ? label : void 0),
34655
- "aria-describedby": helperText ? helperId : void 0,
34656
- "aria-disabled": disabled || void 0,
34657
- contentEditable: !disabled,
34658
- suppressContentEditableWarning: true,
34659
- "data-placeholder": placeholder,
34660
- className: cn(
34661
- "ods-expression-field__input",
34662
- disabled && "ods-expression-field__input--disabled"
34663
- ),
34664
- style: { minHeight: `${Math.max(1, rows) * 1.5 + 1}rem` },
34665
- onInput: commit,
34666
- onBlur: commit,
34667
- onDragOver: handleDragOver,
34668
- onDragLeave: handleDragLeave,
34669
- onDrop: handleDrop,
34670
- onPaste: handlePaste
34671
- }
34672
- ),
34940
+ /* @__PURE__ */ jsxs141("div", { className: "ods-expression-field__body", children: [
34941
+ /* @__PURE__ */ jsx147(
34942
+ "div",
34943
+ {
34944
+ id: fieldId,
34945
+ ref: fieldRef,
34946
+ role: "textbox",
34947
+ "aria-multiline": "true",
34948
+ "aria-label": ariaLabel ?? (typeof label === "string" ? label : void 0),
34949
+ "aria-describedby": helperText ? helperId : void 0,
34950
+ "aria-disabled": disabled || void 0,
34951
+ contentEditable: !disabled,
34952
+ suppressContentEditableWarning: true,
34953
+ "data-placeholder": placeholder,
34954
+ className: cn(
34955
+ "ods-expression-field__input",
34956
+ disabled && "ods-expression-field__input--disabled"
34957
+ ),
34958
+ style: { minHeight: `${Math.max(1, rows) * 1.5 + 1}rem` },
34959
+ onInput: commit,
34960
+ onBlur: handleBlur,
34961
+ onClick: handleClick,
34962
+ onKeyDown: handleKeyDown,
34963
+ onCopy: handleCopy,
34964
+ onCut: (e) => handleCopy(e, true),
34965
+ onPaste: handlePaste,
34966
+ onDragOver: handleDragOver,
34967
+ onDragLeave: handleDragLeave,
34968
+ onDrop: handleDrop
34969
+ }
34970
+ ),
34971
+ editing && /* @__PURE__ */ jsx147(
34972
+ CallEditor,
34973
+ {
34974
+ raw: editing.raw,
34975
+ rect: editing.rect,
34976
+ nameOf: callLabel,
34977
+ labelFor,
34978
+ resolveCall,
34979
+ onCancel: () => setEditing(null),
34980
+ onApply: applyEditor
34981
+ }
34982
+ )
34983
+ ] }),
34673
34984
  helperText && /* @__PURE__ */ jsx147("span", { id: helperId, className: "ods-expression-field__helper", children: helperText })
34674
34985
  ] });
34675
34986
  }
34987
+ function CallEditor({
34988
+ raw,
34989
+ rect,
34990
+ nameOf,
34991
+ labelFor,
34992
+ resolveCall,
34993
+ onApply,
34994
+ onCancel
34995
+ }) {
34996
+ const [draft, setDraft] = useState71(raw);
34997
+ const [path, setPath] = useState71([]);
34998
+ const [rawMode, setRawMode] = useState71(false);
34999
+ const panelRef = useRef50(null);
35000
+ useEffect56(() => {
35001
+ const onKey = (e) => {
35002
+ if (e.key === "Escape") onCancel();
35003
+ };
35004
+ document.addEventListener("keydown", onKey);
35005
+ return () => document.removeEventListener("keydown", onKey);
35006
+ }, [onCancel]);
35007
+ const current = callAt(draft, path);
35008
+ const signature = current ? resolveCall?.(current.name) : void 0;
35009
+ const names = signature?.args ?? [];
35010
+ const args = current ? current.args.length ? current.args : [""] : [];
35011
+ const crumbs = [];
35012
+ for (let i = 0; i <= path.length; i++) {
35013
+ const at = callAt(draft, path.slice(0, i));
35014
+ if (at) crumbs.push({ label: nameOf(at.name), path: path.slice(0, i) });
35015
+ }
35016
+ const setArg = (index, next) => setDraft((d) => replaceArgument(d, [...path, index], next));
35017
+ const dropInto = (index) => (e) => {
35018
+ const dropped = e.dataTransfer.getData("application/x-fx-insert");
35019
+ if (!dropped) return;
35020
+ e.preventDefault();
35021
+ e.stopPropagation();
35022
+ const kind = e.dataTransfer.getData("application/x-fx-type");
35023
+ const bare = dropped.startsWith("{{") && dropped.endsWith("}}") ? dropped.slice(2, -2).trim() : dropped;
35024
+ setDraft((d) => replaceArgument(d, [...path, index], bare));
35025
+ if (kind === "function" || parseCall(bare)) setPath((pth) => [...pth, index]);
35026
+ };
35027
+ return /* @__PURE__ */ jsxs141(
35028
+ "div",
35029
+ {
35030
+ ref: panelRef,
35031
+ className: "ods-expression-field__editor",
35032
+ style: { top: rect.top, left: rect.left, width: rect.width },
35033
+ role: "dialog",
35034
+ "aria-label": `Edit ${crumbs[0]?.label ?? "function"}`,
35035
+ children: [
35036
+ /* @__PURE__ */ jsxs141("div", { className: "ods-expression-field__editor-head", children: [
35037
+ /* @__PURE__ */ jsx147("nav", { className: "ods-expression-field__editor-crumbs", "aria-label": "Nested functions", children: crumbs.map((crumb, i) => /* @__PURE__ */ jsxs141("span", { children: [
35038
+ i > 0 && /* @__PURE__ */ jsx147("span", { "aria-hidden": "true", children: " \u203A " }),
35039
+ /* @__PURE__ */ jsx147(
35040
+ "button",
35041
+ {
35042
+ type: "button",
35043
+ disabled: i === crumbs.length - 1,
35044
+ onClick: () => setPath(crumb.path),
35045
+ children: crumb.label
35046
+ }
35047
+ )
35048
+ ] }, crumb.path.join(".") || "root")) }),
35049
+ /* @__PURE__ */ jsx147(
35050
+ "button",
35051
+ {
35052
+ type: "button",
35053
+ className: "ods-expression-field__editor-link",
35054
+ onClick: () => setRawMode((r) => !r),
35055
+ children: rawMode ? "Back to fields" : "Edit as text"
35056
+ }
35057
+ )
35058
+ ] }),
35059
+ rawMode ? /* @__PURE__ */ jsx147(
35060
+ "textarea",
35061
+ {
35062
+ className: "ods-expression-field__editor-raw",
35063
+ value: draft,
35064
+ rows: 3,
35065
+ "aria-label": "Expression",
35066
+ onChange: (e) => setDraft(e.target.value)
35067
+ }
35068
+ ) : /* @__PURE__ */ jsxs141("div", { className: "ods-expression-field__editor-rows", children: [
35069
+ args.map((arg, index) => {
35070
+ const kind = argumentKind(arg);
35071
+ const rowLabel = names[index] ?? `argument ${index + 1}`;
35072
+ const nested = kind === "call" ? parseCall(arg) : void 0;
35073
+ return (
35074
+ // eslint-disable-next-line react/no-array-index-key
35075
+ /* @__PURE__ */ jsxs141("div", { className: "ods-expression-field__editor-row", children: [
35076
+ /* @__PURE__ */ jsx147("span", { id: `arg-${index}-label`, children: rowLabel }),
35077
+ nested ? /* @__PURE__ */ jsxs141(
35078
+ "div",
35079
+ {
35080
+ className: "ods-expression-field__editor-nested",
35081
+ onDragOver: (e) => {
35082
+ if (e.dataTransfer.types.includes("application/x-fx-insert")) e.preventDefault();
35083
+ },
35084
+ onDrop: dropInto(index),
35085
+ children: [
35086
+ /* @__PURE__ */ jsx147("code", { children: argSummary(arg, nameOf) }),
35087
+ /* @__PURE__ */ jsxs141("span", { children: [
35088
+ /* @__PURE__ */ jsx147("button", { type: "button", onClick: () => setPath([...path, index]), children: "Open" }),
35089
+ /* @__PURE__ */ jsx147("button", { type: "button", onClick: () => setArg(index, ""), "aria-label": `Clear ${rowLabel}`, children: "Clear" })
35090
+ ] })
35091
+ ]
35092
+ }
35093
+ ) : kind === "reference" ? (
35094
+ // A reference is a value from somewhere else — named, never
35095
+ // its raw `ods://…` path, here as much as in the field.
35096
+ /* @__PURE__ */ jsxs141(
35097
+ "div",
35098
+ {
35099
+ className: "ods-expression-field__editor-ref",
35100
+ onDragOver: (e) => {
35101
+ if (e.dataTransfer.types.includes("application/x-fx-insert")) e.preventDefault();
35102
+ },
35103
+ onDrop: dropInto(index),
35104
+ children: [
35105
+ /* @__PURE__ */ jsx147(
35106
+ "span",
35107
+ {
35108
+ className: "ods-expression-field__pill",
35109
+ title: arg,
35110
+ "aria-labelledby": `arg-${index}-label`,
35111
+ children: labelFor(arg.startsWith("ods://") ? `{{${arg}}}` : arg)
35112
+ }
35113
+ ),
35114
+ /* @__PURE__ */ jsx147("button", { type: "button", onClick: () => setArg(index, ""), "aria-label": `Clear ${rowLabel}`, children: "Clear" })
35115
+ ]
35116
+ }
35117
+ )
35118
+ ) : /* @__PURE__ */ jsx147(
35119
+ "input",
35120
+ {
35121
+ "aria-labelledby": `arg-${index}-label`,
35122
+ value: arg,
35123
+ placeholder: 'a value, "text", or drag one in',
35124
+ "data-kind": kind,
35125
+ onChange: (e) => setArg(index, e.target.value),
35126
+ onDragOver: (e) => {
35127
+ if (e.dataTransfer.types.includes("application/x-fx-insert")) e.preventDefault();
35128
+ },
35129
+ onDrop: dropInto(index)
35130
+ }
35131
+ )
35132
+ ] }, index)
35133
+ );
35134
+ }),
35135
+ /* @__PURE__ */ jsx147(
35136
+ "button",
35137
+ {
35138
+ type: "button",
35139
+ className: "ods-expression-field__editor-link",
35140
+ onClick: () => setDraft((d) => {
35141
+ const at = callAt(d, path);
35142
+ if (!at) return d;
35143
+ const next = [...at.args, '""'];
35144
+ return path.length === 0 ? formatCall(at.name, next) : replaceArgument(d, path, `${at.name}(${next.join(", ")})`);
35145
+ }),
35146
+ children: "+ argument"
35147
+ }
35148
+ )
35149
+ ] }),
35150
+ /* @__PURE__ */ jsxs141("div", { className: "ods-expression-field__editor-foot", children: [
35151
+ /* @__PURE__ */ jsx147("button", { type: "button", onClick: onCancel, children: "Cancel" }),
35152
+ /* @__PURE__ */ jsx147(
35153
+ "button",
35154
+ {
35155
+ type: "button",
35156
+ className: "ods-expression-field__editor-primary",
35157
+ onClick: () => onApply(draft),
35158
+ children: "Apply"
35159
+ }
35160
+ )
35161
+ ] })
35162
+ ]
35163
+ }
35164
+ );
35165
+ }
34676
35166
 
34677
35167
  // src/components/TopBar/TopBar.tsx
34678
35168
  import { jsx as jsx148, jsxs as jsxs142 } from "react/jsx-runtime";
@@ -34761,7 +35251,7 @@ import {
34761
35251
  forwardRef as forwardRef97,
34762
35252
  useCallback as useCallback51,
34763
35253
  useId as useId62,
34764
- useState as useState71
35254
+ useState as useState72
34765
35255
  } from "react";
34766
35256
  import {
34767
35257
  BuildToolIcon,
@@ -34807,7 +35297,7 @@ var TraceStep = forwardRef97(
34807
35297
  const baseId = providedId ?? `ods-trace-step-${reactId}`;
34808
35298
  const headId = `${baseId}-head`;
34809
35299
  const contentId = `${baseId}-content`;
34810
- const [uncontrolledOpen, setUncontrolledOpen] = useState71(defaultOpen);
35300
+ const [uncontrolledOpen, setUncontrolledOpen] = useState72(defaultOpen);
34811
35301
  const isControlled = controlledOpen !== void 0;
34812
35302
  const open = isControlled ? controlledOpen : uncontrolledOpen;
34813
35303
  const canExpand = !!children;
@@ -35166,7 +35656,7 @@ var UserCard = forwardRef99(function UserCard2({
35166
35656
  UserCard.displayName = "UserCard";
35167
35657
 
35168
35658
  // src/components/WorkflowEditor/WorkflowEditor.tsx
35169
- import { useCallback as useCallback53, useMemo as useMemo39, useState as useState72 } from "react";
35659
+ import { useCallback as useCallback53, useMemo as useMemo39, useState as useState73 } from "react";
35170
35660
 
35171
35661
  // src/hooks/useWorkflow.ts
35172
35662
  import { useCallback as useCallback52, useMemo as useMemo38, useReducer } from "react";
@@ -35934,10 +36424,10 @@ function WorkflowEditor({
35934
36424
  }) {
35935
36425
  const wf = useWorkflow(options2);
35936
36426
  const { workflow, dispatch, canUndo, canRedo, undo, redo } = wf;
35937
- const [uncontrolledLock, setUncontrolledLock] = useState72(false);
35938
- const [uncontrolledDrawer, setUncontrolledDrawer] = useState72(!!drawer);
35939
- const [uncontrolledHistory, setUncontrolledHistory] = useState72(false);
35940
- const [uncontrolledDebug, setUncontrolledDebug] = useState72(false);
36427
+ const [uncontrolledLock, setUncontrolledLock] = useState73(false);
36428
+ const [uncontrolledDrawer, setUncontrolledDrawer] = useState73(!!drawer);
36429
+ const [uncontrolledHistory, setUncontrolledHistory] = useState73(false);
36430
+ const [uncontrolledDebug, setUncontrolledDebug] = useState73(false);
35941
36431
  const isLockMode = controlledLockMode ?? uncontrolledLock;
35942
36432
  const isDrawerOpen = controlledDrawerOpen ?? uncontrolledDrawer;
35943
36433
  const isHistoryOpen = controlledHistoryOpen ?? uncontrolledHistory;
@@ -36028,7 +36518,7 @@ function WorkflowEditor({
36028
36518
  },
36029
36519
  [wf]
36030
36520
  );
36031
- const [canvasSize, setCanvasSize] = useState72({ w: 0, h: 0 });
36521
+ const [canvasSize, setCanvasSize] = useState73({ w: 0, h: 0 });
36032
36522
  const handleCanvasRef = useCallback53((node) => {
36033
36523
  if (!node) return;
36034
36524
  setCanvasSize({ w: node.clientWidth, h: node.clientHeight });
@@ -36278,7 +36768,7 @@ import {
36278
36768
  } from "@octaviaflow/icons";
36279
36769
 
36280
36770
  // src/hooks/useWorkflowRuntime.ts
36281
- import { useEffect as useEffect57, useState as useState73 } from "react";
36771
+ import { useEffect as useEffect57, useState as useState74 } from "react";
36282
36772
  function toMs2(t) {
36283
36773
  if (t == null) return null;
36284
36774
  return typeof t === "number" ? t : t.getTime();
@@ -36292,7 +36782,7 @@ function formatWorkflowRuntime(ms) {
36292
36782
  return h > 0 ? `${h}:${pad2(m)}:${pad2(sec)}` : `${pad2(m)}:${pad2(sec)}`;
36293
36783
  }
36294
36784
  function useWorkflowRuntime(startedAt, active) {
36295
- const [, force] = useState73(0);
36785
+ const [, force] = useState74(0);
36296
36786
  useEffect57(() => {
36297
36787
  if (!active || startedAt == null) return;
36298
36788
  if (typeof window === "undefined") return;
@@ -36673,10 +37163,10 @@ function WorkflowHeaderExpanded({
36673
37163
  }
36674
37164
 
36675
37165
  // src/hooks/useBanner.ts
36676
- import { useCallback as useCallback54, useEffect as useEffect58, useRef as useRef51, useState as useState74 } from "react";
37166
+ import { useCallback as useCallback54, useEffect as useEffect58, useRef as useRef51, useState as useState75 } from "react";
36677
37167
  function useBanner(options2 = {}) {
36678
37168
  const { defaultOpen = false, autoDismissAfter, onDismiss } = options2;
36679
- const [open, setOpen] = useState74(defaultOpen);
37169
+ const [open, setOpen] = useState75(defaultOpen);
36680
37170
  const timerRef = useRef51(null);
36681
37171
  const onDismissRef = useRef51(onDismiss);
36682
37172
  useEffect58(() => {
@@ -36721,7 +37211,7 @@ function useBanner(options2 = {}) {
36721
37211
  }
36722
37212
 
36723
37213
  // src/hooks/useCallout.ts
36724
- import { useCallback as useCallback55, useEffect as useEffect59, useState as useState75 } from "react";
37214
+ import { useCallback as useCallback55, useEffect as useEffect59, useState as useState76 } from "react";
36725
37215
  var STORAGE_PREFIX = "ods:callout:dismissed:";
36726
37216
  function readPersisted(key) {
36727
37217
  if (!key || typeof window === "undefined") return false;
@@ -36744,7 +37234,7 @@ function writePersisted(key, value) {
36744
37234
  }
36745
37235
  function useCallout(options2 = {}) {
36746
37236
  const { defaultOpen = true, persistKey, onDismiss } = options2;
36747
- const [open, setOpen] = useState75(defaultOpen);
37237
+ const [open, setOpen] = useState76(defaultOpen);
36748
37238
  useEffect59(() => {
36749
37239
  if (persistKey && readPersisted(persistKey)) {
36750
37240
  setOpen(false);
@@ -36845,7 +37335,7 @@ function breakpointMinWidth(name) {
36845
37335
  }
36846
37336
 
36847
37337
  // src/hooks/useRating.ts
36848
- import { useCallback as useCallback56, useState as useState76 } from "react";
37338
+ import { useCallback as useCallback56, useState as useState77 } from "react";
36849
37339
  function useRating({
36850
37340
  value: controlled,
36851
37341
  defaultValue = 0,
@@ -36853,7 +37343,7 @@ function useRating({
36853
37343
  step = 1,
36854
37344
  onChange
36855
37345
  } = {}) {
36856
- const [internal, setInternal] = useState76(defaultValue);
37346
+ const [internal, setInternal] = useState77(defaultValue);
36857
37347
  const value = controlled ?? internal;
36858
37348
  const isControlled = controlled !== void 0;
36859
37349
  const clamp2 = useCallback56(
@@ -36882,7 +37372,7 @@ function useBinaryRating({
36882
37372
  defaultValue = null,
36883
37373
  onChange
36884
37374
  } = {}) {
36885
- const [internal, setInternal] = useState76(defaultValue);
37375
+ const [internal, setInternal] = useState77(defaultValue);
36886
37376
  const value = controlled ?? internal;
36887
37377
  const isControlled = controlled !== void 0;
36888
37378
  const set = useCallback56(
@@ -36904,13 +37394,13 @@ function useBinaryRating({
36904
37394
  }
36905
37395
 
36906
37396
  // src/hooks/useTimeline.ts
36907
- import { useCallback as useCallback57, useState as useState77 } from "react";
37397
+ import { useCallback as useCallback57, useState as useState78 } from "react";
36908
37398
  function useTimeline({
36909
37399
  items: controlled,
36910
37400
  defaultItems = [],
36911
37401
  onChange
36912
37402
  } = {}) {
36913
- const [internal, setInternal] = useState77(defaultItems);
37403
+ const [internal, setInternal] = useState78(defaultItems);
36914
37404
  const items = controlled ?? internal;
36915
37405
  const isControlled = controlled !== void 0;
36916
37406
  const commit = useCallback57(
@@ -36970,7 +37460,7 @@ function useTimeline({
36970
37460
  }
36971
37461
 
36972
37462
  // src/hooks/useTraceTimeline.ts
36973
- import { useCallback as useCallback58, useRef as useRef52, useState as useState78 } from "react";
37463
+ import { useCallback as useCallback58, useRef as useRef52, useState as useState79 } from "react";
36974
37464
  var GLOBAL_SEQ = 0;
36975
37465
  var generateId = () => `ods-trace-${Date.now().toString(36)}-${(GLOBAL_SEQ++).toString(36)}`;
36976
37466
  function useTraceTimeline({
@@ -36978,7 +37468,7 @@ function useTraceTimeline({
36978
37468
  defaultSteps = [],
36979
37469
  onChange
36980
37470
  } = {}) {
36981
- const [internal, setInternal] = useState78(defaultSteps);
37471
+ const [internal, setInternal] = useState79(defaultSteps);
36982
37472
  const steps = controlled ?? internal;
36983
37473
  const isControlled = controlled !== void 0;
36984
37474
  const stepsRef = useRef52(steps);
@@ -38141,7 +38631,7 @@ DeliveryFrequencyControl.displayName = "DeliveryFrequencyControl";
38141
38631
 
38142
38632
  // src/notifications/components/EventCatalogPicker/EventCatalogPicker.tsx
38143
38633
  import { SearchIcon as SearchIcon16 } from "@octaviaflow/icons";
38144
- import { useId as useId67, useMemo as useMemo41, useState as useState79 } from "react";
38634
+ import { useId as useId67, useMemo as useMemo41, useState as useState80 } from "react";
38145
38635
 
38146
38636
  // src/notifications/components/meta.tsx
38147
38637
  import {
@@ -38247,8 +38737,8 @@ function EventCatalogPicker({
38247
38737
  }) {
38248
38738
  const reactId = useId67();
38249
38739
  const baseId = id ?? `${reactId}-events`;
38250
- const [query, setQuery] = useState79("");
38251
- const [collapsed, setCollapsed] = useState79({});
38740
+ const [query, setQuery] = useState80("");
38741
+ const [collapsed, setCollapsed] = useState80({});
38252
38742
  const selected = useMemo41(() => new Set(value), [value]);
38253
38743
  const filtered = useMemo41(() => {
38254
38744
  const q2 = query.trim().toLowerCase();
@@ -38394,7 +38884,7 @@ EventCatalogPicker.displayName = "EventCatalogPicker";
38394
38884
 
38395
38885
  // src/notifications/components/NotificationBell/NotificationBell.tsx
38396
38886
  import { NotificationIcon as NotificationIcon2 } from "@octaviaflow/icons";
38397
- import { useCallback as useCallback59, useState as useState81 } from "react";
38887
+ import { useCallback as useCallback59, useState as useState82 } from "react";
38398
38888
 
38399
38889
  // src/notifications/engine/inbox.ts
38400
38890
  function resolveInboxAction(item, catalog) {
@@ -38549,7 +39039,7 @@ NotificationDetailDialog.displayName = "NotificationDetailDialog";
38549
39039
 
38550
39040
  // src/notifications/components/NotificationInbox/NotificationInbox.tsx
38551
39041
  import { ArchiveIcon as ArchiveIcon3, CheckmarkIcon as CheckmarkIcon16, NotificationOffIcon } from "@octaviaflow/icons";
38552
- import { useMemo as useMemo42, useState as useState80 } from "react";
39042
+ import { useMemo as useMemo42, useState as useState81 } from "react";
38553
39043
  import { Fragment as Fragment66, jsx as jsx161, jsxs as jsxs153 } from "react/jsx-runtime";
38554
39044
  var FILTER_OPTIONS = [
38555
39045
  { value: "all", label: "All" },
@@ -38576,13 +39066,13 @@ function NotificationInbox({
38576
39066
  now: now2,
38577
39067
  className
38578
39068
  }) {
38579
- const [internalFilter, setInternalFilter] = useState80(defaultFilter2);
39069
+ const [internalFilter, setInternalFilter] = useState81(defaultFilter2);
38580
39070
  const activeFilter = filter ?? internalFilter;
38581
39071
  const setFilter = (f) => {
38582
39072
  if (filter === void 0) setInternalFilter(f);
38583
39073
  onFilterChange?.(f);
38584
39074
  };
38585
- const [detailItem, setDetailItem] = useState80(null);
39075
+ const [detailItem, setDetailItem] = useState81(null);
38586
39076
  const unread = countUnread(items);
38587
39077
  const visible = useMemo42(() => {
38588
39078
  const list = filterInbox(items, activeFilter);
@@ -38749,7 +39239,7 @@ function NotificationBell({
38749
39239
  const unread = unreadCount ?? countUnread(items);
38750
39240
  const hasUnread = unread > 0;
38751
39241
  const countLabel = unread > 99 ? "99+" : String(unread);
38752
- const [internalOpen, setInternalOpen] = useState81(false);
39242
+ const [internalOpen, setInternalOpen] = useState82(false);
38753
39243
  const isOpen = open ?? internalOpen;
38754
39244
  const setOpen = useCallback59(
38755
39245
  (next) => {
@@ -38758,7 +39248,7 @@ function NotificationBell({
38758
39248
  },
38759
39249
  [open, onOpenChange]
38760
39250
  );
38761
- const [detailItem, setDetailItem] = useState81(null);
39251
+ const [detailItem, setDetailItem] = useState82(null);
38762
39252
  const handleOpenItem = (item, action) => {
38763
39253
  setOpen(false);
38764
39254
  if (action.kind === "modal") setDetailItem(item);
@@ -38958,7 +39448,7 @@ NotificationChannelCard.displayName = "NotificationChannelCard";
38958
39448
 
38959
39449
  // src/notifications/components/NotificationChannelDialog/NotificationChannelDialog.tsx
38960
39450
  import { SendIcon as SendIcon2 } from "@octaviaflow/icons";
38961
- import { useEffect as useEffect61, useMemo as useMemo43, useState as useState83 } from "react";
39451
+ import { useEffect as useEffect61, useMemo as useMemo43, useState as useState84 } from "react";
38962
39452
 
38963
39453
  // src/notifications/engine/channels.ts
38964
39454
  var CONNECTED_CHANNEL_TYPES = [
@@ -39064,7 +39554,7 @@ function draftFromChannel(channel, fallbackType = "slack") {
39064
39554
 
39065
39555
  // src/notifications/components/NotificationChannelForm/NotificationChannelForm.tsx
39066
39556
  import { AddIcon as AddIcon6, CheckmarkFilledIcon as CheckmarkFilledIcon3, LaunchIcon as LaunchIcon2 } from "@octaviaflow/icons";
39067
- import { useId as useId68, useState as useState82 } from "react";
39557
+ import { useId as useId68, useState as useState83 } from "react";
39068
39558
  import { jsx as jsx164, jsxs as jsxs156 } from "react/jsx-runtime";
39069
39559
  var DEFAULT_TYPES = [
39070
39560
  "slack",
@@ -39108,7 +39598,7 @@ function NotificationChannelForm({
39108
39598
  const baseId = id ?? `${reactId}-channel`;
39109
39599
  const typeMeta = CHANNEL_TYPE_META[value.type];
39110
39600
  const patch = (next) => onChange({ ...value, ...next });
39111
- const [touched, setTouched] = useState82({});
39601
+ const [touched, setTouched] = useState83({});
39112
39602
  const touch = (field) => setTouched((t) => t[field] ? t : { ...t, [field]: true });
39113
39603
  const errorFor = (field) => errors?.[field] && (showAllErrors || touched[field]) ? errors[field] : void 0;
39114
39604
  const errorText = (field) => {
@@ -39124,7 +39614,7 @@ function NotificationChannelForm({
39124
39614
  const targetLabel = targetOptions?.find((o) => o.value === value.target)?.label;
39125
39615
  const summary = describeChannelDraft(value, instances, targetLabel);
39126
39616
  const suggestedLabel = suggestChannelLabel(value, instances, targetLabel);
39127
- const [recipientDraft, setRecipientDraft] = useState82("");
39617
+ const [recipientDraft, setRecipientDraft] = useState83("");
39128
39618
  const recipients = value.recipients ?? [];
39129
39619
  const commitRecipient = () => {
39130
39620
  const parts = recipientDraft.split(/[,\s;]+/).map((s) => s.trim()).filter(Boolean);
@@ -39409,13 +39899,13 @@ function NotificationChannelDialog({
39409
39899
  className
39410
39900
  }) {
39411
39901
  const editing = Boolean(channel?.id);
39412
- const [draft, setDraft] = useState83(
39902
+ const [draft, setDraft] = useState84(
39413
39903
  () => draftFromChannel(channel, initialType)
39414
39904
  );
39415
- const [submitted, setSubmitted] = useState83(false);
39416
- const [testing, setTesting] = useState83(false);
39417
- const [testResult, setTestResult] = useState83(null);
39418
- const [submitError, setSubmitError] = useState83(null);
39905
+ const [submitted, setSubmitted] = useState84(false);
39906
+ const [testing, setTesting] = useState84(false);
39907
+ const [testResult, setTestResult] = useState84(null);
39908
+ const [submitError, setSubmitError] = useState84(null);
39419
39909
  useEffect61(() => {
39420
39910
  if (!open) return;
39421
39911
  setDraft(draftFromChannel(channel, initialType));
@@ -39598,7 +40088,7 @@ NotificationChannelList.displayName = "NotificationChannelList";
39598
40088
 
39599
40089
  // src/notifications/components/NotificationPreferenceMatrix/NotificationPreferenceMatrix.tsx
39600
40090
  import { LockedIcon as LockedIcon2 } from "@octaviaflow/icons";
39601
- import { useId as useId69, useMemo as useMemo44, useState as useState84 } from "react";
40091
+ import { useId as useId69, useMemo as useMemo44, useState as useState85 } from "react";
39602
40092
 
39603
40093
  // src/notifications/engine/preferences.ts
39604
40094
  var CRITICAL_CHANNELS = ["in_app", "email"];
@@ -39692,7 +40182,7 @@ function NotificationPreferenceMatrix({
39692
40182
  () => filterCatalog(catalog, personalAudiences(isAdmin)),
39693
40183
  [catalog, isAdmin]
39694
40184
  );
39695
- const [notice, setNotice] = useState84(null);
40185
+ const [notice, setNotice] = useState85(null);
39696
40186
  const toggle = (eventKey, channel, on) => {
39697
40187
  const r = setPreference(value, catalog, eventKey, channel, on);
39698
40188
  if (r.blocked) {