@estiva-app/ui 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -817,9 +817,9 @@ import {
817
817
  createContext as createContext3,
818
818
  useContext as useContext3,
819
819
  useEffect,
820
- useLayoutEffect as useLayoutEffect2,
820
+ useLayoutEffect as useLayoutEffect3,
821
821
  useMemo as useMemo2,
822
- useRef as useRef2,
822
+ useRef as useRef3,
823
823
  useState as useState3
824
824
  } from "react";
825
825
  import { Dialog } from "@base-ui/react/dialog";
@@ -1255,39 +1255,129 @@ function FieldLine({ tone = "helper", children, className }) {
1255
1255
  return /* @__PURE__ */ jsx20("p", { role: tone === "error" ? "alert" : "status", className: cn(LINE_STYLES[tone], className), children });
1256
1256
  }
1257
1257
 
1258
+ // src/Form.tsx
1259
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef2 } from "react";
1260
+ import { Fieldset } from "@base-ui/react/fieldset";
1261
+ import { Form as BaseForm } from "@base-ui/react/form";
1262
+ import { jsx as jsx21 } from "react/jsx-runtime";
1263
+ var CONTROL = 'input:not([type="hidden"]), textarea, select, button, [role="checkbox"], [role="combobox"], [tabindex]:not([tabindex="-1"])';
1264
+ var ONE_LINE = /* @__PURE__ */ new Set(["", "text", "search", "email", "url", "tel", "password", "number"]);
1265
+ function usable(element) {
1266
+ return element instanceof HTMLElement && element.isConnected && element.matches(CONTROL) && !element.matches(":disabled") && element.getAttribute("aria-disabled") !== "true";
1267
+ }
1268
+ function firstInvalid(form) {
1269
+ for (const marked of form.querySelectorAll("[data-invalid]")) {
1270
+ if (usable(marked)) return marked;
1271
+ const inside = Array.from(marked.querySelectorAll(CONTROL)).find(usable);
1272
+ if (inside) return inside;
1273
+ }
1274
+ return void 0;
1275
+ }
1276
+ function Form({ onSubmit, busy: ownBusy = false, enterSends = true, className, children, ...props }) {
1277
+ const outerBusy = useFormBusy();
1278
+ const busy = ownBusy || outerBusy;
1279
+ const form = useRef2(null);
1280
+ const busyNow = useRef2(busy);
1281
+ busyNow.current = busy;
1282
+ const sender = useRef2(null);
1283
+ const holding = useRef2(false);
1284
+ const lastInside = useRef2(null);
1285
+ useLayoutEffect2(() => {
1286
+ const element = form.current;
1287
+ if (!element) return;
1288
+ if (busy) {
1289
+ const active = document.activeElement;
1290
+ const from = active && active !== element && element.contains(active) ? active : !active || active === document.body ? lastInside.current : null;
1291
+ if (from?.isConnected) {
1292
+ sender.current = from;
1293
+ holding.current = true;
1294
+ element.focus();
1295
+ }
1296
+ return;
1297
+ }
1298
+ if (!holding.current) return;
1299
+ holding.current = false;
1300
+ if (document.activeElement !== element && document.activeElement !== document.body) return;
1301
+ const target = firstInvalid(element) ?? (usable(sender.current) ? sender.current : Array.from(element.querySelectorAll(CONTROL)).find(usable));
1302
+ sender.current = null;
1303
+ target?.focus();
1304
+ }, [busy]);
1305
+ const onKeyDown = (event) => {
1306
+ props.onKeyDown?.(event);
1307
+ if (event.key !== "Enter" || event.defaultPrevented || event.nativeEvent.isComposing) return;
1308
+ const target = event.target;
1309
+ const inInput = target instanceof HTMLInputElement;
1310
+ if (inInput) event.preventDefault();
1311
+ if (event.altKey || event.shiftKey) return;
1312
+ if (event.ctrlKey || event.metaKey) {
1313
+ event.preventDefault();
1314
+ form.current?.requestSubmit();
1315
+ return;
1316
+ }
1317
+ if (!inInput) return;
1318
+ const oneLine = ONE_LINE.has((target.getAttribute("type") ?? "").toLowerCase()) && target.getAttribute("role") !== "combobox";
1319
+ if (enterSends && oneLine) form.current?.requestSubmit();
1320
+ };
1321
+ return /* @__PURE__ */ jsx21(
1322
+ BaseForm,
1323
+ {
1324
+ ref: form,
1325
+ ...props,
1326
+ tabIndex: busy ? -1 : props.tabIndex,
1327
+ className: cn("outline-none", className),
1328
+ onFocus: (event) => {
1329
+ if (event.target !== form.current) lastInside.current = event.target;
1330
+ props.onFocus?.(event);
1331
+ },
1332
+ onBlur: (event) => {
1333
+ const next = event.relatedTarget;
1334
+ if (next && !form.current?.contains(next)) lastInside.current = null;
1335
+ props.onBlur?.(event);
1336
+ },
1337
+ onKeyDown,
1338
+ onSubmit: (event) => {
1339
+ event.preventDefault();
1340
+ if (busyNow.current) return;
1341
+ void onSubmit();
1342
+ },
1343
+ children: /* @__PURE__ */ jsx21(Fieldset.Root, { disabled: busy, className: "contents", children: /* @__PURE__ */ jsx21(FormBusyContext.Provider, { value: busy, children }) })
1344
+ }
1345
+ );
1346
+ }
1347
+
1258
1348
  // src/Skeleton.tsx
1259
- import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
1349
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
1260
1350
  function SkeletonBar({ className, style }) {
1261
- return /* @__PURE__ */ jsx21("div", { className: cn("bg-bg-inset rounded-sm animate-pulse", className), style });
1351
+ return /* @__PURE__ */ jsx22("div", { className: cn("bg-bg-inset rounded-sm animate-pulse", className), style });
1262
1352
  }
1263
1353
  var ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160];
1264
1354
  function SkeletonRow({ barWidth = 130 }) {
1265
1355
  return /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 px-2 h-[32px] rounded-lg", children: [
1266
- /* @__PURE__ */ jsx21(SkeletonBar, { className: "w-4 h-4 shrink-0" }),
1267
- /* @__PURE__ */ jsx21(SkeletonBar, { className: "h-3.5", style: { width: barWidth } })
1356
+ /* @__PURE__ */ jsx22(SkeletonBar, { className: "w-4 h-4 shrink-0" }),
1357
+ /* @__PURE__ */ jsx22(SkeletonBar, { className: "h-3.5", style: { width: barWidth } })
1268
1358
  ] });
1269
1359
  }
1270
1360
  function SkeletonList({ rows = 8, className }) {
1271
- return /* @__PURE__ */ jsx21("div", { className: cn("flex flex-col gap-0.5 animate-skeleton-in", className), "aria-hidden": true, children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx21(SkeletonRow, { barWidth: ROW_BAR_WIDTHS[i % ROW_BAR_WIDTHS.length] }, i)) });
1361
+ return /* @__PURE__ */ jsx22("div", { className: cn("flex flex-col gap-0.5 animate-skeleton-in", className), "aria-hidden": true, children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx22(SkeletonRow, { barWidth: ROW_BAR_WIDTHS[i % ROW_BAR_WIDTHS.length] }, i)) });
1272
1362
  }
1273
1363
 
1274
1364
  // src/CommandPalette.tsx
1275
- import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
1365
+ import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
1276
1366
  var PaletteContext = createContext3(null);
1277
1367
  function usePalette(part) {
1278
1368
  const palette = useContext3(PaletteContext);
1279
1369
  if (!palette) throw new Error(`[@estiva-app/ui] ${part} must be inside a CommandPalette.`);
1280
1370
  return palette;
1281
1371
  }
1282
- var CONTROL = 'input:not([type="hidden"]):not([tabindex="-1"]), textarea, button:not([tabindex="-1"]), [role="combobox"]';
1372
+ var CONTROL2 = 'input:not([type="hidden"]):not([tabindex="-1"]), textarea, button:not([tabindex="-1"]), [role="combobox"]';
1283
1373
  function firstControl(popup) {
1284
1374
  if (!popup) return null;
1285
1375
  const field = popup.querySelector("[data-command-palette-field]");
1286
1376
  if (field) return field;
1287
- return popup.querySelector("[data-command-palette-fields]")?.querySelector(CONTROL) ?? null;
1377
+ return popup.querySelector("[data-command-palette-fields]")?.querySelector(CONTROL2) ?? null;
1288
1378
  }
1289
1379
  function CommandPalette({ open, onOpenChange, label, where, modKey = "Ctrl", children }) {
1290
- const popupRef = useRef2(null);
1380
+ const popupRef = useRef3(null);
1291
1381
  useEffect(() => {
1292
1382
  if (!open) return;
1293
1383
  const frame = requestAnimationFrame(() => {
@@ -1298,9 +1388,9 @@ function CommandPalette({ open, onOpenChange, label, where, modKey = "Ctrl", chi
1298
1388
  return () => cancelAnimationFrame(frame);
1299
1389
  });
1300
1390
  const context = useMemo2(() => ({ where, modKey, popupRef }), [where, modKey]);
1301
- return /* @__PURE__ */ jsx22(Dialog.Root, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs16(Dialog.Portal, { children: [
1302
- /* @__PURE__ */ jsx22(Dialog.Backdrop, { className: "fixed inset-0 z-40 bg-scrim" }),
1303
- /* @__PURE__ */ jsx22(Dialog.Viewport, { className: "fixed inset-0 z-50 flex items-start justify-center pt-[16vh]", children: /* @__PURE__ */ jsx22(
1391
+ return /* @__PURE__ */ jsx23(Dialog.Root, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs16(Dialog.Portal, { children: [
1392
+ /* @__PURE__ */ jsx23(Dialog.Backdrop, { className: "fixed inset-0 z-40 bg-scrim" }),
1393
+ /* @__PURE__ */ jsx23(Dialog.Viewport, { className: "fixed inset-0 z-50 flex items-start justify-center pt-[16vh]", children: /* @__PURE__ */ jsx23(
1304
1394
  Dialog.Popup,
1305
1395
  {
1306
1396
  ref: popupRef,
@@ -1310,13 +1400,13 @@ function CommandPalette({ open, onOpenChange, label, where, modKey = "Ctrl", chi
1310
1400
  if (e.target === popupRef.current) firstControl(popupRef.current)?.focus();
1311
1401
  },
1312
1402
  className: "flex w-[658px] max-w-[calc(100vw-32px)] flex-col overflow-hidden rounded-lg border border-border-default bg-bg-elevated shadow-lg outline-none",
1313
- children: /* @__PURE__ */ jsx22(PaletteContext.Provider, { value: context, children })
1403
+ children: /* @__PURE__ */ jsx23(PaletteContext.Provider, { value: context, children })
1314
1404
  }
1315
1405
  ) })
1316
1406
  ] }) });
1317
1407
  }
1318
1408
  function LevelChip({ chip, onBack }) {
1319
- return /* @__PURE__ */ jsx22(InputChip, { label: chip.label, leading: chip.leading, onRemove: onBack, removeLabel: `Leave ${chip.label}`, truncate: true, className: "max-w-[272px] shrink-0" });
1409
+ return /* @__PURE__ */ jsx23(InputChip, { label: chip.label, leading: chip.leading, onRemove: onBack, removeLabel: `Leave ${chip.label}`, truncate: true, className: "max-w-[272px] shrink-0" });
1320
1410
  }
1321
1411
  function useBack(chip, popupRef) {
1322
1412
  return () => {
@@ -1328,9 +1418,9 @@ function useBack(chip, popupRef) {
1328
1418
  function Footer({ keys }) {
1329
1419
  const { where } = usePalette("The footer");
1330
1420
  return /* @__PURE__ */ jsxs16("div", { className: "flex h-9 shrink-0 items-center gap-4 border-t border-border-subtle px-5 text-caption text-text-secondary signal:font-mono signal:text-small signal:text-text-muted", children: [
1331
- /* @__PURE__ */ jsx22("span", { className: "min-w-0 flex-1 truncate", children: where }),
1421
+ /* @__PURE__ */ jsx23("span", { className: "min-w-0 flex-1 truncate", children: where }),
1332
1422
  keys.map(([key, word]) => /* @__PURE__ */ jsxs16("span", { className: "flex shrink-0 items-center gap-1.5", children: [
1333
- /* @__PURE__ */ jsx22(Kbd, { children: key }),
1423
+ /* @__PURE__ */ jsx23(Kbd, { children: key }),
1334
1424
  " ",
1335
1425
  word
1336
1426
  ] }, key))
@@ -1344,11 +1434,11 @@ function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip,
1344
1434
  const rows = items.flatMap((g) => g.items);
1345
1435
  const [litId, setLitId] = useState3();
1346
1436
  const lit = litId === void 0 ? void 0 : rows.find((r) => r.id === litId);
1347
- const inputRef = useRef2(null);
1348
- const reported = useRef2({});
1349
- const settled = useRef2(null);
1350
- const moved = useRef2(false);
1351
- useLayoutEffect2(() => {
1437
+ const inputRef = useRef3(null);
1438
+ const reported = useRef3({});
1439
+ const settled = useRef3(null);
1440
+ const moved = useRef3(false);
1441
+ useLayoutEffect3(() => {
1352
1442
  const before = settled.current;
1353
1443
  const now = reported.current;
1354
1444
  const sameLevel = before?.query === query && before?.level === chip?.label;
@@ -1431,9 +1521,9 @@ function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip,
1431
1521
  },
1432
1522
  children: [
1433
1523
  /* @__PURE__ */ jsxs16("div", { className: "flex h-12 shrink-0 items-center gap-3 border-b border-border-subtle px-5", children: [
1434
- /* @__PURE__ */ jsx22(IconSearch, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" }),
1435
- chip && /* @__PURE__ */ jsx22(LevelChip, { chip, onBack: back }),
1436
- /* @__PURE__ */ jsx22(
1524
+ /* @__PURE__ */ jsx23(IconSearch, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" }),
1525
+ chip && /* @__PURE__ */ jsx23(LevelChip, { chip, onBack: back }),
1526
+ /* @__PURE__ */ jsx23(
1437
1527
  Autocomplete.Input,
1438
1528
  {
1439
1529
  ref: inputRef,
@@ -1446,42 +1536,42 @@ function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip,
1446
1536
  )
1447
1537
  ] }),
1448
1538
  /* @__PURE__ */ jsxs16(ScrollArea, { viewportClassName: "max-h-[420px]", contentClassName: "flex flex-col p-2", children: [
1449
- children != null && /* @__PURE__ */ jsx22("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children }),
1450
- /* @__PURE__ */ jsx22(Autocomplete.List, { className: "flex flex-col", children: (group) => /* @__PURE__ */ jsxs16(Autocomplete.Group, { items: group.items, className: "flex flex-col", children: [
1451
- /* @__PURE__ */ jsx22(Autocomplete.GroupLabel, { className: "flex h-7 shrink-0 items-center px-3", children: /* @__PURE__ */ jsx22(SectionLabel, { className: "text-text-secondary", children: group.value }) }),
1452
- /* @__PURE__ */ jsx22(Autocomplete.Collection, { children: (row) => (
1539
+ children != null && /* @__PURE__ */ jsx23("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children }),
1540
+ /* @__PURE__ */ jsx23(Autocomplete.List, { className: "flex flex-col", children: (group) => /* @__PURE__ */ jsxs16(Autocomplete.Group, { items: group.items, className: "flex flex-col", children: [
1541
+ /* @__PURE__ */ jsx23(Autocomplete.GroupLabel, { className: "flex h-7 shrink-0 items-center px-3", children: /* @__PURE__ */ jsx23(SectionLabel, { className: "text-text-secondary", children: group.value }) }),
1542
+ /* @__PURE__ */ jsx23(Autocomplete.Collection, { children: (row) => (
1453
1543
  /* The menu row, as the list's own option — the way Select
1454
1544
  puts it on `Select.Item`. One row, two parts. */
1455
- /* @__PURE__ */ jsx22(Autocomplete.Item, { value: row, onClick: () => row.onSelect(), className: menuItemClassName({ size: "tall" }), children: /* @__PURE__ */ jsx22(
1545
+ /* @__PURE__ */ jsx23(Autocomplete.Item, { value: row, onClick: () => row.onSelect(), className: menuItemClassName({ size: "tall" }), children: /* @__PURE__ */ jsx23(
1456
1546
  MenuItemBody,
1457
1547
  {
1458
1548
  size: "tall",
1459
1549
  label: row.label,
1460
1550
  description: row.description,
1461
- leading: /* @__PURE__ */ jsx22(RowLeading, { row }),
1551
+ leading: /* @__PURE__ */ jsx23(RowLeading, { row }),
1462
1552
  submenu: !!row.onGoIn,
1463
- hint: row.onGoIn ? /* @__PURE__ */ jsx22(Kbd, { children: "Tab" }) : /* @__PURE__ */ jsx22(EnterHint, {})
1553
+ hint: row.onGoIn ? /* @__PURE__ */ jsx23(Kbd, { children: "Tab" }) : /* @__PURE__ */ jsx23(EnterHint, {})
1464
1554
  }
1465
1555
  ) }, row.id)
1466
1556
  ) })
1467
1557
  ] }, group.value) }),
1468
- /* @__PURE__ */ jsx22(Autocomplete.Status, { className: "flex items-center gap-2 px-3 [&:not(:empty)]:h-8", children: pending && /* @__PURE__ */ jsxs16(Fragment3, { children: [
1469
- /* @__PURE__ */ jsx22(IconLoader22, { size: 12, stroke: 1.5, className: "shrink-0 animate-spin text-text-muted" }),
1470
- /* @__PURE__ */ jsx22("span", { className: "text-caption text-text-muted", children: pending })
1558
+ /* @__PURE__ */ jsx23(Autocomplete.Status, { className: "flex items-center gap-2 px-3 [&:not(:empty)]:h-8", children: pending && /* @__PURE__ */ jsxs16(Fragment3, { children: [
1559
+ /* @__PURE__ */ jsx23(IconLoader22, { size: 12, stroke: 1.5, className: "shrink-0 animate-spin text-text-muted" }),
1560
+ /* @__PURE__ */ jsx23("span", { className: "text-caption text-text-muted", children: pending })
1471
1561
  ] }) }),
1472
- notes.map((note) => /* @__PURE__ */ jsx22("div", { className: "flex min-h-8 items-center px-3", children: /* @__PURE__ */ jsx22(FieldLine, { children: note }) }, note)),
1473
- /* @__PURE__ */ jsx22(Autocomplete.Empty, { className: "flex items-center px-3 [&:not(:empty)]:min-h-10", children: empty && /* @__PURE__ */ jsx22(EmptyState, { scope: "section", message: empty }) })
1562
+ notes.map((note) => /* @__PURE__ */ jsx23("div", { className: "flex min-h-8 items-center px-3", children: /* @__PURE__ */ jsx23(FieldLine, { children: note }) }, note)),
1563
+ /* @__PURE__ */ jsx23(Autocomplete.Empty, { className: "flex items-center px-3 [&:not(:empty)]:min-h-10", children: empty && /* @__PURE__ */ jsx23(EmptyState, { scope: "section", message: empty }) })
1474
1564
  ] }),
1475
- /* @__PURE__ */ jsx22(Footer, { keys })
1565
+ /* @__PURE__ */ jsx23(Footer, { keys })
1476
1566
  ]
1477
1567
  }
1478
1568
  );
1479
1569
  }
1480
1570
  function RowLeading({ row }) {
1481
1571
  if (row.icon != null) {
1482
- return /* @__PURE__ */ jsx22("span", { className: "flex size-8 items-center justify-center rounded-sm bg-bg-inset text-text-secondary", children: row.icon });
1572
+ return /* @__PURE__ */ jsx23("span", { className: "flex size-8 items-center justify-center rounded-sm bg-bg-inset text-text-secondary", children: row.icon });
1483
1573
  }
1484
- return /* @__PURE__ */ jsx22("span", { className: "flex size-8 items-center justify-center", children: row.leading });
1574
+ return /* @__PURE__ */ jsx23("span", { className: "flex size-8 items-center justify-center", children: row.leading });
1485
1575
  }
1486
1576
  var TEXT_TYPES = /* @__PURE__ */ new Set(["", "text", "search", "email", "url", "tel", "password", "number"]);
1487
1577
  function isTextField(el) {
@@ -1491,31 +1581,18 @@ function isTextField(el) {
1491
1581
  function CommandPaletteForm({ chip, icon, submitLabel, onSubmit, submitWaits, working, error, children }) {
1492
1582
  const { modKey, popupRef } = usePalette("CommandPaletteForm");
1493
1583
  const back = useBack(chip, popupRef);
1494
- const frameRef = useRef2(null);
1584
+ const frameRef = useRef3(null);
1495
1585
  const busy = !!working;
1496
- const busyRef = useRef2(busy);
1586
+ const busyRef = useRef3(busy);
1497
1587
  busyRef.current = busy;
1498
- const returnTo = useRef2(null);
1499
- const firstInvalid = () => frameRef.current?.querySelector("[data-invalid]")?.querySelector(CONTROL) ?? null;
1588
+ const firstInvalid2 = () => frameRef.current?.querySelector("[data-invalid]")?.querySelector(CONTROL2) ?? null;
1500
1589
  const submit = () => {
1501
1590
  if (busyRef.current || submitWaits) return;
1502
- const active = document.activeElement;
1503
- returnTo.current = active instanceof HTMLElement && frameRef.current?.contains(active) ? active : null;
1504
1591
  onSubmit();
1505
1592
  requestAnimationFrame(() => {
1506
- if (!busyRef.current) firstInvalid()?.focus();
1593
+ if (!busyRef.current) firstInvalid2()?.focus();
1507
1594
  });
1508
1595
  };
1509
- useLayoutEffect2(() => {
1510
- if (busy) {
1511
- frameRef.current?.focus();
1512
- return;
1513
- }
1514
- if (document.activeElement !== frameRef.current) return;
1515
- const was = returnTo.current;
1516
- const target = firstInvalid() ?? (was?.isConnected && !was.disabled ? was : null) ?? firstControl(popupRef.current);
1517
- target?.focus();
1518
- }, [busy]);
1519
1596
  const backWorksFrom = (el) => {
1520
1597
  const frame = frameRef.current;
1521
1598
  if (!frame || busy || !el || !frame.contains(el)) return false;
@@ -1524,13 +1601,8 @@ function CommandPaletteForm({ chip, icon, submitLabel, onSubmit, submitWaits, wo
1524
1601
  };
1525
1602
  const [backNamed, setBackNamed] = useState3(false);
1526
1603
  const measure = () => setBackNamed(backWorksFrom(document.activeElement));
1527
- useLayoutEffect2(measure);
1604
+ useLayoutEffect3(measure);
1528
1605
  const onKeyDown = (e) => {
1529
- if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
1530
- e.preventDefault();
1531
- submit();
1532
- return;
1533
- }
1534
1606
  if (e.key === "Backspace" && !e.ctrlKey && !e.metaKey && !e.altKey && backWorksFrom(e.target)) {
1535
1607
  e.preventDefault();
1536
1608
  back();
@@ -1543,52 +1615,54 @@ function CommandPaletteForm({ chip, icon, submitLabel, onSubmit, submitWaits, wo
1543
1615
  ];
1544
1616
  return /* @__PURE__ */ jsxs16("div", { ref: frameRef, tabIndex: -1, onKeyDown, onFocus: measure, onBlur: measure, onInput: measure, className: "flex min-h-0 flex-col outline-none", children: [
1545
1617
  /* @__PURE__ */ jsxs16("div", { className: "flex h-12 shrink-0 items-center gap-3 border-b border-border-subtle px-5", children: [
1546
- icon != null && /* @__PURE__ */ jsx22("span", { className: "flex shrink-0 items-center text-text-secondary", children: icon }),
1547
- /* @__PURE__ */ jsx22(LevelChip, { chip, onBack: back })
1618
+ icon != null && /* @__PURE__ */ jsx23("span", { className: "flex shrink-0 items-center text-text-secondary", children: icon }),
1619
+ /* @__PURE__ */ jsx23(LevelChip, { chip, onBack: back })
1548
1620
  ] }),
1549
- /* @__PURE__ */ jsx22(ScrollArea, { viewportClassName: "max-h-[420px]", contentClassName: "flex flex-col px-5 py-4", children: /* @__PURE__ */ jsx22("fieldset", { "data-command-palette-fields": "", disabled: busy, className: "contents", children: /* @__PURE__ */ jsx22("div", { className: "flex flex-col gap-4", children }) }) }),
1550
- /* @__PURE__ */ jsxs16("div", { className: "flex shrink-0 items-center gap-3 px-5 pb-4", children: [
1551
- /* @__PURE__ */ jsx22("div", { className: "min-w-0 flex-1", children: working ? /* @__PURE__ */ jsx22(FieldLine, { children: working.line }) : error ? /* @__PURE__ */ jsx22(FieldLine, { tone: "error", children: error }) : null }),
1552
- /* @__PURE__ */ jsx22(
1553
- Button,
1554
- {
1555
- variant: "primary",
1556
- onClick: submit,
1557
- disabled: busy,
1558
- disabledReason: busy ? void 0 : submitWaits,
1559
- leadingIcon: busy ? /* @__PURE__ */ jsx22(IconLoader22, { size: 16, stroke: 1.5, className: "animate-spin" }) : void 0,
1560
- children: working ? working.button : submitLabel
1561
- }
1562
- )
1621
+ /* @__PURE__ */ jsxs16(Form, { "data-command-palette-fields": "", busy, enterSends: false, onSubmit: submit, className: "flex min-h-0 flex-col", children: [
1622
+ /* @__PURE__ */ jsx23(ScrollArea, { viewportClassName: "max-h-[420px]", contentClassName: "flex flex-col px-5 py-4", children: /* @__PURE__ */ jsx23("div", { className: "flex flex-col gap-4", children }) }),
1623
+ /* @__PURE__ */ jsxs16("div", { className: "flex shrink-0 items-center gap-3 px-5 pb-4", children: [
1624
+ /* @__PURE__ */ jsx23("div", { className: "min-w-0 flex-1", children: working ? /* @__PURE__ */ jsx23(FieldLine, { children: working.line }) : error ? /* @__PURE__ */ jsx23(FieldLine, { tone: "error", children: error }) : null }),
1625
+ /* @__PURE__ */ jsx23(
1626
+ Button,
1627
+ {
1628
+ type: "submit",
1629
+ variant: "primary",
1630
+ disabled: busy,
1631
+ disabledReason: busy ? void 0 : submitWaits,
1632
+ leadingIcon: busy ? /* @__PURE__ */ jsx23(IconLoader22, { size: 16, stroke: 1.5, className: "animate-spin" }) : void 0,
1633
+ children: working ? working.button : submitLabel
1634
+ }
1635
+ )
1636
+ ] })
1563
1637
  ] }),
1564
- /* @__PURE__ */ jsx22(Footer, { keys })
1638
+ /* @__PURE__ */ jsx23(Footer, { keys })
1565
1639
  ] });
1566
1640
  }
1567
1641
  var BAR_WIDTHS = ["w-4/5", "w-3/5", "w-2/3"];
1568
1642
  function CommandPaletteWorking({ children, bars = 2 }) {
1569
1643
  return /* @__PURE__ */ jsxs16("div", { role: "status", className: "flex flex-col gap-2", children: [
1570
1644
  /* @__PURE__ */ jsxs16("span", { className: "flex items-center gap-2 text-caption text-text-secondary", children: [
1571
- /* @__PURE__ */ jsx22(IconLoader22, { size: 14, stroke: 1.5, className: "shrink-0 animate-spin" }),
1645
+ /* @__PURE__ */ jsx23(IconLoader22, { size: 14, stroke: 1.5, className: "shrink-0 animate-spin" }),
1572
1646
  children
1573
1647
  ] }),
1574
- BAR_WIDTHS.slice(0, bars).map((width) => /* @__PURE__ */ jsx22(SkeletonBar, { className: cn("h-3", width) }, width))
1648
+ BAR_WIDTHS.slice(0, bars).map((width) => /* @__PURE__ */ jsx23(SkeletonBar, { className: cn("h-3", width) }, width))
1575
1649
  ] });
1576
1650
  }
1577
1651
  function CommandPaletteAnswer({ children, note }) {
1578
1652
  const paragraphs = children.split(/\n\s*\n/).filter((p) => p.trim() !== "");
1579
1653
  return /* @__PURE__ */ jsxs16("div", { className: "flex flex-col gap-2", children: [
1580
- paragraphs.map((paragraph, i) => /* @__PURE__ */ jsx22("p", { className: "whitespace-pre-wrap text-body-2 text-text-primary", children: paragraph.split(/\s*(\[\d+\])/).map(
1581
- (part, j) => /^\[\d+\]$/.test(part) ? /* @__PURE__ */ jsx22("sup", { className: "ml-0.5 font-mono text-small text-accent-primary", children: part.slice(1, -1) }, j) : part
1654
+ paragraphs.map((paragraph, i) => /* @__PURE__ */ jsx23("p", { className: "whitespace-pre-wrap text-body-2 text-text-primary", children: paragraph.split(/\s*(\[\d+\])/).map(
1655
+ (part, j) => /^\[\d+\]$/.test(part) ? /* @__PURE__ */ jsx23("sup", { className: "ml-0.5 font-mono text-small text-accent-primary", children: part.slice(1, -1) }, j) : part
1582
1656
  ) }, i)),
1583
- note && /* @__PURE__ */ jsx22(FieldLine, { children: note })
1657
+ note && /* @__PURE__ */ jsx23(FieldLine, { children: note })
1584
1658
  ] });
1585
1659
  }
1586
1660
  function CommandPaletteQuote({ children }) {
1587
- return /* @__PURE__ */ jsx22("p", { className: "whitespace-pre-wrap border-l-2 border-border-default pl-3 text-body-2 text-text-primary", children });
1661
+ return /* @__PURE__ */ jsx23("p", { className: "whitespace-pre-wrap border-l-2 border-border-default pl-3 text-body-2 text-text-primary", children });
1588
1662
  }
1589
1663
 
1590
1664
  // src/InlineChip.tsx
1591
- import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
1665
+ import { Fragment as Fragment4, jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
1592
1666
  var INLINE_CHIP_CLASSES = "inline-flex h-[1.4em] items-center gap-1 rounded-sm px-1 mx-0.5 align-top text-body-2 font-normal select-none";
1593
1667
  var INLINE_CHIP_TONE_CLASSES = {
1594
1668
  /** A thing or a place — anything that is not a person. */
@@ -1611,22 +1685,22 @@ function inlineChipClassName(tone, className) {
1611
1685
  }
1612
1686
  function InlineChip({ tone = "neutral", icon, href, className, children, ...props }) {
1613
1687
  const body = /* @__PURE__ */ jsxs17(Fragment4, { children: [
1614
- icon && /* @__PURE__ */ jsx23("span", { className: "flex size-4 shrink-0 items-center justify-center text-text-secondary", children: icon }),
1688
+ icon && /* @__PURE__ */ jsx24("span", { className: "flex size-4 shrink-0 items-center justify-center text-text-secondary", children: icon }),
1615
1689
  children
1616
1690
  ] });
1617
1691
  const classes = inlineChipClassName(tone, className);
1618
1692
  if (href === void 0) {
1619
- return /* @__PURE__ */ jsx23("span", { className: classes, ...props, children: body });
1693
+ return /* @__PURE__ */ jsx24("span", { className: classes, ...props, children: body });
1620
1694
  }
1621
- return /* @__PURE__ */ jsx23("a", { href, className: classes, ...props, children: body });
1695
+ return /* @__PURE__ */ jsx24("a", { href, className: classes, ...props, children: body });
1622
1696
  }
1623
1697
 
1624
1698
  // src/IdentityMenu.tsx
1625
- import { useCallback, useRef as useRef3 } from "react";
1699
+ import { useCallback, useRef as useRef4 } from "react";
1626
1700
 
1627
1701
  // src/Divider.tsx
1628
1702
  import { Separator } from "@base-ui/react/separator";
1629
- import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
1703
+ import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
1630
1704
  function Divider({ orientation = "horizontal", label, tone = "default", className }) {
1631
1705
  if (label && orientation === "horizontal") {
1632
1706
  const line = tone === "warning" ? "bg-warning-muted" : "bg-border-subtle";
@@ -1637,14 +1711,14 @@ function Divider({ orientation = "horizontal", label, tone = "default", classNam
1637
1711
  "aria-label": label,
1638
1712
  className: cn("flex shrink-0 items-center gap-2 mx-3", className),
1639
1713
  children: [
1640
- /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", className: cn("h-px flex-1", line) }),
1641
- /* @__PURE__ */ jsx24("span", { className: cn("shrink-0 text-caption", tone === "warning" ? "text-warning-default" : "text-text-muted"), children: label }),
1642
- /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", className: cn("h-px flex-1", line) })
1714
+ /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", className: cn("h-px flex-1", line) }),
1715
+ /* @__PURE__ */ jsx25("span", { className: cn("shrink-0 text-caption", tone === "warning" ? "text-warning-default" : "text-text-muted"), children: label }),
1716
+ /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", className: cn("h-px flex-1", line) })
1643
1717
  ]
1644
1718
  }
1645
1719
  );
1646
1720
  }
1647
- return /* @__PURE__ */ jsx24(
1721
+ return /* @__PURE__ */ jsx25(
1648
1722
  Separator,
1649
1723
  {
1650
1724
  orientation,
@@ -1654,15 +1728,15 @@ function Divider({ orientation = "horizontal", label, tone = "default", classNam
1654
1728
  }
1655
1729
 
1656
1730
  // src/Person.tsx
1657
- import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
1731
+ import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
1658
1732
  function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
1659
1733
  return (
1660
1734
  // gap-2: the face-to-name distance is one rule (8px) wherever a person
1661
1735
  // appears — Katerina, 2026-09-01, set on Person so PersonTrigger and every
1662
1736
  // row agree by construction.
1663
1737
  /* @__PURE__ */ jsxs19("span", { className: cn("flex min-w-0 items-center gap-2", className), children: [
1664
- /* @__PURE__ */ jsx25(Avatar, { name, src: picture, size }),
1665
- /* @__PURE__ */ jsx25("span", { className: cn("truncate", !name && "text-text-muted"), children: name ?? fallback })
1738
+ /* @__PURE__ */ jsx26(Avatar, { name, src: picture, size }),
1739
+ /* @__PURE__ */ jsx26("span", { className: cn("truncate", !name && "text-text-muted"), children: name ?? fallback })
1666
1740
  ] })
1667
1741
  );
1668
1742
  }
@@ -1670,10 +1744,10 @@ function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
1670
1744
  // src/PersonTrigger.tsx
1671
1745
  import { Button as BaseButton5 } from "@base-ui/react/button";
1672
1746
  import { IconChevronDown } from "@tabler/icons-react";
1673
- import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
1747
+ import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
1674
1748
  function PersonTrigger({ name, picture, fallback, size, open = false, compact = false, className, ...props }) {
1675
1749
  if (compact) {
1676
- return /* @__PURE__ */ jsx26(
1750
+ return /* @__PURE__ */ jsx27(
1677
1751
  BaseButton5,
1678
1752
  {
1679
1753
  type: "button",
@@ -1682,7 +1756,7 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
1682
1756
  "aria-label": props["aria-label"] ?? name ?? fallback,
1683
1757
  className: cn("cursor-pointer rounded-full", className),
1684
1758
  ...props,
1685
- children: /* @__PURE__ */ jsx26(Avatar, { name, src: picture, size: size ?? 36 })
1759
+ children: /* @__PURE__ */ jsx27(Avatar, { name, src: picture, size: size ?? 36 })
1686
1760
  }
1687
1761
  );
1688
1762
  }
@@ -1704,18 +1778,18 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
1704
1778
  ),
1705
1779
  ...props,
1706
1780
  children: [
1707
- /* @__PURE__ */ jsx26(Person, { name, picture, fallback, size: size ?? 22 }),
1708
- /* @__PURE__ */ jsx26(IconChevronDown, { size: 14, stroke: 1.5, className: "shrink-0 text-text-muted" })
1781
+ /* @__PURE__ */ jsx27(Person, { name, picture, fallback, size: size ?? 22 }),
1782
+ /* @__PURE__ */ jsx27(IconChevronDown, { size: 14, stroke: 1.5, className: "shrink-0 text-text-muted" })
1709
1783
  ]
1710
1784
  }
1711
1785
  );
1712
1786
  }
1713
1787
 
1714
1788
  // src/IdentityMenu.tsx
1715
- import { Fragment as Fragment5, jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
1789
+ import { Fragment as Fragment5, jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
1716
1790
  function IdentityPanel({ className, onClose = () => {
1717
1791
  }, ...rest }) {
1718
- return /* @__PURE__ */ jsx27(MenuPanel, { className: cn("w-72", className), children: /* @__PURE__ */ jsx27(IdentityRows, { ...rest, onClose }) });
1792
+ return /* @__PURE__ */ jsx28(MenuPanel, { className: cn("w-72", className), children: /* @__PURE__ */ jsx28(IdentityRows, { ...rest, onClose }) });
1719
1793
  }
1720
1794
  function IdentityRows({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, onClose, children }) {
1721
1795
  const act = (action) => () => {
@@ -1725,51 +1799,51 @@ function IdentityRows({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, on
1725
1799
  const appRows = typeof children === "function" ? children(onClose) : children;
1726
1800
  return /* @__PURE__ */ jsxs21(Fragment5, { children: [
1727
1801
  /* @__PURE__ */ jsxs21(MenuSection, { label: signedIn ? "Signed in as" : "Acting as", children: [
1728
- /* @__PURE__ */ jsx27(MenuRow, { children: /* @__PURE__ */ jsx27(Person, { name: me.name, picture: me.picture, fallback: "Anonymous", size: 28, className: "text-body-2-strong" }) }),
1729
- /* @__PURE__ */ jsx27(Note, { children: signedIn ? "Your Estiva ID. The same person you are in every Estiva app." : "A key held by this browser only. Nobody else knows who you are." }),
1802
+ /* @__PURE__ */ jsx28(MenuRow, { children: /* @__PURE__ */ jsx28(Person, { name: me.name, picture: me.picture, fallback: "Anonymous", size: 28, className: "text-body-2-strong" }) }),
1803
+ /* @__PURE__ */ jsx28(Note, { children: signedIn ? "Your Estiva ID. The same person you are in every Estiva app." : "A key held by this browser only. Nobody else knows who you are." }),
1730
1804
  me.email && /* @__PURE__ */ jsxs21(Note, { children: [
1731
1805
  me.email,
1732
1806
  " \xB7 known to Estiva, never published to the relay"
1733
1807
  ] })
1734
1808
  ] }),
1735
1809
  relayUrl && /* @__PURE__ */ jsxs21(Fragment5, { children: [
1736
- /* @__PURE__ */ jsx27(Divider, { className: "mx-0 my-2" }),
1810
+ /* @__PURE__ */ jsx28(Divider, { className: "mx-0 my-2" }),
1737
1811
  /* @__PURE__ */ jsxs21(MenuSection, { label: "Workspace", children: [
1738
- /* @__PURE__ */ jsx27(MenuRow, { children: /* @__PURE__ */ jsx27("span", { className: "break-all font-mono text-caption text-text-primary", children: relayUrl }) }),
1739
- /* @__PURE__ */ jsx27(Note, { children: "Everyone on this relay shares this workspace, in every app." })
1812
+ /* @__PURE__ */ jsx28(MenuRow, { children: /* @__PURE__ */ jsx28("span", { className: "break-all font-mono text-caption text-text-primary", children: relayUrl }) }),
1813
+ /* @__PURE__ */ jsx28(Note, { children: "Everyone on this relay shares this workspace, in every app." })
1740
1814
  ] })
1741
1815
  ] }),
1742
1816
  appRows && /* @__PURE__ */ jsxs21(Fragment5, { children: [
1743
- /* @__PURE__ */ jsx27(Divider, { className: "mx-0 my-2" }),
1817
+ /* @__PURE__ */ jsx28(Divider, { className: "mx-0 my-2" }),
1744
1818
  appRows
1745
1819
  ] }),
1746
- signedIn && idBase || onCopyKey || idBase && onSignOut ? /* @__PURE__ */ jsx27(Divider, { className: "mx-0 my-2" }) : null,
1747
- signedIn && idBase && /* @__PURE__ */ jsx27(
1820
+ signedIn && idBase || onCopyKey || idBase && onSignOut ? /* @__PURE__ */ jsx28(Divider, { className: "mx-0 my-2" }) : null,
1821
+ signedIn && idBase && /* @__PURE__ */ jsx28(
1748
1822
  MenuItem,
1749
1823
  {
1750
1824
  label: "Edit your profile in Estiva ID",
1751
1825
  onClick: act(() => window.open(idBase, "_blank", "noopener,noreferrer"))
1752
1826
  }
1753
1827
  ),
1754
- onCopyKey && /* @__PURE__ */ jsx27(MenuItem, { label: "Copy public key", onClick: act(onCopyKey) }),
1755
- idBase && onSignOut && /* @__PURE__ */ jsx27(MenuItem, { label: "Sign out", onClick: act(onSignOut) })
1828
+ onCopyKey && /* @__PURE__ */ jsx28(MenuItem, { label: "Copy public key", onClick: act(onCopyKey) }),
1829
+ idBase && onSignOut && /* @__PURE__ */ jsx28(MenuItem, { label: "Sign out", onClick: act(onSignOut) })
1756
1830
  ] });
1757
1831
  }
1758
1832
  function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, compact = false, className, children }) {
1759
- const actions = useRef3(null);
1833
+ const actions = useRef4(null);
1760
1834
  const close = useCallback(() => actions.current?.close(), []);
1761
1835
  return (
1762
1836
  /* The wrapper carries the caller's `className` and nothing else. It used
1763
1837
  to be `relative`, because the panel was positioned against it; the panel
1764
1838
  hangs from the trigger and portals now, so a positioning context here
1765
1839
  would only be a lie about what this box does. */
1766
- /* @__PURE__ */ jsx27("div", { className, children: /* @__PURE__ */ jsx27(
1840
+ /* @__PURE__ */ jsx28("div", { className, children: /* @__PURE__ */ jsx28(
1767
1841
  Menu,
1768
1842
  {
1769
1843
  align: "right",
1770
1844
  actionsRef: actions,
1771
1845
  className: "w-72",
1772
- trigger: /* @__PURE__ */ jsx27(
1846
+ trigger: /* @__PURE__ */ jsx28(
1773
1847
  PersonTrigger,
1774
1848
  {
1775
1849
  name: me.name,
@@ -1780,7 +1854,7 @@ function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, co
1780
1854
  "aria-label": compact ? "Account menu" : void 0
1781
1855
  }
1782
1856
  ),
1783
- children: /* @__PURE__ */ jsx27(
1857
+ children: /* @__PURE__ */ jsx28(
1784
1858
  IdentityRows,
1785
1859
  {
1786
1860
  me,
@@ -1798,14 +1872,14 @@ function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, co
1798
1872
  );
1799
1873
  }
1800
1874
  function Note({ children }) {
1801
- return /* @__PURE__ */ jsx27("span", { className: "px-2 pb-1.5 text-caption text-text-secondary", children });
1875
+ return /* @__PURE__ */ jsx28("span", { className: "px-2 pb-1.5 text-caption text-text-secondary", children });
1802
1876
  }
1803
1877
 
1804
1878
  // src/FilePicker.tsx
1805
1879
  import { forwardRef } from "react";
1806
- import { jsx as jsx28 } from "react/jsx-runtime";
1880
+ import { jsx as jsx29 } from "react/jsx-runtime";
1807
1881
  var FilePicker = forwardRef(function FilePicker2({ onPick, ...props }, ref) {
1808
- return /* @__PURE__ */ jsx28(
1882
+ return /* @__PURE__ */ jsx29(
1809
1883
  "input",
1810
1884
  {
1811
1885
  ref,
@@ -1823,68 +1897,6 @@ var FilePicker = forwardRef(function FilePicker2({ onPick, ...props }, ref) {
1823
1897
  );
1824
1898
  });
1825
1899
 
1826
- // src/Form.tsx
1827
- import { useLayoutEffect as useLayoutEffect3, useRef as useRef4 } from "react";
1828
- import { Fieldset } from "@base-ui/react/fieldset";
1829
- import { Form as BaseForm } from "@base-ui/react/form";
1830
- import { jsx as jsx29 } from "react/jsx-runtime";
1831
- var CONTROL2 = 'input:not([type="hidden"]), textarea, select, button, [role="checkbox"], [role="combobox"], [tabindex]:not([tabindex="-1"])';
1832
- function usable(element) {
1833
- return element instanceof HTMLElement && element.isConnected && element.matches(CONTROL2) && !element.matches(":disabled") && element.getAttribute("aria-disabled") !== "true";
1834
- }
1835
- function Form({ onSubmit, busy: ownBusy = false, className, children, ...props }) {
1836
- const outerBusy = useFormBusy();
1837
- const busy = ownBusy || outerBusy;
1838
- const form = useRef4(null);
1839
- const sender = useRef4(null);
1840
- const holding = useRef4(false);
1841
- const lastInside = useRef4(null);
1842
- useLayoutEffect3(() => {
1843
- const element = form.current;
1844
- if (!element) return;
1845
- if (busy) {
1846
- const active = document.activeElement;
1847
- const from = active && active !== element && element.contains(active) ? active : !active || active === document.body ? lastInside.current : null;
1848
- if (from?.isConnected) {
1849
- sender.current = from;
1850
- holding.current = true;
1851
- element.focus();
1852
- }
1853
- return;
1854
- }
1855
- if (!holding.current) return;
1856
- holding.current = false;
1857
- if (document.activeElement !== element && document.activeElement !== document.body) return;
1858
- const invalid = Array.from(element.querySelectorAll("[data-invalid]")).find(usable);
1859
- const target = invalid ?? (usable(sender.current) ? sender.current : Array.from(element.querySelectorAll(CONTROL2)).find(usable));
1860
- sender.current = null;
1861
- target?.focus();
1862
- }, [busy]);
1863
- return /* @__PURE__ */ jsx29(
1864
- BaseForm,
1865
- {
1866
- ref: form,
1867
- ...props,
1868
- tabIndex: busy ? -1 : props.tabIndex,
1869
- className: cn("outline-none", className),
1870
- onFocus: (event) => {
1871
- if (event.target !== form.current) lastInside.current = event.target;
1872
- props.onFocus?.(event);
1873
- },
1874
- onBlur: (event) => {
1875
- const next = event.relatedTarget;
1876
- if (next && !form.current?.contains(next)) lastInside.current = null;
1877
- props.onBlur?.(event);
1878
- },
1879
- onSubmit: (event) => {
1880
- event.preventDefault();
1881
- void onSubmit();
1882
- },
1883
- children: /* @__PURE__ */ jsx29(Fieldset.Root, { disabled: busy, className: "contents", children: /* @__PURE__ */ jsx29(FormBusyContext.Provider, { value: busy, children }) })
1884
- }
1885
- );
1886
- }
1887
-
1888
1900
  // src/Select.tsx
1889
1901
  import { IconCheck as IconCheck2, IconChevronDown as IconChevronDown2 } from "@tabler/icons-react";
1890
1902
  import { Select as BaseSelect } from "@base-ui/react/select";