@flytedan/flytebot-design-system 0.28.0 → 0.30.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.cjs CHANGED
@@ -438,6 +438,15 @@ var ROOT_LAYER = 0;
438
438
  var LAYER_STEP = 10;
439
439
  var ROOT_STEP = 100;
440
440
  var OVERLAY_CEILING = 1e4;
441
+ var openEscapeIds = [];
442
+ var registerEscapeLayer = (id) => {
443
+ if (openEscapeIds.includes(id)) return;
444
+ openEscapeIds = [...openEscapeIds, id];
445
+ };
446
+ var unregisterEscapeLayer = (id) => {
447
+ if (!openEscapeIds.includes(id)) return;
448
+ openEscapeIds = openEscapeIds.filter((existing) => existing !== id);
449
+ };
441
450
  var LayerContext = React2.createContext(ROOT_LAYER);
442
451
  var openRootIds = [];
443
452
  var rootListeners = /* @__PURE__ */ new Set();
@@ -515,6 +524,12 @@ function Popover({
515
524
  });
516
525
  const restore = React2.useRef(null);
517
526
  const zIndex = useOverlayLayer(!!open);
527
+ const escapeId = React2.useId();
528
+ React2.useLayoutEffect(() => {
529
+ if (!open) return;
530
+ registerEscapeLayer(escapeId);
531
+ return () => unregisterEscapeLayer(escapeId);
532
+ }, [open, escapeId]);
518
533
  React2.useEffect(() => {
519
534
  if (!open) return;
520
535
  restore.current = typeof document !== "undefined" ? document.activeElement : null;
@@ -539,10 +554,10 @@ function Popover({
539
554
  onClose && onClose();
540
555
  };
541
556
  const key = (e) => {
542
- if (closeOnEscape && e.key === "Escape") {
543
- e.stopPropagation();
544
- onClose && onClose();
545
- }
557
+ if (!closeOnEscape || e.key !== "Escape") return;
558
+ if (openEscapeIds[openEscapeIds.length - 1] !== escapeId) return;
559
+ e.stopPropagation();
560
+ onClose && onClose();
546
561
  };
547
562
  document.addEventListener("pointerdown", away, true);
548
563
  document.addEventListener("keydown", key);
@@ -550,7 +565,7 @@ function Popover({
550
565
  document.removeEventListener("pointerdown", away, true);
551
566
  document.removeEventListener("keydown", key);
552
567
  };
553
- }, [open, closeOnOutside, closeOnEscape, onClose, anchorRef, triggerRef]);
568
+ }, [open, closeOnOutside, closeOnEscape, onClose, anchorRef, triggerRef, escapeId]);
554
569
  if (!open || !pos) return null;
555
570
  return portal(
556
571
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
@@ -1739,11 +1754,13 @@ var BANNER_TONE_STYLE = {
1739
1754
  text: "var(--danger-text)"
1740
1755
  }
1741
1756
  };
1742
- function Banner({ tone = "info", title, detail, actions }) {
1757
+ function Banner({ tone = "info", title, detail, actions, role }) {
1743
1758
  const t = BANNER_TONE_STYLE[tone];
1759
+ const liveRole = role || (tone === "danger" ? "alert" : "status");
1744
1760
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
1745
1761
  "div",
1746
1762
  {
1763
+ role: liveRole,
1747
1764
  style: {
1748
1765
  display: "flex",
1749
1766
  alignItems: "flex-start",
@@ -2953,7 +2970,7 @@ function DataTable({
2953
2970
  }
2954
2971
 
2955
2972
  // src/components/data/FilterBar.tsx
2956
- var React23 = __toESM(require("react"), 1);
2973
+ var React24 = __toESM(require("react"), 1);
2957
2974
 
2958
2975
  // src/components/forms/NumberInput.tsx
2959
2976
  var React21 = __toESM(require("react"), 1);
@@ -3001,21 +3018,36 @@ function NumberInput({
3001
3018
  "aria-label": ariaLabel
3002
3019
  }) {
3003
3020
  const fieldId = useFieldId(id);
3021
+ const errorId = fieldId + "-error";
3004
3022
  const num = (v) => {
3005
- const n = Number(String(v == null ? "" : v).replace(/[^0-9.-]/g, ""));
3023
+ const s = String(v == null ? "" : v).replace(/[^0-9.-]/g, "");
3024
+ if (s === "" || s === "-") return null;
3025
+ const n = Number(s);
3006
3026
  return isNaN(n) ? null : n;
3007
3027
  };
3028
+ const requiresInteger = Number.isInteger(step);
3029
+ const validationMessage = (n) => {
3030
+ if (requiresInteger && !Number.isInteger(n)) return "Enter a whole number";
3031
+ if (n < min) return "Must be \u2265 " + min;
3032
+ if (n > max) return "Must be \u2264 " + max;
3033
+ return null;
3034
+ };
3008
3035
  const [text, setText] = React21.useState(value == null || value === "" ? "" : String(value));
3009
3036
  const [editing, setEditing] = React21.useState(false);
3037
+ const [invalidMessage, setInvalidMessage] = React21.useState(null);
3010
3038
  const timer = React21.useRef(null);
3011
3039
  React21.useEffect(() => {
3012
- if (!editing) setText(value == null || value === "" ? "" : String(value));
3040
+ if (!editing) {
3041
+ setText(value == null || value === "" ? "" : String(value));
3042
+ setInvalidMessage(null);
3043
+ }
3013
3044
  }, [value, editing]);
3014
3045
  const clamp = (n) => Math.min(max, Math.max(min, n));
3015
3046
  const fire = (n) => onChange && onChange({ target: { value: n } });
3016
3047
  const bump = (dir, mult = 1) => {
3017
3048
  const cur = num(text) ?? num(value) ?? (min === -Infinity ? 0 : min);
3018
3049
  const next = clamp(cur + dir * (bigStep && mult > 1 ? bigStep : step) * (bigStep ? 1 : mult));
3050
+ setInvalidMessage(null);
3019
3051
  setText(String(next));
3020
3052
  fire(next);
3021
3053
  };
@@ -3039,7 +3071,8 @@ function NumberInput({
3039
3071
  const n = num(text);
3040
3072
  return n == null ? "" : format ? n.toLocaleString() : String(n);
3041
3073
  })();
3042
- const box = ["fd-input", "fd-input-num", error ? "fd-input-error" : "", disabled ? "fd-input-disabled" : ""].filter(Boolean).join(" ");
3074
+ const shownError = error || invalidMessage || void 0;
3075
+ const box = ["fd-input", "fd-input-num", shownError ? "fd-input-error" : "", disabled ? "fd-input-disabled" : ""].filter(Boolean).join(" ");
3043
3076
  const fieldName = label || ariaLabel;
3044
3077
  const stepperName = (verb) => fieldName ? verb + " " + fieldName : verb;
3045
3078
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: ["fd-field", className].filter(Boolean).join(" "), style, children: [
@@ -3055,20 +3088,29 @@ function NumberInput({
3055
3088
  disabled,
3056
3089
  placeholder,
3057
3090
  value: shown,
3091
+ "aria-invalid": shownError ? "true" : void 0,
3092
+ "aria-describedby": shownError ? errorId : void 0,
3058
3093
  onFocus: () => setEditing(true),
3059
3094
  onBlur: () => {
3060
3095
  setEditing(false);
3061
- const n = num(text);
3062
- if (n != null) {
3063
- const c = clamp(n);
3064
- setText(String(c));
3065
- fire(c);
3066
- }
3096
+ if (text.trim() === "") fire(void 0);
3067
3097
  },
3068
3098
  onChange: (e) => {
3069
- setText(e.target.value);
3070
- const n = num(e.target.value);
3071
- if (n != null) fire(n);
3099
+ const raw = e.target.value;
3100
+ setText(raw);
3101
+ if (raw.trim() === "") {
3102
+ setInvalidMessage(null);
3103
+ fire(void 0);
3104
+ return;
3105
+ }
3106
+ const n = num(raw);
3107
+ if (n == null) {
3108
+ setInvalidMessage("Enter a number");
3109
+ return;
3110
+ }
3111
+ const message = validationMessage(n);
3112
+ setInvalidMessage(message);
3113
+ if (!message) fire(n);
3072
3114
  },
3073
3115
  onKeyDown: (e) => {
3074
3116
  if (e.key === "ArrowUp") {
@@ -3111,9 +3153,9 @@ function NumberInput({
3111
3153
  )
3112
3154
  ] })
3113
3155
  ] }),
3114
- error ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "fd-field-error", children: [
3156
+ shownError ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { id: errorId, className: "fd-field-error", children: [
3115
3157
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
3116
- error
3158
+ shownError
3117
3159
  ] }) : help ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "fd-field-help", children: help }) : null
3118
3160
  ] });
3119
3161
  }
@@ -3335,16 +3377,48 @@ function Textarea({ label, help, error, required = false, rows = 4, disabled = f
3335
3377
  ] });
3336
3378
  }
3337
3379
 
3380
+ // src/components/forms/listbox.ts
3381
+ var React23 = __toESM(require("react"), 1);
3382
+ function useListboxNav({ count, onSelect }) {
3383
+ const [active, setActive] = React23.useState(-1);
3384
+ return {
3385
+ active,
3386
+ setActive,
3387
+ move: (dir) => setActive((a) => Math.min(count - 1, Math.max(0, a + dir))),
3388
+ first: () => setActive(0),
3389
+ last: () => setActive(count - 1),
3390
+ activate: () => {
3391
+ if (active >= 0 && active < count) onSelect(active);
3392
+ }
3393
+ };
3394
+ }
3395
+ function useListboxScrollIntoView(active, listRef, enabled) {
3396
+ React23.useEffect(() => {
3397
+ if (!enabled || active < 0 || !listRef.current) return;
3398
+ const list = listRef.current;
3399
+ const el = list.querySelector('[data-i="' + active + '"]');
3400
+ if (!el) return;
3401
+ const top = el.offsetTop - list.offsetTop;
3402
+ if (top < list.scrollTop) list.scrollTop = top - 4;
3403
+ else if (top + el.offsetHeight > list.scrollTop + list.clientHeight) {
3404
+ list.scrollTop = top + el.offsetHeight - list.clientHeight + 4;
3405
+ }
3406
+ }, [active, enabled]);
3407
+ }
3408
+
3338
3409
  // src/components/data/FilterBar.tsx
3339
3410
  var import_jsx_runtime45 = require("react/jsx-runtime");
3411
+ var PANEL_WIDTH = { multiSelect: 280, range: 240, date: 300 };
3340
3412
  var isSet = (v) => !(v == null || v === "" || v === false);
3413
+ function operatorsFor(field) {
3414
+ return field.operators && field.operators.length ? field.operators : ["in", "notIn"];
3415
+ }
3341
3416
  function isFieldSet(field, value) {
3342
3417
  switch (field.type) {
3343
3418
  case "text":
3344
3419
  case "select":
3345
3420
  return isSet(value);
3346
- case "multiSelect":
3347
- case "pasteList": {
3421
+ case "multiSelect": {
3348
3422
  const v = value;
3349
3423
  return !!v && Array.isArray(v.values) && v.values.length > 0;
3350
3424
  }
@@ -3355,7 +3429,7 @@ function isFieldSet(field, value) {
3355
3429
  case "date": {
3356
3430
  const v = value;
3357
3431
  if (!v) return false;
3358
- return v.kind === "withinDays" ? v.days != null : !!(v.from || v.to);
3432
+ return v.kind === "withinDays" ? v.days != null : !!(v.from && v.to);
3359
3433
  }
3360
3434
  }
3361
3435
  }
@@ -3367,8 +3441,7 @@ function chipLabel(field, value) {
3367
3441
  const chosen = field.options.find((o) => o.value === value);
3368
3442
  return chosen ? chosen.label : String(value ?? "");
3369
3443
  }
3370
- case "multiSelect":
3371
- case "pasteList": {
3444
+ case "multiSelect": {
3372
3445
  const v = value;
3373
3446
  if (!v || !v.values.length) return "";
3374
3447
  const prefix = v.operator === "notIn" ? "Not " : "";
@@ -3386,7 +3459,7 @@ function chipLabel(field, value) {
3386
3459
  const v = value;
3387
3460
  if (!v) return "";
3388
3461
  if (v.kind === "withinDays") return "Last " + v.days + " day" + (v.days === 1 ? "" : "s");
3389
- return (v.from || "\u2026") + " \u2192 " + (v.to || "\u2026");
3462
+ return v.from + " \u2192 " + v.to;
3390
3463
  }
3391
3464
  }
3392
3465
  }
@@ -3408,338 +3481,395 @@ function OperatorToggle({
3408
3481
  }
3409
3482
  );
3410
3483
  }
3411
- function EditPopover({ zIndex, width, children }) {
3412
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3413
- "span",
3414
- {
3415
- className: "fd-view-enter",
3416
- style: {
3417
- position: "absolute",
3418
- top: "calc(100% + 6px)",
3419
- left: 0,
3420
- zIndex,
3421
- width,
3422
- maxWidth: "calc(100vw - 32px)",
3423
- display: "block",
3424
- background: "var(--surface)",
3425
- border: "1px solid var(--border)",
3426
- borderRadius: 10,
3427
- boxShadow: "0 16px 44px rgba(11,13,17,.18)",
3428
- padding: "var(--s-8)"
3429
- },
3430
- children
3431
- }
3432
- ) });
3433
- }
3434
3484
  function MultiSelectPanel({
3435
3485
  field,
3436
3486
  value,
3487
+ anchorRef,
3437
3488
  onChange,
3489
+ onRemove,
3438
3490
  onClose
3439
3491
  }) {
3440
- const operator = value?.operator ?? "in";
3492
+ const allowedOps = operatorsFor(field);
3493
+ const [draftOperator, setDraftOperator] = React24.useState(value?.operator ?? allowedOps[0]);
3494
+ const operator = allowedOps.length === 1 ? allowedOps[0] : value?.operator ?? draftOperator;
3441
3495
  const selected = value?.values ?? [];
3442
- const [query, setQuery] = React23.useState("");
3443
- const [asyncOptions, setAsyncOptions] = React23.useState(null);
3444
- const [loading, setLoading] = React23.useState(!!field.loadOptions);
3445
- const [active, setActive] = React23.useState(0);
3446
- const inputRef = React23.useRef(null);
3447
- const listId = React23.useId();
3448
- React23.useEffect(() => {
3496
+ const selectedRef = React24.useRef(selected);
3497
+ selectedRef.current = selected;
3498
+ const commit = (nextValues, nextOperator = operator) => {
3499
+ if (nextValues.length === 0) onRemove();
3500
+ else onChange({ operator: nextOperator, values: nextValues });
3501
+ };
3502
+ const setOperator = (op) => {
3503
+ setDraftOperator(op);
3504
+ if (selected.length > 0) commit(selected, op);
3505
+ };
3506
+ const [mode2, setMode] = React24.useState("search");
3507
+ const [query, setQuery] = React24.useState("");
3508
+ const [asyncState, setAsyncState] = React24.useState(
3509
+ { options: null, loading: !!field.loadOptions, error: false }
3510
+ );
3511
+ const [retryTick, setRetryTick] = React24.useState(0);
3512
+ const inputRef = React24.useRef(null);
3513
+ const listRef = React24.useRef(null);
3514
+ const listId = React24.useId();
3515
+ const loadOptionsRef = React24.useRef(field.loadOptions);
3516
+ loadOptionsRef.current = field.loadOptions;
3517
+ React24.useEffect(() => {
3449
3518
  inputRef.current?.focus();
3450
3519
  }, []);
3451
- React23.useEffect(() => {
3452
- if (!field.loadOptions) return;
3453
- let cancelled = false;
3454
- setLoading(true);
3455
- const loadOptions = field.loadOptions;
3520
+ React24.useEffect(() => {
3521
+ const load = loadOptionsRef.current;
3522
+ if (!load) return;
3523
+ const controller = new AbortController();
3524
+ setAsyncState((s) => ({ options: s.options, loading: true, error: false }));
3456
3525
  const t = setTimeout(() => {
3457
- loadOptions(query).then((opts) => {
3458
- if (!cancelled) {
3459
- setAsyncOptions(opts);
3460
- setLoading(false);
3461
- }
3462
- }).catch(() => {
3463
- if (!cancelled) {
3464
- setAsyncOptions([]);
3465
- setLoading(false);
3466
- }
3526
+ load(query, controller.signal).then((opts) => {
3527
+ if (controller.signal.aborted) return;
3528
+ setAsyncState({ options: opts, loading: false, error: false });
3529
+ }).catch((err) => {
3530
+ if (controller.signal.aborted || err && err.name === "AbortError") return;
3531
+ setAsyncState({ options: null, loading: false, error: true });
3467
3532
  });
3468
3533
  }, 300);
3469
3534
  return () => {
3470
- cancelled = true;
3471
3535
  clearTimeout(t);
3536
+ controller.abort();
3472
3537
  };
3473
- }, [query, field.loadOptions]);
3474
- const options = field.loadOptions ? asyncOptions ?? [] : (field.options ?? []).filter((o) => o.label.toLowerCase().includes(query.toLowerCase()));
3475
- React23.useEffect(() => {
3476
- setActive(0);
3477
- }, [query, options.length]);
3538
+ }, [query, retryTick]);
3539
+ const options = field.loadOptions ? asyncState.options ?? [] : (field.options ?? []).filter((o) => o.label.toLowerCase().includes(query.toLowerCase()));
3540
+ const showLoading = !!field.loadOptions && asyncState.loading;
3541
+ const showError = !!field.loadOptions && asyncState.error;
3542
+ const hasListbox = !showLoading && !showError && options.length > 0;
3478
3543
  const isSelected = (o) => selected.some((s) => s.value === o.value);
3479
3544
  const toggle = (o) => {
3480
3545
  const next = isSelected(o) ? selected.filter((s) => s.value !== o.value) : [...selected, o];
3481
- onChange({ operator, values: next });
3546
+ commit(next);
3547
+ };
3548
+ const nav = useListboxNav({ count: options.length, onSelect: (i) => toggle(options[i]) });
3549
+ useListboxScrollIntoView(nav.active, listRef, hasListbox);
3550
+ React24.useEffect(() => {
3551
+ nav.setActive(0);
3552
+ }, [query, options.length]);
3553
+ const activeId = hasListbox && options[nav.active] ? listId + "-opt-" + nav.active : void 0;
3554
+ const liveRole = showError ? "alert" : "status";
3555
+ const liveMessage = showLoading ? "Loading\u2026" : showError ? "Couldn't load options." : options.length === 0 ? "No matches" : "";
3556
+ const [pasteText, setPasteText] = React24.useState("");
3557
+ const [pasteLoading, setPasteLoading] = React24.useState(false);
3558
+ const [unmatchedTokens, setUnmatchedTokens] = React24.useState([]);
3559
+ const [pasteError, setPasteError] = React24.useState(null);
3560
+ const pasteWrapRef = React24.useRef(null);
3561
+ React24.useEffect(() => {
3562
+ if (mode2 === "paste") pasteWrapRef.current?.querySelector("textarea")?.focus();
3563
+ }, [mode2]);
3564
+ const doResolve = () => {
3565
+ if (!field.resolve) return;
3566
+ const tokens = Array.from(new Set(pasteText.split(/[\n,]/).map((t) => t.trim()).filter(Boolean)));
3567
+ if (!tokens.length) return;
3568
+ setPasteLoading(true);
3569
+ setPasteError(null);
3570
+ field.resolve(tokens).then((result) => {
3571
+ setPasteLoading(false);
3572
+ setUnmatchedTokens(result.unmatched);
3573
+ const merged = new Map(selectedRef.current.map((s) => [s.value, s]));
3574
+ for (const m of result.matched) merged.set(m.value, { value: m.value, label: m.label });
3575
+ commit(Array.from(merged.values()));
3576
+ setPasteText("");
3577
+ }).catch((err) => {
3578
+ setPasteLoading(false);
3579
+ setPasteError(err instanceof Error ? err.message : "Could not resolve the pasted list.");
3580
+ });
3482
3581
  };
3483
- const activeId = options[active] ? listId + "-opt-" + active : void 0;
3484
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack", style: { gap: "var(--s-8)", width: 260 }, children: [
3485
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(OperatorToggle, { operator, onChange: (op) => onChange({ operator: op, values: selected }), label: field.label }),
3582
+ const header = /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
3583
+ allowedOps.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(OperatorToggle, { operator, onChange: setOperator, label: field.label }) : null,
3584
+ field.resolve ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3585
+ SegmentedControl,
3586
+ {
3587
+ "aria-label": field.label + " \u2014 search or paste a list",
3588
+ options: [{ value: "search", label: "Search" }, { value: "paste", label: "Paste a list" }],
3589
+ value: mode2,
3590
+ onChange: (m) => setMode(m)
3591
+ }
3592
+ ) : null,
3593
+ mode2 === "search" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-pop-search", children: [
3594
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("i", { className: "ph ph-magnifying-glass", "aria-hidden": "true" }),
3595
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3596
+ "input",
3597
+ {
3598
+ ref: inputRef,
3599
+ role: "combobox",
3600
+ "aria-expanded": hasListbox,
3601
+ "aria-controls": hasListbox ? listId : void 0,
3602
+ "aria-autocomplete": "list",
3603
+ "aria-activedescendant": activeId,
3604
+ "aria-label": field.label,
3605
+ value: query,
3606
+ placeholder: field.placeholder || "Search\u2026",
3607
+ onChange: (e) => setQuery(e.target.value),
3608
+ onKeyDown: (e) => {
3609
+ if (e.key === "ArrowDown") {
3610
+ e.preventDefault();
3611
+ nav.move(1);
3612
+ } else if (e.key === "ArrowUp") {
3613
+ e.preventDefault();
3614
+ nav.move(-1);
3615
+ } else if (e.key === "Home") {
3616
+ e.preventDefault();
3617
+ nav.first();
3618
+ } else if (e.key === "End") {
3619
+ e.preventDefault();
3620
+ nav.last();
3621
+ } else if (e.key === "Enter") {
3622
+ e.preventDefault();
3623
+ nav.activate();
3624
+ }
3625
+ }
3626
+ }
3627
+ )
3628
+ ] }) : null
3629
+ ] });
3630
+ const body = mode2 === "paste" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", ref: pasteWrapRef, children: [
3486
3631
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3487
- "input",
3632
+ Textarea,
3488
3633
  {
3489
- ref: inputRef,
3490
- role: "combobox",
3491
- "aria-expanded": "true",
3492
- "aria-controls": listId,
3493
- "aria-autocomplete": "list",
3494
- "aria-activedescendant": activeId,
3495
- "aria-label": field.label,
3496
- className: "fd-input",
3497
- style: { width: "100%", padding: "6px 10px" },
3498
- value: query,
3499
- placeholder: field.placeholder || "Search\u2026",
3500
- onChange: (e) => setQuery(e.target.value),
3634
+ "aria-label": field.label + " \u2014 paste a list",
3635
+ rows: 4,
3636
+ placeholder: field.pastePlaceholder || field.placeholder || "Paste names, one per line or comma-separated",
3637
+ value: pasteText,
3638
+ disabled: pasteLoading,
3639
+ onChange: (e) => setPasteText(e.target.value),
3501
3640
  onKeyDown: (e) => {
3502
- if (e.key === "ArrowDown") {
3503
- e.preventDefault();
3504
- setActive((a) => Math.min(a + 1, options.length - 1));
3505
- } else if (e.key === "ArrowUp") {
3506
- e.preventDefault();
3507
- setActive((a) => Math.max(a - 1, 0));
3508
- } else if (e.key === "Enter") {
3641
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
3509
3642
  e.preventDefault();
3510
- if (options[active]) toggle(options[active]);
3511
- } else if (e.key === "Escape") {
3512
- e.stopPropagation();
3513
- onClose();
3643
+ doResolve();
3514
3644
  }
3515
3645
  }
3516
3646
  }
3517
3647
  ),
3518
- loading ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: "var(--s-4)" }, children: [
3519
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-spinner", "aria-hidden": "true" }),
3520
- " Loading\u2026"
3521
- ] }) : options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-body-sm fd-muted", children: "No matches" }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3648
+ pasteError ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Banner, { tone: "danger", title: "Couldn't resolve the list", detail: pasteError }) : null,
3649
+ unmatchedTokens.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Banner, { tone: "warn", title: unmatchedTokens.length + " didn't match", detail: unmatchedTokens.join(", ") }) : null,
3650
+ selected.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-xs", children: [
3651
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
3652
+ selected.length,
3653
+ " matched"
3654
+ ] }),
3655
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-row fd-wrap fd-gap-xs", children: selected.map((v) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Tag, { onRemove: () => commit(selected.filter((s) => s.value !== v.value)), children: v.label }, v.value)) })
3656
+ ] }) : null
3657
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
3658
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
3522
3659
  "span",
3523
3660
  {
3524
- id: listId,
3525
- role: "listbox",
3526
- "aria-label": field.label,
3527
- "aria-multiselectable": "true",
3528
- className: "fd-stack",
3529
- style: { gap: 2, maxHeight: 220, overflowY: "auto" },
3530
- children: options.map((o, i) => {
3531
- const on = isSelected(o);
3532
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
3533
- "button",
3534
- {
3535
- type: "button",
3536
- id: listId + "-opt-" + i,
3537
- role: "option",
3538
- "aria-selected": on,
3539
- className: "fd-row",
3540
- onMouseEnter: () => setActive(i),
3541
- onClick: () => toggle(o),
3542
- style: {
3543
- gap: "var(--s-8)",
3544
- padding: "4px 8px",
3545
- borderRadius: 7,
3546
- border: 0,
3547
- width: "100%",
3548
- textAlign: "left",
3549
- cursor: "pointer",
3550
- background: i === active ? "var(--surface-brand)" : "transparent"
3551
- },
3552
- children: [
3553
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("i", { className: on ? "ph ph-check-square" : "ph ph-square", "aria-hidden": "true" }),
3554
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-body-sm", style: { flex: 1, color: "var(--text)" }, children: o.label })
3555
- ]
3556
- },
3557
- o.value
3558
- );
3559
- })
3661
+ className: "fd-row fd-gap-xs" + (liveMessage && !showError ? " fd-muted" : ""),
3662
+ role: liveRole,
3663
+ children: [
3664
+ showLoading ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-spinner", "aria-hidden": "true" }) : null,
3665
+ liveMessage ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-body-sm", children: liveMessage }) : null
3666
+ ]
3560
3667
  }
3561
3668
  ),
3562
- selected.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
3563
- selected.length,
3564
- " selected"
3565
- ] }) : null
3669
+ showError ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: () => setRetryTick((n) => n + 1), children: "Retry" }) : null,
3670
+ hasListbox ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { id: listId, role: "listbox", "aria-label": field.label, "aria-multiselectable": "true", ref: listRef, className: "fd-pop-list", children: options.map((o, i) => {
3671
+ const on = isSelected(o);
3672
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
3673
+ "div",
3674
+ {
3675
+ id: listId + "-opt-" + i,
3676
+ "data-i": i,
3677
+ role: "option",
3678
+ tabIndex: -1,
3679
+ "aria-selected": on,
3680
+ className: ["fd-opt", i === nav.active ? "is-active" : "", on ? "is-selected" : ""].filter(Boolean).join(" "),
3681
+ onMouseEnter: () => nav.setActive(i),
3682
+ onClick: () => toggle(o),
3683
+ children: [
3684
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("i", { className: "fd-filterbar-opt-check ph " + (on ? "ph-check-square is-checked" : "ph-square"), "aria-hidden": "true" }),
3685
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-opt-label", children: o.label })
3686
+ ]
3687
+ },
3688
+ o.value
3689
+ );
3690
+ }) }) : null
3566
3691
  ] });
3692
+ const footer = mode2 === "paste" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: doResolve, disabled: pasteLoading || !pasteText.trim(), children: pasteLoading ? "Resolving\u2026" : "Resolve" }) : void 0;
3693
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3694
+ Popover,
3695
+ {
3696
+ open: true,
3697
+ anchorRef,
3698
+ onClose,
3699
+ placement: "bottom-start",
3700
+ offset: 6,
3701
+ width: PANEL_WIDTH.multiSelect,
3702
+ label: field.label,
3703
+ header,
3704
+ footer,
3705
+ children: body
3706
+ }
3707
+ );
3567
3708
  }
3568
3709
  function RangePanel({
3569
3710
  field,
3570
3711
  value,
3712
+ anchorRef,
3571
3713
  onChange,
3714
+ onRemove,
3572
3715
  onClose
3573
3716
  }) {
3574
- const min = value?.min;
3575
- const max = value?.max;
3576
- const rangeError = min != null && max != null && min > max ? "Must be \u2265 " + (field.minLabel || "min").toLowerCase() : void 0;
3577
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack", style: { gap: "var(--s-8)", width: 240 }, children: [
3578
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3579
- NumberInput,
3580
- {
3581
- label: field.minLabel || "At least",
3582
- value: min ?? "",
3583
- min: field.min,
3584
- max: field.max,
3585
- step: field.step,
3586
- placeholder: field.minPlaceholder,
3587
- onChange: (e) => onChange({ min: e.target.value, max })
3588
- }
3589
- ),
3590
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3591
- NumberInput,
3592
- {
3593
- label: field.maxLabel || "At most",
3594
- value: max ?? "",
3595
- min: field.min,
3596
- max: field.max,
3597
- step: field.step,
3598
- error: rangeError,
3599
- placeholder: field.maxPlaceholder,
3600
- onChange: (e) => onChange({ min, max: e.target.value })
3601
- }
3602
- ),
3603
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: onClose, children: "Done" })
3604
- ] });
3717
+ const [draftMin, setDraftMin] = React24.useState(value?.min);
3718
+ const [draftMax, setDraftMax] = React24.useState(value?.max);
3719
+ const invalid = draftMin != null && draftMax != null && draftMin > draftMax;
3720
+ const apply = (nextMin, nextMax) => {
3721
+ setDraftMin(nextMin);
3722
+ setDraftMax(nextMax);
3723
+ if (nextMin == null && nextMax == null) {
3724
+ onRemove();
3725
+ return;
3726
+ }
3727
+ if (nextMin != null && nextMax != null && nextMin > nextMax) return;
3728
+ onChange({ min: nextMin, max: nextMax });
3729
+ };
3730
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3731
+ Popover,
3732
+ {
3733
+ open: true,
3734
+ anchorRef,
3735
+ onClose,
3736
+ placement: "bottom-start",
3737
+ offset: 6,
3738
+ width: PANEL_WIDTH.range,
3739
+ label: field.label,
3740
+ footer: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: onClose, disabled: invalid, children: "Done" }),
3741
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
3742
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3743
+ NumberInput,
3744
+ {
3745
+ label: field.minLabel || "At least",
3746
+ value: draftMin ?? "",
3747
+ min: field.min,
3748
+ max: field.max,
3749
+ step: field.step,
3750
+ placeholder: field.minPlaceholder,
3751
+ onChange: (e) => apply(e.target.value, draftMax)
3752
+ }
3753
+ ),
3754
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3755
+ NumberInput,
3756
+ {
3757
+ label: field.maxLabel || "At most",
3758
+ value: draftMax ?? "",
3759
+ min: field.min,
3760
+ max: field.max,
3761
+ step: field.step,
3762
+ error: invalid ? "Must be \u2265 " + draftMin : void 0,
3763
+ placeholder: field.maxPlaceholder,
3764
+ onChange: (e) => apply(draftMin, e.target.value)
3765
+ }
3766
+ )
3767
+ ] })
3768
+ }
3769
+ );
3605
3770
  }
3606
3771
  function DatePanel({
3607
3772
  field,
3608
3773
  value,
3774
+ anchorRef,
3609
3775
  onChange,
3776
+ onRemove,
3610
3777
  onClose
3611
3778
  }) {
3612
- const kind = value?.kind ?? "withinDays";
3613
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack", style: { gap: "var(--s-8)", width: 300 }, children: [
3614
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3615
- SegmentedControl,
3616
- {
3617
- "aria-label": field.label + " \u2014 relative or between dates",
3618
- options: [
3619
- { value: "withinDays", label: "Within last N days" },
3620
- { value: "between", label: "Between dates" }
3621
- ],
3622
- value: kind,
3623
- onChange: (k) => {
3624
- if (k === "withinDays") {
3625
- onChange({ kind: "withinDays", days: value?.kind === "withinDays" ? value.days : field.defaultDays ?? 30 });
3626
- } else {
3627
- onChange({ kind: "between", from: value?.kind === "between" ? value.from : "", to: value?.kind === "between" ? value.to : "" });
3628
- }
3629
- }
3630
- }
3631
- ),
3632
- kind === "withinDays" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3633
- NumberInput,
3634
- {
3635
- label: "Days",
3636
- min: 1,
3637
- value: value?.kind === "withinDays" ? value.days : field.defaultDays ?? 30,
3638
- onChange: (e) => onChange({ kind: "withinDays", days: e.target.value })
3639
- }
3640
- ) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3641
- DatePicker,
3642
- {
3643
- range: true,
3644
- label: "Date range",
3645
- value: value?.kind === "between" ? { start: value.from, end: value.to } : void 0,
3646
- onChange: (e) => onChange({ kind: "between", from: e.target.value.start || "", to: e.target.value.end || "" })
3779
+ const defaultDays = field.defaultDays ?? 30;
3780
+ const maxDays = field.maxDays ?? 3650;
3781
+ const [kind, setKind] = React24.useState(value?.kind ?? "withinDays");
3782
+ const [daysDraft, setDaysDraft] = React24.useState(
3783
+ value?.kind === "withinDays" ? value.days : defaultDays
3784
+ );
3785
+ const [between, setBetween] = React24.useState(
3786
+ value?.kind === "between" ? { from: value.from, to: value.to } : { from: "", to: "" }
3787
+ );
3788
+ const committedRef = React24.useRef(!!value);
3789
+ const applyWithinDays = (days) => {
3790
+ setDaysDraft(days);
3791
+ if (days != null) {
3792
+ committedRef.current = true;
3793
+ onChange({ kind: "withinDays", days });
3794
+ } else if (committedRef.current) {
3795
+ committedRef.current = false;
3796
+ onRemove();
3797
+ }
3798
+ };
3799
+ const applyBetween = (from, to) => {
3800
+ setBetween({ from, to });
3801
+ if (from && to) {
3802
+ committedRef.current = true;
3803
+ onChange({ kind: "between", from, to });
3804
+ } else if (committedRef.current) {
3805
+ committedRef.current = false;
3806
+ onRemove();
3807
+ }
3808
+ };
3809
+ const switchKind = (next) => {
3810
+ setKind(next);
3811
+ if (next === "between") {
3812
+ setBetween({ from: "", to: "" });
3813
+ if (committedRef.current) {
3814
+ committedRef.current = false;
3815
+ onRemove();
3647
3816
  }
3648
- ),
3649
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: onClose, children: "Done" })
3650
- ] });
3651
- }
3652
- function PasteListPanel({
3653
- field,
3654
- value,
3655
- onChange,
3656
- onClose
3657
- }) {
3658
- const operator = value?.operator ?? "in";
3659
- const selected = value?.values ?? [];
3660
- const [text, setText] = React23.useState("");
3661
- const [loading, setLoading] = React23.useState(false);
3662
- const [unmatched, setUnmatched] = React23.useState([]);
3663
- const [error, setError] = React23.useState(null);
3664
- const wrapRef = React23.useRef(null);
3665
- React23.useEffect(() => {
3666
- wrapRef.current?.querySelector("textarea")?.focus();
3667
- }, []);
3668
- const doResolve = () => {
3669
- const tokens = text.split(/[\n,]/).map((t) => t.trim()).filter(Boolean);
3670
- if (!tokens.length) return;
3671
- setLoading(true);
3672
- setError(null);
3673
- field.resolve(tokens).then((result) => {
3674
- setLoading(false);
3675
- setUnmatched(result.unmatched);
3676
- const merged = new Map(selected.map((s) => [s.value, s]));
3677
- for (const m of result.matched) merged.set(m.value, { value: m.value, label: m.label });
3678
- onChange({ operator, values: Array.from(merged.values()) });
3679
- setText("");
3680
- }).catch((err) => {
3681
- setLoading(false);
3682
- setError(err instanceof Error ? err.message : "Could not resolve the pasted list.");
3683
- });
3817
+ }
3684
3818
  };
3685
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack", style: { gap: "var(--s-8)", width: 320 }, children: [
3686
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(OperatorToggle, { operator, onChange: (op) => onChange({ operator: op, values: selected }), label: field.label }),
3687
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: wrapRef, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3688
- Textarea,
3689
- {
3690
- "aria-label": field.label + " \u2014 paste a list",
3691
- rows: 4,
3692
- placeholder: field.placeholder || "Paste names, one per line or comma-separated",
3693
- value: text,
3694
- disabled: loading,
3695
- onChange: (e) => setText(e.target.value),
3696
- onKeyDown: (e) => {
3697
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
3698
- e.preventDefault();
3699
- doResolve();
3819
+ const confirm = () => {
3820
+ if (kind === "withinDays" && daysDraft != null) {
3821
+ committedRef.current = true;
3822
+ onChange({ kind: "withinDays", days: daysDraft });
3823
+ }
3824
+ onClose();
3825
+ };
3826
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3827
+ Popover,
3828
+ {
3829
+ open: true,
3830
+ anchorRef,
3831
+ onClose,
3832
+ placement: "bottom-start",
3833
+ offset: 6,
3834
+ width: PANEL_WIDTH.date,
3835
+ label: field.label,
3836
+ footer: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: confirm, children: "Done" }),
3837
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
3838
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3839
+ SegmentedControl,
3840
+ {
3841
+ "aria-label": field.label + " \u2014 relative or between dates",
3842
+ options: [
3843
+ { value: "withinDays", label: "Within last N days" },
3844
+ { value: "between", label: "Between dates" }
3845
+ ],
3846
+ value: kind,
3847
+ onChange: (k) => switchKind(k)
3700
3848
  }
3701
- }
3702
- }
3703
- ) }),
3704
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: doResolve, disabled: loading || !text.trim(), children: loading ? "Resolving\u2026" : "Resolve" }),
3705
- error ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Banner, { tone: "danger", title: "Couldn't resolve the list", detail: error }) : null,
3706
- unmatched.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Banner, { tone: "warn", title: unmatched.length + " didn't match", detail: unmatched.join(", ") }) : null,
3707
- selected.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack", style: { gap: "var(--s-4)" }, children: [
3708
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
3709
- selected.length,
3710
- " matched"
3711
- ] }),
3712
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-row", style: { flexWrap: "wrap", gap: "var(--s-4)" }, children: selected.map((v) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Tag, { onRemove: () => onChange({ operator, values: selected.filter((s) => s.value !== v.value) }), children: v.label }, v.value)) })
3713
- ] }) : null,
3714
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: onClose, children: "Done" })
3715
- ] });
3849
+ ),
3850
+ kind === "withinDays" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(NumberInput, { label: "Days", min: 1, max: maxDays, step: 1, value: daysDraft ?? "", onChange: (e) => applyWithinDays(e.target.value) }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3851
+ DatePicker,
3852
+ {
3853
+ range: true,
3854
+ label: "Date range",
3855
+ value: { start: between.from, end: between.to },
3856
+ onChange: (e) => applyBetween(e.target.value.start || "", e.target.value.end || "")
3857
+ }
3858
+ )
3859
+ ] })
3860
+ }
3861
+ );
3716
3862
  }
3717
3863
  function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align = "left", className = "", ...rest }) {
3718
- const [panel, setPanel] = React23.useState(null);
3719
- const zIndex = useOverlayLayer(!!panel);
3720
- const rootRef = React23.useRef(null);
3721
- const textInputRef = React23.useRef(null);
3722
- const optionsRef = React23.useRef(null);
3864
+ const [panel, setPanel] = React24.useState(null);
3865
+ const editAnchorRef = React24.useRef(null);
3866
+ const addAnchorRef = React24.useRef(null);
3867
+ const textInputRef = React24.useRef(null);
3868
+ const optionsRef = React24.useRef(null);
3723
3869
  const editingField = panel && panel.kind === "edit" ? fields.find((f) => f.key === panel.key) : void 0;
3724
- const dismissible = !!panel && (panel.kind === "add" || panel.kind === "edit" && editingField?.type !== "text");
3725
- React23.useEffect(() => {
3726
- if (!dismissible) return;
3727
- const away = (e) => {
3728
- const target = e.target;
3729
- if (target.closest && target.closest(".fd-pop")) return;
3730
- if (rootRef.current && !rootRef.current.contains(e.target)) setPanel(null);
3731
- };
3732
- const esc = (e) => {
3733
- if (e.key === "Escape") setPanel(null);
3734
- };
3735
- document.addEventListener("pointerdown", away);
3736
- document.addEventListener("keydown", esc);
3737
- return () => {
3738
- document.removeEventListener("pointerdown", away);
3739
- document.removeEventListener("keydown", esc);
3740
- };
3741
- }, [dismissible]);
3742
- React23.useEffect(() => {
3870
+ const startEditing = (key) => setPanel({ kind: "edit", key });
3871
+ const closeEditing = () => setPanel(null);
3872
+ React24.useEffect(() => {
3743
3873
  if (!panel || panel.kind !== "edit") return;
3744
3874
  if (editingField?.type === "text") {
3745
3875
  textInputRef.current?.focus();
@@ -3749,72 +3879,77 @@ function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align
3749
3879
  }
3750
3880
  }, [panel]);
3751
3881
  const addableFields = fields.filter((f) => !isFieldSet(f, filters[f.key]));
3752
- const startEditing = (key) => setPanel({ kind: "edit", key });
3753
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className, style: { position: "relative", display: "inline-flex", flexWrap: "wrap", alignItems: "center", gap: 8 }, ref: rootRef, ...rest, children: [
3882
+ const addMenuItems = [
3883
+ { kind: "section", label: "Add filter" },
3884
+ ...addableFields.map((f) => ({ id: f.key, label: f.label, onSelect: () => startEditing(f.key) }))
3885
+ ];
3886
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: ["fd-filterbar", className].filter(Boolean).join(" "), ...rest, children: [
3754
3887
  fields.map((field) => {
3755
3888
  const value = filters[field.key];
3756
3889
  const editing = panel?.kind === "edit" && panel.key === field.key;
3757
3890
  if (editing && field.type === "text") {
3758
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
3759
- "span",
3760
- {
3761
- className: "fd-tag",
3762
- style: { background: "var(--surface-brand)", borderColor: "var(--border-brand)", cursor: "text" },
3763
- children: [
3764
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3765
- "input",
3766
- {
3767
- ref: textInputRef,
3768
- value: value ?? "",
3769
- placeholder: field.placeholder || field.label,
3770
- "aria-label": field.label,
3771
- onChange: (e) => onFilterChange(field.key, e.target.value),
3772
- onKeyDown: (e) => {
3773
- if (e.key === "Enter") setPanel(null);
3774
- else if (e.key === "Escape") {
3775
- e.stopPropagation();
3776
- setPanel(null);
3777
- }
3778
- },
3779
- onBlur: () => setPanel(null),
3780
- style: { all: "unset", minWidth: 90, color: "var(--text-on-surface-brand)" }
3781
- }
3782
- ),
3783
- isSet(value) ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3784
- "span",
3785
- {
3786
- className: "fd-tag-remove",
3787
- role: "button",
3788
- "aria-label": "Remove " + field.label,
3789
- onClick: (e) => {
3790
- e.stopPropagation();
3791
- onFilterRemove(field.key);
3792
- setPanel(null);
3793
- },
3794
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("i", { className: "ph ph-x", "aria-hidden": "true" })
3891
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: ["fd-tag", "fd-filterbar-text"].join(" "), children: [
3892
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3893
+ "input",
3894
+ {
3895
+ ref: textInputRef,
3896
+ className: "fd-filterbar-text-input",
3897
+ value: value ?? "",
3898
+ placeholder: field.placeholder || field.label,
3899
+ "aria-label": field.label,
3900
+ onChange: (e) => onFilterChange(field.key, e.target.value),
3901
+ onKeyDown: (e) => {
3902
+ if (e.key === "Enter") setPanel(null);
3903
+ else if (e.key === "Escape") {
3904
+ e.stopPropagation();
3905
+ setPanel(null);
3795
3906
  }
3796
- ) : null
3797
- ]
3798
- },
3799
- field.key
3800
- );
3801
- }
3802
- if (editing && field.type === "select") {
3803
- const chosen = field.options.find((o) => o.value === value);
3804
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, children: [
3805
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Tag, { selected: true, onClick: () => setPanel(null), onRemove: () => {
3806
- if (isSet(value)) onFilterRemove(field.key);
3807
- setPanel(null);
3808
- }, children: [
3809
- field.label,
3810
- chosen ? ": " + chosen.label : ""
3811
- ] }),
3812
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3907
+ },
3908
+ onBlur: () => setPanel(null)
3909
+ }
3910
+ ),
3911
+ isSet(value) ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3813
3912
  "span",
3814
3913
  {
3815
- className: "fd-view-enter",
3816
- style: { position: "absolute", top: "calc(100% + 6px)", left: 0, zIndex, minWidth: 200, maxWidth: "calc(100vw - 32px)", display: "block", background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 10, boxShadow: "0 16px 44px rgba(11,13,17,.18)", padding: 8 },
3817
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-stack", role: "listbox", "aria-label": field.label, ref: optionsRef, style: { gap: 2 }, children: field.options.map((o) => {
3914
+ className: "fd-tag-remove",
3915
+ role: "button",
3916
+ "aria-label": "Remove " + field.label,
3917
+ onClick: (e) => {
3918
+ e.stopPropagation();
3919
+ onFilterRemove(field.key);
3920
+ setPanel(null);
3921
+ },
3922
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("i", { className: "ph ph-x", "aria-hidden": "true" })
3923
+ }
3924
+ ) : null
3925
+ ] }, field.key);
3926
+ }
3927
+ const set = isFieldSet(field, value);
3928
+ if (!editing && !set) return null;
3929
+ const chipRef = editing ? editAnchorRef : void 0;
3930
+ const toggleOrClose = () => editing ? closeEditing() : startEditing(field.key);
3931
+ const remove = () => {
3932
+ if (set) onFilterRemove(field.key);
3933
+ closeEditing();
3934
+ };
3935
+ const chipText = field.label + (set ? ": " + chipLabel(field, value) : "");
3936
+ if (field.type === "text") {
3937
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }, field.key);
3938
+ }
3939
+ if (field.type === "select") {
3940
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { children: [
3941
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: chipRef, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }) }),
3942
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3943
+ Popover,
3944
+ {
3945
+ open: true,
3946
+ anchorRef: editAnchorRef,
3947
+ onClose: closeEditing,
3948
+ placement: align === "right" ? "bottom-end" : "bottom-start",
3949
+ offset: 6,
3950
+ minWidth: 200,
3951
+ label: field.label,
3952
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { role: "listbox", "aria-label": field.label, ref: optionsRef, className: "fd-pop-list", children: field.options.map((o) => {
3818
3953
  const on = o.value === value;
3819
3954
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3820
3955
  "button",
@@ -3822,84 +3957,72 @@ function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align
3822
3957
  type: "button",
3823
3958
  role: "option",
3824
3959
  "aria-selected": on,
3825
- className: "fd-row",
3960
+ className: ["fd-opt", on ? "is-selected" : ""].filter(Boolean).join(" "),
3826
3961
  onClick: () => {
3827
3962
  onFilterChange(field.key, o.value);
3828
3963
  setPanel(null);
3829
3964
  },
3830
- style: { gap: 8, padding: "4px 8px", borderRadius: 7, border: 0, width: "100%", textAlign: "left", cursor: "pointer", background: on ? "var(--surface-brand)" : "transparent" },
3831
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-body-sm", style: { flex: 1, fontWeight: on ? 700 : 500, color: on ? "var(--text-on-surface-brand)" : "var(--text)" }, children: o.label })
3965
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-opt-label", children: o.label })
3832
3966
  },
3833
3967
  o.value
3834
3968
  );
3835
3969
  }) })
3836
3970
  }
3837
- ) })
3838
- ] }, field.key);
3839
- }
3840
- if (editing && field.type === "multiSelect") {
3841
- const mv = value;
3842
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, children: [
3843
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Tag, { selected: true, onClick: () => setPanel(null), onRemove: () => {
3844
- if (isFieldSet(field, value)) onFilterRemove(field.key);
3845
- setPanel(null);
3846
- }, children: [
3847
- field.label,
3848
- isFieldSet(field, value) ? ": " + chipLabel(field, value) : ""
3849
- ] }),
3850
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(EditPopover, { zIndex, width: 260, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(MultiSelectPanel, { field, value: mv, onChange: (v) => onFilterChange(field.key, v), onClose: () => setPanel(null) }) })
3851
- ] }, field.key);
3852
- }
3853
- if (editing && field.type === "range") {
3854
- const rv = value;
3855
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, children: [
3856
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Tag, { selected: true, onClick: () => setPanel(null), onRemove: () => {
3857
- if (isFieldSet(field, value)) onFilterRemove(field.key);
3858
- setPanel(null);
3859
- }, children: [
3860
- field.label,
3861
- isFieldSet(field, value) ? ": " + chipLabel(field, value) : ""
3862
- ] }),
3863
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(EditPopover, { zIndex, width: 240, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(RangePanel, { field, value: rv, onChange: (v) => onFilterChange(field.key, v), onClose: () => setPanel(null) }) })
3971
+ ) : null
3864
3972
  ] }, field.key);
3865
3973
  }
3866
- if (editing && field.type === "date") {
3867
- const dv = value;
3868
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, children: [
3869
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Tag, { selected: true, onClick: () => setPanel(null), onRemove: () => {
3870
- if (isFieldSet(field, value)) onFilterRemove(field.key);
3871
- setPanel(null);
3872
- }, children: [
3873
- field.label,
3874
- isFieldSet(field, value) ? ": " + chipLabel(field, value) : ""
3875
- ] }),
3876
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(EditPopover, { zIndex, width: 300, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(DatePanel, { field, value: dv, onChange: (v) => onFilterChange(field.key, v), onClose: () => setPanel(null) }) })
3974
+ if (field.type === "multiSelect") {
3975
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { children: [
3976
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: chipRef, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }) }),
3977
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3978
+ MultiSelectPanel,
3979
+ {
3980
+ field,
3981
+ value,
3982
+ anchorRef: editAnchorRef,
3983
+ onChange: (v) => onFilterChange(field.key, v),
3984
+ onRemove: () => onFilterRemove(field.key),
3985
+ onClose: closeEditing
3986
+ }
3987
+ ) : null
3877
3988
  ] }, field.key);
3878
3989
  }
3879
- if (editing && field.type === "pasteList") {
3880
- const pv = value;
3881
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, children: [
3882
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Tag, { selected: true, onClick: () => setPanel(null), onRemove: () => {
3883
- if (isFieldSet(field, value)) onFilterRemove(field.key);
3884
- setPanel(null);
3885
- }, children: [
3886
- field.label,
3887
- isFieldSet(field, value) ? ": " + chipLabel(field, value) : ""
3888
- ] }),
3889
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(EditPopover, { zIndex, width: 320, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(PasteListPanel, { field, value: pv, onChange: (v) => onFilterChange(field.key, v), onClose: () => setPanel(null) }) })
3990
+ if (field.type === "range") {
3991
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { children: [
3992
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: chipRef, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }) }),
3993
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3994
+ RangePanel,
3995
+ {
3996
+ field,
3997
+ value,
3998
+ anchorRef: editAnchorRef,
3999
+ onChange: (v) => onFilterChange(field.key, v),
4000
+ onRemove: () => onFilterRemove(field.key),
4001
+ onClose: closeEditing
4002
+ }
4003
+ ) : null
3890
4004
  ] }, field.key);
3891
4005
  }
3892
- if (isFieldSet(field, value)) {
3893
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Tag, { selected: true, onClick: () => startEditing(field.key), onRemove: () => onFilterRemove(field.key), children: [
3894
- field.label,
3895
- ": ",
3896
- chipLabel(field, value)
4006
+ if (field.type === "date") {
4007
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { children: [
4008
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: chipRef, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }) }),
4009
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4010
+ DatePanel,
4011
+ {
4012
+ field,
4013
+ value,
4014
+ anchorRef: editAnchorRef,
4015
+ onChange: (v) => onFilterChange(field.key, v),
4016
+ onRemove: () => onFilterRemove(field.key),
4017
+ onClose: closeEditing
4018
+ }
4019
+ ) : null
3897
4020
  ] }, field.key);
3898
4021
  }
3899
4022
  return null;
3900
4023
  }),
3901
- addableFields.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, children: [
3902
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4024
+ addableFields.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { children: [
4025
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: addAnchorRef, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3903
4026
  Button,
3904
4027
  {
3905
4028
  size: "sm",
@@ -3910,29 +4033,20 @@ function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align
3910
4033
  onClick: () => setPanel(panel?.kind === "add" ? null : { kind: "add" }),
3911
4034
  children: "Filter"
3912
4035
  }
3913
- ),
3914
- panel?.kind === "add" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3915
- "span",
4036
+ ) }),
4037
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4038
+ Popover,
3916
4039
  {
3917
- className: "fd-view-enter",
3918
- style: { position: "absolute", top: "calc(100% + 6px)", [align]: 0, zIndex, width: 220, maxWidth: "calc(100vw - 32px)", display: "block", background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 10, boxShadow: "0 16px 44px rgba(11,13,17,.18)", padding: 8 },
3919
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack", role: "menu", "aria-label": "Add filter", style: { gap: 2 }, children: [
3920
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-overline fd-muted", style: { padding: "4px 8px 6px" }, children: "Add filter" }),
3921
- addableFields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3922
- "button",
3923
- {
3924
- type: "button",
3925
- role: "menuitem",
3926
- className: "fd-row",
3927
- onClick: () => startEditing(f.key),
3928
- style: { gap: 8, padding: "4px 8px", borderRadius: 7, border: 0, width: "100%", textAlign: "left", cursor: "pointer", background: "transparent" },
3929
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-body-sm", style: { flex: 1, color: "var(--text)" }, children: f.label })
3930
- },
3931
- f.key
3932
- ))
3933
- ] })
4040
+ open: panel?.kind === "add",
4041
+ anchorRef: addAnchorRef,
4042
+ onClose: closeEditing,
4043
+ placement: align === "right" ? "bottom-end" : "bottom-start",
4044
+ minWidth: 200,
4045
+ role: "menu",
4046
+ label: "Add filter",
4047
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Menu, { items: addMenuItems })
3934
4048
  }
3935
- ) }) : null
4049
+ )
3936
4050
  ] }) : null
3937
4051
  ] });
3938
4052
  }
@@ -3964,7 +4078,7 @@ function StatTile({ label, value, sub, delta, invert = false, loading = false, c
3964
4078
  }
3965
4079
 
3966
4080
  // src/components/data/CodeBlock.tsx
3967
- var React24 = __toESM(require("react"), 1);
4081
+ var React25 = __toESM(require("react"), 1);
3968
4082
  var import_jsx_runtime47 = require("react/jsx-runtime");
3969
4083
  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)$/;
3970
4084
  var RULES = {
@@ -4120,13 +4234,13 @@ function tokenize(src, lang) {
4120
4234
  return out;
4121
4235
  }
4122
4236
  function Highlighted({ code, lang }) {
4123
- const toks = React24.useMemo(() => tokenize(code, lang), [code, lang]);
4237
+ const toks = React25.useMemo(() => tokenize(code, lang), [code, lang]);
4124
4238
  return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_jsx_runtime47.Fragment, { children: toks.map((tk, i) => tk.c ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "tok-" + tk.c, children: tk.t }, i) : tk.t) });
4125
4239
  }
4126
4240
  function useCopy() {
4127
- const [done, setDone] = React24.useState(false);
4128
- const timer = React24.useRef(null);
4129
- React24.useEffect(() => () => {
4241
+ const [done, setDone] = React25.useState(false);
4242
+ const timer = React25.useRef(null);
4243
+ React25.useEffect(() => () => {
4130
4244
  if (timer.current) clearTimeout(timer.current);
4131
4245
  }, []);
4132
4246
  const copy = (text) => {
@@ -4162,8 +4276,8 @@ function CodeBlock({
4162
4276
  actions,
4163
4277
  className = ""
4164
4278
  }) {
4165
- const [wrap, setWrap] = React24.useState(!!wrapDefault);
4166
- const [open, setOpen] = React24.useState(false);
4279
+ const [wrap, setWrap] = React25.useState(!!wrapDefault);
4280
+ const [open, setOpen] = React25.useState(false);
4167
4281
  const [copied, copy] = useCopy();
4168
4282
  const body = String(code).replace(/\n$/, "");
4169
4283
  const lines = body.split("\n");
@@ -4192,7 +4306,7 @@ function CodeBlock({
4192
4306
  ] });
4193
4307
  }
4194
4308
  function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" }) {
4195
- const [open, setOpen] = React24.useState(false);
4309
+ const [open, setOpen] = React25.useState(false);
4196
4310
  const [copied, copy] = useCopy();
4197
4311
  const raw = String(diff).replace(/\n$/, "");
4198
4312
  const all = raw.split("\n").map((line) => {
@@ -4238,7 +4352,7 @@ function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" })
4238
4352
  }
4239
4353
 
4240
4354
  // src/components/data/Markdown.tsx
4241
- var React25 = __toESM(require("react"), 1);
4355
+ var React26 = __toESM(require("react"), 1);
4242
4356
  var import_jsx_runtime48 = require("react/jsx-runtime");
4243
4357
  var SAFE = /^(?:https?:|mailto:|tel:|#|\/|\.{1,2}\/)/i;
4244
4358
  var safeHref = (h) => SAFE.test(String(h || "").trim()) ? String(h).trim() : null;
@@ -4277,7 +4391,7 @@ function inlineNodes(src, ctx, keyBase) {
4277
4391
  const href = safeHref(g(2));
4278
4392
  out.push(href ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("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__ */ (0, import_jsx_runtime48.jsx)("span", { children: g(1) }, key()));
4279
4393
  } else if (kind === "cite") {
4280
- out.push(ctx.renderCitation ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(React25.Fragment, { children: ctx.renderCitation(g(1)) }, key()) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("sup", { className: "fd-md-cite", children: g(1) }, key()));
4394
+ out.push(ctx.renderCitation ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(React26.Fragment, { children: ctx.renderCitation(g(1)) }, key()) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("sup", { className: "fd-md-cite", children: g(1) }, key()));
4281
4395
  } else if (kind === "strongem") out.push(/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("strong", { children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("em", { children: inlineNodes(g(1), ctx, key()) }) }, key()));
4282
4396
  else if (kind === "strong") out.push(/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("strong", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
4283
4397
  else if (kind === "em") out.push(/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("em", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
@@ -4442,7 +4556,7 @@ function Markdown({
4442
4556
  onLinkClick
4443
4557
  }) {
4444
4558
  const ctx = { allowImages, headingOffset, renderCitation, onLinkClick, codeProps };
4445
- const blocks = React25.useMemo(() => parseBlocks(source), [source]);
4559
+ const blocks = React26.useMemo(() => parseBlocks(source), [source]);
4446
4560
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: ["fd-md", className].filter(Boolean).join(" "), children: renderBlocks(blocks, ctx, "b") });
4447
4561
  }
4448
4562
  function MarkdownInline({ source = "", className = "", onLinkClick }) {
@@ -4556,7 +4670,7 @@ function Pagination({
4556
4670
  }
4557
4671
 
4558
4672
  // src/components/data/RelativeTime.tsx
4559
- var React26 = __toESM(require("react"), 1);
4673
+ var React27 = __toESM(require("react"), 1);
4560
4674
  var import_jsx_runtime51 = require("react/jsx-runtime");
4561
4675
  var MIN = 6e4;
4562
4676
  var HOUR = 36e5;
@@ -4608,9 +4722,9 @@ function formatDuration(ms) {
4608
4722
  return h + "h" + (m % 60 ? " " + m % 60 + "m" : "");
4609
4723
  }
4610
4724
  function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true, ...rest }) {
4611
- const [, force] = React26.useState(0);
4725
+ const [, force] = React27.useState(0);
4612
4726
  const t = value instanceof Date ? value.getTime() : Date.parse(value);
4613
- React26.useEffect(() => {
4727
+ React27.useEffect(() => {
4614
4728
  if (!refresh2 || !t || isNaN(t)) return;
4615
4729
  const abs = Math.abs(t - Date.now());
4616
4730
  if (abs > DAY) return;
@@ -4626,7 +4740,7 @@ function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true,
4626
4740
  }
4627
4741
 
4628
4742
  // src/components/data/ScoreMeter.tsx
4629
- var React27 = __toESM(require("react"), 1);
4743
+ var React28 = __toESM(require("react"), 1);
4630
4744
  var import_jsx_runtime52 = require("react/jsx-runtime");
4631
4745
  function ScoreMeter({
4632
4746
  score,
@@ -4641,10 +4755,10 @@ function ScoreMeter({
4641
4755
  breakdownWidth = 280,
4642
4756
  className = ""
4643
4757
  }) {
4644
- const [open, setOpen] = React27.useState(false);
4645
- const ref = React27.useRef(null);
4758
+ const [open, setOpen] = React28.useState(false);
4759
+ const ref = React28.useRef(null);
4646
4760
  const value = Math.max(0, Math.min(100, Math.round(Number(score) || 0)));
4647
- const body = /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(React27.Fragment, { children: [
4761
+ const body = /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(React28.Fragment, { children: [
4648
4762
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "fd-scoremeter-rail", style: { width }, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(SegmentedMeter, { total: 100, segments, height, showTotal: false, showPercent: false, legend: false }) }),
4649
4763
  showScore ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "fd-scoremeter-value fd-tabular", "aria-hidden": "true", children: value }) : null
4650
4764
  ] });
@@ -4663,7 +4777,7 @@ function ScoreMeter({
4663
4777
  }
4664
4778
  );
4665
4779
  }
4666
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(React27.Fragment, { children: [
4780
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(React28.Fragment, { children: [
4667
4781
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
4668
4782
  "button",
4669
4783
  {
@@ -4694,13 +4808,13 @@ function ScoreMeter({
4694
4808
  }
4695
4809
 
4696
4810
  // src/components/data/SortMenu.tsx
4697
- var React28 = __toESM(require("react"), 1);
4811
+ var React29 = __toESM(require("react"), 1);
4698
4812
  var import_jsx_runtime53 = require("react/jsx-runtime");
4699
4813
  function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", className = "", ...rest }) {
4700
- const [open, setOpen] = React28.useState(false);
4814
+ const [open, setOpen] = React29.useState(false);
4701
4815
  const zIndex = useOverlayLayer(open);
4702
- const ref = React28.useRef(null);
4703
- React28.useEffect(() => {
4816
+ const ref = React29.useRef(null);
4817
+ React29.useEffect(() => {
4704
4818
  if (!open) return;
4705
4819
  const away = (e) => {
4706
4820
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -4762,7 +4876,7 @@ function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", cla
4762
4876
  }
4763
4877
 
4764
4878
  // src/components/data/StepList.tsx
4765
- var React29 = __toESM(require("react"), 1);
4879
+ var React30 = __toESM(require("react"), 1);
4766
4880
  var import_jsx_runtime54 = require("react/jsx-runtime");
4767
4881
  var KIND_ICON = { tool: "wrench", search: "magnifying-glass", read: "file-text", write: "pencil-simple", reasoning: "brain", run: "terminal", fetch: "cloud-arrow-down" };
4768
4882
  function StepList({
@@ -4775,9 +4889,9 @@ function StepList({
4775
4889
  dense = false,
4776
4890
  className = ""
4777
4891
  }) {
4778
- const [openState, setOpenState] = React29.useState(defaultOpen);
4892
+ const [openState, setOpenState] = React30.useState(defaultOpen);
4779
4893
  const open = openProp == null ? openState : openProp;
4780
- const [expanded, setExpanded] = React29.useState({});
4894
+ const [expanded, setExpanded] = React30.useState({});
4781
4895
  if (!steps.length) return null;
4782
4896
  const running = steps.some((s) => s.status === "running");
4783
4897
  const failed = steps.filter((s) => s.status === "error").length;
@@ -4835,7 +4949,7 @@ function StepList({
4835
4949
  }
4836
4950
 
4837
4951
  // src/components/data/VirtualList.tsx
4838
- var React30 = __toESM(require("react"), 1);
4952
+ var React31 = __toESM(require("react"), 1);
4839
4953
  var import_jsx_runtime55 = require("react/jsx-runtime");
4840
4954
  var VIRTUAL_LIST_BUFFER_ROWS = 20;
4841
4955
  var VIRTUAL_LIST_ROW_GAP = 6;
@@ -4887,35 +5001,35 @@ function VirtualList({
4887
5001
  className = "",
4888
5002
  style
4889
5003
  }) {
4890
- const [scrollTop, setScrollTop] = React30.useState(0);
4891
- const loaded = React30.useMemo(
5004
+ const [scrollTop, setScrollTop] = React31.useState(0);
5005
+ const loaded = React31.useMemo(
4892
5006
  () => ({ firstItemIndex, count: items.length }),
4893
5007
  [firstItemIndex, items.length]
4894
5008
  );
4895
5009
  const total = Math.max(totalCount == null ? 0 : totalCount, firstItemIndex + items.length);
4896
5010
  const bandKey = firstItemIndex + ":" + items.length;
4897
- const win = React30.useMemo(
5011
+ const win = React31.useMemo(
4898
5012
  () => computeVirtualWindow({ scrollTop, viewportHeight: height, itemHeight, itemCount: total, loaded }),
4899
5013
  [scrollTop, height, itemHeight, total, loaded]
4900
5014
  );
4901
- const need = React30.useMemo(() => computeNeedMore(win, loaded), [win, loaded]);
5015
+ const need = React31.useMemo(() => computeNeedMore(win, loaded), [win, loaded]);
4902
5016
  const wantStart = need.start && hasMore?.start !== false;
4903
5017
  const wantEnd = need.end && hasMore?.end !== false;
4904
5018
  const bucket2 = Math.floor(win.visibleStart / VIRTUAL_LIST_BUFFER_ROWS);
4905
5019
  const askKey = bandKey + "@" + bucket2;
4906
- const asked = React30.useRef({ start: null, end: null });
4907
- React30.useEffect(() => {
5020
+ const asked = React31.useRef({ start: null, end: null });
5021
+ React31.useEffect(() => {
4908
5022
  if (loading || !wantStart || asked.current.start === askKey) return;
4909
5023
  asked.current.start = askKey;
4910
5024
  onNeedMore("start", win);
4911
5025
  }, [wantStart, loading, askKey, onNeedMore, win]);
4912
- React30.useEffect(() => {
5026
+ React31.useEffect(() => {
4913
5027
  if (loading || !wantEnd || asked.current.end === askKey) return;
4914
5028
  asked.current.end = askKey;
4915
5029
  onNeedMore("end", win);
4916
5030
  }, [wantEnd, loading, askKey, onNeedMore, win]);
4917
- const lastKeysRef = React30.useRef(null);
4918
- const newKeys = React30.useMemo(() => {
5031
+ const lastKeysRef = React31.useRef(null);
5032
+ const newKeys = React31.useMemo(() => {
4919
5033
  const prev = lastKeysRef.current;
4920
5034
  if (!prev) return /* @__PURE__ */ new Set();
4921
5035
  const fresh = /* @__PURE__ */ new Set();
@@ -4925,7 +5039,7 @@ function VirtualList({
4925
5039
  }
4926
5040
  return fresh;
4927
5041
  }, [items]);
4928
- React30.useEffect(() => {
5042
+ React31.useEffect(() => {
4929
5043
  lastKeysRef.current = new Set(items.map(keyOf));
4930
5044
  }, [items]);
4931
5045
  const gapStyle = { "--fd-vlist-gap": rowGap + "px" };
@@ -4970,7 +5084,7 @@ function VirtualList({
4970
5084
  }
4971
5085
 
4972
5086
  // src/components/data/EntityRow.tsx
4973
- var React31 = __toESM(require("react"), 1);
5087
+ var React32 = __toESM(require("react"), 1);
4974
5088
  var import_jsx_runtime56 = require("react/jsx-runtime");
4975
5089
  var ENTITY_ROW_METRIC_WIDTH = 74;
4976
5090
  var METRIC_TONE_COLOR = {
@@ -4983,7 +5097,7 @@ function EntityRowMetrics({ metrics, className = "", style }) {
4983
5097
  const key = m.id || m.label || i;
4984
5098
  const className2 = ["fd-erow-metric", m.tone && m.tone !== "default" ? "is-" + m.tone : "", m.tooltip != null ? "is-described" : ""].filter(Boolean).join(" ");
4985
5099
  const tone = { color: METRIC_TONE_COLOR[m.tone || "default"] };
4986
- const content = /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(React31.Fragment, { children: [
5100
+ const content = /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(React32.Fragment, { children: [
4987
5101
  m.icon ? /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }) : null,
4988
5102
  /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "fd-erow-metric-value fd-tabular", children: m.value }),
4989
5103
  /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "fd-sr", children: " " + m.label })
@@ -5075,7 +5189,7 @@ function EntityRow({
5075
5189
  }
5076
5190
 
5077
5191
  // src/components/data/TransferList.tsx
5078
- var React32 = __toESM(require("react"), 1);
5192
+ var React33 = __toESM(require("react"), 1);
5079
5193
  var import_react_dom5 = require("react-dom");
5080
5194
 
5081
5195
  // src/components/forms/Input.tsx
@@ -5181,12 +5295,12 @@ function TransferList({
5181
5295
  listHeight = 440,
5182
5296
  className = ""
5183
5297
  }) {
5184
- const [drag, setDrag] = React32.useState(null);
5185
- const [dragOverSide, setDragOverSide] = React32.useState(null);
5186
- const dragRef = React32.useRef(null);
5187
- const armedRef = React32.useRef(null);
5188
- const sideRefs = React32.useRef({ left: null, right: null });
5189
- const hitTestSide = React32.useCallback((x, y) => {
5298
+ const [drag, setDrag] = React33.useState(null);
5299
+ const [dragOverSide, setDragOverSide] = React33.useState(null);
5300
+ const dragRef = React33.useRef(null);
5301
+ const armedRef = React33.useRef(null);
5302
+ const sideRefs = React33.useRef({ left: null, right: null });
5303
+ const hitTestSide = React33.useCallback((x, y) => {
5190
5304
  for (const side of ["left", "right"]) {
5191
5305
  const el = sideRefs.current[side];
5192
5306
  if (!el) continue;
@@ -5195,7 +5309,7 @@ function TransferList({
5195
5309
  }
5196
5310
  return null;
5197
5311
  }, []);
5198
- const findItem = React32.useCallback(
5312
+ const findItem = React33.useCallback(
5199
5313
  (side, key) => (side === "left" ? left.items : right.items).find((it) => keyOf(it) === key),
5200
5314
  [left.items, right.items, keyOf]
5201
5315
  );
@@ -5217,7 +5331,7 @@ function TransferList({
5217
5331
  height: rect.height
5218
5332
  };
5219
5333
  };
5220
- React32.useEffect(() => {
5334
+ React33.useEffect(() => {
5221
5335
  const onMovePointer = (e) => {
5222
5336
  const armed = armedRef.current;
5223
5337
  if (!armed || e.pointerId !== armed.pointerId) return;
@@ -5270,7 +5384,7 @@ function TransferList({
5270
5384
  window.removeEventListener("pointercancel", endDrag);
5271
5385
  };
5272
5386
  }, [hitTestSide, findItem, onMove]);
5273
- React32.useEffect(() => {
5387
+ React33.useEffect(() => {
5274
5388
  if (!drag || typeof document === "undefined") return;
5275
5389
  const prev = document.body.style.userSelect;
5276
5390
  document.body.style.userSelect = "none";
@@ -5378,11 +5492,11 @@ function TransferList({
5378
5492
  }
5379
5493
 
5380
5494
  // src/components/forms/Checkbox.tsx
5381
- var React33 = __toESM(require("react"), 1);
5495
+ var React34 = __toESM(require("react"), 1);
5382
5496
  var import_jsx_runtime60 = require("react/jsx-runtime");
5383
5497
  function Checkbox({ label, description, card = false, indeterminate = false, className = "", ...rest }) {
5384
- const ref = React33.useRef(null);
5385
- React33.useEffect(() => {
5498
+ const ref = React34.useRef(null);
5499
+ React34.useEffect(() => {
5386
5500
  if (ref.current) ref.current.indeterminate = indeterminate;
5387
5501
  }, [indeterminate]);
5388
5502
  return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("label", { className: ["fd-choice", card ? "fd-choice-card" : "", className].filter(Boolean).join(" "), children: [
@@ -5426,7 +5540,7 @@ function Switch({ label, description, className = "", ...rest }) {
5426
5540
  }
5427
5541
 
5428
5542
  // src/components/forms/Select.tsx
5429
- var React34 = __toESM(require("react"), 1);
5543
+ var React35 = __toESM(require("react"), 1);
5430
5544
  var import_jsx_runtime63 = require("react/jsx-runtime");
5431
5545
  var norm = (o) => typeof o === "string" ? { value: o, label: o } : o;
5432
5546
  function Select({
@@ -5453,13 +5567,12 @@ function Select({
5453
5567
  const isOn = (v) => multiple ? vals.includes(v) : v === value;
5454
5568
  const hasSearch = searchable === void 0 ? opts.length > 8 : searchable;
5455
5569
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
5456
- const [open, setOpen] = React34.useState(false);
5457
- const [q, setQ] = React34.useState("");
5458
- const [active, setActive] = React34.useState(-1);
5459
- const rootRef = React34.useRef(null);
5460
- const boxRef = React34.useRef(null);
5461
- const listRef = React34.useRef(null);
5462
- const typeBuf = React34.useRef({ s: "", t: 0 });
5570
+ const [open, setOpen] = React35.useState(false);
5571
+ const [q, setQ] = React35.useState("");
5572
+ const rootRef = React35.useRef(null);
5573
+ const boxRef = React35.useRef(null);
5574
+ const listRef = React35.useRef(null);
5575
+ const typeBuf = React35.useRef({ s: "", t: 0 });
5463
5576
  const selected = multiple ? null : opts.find((o) => o.value === value);
5464
5577
  const chosen = multiple ? opts.filter((o) => vals.includes(o.value)) : [];
5465
5578
  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;
@@ -5476,23 +5589,16 @@ function Select({
5476
5589
  fire(o.value);
5477
5590
  setOpen(false);
5478
5591
  };
5592
+ const nav = useListboxNav({ count: visible.length, onSelect: (i) => pick(visible[i]) });
5593
+ useListboxScrollIntoView(nav.active, listRef, open);
5479
5594
  const toggle = () => {
5480
5595
  if (disabled || loading) return;
5481
5596
  if (!open) {
5482
5597
  setQ("");
5483
- setActive(Math.max(0, opts.findIndex((o) => isOn(o.value))));
5598
+ nav.setActive(Math.max(0, opts.findIndex((o) => isOn(o.value))));
5484
5599
  }
5485
5600
  setOpen(!open);
5486
5601
  };
5487
- React34.useEffect(() => {
5488
- if (!open || active < 0 || !listRef.current) return;
5489
- const el = listRef.current.querySelector('[data-i="' + active + '"]');
5490
- if (el) {
5491
- const L = listRef.current, top = el.offsetTop - L.offsetTop;
5492
- if (top < L.scrollTop) L.scrollTop = top - 4;
5493
- else if (top + el.offsetHeight > L.scrollTop + L.clientHeight) L.scrollTop = top + el.offsetHeight - L.clientHeight + 4;
5494
- }
5495
- }, [active, open]);
5496
5602
  const onKey = (e) => {
5497
5603
  if (disabled) return;
5498
5604
  if (!open && (e.key === "Enter" || e.key === " " || e.key === "ArrowDown" || e.key === "ArrowUp")) {
@@ -5514,19 +5620,19 @@ function Select({
5514
5620
  setOpen(false);
5515
5621
  } else if (e.key === "ArrowDown") {
5516
5622
  e.preventDefault();
5517
- setActive((a) => Math.min(visible.length - 1, a + 1));
5623
+ nav.move(1);
5518
5624
  } else if (e.key === "ArrowUp") {
5519
5625
  e.preventDefault();
5520
- setActive((a) => Math.max(0, a - 1));
5626
+ nav.move(-1);
5521
5627
  } else if (e.key === "Home") {
5522
5628
  e.preventDefault();
5523
- setActive(0);
5629
+ nav.first();
5524
5630
  } else if (e.key === "End") {
5525
5631
  e.preventDefault();
5526
- setActive(visible.length - 1);
5632
+ nav.last();
5527
5633
  } else if (e.key === "Enter") {
5528
5634
  e.preventDefault();
5529
- if (visible[active]) pick(visible[active]);
5635
+ nav.activate();
5530
5636
  } else if (e.key === "Tab") setOpen(false);
5531
5637
  };
5532
5638
  const groups = [];
@@ -5599,13 +5705,13 @@ function Select({
5599
5705
  onKeyDown: onKey,
5600
5706
  onChange: (e) => {
5601
5707
  setQ(e.target.value);
5602
- setActive(0);
5708
+ nav.setActive(0);
5603
5709
  }
5604
5710
  }
5605
5711
  ),
5606
5712
  q ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { className: "fd-body-sm fd-muted", children: visible.length }) : null
5607
5713
  ] }) : void 0,
5608
- footer: multiple ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(React34.Fragment, { children: [
5714
+ footer: multiple ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(React35.Fragment, { children: [
5609
5715
  /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
5610
5716
  "button",
5611
5717
  {
@@ -5636,7 +5742,7 @@ function Select({
5636
5742
  'Nothing matches "',
5637
5743
  q,
5638
5744
  '".'
5639
- ] }) : groups.map((grp) => /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(React34.Fragment, { children: [
5745
+ ] }) : groups.map((grp) => /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(React35.Fragment, { children: [
5640
5746
  grp.g ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("div", { className: "fd-pop-group", children: grp.g }) : null,
5641
5747
  grp.items.map(({ o, i }) => /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(
5642
5748
  "button",
@@ -5646,8 +5752,8 @@ function Select({
5646
5752
  role: "option",
5647
5753
  "aria-selected": isOn(o.value),
5648
5754
  "aria-disabled": o.disabled || void 0,
5649
- className: ["fd-opt", i === active ? "is-active" : "", isOn(o.value) ? "is-selected" : ""].filter(Boolean).join(" "),
5650
- onMouseEnter: () => setActive(i),
5755
+ className: ["fd-opt", i === nav.active ? "is-active" : "", isOn(o.value) ? "is-selected" : ""].filter(Boolean).join(" "),
5756
+ onMouseEnter: () => nav.setActive(i),
5651
5757
  onClick: () => pick(o),
5652
5758
  children: [
5653
5759
  multiple ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("span", { "aria-hidden": "true", style: { flex: "none", display: "grid", placeItems: "center", width: 16, height: 16, borderRadius: 4, border: "1.5px solid " + (isOn(o.value) ? "var(--brand)" : "var(--border-strong, var(--border))"), background: isOn(o.value) ? "var(--brand)" : "var(--surface)", color: "#fff" }, children: isOn(o.value) ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)("i", { className: "ph ph-check", style: { fontSize: 11 } }) : null }) : null,
@@ -5673,7 +5779,7 @@ function Select({
5673
5779
  }
5674
5780
 
5675
5781
  // src/components/forms/TimePicker.tsx
5676
- var React35 = __toESM(require("react"), 1);
5782
+ var React36 = __toESM(require("react"), 1);
5677
5783
  var import_jsx_runtime64 = require("react/jsx-runtime");
5678
5784
  var pad = (n) => String(n).padStart(2, "0");
5679
5785
  function ClockFace({ value = "09:00", onChange }) {
@@ -5682,9 +5788,9 @@ function ClockFace({ value = "09:00", onChange }) {
5682
5788
  if (isNaN(m)) m = 0;
5683
5789
  const pm = h24 >= 12;
5684
5790
  const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
5685
- const [mode2, setMode] = React35.useState("h");
5686
- const faceRef = React35.useRef(null);
5687
- const dragging = React35.useRef(false);
5791
+ const [mode2, setMode] = React36.useState("h");
5792
+ const faceRef = React36.useRef(null);
5793
+ const dragging = React36.useRef(false);
5688
5794
  const set = (h, mm, isPm) => onChange((isPm ? h % 12 + 12 : h % 12) + ":" + pad(mm));
5689
5795
  const R = 108, NR = 80;
5690
5796
  const nums = mode2 === "h" ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 12 }, (_, i) => i * 5);
@@ -5757,9 +5863,9 @@ function ClockFace({ value = "09:00", onChange }) {
5757
5863
  }
5758
5864
  function TimePicker({ label, help, error, required = false, disabled = false, value = "", onChange, placeholder = "Pick a time", id, className = "", style }) {
5759
5865
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
5760
- const [open, setOpen] = React35.useState(false);
5761
- const rootRef = React35.useRef(null);
5762
- const boxRef = React35.useRef(null);
5866
+ const [open, setOpen] = React36.useState(false);
5867
+ const rootRef = React36.useRef(null);
5868
+ const boxRef = React36.useRef(null);
5763
5869
  const disp = () => {
5764
5870
  if (!value) return "";
5765
5871
  const [h, m] = value.split(":").map(Number);
@@ -5802,7 +5908,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
5802
5908
  offset: 6,
5803
5909
  width: 262,
5804
5910
  role: "presentation",
5805
- footer: /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(React35.Fragment, { children: [
5911
+ footer: /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(React36.Fragment, { children: [
5806
5912
  /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
5807
5913
  "button",
5808
5914
  {
@@ -5830,7 +5936,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
5830
5936
  }
5831
5937
 
5832
5938
  // src/components/forms/Slider.tsx
5833
- var React36 = __toESM(require("react"), 1);
5939
+ var React37 = __toESM(require("react"), 1);
5834
5940
  var import_jsx_runtime65 = require("react/jsx-runtime");
5835
5941
  function Slider({
5836
5942
  label,
@@ -5848,7 +5954,7 @@ function Slider({
5848
5954
  ...rest
5849
5955
  }) {
5850
5956
  const fieldId = useFieldId(id);
5851
- const [dragging, setDragging] = React36.useState(false);
5957
+ const [dragging, setDragging] = React37.useState(false);
5852
5958
  const v = value === void 0 ? min : Number(value);
5853
5959
  const pct = max === min ? 0 : (v - min) / (max - min) * 100;
5854
5960
  return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("div", { className: ["fd-field", className].filter(Boolean).join(" "), children: [
@@ -5882,7 +5988,7 @@ function Slider({
5882
5988
  }
5883
5989
 
5884
5990
  // src/components/forms/RangeSlider.tsx
5885
- var React37 = __toESM(require("react"), 1);
5991
+ var React38 = __toESM(require("react"), 1);
5886
5992
  var import_jsx_runtime66 = require("react/jsx-runtime");
5887
5993
  function RangeSlider({
5888
5994
  label,
@@ -5902,9 +6008,9 @@ function RangeSlider({
5902
6008
  }) {
5903
6009
  const fmt2 = format || ((v) => String(v));
5904
6010
  const [a, b] = value || [min, max];
5905
- const [drag, setDrag] = React37.useState(null);
5906
- const [focus, setFocus] = React37.useState(null);
5907
- const railRef = React37.useRef(null);
6011
+ const [drag, setDrag] = React38.useState(null);
6012
+ const [focus, setFocus] = React38.useState(null);
6013
+ const railRef = React38.useRef(null);
5908
6014
  const pct = (v) => max === min ? 0 : (v - min) / (max - min) * 100;
5909
6015
  const clampPair = (i, v) => {
5910
6016
  v = Math.min(max, Math.max(min, Math.round(v / step) * step));
@@ -5919,7 +6025,7 @@ function RangeSlider({
5919
6025
  const r = railRef.current.getBoundingClientRect();
5920
6026
  return min + Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)) * (max - min);
5921
6027
  };
5922
- React37.useEffect(() => {
6028
+ React38.useEffect(() => {
5923
6029
  if (drag === null) return;
5924
6030
  const mv = (e) => onChange && onChange(clampPair(drag, fromEvent(e)));
5925
6031
  const upH = () => setDrag(null);
@@ -6000,7 +6106,7 @@ function RangeSlider({
6000
6106
  /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)("div", { className: "fd-range" + (hasLabels ? " has-labels" : ""), onPointerDown: onRailDown, children: [
6001
6107
  /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-range-rail", ref: railRef }),
6002
6108
  /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-range-fill", style: { left: pct(a) + "%", width: pct(b) - pct(a) + "%" } }),
6003
- marks.map((m) => /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(React37.Fragment, { children: [
6109
+ marks.map((m) => /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(React38.Fragment, { children: [
6004
6110
  /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-range-mark" + (m.value >= a && m.value <= b ? " is-in" : ""), style: { left: pct(m.value) + "%" } }),
6005
6111
  m.label ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { className: "fd-range-mark-label", style: { left: pct(m.value) + "%" }, children: m.label }) : null
6006
6112
  ] }, m.value)),
@@ -6029,7 +6135,7 @@ function RangeSlider({
6029
6135
  }
6030
6136
 
6031
6137
  // src/components/forms/Dropzone.tsx
6032
- var React38 = __toESM(require("react"), 1);
6138
+ var React39 = __toESM(require("react"), 1);
6033
6139
  var import_jsx_runtime67 = require("react/jsx-runtime");
6034
6140
  function Dropzone({
6035
6141
  onFiles,
@@ -6044,8 +6150,8 @@ function Dropzone({
6044
6150
  className = "",
6045
6151
  style
6046
6152
  }) {
6047
- const [over, setOver] = React38.useState(false);
6048
- const depth = React38.useRef(0);
6153
+ const [over, setOver] = React39.useState(false);
6154
+ const depth = React39.useRef(0);
6049
6155
  const has = (e) => {
6050
6156
  const dt = e.dataTransfer;
6051
6157
  if (!dt) return false;
@@ -6107,8 +6213,8 @@ function FilePickButton({
6107
6213
  className = "",
6108
6214
  children
6109
6215
  }) {
6110
- const ref = React38.useRef(null);
6111
- return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(React38.Fragment, { children: [
6216
+ const ref = React39.useRef(null);
6217
+ return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(React39.Fragment, { children: [
6112
6218
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
6113
6219
  "button",
6114
6220
  {
@@ -6141,10 +6247,10 @@ function FilePickButton({
6141
6247
  }
6142
6248
  function useStagedFiles(upload, opts) {
6143
6249
  const o = opts || {};
6144
- const [items, setItems] = React38.useState([]);
6145
- const controllers = React38.useRef({});
6250
+ const [items, setItems] = React39.useState([]);
6251
+ const controllers = React39.useRef({});
6146
6252
  const patch = (id, next) => setItems((list) => list.map((f) => f.id === id ? Object.assign({}, f, next) : f));
6147
- const run = React38.useCallback((att) => {
6253
+ const run = React39.useCallback((att) => {
6148
6254
  if (!upload) return;
6149
6255
  const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
6150
6256
  controllers.current[att.id] = ac;
@@ -6161,14 +6267,14 @@ function useStagedFiles(upload, opts) {
6161
6267
  delete controllers.current[att.id];
6162
6268
  });
6163
6269
  }, [upload]);
6164
- const add = React38.useCallback((files) => {
6270
+ const add = React39.useCallback((files) => {
6165
6271
  if (!upload) return [];
6166
6272
  const atts = Array.from(files).map((f) => toAttachment(f));
6167
6273
  setItems((list) => list.concat(atts));
6168
6274
  atts.forEach(run);
6169
6275
  return atts;
6170
6276
  }, [upload, run]);
6171
- const remove = React38.useCallback((att) => {
6277
+ const remove = React39.useCallback((att) => {
6172
6278
  const ac = controllers.current[att.id];
6173
6279
  if (ac) {
6174
6280
  try {
@@ -6179,10 +6285,10 @@ function useStagedFiles(upload, opts) {
6179
6285
  }
6180
6286
  setItems((list) => list.filter((f) => f.id !== att.id));
6181
6287
  }, []);
6182
- const retry = React38.useCallback((att) => {
6288
+ const retry = React39.useCallback((att) => {
6183
6289
  run(att);
6184
6290
  }, [run]);
6185
- const clear = React38.useCallback(() => {
6291
+ const clear = React39.useCallback(() => {
6186
6292
  Object.values(controllers.current).forEach((ac) => {
6187
6293
  try {
6188
6294
  ac && ac.abort();
@@ -6306,7 +6412,7 @@ function FileGrid({ files = [], onOpen, onRemove, onRetry, tiles = true, maxHeig
6306
6412
  }
6307
6413
 
6308
6414
  // src/components/forms/MarkdownEditor.tsx
6309
- var React39 = __toESM(require("react"), 1);
6415
+ var React40 = __toESM(require("react"), 1);
6310
6416
  var import_jsx_runtime69 = require("react/jsx-runtime");
6311
6417
  var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || "");
6312
6418
  var INLINE_RE = /(\*\*\*[^*\n]+\*\*\*|\*\*[^*\n]+\*\*|__[^_\n]+__|~~[^~\n]+~~|`[^`\n]+`|\*[^*\s][^*\n]*\*|(?<![A-Za-z0-9_])_[^_\s][^_\n]*_|\[[^\]\n]*\]\([^)\s\n]*\)|https?:\/\/\S+)/g;
@@ -6520,7 +6626,7 @@ function syncDom(root, value) {
6520
6626
  while (root.children.length > lines.length) root.removeChild(root.lastChild);
6521
6627
  }
6522
6628
  var LIST_CONT = RE_LI;
6523
- var MarkdownEditor = React39.forwardRef(function MarkdownEditor2({
6629
+ var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
6524
6630
  value = "",
6525
6631
  onChange,
6526
6632
  onSubmit,
@@ -6539,10 +6645,10 @@ var MarkdownEditor = React39.forwardRef(function MarkdownEditor2({
6539
6645
  className = "",
6540
6646
  id
6541
6647
  }, ref) {
6542
- const boxRef = React39.useRef(null);
6543
- const composing = React39.useRef(false);
6544
- const pendingCaret = React39.useRef(null);
6545
- React39.useLayoutEffect(() => {
6648
+ const boxRef = React40.useRef(null);
6649
+ const composing = React40.useRef(false);
6650
+ const pendingCaret = React40.useRef(null);
6651
+ React40.useLayoutEffect(() => {
6546
6652
  const root = boxRef.current;
6547
6653
  if (!root || composing.current) return;
6548
6654
  const active = document.activeElement === root || root.contains(document.activeElement);
@@ -6551,7 +6657,7 @@ var MarkdownEditor = React39.forwardRef(function MarkdownEditor2({
6551
6657
  pendingCaret.current = null;
6552
6658
  if (active && caret != null) placeCaret(root, caret);
6553
6659
  }, [value]);
6554
- React39.useEffect(() => {
6660
+ React40.useEffect(() => {
6555
6661
  if (autoFocus && boxRef.current) boxRef.current.focus();
6556
6662
  }, [autoFocus]);
6557
6663
  const caretNow = () => {
@@ -6618,7 +6724,7 @@ var MarkdownEditor = React39.forwardRef(function MarkdownEditor2({
6618
6724
  api.replaceRange(from, to, next, from + next.length);
6619
6725
  }
6620
6726
  };
6621
- React39.useImperativeHandle(ref, () => api);
6727
+ React40.useImperativeHandle(ref, () => api);
6622
6728
  function detect(text, caret) {
6623
6729
  if (!onTrigger) return;
6624
6730
  const upto = text.slice(0, caret);
@@ -6770,16 +6876,16 @@ var MarkdownEditor = React39.forwardRef(function MarkdownEditor2({
6770
6876
  });
6771
6877
 
6772
6878
  // src/components/platform/AccountMenu.tsx
6773
- var React41 = __toESM(require("react"), 1);
6879
+ var React42 = __toESM(require("react"), 1);
6774
6880
 
6775
6881
  // src/kits/sessionContext.tsx
6776
- var React40 = __toESM(require("react"), 1);
6777
- var SessionContext = React40.createContext(null);
6882
+ var React41 = __toESM(require("react"), 1);
6883
+ var SessionContext = React41.createContext(null);
6778
6884
  function SessionProvider({ value, children }) {
6779
- return React40.createElement(SessionContext.Provider, { value }, children);
6885
+ return React41.createElement(SessionContext.Provider, { value }, children);
6780
6886
  }
6781
6887
  function useSessionContext() {
6782
- const ctx = React40.useContext(SessionContext);
6888
+ const ctx = React41.useContext(SessionContext);
6783
6889
  if (!ctx) {
6784
6890
  throw new Error(
6785
6891
  "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."
@@ -6819,11 +6925,11 @@ function AccountMenu({
6819
6925
  ...rest
6820
6926
  }) {
6821
6927
  const { user, roles, can: can2, signOut: ctxSignOut, demo } = useSessionContext();
6822
- const [open, setOpen] = React41.useState(false);
6823
- const [switching, setSwitching] = React41.useState(false);
6824
- const [signingOut, setSigningOut] = React41.useState(false);
6825
- const [signOutError, setSignOutError] = React41.useState(null);
6826
- const ref = React41.useRef(null);
6928
+ const [open, setOpen] = React42.useState(false);
6929
+ const [switching, setSwitching] = React42.useState(false);
6930
+ const [signingOut, setSigningOut] = React42.useState(false);
6931
+ const [signOutError, setSignOutError] = React42.useState(null);
6932
+ const ref = React42.useRef(null);
6827
6933
  const visibleLinks = links.filter((l) => !l.perm || can2(l.perm));
6828
6934
  const visibleGroups = groups.map((g) => ({ label: g.label, links: g.links.filter((l) => !l.perm || can2(l.perm)) })).filter((g) => g.links.length);
6829
6935
  const closeMenu = () => {
@@ -6948,13 +7054,13 @@ function AccountMenu({
6948
7054
  }
6949
7055
 
6950
7056
  // src/components/platform/AppSwitcher.tsx
6951
- var React42 = __toESM(require("react"), 1);
7057
+ var React43 = __toESM(require("react"), 1);
6952
7058
  var import_jsx_runtime71 = require("react/jsx-runtime");
6953
7059
  function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
6954
7060
  const { apps: contextApps } = useSessionContext();
6955
7061
  const apps = appsProp ?? contextApps ?? [];
6956
- const [open, setOpen] = React42.useState(false);
6957
- const anchorRef = React42.useRef(null);
7062
+ const [open, setOpen] = React43.useState(false);
7063
+ const anchorRef = React43.useRef(null);
6958
7064
  if (!apps.length) return null;
6959
7065
  const items = apps.map((app) => ({
6960
7066
  id: app.key,
@@ -6968,7 +7074,7 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
6968
7074
  window.location.href = app.url;
6969
7075
  }
6970
7076
  }));
6971
- return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(React42.Fragment, { children: [
7077
+ return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(React43.Fragment, { children: [
6972
7078
  /* @__PURE__ */ (0, import_jsx_runtime71.jsx)("span", { ref: anchorRef, className: ["fd-appswitch-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
6973
7079
  IconButton,
6974
7080
  {
@@ -6997,10 +7103,10 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
6997
7103
  }
6998
7104
 
6999
7105
  // src/components/platform/AppearanceMenu.tsx
7000
- var React44 = __toESM(require("react"), 1);
7106
+ var React45 = __toESM(require("react"), 1);
7001
7107
 
7002
7108
  // src/kits/appearance.ts
7003
- var React43 = __toESM(require("react"), 1);
7109
+ var React44 = __toESM(require("react"), 1);
7004
7110
  var DEFAULT_STORAGE_KEY = "fd-appearance";
7005
7111
  var DEFAULT_PREFS = {
7006
7112
  theme: "system",
@@ -7095,11 +7201,11 @@ function applyPrefs(prefs, root = document.documentElement) {
7095
7201
  else root.removeAttribute("data-motion");
7096
7202
  }
7097
7203
  function useAppearancePrefs(store) {
7098
- const prefs = React43.useSyncExternalStore(store.subscribe, store.getSnapshot);
7099
- React43.useEffect(() => {
7204
+ const prefs = React44.useSyncExternalStore(store.subscribe, store.getSnapshot);
7205
+ React44.useEffect(() => {
7100
7206
  applyPrefs(prefs);
7101
7207
  }, [prefs]);
7102
- React43.useEffect(() => {
7208
+ React44.useEffect(() => {
7103
7209
  if (prefs.theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
7104
7210
  return;
7105
7211
  }
@@ -7108,7 +7214,7 @@ function useAppearancePrefs(store) {
7108
7214
  mq.addEventListener("change", onChange);
7109
7215
  return () => mq.removeEventListener("change", onChange);
7110
7216
  }, [prefs]);
7111
- const update = React43.useCallback(
7217
+ const update = React44.useCallback(
7112
7218
  (patch) => {
7113
7219
  store.write({ ...store.getSnapshot(), ...patch });
7114
7220
  },
@@ -7131,15 +7237,15 @@ var TEXT_SCALE_OPTIONS = [
7131
7237
  { value: "xl", label: "XL" }
7132
7238
  ];
7133
7239
  function AppearanceMenu({ className = "", store: storeProp, storageKey, storage, defaults }) {
7134
- const store = React44.useMemo(
7240
+ const store = React45.useMemo(
7135
7241
  () => storeProp ?? createLocalStorageAppearanceStore({ storageKey, storage, defaults }),
7136
7242
  [storeProp, storageKey, storage, defaults]
7137
7243
  );
7138
7244
  const [prefs, update] = useAppearancePrefs(store);
7139
- const [open, setOpen] = React44.useState(false);
7140
- const anchorRef = React44.useRef(null);
7245
+ const [open, setOpen] = React45.useState(false);
7246
+ const anchorRef = React45.useRef(null);
7141
7247
  const patch = (p) => update(p);
7142
- return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(React44.Fragment, { children: [
7248
+ return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(React45.Fragment, { children: [
7143
7249
  /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("span", { ref: anchorRef, className: ["fd-appearance-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
7144
7250
  IconButton,
7145
7251
  {
@@ -7208,14 +7314,14 @@ function AppearanceMenu({ className = "", store: storeProp, storageKey, storage,
7208
7314
  }
7209
7315
 
7210
7316
  // src/components/platform/ApiSpecBrowser.tsx
7211
- var React45 = __toESM(require("react"), 1);
7317
+ var React46 = __toESM(require("react"), 1);
7212
7318
  var import_jsx_runtime73 = require("react/jsx-runtime");
7213
7319
  var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
7214
7320
  function Json({ obj }) {
7215
7321
  return /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
7216
7322
  }
7217
7323
  function Endpoint({ s, onRequest }) {
7218
- const [tried, setTried] = React45.useState(null);
7324
+ const [tried, setTried] = React46.useState(null);
7219
7325
  const run = async () => {
7220
7326
  setTried("busy");
7221
7327
  const t0 = (window.performance || Date).now();
@@ -7288,10 +7394,10 @@ function ApiSpecBrowser({
7288
7394
  className = "",
7289
7395
  ...rest
7290
7396
  }) {
7291
- const [q, setQ] = React45.useState("");
7292
- const [method, setMethod] = React45.useState(null);
7293
- const [mod, setMod] = React45.useState(null);
7294
- const [listOnly, setListOnly] = React45.useState(false);
7397
+ const [q, setQ] = React46.useState("");
7398
+ const [method, setMethod] = React46.useState(null);
7399
+ const [mod, setMod] = React46.useState(null);
7400
+ const [listOnly, setListOnly] = React46.useState(false);
7295
7401
  const activeModule = modules && modules.find((m) => m.id === mod);
7296
7402
  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())));
7297
7403
  const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
@@ -7370,7 +7476,7 @@ function ApiSpecBrowser({
7370
7476
  }
7371
7477
 
7372
7478
  // src/components/platform/ProfilePage.tsx
7373
- var React46 = __toESM(require("react"), 1);
7479
+ var React47 = __toESM(require("react"), 1);
7374
7480
  var import_jsx_runtime74 = require("react/jsx-runtime");
7375
7481
  function fieldInitialValue(field, user) {
7376
7482
  if (field.value) return field.value(user);
@@ -7406,21 +7512,21 @@ function ProfilePage({
7406
7512
  }) {
7407
7513
  const { user: sessionUser, roles, permissions, saveProfile: saveProfile2 } = useSessionContext();
7408
7514
  const user = userProp || sessionUser;
7409
- const initialFieldValues = React46.useMemo(() => {
7515
+ const initialFieldValues = React47.useMemo(() => {
7410
7516
  const values2 = {};
7411
7517
  for (const field of fields) values2[field.key] = fieldInitialValue(field, user);
7412
7518
  return values2;
7413
7519
  }, [user.id, fields]);
7414
- const initialNotify = React46.useMemo(() => {
7520
+ const initialNotify = React47.useMemo(() => {
7415
7521
  const values2 = {};
7416
7522
  for (const n of notifications) values2[n.key] = !!user.notify?.[n.key];
7417
7523
  return values2;
7418
7524
  }, [user.id, notifications]);
7419
- const [values, setValues] = React46.useState(initialFieldValues);
7420
- const [notify, setNotify] = React46.useState(initialNotify);
7421
- const [saving, setSaving] = React46.useState(false);
7422
- const [saved, setSaved] = React46.useState(false);
7423
- const [prevUserId, setPrevUserId] = React46.useState(user.id);
7525
+ const [values, setValues] = React47.useState(initialFieldValues);
7526
+ const [notify, setNotify] = React47.useState(initialNotify);
7527
+ const [saving, setSaving] = React47.useState(false);
7528
+ const [saved, setSaved] = React47.useState(false);
7529
+ const [prevUserId, setPrevUserId] = React47.useState(user.id);
7424
7530
  if (user.id !== prevUserId) {
7425
7531
  setPrevUserId(user.id);
7426
7532
  setValues(initialFieldValues);
@@ -7608,11 +7714,11 @@ function ProfilePage({
7608
7714
  }
7609
7715
 
7610
7716
  // src/components/platform/SignInPage.tsx
7611
- var React47 = __toESM(require("react"), 1);
7717
+ var React48 = __toESM(require("react"), 1);
7612
7718
  var import_jsx_runtime75 = require("react/jsx-runtime");
7613
7719
  function SignInPage({ productName, onSignIn, continueAs, error }) {
7614
- const [signingIn, setSigningIn] = React47.useState(false);
7615
- const [resuming, setResuming] = React47.useState(false);
7720
+ const [signingIn, setSigningIn] = React48.useState(false);
7721
+ const [resuming, setResuming] = React48.useState(false);
7616
7722
  function handleSignIn() {
7617
7723
  setSigningIn(true);
7618
7724
  onSignIn();
@@ -7801,10 +7907,10 @@ function GoogleGlyph() {
7801
7907
  }
7802
7908
 
7803
7909
  // src/components/platform/RoadmapTimeline.tsx
7804
- var React50 = __toESM(require("react"), 1);
7910
+ var React51 = __toESM(require("react"), 1);
7805
7911
 
7806
7912
  // src/kits/session.ts
7807
- var React48 = __toESM(require("react"), 1);
7913
+ var React49 = __toESM(require("react"), 1);
7808
7914
  var PERMISSION_CATALOG = [
7809
7915
  { group: "Plans", items: [
7810
7916
  { key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
@@ -9371,8 +9477,8 @@ function roadmap(overrides) {
9371
9477
  };
9372
9478
  }
9373
9479
  function useSession() {
9374
- const [s, setS] = React48.useState(getSession);
9375
- React48.useEffect(() => subscribe(setS), []);
9480
+ const [s, setS] = React49.useState(getSession);
9481
+ React49.useEffect(() => subscribe(setS), []);
9376
9482
  return s;
9377
9483
  }
9378
9484
  var SessionKit = {
@@ -9405,7 +9511,7 @@ var SessionKit = {
9405
9511
  };
9406
9512
  function DemoSessionProvider({ children }) {
9407
9513
  const session = useSession();
9408
- const value = React48.useMemo(
9514
+ const value = React49.useMemo(
9409
9515
  () => ({
9410
9516
  user: session.user,
9411
9517
  roles: session.roles,
@@ -9421,11 +9527,11 @@ function DemoSessionProvider({ children }) {
9421
9527
  }),
9422
9528
  [session]
9423
9529
  );
9424
- return React48.createElement(SessionProvider, { value }, children);
9530
+ return React49.createElement(SessionProvider, { value }, children);
9425
9531
  }
9426
9532
 
9427
9533
  // src/kits/runtime.ts
9428
- var React49 = __toESM(require("react"), 1);
9534
+ var React50 = __toESM(require("react"), 1);
9429
9535
  var WIRED_ENDPOINTS = [
9430
9536
  // Nothing yet. Every id below would come from a real service:
9431
9537
  // "plan.get", "placements.list", …
@@ -9596,8 +9702,8 @@ var RuntimeKit = {
9596
9702
  };
9597
9703
  RuntimeKit.declare(FEATURE_NEEDS);
9598
9704
  function useRuntimeMode() {
9599
- const [m, setM] = React49.useState(RuntimeKit.getMode());
9600
- React49.useEffect(() => RuntimeKit.subscribe(setM), []);
9705
+ const [m, setM] = React50.useState(RuntimeKit.getMode());
9706
+ React50.useEffect(() => RuntimeKit.subscribe(setM), []);
9601
9707
  return m;
9602
9708
  }
9603
9709
  function useFeatureStatus(key) {
@@ -9680,12 +9786,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
9680
9786
  }
9681
9787
  function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9682
9788
  const session = SessionKit.useSession();
9683
- const [project, setProject] = React50.useState("");
9684
- const [q, setQ] = React50.useState("");
9685
- const scrollRef = React50.useRef(null);
9686
- const nowRef = React50.useRef(null);
9687
- const rm = React50.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
9688
- const byKey = React50.useMemo(() => {
9789
+ const [project, setProject] = React51.useState("");
9790
+ const [q, setQ] = React51.useState("");
9791
+ const scrollRef = React51.useRef(null);
9792
+ const nowRef = React51.useRef(null);
9793
+ const rm = React51.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
9794
+ const byKey = React51.useMemo(() => {
9689
9795
  const m = {};
9690
9796
  rm.items.forEach((i) => {
9691
9797
  m[i.key] = i;
@@ -9694,7 +9800,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9694
9800
  }, [rm]);
9695
9801
  const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
9696
9802
  const projects = [...new Set(rm.items.map((i) => i.project))];
9697
- React50.useEffect(() => {
9803
+ React51.useEffect(() => {
9698
9804
  let raf1 = 0, raf2 = 0;
9699
9805
  const place = () => {
9700
9806
  const box = scrollRef.current, mark = nowRef.current;
@@ -9768,7 +9874,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9768
9874
  lastPhase = item.phase;
9769
9875
  const inPhase = items.filter((i) => i.phase === item.phase).length;
9770
9876
  const isBoundary = n === firstPending;
9771
- return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React50.Fragment, { children: [
9877
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React51.Fragment, { children: [
9772
9878
  showPhase ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fd-rm-era", children: [
9773
9879
  /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
9774
9880
  /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
@@ -9812,7 +9918,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
9812
9918
  }
9813
9919
 
9814
9920
  // src/components/platform/ComingSoon.tsx
9815
- var React51 = __toESM(require("react"), 1);
9921
+ var React52 = __toESM(require("react"), 1);
9816
9922
  var import_react_dom6 = require("react-dom");
9817
9923
  var import_jsx_runtime77 = require("react/jsx-runtime");
9818
9924
  var BYPASS_STORE = "fd.soon.bypass.v1";
@@ -9830,8 +9936,8 @@ function writeBypassed(list) {
9830
9936
  }
9831
9937
  }
9832
9938
  function useBypass(key) {
9833
- const [on, setOn] = React51.useState(() => !!key && readBypassed().indexOf(key) >= 0);
9834
- React51.useEffect(() => {
9939
+ const [on, setOn] = React52.useState(() => !!key && readBypassed().indexOf(key) >= 0);
9940
+ React52.useEffect(() => {
9835
9941
  setOn(!!key && readBypassed().indexOf(key) >= 0);
9836
9942
  }, [key]);
9837
9943
  const set = (next) => {
@@ -9878,9 +9984,9 @@ function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onB
9878
9984
  ] });
9879
9985
  }
9880
9986
  function useHoverCard(open) {
9881
- const anchor = React51.useRef(null);
9882
- const [pos, setPos] = React51.useState(null);
9883
- React51.useLayoutEffect(() => {
9987
+ const anchor = React52.useRef(null);
9988
+ const [pos, setPos] = React52.useState(null);
9989
+ React52.useLayoutEffect(() => {
9884
9990
  if (!open || !anchor.current) {
9885
9991
  setPos(null);
9886
9992
  return;
@@ -10007,10 +10113,10 @@ function ComingSoon({
10007
10113
  ] });
10008
10114
  }
10009
10115
  function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
10010
- const [open, setOpen] = React51.useState(false);
10116
+ const [open, setOpen] = React52.useState(false);
10011
10117
  const [anchor, pos] = useHoverCard(open);
10012
10118
  const zIndex = useOverlayLayer(open);
10013
- const close = React51.useRef(null);
10119
+ const close = React52.useRef(null);
10014
10120
  const show = () => {
10015
10121
  if (close.current) {
10016
10122
  clearTimeout(close.current);
@@ -10026,7 +10132,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
10026
10132
  setOpen(false);
10027
10133
  }, 140);
10028
10134
  };
10029
- React51.useEffect(() => () => {
10135
+ React52.useEffect(() => () => {
10030
10136
  if (close.current) clearTimeout(close.current);
10031
10137
  }, []);
10032
10138
  return /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)(
@@ -10197,14 +10303,14 @@ function PermissionHint({ perm, children }) {
10197
10303
  }
10198
10304
 
10199
10305
  // src/components/platform/ModeSwitch.tsx
10200
- var React52 = __toESM(require("react"), 1);
10306
+ var React53 = __toESM(require("react"), 1);
10201
10307
  var import_jsx_runtime79 = require("react/jsx-runtime");
10202
10308
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
10203
10309
  const mode2 = useRuntimeMode();
10204
- const [open, setOpen] = React52.useState(false);
10310
+ const [open, setOpen] = React53.useState(false);
10205
10311
  const zIndex = useOverlayLayer(open);
10206
- const ref = React52.useRef(null);
10207
- React52.useEffect(() => {
10312
+ const ref = React53.useRef(null);
10313
+ React53.useEffect(() => {
10208
10314
  if (!open) return;
10209
10315
  const away = (e) => {
10210
10316
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -10460,7 +10566,7 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
10460
10566
  }
10461
10567
 
10462
10568
  // src/components/planner/MixGap.tsx
10463
- var React53 = __toESM(require("react"), 1);
10569
+ var React54 = __toESM(require("react"), 1);
10464
10570
  var import_jsx_runtime82 = require("react/jsx-runtime");
10465
10571
  var MIX_GAP_LAYOUT = {
10466
10572
  trackHeight: 30,
@@ -10475,7 +10581,7 @@ function mixGapOffset(value, max) {
10475
10581
  }
10476
10582
  function MixGap({ rows = [], loading = false, className = "" }) {
10477
10583
  const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
10478
- const [hover, setHover] = React53.useState(null);
10584
+ const [hover, setHover] = React54.useState(null);
10479
10585
  const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
10480
10586
  const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
10481
10587
  const { trackHeight, barHeight, targetWidth, labelGap } = MIX_GAP_LAYOUT;
@@ -10618,7 +10724,7 @@ function ChannelContribution({
10618
10724
  }
10619
10725
 
10620
10726
  // src/components/planner/BudgetReallocator.tsx
10621
- var React54 = __toESM(require("react"), 1);
10727
+ var React55 = __toESM(require("react"), 1);
10622
10728
  var import_jsx_runtime84 = require("react/jsx-runtime");
10623
10729
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
10624
10730
  function BudgetReallocator({
@@ -10634,8 +10740,8 @@ function BudgetReallocator({
10634
10740
  onCancel,
10635
10741
  className = ""
10636
10742
  }) {
10637
- const [draft, setDraft] = React54.useState(spend);
10638
- React54.useEffect(() => setDraft(spend), [spend]);
10743
+ const [draft, setDraft] = React55.useState(spend);
10744
+ React55.useEffect(() => setDraft(spend), [spend]);
10639
10745
  const dirty = draft !== spend;
10640
10746
  const nextCrp = scoreFor ? scoreFor(draft) : crp;
10641
10747
  const nextBand = bandFor(nextCrp);
@@ -10650,10 +10756,10 @@ function BudgetReallocator({
10650
10756
  setDraft(spend);
10651
10757
  if (onCancel) onCancel();
10652
10758
  };
10653
- const trackRef = React54.useRef(null);
10654
- const chipRef = React54.useRef(null);
10655
- const [chipLeft, setChipLeft] = React54.useState(null);
10656
- React54.useLayoutEffect(() => {
10759
+ const trackRef = React55.useRef(null);
10760
+ const chipRef = React55.useRef(null);
10761
+ const [chipLeft, setChipLeft] = React55.useState(null);
10762
+ React55.useLayoutEffect(() => {
10657
10763
  const track = trackRef.current;
10658
10764
  if (!dirty || !track) {
10659
10765
  setChipLeft(null);
@@ -10840,7 +10946,7 @@ function ImportanceTag({ level, showLabel = true, size = "sm", className = "" })
10840
10946
  }
10841
10947
 
10842
10948
  // src/components/planner/PreferenceMeter.tsx
10843
- var React55 = __toESM(require("react"), 1);
10949
+ var React56 = __toESM(require("react"), 1);
10844
10950
  var import_jsx_runtime87 = require("react/jsx-runtime");
10845
10951
  function preferenceScore(criteria) {
10846
10952
  const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
@@ -10870,7 +10976,7 @@ function PreferenceMeter({
10870
10976
  className = ""
10871
10977
  }) {
10872
10978
  const value = score == null ? preferenceScore(criteria) : score;
10873
- const segments = React55.useMemo(() => preferenceSegments(criteria), [criteria]);
10979
+ const segments = React56.useMemo(() => preferenceSegments(criteria), [criteria]);
10874
10980
  return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
10875
10981
  ScoreMeter,
10876
10982
  {
@@ -10909,10 +11015,10 @@ function PreferenceMeter({
10909
11015
  }
10910
11016
 
10911
11017
  // src/components/chat/AgentChatPanel.tsx
10912
- var React61 = __toESM(require("react"), 1);
11018
+ var React62 = __toESM(require("react"), 1);
10913
11019
 
10914
11020
  // src/components/chat/chatEngine.ts
10915
- var React56 = __toESM(require("react"), 1);
11021
+ var React57 = __toESM(require("react"), 1);
10916
11022
  var CHAT_UNAVAILABLE = "chat_unavailable";
10917
11023
  var JOB_PENDING = ["queued", "running"];
10918
11024
  var JOB_SUCCESS = ["completed", "recovered"];
@@ -10966,22 +11072,22 @@ function useChatEngine(opts) {
10966
11072
  onClear,
10967
11073
  onFeedback
10968
11074
  } = opts || {};
10969
- const [status, setStatus] = React56.useState("idle");
10970
- const [threadId, setThreadId] = React56.useState(null);
10971
- const [messages, setMessages] = React56.useState([]);
10972
- const [queue, setQueue] = React56.useState([]);
10973
- const [fatal, setFatal] = React56.useState(null);
10974
- const [busy, setBusy] = React56.useState(false);
10975
- const [turnStartedAt, setTurnStartedAt] = React56.useState(null);
10976
- const listRef = React56.useRef([]);
10977
- const queueRef = React56.useRef([]);
10978
- const busyRef = React56.useRef(false);
10979
- const stoppedRef = React56.useRef(false);
10980
- const abortRef = React56.useRef(null);
10981
- const serverCount = React56.useRef(0);
10982
- const threadRef = React56.useRef(null);
10983
- const mounted = React56.useRef(true);
10984
- React56.useEffect(() => {
11075
+ const [status, setStatus] = React57.useState("idle");
11076
+ const [threadId, setThreadId] = React57.useState(null);
11077
+ const [messages, setMessages] = React57.useState([]);
11078
+ const [queue, setQueue] = React57.useState([]);
11079
+ const [fatal, setFatal] = React57.useState(null);
11080
+ const [busy, setBusy] = React57.useState(false);
11081
+ const [turnStartedAt, setTurnStartedAt] = React57.useState(null);
11082
+ const listRef = React57.useRef([]);
11083
+ const queueRef = React57.useRef([]);
11084
+ const busyRef = React57.useRef(false);
11085
+ const stoppedRef = React57.useRef(false);
11086
+ const abortRef = React57.useRef(null);
11087
+ const serverCount = React57.useRef(0);
11088
+ const threadRef = React57.useRef(null);
11089
+ const mounted = React57.useRef(true);
11090
+ React57.useEffect(() => {
10985
11091
  mounted.current = true;
10986
11092
  return () => {
10987
11093
  mounted.current = false;
@@ -11003,14 +11109,14 @@ function useChatEngine(opts) {
11003
11109
  }
11004
11110
  return false;
11005
11111
  };
11006
- const loadThread = React56.useCallback(async (id) => {
11112
+ const loadThread = React57.useCallback(async (id) => {
11007
11113
  const data = await apiAdapter.getThread(id);
11008
11114
  const list = [...data && data.messages || []];
11009
11115
  serverCount.current = list.length;
11010
11116
  commit(list);
11011
11117
  return list;
11012
11118
  }, [apiAdapter]);
11013
- React56.useEffect(() => {
11119
+ React57.useEffect(() => {
11014
11120
  if (!apiAdapter) {
11015
11121
  setStatus("idle");
11016
11122
  setFatal(null);
@@ -11223,7 +11329,7 @@ function useChatEngine(opts) {
11223
11329
  await dispatchTurn(turn);
11224
11330
  }
11225
11331
  }
11226
- const send = React56.useCallback((text, attachments) => {
11332
+ const send = React57.useCallback((text, attachments) => {
11227
11333
  const body = (text || "").trim();
11228
11334
  if (!body && !(attachments && attachments.length)) return;
11229
11335
  if (status === "disconnected") return;
@@ -11233,7 +11339,7 @@ function useChatEngine(opts) {
11233
11339
  stoppedRef.current = false;
11234
11340
  drain();
11235
11341
  }, [status]);
11236
- const stop = React56.useCallback(() => {
11342
+ const stop = React57.useCallback(() => {
11237
11343
  stoppedRef.current = true;
11238
11344
  const ac = abortRef.current;
11239
11345
  if (ac) {
@@ -11252,11 +11358,11 @@ function useChatEngine(opts) {
11252
11358
  store.del(STORAGE_PREFIX + threadRef.current);
11253
11359
  }
11254
11360
  }, [apiAdapter]);
11255
- const removeQueued = React56.useCallback((id) => {
11361
+ const removeQueued = React57.useCallback((id) => {
11256
11362
  queueRef.current = queueRef.current.filter((t) => t.id !== id);
11257
11363
  setQueue(queueRef.current.slice());
11258
11364
  }, []);
11259
- const retry = React56.useCallback(() => {
11365
+ const retry = React57.useCallback(() => {
11260
11366
  const list = listRef.current;
11261
11367
  let at = -1;
11262
11368
  for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
@@ -11272,19 +11378,19 @@ function useChatEngine(opts) {
11272
11378
  setQueue(queueRef.current.slice());
11273
11379
  drain();
11274
11380
  }, []);
11275
- const clear = React56.useCallback(() => {
11381
+ const clear = React57.useCallback(() => {
11276
11382
  commit([]);
11277
11383
  serverCount.current = 0;
11278
11384
  queueRef.current = [];
11279
11385
  setQueue([]);
11280
11386
  onClear && onClear();
11281
11387
  }, [onClear]);
11282
- const setFeedback = React56.useCallback((id, value) => {
11388
+ const setFeedback = React57.useCallback((id, value) => {
11283
11389
  patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
11284
11390
  const msg = listRef.current.find((m) => m.id === id);
11285
11391
  onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
11286
11392
  }, [onFeedback]);
11287
- const reload = React56.useCallback(async () => {
11393
+ const reload = React57.useCallback(async () => {
11288
11394
  if (!threadRef.current) return;
11289
11395
  setStatus("loading");
11290
11396
  try {
@@ -11322,10 +11428,10 @@ var ChatKit = {
11322
11428
  };
11323
11429
 
11324
11430
  // src/components/chat/ChatTranscript.tsx
11325
- var React58 = __toESM(require("react"), 1);
11431
+ var React59 = __toESM(require("react"), 1);
11326
11432
 
11327
11433
  // src/components/chat/ChatTurn.tsx
11328
- var React57 = __toESM(require("react"), 1);
11434
+ var React58 = __toESM(require("react"), 1);
11329
11435
  var import_jsx_runtime88 = require("react/jsx-runtime");
11330
11436
  function JsonView({ value }) {
11331
11437
  let text;
@@ -11362,7 +11468,7 @@ function PacketCard({ packet, schema, render, onApply, applied }) {
11362
11468
  ] });
11363
11469
  }
11364
11470
  function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
11365
- const [open, setOpen] = React57.useState(defaultOpen);
11471
+ const [open, setOpen] = React58.useState(defaultOpen);
11366
11472
  if (!text) return null;
11367
11473
  return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
11368
11474
  /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -11379,7 +11485,7 @@ function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
11379
11485
  ] });
11380
11486
  }
11381
11487
  function Citations({ items = [], onOpen }) {
11382
- const [open, setOpen] = React57.useState(false);
11488
+ const [open, setOpen] = React58.useState(false);
11383
11489
  if (!items.length) return null;
11384
11490
  return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "fdc-cites", children: [
11385
11491
  /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -11405,7 +11511,7 @@ function clampText(text, max) {
11405
11511
  return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
11406
11512
  }
11407
11513
  function MessageBody({ message: m, ctx }) {
11408
- const [expanded, setExpanded] = React57.useState(false);
11514
+ const [expanded, setExpanded] = React58.useState(false);
11409
11515
  const isUser = m.role === "user";
11410
11516
  const raw = m.text || "";
11411
11517
  const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
@@ -11472,9 +11578,9 @@ function MessageBody({ message: m, ctx }) {
11472
11578
  ] });
11473
11579
  }
11474
11580
  function useCopyRun() {
11475
- const [done, setDone] = React57.useState(false);
11476
- const t = React57.useRef(null);
11477
- React57.useEffect(() => () => {
11581
+ const [done, setDone] = React58.useState(false);
11582
+ const t = React58.useRef(null);
11583
+ React58.useEffect(() => () => {
11478
11584
  if (t.current) clearTimeout(t.current);
11479
11585
  }, []);
11480
11586
  return [done, (text) => {
@@ -11501,7 +11607,7 @@ function RunActions({ group, ctx }) {
11501
11607
  ] }),
11502
11608
  isAssistant && ctx.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
11503
11609
  group.role === "user" && ctx.onEdit ? /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
11504
- isAssistant && ctx.onFeedback ? /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(React57.Fragment, { children: [
11610
+ isAssistant && ctx.onFeedback ? /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(React58.Fragment, { children: [
11505
11611
  /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("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__ */ (0, import_jsx_runtime88.jsx)("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
11506
11612
  /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("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__ */ (0, import_jsx_runtime88.jsx)("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
11507
11613
  ] }) : null,
@@ -11596,10 +11702,10 @@ function ChatTranscript({
11596
11702
  renderEmpty,
11597
11703
  className = ""
11598
11704
  }) {
11599
- const scroller = React58.useRef(null);
11600
- const stick = React58.useRef(true);
11601
- const [pill, setPill] = React58.useState(0);
11602
- const seen = React58.useRef(0);
11705
+ const scroller = React59.useRef(null);
11706
+ const stick = React59.useRef(true);
11707
+ const [pill, setPill] = React59.useState(0);
11708
+ const seen = React59.useRef(0);
11603
11709
  const toBottom = (smooth) => {
11604
11710
  const el = scroller.current;
11605
11711
  if (!el) return;
@@ -11618,7 +11724,7 @@ function ChatTranscript({
11618
11724
  seen.current = messages.length;
11619
11725
  }
11620
11726
  };
11621
- React58.useLayoutEffect(() => {
11727
+ React59.useLayoutEffect(() => {
11622
11728
  const el = scroller.current;
11623
11729
  if (!el) return;
11624
11730
  if (stick.current) {
@@ -11626,7 +11732,7 @@ function ChatTranscript({
11626
11732
  seen.current = messages.length;
11627
11733
  } else setPill(Math.max(0, messages.length - seen.current));
11628
11734
  }, [messages]);
11629
- React58.useEffect(() => {
11735
+ React59.useEffect(() => {
11630
11736
  const el = scroller.current;
11631
11737
  const inner = el && el.firstChild;
11632
11738
  if (!el || !inner || typeof ResizeObserver === "undefined") return;
@@ -11636,7 +11742,7 @@ function ChatTranscript({
11636
11742
  ro.observe(inner);
11637
11743
  return () => ro.disconnect();
11638
11744
  }, []);
11639
- const groups = React58.useMemo(() => groupMessages(messages), [messages]);
11745
+ const groups = React59.useMemo(() => groupMessages(messages), [messages]);
11640
11746
  const empty = !messages.length && status === "ready";
11641
11747
  return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
11642
11748
  /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
@@ -11660,7 +11766,7 @@ function ChatTranscript({
11660
11766
  const prev = groups[i - 1];
11661
11767
  const k = dayKey(g.messages[0].timestamp);
11662
11768
  const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
11663
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(React58.Fragment, { children: [
11769
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(React59.Fragment, { children: [
11664
11770
  showDay ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "fdc-day", children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { children: dayLabel(k) }) }) : null,
11665
11771
  /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(ChatTurn, { group: g, ctx })
11666
11772
  ] }, g.key || i);
@@ -11677,7 +11783,7 @@ function ChatTranscript({
11677
11783
  var TranscriptKit = { groupMessages };
11678
11784
 
11679
11785
  // src/components/chat/ChatComposer.tsx
11680
- var React59 = __toESM(require("react"), 1);
11786
+ var React60 = __toESM(require("react"), 1);
11681
11787
  var import_jsx_runtime90 = require("react/jsx-runtime");
11682
11788
  function ChatComposer({
11683
11789
  onSubmit,
@@ -11705,17 +11811,17 @@ function ChatComposer({
11705
11811
  onReject,
11706
11812
  onOpenAttachment
11707
11813
  }) {
11708
- const [text, setText] = React59.useState(draft || "");
11709
- const [trigger, setTrigger] = React59.useState(null);
11710
- const [mentionItems, setMentionItems] = React59.useState([]);
11711
- const [listening, setListening] = React59.useState(false);
11712
- const [notice, setNotice] = React59.useState(null);
11713
- const editor = React59.useRef(null);
11714
- const wrap = React59.useRef(null);
11715
- const stopVoice = React59.useRef(null);
11814
+ const [text, setText] = React60.useState(draft || "");
11815
+ const [trigger, setTrigger] = React60.useState(null);
11816
+ const [mentionItems, setMentionItems] = React60.useState([]);
11817
+ const [listening, setListening] = React60.useState(false);
11818
+ const [notice, setNotice] = React60.useState(null);
11819
+ const editor = React60.useRef(null);
11820
+ const wrap = React60.useRef(null);
11821
+ const stopVoice = React60.useRef(null);
11716
11822
  const staged = useStagedFiles(fileUploadHandler, { onError: () => {
11717
11823
  } });
11718
- React59.useEffect(() => {
11824
+ React60.useEffect(() => {
11719
11825
  if (draft != null && draft !== text) setText(draft);
11720
11826
  }, [draft]);
11721
11827
  const change = (v) => {
@@ -11751,7 +11857,7 @@ function ChatComposer({
11751
11857
  e.preventDefault();
11752
11858
  staged.add(found.files);
11753
11859
  };
11754
- React59.useEffect(() => {
11860
+ React60.useEffect(() => {
11755
11861
  if (!trigger || trigger.type !== "mention" || !mentionSources) {
11756
11862
  setMentionItems([]);
11757
11863
  return;
@@ -11769,7 +11875,7 @@ function ChatComposer({
11769
11875
  const q = (trigger.query || "").toLowerCase();
11770
11876
  setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
11771
11877
  }, [trigger, mentionSources]);
11772
- const slashItems = React59.useMemo(() => {
11878
+ const slashItems = React60.useMemo(() => {
11773
11879
  if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
11774
11880
  const q = (trigger.query || "").toLowerCase();
11775
11881
  return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
@@ -11943,12 +12049,12 @@ function ChatComposer({
11943
12049
  }
11944
12050
 
11945
12051
  // src/components/chat/ChatSessionBar.tsx
11946
- var React60 = __toESM(require("react"), 1);
12052
+ var React61 = __toESM(require("react"), 1);
11947
12053
  var import_jsx_runtime91 = require("react/jsx-runtime");
11948
12054
  var compact2 = meterFormats.compact;
11949
12055
  function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
11950
- const [open, setOpen] = React60.useState(false);
11951
- const anchor = React60.useRef(null);
12056
+ const [open, setOpen] = React61.useState(false);
12057
+ const anchor = React61.useRef(null);
11952
12058
  const stats = sessionStats || null;
11953
12059
  const cu = contextUsage || null;
11954
12060
  const limits = usageLimits || null;
@@ -11963,7 +12069,7 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
11963
12069
  if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
11964
12070
  if (cu) bits.push(pct + "% context");
11965
12071
  const expandable = !!(cu || limits);
11966
- return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(React60.Fragment, { children: [
12072
+ return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(React61.Fragment, { children: [
11967
12073
  /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("div", { className: "fdc-bar", children: [
11968
12074
  /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(
11969
12075
  "button",
@@ -12104,7 +12210,7 @@ function ModelControls({
12104
12210
  ] })
12105
12211
  });
12106
12212
  }
12107
- return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(React60.Fragment, { children: [
12213
+ return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(React61.Fragment, { children: [
12108
12214
  /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
12109
12215
  MenuButton,
12110
12216
  {
@@ -12221,11 +12327,11 @@ function AgentChatPanel({
12221
12327
  onFeedback,
12222
12328
  onEditMessage
12223
12329
  }) {
12224
- const [panelWidth, setPanelWidth] = React61.useState(width);
12225
- const [applied, setApplied] = React61.useState({});
12226
- const [draft, setDraft] = React61.useState("");
12227
- const dragging = React61.useRef(null);
12228
- React61.useEffect(() => setPanelWidth(width), [width]);
12330
+ const [panelWidth, setPanelWidth] = React62.useState(width);
12331
+ const [applied, setApplied] = React62.useState({});
12332
+ const [draft, setDraft] = React62.useState("");
12333
+ const dragging = React62.useRef(null);
12334
+ React62.useEffect(() => setPanelWidth(width), [width]);
12229
12335
  const engine = useChatEngine({
12230
12336
  contextType,
12231
12337
  contextId,
@@ -12429,7 +12535,7 @@ function AgentChatPanel({
12429
12535
  }
12430
12536
 
12431
12537
  // src/components/files/FileViewer.tsx
12432
- var React62 = __toESM(require("react"), 1);
12538
+ var React63 = __toESM(require("react"), 1);
12433
12539
  var import_jsx_runtime93 = require("react/jsx-runtime");
12434
12540
  function previewKindOf(file) {
12435
12541
  const mime = (file.mime || "").toLowerCase();
@@ -12486,13 +12592,13 @@ function parseDelimited(text, delimiter) {
12486
12592
  return rows;
12487
12593
  }
12488
12594
  function usePreview(file, kind, resolveUrl) {
12489
- const [state, setState] = React62.useState({ status: "loading" });
12490
- const [attempt, setAttempt] = React62.useState(0);
12491
- const resolveRef = React62.useRef(resolveUrl);
12492
- React62.useEffect(() => {
12595
+ const [state, setState] = React63.useState({ status: "loading" });
12596
+ const [attempt, setAttempt] = React63.useState(0);
12597
+ const resolveRef = React63.useRef(resolveUrl);
12598
+ React63.useEffect(() => {
12493
12599
  resolveRef.current = resolveUrl;
12494
12600
  }, [resolveUrl]);
12495
- React62.useEffect(() => {
12601
+ React63.useEffect(() => {
12496
12602
  if (kind === "none") {
12497
12603
  setState({ status: "ready" });
12498
12604
  return;
@@ -12522,7 +12628,7 @@ function usePreview(file, kind, resolveUrl) {
12522
12628
  if (ac) ac.abort();
12523
12629
  };
12524
12630
  }, [file.id, kind, attempt]);
12525
- return { ...state, retry: React62.useCallback(() => setAttempt((a) => a + 1), []) };
12631
+ return { ...state, retry: React63.useCallback(() => setAttempt((a) => a + 1), []) };
12526
12632
  }
12527
12633
  function PreviewBody({ file, kind, state }) {
12528
12634
  if (kind === "image") return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
@@ -12567,13 +12673,13 @@ function PreviewBody({ file, kind, state }) {
12567
12673
  ] });
12568
12674
  }
12569
12675
  function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", onClose, actions, className = "" }) {
12570
- const kind = React62.useMemo(() => previewKindOf(file), [file]);
12676
+ const kind = React63.useMemo(() => previewKindOf(file), [file]);
12571
12677
  const preview = usePreview(file, kind, resolveUrl);
12572
- const [tab, setTab] = React62.useState("preview");
12573
- const [downloading, setDownloading] = React62.useState(false);
12574
- const [downloadError, setDownloadError] = React62.useState(null);
12678
+ const [tab, setTab] = React63.useState("preview");
12679
+ const [downloading, setDownloading] = React63.useState(false);
12680
+ const [downloadError, setDownloadError] = React63.useState(null);
12575
12681
  const hasSide = sidePanel != null;
12576
- React62.useEffect(() => {
12682
+ React63.useEffect(() => {
12577
12683
  setTab("preview");
12578
12684
  }, [file.id]);
12579
12685
  const download = () => {
@@ -12640,7 +12746,7 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
12640
12746
  }
12641
12747
 
12642
12748
  // src/components/files/FileBrowser.tsx
12643
- var React63 = __toESM(require("react"), 1);
12749
+ var React64 = __toESM(require("react"), 1);
12644
12750
  var import_jsx_runtime94 = require("react/jsx-runtime");
12645
12751
  function childrenOf(folders, parentId) {
12646
12752
  return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
@@ -12711,10 +12817,10 @@ function FolderPickerMenu({
12711
12817
  destinations,
12712
12818
  onPick
12713
12819
  }) {
12714
- const [open, setOpen] = React63.useState(false);
12820
+ const [open, setOpen] = React64.useState(false);
12715
12821
  const zIndex = useOverlayLayer(open);
12716
- const ref = React63.useRef(null);
12717
- React63.useEffect(() => {
12822
+ const ref = React64.useRef(null);
12823
+ React64.useEffect(() => {
12718
12824
  if (!open) return;
12719
12825
  const away = (e) => {
12720
12826
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -12766,8 +12872,8 @@ function FolderPickerMenu({
12766
12872
  ] });
12767
12873
  }
12768
12874
  function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
12769
- const [over, setOver] = React63.useState(false);
12770
- const dragCounter = React63.useRef(0);
12875
+ const [over, setOver] = React64.useState(false);
12876
+ const dragCounter = React64.useRef(0);
12771
12877
  const dropHandlers = isDropTarget ? {
12772
12878
  onDragEnter: (e) => {
12773
12879
  if (!hasInternalPayload(e)) return;
@@ -12815,8 +12921,8 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
12815
12921
  }
12816
12922
  function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
12817
12923
  const kids = childrenOf(folders, folder.id);
12818
- const [expanded, setExpanded] = React63.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
12819
- React63.useEffect(() => {
12924
+ const [expanded, setExpanded] = React64.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
12925
+ React64.useEffect(() => {
12820
12926
  if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
12821
12927
  }, [currentFolderId, folder.id, folders]);
12822
12928
  const isCurrent = folder.id === currentFolderId;
@@ -12882,15 +12988,15 @@ function FileBrowser({
12882
12988
  const canUpload = perm.canUpload !== false && !!onUpload;
12883
12989
  const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
12884
12990
  const canDeleteFolder = perm.canDeleteFolder !== false && !!onDeleteFolder;
12885
- const [createOpen, setCreateOpen] = React63.useState(false);
12886
- const [createName, setCreateName] = React63.useState("");
12887
- const [renameTarget, setRenameTarget] = React63.useState(null);
12888
- const [renameValue, setRenameValue] = React63.useState("");
12889
- const [deleteTarget, setDeleteTarget] = React63.useState(null);
12890
- const uploadInputRef = React63.useRef(null);
12891
- const crumbFolders = React63.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
12892
- const subfolders = React63.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
12893
- const currentFiles = React63.useMemo(
12991
+ const [createOpen, setCreateOpen] = React64.useState(false);
12992
+ const [createName, setCreateName] = React64.useState("");
12993
+ const [renameTarget, setRenameTarget] = React64.useState(null);
12994
+ const [renameValue, setRenameValue] = React64.useState("");
12995
+ const [deleteTarget, setDeleteTarget] = React64.useState(null);
12996
+ const uploadInputRef = React64.useRef(null);
12997
+ const crumbFolders = React64.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
12998
+ const subfolders = React64.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
12999
+ const currentFiles = React64.useMemo(
12894
13000
  () => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
12895
13001
  [files, currentFolderId]
12896
13002
  );
@@ -13122,8 +13228,8 @@ function BreadcrumbCrumb({
13122
13228
  isDropTarget,
13123
13229
  onDropPayload
13124
13230
  }) {
13125
- const [over, setOver] = React63.useState(false);
13126
- const dragCounter = React63.useRef(0);
13231
+ const [over, setOver] = React64.useState(false);
13232
+ const dragCounter = React64.useRef(0);
13127
13233
  const dropHandlers = isDropTarget ? {
13128
13234
  onDragEnter: (e) => {
13129
13235
  if (!hasInternalPayload(e)) return;
@@ -13152,9 +13258,27 @@ function BreadcrumbCrumb({
13152
13258
  }
13153
13259
 
13154
13260
  // src/kits/query.ts
13155
- var React64 = __toESM(require("react"), 1);
13261
+ var React65 = __toESM(require("react"), 1);
13262
+ var isMultiSelectShaped = (v) => !!v && typeof v === "object" && Array.isArray(v.values);
13263
+ var isRangeShaped = (v) => !!v && typeof v === "object" && ("min" in v || "max" in v);
13264
+ var isDateShaped = (v) => !!v && typeof v === "object" && "kind" in v;
13265
+ function isActiveFilterValue(v) {
13266
+ if (v == null || v === "" || v === false) return false;
13267
+ if (Array.isArray(v)) return v.length > 0;
13268
+ if (isMultiSelectShaped(v)) return v.values.length > 0;
13269
+ if (isRangeShaped(v)) return v.min != null || v.max != null;
13270
+ if (isDateShaped(v)) return v.kind === "withinDays" ? v.days != null : !!(v.from && v.to);
13271
+ return true;
13272
+ }
13156
13273
  function eqFilter(get2) {
13157
- return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
13274
+ return (row, value) => {
13275
+ if (Array.isArray(value)) return value.includes(get2(row));
13276
+ if (isMultiSelectShaped(value)) {
13277
+ const hit = value.values.some((o) => o.value === get2(row));
13278
+ return value.operator === "notIn" ? !hit : hit;
13279
+ }
13280
+ return get2(row) === value;
13281
+ };
13158
13282
  }
13159
13283
  function anyOfFilter(get2) {
13160
13284
  return (row, value) => {
@@ -13165,10 +13289,15 @@ function anyOfFilter(get2) {
13165
13289
  function rangeFilter(get2) {
13166
13290
  return (row, value) => {
13167
13291
  const n = get2(row);
13168
- const bounds = Array.isArray(value) ? value : [0, Number.POSITIVE_INFINITY];
13169
- const lo = Number(bounds[0] ?? 0);
13170
- const hiRaw = bounds[1];
13171
- const hi = hiRaw == null ? Number.POSITIVE_INFINITY : Number(hiRaw);
13292
+ let lo = 0;
13293
+ let hi = Number.POSITIVE_INFINITY;
13294
+ if (Array.isArray(value)) {
13295
+ lo = Number(value[0] ?? 0);
13296
+ hi = value[1] == null ? Number.POSITIVE_INFINITY : Number(value[1]);
13297
+ } else if (isRangeShaped(value)) {
13298
+ lo = value.min ?? 0;
13299
+ hi = value.max ?? Number.POSITIVE_INFINITY;
13300
+ }
13172
13301
  return n >= lo && (hi === Number.POSITIVE_INFINITY || n <= hi);
13173
13302
  };
13174
13303
  }
@@ -13184,22 +13313,22 @@ function compare(a, b, dir) {
13184
13313
  var API = null;
13185
13314
  var PREFS = null;
13186
13315
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
13187
- const [query, setQuery] = React64.useState(() => {
13316
+ const [query, setQuery] = React65.useState(() => {
13188
13317
  const store = PREFS || window.PlannerPrefs;
13189
13318
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
13190
13319
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
13191
13320
  });
13192
- const savePref = React64.useCallback((patch2) => {
13321
+ const savePref = React65.useCallback((patch2) => {
13193
13322
  const store = PREFS || window.PlannerPrefs;
13194
13323
  if (prefsKey && store) store.setTable(prefsKey, patch2);
13195
13324
  }, [prefsKey]);
13196
- const [res, setRes] = React64.useState(null);
13197
- const [loading, setLoading] = React64.useState(true);
13198
- const seq2 = React64.useRef(0);
13325
+ const [res, setRes] = React65.useState(null);
13326
+ const [loading, setLoading] = React65.useState(true);
13327
+ const seq2 = React65.useRef(0);
13199
13328
  const depKey = (deps || []).join("|");
13200
13329
  const paramKey = JSON.stringify(params || {});
13201
13330
  const queryKey = JSON.stringify(query);
13202
- React64.useEffect(() => {
13331
+ React65.useEffect(() => {
13203
13332
  const id = ++seq2.current;
13204
13333
  setLoading(true);
13205
13334
  const t = setTimeout(() => {
@@ -13235,7 +13364,7 @@ function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
13235
13364
  loading,
13236
13365
  /** True only on the very first load — later loads keep showing stale rows. */
13237
13366
  initialLoading: loading && !res,
13238
- activeFilterCount: Object.values(query.filters).filter((v) => !(v == null || v === "" || v === false)).length,
13367
+ activeFilterCount: Object.values(query.filters).filter(isActiveFilterValue).length,
13239
13368
  setPage: (page) => patch({ page }, false),
13240
13369
  setPageSize: (pageSize) => {
13241
13370
  savePref({ pageSize });
@@ -13298,7 +13427,6 @@ var QueryKit = {
13298
13427
  const Q = { ...DEFAULTS, ...query || {} };
13299
13428
  const c = cfg;
13300
13429
  const filters = Q.filters || {};
13301
- const active = (v) => !(v == null || v === "" || v === false || Array.isArray(v) && !v.length);
13302
13430
  let out = rows;
13303
13431
  const term = (Q.q || "").trim().toLowerCase();
13304
13432
  if (term && c.searchKeys) {
@@ -13308,7 +13436,7 @@ var QueryKit = {
13308
13436
  const searched = out;
13309
13437
  Object.entries(filters).forEach(([id, val]) => {
13310
13438
  const fn = c.filters && c.filters[id];
13311
- if (!active(val) || !fn) return;
13439
+ if (!isActiveFilterValue(val) || !fn) return;
13312
13440
  out = out.filter((r) => fn(r, val));
13313
13441
  });
13314
13442
  const matched = out;
@@ -13355,9 +13483,30 @@ var QueryKit = {
13355
13483
  if (Q.sort) parts.push("sort=" + Q.sort + "&dir=" + Q.dir);
13356
13484
  if (Q.q) parts.push("q=" + encodeURIComponent(Q.q));
13357
13485
  Object.entries(Q.filters || {}).forEach(([k, v]) => {
13358
- if (v == null || v === "" || v === false || Array.isArray(v) && !v.length) return;
13359
- if (Array.isArray(v)) v.forEach((x) => parts.push(k + "=" + encodeURIComponent(x === Infinity ? "*" : x)));
13360
- else parts.push(k + "=" + encodeURIComponent(v));
13486
+ if (!isActiveFilterValue(v)) return;
13487
+ if (Array.isArray(v)) {
13488
+ v.forEach((x) => parts.push(k + "=" + encodeURIComponent(x === Infinity ? "*" : x)));
13489
+ return;
13490
+ }
13491
+ if (isMultiSelectShaped(v)) {
13492
+ const prefix = v.operator === "notIn" ? "notIn:" : "";
13493
+ v.values.forEach((o) => parts.push(k + "=" + encodeURIComponent(prefix + String(o.value))));
13494
+ return;
13495
+ }
13496
+ if (isRangeShaped(v)) {
13497
+ if (v.min != null) parts.push(k + ".min=" + encodeURIComponent(String(v.min)));
13498
+ if (v.max != null) parts.push(k + ".max=" + encodeURIComponent(String(v.max)));
13499
+ return;
13500
+ }
13501
+ if (isDateShaped(v)) {
13502
+ if (v.kind === "withinDays") parts.push(k + "=withinDays:" + encodeURIComponent(String(v.days)));
13503
+ else {
13504
+ parts.push(k + ".from=" + encodeURIComponent(String(v.from)));
13505
+ parts.push(k + ".to=" + encodeURIComponent(String(v.to)));
13506
+ }
13507
+ return;
13508
+ }
13509
+ parts.push(k + "=" + encodeURIComponent(v));
13361
13510
  });
13362
13511
  return parts.join("&");
13363
13512
  },
@@ -13498,12 +13647,12 @@ function createVersionStore(initialState, options) {
13498
13647
  }
13499
13648
 
13500
13649
  // src/kits/documentTitle.ts
13501
- var React65 = __toESM(require("react"), 1);
13650
+ var React66 = __toESM(require("react"), 1);
13502
13651
  function formatDocumentTitle(routeTitle, appName) {
13503
13652
  return [routeTitle, appName, "Flytedesk"].filter((part) => !!part && part.length > 0).join(" \xB7 ");
13504
13653
  }
13505
13654
  function useDocumentTitle(routeTitle, appName) {
13506
- React65.useEffect(() => {
13655
+ React66.useEffect(() => {
13507
13656
  if (typeof document === "undefined") return;
13508
13657
  document.title = formatDocumentTitle(routeTitle, appName);
13509
13658
  }, [routeTitle, appName]);