@flytedan/flytebot-design-system 0.10.0 → 0.11.1

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
@@ -106,6 +106,7 @@ __export(index_exports, {
106
106
  ModeSwitch: () => ModeSwitch,
107
107
  ModelControls: () => ModelControls,
108
108
  NumberInput: () => NumberInput,
109
+ POPOVER_BODY_MIN: () => POPOVER_BODY_MIN,
109
110
  PacketCard: () => PacketCard,
110
111
  Pagination: () => Pagination,
111
112
  PermissionDenied: () => PermissionDenied,
@@ -259,6 +260,7 @@ function IconButton({
259
260
  label,
260
261
  size = "md",
261
262
  variant = "ghost",
263
+ tone = "neutral",
262
264
  round = false,
263
265
  disabled = false,
264
266
  className = "",
@@ -266,7 +268,8 @@ function IconButton({
266
268
  }) {
267
269
  const cls = [
268
270
  "fd-btn-icon",
269
- variant === "outline" ? "fd-btn-icon-outline" : "",
271
+ variant === "outline" ? "fd-btn-icon-outline" : variant === "solid" ? "fd-btn-icon-solid" : "",
272
+ tone !== "neutral" ? "is-" + tone : "",
270
273
  size === "sm" ? "fd-btn-icon-sm" : size === "lg" ? "fd-btn-icon-lg" : "",
271
274
  round ? "fd-btn-icon-round" : "",
272
275
  className
@@ -287,6 +290,23 @@ var samePosition = (a, b) => {
287
290
  if (!a || !b) return a === b;
288
291
  return a.left === b.left && a.top === b.top && a.bottom === b.bottom && a.width === b.width && a.minWidth === b.minWidth && a.maxH === b.maxH && a.side === b.side && a.anchorWidth === b.anchorWidth;
289
292
  };
293
+ var SCROLL_REGIONS = ".fd-pop-body, .fd-pop-list";
294
+ var PINNED_REGIONS = ":scope > .fd-pop-header, :scope > .fd-pop-footer";
295
+ var POPOVER_BODY_MIN = 96;
296
+ function measureLayer(el) {
297
+ if (!el || !el.offsetHeight) return { width: el ? el.offsetWidth : 0, height: 0, pinned: 0 };
298
+ let hidden = 0;
299
+ el.querySelectorAll(SCROLL_REGIONS).forEach((r) => {
300
+ hidden += Math.max(0, r.scrollHeight - r.clientHeight);
301
+ });
302
+ let pinned = 0;
303
+ el.querySelectorAll(PINNED_REGIONS).forEach((r) => {
304
+ pinned += r.offsetHeight;
305
+ });
306
+ const border = Math.max(0, el.offsetHeight - el.clientHeight);
307
+ const content = Math.max(el.scrollHeight, el.clientHeight);
308
+ return { width: el.offsetWidth, height: content + border + hidden, pinned: pinned + border };
309
+ }
290
310
  function usePopoverPosition(open, anchorRef, opts) {
291
311
  const o = opts || {};
292
312
  const { side: wantSide, align } = parsePlacement(o.placement);
@@ -294,6 +314,7 @@ function usePopoverPosition(open, anchorRef, opts) {
294
314
  const posRef = React.useRef(null);
295
315
  const detachRef = React.useRef(o.onDetach);
296
316
  detachRef.current = o.onDetach;
317
+ const layerRef = o.layerRef;
297
318
  React.useLayoutEffect(() => {
298
319
  if (!open || !anchorRef || !anchorRef.current) {
299
320
  posRef.current = null;
@@ -305,23 +326,36 @@ function usePopoverPosition(open, anchorRef, opts) {
305
326
  if (!el) return;
306
327
  const r = el.getBoundingClientRect();
307
328
  const offset = o.offset == null ? 8 : o.offset;
308
- const estH = o.estHeight || 300;
309
- const below = window.innerHeight - r.bottom - offset - MARGIN;
329
+ const layer = measureLayer(layerRef ? layerRef.current : null);
330
+ const vh = window.innerHeight;
331
+ const need = layer.height || Math.min(o.estHeight || 300, 180);
332
+ const below = vh - r.bottom - offset - MARGIN;
310
333
  const above = r.top - offset - MARGIN;
311
334
  let side = wantSide;
312
- if (side === "bottom" && below < Math.min(estH, 180) && above > below) side = "top";
313
- else if (side === "top" && above < Math.min(estH, 180) && below > above) side = "bottom";
314
- const width = o.matchWidth ? r.width : Math.max(o.width || 0, o.minWidth || 0);
315
- const w = width || o.estWidth || 260;
335
+ if (side === "bottom" && below < need && above > below) side = "top";
336
+ else if (side === "top" && above < need && below > above) side = "bottom";
337
+ const ceiling = Math.min(vh - 2 * MARGIN, o.maxHeight != null ? o.maxHeight : Infinity);
338
+ const floor = Math.min(
339
+ ceiling,
340
+ layer.height ? layer.pinned + Math.min(POPOVER_BODY_MIN, layer.height - layer.pinned) : Math.min(need, 140)
341
+ );
342
+ const room = side === "top" ? above : below;
343
+ const beside = room >= floor;
344
+ const edge = beside ? 0 : Math.max(MARGIN, vh - MARGIN - Math.min(need, ceiling));
345
+ const top = side === "bottom" ? beside ? r.bottom + offset : edge : null;
346
+ const bottom = side === "top" ? beside ? vh - r.top + offset : edge : null;
347
+ const maxH = beside ? room : vh - MARGIN - edge;
348
+ const exact = o.matchWidth === true;
349
+ const width = exact ? r.width : Math.max(o.width || 0, o.minWidth || 0, o.matchWidth === "min" ? r.width : 0);
350
+ const w = exact || o.width ? width : Math.max(layer.width, width) || o.estWidth || 260;
316
351
  let left = align === "end" ? r.right - w : align === "center" ? r.left + r.width / 2 - w / 2 : r.left;
317
352
  left = Math.max(MARGIN, Math.min(left, window.innerWidth - w - MARGIN));
318
- const maxH = Math.max(140, side === "top" ? above : below);
319
353
  const next = {
320
354
  left,
321
- width: o.matchWidth ? r.width : void 0,
355
+ width: exact ? r.width : void 0,
322
356
  minWidth: o.minWidth,
323
- top: side === "bottom" ? r.bottom + offset : null,
324
- bottom: side === "top" ? window.innerHeight - r.top + offset : null,
357
+ top,
358
+ bottom,
325
359
  maxH,
326
360
  side,
327
361
  anchorWidth: r.width
@@ -344,7 +378,7 @@ function usePopoverPosition(open, anchorRef, opts) {
344
378
  };
345
379
  raf = requestAnimationFrame(tick);
346
380
  return () => cancelAnimationFrame(raf);
347
- }, [open, o.placement, o.offset, o.matchWidth, o.width, o.minWidth, o.estHeight]);
381
+ }, [open, o.placement, o.offset, o.matchWidth, o.width, o.minWidth, o.maxHeight, o.estHeight]);
348
382
  return pos;
349
383
  }
350
384
  var portal = (node) => {
@@ -354,6 +388,7 @@ var portal = (node) => {
354
388
  function Popover({
355
389
  open,
356
390
  anchorRef,
391
+ triggerRef,
357
392
  onClose,
358
393
  placement = "bottom-start",
359
394
  offset = 8,
@@ -369,6 +404,8 @@ function Popover({
369
404
  returnFocus = true,
370
405
  className = "",
371
406
  style,
407
+ header,
408
+ footer,
372
409
  children
373
410
  }) {
374
411
  const ref = React.useRef(null);
@@ -378,8 +415,10 @@ function Popover({
378
415
  matchWidth,
379
416
  width,
380
417
  minWidth,
418
+ maxHeight,
381
419
  estHeight: maxHeight || 320,
382
- onDetach: onClose
420
+ onDetach: onClose,
421
+ layerRef: ref
383
422
  });
384
423
  const restore = React.useRef(null);
385
424
  React.useEffect(() => {
@@ -400,7 +439,8 @@ function Popover({
400
439
  if (!closeOnOutside) return;
401
440
  const target = e.target;
402
441
  if (ref.current && ref.current.contains(target)) return;
403
- if (anchorRef && anchorRef.current && anchorRef.current.contains(target)) return;
442
+ const trigger = (triggerRef || anchorRef).current;
443
+ if (trigger && trigger.contains(target)) return;
404
444
  if (e.target.closest && e.target.closest(".fd-pop")) return;
405
445
  onClose && onClose();
406
446
  };
@@ -416,23 +456,23 @@ function Popover({
416
456
  document.removeEventListener("pointerdown", away, true);
417
457
  document.removeEventListener("keydown", key);
418
458
  };
419
- }, [open, closeOnOutside, closeOnEscape, onClose, anchorRef]);
459
+ }, [open, closeOnOutside, closeOnEscape, onClose, anchorRef, triggerRef]);
420
460
  if (!open || !pos) return null;
421
461
  return portal(
422
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
462
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
423
463
  "div",
424
464
  {
425
465
  ref,
426
466
  role,
427
467
  "aria-label": label,
428
- className: ["fd-pop", pos.side === "top" ? "is-up" : "", padded ? "fd-pop-padded" : "", className].filter(Boolean).join(" "),
468
+ className: ["fd-pop", pos.side === "top" ? "is-up" : "", className].filter(Boolean).join(" "),
429
469
  style: {
430
470
  position: "fixed",
431
471
  left: pos.left,
432
472
  top: pos.top == null ? "auto" : pos.top,
433
473
  bottom: pos.bottom == null ? "auto" : pos.bottom,
434
- width: matchWidth ? pos.width : width,
435
- minWidth: minWidth || (matchWidth ? void 0 : 200),
474
+ width: matchWidth === true ? pos.width : width,
475
+ minWidth: matchWidth === "min" ? Math.max(pos.anchorWidth, minWidth || 0) : minWidth || (matchWidth ? void 0 : 200),
436
476
  // Size to content by default — cap only at real available room between the
437
477
  // anchor and the viewport edge (pos.maxH, computed by usePopoverPosition).
438
478
  // A caller-supplied `maxHeight` narrows that further (e.g. a long menu that
@@ -445,7 +485,11 @@ function Popover({
445
485
  zIndex: 140,
446
486
  ...style
447
487
  },
448
- children
488
+ children: [
489
+ header != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-pop-header", children: header }) : null,
490
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: ["fd-pop-body", padded ? "fd-pop-padded" : ""].filter(Boolean).join(" "), children }),
491
+ footer != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-pop-footer", children: footer }) : null
492
+ ]
449
493
  }
450
494
  )
451
495
  );
@@ -520,77 +564,79 @@ function Menu({ items = [], onSelect, onClose, autoFocus = true, className = "",
520
564
  };
521
565
  const sub = openSub != null ? items[openSub] : null;
522
566
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(React.Fragment, { children: [
523
- header,
524
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
525
- "div",
526
- {
527
- ref: listRef,
528
- tabIndex: -1,
529
- role: "menu",
530
- onKeyDown: onKey,
531
- className: ["fd-pop-list", "fd-menu", className].filter(Boolean).join(" "),
532
- children: items.map((it, i) => {
533
- if (!it) return null;
534
- if (it.kind === "separator") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-sep", role: "separator" }, "s" + i);
535
- if (it.kind === "section") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-pop-group", role: "presentation", children: it.label }, "h" + i);
536
- if (it.kind === "custom") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-custom", children: it.render ? it.render() : null }, "c" + i);
537
- const on = !!it.checked;
538
- const row = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
539
- "button",
540
- {
541
- type: "button",
542
- "data-i": i,
543
- role: it.checked != null ? "menuitemradio" : "menuitem",
544
- "aria-checked": it.checked != null ? on : void 0,
545
- "aria-haspopup": it.submenu ? "menu" : void 0,
546
- "aria-expanded": it.submenu ? openSub === i : void 0,
547
- "aria-disabled": it.disabled || void 0,
548
- className: ["fd-opt", "fd-menu-item", i === active ? "is-active" : "", on ? "is-selected" : ""].filter(Boolean).join(" "),
549
- onMouseEnter: () => {
550
- setActive(i);
551
- if (openSub != null && openSub !== i) setOpenSub(null);
552
- },
553
- onClick: () => choose(it, i),
554
- children: [
555
- it.icon ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-menu-icon", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-" + it.icon, "aria-hidden": "true" }) }) : null,
556
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "fd-menu-text", children: [
557
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-label", children: it.label }),
558
- it.description ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-desc", children: it.description }) : null
559
- ] }),
560
- it.meta ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-meta", children: it.meta }) : null,
561
- it.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-menu-shortcut", "aria-hidden": "true", children: it.shortcut }) : null,
562
- it.submenu ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-caret-right fd-menu-more", "aria-hidden": "true" }) : null,
563
- it.checked != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-check", children: on ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-check", "aria-hidden": "true" }) : null }) : null
564
- ]
565
- },
566
- it.id || it.label || i
567
- );
568
- const ta = it.trailingAction;
569
- if (!ta) return row;
570
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fd-menu-row", onMouseEnter: () => setActive(i), children: [
571
- row,
572
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
567
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fd-menu-frame", children: [
568
+ header != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-header", children: header }) : null,
569
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
570
+ "div",
571
+ {
572
+ ref: listRef,
573
+ tabIndex: -1,
574
+ role: "menu",
575
+ onKeyDown: onKey,
576
+ className: ["fd-pop-list", "fd-menu", className].filter(Boolean).join(" "),
577
+ children: items.map((it, i) => {
578
+ if (!it) return null;
579
+ if (it.kind === "separator") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-sep", role: "separator" }, "s" + i);
580
+ if (it.kind === "section") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-pop-group", role: "presentation", children: it.label }, "h" + i);
581
+ if (it.kind === "custom") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-custom", children: it.render ? it.render() : null }, "c" + i);
582
+ const on = !!it.checked;
583
+ const row = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
573
584
  "button",
574
585
  {
575
586
  type: "button",
576
- className: ["fd-menu-row-act", ta.danger ? "is-danger" : ""].filter(Boolean).join(" "),
577
- "aria-label": ta.label,
578
- title: ta.label,
579
- onClick: (e) => {
580
- e.stopPropagation();
581
- ta.onSelect && ta.onSelect(it);
582
- if (ta.closeOnSelect !== false) {
583
- onClose && onClose();
584
- }
587
+ "data-i": i,
588
+ role: it.checked != null ? "menuitemradio" : "menuitem",
589
+ "aria-checked": it.checked != null ? on : void 0,
590
+ "aria-haspopup": it.submenu ? "menu" : void 0,
591
+ "aria-expanded": it.submenu ? openSub === i : void 0,
592
+ "aria-disabled": it.disabled || void 0,
593
+ className: ["fd-opt", "fd-menu-item", i === active ? "is-active" : "", on ? "is-selected" : ""].filter(Boolean).join(" "),
594
+ onMouseEnter: () => {
595
+ setActive(i);
596
+ if (openSub != null && openSub !== i) setOpenSub(null);
585
597
  },
586
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-" + (ta.icon || "trash"), "aria-hidden": "true" })
587
- }
588
- )
589
- ] }, it.id || it.label || i);
590
- })
591
- }
592
- ),
593
- footer,
598
+ onClick: () => choose(it, i),
599
+ children: [
600
+ it.icon ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-menu-icon", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-" + it.icon, "aria-hidden": "true" }) }) : null,
601
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "fd-menu-text", children: [
602
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-label", children: it.label }),
603
+ it.description ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-desc", children: it.description }) : null
604
+ ] }),
605
+ it.meta ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-meta", children: it.meta }) : null,
606
+ it.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-menu-shortcut", "aria-hidden": "true", children: it.shortcut }) : null,
607
+ it.submenu ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-caret-right fd-menu-more", "aria-hidden": "true" }) : null,
608
+ it.checked != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-check", children: on ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-check", "aria-hidden": "true" }) : null }) : null
609
+ ]
610
+ },
611
+ it.id || it.label || i
612
+ );
613
+ const ta = it.trailingAction;
614
+ if (!ta) return row;
615
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fd-menu-row", onMouseEnter: () => setActive(i), children: [
616
+ row,
617
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
618
+ "button",
619
+ {
620
+ type: "button",
621
+ className: ["fd-menu-row-act", ta.danger ? "is-danger" : ""].filter(Boolean).join(" "),
622
+ "aria-label": ta.label,
623
+ title: ta.label,
624
+ onClick: (e) => {
625
+ e.stopPropagation();
626
+ ta.onSelect && ta.onSelect(it);
627
+ if (ta.closeOnSelect !== false) {
628
+ onClose && onClose();
629
+ }
630
+ },
631
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-" + (ta.icon || "trash"), "aria-hidden": "true" })
632
+ }
633
+ )
634
+ ] }, it.id || it.label || i);
635
+ })
636
+ }
637
+ ),
638
+ footer != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-footer", children: footer }) : null
639
+ ] }),
594
640
  sub && sub.submenu ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
595
641
  Popover,
596
642
  {
@@ -1218,20 +1264,123 @@ function Toast({ title, children, tone = "success", onUndo, onDismiss, className
1218
1264
  // src/components/feedback/Tooltip.tsx
1219
1265
  var React6 = __toESM(require("react"), 1);
1220
1266
  var import_jsx_runtime24 = require("react/jsx-runtime");
1267
+ var CLOSE_DELAY_MS = 120;
1221
1268
  function Tooltip({ label, placement = "top", children, className = "" }) {
1222
- const [show, setShow] = React6.useState(false);
1223
- const pos = placement === "bottom" ? { top: "calc(100% + 8px)", left: "50%", transform: "translateX(-50%)" } : { bottom: "calc(100% + 8px)", left: "50%", transform: "translateX(-50%)" };
1269
+ const anchorRef = React6.useRef(null);
1270
+ const bubbleRef = React6.useRef(null);
1271
+ const [reason, setReason] = React6.useState(null);
1272
+ const closeTimer = React6.useRef(null);
1273
+ const id = React6.useId();
1274
+ const descId = id + "-desc";
1275
+ const open = reason != null;
1276
+ const cancelClose = React6.useCallback(() => {
1277
+ if (closeTimer.current) {
1278
+ clearTimeout(closeTimer.current);
1279
+ closeTimer.current = null;
1280
+ }
1281
+ }, []);
1282
+ const show = (why) => {
1283
+ cancelClose();
1284
+ setReason(why);
1285
+ };
1286
+ const hide = React6.useCallback(() => {
1287
+ cancelClose();
1288
+ setReason(null);
1289
+ }, [cancelClose]);
1290
+ const hover = () => {
1291
+ cancelClose();
1292
+ setReason((r) => r || "hover");
1293
+ };
1294
+ const hideSoon = () => {
1295
+ cancelClose();
1296
+ closeTimer.current = setTimeout(() => {
1297
+ closeTimer.current = null;
1298
+ setReason((r) => r === "hover" ? null : r);
1299
+ }, CLOSE_DELAY_MS);
1300
+ };
1301
+ React6.useEffect(() => cancelClose, [cancelClose]);
1302
+ const pos = usePopoverPosition(open, anchorRef, {
1303
+ placement: placement === "bottom" ? "bottom-center" : "top-center",
1304
+ offset: 8,
1305
+ estHeight: 40,
1306
+ estWidth: 160,
1307
+ layerRef: bubbleRef,
1308
+ onDetach: hide
1309
+ });
1310
+ React6.useEffect(() => {
1311
+ if (!open) return;
1312
+ const key = (e) => {
1313
+ if (e.key === "Escape") hide();
1314
+ };
1315
+ const away = (e) => {
1316
+ const t = e.target;
1317
+ if (anchorRef.current && anchorRef.current.contains(t)) return;
1318
+ if (bubbleRef.current && bubbleRef.current.contains(t)) return;
1319
+ hide();
1320
+ };
1321
+ document.addEventListener("keydown", key);
1322
+ document.addEventListener("pointerdown", away, true);
1323
+ return () => {
1324
+ document.removeEventListener("keydown", key);
1325
+ document.removeEventListener("pointerdown", away, true);
1326
+ };
1327
+ }, [open, hide]);
1328
+ const isMouse = (e) => e.pointerType === "mouse";
1329
+ const onFocus = (e) => {
1330
+ const el = e.target;
1331
+ let keyboard = true;
1332
+ try {
1333
+ keyboard = el.matches(":focus-visible");
1334
+ } catch {
1335
+ }
1336
+ if (keyboard) show("focus");
1337
+ };
1338
+ const described = React6.isValidElement(children) ? React6.cloneElement(children, {
1339
+ "aria-describedby": [children.props["aria-describedby"], descId].filter(Boolean).join(" ")
1340
+ }) : children;
1224
1341
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1225
1342
  "span",
1226
1343
  {
1227
- style: { position: "relative", display: "inline-flex" },
1228
- onMouseEnter: () => setShow(true),
1229
- onMouseLeave: () => setShow(false),
1230
- onFocus: () => setShow(true),
1231
- onBlur: () => setShow(false),
1344
+ ref: anchorRef,
1345
+ className: "fd-tooltip-anchor",
1346
+ "aria-describedby": React6.isValidElement(children) ? void 0 : descId,
1347
+ onPointerEnter: (e) => {
1348
+ if (isMouse(e)) hover();
1349
+ },
1350
+ onPointerLeave: (e) => {
1351
+ if (isMouse(e)) hideSoon();
1352
+ },
1353
+ onPointerUp: (e) => {
1354
+ if (!isMouse(e)) {
1355
+ if (reason === "tap") hide();
1356
+ else show("tap");
1357
+ }
1358
+ },
1359
+ onFocus,
1360
+ onBlur: () => {
1361
+ if (reason === "focus") hide();
1362
+ },
1232
1363
  children: [
1233
- children,
1234
- show ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: ["fd-tooltip", className].filter(Boolean).join(" "), role: "tooltip", style: pos, children: label }) : null
1364
+ described,
1365
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { id: descId, hidden: true, children: label }),
1366
+ open && pos ? portal(
1367
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1368
+ "span",
1369
+ {
1370
+ ref: bubbleRef,
1371
+ role: "tooltip",
1372
+ className: ["fd-tooltip", pos.side === "bottom" ? "is-below" : "", className].filter(Boolean).join(" "),
1373
+ style: { left: pos.left, top: pos.top == null ? "auto" : pos.top, bottom: pos.bottom == null ? "auto" : pos.bottom },
1374
+ onPointerEnter: (e) => {
1375
+ if (isMouse(e)) cancelClose();
1376
+ },
1377
+ onPointerLeave: (e) => {
1378
+ if (isMouse(e)) hideSoon();
1379
+ },
1380
+ children: label
1381
+ }
1382
+ )
1383
+ ) : null
1235
1384
  ]
1236
1385
  }
1237
1386
  );
@@ -2865,6 +3014,7 @@ function VirtualList({
2865
3014
  }
2866
3015
 
2867
3016
  // src/components/data/EntityRow.tsx
3017
+ var React17 = __toESM(require("react"), 1);
2868
3018
  var import_jsx_runtime40 = require("react/jsx-runtime");
2869
3019
  var ENTITY_ROW_METRIC_WIDTH = 74;
2870
3020
  var METRIC_TONE_COLOR = {
@@ -2873,20 +3023,20 @@ var METRIC_TONE_COLOR = {
2873
3023
  danger: "var(--danger-text)"
2874
3024
  };
2875
3025
  function EntityRowMetrics({ metrics, className = "", style }) {
2876
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: ["fd-erow-metrics", className].filter(Boolean).join(" "), style, children: metrics.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2877
- "span",
2878
- {
2879
- className: ["fd-erow-metric", m.tone && m.tone !== "default" ? "is-" + m.tone : ""].filter(Boolean).join(" "),
2880
- style: { width: (m.width || ENTITY_ROW_METRIC_WIDTH) + "px", color: METRIC_TONE_COLOR[m.tone || "default"] },
2881
- title: m.label,
2882
- children: [
2883
- m.icon ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }) : null,
2884
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "fd-erow-metric-value fd-tabular", children: m.value }),
2885
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "fd-sr", children: " " + m.label })
2886
- ]
2887
- },
2888
- m.id || m.label || i
2889
- )) });
3026
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: ["fd-erow-metrics", className].filter(Boolean).join(" "), style, children: metrics.map((m, i) => {
3027
+ const key = m.id || m.label || i;
3028
+ const className2 = ["fd-erow-metric", m.tone && m.tone !== "default" ? "is-" + m.tone : "", m.tooltip != null ? "is-described" : ""].filter(Boolean).join(" ");
3029
+ const cellStyle = { width: (m.width || ENTITY_ROW_METRIC_WIDTH) + "px", color: METRIC_TONE_COLOR[m.tone || "default"] };
3030
+ const content = /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(React17.Fragment, { children: [
3031
+ m.icon ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }) : null,
3032
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "fd-erow-metric-value fd-tabular", children: m.value }),
3033
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "fd-sr", children: " " + m.label })
3034
+ ] });
3035
+ if (m.tooltip == null) {
3036
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: className2, style: cellStyle, title: m.label, children: content }, key);
3037
+ }
3038
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Tooltip, { label: m.tooltip, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: className2, style: cellStyle, children: content }) }, key);
3039
+ }) });
2890
3040
  }
2891
3041
  function EntityRow({
2892
3042
  title,
@@ -2936,8 +3086,10 @@ function EntityRow({
2936
3086
  icon: action.icon,
2937
3087
  label: action.label,
2938
3088
  size: "sm",
2939
- onClick: action.onClick,
2940
- style: { color: action.tone === "add" ? "var(--ok-text)" : action.tone === "remove" ? "var(--danger-text)" : void 0 }
3089
+ variant: "solid",
3090
+ tone: action.tone === "add" ? "ok" : action.tone === "remove" ? "danger" : "neutral",
3091
+ className: "fd-erow-action",
3092
+ onClick: action.onClick
2941
3093
  }
2942
3094
  ) : null
2943
3095
  ]
@@ -2946,14 +3098,14 @@ function EntityRow({
2946
3098
  }
2947
3099
 
2948
3100
  // src/components/data/TransferList.tsx
2949
- var React18 = __toESM(require("react"), 1);
3101
+ var React19 = __toESM(require("react"), 1);
2950
3102
  var import_react_dom4 = require("react-dom");
2951
3103
 
2952
3104
  // src/components/forms/field.tsx
2953
- var React17 = __toESM(require("react"), 1);
3105
+ var React18 = __toESM(require("react"), 1);
2954
3106
  var import_jsx_runtime41 = require("react/jsx-runtime");
2955
3107
  function useFieldId(id) {
2956
- const generated = React17.useId();
3108
+ const generated = React18.useId();
2957
3109
  return id || generated;
2958
3110
  }
2959
3111
  function FieldLabel({ htmlFor, id, required = false, onClick, children }) {
@@ -2994,10 +3146,9 @@ function Input({
2994
3146
  numeric ? "fd-input-num" : "",
2995
3147
  error ? "fd-input-error" : "",
2996
3148
  disabled ? "fd-input-disabled" : "",
2997
- size === "lg" ? "fd-input-lg" : "",
2998
- className
3149
+ size === "lg" ? "fd-input-lg" : ""
2999
3150
  ].filter(Boolean).join(" ");
3000
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "fd-field", style, children: [
3151
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: ["fd-field", className].filter(Boolean).join(" "), style, children: [
3001
3152
  label ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(FieldLabel, { htmlFor: fieldId, required, children: label }) : null,
3002
3153
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: box, children: [
3003
3154
  icon ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "fd-input-icon", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("i", { className: "ph ph-" + icon, "aria-hidden": "true" }) }) : null,
@@ -3072,12 +3223,12 @@ function TransferList({
3072
3223
  listHeight = 440,
3073
3224
  className = ""
3074
3225
  }) {
3075
- const [drag, setDrag] = React18.useState(null);
3076
- const [dragOverSide, setDragOverSide] = React18.useState(null);
3077
- const dragRef = React18.useRef(null);
3078
- const armedRef = React18.useRef(null);
3079
- const sideRefs = React18.useRef({ left: null, right: null });
3080
- const hitTestSide = React18.useCallback((x, y) => {
3226
+ const [drag, setDrag] = React19.useState(null);
3227
+ const [dragOverSide, setDragOverSide] = React19.useState(null);
3228
+ const dragRef = React19.useRef(null);
3229
+ const armedRef = React19.useRef(null);
3230
+ const sideRefs = React19.useRef({ left: null, right: null });
3231
+ const hitTestSide = React19.useCallback((x, y) => {
3081
3232
  for (const side of ["left", "right"]) {
3082
3233
  const el = sideRefs.current[side];
3083
3234
  if (!el) continue;
@@ -3086,7 +3237,7 @@ function TransferList({
3086
3237
  }
3087
3238
  return null;
3088
3239
  }, []);
3089
- const findItem = React18.useCallback(
3240
+ const findItem = React19.useCallback(
3090
3241
  (side, key) => (side === "left" ? left.items : right.items).find((it) => keyOf(it) === key),
3091
3242
  [left.items, right.items, keyOf]
3092
3243
  );
@@ -3108,7 +3259,7 @@ function TransferList({
3108
3259
  height: rect.height
3109
3260
  };
3110
3261
  };
3111
- React18.useEffect(() => {
3262
+ React19.useEffect(() => {
3112
3263
  const onMovePointer = (e) => {
3113
3264
  const armed = armedRef.current;
3114
3265
  if (!armed || e.pointerId !== armed.pointerId) return;
@@ -3161,7 +3312,7 @@ function TransferList({
3161
3312
  window.removeEventListener("pointercancel", endDrag);
3162
3313
  };
3163
3314
  }, [hitTestSide, findItem, onMove]);
3164
- React18.useEffect(() => {
3315
+ React19.useEffect(() => {
3165
3316
  if (!drag || typeof document === "undefined") return;
3166
3317
  const prev = document.body.style.userSelect;
3167
3318
  document.body.style.userSelect = "none";
@@ -3273,11 +3424,11 @@ function TransferList({
3273
3424
  }
3274
3425
 
3275
3426
  // src/components/forms/Checkbox.tsx
3276
- var React19 = __toESM(require("react"), 1);
3427
+ var React20 = __toESM(require("react"), 1);
3277
3428
  var import_jsx_runtime45 = require("react/jsx-runtime");
3278
3429
  function Checkbox({ label, description, card = false, indeterminate = false, className = "", ...rest }) {
3279
- const ref = React19.useRef(null);
3280
- React19.useEffect(() => {
3430
+ const ref = React20.useRef(null);
3431
+ React20.useEffect(() => {
3281
3432
  if (ref.current) ref.current.indeterminate = indeterminate;
3282
3433
  }, [indeterminate]);
3283
3434
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("label", { className: ["fd-choice", card ? "fd-choice-card" : "", className].filter(Boolean).join(" "), children: [
@@ -3324,8 +3475,8 @@ function Switch({ label, description, className = "", ...rest }) {
3324
3475
  var import_jsx_runtime48 = require("react/jsx-runtime");
3325
3476
  function Textarea({ label, help, error, required = false, rows = 4, disabled = false, id, className = "", style, ...rest }) {
3326
3477
  const fieldId = useFieldId(id);
3327
- const box = ["fd-input", "fd-input-textarea", error ? "fd-input-error" : "", disabled ? "fd-input-disabled" : "", className].filter(Boolean).join(" ");
3328
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "fd-field", style, children: [
3478
+ const box = ["fd-input", "fd-input-textarea", error ? "fd-input-error" : "", disabled ? "fd-input-disabled" : ""].filter(Boolean).join(" ");
3479
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: ["fd-field", className].filter(Boolean).join(" "), style, children: [
3329
3480
  label ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(FieldLabel, { htmlFor: fieldId, required, children: label }) : null,
3330
3481
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: box, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("textarea", { id: fieldId, rows, disabled, "aria-invalid": error ? "true" : void 0, ...rest }) }),
3331
3482
  error ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "fd-field-error", children: [
@@ -3336,7 +3487,7 @@ function Textarea({ label, help, error, required = false, rows = 4, disabled = f
3336
3487
  }
3337
3488
 
3338
3489
  // src/components/forms/NumberInput.tsx
3339
- var React20 = __toESM(require("react"), 1);
3490
+ var React21 = __toESM(require("react"), 1);
3340
3491
  var import_jsx_runtime49 = require("react/jsx-runtime");
3341
3492
  function NumberInput({
3342
3493
  label,
@@ -3364,10 +3515,10 @@ function NumberInput({
3364
3515
  const n = Number(String(v == null ? "" : v).replace(/[^0-9.-]/g, ""));
3365
3516
  return isNaN(n) ? null : n;
3366
3517
  };
3367
- const [text, setText] = React20.useState(value == null || value === "" ? "" : String(value));
3368
- const [editing, setEditing] = React20.useState(false);
3369
- const timer = React20.useRef(null);
3370
- React20.useEffect(() => {
3518
+ const [text, setText] = React21.useState(value == null || value === "" ? "" : String(value));
3519
+ const [editing, setEditing] = React21.useState(false);
3520
+ const timer = React21.useRef(null);
3521
+ React21.useEffect(() => {
3371
3522
  if (!editing) setText(value == null || value === "" ? "" : String(value));
3372
3523
  }, [value, editing]);
3373
3524
  const clamp = (n) => Math.min(max, Math.max(min, n));
@@ -3391,7 +3542,7 @@ function NumberInput({
3391
3542
  const release = () => {
3392
3543
  if (timer.current) clearTimeout(timer.current);
3393
3544
  };
3394
- React20.useEffect(() => () => {
3545
+ React21.useEffect(() => () => {
3395
3546
  if (timer.current) clearTimeout(timer.current);
3396
3547
  }, []);
3397
3548
  const shown = editing ? text : (() => {
@@ -3478,52 +3629,9 @@ function NumberInput({
3478
3629
  }
3479
3630
 
3480
3631
  // src/components/forms/Select.tsx
3481
- var React21 = __toESM(require("react"), 1);
3482
- var import_react_dom5 = require("react-dom");
3632
+ var React22 = __toESM(require("react"), 1);
3483
3633
  var import_jsx_runtime50 = require("react/jsx-runtime");
3484
3634
  var norm = (o) => typeof o === "string" ? { value: o, label: o } : o;
3485
- function usePopPos(open, ref, estH, estW) {
3486
- const [pos, setPos] = React21.useState(null);
3487
- React21.useLayoutEffect(() => {
3488
- if (!open || !ref.current) {
3489
- setPos(null);
3490
- return;
3491
- }
3492
- const calc = () => {
3493
- const r = ref.current.getBoundingClientRect();
3494
- const below = window.innerHeight - r.bottom;
3495
- const up = below < estH && r.top > below;
3496
- const w = Math.max(r.width, estW || 0);
3497
- const maxH = Math.min(window.innerHeight - 16, Math.max(160, (up ? r.top : below) - 14));
3498
- setPos({
3499
- left: Math.max(8, Math.min(r.left, window.innerWidth - w - 8)),
3500
- width: r.width,
3501
- top: up ? null : r.bottom + 6,
3502
- bottom: up ? window.innerHeight - r.top + 6 : null,
3503
- up,
3504
- maxH
3505
- });
3506
- };
3507
- calc();
3508
- window.addEventListener("scroll", calc, true);
3509
- window.addEventListener("resize", calc);
3510
- return () => {
3511
- window.removeEventListener("scroll", calc, true);
3512
- window.removeEventListener("resize", calc);
3513
- };
3514
- }, [open]);
3515
- return pos;
3516
- }
3517
- var popStyle = (pos, extra) => ({
3518
- position: "fixed",
3519
- left: pos.left,
3520
- top: pos.top == null ? "auto" : pos.top,
3521
- bottom: pos.bottom == null ? "auto" : pos.bottom,
3522
- transformOrigin: pos.up ? "bottom center" : "top center",
3523
- maxHeight: pos.maxH,
3524
- overflowY: "auto",
3525
- ...extra
3526
- });
3527
3635
  function Select({
3528
3636
  label,
3529
3637
  help,
@@ -3548,17 +3656,15 @@ function Select({
3548
3656
  const isOn = (v) => multiple ? vals.includes(v) : v === value;
3549
3657
  const hasSearch = searchable === void 0 ? opts.length > 8 : searchable;
3550
3658
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
3551
- const [open, setOpen] = React21.useState(false);
3552
- const [q, setQ] = React21.useState("");
3553
- const [active, setActive] = React21.useState(-1);
3554
- const rootRef = React21.useRef(null);
3555
- const boxRef = React21.useRef(null);
3556
- const popRef = React21.useRef(null);
3557
- const listRef = React21.useRef(null);
3558
- const typeBuf = React21.useRef({ s: "", t: 0 });
3659
+ const [open, setOpen] = React22.useState(false);
3660
+ const [q, setQ] = React22.useState("");
3661
+ const [active, setActive] = React22.useState(-1);
3662
+ const rootRef = React22.useRef(null);
3663
+ const boxRef = React22.useRef(null);
3664
+ const listRef = React22.useRef(null);
3665
+ const typeBuf = React22.useRef({ s: "", t: 0 });
3559
3666
  const selected = multiple ? null : opts.find((o) => o.value === value);
3560
3667
  const chosen = multiple ? opts.filter((o) => vals.includes(o.value)) : [];
3561
- const pos = usePopPos(open, boxRef, hasSearch ? 390 : 340, 260);
3562
3668
  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;
3563
3669
  const visible = q ? opts.filter((o) => (o.label + " " + (o.description || "") + " " + (o.group || "")).toLowerCase().includes(q.toLowerCase())) : opts;
3564
3670
  const fire = (v) => {
@@ -3581,17 +3687,7 @@ function Select({
3581
3687
  }
3582
3688
  setOpen(!open);
3583
3689
  };
3584
- React21.useEffect(() => {
3585
- if (!open) return;
3586
- const away = (e) => {
3587
- if (rootRef.current && rootRef.current.contains(e.target)) return;
3588
- if (popRef.current && popRef.current.contains(e.target)) return;
3589
- setOpen(false);
3590
- };
3591
- document.addEventListener("pointerdown", away);
3592
- return () => document.removeEventListener("pointerdown", away);
3593
- }, [open]);
3594
- React21.useEffect(() => {
3690
+ React22.useEffect(() => {
3595
3691
  if (!open || active < 0 || !listRef.current) return;
3596
3692
  const el = listRef.current.querySelector('[data-i="' + active + '"]');
3597
3693
  if (el) {
@@ -3682,94 +3778,96 @@ function Select({
3682
3778
  ) : null,
3683
3779
  loading ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-spinner", style: { color: "var(--text-muted)" }, "aria-label": "Loading options" }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-select-caret", onClick: toggle, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true" }) })
3684
3780
  ] }),
3685
- open && pos ? (0, import_react_dom5.createPortal)(
3686
- /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3687
- "div",
3688
- {
3689
- className: "fd-pop" + (pos.up ? " is-up" : ""),
3690
- ref: popRef,
3691
- style: popStyle(pos, { minWidth: Math.max(pos.width, 260), maxWidth: 380, zIndex: 130, overflowY: "hidden" }),
3692
- children: [
3693
- hasSearch ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "fd-pop-search", children: [
3694
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-magnifying-glass", style: { color: "var(--text-muted)", fontSize: 14 } }),
3695
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3696
- "input",
3697
- {
3698
- autoFocus: true,
3699
- placeholder: "Search\u2026",
3700
- value: q,
3701
- onKeyDown: onKey,
3702
- onChange: (e) => {
3703
- setQ(e.target.value);
3704
- setActive(0);
3705
- }
3706
- }
3707
- ),
3708
- q ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-body-sm fd-muted", children: visible.length }) : null
3709
- ] }) : null,
3710
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "fd-pop-list", role: "listbox", "aria-multiselectable": multiple || void 0, ref: listRef, style: { maxHeight: Math.max(120, pos.maxH - (hasSearch ? 64 : 12) - (multiple ? 42 : 0)) }, children: visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "fd-pop-empty", children: [
3711
- 'Nothing matches "',
3712
- q,
3713
- '".'
3714
- ] }) : groups.map((grp) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(React21.Fragment, { children: [
3715
- grp.g ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "fd-pop-group", children: grp.g }) : null,
3716
- grp.items.map(({ o, i }) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3717
- "button",
3718
- {
3719
- type: "button",
3720
- "data-i": i,
3721
- role: "option",
3722
- "aria-selected": isOn(o.value),
3723
- "aria-disabled": o.disabled || void 0,
3724
- className: ["fd-opt", i === active ? "is-active" : "", isOn(o.value) ? "is-selected" : ""].filter(Boolean).join(" "),
3725
- onMouseEnter: () => setActive(i),
3726
- onClick: () => pick(o),
3727
- children: [
3728
- multiple ? /* @__PURE__ */ (0, import_jsx_runtime50.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_runtime50.jsx)("i", { className: "ph ph-check", style: { fontSize: 11 } }) : null }) : null,
3729
- o.icon ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-icon", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-" + o.icon }) }) : null,
3730
- /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { style: { flex: 1, minWidth: 0 }, children: [
3731
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-label", children: o.label }),
3732
- o.description ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-desc", children: o.description }) : null
3733
- ] }),
3734
- o.meta ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-meta", children: o.meta }) : null,
3735
- multiple ? null : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-check", children: o.value === value ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-check" }) : null })
3736
- ]
3737
- },
3738
- String(o.value)
3739
- ))
3740
- ] }, grp.g || "_")) }),
3741
- multiple ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "fd-row", style: { gap: 10, padding: "8px 12px", borderTop: "1px solid var(--border)" }, children: [
3742
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3743
- "button",
3744
- {
3745
- type: "button",
3746
- onClick: () => fire(visible.filter((o) => !o.disabled).map((o) => o.value)),
3747
- style: { all: "unset", cursor: "pointer", fontSize: "var(--body-sm-size)", fontWeight: 600, color: "var(--brand)" },
3748
- children: "Select all"
3749
- }
3750
- ),
3751
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { style: { flex: 1 } }),
3752
- /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
3753
- vals.length,
3754
- " of ",
3755
- opts.length
3756
- ] }),
3757
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3758
- "button",
3759
- {
3760
- type: "button",
3761
- disabled: !vals.length,
3762
- onClick: () => fire([]),
3763
- style: { all: "unset", cursor: vals.length ? "pointer" : "default", fontSize: "var(--body-sm-size)", fontWeight: 600, color: vals.length ? "var(--text-2)" : "var(--text-muted)" },
3764
- children: "Clear"
3765
- }
3766
- )
3767
- ] }) : null
3768
- ]
3769
- }
3770
- ),
3771
- document.body
3772
- ) : null,
3781
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3782
+ Popover,
3783
+ {
3784
+ open,
3785
+ anchorRef: boxRef,
3786
+ triggerRef: rootRef,
3787
+ onClose: () => setOpen(false),
3788
+ placement: "bottom-start",
3789
+ offset: 6,
3790
+ matchWidth: "min",
3791
+ minWidth: 260,
3792
+ role: "presentation",
3793
+ style: { maxWidth: 380, zIndex: 130 },
3794
+ header: hasSearch ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "fd-pop-search", children: [
3795
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-magnifying-glass", style: { color: "var(--text-muted)", fontSize: 14 } }),
3796
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3797
+ "input",
3798
+ {
3799
+ autoFocus: true,
3800
+ placeholder: "Search\u2026",
3801
+ value: q,
3802
+ onKeyDown: onKey,
3803
+ onChange: (e) => {
3804
+ setQ(e.target.value);
3805
+ setActive(0);
3806
+ }
3807
+ }
3808
+ ),
3809
+ q ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-body-sm fd-muted", children: visible.length }) : null
3810
+ ] }) : void 0,
3811
+ footer: multiple ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(React22.Fragment, { children: [
3812
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3813
+ "button",
3814
+ {
3815
+ type: "button",
3816
+ onClick: () => fire(visible.filter((o) => !o.disabled).map((o) => o.value)),
3817
+ style: { all: "unset", cursor: "pointer", fontSize: "var(--body-sm-size)", fontWeight: 600, color: "var(--brand)" },
3818
+ children: "Select all"
3819
+ }
3820
+ ),
3821
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { style: { flex: 1 } }),
3822
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
3823
+ vals.length,
3824
+ " of ",
3825
+ opts.length
3826
+ ] }),
3827
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3828
+ "button",
3829
+ {
3830
+ type: "button",
3831
+ disabled: !vals.length,
3832
+ onClick: () => fire([]),
3833
+ style: { all: "unset", cursor: vals.length ? "pointer" : "default", fontSize: "var(--body-sm-size)", fontWeight: 600, color: vals.length ? "var(--text-2)" : "var(--text-muted)" },
3834
+ children: "Clear"
3835
+ }
3836
+ )
3837
+ ] }) : void 0,
3838
+ children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "fd-pop-list", role: "listbox", "aria-multiselectable": multiple || void 0, ref: listRef, children: visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "fd-pop-empty", children: [
3839
+ 'Nothing matches "',
3840
+ q,
3841
+ '".'
3842
+ ] }) : groups.map((grp) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(React22.Fragment, { children: [
3843
+ grp.g ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "fd-pop-group", children: grp.g }) : null,
3844
+ grp.items.map(({ o, i }) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3845
+ "button",
3846
+ {
3847
+ type: "button",
3848
+ "data-i": i,
3849
+ role: "option",
3850
+ "aria-selected": isOn(o.value),
3851
+ "aria-disabled": o.disabled || void 0,
3852
+ className: ["fd-opt", i === active ? "is-active" : "", isOn(o.value) ? "is-selected" : ""].filter(Boolean).join(" "),
3853
+ onMouseEnter: () => setActive(i),
3854
+ onClick: () => pick(o),
3855
+ children: [
3856
+ multiple ? /* @__PURE__ */ (0, import_jsx_runtime50.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_runtime50.jsx)("i", { className: "ph ph-check", style: { fontSize: 11 } }) : null }) : null,
3857
+ o.icon ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-icon", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-" + o.icon }) }) : null,
3858
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { style: { flex: 1, minWidth: 0 }, children: [
3859
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-label", children: o.label }),
3860
+ o.description ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-desc", children: o.description }) : null
3861
+ ] }),
3862
+ o.meta ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-meta", children: o.meta }) : null,
3863
+ multiple ? null : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-check", children: o.value === value ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-check" }) : null })
3864
+ ]
3865
+ },
3866
+ String(o.value)
3867
+ ))
3868
+ ] }, grp.g || "_")) })
3869
+ }
3870
+ ),
3773
3871
  error ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: "fd-field-error", children: [
3774
3872
  /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
3775
3873
  error
@@ -3778,8 +3876,7 @@ function Select({
3778
3876
  }
3779
3877
 
3780
3878
  // src/components/forms/DatePicker.tsx
3781
- var React22 = __toESM(require("react"), 1);
3782
- var import_react_dom6 = require("react-dom");
3879
+ var React23 = __toESM(require("react"), 1);
3783
3880
  var import_jsx_runtime51 = require("react/jsx-runtime");
3784
3881
  var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
3785
3882
  var DOW = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
@@ -3793,56 +3890,14 @@ var fmt = (s) => {
3793
3890
  const d = parse(s);
3794
3891
  return d ? MONTHS[d.getMonth()].slice(0, 3) + " " + d.getDate() + ", " + d.getFullYear() : "";
3795
3892
  };
3796
- function usePopPos2(open, ref, estH, estW) {
3797
- const [pos, setPos] = React22.useState(null);
3798
- React22.useLayoutEffect(() => {
3799
- if (!open || !ref.current) {
3800
- setPos(null);
3801
- return;
3802
- }
3803
- const calc = () => {
3804
- const r = ref.current.getBoundingClientRect();
3805
- const below = window.innerHeight - r.bottom;
3806
- const up = below < estH && r.top > below;
3807
- const w = Math.max(r.width, estW || 0);
3808
- const maxH = Math.min(window.innerHeight - 16, Math.max(160, (up ? r.top : below) - 14));
3809
- setPos({
3810
- left: Math.max(8, Math.min(r.left, window.innerWidth - w - 8)),
3811
- width: r.width,
3812
- top: up ? null : r.bottom + 6,
3813
- bottom: up ? window.innerHeight - r.top + 6 : null,
3814
- up,
3815
- maxH
3816
- });
3817
- };
3818
- calc();
3819
- window.addEventListener("scroll", calc, true);
3820
- window.addEventListener("resize", calc);
3821
- return () => {
3822
- window.removeEventListener("scroll", calc, true);
3823
- window.removeEventListener("resize", calc);
3824
- };
3825
- }, [open]);
3826
- return pos;
3827
- }
3828
- var popStyle2 = (pos, extra) => ({
3829
- position: "fixed",
3830
- left: pos.left,
3831
- top: pos.top == null ? "auto" : pos.top,
3832
- bottom: pos.bottom == null ? "auto" : pos.bottom,
3833
- transformOrigin: pos.up ? "bottom center" : "top center",
3834
- maxHeight: pos.maxH,
3835
- overflowY: "auto",
3836
- ...extra
3837
- });
3838
3893
  function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3839
3894
  const today = /* @__PURE__ */ new Date();
3840
3895
  const sel = range ? value || {} : { start: value, end: value };
3841
3896
  const anchor = parse(sel.start) || parse(initialMonth) || today;
3842
- const [vy, setVy] = React22.useState(anchor.getFullYear());
3843
- const [vm, setVm] = React22.useState(anchor.getMonth());
3844
- const [mode2, setMode] = React22.useState("days");
3845
- const [hover, setHover] = React22.useState(null);
3897
+ const [vy, setVy] = React23.useState(anchor.getFullYear());
3898
+ const [vm, setVm] = React23.useState(anchor.getMonth());
3899
+ const [mode2, setMode] = React23.useState("days");
3900
+ const [hover, setHover] = React23.useState(null);
3846
3901
  const s = parse(sel.start), e = parse(sel.end);
3847
3902
  const hoverEnd = range && s && !e && hover ? parse(hover) : null;
3848
3903
  const inRange = (d) => {
@@ -3958,28 +4013,9 @@ function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3958
4013
  }
3959
4014
  function DatePicker({ label, help, error, required = false, disabled = false, range = false, value, onChange, placeholder, id, className = "", style, ...rest }) {
3960
4015
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
3961
- const [open, setOpen] = React22.useState(false);
3962
- const rootRef = React22.useRef(null);
3963
- const boxRef = React22.useRef(null);
3964
- const popRef = React22.useRef(null);
3965
- const pos = usePopPos2(open, boxRef, 430, range ? 600 : 316);
3966
- React22.useEffect(() => {
3967
- if (!open) return;
3968
- const away = (e) => {
3969
- if (rootRef.current && rootRef.current.contains(e.target)) return;
3970
- if (popRef.current && popRef.current.contains(e.target)) return;
3971
- setOpen(false);
3972
- };
3973
- const esc = (e) => {
3974
- if (e.key === "Escape") setOpen(false);
3975
- };
3976
- document.addEventListener("pointerdown", away);
3977
- document.addEventListener("keydown", esc);
3978
- return () => {
3979
- document.removeEventListener("pointerdown", away);
3980
- document.removeEventListener("keydown", esc);
3981
- };
3982
- }, [open]);
4016
+ const [open, setOpen] = React23.useState(false);
4017
+ const rootRef = React23.useRef(null);
4018
+ const boxRef = React23.useRef(null);
3983
4019
  const display = range ? value && value.start ? fmt(value.start) + (value.end ? " \u2192 " + fmt(value.end) : " \u2192 \u2026") : "" : fmt(value);
3984
4020
  const toggle = () => {
3985
4021
  if (!disabled) setOpen(!open);
@@ -4008,21 +4044,31 @@ function DatePicker({ label, help, error, required = false, disabled = false, ra
4008
4044
  ),
4009
4045
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "fd-select-caret", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true" }) })
4010
4046
  ] }),
4011
- open && pos ? (0, import_react_dom6.createPortal)(
4012
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "fd-pop" + (pos.up ? " is-up" : ""), ref: popRef, style: popStyle2(pos, { width: "max-content", minWidth: 0, zIndex: 130 }), children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
4013
- Calendar,
4014
- {
4015
- range,
4016
- months: range ? 2 : 1,
4017
- value,
4018
- onPick: (v) => {
4019
- onChange && onChange({ target: { value: v } });
4020
- if (!range || v.start && v.end) setOpen(false);
4047
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
4048
+ Popover,
4049
+ {
4050
+ open,
4051
+ anchorRef: boxRef,
4052
+ triggerRef: rootRef,
4053
+ onClose: () => setOpen(false),
4054
+ placement: "bottom-start",
4055
+ offset: 6,
4056
+ role: "presentation",
4057
+ style: { width: "max-content", zIndex: 130 },
4058
+ children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
4059
+ Calendar,
4060
+ {
4061
+ range,
4062
+ months: range ? 2 : 1,
4063
+ value,
4064
+ onPick: (v) => {
4065
+ onChange && onChange({ target: { value: v } });
4066
+ if (!range || v.start && v.end) setOpen(false);
4067
+ }
4021
4068
  }
4022
- }
4023
- ) }),
4024
- document.body
4025
- ) : null,
4069
+ )
4070
+ }
4071
+ ),
4026
4072
  error ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("span", { className: "fd-field-error", children: [
4027
4073
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
4028
4074
  error
@@ -4031,61 +4077,18 @@ function DatePicker({ label, help, error, required = false, disabled = false, ra
4031
4077
  }
4032
4078
 
4033
4079
  // src/components/forms/TimePicker.tsx
4034
- var React23 = __toESM(require("react"), 1);
4035
- var import_react_dom7 = require("react-dom");
4080
+ var React24 = __toESM(require("react"), 1);
4036
4081
  var import_jsx_runtime52 = require("react/jsx-runtime");
4037
4082
  var pad = (n) => String(n).padStart(2, "0");
4038
- function usePopPos3(open, ref, estH, estW) {
4039
- const [pos, setPos] = React23.useState(null);
4040
- React23.useLayoutEffect(() => {
4041
- if (!open || !ref.current) {
4042
- setPos(null);
4043
- return;
4044
- }
4045
- const calc = () => {
4046
- const r = ref.current.getBoundingClientRect();
4047
- const below = window.innerHeight - r.bottom;
4048
- const up = below < estH && r.top > below;
4049
- const w = Math.max(r.width, estW || 0);
4050
- const maxH = Math.min(window.innerHeight - 16, Math.max(160, (up ? r.top : below) - 14));
4051
- setPos({
4052
- left: Math.max(8, Math.min(r.left, window.innerWidth - w - 8)),
4053
- width: r.width,
4054
- top: up ? null : r.bottom + 6,
4055
- bottom: up ? window.innerHeight - r.top + 6 : null,
4056
- up,
4057
- maxH
4058
- });
4059
- };
4060
- calc();
4061
- window.addEventListener("scroll", calc, true);
4062
- window.addEventListener("resize", calc);
4063
- return () => {
4064
- window.removeEventListener("scroll", calc, true);
4065
- window.removeEventListener("resize", calc);
4066
- };
4067
- }, [open]);
4068
- return pos;
4069
- }
4070
- var popStyle3 = (pos, extra) => ({
4071
- position: "fixed",
4072
- left: pos.left,
4073
- top: pos.top == null ? "auto" : pos.top,
4074
- bottom: pos.bottom == null ? "auto" : pos.bottom,
4075
- transformOrigin: pos.up ? "bottom center" : "top center",
4076
- maxHeight: pos.maxH,
4077
- overflowY: "auto",
4078
- ...extra
4079
- });
4080
4083
  function ClockFace({ value = "09:00", onChange }) {
4081
4084
  let [h24, m] = value.split(":").map(Number);
4082
4085
  if (isNaN(h24)) h24 = 9;
4083
4086
  if (isNaN(m)) m = 0;
4084
4087
  const pm = h24 >= 12;
4085
4088
  const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
4086
- const [mode2, setMode] = React23.useState("h");
4087
- const faceRef = React23.useRef(null);
4088
- const dragging = React23.useRef(false);
4089
+ const [mode2, setMode] = React24.useState("h");
4090
+ const faceRef = React24.useRef(null);
4091
+ const dragging = React24.useRef(false);
4089
4092
  const set = (h, mm, isPm) => onChange((isPm ? h % 12 + 12 : h % 12) + ":" + pad(mm));
4090
4093
  const R = 108, NR = 80;
4091
4094
  const nums = mode2 === "h" ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 12 }, (_, i) => i * 5);
@@ -4158,28 +4161,9 @@ function ClockFace({ value = "09:00", onChange }) {
4158
4161
  }
4159
4162
  function TimePicker({ label, help, error, required = false, disabled = false, value = "", onChange, placeholder = "Pick a time", id, className = "", style }) {
4160
4163
  const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
4161
- const [open, setOpen] = React23.useState(false);
4162
- const rootRef = React23.useRef(null);
4163
- const boxRef = React23.useRef(null);
4164
- const popRef = React23.useRef(null);
4165
- const pos = usePopPos3(open, boxRef, 420, 262);
4166
- React23.useEffect(() => {
4167
- if (!open) return;
4168
- const away = (e) => {
4169
- if (rootRef.current && rootRef.current.contains(e.target)) return;
4170
- if (popRef.current && popRef.current.contains(e.target)) return;
4171
- setOpen(false);
4172
- };
4173
- const esc = (e) => {
4174
- if (e.key === "Escape") setOpen(false);
4175
- };
4176
- document.addEventListener("pointerdown", away);
4177
- document.addEventListener("keydown", esc);
4178
- return () => {
4179
- document.removeEventListener("pointerdown", away);
4180
- document.removeEventListener("keydown", esc);
4181
- };
4182
- }, [open]);
4164
+ const [open, setOpen] = React24.useState(false);
4165
+ const rootRef = React24.useRef(null);
4166
+ const boxRef = React24.useRef(null);
4183
4167
  const disp = () => {
4184
4168
  if (!value) return "";
4185
4169
  const [h, m] = value.split(":").map(Number);
@@ -4211,10 +4195,19 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
4211
4195
  ),
4212
4196
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "fd-select-caret", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true" }) })
4213
4197
  ] }),
4214
- open && pos ? (0, import_react_dom7.createPortal)(
4215
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "fd-pop" + (pos.up ? " is-up" : ""), ref: popRef, style: popStyle3(pos, { width: 262, minWidth: 0, zIndex: 130 }), children: [
4216
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ClockFace, { value: value || "09:00", onChange: (v) => onChange && onChange({ target: { value: v } }) }),
4217
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "fd-cal-foot", style: { margin: "0 14px 12px", paddingTop: 10 }, children: [
4198
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
4199
+ Popover,
4200
+ {
4201
+ open,
4202
+ anchorRef: boxRef,
4203
+ triggerRef: rootRef,
4204
+ onClose: () => setOpen(false),
4205
+ placement: "bottom-start",
4206
+ offset: 6,
4207
+ width: 262,
4208
+ role: "presentation",
4209
+ style: { zIndex: 130 },
4210
+ footer: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(React24.Fragment, { children: [
4218
4211
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
4219
4212
  "button",
4220
4213
  {
@@ -4230,10 +4223,10 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
4230
4223
  ),
4231
4224
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { style: { flex: 1 } }),
4232
4225
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("button", { type: "button", className: "fd-btn fd-btn-primary fd-btn-sm", onClick: () => setOpen(false), children: "Done" })
4233
- ] })
4234
- ] }),
4235
- document.body
4236
- ) : null,
4226
+ ] }),
4227
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ClockFace, { value: value || "09:00", onChange: (v) => onChange && onChange({ target: { value: v } }) })
4228
+ }
4229
+ ),
4237
4230
  error ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("span", { className: "fd-field-error", children: [
4238
4231
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
4239
4232
  error
@@ -4242,7 +4235,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
4242
4235
  }
4243
4236
 
4244
4237
  // src/components/forms/Slider.tsx
4245
- var React24 = __toESM(require("react"), 1);
4238
+ var React25 = __toESM(require("react"), 1);
4246
4239
  var import_jsx_runtime53 = require("react/jsx-runtime");
4247
4240
  function Slider({
4248
4241
  label,
@@ -4260,7 +4253,7 @@ function Slider({
4260
4253
  ...rest
4261
4254
  }) {
4262
4255
  const fieldId = useFieldId(id);
4263
- const [dragging, setDragging] = React24.useState(false);
4256
+ const [dragging, setDragging] = React25.useState(false);
4264
4257
  const v = value === void 0 ? min : Number(value);
4265
4258
  const pct = max === min ? 0 : (v - min) / (max - min) * 100;
4266
4259
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: ["fd-field", className].filter(Boolean).join(" "), children: [
@@ -4294,7 +4287,7 @@ function Slider({
4294
4287
  }
4295
4288
 
4296
4289
  // src/components/forms/RangeSlider.tsx
4297
- var React25 = __toESM(require("react"), 1);
4290
+ var React26 = __toESM(require("react"), 1);
4298
4291
  var import_jsx_runtime54 = require("react/jsx-runtime");
4299
4292
  function RangeSlider({
4300
4293
  label,
@@ -4314,9 +4307,9 @@ function RangeSlider({
4314
4307
  }) {
4315
4308
  const fmt2 = format || ((v) => String(v));
4316
4309
  const [a, b] = value || [min, max];
4317
- const [drag, setDrag] = React25.useState(null);
4318
- const [focus, setFocus] = React25.useState(null);
4319
- const railRef = React25.useRef(null);
4310
+ const [drag, setDrag] = React26.useState(null);
4311
+ const [focus, setFocus] = React26.useState(null);
4312
+ const railRef = React26.useRef(null);
4320
4313
  const pct = (v) => max === min ? 0 : (v - min) / (max - min) * 100;
4321
4314
  const clampPair = (i, v) => {
4322
4315
  v = Math.min(max, Math.max(min, Math.round(v / step) * step));
@@ -4331,7 +4324,7 @@ function RangeSlider({
4331
4324
  const r = railRef.current.getBoundingClientRect();
4332
4325
  return min + Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)) * (max - min);
4333
4326
  };
4334
- React25.useEffect(() => {
4327
+ React26.useEffect(() => {
4335
4328
  if (drag === null) return;
4336
4329
  const mv = (e) => onChange && onChange(clampPair(drag, fromEvent(e)));
4337
4330
  const upH = () => setDrag(null);
@@ -4412,7 +4405,7 @@ function RangeSlider({
4412
4405
  /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "fd-range" + (hasLabels ? " has-labels" : ""), onPointerDown: onRailDown, children: [
4413
4406
  /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "fd-range-rail", ref: railRef }),
4414
4407
  /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "fd-range-fill", style: { left: pct(a) + "%", width: pct(b) - pct(a) + "%" } }),
4415
- marks.map((m) => /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(React25.Fragment, { children: [
4408
+ marks.map((m) => /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(React26.Fragment, { children: [
4416
4409
  /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "fd-range-mark" + (m.value >= a && m.value <= b ? " is-in" : ""), style: { left: pct(m.value) + "%" } }),
4417
4410
  m.label ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "fd-range-mark-label", style: { left: pct(m.value) + "%" }, children: m.label }) : null
4418
4411
  ] }, m.value)),
@@ -4441,7 +4434,7 @@ function RangeSlider({
4441
4434
  }
4442
4435
 
4443
4436
  // src/components/forms/Dropzone.tsx
4444
- var React26 = __toESM(require("react"), 1);
4437
+ var React27 = __toESM(require("react"), 1);
4445
4438
  var import_jsx_runtime55 = require("react/jsx-runtime");
4446
4439
  function Dropzone({
4447
4440
  onFiles,
@@ -4456,8 +4449,8 @@ function Dropzone({
4456
4449
  className = "",
4457
4450
  style
4458
4451
  }) {
4459
- const [over, setOver] = React26.useState(false);
4460
- const depth = React26.useRef(0);
4452
+ const [over, setOver] = React27.useState(false);
4453
+ const depth = React27.useRef(0);
4461
4454
  const has = (e) => {
4462
4455
  const dt = e.dataTransfer;
4463
4456
  if (!dt) return false;
@@ -4519,8 +4512,8 @@ function FilePickButton({
4519
4512
  className = "",
4520
4513
  children
4521
4514
  }) {
4522
- const ref = React26.useRef(null);
4523
- return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(React26.Fragment, { children: [
4515
+ const ref = React27.useRef(null);
4516
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(React27.Fragment, { children: [
4524
4517
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4525
4518
  "button",
4526
4519
  {
@@ -4553,10 +4546,10 @@ function FilePickButton({
4553
4546
  }
4554
4547
  function useStagedFiles(upload, opts) {
4555
4548
  const o = opts || {};
4556
- const [items, setItems] = React26.useState([]);
4557
- const controllers = React26.useRef({});
4549
+ const [items, setItems] = React27.useState([]);
4550
+ const controllers = React27.useRef({});
4558
4551
  const patch = (id, next) => setItems((list) => list.map((f) => f.id === id ? Object.assign({}, f, next) : f));
4559
- const run = React26.useCallback((att) => {
4552
+ const run = React27.useCallback((att) => {
4560
4553
  if (!upload) return;
4561
4554
  const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
4562
4555
  controllers.current[att.id] = ac;
@@ -4573,14 +4566,14 @@ function useStagedFiles(upload, opts) {
4573
4566
  delete controllers.current[att.id];
4574
4567
  });
4575
4568
  }, [upload]);
4576
- const add = React26.useCallback((files) => {
4569
+ const add = React27.useCallback((files) => {
4577
4570
  if (!upload) return [];
4578
4571
  const atts = Array.from(files).map((f) => toAttachment(f));
4579
4572
  setItems((list) => list.concat(atts));
4580
4573
  atts.forEach(run);
4581
4574
  return atts;
4582
4575
  }, [upload, run]);
4583
- const remove = React26.useCallback((att) => {
4576
+ const remove = React27.useCallback((att) => {
4584
4577
  const ac = controllers.current[att.id];
4585
4578
  if (ac) {
4586
4579
  try {
@@ -4591,10 +4584,10 @@ function useStagedFiles(upload, opts) {
4591
4584
  }
4592
4585
  setItems((list) => list.filter((f) => f.id !== att.id));
4593
4586
  }, []);
4594
- const retry = React26.useCallback((att) => {
4587
+ const retry = React27.useCallback((att) => {
4595
4588
  run(att);
4596
4589
  }, [run]);
4597
- const clear = React26.useCallback(() => {
4590
+ const clear = React27.useCallback(() => {
4598
4591
  Object.values(controllers.current).forEach((ac) => {
4599
4592
  try {
4600
4593
  ac && ac.abort();
@@ -4718,7 +4711,7 @@ function FileGrid({ files = [], onOpen, onRemove, onRetry, tiles = true, maxHeig
4718
4711
  }
4719
4712
 
4720
4713
  // src/components/forms/MarkdownEditor.tsx
4721
- var React27 = __toESM(require("react"), 1);
4714
+ var React28 = __toESM(require("react"), 1);
4722
4715
  var import_jsx_runtime57 = require("react/jsx-runtime");
4723
4716
  var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || "");
4724
4717
  var INLINE_RE = /(\*\*\*[^*\n]+\*\*\*|\*\*[^*\n]+\*\*|__[^_\n]+__|~~[^~\n]+~~|`[^`\n]+`|\*[^*\s][^*\n]*\*|(?<![A-Za-z0-9_])_[^_\s][^_\n]*_|\[[^\]\n]*\]\([^)\s\n]*\)|https?:\/\/\S+)/g;
@@ -4932,7 +4925,7 @@ function syncDom(root, value) {
4932
4925
  while (root.children.length > lines.length) root.removeChild(root.lastChild);
4933
4926
  }
4934
4927
  var LIST_CONT = RE_LI;
4935
- var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
4928
+ var MarkdownEditor = React28.forwardRef(function MarkdownEditor2({
4936
4929
  value = "",
4937
4930
  onChange,
4938
4931
  onSubmit,
@@ -4951,10 +4944,10 @@ var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
4951
4944
  className = "",
4952
4945
  id
4953
4946
  }, ref) {
4954
- const boxRef = React27.useRef(null);
4955
- const composing = React27.useRef(false);
4956
- const pendingCaret = React27.useRef(null);
4957
- React27.useLayoutEffect(() => {
4947
+ const boxRef = React28.useRef(null);
4948
+ const composing = React28.useRef(false);
4949
+ const pendingCaret = React28.useRef(null);
4950
+ React28.useLayoutEffect(() => {
4958
4951
  const root = boxRef.current;
4959
4952
  if (!root || composing.current) return;
4960
4953
  const active = document.activeElement === root || root.contains(document.activeElement);
@@ -4963,7 +4956,7 @@ var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
4963
4956
  pendingCaret.current = null;
4964
4957
  if (active && caret != null) placeCaret(root, caret);
4965
4958
  }, [value]);
4966
- React27.useEffect(() => {
4959
+ React28.useEffect(() => {
4967
4960
  if (autoFocus && boxRef.current) boxRef.current.focus();
4968
4961
  }, [autoFocus]);
4969
4962
  const caretNow = () => {
@@ -5030,7 +5023,7 @@ var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
5030
5023
  api.replaceRange(from, to, next, from + next.length);
5031
5024
  }
5032
5025
  };
5033
- React27.useImperativeHandle(ref, () => api);
5026
+ React28.useImperativeHandle(ref, () => api);
5034
5027
  function detect(text, caret) {
5035
5028
  if (!onTrigger) return;
5036
5029
  const upto = text.slice(0, caret);
@@ -5182,10 +5175,10 @@ var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
5182
5175
  });
5183
5176
 
5184
5177
  // src/components/platform/AccountMenu.tsx
5185
- var React29 = __toESM(require("react"), 1);
5178
+ var React30 = __toESM(require("react"), 1);
5186
5179
 
5187
5180
  // src/kits/session.ts
5188
- var React28 = __toESM(require("react"), 1);
5181
+ var React29 = __toESM(require("react"), 1);
5189
5182
  var PERMISSION_CATALOG = [
5190
5183
  { group: "Plans", items: [
5191
5184
  { key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
@@ -6752,8 +6745,8 @@ function roadmap(overrides) {
6752
6745
  };
6753
6746
  }
6754
6747
  function useSession() {
6755
- const [s, setS] = React28.useState(getSession);
6756
- React28.useEffect(() => subscribe(setS), []);
6748
+ const [s, setS] = React29.useState(getSession);
6749
+ React29.useEffect(() => subscribe(setS), []);
6757
6750
  return s;
6758
6751
  }
6759
6752
  var SessionKit = {
@@ -6823,9 +6816,9 @@ function AccountMenu({
6823
6816
  ...rest
6824
6817
  }) {
6825
6818
  const session = SessionKit.useSession();
6826
- const [open, setOpen] = React29.useState(false);
6827
- const [switching, setSwitching] = React29.useState(false);
6828
- const ref = React29.useRef(null);
6819
+ const [open, setOpen] = React30.useState(false);
6820
+ const [switching, setSwitching] = React30.useState(false);
6821
+ const ref = React30.useRef(null);
6829
6822
  const user = session.user;
6830
6823
  const visibleAdmin = adminLinks.filter((l) => !l.perm || SessionKit.can(l.perm));
6831
6824
  const pick = (item) => {
@@ -6877,7 +6870,7 @@ function AccountMenu({
6877
6870
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: "View as another member" }),
6878
6871
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-" + (switching ? "up" : "down"), "aria-hidden": "true", style: { fontSize: 11 } })
6879
6872
  ] }),
6880
- switching ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-stack", style: { gap: 0, maxHeight: 208, overflowY: "auto" }, children: session.allUsers.filter((u) => u.status === "active").map((u) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
6873
+ switching ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "fd-stack", style: { gap: 0 }, children: session.allUsers.filter((u) => u.status === "active").map((u) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
6881
6874
  "button",
6882
6875
  {
6883
6876
  type: "button",
@@ -6909,14 +6902,14 @@ function AccountMenu({
6909
6902
  }
6910
6903
 
6911
6904
  // src/components/platform/ApiSpecBrowser.tsx
6912
- var React30 = __toESM(require("react"), 1);
6905
+ var React31 = __toESM(require("react"), 1);
6913
6906
  var import_jsx_runtime59 = require("react/jsx-runtime");
6914
6907
  var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
6915
6908
  function Json({ obj }) {
6916
6909
  return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
6917
6910
  }
6918
6911
  function Endpoint({ s, onRequest }) {
6919
- const [tried, setTried] = React30.useState(null);
6912
+ const [tried, setTried] = React31.useState(null);
6920
6913
  const run = async () => {
6921
6914
  setTried("busy");
6922
6915
  const t0 = (window.performance || Date).now();
@@ -6989,10 +6982,10 @@ function ApiSpecBrowser({
6989
6982
  className = "",
6990
6983
  ...rest
6991
6984
  }) {
6992
- const [q, setQ] = React30.useState("");
6993
- const [method, setMethod] = React30.useState(null);
6994
- const [mod, setMod] = React30.useState(null);
6995
- const [listOnly, setListOnly] = React30.useState(false);
6985
+ const [q, setQ] = React31.useState("");
6986
+ const [method, setMethod] = React31.useState(null);
6987
+ const [mod, setMod] = React31.useState(null);
6988
+ const [listOnly, setListOnly] = React31.useState(false);
6996
6989
  const activeModule = modules && modules.find((m) => m.id === mod);
6997
6990
  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())));
6998
6991
  const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
@@ -7071,7 +7064,7 @@ function ApiSpecBrowser({
7071
7064
  }
7072
7065
 
7073
7066
  // src/components/platform/ProfilePage.tsx
7074
- var React31 = __toESM(require("react"), 1);
7067
+ var React32 = __toESM(require("react"), 1);
7075
7068
  var import_jsx_runtime60 = require("react/jsx-runtime");
7076
7069
  var TIMEZONES = ["America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles", "America/Anchorage", "Pacific/Honolulu", "Europe/London", "Europe/Berlin"];
7077
7070
  var NOTIFY = [
@@ -7084,7 +7077,7 @@ var NOTIFY = [
7084
7077
  function ProfilePage({ user: userProp, onSave, onNavigate, sessions, showSessions = true, className = "", ...rest }) {
7085
7078
  const session = SessionKit.useSession();
7086
7079
  const user = userProp || session.user;
7087
- const [draft, setDraft] = React31.useState(() => ({
7080
+ const [draft, setDraft] = React32.useState(() => ({
7088
7081
  name: user.name || "",
7089
7082
  title: user.title || "",
7090
7083
  email: user.email || "",
@@ -7093,8 +7086,8 @@ function ProfilePage({ user: userProp, onSave, onNavigate, sessions, showSession
7093
7086
  bio: user.bio || "",
7094
7087
  notify: user.notify || { planShared: true, planChanged: true, goalMissed: true, flagChanged: false, weekly: true }
7095
7088
  }));
7096
- const [saving, setSaving] = React31.useState(false);
7097
- const [saved, setSaved] = React31.useState(false);
7089
+ const [saving, setSaving] = React32.useState(false);
7090
+ const [saved, setSaved] = React32.useState(false);
7098
7091
  const set = (k, v) => {
7099
7092
  setDraft((d) => Object.assign({}, d, { [k]: v }));
7100
7093
  setSaved(false);
@@ -7284,10 +7277,10 @@ function ProfilePage({ user: userProp, onSave, onNavigate, sessions, showSession
7284
7277
  }
7285
7278
 
7286
7279
  // src/components/platform/RoadmapTimeline.tsx
7287
- var React33 = __toESM(require("react"), 1);
7280
+ var React34 = __toESM(require("react"), 1);
7288
7281
 
7289
7282
  // src/kits/runtime.ts
7290
- var React32 = __toESM(require("react"), 1);
7283
+ var React33 = __toESM(require("react"), 1);
7291
7284
  var WIRED_ENDPOINTS = [
7292
7285
  // Nothing yet. Every id below would come from a real service:
7293
7286
  // "plan.get", "placements.list", …
@@ -7458,8 +7451,8 @@ var RuntimeKit = {
7458
7451
  };
7459
7452
  RuntimeKit.declare(FEATURE_NEEDS);
7460
7453
  function useRuntimeMode() {
7461
- const [m, setM] = React32.useState(RuntimeKit.getMode());
7462
- React32.useEffect(() => RuntimeKit.subscribe(setM), []);
7454
+ const [m, setM] = React33.useState(RuntimeKit.getMode());
7455
+ React33.useEffect(() => RuntimeKit.subscribe(setM), []);
7463
7456
  return m;
7464
7457
  }
7465
7458
  function useFeatureStatus(key) {
@@ -7542,12 +7535,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
7542
7535
  }
7543
7536
  function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7544
7537
  const session = SessionKit.useSession();
7545
- const [project, setProject] = React33.useState("");
7546
- const [q, setQ] = React33.useState("");
7547
- const scrollRef = React33.useRef(null);
7548
- const nowRef = React33.useRef(null);
7549
- const rm = React33.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
7550
- const byKey = React33.useMemo(() => {
7538
+ const [project, setProject] = React34.useState("");
7539
+ const [q, setQ] = React34.useState("");
7540
+ const scrollRef = React34.useRef(null);
7541
+ const nowRef = React34.useRef(null);
7542
+ const rm = React34.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
7543
+ const byKey = React34.useMemo(() => {
7551
7544
  const m = {};
7552
7545
  rm.items.forEach((i) => {
7553
7546
  m[i.key] = i;
@@ -7556,7 +7549,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7556
7549
  }, [rm]);
7557
7550
  const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
7558
7551
  const projects = [...new Set(rm.items.map((i) => i.project))];
7559
- React33.useEffect(() => {
7552
+ React34.useEffect(() => {
7560
7553
  let raf1 = 0, raf2 = 0;
7561
7554
  const place = () => {
7562
7555
  const box = scrollRef.current, mark = nowRef.current;
@@ -7630,7 +7623,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7630
7623
  lastPhase = item.phase;
7631
7624
  const inPhase = items.filter((i) => i.phase === item.phase).length;
7632
7625
  const isBoundary = n === firstPending;
7633
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(React33.Fragment, { children: [
7626
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(React34.Fragment, { children: [
7634
7627
  showPhase ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-rm-era", children: [
7635
7628
  /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
7636
7629
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
@@ -7674,8 +7667,8 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7674
7667
  }
7675
7668
 
7676
7669
  // src/components/platform/ComingSoon.tsx
7677
- var React34 = __toESM(require("react"), 1);
7678
- var import_react_dom8 = require("react-dom");
7670
+ var React35 = __toESM(require("react"), 1);
7671
+ var import_react_dom5 = require("react-dom");
7679
7672
  var import_jsx_runtime62 = require("react/jsx-runtime");
7680
7673
  var BYPASS_STORE = "fd.soon.bypass.v1";
7681
7674
  function readBypassed() {
@@ -7692,8 +7685,8 @@ function writeBypassed(list) {
7692
7685
  }
7693
7686
  }
7694
7687
  function useBypass(key) {
7695
- const [on, setOn] = React34.useState(() => !!key && readBypassed().indexOf(key) >= 0);
7696
- React34.useEffect(() => {
7688
+ const [on, setOn] = React35.useState(() => !!key && readBypassed().indexOf(key) >= 0);
7689
+ React35.useEffect(() => {
7697
7690
  setOn(!!key && readBypassed().indexOf(key) >= 0);
7698
7691
  }, [key]);
7699
7692
  const set = (next) => {
@@ -7740,9 +7733,9 @@ function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onB
7740
7733
  ] });
7741
7734
  }
7742
7735
  function useHoverCard(open) {
7743
- const anchor = React34.useRef(null);
7744
- const [pos, setPos] = React34.useState(null);
7745
- React34.useLayoutEffect(() => {
7736
+ const anchor = React35.useRef(null);
7737
+ const [pos, setPos] = React35.useState(null);
7738
+ React35.useLayoutEffect(() => {
7746
7739
  if (!open || !anchor.current) {
7747
7740
  setPos(null);
7748
7741
  return;
@@ -7869,9 +7862,9 @@ function ComingSoon({
7869
7862
  ] });
7870
7863
  }
7871
7864
  function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
7872
- const [open, setOpen] = React34.useState(false);
7865
+ const [open, setOpen] = React35.useState(false);
7873
7866
  const [anchor, pos] = useHoverCard(open);
7874
- const close = React34.useRef(null);
7867
+ const close = React35.useRef(null);
7875
7868
  const show = () => {
7876
7869
  if (close.current) {
7877
7870
  clearTimeout(close.current);
@@ -7887,7 +7880,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
7887
7880
  setOpen(false);
7888
7881
  }, 140);
7889
7882
  };
7890
- React34.useEffect(() => () => {
7883
+ React35.useEffect(() => () => {
7891
7884
  if (close.current) clearTimeout(close.current);
7892
7885
  }, []);
7893
7886
  return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
@@ -7916,7 +7909,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
7916
7909
  children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
7917
7910
  }
7918
7911
  ),
7919
- open && pos ? (0, import_react_dom8.createPortal)(
7912
+ open && pos ? (0, import_react_dom5.createPortal)(
7920
7913
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
7921
7914
  "div",
7922
7915
  {
@@ -8056,13 +8049,13 @@ function PermissionHint({ perm, children }) {
8056
8049
  }
8057
8050
 
8058
8051
  // src/components/platform/ModeSwitch.tsx
8059
- var React35 = __toESM(require("react"), 1);
8052
+ var React36 = __toESM(require("react"), 1);
8060
8053
  var import_jsx_runtime64 = require("react/jsx-runtime");
8061
8054
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
8062
8055
  const mode2 = useRuntimeMode();
8063
- const [open, setOpen] = React35.useState(false);
8064
- const ref = React35.useRef(null);
8065
- React35.useEffect(() => {
8056
+ const [open, setOpen] = React36.useState(false);
8057
+ const ref = React36.useRef(null);
8058
+ React36.useEffect(() => {
8066
8059
  if (!open) return;
8067
8060
  const away = (e) => {
8068
8061
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -8311,11 +8304,11 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8311
8304
  }
8312
8305
 
8313
8306
  // src/components/planner/MixGap.tsx
8314
- var React36 = __toESM(require("react"), 1);
8307
+ var React37 = __toESM(require("react"), 1);
8315
8308
  var import_jsx_runtime67 = require("react/jsx-runtime");
8316
8309
  function MixGap({ rows = [], loading = false, className = "" }) {
8317
8310
  const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
8318
- const [hover, setHover] = React36.useState(null);
8311
+ const [hover, setHover] = React37.useState(null);
8319
8312
  const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
8320
8313
  const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
8321
8314
  return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 4 }, children: [
@@ -8455,7 +8448,7 @@ function ChannelContribution({
8455
8448
  }
8456
8449
 
8457
8450
  // src/components/planner/BudgetReallocator.tsx
8458
- var React37 = __toESM(require("react"), 1);
8451
+ var React38 = __toESM(require("react"), 1);
8459
8452
  var import_jsx_runtime69 = require("react/jsx-runtime");
8460
8453
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
8461
8454
  function BudgetReallocator({
@@ -8471,8 +8464,8 @@ function BudgetReallocator({
8471
8464
  onCancel,
8472
8465
  className = ""
8473
8466
  }) {
8474
- const [draft, setDraft] = React37.useState(spend);
8475
- React37.useEffect(() => setDraft(spend), [spend]);
8467
+ const [draft, setDraft] = React38.useState(spend);
8468
+ React38.useEffect(() => setDraft(spend), [spend]);
8476
8469
  const dirty = draft !== spend;
8477
8470
  const nextCrp = scoreFor ? scoreFor(draft) : crp;
8478
8471
  const nextBand = bandFor(nextCrp);
@@ -8642,7 +8635,7 @@ function ImportanceTag({ level, showLabel = true, size = "sm", className = "" })
8642
8635
  }
8643
8636
 
8644
8637
  // src/components/planner/PreferenceMeter.tsx
8645
- var React38 = __toESM(require("react"), 1);
8638
+ var React39 = __toESM(require("react"), 1);
8646
8639
  var import_jsx_runtime72 = require("react/jsx-runtime");
8647
8640
  function preferenceScore(criteria) {
8648
8641
  const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
@@ -8672,7 +8665,7 @@ function PreferenceMeter({
8672
8665
  className = ""
8673
8666
  }) {
8674
8667
  const value = score == null ? preferenceScore(criteria) : score;
8675
- const segments = React38.useMemo(() => preferenceSegments(criteria), [criteria]);
8668
+ const segments = React39.useMemo(() => preferenceSegments(criteria), [criteria]);
8676
8669
  return /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
8677
8670
  ScoreMeter,
8678
8671
  {
@@ -8711,10 +8704,10 @@ function PreferenceMeter({
8711
8704
  }
8712
8705
 
8713
8706
  // src/components/chat/AgentChatPanel.tsx
8714
- var React44 = __toESM(require("react"), 1);
8707
+ var React45 = __toESM(require("react"), 1);
8715
8708
 
8716
8709
  // src/components/chat/chatEngine.ts
8717
- var React39 = __toESM(require("react"), 1);
8710
+ var React40 = __toESM(require("react"), 1);
8718
8711
  var CHAT_UNAVAILABLE = "chat_unavailable";
8719
8712
  var JOB_PENDING = ["queued", "running"];
8720
8713
  var JOB_SUCCESS = ["completed", "recovered"];
@@ -8768,22 +8761,22 @@ function useChatEngine(opts) {
8768
8761
  onClear,
8769
8762
  onFeedback
8770
8763
  } = opts || {};
8771
- const [status, setStatus] = React39.useState("idle");
8772
- const [threadId, setThreadId] = React39.useState(null);
8773
- const [messages, setMessages] = React39.useState([]);
8774
- const [queue, setQueue] = React39.useState([]);
8775
- const [fatal, setFatal] = React39.useState(null);
8776
- const [busy, setBusy] = React39.useState(false);
8777
- const [turnStartedAt, setTurnStartedAt] = React39.useState(null);
8778
- const listRef = React39.useRef([]);
8779
- const queueRef = React39.useRef([]);
8780
- const busyRef = React39.useRef(false);
8781
- const stoppedRef = React39.useRef(false);
8782
- const abortRef = React39.useRef(null);
8783
- const serverCount = React39.useRef(0);
8784
- const threadRef = React39.useRef(null);
8785
- const mounted = React39.useRef(true);
8786
- React39.useEffect(() => {
8764
+ const [status, setStatus] = React40.useState("idle");
8765
+ const [threadId, setThreadId] = React40.useState(null);
8766
+ const [messages, setMessages] = React40.useState([]);
8767
+ const [queue, setQueue] = React40.useState([]);
8768
+ const [fatal, setFatal] = React40.useState(null);
8769
+ const [busy, setBusy] = React40.useState(false);
8770
+ const [turnStartedAt, setTurnStartedAt] = React40.useState(null);
8771
+ const listRef = React40.useRef([]);
8772
+ const queueRef = React40.useRef([]);
8773
+ const busyRef = React40.useRef(false);
8774
+ const stoppedRef = React40.useRef(false);
8775
+ const abortRef = React40.useRef(null);
8776
+ const serverCount = React40.useRef(0);
8777
+ const threadRef = React40.useRef(null);
8778
+ const mounted = React40.useRef(true);
8779
+ React40.useEffect(() => {
8787
8780
  mounted.current = true;
8788
8781
  return () => {
8789
8782
  mounted.current = false;
@@ -8805,14 +8798,14 @@ function useChatEngine(opts) {
8805
8798
  }
8806
8799
  return false;
8807
8800
  };
8808
- const loadThread = React39.useCallback(async (id) => {
8801
+ const loadThread = React40.useCallback(async (id) => {
8809
8802
  const data = await apiAdapter.getThread(id);
8810
8803
  const list = [...data && data.messages || []];
8811
8804
  serverCount.current = list.length;
8812
8805
  commit(list);
8813
8806
  return list;
8814
8807
  }, [apiAdapter]);
8815
- React39.useEffect(() => {
8808
+ React40.useEffect(() => {
8816
8809
  if (!apiAdapter) {
8817
8810
  setStatus("idle");
8818
8811
  setFatal(null);
@@ -9025,7 +9018,7 @@ function useChatEngine(opts) {
9025
9018
  await dispatchTurn(turn);
9026
9019
  }
9027
9020
  }
9028
- const send = React39.useCallback((text, attachments) => {
9021
+ const send = React40.useCallback((text, attachments) => {
9029
9022
  const body = (text || "").trim();
9030
9023
  if (!body && !(attachments && attachments.length)) return;
9031
9024
  if (status === "disconnected") return;
@@ -9035,7 +9028,7 @@ function useChatEngine(opts) {
9035
9028
  stoppedRef.current = false;
9036
9029
  drain();
9037
9030
  }, [status]);
9038
- const stop = React39.useCallback(() => {
9031
+ const stop = React40.useCallback(() => {
9039
9032
  stoppedRef.current = true;
9040
9033
  const ac = abortRef.current;
9041
9034
  if (ac) {
@@ -9054,11 +9047,11 @@ function useChatEngine(opts) {
9054
9047
  store.del(STORAGE_PREFIX + threadRef.current);
9055
9048
  }
9056
9049
  }, [apiAdapter]);
9057
- const removeQueued = React39.useCallback((id) => {
9050
+ const removeQueued = React40.useCallback((id) => {
9058
9051
  queueRef.current = queueRef.current.filter((t) => t.id !== id);
9059
9052
  setQueue(queueRef.current.slice());
9060
9053
  }, []);
9061
- const retry = React39.useCallback(() => {
9054
+ const retry = React40.useCallback(() => {
9062
9055
  const list = listRef.current;
9063
9056
  let at = -1;
9064
9057
  for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
@@ -9074,19 +9067,19 @@ function useChatEngine(opts) {
9074
9067
  setQueue(queueRef.current.slice());
9075
9068
  drain();
9076
9069
  }, []);
9077
- const clear = React39.useCallback(() => {
9070
+ const clear = React40.useCallback(() => {
9078
9071
  commit([]);
9079
9072
  serverCount.current = 0;
9080
9073
  queueRef.current = [];
9081
9074
  setQueue([]);
9082
9075
  onClear && onClear();
9083
9076
  }, [onClear]);
9084
- const setFeedback = React39.useCallback((id, value) => {
9077
+ const setFeedback = React40.useCallback((id, value) => {
9085
9078
  patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
9086
9079
  const msg = listRef.current.find((m) => m.id === id);
9087
9080
  onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
9088
9081
  }, [onFeedback]);
9089
- const reload = React39.useCallback(async () => {
9082
+ const reload = React40.useCallback(async () => {
9090
9083
  if (!threadRef.current) return;
9091
9084
  setStatus("loading");
9092
9085
  try {
@@ -9124,10 +9117,10 @@ var ChatKit = {
9124
9117
  };
9125
9118
 
9126
9119
  // src/components/chat/ChatTranscript.tsx
9127
- var React41 = __toESM(require("react"), 1);
9120
+ var React42 = __toESM(require("react"), 1);
9128
9121
 
9129
9122
  // src/components/chat/ChatTurn.tsx
9130
- var React40 = __toESM(require("react"), 1);
9123
+ var React41 = __toESM(require("react"), 1);
9131
9124
  var import_jsx_runtime73 = require("react/jsx-runtime");
9132
9125
  function JsonView({ value }) {
9133
9126
  let text;
@@ -9164,7 +9157,7 @@ function PacketCard({ packet, schema, render, onApply, applied }) {
9164
9157
  ] });
9165
9158
  }
9166
9159
  function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
9167
- const [open, setOpen] = React40.useState(defaultOpen);
9160
+ const [open, setOpen] = React41.useState(defaultOpen);
9168
9161
  if (!text) return null;
9169
9162
  return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
9170
9163
  /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -9181,7 +9174,7 @@ function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
9181
9174
  ] });
9182
9175
  }
9183
9176
  function Citations({ items = [], onOpen }) {
9184
- const [open, setOpen] = React40.useState(false);
9177
+ const [open, setOpen] = React41.useState(false);
9185
9178
  if (!items.length) return null;
9186
9179
  return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-cites", children: [
9187
9180
  /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -9207,7 +9200,7 @@ function clampText(text, max) {
9207
9200
  return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
9208
9201
  }
9209
9202
  function MessageBody({ message: m, ctx }) {
9210
- const [expanded, setExpanded] = React40.useState(false);
9203
+ const [expanded, setExpanded] = React41.useState(false);
9211
9204
  const isUser = m.role === "user";
9212
9205
  const raw = m.text || "";
9213
9206
  const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
@@ -9274,9 +9267,9 @@ function MessageBody({ message: m, ctx }) {
9274
9267
  ] });
9275
9268
  }
9276
9269
  function useCopyRun() {
9277
- const [done, setDone] = React40.useState(false);
9278
- const t = React40.useRef(null);
9279
- React40.useEffect(() => () => {
9270
+ const [done, setDone] = React41.useState(false);
9271
+ const t = React41.useRef(null);
9272
+ React41.useEffect(() => () => {
9280
9273
  if (t.current) clearTimeout(t.current);
9281
9274
  }, []);
9282
9275
  return [done, (text) => {
@@ -9303,7 +9296,7 @@ function RunActions({ group, ctx }) {
9303
9296
  ] }),
9304
9297
  isAssistant && ctx.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
9305
9298
  group.role === "user" && ctx.onEdit ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
9306
- isAssistant && ctx.onFeedback ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(React40.Fragment, { children: [
9299
+ isAssistant && ctx.onFeedback ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(React41.Fragment, { children: [
9307
9300
  /* @__PURE__ */ (0, import_jsx_runtime73.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_runtime73.jsx)("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
9308
9301
  /* @__PURE__ */ (0, import_jsx_runtime73.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_runtime73.jsx)("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
9309
9302
  ] }) : null,
@@ -9398,10 +9391,10 @@ function ChatTranscript({
9398
9391
  renderEmpty,
9399
9392
  className = ""
9400
9393
  }) {
9401
- const scroller = React41.useRef(null);
9402
- const stick = React41.useRef(true);
9403
- const [pill, setPill] = React41.useState(0);
9404
- const seen = React41.useRef(0);
9394
+ const scroller = React42.useRef(null);
9395
+ const stick = React42.useRef(true);
9396
+ const [pill, setPill] = React42.useState(0);
9397
+ const seen = React42.useRef(0);
9405
9398
  const toBottom = (smooth) => {
9406
9399
  const el = scroller.current;
9407
9400
  if (!el) return;
@@ -9420,7 +9413,7 @@ function ChatTranscript({
9420
9413
  seen.current = messages.length;
9421
9414
  }
9422
9415
  };
9423
- React41.useLayoutEffect(() => {
9416
+ React42.useLayoutEffect(() => {
9424
9417
  const el = scroller.current;
9425
9418
  if (!el) return;
9426
9419
  if (stick.current) {
@@ -9428,7 +9421,7 @@ function ChatTranscript({
9428
9421
  seen.current = messages.length;
9429
9422
  } else setPill(Math.max(0, messages.length - seen.current));
9430
9423
  }, [messages]);
9431
- React41.useEffect(() => {
9424
+ React42.useEffect(() => {
9432
9425
  const el = scroller.current;
9433
9426
  const inner = el && el.firstChild;
9434
9427
  if (!el || !inner || typeof ResizeObserver === "undefined") return;
@@ -9438,7 +9431,7 @@ function ChatTranscript({
9438
9431
  ro.observe(inner);
9439
9432
  return () => ro.disconnect();
9440
9433
  }, []);
9441
- const groups = React41.useMemo(() => groupMessages(messages), [messages]);
9434
+ const groups = React42.useMemo(() => groupMessages(messages), [messages]);
9442
9435
  const empty = !messages.length && status === "ready";
9443
9436
  return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
9444
9437
  /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
@@ -9462,7 +9455,7 @@ function ChatTranscript({
9462
9455
  const prev = groups[i - 1];
9463
9456
  const k = dayKey(g.messages[0].timestamp);
9464
9457
  const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
9465
- return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(React41.Fragment, { children: [
9458
+ return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(React42.Fragment, { children: [
9466
9459
  showDay ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "fdc-day", children: /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { children: dayLabel(k) }) }) : null,
9467
9460
  /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(ChatTurn, { group: g, ctx })
9468
9461
  ] }, g.key || i);
@@ -9479,7 +9472,7 @@ function ChatTranscript({
9479
9472
  var TranscriptKit = { groupMessages };
9480
9473
 
9481
9474
  // src/components/chat/ChatComposer.tsx
9482
- var React42 = __toESM(require("react"), 1);
9475
+ var React43 = __toESM(require("react"), 1);
9483
9476
  var import_jsx_runtime75 = require("react/jsx-runtime");
9484
9477
  function ChatComposer({
9485
9478
  onSubmit,
@@ -9507,17 +9500,17 @@ function ChatComposer({
9507
9500
  onReject,
9508
9501
  onOpenAttachment
9509
9502
  }) {
9510
- const [text, setText] = React42.useState(draft || "");
9511
- const [trigger, setTrigger] = React42.useState(null);
9512
- const [mentionItems, setMentionItems] = React42.useState([]);
9513
- const [listening, setListening] = React42.useState(false);
9514
- const [notice, setNotice] = React42.useState(null);
9515
- const editor = React42.useRef(null);
9516
- const wrap = React42.useRef(null);
9517
- const stopVoice = React42.useRef(null);
9503
+ const [text, setText] = React43.useState(draft || "");
9504
+ const [trigger, setTrigger] = React43.useState(null);
9505
+ const [mentionItems, setMentionItems] = React43.useState([]);
9506
+ const [listening, setListening] = React43.useState(false);
9507
+ const [notice, setNotice] = React43.useState(null);
9508
+ const editor = React43.useRef(null);
9509
+ const wrap = React43.useRef(null);
9510
+ const stopVoice = React43.useRef(null);
9518
9511
  const staged = useStagedFiles(fileUploadHandler, { onError: () => {
9519
9512
  } });
9520
- React42.useEffect(() => {
9513
+ React43.useEffect(() => {
9521
9514
  if (draft != null && draft !== text) setText(draft);
9522
9515
  }, [draft]);
9523
9516
  const change = (v) => {
@@ -9551,7 +9544,7 @@ function ChatComposer({
9551
9544
  e.preventDefault();
9552
9545
  staged.add(found.files);
9553
9546
  };
9554
- React42.useEffect(() => {
9547
+ React43.useEffect(() => {
9555
9548
  if (!trigger || trigger.type !== "mention" || !mentionSources) {
9556
9549
  setMentionItems([]);
9557
9550
  return;
@@ -9569,7 +9562,7 @@ function ChatComposer({
9569
9562
  const q = (trigger.query || "").toLowerCase();
9570
9563
  setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
9571
9564
  }, [trigger, mentionSources]);
9572
- const slashItems = React42.useMemo(() => {
9565
+ const slashItems = React43.useMemo(() => {
9573
9566
  if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
9574
9567
  const q = (trigger.query || "").toLowerCase();
9575
9568
  return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
@@ -9743,12 +9736,12 @@ function ChatComposer({
9743
9736
  }
9744
9737
 
9745
9738
  // src/components/chat/ChatSessionBar.tsx
9746
- var React43 = __toESM(require("react"), 1);
9739
+ var React44 = __toESM(require("react"), 1);
9747
9740
  var import_jsx_runtime76 = require("react/jsx-runtime");
9748
9741
  var compact2 = meterFormats.compact;
9749
9742
  function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
9750
- const [open, setOpen] = React43.useState(false);
9751
- const anchor = React43.useRef(null);
9743
+ const [open, setOpen] = React44.useState(false);
9744
+ const anchor = React44.useRef(null);
9752
9745
  const stats = sessionStats || null;
9753
9746
  const cu = contextUsage || null;
9754
9747
  const limits = usageLimits || null;
@@ -9763,7 +9756,7 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9763
9756
  if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
9764
9757
  if (cu) bits.push(pct + "% context");
9765
9758
  const expandable = !!(cu || limits);
9766
- return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React43.Fragment, { children: [
9759
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React44.Fragment, { children: [
9767
9760
  /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-bar", children: [
9768
9761
  /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(
9769
9762
  "button",
@@ -9784,60 +9777,73 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9784
9777
  ),
9785
9778
  extras
9786
9779
  ] }),
9787
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Popover, { open, anchorRef: anchor, placement: "top-start", onClose: () => setOpen(false), minWidth: 330, maxHeight: 460, padded: true, label: "Usage", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-usage", children: [
9788
- cu ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("section", { className: "fdc-usage-sec", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9789
- SegmentedMeter,
9790
- {
9791
- total,
9792
- segments: cu.segments,
9793
- label: cu.label || "Context window",
9794
- format: cu.format === "bytes" ? meterFormats.bytes : compact2,
9795
- height: 8,
9796
- legend: true,
9797
- remainderLabel: "Free"
9798
- }
9799
- ) }) : null,
9800
- limits && limits.length ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { className: "fdc-usage-sec", children: [
9801
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h4", { className: "fdc-usage-h", children: "Usage limits" }),
9802
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-usage-rows", children: limits.map((l) => /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9803
- QuotaRow,
9804
- {
9805
- label: l.label,
9806
- percent: l.percent,
9807
- note: l.resetsAt ? "Resets " + formatRelative(l.resetsAt) : l.note
9808
- },
9809
- l.id
9810
- )) })
9811
- ] }) : null,
9812
- stats ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { className: "fdc-usage-sec fdc-usage-stats", children: [
9813
- stats.elapsedMs ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9814
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Session" }),
9815
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: formatDuration(stats.elapsedMs) })
9816
- ] }) : null,
9817
- stats.tokens ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9818
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Tokens" }),
9819
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: stats.tokens.toLocaleString() })
9820
- ] }) : null,
9821
- stats.costUsd != null ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9822
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Cost" }),
9823
- /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("b", { className: "fd-tabular", children: [
9824
- "$",
9825
- Number(stats.costUsd).toFixed(3)
9826
- ] })
9827
- ] }) : null,
9828
- stats.turns ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9829
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Turns" }),
9830
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: stats.turns })
9831
- ] }) : null
9832
- ] }) : null,
9833
- onClear ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("footer", { className: "fdc-usage-foot", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fdc-usage-clear", onClick: () => {
9834
- setOpen(false);
9835
- onClear();
9836
- }, children: [
9837
- /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-trash", "aria-hidden": "true" }),
9838
- "Clear conversation"
9839
- ] }) }) : null
9840
- ] }) })
9780
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9781
+ Popover,
9782
+ {
9783
+ open,
9784
+ anchorRef: anchor,
9785
+ placement: "top-start",
9786
+ onClose: () => setOpen(false),
9787
+ minWidth: 330,
9788
+ maxHeight: 460,
9789
+ padded: true,
9790
+ label: "Usage",
9791
+ footer: onClear ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fdc-usage-clear", onClick: () => {
9792
+ setOpen(false);
9793
+ onClear();
9794
+ }, children: [
9795
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-trash", "aria-hidden": "true" }),
9796
+ "Clear conversation"
9797
+ ] }) : void 0,
9798
+ children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-usage", children: [
9799
+ cu ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("section", { className: "fdc-usage-sec", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9800
+ SegmentedMeter,
9801
+ {
9802
+ total,
9803
+ segments: cu.segments,
9804
+ label: cu.label || "Context window",
9805
+ format: cu.format === "bytes" ? meterFormats.bytes : compact2,
9806
+ height: 8,
9807
+ legend: true,
9808
+ remainderLabel: "Free"
9809
+ }
9810
+ ) }) : null,
9811
+ limits && limits.length ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { className: "fdc-usage-sec", children: [
9812
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h4", { className: "fdc-usage-h", children: "Usage limits" }),
9813
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-usage-rows", children: limits.map((l) => /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9814
+ QuotaRow,
9815
+ {
9816
+ label: l.label,
9817
+ percent: l.percent,
9818
+ note: l.resetsAt ? "Resets " + formatRelative(l.resetsAt) : l.note
9819
+ },
9820
+ l.id
9821
+ )) })
9822
+ ] }) : null,
9823
+ stats ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { className: "fdc-usage-sec fdc-usage-stats", children: [
9824
+ stats.elapsedMs ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9825
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Session" }),
9826
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: formatDuration(stats.elapsedMs) })
9827
+ ] }) : null,
9828
+ stats.tokens ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9829
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Tokens" }),
9830
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: stats.tokens.toLocaleString() })
9831
+ ] }) : null,
9832
+ stats.costUsd != null ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9833
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Cost" }),
9834
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("b", { className: "fd-tabular", children: [
9835
+ "$",
9836
+ Number(stats.costUsd).toFixed(3)
9837
+ ] })
9838
+ ] }) : null,
9839
+ stats.turns ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9840
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Turns" }),
9841
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: stats.turns })
9842
+ ] }) : null
9843
+ ] }) : null
9844
+ ] })
9845
+ }
9846
+ )
9841
9847
  ] });
9842
9848
  }
9843
9849
  function ModelControls({
@@ -9891,7 +9897,7 @@ function ModelControls({
9891
9897
  ] })
9892
9898
  });
9893
9899
  }
9894
- return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React43.Fragment, { children: [
9900
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React44.Fragment, { children: [
9895
9901
  /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9896
9902
  MenuButton,
9897
9903
  {
@@ -10008,11 +10014,11 @@ function AgentChatPanel({
10008
10014
  onFeedback,
10009
10015
  onEditMessage
10010
10016
  }) {
10011
- const [panelWidth, setPanelWidth] = React44.useState(width);
10012
- const [applied, setApplied] = React44.useState({});
10013
- const [draft, setDraft] = React44.useState("");
10014
- const dragging = React44.useRef(null);
10015
- React44.useEffect(() => setPanelWidth(width), [width]);
10017
+ const [panelWidth, setPanelWidth] = React45.useState(width);
10018
+ const [applied, setApplied] = React45.useState({});
10019
+ const [draft, setDraft] = React45.useState("");
10020
+ const dragging = React45.useRef(null);
10021
+ React45.useEffect(() => setPanelWidth(width), [width]);
10016
10022
  const engine = useChatEngine({
10017
10023
  contextType,
10018
10024
  contextId,
@@ -10216,7 +10222,7 @@ function AgentChatPanel({
10216
10222
  }
10217
10223
 
10218
10224
  // src/kits/query.ts
10219
- var React45 = __toESM(require("react"), 1);
10225
+ var React46 = __toESM(require("react"), 1);
10220
10226
  function eqFilter(get2) {
10221
10227
  return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
10222
10228
  }
@@ -10248,22 +10254,22 @@ function compare(a, b, dir) {
10248
10254
  var API = null;
10249
10255
  var PREFS = null;
10250
10256
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
10251
- const [query, setQuery] = React45.useState(() => {
10257
+ const [query, setQuery] = React46.useState(() => {
10252
10258
  const store = PREFS || window.PlannerPrefs;
10253
10259
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
10254
10260
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
10255
10261
  });
10256
- const savePref = React45.useCallback((patch2) => {
10262
+ const savePref = React46.useCallback((patch2) => {
10257
10263
  const store = PREFS || window.PlannerPrefs;
10258
10264
  if (prefsKey && store) store.setTable(prefsKey, patch2);
10259
10265
  }, [prefsKey]);
10260
- const [res, setRes] = React45.useState(null);
10261
- const [loading, setLoading] = React45.useState(true);
10262
- const seq2 = React45.useRef(0);
10266
+ const [res, setRes] = React46.useState(null);
10267
+ const [loading, setLoading] = React46.useState(true);
10268
+ const seq2 = React46.useRef(0);
10263
10269
  const depKey = (deps || []).join("|");
10264
10270
  const paramKey = JSON.stringify(params || {});
10265
10271
  const queryKey = JSON.stringify(query);
10266
- React45.useEffect(() => {
10272
+ React46.useEffect(() => {
10267
10273
  const id = ++seq2.current;
10268
10274
  setLoading(true);
10269
10275
  const t = setTimeout(() => {
@@ -10638,6 +10644,7 @@ function createVersionStore(initialState, options) {
10638
10644
  ModeSwitch,
10639
10645
  ModelControls,
10640
10646
  NumberInput,
10647
+ POPOVER_BODY_MIN,
10641
10648
  PacketCard,
10642
10649
  Pagination,
10643
10650
  PermissionDenied,