react-os-shell 4.21.0 → 4.31.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.
Files changed (54) hide show
  1. package/dist/{Browser-6ACRB3XK.js → Browser-63NYU74S.js} +6 -6
  2. package/dist/{Browser-6ACRB3XK.js.map → Browser-63NYU74S.js.map} +1 -1
  3. package/dist/{Calculator-QCAJ6ITI.js → Calculator-XERXEV35.js} +5 -5
  4. package/dist/{Calculator-QCAJ6ITI.js.map → Calculator-XERXEV35.js.map} +1 -1
  5. package/dist/ConfirmDialog-EEH4CWSW.js +3 -0
  6. package/dist/{ConfirmDialog-WAM5OKAV.js.map → ConfirmDialog-EEH4CWSW.js.map} +1 -1
  7. package/dist/{CurrencyConverter-PCYC6RL5.js → CurrencyConverter-OSZISLHT.js} +5 -5
  8. package/dist/{CurrencyConverter-PCYC6RL5.js.map → CurrencyConverter-OSZISLHT.js.map} +1 -1
  9. package/dist/{Documents-XME7555L.js → Documents-YP4GBBRF.js} +6 -6
  10. package/dist/{Documents-XME7555L.js.map → Documents-YP4GBBRF.js.map} +1 -1
  11. package/dist/{Files-6Q4HZKZD.js → Files-6JWIE3VH.js} +7 -7
  12. package/dist/{Files-6Q4HZKZD.js.map → Files-6JWIE3VH.js.map} +1 -1
  13. package/dist/{Notepad-W6LXMKWD.js → Notepad-EHO56UNK.js} +6 -6
  14. package/dist/{Notepad-W6LXMKWD.js.map → Notepad-EHO56UNK.js.map} +1 -1
  15. package/dist/{PomodoroTimer-YABFEPEV.js → PomodoroTimer-5ZSIG7SQ.js} +5 -5
  16. package/dist/{PomodoroTimer-YABFEPEV.js.map → PomodoroTimer-5ZSIG7SQ.js.map} +1 -1
  17. package/dist/{Preview-TYJQ6ASR.js → Preview-67IPCMSN.js} +6 -6
  18. package/dist/{Preview-TYJQ6ASR.js.map → Preview-67IPCMSN.js.map} +1 -1
  19. package/dist/{Spreadsheet-TAO2PWRL.js → Spreadsheet-HVXBAFR7.js} +6 -6
  20. package/dist/{Spreadsheet-TAO2PWRL.js.map → Spreadsheet-HVXBAFR7.js.map} +1 -1
  21. package/dist/{Stock-IPLLRUOC.js → Stock-4BORXILI.js} +5 -5
  22. package/dist/{Stock-IPLLRUOC.js.map → Stock-4BORXILI.js.map} +1 -1
  23. package/dist/{Weather-SI5QN7X2.js → Weather-ZPPSEUIY.js} +5 -5
  24. package/dist/{Weather-SI5QN7X2.js.map → Weather-ZPPSEUIY.js.map} +1 -1
  25. package/dist/{WorldClock-WYDSMUWX.js → WorldClock-LBKYWDWE.js} +5 -5
  26. package/dist/{WorldClock-WYDSMUWX.js.map → WorldClock-LBKYWDWE.js.map} +1 -1
  27. package/dist/apps/index.js +16 -16
  28. package/dist/{chunk-UPGCISED.js → chunk-7NIFBQUX.js} +348 -76
  29. package/dist/chunk-7NIFBQUX.js.map +1 -0
  30. package/dist/{chunk-VX634UB2.js → chunk-BT3EQNH3.js} +4 -4
  31. package/dist/{chunk-VX634UB2.js.map → chunk-BT3EQNH3.js.map} +1 -1
  32. package/dist/{chunk-IGPSRRL7.js → chunk-GUWAKATU.js} +10 -7
  33. package/dist/chunk-GUWAKATU.js.map +1 -0
  34. package/dist/{chunk-HYAW6KIL.js → chunk-HNH7FOYB.js} +16 -4
  35. package/dist/chunk-HNH7FOYB.js.map +1 -0
  36. package/dist/{chunk-L2O6B5KT.js → chunk-OXLLRKBC.js} +24 -4
  37. package/dist/chunk-OXLLRKBC.js.map +1 -0
  38. package/dist/chunk-SGS3IQEL.js +7 -0
  39. package/dist/{chunk-4OPGKWMT.js.map → chunk-SGS3IQEL.js.map} +1 -1
  40. package/dist/{chunk-Q3AOIJ7B.js → chunk-X6DOWIY2.js} +3 -3
  41. package/dist/{chunk-Q3AOIJ7B.js.map → chunk-X6DOWIY2.js.map} +1 -1
  42. package/dist/{index-Czn1re5M.d.ts → index-CdxMm6Dk.d.ts} +175 -35
  43. package/dist/index.d.ts +53 -29
  44. package/dist/index.js +54 -146
  45. package/dist/index.js.map +1 -1
  46. package/dist/ui/index.d.ts +1 -1
  47. package/dist/ui/index.js +4 -4
  48. package/package.json +1 -1
  49. package/dist/ConfirmDialog-WAM5OKAV.js +0 -3
  50. package/dist/chunk-4OPGKWMT.js +0 -7
  51. package/dist/chunk-HYAW6KIL.js.map +0 -1
  52. package/dist/chunk-IGPSRRL7.js.map +0 -1
  53. package/dist/chunk-L2O6B5KT.js.map +0 -1
  54. package/dist/chunk-UPGCISED.js.map +0 -1
@@ -1,13 +1,14 @@
1
+ import { Breadcrumbs } from './chunk-HNH7FOYB.js';
1
2
  import { SidebarLayout } from './chunk-YG45DAUT.js';
2
3
  import { useIsMobile, useShellPrefs } from './chunk-C22EFSND.js';
3
- import { registerModalEscapeInterceptor, useFocusTrap, useScrollLock } from './chunk-L2O6B5KT.js';
4
+ import { registerModalEscapeInterceptor, useFocusTrap, useScrollLock } from './chunk-OXLLRKBC.js';
4
5
  import { glassStyle } from './chunk-3CUSBZWG.js';
5
- import { forwardRef, useId, useCallback, useState, useRef, useEffect, createContext, useLayoutEffect, useMemo, Children, useContext, Fragment as Fragment$1, useSyncExternalStore } from 'react';
6
+ import { forwardRef, useId, useCallback, useState, useRef, useEffect, createContext, useLayoutEffect, useMemo, Children, isValidElement, cloneElement, useContext, Fragment as Fragment$1, useSyncExternalStore } from 'react';
6
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
8
  import { createPortal } from 'react-dom';
8
9
 
9
- var BASE = "inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 disabled:cursor-not-allowed disabled:opacity-60";
10
- var VARIANTS = {
10
+ var BUTTON_BASE = "inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 disabled:cursor-not-allowed disabled:opacity-60";
11
+ var BUTTON_VARIANTS = {
11
12
  primary: "bg-blue-600 text-white shadow-sm hover:bg-blue-700 focus:ring-blue-400/40",
12
13
  secondary: "border border-gray-300 bg-white text-gray-700 shadow-sm hover:bg-gray-50 focus:ring-blue-400/30",
13
14
  ghost: "text-gray-700 hover:bg-gray-100 focus:ring-blue-400/30",
@@ -62,7 +63,7 @@ var Button = forwardRef(function Button2({
62
63
  disabled: isDisabled,
63
64
  "aria-busy": loading || void 0,
64
65
  "aria-describedby": reasonId,
65
- className: [BASE, VARIANTS[variant], SIZES[size], block ? "w-full" : "", className].filter(Boolean).join(" "),
66
+ className: [BUTTON_BASE, BUTTON_VARIANTS[variant], SIZES[size], block ? "w-full" : "", className].filter(Boolean).join(" "),
66
67
  ...rest,
67
68
  children: [
68
69
  loading ? /* @__PURE__ */ jsx(Spinner, {}) : leftIcon,
@@ -78,6 +79,27 @@ var Button = forwardRef(function Button2({
78
79
  ] });
79
80
  });
80
81
  var Button_default = Button;
82
+ var SQUARE = {
83
+ sm: "h-6 w-6",
84
+ md: "h-8 w-8",
85
+ "touch-sm": "h-11 w-11",
86
+ "touch": "h-14 w-14",
87
+ "touch-lg": "h-16 w-16",
88
+ "touch-xl": "h-20 w-20"
89
+ };
90
+ var IconButton = forwardRef(function IconButton2({ children, variant = "ghost", size = "md", type = "button", className = "", ...rest }, ref) {
91
+ return /* @__PURE__ */ jsx(
92
+ "button",
93
+ {
94
+ ref,
95
+ type,
96
+ className: [BUTTON_BASE, BUTTON_VARIANTS[variant], SQUARE[size], "shrink-0 p-0", className].filter(Boolean).join(" "),
97
+ ...rest,
98
+ children
99
+ }
100
+ );
101
+ });
102
+ var IconButton_default = IconButton;
81
103
 
82
104
  // src/forms/styles.ts
83
105
  var INPUT_SHAPE_PRE = "block w-full rounded-md border border-gray-300 bg-white";
@@ -451,34 +473,33 @@ var Select = forwardRef(function Select2(props, ref) {
451
473
  });
452
474
  var Select_default = Select;
453
475
  var BOX = "h-4 w-4 shrink-0 rounded border-gray-300 accent-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-400/30 disabled:cursor-not-allowed disabled:opacity-60";
454
- var Checkbox = forwardRef(function Checkbox2({ checked, onChange, label, description, disabled, className = "", ...rest }, ref) {
476
+ var Checkbox = forwardRef(function Checkbox2({ checked, onChange, indeterminate = false, label, description, disabled, className = "", ...rest }, ref) {
477
+ const inner = useRef(null);
478
+ useEffect(() => {
479
+ if (inner.current) inner.current.indeterminate = indeterminate;
480
+ }, [indeterminate, checked]);
481
+ const attachRef = useCallback((node) => {
482
+ inner.current = node;
483
+ if (typeof ref === "function") ref(node);
484
+ else if (ref) ref.current = node;
485
+ }, [ref]);
486
+ const input = (extra = "") => /* @__PURE__ */ jsx(
487
+ "input",
488
+ {
489
+ ref: attachRef,
490
+ type: "checkbox",
491
+ checked,
492
+ disabled,
493
+ onChange: (e) => onChange(e.target.checked),
494
+ className: `${BOX} ${extra}`.trim(),
495
+ ...rest
496
+ }
497
+ );
455
498
  if (!label && !description) {
456
- return /* @__PURE__ */ jsx(
457
- "input",
458
- {
459
- ref,
460
- type: "checkbox",
461
- checked,
462
- disabled,
463
- onChange: (e) => onChange(e.target.checked),
464
- className: `${BOX} ${className}`.trim(),
465
- ...rest
466
- }
467
- );
499
+ return input(className);
468
500
  }
469
501
  return /* @__PURE__ */ jsxs("label", { className: `flex items-start gap-2 ${disabled ? "cursor-not-allowed opacity-60" : "cursor-pointer"} ${className}`.trim(), children: [
470
- /* @__PURE__ */ jsx(
471
- "input",
472
- {
473
- ref,
474
- type: "checkbox",
475
- checked,
476
- disabled,
477
- onChange: (e) => onChange(e.target.checked),
478
- className: `${BOX} mt-0.5`,
479
- ...rest
480
- }
481
- ),
502
+ input("mt-0.5"),
482
503
  /* @__PURE__ */ jsxs("span", { className: "text-sm leading-tight", children: [
483
504
  label && /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-700", children: label }),
484
505
  description && /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-gray-500", children: description })
@@ -1372,6 +1393,36 @@ function DateRangePicker({
1372
1393
  )
1373
1394
  ] });
1374
1395
  }
1396
+ var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
1397
+ function toInputValue(value) {
1398
+ if (value === null || value === void 0 || value === "") return "";
1399
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? "" : toISODate(value);
1400
+ if (ISO_DATE.test(value)) return value;
1401
+ const parsed = new Date(value);
1402
+ return Number.isNaN(parsed.getTime()) ? "" : toISODate(parsed);
1403
+ }
1404
+ function fromInputValue(raw) {
1405
+ if (!ISO_DATE.test(raw)) return null;
1406
+ const [year, month, day] = raw.split("-").map(Number);
1407
+ const date = new Date(year, month - 1, day);
1408
+ return date.getMonth() === month - 1 && date.getDate() === day ? date : null;
1409
+ }
1410
+ var DatePicker = forwardRef(function DatePicker2({ value, onChange, min, max, invalid, size, className = "", ...rest }, ref) {
1411
+ return /* @__PURE__ */ jsx(
1412
+ "input",
1413
+ {
1414
+ ref,
1415
+ type: "date",
1416
+ value: toInputValue(value),
1417
+ min: toInputValue(min) || void 0,
1418
+ max: toInputValue(max) || void 0,
1419
+ onChange: (event) => onChange?.(fromInputValue(event.target.value)),
1420
+ className: inputClasses({ invalid, size, className }),
1421
+ ...rest
1422
+ }
1423
+ );
1424
+ });
1425
+ var DatePicker_default = DatePicker;
1375
1426
 
1376
1427
  // src/forms/keypadInput.ts
1377
1428
  var MAX_FRACTION_DIGITS = 2;
@@ -1398,13 +1449,16 @@ function NumericKeypad({
1398
1449
  enterDisabled,
1399
1450
  className = ""
1400
1451
  }) {
1452
+ const press = (next) => {
1453
+ if (next !== value) onChange(next);
1454
+ };
1401
1455
  return /* @__PURE__ */ jsxs("div", { className: `grid grid-cols-3 gap-3 ${className}`.trim(), children: [
1402
1456
  KEYS.map((key) => /* @__PURE__ */ jsx(
1403
1457
  Button_default,
1404
1458
  {
1405
1459
  size: "touch-xl",
1406
1460
  variant: "secondary",
1407
- onClick: () => onChange(appendKey(value, key)),
1461
+ onClick: () => press(appendKey(value, key)),
1408
1462
  children: key
1409
1463
  },
1410
1464
  key
@@ -1415,7 +1469,7 @@ function NumericKeypad({
1415
1469
  size: "touch-xl",
1416
1470
  variant: "secondary",
1417
1471
  "aria-label": "Backspace",
1418
- onClick: () => onChange(backspace(value)),
1472
+ onClick: () => press(backspace(value)),
1419
1473
  children: "\u232B"
1420
1474
  }
1421
1475
  ),
@@ -1556,7 +1610,7 @@ var TRAVEL = {
1556
1610
  md: "translate-x-5",
1557
1611
  touch: "translate-x-6"
1558
1612
  };
1559
- function Switch({
1613
+ var Switch = forwardRef(function Switch2({
1560
1614
  checked,
1561
1615
  onChange,
1562
1616
  disabled = false,
@@ -1564,14 +1618,16 @@ function Switch({
1564
1618
  label,
1565
1619
  hint,
1566
1620
  id,
1567
- className = ""
1568
- }) {
1621
+ className = "",
1622
+ ...rest
1623
+ }, ref) {
1569
1624
  const generated = useId();
1570
1625
  const switchId = id ?? generated;
1571
1626
  const hintId = hint ? `${switchId}-hint` : void 0;
1572
1627
  const control = /* @__PURE__ */ jsx(
1573
1628
  "button",
1574
1629
  {
1630
+ ref,
1575
1631
  id: switchId,
1576
1632
  type: "button",
1577
1633
  role: "switch",
@@ -1579,6 +1635,7 @@ function Switch({
1579
1635
  "aria-describedby": hintId,
1580
1636
  disabled,
1581
1637
  onClick: () => onChange(!checked),
1638
+ ...rest,
1582
1639
  className: [
1583
1640
  "relative inline-flex shrink-0 items-center rounded-full p-0.5 transition-colors",
1584
1641
  "focus:outline-none focus:ring-2 focus:ring-blue-400/40",
@@ -1606,7 +1663,8 @@ function Switch({
1606
1663
  hint && /* @__PURE__ */ jsx("span", { id: hintId, className: "mt-0.5 block text-xs text-gray-500", children: hint })
1607
1664
  ] })
1608
1665
  ] });
1609
- }
1666
+ });
1667
+ var Switch_default = Switch;
1610
1668
  var format = (v, precision) => v == null ? "" : precision != null ? v.toFixed(precision) : String(v);
1611
1669
  function parse(text) {
1612
1670
  const t = text.trim();
@@ -2199,6 +2257,7 @@ function Divider({
2199
2257
  spacing = "md",
2200
2258
  className = ""
2201
2259
  }) {
2260
+ const labelId = useId();
2202
2261
  if (orientation === "vertical") {
2203
2262
  return /* @__PURE__ */ jsx(
2204
2263
  "span",
@@ -2213,11 +2272,31 @@ function Divider({
2213
2272
  }
2214
2273
  const before = align === "left" ? "w-4 shrink-0" : "flex-1";
2215
2274
  const after = align === "right" ? "w-4 shrink-0" : "flex-1";
2216
- return /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-3 ${SPACING[spacing]} ${className}`.trim(), children: [
2217
- /* @__PURE__ */ jsx("span", { className: `${before} border-t border-gray-200` }),
2218
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs font-medium text-gray-500", children }),
2219
- /* @__PURE__ */ jsx("span", { className: `${after} border-t border-gray-200` })
2220
- ] });
2275
+ return (
2276
+ // `role="separator"` because the <hr> above cannot be used once there is a
2277
+ // label to place inside the rule and losing the element must not mean
2278
+ // losing what it meant. The two rules either side are decoration.
2279
+ //
2280
+ // The name is pointed at explicitly. `separator` takes its name from the
2281
+ // AUTHOR only — it is not one of the roles that names itself from its
2282
+ // contents — so the label sitting inside the element did NOT become the
2283
+ // separator's accessible name, and the rule was announced unnamed with the
2284
+ // text loose beside it. `aria-labelledby` is what actually attaches it.
2285
+ /* @__PURE__ */ jsxs(
2286
+ "div",
2287
+ {
2288
+ role: "separator",
2289
+ "aria-orientation": "horizontal",
2290
+ "aria-labelledby": labelId,
2291
+ className: `flex items-center gap-3 ${SPACING[spacing]} ${className}`.trim(),
2292
+ children: [
2293
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: `${before} border-t border-gray-200` }),
2294
+ /* @__PURE__ */ jsx("span", { id: labelId, className: "shrink-0 text-xs font-medium text-gray-500", children }),
2295
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: `${after} border-t border-gray-200` })
2296
+ ]
2297
+ }
2298
+ )
2299
+ );
2221
2300
  }
2222
2301
  var SHAPE = {
2223
2302
  text: "rounded",
@@ -2334,13 +2413,21 @@ function Result({
2334
2413
  className = ""
2335
2414
  }) {
2336
2415
  const tone = STATUS_TONE[status];
2337
- return /* @__PURE__ */ jsxs("div", { className: `flex flex-col items-center justify-center px-6 py-12 text-center ${className}`.trim(), children: [
2338
- /* @__PURE__ */ jsx("div", { className: `flex h-14 w-14 items-center justify-center rounded-full ${TONE_CLASS[tone]}`, children: icon ?? /* @__PURE__ */ jsx(StatusIcon, { tone }) }),
2339
- /* @__PURE__ */ jsx("h2", { className: "mt-4 text-lg font-semibold text-gray-900", children: title ?? DEFAULT_TITLE[status] ?? "" }),
2340
- subTitle && /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-md text-sm text-gray-500", children: subTitle }),
2341
- children && /* @__PURE__ */ jsx("div", { className: "mt-4 w-full max-w-md text-left", children }),
2342
- extra && /* @__PURE__ */ jsx("div", { className: "mt-6 flex flex-wrap items-center justify-center gap-3", children: extra })
2343
- ] });
2416
+ const announces = tone === "error";
2417
+ return /* @__PURE__ */ jsxs(
2418
+ "div",
2419
+ {
2420
+ role: announces ? "alert" : void 0,
2421
+ className: `flex flex-col items-center justify-center px-6 py-12 text-center ${className}`.trim(),
2422
+ children: [
2423
+ /* @__PURE__ */ jsx("div", { className: `flex h-14 w-14 items-center justify-center rounded-full ${TONE_CLASS[tone]}`, children: icon ?? /* @__PURE__ */ jsx(StatusIcon, { tone }) }),
2424
+ /* @__PURE__ */ jsx("h2", { className: "mt-4 text-lg font-semibold text-gray-900", children: title ?? DEFAULT_TITLE[status] ?? "" }),
2425
+ subTitle && /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-md text-sm text-gray-500", children: subTitle }),
2426
+ children && /* @__PURE__ */ jsx("div", { className: "mt-4 w-full max-w-md text-left", children }),
2427
+ extra && /* @__PURE__ */ jsx("div", { className: "mt-6 flex flex-wrap items-center justify-center gap-3", children: extra })
2428
+ ]
2429
+ }
2430
+ );
2344
2431
  }
2345
2432
  var TONE2 = {
2346
2433
  danger: "bg-red-600 text-white",
@@ -2426,23 +2513,49 @@ function initials(name) {
2426
2513
  function Avatar({ src, name, size = "md", status, className = "" }) {
2427
2514
  const px = SIZE_PX[size];
2428
2515
  const dot = Math.max(8, Math.round(px * 0.28));
2429
- return /* @__PURE__ */ jsxs("span", { className: `relative inline-flex shrink-0 ${className}`.trim(), style: { width: px, height: px }, children: [
2430
- src ? /* @__PURE__ */ jsx("img", { src, alt: name ?? "", className: "h-full w-full rounded-full object-cover" }) : /* @__PURE__ */ jsx(
2431
- "span",
2432
- {
2433
- className: "flex h-full w-full items-center justify-center rounded-full bg-gray-100 font-medium text-gray-600",
2434
- style: { fontSize: Math.round(px * 0.4) },
2435
- children: initials(name)
2436
- }
2437
- ),
2438
- status && /* @__PURE__ */ jsx(
2516
+ const [broken, setBroken] = useState(false);
2517
+ useEffect(() => {
2518
+ setBroken(false);
2519
+ }, [src]);
2520
+ const showImage = Boolean(src) && !broken;
2521
+ return (
2522
+ // The role and the name live on the wrapper rather than on the <img>, so
2523
+ // that an avatar showing initials is announced the same way as one showing
2524
+ // a photo. The inner image is then decoration and takes an empty alt.
2525
+ /* @__PURE__ */ jsxs(
2439
2526
  "span",
2440
2527
  {
2441
- className: `absolute bottom-0 right-0 rounded-full ring-2 ring-white ${STATUS_COLOR[status]}`,
2442
- style: { width: dot, height: dot }
2528
+ className: `relative inline-flex shrink-0 ${className}`.trim(),
2529
+ style: { width: px, height: px },
2530
+ ...name ? { role: "img", "aria-label": name } : { "aria-hidden": true },
2531
+ children: [
2532
+ showImage ? /* @__PURE__ */ jsx(
2533
+ "img",
2534
+ {
2535
+ src,
2536
+ alt: "",
2537
+ onError: () => setBroken(true),
2538
+ className: "h-full w-full rounded-full object-cover"
2539
+ }
2540
+ ) : /* @__PURE__ */ jsx(
2541
+ "span",
2542
+ {
2543
+ className: "flex h-full w-full items-center justify-center rounded-full bg-gray-100 font-medium text-gray-600",
2544
+ style: { fontSize: Math.round(px * 0.4) },
2545
+ children: initials(name)
2546
+ }
2547
+ ),
2548
+ status && /* @__PURE__ */ jsx(
2549
+ "span",
2550
+ {
2551
+ className: `absolute bottom-0 right-0 rounded-full ring-2 ring-white ${STATUS_COLOR[status]}`,
2552
+ style: { width: dot, height: dot }
2553
+ }
2554
+ )
2555
+ ]
2443
2556
  }
2444
2557
  )
2445
- ] });
2558
+ );
2446
2559
  }
2447
2560
  function AvatarGroup({ children, max, size = "md", className = "" }) {
2448
2561
  const items = Children.toArray(children);
@@ -2639,17 +2752,29 @@ function Tooltip({ content, side = "top", delay = 200, children }) {
2639
2752
  setShow(false);
2640
2753
  };
2641
2754
  useEffect(() => () => window.clearTimeout(timer.current), []);
2755
+ useEffect(() => {
2756
+ if (!show) return;
2757
+ return registerModalEscapeInterceptor((event) => {
2758
+ if (event.key !== "Escape") return false;
2759
+ close();
2760
+ return true;
2761
+ });
2762
+ }, [show]);
2763
+ const describedBy = show ? id : void 0;
2764
+ const trigger = isValidElement(children) ? cloneElement(children, {
2765
+ "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0
2766
+ }) : children;
2642
2767
  return /* @__PURE__ */ jsxs(
2643
2768
  "span",
2644
2769
  {
2645
2770
  className: "relative inline-flex",
2646
- "aria-describedby": show ? id : void 0,
2771
+ "aria-describedby": isValidElement(children) ? void 0 : describedBy,
2647
2772
  onMouseEnter: open,
2648
2773
  onMouseLeave: close,
2649
2774
  onFocus: open,
2650
2775
  onBlur: close,
2651
2776
  children: [
2652
- children,
2777
+ trigger,
2653
2778
  show && /* @__PURE__ */ jsx(
2654
2779
  "span",
2655
2780
  {
@@ -2717,15 +2842,29 @@ function EmptyState({
2717
2842
  children && /* @__PURE__ */ jsx("div", { className: "mt-4", children })
2718
2843
  ] });
2719
2844
  }
2720
- function PageHeader({ title, description, subtitle, actions, children }) {
2845
+ function PageHeader({
2846
+ title,
2847
+ description,
2848
+ subtitle,
2849
+ actions,
2850
+ children,
2851
+ icon,
2852
+ breadcrumbs
2853
+ }) {
2721
2854
  const desc = description ?? subtitle;
2722
2855
  const right = actions ?? children;
2723
- return /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-4", children: [
2724
- /* @__PURE__ */ jsxs("div", { children: [
2725
- /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-gray-900", children: title }),
2726
- desc && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: desc })
2727
- ] }),
2728
- right && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: right })
2856
+ return /* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
2857
+ breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx(Breadcrumbs, { items: breadcrumbs, className: "mb-3" }),
2858
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
2859
+ /* @__PURE__ */ jsxs("div", { children: [
2860
+ /* @__PURE__ */ jsxs("h1", { className: "flex items-center gap-2 text-xl font-semibold text-gray-900", children: [
2861
+ icon && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-gray-400", children: icon }),
2862
+ title
2863
+ ] }),
2864
+ desc && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: desc })
2865
+ ] }),
2866
+ right && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: right })
2867
+ ] })
2729
2868
  ] });
2730
2869
  }
2731
2870
  function LoadingSpinner({ size = "md", padding = "py-12", label, className = "" }) {
@@ -3624,7 +3763,7 @@ function HelpCenter({
3624
3763
  ] }) : /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center px-6 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: loading ? "Loading\u2026" : "Pick a help article from the left." }) }) })
3625
3764
  ] });
3626
3765
  }
3627
- var VARIANTS2 = {
3766
+ var VARIANTS = {
3628
3767
  primary: "bg-blue-600 text-white hover:bg-blue-700",
3629
3768
  secondary: "bg-white text-gray-700 border border-gray-300 hover:bg-gray-50"
3630
3769
  };
@@ -3645,7 +3784,7 @@ function SidebarActionButton({
3645
3784
  onClick,
3646
3785
  disabled,
3647
3786
  title,
3648
- className: `w-full inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50 ${VARIANTS2[variant]} ${className}`,
3787
+ className: `w-full inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50 ${VARIANTS[variant]} ${className}`,
3649
3788
  children: [
3650
3789
  children,
3651
3790
  hotkey && /* @__PURE__ */ jsx("kbd", { className: "rounded border border-blue-400/50 bg-blue-500/30 px-1.5 py-0.5 text-[10px] font-medium", children: hotkey })
@@ -3734,6 +3873,7 @@ function Drawer({
3734
3873
  className = ""
3735
3874
  }) {
3736
3875
  const panelRef = useRef(null);
3876
+ const bodyId = useId();
3737
3877
  useFocusTrap(panelRef, open, initialFocus);
3738
3878
  useScrollLock(open);
3739
3879
  useEffect(() => {
@@ -3756,6 +3896,7 @@ function Drawer({
3756
3896
  role: "dialog",
3757
3897
  "aria-modal": "true",
3758
3898
  "aria-label": typeof title === "string" ? title : void 0,
3899
+ "aria-describedby": children ? bodyId : void 0,
3759
3900
  style: panelStyle,
3760
3901
  className: [
3761
3902
  "fixed flex flex-col bg-white shadow-xl",
@@ -3777,13 +3918,144 @@ function Drawer({
3777
3918
  }
3778
3919
  )
3779
3920
  ] }),
3780
- /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-4 py-4", children }),
3921
+ /* @__PURE__ */ jsx("div", { id: bodyId, className: "min-h-0 flex-1 overflow-y-auto px-4 py-4", children }),
3781
3922
  footer && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 justify-end gap-3 border-t border-gray-100 px-4 py-3", children: footer })
3782
3923
  ]
3783
3924
  }
3784
3925
  )
3785
3926
  ] });
3786
3927
  }
3928
+ var DEFAULT_MAGNIFIER = "M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z";
3929
+ function GlobalSearch({ providers, typeIcons, placeholder = "Search...", onSelect } = {}) {
3930
+ const [open, setOpen] = useState(false);
3931
+ const [query, setQuery] = useState("");
3932
+ const [results, setResults] = useState([]);
3933
+ const [loading, setLoading] = useState(false);
3934
+ const [activeIdx, setActiveIdx] = useState(0);
3935
+ const inputRef = useRef(null);
3936
+ const debounceRef = useRef();
3937
+ useEffect(() => {
3938
+ const handler = (e) => {
3939
+ if ((e.ctrlKey || e.metaKey) && e.key === "k") {
3940
+ e.preventDefault();
3941
+ setOpen((prev) => !prev);
3942
+ }
3943
+ if (e.key === "Escape" && open) {
3944
+ setOpen(false);
3945
+ }
3946
+ };
3947
+ window.addEventListener("keydown", handler);
3948
+ return () => window.removeEventListener("keydown", handler);
3949
+ }, [open]);
3950
+ useEffect(() => {
3951
+ if (open) {
3952
+ setTimeout(() => inputRef.current?.focus(), 50);
3953
+ } else {
3954
+ setQuery("");
3955
+ setResults([]);
3956
+ }
3957
+ }, [open]);
3958
+ useEffect(() => {
3959
+ if (debounceRef.current) clearTimeout(debounceRef.current);
3960
+ if (query.length < 2) {
3961
+ setResults([]);
3962
+ return;
3963
+ }
3964
+ if (!providers || providers.length === 0) {
3965
+ setResults([]);
3966
+ return;
3967
+ }
3968
+ debounceRef.current = setTimeout(async () => {
3969
+ setLoading(true);
3970
+ try {
3971
+ const responses = await Promise.all(providers.map((p) => p(query).catch(() => [])));
3972
+ setResults(responses.flat());
3973
+ setActiveIdx(0);
3974
+ } catch {
3975
+ setResults([]);
3976
+ }
3977
+ setLoading(false);
3978
+ }, 250);
3979
+ return () => {
3980
+ if (debounceRef.current) clearTimeout(debounceRef.current);
3981
+ };
3982
+ }, [query, providers]);
3983
+ const handleSelect = useCallback((result) => {
3984
+ setOpen(false);
3985
+ onSelect?.(result);
3986
+ }, [onSelect]);
3987
+ const handleKeyDown = (e) => {
3988
+ if (e.key === "ArrowDown") {
3989
+ e.preventDefault();
3990
+ setActiveIdx((i) => Math.min(i + 1, results.length - 1));
3991
+ } else if (e.key === "ArrowUp") {
3992
+ e.preventDefault();
3993
+ setActiveIdx((i) => Math.max(i - 1, 0));
3994
+ } else if (e.key === "Enter" && results[activeIdx]) {
3995
+ handleSelect(results[activeIdx]);
3996
+ }
3997
+ };
3998
+ if (!open) return null;
3999
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[200] flex items-start justify-center pt-[15vh]", onClick: () => setOpen(false), children: [
4000
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm" }),
4001
+ /* @__PURE__ */ jsxs("div", { className: "relative z-10 w-full max-w-xl rounded-2xl overflow-hidden", onClick: (e) => e.stopPropagation(), style: glassStyle(), children: [
4002
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3 border-b border-gray-200", children: [
4003
+ /* @__PURE__ */ jsx("svg", { className: "h-5 w-5 text-gray-400 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: DEFAULT_MAGNIFIER }) }),
4004
+ /* @__PURE__ */ jsx(
4005
+ "input",
4006
+ {
4007
+ ref: inputRef,
4008
+ type: "text",
4009
+ value: query,
4010
+ onChange: (e) => setQuery(e.target.value),
4011
+ onKeyDown: handleKeyDown,
4012
+ placeholder,
4013
+ className: "flex-1 text-sm bg-transparent border-0 outline-none placeholder-gray-400"
4014
+ }
4015
+ ),
4016
+ /* @__PURE__ */ jsx("kbd", { className: "hidden sm:inline-flex items-center rounded border border-gray-300 px-1.5 py-0.5 text-[10px] font-medium text-gray-400", children: "ESC" })
4017
+ ] }),
4018
+ /* @__PURE__ */ jsxs("div", { className: "max-h-[50vh] overflow-y-auto", children: [
4019
+ loading && /* @__PURE__ */ jsx("div", { className: "px-4 py-6 text-center text-sm text-gray-400", children: "Searching..." }),
4020
+ !loading && query.length >= 2 && results.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-4 py-6 text-center text-sm text-gray-400", children: "No results found." }),
4021
+ !loading && results.length > 0 && /* @__PURE__ */ jsx("ul", { className: "py-2", children: results.map((r, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
4022
+ "button",
4023
+ {
4024
+ className: `flex items-center gap-3 w-full px-4 py-2.5 text-left transition-colors ${i === activeIdx ? "bg-blue-50" : "hover:bg-gray-50"}`,
4025
+ onClick: () => handleSelect(r),
4026
+ onMouseEnter: () => setActiveIdx(i),
4027
+ children: [
4028
+ /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 text-gray-400 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: typeIcons?.[r.type] || DEFAULT_MAGNIFIER }) }),
4029
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
4030
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900 truncate", children: r.label }),
4031
+ r.sub && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 truncate", children: r.sub })
4032
+ ] }),
4033
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-medium text-gray-500", children: r.type })
4034
+ ]
4035
+ }
4036
+ ) }, `${r.type}-${r.label}-${i}`)) }),
4037
+ !loading && query.length < 2 && /* @__PURE__ */ jsx("div", { className: "px-4 py-6 text-center text-sm text-gray-400", children: "Type at least 2 characters to search..." })
4038
+ ] }),
4039
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-2 border-t border-gray-100 bg-gray-50 text-[11px] text-gray-400", children: [
4040
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
4041
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
4042
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 px-1 py-0.5 font-medium", children: "\u2191" }),
4043
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 px-1 py-0.5 font-medium", children: "\u2193" }),
4044
+ " navigate"
4045
+ ] }),
4046
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
4047
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 px-1 py-0.5 font-medium", children: "Enter" }),
4048
+ " open"
4049
+ ] })
4050
+ ] }),
4051
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
4052
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 px-1 py-0.5 font-medium", children: "Esc" }),
4053
+ " close"
4054
+ ] })
4055
+ ] })
4056
+ ] })
4057
+ ] });
4058
+ }
3787
4059
  function Kanban({
3788
4060
  items,
3789
4061
  columns,
@@ -5010,6 +5282,6 @@ var ALT_SHIFT_D = isMac ? "\u2325\u21E7D" : "Alt+Shift+D";
5010
5282
  var ALT_SHIFT_E = isMac ? "\u2325\u21E7E" : "Alt+Shift+E";
5011
5283
  var ALT_SHIFT_N = isMac ? "\u2325\u21E7N" : "Alt+Shift+N";
5012
5284
 
5013
- export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, Button_default, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, CMD_SHIFT_Z, CMD_Z, Card, ChatTemplate, Checkbox_default, CheckoutTemplate, ColoredBadge, ContainerFillChart, CountBadge, DashboardTemplate, DataTable, DataTablePage, DateRangePicker, DescriptionList, Divider, DonutChart, Drawer, ENTER, EmailTemplate, EmptyState, ErrorPage, FilePicker, FormField, FormLayoutPage, GalleryTemplate, Grid, HelpCenter, INPUT_BASE, INPUT_SIZES, Inline, InputNumber_default, Input_default, Kanban, Label, ListFooter, ListLoadError, LoadingSpinner, MAX_FRACTION_DIGITS, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NumericKeypad, PageHeader, Pagination, Paragraph, Radio_default, Result, SHIFT, SearchableSelect, Segmented, Select_default, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, Skeleton, Sparkline, Stack, StatCard, Statistic, StatusBadge, StatusBadgeProvider, Switch, Tabs, Text, Textarea_default, TileButton_default, Title, Tooltip, TopNav, appendKey, applyThemePrefs, backspace, firstEnabledIndex, formatDate, inputClasses, isMac, isSeverityTone, lastEnabledIndex, matchTypeahead, mediaFileName, nextEnabledIndex, resolveTheme, severityOf, toISODate, useClickOutside, useTheme };
5014
- //# sourceMappingURL=chunk-UPGCISED.js.map
5015
- //# sourceMappingURL=chunk-UPGCISED.js.map
5285
+ export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, Button_default, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, CMD_SHIFT_Z, CMD_Z, Card, ChatTemplate, Checkbox_default, CheckoutTemplate, ColoredBadge, ContainerFillChart, CountBadge, DashboardTemplate, DataTable, DataTablePage, DatePicker_default, DateRangePicker, DescriptionList, Divider, DonutChart, Drawer, ENTER, EmailTemplate, EmptyState, ErrorPage, FilePicker, FormField, FormLayoutPage, GalleryTemplate, GlobalSearch, Grid, HelpCenter, INPUT_BASE, INPUT_SIZES, IconButton_default, Inline, InputNumber_default, Input_default, Kanban, Label, ListFooter, ListLoadError, LoadingSpinner, MAX_FRACTION_DIGITS, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NumericKeypad, PageHeader, Pagination, Paragraph, Radio_default, Result, SHIFT, SearchableSelect, Segmented, Select_default, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, Skeleton, Sparkline, Stack, StatCard, Statistic, StatusBadge, StatusBadgeProvider, Switch_default, Tabs, Text, Textarea_default, TileButton_default, Title, Tooltip, TopNav, appendKey, applyThemePrefs, backspace, firstEnabledIndex, formatDate, inputClasses, isMac, isSeverityTone, lastEnabledIndex, matchTypeahead, mediaFileName, nextEnabledIndex, resolveTheme, severityOf, toISODate, useClickOutside, useTheme };
5286
+ //# sourceMappingURL=chunk-7NIFBQUX.js.map
5287
+ //# sourceMappingURL=chunk-7NIFBQUX.js.map