@flytedan/flytebot-design-system 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -259,6 +259,7 @@ function IconButton({
259
259
  label,
260
260
  size = "md",
261
261
  variant = "ghost",
262
+ tone = "neutral",
262
263
  round = false,
263
264
  disabled = false,
264
265
  className = "",
@@ -266,7 +267,8 @@ function IconButton({
266
267
  }) {
267
268
  const cls = [
268
269
  "fd-btn-icon",
269
- variant === "outline" ? "fd-btn-icon-outline" : "",
270
+ variant === "outline" ? "fd-btn-icon-outline" : variant === "solid" ? "fd-btn-icon-solid" : "",
271
+ tone !== "neutral" ? "is-" + tone : "",
270
272
  size === "sm" ? "fd-btn-icon-sm" : size === "lg" ? "fd-btn-icon-lg" : "",
271
273
  round ? "fd-btn-icon-round" : "",
272
274
  className
@@ -287,6 +289,15 @@ var samePosition = (a, b) => {
287
289
  if (!a || !b) return a === b;
288
290
  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
291
  };
292
+ var SCROLL_REGIONS = ".fd-pop-body, .fd-pop-list";
293
+ function measureLayer(el) {
294
+ if (!el) return { width: 0, height: 0 };
295
+ let hidden = 0;
296
+ el.querySelectorAll(SCROLL_REGIONS).forEach((r) => {
297
+ hidden += Math.max(0, r.scrollHeight - r.clientHeight);
298
+ });
299
+ return { width: el.offsetWidth, height: el.offsetHeight ? el.offsetHeight + hidden : 0 };
300
+ }
290
301
  function usePopoverPosition(open, anchorRef, opts) {
291
302
  const o = opts || {};
292
303
  const { side: wantSide, align } = parsePlacement(o.placement);
@@ -294,6 +305,7 @@ function usePopoverPosition(open, anchorRef, opts) {
294
305
  const posRef = React.useRef(null);
295
306
  const detachRef = React.useRef(o.onDetach);
296
307
  detachRef.current = o.onDetach;
308
+ const layerRef = o.layerRef;
297
309
  React.useLayoutEffect(() => {
298
310
  if (!open || !anchorRef || !anchorRef.current) {
299
311
  posRef.current = null;
@@ -305,20 +317,22 @@ function usePopoverPosition(open, anchorRef, opts) {
305
317
  if (!el) return;
306
318
  const r = el.getBoundingClientRect();
307
319
  const offset = o.offset == null ? 8 : o.offset;
308
- const estH = o.estHeight || 300;
320
+ const layer = measureLayer(layerRef ? layerRef.current : null);
321
+ const need = layer.height || Math.min(o.estHeight || 300, 180);
309
322
  const below = window.innerHeight - r.bottom - offset - MARGIN;
310
323
  const above = r.top - offset - MARGIN;
311
324
  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;
325
+ if (side === "bottom" && below < need && above > below) side = "top";
326
+ else if (side === "top" && above < need && below > above) side = "bottom";
327
+ const exact = o.matchWidth === true;
328
+ const width = exact ? r.width : Math.max(o.width || 0, o.minWidth || 0, o.matchWidth === "min" ? r.width : 0);
329
+ const w = exact || o.width ? width : Math.max(layer.width, width) || o.estWidth || 260;
316
330
  let left = align === "end" ? r.right - w : align === "center" ? r.left + r.width / 2 - w / 2 : r.left;
317
331
  left = Math.max(MARGIN, Math.min(left, window.innerWidth - w - MARGIN));
318
332
  const maxH = Math.max(140, side === "top" ? above : below);
319
333
  const next = {
320
334
  left,
321
- width: o.matchWidth ? r.width : void 0,
335
+ width: exact ? r.width : void 0,
322
336
  minWidth: o.minWidth,
323
337
  top: side === "bottom" ? r.bottom + offset : null,
324
338
  bottom: side === "top" ? window.innerHeight - r.top + offset : null,
@@ -354,6 +368,7 @@ var portal = (node) => {
354
368
  function Popover({
355
369
  open,
356
370
  anchorRef,
371
+ triggerRef,
357
372
  onClose,
358
373
  placement = "bottom-start",
359
374
  offset = 8,
@@ -369,6 +384,8 @@ function Popover({
369
384
  returnFocus = true,
370
385
  className = "",
371
386
  style,
387
+ header,
388
+ footer,
372
389
  children
373
390
  }) {
374
391
  const ref = React.useRef(null);
@@ -379,7 +396,8 @@ function Popover({
379
396
  width,
380
397
  minWidth,
381
398
  estHeight: maxHeight || 320,
382
- onDetach: onClose
399
+ onDetach: onClose,
400
+ layerRef: ref
383
401
  });
384
402
  const restore = React.useRef(null);
385
403
  React.useEffect(() => {
@@ -400,7 +418,8 @@ function Popover({
400
418
  if (!closeOnOutside) return;
401
419
  const target = e.target;
402
420
  if (ref.current && ref.current.contains(target)) return;
403
- if (anchorRef && anchorRef.current && anchorRef.current.contains(target)) return;
421
+ const trigger = (triggerRef || anchorRef).current;
422
+ if (trigger && trigger.contains(target)) return;
404
423
  if (e.target.closest && e.target.closest(".fd-pop")) return;
405
424
  onClose && onClose();
406
425
  };
@@ -416,23 +435,23 @@ function Popover({
416
435
  document.removeEventListener("pointerdown", away, true);
417
436
  document.removeEventListener("keydown", key);
418
437
  };
419
- }, [open, closeOnOutside, closeOnEscape, onClose, anchorRef]);
438
+ }, [open, closeOnOutside, closeOnEscape, onClose, anchorRef, triggerRef]);
420
439
  if (!open || !pos) return null;
421
440
  return portal(
422
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
441
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
423
442
  "div",
424
443
  {
425
444
  ref,
426
445
  role,
427
446
  "aria-label": label,
428
- className: ["fd-pop", pos.side === "top" ? "is-up" : "", padded ? "fd-pop-padded" : "", className].filter(Boolean).join(" "),
447
+ className: ["fd-pop", pos.side === "top" ? "is-up" : "", className].filter(Boolean).join(" "),
429
448
  style: {
430
449
  position: "fixed",
431
450
  left: pos.left,
432
451
  top: pos.top == null ? "auto" : pos.top,
433
452
  bottom: pos.bottom == null ? "auto" : pos.bottom,
434
- width: matchWidth ? pos.width : width,
435
- minWidth: minWidth || (matchWidth ? void 0 : 200),
453
+ width: matchWidth === true ? pos.width : width,
454
+ minWidth: matchWidth === "min" ? Math.max(pos.anchorWidth, minWidth || 0) : minWidth || (matchWidth ? void 0 : 200),
436
455
  // Size to content by default — cap only at real available room between the
437
456
  // anchor and the viewport edge (pos.maxH, computed by usePopoverPosition).
438
457
  // A caller-supplied `maxHeight` narrows that further (e.g. a long menu that
@@ -445,7 +464,11 @@ function Popover({
445
464
  zIndex: 140,
446
465
  ...style
447
466
  },
448
- children
467
+ children: [
468
+ header != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-pop-header", children: header }) : null,
469
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: ["fd-pop-body", padded ? "fd-pop-padded" : ""].filter(Boolean).join(" "), children }),
470
+ footer != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-pop-footer", children: footer }) : null
471
+ ]
449
472
  }
450
473
  )
451
474
  );
@@ -520,77 +543,79 @@ function Menu({ items = [], onSelect, onClose, autoFocus = true, className = "",
520
543
  };
521
544
  const sub = openSub != null ? items[openSub] : null;
522
545
  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)(
546
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fd-menu-frame", children: [
547
+ header != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-header", children: header }) : null,
548
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
549
+ "div",
550
+ {
551
+ ref: listRef,
552
+ tabIndex: -1,
553
+ role: "menu",
554
+ onKeyDown: onKey,
555
+ className: ["fd-pop-list", "fd-menu", className].filter(Boolean).join(" "),
556
+ children: items.map((it, i) => {
557
+ if (!it) return null;
558
+ if (it.kind === "separator") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-sep", role: "separator" }, "s" + i);
559
+ if (it.kind === "section") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-pop-group", role: "presentation", children: it.label }, "h" + i);
560
+ if (it.kind === "custom") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-custom", children: it.render ? it.render() : null }, "c" + i);
561
+ const on = !!it.checked;
562
+ const row = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
573
563
  "button",
574
564
  {
575
565
  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
- }
566
+ "data-i": i,
567
+ role: it.checked != null ? "menuitemradio" : "menuitem",
568
+ "aria-checked": it.checked != null ? on : void 0,
569
+ "aria-haspopup": it.submenu ? "menu" : void 0,
570
+ "aria-expanded": it.submenu ? openSub === i : void 0,
571
+ "aria-disabled": it.disabled || void 0,
572
+ className: ["fd-opt", "fd-menu-item", i === active ? "is-active" : "", on ? "is-selected" : ""].filter(Boolean).join(" "),
573
+ onMouseEnter: () => {
574
+ setActive(i);
575
+ if (openSub != null && openSub !== i) setOpenSub(null);
585
576
  },
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,
577
+ onClick: () => choose(it, i),
578
+ children: [
579
+ 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,
580
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "fd-menu-text", children: [
581
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-label", children: it.label }),
582
+ it.description ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-desc", children: it.description }) : null
583
+ ] }),
584
+ it.meta ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-opt-meta", children: it.meta }) : null,
585
+ it.shortcut ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fd-menu-shortcut", "aria-hidden": "true", children: it.shortcut }) : null,
586
+ it.submenu ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-caret-right fd-menu-more", "aria-hidden": "true" }) : null,
587
+ 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
588
+ ]
589
+ },
590
+ it.id || it.label || i
591
+ );
592
+ const ta = it.trailingAction;
593
+ if (!ta) return row;
594
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "fd-menu-row", onMouseEnter: () => setActive(i), children: [
595
+ row,
596
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
597
+ "button",
598
+ {
599
+ type: "button",
600
+ className: ["fd-menu-row-act", ta.danger ? "is-danger" : ""].filter(Boolean).join(" "),
601
+ "aria-label": ta.label,
602
+ title: ta.label,
603
+ onClick: (e) => {
604
+ e.stopPropagation();
605
+ ta.onSelect && ta.onSelect(it);
606
+ if (ta.closeOnSelect !== false) {
607
+ onClose && onClose();
608
+ }
609
+ },
610
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", { className: "ph ph-" + (ta.icon || "trash"), "aria-hidden": "true" })
611
+ }
612
+ )
613
+ ] }, it.id || it.label || i);
614
+ })
615
+ }
616
+ ),
617
+ footer != null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fd-menu-footer", children: footer }) : null
618
+ ] }),
594
619
  sub && sub.submenu ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
595
620
  Popover,
596
621
  {
@@ -1218,20 +1243,123 @@ function Toast({ title, children, tone = "success", onUndo, onDismiss, className
1218
1243
  // src/components/feedback/Tooltip.tsx
1219
1244
  var React6 = __toESM(require("react"), 1);
1220
1245
  var import_jsx_runtime24 = require("react/jsx-runtime");
1246
+ var CLOSE_DELAY_MS = 120;
1221
1247
  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%)" };
1248
+ const anchorRef = React6.useRef(null);
1249
+ const bubbleRef = React6.useRef(null);
1250
+ const [reason, setReason] = React6.useState(null);
1251
+ const closeTimer = React6.useRef(null);
1252
+ const id = React6.useId();
1253
+ const descId = id + "-desc";
1254
+ const open = reason != null;
1255
+ const cancelClose = React6.useCallback(() => {
1256
+ if (closeTimer.current) {
1257
+ clearTimeout(closeTimer.current);
1258
+ closeTimer.current = null;
1259
+ }
1260
+ }, []);
1261
+ const show = (why) => {
1262
+ cancelClose();
1263
+ setReason(why);
1264
+ };
1265
+ const hide = React6.useCallback(() => {
1266
+ cancelClose();
1267
+ setReason(null);
1268
+ }, [cancelClose]);
1269
+ const hover = () => {
1270
+ cancelClose();
1271
+ setReason((r) => r || "hover");
1272
+ };
1273
+ const hideSoon = () => {
1274
+ cancelClose();
1275
+ closeTimer.current = setTimeout(() => {
1276
+ closeTimer.current = null;
1277
+ setReason((r) => r === "hover" ? null : r);
1278
+ }, CLOSE_DELAY_MS);
1279
+ };
1280
+ React6.useEffect(() => cancelClose, [cancelClose]);
1281
+ const pos = usePopoverPosition(open, anchorRef, {
1282
+ placement: placement === "bottom" ? "bottom-center" : "top-center",
1283
+ offset: 8,
1284
+ estHeight: 40,
1285
+ estWidth: 160,
1286
+ layerRef: bubbleRef,
1287
+ onDetach: hide
1288
+ });
1289
+ React6.useEffect(() => {
1290
+ if (!open) return;
1291
+ const key = (e) => {
1292
+ if (e.key === "Escape") hide();
1293
+ };
1294
+ const away = (e) => {
1295
+ const t = e.target;
1296
+ if (anchorRef.current && anchorRef.current.contains(t)) return;
1297
+ if (bubbleRef.current && bubbleRef.current.contains(t)) return;
1298
+ hide();
1299
+ };
1300
+ document.addEventListener("keydown", key);
1301
+ document.addEventListener("pointerdown", away, true);
1302
+ return () => {
1303
+ document.removeEventListener("keydown", key);
1304
+ document.removeEventListener("pointerdown", away, true);
1305
+ };
1306
+ }, [open, hide]);
1307
+ const isMouse = (e) => e.pointerType === "mouse";
1308
+ const onFocus = (e) => {
1309
+ const el = e.target;
1310
+ let keyboard = true;
1311
+ try {
1312
+ keyboard = el.matches(":focus-visible");
1313
+ } catch {
1314
+ }
1315
+ if (keyboard) show("focus");
1316
+ };
1317
+ const described = React6.isValidElement(children) ? React6.cloneElement(children, {
1318
+ "aria-describedby": [children.props["aria-describedby"], descId].filter(Boolean).join(" ")
1319
+ }) : children;
1224
1320
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1225
1321
  "span",
1226
1322
  {
1227
- style: { position: "relative", display: "inline-flex" },
1228
- onMouseEnter: () => setShow(true),
1229
- onMouseLeave: () => setShow(false),
1230
- onFocus: () => setShow(true),
1231
- onBlur: () => setShow(false),
1323
+ ref: anchorRef,
1324
+ className: "fd-tooltip-anchor",
1325
+ "aria-describedby": React6.isValidElement(children) ? void 0 : descId,
1326
+ onPointerEnter: (e) => {
1327
+ if (isMouse(e)) hover();
1328
+ },
1329
+ onPointerLeave: (e) => {
1330
+ if (isMouse(e)) hideSoon();
1331
+ },
1332
+ onPointerUp: (e) => {
1333
+ if (!isMouse(e)) {
1334
+ if (reason === "tap") hide();
1335
+ else show("tap");
1336
+ }
1337
+ },
1338
+ onFocus,
1339
+ onBlur: () => {
1340
+ if (reason === "focus") hide();
1341
+ },
1232
1342
  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
1343
+ described,
1344
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { id: descId, hidden: true, children: label }),
1345
+ open && pos ? portal(
1346
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1347
+ "span",
1348
+ {
1349
+ ref: bubbleRef,
1350
+ role: "tooltip",
1351
+ className: ["fd-tooltip", pos.side === "bottom" ? "is-below" : "", className].filter(Boolean).join(" "),
1352
+ style: { left: pos.left, top: pos.top == null ? "auto" : pos.top, bottom: pos.bottom == null ? "auto" : pos.bottom },
1353
+ onPointerEnter: (e) => {
1354
+ if (isMouse(e)) cancelClose();
1355
+ },
1356
+ onPointerLeave: (e) => {
1357
+ if (isMouse(e)) hideSoon();
1358
+ },
1359
+ children: label
1360
+ }
1361
+ )
1362
+ ) : null
1235
1363
  ]
1236
1364
  }
1237
1365
  );
@@ -2865,6 +2993,7 @@ function VirtualList({
2865
2993
  }
2866
2994
 
2867
2995
  // src/components/data/EntityRow.tsx
2996
+ var React17 = __toESM(require("react"), 1);
2868
2997
  var import_jsx_runtime40 = require("react/jsx-runtime");
2869
2998
  var ENTITY_ROW_METRIC_WIDTH = 74;
2870
2999
  var METRIC_TONE_COLOR = {
@@ -2873,20 +3002,20 @@ var METRIC_TONE_COLOR = {
2873
3002
  danger: "var(--danger-text)"
2874
3003
  };
2875
3004
  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
- )) });
3005
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: ["fd-erow-metrics", className].filter(Boolean).join(" "), style, children: metrics.map((m, i) => {
3006
+ const key = m.id || m.label || i;
3007
+ const className2 = ["fd-erow-metric", m.tone && m.tone !== "default" ? "is-" + m.tone : "", m.tooltip != null ? "is-described" : ""].filter(Boolean).join(" ");
3008
+ const cellStyle = { width: (m.width || ENTITY_ROW_METRIC_WIDTH) + "px", color: METRIC_TONE_COLOR[m.tone || "default"] };
3009
+ const content = /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(React17.Fragment, { children: [
3010
+ m.icon ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }) : null,
3011
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "fd-erow-metric-value fd-tabular", children: m.value }),
3012
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "fd-sr", children: " " + m.label })
3013
+ ] });
3014
+ if (m.tooltip == null) {
3015
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: className2, style: cellStyle, title: m.label, children: content }, key);
3016
+ }
3017
+ 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);
3018
+ }) });
2890
3019
  }
2891
3020
  function EntityRow({
2892
3021
  title,
@@ -2936,8 +3065,10 @@ function EntityRow({
2936
3065
  icon: action.icon,
2937
3066
  label: action.label,
2938
3067
  size: "sm",
2939
- onClick: action.onClick,
2940
- style: { color: action.tone === "add" ? "var(--ok-text)" : action.tone === "remove" ? "var(--danger-text)" : void 0 }
3068
+ variant: "solid",
3069
+ tone: action.tone === "add" ? "ok" : action.tone === "remove" ? "danger" : "neutral",
3070
+ className: "fd-erow-action",
3071
+ onClick: action.onClick
2941
3072
  }
2942
3073
  ) : null
2943
3074
  ]
@@ -2946,14 +3077,14 @@ function EntityRow({
2946
3077
  }
2947
3078
 
2948
3079
  // src/components/data/TransferList.tsx
2949
- var React18 = __toESM(require("react"), 1);
3080
+ var React19 = __toESM(require("react"), 1);
2950
3081
  var import_react_dom4 = require("react-dom");
2951
3082
 
2952
3083
  // src/components/forms/field.tsx
2953
- var React17 = __toESM(require("react"), 1);
3084
+ var React18 = __toESM(require("react"), 1);
2954
3085
  var import_jsx_runtime41 = require("react/jsx-runtime");
2955
3086
  function useFieldId(id) {
2956
- const generated = React17.useId();
3087
+ const generated = React18.useId();
2957
3088
  return id || generated;
2958
3089
  }
2959
3090
  function FieldLabel({ htmlFor, id, required = false, onClick, children }) {
@@ -2994,10 +3125,9 @@ function Input({
2994
3125
  numeric ? "fd-input-num" : "",
2995
3126
  error ? "fd-input-error" : "",
2996
3127
  disabled ? "fd-input-disabled" : "",
2997
- size === "lg" ? "fd-input-lg" : "",
2998
- className
3128
+ size === "lg" ? "fd-input-lg" : ""
2999
3129
  ].filter(Boolean).join(" ");
3000
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "fd-field", style, children: [
3130
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: ["fd-field", className].filter(Boolean).join(" "), style, children: [
3001
3131
  label ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(FieldLabel, { htmlFor: fieldId, required, children: label }) : null,
3002
3132
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: box, children: [
3003
3133
  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 +3202,12 @@ function TransferList({
3072
3202
  listHeight = 440,
3073
3203
  className = ""
3074
3204
  }) {
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) => {
3205
+ const [drag, setDrag] = React19.useState(null);
3206
+ const [dragOverSide, setDragOverSide] = React19.useState(null);
3207
+ const dragRef = React19.useRef(null);
3208
+ const armedRef = React19.useRef(null);
3209
+ const sideRefs = React19.useRef({ left: null, right: null });
3210
+ const hitTestSide = React19.useCallback((x, y) => {
3081
3211
  for (const side of ["left", "right"]) {
3082
3212
  const el = sideRefs.current[side];
3083
3213
  if (!el) continue;
@@ -3086,7 +3216,7 @@ function TransferList({
3086
3216
  }
3087
3217
  return null;
3088
3218
  }, []);
3089
- const findItem = React18.useCallback(
3219
+ const findItem = React19.useCallback(
3090
3220
  (side, key) => (side === "left" ? left.items : right.items).find((it) => keyOf(it) === key),
3091
3221
  [left.items, right.items, keyOf]
3092
3222
  );
@@ -3108,7 +3238,7 @@ function TransferList({
3108
3238
  height: rect.height
3109
3239
  };
3110
3240
  };
3111
- React18.useEffect(() => {
3241
+ React19.useEffect(() => {
3112
3242
  const onMovePointer = (e) => {
3113
3243
  const armed = armedRef.current;
3114
3244
  if (!armed || e.pointerId !== armed.pointerId) return;
@@ -3161,7 +3291,7 @@ function TransferList({
3161
3291
  window.removeEventListener("pointercancel", endDrag);
3162
3292
  };
3163
3293
  }, [hitTestSide, findItem, onMove]);
3164
- React18.useEffect(() => {
3294
+ React19.useEffect(() => {
3165
3295
  if (!drag || typeof document === "undefined") return;
3166
3296
  const prev = document.body.style.userSelect;
3167
3297
  document.body.style.userSelect = "none";
@@ -3273,11 +3403,11 @@ function TransferList({
3273
3403
  }
3274
3404
 
3275
3405
  // src/components/forms/Checkbox.tsx
3276
- var React19 = __toESM(require("react"), 1);
3406
+ var React20 = __toESM(require("react"), 1);
3277
3407
  var import_jsx_runtime45 = require("react/jsx-runtime");
3278
3408
  function Checkbox({ label, description, card = false, indeterminate = false, className = "", ...rest }) {
3279
- const ref = React19.useRef(null);
3280
- React19.useEffect(() => {
3409
+ const ref = React20.useRef(null);
3410
+ React20.useEffect(() => {
3281
3411
  if (ref.current) ref.current.indeterminate = indeterminate;
3282
3412
  }, [indeterminate]);
3283
3413
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("label", { className: ["fd-choice", card ? "fd-choice-card" : "", className].filter(Boolean).join(" "), children: [
@@ -3324,8 +3454,8 @@ function Switch({ label, description, className = "", ...rest }) {
3324
3454
  var import_jsx_runtime48 = require("react/jsx-runtime");
3325
3455
  function Textarea({ label, help, error, required = false, rows = 4, disabled = false, id, className = "", style, ...rest }) {
3326
3456
  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: [
3457
+ const box = ["fd-input", "fd-input-textarea", error ? "fd-input-error" : "", disabled ? "fd-input-disabled" : ""].filter(Boolean).join(" ");
3458
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: ["fd-field", className].filter(Boolean).join(" "), style, children: [
3329
3459
  label ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(FieldLabel, { htmlFor: fieldId, required, children: label }) : null,
3330
3460
  /* @__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
3461
  error ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "fd-field-error", children: [
@@ -3336,7 +3466,7 @@ function Textarea({ label, help, error, required = false, rows = 4, disabled = f
3336
3466
  }
3337
3467
 
3338
3468
  // src/components/forms/NumberInput.tsx
3339
- var React20 = __toESM(require("react"), 1);
3469
+ var React21 = __toESM(require("react"), 1);
3340
3470
  var import_jsx_runtime49 = require("react/jsx-runtime");
3341
3471
  function NumberInput({
3342
3472
  label,
@@ -3364,10 +3494,10 @@ function NumberInput({
3364
3494
  const n = Number(String(v == null ? "" : v).replace(/[^0-9.-]/g, ""));
3365
3495
  return isNaN(n) ? null : n;
3366
3496
  };
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(() => {
3497
+ const [text, setText] = React21.useState(value == null || value === "" ? "" : String(value));
3498
+ const [editing, setEditing] = React21.useState(false);
3499
+ const timer = React21.useRef(null);
3500
+ React21.useEffect(() => {
3371
3501
  if (!editing) setText(value == null || value === "" ? "" : String(value));
3372
3502
  }, [value, editing]);
3373
3503
  const clamp = (n) => Math.min(max, Math.max(min, n));
@@ -3391,7 +3521,7 @@ function NumberInput({
3391
3521
  const release = () => {
3392
3522
  if (timer.current) clearTimeout(timer.current);
3393
3523
  };
3394
- React20.useEffect(() => () => {
3524
+ React21.useEffect(() => () => {
3395
3525
  if (timer.current) clearTimeout(timer.current);
3396
3526
  }, []);
3397
3527
  const shown = editing ? text : (() => {
@@ -3478,52 +3608,9 @@ function NumberInput({
3478
3608
  }
3479
3609
 
3480
3610
  // src/components/forms/Select.tsx
3481
- var React21 = __toESM(require("react"), 1);
3482
- var import_react_dom5 = require("react-dom");
3611
+ var React22 = __toESM(require("react"), 1);
3483
3612
  var import_jsx_runtime50 = require("react/jsx-runtime");
3484
3613
  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
3614
  function Select({
3528
3615
  label,
3529
3616
  help,
@@ -3548,17 +3635,15 @@ function Select({
3548
3635
  const isOn = (v) => multiple ? vals.includes(v) : v === value;
3549
3636
  const hasSearch = searchable === void 0 ? opts.length > 8 : searchable;
3550
3637
  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 });
3638
+ const [open, setOpen] = React22.useState(false);
3639
+ const [q, setQ] = React22.useState("");
3640
+ const [active, setActive] = React22.useState(-1);
3641
+ const rootRef = React22.useRef(null);
3642
+ const boxRef = React22.useRef(null);
3643
+ const listRef = React22.useRef(null);
3644
+ const typeBuf = React22.useRef({ s: "", t: 0 });
3559
3645
  const selected = multiple ? null : opts.find((o) => o.value === value);
3560
3646
  const chosen = multiple ? opts.filter((o) => vals.includes(o.value)) : [];
3561
- const pos = usePopPos(open, boxRef, hasSearch ? 390 : 340, 260);
3562
3647
  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
3648
  const visible = q ? opts.filter((o) => (o.label + " " + (o.description || "") + " " + (o.group || "")).toLowerCase().includes(q.toLowerCase())) : opts;
3564
3649
  const fire = (v) => {
@@ -3581,17 +3666,7 @@ function Select({
3581
3666
  }
3582
3667
  setOpen(!open);
3583
3668
  };
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(() => {
3669
+ React22.useEffect(() => {
3595
3670
  if (!open || active < 0 || !listRef.current) return;
3596
3671
  const el = listRef.current.querySelector('[data-i="' + active + '"]');
3597
3672
  if (el) {
@@ -3682,94 +3757,96 @@ function Select({
3682
3757
  ) : null,
3683
3758
  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
3759
  ] }),
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,
3760
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3761
+ Popover,
3762
+ {
3763
+ open,
3764
+ anchorRef: boxRef,
3765
+ triggerRef: rootRef,
3766
+ onClose: () => setOpen(false),
3767
+ placement: "bottom-start",
3768
+ offset: 6,
3769
+ matchWidth: "min",
3770
+ minWidth: 260,
3771
+ role: "presentation",
3772
+ style: { maxWidth: 380, zIndex: 130 },
3773
+ header: hasSearch ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "fd-pop-search", children: [
3774
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-magnifying-glass", style: { color: "var(--text-muted)", fontSize: 14 } }),
3775
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3776
+ "input",
3777
+ {
3778
+ autoFocus: true,
3779
+ placeholder: "Search\u2026",
3780
+ value: q,
3781
+ onKeyDown: onKey,
3782
+ onChange: (e) => {
3783
+ setQ(e.target.value);
3784
+ setActive(0);
3785
+ }
3786
+ }
3787
+ ),
3788
+ q ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-body-sm fd-muted", children: visible.length }) : null
3789
+ ] }) : void 0,
3790
+ footer: multiple ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(React22.Fragment, { children: [
3791
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3792
+ "button",
3793
+ {
3794
+ type: "button",
3795
+ onClick: () => fire(visible.filter((o) => !o.disabled).map((o) => o.value)),
3796
+ style: { all: "unset", cursor: "pointer", fontSize: "var(--body-sm-size)", fontWeight: 600, color: "var(--brand)" },
3797
+ children: "Select all"
3798
+ }
3799
+ ),
3800
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { style: { flex: 1 } }),
3801
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: "fd-body-sm fd-muted", children: [
3802
+ vals.length,
3803
+ " of ",
3804
+ opts.length
3805
+ ] }),
3806
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3807
+ "button",
3808
+ {
3809
+ type: "button",
3810
+ disabled: !vals.length,
3811
+ onClick: () => fire([]),
3812
+ style: { all: "unset", cursor: vals.length ? "pointer" : "default", fontSize: "var(--body-sm-size)", fontWeight: 600, color: vals.length ? "var(--text-2)" : "var(--text-muted)" },
3813
+ children: "Clear"
3814
+ }
3815
+ )
3816
+ ] }) : void 0,
3817
+ 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: [
3818
+ 'Nothing matches "',
3819
+ q,
3820
+ '".'
3821
+ ] }) : groups.map((grp) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(React22.Fragment, { children: [
3822
+ grp.g ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "fd-pop-group", children: grp.g }) : null,
3823
+ grp.items.map(({ o, i }) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3824
+ "button",
3825
+ {
3826
+ type: "button",
3827
+ "data-i": i,
3828
+ role: "option",
3829
+ "aria-selected": isOn(o.value),
3830
+ "aria-disabled": o.disabled || void 0,
3831
+ className: ["fd-opt", i === active ? "is-active" : "", isOn(o.value) ? "is-selected" : ""].filter(Boolean).join(" "),
3832
+ onMouseEnter: () => setActive(i),
3833
+ onClick: () => pick(o),
3834
+ children: [
3835
+ 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,
3836
+ 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,
3837
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { style: { flex: 1, minWidth: 0 }, children: [
3838
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-label", children: o.label }),
3839
+ o.description ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-desc", children: o.description }) : null
3840
+ ] }),
3841
+ o.meta ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-opt-meta", children: o.meta }) : null,
3842
+ 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 })
3843
+ ]
3844
+ },
3845
+ String(o.value)
3846
+ ))
3847
+ ] }, grp.g || "_")) })
3848
+ }
3849
+ ),
3773
3850
  error ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: "fd-field-error", children: [
3774
3851
  /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
3775
3852
  error
@@ -3778,8 +3855,7 @@ function Select({
3778
3855
  }
3779
3856
 
3780
3857
  // src/components/forms/DatePicker.tsx
3781
- var React22 = __toESM(require("react"), 1);
3782
- var import_react_dom6 = require("react-dom");
3858
+ var React23 = __toESM(require("react"), 1);
3783
3859
  var import_jsx_runtime51 = require("react/jsx-runtime");
3784
3860
  var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
3785
3861
  var DOW = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
@@ -3793,56 +3869,14 @@ var fmt = (s) => {
3793
3869
  const d = parse(s);
3794
3870
  return d ? MONTHS[d.getMonth()].slice(0, 3) + " " + d.getDate() + ", " + d.getFullYear() : "";
3795
3871
  };
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
3872
  function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3839
3873
  const today = /* @__PURE__ */ new Date();
3840
3874
  const sel = range ? value || {} : { start: value, end: value };
3841
3875
  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);
3876
+ const [vy, setVy] = React23.useState(anchor.getFullYear());
3877
+ const [vm, setVm] = React23.useState(anchor.getMonth());
3878
+ const [mode2, setMode] = React23.useState("days");
3879
+ const [hover, setHover] = React23.useState(null);
3846
3880
  const s = parse(sel.start), e = parse(sel.end);
3847
3881
  const hoverEnd = range && s && !e && hover ? parse(hover) : null;
3848
3882
  const inRange = (d) => {
@@ -3958,28 +3992,9 @@ function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3958
3992
  }
3959
3993
  function DatePicker({ label, help, error, required = false, disabled = false, range = false, value, onChange, placeholder, id, className = "", style, ...rest }) {
3960
3994
  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]);
3995
+ const [open, setOpen] = React23.useState(false);
3996
+ const rootRef = React23.useRef(null);
3997
+ const boxRef = React23.useRef(null);
3983
3998
  const display = range ? value && value.start ? fmt(value.start) + (value.end ? " \u2192 " + fmt(value.end) : " \u2192 \u2026") : "" : fmt(value);
3984
3999
  const toggle = () => {
3985
4000
  if (!disabled) setOpen(!open);
@@ -4008,21 +4023,31 @@ function DatePicker({ label, help, error, required = false, disabled = false, ra
4008
4023
  ),
4009
4024
  /* @__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
4025
  ] }),
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);
4026
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
4027
+ Popover,
4028
+ {
4029
+ open,
4030
+ anchorRef: boxRef,
4031
+ triggerRef: rootRef,
4032
+ onClose: () => setOpen(false),
4033
+ placement: "bottom-start",
4034
+ offset: 6,
4035
+ role: "presentation",
4036
+ style: { width: "max-content", zIndex: 130 },
4037
+ children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
4038
+ Calendar,
4039
+ {
4040
+ range,
4041
+ months: range ? 2 : 1,
4042
+ value,
4043
+ onPick: (v) => {
4044
+ onChange && onChange({ target: { value: v } });
4045
+ if (!range || v.start && v.end) setOpen(false);
4046
+ }
4021
4047
  }
4022
- }
4023
- ) }),
4024
- document.body
4025
- ) : null,
4048
+ )
4049
+ }
4050
+ ),
4026
4051
  error ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("span", { className: "fd-field-error", children: [
4027
4052
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
4028
4053
  error
@@ -4031,61 +4056,18 @@ function DatePicker({ label, help, error, required = false, disabled = false, ra
4031
4056
  }
4032
4057
 
4033
4058
  // src/components/forms/TimePicker.tsx
4034
- var React23 = __toESM(require("react"), 1);
4035
- var import_react_dom7 = require("react-dom");
4059
+ var React24 = __toESM(require("react"), 1);
4036
4060
  var import_jsx_runtime52 = require("react/jsx-runtime");
4037
4061
  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
4062
  function ClockFace({ value = "09:00", onChange }) {
4081
4063
  let [h24, m] = value.split(":").map(Number);
4082
4064
  if (isNaN(h24)) h24 = 9;
4083
4065
  if (isNaN(m)) m = 0;
4084
4066
  const pm = h24 >= 12;
4085
4067
  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);
4068
+ const [mode2, setMode] = React24.useState("h");
4069
+ const faceRef = React24.useRef(null);
4070
+ const dragging = React24.useRef(false);
4089
4071
  const set = (h, mm, isPm) => onChange((isPm ? h % 12 + 12 : h % 12) + ":" + pad(mm));
4090
4072
  const R = 108, NR = 80;
4091
4073
  const nums = mode2 === "h" ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 12 }, (_, i) => i * 5);
@@ -4158,28 +4140,9 @@ function ClockFace({ value = "09:00", onChange }) {
4158
4140
  }
4159
4141
  function TimePicker({ label, help, error, required = false, disabled = false, value = "", onChange, placeholder = "Pick a time", id, className = "", style }) {
4160
4142
  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]);
4143
+ const [open, setOpen] = React24.useState(false);
4144
+ const rootRef = React24.useRef(null);
4145
+ const boxRef = React24.useRef(null);
4183
4146
  const disp = () => {
4184
4147
  if (!value) return "";
4185
4148
  const [h, m] = value.split(":").map(Number);
@@ -4211,10 +4174,19 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
4211
4174
  ),
4212
4175
  /* @__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
4176
  ] }),
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: [
4177
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
4178
+ Popover,
4179
+ {
4180
+ open,
4181
+ anchorRef: boxRef,
4182
+ triggerRef: rootRef,
4183
+ onClose: () => setOpen(false),
4184
+ placement: "bottom-start",
4185
+ offset: 6,
4186
+ width: 262,
4187
+ role: "presentation",
4188
+ style: { zIndex: 130 },
4189
+ footer: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(React24.Fragment, { children: [
4218
4190
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
4219
4191
  "button",
4220
4192
  {
@@ -4230,10 +4202,10 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
4230
4202
  ),
4231
4203
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { style: { flex: 1 } }),
4232
4204
  /* @__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,
4205
+ ] }),
4206
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ClockFace, { value: value || "09:00", onChange: (v) => onChange && onChange({ target: { value: v } }) })
4207
+ }
4208
+ ),
4237
4209
  error ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("span", { className: "fd-field-error", children: [
4238
4210
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
4239
4211
  error
@@ -4242,7 +4214,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
4242
4214
  }
4243
4215
 
4244
4216
  // src/components/forms/Slider.tsx
4245
- var React24 = __toESM(require("react"), 1);
4217
+ var React25 = __toESM(require("react"), 1);
4246
4218
  var import_jsx_runtime53 = require("react/jsx-runtime");
4247
4219
  function Slider({
4248
4220
  label,
@@ -4260,7 +4232,7 @@ function Slider({
4260
4232
  ...rest
4261
4233
  }) {
4262
4234
  const fieldId = useFieldId(id);
4263
- const [dragging, setDragging] = React24.useState(false);
4235
+ const [dragging, setDragging] = React25.useState(false);
4264
4236
  const v = value === void 0 ? min : Number(value);
4265
4237
  const pct = max === min ? 0 : (v - min) / (max - min) * 100;
4266
4238
  return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: ["fd-field", className].filter(Boolean).join(" "), children: [
@@ -4294,7 +4266,7 @@ function Slider({
4294
4266
  }
4295
4267
 
4296
4268
  // src/components/forms/RangeSlider.tsx
4297
- var React25 = __toESM(require("react"), 1);
4269
+ var React26 = __toESM(require("react"), 1);
4298
4270
  var import_jsx_runtime54 = require("react/jsx-runtime");
4299
4271
  function RangeSlider({
4300
4272
  label,
@@ -4314,9 +4286,9 @@ function RangeSlider({
4314
4286
  }) {
4315
4287
  const fmt2 = format || ((v) => String(v));
4316
4288
  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);
4289
+ const [drag, setDrag] = React26.useState(null);
4290
+ const [focus, setFocus] = React26.useState(null);
4291
+ const railRef = React26.useRef(null);
4320
4292
  const pct = (v) => max === min ? 0 : (v - min) / (max - min) * 100;
4321
4293
  const clampPair = (i, v) => {
4322
4294
  v = Math.min(max, Math.max(min, Math.round(v / step) * step));
@@ -4331,7 +4303,7 @@ function RangeSlider({
4331
4303
  const r = railRef.current.getBoundingClientRect();
4332
4304
  return min + Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)) * (max - min);
4333
4305
  };
4334
- React25.useEffect(() => {
4306
+ React26.useEffect(() => {
4335
4307
  if (drag === null) return;
4336
4308
  const mv = (e) => onChange && onChange(clampPair(drag, fromEvent(e)));
4337
4309
  const upH = () => setDrag(null);
@@ -4412,7 +4384,7 @@ function RangeSlider({
4412
4384
  /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "fd-range" + (hasLabels ? " has-labels" : ""), onPointerDown: onRailDown, children: [
4413
4385
  /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "fd-range-rail", ref: railRef }),
4414
4386
  /* @__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: [
4387
+ marks.map((m) => /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(React26.Fragment, { children: [
4416
4388
  /* @__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
4389
  m.label ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "fd-range-mark-label", style: { left: pct(m.value) + "%" }, children: m.label }) : null
4418
4390
  ] }, m.value)),
@@ -4441,7 +4413,7 @@ function RangeSlider({
4441
4413
  }
4442
4414
 
4443
4415
  // src/components/forms/Dropzone.tsx
4444
- var React26 = __toESM(require("react"), 1);
4416
+ var React27 = __toESM(require("react"), 1);
4445
4417
  var import_jsx_runtime55 = require("react/jsx-runtime");
4446
4418
  function Dropzone({
4447
4419
  onFiles,
@@ -4456,8 +4428,8 @@ function Dropzone({
4456
4428
  className = "",
4457
4429
  style
4458
4430
  }) {
4459
- const [over, setOver] = React26.useState(false);
4460
- const depth = React26.useRef(0);
4431
+ const [over, setOver] = React27.useState(false);
4432
+ const depth = React27.useRef(0);
4461
4433
  const has = (e) => {
4462
4434
  const dt = e.dataTransfer;
4463
4435
  if (!dt) return false;
@@ -4519,8 +4491,8 @@ function FilePickButton({
4519
4491
  className = "",
4520
4492
  children
4521
4493
  }) {
4522
- const ref = React26.useRef(null);
4523
- return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(React26.Fragment, { children: [
4494
+ const ref = React27.useRef(null);
4495
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(React27.Fragment, { children: [
4524
4496
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4525
4497
  "button",
4526
4498
  {
@@ -4553,10 +4525,10 @@ function FilePickButton({
4553
4525
  }
4554
4526
  function useStagedFiles(upload, opts) {
4555
4527
  const o = opts || {};
4556
- const [items, setItems] = React26.useState([]);
4557
- const controllers = React26.useRef({});
4528
+ const [items, setItems] = React27.useState([]);
4529
+ const controllers = React27.useRef({});
4558
4530
  const patch = (id, next) => setItems((list) => list.map((f) => f.id === id ? Object.assign({}, f, next) : f));
4559
- const run = React26.useCallback((att) => {
4531
+ const run = React27.useCallback((att) => {
4560
4532
  if (!upload) return;
4561
4533
  const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
4562
4534
  controllers.current[att.id] = ac;
@@ -4573,14 +4545,14 @@ function useStagedFiles(upload, opts) {
4573
4545
  delete controllers.current[att.id];
4574
4546
  });
4575
4547
  }, [upload]);
4576
- const add = React26.useCallback((files) => {
4548
+ const add = React27.useCallback((files) => {
4577
4549
  if (!upload) return [];
4578
4550
  const atts = Array.from(files).map((f) => toAttachment(f));
4579
4551
  setItems((list) => list.concat(atts));
4580
4552
  atts.forEach(run);
4581
4553
  return atts;
4582
4554
  }, [upload, run]);
4583
- const remove = React26.useCallback((att) => {
4555
+ const remove = React27.useCallback((att) => {
4584
4556
  const ac = controllers.current[att.id];
4585
4557
  if (ac) {
4586
4558
  try {
@@ -4591,10 +4563,10 @@ function useStagedFiles(upload, opts) {
4591
4563
  }
4592
4564
  setItems((list) => list.filter((f) => f.id !== att.id));
4593
4565
  }, []);
4594
- const retry = React26.useCallback((att) => {
4566
+ const retry = React27.useCallback((att) => {
4595
4567
  run(att);
4596
4568
  }, [run]);
4597
- const clear = React26.useCallback(() => {
4569
+ const clear = React27.useCallback(() => {
4598
4570
  Object.values(controllers.current).forEach((ac) => {
4599
4571
  try {
4600
4572
  ac && ac.abort();
@@ -4718,7 +4690,7 @@ function FileGrid({ files = [], onOpen, onRemove, onRetry, tiles = true, maxHeig
4718
4690
  }
4719
4691
 
4720
4692
  // src/components/forms/MarkdownEditor.tsx
4721
- var React27 = __toESM(require("react"), 1);
4693
+ var React28 = __toESM(require("react"), 1);
4722
4694
  var import_jsx_runtime57 = require("react/jsx-runtime");
4723
4695
  var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || "");
4724
4696
  var INLINE_RE = /(\*\*\*[^*\n]+\*\*\*|\*\*[^*\n]+\*\*|__[^_\n]+__|~~[^~\n]+~~|`[^`\n]+`|\*[^*\s][^*\n]*\*|(?<![A-Za-z0-9_])_[^_\s][^_\n]*_|\[[^\]\n]*\]\([^)\s\n]*\)|https?:\/\/\S+)/g;
@@ -4932,7 +4904,7 @@ function syncDom(root, value) {
4932
4904
  while (root.children.length > lines.length) root.removeChild(root.lastChild);
4933
4905
  }
4934
4906
  var LIST_CONT = RE_LI;
4935
- var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
4907
+ var MarkdownEditor = React28.forwardRef(function MarkdownEditor2({
4936
4908
  value = "",
4937
4909
  onChange,
4938
4910
  onSubmit,
@@ -4951,10 +4923,10 @@ var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
4951
4923
  className = "",
4952
4924
  id
4953
4925
  }, ref) {
4954
- const boxRef = React27.useRef(null);
4955
- const composing = React27.useRef(false);
4956
- const pendingCaret = React27.useRef(null);
4957
- React27.useLayoutEffect(() => {
4926
+ const boxRef = React28.useRef(null);
4927
+ const composing = React28.useRef(false);
4928
+ const pendingCaret = React28.useRef(null);
4929
+ React28.useLayoutEffect(() => {
4958
4930
  const root = boxRef.current;
4959
4931
  if (!root || composing.current) return;
4960
4932
  const active = document.activeElement === root || root.contains(document.activeElement);
@@ -4963,7 +4935,7 @@ var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
4963
4935
  pendingCaret.current = null;
4964
4936
  if (active && caret != null) placeCaret(root, caret);
4965
4937
  }, [value]);
4966
- React27.useEffect(() => {
4938
+ React28.useEffect(() => {
4967
4939
  if (autoFocus && boxRef.current) boxRef.current.focus();
4968
4940
  }, [autoFocus]);
4969
4941
  const caretNow = () => {
@@ -5030,7 +5002,7 @@ var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
5030
5002
  api.replaceRange(from, to, next, from + next.length);
5031
5003
  }
5032
5004
  };
5033
- React27.useImperativeHandle(ref, () => api);
5005
+ React28.useImperativeHandle(ref, () => api);
5034
5006
  function detect(text, caret) {
5035
5007
  if (!onTrigger) return;
5036
5008
  const upto = text.slice(0, caret);
@@ -5182,10 +5154,10 @@ var MarkdownEditor = React27.forwardRef(function MarkdownEditor2({
5182
5154
  });
5183
5155
 
5184
5156
  // src/components/platform/AccountMenu.tsx
5185
- var React29 = __toESM(require("react"), 1);
5157
+ var React30 = __toESM(require("react"), 1);
5186
5158
 
5187
5159
  // src/kits/session.ts
5188
- var React28 = __toESM(require("react"), 1);
5160
+ var React29 = __toESM(require("react"), 1);
5189
5161
  var PERMISSION_CATALOG = [
5190
5162
  { group: "Plans", items: [
5191
5163
  { key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
@@ -6752,8 +6724,8 @@ function roadmap(overrides) {
6752
6724
  };
6753
6725
  }
6754
6726
  function useSession() {
6755
- const [s, setS] = React28.useState(getSession);
6756
- React28.useEffect(() => subscribe(setS), []);
6727
+ const [s, setS] = React29.useState(getSession);
6728
+ React29.useEffect(() => subscribe(setS), []);
6757
6729
  return s;
6758
6730
  }
6759
6731
  var SessionKit = {
@@ -6823,9 +6795,9 @@ function AccountMenu({
6823
6795
  ...rest
6824
6796
  }) {
6825
6797
  const session = SessionKit.useSession();
6826
- const [open, setOpen] = React29.useState(false);
6827
- const [switching, setSwitching] = React29.useState(false);
6828
- const ref = React29.useRef(null);
6798
+ const [open, setOpen] = React30.useState(false);
6799
+ const [switching, setSwitching] = React30.useState(false);
6800
+ const ref = React30.useRef(null);
6829
6801
  const user = session.user;
6830
6802
  const visibleAdmin = adminLinks.filter((l) => !l.perm || SessionKit.can(l.perm));
6831
6803
  const pick = (item) => {
@@ -6877,7 +6849,7 @@ function AccountMenu({
6877
6849
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { flex: 1 }, children: "View as another member" }),
6878
6850
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("i", { className: "ph ph-caret-" + (switching ? "up" : "down"), "aria-hidden": "true", style: { fontSize: 11 } })
6879
6851
  ] }),
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)(
6852
+ 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
6853
  "button",
6882
6854
  {
6883
6855
  type: "button",
@@ -6909,14 +6881,14 @@ function AccountMenu({
6909
6881
  }
6910
6882
 
6911
6883
  // src/components/platform/ApiSpecBrowser.tsx
6912
- var React30 = __toESM(require("react"), 1);
6884
+ var React31 = __toESM(require("react"), 1);
6913
6885
  var import_jsx_runtime59 = require("react/jsx-runtime");
6914
6886
  var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
6915
6887
  function Json({ obj }) {
6916
6888
  return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
6917
6889
  }
6918
6890
  function Endpoint({ s, onRequest }) {
6919
- const [tried, setTried] = React30.useState(null);
6891
+ const [tried, setTried] = React31.useState(null);
6920
6892
  const run = async () => {
6921
6893
  setTried("busy");
6922
6894
  const t0 = (window.performance || Date).now();
@@ -6989,10 +6961,10 @@ function ApiSpecBrowser({
6989
6961
  className = "",
6990
6962
  ...rest
6991
6963
  }) {
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);
6964
+ const [q, setQ] = React31.useState("");
6965
+ const [method, setMethod] = React31.useState(null);
6966
+ const [mod, setMod] = React31.useState(null);
6967
+ const [listOnly, setListOnly] = React31.useState(false);
6996
6968
  const activeModule = modules && modules.find((m) => m.id === mod);
6997
6969
  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
6970
  const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
@@ -7071,7 +7043,7 @@ function ApiSpecBrowser({
7071
7043
  }
7072
7044
 
7073
7045
  // src/components/platform/ProfilePage.tsx
7074
- var React31 = __toESM(require("react"), 1);
7046
+ var React32 = __toESM(require("react"), 1);
7075
7047
  var import_jsx_runtime60 = require("react/jsx-runtime");
7076
7048
  var TIMEZONES = ["America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles", "America/Anchorage", "Pacific/Honolulu", "Europe/London", "Europe/Berlin"];
7077
7049
  var NOTIFY = [
@@ -7084,7 +7056,7 @@ var NOTIFY = [
7084
7056
  function ProfilePage({ user: userProp, onSave, onNavigate, sessions, showSessions = true, className = "", ...rest }) {
7085
7057
  const session = SessionKit.useSession();
7086
7058
  const user = userProp || session.user;
7087
- const [draft, setDraft] = React31.useState(() => ({
7059
+ const [draft, setDraft] = React32.useState(() => ({
7088
7060
  name: user.name || "",
7089
7061
  title: user.title || "",
7090
7062
  email: user.email || "",
@@ -7093,8 +7065,8 @@ function ProfilePage({ user: userProp, onSave, onNavigate, sessions, showSession
7093
7065
  bio: user.bio || "",
7094
7066
  notify: user.notify || { planShared: true, planChanged: true, goalMissed: true, flagChanged: false, weekly: true }
7095
7067
  }));
7096
- const [saving, setSaving] = React31.useState(false);
7097
- const [saved, setSaved] = React31.useState(false);
7068
+ const [saving, setSaving] = React32.useState(false);
7069
+ const [saved, setSaved] = React32.useState(false);
7098
7070
  const set = (k, v) => {
7099
7071
  setDraft((d) => Object.assign({}, d, { [k]: v }));
7100
7072
  setSaved(false);
@@ -7284,10 +7256,10 @@ function ProfilePage({ user: userProp, onSave, onNavigate, sessions, showSession
7284
7256
  }
7285
7257
 
7286
7258
  // src/components/platform/RoadmapTimeline.tsx
7287
- var React33 = __toESM(require("react"), 1);
7259
+ var React34 = __toESM(require("react"), 1);
7288
7260
 
7289
7261
  // src/kits/runtime.ts
7290
- var React32 = __toESM(require("react"), 1);
7262
+ var React33 = __toESM(require("react"), 1);
7291
7263
  var WIRED_ENDPOINTS = [
7292
7264
  // Nothing yet. Every id below would come from a real service:
7293
7265
  // "plan.get", "placements.list", …
@@ -7458,8 +7430,8 @@ var RuntimeKit = {
7458
7430
  };
7459
7431
  RuntimeKit.declare(FEATURE_NEEDS);
7460
7432
  function useRuntimeMode() {
7461
- const [m, setM] = React32.useState(RuntimeKit.getMode());
7462
- React32.useEffect(() => RuntimeKit.subscribe(setM), []);
7433
+ const [m, setM] = React33.useState(RuntimeKit.getMode());
7434
+ React33.useEffect(() => RuntimeKit.subscribe(setM), []);
7463
7435
  return m;
7464
7436
  }
7465
7437
  function useFeatureStatus(key) {
@@ -7542,12 +7514,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
7542
7514
  }
7543
7515
  function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7544
7516
  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(() => {
7517
+ const [project, setProject] = React34.useState("");
7518
+ const [q, setQ] = React34.useState("");
7519
+ const scrollRef = React34.useRef(null);
7520
+ const nowRef = React34.useRef(null);
7521
+ const rm = React34.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
7522
+ const byKey = React34.useMemo(() => {
7551
7523
  const m = {};
7552
7524
  rm.items.forEach((i) => {
7553
7525
  m[i.key] = i;
@@ -7556,7 +7528,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7556
7528
  }, [rm]);
7557
7529
  const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
7558
7530
  const projects = [...new Set(rm.items.map((i) => i.project))];
7559
- React33.useEffect(() => {
7531
+ React34.useEffect(() => {
7560
7532
  let raf1 = 0, raf2 = 0;
7561
7533
  const place = () => {
7562
7534
  const box = scrollRef.current, mark = nowRef.current;
@@ -7630,7 +7602,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7630
7602
  lastPhase = item.phase;
7631
7603
  const inPhase = items.filter((i) => i.phase === item.phase).length;
7632
7604
  const isBoundary = n === firstPending;
7633
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(React33.Fragment, { children: [
7605
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(React34.Fragment, { children: [
7634
7606
  showPhase ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("div", { className: "fd-rm-era", children: [
7635
7607
  /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
7636
7608
  /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
@@ -7674,8 +7646,8 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7674
7646
  }
7675
7647
 
7676
7648
  // src/components/platform/ComingSoon.tsx
7677
- var React34 = __toESM(require("react"), 1);
7678
- var import_react_dom8 = require("react-dom");
7649
+ var React35 = __toESM(require("react"), 1);
7650
+ var import_react_dom5 = require("react-dom");
7679
7651
  var import_jsx_runtime62 = require("react/jsx-runtime");
7680
7652
  var BYPASS_STORE = "fd.soon.bypass.v1";
7681
7653
  function readBypassed() {
@@ -7692,8 +7664,8 @@ function writeBypassed(list) {
7692
7664
  }
7693
7665
  }
7694
7666
  function useBypass(key) {
7695
- const [on, setOn] = React34.useState(() => !!key && readBypassed().indexOf(key) >= 0);
7696
- React34.useEffect(() => {
7667
+ const [on, setOn] = React35.useState(() => !!key && readBypassed().indexOf(key) >= 0);
7668
+ React35.useEffect(() => {
7697
7669
  setOn(!!key && readBypassed().indexOf(key) >= 0);
7698
7670
  }, [key]);
7699
7671
  const set = (next) => {
@@ -7740,9 +7712,9 @@ function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onB
7740
7712
  ] });
7741
7713
  }
7742
7714
  function useHoverCard(open) {
7743
- const anchor = React34.useRef(null);
7744
- const [pos, setPos] = React34.useState(null);
7745
- React34.useLayoutEffect(() => {
7715
+ const anchor = React35.useRef(null);
7716
+ const [pos, setPos] = React35.useState(null);
7717
+ React35.useLayoutEffect(() => {
7746
7718
  if (!open || !anchor.current) {
7747
7719
  setPos(null);
7748
7720
  return;
@@ -7869,9 +7841,9 @@ function ComingSoon({
7869
7841
  ] });
7870
7842
  }
7871
7843
  function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
7872
- const [open, setOpen] = React34.useState(false);
7844
+ const [open, setOpen] = React35.useState(false);
7873
7845
  const [anchor, pos] = useHoverCard(open);
7874
- const close = React34.useRef(null);
7846
+ const close = React35.useRef(null);
7875
7847
  const show = () => {
7876
7848
  if (close.current) {
7877
7849
  clearTimeout(close.current);
@@ -7887,7 +7859,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
7887
7859
  setOpen(false);
7888
7860
  }, 140);
7889
7861
  };
7890
- React34.useEffect(() => () => {
7862
+ React35.useEffect(() => () => {
7891
7863
  if (close.current) clearTimeout(close.current);
7892
7864
  }, []);
7893
7865
  return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
@@ -7916,7 +7888,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
7916
7888
  children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
7917
7889
  }
7918
7890
  ),
7919
- open && pos ? (0, import_react_dom8.createPortal)(
7891
+ open && pos ? (0, import_react_dom5.createPortal)(
7920
7892
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
7921
7893
  "div",
7922
7894
  {
@@ -8056,13 +8028,13 @@ function PermissionHint({ perm, children }) {
8056
8028
  }
8057
8029
 
8058
8030
  // src/components/platform/ModeSwitch.tsx
8059
- var React35 = __toESM(require("react"), 1);
8031
+ var React36 = __toESM(require("react"), 1);
8060
8032
  var import_jsx_runtime64 = require("react/jsx-runtime");
8061
8033
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
8062
8034
  const mode2 = useRuntimeMode();
8063
- const [open, setOpen] = React35.useState(false);
8064
- const ref = React35.useRef(null);
8065
- React35.useEffect(() => {
8035
+ const [open, setOpen] = React36.useState(false);
8036
+ const ref = React36.useRef(null);
8037
+ React36.useEffect(() => {
8066
8038
  if (!open) return;
8067
8039
  const away = (e) => {
8068
8040
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -8311,11 +8283,11 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8311
8283
  }
8312
8284
 
8313
8285
  // src/components/planner/MixGap.tsx
8314
- var React36 = __toESM(require("react"), 1);
8286
+ var React37 = __toESM(require("react"), 1);
8315
8287
  var import_jsx_runtime67 = require("react/jsx-runtime");
8316
8288
  function MixGap({ rows = [], loading = false, className = "" }) {
8317
8289
  const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
8318
- const [hover, setHover] = React36.useState(null);
8290
+ const [hover, setHover] = React37.useState(null);
8319
8291
  const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
8320
8292
  const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
8321
8293
  return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 4 }, children: [
@@ -8455,7 +8427,7 @@ function ChannelContribution({
8455
8427
  }
8456
8428
 
8457
8429
  // src/components/planner/BudgetReallocator.tsx
8458
- var React37 = __toESM(require("react"), 1);
8430
+ var React38 = __toESM(require("react"), 1);
8459
8431
  var import_jsx_runtime69 = require("react/jsx-runtime");
8460
8432
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
8461
8433
  function BudgetReallocator({
@@ -8471,8 +8443,8 @@ function BudgetReallocator({
8471
8443
  onCancel,
8472
8444
  className = ""
8473
8445
  }) {
8474
- const [draft, setDraft] = React37.useState(spend);
8475
- React37.useEffect(() => setDraft(spend), [spend]);
8446
+ const [draft, setDraft] = React38.useState(spend);
8447
+ React38.useEffect(() => setDraft(spend), [spend]);
8476
8448
  const dirty = draft !== spend;
8477
8449
  const nextCrp = scoreFor ? scoreFor(draft) : crp;
8478
8450
  const nextBand = bandFor(nextCrp);
@@ -8642,7 +8614,7 @@ function ImportanceTag({ level, showLabel = true, size = "sm", className = "" })
8642
8614
  }
8643
8615
 
8644
8616
  // src/components/planner/PreferenceMeter.tsx
8645
- var React38 = __toESM(require("react"), 1);
8617
+ var React39 = __toESM(require("react"), 1);
8646
8618
  var import_jsx_runtime72 = require("react/jsx-runtime");
8647
8619
  function preferenceScore(criteria) {
8648
8620
  const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
@@ -8672,7 +8644,7 @@ function PreferenceMeter({
8672
8644
  className = ""
8673
8645
  }) {
8674
8646
  const value = score == null ? preferenceScore(criteria) : score;
8675
- const segments = React38.useMemo(() => preferenceSegments(criteria), [criteria]);
8647
+ const segments = React39.useMemo(() => preferenceSegments(criteria), [criteria]);
8676
8648
  return /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
8677
8649
  ScoreMeter,
8678
8650
  {
@@ -8711,10 +8683,10 @@ function PreferenceMeter({
8711
8683
  }
8712
8684
 
8713
8685
  // src/components/chat/AgentChatPanel.tsx
8714
- var React44 = __toESM(require("react"), 1);
8686
+ var React45 = __toESM(require("react"), 1);
8715
8687
 
8716
8688
  // src/components/chat/chatEngine.ts
8717
- var React39 = __toESM(require("react"), 1);
8689
+ var React40 = __toESM(require("react"), 1);
8718
8690
  var CHAT_UNAVAILABLE = "chat_unavailable";
8719
8691
  var JOB_PENDING = ["queued", "running"];
8720
8692
  var JOB_SUCCESS = ["completed", "recovered"];
@@ -8768,22 +8740,22 @@ function useChatEngine(opts) {
8768
8740
  onClear,
8769
8741
  onFeedback
8770
8742
  } = 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(() => {
8743
+ const [status, setStatus] = React40.useState("idle");
8744
+ const [threadId, setThreadId] = React40.useState(null);
8745
+ const [messages, setMessages] = React40.useState([]);
8746
+ const [queue, setQueue] = React40.useState([]);
8747
+ const [fatal, setFatal] = React40.useState(null);
8748
+ const [busy, setBusy] = React40.useState(false);
8749
+ const [turnStartedAt, setTurnStartedAt] = React40.useState(null);
8750
+ const listRef = React40.useRef([]);
8751
+ const queueRef = React40.useRef([]);
8752
+ const busyRef = React40.useRef(false);
8753
+ const stoppedRef = React40.useRef(false);
8754
+ const abortRef = React40.useRef(null);
8755
+ const serverCount = React40.useRef(0);
8756
+ const threadRef = React40.useRef(null);
8757
+ const mounted = React40.useRef(true);
8758
+ React40.useEffect(() => {
8787
8759
  mounted.current = true;
8788
8760
  return () => {
8789
8761
  mounted.current = false;
@@ -8805,14 +8777,14 @@ function useChatEngine(opts) {
8805
8777
  }
8806
8778
  return false;
8807
8779
  };
8808
- const loadThread = React39.useCallback(async (id) => {
8780
+ const loadThread = React40.useCallback(async (id) => {
8809
8781
  const data = await apiAdapter.getThread(id);
8810
8782
  const list = [...data && data.messages || []];
8811
8783
  serverCount.current = list.length;
8812
8784
  commit(list);
8813
8785
  return list;
8814
8786
  }, [apiAdapter]);
8815
- React39.useEffect(() => {
8787
+ React40.useEffect(() => {
8816
8788
  if (!apiAdapter) {
8817
8789
  setStatus("idle");
8818
8790
  setFatal(null);
@@ -9025,7 +8997,7 @@ function useChatEngine(opts) {
9025
8997
  await dispatchTurn(turn);
9026
8998
  }
9027
8999
  }
9028
- const send = React39.useCallback((text, attachments) => {
9000
+ const send = React40.useCallback((text, attachments) => {
9029
9001
  const body = (text || "").trim();
9030
9002
  if (!body && !(attachments && attachments.length)) return;
9031
9003
  if (status === "disconnected") return;
@@ -9035,7 +9007,7 @@ function useChatEngine(opts) {
9035
9007
  stoppedRef.current = false;
9036
9008
  drain();
9037
9009
  }, [status]);
9038
- const stop = React39.useCallback(() => {
9010
+ const stop = React40.useCallback(() => {
9039
9011
  stoppedRef.current = true;
9040
9012
  const ac = abortRef.current;
9041
9013
  if (ac) {
@@ -9054,11 +9026,11 @@ function useChatEngine(opts) {
9054
9026
  store.del(STORAGE_PREFIX + threadRef.current);
9055
9027
  }
9056
9028
  }, [apiAdapter]);
9057
- const removeQueued = React39.useCallback((id) => {
9029
+ const removeQueued = React40.useCallback((id) => {
9058
9030
  queueRef.current = queueRef.current.filter((t) => t.id !== id);
9059
9031
  setQueue(queueRef.current.slice());
9060
9032
  }, []);
9061
- const retry = React39.useCallback(() => {
9033
+ const retry = React40.useCallback(() => {
9062
9034
  const list = listRef.current;
9063
9035
  let at = -1;
9064
9036
  for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
@@ -9074,19 +9046,19 @@ function useChatEngine(opts) {
9074
9046
  setQueue(queueRef.current.slice());
9075
9047
  drain();
9076
9048
  }, []);
9077
- const clear = React39.useCallback(() => {
9049
+ const clear = React40.useCallback(() => {
9078
9050
  commit([]);
9079
9051
  serverCount.current = 0;
9080
9052
  queueRef.current = [];
9081
9053
  setQueue([]);
9082
9054
  onClear && onClear();
9083
9055
  }, [onClear]);
9084
- const setFeedback = React39.useCallback((id, value) => {
9056
+ const setFeedback = React40.useCallback((id, value) => {
9085
9057
  patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
9086
9058
  const msg = listRef.current.find((m) => m.id === id);
9087
9059
  onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
9088
9060
  }, [onFeedback]);
9089
- const reload = React39.useCallback(async () => {
9061
+ const reload = React40.useCallback(async () => {
9090
9062
  if (!threadRef.current) return;
9091
9063
  setStatus("loading");
9092
9064
  try {
@@ -9124,10 +9096,10 @@ var ChatKit = {
9124
9096
  };
9125
9097
 
9126
9098
  // src/components/chat/ChatTranscript.tsx
9127
- var React41 = __toESM(require("react"), 1);
9099
+ var React42 = __toESM(require("react"), 1);
9128
9100
 
9129
9101
  // src/components/chat/ChatTurn.tsx
9130
- var React40 = __toESM(require("react"), 1);
9102
+ var React41 = __toESM(require("react"), 1);
9131
9103
  var import_jsx_runtime73 = require("react/jsx-runtime");
9132
9104
  function JsonView({ value }) {
9133
9105
  let text;
@@ -9164,7 +9136,7 @@ function PacketCard({ packet, schema, render, onApply, applied }) {
9164
9136
  ] });
9165
9137
  }
9166
9138
  function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
9167
- const [open, setOpen] = React40.useState(defaultOpen);
9139
+ const [open, setOpen] = React41.useState(defaultOpen);
9168
9140
  if (!text) return null;
9169
9141
  return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
9170
9142
  /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -9181,7 +9153,7 @@ function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
9181
9153
  ] });
9182
9154
  }
9183
9155
  function Citations({ items = [], onOpen }) {
9184
- const [open, setOpen] = React40.useState(false);
9156
+ const [open, setOpen] = React41.useState(false);
9185
9157
  if (!items.length) return null;
9186
9158
  return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("div", { className: "fdc-cites", children: [
9187
9159
  /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
@@ -9207,7 +9179,7 @@ function clampText(text, max) {
9207
9179
  return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
9208
9180
  }
9209
9181
  function MessageBody({ message: m, ctx }) {
9210
- const [expanded, setExpanded] = React40.useState(false);
9182
+ const [expanded, setExpanded] = React41.useState(false);
9211
9183
  const isUser = m.role === "user";
9212
9184
  const raw = m.text || "";
9213
9185
  const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
@@ -9274,9 +9246,9 @@ function MessageBody({ message: m, ctx }) {
9274
9246
  ] });
9275
9247
  }
9276
9248
  function useCopyRun() {
9277
- const [done, setDone] = React40.useState(false);
9278
- const t = React40.useRef(null);
9279
- React40.useEffect(() => () => {
9249
+ const [done, setDone] = React41.useState(false);
9250
+ const t = React41.useRef(null);
9251
+ React41.useEffect(() => () => {
9280
9252
  if (t.current) clearTimeout(t.current);
9281
9253
  }, []);
9282
9254
  return [done, (text) => {
@@ -9303,7 +9275,7 @@ function RunActions({ group, ctx }) {
9303
9275
  ] }),
9304
9276
  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
9277
  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: [
9278
+ isAssistant && ctx.onFeedback ? /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(React41.Fragment, { children: [
9307
9279
  /* @__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
9280
  /* @__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
9281
  ] }) : null,
@@ -9398,10 +9370,10 @@ function ChatTranscript({
9398
9370
  renderEmpty,
9399
9371
  className = ""
9400
9372
  }) {
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);
9373
+ const scroller = React42.useRef(null);
9374
+ const stick = React42.useRef(true);
9375
+ const [pill, setPill] = React42.useState(0);
9376
+ const seen = React42.useRef(0);
9405
9377
  const toBottom = (smooth) => {
9406
9378
  const el = scroller.current;
9407
9379
  if (!el) return;
@@ -9420,7 +9392,7 @@ function ChatTranscript({
9420
9392
  seen.current = messages.length;
9421
9393
  }
9422
9394
  };
9423
- React41.useLayoutEffect(() => {
9395
+ React42.useLayoutEffect(() => {
9424
9396
  const el = scroller.current;
9425
9397
  if (!el) return;
9426
9398
  if (stick.current) {
@@ -9428,7 +9400,7 @@ function ChatTranscript({
9428
9400
  seen.current = messages.length;
9429
9401
  } else setPill(Math.max(0, messages.length - seen.current));
9430
9402
  }, [messages]);
9431
- React41.useEffect(() => {
9403
+ React42.useEffect(() => {
9432
9404
  const el = scroller.current;
9433
9405
  const inner = el && el.firstChild;
9434
9406
  if (!el || !inner || typeof ResizeObserver === "undefined") return;
@@ -9438,7 +9410,7 @@ function ChatTranscript({
9438
9410
  ro.observe(inner);
9439
9411
  return () => ro.disconnect();
9440
9412
  }, []);
9441
- const groups = React41.useMemo(() => groupMessages(messages), [messages]);
9413
+ const groups = React42.useMemo(() => groupMessages(messages), [messages]);
9442
9414
  const empty = !messages.length && status === "ready";
9443
9415
  return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
9444
9416
  /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
@@ -9462,7 +9434,7 @@ function ChatTranscript({
9462
9434
  const prev = groups[i - 1];
9463
9435
  const k = dayKey(g.messages[0].timestamp);
9464
9436
  const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
9465
- return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(React41.Fragment, { children: [
9437
+ return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(React42.Fragment, { children: [
9466
9438
  showDay ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "fdc-day", children: /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { children: dayLabel(k) }) }) : null,
9467
9439
  /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(ChatTurn, { group: g, ctx })
9468
9440
  ] }, g.key || i);
@@ -9479,7 +9451,7 @@ function ChatTranscript({
9479
9451
  var TranscriptKit = { groupMessages };
9480
9452
 
9481
9453
  // src/components/chat/ChatComposer.tsx
9482
- var React42 = __toESM(require("react"), 1);
9454
+ var React43 = __toESM(require("react"), 1);
9483
9455
  var import_jsx_runtime75 = require("react/jsx-runtime");
9484
9456
  function ChatComposer({
9485
9457
  onSubmit,
@@ -9507,17 +9479,17 @@ function ChatComposer({
9507
9479
  onReject,
9508
9480
  onOpenAttachment
9509
9481
  }) {
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);
9482
+ const [text, setText] = React43.useState(draft || "");
9483
+ const [trigger, setTrigger] = React43.useState(null);
9484
+ const [mentionItems, setMentionItems] = React43.useState([]);
9485
+ const [listening, setListening] = React43.useState(false);
9486
+ const [notice, setNotice] = React43.useState(null);
9487
+ const editor = React43.useRef(null);
9488
+ const wrap = React43.useRef(null);
9489
+ const stopVoice = React43.useRef(null);
9518
9490
  const staged = useStagedFiles(fileUploadHandler, { onError: () => {
9519
9491
  } });
9520
- React42.useEffect(() => {
9492
+ React43.useEffect(() => {
9521
9493
  if (draft != null && draft !== text) setText(draft);
9522
9494
  }, [draft]);
9523
9495
  const change = (v) => {
@@ -9551,7 +9523,7 @@ function ChatComposer({
9551
9523
  e.preventDefault();
9552
9524
  staged.add(found.files);
9553
9525
  };
9554
- React42.useEffect(() => {
9526
+ React43.useEffect(() => {
9555
9527
  if (!trigger || trigger.type !== "mention" || !mentionSources) {
9556
9528
  setMentionItems([]);
9557
9529
  return;
@@ -9569,7 +9541,7 @@ function ChatComposer({
9569
9541
  const q = (trigger.query || "").toLowerCase();
9570
9542
  setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
9571
9543
  }, [trigger, mentionSources]);
9572
- const slashItems = React42.useMemo(() => {
9544
+ const slashItems = React43.useMemo(() => {
9573
9545
  if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
9574
9546
  const q = (trigger.query || "").toLowerCase();
9575
9547
  return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
@@ -9743,12 +9715,12 @@ function ChatComposer({
9743
9715
  }
9744
9716
 
9745
9717
  // src/components/chat/ChatSessionBar.tsx
9746
- var React43 = __toESM(require("react"), 1);
9718
+ var React44 = __toESM(require("react"), 1);
9747
9719
  var import_jsx_runtime76 = require("react/jsx-runtime");
9748
9720
  var compact2 = meterFormats.compact;
9749
9721
  function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
9750
- const [open, setOpen] = React43.useState(false);
9751
- const anchor = React43.useRef(null);
9722
+ const [open, setOpen] = React44.useState(false);
9723
+ const anchor = React44.useRef(null);
9752
9724
  const stats = sessionStats || null;
9753
9725
  const cu = contextUsage || null;
9754
9726
  const limits = usageLimits || null;
@@ -9763,7 +9735,7 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9763
9735
  if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
9764
9736
  if (cu) bits.push(pct + "% context");
9765
9737
  const expandable = !!(cu || limits);
9766
- return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React43.Fragment, { children: [
9738
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React44.Fragment, { children: [
9767
9739
  /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-bar", children: [
9768
9740
  /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(
9769
9741
  "button",
@@ -9784,60 +9756,73 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
9784
9756
  ),
9785
9757
  extras
9786
9758
  ] }),
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
- ] }) })
9759
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9760
+ Popover,
9761
+ {
9762
+ open,
9763
+ anchorRef: anchor,
9764
+ placement: "top-start",
9765
+ onClose: () => setOpen(false),
9766
+ minWidth: 330,
9767
+ maxHeight: 460,
9768
+ padded: true,
9769
+ label: "Usage",
9770
+ footer: onClear ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("button", { type: "button", className: "fdc-usage-clear", onClick: () => {
9771
+ setOpen(false);
9772
+ onClear();
9773
+ }, children: [
9774
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("i", { className: "ph ph-trash", "aria-hidden": "true" }),
9775
+ "Clear conversation"
9776
+ ] }) : void 0,
9777
+ children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "fdc-usage", children: [
9778
+ cu ? /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("section", { className: "fdc-usage-sec", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9779
+ SegmentedMeter,
9780
+ {
9781
+ total,
9782
+ segments: cu.segments,
9783
+ label: cu.label || "Context window",
9784
+ format: cu.format === "bytes" ? meterFormats.bytes : compact2,
9785
+ height: 8,
9786
+ legend: true,
9787
+ remainderLabel: "Free"
9788
+ }
9789
+ ) }) : null,
9790
+ limits && limits.length ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { className: "fdc-usage-sec", children: [
9791
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("h4", { className: "fdc-usage-h", children: "Usage limits" }),
9792
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "fdc-usage-rows", children: limits.map((l) => /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9793
+ QuotaRow,
9794
+ {
9795
+ label: l.label,
9796
+ percent: l.percent,
9797
+ note: l.resetsAt ? "Resets " + formatRelative(l.resetsAt) : l.note
9798
+ },
9799
+ l.id
9800
+ )) })
9801
+ ] }) : null,
9802
+ stats ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("section", { className: "fdc-usage-sec fdc-usage-stats", children: [
9803
+ stats.elapsedMs ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9804
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Session" }),
9805
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: formatDuration(stats.elapsedMs) })
9806
+ ] }) : null,
9807
+ stats.tokens ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9808
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Tokens" }),
9809
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: stats.tokens.toLocaleString() })
9810
+ ] }) : null,
9811
+ stats.costUsd != null ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9812
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Cost" }),
9813
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("b", { className: "fd-tabular", children: [
9814
+ "$",
9815
+ Number(stats.costUsd).toFixed(3)
9816
+ ] })
9817
+ ] }) : null,
9818
+ stats.turns ? /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { children: [
9819
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("span", { children: "Turns" }),
9820
+ /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("b", { className: "fd-tabular", children: stats.turns })
9821
+ ] }) : null
9822
+ ] }) : null
9823
+ ] })
9824
+ }
9825
+ )
9841
9826
  ] });
9842
9827
  }
9843
9828
  function ModelControls({
@@ -9891,7 +9876,7 @@ function ModelControls({
9891
9876
  ] })
9892
9877
  });
9893
9878
  }
9894
- return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React43.Fragment, { children: [
9879
+ return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(React44.Fragment, { children: [
9895
9880
  /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
9896
9881
  MenuButton,
9897
9882
  {
@@ -10008,11 +9993,11 @@ function AgentChatPanel({
10008
9993
  onFeedback,
10009
9994
  onEditMessage
10010
9995
  }) {
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]);
9996
+ const [panelWidth, setPanelWidth] = React45.useState(width);
9997
+ const [applied, setApplied] = React45.useState({});
9998
+ const [draft, setDraft] = React45.useState("");
9999
+ const dragging = React45.useRef(null);
10000
+ React45.useEffect(() => setPanelWidth(width), [width]);
10016
10001
  const engine = useChatEngine({
10017
10002
  contextType,
10018
10003
  contextId,
@@ -10216,7 +10201,7 @@ function AgentChatPanel({
10216
10201
  }
10217
10202
 
10218
10203
  // src/kits/query.ts
10219
- var React45 = __toESM(require("react"), 1);
10204
+ var React46 = __toESM(require("react"), 1);
10220
10205
  function eqFilter(get2) {
10221
10206
  return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
10222
10207
  }
@@ -10248,22 +10233,22 @@ function compare(a, b, dir) {
10248
10233
  var API = null;
10249
10234
  var PREFS = null;
10250
10235
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
10251
- const [query, setQuery] = React45.useState(() => {
10236
+ const [query, setQuery] = React46.useState(() => {
10252
10237
  const store = PREFS || window.PlannerPrefs;
10253
10238
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
10254
10239
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
10255
10240
  });
10256
- const savePref = React45.useCallback((patch2) => {
10241
+ const savePref = React46.useCallback((patch2) => {
10257
10242
  const store = PREFS || window.PlannerPrefs;
10258
10243
  if (prefsKey && store) store.setTable(prefsKey, patch2);
10259
10244
  }, [prefsKey]);
10260
- const [res, setRes] = React45.useState(null);
10261
- const [loading, setLoading] = React45.useState(true);
10262
- const seq2 = React45.useRef(0);
10245
+ const [res, setRes] = React46.useState(null);
10246
+ const [loading, setLoading] = React46.useState(true);
10247
+ const seq2 = React46.useRef(0);
10263
10248
  const depKey = (deps || []).join("|");
10264
10249
  const paramKey = JSON.stringify(params || {});
10265
10250
  const queryKey = JSON.stringify(query);
10266
- React45.useEffect(() => {
10251
+ React46.useEffect(() => {
10267
10252
  const id = ++seq2.current;
10268
10253
  setLoading(true);
10269
10254
  const t = setTimeout(() => {