@flytedan/flytebot-design-system 0.33.0 → 0.34.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.
package/dist/index.js CHANGED
@@ -2731,10 +2731,10 @@ function DataTable({
2731
2731
  }
2732
2732
 
2733
2733
  // src/components/data/FilterBar.tsx
2734
- import * as React24 from "react";
2734
+ import * as React25 from "react";
2735
2735
 
2736
2736
  // src/components/forms/NumberInput.tsx
2737
- import * as React21 from "react";
2737
+ import * as React22 from "react";
2738
2738
 
2739
2739
  // src/components/forms/field.tsx
2740
2740
  import * as React20 from "react";
@@ -2755,6 +2755,16 @@ function useTriggerLabelling(label, id) {
2755
2755
  return { fieldId, labelId, ariaLabelledBy: label ? labelId + " " + fieldId : void 0 };
2756
2756
  }
2757
2757
 
2758
+ // src/components/useLatest.ts
2759
+ import * as React21 from "react";
2760
+ function useLatest(value) {
2761
+ const ref = React21.useRef(value);
2762
+ React21.useInsertionEffect(() => {
2763
+ ref.current = value;
2764
+ });
2765
+ return ref;
2766
+ }
2767
+
2758
2768
  // src/components/forms/NumberInput.tsx
2759
2769
  import { jsx as jsx42, jsxs as jsxs36 } from "react/jsx-runtime";
2760
2770
  var HOLD_DELAY_MS = 400;
@@ -2832,24 +2842,22 @@ function NumberInput({
2832
2842
  const n = typeof v === "number" ? v : parseNumberText(v);
2833
2843
  return n == null || Number.isNaN(n) ? null : n;
2834
2844
  };
2835
- const [text, setText] = React21.useState(() => toText(value));
2836
- const [editing, setEditing] = React21.useState(false);
2845
+ const [text, setText] = React22.useState(() => toText(value));
2846
+ const [editing, setEditing] = React22.useState(false);
2837
2847
  const parsed = parseNumberText(text);
2838
2848
  const invalidMessage = parsed == null ? null : Number.isNaN(parsed) ? "Enter a number" : integer && !Number.isInteger(parsed) ? "Enter a whole number" : parsed < lo ? "Must be \u2265 " + lo : parsed > hi ? "Must be \u2264 " + hi : null;
2839
2849
  const valid = invalidMessage == null;
2840
- const committedRef = React21.useRef(toNumber(value));
2841
- const onChangeRef = React21.useRef(onChange);
2842
- onChangeRef.current = onChange;
2843
- const onValidityChangeRef = React21.useRef(onValidityChange);
2844
- onValidityChangeRef.current = onValidityChange;
2845
- const focusedRef = React21.useRef(false);
2846
- React21.useEffect(() => {
2850
+ const committedRef = React22.useRef(toNumber(value));
2851
+ const onChangeRef = useLatest(onChange);
2852
+ const onValidityChangeRef = useLatest(onValidityChange);
2853
+ const focusedRef = React22.useRef(false);
2854
+ React22.useEffect(() => {
2847
2855
  committedRef.current = toNumber(value);
2848
2856
  if (!focusedRef.current) setText(toText(value));
2849
2857
  }, [value]);
2850
- React21.useLayoutEffect(() => {
2858
+ React22.useLayoutEffect(() => {
2851
2859
  onValidityChangeRef.current?.(valid);
2852
- }, [valid]);
2860
+ }, [valid, onValidityChangeRef]);
2853
2861
  const fire = (n) => {
2854
2862
  committedRef.current = n ?? null;
2855
2863
  onChangeRef.current?.({ target: { value: n } });
@@ -2862,9 +2870,8 @@ function NumberInput({
2862
2870
  fire(next);
2863
2871
  return true;
2864
2872
  };
2865
- const bumpRef = React21.useRef(bump);
2866
- bumpRef.current = bump;
2867
- const holdTimer = React21.useRef(null);
2873
+ const bumpRef = useLatest(bump);
2874
+ const holdTimer = React22.useRef(null);
2868
2875
  const release = () => {
2869
2876
  if (holdTimer.current) clearTimeout(holdTimer.current);
2870
2877
  holdTimer.current = null;
@@ -2883,7 +2890,7 @@ function NumberInput({
2883
2890
  };
2884
2891
  holdTimer.current = setTimeout(tick, HOLD_DELAY_MS);
2885
2892
  };
2886
- React21.useEffect(() => () => {
2893
+ React22.useEffect(() => () => {
2887
2894
  if (holdTimer.current) clearTimeout(holdTimer.current);
2888
2895
  }, []);
2889
2896
  const current2 = committedRef.current;
@@ -2986,7 +2993,7 @@ function NumberInput({
2986
2993
  }
2987
2994
 
2988
2995
  // src/components/forms/DatePicker.tsx
2989
- import * as React22 from "react";
2996
+ import * as React23 from "react";
2990
2997
  import { jsx as jsx43, jsxs as jsxs37 } from "react/jsx-runtime";
2991
2998
  var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
2992
2999
  var DOW = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
@@ -3004,10 +3011,10 @@ function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3004
3011
  const today = /* @__PURE__ */ new Date();
3005
3012
  const sel = range ? value || {} : { start: value, end: value };
3006
3013
  const anchor = parse(sel.start) || parse(initialMonth) || today;
3007
- const [vy, setVy] = React22.useState(anchor.getFullYear());
3008
- const [vm, setVm] = React22.useState(anchor.getMonth());
3009
- const [mode2, setMode] = React22.useState("days");
3010
- const [hover, setHover] = React22.useState(null);
3014
+ const [vy, setVy] = React23.useState(anchor.getFullYear());
3015
+ const [vm, setVm] = React23.useState(anchor.getMonth());
3016
+ const [mode2, setMode] = React23.useState("days");
3017
+ const [hover, setHover] = React23.useState(null);
3011
3018
  const s = parse(sel.start), e = parse(sel.end);
3012
3019
  const hoverEnd = range && s && !e && hover ? parse(hover) : null;
3013
3020
  const inRange = (d) => {
@@ -3123,9 +3130,9 @@ function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3123
3130
  }
3124
3131
  function DatePicker({ label, help, error, required = false, disabled = false, range = false, value, onChange, placeholder, id, className = "", style, ...rest }) {
3125
3132
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
3126
- const [open, setOpen] = React22.useState(false);
3127
- const rootRef = React22.useRef(null);
3128
- const boxRef = React22.useRef(null);
3133
+ const [open, setOpen] = React23.useState(false);
3134
+ const rootRef = React23.useRef(null);
3135
+ const boxRef = React23.useRef(null);
3129
3136
  const display = range ? value && value.start ? fmt(value.start) + (value.end ? " \u2192 " + fmt(value.end) : " \u2192 \u2026") : "" : fmt(value);
3130
3137
  const toggle = () => {
3131
3138
  if (!disabled) setOpen(!open);
@@ -3203,9 +3210,9 @@ function Textarea({ label, help, error, required = false, rows = 4, disabled = f
3203
3210
  }
3204
3211
 
3205
3212
  // src/components/forms/listbox.ts
3206
- import * as React23 from "react";
3213
+ import * as React24 from "react";
3207
3214
  function useListboxNav({ count, onSelect }) {
3208
- const [active, setActive] = React23.useState(-1);
3215
+ const [active, setActive] = React24.useState(-1);
3209
3216
  return {
3210
3217
  active,
3211
3218
  setActive,
@@ -3218,7 +3225,7 @@ function useListboxNav({ count, onSelect }) {
3218
3225
  };
3219
3226
  }
3220
3227
  function useListboxScrollIntoView(active, listRef, enabled) {
3221
- React23.useEffect(() => {
3228
+ React24.useEffect(() => {
3222
3229
  if (!enabled || active < 0 || !listRef.current) return;
3223
3230
  const list = listRef.current;
3224
3231
  const el = list.querySelector('[data-i="' + active + '"]');
@@ -3405,40 +3412,40 @@ function MultiSelectPanel({
3405
3412
  onClose
3406
3413
  }) {
3407
3414
  const allowedOps = operatorsFor(field);
3408
- const [draftOperator, setDraftOperator] = React24.useState(value?.operator ?? allowedOps[0]);
3415
+ const [draftOperator, setDraftOperator] = React25.useState(value?.operator ?? allowedOps[0]);
3409
3416
  const operator = allowedOps.length === 1 ? allowedOps[0] : value?.operator ?? draftOperator;
3410
3417
  const selected = value?.values ?? [];
3411
- const selectedRef = React24.useRef(selected);
3412
- selectedRef.current = selected;
3413
- const operatorRef = React24.useRef(operator);
3414
- operatorRef.current = operator;
3415
- const mountedRef = React24.useRef(true);
3416
- React24.useEffect(() => {
3418
+ const selectedRef = useLatest(selected);
3419
+ const operatorRef = useLatest(operator);
3420
+ const mountedRef = React25.useRef(true);
3421
+ React25.useEffect(() => {
3417
3422
  mountedRef.current = true;
3418
3423
  return () => {
3419
3424
  mountedRef.current = false;
3420
3425
  };
3421
3426
  }, []);
3422
- const commit = (nextValues, nextOperator = operator) => onCommit({ operator: nextOperator, values: nextValues });
3427
+ const commit = (nextValues, nextOperator = operatorRef.current) => {
3428
+ setDraftOperator(nextOperator);
3429
+ return onCommit({ operator: nextOperator, values: nextValues });
3430
+ };
3423
3431
  const setOperator = (op) => {
3424
- setDraftOperator(op);
3425
3432
  if (selectedRef.current.length > 0) commit(selectedRef.current, op);
3433
+ else setDraftOperator(op);
3426
3434
  };
3427
- const [mode2, setMode] = React24.useState("search");
3428
- const [query, setQuery] = React24.useState("");
3429
- const [asyncState, setAsyncState] = React24.useState(
3435
+ const [mode2, setMode] = React25.useState("search");
3436
+ const [query, setQuery] = React25.useState("");
3437
+ const [asyncState, setAsyncState] = React25.useState(
3430
3438
  { options: null, loading: !!field.loadOptions, error: false }
3431
3439
  );
3432
- const [retryTick, setRetryTick] = React24.useState(0);
3433
- const inputRef = React24.useRef(null);
3434
- const listRef = React24.useRef(null);
3435
- const listId = React24.useId();
3436
- const loadOptionsRef = React24.useRef(field.loadOptions);
3437
- loadOptionsRef.current = field.loadOptions;
3438
- React24.useEffect(() => {
3440
+ const [retryTick, setRetryTick] = React25.useState(0);
3441
+ const inputRef = React25.useRef(null);
3442
+ const listRef = React25.useRef(null);
3443
+ const listId = React25.useId();
3444
+ const loadOptionsRef = useLatest(field.loadOptions);
3445
+ React25.useEffect(() => {
3439
3446
  inputRef.current?.focus();
3440
3447
  }, []);
3441
- React24.useEffect(() => {
3448
+ React25.useEffect(() => {
3442
3449
  const load = loadOptionsRef.current;
3443
3450
  if (!load) return;
3444
3451
  const controller = new AbortController();
@@ -3456,7 +3463,7 @@ function MultiSelectPanel({
3456
3463
  clearTimeout(t);
3457
3464
  controller.abort();
3458
3465
  };
3459
- }, [query, retryTick]);
3466
+ }, [query, retryTick, loadOptionsRef]);
3460
3467
  const options = field.loadOptions ? asyncState.options ?? [] : (field.options ?? []).filter((o) => o.label.toLowerCase().includes(query.toLowerCase()));
3461
3468
  const showLoading = !!field.loadOptions && asyncState.loading;
3462
3469
  const showError = !!field.loadOptions && asyncState.error;
@@ -3471,17 +3478,17 @@ function MultiSelectPanel({
3471
3478
  };
3472
3479
  const nav = useListboxNav({ count: options.length, onSelect: (i) => toggle(options[i]) });
3473
3480
  useListboxScrollIntoView(nav.active, listRef, hasListbox);
3474
- React24.useEffect(() => {
3481
+ React25.useEffect(() => {
3475
3482
  nav.setActive(0);
3476
3483
  }, [query, options.length]);
3477
3484
  const activeId = hasListbox && options[nav.active] ? listId + "-opt-" + nav.active : void 0;
3478
3485
  const liveMessage = showLoading ? "Loading\u2026" : showError ? "Couldn't load options." : options.length === 0 ? "No matches" : atLimit ? "Limit reached: at most " + field.maxValues + " values" : "";
3479
- const [pasteText, setPasteText] = React24.useState("");
3480
- const [pasteLoading, setPasteLoading] = React24.useState(false);
3481
- const [unmatchedTokens, setUnmatchedTokens] = React24.useState([]);
3482
- const [pasteError, setPasteError] = React24.useState(null);
3483
- const pasteWrapRef = React24.useRef(null);
3484
- React24.useEffect(() => {
3486
+ const [pasteText, setPasteText] = React25.useState("");
3487
+ const [pasteLoading, setPasteLoading] = React25.useState(false);
3488
+ const [unmatchedTokens, setUnmatchedTokens] = React25.useState([]);
3489
+ const [pasteError, setPasteError] = React25.useState(null);
3490
+ const pasteWrapRef = React25.useRef(null);
3491
+ React25.useEffect(() => {
3485
3492
  if (mode2 === "paste") pasteWrapRef.current?.querySelector("textarea")?.focus();
3486
3493
  }, [mode2]);
3487
3494
  const doResolve = () => {
@@ -3500,7 +3507,7 @@ function MultiSelectPanel({
3500
3507
  setPasteError("This filter allows at most " + field.maxValues + " values; adding this list would make " + merged.size + ".");
3501
3508
  return;
3502
3509
  }
3503
- const applied = commit(Array.from(merged.values()), operatorRef.current);
3510
+ const applied = commit(Array.from(merged.values()));
3504
3511
  if (!applied.ok) {
3505
3512
  setPasteError(applied.reason);
3506
3513
  return;
@@ -3642,10 +3649,9 @@ function MultiSelectPanel({
3642
3649
  );
3643
3650
  }
3644
3651
  function useTextValidity(onFlip) {
3645
- const invalidRef = React24.useRef(/* @__PURE__ */ new Set());
3646
- const [, rerender] = React24.useReducer((n) => n + 1, 0);
3647
- const onFlipRef = React24.useRef(onFlip);
3648
- onFlipRef.current = onFlip;
3652
+ const invalidRef = React25.useRef(/* @__PURE__ */ new Set());
3653
+ const [, rerender] = React25.useReducer((n) => n + 1, 0);
3654
+ const onFlipRef = useLatest(onFlip);
3649
3655
  const record = (name, valid) => {
3650
3656
  const invalid = invalidRef.current;
3651
3657
  if (valid !== invalid.has(name)) return false;
@@ -3662,6 +3668,12 @@ function useTextValidity(onFlip) {
3662
3668
  /** For an input that is about to unmount: it has nothing left to show. */
3663
3669
  forget: (name) => {
3664
3670
  record(name, true);
3671
+ },
3672
+ /** For a panel that is about to remount every input: each reports afresh on mount. */
3673
+ reset: () => {
3674
+ if (invalidRef.current.size === 0) return;
3675
+ invalidRef.current.clear();
3676
+ rerender();
3665
3677
  }
3666
3678
  };
3667
3679
  }
@@ -3669,6 +3681,26 @@ function applicable(field, candidate, textValid) {
3669
3681
  if (!textValid || candidate === void 0) return void 0;
3670
3682
  return validateFilterValue(field, candidate).ok ? candidate : void 0;
3671
3683
  }
3684
+ function sameValue(a, b) {
3685
+ if (a === b) return true;
3686
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
3687
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
3688
+ const left = a;
3689
+ const right = b;
3690
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)])) {
3691
+ if (!sameValue(left[key], right[key])) return false;
3692
+ }
3693
+ return true;
3694
+ }
3695
+ function useReseedFromProps(value, applies, reseed) {
3696
+ const seen = React25.useRef(value);
3697
+ React25.useLayoutEffect(() => {
3698
+ if (seen.current === value) return;
3699
+ seen.current = value;
3700
+ if (!sameValue(value, applies())) reseed(value);
3701
+ }, [value, applies, reseed]);
3702
+ }
3703
+ var rangeDraft = (value) => ({ min: value?.min, max: value?.max });
3672
3704
  function RangePanel({
3673
3705
  field,
3674
3706
  value,
@@ -3676,13 +3708,23 @@ function RangePanel({
3676
3708
  onCommit,
3677
3709
  onClose
3678
3710
  }) {
3679
- const [draft, setDraft] = React24.useState({ min: value?.min, max: value?.max });
3680
- const draftRef = React24.useRef(draft);
3681
- const publish = () => {
3711
+ const [draft, setDraft] = React25.useState(() => rangeDraft(value));
3712
+ const [seed, setSeed] = React25.useState(0);
3713
+ const draftRef = React25.useRef(draft);
3714
+ const shown = () => {
3682
3715
  const { min, max } = draftRef.current;
3683
- onCommit(applicable(field, min == null && max == null ? void 0 : { min, max }, validity.allValid()));
3716
+ return applicable(field, min == null && max == null ? void 0 : { min, max }, validity.allValid());
3717
+ };
3718
+ const publish = () => {
3719
+ onCommit(shown());
3684
3720
  };
3685
3721
  const validity = useTextValidity(publish);
3722
+ useReseedFromProps(value, shown, (next) => {
3723
+ draftRef.current = rangeDraft(next);
3724
+ setDraft(draftRef.current);
3725
+ validity.reset();
3726
+ setSeed((n) => n + 1);
3727
+ });
3686
3728
  const draftMin = draft.min;
3687
3729
  const draftMax = draft.max;
3688
3730
  const isEmpty = draftMin == null && draftMax == null;
@@ -3719,7 +3761,8 @@ function RangePanel({
3719
3761
  placeholder: field.minPlaceholder,
3720
3762
  onChange: (e) => apply({ min: e.target.value }),
3721
3763
  onValidityChange: validity.report("min")
3722
- }
3764
+ },
3765
+ "min-" + seed
3723
3766
  ),
3724
3767
  /* @__PURE__ */ jsx45(
3725
3768
  NumberInput,
@@ -3734,12 +3777,20 @@ function RangePanel({
3734
3777
  placeholder: field.maxPlaceholder,
3735
3778
  onChange: (e) => apply({ max: e.target.value }),
3736
3779
  onValidityChange: validity.report("max")
3737
- }
3780
+ },
3781
+ "max-" + seed
3738
3782
  )
3739
3783
  ] })
3740
3784
  }
3741
3785
  );
3742
3786
  }
3787
+ function dateDraft(field, value) {
3788
+ return {
3789
+ kind: value?.kind ?? "withinDays",
3790
+ days: value?.kind === "withinDays" ? value.days : field.defaultDays ?? 30,
3791
+ between: value?.kind === "between" ? { from: value.from, to: value.to } : { from: "", to: "" }
3792
+ };
3793
+ }
3743
3794
  function DatePanel({
3744
3795
  field,
3745
3796
  value,
@@ -3747,23 +3798,41 @@ function DatePanel({
3747
3798
  onCommit,
3748
3799
  onClose
3749
3800
  }) {
3750
- const defaultDays = field.defaultDays ?? 30;
3751
3801
  const maxDays = effectiveMaxDays(field);
3752
- const [kind, setKind] = React24.useState(value?.kind ?? "withinDays");
3753
- const [daysDraft, setDaysDraft] = React24.useState(
3754
- value?.kind === "withinDays" ? value.days : defaultDays
3755
- );
3756
- const daysRef = React24.useRef(daysDraft);
3757
- const [between, setBetween] = React24.useState(
3758
- value?.kind === "between" ? { from: value.from, to: value.to } : { from: "", to: "" }
3759
- );
3802
+ const [initial] = React25.useState(() => dateDraft(field, value));
3803
+ const [kind, setKind] = React25.useState(initial.kind);
3804
+ const [daysDraft, setDaysDraft] = React25.useState(initial.days);
3805
+ const daysRef = React25.useRef(daysDraft);
3806
+ const daysConfirmed = React25.useRef(value?.kind === "withinDays");
3807
+ const [between, setBetween] = React25.useState(initial.between);
3808
+ const [seed, setSeed] = React25.useState(0);
3760
3809
  const shownWithinDays = () => {
3761
3810
  const days = daysRef.current;
3762
3811
  return applicable(field, days == null ? void 0 : { kind: "withinDays", days }, validity.allValid());
3763
3812
  };
3764
- const validity = useTextValidity(() => onCommit(shownWithinDays()));
3813
+ const validity = useTextValidity(() => {
3814
+ daysConfirmed.current = true;
3815
+ onCommit(shownWithinDays());
3816
+ });
3817
+ const applies = () => {
3818
+ if (kind === "between") {
3819
+ return applicable(field, between.from && between.to ? { kind: "between", ...between } : void 0, true);
3820
+ }
3821
+ return daysConfirmed.current ? shownWithinDays() : void 0;
3822
+ };
3823
+ useReseedFromProps(value, applies, (next) => {
3824
+ const d = dateDraft(field, next);
3825
+ setKind(d.kind);
3826
+ daysRef.current = d.days;
3827
+ daysConfirmed.current = next?.kind === "withinDays";
3828
+ setDaysDraft(d.days);
3829
+ setBetween(d.between);
3830
+ validity.reset();
3831
+ setSeed((n) => n + 1);
3832
+ });
3765
3833
  const applyWithinDays = (days) => {
3766
3834
  daysRef.current = days;
3835
+ daysConfirmed.current = true;
3767
3836
  setDaysDraft(days);
3768
3837
  onCommit(shownWithinDays());
3769
3838
  };
@@ -3778,8 +3847,10 @@ function DatePanel({
3778
3847
  setBetween({ from: "", to: "" });
3779
3848
  validity.forget("days");
3780
3849
  } else {
3781
- daysRef.current = defaultDays;
3782
- setDaysDraft(defaultDays);
3850
+ const { days } = dateDraft(field, void 0);
3851
+ daysRef.current = days;
3852
+ daysConfirmed.current = false;
3853
+ setDaysDraft(days);
3783
3854
  }
3784
3855
  onCommit(void 0);
3785
3856
  };
@@ -3827,7 +3898,8 @@ function DatePanel({
3827
3898
  value: daysDraft ?? "",
3828
3899
  onChange: (e) => applyWithinDays(e.target.value),
3829
3900
  onValidityChange: validity.report("days")
3830
- }
3901
+ },
3902
+ "days-" + seed
3831
3903
  ) : /* @__PURE__ */ jsx45(
3832
3904
  DatePicker,
3833
3905
  {
@@ -3835,30 +3907,23 @@ function DatePanel({
3835
3907
  label: "Date range",
3836
3908
  value: { start: between.from, end: between.to },
3837
3909
  onChange: (e) => applyBetween(e.target.value.start || "", e.target.value.end || "")
3838
- }
3910
+ },
3911
+ "between-" + seed
3839
3912
  )
3840
3913
  ] })
3841
3914
  }
3842
3915
  );
3843
3916
  }
3844
3917
  function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align = "left", className = "", ...rest }) {
3845
- const [panel, setPanel] = React24.useState(null);
3846
- const editAnchorRef = React24.useRef(null);
3847
- const addAnchorRef = React24.useRef(null);
3848
- const textInputRef = React24.useRef(null);
3849
- const optionsRef = React24.useRef(null);
3918
+ const [panel, setPanel] = React25.useState(null);
3919
+ const editAnchorRef = React25.useRef(null);
3920
+ const addAnchorRef = React25.useRef(null);
3921
+ const textInputRef = React25.useRef(null);
3922
+ const optionsRef = React25.useRef(null);
3850
3923
  if (process.env.NODE_ENV !== "production") {
3851
3924
  for (const f of fields) assertValidFieldDefinition(f);
3852
3925
  }
3853
- const latest = React24.useRef({ fields, filters, onFilterChange, onFilterRemove });
3854
- const filtersProp = React24.useRef(filters);
3855
- if (filters !== filtersProp.current) {
3856
- filtersProp.current = filters;
3857
- latest.current.filters = filters;
3858
- }
3859
- latest.current.fields = fields;
3860
- latest.current.onFilterChange = onFilterChange;
3861
- latest.current.onFilterRemove = onFilterRemove;
3926
+ const latest = useLatest({ fields, filters, onFilterChange, onFilterRemove });
3862
3927
  const commit = (field, candidate) => {
3863
3928
  const current2 = latest.current.fields.find((f) => f.key === field.key);
3864
3929
  if (!current2) return { ok: false, reason: "This filter is no longer available" };
@@ -3867,24 +3932,17 @@ function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align
3867
3932
  return { ok: true };
3868
3933
  }
3869
3934
  const result = validateFilterValue(current2, candidate);
3870
- if (result.ok) {
3871
- latest.current.filters = { ...latest.current.filters, [current2.key]: candidate };
3872
- latest.current.onFilterChange(current2.key, candidate);
3873
- }
3935
+ if (result.ok) latest.current.onFilterChange(current2.key, candidate);
3874
3936
  return result;
3875
3937
  };
3876
3938
  const removeField = (field) => {
3877
3939
  const { filters: now, onFilterRemove: remove } = latest.current;
3878
- if (!isFieldSet(field, now[field.key])) return;
3879
- const next = { ...now };
3880
- delete next[field.key];
3881
- latest.current.filters = next;
3882
- remove(field.key);
3940
+ if (isFieldSet(field, now[field.key])) remove(field.key);
3883
3941
  };
3884
3942
  const editingField = panel && panel.kind === "edit" ? fields.find((f) => f.key === panel.key) : void 0;
3885
3943
  const startEditing = (key) => setPanel({ kind: "edit", key });
3886
3944
  const closeEditing = () => setPanel(null);
3887
- React24.useEffect(() => {
3945
+ React25.useEffect(() => {
3888
3946
  if (!panel || panel.kind !== "edit") return;
3889
3947
  if (editingField?.type === "text") {
3890
3948
  textInputRef.current?.focus();
@@ -4090,7 +4148,7 @@ function StatTile({ label, value, sub, delta, invert = false, loading = false, c
4090
4148
  }
4091
4149
 
4092
4150
  // src/components/data/CodeBlock.tsx
4093
- import * as React25 from "react";
4151
+ import * as React26 from "react";
4094
4152
  import { Fragment as Fragment6, jsx as jsx47, jsxs as jsxs41 } from "react/jsx-runtime";
4095
4153
  var JS_KW = /^(?:const|let|var|function|return|if|else|for|while|do|new|class|extends|import|from|export|default|await|async|try|catch|finally|throw|typeof|instanceof|of|in|switch|case|break|continue|delete|void|yield|static|get|set|this|super|interface|type|enum|implements|public|private|protected|readonly|abstract|as|satisfies|declare|namespace)$/;
4096
4154
  var RULES = {
@@ -4246,13 +4304,13 @@ function tokenize(src, lang) {
4246
4304
  return out;
4247
4305
  }
4248
4306
  function Highlighted({ code, lang }) {
4249
- const toks = React25.useMemo(() => tokenize(code, lang), [code, lang]);
4307
+ const toks = React26.useMemo(() => tokenize(code, lang), [code, lang]);
4250
4308
  return /* @__PURE__ */ jsx47(Fragment6, { children: toks.map((tk, i) => tk.c ? /* @__PURE__ */ jsx47("span", { className: "tok-" + tk.c, children: tk.t }, i) : tk.t) });
4251
4309
  }
4252
4310
  function useCopy() {
4253
- const [done, setDone] = React25.useState(false);
4254
- const timer = React25.useRef(null);
4255
- React25.useEffect(() => () => {
4311
+ const [done, setDone] = React26.useState(false);
4312
+ const timer = React26.useRef(null);
4313
+ React26.useEffect(() => () => {
4256
4314
  if (timer.current) clearTimeout(timer.current);
4257
4315
  }, []);
4258
4316
  const copy = (text) => {
@@ -4288,8 +4346,8 @@ function CodeBlock({
4288
4346
  actions,
4289
4347
  className = ""
4290
4348
  }) {
4291
- const [wrap, setWrap] = React25.useState(!!wrapDefault);
4292
- const [open, setOpen] = React25.useState(false);
4349
+ const [wrap, setWrap] = React26.useState(!!wrapDefault);
4350
+ const [open, setOpen] = React26.useState(false);
4293
4351
  const [copied, copy] = useCopy();
4294
4352
  const body = String(code).replace(/\n$/, "");
4295
4353
  const lines = body.split("\n");
@@ -4318,7 +4376,7 @@ function CodeBlock({
4318
4376
  ] });
4319
4377
  }
4320
4378
  function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" }) {
4321
- const [open, setOpen] = React25.useState(false);
4379
+ const [open, setOpen] = React26.useState(false);
4322
4380
  const [copied, copy] = useCopy();
4323
4381
  const raw = String(diff).replace(/\n$/, "");
4324
4382
  const all = raw.split("\n").map((line) => {
@@ -4364,7 +4422,7 @@ function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" })
4364
4422
  }
4365
4423
 
4366
4424
  // src/components/data/Markdown.tsx
4367
- import * as React26 from "react";
4425
+ import * as React27 from "react";
4368
4426
  import { jsx as jsx48, jsxs as jsxs42 } from "react/jsx-runtime";
4369
4427
  var SAFE = /^(?:https?:|mailto:|tel:|#|\/|\.{1,2}\/)/i;
4370
4428
  var safeHref = (h) => SAFE.test(String(h || "").trim()) ? String(h).trim() : null;
@@ -4403,7 +4461,7 @@ function inlineNodes(src, ctx, keyBase) {
4403
4461
  const href = safeHref(g(2));
4404
4462
  out.push(href ? /* @__PURE__ */ jsx48("a", { href, title: g(3) || void 0, target: /^https?:/i.test(href) ? "_blank" : void 0, rel: "noopener noreferrer", onClick: ctx.onLinkClick, children: inlineNodes(g(1), ctx, key()) }, key()) : /* @__PURE__ */ jsx48("span", { children: g(1) }, key()));
4405
4463
  } else if (kind === "cite") {
4406
- out.push(ctx.renderCitation ? /* @__PURE__ */ jsx48(React26.Fragment, { children: ctx.renderCitation(g(1)) }, key()) : /* @__PURE__ */ jsx48("sup", { className: "fd-md-cite", children: g(1) }, key()));
4464
+ out.push(ctx.renderCitation ? /* @__PURE__ */ jsx48(React27.Fragment, { children: ctx.renderCitation(g(1)) }, key()) : /* @__PURE__ */ jsx48("sup", { className: "fd-md-cite", children: g(1) }, key()));
4407
4465
  } else if (kind === "strongem") out.push(/* @__PURE__ */ jsx48("strong", { children: /* @__PURE__ */ jsx48("em", { children: inlineNodes(g(1), ctx, key()) }) }, key()));
4408
4466
  else if (kind === "strong") out.push(/* @__PURE__ */ jsx48("strong", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
4409
4467
  else if (kind === "em") out.push(/* @__PURE__ */ jsx48("em", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
@@ -4568,7 +4626,7 @@ function Markdown({
4568
4626
  onLinkClick
4569
4627
  }) {
4570
4628
  const ctx = { allowImages, headingOffset, renderCitation, onLinkClick, codeProps };
4571
- const blocks = React26.useMemo(() => parseBlocks(source), [source]);
4629
+ const blocks = React27.useMemo(() => parseBlocks(source), [source]);
4572
4630
  return /* @__PURE__ */ jsx48("div", { className: ["fd-md", className].filter(Boolean).join(" "), children: renderBlocks(blocks, ctx, "b") });
4573
4631
  }
4574
4632
  function MarkdownInline({ source = "", className = "", onLinkClick }) {
@@ -4682,7 +4740,7 @@ function Pagination({
4682
4740
  }
4683
4741
 
4684
4742
  // src/components/data/RelativeTime.tsx
4685
- import * as React27 from "react";
4743
+ import * as React28 from "react";
4686
4744
  import { jsxs as jsxs45 } from "react/jsx-runtime";
4687
4745
  var MIN = 6e4;
4688
4746
  var HOUR = 36e5;
@@ -4734,9 +4792,9 @@ function formatDuration(ms) {
4734
4792
  return h + "h" + (m % 60 ? " " + m % 60 + "m" : "");
4735
4793
  }
4736
4794
  function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true, ...rest }) {
4737
- const [, force] = React27.useState(0);
4795
+ const [, force] = React28.useState(0);
4738
4796
  const t = value instanceof Date ? value.getTime() : Date.parse(value);
4739
- React27.useEffect(() => {
4797
+ React28.useEffect(() => {
4740
4798
  if (!refresh2 || !t || isNaN(t)) return;
4741
4799
  const abs = Math.abs(t - Date.now());
4742
4800
  if (abs > DAY) return;
@@ -4752,7 +4810,7 @@ function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true,
4752
4810
  }
4753
4811
 
4754
4812
  // src/components/data/ScoreMeter.tsx
4755
- import * as React28 from "react";
4813
+ import * as React29 from "react";
4756
4814
  import { jsx as jsx51, jsxs as jsxs46 } from "react/jsx-runtime";
4757
4815
  function ScoreMeter({
4758
4816
  score,
@@ -4767,10 +4825,10 @@ function ScoreMeter({
4767
4825
  breakdownWidth = 280,
4768
4826
  className = ""
4769
4827
  }) {
4770
- const [open, setOpen] = React28.useState(false);
4771
- const ref = React28.useRef(null);
4828
+ const [open, setOpen] = React29.useState(false);
4829
+ const ref = React29.useRef(null);
4772
4830
  const value = Math.max(0, Math.min(100, Math.round(Number(score) || 0)));
4773
- const body = /* @__PURE__ */ jsxs46(React28.Fragment, { children: [
4831
+ const body = /* @__PURE__ */ jsxs46(React29.Fragment, { children: [
4774
4832
  /* @__PURE__ */ jsx51("span", { className: "fd-scoremeter-rail", style: { width }, "aria-hidden": "true", children: /* @__PURE__ */ jsx51(SegmentedMeter, { total: 100, segments, height, showTotal: false, showPercent: false, legend: false }) }),
4775
4833
  showScore ? /* @__PURE__ */ jsx51("span", { className: "fd-scoremeter-value fd-tabular", "aria-hidden": "true", children: value }) : null
4776
4834
  ] });
@@ -4789,7 +4847,7 @@ function ScoreMeter({
4789
4847
  }
4790
4848
  );
4791
4849
  }
4792
- return /* @__PURE__ */ jsxs46(React28.Fragment, { children: [
4850
+ return /* @__PURE__ */ jsxs46(React29.Fragment, { children: [
4793
4851
  /* @__PURE__ */ jsx51(
4794
4852
  "button",
4795
4853
  {
@@ -4820,13 +4878,13 @@ function ScoreMeter({
4820
4878
  }
4821
4879
 
4822
4880
  // src/components/data/SortMenu.tsx
4823
- import * as React29 from "react";
4881
+ import * as React30 from "react";
4824
4882
  import { jsx as jsx52, jsxs as jsxs47 } from "react/jsx-runtime";
4825
4883
  function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", className = "", ...rest }) {
4826
- const [open, setOpen] = React29.useState(false);
4884
+ const [open, setOpen] = React30.useState(false);
4827
4885
  const zIndex = useOverlayLayer(open);
4828
- const ref = React29.useRef(null);
4829
- React29.useEffect(() => {
4886
+ const ref = React30.useRef(null);
4887
+ React30.useEffect(() => {
4830
4888
  if (!open) return;
4831
4889
  const away = (e) => {
4832
4890
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -4888,7 +4946,7 @@ function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", cla
4888
4946
  }
4889
4947
 
4890
4948
  // src/components/data/StepList.tsx
4891
- import * as React30 from "react";
4949
+ import * as React31 from "react";
4892
4950
  import { jsx as jsx53, jsxs as jsxs48 } from "react/jsx-runtime";
4893
4951
  var KIND_ICON = { tool: "wrench", search: "magnifying-glass", read: "file-text", write: "pencil-simple", reasoning: "brain", run: "terminal", fetch: "cloud-arrow-down" };
4894
4952
  function StepList({
@@ -4901,9 +4959,9 @@ function StepList({
4901
4959
  dense = false,
4902
4960
  className = ""
4903
4961
  }) {
4904
- const [openState, setOpenState] = React30.useState(defaultOpen);
4962
+ const [openState, setOpenState] = React31.useState(defaultOpen);
4905
4963
  const open = openProp == null ? openState : openProp;
4906
- const [expanded, setExpanded] = React30.useState({});
4964
+ const [expanded, setExpanded] = React31.useState({});
4907
4965
  if (!steps.length) return null;
4908
4966
  const running = steps.some((s) => s.status === "running");
4909
4967
  const failed = steps.filter((s) => s.status === "error").length;
@@ -4961,7 +5019,7 @@ function StepList({
4961
5019
  }
4962
5020
 
4963
5021
  // src/components/data/VirtualList.tsx
4964
- import * as React31 from "react";
5022
+ import * as React32 from "react";
4965
5023
  import { jsx as jsx54, jsxs as jsxs49 } from "react/jsx-runtime";
4966
5024
  var VIRTUAL_LIST_BUFFER_ROWS = 20;
4967
5025
  var VIRTUAL_LIST_ROW_GAP = 6;
@@ -5013,35 +5071,35 @@ function VirtualList({
5013
5071
  className = "",
5014
5072
  style
5015
5073
  }) {
5016
- const [scrollTop, setScrollTop] = React31.useState(0);
5017
- const loaded = React31.useMemo(
5074
+ const [scrollTop, setScrollTop] = React32.useState(0);
5075
+ const loaded = React32.useMemo(
5018
5076
  () => ({ firstItemIndex, count: items.length }),
5019
5077
  [firstItemIndex, items.length]
5020
5078
  );
5021
5079
  const total = Math.max(totalCount == null ? 0 : totalCount, firstItemIndex + items.length);
5022
5080
  const bandKey = firstItemIndex + ":" + items.length;
5023
- const win = React31.useMemo(
5081
+ const win = React32.useMemo(
5024
5082
  () => computeVirtualWindow({ scrollTop, viewportHeight: height, itemHeight, itemCount: total, loaded }),
5025
5083
  [scrollTop, height, itemHeight, total, loaded]
5026
5084
  );
5027
- const need = React31.useMemo(() => computeNeedMore(win, loaded), [win, loaded]);
5085
+ const need = React32.useMemo(() => computeNeedMore(win, loaded), [win, loaded]);
5028
5086
  const wantStart = need.start && hasMore?.start !== false;
5029
5087
  const wantEnd = need.end && hasMore?.end !== false;
5030
5088
  const bucket2 = Math.floor(win.visibleStart / VIRTUAL_LIST_BUFFER_ROWS);
5031
5089
  const askKey = bandKey + "@" + bucket2;
5032
- const asked = React31.useRef({ start: null, end: null });
5033
- React31.useEffect(() => {
5090
+ const asked = React32.useRef({ start: null, end: null });
5091
+ React32.useEffect(() => {
5034
5092
  if (loading || !wantStart || asked.current.start === askKey) return;
5035
5093
  asked.current.start = askKey;
5036
5094
  onNeedMore("start", win);
5037
5095
  }, [wantStart, loading, askKey, onNeedMore, win]);
5038
- React31.useEffect(() => {
5096
+ React32.useEffect(() => {
5039
5097
  if (loading || !wantEnd || asked.current.end === askKey) return;
5040
5098
  asked.current.end = askKey;
5041
5099
  onNeedMore("end", win);
5042
5100
  }, [wantEnd, loading, askKey, onNeedMore, win]);
5043
- const lastKeysRef = React31.useRef(null);
5044
- const newKeys = React31.useMemo(() => {
5101
+ const lastKeysRef = React32.useRef(null);
5102
+ const newKeys = React32.useMemo(() => {
5045
5103
  const prev = lastKeysRef.current;
5046
5104
  if (!prev) return /* @__PURE__ */ new Set();
5047
5105
  const fresh = /* @__PURE__ */ new Set();
@@ -5051,7 +5109,7 @@ function VirtualList({
5051
5109
  }
5052
5110
  return fresh;
5053
5111
  }, [items]);
5054
- React31.useEffect(() => {
5112
+ React32.useEffect(() => {
5055
5113
  lastKeysRef.current = new Set(items.map(keyOf));
5056
5114
  }, [items]);
5057
5115
  const gapStyle = { "--fd-vlist-gap": rowGap + "px" };
@@ -5096,7 +5154,7 @@ function VirtualList({
5096
5154
  }
5097
5155
 
5098
5156
  // src/components/data/EntityRow.tsx
5099
- import * as React32 from "react";
5157
+ import * as React33 from "react";
5100
5158
  import { jsx as jsx55, jsxs as jsxs50 } from "react/jsx-runtime";
5101
5159
  var ENTITY_ROW_METRIC_WIDTH = 74;
5102
5160
  var METRIC_TONE_COLOR = {
@@ -5109,7 +5167,7 @@ function EntityRowMetrics({ metrics, className = "", style }) {
5109
5167
  const key = m.id || m.label || i;
5110
5168
  const className2 = ["fd-erow-metric", m.tone && m.tone !== "default" ? "is-" + m.tone : "", m.tooltip != null ? "is-described" : ""].filter(Boolean).join(" ");
5111
5169
  const tone = { color: METRIC_TONE_COLOR[m.tone || "default"] };
5112
- const content = /* @__PURE__ */ jsxs50(React32.Fragment, { children: [
5170
+ const content = /* @__PURE__ */ jsxs50(React33.Fragment, { children: [
5113
5171
  m.icon ? /* @__PURE__ */ jsx55("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }) : null,
5114
5172
  /* @__PURE__ */ jsx55("span", { className: "fd-erow-metric-value fd-tabular", children: m.value }),
5115
5173
  /* @__PURE__ */ jsx55("span", { className: "fd-sr", children: " " + m.label })
@@ -5201,7 +5259,7 @@ function EntityRow({
5201
5259
  }
5202
5260
 
5203
5261
  // src/components/data/TransferList.tsx
5204
- import * as React33 from "react";
5262
+ import * as React34 from "react";
5205
5263
  import { createPortal as createPortal4 } from "react-dom";
5206
5264
 
5207
5265
  // src/components/forms/Input.tsx
@@ -5307,12 +5365,12 @@ function TransferList({
5307
5365
  listHeight = 440,
5308
5366
  className = ""
5309
5367
  }) {
5310
- const [drag, setDrag] = React33.useState(null);
5311
- const [dragOverSide, setDragOverSide] = React33.useState(null);
5312
- const dragRef = React33.useRef(null);
5313
- const armedRef = React33.useRef(null);
5314
- const sideRefs = React33.useRef({ left: null, right: null });
5315
- const hitTestSide = React33.useCallback((x, y) => {
5368
+ const [drag, setDrag] = React34.useState(null);
5369
+ const [dragOverSide, setDragOverSide] = React34.useState(null);
5370
+ const dragRef = React34.useRef(null);
5371
+ const armedRef = React34.useRef(null);
5372
+ const sideRefs = React34.useRef({ left: null, right: null });
5373
+ const hitTestSide = React34.useCallback((x, y) => {
5316
5374
  for (const side of ["left", "right"]) {
5317
5375
  const el = sideRefs.current[side];
5318
5376
  if (!el) continue;
@@ -5321,7 +5379,7 @@ function TransferList({
5321
5379
  }
5322
5380
  return null;
5323
5381
  }, []);
5324
- const findItem = React33.useCallback(
5382
+ const findItem = React34.useCallback(
5325
5383
  (side, key) => (side === "left" ? left.items : right.items).find((it) => keyOf(it) === key),
5326
5384
  [left.items, right.items, keyOf]
5327
5385
  );
@@ -5343,7 +5401,7 @@ function TransferList({
5343
5401
  height: rect.height
5344
5402
  };
5345
5403
  };
5346
- React33.useEffect(() => {
5404
+ React34.useEffect(() => {
5347
5405
  const onMovePointer = (e) => {
5348
5406
  const armed = armedRef.current;
5349
5407
  if (!armed || e.pointerId !== armed.pointerId) return;
@@ -5396,7 +5454,7 @@ function TransferList({
5396
5454
  window.removeEventListener("pointercancel", endDrag);
5397
5455
  };
5398
5456
  }, [hitTestSide, findItem, onMove]);
5399
- React33.useEffect(() => {
5457
+ React34.useEffect(() => {
5400
5458
  if (!drag || typeof document === "undefined") return;
5401
5459
  const prev = document.body.style.userSelect;
5402
5460
  document.body.style.userSelect = "none";
@@ -5504,11 +5562,11 @@ function TransferList({
5504
5562
  }
5505
5563
 
5506
5564
  // src/components/forms/Checkbox.tsx
5507
- import * as React34 from "react";
5565
+ import * as React35 from "react";
5508
5566
  import { jsx as jsx59, jsxs as jsxs53 } from "react/jsx-runtime";
5509
5567
  function Checkbox({ label, description, card = false, indeterminate = false, className = "", ...rest }) {
5510
- const ref = React34.useRef(null);
5511
- React34.useEffect(() => {
5568
+ const ref = React35.useRef(null);
5569
+ React35.useEffect(() => {
5512
5570
  if (ref.current) ref.current.indeterminate = indeterminate;
5513
5571
  }, [indeterminate]);
5514
5572
  return /* @__PURE__ */ jsxs53("label", { className: ["fd-choice", card ? "fd-choice-card" : "", className].filter(Boolean).join(" "), children: [
@@ -5552,7 +5610,7 @@ function Switch({ label, description, className = "", ...rest }) {
5552
5610
  }
5553
5611
 
5554
5612
  // src/components/forms/Select.tsx
5555
- import * as React35 from "react";
5613
+ import * as React36 from "react";
5556
5614
  import { jsx as jsx62, jsxs as jsxs56 } from "react/jsx-runtime";
5557
5615
  var norm = (o) => typeof o === "string" ? { value: o, label: o } : o;
5558
5616
  function Select({
@@ -5579,12 +5637,12 @@ function Select({
5579
5637
  const isOn = (v) => multiple ? vals.includes(v) : v === value;
5580
5638
  const hasSearch = searchable === void 0 ? opts.length > 8 : searchable;
5581
5639
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
5582
- const [open, setOpen] = React35.useState(false);
5583
- const [q, setQ] = React35.useState("");
5584
- const rootRef = React35.useRef(null);
5585
- const boxRef = React35.useRef(null);
5586
- const listRef = React35.useRef(null);
5587
- const typeBuf = React35.useRef({ s: "", t: 0 });
5640
+ const [open, setOpen] = React36.useState(false);
5641
+ const [q, setQ] = React36.useState("");
5642
+ const rootRef = React36.useRef(null);
5643
+ const boxRef = React36.useRef(null);
5644
+ const listRef = React36.useRef(null);
5645
+ const typeBuf = React36.useRef({ s: "", t: 0 });
5588
5646
  const selected = multiple ? null : opts.find((o) => o.value === value);
5589
5647
  const chosen = multiple ? opts.filter((o) => vals.includes(o.value)) : [];
5590
5648
  const boxText = multiple ? chosen.length === 0 ? placeholder : chosen.length === 1 ? chosen[0].label : summary ? summary(chosen.map((o) => o.value)) : chosen.length + " selected" : selected ? selected.label : placeholder;
@@ -5723,7 +5781,7 @@ function Select({
5723
5781
  ),
5724
5782
  q ? /* @__PURE__ */ jsx62("span", { className: "fd-body-sm fd-muted", children: visible.length }) : null
5725
5783
  ] }) : void 0,
5726
- footer: multiple ? /* @__PURE__ */ jsxs56(React35.Fragment, { children: [
5784
+ footer: multiple ? /* @__PURE__ */ jsxs56(React36.Fragment, { children: [
5727
5785
  /* @__PURE__ */ jsx62(
5728
5786
  "button",
5729
5787
  {
@@ -5754,7 +5812,7 @@ function Select({
5754
5812
  'Nothing matches "',
5755
5813
  q,
5756
5814
  '".'
5757
- ] }) : groups.map((grp) => /* @__PURE__ */ jsxs56(React35.Fragment, { children: [
5815
+ ] }) : groups.map((grp) => /* @__PURE__ */ jsxs56(React36.Fragment, { children: [
5758
5816
  grp.g ? /* @__PURE__ */ jsx62("div", { className: "fd-pop-group", children: grp.g }) : null,
5759
5817
  grp.items.map(({ o, i }) => /* @__PURE__ */ jsxs56(
5760
5818
  "button",
@@ -5791,7 +5849,7 @@ function Select({
5791
5849
  }
5792
5850
 
5793
5851
  // src/components/forms/TimePicker.tsx
5794
- import * as React36 from "react";
5852
+ import * as React37 from "react";
5795
5853
  import { jsx as jsx63, jsxs as jsxs57 } from "react/jsx-runtime";
5796
5854
  var pad = (n) => String(n).padStart(2, "0");
5797
5855
  function ClockFace({ value = "09:00", onChange }) {
@@ -5800,9 +5858,9 @@ function ClockFace({ value = "09:00", onChange }) {
5800
5858
  if (isNaN(m)) m = 0;
5801
5859
  const pm = h24 >= 12;
5802
5860
  const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
5803
- const [mode2, setMode] = React36.useState("h");
5804
- const faceRef = React36.useRef(null);
5805
- const dragging = React36.useRef(false);
5861
+ const [mode2, setMode] = React37.useState("h");
5862
+ const faceRef = React37.useRef(null);
5863
+ const dragging = React37.useRef(false);
5806
5864
  const set = (h, mm, isPm) => onChange((isPm ? h % 12 + 12 : h % 12) + ":" + pad(mm));
5807
5865
  const R = 108, NR = 80;
5808
5866
  const nums = mode2 === "h" ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 12 }, (_, i) => i * 5);
@@ -5875,9 +5933,9 @@ function ClockFace({ value = "09:00", onChange }) {
5875
5933
  }
5876
5934
  function TimePicker({ label, help, error, required = false, disabled = false, value = "", onChange, placeholder = "Pick a time", id, className = "", style }) {
5877
5935
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
5878
- const [open, setOpen] = React36.useState(false);
5879
- const rootRef = React36.useRef(null);
5880
- const boxRef = React36.useRef(null);
5936
+ const [open, setOpen] = React37.useState(false);
5937
+ const rootRef = React37.useRef(null);
5938
+ const boxRef = React37.useRef(null);
5881
5939
  const disp = () => {
5882
5940
  if (!value) return "";
5883
5941
  const [h, m] = value.split(":").map(Number);
@@ -5920,7 +5978,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
5920
5978
  offset: 6,
5921
5979
  width: 262,
5922
5980
  role: "presentation",
5923
- footer: /* @__PURE__ */ jsxs57(React36.Fragment, { children: [
5981
+ footer: /* @__PURE__ */ jsxs57(React37.Fragment, { children: [
5924
5982
  /* @__PURE__ */ jsx63(
5925
5983
  "button",
5926
5984
  {
@@ -5948,7 +6006,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
5948
6006
  }
5949
6007
 
5950
6008
  // src/components/forms/Slider.tsx
5951
- import * as React37 from "react";
6009
+ import * as React38 from "react";
5952
6010
  import { jsx as jsx64, jsxs as jsxs58 } from "react/jsx-runtime";
5953
6011
  function Slider({
5954
6012
  label,
@@ -5966,7 +6024,7 @@ function Slider({
5966
6024
  ...rest
5967
6025
  }) {
5968
6026
  const fieldId = useFieldId(id);
5969
- const [dragging, setDragging] = React37.useState(false);
6027
+ const [dragging, setDragging] = React38.useState(false);
5970
6028
  const v = value === void 0 ? min : Number(value);
5971
6029
  const pct = max === min ? 0 : (v - min) / (max - min) * 100;
5972
6030
  return /* @__PURE__ */ jsxs58("div", { className: ["fd-field", className].filter(Boolean).join(" "), children: [
@@ -6000,7 +6058,7 @@ function Slider({
6000
6058
  }
6001
6059
 
6002
6060
  // src/components/forms/RangeSlider.tsx
6003
- import * as React38 from "react";
6061
+ import * as React39 from "react";
6004
6062
  import { jsx as jsx65, jsxs as jsxs59 } from "react/jsx-runtime";
6005
6063
  function RangeSlider({
6006
6064
  label,
@@ -6020,9 +6078,9 @@ function RangeSlider({
6020
6078
  }) {
6021
6079
  const fmt2 = format || ((v) => String(v));
6022
6080
  const [a, b] = value || [min, max];
6023
- const [drag, setDrag] = React38.useState(null);
6024
- const [focus, setFocus] = React38.useState(null);
6025
- const railRef = React38.useRef(null);
6081
+ const [drag, setDrag] = React39.useState(null);
6082
+ const [focus, setFocus] = React39.useState(null);
6083
+ const railRef = React39.useRef(null);
6026
6084
  const pct = (v) => max === min ? 0 : (v - min) / (max - min) * 100;
6027
6085
  const clampPair = (i, v) => {
6028
6086
  v = Math.min(max, Math.max(min, Math.round(v / step) * step));
@@ -6037,7 +6095,7 @@ function RangeSlider({
6037
6095
  const r = railRef.current.getBoundingClientRect();
6038
6096
  return min + Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)) * (max - min);
6039
6097
  };
6040
- React38.useEffect(() => {
6098
+ React39.useEffect(() => {
6041
6099
  if (drag === null) return;
6042
6100
  const mv = (e) => onChange && onChange(clampPair(drag, fromEvent(e)));
6043
6101
  const upH = () => setDrag(null);
@@ -6118,7 +6176,7 @@ function RangeSlider({
6118
6176
  /* @__PURE__ */ jsxs59("div", { className: "fd-range" + (hasLabels ? " has-labels" : ""), onPointerDown: onRailDown, children: [
6119
6177
  /* @__PURE__ */ jsx65("span", { className: "fd-range-rail", ref: railRef }),
6120
6178
  /* @__PURE__ */ jsx65("span", { className: "fd-range-fill", style: { left: pct(a) + "%", width: pct(b) - pct(a) + "%" } }),
6121
- marks.map((m) => /* @__PURE__ */ jsxs59(React38.Fragment, { children: [
6179
+ marks.map((m) => /* @__PURE__ */ jsxs59(React39.Fragment, { children: [
6122
6180
  /* @__PURE__ */ jsx65("span", { className: "fd-range-mark" + (m.value >= a && m.value <= b ? " is-in" : ""), style: { left: pct(m.value) + "%" } }),
6123
6181
  m.label ? /* @__PURE__ */ jsx65("span", { className: "fd-range-mark-label", style: { left: pct(m.value) + "%" }, children: m.label }) : null
6124
6182
  ] }, m.value)),
@@ -6147,7 +6205,7 @@ function RangeSlider({
6147
6205
  }
6148
6206
 
6149
6207
  // src/components/forms/Dropzone.tsx
6150
- import * as React39 from "react";
6208
+ import * as React40 from "react";
6151
6209
  import { jsx as jsx66, jsxs as jsxs60 } from "react/jsx-runtime";
6152
6210
  function Dropzone({
6153
6211
  onFiles,
@@ -6162,8 +6220,8 @@ function Dropzone({
6162
6220
  className = "",
6163
6221
  style
6164
6222
  }) {
6165
- const [over, setOver] = React39.useState(false);
6166
- const depth = React39.useRef(0);
6223
+ const [over, setOver] = React40.useState(false);
6224
+ const depth = React40.useRef(0);
6167
6225
  const has = (e) => {
6168
6226
  const dt = e.dataTransfer;
6169
6227
  if (!dt) return false;
@@ -6225,8 +6283,8 @@ function FilePickButton({
6225
6283
  className = "",
6226
6284
  children
6227
6285
  }) {
6228
- const ref = React39.useRef(null);
6229
- return /* @__PURE__ */ jsxs60(React39.Fragment, { children: [
6286
+ const ref = React40.useRef(null);
6287
+ return /* @__PURE__ */ jsxs60(React40.Fragment, { children: [
6230
6288
  /* @__PURE__ */ jsx66(
6231
6289
  "button",
6232
6290
  {
@@ -6259,10 +6317,10 @@ function FilePickButton({
6259
6317
  }
6260
6318
  function useStagedFiles(upload, opts) {
6261
6319
  const o = opts || {};
6262
- const [items, setItems] = React39.useState([]);
6263
- const controllers = React39.useRef({});
6320
+ const [items, setItems] = React40.useState([]);
6321
+ const controllers = React40.useRef({});
6264
6322
  const patch = (id, next) => setItems((list) => list.map((f) => f.id === id ? Object.assign({}, f, next) : f));
6265
- const run = React39.useCallback((att) => {
6323
+ const run = React40.useCallback((att) => {
6266
6324
  if (!upload) return;
6267
6325
  const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
6268
6326
  controllers.current[att.id] = ac;
@@ -6279,14 +6337,14 @@ function useStagedFiles(upload, opts) {
6279
6337
  delete controllers.current[att.id];
6280
6338
  });
6281
6339
  }, [upload]);
6282
- const add = React39.useCallback((files) => {
6340
+ const add = React40.useCallback((files) => {
6283
6341
  if (!upload) return [];
6284
6342
  const atts = Array.from(files).map((f) => toAttachment(f));
6285
6343
  setItems((list) => list.concat(atts));
6286
6344
  atts.forEach(run);
6287
6345
  return atts;
6288
6346
  }, [upload, run]);
6289
- const remove = React39.useCallback((att) => {
6347
+ const remove = React40.useCallback((att) => {
6290
6348
  const ac = controllers.current[att.id];
6291
6349
  if (ac) {
6292
6350
  try {
@@ -6297,10 +6355,10 @@ function useStagedFiles(upload, opts) {
6297
6355
  }
6298
6356
  setItems((list) => list.filter((f) => f.id !== att.id));
6299
6357
  }, []);
6300
- const retry = React39.useCallback((att) => {
6358
+ const retry = React40.useCallback((att) => {
6301
6359
  run(att);
6302
6360
  }, [run]);
6303
- const clear = React39.useCallback(() => {
6361
+ const clear = React40.useCallback(() => {
6304
6362
  Object.values(controllers.current).forEach((ac) => {
6305
6363
  try {
6306
6364
  ac && ac.abort();
@@ -6424,7 +6482,7 @@ function FileGrid({ files = [], onOpen, onRemove, onRetry, tiles = true, maxHeig
6424
6482
  }
6425
6483
 
6426
6484
  // src/components/forms/MarkdownEditor.tsx
6427
- import * as React40 from "react";
6485
+ import * as React41 from "react";
6428
6486
  import { jsx as jsx68, jsxs as jsxs62 } from "react/jsx-runtime";
6429
6487
  var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || "");
6430
6488
  var INLINE_RE = /(\*\*\*[^*\n]+\*\*\*|\*\*[^*\n]+\*\*|__[^_\n]+__|~~[^~\n]+~~|`[^`\n]+`|\*[^*\s][^*\n]*\*|(?<![A-Za-z0-9_])_[^_\s][^_\n]*_|\[[^\]\n]*\]\([^)\s\n]*\)|https?:\/\/\S+)/g;
@@ -6638,7 +6696,7 @@ function syncDom(root, value) {
6638
6696
  while (root.children.length > lines.length) root.removeChild(root.lastChild);
6639
6697
  }
6640
6698
  var LIST_CONT = RE_LI;
6641
- var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6699
+ var MarkdownEditor = React41.forwardRef(function MarkdownEditor2({
6642
6700
  value = "",
6643
6701
  onChange,
6644
6702
  onSubmit,
@@ -6657,10 +6715,10 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6657
6715
  className = "",
6658
6716
  id
6659
6717
  }, ref) {
6660
- const boxRef = React40.useRef(null);
6661
- const composing = React40.useRef(false);
6662
- const pendingCaret = React40.useRef(null);
6663
- React40.useLayoutEffect(() => {
6718
+ const boxRef = React41.useRef(null);
6719
+ const composing = React41.useRef(false);
6720
+ const pendingCaret = React41.useRef(null);
6721
+ React41.useLayoutEffect(() => {
6664
6722
  const root = boxRef.current;
6665
6723
  if (!root || composing.current) return;
6666
6724
  const active = document.activeElement === root || root.contains(document.activeElement);
@@ -6669,7 +6727,7 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6669
6727
  pendingCaret.current = null;
6670
6728
  if (active && caret != null) placeCaret(root, caret);
6671
6729
  }, [value]);
6672
- React40.useEffect(() => {
6730
+ React41.useEffect(() => {
6673
6731
  if (autoFocus && boxRef.current) boxRef.current.focus();
6674
6732
  }, [autoFocus]);
6675
6733
  const caretNow = () => {
@@ -6736,7 +6794,7 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6736
6794
  api.replaceRange(from, to, next, from + next.length);
6737
6795
  }
6738
6796
  };
6739
- React40.useImperativeHandle(ref, () => api);
6797
+ React41.useImperativeHandle(ref, () => api);
6740
6798
  function detect(text, caret) {
6741
6799
  if (!onTrigger) return;
6742
6800
  const upto = text.slice(0, caret);
@@ -6888,16 +6946,16 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6888
6946
  });
6889
6947
 
6890
6948
  // src/components/platform/AccountMenu.tsx
6891
- import * as React42 from "react";
6949
+ import * as React43 from "react";
6892
6950
 
6893
6951
  // src/kits/sessionContext.tsx
6894
- import * as React41 from "react";
6895
- var SessionContext = React41.createContext(null);
6952
+ import * as React42 from "react";
6953
+ var SessionContext = React42.createContext(null);
6896
6954
  function SessionProvider({ value, children }) {
6897
- return React41.createElement(SessionContext.Provider, { value }, children);
6955
+ return React42.createElement(SessionContext.Provider, { value }, children);
6898
6956
  }
6899
6957
  function useSessionContext() {
6900
- const ctx = React41.useContext(SessionContext);
6958
+ const ctx = React42.useContext(SessionContext);
6901
6959
  if (!ctx) {
6902
6960
  throw new Error(
6903
6961
  "useSessionContext() was called outside a <SessionProvider>. Wrap your app root in <SessionProvider value={...}> with your real session, or \u2014 in the docs site or a demo \u2014 in <DemoSessionProvider>, before rendering AccountMenu, Gate, ProfilePage or AppSwitcher."
@@ -6937,11 +6995,11 @@ function AccountMenu({
6937
6995
  ...rest
6938
6996
  }) {
6939
6997
  const { user, roles, can: can2, signOut: ctxSignOut, demo } = useSessionContext();
6940
- const [open, setOpen] = React42.useState(false);
6941
- const [switching, setSwitching] = React42.useState(false);
6942
- const [signingOut, setSigningOut] = React42.useState(false);
6943
- const [signOutError, setSignOutError] = React42.useState(null);
6944
- const ref = React42.useRef(null);
6998
+ const [open, setOpen] = React43.useState(false);
6999
+ const [switching, setSwitching] = React43.useState(false);
7000
+ const [signingOut, setSigningOut] = React43.useState(false);
7001
+ const [signOutError, setSignOutError] = React43.useState(null);
7002
+ const ref = React43.useRef(null);
6945
7003
  const visibleLinks = links.filter((l) => !l.perm || can2(l.perm));
6946
7004
  const visibleGroups = groups.map((g) => ({ label: g.label, links: g.links.filter((l) => !l.perm || can2(l.perm)) })).filter((g) => g.links.length);
6947
7005
  const closeMenu = () => {
@@ -7066,13 +7124,13 @@ function AccountMenu({
7066
7124
  }
7067
7125
 
7068
7126
  // src/components/platform/AppSwitcher.tsx
7069
- import * as React43 from "react";
7127
+ import * as React44 from "react";
7070
7128
  import { jsx as jsx70, jsxs as jsxs64 } from "react/jsx-runtime";
7071
7129
  function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
7072
7130
  const { apps: contextApps } = useSessionContext();
7073
7131
  const apps = appsProp ?? contextApps ?? [];
7074
- const [open, setOpen] = React43.useState(false);
7075
- const anchorRef = React43.useRef(null);
7132
+ const [open, setOpen] = React44.useState(false);
7133
+ const anchorRef = React44.useRef(null);
7076
7134
  if (!apps.length) return null;
7077
7135
  const items = apps.map((app) => ({
7078
7136
  id: app.key,
@@ -7086,7 +7144,7 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
7086
7144
  window.location.href = app.url;
7087
7145
  }
7088
7146
  }));
7089
- return /* @__PURE__ */ jsxs64(React43.Fragment, { children: [
7147
+ return /* @__PURE__ */ jsxs64(React44.Fragment, { children: [
7090
7148
  /* @__PURE__ */ jsx70("span", { ref: anchorRef, className: ["fd-appswitch-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx70(
7091
7149
  IconButton,
7092
7150
  {
@@ -7115,10 +7173,10 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
7115
7173
  }
7116
7174
 
7117
7175
  // src/components/platform/AppearanceMenu.tsx
7118
- import * as React45 from "react";
7176
+ import * as React46 from "react";
7119
7177
 
7120
7178
  // src/kits/appearance.ts
7121
- import * as React44 from "react";
7179
+ import * as React45 from "react";
7122
7180
  var DEFAULT_STORAGE_KEY = "fd-appearance";
7123
7181
  var DEFAULT_PREFS = {
7124
7182
  theme: "system",
@@ -7213,11 +7271,11 @@ function applyPrefs(prefs, root = document.documentElement) {
7213
7271
  else root.removeAttribute("data-motion");
7214
7272
  }
7215
7273
  function useAppearancePrefs(store) {
7216
- const prefs = React44.useSyncExternalStore(store.subscribe, store.getSnapshot);
7217
- React44.useEffect(() => {
7274
+ const prefs = React45.useSyncExternalStore(store.subscribe, store.getSnapshot);
7275
+ React45.useEffect(() => {
7218
7276
  applyPrefs(prefs);
7219
7277
  }, [prefs]);
7220
- React44.useEffect(() => {
7278
+ React45.useEffect(() => {
7221
7279
  if (prefs.theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
7222
7280
  return;
7223
7281
  }
@@ -7226,7 +7284,7 @@ function useAppearancePrefs(store) {
7226
7284
  mq.addEventListener("change", onChange);
7227
7285
  return () => mq.removeEventListener("change", onChange);
7228
7286
  }, [prefs]);
7229
- const update = React44.useCallback(
7287
+ const update = React45.useCallback(
7230
7288
  (patch) => {
7231
7289
  store.write({ ...store.getSnapshot(), ...patch });
7232
7290
  },
@@ -7249,15 +7307,15 @@ var TEXT_SCALE_OPTIONS = [
7249
7307
  { value: "xl", label: "XL" }
7250
7308
  ];
7251
7309
  function AppearanceMenu({ className = "", store: storeProp, storageKey, storage, defaults }) {
7252
- const store = React45.useMemo(
7310
+ const store = React46.useMemo(
7253
7311
  () => storeProp ?? createLocalStorageAppearanceStore({ storageKey, storage, defaults }),
7254
7312
  [storeProp, storageKey, storage, defaults]
7255
7313
  );
7256
7314
  const [prefs, update] = useAppearancePrefs(store);
7257
- const [open, setOpen] = React45.useState(false);
7258
- const anchorRef = React45.useRef(null);
7315
+ const [open, setOpen] = React46.useState(false);
7316
+ const anchorRef = React46.useRef(null);
7259
7317
  const patch = (p) => update(p);
7260
- return /* @__PURE__ */ jsxs65(React45.Fragment, { children: [
7318
+ return /* @__PURE__ */ jsxs65(React46.Fragment, { children: [
7261
7319
  /* @__PURE__ */ jsx71("span", { ref: anchorRef, className: ["fd-appearance-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx71(
7262
7320
  IconButton,
7263
7321
  {
@@ -7326,14 +7384,14 @@ function AppearanceMenu({ className = "", store: storeProp, storageKey, storage,
7326
7384
  }
7327
7385
 
7328
7386
  // src/components/platform/ApiSpecBrowser.tsx
7329
- import * as React46 from "react";
7387
+ import * as React47 from "react";
7330
7388
  import { jsx as jsx72, jsxs as jsxs66 } from "react/jsx-runtime";
7331
7389
  var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
7332
7390
  function Json({ obj }) {
7333
7391
  return /* @__PURE__ */ jsx72("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
7334
7392
  }
7335
7393
  function Endpoint({ s, onRequest }) {
7336
- const [tried, setTried] = React46.useState(null);
7394
+ const [tried, setTried] = React47.useState(null);
7337
7395
  const run = async () => {
7338
7396
  setTried("busy");
7339
7397
  const t0 = (window.performance || Date).now();
@@ -7406,10 +7464,10 @@ function ApiSpecBrowser({
7406
7464
  className = "",
7407
7465
  ...rest
7408
7466
  }) {
7409
- const [q, setQ] = React46.useState("");
7410
- const [method, setMethod] = React46.useState(null);
7411
- const [mod, setMod] = React46.useState(null);
7412
- const [listOnly, setListOnly] = React46.useState(false);
7467
+ const [q, setQ] = React47.useState("");
7468
+ const [method, setMethod] = React47.useState(null);
7469
+ const [mod, setMod] = React47.useState(null);
7470
+ const [listOnly, setListOnly] = React47.useState(false);
7413
7471
  const activeModule = modules && modules.find((m) => m.id === mod);
7414
7472
  const hits = spec.filter((s) => (!method || s.method === method) && (!listOnly || s.isList) && (!activeModule || activeModule.endpoints.indexOf(s.id) >= 0) && (!q || (s.path + " " + s.title + " " + s.purpose + " " + (s.usedBy || []).join(" ")).toLowerCase().includes(q.toLowerCase())));
7415
7473
  const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
@@ -7488,7 +7546,7 @@ function ApiSpecBrowser({
7488
7546
  }
7489
7547
 
7490
7548
  // src/components/platform/ProfilePage.tsx
7491
- import * as React47 from "react";
7549
+ import * as React48 from "react";
7492
7550
  import { jsx as jsx73, jsxs as jsxs67 } from "react/jsx-runtime";
7493
7551
  function fieldInitialValue(field, user) {
7494
7552
  if (field.value) return field.value(user);
@@ -7524,21 +7582,21 @@ function ProfilePage({
7524
7582
  }) {
7525
7583
  const { user: sessionUser, roles, permissions, saveProfile: saveProfile2 } = useSessionContext();
7526
7584
  const user = userProp || sessionUser;
7527
- const initialFieldValues = React47.useMemo(() => {
7585
+ const initialFieldValues = React48.useMemo(() => {
7528
7586
  const values2 = {};
7529
7587
  for (const field of fields) values2[field.key] = fieldInitialValue(field, user);
7530
7588
  return values2;
7531
7589
  }, [user.id, fields]);
7532
- const initialNotify = React47.useMemo(() => {
7590
+ const initialNotify = React48.useMemo(() => {
7533
7591
  const values2 = {};
7534
7592
  for (const n of notifications) values2[n.key] = !!user.notify?.[n.key];
7535
7593
  return values2;
7536
7594
  }, [user.id, notifications]);
7537
- const [values, setValues] = React47.useState(initialFieldValues);
7538
- const [notify, setNotify] = React47.useState(initialNotify);
7539
- const [saving, setSaving] = React47.useState(false);
7540
- const [saved, setSaved] = React47.useState(false);
7541
- const [prevUserId, setPrevUserId] = React47.useState(user.id);
7595
+ const [values, setValues] = React48.useState(initialFieldValues);
7596
+ const [notify, setNotify] = React48.useState(initialNotify);
7597
+ const [saving, setSaving] = React48.useState(false);
7598
+ const [saved, setSaved] = React48.useState(false);
7599
+ const [prevUserId, setPrevUserId] = React48.useState(user.id);
7542
7600
  if (user.id !== prevUserId) {
7543
7601
  setPrevUserId(user.id);
7544
7602
  setValues(initialFieldValues);
@@ -7726,11 +7784,11 @@ function ProfilePage({
7726
7784
  }
7727
7785
 
7728
7786
  // src/components/platform/SignInPage.tsx
7729
- import * as React48 from "react";
7787
+ import * as React49 from "react";
7730
7788
  import { Fragment as Fragment18, jsx as jsx74, jsxs as jsxs68 } from "react/jsx-runtime";
7731
7789
  function SignInPage({ productName, onSignIn, continueAs, error }) {
7732
- const [signingIn, setSigningIn] = React48.useState(false);
7733
- const [resuming, setResuming] = React48.useState(false);
7790
+ const [signingIn, setSigningIn] = React49.useState(false);
7791
+ const [resuming, setResuming] = React49.useState(false);
7734
7792
  function handleSignIn() {
7735
7793
  setSigningIn(true);
7736
7794
  onSignIn();
@@ -7919,10 +7977,10 @@ function GoogleGlyph() {
7919
7977
  }
7920
7978
 
7921
7979
  // src/components/platform/RoadmapTimeline.tsx
7922
- import * as React51 from "react";
7980
+ import * as React52 from "react";
7923
7981
 
7924
7982
  // src/kits/session.ts
7925
- import * as React49 from "react";
7983
+ import * as React50 from "react";
7926
7984
  var PERMISSION_CATALOG = [
7927
7985
  { group: "Plans", items: [
7928
7986
  { key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
@@ -9489,8 +9547,8 @@ function roadmap(overrides) {
9489
9547
  };
9490
9548
  }
9491
9549
  function useSession() {
9492
- const [s, setS] = React49.useState(getSession);
9493
- React49.useEffect(() => subscribe(setS), []);
9550
+ const [s, setS] = React50.useState(getSession);
9551
+ React50.useEffect(() => subscribe(setS), []);
9494
9552
  return s;
9495
9553
  }
9496
9554
  var SessionKit = {
@@ -9523,7 +9581,7 @@ var SessionKit = {
9523
9581
  };
9524
9582
  function DemoSessionProvider({ children }) {
9525
9583
  const session = useSession();
9526
- const value = React49.useMemo(
9584
+ const value = React50.useMemo(
9527
9585
  () => ({
9528
9586
  user: session.user,
9529
9587
  roles: session.roles,
@@ -9539,11 +9597,11 @@ function DemoSessionProvider({ children }) {
9539
9597
  }),
9540
9598
  [session]
9541
9599
  );
9542
- return React49.createElement(SessionProvider, { value }, children);
9600
+ return React50.createElement(SessionProvider, { value }, children);
9543
9601
  }
9544
9602
 
9545
9603
  // src/kits/runtime.ts
9546
- import * as React50 from "react";
9604
+ import * as React51 from "react";
9547
9605
  var WIRED_ENDPOINTS = [
9548
9606
  // Nothing yet. Every id below would come from a real service:
9549
9607
  // "plan.get", "placements.list", …
@@ -9714,8 +9772,8 @@ var RuntimeKit = {
9714
9772
  };
9715
9773
  RuntimeKit.declare(FEATURE_NEEDS);
9716
9774
  function useRuntimeMode() {
9717
- const [m, setM] = React50.useState(RuntimeKit.getMode());
9718
- React50.useEffect(() => RuntimeKit.subscribe(setM), []);
9775
+ const [m, setM] = React51.useState(RuntimeKit.getMode());
9776
+ React51.useEffect(() => RuntimeKit.subscribe(setM), []);
9719
9777
  return m;
9720
9778
  }
9721
9779
  function useFeatureStatus(key) {
@@ -9798,12 +9856,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
9798
9856
  }
9799
9857
  function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9800
9858
  const session = SessionKit.useSession();
9801
- const [project, setProject] = React51.useState("");
9802
- const [q, setQ] = React51.useState("");
9803
- const scrollRef = React51.useRef(null);
9804
- const nowRef = React51.useRef(null);
9805
- const rm = React51.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
9806
- const byKey = React51.useMemo(() => {
9859
+ const [project, setProject] = React52.useState("");
9860
+ const [q, setQ] = React52.useState("");
9861
+ const scrollRef = React52.useRef(null);
9862
+ const nowRef = React52.useRef(null);
9863
+ const rm = React52.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
9864
+ const byKey = React52.useMemo(() => {
9807
9865
  const m = {};
9808
9866
  rm.items.forEach((i) => {
9809
9867
  m[i.key] = i;
@@ -9812,7 +9870,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9812
9870
  }, [rm]);
9813
9871
  const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
9814
9872
  const projects = [...new Set(rm.items.map((i) => i.project))];
9815
- React51.useEffect(() => {
9873
+ React52.useEffect(() => {
9816
9874
  let raf1 = 0, raf2 = 0;
9817
9875
  const place = () => {
9818
9876
  const box = scrollRef.current, mark = nowRef.current;
@@ -9886,7 +9944,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9886
9944
  lastPhase = item.phase;
9887
9945
  const inPhase = items.filter((i) => i.phase === item.phase).length;
9888
9946
  const isBoundary = n === firstPending;
9889
- return /* @__PURE__ */ jsxs69(React51.Fragment, { children: [
9947
+ return /* @__PURE__ */ jsxs69(React52.Fragment, { children: [
9890
9948
  showPhase ? /* @__PURE__ */ jsxs69("div", { className: "fd-rm-era", children: [
9891
9949
  /* @__PURE__ */ jsxs69("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
9892
9950
  /* @__PURE__ */ jsx75("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
@@ -9930,7 +9988,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9930
9988
  }
9931
9989
 
9932
9990
  // src/components/platform/ComingSoon.tsx
9933
- import * as React52 from "react";
9991
+ import * as React53 from "react";
9934
9992
  import { createPortal as createPortal5 } from "react-dom";
9935
9993
  import { Fragment as Fragment20, jsx as jsx76, jsxs as jsxs70 } from "react/jsx-runtime";
9936
9994
  var BYPASS_STORE = "fd.soon.bypass.v1";
@@ -9948,8 +10006,8 @@ function writeBypassed(list) {
9948
10006
  }
9949
10007
  }
9950
10008
  function useBypass(key) {
9951
- const [on, setOn] = React52.useState(() => !!key && readBypassed().indexOf(key) >= 0);
9952
- React52.useEffect(() => {
10009
+ const [on, setOn] = React53.useState(() => !!key && readBypassed().indexOf(key) >= 0);
10010
+ React53.useEffect(() => {
9953
10011
  setOn(!!key && readBypassed().indexOf(key) >= 0);
9954
10012
  }, [key]);
9955
10013
  const set = (next) => {
@@ -9996,9 +10054,9 @@ function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onB
9996
10054
  ] });
9997
10055
  }
9998
10056
  function useHoverCard(open) {
9999
- const anchor = React52.useRef(null);
10000
- const [pos, setPos] = React52.useState(null);
10001
- React52.useLayoutEffect(() => {
10057
+ const anchor = React53.useRef(null);
10058
+ const [pos, setPos] = React53.useState(null);
10059
+ React53.useLayoutEffect(() => {
10002
10060
  if (!open || !anchor.current) {
10003
10061
  setPos(null);
10004
10062
  return;
@@ -10125,10 +10183,10 @@ function ComingSoon({
10125
10183
  ] });
10126
10184
  }
10127
10185
  function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
10128
- const [open, setOpen] = React52.useState(false);
10186
+ const [open, setOpen] = React53.useState(false);
10129
10187
  const [anchor, pos] = useHoverCard(open);
10130
10188
  const zIndex = useOverlayLayer(open);
10131
- const close = React52.useRef(null);
10189
+ const close = React53.useRef(null);
10132
10190
  const show = () => {
10133
10191
  if (close.current) {
10134
10192
  clearTimeout(close.current);
@@ -10144,7 +10202,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
10144
10202
  setOpen(false);
10145
10203
  }, 140);
10146
10204
  };
10147
- React52.useEffect(() => () => {
10205
+ React53.useEffect(() => () => {
10148
10206
  if (close.current) clearTimeout(close.current);
10149
10207
  }, []);
10150
10208
  return /* @__PURE__ */ jsxs70(
@@ -10315,14 +10373,14 @@ function PermissionHint({ perm, children }) {
10315
10373
  }
10316
10374
 
10317
10375
  // src/components/platform/ModeSwitch.tsx
10318
- import * as React53 from "react";
10376
+ import * as React54 from "react";
10319
10377
  import { Fragment as Fragment21, jsx as jsx78, jsxs as jsxs72 } from "react/jsx-runtime";
10320
10378
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
10321
10379
  const mode2 = useRuntimeMode();
10322
- const [open, setOpen] = React53.useState(false);
10380
+ const [open, setOpen] = React54.useState(false);
10323
10381
  const zIndex = useOverlayLayer(open);
10324
- const ref = React53.useRef(null);
10325
- React53.useEffect(() => {
10382
+ const ref = React54.useRef(null);
10383
+ React54.useEffect(() => {
10326
10384
  if (!open) return;
10327
10385
  const away = (e) => {
10328
10386
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -10578,7 +10636,7 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
10578
10636
  }
10579
10637
 
10580
10638
  // src/components/planner/MixGap.tsx
10581
- import * as React54 from "react";
10639
+ import * as React55 from "react";
10582
10640
  import { Fragment as Fragment22, jsx as jsx81, jsxs as jsxs75 } from "react/jsx-runtime";
10583
10641
  var MIX_GAP_LAYOUT = {
10584
10642
  trackHeight: 30,
@@ -10593,7 +10651,7 @@ function mixGapOffset(value, max) {
10593
10651
  }
10594
10652
  function MixGap({ rows = [], loading = false, className = "" }) {
10595
10653
  const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
10596
- const [hover, setHover] = React54.useState(null);
10654
+ const [hover, setHover] = React55.useState(null);
10597
10655
  const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
10598
10656
  const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
10599
10657
  const { trackHeight, barHeight, targetWidth, labelGap } = MIX_GAP_LAYOUT;
@@ -10736,7 +10794,7 @@ function ChannelContribution({
10736
10794
  }
10737
10795
 
10738
10796
  // src/components/planner/BudgetReallocator.tsx
10739
- import * as React55 from "react";
10797
+ import * as React56 from "react";
10740
10798
  import { Fragment as Fragment23, jsx as jsx83, jsxs as jsxs77 } from "react/jsx-runtime";
10741
10799
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
10742
10800
  function BudgetReallocator({
@@ -10752,8 +10810,8 @@ function BudgetReallocator({
10752
10810
  onCancel,
10753
10811
  className = ""
10754
10812
  }) {
10755
- const [draft, setDraft] = React55.useState(spend);
10756
- React55.useEffect(() => setDraft(spend), [spend]);
10813
+ const [draft, setDraft] = React56.useState(spend);
10814
+ React56.useEffect(() => setDraft(spend), [spend]);
10757
10815
  const dirty = draft !== spend;
10758
10816
  const nextCrp = scoreFor ? scoreFor(draft) : crp;
10759
10817
  const nextBand = bandFor(nextCrp);
@@ -10768,10 +10826,10 @@ function BudgetReallocator({
10768
10826
  setDraft(spend);
10769
10827
  if (onCancel) onCancel();
10770
10828
  };
10771
- const trackRef = React55.useRef(null);
10772
- const chipRef = React55.useRef(null);
10773
- const [chipLeft, setChipLeft] = React55.useState(null);
10774
- React55.useLayoutEffect(() => {
10829
+ const trackRef = React56.useRef(null);
10830
+ const chipRef = React56.useRef(null);
10831
+ const [chipLeft, setChipLeft] = React56.useState(null);
10832
+ React56.useLayoutEffect(() => {
10775
10833
  const track = trackRef.current;
10776
10834
  if (!dirty || !track) {
10777
10835
  setChipLeft(null);
@@ -10958,7 +11016,7 @@ function ImportanceTag({ level, showLabel = true, size = "sm", className = "" })
10958
11016
  }
10959
11017
 
10960
11018
  // src/components/planner/PreferenceMeter.tsx
10961
- import * as React56 from "react";
11019
+ import * as React57 from "react";
10962
11020
  import { jsx as jsx86, jsxs as jsxs80 } from "react/jsx-runtime";
10963
11021
  function preferenceScore(criteria) {
10964
11022
  const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
@@ -10988,7 +11046,7 @@ function PreferenceMeter({
10988
11046
  className = ""
10989
11047
  }) {
10990
11048
  const value = score == null ? preferenceScore(criteria) : score;
10991
- const segments = React56.useMemo(() => preferenceSegments(criteria), [criteria]);
11049
+ const segments = React57.useMemo(() => preferenceSegments(criteria), [criteria]);
10992
11050
  return /* @__PURE__ */ jsx86(
10993
11051
  ScoreMeter,
10994
11052
  {
@@ -11027,10 +11085,10 @@ function PreferenceMeter({
11027
11085
  }
11028
11086
 
11029
11087
  // src/components/chat/AgentChatPanel.tsx
11030
- import * as React62 from "react";
11088
+ import * as React63 from "react";
11031
11089
 
11032
11090
  // src/components/chat/chatEngine.ts
11033
- import * as React57 from "react";
11091
+ import * as React58 from "react";
11034
11092
  var CHAT_UNAVAILABLE = "chat_unavailable";
11035
11093
  var JOB_PENDING = ["queued", "running"];
11036
11094
  var JOB_SUCCESS = ["completed", "recovered"];
@@ -11084,22 +11142,22 @@ function useChatEngine(opts) {
11084
11142
  onClear,
11085
11143
  onFeedback
11086
11144
  } = opts || {};
11087
- const [status, setStatus] = React57.useState("idle");
11088
- const [threadId, setThreadId] = React57.useState(null);
11089
- const [messages, setMessages] = React57.useState([]);
11090
- const [queue, setQueue] = React57.useState([]);
11091
- const [fatal, setFatal] = React57.useState(null);
11092
- const [busy, setBusy] = React57.useState(false);
11093
- const [turnStartedAt, setTurnStartedAt] = React57.useState(null);
11094
- const listRef = React57.useRef([]);
11095
- const queueRef = React57.useRef([]);
11096
- const busyRef = React57.useRef(false);
11097
- const stoppedRef = React57.useRef(false);
11098
- const abortRef = React57.useRef(null);
11099
- const serverCount = React57.useRef(0);
11100
- const threadRef = React57.useRef(null);
11101
- const mounted = React57.useRef(true);
11102
- React57.useEffect(() => {
11145
+ const [status, setStatus] = React58.useState("idle");
11146
+ const [threadId, setThreadId] = React58.useState(null);
11147
+ const [messages, setMessages] = React58.useState([]);
11148
+ const [queue, setQueue] = React58.useState([]);
11149
+ const [fatal, setFatal] = React58.useState(null);
11150
+ const [busy, setBusy] = React58.useState(false);
11151
+ const [turnStartedAt, setTurnStartedAt] = React58.useState(null);
11152
+ const listRef = React58.useRef([]);
11153
+ const queueRef = React58.useRef([]);
11154
+ const busyRef = React58.useRef(false);
11155
+ const stoppedRef = React58.useRef(false);
11156
+ const abortRef = React58.useRef(null);
11157
+ const serverCount = React58.useRef(0);
11158
+ const threadRef = React58.useRef(null);
11159
+ const mounted = React58.useRef(true);
11160
+ React58.useEffect(() => {
11103
11161
  mounted.current = true;
11104
11162
  return () => {
11105
11163
  mounted.current = false;
@@ -11121,14 +11179,14 @@ function useChatEngine(opts) {
11121
11179
  }
11122
11180
  return false;
11123
11181
  };
11124
- const loadThread = React57.useCallback(async (id) => {
11182
+ const loadThread = React58.useCallback(async (id) => {
11125
11183
  const data = await apiAdapter.getThread(id);
11126
11184
  const list = [...data && data.messages || []];
11127
11185
  serverCount.current = list.length;
11128
11186
  commit(list);
11129
11187
  return list;
11130
11188
  }, [apiAdapter]);
11131
- React57.useEffect(() => {
11189
+ React58.useEffect(() => {
11132
11190
  if (!apiAdapter) {
11133
11191
  setStatus("idle");
11134
11192
  setFatal(null);
@@ -11341,7 +11399,7 @@ function useChatEngine(opts) {
11341
11399
  await dispatchTurn(turn);
11342
11400
  }
11343
11401
  }
11344
- const send = React57.useCallback((text, attachments) => {
11402
+ const send = React58.useCallback((text, attachments) => {
11345
11403
  const body = (text || "").trim();
11346
11404
  if (!body && !(attachments && attachments.length)) return;
11347
11405
  if (status === "disconnected") return;
@@ -11351,7 +11409,7 @@ function useChatEngine(opts) {
11351
11409
  stoppedRef.current = false;
11352
11410
  drain();
11353
11411
  }, [status]);
11354
- const stop = React57.useCallback(() => {
11412
+ const stop = React58.useCallback(() => {
11355
11413
  stoppedRef.current = true;
11356
11414
  const ac = abortRef.current;
11357
11415
  if (ac) {
@@ -11370,11 +11428,11 @@ function useChatEngine(opts) {
11370
11428
  store.del(STORAGE_PREFIX + threadRef.current);
11371
11429
  }
11372
11430
  }, [apiAdapter]);
11373
- const removeQueued = React57.useCallback((id) => {
11431
+ const removeQueued = React58.useCallback((id) => {
11374
11432
  queueRef.current = queueRef.current.filter((t) => t.id !== id);
11375
11433
  setQueue(queueRef.current.slice());
11376
11434
  }, []);
11377
- const retry = React57.useCallback(() => {
11435
+ const retry = React58.useCallback(() => {
11378
11436
  const list = listRef.current;
11379
11437
  let at = -1;
11380
11438
  for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
@@ -11390,19 +11448,19 @@ function useChatEngine(opts) {
11390
11448
  setQueue(queueRef.current.slice());
11391
11449
  drain();
11392
11450
  }, []);
11393
- const clear = React57.useCallback(() => {
11451
+ const clear = React58.useCallback(() => {
11394
11452
  commit([]);
11395
11453
  serverCount.current = 0;
11396
11454
  queueRef.current = [];
11397
11455
  setQueue([]);
11398
11456
  onClear && onClear();
11399
11457
  }, [onClear]);
11400
- const setFeedback = React57.useCallback((id, value) => {
11458
+ const setFeedback = React58.useCallback((id, value) => {
11401
11459
  patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
11402
11460
  const msg = listRef.current.find((m) => m.id === id);
11403
11461
  onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
11404
11462
  }, [onFeedback]);
11405
- const reload = React57.useCallback(async () => {
11463
+ const reload = React58.useCallback(async () => {
11406
11464
  if (!threadRef.current) return;
11407
11465
  setStatus("loading");
11408
11466
  try {
@@ -11440,10 +11498,10 @@ var ChatKit = {
11440
11498
  };
11441
11499
 
11442
11500
  // src/components/chat/ChatTranscript.tsx
11443
- import * as React59 from "react";
11501
+ import * as React60 from "react";
11444
11502
 
11445
11503
  // src/components/chat/ChatTurn.tsx
11446
- import * as React58 from "react";
11504
+ import * as React59 from "react";
11447
11505
  import { jsx as jsx87, jsxs as jsxs81 } from "react/jsx-runtime";
11448
11506
  function JsonView({ value }) {
11449
11507
  let text;
@@ -11480,7 +11538,7 @@ function PacketCard({ packet, schema, render, onApply, applied }) {
11480
11538
  ] });
11481
11539
  }
11482
11540
  function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
11483
- const [open, setOpen] = React58.useState(defaultOpen);
11541
+ const [open, setOpen] = React59.useState(defaultOpen);
11484
11542
  if (!text) return null;
11485
11543
  return /* @__PURE__ */ jsxs81("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
11486
11544
  /* @__PURE__ */ jsxs81("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -11497,7 +11555,7 @@ function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
11497
11555
  ] });
11498
11556
  }
11499
11557
  function Citations({ items = [], onOpen }) {
11500
- const [open, setOpen] = React58.useState(false);
11558
+ const [open, setOpen] = React59.useState(false);
11501
11559
  if (!items.length) return null;
11502
11560
  return /* @__PURE__ */ jsxs81("div", { className: "fdc-cites", children: [
11503
11561
  /* @__PURE__ */ jsxs81("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -11523,7 +11581,7 @@ function clampText(text, max) {
11523
11581
  return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
11524
11582
  }
11525
11583
  function MessageBody({ message: m, ctx }) {
11526
- const [expanded, setExpanded] = React58.useState(false);
11584
+ const [expanded, setExpanded] = React59.useState(false);
11527
11585
  const isUser = m.role === "user";
11528
11586
  const raw = m.text || "";
11529
11587
  const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
@@ -11590,9 +11648,9 @@ function MessageBody({ message: m, ctx }) {
11590
11648
  ] });
11591
11649
  }
11592
11650
  function useCopyRun() {
11593
- const [done, setDone] = React58.useState(false);
11594
- const t = React58.useRef(null);
11595
- React58.useEffect(() => () => {
11651
+ const [done, setDone] = React59.useState(false);
11652
+ const t = React59.useRef(null);
11653
+ React59.useEffect(() => () => {
11596
11654
  if (t.current) clearTimeout(t.current);
11597
11655
  }, []);
11598
11656
  return [done, (text) => {
@@ -11619,7 +11677,7 @@ function RunActions({ group, ctx }) {
11619
11677
  ] }),
11620
11678
  isAssistant && ctx.onRetry ? /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
11621
11679
  group.role === "user" && ctx.onEdit ? /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
11622
- isAssistant && ctx.onFeedback ? /* @__PURE__ */ jsxs81(React58.Fragment, { children: [
11680
+ isAssistant && ctx.onFeedback ? /* @__PURE__ */ jsxs81(React59.Fragment, { children: [
11623
11681
  /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act" + (fb === "up" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "up"), "aria-pressed": fb === "up", "aria-label": "Good response", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
11624
11682
  /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act" + (fb === "down" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "down"), "aria-pressed": fb === "down", "aria-label": "Bad response", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
11625
11683
  ] }) : null,
@@ -11714,10 +11772,10 @@ function ChatTranscript({
11714
11772
  renderEmpty,
11715
11773
  className = ""
11716
11774
  }) {
11717
- const scroller = React59.useRef(null);
11718
- const stick = React59.useRef(true);
11719
- const [pill, setPill] = React59.useState(0);
11720
- const seen = React59.useRef(0);
11775
+ const scroller = React60.useRef(null);
11776
+ const stick = React60.useRef(true);
11777
+ const [pill, setPill] = React60.useState(0);
11778
+ const seen = React60.useRef(0);
11721
11779
  const toBottom = (smooth) => {
11722
11780
  const el = scroller.current;
11723
11781
  if (!el) return;
@@ -11736,7 +11794,7 @@ function ChatTranscript({
11736
11794
  seen.current = messages.length;
11737
11795
  }
11738
11796
  };
11739
- React59.useLayoutEffect(() => {
11797
+ React60.useLayoutEffect(() => {
11740
11798
  const el = scroller.current;
11741
11799
  if (!el) return;
11742
11800
  if (stick.current) {
@@ -11744,7 +11802,7 @@ function ChatTranscript({
11744
11802
  seen.current = messages.length;
11745
11803
  } else setPill(Math.max(0, messages.length - seen.current));
11746
11804
  }, [messages]);
11747
- React59.useEffect(() => {
11805
+ React60.useEffect(() => {
11748
11806
  const el = scroller.current;
11749
11807
  const inner = el && el.firstChild;
11750
11808
  if (!el || !inner || typeof ResizeObserver === "undefined") return;
@@ -11754,7 +11812,7 @@ function ChatTranscript({
11754
11812
  ro.observe(inner);
11755
11813
  return () => ro.disconnect();
11756
11814
  }, []);
11757
- const groups = React59.useMemo(() => groupMessages(messages), [messages]);
11815
+ const groups = React60.useMemo(() => groupMessages(messages), [messages]);
11758
11816
  const empty = !messages.length && status === "ready";
11759
11817
  return /* @__PURE__ */ jsxs82("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
11760
11818
  /* @__PURE__ */ jsxs82("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
@@ -11778,7 +11836,7 @@ function ChatTranscript({
11778
11836
  const prev = groups[i - 1];
11779
11837
  const k = dayKey(g.messages[0].timestamp);
11780
11838
  const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
11781
- return /* @__PURE__ */ jsxs82(React59.Fragment, { children: [
11839
+ return /* @__PURE__ */ jsxs82(React60.Fragment, { children: [
11782
11840
  showDay ? /* @__PURE__ */ jsx88("div", { className: "fdc-day", children: /* @__PURE__ */ jsx88("span", { children: dayLabel(k) }) }) : null,
11783
11841
  /* @__PURE__ */ jsx88(ChatTurn, { group: g, ctx })
11784
11842
  ] }, g.key || i);
@@ -11795,7 +11853,7 @@ function ChatTranscript({
11795
11853
  var TranscriptKit = { groupMessages };
11796
11854
 
11797
11855
  // src/components/chat/ChatComposer.tsx
11798
- import * as React60 from "react";
11856
+ import * as React61 from "react";
11799
11857
  import { jsx as jsx89, jsxs as jsxs83 } from "react/jsx-runtime";
11800
11858
  function ChatComposer({
11801
11859
  onSubmit,
@@ -11823,17 +11881,17 @@ function ChatComposer({
11823
11881
  onReject,
11824
11882
  onOpenAttachment
11825
11883
  }) {
11826
- const [text, setText] = React60.useState(draft || "");
11827
- const [trigger, setTrigger] = React60.useState(null);
11828
- const [mentionItems, setMentionItems] = React60.useState([]);
11829
- const [listening, setListening] = React60.useState(false);
11830
- const [notice, setNotice] = React60.useState(null);
11831
- const editor = React60.useRef(null);
11832
- const wrap = React60.useRef(null);
11833
- const stopVoice = React60.useRef(null);
11884
+ const [text, setText] = React61.useState(draft || "");
11885
+ const [trigger, setTrigger] = React61.useState(null);
11886
+ const [mentionItems, setMentionItems] = React61.useState([]);
11887
+ const [listening, setListening] = React61.useState(false);
11888
+ const [notice, setNotice] = React61.useState(null);
11889
+ const editor = React61.useRef(null);
11890
+ const wrap = React61.useRef(null);
11891
+ const stopVoice = React61.useRef(null);
11834
11892
  const staged = useStagedFiles(fileUploadHandler, { onError: () => {
11835
11893
  } });
11836
- React60.useEffect(() => {
11894
+ React61.useEffect(() => {
11837
11895
  if (draft != null && draft !== text) setText(draft);
11838
11896
  }, [draft]);
11839
11897
  const change = (v) => {
@@ -11869,7 +11927,7 @@ function ChatComposer({
11869
11927
  e.preventDefault();
11870
11928
  staged.add(found.files);
11871
11929
  };
11872
- React60.useEffect(() => {
11930
+ React61.useEffect(() => {
11873
11931
  if (!trigger || trigger.type !== "mention" || !mentionSources) {
11874
11932
  setMentionItems([]);
11875
11933
  return;
@@ -11887,7 +11945,7 @@ function ChatComposer({
11887
11945
  const q = (trigger.query || "").toLowerCase();
11888
11946
  setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
11889
11947
  }, [trigger, mentionSources]);
11890
- const slashItems = React60.useMemo(() => {
11948
+ const slashItems = React61.useMemo(() => {
11891
11949
  if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
11892
11950
  const q = (trigger.query || "").toLowerCase();
11893
11951
  return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
@@ -12061,12 +12119,12 @@ function ChatComposer({
12061
12119
  }
12062
12120
 
12063
12121
  // src/components/chat/ChatSessionBar.tsx
12064
- import * as React61 from "react";
12122
+ import * as React62 from "react";
12065
12123
  import { jsx as jsx90, jsxs as jsxs84 } from "react/jsx-runtime";
12066
12124
  var compact2 = meterFormats.compact;
12067
12125
  function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
12068
- const [open, setOpen] = React61.useState(false);
12069
- const anchor = React61.useRef(null);
12126
+ const [open, setOpen] = React62.useState(false);
12127
+ const anchor = React62.useRef(null);
12070
12128
  const stats = sessionStats || null;
12071
12129
  const cu = contextUsage || null;
12072
12130
  const limits = usageLimits || null;
@@ -12081,7 +12139,7 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
12081
12139
  if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
12082
12140
  if (cu) bits.push(pct + "% context");
12083
12141
  const expandable = !!(cu || limits);
12084
- return /* @__PURE__ */ jsxs84(React61.Fragment, { children: [
12142
+ return /* @__PURE__ */ jsxs84(React62.Fragment, { children: [
12085
12143
  /* @__PURE__ */ jsxs84("div", { className: "fdc-bar", children: [
12086
12144
  /* @__PURE__ */ jsxs84(
12087
12145
  "button",
@@ -12222,7 +12280,7 @@ function ModelControls({
12222
12280
  ] })
12223
12281
  });
12224
12282
  }
12225
- return /* @__PURE__ */ jsxs84(React61.Fragment, { children: [
12283
+ return /* @__PURE__ */ jsxs84(React62.Fragment, { children: [
12226
12284
  /* @__PURE__ */ jsx90(
12227
12285
  MenuButton,
12228
12286
  {
@@ -12339,11 +12397,11 @@ function AgentChatPanel({
12339
12397
  onFeedback,
12340
12398
  onEditMessage
12341
12399
  }) {
12342
- const [panelWidth, setPanelWidth] = React62.useState(width);
12343
- const [applied, setApplied] = React62.useState({});
12344
- const [draft, setDraft] = React62.useState("");
12345
- const dragging = React62.useRef(null);
12346
- React62.useEffect(() => setPanelWidth(width), [width]);
12400
+ const [panelWidth, setPanelWidth] = React63.useState(width);
12401
+ const [applied, setApplied] = React63.useState({});
12402
+ const [draft, setDraft] = React63.useState("");
12403
+ const dragging = React63.useRef(null);
12404
+ React63.useEffect(() => setPanelWidth(width), [width]);
12347
12405
  const engine = useChatEngine({
12348
12406
  contextType,
12349
12407
  contextId,
@@ -12547,7 +12605,7 @@ function AgentChatPanel({
12547
12605
  }
12548
12606
 
12549
12607
  // src/components/files/FileViewer.tsx
12550
- import * as React63 from "react";
12608
+ import * as React64 from "react";
12551
12609
  import { jsx as jsx92, jsxs as jsxs86 } from "react/jsx-runtime";
12552
12610
  function previewKindOf(file) {
12553
12611
  const mime = (file.mime || "").toLowerCase();
@@ -12604,13 +12662,13 @@ function parseDelimited(text, delimiter) {
12604
12662
  return rows;
12605
12663
  }
12606
12664
  function usePreview(file, kind, resolveUrl) {
12607
- const [state, setState] = React63.useState({ status: "loading" });
12608
- const [attempt, setAttempt] = React63.useState(0);
12609
- const resolveRef = React63.useRef(resolveUrl);
12610
- React63.useEffect(() => {
12665
+ const [state, setState] = React64.useState({ status: "loading" });
12666
+ const [attempt, setAttempt] = React64.useState(0);
12667
+ const resolveRef = React64.useRef(resolveUrl);
12668
+ React64.useEffect(() => {
12611
12669
  resolveRef.current = resolveUrl;
12612
12670
  }, [resolveUrl]);
12613
- React63.useEffect(() => {
12671
+ React64.useEffect(() => {
12614
12672
  if (kind === "none") {
12615
12673
  setState({ status: "ready" });
12616
12674
  return;
@@ -12640,7 +12698,7 @@ function usePreview(file, kind, resolveUrl) {
12640
12698
  if (ac) ac.abort();
12641
12699
  };
12642
12700
  }, [file.id, kind, attempt]);
12643
- return { ...state, retry: React63.useCallback(() => setAttempt((a) => a + 1), []) };
12701
+ return { ...state, retry: React64.useCallback(() => setAttempt((a) => a + 1), []) };
12644
12702
  }
12645
12703
  function PreviewBody({ file, kind, state }) {
12646
12704
  if (kind === "image") return /* @__PURE__ */ jsx92("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
@@ -12685,13 +12743,13 @@ function PreviewBody({ file, kind, state }) {
12685
12743
  ] });
12686
12744
  }
12687
12745
  function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", onClose, actions, className = "" }) {
12688
- const kind = React63.useMemo(() => previewKindOf(file), [file]);
12746
+ const kind = React64.useMemo(() => previewKindOf(file), [file]);
12689
12747
  const preview = usePreview(file, kind, resolveUrl);
12690
- const [tab, setTab] = React63.useState("preview");
12691
- const [downloading, setDownloading] = React63.useState(false);
12692
- const [downloadError, setDownloadError] = React63.useState(null);
12748
+ const [tab, setTab] = React64.useState("preview");
12749
+ const [downloading, setDownloading] = React64.useState(false);
12750
+ const [downloadError, setDownloadError] = React64.useState(null);
12693
12751
  const hasSide = sidePanel != null;
12694
- React63.useEffect(() => {
12752
+ React64.useEffect(() => {
12695
12753
  setTab("preview");
12696
12754
  }, [file.id]);
12697
12755
  const download = () => {
@@ -12758,7 +12816,7 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
12758
12816
  }
12759
12817
 
12760
12818
  // src/components/files/FileBrowser.tsx
12761
- import * as React64 from "react";
12819
+ import * as React65 from "react";
12762
12820
  import { Fragment as Fragment27, jsx as jsx93, jsxs as jsxs87 } from "react/jsx-runtime";
12763
12821
  function childrenOf(folders, parentId) {
12764
12822
  return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
@@ -12829,10 +12887,10 @@ function FolderPickerMenu({
12829
12887
  destinations,
12830
12888
  onPick
12831
12889
  }) {
12832
- const [open, setOpen] = React64.useState(false);
12890
+ const [open, setOpen] = React65.useState(false);
12833
12891
  const zIndex = useOverlayLayer(open);
12834
- const ref = React64.useRef(null);
12835
- React64.useEffect(() => {
12892
+ const ref = React65.useRef(null);
12893
+ React65.useEffect(() => {
12836
12894
  if (!open) return;
12837
12895
  const away = (e) => {
12838
12896
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -12884,8 +12942,8 @@ function FolderPickerMenu({
12884
12942
  ] });
12885
12943
  }
12886
12944
  function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
12887
- const [over, setOver] = React64.useState(false);
12888
- const dragCounter = React64.useRef(0);
12945
+ const [over, setOver] = React65.useState(false);
12946
+ const dragCounter = React65.useRef(0);
12889
12947
  const dropHandlers = isDropTarget ? {
12890
12948
  onDragEnter: (e) => {
12891
12949
  if (!hasInternalPayload(e)) return;
@@ -12933,8 +12991,8 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
12933
12991
  }
12934
12992
  function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
12935
12993
  const kids = childrenOf(folders, folder.id);
12936
- const [expanded, setExpanded] = React64.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
12937
- React64.useEffect(() => {
12994
+ const [expanded, setExpanded] = React65.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
12995
+ React65.useEffect(() => {
12938
12996
  if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
12939
12997
  }, [currentFolderId, folder.id, folders]);
12940
12998
  const isCurrent = folder.id === currentFolderId;
@@ -13000,15 +13058,15 @@ function FileBrowser({
13000
13058
  const canUpload = perm.canUpload !== false && !!onUpload;
13001
13059
  const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
13002
13060
  const canDeleteFolder = perm.canDeleteFolder !== false && !!onDeleteFolder;
13003
- const [createOpen, setCreateOpen] = React64.useState(false);
13004
- const [createName, setCreateName] = React64.useState("");
13005
- const [renameTarget, setRenameTarget] = React64.useState(null);
13006
- const [renameValue, setRenameValue] = React64.useState("");
13007
- const [deleteTarget, setDeleteTarget] = React64.useState(null);
13008
- const uploadInputRef = React64.useRef(null);
13009
- const crumbFolders = React64.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
13010
- const subfolders = React64.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
13011
- const currentFiles = React64.useMemo(
13061
+ const [createOpen, setCreateOpen] = React65.useState(false);
13062
+ const [createName, setCreateName] = React65.useState("");
13063
+ const [renameTarget, setRenameTarget] = React65.useState(null);
13064
+ const [renameValue, setRenameValue] = React65.useState("");
13065
+ const [deleteTarget, setDeleteTarget] = React65.useState(null);
13066
+ const uploadInputRef = React65.useRef(null);
13067
+ const crumbFolders = React65.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
13068
+ const subfolders = React65.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
13069
+ const currentFiles = React65.useMemo(
13012
13070
  () => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
13013
13071
  [files, currentFolderId]
13014
13072
  );
@@ -13240,8 +13298,8 @@ function BreadcrumbCrumb({
13240
13298
  isDropTarget,
13241
13299
  onDropPayload
13242
13300
  }) {
13243
- const [over, setOver] = React64.useState(false);
13244
- const dragCounter = React64.useRef(0);
13301
+ const [over, setOver] = React65.useState(false);
13302
+ const dragCounter = React65.useRef(0);
13245
13303
  const dropHandlers = isDropTarget ? {
13246
13304
  onDragEnter: (e) => {
13247
13305
  if (!hasInternalPayload(e)) return;
@@ -13270,7 +13328,7 @@ function BreadcrumbCrumb({
13270
13328
  }
13271
13329
 
13272
13330
  // src/kits/query.ts
13273
- import * as React65 from "react";
13331
+ import * as React66 from "react";
13274
13332
  var isMultiSelectShaped = (v) => !!v && typeof v === "object" && Array.isArray(v.values);
13275
13333
  var isRangeShaped = (v) => !!v && typeof v === "object" && ("min" in v || "max" in v);
13276
13334
  var isDateShaped = (v) => !!v && typeof v === "object" && "kind" in v;
@@ -13325,22 +13383,22 @@ function compare(a, b, dir) {
13325
13383
  var API = null;
13326
13384
  var PREFS = null;
13327
13385
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
13328
- const [query, setQuery] = React65.useState(() => {
13386
+ const [query, setQuery] = React66.useState(() => {
13329
13387
  const store = PREFS || window.PlannerPrefs;
13330
13388
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
13331
13389
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
13332
13390
  });
13333
- const savePref = React65.useCallback((patch2) => {
13391
+ const savePref = React66.useCallback((patch2) => {
13334
13392
  const store = PREFS || window.PlannerPrefs;
13335
13393
  if (prefsKey && store) store.setTable(prefsKey, patch2);
13336
13394
  }, [prefsKey]);
13337
- const [res, setRes] = React65.useState(null);
13338
- const [loading, setLoading] = React65.useState(true);
13339
- const seq2 = React65.useRef(0);
13395
+ const [res, setRes] = React66.useState(null);
13396
+ const [loading, setLoading] = React66.useState(true);
13397
+ const seq2 = React66.useRef(0);
13340
13398
  const depKey = (deps || []).join("|");
13341
13399
  const paramKey = JSON.stringify(params || {});
13342
13400
  const queryKey = JSON.stringify(query);
13343
- React65.useEffect(() => {
13401
+ React66.useEffect(() => {
13344
13402
  const id = ++seq2.current;
13345
13403
  setLoading(true);
13346
13404
  const t = setTimeout(() => {
@@ -13659,12 +13717,12 @@ function createVersionStore(initialState, options) {
13659
13717
  }
13660
13718
 
13661
13719
  // src/kits/documentTitle.ts
13662
- import * as React66 from "react";
13720
+ import * as React67 from "react";
13663
13721
  function formatDocumentTitle(routeTitle, appName) {
13664
13722
  return [routeTitle, appName, "Flytedesk"].filter((part) => !!part && part.length > 0).join(" \xB7 ");
13665
13723
  }
13666
13724
  function useDocumentTitle(routeTitle, appName) {
13667
- React66.useEffect(() => {
13725
+ React67.useEffect(() => {
13668
13726
  if (typeof document === "undefined") return;
13669
13727
  document.title = formatDocumentTitle(routeTitle, appName);
13670
13728
  }, [routeTitle, appName]);