@flytedan/flytebot-design-system 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2731,10 +2731,10 @@ function DataTable({
2731
2731
  }
2732
2732
 
2733
2733
  // src/components/data/FilterBar.tsx
2734
- import * as React24 from "react";
2734
+ import * as React25 from "react";
2735
2735
 
2736
2736
  // src/components/forms/NumberInput.tsx
2737
- import * as React21 from "react";
2737
+ import * as React22 from "react";
2738
2738
 
2739
2739
  // src/components/forms/field.tsx
2740
2740
  import * as React20 from "react";
@@ -2755,6 +2755,16 @@ function useTriggerLabelling(label, id) {
2755
2755
  return { fieldId, labelId, ariaLabelledBy: label ? labelId + " " + fieldId : void 0 };
2756
2756
  }
2757
2757
 
2758
+ // src/components/useLatest.ts
2759
+ import * as React21 from "react";
2760
+ function useLatest(value) {
2761
+ const ref = React21.useRef(value);
2762
+ React21.useInsertionEffect(() => {
2763
+ ref.current = value;
2764
+ });
2765
+ return ref;
2766
+ }
2767
+
2758
2768
  // src/components/forms/NumberInput.tsx
2759
2769
  import { jsx as jsx42, jsxs as jsxs36 } from "react/jsx-runtime";
2760
2770
  var HOLD_DELAY_MS = 400;
@@ -2832,24 +2842,22 @@ function NumberInput({
2832
2842
  const n = typeof v === "number" ? v : parseNumberText(v);
2833
2843
  return n == null || Number.isNaN(n) ? null : n;
2834
2844
  };
2835
- const [text, setText] = React21.useState(() => toText(value));
2836
- const [editing, setEditing] = React21.useState(false);
2845
+ const [text, setText] = React22.useState(() => toText(value));
2846
+ const [editing, setEditing] = React22.useState(false);
2837
2847
  const parsed = parseNumberText(text);
2838
2848
  const invalidMessage = parsed == null ? null : Number.isNaN(parsed) ? "Enter a number" : integer && !Number.isInteger(parsed) ? "Enter a whole number" : parsed < lo ? "Must be \u2265 " + lo : parsed > hi ? "Must be \u2264 " + hi : null;
2839
2849
  const valid = invalidMessage == null;
2840
- const committedRef = React21.useRef(toNumber(value));
2841
- const onChangeRef = React21.useRef(onChange);
2842
- onChangeRef.current = onChange;
2843
- const onValidityChangeRef = React21.useRef(onValidityChange);
2844
- onValidityChangeRef.current = onValidityChange;
2845
- const focusedRef = React21.useRef(false);
2846
- React21.useEffect(() => {
2850
+ const committedRef = React22.useRef(toNumber(value));
2851
+ const onChangeRef = useLatest(onChange);
2852
+ const onValidityChangeRef = useLatest(onValidityChange);
2853
+ const focusedRef = React22.useRef(false);
2854
+ React22.useEffect(() => {
2847
2855
  committedRef.current = toNumber(value);
2848
2856
  if (!focusedRef.current) setText(toText(value));
2849
2857
  }, [value]);
2850
- React21.useLayoutEffect(() => {
2858
+ React22.useLayoutEffect(() => {
2851
2859
  onValidityChangeRef.current?.(valid);
2852
- }, [valid]);
2860
+ }, [valid, onValidityChangeRef]);
2853
2861
  const fire = (n) => {
2854
2862
  committedRef.current = n ?? null;
2855
2863
  onChangeRef.current?.({ target: { value: n } });
@@ -2862,9 +2870,8 @@ function NumberInput({
2862
2870
  fire(next);
2863
2871
  return true;
2864
2872
  };
2865
- const bumpRef = React21.useRef(bump);
2866
- bumpRef.current = bump;
2867
- const holdTimer = React21.useRef(null);
2873
+ const bumpRef = useLatest(bump);
2874
+ const holdTimer = React22.useRef(null);
2868
2875
  const release = () => {
2869
2876
  if (holdTimer.current) clearTimeout(holdTimer.current);
2870
2877
  holdTimer.current = null;
@@ -2883,7 +2890,7 @@ function NumberInput({
2883
2890
  };
2884
2891
  holdTimer.current = setTimeout(tick, HOLD_DELAY_MS);
2885
2892
  };
2886
- React21.useEffect(() => () => {
2893
+ React22.useEffect(() => () => {
2887
2894
  if (holdTimer.current) clearTimeout(holdTimer.current);
2888
2895
  }, []);
2889
2896
  const current2 = committedRef.current;
@@ -2977,7 +2984,8 @@ function NumberInput({
2977
2984
  )
2978
2985
  ] })
2979
2986
  ] }),
2980
- shownError ? /* @__PURE__ */ jsxs36("span", { id: errorId, className: "fd-field-error", children: [
2987
+ /* @__PURE__ */ jsx42("span", { id: errorId, className: "fd-sr", role: "status", children: shownError ?? "" }),
2988
+ shownError ? /* @__PURE__ */ jsxs36("span", { className: "fd-field-error", "aria-hidden": "true", children: [
2981
2989
  /* @__PURE__ */ jsx42("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
2982
2990
  shownError
2983
2991
  ] }) : help ? /* @__PURE__ */ jsx42("span", { className: "fd-field-help", children: help }) : null
@@ -2985,7 +2993,7 @@ function NumberInput({
2985
2993
  }
2986
2994
 
2987
2995
  // src/components/forms/DatePicker.tsx
2988
- import * as React22 from "react";
2996
+ import * as React23 from "react";
2989
2997
  import { jsx as jsx43, jsxs as jsxs37 } from "react/jsx-runtime";
2990
2998
  var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
2991
2999
  var DOW = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
@@ -3003,10 +3011,10 @@ function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3003
3011
  const today = /* @__PURE__ */ new Date();
3004
3012
  const sel = range ? value || {} : { start: value, end: value };
3005
3013
  const anchor = parse(sel.start) || parse(initialMonth) || today;
3006
- const [vy, setVy] = React22.useState(anchor.getFullYear());
3007
- const [vm, setVm] = React22.useState(anchor.getMonth());
3008
- const [mode2, setMode] = React22.useState("days");
3009
- 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);
3010
3018
  const s = parse(sel.start), e = parse(sel.end);
3011
3019
  const hoverEnd = range && s && !e && hover ? parse(hover) : null;
3012
3020
  const inRange = (d) => {
@@ -3122,9 +3130,9 @@ function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3122
3130
  }
3123
3131
  function DatePicker({ label, help, error, required = false, disabled = false, range = false, value, onChange, placeholder, id, className = "", style, ...rest }) {
3124
3132
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
3125
- const [open, setOpen] = React22.useState(false);
3126
- const rootRef = React22.useRef(null);
3127
- const boxRef = React22.useRef(null);
3133
+ const [open, setOpen] = React23.useState(false);
3134
+ const rootRef = React23.useRef(null);
3135
+ const boxRef = React23.useRef(null);
3128
3136
  const display = range ? value && value.start ? fmt(value.start) + (value.end ? " \u2192 " + fmt(value.end) : " \u2192 \u2026") : "" : fmt(value);
3129
3137
  const toggle = () => {
3130
3138
  if (!disabled) setOpen(!open);
@@ -3202,9 +3210,9 @@ function Textarea({ label, help, error, required = false, rows = 4, disabled = f
3202
3210
  }
3203
3211
 
3204
3212
  // src/components/forms/listbox.ts
3205
- import * as React23 from "react";
3213
+ import * as React24 from "react";
3206
3214
  function useListboxNav({ count, onSelect }) {
3207
- const [active, setActive] = React23.useState(-1);
3215
+ const [active, setActive] = React24.useState(-1);
3208
3216
  return {
3209
3217
  active,
3210
3218
  setActive,
@@ -3217,7 +3225,7 @@ function useListboxNav({ count, onSelect }) {
3217
3225
  };
3218
3226
  }
3219
3227
  function useListboxScrollIntoView(active, listRef, enabled) {
3220
- React23.useEffect(() => {
3228
+ React24.useEffect(() => {
3221
3229
  if (!enabled || active < 0 || !listRef.current) return;
3222
3230
  const list = listRef.current;
3223
3231
  const el = list.querySelector('[data-i="' + active + '"]');
@@ -3404,42 +3412,40 @@ function MultiSelectPanel({
3404
3412
  onClose
3405
3413
  }) {
3406
3414
  const allowedOps = operatorsFor(field);
3407
- const [draftOperator, setDraftOperator] = React24.useState(value?.operator ?? allowedOps[0]);
3415
+ const [draftOperator, setDraftOperator] = React25.useState(value?.operator ?? allowedOps[0]);
3408
3416
  const operator = allowedOps.length === 1 ? allowedOps[0] : value?.operator ?? draftOperator;
3409
3417
  const selected = value?.values ?? [];
3410
- const selectedRef = React24.useRef(selected);
3411
- selectedRef.current = selected;
3412
- const operatorRef = React24.useRef(operator);
3413
- operatorRef.current = operator;
3414
- const mountedRef = React24.useRef(true);
3415
- React24.useEffect(() => {
3418
+ const selectedRef = useLatest(selected);
3419
+ const operatorRef = useLatest(operator);
3420
+ const mountedRef = React25.useRef(true);
3421
+ React25.useEffect(() => {
3416
3422
  mountedRef.current = true;
3417
3423
  return () => {
3418
3424
  mountedRef.current = false;
3419
3425
  };
3420
3426
  }, []);
3421
- const commit = (nextValues, nextOperator = operator) => {
3422
- onCommit({ operator: nextOperator, values: nextValues });
3427
+ const commit = (nextValues, nextOperator = operatorRef.current) => {
3428
+ setDraftOperator(nextOperator);
3429
+ return onCommit({ operator: nextOperator, values: nextValues });
3423
3430
  };
3424
3431
  const setOperator = (op) => {
3425
- setDraftOperator(op);
3426
3432
  if (selectedRef.current.length > 0) commit(selectedRef.current, op);
3433
+ else setDraftOperator(op);
3427
3434
  };
3428
- const [mode2, setMode] = React24.useState("search");
3429
- const [query, setQuery] = React24.useState("");
3430
- const [asyncState, setAsyncState] = React24.useState(
3435
+ const [mode2, setMode] = React25.useState("search");
3436
+ const [query, setQuery] = React25.useState("");
3437
+ const [asyncState, setAsyncState] = React25.useState(
3431
3438
  { options: null, loading: !!field.loadOptions, error: false }
3432
3439
  );
3433
- const [retryTick, setRetryTick] = React24.useState(0);
3434
- const inputRef = React24.useRef(null);
3435
- const listRef = React24.useRef(null);
3436
- const listId = React24.useId();
3437
- const loadOptionsRef = React24.useRef(field.loadOptions);
3438
- loadOptionsRef.current = field.loadOptions;
3439
- 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(() => {
3440
3446
  inputRef.current?.focus();
3441
3447
  }, []);
3442
- React24.useEffect(() => {
3448
+ React25.useEffect(() => {
3443
3449
  const load = loadOptionsRef.current;
3444
3450
  if (!load) return;
3445
3451
  const controller = new AbortController();
@@ -3457,7 +3463,7 @@ function MultiSelectPanel({
3457
3463
  clearTimeout(t);
3458
3464
  controller.abort();
3459
3465
  };
3460
- }, [query, retryTick]);
3466
+ }, [query, retryTick, loadOptionsRef]);
3461
3467
  const options = field.loadOptions ? asyncState.options ?? [] : (field.options ?? []).filter((o) => o.label.toLowerCase().includes(query.toLowerCase()));
3462
3468
  const showLoading = !!field.loadOptions && asyncState.loading;
3463
3469
  const showError = !!field.loadOptions && asyncState.error;
@@ -3472,17 +3478,17 @@ function MultiSelectPanel({
3472
3478
  };
3473
3479
  const nav = useListboxNav({ count: options.length, onSelect: (i) => toggle(options[i]) });
3474
3480
  useListboxScrollIntoView(nav.active, listRef, hasListbox);
3475
- React24.useEffect(() => {
3481
+ React25.useEffect(() => {
3476
3482
  nav.setActive(0);
3477
3483
  }, [query, options.length]);
3478
3484
  const activeId = hasListbox && options[nav.active] ? listId + "-opt-" + nav.active : void 0;
3479
3485
  const liveMessage = showLoading ? "Loading\u2026" : showError ? "Couldn't load options." : options.length === 0 ? "No matches" : atLimit ? "Limit reached: at most " + field.maxValues + " values" : "";
3480
- const [pasteText, setPasteText] = React24.useState("");
3481
- const [pasteLoading, setPasteLoading] = React24.useState(false);
3482
- const [unmatchedTokens, setUnmatchedTokens] = React24.useState([]);
3483
- const [pasteError, setPasteError] = React24.useState(null);
3484
- const pasteWrapRef = React24.useRef(null);
3485
- React24.useEffect(() => {
3486
+ const [pasteText, setPasteText] = React25.useState("");
3487
+ const [pasteLoading, setPasteLoading] = React25.useState(false);
3488
+ const [unmatchedTokens, setUnmatchedTokens] = React25.useState([]);
3489
+ const [pasteError, setPasteError] = React25.useState(null);
3490
+ const pasteWrapRef = React25.useRef(null);
3491
+ React25.useEffect(() => {
3486
3492
  if (mode2 === "paste") pasteWrapRef.current?.querySelector("textarea")?.focus();
3487
3493
  }, [mode2]);
3488
3494
  const doResolve = () => {
@@ -3501,7 +3507,11 @@ function MultiSelectPanel({
3501
3507
  setPasteError("This filter allows at most " + field.maxValues + " values; adding this list would make " + merged.size + ".");
3502
3508
  return;
3503
3509
  }
3504
- commit(Array.from(merged.values()), operatorRef.current);
3510
+ const applied = commit(Array.from(merged.values()));
3511
+ if (!applied.ok) {
3512
+ setPasteError(applied.reason);
3513
+ return;
3514
+ }
3505
3515
  setPasteText("");
3506
3516
  }, (err) => {
3507
3517
  if (!mountedRef.current) return;
@@ -3638,6 +3648,59 @@ function MultiSelectPanel({
3638
3648
  }
3639
3649
  );
3640
3650
  }
3651
+ function useTextValidity(onFlip) {
3652
+ const invalidRef = React25.useRef(/* @__PURE__ */ new Set());
3653
+ const [, rerender] = React25.useReducer((n) => n + 1, 0);
3654
+ const onFlipRef = useLatest(onFlip);
3655
+ const record = (name, valid) => {
3656
+ const invalid = invalidRef.current;
3657
+ if (valid !== invalid.has(name)) return false;
3658
+ if (valid) invalid.delete(name);
3659
+ else invalid.add(name);
3660
+ rerender();
3661
+ return true;
3662
+ };
3663
+ return {
3664
+ allValid: () => invalidRef.current.size === 0,
3665
+ report: (name) => (valid) => {
3666
+ if (record(name, valid)) onFlipRef.current();
3667
+ },
3668
+ /** For an input that is about to unmount: it has nothing left to show. */
3669
+ forget: (name) => {
3670
+ record(name, true);
3671
+ },
3672
+ /** For a panel that is about to remount every input: each reports afresh on mount. */
3673
+ reset: () => {
3674
+ if (invalidRef.current.size === 0) return;
3675
+ invalidRef.current.clear();
3676
+ rerender();
3677
+ }
3678
+ };
3679
+ }
3680
+ function applicable(field, candidate, textValid) {
3681
+ if (!textValid || candidate === void 0) return void 0;
3682
+ return validateFilterValue(field, candidate).ok ? candidate : void 0;
3683
+ }
3684
+ function sameValue(a, b) {
3685
+ if (a === b) return true;
3686
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
3687
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
3688
+ const left = a;
3689
+ const right = b;
3690
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)])) {
3691
+ if (!sameValue(left[key], right[key])) return false;
3692
+ }
3693
+ return true;
3694
+ }
3695
+ function useReseedFromProps(value, applies, reseed) {
3696
+ const seen = React25.useRef(value);
3697
+ React25.useLayoutEffect(() => {
3698
+ if (seen.current === value) return;
3699
+ seen.current = value;
3700
+ if (!sameValue(value, applies())) reseed(value);
3701
+ }, [value, applies, reseed]);
3702
+ }
3703
+ var rangeDraft = (value) => ({ min: value?.min, max: value?.max });
3641
3704
  function RangePanel({
3642
3705
  field,
3643
3706
  value,
@@ -3645,21 +3708,34 @@ function RangePanel({
3645
3708
  onCommit,
3646
3709
  onClose
3647
3710
  }) {
3648
- const [draft, setDraft] = React24.useState({ min: value?.min, max: value?.max });
3649
- const draftRef = React24.useRef(draft);
3650
- const [minTextValid, setMinTextValid] = React24.useState(true);
3651
- const [maxTextValid, setMaxTextValid] = React24.useState(true);
3711
+ const [draft, setDraft] = React25.useState(() => rangeDraft(value));
3712
+ const [seed, setSeed] = React25.useState(0);
3713
+ const draftRef = React25.useRef(draft);
3714
+ const shown = () => {
3715
+ const { min, max } = draftRef.current;
3716
+ return applicable(field, min == null && max == null ? void 0 : { min, max }, validity.allValid());
3717
+ };
3718
+ const publish = () => {
3719
+ onCommit(shown());
3720
+ };
3721
+ const validity = useTextValidity(publish);
3722
+ useReseedFromProps(value, shown, (next) => {
3723
+ draftRef.current = rangeDraft(next);
3724
+ setDraft(draftRef.current);
3725
+ validity.reset();
3726
+ setSeed((n) => n + 1);
3727
+ });
3652
3728
  const draftMin = draft.min;
3653
3729
  const draftMax = draft.max;
3654
3730
  const isEmpty = draftMin == null && draftMax == null;
3655
3731
  const validation = isEmpty ? { ok: true } : validateFilterValue(field, { min: draftMin, max: draftMax });
3656
3732
  const comboError = !isEmpty && !validation.ok ? validation.reason : void 0;
3657
- const doneDisabled = !minTextValid || !maxTextValid || !!comboError;
3733
+ const doneDisabled = !validity.allValid() || !!comboError;
3658
3734
  const apply = (patch) => {
3659
3735
  const next = { ...draftRef.current, ...patch };
3660
3736
  draftRef.current = next;
3661
3737
  setDraft(next);
3662
- onCommit(next.min == null && next.max == null ? void 0 : next);
3738
+ publish();
3663
3739
  };
3664
3740
  return /* @__PURE__ */ jsx45(
3665
3741
  Popover,
@@ -3684,8 +3760,9 @@ function RangePanel({
3684
3760
  integer: field.integer,
3685
3761
  placeholder: field.minPlaceholder,
3686
3762
  onChange: (e) => apply({ min: e.target.value }),
3687
- onValidityChange: setMinTextValid
3688
- }
3763
+ onValidityChange: validity.report("min")
3764
+ },
3765
+ "min-" + seed
3689
3766
  ),
3690
3767
  /* @__PURE__ */ jsx45(
3691
3768
  NumberInput,
@@ -3699,13 +3776,21 @@ function RangePanel({
3699
3776
  error: comboError,
3700
3777
  placeholder: field.maxPlaceholder,
3701
3778
  onChange: (e) => apply({ max: e.target.value }),
3702
- onValidityChange: setMaxTextValid
3703
- }
3779
+ onValidityChange: validity.report("max")
3780
+ },
3781
+ "max-" + seed
3704
3782
  )
3705
3783
  ] })
3706
3784
  }
3707
3785
  );
3708
3786
  }
3787
+ function dateDraft(field, value) {
3788
+ return {
3789
+ kind: value?.kind ?? "withinDays",
3790
+ days: value?.kind === "withinDays" ? value.days : field.defaultDays ?? 30,
3791
+ between: value?.kind === "between" ? { from: value.from, to: value.to } : { from: "", to: "" }
3792
+ };
3793
+ }
3709
3794
  function DatePanel({
3710
3795
  field,
3711
3796
  value,
@@ -3713,36 +3798,69 @@ function DatePanel({
3713
3798
  onCommit,
3714
3799
  onClose
3715
3800
  }) {
3716
- const defaultDays = field.defaultDays ?? 30;
3717
3801
  const maxDays = effectiveMaxDays(field);
3718
- const [daysTextValid, setDaysTextValid] = React24.useState(true);
3719
- const [kind, setKind] = React24.useState(value?.kind ?? "withinDays");
3720
- const [daysDraft, setDaysDraft] = React24.useState(
3721
- value?.kind === "withinDays" ? value.days : defaultDays
3722
- );
3723
- const [between, setBetween] = React24.useState(
3724
- value?.kind === "between" ? { from: value.from, to: value.to } : { from: "", to: "" }
3725
- );
3802
+ const [initial] = React25.useState(() => dateDraft(field, value));
3803
+ const [kind, setKind] = React25.useState(initial.kind);
3804
+ const [daysDraft, setDaysDraft] = React25.useState(initial.days);
3805
+ const daysRef = React25.useRef(daysDraft);
3806
+ const daysConfirmed = React25.useRef(value?.kind === "withinDays");
3807
+ const [between, setBetween] = React25.useState(initial.between);
3808
+ const [seed, setSeed] = React25.useState(0);
3809
+ const shownWithinDays = () => {
3810
+ const days = daysRef.current;
3811
+ return applicable(field, days == null ? void 0 : { kind: "withinDays", days }, validity.allValid());
3812
+ };
3813
+ const validity = useTextValidity(() => {
3814
+ daysConfirmed.current = true;
3815
+ onCommit(shownWithinDays());
3816
+ });
3817
+ const applies = () => {
3818
+ if (kind === "between") {
3819
+ return applicable(field, between.from && between.to ? { kind: "between", ...between } : void 0, true);
3820
+ }
3821
+ return daysConfirmed.current ? shownWithinDays() : void 0;
3822
+ };
3823
+ useReseedFromProps(value, applies, (next) => {
3824
+ const d = dateDraft(field, next);
3825
+ setKind(d.kind);
3826
+ daysRef.current = d.days;
3827
+ daysConfirmed.current = next?.kind === "withinDays";
3828
+ setDaysDraft(d.days);
3829
+ setBetween(d.between);
3830
+ validity.reset();
3831
+ setSeed((n) => n + 1);
3832
+ });
3726
3833
  const applyWithinDays = (days) => {
3834
+ daysRef.current = days;
3835
+ daysConfirmed.current = true;
3727
3836
  setDaysDraft(days);
3728
- onCommit(days == null ? void 0 : { kind: "withinDays", days });
3837
+ onCommit(shownWithinDays());
3729
3838
  };
3730
3839
  const applyBetween = (from, to) => {
3731
3840
  setBetween({ from, to });
3732
- onCommit(from && to ? { kind: "between", from, to } : void 0);
3841
+ onCommit(applicable(field, from && to ? { kind: "between", from, to } : void 0, true));
3733
3842
  };
3734
3843
  const switchKind = (next) => {
3735
3844
  if (next === kind) return;
3736
3845
  setKind(next);
3737
- if (next === "between") setBetween({ from: "", to: "" });
3738
- else setDaysDraft(defaultDays);
3739
- setDaysTextValid(true);
3846
+ if (next === "between") {
3847
+ setBetween({ from: "", to: "" });
3848
+ validity.forget("days");
3849
+ } else {
3850
+ const { days } = dateDraft(field, void 0);
3851
+ daysRef.current = days;
3852
+ daysConfirmed.current = false;
3853
+ setDaysDraft(days);
3854
+ }
3740
3855
  onCommit(void 0);
3741
3856
  };
3742
- const doneDisabled = kind === "withinDays" && !daysTextValid;
3857
+ const doneDisabled = kind === "withinDays" && !validity.allValid();
3743
3858
  const confirm = () => {
3744
3859
  if (doneDisabled) return;
3745
- if (kind === "withinDays" && daysDraft != null) onCommit({ kind: "withinDays", days: daysDraft });
3860
+ if (kind === "withinDays") {
3861
+ const shown = shownWithinDays();
3862
+ if (shown) onCommit(shown);
3863
+ }
3746
3864
  onClose();
3747
3865
  };
3748
3866
  return /* @__PURE__ */ jsx45(
@@ -3779,8 +3897,9 @@ function DatePanel({
3779
3897
  integer: true,
3780
3898
  value: daysDraft ?? "",
3781
3899
  onChange: (e) => applyWithinDays(e.target.value),
3782
- onValidityChange: setDaysTextValid
3783
- }
3900
+ onValidityChange: validity.report("days")
3901
+ },
3902
+ "days-" + seed
3784
3903
  ) : /* @__PURE__ */ jsx45(
3785
3904
  DatePicker,
3786
3905
  {
@@ -3788,23 +3907,23 @@ function DatePanel({
3788
3907
  label: "Date range",
3789
3908
  value: { start: between.from, end: between.to },
3790
3909
  onChange: (e) => applyBetween(e.target.value.start || "", e.target.value.end || "")
3791
- }
3910
+ },
3911
+ "between-" + seed
3792
3912
  )
3793
3913
  ] })
3794
3914
  }
3795
3915
  );
3796
3916
  }
3797
3917
  function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align = "left", className = "", ...rest }) {
3798
- const [panel, setPanel] = React24.useState(null);
3799
- const editAnchorRef = React24.useRef(null);
3800
- const addAnchorRef = React24.useRef(null);
3801
- const textInputRef = React24.useRef(null);
3802
- const optionsRef = React24.useRef(null);
3918
+ const [panel, setPanel] = React25.useState(null);
3919
+ const editAnchorRef = React25.useRef(null);
3920
+ const addAnchorRef = React25.useRef(null);
3921
+ const textInputRef = React25.useRef(null);
3922
+ const optionsRef = React25.useRef(null);
3803
3923
  if (process.env.NODE_ENV !== "production") {
3804
3924
  for (const f of fields) assertValidFieldDefinition(f);
3805
3925
  }
3806
- const latest = React24.useRef({ fields, filters, onFilterChange, onFilterRemove });
3807
- latest.current = { fields, filters, onFilterChange, onFilterRemove };
3926
+ const latest = useLatest({ fields, filters, onFilterChange, onFilterRemove });
3808
3927
  const commit = (field, candidate) => {
3809
3928
  const current2 = latest.current.fields.find((f) => f.key === field.key);
3810
3929
  if (!current2) return { ok: false, reason: "This filter is no longer available" };
@@ -3813,24 +3932,17 @@ function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align
3813
3932
  return { ok: true };
3814
3933
  }
3815
3934
  const result = validateFilterValue(current2, candidate);
3816
- if (result.ok) {
3817
- latest.current.filters = { ...latest.current.filters, [current2.key]: candidate };
3818
- latest.current.onFilterChange(current2.key, candidate);
3819
- }
3935
+ if (result.ok) latest.current.onFilterChange(current2.key, candidate);
3820
3936
  return result;
3821
3937
  };
3822
3938
  const removeField = (field) => {
3823
3939
  const { filters: now, onFilterRemove: remove } = latest.current;
3824
- if (!isFieldSet(field, now[field.key])) return;
3825
- const next = { ...now };
3826
- delete next[field.key];
3827
- latest.current.filters = next;
3828
- remove(field.key);
3940
+ if (isFieldSet(field, now[field.key])) remove(field.key);
3829
3941
  };
3830
3942
  const editingField = panel && panel.kind === "edit" ? fields.find((f) => f.key === panel.key) : void 0;
3831
3943
  const startEditing = (key) => setPanel({ kind: "edit", key });
3832
3944
  const closeEditing = () => setPanel(null);
3833
- React24.useEffect(() => {
3945
+ React25.useEffect(() => {
3834
3946
  if (!panel || panel.kind !== "edit") return;
3835
3947
  if (editingField?.type === "text") {
3836
3948
  textInputRef.current?.focus();
@@ -4036,7 +4148,7 @@ function StatTile({ label, value, sub, delta, invert = false, loading = false, c
4036
4148
  }
4037
4149
 
4038
4150
  // src/components/data/CodeBlock.tsx
4039
- import * as React25 from "react";
4151
+ import * as React26 from "react";
4040
4152
  import { Fragment as Fragment6, jsx as jsx47, jsxs as jsxs41 } from "react/jsx-runtime";
4041
4153
  var JS_KW = /^(?:const|let|var|function|return|if|else|for|while|do|new|class|extends|import|from|export|default|await|async|try|catch|finally|throw|typeof|instanceof|of|in|switch|case|break|continue|delete|void|yield|static|get|set|this|super|interface|type|enum|implements|public|private|protected|readonly|abstract|as|satisfies|declare|namespace)$/;
4042
4154
  var RULES = {
@@ -4192,13 +4304,13 @@ function tokenize(src, lang) {
4192
4304
  return out;
4193
4305
  }
4194
4306
  function Highlighted({ code, lang }) {
4195
- const toks = React25.useMemo(() => tokenize(code, lang), [code, lang]);
4307
+ const toks = React26.useMemo(() => tokenize(code, lang), [code, lang]);
4196
4308
  return /* @__PURE__ */ jsx47(Fragment6, { children: toks.map((tk, i) => tk.c ? /* @__PURE__ */ jsx47("span", { className: "tok-" + tk.c, children: tk.t }, i) : tk.t) });
4197
4309
  }
4198
4310
  function useCopy() {
4199
- const [done, setDone] = React25.useState(false);
4200
- const timer = React25.useRef(null);
4201
- React25.useEffect(() => () => {
4311
+ const [done, setDone] = React26.useState(false);
4312
+ const timer = React26.useRef(null);
4313
+ React26.useEffect(() => () => {
4202
4314
  if (timer.current) clearTimeout(timer.current);
4203
4315
  }, []);
4204
4316
  const copy = (text) => {
@@ -4234,8 +4346,8 @@ function CodeBlock({
4234
4346
  actions,
4235
4347
  className = ""
4236
4348
  }) {
4237
- const [wrap, setWrap] = React25.useState(!!wrapDefault);
4238
- const [open, setOpen] = React25.useState(false);
4349
+ const [wrap, setWrap] = React26.useState(!!wrapDefault);
4350
+ const [open, setOpen] = React26.useState(false);
4239
4351
  const [copied, copy] = useCopy();
4240
4352
  const body = String(code).replace(/\n$/, "");
4241
4353
  const lines = body.split("\n");
@@ -4264,7 +4376,7 @@ function CodeBlock({
4264
4376
  ] });
4265
4377
  }
4266
4378
  function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" }) {
4267
- const [open, setOpen] = React25.useState(false);
4379
+ const [open, setOpen] = React26.useState(false);
4268
4380
  const [copied, copy] = useCopy();
4269
4381
  const raw = String(diff).replace(/\n$/, "");
4270
4382
  const all = raw.split("\n").map((line) => {
@@ -4310,7 +4422,7 @@ function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" })
4310
4422
  }
4311
4423
 
4312
4424
  // src/components/data/Markdown.tsx
4313
- import * as React26 from "react";
4425
+ import * as React27 from "react";
4314
4426
  import { jsx as jsx48, jsxs as jsxs42 } from "react/jsx-runtime";
4315
4427
  var SAFE = /^(?:https?:|mailto:|tel:|#|\/|\.{1,2}\/)/i;
4316
4428
  var safeHref = (h) => SAFE.test(String(h || "").trim()) ? String(h).trim() : null;
@@ -4349,7 +4461,7 @@ function inlineNodes(src, ctx, keyBase) {
4349
4461
  const href = safeHref(g(2));
4350
4462
  out.push(href ? /* @__PURE__ */ jsx48("a", { href, title: g(3) || void 0, target: /^https?:/i.test(href) ? "_blank" : void 0, rel: "noopener noreferrer", onClick: ctx.onLinkClick, children: inlineNodes(g(1), ctx, key()) }, key()) : /* @__PURE__ */ jsx48("span", { children: g(1) }, key()));
4351
4463
  } else if (kind === "cite") {
4352
- out.push(ctx.renderCitation ? /* @__PURE__ */ jsx48(React26.Fragment, { children: ctx.renderCitation(g(1)) }, key()) : /* @__PURE__ */ jsx48("sup", { className: "fd-md-cite", children: g(1) }, key()));
4464
+ out.push(ctx.renderCitation ? /* @__PURE__ */ jsx48(React27.Fragment, { children: ctx.renderCitation(g(1)) }, key()) : /* @__PURE__ */ jsx48("sup", { className: "fd-md-cite", children: g(1) }, key()));
4353
4465
  } else if (kind === "strongem") out.push(/* @__PURE__ */ jsx48("strong", { children: /* @__PURE__ */ jsx48("em", { children: inlineNodes(g(1), ctx, key()) }) }, key()));
4354
4466
  else if (kind === "strong") out.push(/* @__PURE__ */ jsx48("strong", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
4355
4467
  else if (kind === "em") out.push(/* @__PURE__ */ jsx48("em", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
@@ -4514,7 +4626,7 @@ function Markdown({
4514
4626
  onLinkClick
4515
4627
  }) {
4516
4628
  const ctx = { allowImages, headingOffset, renderCitation, onLinkClick, codeProps };
4517
- const blocks = React26.useMemo(() => parseBlocks(source), [source]);
4629
+ const blocks = React27.useMemo(() => parseBlocks(source), [source]);
4518
4630
  return /* @__PURE__ */ jsx48("div", { className: ["fd-md", className].filter(Boolean).join(" "), children: renderBlocks(blocks, ctx, "b") });
4519
4631
  }
4520
4632
  function MarkdownInline({ source = "", className = "", onLinkClick }) {
@@ -4628,7 +4740,7 @@ function Pagination({
4628
4740
  }
4629
4741
 
4630
4742
  // src/components/data/RelativeTime.tsx
4631
- import * as React27 from "react";
4743
+ import * as React28 from "react";
4632
4744
  import { jsxs as jsxs45 } from "react/jsx-runtime";
4633
4745
  var MIN = 6e4;
4634
4746
  var HOUR = 36e5;
@@ -4680,9 +4792,9 @@ function formatDuration(ms) {
4680
4792
  return h + "h" + (m % 60 ? " " + m % 60 + "m" : "");
4681
4793
  }
4682
4794
  function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true, ...rest }) {
4683
- const [, force] = React27.useState(0);
4795
+ const [, force] = React28.useState(0);
4684
4796
  const t = value instanceof Date ? value.getTime() : Date.parse(value);
4685
- React27.useEffect(() => {
4797
+ React28.useEffect(() => {
4686
4798
  if (!refresh2 || !t || isNaN(t)) return;
4687
4799
  const abs = Math.abs(t - Date.now());
4688
4800
  if (abs > DAY) return;
@@ -4698,7 +4810,7 @@ function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true,
4698
4810
  }
4699
4811
 
4700
4812
  // src/components/data/ScoreMeter.tsx
4701
- import * as React28 from "react";
4813
+ import * as React29 from "react";
4702
4814
  import { jsx as jsx51, jsxs as jsxs46 } from "react/jsx-runtime";
4703
4815
  function ScoreMeter({
4704
4816
  score,
@@ -4713,10 +4825,10 @@ function ScoreMeter({
4713
4825
  breakdownWidth = 280,
4714
4826
  className = ""
4715
4827
  }) {
4716
- const [open, setOpen] = React28.useState(false);
4717
- const ref = React28.useRef(null);
4828
+ const [open, setOpen] = React29.useState(false);
4829
+ const ref = React29.useRef(null);
4718
4830
  const value = Math.max(0, Math.min(100, Math.round(Number(score) || 0)));
4719
- const body = /* @__PURE__ */ jsxs46(React28.Fragment, { children: [
4831
+ const body = /* @__PURE__ */ jsxs46(React29.Fragment, { children: [
4720
4832
  /* @__PURE__ */ jsx51("span", { className: "fd-scoremeter-rail", style: { width }, "aria-hidden": "true", children: /* @__PURE__ */ jsx51(SegmentedMeter, { total: 100, segments, height, showTotal: false, showPercent: false, legend: false }) }),
4721
4833
  showScore ? /* @__PURE__ */ jsx51("span", { className: "fd-scoremeter-value fd-tabular", "aria-hidden": "true", children: value }) : null
4722
4834
  ] });
@@ -4735,7 +4847,7 @@ function ScoreMeter({
4735
4847
  }
4736
4848
  );
4737
4849
  }
4738
- return /* @__PURE__ */ jsxs46(React28.Fragment, { children: [
4850
+ return /* @__PURE__ */ jsxs46(React29.Fragment, { children: [
4739
4851
  /* @__PURE__ */ jsx51(
4740
4852
  "button",
4741
4853
  {
@@ -4766,13 +4878,13 @@ function ScoreMeter({
4766
4878
  }
4767
4879
 
4768
4880
  // src/components/data/SortMenu.tsx
4769
- import * as React29 from "react";
4881
+ import * as React30 from "react";
4770
4882
  import { jsx as jsx52, jsxs as jsxs47 } from "react/jsx-runtime";
4771
4883
  function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", className = "", ...rest }) {
4772
- const [open, setOpen] = React29.useState(false);
4884
+ const [open, setOpen] = React30.useState(false);
4773
4885
  const zIndex = useOverlayLayer(open);
4774
- const ref = React29.useRef(null);
4775
- React29.useEffect(() => {
4886
+ const ref = React30.useRef(null);
4887
+ React30.useEffect(() => {
4776
4888
  if (!open) return;
4777
4889
  const away = (e) => {
4778
4890
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -4834,7 +4946,7 @@ function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", cla
4834
4946
  }
4835
4947
 
4836
4948
  // src/components/data/StepList.tsx
4837
- import * as React30 from "react";
4949
+ import * as React31 from "react";
4838
4950
  import { jsx as jsx53, jsxs as jsxs48 } from "react/jsx-runtime";
4839
4951
  var KIND_ICON = { tool: "wrench", search: "magnifying-glass", read: "file-text", write: "pencil-simple", reasoning: "brain", run: "terminal", fetch: "cloud-arrow-down" };
4840
4952
  function StepList({
@@ -4847,9 +4959,9 @@ function StepList({
4847
4959
  dense = false,
4848
4960
  className = ""
4849
4961
  }) {
4850
- const [openState, setOpenState] = React30.useState(defaultOpen);
4962
+ const [openState, setOpenState] = React31.useState(defaultOpen);
4851
4963
  const open = openProp == null ? openState : openProp;
4852
- const [expanded, setExpanded] = React30.useState({});
4964
+ const [expanded, setExpanded] = React31.useState({});
4853
4965
  if (!steps.length) return null;
4854
4966
  const running = steps.some((s) => s.status === "running");
4855
4967
  const failed = steps.filter((s) => s.status === "error").length;
@@ -4907,7 +5019,7 @@ function StepList({
4907
5019
  }
4908
5020
 
4909
5021
  // src/components/data/VirtualList.tsx
4910
- import * as React31 from "react";
5022
+ import * as React32 from "react";
4911
5023
  import { jsx as jsx54, jsxs as jsxs49 } from "react/jsx-runtime";
4912
5024
  var VIRTUAL_LIST_BUFFER_ROWS = 20;
4913
5025
  var VIRTUAL_LIST_ROW_GAP = 6;
@@ -4959,35 +5071,35 @@ function VirtualList({
4959
5071
  className = "",
4960
5072
  style
4961
5073
  }) {
4962
- const [scrollTop, setScrollTop] = React31.useState(0);
4963
- const loaded = React31.useMemo(
5074
+ const [scrollTop, setScrollTop] = React32.useState(0);
5075
+ const loaded = React32.useMemo(
4964
5076
  () => ({ firstItemIndex, count: items.length }),
4965
5077
  [firstItemIndex, items.length]
4966
5078
  );
4967
5079
  const total = Math.max(totalCount == null ? 0 : totalCount, firstItemIndex + items.length);
4968
5080
  const bandKey = firstItemIndex + ":" + items.length;
4969
- const win = React31.useMemo(
5081
+ const win = React32.useMemo(
4970
5082
  () => computeVirtualWindow({ scrollTop, viewportHeight: height, itemHeight, itemCount: total, loaded }),
4971
5083
  [scrollTop, height, itemHeight, total, loaded]
4972
5084
  );
4973
- const need = React31.useMemo(() => computeNeedMore(win, loaded), [win, loaded]);
5085
+ const need = React32.useMemo(() => computeNeedMore(win, loaded), [win, loaded]);
4974
5086
  const wantStart = need.start && hasMore?.start !== false;
4975
5087
  const wantEnd = need.end && hasMore?.end !== false;
4976
5088
  const bucket2 = Math.floor(win.visibleStart / VIRTUAL_LIST_BUFFER_ROWS);
4977
5089
  const askKey = bandKey + "@" + bucket2;
4978
- const asked = React31.useRef({ start: null, end: null });
4979
- React31.useEffect(() => {
5090
+ const asked = React32.useRef({ start: null, end: null });
5091
+ React32.useEffect(() => {
4980
5092
  if (loading || !wantStart || asked.current.start === askKey) return;
4981
5093
  asked.current.start = askKey;
4982
5094
  onNeedMore("start", win);
4983
5095
  }, [wantStart, loading, askKey, onNeedMore, win]);
4984
- React31.useEffect(() => {
5096
+ React32.useEffect(() => {
4985
5097
  if (loading || !wantEnd || asked.current.end === askKey) return;
4986
5098
  asked.current.end = askKey;
4987
5099
  onNeedMore("end", win);
4988
5100
  }, [wantEnd, loading, askKey, onNeedMore, win]);
4989
- const lastKeysRef = React31.useRef(null);
4990
- const newKeys = React31.useMemo(() => {
5101
+ const lastKeysRef = React32.useRef(null);
5102
+ const newKeys = React32.useMemo(() => {
4991
5103
  const prev = lastKeysRef.current;
4992
5104
  if (!prev) return /* @__PURE__ */ new Set();
4993
5105
  const fresh = /* @__PURE__ */ new Set();
@@ -4997,7 +5109,7 @@ function VirtualList({
4997
5109
  }
4998
5110
  return fresh;
4999
5111
  }, [items]);
5000
- React31.useEffect(() => {
5112
+ React32.useEffect(() => {
5001
5113
  lastKeysRef.current = new Set(items.map(keyOf));
5002
5114
  }, [items]);
5003
5115
  const gapStyle = { "--fd-vlist-gap": rowGap + "px" };
@@ -5042,7 +5154,7 @@ function VirtualList({
5042
5154
  }
5043
5155
 
5044
5156
  // src/components/data/EntityRow.tsx
5045
- import * as React32 from "react";
5157
+ import * as React33 from "react";
5046
5158
  import { jsx as jsx55, jsxs as jsxs50 } from "react/jsx-runtime";
5047
5159
  var ENTITY_ROW_METRIC_WIDTH = 74;
5048
5160
  var METRIC_TONE_COLOR = {
@@ -5055,7 +5167,7 @@ function EntityRowMetrics({ metrics, className = "", style }) {
5055
5167
  const key = m.id || m.label || i;
5056
5168
  const className2 = ["fd-erow-metric", m.tone && m.tone !== "default" ? "is-" + m.tone : "", m.tooltip != null ? "is-described" : ""].filter(Boolean).join(" ");
5057
5169
  const tone = { color: METRIC_TONE_COLOR[m.tone || "default"] };
5058
- const content = /* @__PURE__ */ jsxs50(React32.Fragment, { children: [
5170
+ const content = /* @__PURE__ */ jsxs50(React33.Fragment, { children: [
5059
5171
  m.icon ? /* @__PURE__ */ jsx55("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }) : null,
5060
5172
  /* @__PURE__ */ jsx55("span", { className: "fd-erow-metric-value fd-tabular", children: m.value }),
5061
5173
  /* @__PURE__ */ jsx55("span", { className: "fd-sr", children: " " + m.label })
@@ -5147,7 +5259,7 @@ function EntityRow({
5147
5259
  }
5148
5260
 
5149
5261
  // src/components/data/TransferList.tsx
5150
- import * as React33 from "react";
5262
+ import * as React34 from "react";
5151
5263
  import { createPortal as createPortal4 } from "react-dom";
5152
5264
 
5153
5265
  // src/components/forms/Input.tsx
@@ -5253,12 +5365,12 @@ function TransferList({
5253
5365
  listHeight = 440,
5254
5366
  className = ""
5255
5367
  }) {
5256
- const [drag, setDrag] = React33.useState(null);
5257
- const [dragOverSide, setDragOverSide] = React33.useState(null);
5258
- const dragRef = React33.useRef(null);
5259
- const armedRef = React33.useRef(null);
5260
- const sideRefs = React33.useRef({ left: null, right: null });
5261
- const hitTestSide = React33.useCallback((x, y) => {
5368
+ const [drag, setDrag] = React34.useState(null);
5369
+ const [dragOverSide, setDragOverSide] = React34.useState(null);
5370
+ const dragRef = React34.useRef(null);
5371
+ const armedRef = React34.useRef(null);
5372
+ const sideRefs = React34.useRef({ left: null, right: null });
5373
+ const hitTestSide = React34.useCallback((x, y) => {
5262
5374
  for (const side of ["left", "right"]) {
5263
5375
  const el = sideRefs.current[side];
5264
5376
  if (!el) continue;
@@ -5267,7 +5379,7 @@ function TransferList({
5267
5379
  }
5268
5380
  return null;
5269
5381
  }, []);
5270
- const findItem = React33.useCallback(
5382
+ const findItem = React34.useCallback(
5271
5383
  (side, key) => (side === "left" ? left.items : right.items).find((it) => keyOf(it) === key),
5272
5384
  [left.items, right.items, keyOf]
5273
5385
  );
@@ -5289,7 +5401,7 @@ function TransferList({
5289
5401
  height: rect.height
5290
5402
  };
5291
5403
  };
5292
- React33.useEffect(() => {
5404
+ React34.useEffect(() => {
5293
5405
  const onMovePointer = (e) => {
5294
5406
  const armed = armedRef.current;
5295
5407
  if (!armed || e.pointerId !== armed.pointerId) return;
@@ -5342,7 +5454,7 @@ function TransferList({
5342
5454
  window.removeEventListener("pointercancel", endDrag);
5343
5455
  };
5344
5456
  }, [hitTestSide, findItem, onMove]);
5345
- React33.useEffect(() => {
5457
+ React34.useEffect(() => {
5346
5458
  if (!drag || typeof document === "undefined") return;
5347
5459
  const prev = document.body.style.userSelect;
5348
5460
  document.body.style.userSelect = "none";
@@ -5450,11 +5562,11 @@ function TransferList({
5450
5562
  }
5451
5563
 
5452
5564
  // src/components/forms/Checkbox.tsx
5453
- import * as React34 from "react";
5565
+ import * as React35 from "react";
5454
5566
  import { jsx as jsx59, jsxs as jsxs53 } from "react/jsx-runtime";
5455
5567
  function Checkbox({ label, description, card = false, indeterminate = false, className = "", ...rest }) {
5456
- const ref = React34.useRef(null);
5457
- React34.useEffect(() => {
5568
+ const ref = React35.useRef(null);
5569
+ React35.useEffect(() => {
5458
5570
  if (ref.current) ref.current.indeterminate = indeterminate;
5459
5571
  }, [indeterminate]);
5460
5572
  return /* @__PURE__ */ jsxs53("label", { className: ["fd-choice", card ? "fd-choice-card" : "", className].filter(Boolean).join(" "), children: [
@@ -5498,7 +5610,7 @@ function Switch({ label, description, className = "", ...rest }) {
5498
5610
  }
5499
5611
 
5500
5612
  // src/components/forms/Select.tsx
5501
- import * as React35 from "react";
5613
+ import * as React36 from "react";
5502
5614
  import { jsx as jsx62, jsxs as jsxs56 } from "react/jsx-runtime";
5503
5615
  var norm = (o) => typeof o === "string" ? { value: o, label: o } : o;
5504
5616
  function Select({
@@ -5525,12 +5637,12 @@ function Select({
5525
5637
  const isOn = (v) => multiple ? vals.includes(v) : v === value;
5526
5638
  const hasSearch = searchable === void 0 ? opts.length > 8 : searchable;
5527
5639
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
5528
- const [open, setOpen] = React35.useState(false);
5529
- const [q, setQ] = React35.useState("");
5530
- const rootRef = React35.useRef(null);
5531
- const boxRef = React35.useRef(null);
5532
- const listRef = React35.useRef(null);
5533
- const typeBuf = React35.useRef({ s: "", t: 0 });
5640
+ const [open, setOpen] = React36.useState(false);
5641
+ const [q, setQ] = React36.useState("");
5642
+ const rootRef = React36.useRef(null);
5643
+ const boxRef = React36.useRef(null);
5644
+ const listRef = React36.useRef(null);
5645
+ const typeBuf = React36.useRef({ s: "", t: 0 });
5534
5646
  const selected = multiple ? null : opts.find((o) => o.value === value);
5535
5647
  const chosen = multiple ? opts.filter((o) => vals.includes(o.value)) : [];
5536
5648
  const boxText = multiple ? chosen.length === 0 ? placeholder : chosen.length === 1 ? chosen[0].label : summary ? summary(chosen.map((o) => o.value)) : chosen.length + " selected" : selected ? selected.label : placeholder;
@@ -5669,7 +5781,7 @@ function Select({
5669
5781
  ),
5670
5782
  q ? /* @__PURE__ */ jsx62("span", { className: "fd-body-sm fd-muted", children: visible.length }) : null
5671
5783
  ] }) : void 0,
5672
- footer: multiple ? /* @__PURE__ */ jsxs56(React35.Fragment, { children: [
5784
+ footer: multiple ? /* @__PURE__ */ jsxs56(React36.Fragment, { children: [
5673
5785
  /* @__PURE__ */ jsx62(
5674
5786
  "button",
5675
5787
  {
@@ -5700,7 +5812,7 @@ function Select({
5700
5812
  'Nothing matches "',
5701
5813
  q,
5702
5814
  '".'
5703
- ] }) : groups.map((grp) => /* @__PURE__ */ jsxs56(React35.Fragment, { children: [
5815
+ ] }) : groups.map((grp) => /* @__PURE__ */ jsxs56(React36.Fragment, { children: [
5704
5816
  grp.g ? /* @__PURE__ */ jsx62("div", { className: "fd-pop-group", children: grp.g }) : null,
5705
5817
  grp.items.map(({ o, i }) => /* @__PURE__ */ jsxs56(
5706
5818
  "button",
@@ -5737,7 +5849,7 @@ function Select({
5737
5849
  }
5738
5850
 
5739
5851
  // src/components/forms/TimePicker.tsx
5740
- import * as React36 from "react";
5852
+ import * as React37 from "react";
5741
5853
  import { jsx as jsx63, jsxs as jsxs57 } from "react/jsx-runtime";
5742
5854
  var pad = (n) => String(n).padStart(2, "0");
5743
5855
  function ClockFace({ value = "09:00", onChange }) {
@@ -5746,9 +5858,9 @@ function ClockFace({ value = "09:00", onChange }) {
5746
5858
  if (isNaN(m)) m = 0;
5747
5859
  const pm = h24 >= 12;
5748
5860
  const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
5749
- const [mode2, setMode] = React36.useState("h");
5750
- const faceRef = React36.useRef(null);
5751
- const dragging = React36.useRef(false);
5861
+ const [mode2, setMode] = React37.useState("h");
5862
+ const faceRef = React37.useRef(null);
5863
+ const dragging = React37.useRef(false);
5752
5864
  const set = (h, mm, isPm) => onChange((isPm ? h % 12 + 12 : h % 12) + ":" + pad(mm));
5753
5865
  const R = 108, NR = 80;
5754
5866
  const nums = mode2 === "h" ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 12 }, (_, i) => i * 5);
@@ -5821,9 +5933,9 @@ function ClockFace({ value = "09:00", onChange }) {
5821
5933
  }
5822
5934
  function TimePicker({ label, help, error, required = false, disabled = false, value = "", onChange, placeholder = "Pick a time", id, className = "", style }) {
5823
5935
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
5824
- const [open, setOpen] = React36.useState(false);
5825
- const rootRef = React36.useRef(null);
5826
- const boxRef = React36.useRef(null);
5936
+ const [open, setOpen] = React37.useState(false);
5937
+ const rootRef = React37.useRef(null);
5938
+ const boxRef = React37.useRef(null);
5827
5939
  const disp = () => {
5828
5940
  if (!value) return "";
5829
5941
  const [h, m] = value.split(":").map(Number);
@@ -5866,7 +5978,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
5866
5978
  offset: 6,
5867
5979
  width: 262,
5868
5980
  role: "presentation",
5869
- footer: /* @__PURE__ */ jsxs57(React36.Fragment, { children: [
5981
+ footer: /* @__PURE__ */ jsxs57(React37.Fragment, { children: [
5870
5982
  /* @__PURE__ */ jsx63(
5871
5983
  "button",
5872
5984
  {
@@ -5894,7 +6006,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
5894
6006
  }
5895
6007
 
5896
6008
  // src/components/forms/Slider.tsx
5897
- import * as React37 from "react";
6009
+ import * as React38 from "react";
5898
6010
  import { jsx as jsx64, jsxs as jsxs58 } from "react/jsx-runtime";
5899
6011
  function Slider({
5900
6012
  label,
@@ -5912,7 +6024,7 @@ function Slider({
5912
6024
  ...rest
5913
6025
  }) {
5914
6026
  const fieldId = useFieldId(id);
5915
- const [dragging, setDragging] = React37.useState(false);
6027
+ const [dragging, setDragging] = React38.useState(false);
5916
6028
  const v = value === void 0 ? min : Number(value);
5917
6029
  const pct = max === min ? 0 : (v - min) / (max - min) * 100;
5918
6030
  return /* @__PURE__ */ jsxs58("div", { className: ["fd-field", className].filter(Boolean).join(" "), children: [
@@ -5946,7 +6058,7 @@ function Slider({
5946
6058
  }
5947
6059
 
5948
6060
  // src/components/forms/RangeSlider.tsx
5949
- import * as React38 from "react";
6061
+ import * as React39 from "react";
5950
6062
  import { jsx as jsx65, jsxs as jsxs59 } from "react/jsx-runtime";
5951
6063
  function RangeSlider({
5952
6064
  label,
@@ -5966,9 +6078,9 @@ function RangeSlider({
5966
6078
  }) {
5967
6079
  const fmt2 = format || ((v) => String(v));
5968
6080
  const [a, b] = value || [min, max];
5969
- const [drag, setDrag] = React38.useState(null);
5970
- const [focus, setFocus] = React38.useState(null);
5971
- const railRef = React38.useRef(null);
6081
+ const [drag, setDrag] = React39.useState(null);
6082
+ const [focus, setFocus] = React39.useState(null);
6083
+ const railRef = React39.useRef(null);
5972
6084
  const pct = (v) => max === min ? 0 : (v - min) / (max - min) * 100;
5973
6085
  const clampPair = (i, v) => {
5974
6086
  v = Math.min(max, Math.max(min, Math.round(v / step) * step));
@@ -5983,7 +6095,7 @@ function RangeSlider({
5983
6095
  const r = railRef.current.getBoundingClientRect();
5984
6096
  return min + Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)) * (max - min);
5985
6097
  };
5986
- React38.useEffect(() => {
6098
+ React39.useEffect(() => {
5987
6099
  if (drag === null) return;
5988
6100
  const mv = (e) => onChange && onChange(clampPair(drag, fromEvent(e)));
5989
6101
  const upH = () => setDrag(null);
@@ -6064,7 +6176,7 @@ function RangeSlider({
6064
6176
  /* @__PURE__ */ jsxs59("div", { className: "fd-range" + (hasLabels ? " has-labels" : ""), onPointerDown: onRailDown, children: [
6065
6177
  /* @__PURE__ */ jsx65("span", { className: "fd-range-rail", ref: railRef }),
6066
6178
  /* @__PURE__ */ jsx65("span", { className: "fd-range-fill", style: { left: pct(a) + "%", width: pct(b) - pct(a) + "%" } }),
6067
- marks.map((m) => /* @__PURE__ */ jsxs59(React38.Fragment, { children: [
6179
+ marks.map((m) => /* @__PURE__ */ jsxs59(React39.Fragment, { children: [
6068
6180
  /* @__PURE__ */ jsx65("span", { className: "fd-range-mark" + (m.value >= a && m.value <= b ? " is-in" : ""), style: { left: pct(m.value) + "%" } }),
6069
6181
  m.label ? /* @__PURE__ */ jsx65("span", { className: "fd-range-mark-label", style: { left: pct(m.value) + "%" }, children: m.label }) : null
6070
6182
  ] }, m.value)),
@@ -6093,7 +6205,7 @@ function RangeSlider({
6093
6205
  }
6094
6206
 
6095
6207
  // src/components/forms/Dropzone.tsx
6096
- import * as React39 from "react";
6208
+ import * as React40 from "react";
6097
6209
  import { jsx as jsx66, jsxs as jsxs60 } from "react/jsx-runtime";
6098
6210
  function Dropzone({
6099
6211
  onFiles,
@@ -6108,8 +6220,8 @@ function Dropzone({
6108
6220
  className = "",
6109
6221
  style
6110
6222
  }) {
6111
- const [over, setOver] = React39.useState(false);
6112
- const depth = React39.useRef(0);
6223
+ const [over, setOver] = React40.useState(false);
6224
+ const depth = React40.useRef(0);
6113
6225
  const has = (e) => {
6114
6226
  const dt = e.dataTransfer;
6115
6227
  if (!dt) return false;
@@ -6171,8 +6283,8 @@ function FilePickButton({
6171
6283
  className = "",
6172
6284
  children
6173
6285
  }) {
6174
- const ref = React39.useRef(null);
6175
- return /* @__PURE__ */ jsxs60(React39.Fragment, { children: [
6286
+ const ref = React40.useRef(null);
6287
+ return /* @__PURE__ */ jsxs60(React40.Fragment, { children: [
6176
6288
  /* @__PURE__ */ jsx66(
6177
6289
  "button",
6178
6290
  {
@@ -6205,10 +6317,10 @@ function FilePickButton({
6205
6317
  }
6206
6318
  function useStagedFiles(upload, opts) {
6207
6319
  const o = opts || {};
6208
- const [items, setItems] = React39.useState([]);
6209
- const controllers = React39.useRef({});
6320
+ const [items, setItems] = React40.useState([]);
6321
+ const controllers = React40.useRef({});
6210
6322
  const patch = (id, next) => setItems((list) => list.map((f) => f.id === id ? Object.assign({}, f, next) : f));
6211
- const run = React39.useCallback((att) => {
6323
+ const run = React40.useCallback((att) => {
6212
6324
  if (!upload) return;
6213
6325
  const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
6214
6326
  controllers.current[att.id] = ac;
@@ -6225,14 +6337,14 @@ function useStagedFiles(upload, opts) {
6225
6337
  delete controllers.current[att.id];
6226
6338
  });
6227
6339
  }, [upload]);
6228
- const add = React39.useCallback((files) => {
6340
+ const add = React40.useCallback((files) => {
6229
6341
  if (!upload) return [];
6230
6342
  const atts = Array.from(files).map((f) => toAttachment(f));
6231
6343
  setItems((list) => list.concat(atts));
6232
6344
  atts.forEach(run);
6233
6345
  return atts;
6234
6346
  }, [upload, run]);
6235
- const remove = React39.useCallback((att) => {
6347
+ const remove = React40.useCallback((att) => {
6236
6348
  const ac = controllers.current[att.id];
6237
6349
  if (ac) {
6238
6350
  try {
@@ -6243,10 +6355,10 @@ function useStagedFiles(upload, opts) {
6243
6355
  }
6244
6356
  setItems((list) => list.filter((f) => f.id !== att.id));
6245
6357
  }, []);
6246
- const retry = React39.useCallback((att) => {
6358
+ const retry = React40.useCallback((att) => {
6247
6359
  run(att);
6248
6360
  }, [run]);
6249
- const clear = React39.useCallback(() => {
6361
+ const clear = React40.useCallback(() => {
6250
6362
  Object.values(controllers.current).forEach((ac) => {
6251
6363
  try {
6252
6364
  ac && ac.abort();
@@ -6370,7 +6482,7 @@ function FileGrid({ files = [], onOpen, onRemove, onRetry, tiles = true, maxHeig
6370
6482
  }
6371
6483
 
6372
6484
  // src/components/forms/MarkdownEditor.tsx
6373
- import * as React40 from "react";
6485
+ import * as React41 from "react";
6374
6486
  import { jsx as jsx68, jsxs as jsxs62 } from "react/jsx-runtime";
6375
6487
  var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || "");
6376
6488
  var INLINE_RE = /(\*\*\*[^*\n]+\*\*\*|\*\*[^*\n]+\*\*|__[^_\n]+__|~~[^~\n]+~~|`[^`\n]+`|\*[^*\s][^*\n]*\*|(?<![A-Za-z0-9_])_[^_\s][^_\n]*_|\[[^\]\n]*\]\([^)\s\n]*\)|https?:\/\/\S+)/g;
@@ -6584,7 +6696,7 @@ function syncDom(root, value) {
6584
6696
  while (root.children.length > lines.length) root.removeChild(root.lastChild);
6585
6697
  }
6586
6698
  var LIST_CONT = RE_LI;
6587
- var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6699
+ var MarkdownEditor = React41.forwardRef(function MarkdownEditor2({
6588
6700
  value = "",
6589
6701
  onChange,
6590
6702
  onSubmit,
@@ -6603,10 +6715,10 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6603
6715
  className = "",
6604
6716
  id
6605
6717
  }, ref) {
6606
- const boxRef = React40.useRef(null);
6607
- const composing = React40.useRef(false);
6608
- const pendingCaret = React40.useRef(null);
6609
- React40.useLayoutEffect(() => {
6718
+ const boxRef = React41.useRef(null);
6719
+ const composing = React41.useRef(false);
6720
+ const pendingCaret = React41.useRef(null);
6721
+ React41.useLayoutEffect(() => {
6610
6722
  const root = boxRef.current;
6611
6723
  if (!root || composing.current) return;
6612
6724
  const active = document.activeElement === root || root.contains(document.activeElement);
@@ -6615,7 +6727,7 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6615
6727
  pendingCaret.current = null;
6616
6728
  if (active && caret != null) placeCaret(root, caret);
6617
6729
  }, [value]);
6618
- React40.useEffect(() => {
6730
+ React41.useEffect(() => {
6619
6731
  if (autoFocus && boxRef.current) boxRef.current.focus();
6620
6732
  }, [autoFocus]);
6621
6733
  const caretNow = () => {
@@ -6682,7 +6794,7 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6682
6794
  api.replaceRange(from, to, next, from + next.length);
6683
6795
  }
6684
6796
  };
6685
- React40.useImperativeHandle(ref, () => api);
6797
+ React41.useImperativeHandle(ref, () => api);
6686
6798
  function detect(text, caret) {
6687
6799
  if (!onTrigger) return;
6688
6800
  const upto = text.slice(0, caret);
@@ -6834,16 +6946,16 @@ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6834
6946
  });
6835
6947
 
6836
6948
  // src/components/platform/AccountMenu.tsx
6837
- import * as React42 from "react";
6949
+ import * as React43 from "react";
6838
6950
 
6839
6951
  // src/kits/sessionContext.tsx
6840
- import * as React41 from "react";
6841
- var SessionContext = React41.createContext(null);
6952
+ import * as React42 from "react";
6953
+ var SessionContext = React42.createContext(null);
6842
6954
  function SessionProvider({ value, children }) {
6843
- return React41.createElement(SessionContext.Provider, { value }, children);
6955
+ return React42.createElement(SessionContext.Provider, { value }, children);
6844
6956
  }
6845
6957
  function useSessionContext() {
6846
- const ctx = React41.useContext(SessionContext);
6958
+ const ctx = React42.useContext(SessionContext);
6847
6959
  if (!ctx) {
6848
6960
  throw new Error(
6849
6961
  "useSessionContext() was called outside a <SessionProvider>. Wrap your app root in <SessionProvider value={...}> with your real session, or \u2014 in the docs site or a demo \u2014 in <DemoSessionProvider>, before rendering AccountMenu, Gate, ProfilePage or AppSwitcher."
@@ -6883,11 +6995,11 @@ function AccountMenu({
6883
6995
  ...rest
6884
6996
  }) {
6885
6997
  const { user, roles, can: can2, signOut: ctxSignOut, demo } = useSessionContext();
6886
- const [open, setOpen] = React42.useState(false);
6887
- const [switching, setSwitching] = React42.useState(false);
6888
- const [signingOut, setSigningOut] = React42.useState(false);
6889
- const [signOutError, setSignOutError] = React42.useState(null);
6890
- const ref = React42.useRef(null);
6998
+ const [open, setOpen] = React43.useState(false);
6999
+ const [switching, setSwitching] = React43.useState(false);
7000
+ const [signingOut, setSigningOut] = React43.useState(false);
7001
+ const [signOutError, setSignOutError] = React43.useState(null);
7002
+ const ref = React43.useRef(null);
6891
7003
  const visibleLinks = links.filter((l) => !l.perm || can2(l.perm));
6892
7004
  const visibleGroups = groups.map((g) => ({ label: g.label, links: g.links.filter((l) => !l.perm || can2(l.perm)) })).filter((g) => g.links.length);
6893
7005
  const closeMenu = () => {
@@ -7012,13 +7124,13 @@ function AccountMenu({
7012
7124
  }
7013
7125
 
7014
7126
  // src/components/platform/AppSwitcher.tsx
7015
- import * as React43 from "react";
7127
+ import * as React44 from "react";
7016
7128
  import { jsx as jsx70, jsxs as jsxs64 } from "react/jsx-runtime";
7017
7129
  function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
7018
7130
  const { apps: contextApps } = useSessionContext();
7019
7131
  const apps = appsProp ?? contextApps ?? [];
7020
- const [open, setOpen] = React43.useState(false);
7021
- const anchorRef = React43.useRef(null);
7132
+ const [open, setOpen] = React44.useState(false);
7133
+ const anchorRef = React44.useRef(null);
7022
7134
  if (!apps.length) return null;
7023
7135
  const items = apps.map((app) => ({
7024
7136
  id: app.key,
@@ -7032,7 +7144,7 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
7032
7144
  window.location.href = app.url;
7033
7145
  }
7034
7146
  }));
7035
- return /* @__PURE__ */ jsxs64(React43.Fragment, { children: [
7147
+ return /* @__PURE__ */ jsxs64(React44.Fragment, { children: [
7036
7148
  /* @__PURE__ */ jsx70("span", { ref: anchorRef, className: ["fd-appswitch-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx70(
7037
7149
  IconButton,
7038
7150
  {
@@ -7061,10 +7173,10 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
7061
7173
  }
7062
7174
 
7063
7175
  // src/components/platform/AppearanceMenu.tsx
7064
- import * as React45 from "react";
7176
+ import * as React46 from "react";
7065
7177
 
7066
7178
  // src/kits/appearance.ts
7067
- import * as React44 from "react";
7179
+ import * as React45 from "react";
7068
7180
  var DEFAULT_STORAGE_KEY = "fd-appearance";
7069
7181
  var DEFAULT_PREFS = {
7070
7182
  theme: "system",
@@ -7159,11 +7271,11 @@ function applyPrefs(prefs, root = document.documentElement) {
7159
7271
  else root.removeAttribute("data-motion");
7160
7272
  }
7161
7273
  function useAppearancePrefs(store) {
7162
- const prefs = React44.useSyncExternalStore(store.subscribe, store.getSnapshot);
7163
- React44.useEffect(() => {
7274
+ const prefs = React45.useSyncExternalStore(store.subscribe, store.getSnapshot);
7275
+ React45.useEffect(() => {
7164
7276
  applyPrefs(prefs);
7165
7277
  }, [prefs]);
7166
- React44.useEffect(() => {
7278
+ React45.useEffect(() => {
7167
7279
  if (prefs.theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
7168
7280
  return;
7169
7281
  }
@@ -7172,7 +7284,7 @@ function useAppearancePrefs(store) {
7172
7284
  mq.addEventListener("change", onChange);
7173
7285
  return () => mq.removeEventListener("change", onChange);
7174
7286
  }, [prefs]);
7175
- const update = React44.useCallback(
7287
+ const update = React45.useCallback(
7176
7288
  (patch) => {
7177
7289
  store.write({ ...store.getSnapshot(), ...patch });
7178
7290
  },
@@ -7195,15 +7307,15 @@ var TEXT_SCALE_OPTIONS = [
7195
7307
  { value: "xl", label: "XL" }
7196
7308
  ];
7197
7309
  function AppearanceMenu({ className = "", store: storeProp, storageKey, storage, defaults }) {
7198
- const store = React45.useMemo(
7310
+ const store = React46.useMemo(
7199
7311
  () => storeProp ?? createLocalStorageAppearanceStore({ storageKey, storage, defaults }),
7200
7312
  [storeProp, storageKey, storage, defaults]
7201
7313
  );
7202
7314
  const [prefs, update] = useAppearancePrefs(store);
7203
- const [open, setOpen] = React45.useState(false);
7204
- const anchorRef = React45.useRef(null);
7315
+ const [open, setOpen] = React46.useState(false);
7316
+ const anchorRef = React46.useRef(null);
7205
7317
  const patch = (p) => update(p);
7206
- return /* @__PURE__ */ jsxs65(React45.Fragment, { children: [
7318
+ return /* @__PURE__ */ jsxs65(React46.Fragment, { children: [
7207
7319
  /* @__PURE__ */ jsx71("span", { ref: anchorRef, className: ["fd-appearance-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx71(
7208
7320
  IconButton,
7209
7321
  {
@@ -7272,14 +7384,14 @@ function AppearanceMenu({ className = "", store: storeProp, storageKey, storage,
7272
7384
  }
7273
7385
 
7274
7386
  // src/components/platform/ApiSpecBrowser.tsx
7275
- import * as React46 from "react";
7387
+ import * as React47 from "react";
7276
7388
  import { jsx as jsx72, jsxs as jsxs66 } from "react/jsx-runtime";
7277
7389
  var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
7278
7390
  function Json({ obj }) {
7279
7391
  return /* @__PURE__ */ jsx72("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
7280
7392
  }
7281
7393
  function Endpoint({ s, onRequest }) {
7282
- const [tried, setTried] = React46.useState(null);
7394
+ const [tried, setTried] = React47.useState(null);
7283
7395
  const run = async () => {
7284
7396
  setTried("busy");
7285
7397
  const t0 = (window.performance || Date).now();
@@ -7352,10 +7464,10 @@ function ApiSpecBrowser({
7352
7464
  className = "",
7353
7465
  ...rest
7354
7466
  }) {
7355
- const [q, setQ] = React46.useState("");
7356
- const [method, setMethod] = React46.useState(null);
7357
- const [mod, setMod] = React46.useState(null);
7358
- const [listOnly, setListOnly] = React46.useState(false);
7467
+ const [q, setQ] = React47.useState("");
7468
+ const [method, setMethod] = React47.useState(null);
7469
+ const [mod, setMod] = React47.useState(null);
7470
+ const [listOnly, setListOnly] = React47.useState(false);
7359
7471
  const activeModule = modules && modules.find((m) => m.id === mod);
7360
7472
  const hits = spec.filter((s) => (!method || s.method === method) && (!listOnly || s.isList) && (!activeModule || activeModule.endpoints.indexOf(s.id) >= 0) && (!q || (s.path + " " + s.title + " " + s.purpose + " " + (s.usedBy || []).join(" ")).toLowerCase().includes(q.toLowerCase())));
7361
7473
  const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
@@ -7434,7 +7546,7 @@ function ApiSpecBrowser({
7434
7546
  }
7435
7547
 
7436
7548
  // src/components/platform/ProfilePage.tsx
7437
- import * as React47 from "react";
7549
+ import * as React48 from "react";
7438
7550
  import { jsx as jsx73, jsxs as jsxs67 } from "react/jsx-runtime";
7439
7551
  function fieldInitialValue(field, user) {
7440
7552
  if (field.value) return field.value(user);
@@ -7470,21 +7582,21 @@ function ProfilePage({
7470
7582
  }) {
7471
7583
  const { user: sessionUser, roles, permissions, saveProfile: saveProfile2 } = useSessionContext();
7472
7584
  const user = userProp || sessionUser;
7473
- const initialFieldValues = React47.useMemo(() => {
7585
+ const initialFieldValues = React48.useMemo(() => {
7474
7586
  const values2 = {};
7475
7587
  for (const field of fields) values2[field.key] = fieldInitialValue(field, user);
7476
7588
  return values2;
7477
7589
  }, [user.id, fields]);
7478
- const initialNotify = React47.useMemo(() => {
7590
+ const initialNotify = React48.useMemo(() => {
7479
7591
  const values2 = {};
7480
7592
  for (const n of notifications) values2[n.key] = !!user.notify?.[n.key];
7481
7593
  return values2;
7482
7594
  }, [user.id, notifications]);
7483
- const [values, setValues] = React47.useState(initialFieldValues);
7484
- const [notify, setNotify] = React47.useState(initialNotify);
7485
- const [saving, setSaving] = React47.useState(false);
7486
- const [saved, setSaved] = React47.useState(false);
7487
- const [prevUserId, setPrevUserId] = React47.useState(user.id);
7595
+ const [values, setValues] = React48.useState(initialFieldValues);
7596
+ const [notify, setNotify] = React48.useState(initialNotify);
7597
+ const [saving, setSaving] = React48.useState(false);
7598
+ const [saved, setSaved] = React48.useState(false);
7599
+ const [prevUserId, setPrevUserId] = React48.useState(user.id);
7488
7600
  if (user.id !== prevUserId) {
7489
7601
  setPrevUserId(user.id);
7490
7602
  setValues(initialFieldValues);
@@ -7672,11 +7784,11 @@ function ProfilePage({
7672
7784
  }
7673
7785
 
7674
7786
  // src/components/platform/SignInPage.tsx
7675
- import * as React48 from "react";
7787
+ import * as React49 from "react";
7676
7788
  import { Fragment as Fragment18, jsx as jsx74, jsxs as jsxs68 } from "react/jsx-runtime";
7677
7789
  function SignInPage({ productName, onSignIn, continueAs, error }) {
7678
- const [signingIn, setSigningIn] = React48.useState(false);
7679
- const [resuming, setResuming] = React48.useState(false);
7790
+ const [signingIn, setSigningIn] = React49.useState(false);
7791
+ const [resuming, setResuming] = React49.useState(false);
7680
7792
  function handleSignIn() {
7681
7793
  setSigningIn(true);
7682
7794
  onSignIn();
@@ -7865,10 +7977,10 @@ function GoogleGlyph() {
7865
7977
  }
7866
7978
 
7867
7979
  // src/components/platform/RoadmapTimeline.tsx
7868
- import * as React51 from "react";
7980
+ import * as React52 from "react";
7869
7981
 
7870
7982
  // src/kits/session.ts
7871
- import * as React49 from "react";
7983
+ import * as React50 from "react";
7872
7984
  var PERMISSION_CATALOG = [
7873
7985
  { group: "Plans", items: [
7874
7986
  { key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
@@ -9435,8 +9547,8 @@ function roadmap(overrides) {
9435
9547
  };
9436
9548
  }
9437
9549
  function useSession() {
9438
- const [s, setS] = React49.useState(getSession);
9439
- React49.useEffect(() => subscribe(setS), []);
9550
+ const [s, setS] = React50.useState(getSession);
9551
+ React50.useEffect(() => subscribe(setS), []);
9440
9552
  return s;
9441
9553
  }
9442
9554
  var SessionKit = {
@@ -9469,7 +9581,7 @@ var SessionKit = {
9469
9581
  };
9470
9582
  function DemoSessionProvider({ children }) {
9471
9583
  const session = useSession();
9472
- const value = React49.useMemo(
9584
+ const value = React50.useMemo(
9473
9585
  () => ({
9474
9586
  user: session.user,
9475
9587
  roles: session.roles,
@@ -9485,11 +9597,11 @@ function DemoSessionProvider({ children }) {
9485
9597
  }),
9486
9598
  [session]
9487
9599
  );
9488
- return React49.createElement(SessionProvider, { value }, children);
9600
+ return React50.createElement(SessionProvider, { value }, children);
9489
9601
  }
9490
9602
 
9491
9603
  // src/kits/runtime.ts
9492
- import * as React50 from "react";
9604
+ import * as React51 from "react";
9493
9605
  var WIRED_ENDPOINTS = [
9494
9606
  // Nothing yet. Every id below would come from a real service:
9495
9607
  // "plan.get", "placements.list", …
@@ -9660,8 +9772,8 @@ var RuntimeKit = {
9660
9772
  };
9661
9773
  RuntimeKit.declare(FEATURE_NEEDS);
9662
9774
  function useRuntimeMode() {
9663
- const [m, setM] = React50.useState(RuntimeKit.getMode());
9664
- React50.useEffect(() => RuntimeKit.subscribe(setM), []);
9775
+ const [m, setM] = React51.useState(RuntimeKit.getMode());
9776
+ React51.useEffect(() => RuntimeKit.subscribe(setM), []);
9665
9777
  return m;
9666
9778
  }
9667
9779
  function useFeatureStatus(key) {
@@ -9744,12 +9856,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
9744
9856
  }
9745
9857
  function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9746
9858
  const session = SessionKit.useSession();
9747
- const [project, setProject] = React51.useState("");
9748
- const [q, setQ] = React51.useState("");
9749
- const scrollRef = React51.useRef(null);
9750
- const nowRef = React51.useRef(null);
9751
- const rm = React51.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
9752
- const byKey = React51.useMemo(() => {
9859
+ const [project, setProject] = React52.useState("");
9860
+ const [q, setQ] = React52.useState("");
9861
+ const scrollRef = React52.useRef(null);
9862
+ const nowRef = React52.useRef(null);
9863
+ const rm = React52.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
9864
+ const byKey = React52.useMemo(() => {
9753
9865
  const m = {};
9754
9866
  rm.items.forEach((i) => {
9755
9867
  m[i.key] = i;
@@ -9758,7 +9870,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9758
9870
  }, [rm]);
9759
9871
  const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
9760
9872
  const projects = [...new Set(rm.items.map((i) => i.project))];
9761
- React51.useEffect(() => {
9873
+ React52.useEffect(() => {
9762
9874
  let raf1 = 0, raf2 = 0;
9763
9875
  const place = () => {
9764
9876
  const box = scrollRef.current, mark = nowRef.current;
@@ -9832,7 +9944,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9832
9944
  lastPhase = item.phase;
9833
9945
  const inPhase = items.filter((i) => i.phase === item.phase).length;
9834
9946
  const isBoundary = n === firstPending;
9835
- return /* @__PURE__ */ jsxs69(React51.Fragment, { children: [
9947
+ return /* @__PURE__ */ jsxs69(React52.Fragment, { children: [
9836
9948
  showPhase ? /* @__PURE__ */ jsxs69("div", { className: "fd-rm-era", children: [
9837
9949
  /* @__PURE__ */ jsxs69("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
9838
9950
  /* @__PURE__ */ jsx75("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
@@ -9876,7 +9988,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9876
9988
  }
9877
9989
 
9878
9990
  // src/components/platform/ComingSoon.tsx
9879
- import * as React52 from "react";
9991
+ import * as React53 from "react";
9880
9992
  import { createPortal as createPortal5 } from "react-dom";
9881
9993
  import { Fragment as Fragment20, jsx as jsx76, jsxs as jsxs70 } from "react/jsx-runtime";
9882
9994
  var BYPASS_STORE = "fd.soon.bypass.v1";
@@ -9894,8 +10006,8 @@ function writeBypassed(list) {
9894
10006
  }
9895
10007
  }
9896
10008
  function useBypass(key) {
9897
- const [on, setOn] = React52.useState(() => !!key && readBypassed().indexOf(key) >= 0);
9898
- React52.useEffect(() => {
10009
+ const [on, setOn] = React53.useState(() => !!key && readBypassed().indexOf(key) >= 0);
10010
+ React53.useEffect(() => {
9899
10011
  setOn(!!key && readBypassed().indexOf(key) >= 0);
9900
10012
  }, [key]);
9901
10013
  const set = (next) => {
@@ -9942,9 +10054,9 @@ function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onB
9942
10054
  ] });
9943
10055
  }
9944
10056
  function useHoverCard(open) {
9945
- const anchor = React52.useRef(null);
9946
- const [pos, setPos] = React52.useState(null);
9947
- React52.useLayoutEffect(() => {
10057
+ const anchor = React53.useRef(null);
10058
+ const [pos, setPos] = React53.useState(null);
10059
+ React53.useLayoutEffect(() => {
9948
10060
  if (!open || !anchor.current) {
9949
10061
  setPos(null);
9950
10062
  return;
@@ -10071,10 +10183,10 @@ function ComingSoon({
10071
10183
  ] });
10072
10184
  }
10073
10185
  function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
10074
- const [open, setOpen] = React52.useState(false);
10186
+ const [open, setOpen] = React53.useState(false);
10075
10187
  const [anchor, pos] = useHoverCard(open);
10076
10188
  const zIndex = useOverlayLayer(open);
10077
- const close = React52.useRef(null);
10189
+ const close = React53.useRef(null);
10078
10190
  const show = () => {
10079
10191
  if (close.current) {
10080
10192
  clearTimeout(close.current);
@@ -10090,7 +10202,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
10090
10202
  setOpen(false);
10091
10203
  }, 140);
10092
10204
  };
10093
- React52.useEffect(() => () => {
10205
+ React53.useEffect(() => () => {
10094
10206
  if (close.current) clearTimeout(close.current);
10095
10207
  }, []);
10096
10208
  return /* @__PURE__ */ jsxs70(
@@ -10261,14 +10373,14 @@ function PermissionHint({ perm, children }) {
10261
10373
  }
10262
10374
 
10263
10375
  // src/components/platform/ModeSwitch.tsx
10264
- import * as React53 from "react";
10376
+ import * as React54 from "react";
10265
10377
  import { Fragment as Fragment21, jsx as jsx78, jsxs as jsxs72 } from "react/jsx-runtime";
10266
10378
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
10267
10379
  const mode2 = useRuntimeMode();
10268
- const [open, setOpen] = React53.useState(false);
10380
+ const [open, setOpen] = React54.useState(false);
10269
10381
  const zIndex = useOverlayLayer(open);
10270
- const ref = React53.useRef(null);
10271
- React53.useEffect(() => {
10382
+ const ref = React54.useRef(null);
10383
+ React54.useEffect(() => {
10272
10384
  if (!open) return;
10273
10385
  const away = (e) => {
10274
10386
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -10524,7 +10636,7 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
10524
10636
  }
10525
10637
 
10526
10638
  // src/components/planner/MixGap.tsx
10527
- import * as React54 from "react";
10639
+ import * as React55 from "react";
10528
10640
  import { Fragment as Fragment22, jsx as jsx81, jsxs as jsxs75 } from "react/jsx-runtime";
10529
10641
  var MIX_GAP_LAYOUT = {
10530
10642
  trackHeight: 30,
@@ -10539,7 +10651,7 @@ function mixGapOffset(value, max) {
10539
10651
  }
10540
10652
  function MixGap({ rows = [], loading = false, className = "" }) {
10541
10653
  const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
10542
- const [hover, setHover] = React54.useState(null);
10654
+ const [hover, setHover] = React55.useState(null);
10543
10655
  const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
10544
10656
  const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
10545
10657
  const { trackHeight, barHeight, targetWidth, labelGap } = MIX_GAP_LAYOUT;
@@ -10682,7 +10794,7 @@ function ChannelContribution({
10682
10794
  }
10683
10795
 
10684
10796
  // src/components/planner/BudgetReallocator.tsx
10685
- import * as React55 from "react";
10797
+ import * as React56 from "react";
10686
10798
  import { Fragment as Fragment23, jsx as jsx83, jsxs as jsxs77 } from "react/jsx-runtime";
10687
10799
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
10688
10800
  function BudgetReallocator({
@@ -10698,8 +10810,8 @@ function BudgetReallocator({
10698
10810
  onCancel,
10699
10811
  className = ""
10700
10812
  }) {
10701
- const [draft, setDraft] = React55.useState(spend);
10702
- React55.useEffect(() => setDraft(spend), [spend]);
10813
+ const [draft, setDraft] = React56.useState(spend);
10814
+ React56.useEffect(() => setDraft(spend), [spend]);
10703
10815
  const dirty = draft !== spend;
10704
10816
  const nextCrp = scoreFor ? scoreFor(draft) : crp;
10705
10817
  const nextBand = bandFor(nextCrp);
@@ -10714,10 +10826,10 @@ function BudgetReallocator({
10714
10826
  setDraft(spend);
10715
10827
  if (onCancel) onCancel();
10716
10828
  };
10717
- const trackRef = React55.useRef(null);
10718
- const chipRef = React55.useRef(null);
10719
- const [chipLeft, setChipLeft] = React55.useState(null);
10720
- React55.useLayoutEffect(() => {
10829
+ const trackRef = React56.useRef(null);
10830
+ const chipRef = React56.useRef(null);
10831
+ const [chipLeft, setChipLeft] = React56.useState(null);
10832
+ React56.useLayoutEffect(() => {
10721
10833
  const track = trackRef.current;
10722
10834
  if (!dirty || !track) {
10723
10835
  setChipLeft(null);
@@ -10904,7 +11016,7 @@ function ImportanceTag({ level, showLabel = true, size = "sm", className = "" })
10904
11016
  }
10905
11017
 
10906
11018
  // src/components/planner/PreferenceMeter.tsx
10907
- import * as React56 from "react";
11019
+ import * as React57 from "react";
10908
11020
  import { jsx as jsx86, jsxs as jsxs80 } from "react/jsx-runtime";
10909
11021
  function preferenceScore(criteria) {
10910
11022
  const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
@@ -10934,7 +11046,7 @@ function PreferenceMeter({
10934
11046
  className = ""
10935
11047
  }) {
10936
11048
  const value = score == null ? preferenceScore(criteria) : score;
10937
- const segments = React56.useMemo(() => preferenceSegments(criteria), [criteria]);
11049
+ const segments = React57.useMemo(() => preferenceSegments(criteria), [criteria]);
10938
11050
  return /* @__PURE__ */ jsx86(
10939
11051
  ScoreMeter,
10940
11052
  {
@@ -10973,10 +11085,10 @@ function PreferenceMeter({
10973
11085
  }
10974
11086
 
10975
11087
  // src/components/chat/AgentChatPanel.tsx
10976
- import * as React62 from "react";
11088
+ import * as React63 from "react";
10977
11089
 
10978
11090
  // src/components/chat/chatEngine.ts
10979
- import * as React57 from "react";
11091
+ import * as React58 from "react";
10980
11092
  var CHAT_UNAVAILABLE = "chat_unavailable";
10981
11093
  var JOB_PENDING = ["queued", "running"];
10982
11094
  var JOB_SUCCESS = ["completed", "recovered"];
@@ -11030,22 +11142,22 @@ function useChatEngine(opts) {
11030
11142
  onClear,
11031
11143
  onFeedback
11032
11144
  } = opts || {};
11033
- const [status, setStatus] = React57.useState("idle");
11034
- const [threadId, setThreadId] = React57.useState(null);
11035
- const [messages, setMessages] = React57.useState([]);
11036
- const [queue, setQueue] = React57.useState([]);
11037
- const [fatal, setFatal] = React57.useState(null);
11038
- const [busy, setBusy] = React57.useState(false);
11039
- const [turnStartedAt, setTurnStartedAt] = React57.useState(null);
11040
- const listRef = React57.useRef([]);
11041
- const queueRef = React57.useRef([]);
11042
- const busyRef = React57.useRef(false);
11043
- const stoppedRef = React57.useRef(false);
11044
- const abortRef = React57.useRef(null);
11045
- const serverCount = React57.useRef(0);
11046
- const threadRef = React57.useRef(null);
11047
- const mounted = React57.useRef(true);
11048
- React57.useEffect(() => {
11145
+ const [status, setStatus] = React58.useState("idle");
11146
+ const [threadId, setThreadId] = React58.useState(null);
11147
+ const [messages, setMessages] = React58.useState([]);
11148
+ const [queue, setQueue] = React58.useState([]);
11149
+ const [fatal, setFatal] = React58.useState(null);
11150
+ const [busy, setBusy] = React58.useState(false);
11151
+ const [turnStartedAt, setTurnStartedAt] = React58.useState(null);
11152
+ const listRef = React58.useRef([]);
11153
+ const queueRef = React58.useRef([]);
11154
+ const busyRef = React58.useRef(false);
11155
+ const stoppedRef = React58.useRef(false);
11156
+ const abortRef = React58.useRef(null);
11157
+ const serverCount = React58.useRef(0);
11158
+ const threadRef = React58.useRef(null);
11159
+ const mounted = React58.useRef(true);
11160
+ React58.useEffect(() => {
11049
11161
  mounted.current = true;
11050
11162
  return () => {
11051
11163
  mounted.current = false;
@@ -11067,14 +11179,14 @@ function useChatEngine(opts) {
11067
11179
  }
11068
11180
  return false;
11069
11181
  };
11070
- const loadThread = React57.useCallback(async (id) => {
11182
+ const loadThread = React58.useCallback(async (id) => {
11071
11183
  const data = await apiAdapter.getThread(id);
11072
11184
  const list = [...data && data.messages || []];
11073
11185
  serverCount.current = list.length;
11074
11186
  commit(list);
11075
11187
  return list;
11076
11188
  }, [apiAdapter]);
11077
- React57.useEffect(() => {
11189
+ React58.useEffect(() => {
11078
11190
  if (!apiAdapter) {
11079
11191
  setStatus("idle");
11080
11192
  setFatal(null);
@@ -11287,7 +11399,7 @@ function useChatEngine(opts) {
11287
11399
  await dispatchTurn(turn);
11288
11400
  }
11289
11401
  }
11290
- const send = React57.useCallback((text, attachments) => {
11402
+ const send = React58.useCallback((text, attachments) => {
11291
11403
  const body = (text || "").trim();
11292
11404
  if (!body && !(attachments && attachments.length)) return;
11293
11405
  if (status === "disconnected") return;
@@ -11297,7 +11409,7 @@ function useChatEngine(opts) {
11297
11409
  stoppedRef.current = false;
11298
11410
  drain();
11299
11411
  }, [status]);
11300
- const stop = React57.useCallback(() => {
11412
+ const stop = React58.useCallback(() => {
11301
11413
  stoppedRef.current = true;
11302
11414
  const ac = abortRef.current;
11303
11415
  if (ac) {
@@ -11316,11 +11428,11 @@ function useChatEngine(opts) {
11316
11428
  store.del(STORAGE_PREFIX + threadRef.current);
11317
11429
  }
11318
11430
  }, [apiAdapter]);
11319
- const removeQueued = React57.useCallback((id) => {
11431
+ const removeQueued = React58.useCallback((id) => {
11320
11432
  queueRef.current = queueRef.current.filter((t) => t.id !== id);
11321
11433
  setQueue(queueRef.current.slice());
11322
11434
  }, []);
11323
- const retry = React57.useCallback(() => {
11435
+ const retry = React58.useCallback(() => {
11324
11436
  const list = listRef.current;
11325
11437
  let at = -1;
11326
11438
  for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
@@ -11336,19 +11448,19 @@ function useChatEngine(opts) {
11336
11448
  setQueue(queueRef.current.slice());
11337
11449
  drain();
11338
11450
  }, []);
11339
- const clear = React57.useCallback(() => {
11451
+ const clear = React58.useCallback(() => {
11340
11452
  commit([]);
11341
11453
  serverCount.current = 0;
11342
11454
  queueRef.current = [];
11343
11455
  setQueue([]);
11344
11456
  onClear && onClear();
11345
11457
  }, [onClear]);
11346
- const setFeedback = React57.useCallback((id, value) => {
11458
+ const setFeedback = React58.useCallback((id, value) => {
11347
11459
  patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
11348
11460
  const msg = listRef.current.find((m) => m.id === id);
11349
11461
  onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
11350
11462
  }, [onFeedback]);
11351
- const reload = React57.useCallback(async () => {
11463
+ const reload = React58.useCallback(async () => {
11352
11464
  if (!threadRef.current) return;
11353
11465
  setStatus("loading");
11354
11466
  try {
@@ -11386,10 +11498,10 @@ var ChatKit = {
11386
11498
  };
11387
11499
 
11388
11500
  // src/components/chat/ChatTranscript.tsx
11389
- import * as React59 from "react";
11501
+ import * as React60 from "react";
11390
11502
 
11391
11503
  // src/components/chat/ChatTurn.tsx
11392
- import * as React58 from "react";
11504
+ import * as React59 from "react";
11393
11505
  import { jsx as jsx87, jsxs as jsxs81 } from "react/jsx-runtime";
11394
11506
  function JsonView({ value }) {
11395
11507
  let text;
@@ -11426,7 +11538,7 @@ function PacketCard({ packet, schema, render, onApply, applied }) {
11426
11538
  ] });
11427
11539
  }
11428
11540
  function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
11429
- const [open, setOpen] = React58.useState(defaultOpen);
11541
+ const [open, setOpen] = React59.useState(defaultOpen);
11430
11542
  if (!text) return null;
11431
11543
  return /* @__PURE__ */ jsxs81("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
11432
11544
  /* @__PURE__ */ jsxs81("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -11443,7 +11555,7 @@ function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
11443
11555
  ] });
11444
11556
  }
11445
11557
  function Citations({ items = [], onOpen }) {
11446
- const [open, setOpen] = React58.useState(false);
11558
+ const [open, setOpen] = React59.useState(false);
11447
11559
  if (!items.length) return null;
11448
11560
  return /* @__PURE__ */ jsxs81("div", { className: "fdc-cites", children: [
11449
11561
  /* @__PURE__ */ jsxs81("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -11469,7 +11581,7 @@ function clampText(text, max) {
11469
11581
  return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
11470
11582
  }
11471
11583
  function MessageBody({ message: m, ctx }) {
11472
- const [expanded, setExpanded] = React58.useState(false);
11584
+ const [expanded, setExpanded] = React59.useState(false);
11473
11585
  const isUser = m.role === "user";
11474
11586
  const raw = m.text || "";
11475
11587
  const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
@@ -11536,9 +11648,9 @@ function MessageBody({ message: m, ctx }) {
11536
11648
  ] });
11537
11649
  }
11538
11650
  function useCopyRun() {
11539
- const [done, setDone] = React58.useState(false);
11540
- const t = React58.useRef(null);
11541
- React58.useEffect(() => () => {
11651
+ const [done, setDone] = React59.useState(false);
11652
+ const t = React59.useRef(null);
11653
+ React59.useEffect(() => () => {
11542
11654
  if (t.current) clearTimeout(t.current);
11543
11655
  }, []);
11544
11656
  return [done, (text) => {
@@ -11565,7 +11677,7 @@ function RunActions({ group, ctx }) {
11565
11677
  ] }),
11566
11678
  isAssistant && ctx.onRetry ? /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
11567
11679
  group.role === "user" && ctx.onEdit ? /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
11568
- isAssistant && ctx.onFeedback ? /* @__PURE__ */ jsxs81(React58.Fragment, { children: [
11680
+ isAssistant && ctx.onFeedback ? /* @__PURE__ */ jsxs81(React59.Fragment, { children: [
11569
11681
  /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act" + (fb === "up" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "up"), "aria-pressed": fb === "up", "aria-label": "Good response", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
11570
11682
  /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act" + (fb === "down" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "down"), "aria-pressed": fb === "down", "aria-label": "Bad response", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
11571
11683
  ] }) : null,
@@ -11660,10 +11772,10 @@ function ChatTranscript({
11660
11772
  renderEmpty,
11661
11773
  className = ""
11662
11774
  }) {
11663
- const scroller = React59.useRef(null);
11664
- const stick = React59.useRef(true);
11665
- const [pill, setPill] = React59.useState(0);
11666
- const seen = React59.useRef(0);
11775
+ const scroller = React60.useRef(null);
11776
+ const stick = React60.useRef(true);
11777
+ const [pill, setPill] = React60.useState(0);
11778
+ const seen = React60.useRef(0);
11667
11779
  const toBottom = (smooth) => {
11668
11780
  const el = scroller.current;
11669
11781
  if (!el) return;
@@ -11682,7 +11794,7 @@ function ChatTranscript({
11682
11794
  seen.current = messages.length;
11683
11795
  }
11684
11796
  };
11685
- React59.useLayoutEffect(() => {
11797
+ React60.useLayoutEffect(() => {
11686
11798
  const el = scroller.current;
11687
11799
  if (!el) return;
11688
11800
  if (stick.current) {
@@ -11690,7 +11802,7 @@ function ChatTranscript({
11690
11802
  seen.current = messages.length;
11691
11803
  } else setPill(Math.max(0, messages.length - seen.current));
11692
11804
  }, [messages]);
11693
- React59.useEffect(() => {
11805
+ React60.useEffect(() => {
11694
11806
  const el = scroller.current;
11695
11807
  const inner = el && el.firstChild;
11696
11808
  if (!el || !inner || typeof ResizeObserver === "undefined") return;
@@ -11700,7 +11812,7 @@ function ChatTranscript({
11700
11812
  ro.observe(inner);
11701
11813
  return () => ro.disconnect();
11702
11814
  }, []);
11703
- const groups = React59.useMemo(() => groupMessages(messages), [messages]);
11815
+ const groups = React60.useMemo(() => groupMessages(messages), [messages]);
11704
11816
  const empty = !messages.length && status === "ready";
11705
11817
  return /* @__PURE__ */ jsxs82("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
11706
11818
  /* @__PURE__ */ jsxs82("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
@@ -11724,7 +11836,7 @@ function ChatTranscript({
11724
11836
  const prev = groups[i - 1];
11725
11837
  const k = dayKey(g.messages[0].timestamp);
11726
11838
  const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
11727
- return /* @__PURE__ */ jsxs82(React59.Fragment, { children: [
11839
+ return /* @__PURE__ */ jsxs82(React60.Fragment, { children: [
11728
11840
  showDay ? /* @__PURE__ */ jsx88("div", { className: "fdc-day", children: /* @__PURE__ */ jsx88("span", { children: dayLabel(k) }) }) : null,
11729
11841
  /* @__PURE__ */ jsx88(ChatTurn, { group: g, ctx })
11730
11842
  ] }, g.key || i);
@@ -11741,7 +11853,7 @@ function ChatTranscript({
11741
11853
  var TranscriptKit = { groupMessages };
11742
11854
 
11743
11855
  // src/components/chat/ChatComposer.tsx
11744
- import * as React60 from "react";
11856
+ import * as React61 from "react";
11745
11857
  import { jsx as jsx89, jsxs as jsxs83 } from "react/jsx-runtime";
11746
11858
  function ChatComposer({
11747
11859
  onSubmit,
@@ -11769,17 +11881,17 @@ function ChatComposer({
11769
11881
  onReject,
11770
11882
  onOpenAttachment
11771
11883
  }) {
11772
- const [text, setText] = React60.useState(draft || "");
11773
- const [trigger, setTrigger] = React60.useState(null);
11774
- const [mentionItems, setMentionItems] = React60.useState([]);
11775
- const [listening, setListening] = React60.useState(false);
11776
- const [notice, setNotice] = React60.useState(null);
11777
- const editor = React60.useRef(null);
11778
- const wrap = React60.useRef(null);
11779
- const stopVoice = React60.useRef(null);
11884
+ const [text, setText] = React61.useState(draft || "");
11885
+ const [trigger, setTrigger] = React61.useState(null);
11886
+ const [mentionItems, setMentionItems] = React61.useState([]);
11887
+ const [listening, setListening] = React61.useState(false);
11888
+ const [notice, setNotice] = React61.useState(null);
11889
+ const editor = React61.useRef(null);
11890
+ const wrap = React61.useRef(null);
11891
+ const stopVoice = React61.useRef(null);
11780
11892
  const staged = useStagedFiles(fileUploadHandler, { onError: () => {
11781
11893
  } });
11782
- React60.useEffect(() => {
11894
+ React61.useEffect(() => {
11783
11895
  if (draft != null && draft !== text) setText(draft);
11784
11896
  }, [draft]);
11785
11897
  const change = (v) => {
@@ -11815,7 +11927,7 @@ function ChatComposer({
11815
11927
  e.preventDefault();
11816
11928
  staged.add(found.files);
11817
11929
  };
11818
- React60.useEffect(() => {
11930
+ React61.useEffect(() => {
11819
11931
  if (!trigger || trigger.type !== "mention" || !mentionSources) {
11820
11932
  setMentionItems([]);
11821
11933
  return;
@@ -11833,7 +11945,7 @@ function ChatComposer({
11833
11945
  const q = (trigger.query || "").toLowerCase();
11834
11946
  setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
11835
11947
  }, [trigger, mentionSources]);
11836
- const slashItems = React60.useMemo(() => {
11948
+ const slashItems = React61.useMemo(() => {
11837
11949
  if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
11838
11950
  const q = (trigger.query || "").toLowerCase();
11839
11951
  return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
@@ -12007,12 +12119,12 @@ function ChatComposer({
12007
12119
  }
12008
12120
 
12009
12121
  // src/components/chat/ChatSessionBar.tsx
12010
- import * as React61 from "react";
12122
+ import * as React62 from "react";
12011
12123
  import { jsx as jsx90, jsxs as jsxs84 } from "react/jsx-runtime";
12012
12124
  var compact2 = meterFormats.compact;
12013
12125
  function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
12014
- const [open, setOpen] = React61.useState(false);
12015
- const anchor = React61.useRef(null);
12126
+ const [open, setOpen] = React62.useState(false);
12127
+ const anchor = React62.useRef(null);
12016
12128
  const stats = sessionStats || null;
12017
12129
  const cu = contextUsage || null;
12018
12130
  const limits = usageLimits || null;
@@ -12027,7 +12139,7 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
12027
12139
  if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
12028
12140
  if (cu) bits.push(pct + "% context");
12029
12141
  const expandable = !!(cu || limits);
12030
- return /* @__PURE__ */ jsxs84(React61.Fragment, { children: [
12142
+ return /* @__PURE__ */ jsxs84(React62.Fragment, { children: [
12031
12143
  /* @__PURE__ */ jsxs84("div", { className: "fdc-bar", children: [
12032
12144
  /* @__PURE__ */ jsxs84(
12033
12145
  "button",
@@ -12168,7 +12280,7 @@ function ModelControls({
12168
12280
  ] })
12169
12281
  });
12170
12282
  }
12171
- return /* @__PURE__ */ jsxs84(React61.Fragment, { children: [
12283
+ return /* @__PURE__ */ jsxs84(React62.Fragment, { children: [
12172
12284
  /* @__PURE__ */ jsx90(
12173
12285
  MenuButton,
12174
12286
  {
@@ -12285,11 +12397,11 @@ function AgentChatPanel({
12285
12397
  onFeedback,
12286
12398
  onEditMessage
12287
12399
  }) {
12288
- const [panelWidth, setPanelWidth] = React62.useState(width);
12289
- const [applied, setApplied] = React62.useState({});
12290
- const [draft, setDraft] = React62.useState("");
12291
- const dragging = React62.useRef(null);
12292
- React62.useEffect(() => setPanelWidth(width), [width]);
12400
+ const [panelWidth, setPanelWidth] = React63.useState(width);
12401
+ const [applied, setApplied] = React63.useState({});
12402
+ const [draft, setDraft] = React63.useState("");
12403
+ const dragging = React63.useRef(null);
12404
+ React63.useEffect(() => setPanelWidth(width), [width]);
12293
12405
  const engine = useChatEngine({
12294
12406
  contextType,
12295
12407
  contextId,
@@ -12493,7 +12605,7 @@ function AgentChatPanel({
12493
12605
  }
12494
12606
 
12495
12607
  // src/components/files/FileViewer.tsx
12496
- import * as React63 from "react";
12608
+ import * as React64 from "react";
12497
12609
  import { jsx as jsx92, jsxs as jsxs86 } from "react/jsx-runtime";
12498
12610
  function previewKindOf(file) {
12499
12611
  const mime = (file.mime || "").toLowerCase();
@@ -12550,13 +12662,13 @@ function parseDelimited(text, delimiter) {
12550
12662
  return rows;
12551
12663
  }
12552
12664
  function usePreview(file, kind, resolveUrl) {
12553
- const [state, setState] = React63.useState({ status: "loading" });
12554
- const [attempt, setAttempt] = React63.useState(0);
12555
- const resolveRef = React63.useRef(resolveUrl);
12556
- React63.useEffect(() => {
12665
+ const [state, setState] = React64.useState({ status: "loading" });
12666
+ const [attempt, setAttempt] = React64.useState(0);
12667
+ const resolveRef = React64.useRef(resolveUrl);
12668
+ React64.useEffect(() => {
12557
12669
  resolveRef.current = resolveUrl;
12558
12670
  }, [resolveUrl]);
12559
- React63.useEffect(() => {
12671
+ React64.useEffect(() => {
12560
12672
  if (kind === "none") {
12561
12673
  setState({ status: "ready" });
12562
12674
  return;
@@ -12586,7 +12698,7 @@ function usePreview(file, kind, resolveUrl) {
12586
12698
  if (ac) ac.abort();
12587
12699
  };
12588
12700
  }, [file.id, kind, attempt]);
12589
- return { ...state, retry: React63.useCallback(() => setAttempt((a) => a + 1), []) };
12701
+ return { ...state, retry: React64.useCallback(() => setAttempt((a) => a + 1), []) };
12590
12702
  }
12591
12703
  function PreviewBody({ file, kind, state }) {
12592
12704
  if (kind === "image") return /* @__PURE__ */ jsx92("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
@@ -12631,13 +12743,13 @@ function PreviewBody({ file, kind, state }) {
12631
12743
  ] });
12632
12744
  }
12633
12745
  function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", onClose, actions, className = "" }) {
12634
- const kind = React63.useMemo(() => previewKindOf(file), [file]);
12746
+ const kind = React64.useMemo(() => previewKindOf(file), [file]);
12635
12747
  const preview = usePreview(file, kind, resolveUrl);
12636
- const [tab, setTab] = React63.useState("preview");
12637
- const [downloading, setDownloading] = React63.useState(false);
12638
- const [downloadError, setDownloadError] = React63.useState(null);
12748
+ const [tab, setTab] = React64.useState("preview");
12749
+ const [downloading, setDownloading] = React64.useState(false);
12750
+ const [downloadError, setDownloadError] = React64.useState(null);
12639
12751
  const hasSide = sidePanel != null;
12640
- React63.useEffect(() => {
12752
+ React64.useEffect(() => {
12641
12753
  setTab("preview");
12642
12754
  }, [file.id]);
12643
12755
  const download = () => {
@@ -12704,7 +12816,7 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
12704
12816
  }
12705
12817
 
12706
12818
  // src/components/files/FileBrowser.tsx
12707
- import * as React64 from "react";
12819
+ import * as React65 from "react";
12708
12820
  import { Fragment as Fragment27, jsx as jsx93, jsxs as jsxs87 } from "react/jsx-runtime";
12709
12821
  function childrenOf(folders, parentId) {
12710
12822
  return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
@@ -12775,10 +12887,10 @@ function FolderPickerMenu({
12775
12887
  destinations,
12776
12888
  onPick
12777
12889
  }) {
12778
- const [open, setOpen] = React64.useState(false);
12890
+ const [open, setOpen] = React65.useState(false);
12779
12891
  const zIndex = useOverlayLayer(open);
12780
- const ref = React64.useRef(null);
12781
- React64.useEffect(() => {
12892
+ const ref = React65.useRef(null);
12893
+ React65.useEffect(() => {
12782
12894
  if (!open) return;
12783
12895
  const away = (e) => {
12784
12896
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -12830,8 +12942,8 @@ function FolderPickerMenu({
12830
12942
  ] });
12831
12943
  }
12832
12944
  function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
12833
- const [over, setOver] = React64.useState(false);
12834
- const dragCounter = React64.useRef(0);
12945
+ const [over, setOver] = React65.useState(false);
12946
+ const dragCounter = React65.useRef(0);
12835
12947
  const dropHandlers = isDropTarget ? {
12836
12948
  onDragEnter: (e) => {
12837
12949
  if (!hasInternalPayload(e)) return;
@@ -12879,8 +12991,8 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
12879
12991
  }
12880
12992
  function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
12881
12993
  const kids = childrenOf(folders, folder.id);
12882
- const [expanded, setExpanded] = React64.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
12883
- React64.useEffect(() => {
12994
+ const [expanded, setExpanded] = React65.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
12995
+ React65.useEffect(() => {
12884
12996
  if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
12885
12997
  }, [currentFolderId, folder.id, folders]);
12886
12998
  const isCurrent = folder.id === currentFolderId;
@@ -12946,15 +13058,15 @@ function FileBrowser({
12946
13058
  const canUpload = perm.canUpload !== false && !!onUpload;
12947
13059
  const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
12948
13060
  const canDeleteFolder = perm.canDeleteFolder !== false && !!onDeleteFolder;
12949
- const [createOpen, setCreateOpen] = React64.useState(false);
12950
- const [createName, setCreateName] = React64.useState("");
12951
- const [renameTarget, setRenameTarget] = React64.useState(null);
12952
- const [renameValue, setRenameValue] = React64.useState("");
12953
- const [deleteTarget, setDeleteTarget] = React64.useState(null);
12954
- const uploadInputRef = React64.useRef(null);
12955
- const crumbFolders = React64.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
12956
- const subfolders = React64.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
12957
- const currentFiles = React64.useMemo(
13061
+ const [createOpen, setCreateOpen] = React65.useState(false);
13062
+ const [createName, setCreateName] = React65.useState("");
13063
+ const [renameTarget, setRenameTarget] = React65.useState(null);
13064
+ const [renameValue, setRenameValue] = React65.useState("");
13065
+ const [deleteTarget, setDeleteTarget] = React65.useState(null);
13066
+ const uploadInputRef = React65.useRef(null);
13067
+ const crumbFolders = React65.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
13068
+ const subfolders = React65.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
13069
+ const currentFiles = React65.useMemo(
12958
13070
  () => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
12959
13071
  [files, currentFolderId]
12960
13072
  );
@@ -13186,8 +13298,8 @@ function BreadcrumbCrumb({
13186
13298
  isDropTarget,
13187
13299
  onDropPayload
13188
13300
  }) {
13189
- const [over, setOver] = React64.useState(false);
13190
- const dragCounter = React64.useRef(0);
13301
+ const [over, setOver] = React65.useState(false);
13302
+ const dragCounter = React65.useRef(0);
13191
13303
  const dropHandlers = isDropTarget ? {
13192
13304
  onDragEnter: (e) => {
13193
13305
  if (!hasInternalPayload(e)) return;
@@ -13216,7 +13328,7 @@ function BreadcrumbCrumb({
13216
13328
  }
13217
13329
 
13218
13330
  // src/kits/query.ts
13219
- import * as React65 from "react";
13331
+ import * as React66 from "react";
13220
13332
  var isMultiSelectShaped = (v) => !!v && typeof v === "object" && Array.isArray(v.values);
13221
13333
  var isRangeShaped = (v) => !!v && typeof v === "object" && ("min" in v || "max" in v);
13222
13334
  var isDateShaped = (v) => !!v && typeof v === "object" && "kind" in v;
@@ -13271,22 +13383,22 @@ function compare(a, b, dir) {
13271
13383
  var API = null;
13272
13384
  var PREFS = null;
13273
13385
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
13274
- const [query, setQuery] = React65.useState(() => {
13386
+ const [query, setQuery] = React66.useState(() => {
13275
13387
  const store = PREFS || window.PlannerPrefs;
13276
13388
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
13277
13389
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
13278
13390
  });
13279
- const savePref = React65.useCallback((patch2) => {
13391
+ const savePref = React66.useCallback((patch2) => {
13280
13392
  const store = PREFS || window.PlannerPrefs;
13281
13393
  if (prefsKey && store) store.setTable(prefsKey, patch2);
13282
13394
  }, [prefsKey]);
13283
- const [res, setRes] = React65.useState(null);
13284
- const [loading, setLoading] = React65.useState(true);
13285
- const seq2 = React65.useRef(0);
13395
+ const [res, setRes] = React66.useState(null);
13396
+ const [loading, setLoading] = React66.useState(true);
13397
+ const seq2 = React66.useRef(0);
13286
13398
  const depKey = (deps || []).join("|");
13287
13399
  const paramKey = JSON.stringify(params || {});
13288
13400
  const queryKey = JSON.stringify(query);
13289
- React65.useEffect(() => {
13401
+ React66.useEffect(() => {
13290
13402
  const id = ++seq2.current;
13291
13403
  setLoading(true);
13292
13404
  const t = setTimeout(() => {
@@ -13605,12 +13717,12 @@ function createVersionStore(initialState, options) {
13605
13717
  }
13606
13718
 
13607
13719
  // src/kits/documentTitle.ts
13608
- import * as React66 from "react";
13720
+ import * as React67 from "react";
13609
13721
  function formatDocumentTitle(routeTitle, appName) {
13610
13722
  return [routeTitle, appName, "Flytedesk"].filter((part) => !!part && part.length > 0).join(" \xB7 ");
13611
13723
  }
13612
13724
  function useDocumentTitle(routeTitle, appName) {
13613
- React66.useEffect(() => {
13725
+ React67.useEffect(() => {
13614
13726
  if (typeof document === "undefined") return;
13615
13727
  document.title = formatDocumentTitle(routeTitle, appName);
13616
13728
  }, [routeTitle, appName]);