@flytedan/flytebot-design-system 0.33.0 → 0.35.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,19 +3478,24 @@ 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
+ const [resolvedAt, setResolvedAt] = React25.useState(0);
3492
+ React25.useEffect(() => {
3485
3493
  if (mode2 === "paste") pasteWrapRef.current?.querySelector("textarea")?.focus();
3486
3494
  }, [mode2]);
3495
+ React25.useLayoutEffect(() => {
3496
+ if (resolvedAt === 0) return;
3497
+ pasteWrapRef.current?.querySelector("textarea")?.focus();
3498
+ }, [resolvedAt]);
3487
3499
  const doResolve = () => {
3488
3500
  if (!field.resolve) return;
3489
3501
  const tokens = Array.from(new Set(pasteText.split(/[\n,]/).map((t) => t.trim()).filter(Boolean)));
@@ -3500,12 +3512,13 @@ function MultiSelectPanel({
3500
3512
  setPasteError("This filter allows at most " + field.maxValues + " values; adding this list would make " + merged.size + ".");
3501
3513
  return;
3502
3514
  }
3503
- const applied = commit(Array.from(merged.values()), operatorRef.current);
3515
+ const applied = commit(Array.from(merged.values()));
3504
3516
  if (!applied.ok) {
3505
3517
  setPasteError(applied.reason);
3506
3518
  return;
3507
3519
  }
3508
3520
  setPasteText("");
3521
+ setResolvedAt((n) => n + 1);
3509
3522
  }, (err) => {
3510
3523
  if (!mountedRef.current) return;
3511
3524
  setPasteLoading(false);
@@ -3642,10 +3655,9 @@ function MultiSelectPanel({
3642
3655
  );
3643
3656
  }
3644
3657
  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;
3658
+ const invalidRef = React25.useRef(/* @__PURE__ */ new Set());
3659
+ const [, rerender] = React25.useReducer((n) => n + 1, 0);
3660
+ const onFlipRef = useLatest(onFlip);
3649
3661
  const record = (name, valid) => {
3650
3662
  const invalid = invalidRef.current;
3651
3663
  if (valid !== invalid.has(name)) return false;
@@ -3662,6 +3674,12 @@ function useTextValidity(onFlip) {
3662
3674
  /** For an input that is about to unmount: it has nothing left to show. */
3663
3675
  forget: (name) => {
3664
3676
  record(name, true);
3677
+ },
3678
+ /** For a panel that is about to remount every input: each reports afresh on mount. */
3679
+ reset: () => {
3680
+ if (invalidRef.current.size === 0) return;
3681
+ invalidRef.current.clear();
3682
+ rerender();
3665
3683
  }
3666
3684
  };
3667
3685
  }
@@ -3669,6 +3687,40 @@ function applicable(field, candidate, textValid) {
3669
3687
  if (!textValid || candidate === void 0) return void 0;
3670
3688
  return validateFilterValue(field, candidate).ok ? candidate : void 0;
3671
3689
  }
3690
+ function sameValue(a, b) {
3691
+ if (a === b) return true;
3692
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
3693
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
3694
+ const left = a;
3695
+ const right = b;
3696
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)])) {
3697
+ if (!sameValue(left[key], right[key])) return false;
3698
+ }
3699
+ return true;
3700
+ }
3701
+ function useReseedFromProps(value, applies, reseed) {
3702
+ const seen = React25.useRef(value);
3703
+ React25.useLayoutEffect(() => {
3704
+ if (seen.current === value) return;
3705
+ seen.current = value;
3706
+ if (!sameValue(value, applies())) reseed(value);
3707
+ }, [value, applies, reseed]);
3708
+ }
3709
+ function usePreserveFocusAcrossRemount(seed) {
3710
+ const pendingId = React25.useRef(null);
3711
+ const capture = (ids) => {
3712
+ const active = document.activeElement;
3713
+ pendingId.current = active instanceof HTMLElement && ids.includes(active.id) ? active.id : null;
3714
+ };
3715
+ React25.useLayoutEffect(() => {
3716
+ const id = pendingId.current;
3717
+ if (!id) return;
3718
+ pendingId.current = null;
3719
+ document.getElementById(id)?.focus();
3720
+ }, [seed]);
3721
+ return capture;
3722
+ }
3723
+ var rangeDraft = (value) => ({ min: value?.min, max: value?.max });
3672
3724
  function RangePanel({
3673
3725
  field,
3674
3726
  value,
@@ -3676,13 +3728,27 @@ function RangePanel({
3676
3728
  onCommit,
3677
3729
  onClose
3678
3730
  }) {
3679
- const [draft, setDraft] = React24.useState({ min: value?.min, max: value?.max });
3680
- const draftRef = React24.useRef(draft);
3681
- const publish = () => {
3731
+ const [draft, setDraft] = React25.useState(() => rangeDraft(value));
3732
+ const [seed, setSeed] = React25.useState(0);
3733
+ const preserveFocus = usePreserveFocusAcrossRemount(seed);
3734
+ const minId = field.key + "-min";
3735
+ const maxId = field.key + "-max";
3736
+ const draftRef = React25.useRef(draft);
3737
+ const shown = () => {
3682
3738
  const { min, max } = draftRef.current;
3683
- onCommit(applicable(field, min == null && max == null ? void 0 : { min, max }, validity.allValid()));
3739
+ return applicable(field, min == null && max == null ? void 0 : { min, max }, validity.allValid());
3740
+ };
3741
+ const publish = () => {
3742
+ onCommit(shown());
3684
3743
  };
3685
3744
  const validity = useTextValidity(publish);
3745
+ useReseedFromProps(value, shown, (next) => {
3746
+ preserveFocus([minId, maxId]);
3747
+ draftRef.current = rangeDraft(next);
3748
+ setDraft(draftRef.current);
3749
+ validity.reset();
3750
+ setSeed((n) => n + 1);
3751
+ });
3686
3752
  const draftMin = draft.min;
3687
3753
  const draftMax = draft.max;
3688
3754
  const isEmpty = draftMin == null && draftMax == null;
@@ -3710,6 +3776,7 @@ function RangePanel({
3710
3776
  /* @__PURE__ */ jsx45(
3711
3777
  NumberInput,
3712
3778
  {
3779
+ id: minId,
3713
3780
  label: field.minLabel || "At least",
3714
3781
  value: draftMin ?? "",
3715
3782
  min: field.min,
@@ -3719,11 +3786,13 @@ function RangePanel({
3719
3786
  placeholder: field.minPlaceholder,
3720
3787
  onChange: (e) => apply({ min: e.target.value }),
3721
3788
  onValidityChange: validity.report("min")
3722
- }
3789
+ },
3790
+ "min-" + seed
3723
3791
  ),
3724
3792
  /* @__PURE__ */ jsx45(
3725
3793
  NumberInput,
3726
3794
  {
3795
+ id: maxId,
3727
3796
  label: field.maxLabel || "At most",
3728
3797
  value: draftMax ?? "",
3729
3798
  min: field.min,
@@ -3734,12 +3803,20 @@ function RangePanel({
3734
3803
  placeholder: field.maxPlaceholder,
3735
3804
  onChange: (e) => apply({ max: e.target.value }),
3736
3805
  onValidityChange: validity.report("max")
3737
- }
3806
+ },
3807
+ "max-" + seed
3738
3808
  )
3739
3809
  ] })
3740
3810
  }
3741
3811
  );
3742
3812
  }
3813
+ function dateDraft(field, value) {
3814
+ return {
3815
+ kind: value?.kind ?? "withinDays",
3816
+ days: value?.kind === "withinDays" ? value.days : field.defaultDays ?? 30,
3817
+ between: value?.kind === "between" ? { from: value.from, to: value.to } : { from: "", to: "" }
3818
+ };
3819
+ }
3743
3820
  function DatePanel({
3744
3821
  field,
3745
3822
  value,
@@ -3747,23 +3824,44 @@ function DatePanel({
3747
3824
  onCommit,
3748
3825
  onClose
3749
3826
  }) {
3750
- const defaultDays = field.defaultDays ?? 30;
3751
3827
  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
- );
3828
+ const [initial] = React25.useState(() => dateDraft(field, value));
3829
+ const [kind, setKind] = React25.useState(initial.kind);
3830
+ const [daysDraft, setDaysDraft] = React25.useState(initial.days);
3831
+ const daysRef = React25.useRef(daysDraft);
3832
+ const daysConfirmed = React25.useRef(value?.kind === "withinDays");
3833
+ const [between, setBetween] = React25.useState(initial.between);
3834
+ const [seed, setSeed] = React25.useState(0);
3835
+ const preserveFocus = usePreserveFocusAcrossRemount(seed);
3836
+ const daysId = field.key + "-days";
3760
3837
  const shownWithinDays = () => {
3761
3838
  const days = daysRef.current;
3762
3839
  return applicable(field, days == null ? void 0 : { kind: "withinDays", days }, validity.allValid());
3763
3840
  };
3764
- const validity = useTextValidity(() => onCommit(shownWithinDays()));
3841
+ const validity = useTextValidity(() => {
3842
+ daysConfirmed.current = true;
3843
+ onCommit(shownWithinDays());
3844
+ });
3845
+ const applies = () => {
3846
+ if (kind === "between") {
3847
+ return applicable(field, between.from && between.to ? { kind: "between", ...between } : void 0, true);
3848
+ }
3849
+ return daysConfirmed.current ? shownWithinDays() : void 0;
3850
+ };
3851
+ useReseedFromProps(value, applies, (next) => {
3852
+ preserveFocus([daysId]);
3853
+ const d = dateDraft(field, next);
3854
+ setKind(d.kind);
3855
+ daysRef.current = d.days;
3856
+ daysConfirmed.current = next?.kind === "withinDays";
3857
+ setDaysDraft(d.days);
3858
+ setBetween(d.between);
3859
+ validity.reset();
3860
+ setSeed((n) => n + 1);
3861
+ });
3765
3862
  const applyWithinDays = (days) => {
3766
3863
  daysRef.current = days;
3864
+ daysConfirmed.current = true;
3767
3865
  setDaysDraft(days);
3768
3866
  onCommit(shownWithinDays());
3769
3867
  };
@@ -3778,8 +3876,10 @@ function DatePanel({
3778
3876
  setBetween({ from: "", to: "" });
3779
3877
  validity.forget("days");
3780
3878
  } else {
3781
- daysRef.current = defaultDays;
3782
- setDaysDraft(defaultDays);
3879
+ const { days } = dateDraft(field, void 0);
3880
+ daysRef.current = days;
3881
+ daysConfirmed.current = false;
3882
+ setDaysDraft(days);
3783
3883
  }
3784
3884
  onCommit(void 0);
3785
3885
  };
@@ -3819,6 +3919,7 @@ function DatePanel({
3819
3919
  kind === "withinDays" ? /* @__PURE__ */ jsx45(
3820
3920
  NumberInput,
3821
3921
  {
3922
+ id: daysId,
3822
3923
  label: "Days",
3823
3924
  min: 1,
3824
3925
  max: maxDays,
@@ -3827,7 +3928,8 @@ function DatePanel({
3827
3928
  value: daysDraft ?? "",
3828
3929
  onChange: (e) => applyWithinDays(e.target.value),
3829
3930
  onValidityChange: validity.report("days")
3830
- }
3931
+ },
3932
+ "days-" + seed
3831
3933
  ) : /* @__PURE__ */ jsx45(
3832
3934
  DatePicker,
3833
3935
  {
@@ -3835,30 +3937,23 @@ function DatePanel({
3835
3937
  label: "Date range",
3836
3938
  value: { start: between.from, end: between.to },
3837
3939
  onChange: (e) => applyBetween(e.target.value.start || "", e.target.value.end || "")
3838
- }
3940
+ },
3941
+ "between-" + seed
3839
3942
  )
3840
3943
  ] })
3841
3944
  }
3842
3945
  );
3843
3946
  }
3844
3947
  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);
3948
+ const [panel, setPanel] = React25.useState(null);
3949
+ const editAnchorRef = React25.useRef(null);
3950
+ const addAnchorRef = React25.useRef(null);
3951
+ const textInputRef = React25.useRef(null);
3952
+ const optionsRef = React25.useRef(null);
3850
3953
  if (process.env.NODE_ENV !== "production") {
3851
3954
  for (const f of fields) assertValidFieldDefinition(f);
3852
3955
  }
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;
3956
+ const latest = useLatest({ fields, filters, onFilterChange, onFilterRemove });
3862
3957
  const commit = (field, candidate) => {
3863
3958
  const current2 = latest.current.fields.find((f) => f.key === field.key);
3864
3959
  if (!current2) return { ok: false, reason: "This filter is no longer available" };
@@ -3867,24 +3962,17 @@ function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align
3867
3962
  return { ok: true };
3868
3963
  }
3869
3964
  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
- }
3965
+ if (result.ok) latest.current.onFilterChange(current2.key, candidate);
3874
3966
  return result;
3875
3967
  };
3876
3968
  const removeField = (field) => {
3877
3969
  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);
3970
+ if (isFieldSet(field, now[field.key])) remove(field.key);
3883
3971
  };
3884
3972
  const editingField = panel && panel.kind === "edit" ? fields.find((f) => f.key === panel.key) : void 0;
3885
3973
  const startEditing = (key) => setPanel({ kind: "edit", key });
3886
3974
  const closeEditing = () => setPanel(null);
3887
- React24.useEffect(() => {
3975
+ React25.useEffect(() => {
3888
3976
  if (!panel || panel.kind !== "edit") return;
3889
3977
  if (editingField?.type === "text") {
3890
3978
  textInputRef.current?.focus();
@@ -4090,7 +4178,7 @@ function StatTile({ label, value, sub, delta, invert = false, loading = false, c
4090
4178
  }
4091
4179
 
4092
4180
  // src/components/data/CodeBlock.tsx
4093
- import * as React25 from "react";
4181
+ import * as React26 from "react";
4094
4182
  import { Fragment as Fragment6, jsx as jsx47, jsxs as jsxs41 } from "react/jsx-runtime";
4095
4183
  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
4184
  var RULES = {
@@ -4246,13 +4334,13 @@ function tokenize(src, lang) {
4246
4334
  return out;
4247
4335
  }
4248
4336
  function Highlighted({ code, lang }) {
4249
- const toks = React25.useMemo(() => tokenize(code, lang), [code, lang]);
4337
+ const toks = React26.useMemo(() => tokenize(code, lang), [code, lang]);
4250
4338
  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
4339
  }
4252
4340
  function useCopy() {
4253
- const [done, setDone] = React25.useState(false);
4254
- const timer = React25.useRef(null);
4255
- React25.useEffect(() => () => {
4341
+ const [done, setDone] = React26.useState(false);
4342
+ const timer = React26.useRef(null);
4343
+ React26.useEffect(() => () => {
4256
4344
  if (timer.current) clearTimeout(timer.current);
4257
4345
  }, []);
4258
4346
  const copy = (text) => {
@@ -4288,8 +4376,8 @@ function CodeBlock({
4288
4376
  actions,
4289
4377
  className = ""
4290
4378
  }) {
4291
- const [wrap, setWrap] = React25.useState(!!wrapDefault);
4292
- const [open, setOpen] = React25.useState(false);
4379
+ const [wrap, setWrap] = React26.useState(!!wrapDefault);
4380
+ const [open, setOpen] = React26.useState(false);
4293
4381
  const [copied, copy] = useCopy();
4294
4382
  const body = String(code).replace(/\n$/, "");
4295
4383
  const lines = body.split("\n");
@@ -4318,7 +4406,7 @@ function CodeBlock({
4318
4406
  ] });
4319
4407
  }
4320
4408
  function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" }) {
4321
- const [open, setOpen] = React25.useState(false);
4409
+ const [open, setOpen] = React26.useState(false);
4322
4410
  const [copied, copy] = useCopy();
4323
4411
  const raw = String(diff).replace(/\n$/, "");
4324
4412
  const all = raw.split("\n").map((line) => {
@@ -4364,7 +4452,7 @@ function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" })
4364
4452
  }
4365
4453
 
4366
4454
  // src/components/data/Markdown.tsx
4367
- import * as React26 from "react";
4455
+ import * as React27 from "react";
4368
4456
  import { jsx as jsx48, jsxs as jsxs42 } from "react/jsx-runtime";
4369
4457
  var SAFE = /^(?:https?:|mailto:|tel:|#|\/|\.{1,2}\/)/i;
4370
4458
  var safeHref = (h) => SAFE.test(String(h || "").trim()) ? String(h).trim() : null;
@@ -4403,7 +4491,7 @@ function inlineNodes(src, ctx, keyBase) {
4403
4491
  const href = safeHref(g(2));
4404
4492
  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
4493
  } 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()));
4494
+ 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
4495
  } else if (kind === "strongem") out.push(/* @__PURE__ */ jsx48("strong", { children: /* @__PURE__ */ jsx48("em", { children: inlineNodes(g(1), ctx, key()) }) }, key()));
4408
4496
  else if (kind === "strong") out.push(/* @__PURE__ */ jsx48("strong", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
4409
4497
  else if (kind === "em") out.push(/* @__PURE__ */ jsx48("em", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
@@ -4568,7 +4656,7 @@ function Markdown({
4568
4656
  onLinkClick
4569
4657
  }) {
4570
4658
  const ctx = { allowImages, headingOffset, renderCitation, onLinkClick, codeProps };
4571
- const blocks = React26.useMemo(() => parseBlocks(source), [source]);
4659
+ const blocks = React27.useMemo(() => parseBlocks(source), [source]);
4572
4660
  return /* @__PURE__ */ jsx48("div", { className: ["fd-md", className].filter(Boolean).join(" "), children: renderBlocks(blocks, ctx, "b") });
4573
4661
  }
4574
4662
  function MarkdownInline({ source = "", className = "", onLinkClick }) {
@@ -4682,7 +4770,7 @@ function Pagination({
4682
4770
  }
4683
4771
 
4684
4772
  // src/components/data/RelativeTime.tsx
4685
- import * as React27 from "react";
4773
+ import * as React28 from "react";
4686
4774
  import { jsxs as jsxs45 } from "react/jsx-runtime";
4687
4775
  var MIN = 6e4;
4688
4776
  var HOUR = 36e5;
@@ -4734,9 +4822,9 @@ function formatDuration(ms) {
4734
4822
  return h + "h" + (m % 60 ? " " + m % 60 + "m" : "");
4735
4823
  }
4736
4824
  function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true, ...rest }) {
4737
- const [, force] = React27.useState(0);
4825
+ const [, force] = React28.useState(0);
4738
4826
  const t = value instanceof Date ? value.getTime() : Date.parse(value);
4739
- React27.useEffect(() => {
4827
+ React28.useEffect(() => {
4740
4828
  if (!refresh2 || !t || isNaN(t)) return;
4741
4829
  const abs = Math.abs(t - Date.now());
4742
4830
  if (abs > DAY) return;
@@ -4752,7 +4840,7 @@ function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true,
4752
4840
  }
4753
4841
 
4754
4842
  // src/components/data/ScoreMeter.tsx
4755
- import * as React28 from "react";
4843
+ import * as React29 from "react";
4756
4844
  import { jsx as jsx51, jsxs as jsxs46 } from "react/jsx-runtime";
4757
4845
  function ScoreMeter({
4758
4846
  score,
@@ -4767,10 +4855,10 @@ function ScoreMeter({
4767
4855
  breakdownWidth = 280,
4768
4856
  className = ""
4769
4857
  }) {
4770
- const [open, setOpen] = React28.useState(false);
4771
- const ref = React28.useRef(null);
4858
+ const [open, setOpen] = React29.useState(false);
4859
+ const ref = React29.useRef(null);
4772
4860
  const value = Math.max(0, Math.min(100, Math.round(Number(score) || 0)));
4773
- const body = /* @__PURE__ */ jsxs46(React28.Fragment, { children: [
4861
+ const body = /* @__PURE__ */ jsxs46(React29.Fragment, { children: [
4774
4862
  /* @__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
4863
  showScore ? /* @__PURE__ */ jsx51("span", { className: "fd-scoremeter-value fd-tabular", "aria-hidden": "true", children: value }) : null
4776
4864
  ] });
@@ -4789,7 +4877,7 @@ function ScoreMeter({
4789
4877
  }
4790
4878
  );
4791
4879
  }
4792
- return /* @__PURE__ */ jsxs46(React28.Fragment, { children: [
4880
+ return /* @__PURE__ */ jsxs46(React29.Fragment, { children: [
4793
4881
  /* @__PURE__ */ jsx51(
4794
4882
  "button",
4795
4883
  {
@@ -4820,13 +4908,13 @@ function ScoreMeter({
4820
4908
  }
4821
4909
 
4822
4910
  // src/components/data/SortMenu.tsx
4823
- import * as React29 from "react";
4911
+ import * as React30 from "react";
4824
4912
  import { jsx as jsx52, jsxs as jsxs47 } from "react/jsx-runtime";
4825
4913
  function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", className = "", ...rest }) {
4826
- const [open, setOpen] = React29.useState(false);
4914
+ const [open, setOpen] = React30.useState(false);
4827
4915
  const zIndex = useOverlayLayer(open);
4828
- const ref = React29.useRef(null);
4829
- React29.useEffect(() => {
4916
+ const ref = React30.useRef(null);
4917
+ React30.useEffect(() => {
4830
4918
  if (!open) return;
4831
4919
  const away = (e) => {
4832
4920
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -4888,7 +4976,7 @@ function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", cla
4888
4976
  }
4889
4977
 
4890
4978
  // src/components/data/StepList.tsx
4891
- import * as React30 from "react";
4979
+ import * as React31 from "react";
4892
4980
  import { jsx as jsx53, jsxs as jsxs48 } from "react/jsx-runtime";
4893
4981
  var KIND_ICON = { tool: "wrench", search: "magnifying-glass", read: "file-text", write: "pencil-simple", reasoning: "brain", run: "terminal", fetch: "cloud-arrow-down" };
4894
4982
  function StepList({
@@ -4901,9 +4989,9 @@ function StepList({
4901
4989
  dense = false,
4902
4990
  className = ""
4903
4991
  }) {
4904
- const [openState, setOpenState] = React30.useState(defaultOpen);
4992
+ const [openState, setOpenState] = React31.useState(defaultOpen);
4905
4993
  const open = openProp == null ? openState : openProp;
4906
- const [expanded, setExpanded] = React30.useState({});
4994
+ const [expanded, setExpanded] = React31.useState({});
4907
4995
  if (!steps.length) return null;
4908
4996
  const running = steps.some((s) => s.status === "running");
4909
4997
  const failed = steps.filter((s) => s.status === "error").length;
@@ -4961,7 +5049,7 @@ function StepList({
4961
5049
  }
4962
5050
 
4963
5051
  // src/components/data/VirtualList.tsx
4964
- import * as React31 from "react";
5052
+ import * as React32 from "react";
4965
5053
  import { jsx as jsx54, jsxs as jsxs49 } from "react/jsx-runtime";
4966
5054
  var VIRTUAL_LIST_BUFFER_ROWS = 20;
4967
5055
  var VIRTUAL_LIST_ROW_GAP = 6;
@@ -5013,35 +5101,35 @@ function VirtualList({
5013
5101
  className = "",
5014
5102
  style
5015
5103
  }) {
5016
- const [scrollTop, setScrollTop] = React31.useState(0);
5017
- const loaded = React31.useMemo(
5104
+ const [scrollTop, setScrollTop] = React32.useState(0);
5105
+ const loaded = React32.useMemo(
5018
5106
  () => ({ firstItemIndex, count: items.length }),
5019
5107
  [firstItemIndex, items.length]
5020
5108
  );
5021
5109
  const total = Math.max(totalCount == null ? 0 : totalCount, firstItemIndex + items.length);
5022
5110
  const bandKey = firstItemIndex + ":" + items.length;
5023
- const win = React31.useMemo(
5111
+ const win = React32.useMemo(
5024
5112
  () => computeVirtualWindow({ scrollTop, viewportHeight: height, itemHeight, itemCount: total, loaded }),
5025
5113
  [scrollTop, height, itemHeight, total, loaded]
5026
5114
  );
5027
- const need = React31.useMemo(() => computeNeedMore(win, loaded), [win, loaded]);
5115
+ const need = React32.useMemo(() => computeNeedMore(win, loaded), [win, loaded]);
5028
5116
  const wantStart = need.start && hasMore?.start !== false;
5029
5117
  const wantEnd = need.end && hasMore?.end !== false;
5030
5118
  const bucket2 = Math.floor(win.visibleStart / VIRTUAL_LIST_BUFFER_ROWS);
5031
5119
  const askKey = bandKey + "@" + bucket2;
5032
- const asked = React31.useRef({ start: null, end: null });
5033
- React31.useEffect(() => {
5120
+ const asked = React32.useRef({ start: null, end: null });
5121
+ React32.useEffect(() => {
5034
5122
  if (loading || !wantStart || asked.current.start === askKey) return;
5035
5123
  asked.current.start = askKey;
5036
5124
  onNeedMore("start", win);
5037
5125
  }, [wantStart, loading, askKey, onNeedMore, win]);
5038
- React31.useEffect(() => {
5126
+ React32.useEffect(() => {
5039
5127
  if (loading || !wantEnd || asked.current.end === askKey) return;
5040
5128
  asked.current.end = askKey;
5041
5129
  onNeedMore("end", win);
5042
5130
  }, [wantEnd, loading, askKey, onNeedMore, win]);
5043
- const lastKeysRef = React31.useRef(null);
5044
- const newKeys = React31.useMemo(() => {
5131
+ const lastKeysRef = React32.useRef(null);
5132
+ const newKeys = React32.useMemo(() => {
5045
5133
  const prev = lastKeysRef.current;
5046
5134
  if (!prev) return /* @__PURE__ */ new Set();
5047
5135
  const fresh = /* @__PURE__ */ new Set();
@@ -5051,7 +5139,7 @@ function VirtualList({
5051
5139
  }
5052
5140
  return fresh;
5053
5141
  }, [items]);
5054
- React31.useEffect(() => {
5142
+ React32.useEffect(() => {
5055
5143
  lastKeysRef.current = new Set(items.map(keyOf));
5056
5144
  }, [items]);
5057
5145
  const gapStyle = { "--fd-vlist-gap": rowGap + "px" };
@@ -5096,7 +5184,7 @@ function VirtualList({
5096
5184
  }
5097
5185
 
5098
5186
  // src/components/data/EntityRow.tsx
5099
- import * as React32 from "react";
5187
+ import * as React33 from "react";
5100
5188
  import { jsx as jsx55, jsxs as jsxs50 } from "react/jsx-runtime";
5101
5189
  var ENTITY_ROW_METRIC_WIDTH = 74;
5102
5190
  var METRIC_TONE_COLOR = {
@@ -5109,7 +5197,7 @@ function EntityRowMetrics({ metrics, className = "", style }) {
5109
5197
  const key = m.id || m.label || i;
5110
5198
  const className2 = ["fd-erow-metric", m.tone && m.tone !== "default" ? "is-" + m.tone : "", m.tooltip != null ? "is-described" : ""].filter(Boolean).join(" ");
5111
5199
  const tone = { color: METRIC_TONE_COLOR[m.tone || "default"] };
5112
- const content = /* @__PURE__ */ jsxs50(React32.Fragment, { children: [
5200
+ const content = /* @__PURE__ */ jsxs50(React33.Fragment, { children: [
5113
5201
  m.icon ? /* @__PURE__ */ jsx55("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }) : null,
5114
5202
  /* @__PURE__ */ jsx55("span", { className: "fd-erow-metric-value fd-tabular", children: m.value }),
5115
5203
  /* @__PURE__ */ jsx55("span", { className: "fd-sr", children: " " + m.label })
@@ -5201,7 +5289,7 @@ function EntityRow({
5201
5289
  }
5202
5290
 
5203
5291
  // src/components/data/TransferList.tsx
5204
- import * as React33 from "react";
5292
+ import * as React34 from "react";
5205
5293
  import { createPortal as createPortal4 } from "react-dom";
5206
5294
 
5207
5295
  // src/components/forms/Input.tsx
@@ -5307,12 +5395,12 @@ function TransferList({
5307
5395
  listHeight = 440,
5308
5396
  className = ""
5309
5397
  }) {
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) => {
5398
+ const [drag, setDrag] = React34.useState(null);
5399
+ const [dragOverSide, setDragOverSide] = React34.useState(null);
5400
+ const dragRef = React34.useRef(null);
5401
+ const armedRef = React34.useRef(null);
5402
+ const sideRefs = React34.useRef({ left: null, right: null });
5403
+ const hitTestSide = React34.useCallback((x, y) => {
5316
5404
  for (const side of ["left", "right"]) {
5317
5405
  const el = sideRefs.current[side];
5318
5406
  if (!el) continue;
@@ -5321,7 +5409,7 @@ function TransferList({
5321
5409
  }
5322
5410
  return null;
5323
5411
  }, []);
5324
- const findItem = React33.useCallback(
5412
+ const findItem = React34.useCallback(
5325
5413
  (side, key) => (side === "left" ? left.items : right.items).find((it) => keyOf(it) === key),
5326
5414
  [left.items, right.items, keyOf]
5327
5415
  );
@@ -5343,7 +5431,7 @@ function TransferList({
5343
5431
  height: rect.height
5344
5432
  };
5345
5433
  };
5346
- React33.useEffect(() => {
5434
+ React34.useEffect(() => {
5347
5435
  const onMovePointer = (e) => {
5348
5436
  const armed = armedRef.current;
5349
5437
  if (!armed || e.pointerId !== armed.pointerId) return;
@@ -5396,7 +5484,7 @@ function TransferList({
5396
5484
  window.removeEventListener("pointercancel", endDrag);
5397
5485
  };
5398
5486
  }, [hitTestSide, findItem, onMove]);
5399
- React33.useEffect(() => {
5487
+ React34.useEffect(() => {
5400
5488
  if (!drag || typeof document === "undefined") return;
5401
5489
  const prev = document.body.style.userSelect;
5402
5490
  document.body.style.userSelect = "none";
@@ -5504,11 +5592,11 @@ function TransferList({
5504
5592
  }
5505
5593
 
5506
5594
  // src/components/forms/Checkbox.tsx
5507
- import * as React34 from "react";
5595
+ import * as React35 from "react";
5508
5596
  import { jsx as jsx59, jsxs as jsxs53 } from "react/jsx-runtime";
5509
5597
  function Checkbox({ label, description, card = false, indeterminate = false, className = "", ...rest }) {
5510
- const ref = React34.useRef(null);
5511
- React34.useEffect(() => {
5598
+ const ref = React35.useRef(null);
5599
+ React35.useEffect(() => {
5512
5600
  if (ref.current) ref.current.indeterminate = indeterminate;
5513
5601
  }, [indeterminate]);
5514
5602
  return /* @__PURE__ */ jsxs53("label", { className: ["fd-choice", card ? "fd-choice-card" : "", className].filter(Boolean).join(" "), children: [
@@ -5552,7 +5640,7 @@ function Switch({ label, description, className = "", ...rest }) {
5552
5640
  }
5553
5641
 
5554
5642
  // src/components/forms/Select.tsx
5555
- import * as React35 from "react";
5643
+ import * as React36 from "react";
5556
5644
  import { jsx as jsx62, jsxs as jsxs56 } from "react/jsx-runtime";
5557
5645
  var norm = (o) => typeof o === "string" ? { value: o, label: o } : o;
5558
5646
  function Select({
@@ -5579,12 +5667,12 @@ function Select({
5579
5667
  const isOn = (v) => multiple ? vals.includes(v) : v === value;
5580
5668
  const hasSearch = searchable === void 0 ? opts.length > 8 : searchable;
5581
5669
  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 });
5670
+ const [open, setOpen] = React36.useState(false);
5671
+ const [q, setQ] = React36.useState("");
5672
+ const rootRef = React36.useRef(null);
5673
+ const boxRef = React36.useRef(null);
5674
+ const listRef = React36.useRef(null);
5675
+ const typeBuf = React36.useRef({ s: "", t: 0 });
5588
5676
  const selected = multiple ? null : opts.find((o) => o.value === value);
5589
5677
  const chosen = multiple ? opts.filter((o) => vals.includes(o.value)) : [];
5590
5678
  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 +5811,7 @@ function Select({
5723
5811
  ),
5724
5812
  q ? /* @__PURE__ */ jsx62("span", { className: "fd-body-sm fd-muted", children: visible.length }) : null
5725
5813
  ] }) : void 0,
5726
- footer: multiple ? /* @__PURE__ */ jsxs56(React35.Fragment, { children: [
5814
+ footer: multiple ? /* @__PURE__ */ jsxs56(React36.Fragment, { children: [
5727
5815
  /* @__PURE__ */ jsx62(
5728
5816
  "button",
5729
5817
  {
@@ -5754,7 +5842,7 @@ function Select({
5754
5842
  'Nothing matches "',
5755
5843
  q,
5756
5844
  '".'
5757
- ] }) : groups.map((grp) => /* @__PURE__ */ jsxs56(React35.Fragment, { children: [
5845
+ ] }) : groups.map((grp) => /* @__PURE__ */ jsxs56(React36.Fragment, { children: [
5758
5846
  grp.g ? /* @__PURE__ */ jsx62("div", { className: "fd-pop-group", children: grp.g }) : null,
5759
5847
  grp.items.map(({ o, i }) => /* @__PURE__ */ jsxs56(
5760
5848
  "button",
@@ -5791,7 +5879,7 @@ function Select({
5791
5879
  }
5792
5880
 
5793
5881
  // src/components/forms/TimePicker.tsx
5794
- import * as React36 from "react";
5882
+ import * as React37 from "react";
5795
5883
  import { jsx as jsx63, jsxs as jsxs57 } from "react/jsx-runtime";
5796
5884
  var pad = (n) => String(n).padStart(2, "0");
5797
5885
  function ClockFace({ value = "09:00", onChange }) {
@@ -5800,9 +5888,9 @@ function ClockFace({ value = "09:00", onChange }) {
5800
5888
  if (isNaN(m)) m = 0;
5801
5889
  const pm = h24 >= 12;
5802
5890
  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);
5891
+ const [mode2, setMode] = React37.useState("h");
5892
+ const faceRef = React37.useRef(null);
5893
+ const dragging = React37.useRef(false);
5806
5894
  const set = (h, mm, isPm) => onChange((isPm ? h % 12 + 12 : h % 12) + ":" + pad(mm));
5807
5895
  const R = 108, NR = 80;
5808
5896
  const nums = mode2 === "h" ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 12 }, (_, i) => i * 5);
@@ -5875,9 +5963,9 @@ function ClockFace({ value = "09:00", onChange }) {
5875
5963
  }
5876
5964
  function TimePicker({ label, help, error, required = false, disabled = false, value = "", onChange, placeholder = "Pick a time", id, className = "", style }) {
5877
5965
  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);
5966
+ const [open, setOpen] = React37.useState(false);
5967
+ const rootRef = React37.useRef(null);
5968
+ const boxRef = React37.useRef(null);
5881
5969
  const disp = () => {
5882
5970
  if (!value) return "";
5883
5971
  const [h, m] = value.split(":").map(Number);
@@ -5920,7 +6008,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
5920
6008
  offset: 6,
5921
6009
  width: 262,
5922
6010
  role: "presentation",
5923
- footer: /* @__PURE__ */ jsxs57(React36.Fragment, { children: [
6011
+ footer: /* @__PURE__ */ jsxs57(React37.Fragment, { children: [
5924
6012
  /* @__PURE__ */ jsx63(
5925
6013
  "button",
5926
6014
  {
@@ -5948,7 +6036,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
5948
6036
  }
5949
6037
 
5950
6038
  // src/components/forms/Slider.tsx
5951
- import * as React37 from "react";
6039
+ import * as React38 from "react";
5952
6040
  import { jsx as jsx64, jsxs as jsxs58 } from "react/jsx-runtime";
5953
6041
  function Slider({
5954
6042
  label,
@@ -5966,7 +6054,7 @@ function Slider({
5966
6054
  ...rest
5967
6055
  }) {
5968
6056
  const fieldId = useFieldId(id);
5969
- const [dragging, setDragging] = React37.useState(false);
6057
+ const [dragging, setDragging] = React38.useState(false);
5970
6058
  const v = value === void 0 ? min : Number(value);
5971
6059
  const pct = max === min ? 0 : (v - min) / (max - min) * 100;
5972
6060
  return /* @__PURE__ */ jsxs58("div", { className: ["fd-field", className].filter(Boolean).join(" "), children: [
@@ -6000,7 +6088,7 @@ function Slider({
6000
6088
  }
6001
6089
 
6002
6090
  // src/components/forms/RangeSlider.tsx
6003
- import * as React38 from "react";
6091
+ import * as React39 from "react";
6004
6092
  import { jsx as jsx65, jsxs as jsxs59 } from "react/jsx-runtime";
6005
6093
  function RangeSlider({
6006
6094
  label,
@@ -6020,9 +6108,9 @@ function RangeSlider({
6020
6108
  }) {
6021
6109
  const fmt2 = format || ((v) => String(v));
6022
6110
  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);
6111
+ const [drag, setDrag] = React39.useState(null);
6112
+ const [focus, setFocus] = React39.useState(null);
6113
+ const railRef = React39.useRef(null);
6026
6114
  const pct = (v) => max === min ? 0 : (v - min) / (max - min) * 100;
6027
6115
  const clampPair = (i, v) => {
6028
6116
  v = Math.min(max, Math.max(min, Math.round(v / step) * step));
@@ -6037,7 +6125,7 @@ function RangeSlider({
6037
6125
  const r = railRef.current.getBoundingClientRect();
6038
6126
  return min + Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)) * (max - min);
6039
6127
  };
6040
- React38.useEffect(() => {
6128
+ React39.useEffect(() => {
6041
6129
  if (drag === null) return;
6042
6130
  const mv = (e) => onChange && onChange(clampPair(drag, fromEvent(e)));
6043
6131
  const upH = () => setDrag(null);
@@ -6118,7 +6206,7 @@ function RangeSlider({
6118
6206
  /* @__PURE__ */ jsxs59("div", { className: "fd-range" + (hasLabels ? " has-labels" : ""), onPointerDown: onRailDown, children: [
6119
6207
  /* @__PURE__ */ jsx65("span", { className: "fd-range-rail", ref: railRef }),
6120
6208
  /* @__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: [
6209
+ marks.map((m) => /* @__PURE__ */ jsxs59(React39.Fragment, { children: [
6122
6210
  /* @__PURE__ */ jsx65("span", { className: "fd-range-mark" + (m.value >= a && m.value <= b ? " is-in" : ""), style: { left: pct(m.value) + "%" } }),
6123
6211
  m.label ? /* @__PURE__ */ jsx65("span", { className: "fd-range-mark-label", style: { left: pct(m.value) + "%" }, children: m.label }) : null
6124
6212
  ] }, m.value)),
@@ -6147,7 +6235,7 @@ function RangeSlider({
6147
6235
  }
6148
6236
 
6149
6237
  // src/components/forms/Dropzone.tsx
6150
- import * as React39 from "react";
6238
+ import * as React40 from "react";
6151
6239
  import { jsx as jsx66, jsxs as jsxs60 } from "react/jsx-runtime";
6152
6240
  function Dropzone({
6153
6241
  onFiles,
@@ -6162,8 +6250,8 @@ function Dropzone({
6162
6250
  className = "",
6163
6251
  style
6164
6252
  }) {
6165
- const [over, setOver] = React39.useState(false);
6166
- const depth = React39.useRef(0);
6253
+ const [over, setOver] = React40.useState(false);
6254
+ const depth = React40.useRef(0);
6167
6255
  const has = (e) => {
6168
6256
  const dt = e.dataTransfer;
6169
6257
  if (!dt) return false;
@@ -6225,8 +6313,8 @@ function FilePickButton({
6225
6313
  className = "",
6226
6314
  children
6227
6315
  }) {
6228
- const ref = React39.useRef(null);
6229
- return /* @__PURE__ */ jsxs60(React39.Fragment, { children: [
6316
+ const ref = React40.useRef(null);
6317
+ return /* @__PURE__ */ jsxs60(React40.Fragment, { children: [
6230
6318
  /* @__PURE__ */ jsx66(
6231
6319
  "button",
6232
6320
  {
@@ -6259,10 +6347,10 @@ function FilePickButton({
6259
6347
  }
6260
6348
  function useStagedFiles(upload, opts) {
6261
6349
  const o = opts || {};
6262
- const [items, setItems] = React39.useState([]);
6263
- const controllers = React39.useRef({});
6350
+ const [items, setItems] = React40.useState([]);
6351
+ const controllers = React40.useRef({});
6264
6352
  const patch = (id, next) => setItems((list) => list.map((f) => f.id === id ? Object.assign({}, f, next) : f));
6265
- const run = React39.useCallback((att) => {
6353
+ const run = React40.useCallback((att) => {
6266
6354
  if (!upload) return;
6267
6355
  const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
6268
6356
  controllers.current[att.id] = ac;
@@ -6279,14 +6367,14 @@ function useStagedFiles(upload, opts) {
6279
6367
  delete controllers.current[att.id];
6280
6368
  });
6281
6369
  }, [upload]);
6282
- const add = React39.useCallback((files) => {
6370
+ const add = React40.useCallback((files) => {
6283
6371
  if (!upload) return [];
6284
6372
  const atts = Array.from(files).map((f) => toAttachment(f));
6285
6373
  setItems((list) => list.concat(atts));
6286
6374
  atts.forEach(run);
6287
6375
  return atts;
6288
6376
  }, [upload, run]);
6289
- const remove = React39.useCallback((att) => {
6377
+ const remove = React40.useCallback((att) => {
6290
6378
  const ac = controllers.current[att.id];
6291
6379
  if (ac) {
6292
6380
  try {
@@ -6297,10 +6385,10 @@ function useStagedFiles(upload, opts) {
6297
6385
  }
6298
6386
  setItems((list) => list.filter((f) => f.id !== att.id));
6299
6387
  }, []);
6300
- const retry = React39.useCallback((att) => {
6388
+ const retry = React40.useCallback((att) => {
6301
6389
  run(att);
6302
6390
  }, [run]);
6303
- const clear = React39.useCallback(() => {
6391
+ const clear = React40.useCallback(() => {
6304
6392
  Object.values(controllers.current).forEach((ac) => {
6305
6393
  try {
6306
6394
  ac && ac.abort();
@@ -6424,7 +6512,7 @@ function FileGrid({ files = [], onOpen, onRemove, onRetry, tiles = true, maxHeig
6424
6512
  }
6425
6513
 
6426
6514
  // src/components/forms/MarkdownEditor.tsx
6427
- import * as React40 from "react";
6515
+ import * as React41 from "react";
6428
6516
  import { jsx as jsx68, jsxs as jsxs62 } from "react/jsx-runtime";
6429
6517
  var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || "");
6430
6518
  var INLINE_RE = /(\*\*\*[^*\n]+\*\*\*|\*\*[^*\n]+\*\*|__[^_\n]+__|~~[^~\n]+~~|`[^`\n]+`|\*[^*\s][^*\n]*\*|(?<![A-Za-z0-9_])_[^_\s][^_\n]*_|\[[^\]\n]*\]\([^)\s\n]*\)|https?:\/\/\S+)/g;
@@ -6638,7 +6726,7 @@ function syncDom(root, value) {
6638
6726
  while (root.children.length > lines.length) root.removeChild(root.lastChild);
6639
6727
  }
6640
6728
  var LIST_CONT = RE_LI;
6641
- var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6729
+ var MarkdownEditor = React41.forwardRef(function MarkdownEditor2({
6642
6730
  value = "",
6643
6731
  onChange,
6644
6732
  onSubmit,
@@ -6657,10 +6745,10 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6657
6745
  className = "",
6658
6746
  id
6659
6747
  }, ref) {
6660
- const boxRef = React40.useRef(null);
6661
- const composing = React40.useRef(false);
6662
- const pendingCaret = React40.useRef(null);
6663
- React40.useLayoutEffect(() => {
6748
+ const boxRef = React41.useRef(null);
6749
+ const composing = React41.useRef(false);
6750
+ const pendingCaret = React41.useRef(null);
6751
+ React41.useLayoutEffect(() => {
6664
6752
  const root = boxRef.current;
6665
6753
  if (!root || composing.current) return;
6666
6754
  const active = document.activeElement === root || root.contains(document.activeElement);
@@ -6669,7 +6757,7 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6669
6757
  pendingCaret.current = null;
6670
6758
  if (active && caret != null) placeCaret(root, caret);
6671
6759
  }, [value]);
6672
- React40.useEffect(() => {
6760
+ React41.useEffect(() => {
6673
6761
  if (autoFocus && boxRef.current) boxRef.current.focus();
6674
6762
  }, [autoFocus]);
6675
6763
  const caretNow = () => {
@@ -6736,7 +6824,7 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6736
6824
  api.replaceRange(from, to, next, from + next.length);
6737
6825
  }
6738
6826
  };
6739
- React40.useImperativeHandle(ref, () => api);
6827
+ React41.useImperativeHandle(ref, () => api);
6740
6828
  function detect(text, caret) {
6741
6829
  if (!onTrigger) return;
6742
6830
  const upto = text.slice(0, caret);
@@ -6888,16 +6976,16 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6888
6976
  });
6889
6977
 
6890
6978
  // src/components/platform/AccountMenu.tsx
6891
- import * as React42 from "react";
6979
+ import * as React43 from "react";
6892
6980
 
6893
6981
  // src/kits/sessionContext.tsx
6894
- import * as React41 from "react";
6895
- var SessionContext = React41.createContext(null);
6982
+ import * as React42 from "react";
6983
+ var SessionContext = React42.createContext(null);
6896
6984
  function SessionProvider({ value, children }) {
6897
- return React41.createElement(SessionContext.Provider, { value }, children);
6985
+ return React42.createElement(SessionContext.Provider, { value }, children);
6898
6986
  }
6899
6987
  function useSessionContext() {
6900
- const ctx = React41.useContext(SessionContext);
6988
+ const ctx = React42.useContext(SessionContext);
6901
6989
  if (!ctx) {
6902
6990
  throw new Error(
6903
6991
  "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 +7025,11 @@ function AccountMenu({
6937
7025
  ...rest
6938
7026
  }) {
6939
7027
  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);
7028
+ const [open, setOpen] = React43.useState(false);
7029
+ const [switching, setSwitching] = React43.useState(false);
7030
+ const [signingOut, setSigningOut] = React43.useState(false);
7031
+ const [signOutError, setSignOutError] = React43.useState(null);
7032
+ const ref = React43.useRef(null);
6945
7033
  const visibleLinks = links.filter((l) => !l.perm || can2(l.perm));
6946
7034
  const visibleGroups = groups.map((g) => ({ label: g.label, links: g.links.filter((l) => !l.perm || can2(l.perm)) })).filter((g) => g.links.length);
6947
7035
  const closeMenu = () => {
@@ -7066,13 +7154,13 @@ function AccountMenu({
7066
7154
  }
7067
7155
 
7068
7156
  // src/components/platform/AppSwitcher.tsx
7069
- import * as React43 from "react";
7157
+ import * as React44 from "react";
7070
7158
  import { jsx as jsx70, jsxs as jsxs64 } from "react/jsx-runtime";
7071
7159
  function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
7072
7160
  const { apps: contextApps } = useSessionContext();
7073
7161
  const apps = appsProp ?? contextApps ?? [];
7074
- const [open, setOpen] = React43.useState(false);
7075
- const anchorRef = React43.useRef(null);
7162
+ const [open, setOpen] = React44.useState(false);
7163
+ const anchorRef = React44.useRef(null);
7076
7164
  if (!apps.length) return null;
7077
7165
  const items = apps.map((app) => ({
7078
7166
  id: app.key,
@@ -7086,7 +7174,7 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
7086
7174
  window.location.href = app.url;
7087
7175
  }
7088
7176
  }));
7089
- return /* @__PURE__ */ jsxs64(React43.Fragment, { children: [
7177
+ return /* @__PURE__ */ jsxs64(React44.Fragment, { children: [
7090
7178
  /* @__PURE__ */ jsx70("span", { ref: anchorRef, className: ["fd-appswitch-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx70(
7091
7179
  IconButton,
7092
7180
  {
@@ -7115,10 +7203,10 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
7115
7203
  }
7116
7204
 
7117
7205
  // src/components/platform/AppearanceMenu.tsx
7118
- import * as React45 from "react";
7206
+ import * as React46 from "react";
7119
7207
 
7120
7208
  // src/kits/appearance.ts
7121
- import * as React44 from "react";
7209
+ import * as React45 from "react";
7122
7210
  var DEFAULT_STORAGE_KEY = "fd-appearance";
7123
7211
  var DEFAULT_PREFS = {
7124
7212
  theme: "system",
@@ -7213,11 +7301,11 @@ function applyPrefs(prefs, root = document.documentElement) {
7213
7301
  else root.removeAttribute("data-motion");
7214
7302
  }
7215
7303
  function useAppearancePrefs(store) {
7216
- const prefs = React44.useSyncExternalStore(store.subscribe, store.getSnapshot);
7217
- React44.useEffect(() => {
7304
+ const prefs = React45.useSyncExternalStore(store.subscribe, store.getSnapshot);
7305
+ React45.useEffect(() => {
7218
7306
  applyPrefs(prefs);
7219
7307
  }, [prefs]);
7220
- React44.useEffect(() => {
7308
+ React45.useEffect(() => {
7221
7309
  if (prefs.theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
7222
7310
  return;
7223
7311
  }
@@ -7226,7 +7314,7 @@ function useAppearancePrefs(store) {
7226
7314
  mq.addEventListener("change", onChange);
7227
7315
  return () => mq.removeEventListener("change", onChange);
7228
7316
  }, [prefs]);
7229
- const update = React44.useCallback(
7317
+ const update = React45.useCallback(
7230
7318
  (patch) => {
7231
7319
  store.write({ ...store.getSnapshot(), ...patch });
7232
7320
  },
@@ -7249,15 +7337,15 @@ var TEXT_SCALE_OPTIONS = [
7249
7337
  { value: "xl", label: "XL" }
7250
7338
  ];
7251
7339
  function AppearanceMenu({ className = "", store: storeProp, storageKey, storage, defaults }) {
7252
- const store = React45.useMemo(
7340
+ const store = React46.useMemo(
7253
7341
  () => storeProp ?? createLocalStorageAppearanceStore({ storageKey, storage, defaults }),
7254
7342
  [storeProp, storageKey, storage, defaults]
7255
7343
  );
7256
7344
  const [prefs, update] = useAppearancePrefs(store);
7257
- const [open, setOpen] = React45.useState(false);
7258
- const anchorRef = React45.useRef(null);
7345
+ const [open, setOpen] = React46.useState(false);
7346
+ const anchorRef = React46.useRef(null);
7259
7347
  const patch = (p) => update(p);
7260
- return /* @__PURE__ */ jsxs65(React45.Fragment, { children: [
7348
+ return /* @__PURE__ */ jsxs65(React46.Fragment, { children: [
7261
7349
  /* @__PURE__ */ jsx71("span", { ref: anchorRef, className: ["fd-appearance-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx71(
7262
7350
  IconButton,
7263
7351
  {
@@ -7326,14 +7414,14 @@ function AppearanceMenu({ className = "", store: storeProp, storageKey, storage,
7326
7414
  }
7327
7415
 
7328
7416
  // src/components/platform/ApiSpecBrowser.tsx
7329
- import * as React46 from "react";
7417
+ import * as React47 from "react";
7330
7418
  import { jsx as jsx72, jsxs as jsxs66 } from "react/jsx-runtime";
7331
7419
  var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
7332
7420
  function Json({ obj }) {
7333
7421
  return /* @__PURE__ */ jsx72("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
7334
7422
  }
7335
7423
  function Endpoint({ s, onRequest }) {
7336
- const [tried, setTried] = React46.useState(null);
7424
+ const [tried, setTried] = React47.useState(null);
7337
7425
  const run = async () => {
7338
7426
  setTried("busy");
7339
7427
  const t0 = (window.performance || Date).now();
@@ -7406,10 +7494,10 @@ function ApiSpecBrowser({
7406
7494
  className = "",
7407
7495
  ...rest
7408
7496
  }) {
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);
7497
+ const [q, setQ] = React47.useState("");
7498
+ const [method, setMethod] = React47.useState(null);
7499
+ const [mod, setMod] = React47.useState(null);
7500
+ const [listOnly, setListOnly] = React47.useState(false);
7413
7501
  const activeModule = modules && modules.find((m) => m.id === mod);
7414
7502
  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
7503
  const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
@@ -7488,7 +7576,7 @@ function ApiSpecBrowser({
7488
7576
  }
7489
7577
 
7490
7578
  // src/components/platform/ProfilePage.tsx
7491
- import * as React47 from "react";
7579
+ import * as React48 from "react";
7492
7580
  import { jsx as jsx73, jsxs as jsxs67 } from "react/jsx-runtime";
7493
7581
  function fieldInitialValue(field, user) {
7494
7582
  if (field.value) return field.value(user);
@@ -7524,21 +7612,21 @@ function ProfilePage({
7524
7612
  }) {
7525
7613
  const { user: sessionUser, roles, permissions, saveProfile: saveProfile2 } = useSessionContext();
7526
7614
  const user = userProp || sessionUser;
7527
- const initialFieldValues = React47.useMemo(() => {
7615
+ const initialFieldValues = React48.useMemo(() => {
7528
7616
  const values2 = {};
7529
7617
  for (const field of fields) values2[field.key] = fieldInitialValue(field, user);
7530
7618
  return values2;
7531
7619
  }, [user.id, fields]);
7532
- const initialNotify = React47.useMemo(() => {
7620
+ const initialNotify = React48.useMemo(() => {
7533
7621
  const values2 = {};
7534
7622
  for (const n of notifications) values2[n.key] = !!user.notify?.[n.key];
7535
7623
  return values2;
7536
7624
  }, [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);
7625
+ const [values, setValues] = React48.useState(initialFieldValues);
7626
+ const [notify, setNotify] = React48.useState(initialNotify);
7627
+ const [saving, setSaving] = React48.useState(false);
7628
+ const [saved, setSaved] = React48.useState(false);
7629
+ const [prevUserId, setPrevUserId] = React48.useState(user.id);
7542
7630
  if (user.id !== prevUserId) {
7543
7631
  setPrevUserId(user.id);
7544
7632
  setValues(initialFieldValues);
@@ -7726,11 +7814,11 @@ function ProfilePage({
7726
7814
  }
7727
7815
 
7728
7816
  // src/components/platform/SignInPage.tsx
7729
- import * as React48 from "react";
7817
+ import * as React49 from "react";
7730
7818
  import { Fragment as Fragment18, jsx as jsx74, jsxs as jsxs68 } from "react/jsx-runtime";
7731
7819
  function SignInPage({ productName, onSignIn, continueAs, error }) {
7732
- const [signingIn, setSigningIn] = React48.useState(false);
7733
- const [resuming, setResuming] = React48.useState(false);
7820
+ const [signingIn, setSigningIn] = React49.useState(false);
7821
+ const [resuming, setResuming] = React49.useState(false);
7734
7822
  function handleSignIn() {
7735
7823
  setSigningIn(true);
7736
7824
  onSignIn();
@@ -7919,10 +8007,10 @@ function GoogleGlyph() {
7919
8007
  }
7920
8008
 
7921
8009
  // src/components/platform/RoadmapTimeline.tsx
7922
- import * as React51 from "react";
8010
+ import * as React52 from "react";
7923
8011
 
7924
8012
  // src/kits/session.ts
7925
- import * as React49 from "react";
8013
+ import * as React50 from "react";
7926
8014
  var PERMISSION_CATALOG = [
7927
8015
  { group: "Plans", items: [
7928
8016
  { key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
@@ -9489,8 +9577,8 @@ function roadmap(overrides) {
9489
9577
  };
9490
9578
  }
9491
9579
  function useSession() {
9492
- const [s, setS] = React49.useState(getSession);
9493
- React49.useEffect(() => subscribe(setS), []);
9580
+ const [s, setS] = React50.useState(getSession);
9581
+ React50.useEffect(() => subscribe(setS), []);
9494
9582
  return s;
9495
9583
  }
9496
9584
  var SessionKit = {
@@ -9523,7 +9611,7 @@ var SessionKit = {
9523
9611
  };
9524
9612
  function DemoSessionProvider({ children }) {
9525
9613
  const session = useSession();
9526
- const value = React49.useMemo(
9614
+ const value = React50.useMemo(
9527
9615
  () => ({
9528
9616
  user: session.user,
9529
9617
  roles: session.roles,
@@ -9539,11 +9627,11 @@ function DemoSessionProvider({ children }) {
9539
9627
  }),
9540
9628
  [session]
9541
9629
  );
9542
- return React49.createElement(SessionProvider, { value }, children);
9630
+ return React50.createElement(SessionProvider, { value }, children);
9543
9631
  }
9544
9632
 
9545
9633
  // src/kits/runtime.ts
9546
- import * as React50 from "react";
9634
+ import * as React51 from "react";
9547
9635
  var WIRED_ENDPOINTS = [
9548
9636
  // Nothing yet. Every id below would come from a real service:
9549
9637
  // "plan.get", "placements.list", …
@@ -9714,8 +9802,8 @@ var RuntimeKit = {
9714
9802
  };
9715
9803
  RuntimeKit.declare(FEATURE_NEEDS);
9716
9804
  function useRuntimeMode() {
9717
- const [m, setM] = React50.useState(RuntimeKit.getMode());
9718
- React50.useEffect(() => RuntimeKit.subscribe(setM), []);
9805
+ const [m, setM] = React51.useState(RuntimeKit.getMode());
9806
+ React51.useEffect(() => RuntimeKit.subscribe(setM), []);
9719
9807
  return m;
9720
9808
  }
9721
9809
  function useFeatureStatus(key) {
@@ -9798,12 +9886,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
9798
9886
  }
9799
9887
  function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9800
9888
  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(() => {
9889
+ const [project, setProject] = React52.useState("");
9890
+ const [q, setQ] = React52.useState("");
9891
+ const scrollRef = React52.useRef(null);
9892
+ const nowRef = React52.useRef(null);
9893
+ const rm = React52.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
9894
+ const byKey = React52.useMemo(() => {
9807
9895
  const m = {};
9808
9896
  rm.items.forEach((i) => {
9809
9897
  m[i.key] = i;
@@ -9812,7 +9900,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9812
9900
  }, [rm]);
9813
9901
  const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
9814
9902
  const projects = [...new Set(rm.items.map((i) => i.project))];
9815
- React51.useEffect(() => {
9903
+ React52.useEffect(() => {
9816
9904
  let raf1 = 0, raf2 = 0;
9817
9905
  const place = () => {
9818
9906
  const box = scrollRef.current, mark = nowRef.current;
@@ -9886,7 +9974,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9886
9974
  lastPhase = item.phase;
9887
9975
  const inPhase = items.filter((i) => i.phase === item.phase).length;
9888
9976
  const isBoundary = n === firstPending;
9889
- return /* @__PURE__ */ jsxs69(React51.Fragment, { children: [
9977
+ return /* @__PURE__ */ jsxs69(React52.Fragment, { children: [
9890
9978
  showPhase ? /* @__PURE__ */ jsxs69("div", { className: "fd-rm-era", children: [
9891
9979
  /* @__PURE__ */ jsxs69("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
9892
9980
  /* @__PURE__ */ jsx75("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
@@ -9930,7 +10018,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9930
10018
  }
9931
10019
 
9932
10020
  // src/components/platform/ComingSoon.tsx
9933
- import * as React52 from "react";
10021
+ import * as React53 from "react";
9934
10022
  import { createPortal as createPortal5 } from "react-dom";
9935
10023
  import { Fragment as Fragment20, jsx as jsx76, jsxs as jsxs70 } from "react/jsx-runtime";
9936
10024
  var BYPASS_STORE = "fd.soon.bypass.v1";
@@ -9948,8 +10036,8 @@ function writeBypassed(list) {
9948
10036
  }
9949
10037
  }
9950
10038
  function useBypass(key) {
9951
- const [on, setOn] = React52.useState(() => !!key && readBypassed().indexOf(key) >= 0);
9952
- React52.useEffect(() => {
10039
+ const [on, setOn] = React53.useState(() => !!key && readBypassed().indexOf(key) >= 0);
10040
+ React53.useEffect(() => {
9953
10041
  setOn(!!key && readBypassed().indexOf(key) >= 0);
9954
10042
  }, [key]);
9955
10043
  const set = (next) => {
@@ -9996,9 +10084,9 @@ function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onB
9996
10084
  ] });
9997
10085
  }
9998
10086
  function useHoverCard(open) {
9999
- const anchor = React52.useRef(null);
10000
- const [pos, setPos] = React52.useState(null);
10001
- React52.useLayoutEffect(() => {
10087
+ const anchor = React53.useRef(null);
10088
+ const [pos, setPos] = React53.useState(null);
10089
+ React53.useLayoutEffect(() => {
10002
10090
  if (!open || !anchor.current) {
10003
10091
  setPos(null);
10004
10092
  return;
@@ -10125,10 +10213,10 @@ function ComingSoon({
10125
10213
  ] });
10126
10214
  }
10127
10215
  function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
10128
- const [open, setOpen] = React52.useState(false);
10216
+ const [open, setOpen] = React53.useState(false);
10129
10217
  const [anchor, pos] = useHoverCard(open);
10130
10218
  const zIndex = useOverlayLayer(open);
10131
- const close = React52.useRef(null);
10219
+ const close = React53.useRef(null);
10132
10220
  const show = () => {
10133
10221
  if (close.current) {
10134
10222
  clearTimeout(close.current);
@@ -10144,7 +10232,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
10144
10232
  setOpen(false);
10145
10233
  }, 140);
10146
10234
  };
10147
- React52.useEffect(() => () => {
10235
+ React53.useEffect(() => () => {
10148
10236
  if (close.current) clearTimeout(close.current);
10149
10237
  }, []);
10150
10238
  return /* @__PURE__ */ jsxs70(
@@ -10315,14 +10403,14 @@ function PermissionHint({ perm, children }) {
10315
10403
  }
10316
10404
 
10317
10405
  // src/components/platform/ModeSwitch.tsx
10318
- import * as React53 from "react";
10406
+ import * as React54 from "react";
10319
10407
  import { Fragment as Fragment21, jsx as jsx78, jsxs as jsxs72 } from "react/jsx-runtime";
10320
10408
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
10321
10409
  const mode2 = useRuntimeMode();
10322
- const [open, setOpen] = React53.useState(false);
10410
+ const [open, setOpen] = React54.useState(false);
10323
10411
  const zIndex = useOverlayLayer(open);
10324
- const ref = React53.useRef(null);
10325
- React53.useEffect(() => {
10412
+ const ref = React54.useRef(null);
10413
+ React54.useEffect(() => {
10326
10414
  if (!open) return;
10327
10415
  const away = (e) => {
10328
10416
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -10578,7 +10666,7 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
10578
10666
  }
10579
10667
 
10580
10668
  // src/components/planner/MixGap.tsx
10581
- import * as React54 from "react";
10669
+ import * as React55 from "react";
10582
10670
  import { Fragment as Fragment22, jsx as jsx81, jsxs as jsxs75 } from "react/jsx-runtime";
10583
10671
  var MIX_GAP_LAYOUT = {
10584
10672
  trackHeight: 30,
@@ -10593,7 +10681,7 @@ function mixGapOffset(value, max) {
10593
10681
  }
10594
10682
  function MixGap({ rows = [], loading = false, className = "" }) {
10595
10683
  const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
10596
- const [hover, setHover] = React54.useState(null);
10684
+ const [hover, setHover] = React55.useState(null);
10597
10685
  const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
10598
10686
  const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
10599
10687
  const { trackHeight, barHeight, targetWidth, labelGap } = MIX_GAP_LAYOUT;
@@ -10736,7 +10824,7 @@ function ChannelContribution({
10736
10824
  }
10737
10825
 
10738
10826
  // src/components/planner/BudgetReallocator.tsx
10739
- import * as React55 from "react";
10827
+ import * as React56 from "react";
10740
10828
  import { Fragment as Fragment23, jsx as jsx83, jsxs as jsxs77 } from "react/jsx-runtime";
10741
10829
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
10742
10830
  function BudgetReallocator({
@@ -10752,8 +10840,8 @@ function BudgetReallocator({
10752
10840
  onCancel,
10753
10841
  className = ""
10754
10842
  }) {
10755
- const [draft, setDraft] = React55.useState(spend);
10756
- React55.useEffect(() => setDraft(spend), [spend]);
10843
+ const [draft, setDraft] = React56.useState(spend);
10844
+ React56.useEffect(() => setDraft(spend), [spend]);
10757
10845
  const dirty = draft !== spend;
10758
10846
  const nextCrp = scoreFor ? scoreFor(draft) : crp;
10759
10847
  const nextBand = bandFor(nextCrp);
@@ -10768,10 +10856,10 @@ function BudgetReallocator({
10768
10856
  setDraft(spend);
10769
10857
  if (onCancel) onCancel();
10770
10858
  };
10771
- const trackRef = React55.useRef(null);
10772
- const chipRef = React55.useRef(null);
10773
- const [chipLeft, setChipLeft] = React55.useState(null);
10774
- React55.useLayoutEffect(() => {
10859
+ const trackRef = React56.useRef(null);
10860
+ const chipRef = React56.useRef(null);
10861
+ const [chipLeft, setChipLeft] = React56.useState(null);
10862
+ React56.useLayoutEffect(() => {
10775
10863
  const track = trackRef.current;
10776
10864
  if (!dirty || !track) {
10777
10865
  setChipLeft(null);
@@ -10958,7 +11046,7 @@ function ImportanceTag({ level, showLabel = true, size = "sm", className = "" })
10958
11046
  }
10959
11047
 
10960
11048
  // src/components/planner/PreferenceMeter.tsx
10961
- import * as React56 from "react";
11049
+ import * as React57 from "react";
10962
11050
  import { jsx as jsx86, jsxs as jsxs80 } from "react/jsx-runtime";
10963
11051
  function preferenceScore(criteria) {
10964
11052
  const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
@@ -10988,7 +11076,7 @@ function PreferenceMeter({
10988
11076
  className = ""
10989
11077
  }) {
10990
11078
  const value = score == null ? preferenceScore(criteria) : score;
10991
- const segments = React56.useMemo(() => preferenceSegments(criteria), [criteria]);
11079
+ const segments = React57.useMemo(() => preferenceSegments(criteria), [criteria]);
10992
11080
  return /* @__PURE__ */ jsx86(
10993
11081
  ScoreMeter,
10994
11082
  {
@@ -11027,10 +11115,10 @@ function PreferenceMeter({
11027
11115
  }
11028
11116
 
11029
11117
  // src/components/chat/AgentChatPanel.tsx
11030
- import * as React62 from "react";
11118
+ import * as React63 from "react";
11031
11119
 
11032
11120
  // src/components/chat/chatEngine.ts
11033
- import * as React57 from "react";
11121
+ import * as React58 from "react";
11034
11122
  var CHAT_UNAVAILABLE = "chat_unavailable";
11035
11123
  var JOB_PENDING = ["queued", "running"];
11036
11124
  var JOB_SUCCESS = ["completed", "recovered"];
@@ -11084,22 +11172,22 @@ function useChatEngine(opts) {
11084
11172
  onClear,
11085
11173
  onFeedback
11086
11174
  } = 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(() => {
11175
+ const [status, setStatus] = React58.useState("idle");
11176
+ const [threadId, setThreadId] = React58.useState(null);
11177
+ const [messages, setMessages] = React58.useState([]);
11178
+ const [queue, setQueue] = React58.useState([]);
11179
+ const [fatal, setFatal] = React58.useState(null);
11180
+ const [busy, setBusy] = React58.useState(false);
11181
+ const [turnStartedAt, setTurnStartedAt] = React58.useState(null);
11182
+ const listRef = React58.useRef([]);
11183
+ const queueRef = React58.useRef([]);
11184
+ const busyRef = React58.useRef(false);
11185
+ const stoppedRef = React58.useRef(false);
11186
+ const abortRef = React58.useRef(null);
11187
+ const serverCount = React58.useRef(0);
11188
+ const threadRef = React58.useRef(null);
11189
+ const mounted = React58.useRef(true);
11190
+ React58.useEffect(() => {
11103
11191
  mounted.current = true;
11104
11192
  return () => {
11105
11193
  mounted.current = false;
@@ -11121,14 +11209,14 @@ function useChatEngine(opts) {
11121
11209
  }
11122
11210
  return false;
11123
11211
  };
11124
- const loadThread = React57.useCallback(async (id) => {
11212
+ const loadThread = React58.useCallback(async (id) => {
11125
11213
  const data = await apiAdapter.getThread(id);
11126
11214
  const list = [...data && data.messages || []];
11127
11215
  serverCount.current = list.length;
11128
11216
  commit(list);
11129
11217
  return list;
11130
11218
  }, [apiAdapter]);
11131
- React57.useEffect(() => {
11219
+ React58.useEffect(() => {
11132
11220
  if (!apiAdapter) {
11133
11221
  setStatus("idle");
11134
11222
  setFatal(null);
@@ -11341,7 +11429,7 @@ function useChatEngine(opts) {
11341
11429
  await dispatchTurn(turn);
11342
11430
  }
11343
11431
  }
11344
- const send = React57.useCallback((text, attachments) => {
11432
+ const send = React58.useCallback((text, attachments) => {
11345
11433
  const body = (text || "").trim();
11346
11434
  if (!body && !(attachments && attachments.length)) return;
11347
11435
  if (status === "disconnected") return;
@@ -11351,7 +11439,7 @@ function useChatEngine(opts) {
11351
11439
  stoppedRef.current = false;
11352
11440
  drain();
11353
11441
  }, [status]);
11354
- const stop = React57.useCallback(() => {
11442
+ const stop = React58.useCallback(() => {
11355
11443
  stoppedRef.current = true;
11356
11444
  const ac = abortRef.current;
11357
11445
  if (ac) {
@@ -11370,11 +11458,11 @@ function useChatEngine(opts) {
11370
11458
  store.del(STORAGE_PREFIX + threadRef.current);
11371
11459
  }
11372
11460
  }, [apiAdapter]);
11373
- const removeQueued = React57.useCallback((id) => {
11461
+ const removeQueued = React58.useCallback((id) => {
11374
11462
  queueRef.current = queueRef.current.filter((t) => t.id !== id);
11375
11463
  setQueue(queueRef.current.slice());
11376
11464
  }, []);
11377
- const retry = React57.useCallback(() => {
11465
+ const retry = React58.useCallback(() => {
11378
11466
  const list = listRef.current;
11379
11467
  let at = -1;
11380
11468
  for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
@@ -11390,19 +11478,19 @@ function useChatEngine(opts) {
11390
11478
  setQueue(queueRef.current.slice());
11391
11479
  drain();
11392
11480
  }, []);
11393
- const clear = React57.useCallback(() => {
11481
+ const clear = React58.useCallback(() => {
11394
11482
  commit([]);
11395
11483
  serverCount.current = 0;
11396
11484
  queueRef.current = [];
11397
11485
  setQueue([]);
11398
11486
  onClear && onClear();
11399
11487
  }, [onClear]);
11400
- const setFeedback = React57.useCallback((id, value) => {
11488
+ const setFeedback = React58.useCallback((id, value) => {
11401
11489
  patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
11402
11490
  const msg = listRef.current.find((m) => m.id === id);
11403
11491
  onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
11404
11492
  }, [onFeedback]);
11405
- const reload = React57.useCallback(async () => {
11493
+ const reload = React58.useCallback(async () => {
11406
11494
  if (!threadRef.current) return;
11407
11495
  setStatus("loading");
11408
11496
  try {
@@ -11440,10 +11528,10 @@ var ChatKit = {
11440
11528
  };
11441
11529
 
11442
11530
  // src/components/chat/ChatTranscript.tsx
11443
- import * as React59 from "react";
11531
+ import * as React60 from "react";
11444
11532
 
11445
11533
  // src/components/chat/ChatTurn.tsx
11446
- import * as React58 from "react";
11534
+ import * as React59 from "react";
11447
11535
  import { jsx as jsx87, jsxs as jsxs81 } from "react/jsx-runtime";
11448
11536
  function JsonView({ value }) {
11449
11537
  let text;
@@ -11480,7 +11568,7 @@ function PacketCard({ packet, schema, render, onApply, applied }) {
11480
11568
  ] });
11481
11569
  }
11482
11570
  function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
11483
- const [open, setOpen] = React58.useState(defaultOpen);
11571
+ const [open, setOpen] = React59.useState(defaultOpen);
11484
11572
  if (!text) return null;
11485
11573
  return /* @__PURE__ */ jsxs81("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
11486
11574
  /* @__PURE__ */ jsxs81("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -11497,7 +11585,7 @@ function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
11497
11585
  ] });
11498
11586
  }
11499
11587
  function Citations({ items = [], onOpen }) {
11500
- const [open, setOpen] = React58.useState(false);
11588
+ const [open, setOpen] = React59.useState(false);
11501
11589
  if (!items.length) return null;
11502
11590
  return /* @__PURE__ */ jsxs81("div", { className: "fdc-cites", children: [
11503
11591
  /* @__PURE__ */ jsxs81("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -11523,7 +11611,7 @@ function clampText(text, max) {
11523
11611
  return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
11524
11612
  }
11525
11613
  function MessageBody({ message: m, ctx }) {
11526
- const [expanded, setExpanded] = React58.useState(false);
11614
+ const [expanded, setExpanded] = React59.useState(false);
11527
11615
  const isUser = m.role === "user";
11528
11616
  const raw = m.text || "";
11529
11617
  const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
@@ -11590,9 +11678,9 @@ function MessageBody({ message: m, ctx }) {
11590
11678
  ] });
11591
11679
  }
11592
11680
  function useCopyRun() {
11593
- const [done, setDone] = React58.useState(false);
11594
- const t = React58.useRef(null);
11595
- React58.useEffect(() => () => {
11681
+ const [done, setDone] = React59.useState(false);
11682
+ const t = React59.useRef(null);
11683
+ React59.useEffect(() => () => {
11596
11684
  if (t.current) clearTimeout(t.current);
11597
11685
  }, []);
11598
11686
  return [done, (text) => {
@@ -11619,7 +11707,7 @@ function RunActions({ group, ctx }) {
11619
11707
  ] }),
11620
11708
  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
11709
  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: [
11710
+ isAssistant && ctx.onFeedback ? /* @__PURE__ */ jsxs81(React59.Fragment, { children: [
11623
11711
  /* @__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
11712
  /* @__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
11713
  ] }) : null,
@@ -11714,10 +11802,10 @@ function ChatTranscript({
11714
11802
  renderEmpty,
11715
11803
  className = ""
11716
11804
  }) {
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);
11805
+ const scroller = React60.useRef(null);
11806
+ const stick = React60.useRef(true);
11807
+ const [pill, setPill] = React60.useState(0);
11808
+ const seen = React60.useRef(0);
11721
11809
  const toBottom = (smooth) => {
11722
11810
  const el = scroller.current;
11723
11811
  if (!el) return;
@@ -11736,7 +11824,7 @@ function ChatTranscript({
11736
11824
  seen.current = messages.length;
11737
11825
  }
11738
11826
  };
11739
- React59.useLayoutEffect(() => {
11827
+ React60.useLayoutEffect(() => {
11740
11828
  const el = scroller.current;
11741
11829
  if (!el) return;
11742
11830
  if (stick.current) {
@@ -11744,7 +11832,7 @@ function ChatTranscript({
11744
11832
  seen.current = messages.length;
11745
11833
  } else setPill(Math.max(0, messages.length - seen.current));
11746
11834
  }, [messages]);
11747
- React59.useEffect(() => {
11835
+ React60.useEffect(() => {
11748
11836
  const el = scroller.current;
11749
11837
  const inner = el && el.firstChild;
11750
11838
  if (!el || !inner || typeof ResizeObserver === "undefined") return;
@@ -11754,7 +11842,7 @@ function ChatTranscript({
11754
11842
  ro.observe(inner);
11755
11843
  return () => ro.disconnect();
11756
11844
  }, []);
11757
- const groups = React59.useMemo(() => groupMessages(messages), [messages]);
11845
+ const groups = React60.useMemo(() => groupMessages(messages), [messages]);
11758
11846
  const empty = !messages.length && status === "ready";
11759
11847
  return /* @__PURE__ */ jsxs82("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
11760
11848
  /* @__PURE__ */ jsxs82("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
@@ -11778,7 +11866,7 @@ function ChatTranscript({
11778
11866
  const prev = groups[i - 1];
11779
11867
  const k = dayKey(g.messages[0].timestamp);
11780
11868
  const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
11781
- return /* @__PURE__ */ jsxs82(React59.Fragment, { children: [
11869
+ return /* @__PURE__ */ jsxs82(React60.Fragment, { children: [
11782
11870
  showDay ? /* @__PURE__ */ jsx88("div", { className: "fdc-day", children: /* @__PURE__ */ jsx88("span", { children: dayLabel(k) }) }) : null,
11783
11871
  /* @__PURE__ */ jsx88(ChatTurn, { group: g, ctx })
11784
11872
  ] }, g.key || i);
@@ -11795,7 +11883,7 @@ function ChatTranscript({
11795
11883
  var TranscriptKit = { groupMessages };
11796
11884
 
11797
11885
  // src/components/chat/ChatComposer.tsx
11798
- import * as React60 from "react";
11886
+ import * as React61 from "react";
11799
11887
  import { jsx as jsx89, jsxs as jsxs83 } from "react/jsx-runtime";
11800
11888
  function ChatComposer({
11801
11889
  onSubmit,
@@ -11823,17 +11911,17 @@ function ChatComposer({
11823
11911
  onReject,
11824
11912
  onOpenAttachment
11825
11913
  }) {
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);
11914
+ const [text, setText] = React61.useState(draft || "");
11915
+ const [trigger, setTrigger] = React61.useState(null);
11916
+ const [mentionItems, setMentionItems] = React61.useState([]);
11917
+ const [listening, setListening] = React61.useState(false);
11918
+ const [notice, setNotice] = React61.useState(null);
11919
+ const editor = React61.useRef(null);
11920
+ const wrap = React61.useRef(null);
11921
+ const stopVoice = React61.useRef(null);
11834
11922
  const staged = useStagedFiles(fileUploadHandler, { onError: () => {
11835
11923
  } });
11836
- React60.useEffect(() => {
11924
+ React61.useEffect(() => {
11837
11925
  if (draft != null && draft !== text) setText(draft);
11838
11926
  }, [draft]);
11839
11927
  const change = (v) => {
@@ -11869,7 +11957,7 @@ function ChatComposer({
11869
11957
  e.preventDefault();
11870
11958
  staged.add(found.files);
11871
11959
  };
11872
- React60.useEffect(() => {
11960
+ React61.useEffect(() => {
11873
11961
  if (!trigger || trigger.type !== "mention" || !mentionSources) {
11874
11962
  setMentionItems([]);
11875
11963
  return;
@@ -11887,7 +11975,7 @@ function ChatComposer({
11887
11975
  const q = (trigger.query || "").toLowerCase();
11888
11976
  setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
11889
11977
  }, [trigger, mentionSources]);
11890
- const slashItems = React60.useMemo(() => {
11978
+ const slashItems = React61.useMemo(() => {
11891
11979
  if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
11892
11980
  const q = (trigger.query || "").toLowerCase();
11893
11981
  return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
@@ -12061,12 +12149,12 @@ function ChatComposer({
12061
12149
  }
12062
12150
 
12063
12151
  // src/components/chat/ChatSessionBar.tsx
12064
- import * as React61 from "react";
12152
+ import * as React62 from "react";
12065
12153
  import { jsx as jsx90, jsxs as jsxs84 } from "react/jsx-runtime";
12066
12154
  var compact2 = meterFormats.compact;
12067
12155
  function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
12068
- const [open, setOpen] = React61.useState(false);
12069
- const anchor = React61.useRef(null);
12156
+ const [open, setOpen] = React62.useState(false);
12157
+ const anchor = React62.useRef(null);
12070
12158
  const stats = sessionStats || null;
12071
12159
  const cu = contextUsage || null;
12072
12160
  const limits = usageLimits || null;
@@ -12081,7 +12169,7 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
12081
12169
  if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
12082
12170
  if (cu) bits.push(pct + "% context");
12083
12171
  const expandable = !!(cu || limits);
12084
- return /* @__PURE__ */ jsxs84(React61.Fragment, { children: [
12172
+ return /* @__PURE__ */ jsxs84(React62.Fragment, { children: [
12085
12173
  /* @__PURE__ */ jsxs84("div", { className: "fdc-bar", children: [
12086
12174
  /* @__PURE__ */ jsxs84(
12087
12175
  "button",
@@ -12222,7 +12310,7 @@ function ModelControls({
12222
12310
  ] })
12223
12311
  });
12224
12312
  }
12225
- return /* @__PURE__ */ jsxs84(React61.Fragment, { children: [
12313
+ return /* @__PURE__ */ jsxs84(React62.Fragment, { children: [
12226
12314
  /* @__PURE__ */ jsx90(
12227
12315
  MenuButton,
12228
12316
  {
@@ -12339,11 +12427,11 @@ function AgentChatPanel({
12339
12427
  onFeedback,
12340
12428
  onEditMessage
12341
12429
  }) {
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]);
12430
+ const [panelWidth, setPanelWidth] = React63.useState(width);
12431
+ const [applied, setApplied] = React63.useState({});
12432
+ const [draft, setDraft] = React63.useState("");
12433
+ const dragging = React63.useRef(null);
12434
+ React63.useEffect(() => setPanelWidth(width), [width]);
12347
12435
  const engine = useChatEngine({
12348
12436
  contextType,
12349
12437
  contextId,
@@ -12547,7 +12635,7 @@ function AgentChatPanel({
12547
12635
  }
12548
12636
 
12549
12637
  // src/components/files/FileViewer.tsx
12550
- import * as React63 from "react";
12638
+ import * as React64 from "react";
12551
12639
  import { jsx as jsx92, jsxs as jsxs86 } from "react/jsx-runtime";
12552
12640
  function previewKindOf(file) {
12553
12641
  const mime = (file.mime || "").toLowerCase();
@@ -12604,13 +12692,13 @@ function parseDelimited(text, delimiter) {
12604
12692
  return rows;
12605
12693
  }
12606
12694
  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(() => {
12695
+ const [state, setState] = React64.useState({ status: "loading" });
12696
+ const [attempt, setAttempt] = React64.useState(0);
12697
+ const resolveRef = React64.useRef(resolveUrl);
12698
+ React64.useEffect(() => {
12611
12699
  resolveRef.current = resolveUrl;
12612
12700
  }, [resolveUrl]);
12613
- React63.useEffect(() => {
12701
+ React64.useEffect(() => {
12614
12702
  if (kind === "none") {
12615
12703
  setState({ status: "ready" });
12616
12704
  return;
@@ -12640,7 +12728,7 @@ function usePreview(file, kind, resolveUrl) {
12640
12728
  if (ac) ac.abort();
12641
12729
  };
12642
12730
  }, [file.id, kind, attempt]);
12643
- return { ...state, retry: React63.useCallback(() => setAttempt((a) => a + 1), []) };
12731
+ return { ...state, retry: React64.useCallback(() => setAttempt((a) => a + 1), []) };
12644
12732
  }
12645
12733
  function PreviewBody({ file, kind, state }) {
12646
12734
  if (kind === "image") return /* @__PURE__ */ jsx92("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
@@ -12685,13 +12773,13 @@ function PreviewBody({ file, kind, state }) {
12685
12773
  ] });
12686
12774
  }
12687
12775
  function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", onClose, actions, className = "" }) {
12688
- const kind = React63.useMemo(() => previewKindOf(file), [file]);
12776
+ const kind = React64.useMemo(() => previewKindOf(file), [file]);
12689
12777
  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);
12778
+ const [tab, setTab] = React64.useState("preview");
12779
+ const [downloading, setDownloading] = React64.useState(false);
12780
+ const [downloadError, setDownloadError] = React64.useState(null);
12693
12781
  const hasSide = sidePanel != null;
12694
- React63.useEffect(() => {
12782
+ React64.useEffect(() => {
12695
12783
  setTab("preview");
12696
12784
  }, [file.id]);
12697
12785
  const download = () => {
@@ -12758,7 +12846,7 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
12758
12846
  }
12759
12847
 
12760
12848
  // src/components/files/FileBrowser.tsx
12761
- import * as React64 from "react";
12849
+ import * as React65 from "react";
12762
12850
  import { Fragment as Fragment27, jsx as jsx93, jsxs as jsxs87 } from "react/jsx-runtime";
12763
12851
  function childrenOf(folders, parentId) {
12764
12852
  return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
@@ -12829,10 +12917,10 @@ function FolderPickerMenu({
12829
12917
  destinations,
12830
12918
  onPick
12831
12919
  }) {
12832
- const [open, setOpen] = React64.useState(false);
12920
+ const [open, setOpen] = React65.useState(false);
12833
12921
  const zIndex = useOverlayLayer(open);
12834
- const ref = React64.useRef(null);
12835
- React64.useEffect(() => {
12922
+ const ref = React65.useRef(null);
12923
+ React65.useEffect(() => {
12836
12924
  if (!open) return;
12837
12925
  const away = (e) => {
12838
12926
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -12884,8 +12972,8 @@ function FolderPickerMenu({
12884
12972
  ] });
12885
12973
  }
12886
12974
  function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
12887
- const [over, setOver] = React64.useState(false);
12888
- const dragCounter = React64.useRef(0);
12975
+ const [over, setOver] = React65.useState(false);
12976
+ const dragCounter = React65.useRef(0);
12889
12977
  const dropHandlers = isDropTarget ? {
12890
12978
  onDragEnter: (e) => {
12891
12979
  if (!hasInternalPayload(e)) return;
@@ -12933,8 +13021,8 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
12933
13021
  }
12934
13022
  function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
12935
13023
  const kids = childrenOf(folders, folder.id);
12936
- const [expanded, setExpanded] = React64.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
12937
- React64.useEffect(() => {
13024
+ const [expanded, setExpanded] = React65.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
13025
+ React65.useEffect(() => {
12938
13026
  if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
12939
13027
  }, [currentFolderId, folder.id, folders]);
12940
13028
  const isCurrent = folder.id === currentFolderId;
@@ -13000,15 +13088,15 @@ function FileBrowser({
13000
13088
  const canUpload = perm.canUpload !== false && !!onUpload;
13001
13089
  const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
13002
13090
  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(
13091
+ const [createOpen, setCreateOpen] = React65.useState(false);
13092
+ const [createName, setCreateName] = React65.useState("");
13093
+ const [renameTarget, setRenameTarget] = React65.useState(null);
13094
+ const [renameValue, setRenameValue] = React65.useState("");
13095
+ const [deleteTarget, setDeleteTarget] = React65.useState(null);
13096
+ const uploadInputRef = React65.useRef(null);
13097
+ const crumbFolders = React65.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
13098
+ const subfolders = React65.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
13099
+ const currentFiles = React65.useMemo(
13012
13100
  () => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
13013
13101
  [files, currentFolderId]
13014
13102
  );
@@ -13240,8 +13328,8 @@ function BreadcrumbCrumb({
13240
13328
  isDropTarget,
13241
13329
  onDropPayload
13242
13330
  }) {
13243
- const [over, setOver] = React64.useState(false);
13244
- const dragCounter = React64.useRef(0);
13331
+ const [over, setOver] = React65.useState(false);
13332
+ const dragCounter = React65.useRef(0);
13245
13333
  const dropHandlers = isDropTarget ? {
13246
13334
  onDragEnter: (e) => {
13247
13335
  if (!hasInternalPayload(e)) return;
@@ -13270,7 +13358,7 @@ function BreadcrumbCrumb({
13270
13358
  }
13271
13359
 
13272
13360
  // src/kits/query.ts
13273
- import * as React65 from "react";
13361
+ import * as React66 from "react";
13274
13362
  var isMultiSelectShaped = (v) => !!v && typeof v === "object" && Array.isArray(v.values);
13275
13363
  var isRangeShaped = (v) => !!v && typeof v === "object" && ("min" in v || "max" in v);
13276
13364
  var isDateShaped = (v) => !!v && typeof v === "object" && "kind" in v;
@@ -13325,22 +13413,22 @@ function compare(a, b, dir) {
13325
13413
  var API = null;
13326
13414
  var PREFS = null;
13327
13415
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
13328
- const [query, setQuery] = React65.useState(() => {
13416
+ const [query, setQuery] = React66.useState(() => {
13329
13417
  const store = PREFS || window.PlannerPrefs;
13330
13418
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
13331
13419
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
13332
13420
  });
13333
- const savePref = React65.useCallback((patch2) => {
13421
+ const savePref = React66.useCallback((patch2) => {
13334
13422
  const store = PREFS || window.PlannerPrefs;
13335
13423
  if (prefsKey && store) store.setTable(prefsKey, patch2);
13336
13424
  }, [prefsKey]);
13337
- const [res, setRes] = React65.useState(null);
13338
- const [loading, setLoading] = React65.useState(true);
13339
- const seq2 = React65.useRef(0);
13425
+ const [res, setRes] = React66.useState(null);
13426
+ const [loading, setLoading] = React66.useState(true);
13427
+ const seq2 = React66.useRef(0);
13340
13428
  const depKey = (deps || []).join("|");
13341
13429
  const paramKey = JSON.stringify(params || {});
13342
13430
  const queryKey = JSON.stringify(query);
13343
- React65.useEffect(() => {
13431
+ React66.useEffect(() => {
13344
13432
  const id = ++seq2.current;
13345
13433
  setLoading(true);
13346
13434
  const t = setTimeout(() => {
@@ -13659,12 +13747,12 @@ function createVersionStore(initialState, options) {
13659
13747
  }
13660
13748
 
13661
13749
  // src/kits/documentTitle.ts
13662
- import * as React66 from "react";
13750
+ import * as React67 from "react";
13663
13751
  function formatDocumentTitle(routeTitle, appName) {
13664
13752
  return [routeTitle, appName, "Flytedesk"].filter((part) => !!part && part.length > 0).join(" \xB7 ");
13665
13753
  }
13666
13754
  function useDocumentTitle(routeTitle, appName) {
13667
- React66.useEffect(() => {
13755
+ React67.useEffect(() => {
13668
13756
  if (typeof document === "undefined") return;
13669
13757
  document.title = formatDocumentTitle(routeTitle, appName);
13670
13758
  }, [routeTitle, appName]);