react-os-shell 3.26.0 → 3.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/{Browser-OYJVFNED.js → Browser-AR4QP23G.js} +5 -5
  2. package/dist/{Browser-OYJVFNED.js.map → Browser-AR4QP23G.js.map} +1 -1
  3. package/dist/{Calculator-DMOIIGKO.js → Calculator-7MRLC77Q.js} +4 -4
  4. package/dist/{Calculator-DMOIIGKO.js.map → Calculator-7MRLC77Q.js.map} +1 -1
  5. package/dist/ConfirmDialog-2BV43KSW.js +4 -0
  6. package/dist/{ConfirmDialog-3RUHJG5R.js.map → ConfirmDialog-2BV43KSW.js.map} +1 -1
  7. package/dist/{CurrencyConverter-ROSY6A3Q.js → CurrencyConverter-6UTL2ZBY.js} +4 -4
  8. package/dist/{CurrencyConverter-ROSY6A3Q.js.map → CurrencyConverter-6UTL2ZBY.js.map} +1 -1
  9. package/dist/{Documents-VLKG4D22.js → Documents-FQKIQ4GH.js} +5 -5
  10. package/dist/{Documents-VLKG4D22.js.map → Documents-FQKIQ4GH.js.map} +1 -1
  11. package/dist/{Files-WRMJX4QG.js → Files-NTUFLWY3.js} +5 -5
  12. package/dist/{Files-WRMJX4QG.js.map → Files-NTUFLWY3.js.map} +1 -1
  13. package/dist/{Notepad-JVB7OIPO.js → Notepad-MQQSDSLT.js} +5 -5
  14. package/dist/{Notepad-JVB7OIPO.js.map → Notepad-MQQSDSLT.js.map} +1 -1
  15. package/dist/{PomodoroTimer-RVNX4CYS.js → PomodoroTimer-YI4D7OLV.js} +5 -5
  16. package/dist/{PomodoroTimer-RVNX4CYS.js.map → PomodoroTimer-YI4D7OLV.js.map} +1 -1
  17. package/dist/{Preview-DUADZWDA.js → Preview-2GUM6NO7.js} +5 -5
  18. package/dist/{Preview-DUADZWDA.js.map → Preview-2GUM6NO7.js.map} +1 -1
  19. package/dist/{Spreadsheet-EIWL7RIF.js → Spreadsheet-6JWSE5NC.js} +5 -5
  20. package/dist/{Spreadsheet-EIWL7RIF.js.map → Spreadsheet-6JWSE5NC.js.map} +1 -1
  21. package/dist/{Stock-7BIHXBPQ.js → Stock-KQA6QWZ2.js} +4 -4
  22. package/dist/{Stock-7BIHXBPQ.js.map → Stock-KQA6QWZ2.js.map} +1 -1
  23. package/dist/{Weather-7KUHPWTZ.js → Weather-45REOKUY.js} +4 -4
  24. package/dist/{Weather-7KUHPWTZ.js.map → Weather-45REOKUY.js.map} +1 -1
  25. package/dist/{WorldClock-I24ASGUI.js → WorldClock-EJNEFDWZ.js} +4 -4
  26. package/dist/{WorldClock-I24ASGUI.js.map → WorldClock-EJNEFDWZ.js.map} +1 -1
  27. package/dist/apps/index.js +15 -15
  28. package/dist/chunk-CZW7CTI6.js +7 -0
  29. package/dist/{chunk-4BOGLRCL.js.map → chunk-CZW7CTI6.js.map} +1 -1
  30. package/dist/{chunk-NQ2QVUVX.js → chunk-EWRKU7EF.js} +3 -3
  31. package/dist/{chunk-NQ2QVUVX.js.map → chunk-EWRKU7EF.js.map} +1 -1
  32. package/dist/{chunk-FPDVIPLM.js → chunk-GHGD4GKR.js} +4 -4
  33. package/dist/{chunk-FPDVIPLM.js.map → chunk-GHGD4GKR.js.map} +1 -1
  34. package/dist/{chunk-E6ZOCXEA.js → chunk-IU7MSBRI.js} +55 -20
  35. package/dist/chunk-IU7MSBRI.js.map +1 -0
  36. package/dist/{chunk-MU7UZXAS.js → chunk-WWT4XQJL.js} +3 -3
  37. package/dist/{chunk-MU7UZXAS.js.map → chunk-WWT4XQJL.js.map} +1 -1
  38. package/dist/index.d.ts +47 -4
  39. package/dist/index.js +529 -68
  40. package/dist/index.js.map +1 -1
  41. package/package.json +1 -1
  42. package/dist/ConfirmDialog-3RUHJG5R.js +0 -4
  43. package/dist/chunk-4BOGLRCL.js +0 -7
  44. package/dist/chunk-E6ZOCXEA.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { subscribePomo, getPomoSnapshot } from './chunk-MU7UZXAS.js';
2
- export { setShellTodoProvider } from './chunk-MU7UZXAS.js';
1
+ import { subscribePomo, getPomoSnapshot } from './chunk-WWT4XQJL.js';
2
+ export { setShellTodoProvider } from './chunk-WWT4XQJL.js';
3
3
  export { ImageAnnotator_default as ImageAnnotator } from './chunk-QU5L5ZYM.js';
4
4
  import { PREVIEW_OPENED_EVENT, openPreviewFile } from './chunk-DINNWYVB.js';
5
5
  export { Breadcrumbs } from './chunk-DINNWYVB.js';
@@ -15,13 +15,13 @@ export { ShellAuthProvider, setShellAuthBridge, useShellAuth } from './chunk-ADJ
15
15
  import { EditableGrid } from './chunk-GP4Y3VCB.js';
16
16
  export { EditableGrid } from './chunk-GP4Y3VCB.js';
17
17
  import './chunk-4SQU5YV6.js';
18
- import { APP_VERSION, VERSION } from './chunk-4BOGLRCL.js';
19
- export { VERSION } from './chunk-4BOGLRCL.js';
20
- import { useWindowManager, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, WINDOW_REGISTRY, isPageEntry, Modal, useShellPrefs, useIsMobile, ModalActions, useModalActive, client_default, LoadingSpinner, isShellApiClientConfigured, CancelButton, setWindowPosition, ThumbCard, activateModal } from './chunk-E6ZOCXEA.js';
21
- export { CancelButton, ConfirmProvider, CopyButton, DocFavStar, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ShellPrefsProvider, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, commitExposeHighlight, confirm, confirmDestructive, exitExposeMode, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, prompt, registerModalEscapeInterceptor, setExposeHighlight, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useLocalStoragePrefs, useModalActive, useShellPrefs, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-E6ZOCXEA.js';
18
+ import { APP_VERSION, VERSION } from './chunk-CZW7CTI6.js';
19
+ export { VERSION } from './chunk-CZW7CTI6.js';
20
+ import { registerModalEscapeInterceptor, useIsMobile, useWindowManager, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, WINDOW_REGISTRY, isPageEntry, Modal, useShellPrefs, SIDEBAR_STRIP_W, forgetMaximizedWindowBoxes, ModalActions, useModalActive, client_default, LoadingSpinner, isShellApiClientConfigured, CancelButton, setWindowPosition, ThumbCard, activateModal } from './chunk-IU7MSBRI.js';
21
+ export { CancelButton, ConfirmProvider, CopyButton, DocFavStar, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ShellPrefsProvider, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, commitExposeHighlight, confirm, confirmDestructive, exitExposeMode, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, prompt, registerModalEscapeInterceptor, setExposeHighlight, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useLocalStoragePrefs, useModalActive, useShellPrefs, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-IU7MSBRI.js';
22
22
  import { glassStyle, startMenuCategories, navSections, isSection, GLASS_INPUT_BG, navIcons, sectionIcons } from './chunk-GW3RA6IS.js';
23
23
  export { GLASS_DIVIDER, GLASS_INPUT_BG, glassStyle, setShellNavIcons } from './chunk-GW3RA6IS.js';
24
- import { createContext, lazy, forwardRef, useCallback, useState, useRef, useEffect, useMemo, useLayoutEffect, useContext, isValidElement, cloneElement, Suspense, Fragment as Fragment$1, useId, Children, useSyncExternalStore } from 'react';
24
+ import { createContext, lazy, forwardRef, useCallback, useState, useRef, useId, useEffect, useLayoutEffect, useMemo, useContext, isValidElement, cloneElement, Suspense, Fragment as Fragment$1, Children, useSyncExternalStore } from 'react';
25
25
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
26
26
  import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react';
27
27
  import { createPortal } from 'react-dom';
@@ -4370,7 +4370,7 @@ function Layout({
4370
4370
  const { prefs, save: savePrefs } = useShellPrefs();
4371
4371
  const layoutMode = prefs.layout_mode === "sidebar" ? "sidebar" : "classic";
4372
4372
  const sidebarMode = layoutMode === "sidebar";
4373
- const sidebarWidth = sidebarMode ? 280 : 0;
4373
+ const sidebarWidth = sidebarMode ? SIDEBAR_STRIP_W : 0;
4374
4374
  const taskbarPositionRaw = prefs.taskbar_position || "bottom";
4375
4375
  const taskbarPosition = sidebarMode ? "bottom" : taskbarPositionRaw;
4376
4376
  const taskbarSize = prefs.taskbar_size || "medium";
@@ -5081,7 +5081,7 @@ function Customization({ omit, section } = {}) {
5081
5081
  inSection("layout") && /* @__PURE__ */ jsxs(Fragment, { children: [
5082
5082
  /* @__PURE__ */ jsxs("div", { children: [
5083
5083
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-3", children: "Layout Mode" }),
5084
- /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mb-3", children: "Sidebar mode is designed for small screens \u2014 windows always run maximized and the start menu lives in a persistent left sidebar (sections expand inline instead of opening flyouts)." }),
5084
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mb-3", children: "Sidebar mode is designed for small screens \u2014 windows always run maximized and the start menu lives in a persistent left sidebar (sections expand inline instead of opening flyouts). Classic is the windowed mode: choosing it puts every open window back to its normal size." }),
5085
5085
  /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: [
5086
5086
  { key: "classic", label: "Classic" },
5087
5087
  { key: "sidebar", label: "Sidebar" }
@@ -5090,7 +5090,11 @@ function Customization({ omit, section } = {}) {
5090
5090
  {
5091
5091
  onClick: () => {
5092
5092
  savePref("layout_mode", m.key);
5093
- if (m.key === "sidebar") savePref("default_window_size", "maximized");
5093
+ if (m.key === "classic") {
5094
+ if (prefs.default_window_size === "maximized") savePref("default_window_size", "large");
5095
+ forgetMaximizedWindowBoxes();
5096
+ window.dispatchEvent(new CustomEvent("react-os-shell:restore-windowed"));
5097
+ }
5094
5098
  },
5095
5099
  className: `px-3 py-1.5 text-xs font-medium rounded-lg border transition-colors ${(prefs.layout_mode || "classic") === m.key ? "bg-blue-600 text-white border-blue-600" : "bg-white text-gray-700 border-gray-300 hover:bg-gray-50"}`,
5096
5100
  children: m.label
@@ -5978,12 +5982,76 @@ function ListFooter({
5978
5982
  isFetchingMore ? " \xB7 Loading more..." : ""
5979
5983
  ] });
5980
5984
  }
5985
+ 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";
5986
+ var VARIANTS2 = {
5987
+ primary: "bg-blue-600 text-white shadow-sm hover:bg-blue-700 focus:ring-blue-400/40",
5988
+ secondary: "border border-gray-300 bg-white text-gray-700 shadow-sm hover:bg-gray-50 focus:ring-blue-400/30",
5989
+ ghost: "text-gray-700 hover:bg-gray-100 focus:ring-blue-400/30",
5990
+ danger: "bg-red-600 text-white shadow-sm hover:bg-red-700 focus:ring-red-400/40"
5991
+ };
5992
+ var SIZES = {
5993
+ sm: "gap-1 px-2.5 py-1 text-xs",
5994
+ md: "gap-1.5 px-3 py-1.5 text-sm"
5995
+ };
5996
+ function Spinner() {
5997
+ return /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5 animate-spin", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
5998
+ /* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
5999
+ /* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" })
6000
+ ] });
6001
+ }
6002
+ var Button = forwardRef(function Button2({
6003
+ variant = "primary",
6004
+ size = "md",
6005
+ loading = false,
6006
+ leftIcon,
6007
+ rightIcon,
6008
+ block = false,
6009
+ disabled,
6010
+ type = "button",
6011
+ children,
6012
+ className = "",
6013
+ ...rest
6014
+ }, ref) {
6015
+ return /* @__PURE__ */ jsxs(
6016
+ "button",
6017
+ {
6018
+ ref,
6019
+ type,
6020
+ disabled: disabled || loading,
6021
+ "aria-busy": loading || void 0,
6022
+ className: [BASE, VARIANTS2[variant], SIZES[size], block ? "w-full" : "", className].filter(Boolean).join(" "),
6023
+ ...rest,
6024
+ children: [
6025
+ loading ? /* @__PURE__ */ jsx(Spinner, {}) : leftIcon,
6026
+ children,
6027
+ !loading && rightIcon
6028
+ ]
6029
+ }
6030
+ );
6031
+ });
6032
+ var Button_default = Button;
6033
+ function ListLoadError({
6034
+ title = "Couldn't load this list",
6035
+ message = "Something went wrong fetching these records. Check your connection and try again.",
6036
+ onRetry,
6037
+ variant = "dashed"
6038
+ }) {
6039
+ const frame = variant === "card" ? "rounded-md border border-gray-200 bg-white px-6 py-12" : variant === "none" ? "px-6 py-12" : "rounded-lg border-2 border-dashed border-red-200 px-6 py-12";
6040
+ return /* @__PURE__ */ jsxs("div", { className: `flex flex-col items-center justify-center text-center ${frame}`, role: "alert", children: [
6041
+ /* @__PURE__ */ jsx("svg", { className: "h-10 w-10 text-red-300 mb-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1, "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v3.75m0 3.75h.008M10.34 3.94l-8.4 14.55A1.5 1.5 0 003.24 21h17.52a1.5 1.5 0 001.3-2.51l-8.4-14.55a1.5 1.5 0 00-2.6 0z" }) }),
6042
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-gray-900", children: title }),
6043
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-2", children: message }),
6044
+ onRetry && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(Button_default, { variant: "secondary", size: "sm", onClick: onRetry, children: "Try again" }) })
6045
+ ] });
6046
+ }
5981
6047
  function EntityList(props) {
5982
6048
  const {
5983
6049
  items,
5984
6050
  isLoading,
5985
6051
  emptyState,
5986
6052
  totalCount,
6053
+ isError,
6054
+ onRetry,
5987
6055
  tableId,
5988
6056
  columns,
5989
6057
  renderCell,
@@ -6069,6 +6137,7 @@ function EntityList(props) {
6069
6137
  }
6070
6138
  };
6071
6139
  if (isLoading) return /* @__PURE__ */ jsx(LoadingSpinner, {});
6140
+ if (isError && items.length === 0) return /* @__PURE__ */ jsx(ListLoadError, { onRetry });
6072
6141
  if (items.length === 0) return /* @__PURE__ */ jsx(Fragment, { children: emptyState });
6073
6142
  const allSelected = items.length > 0 && selected.size === items.length;
6074
6143
  const fullColumns = [
@@ -6426,6 +6495,10 @@ function useInfiniteScroll({
6426
6495
  items: allItems,
6427
6496
  totalCount,
6428
6497
  isLoading: query.isLoading,
6498
+ // Surface the query's error state so a failed fetch renders as an error
6499
+ // (with retry) rather than a misleading empty list. `refetch` is the retry.
6500
+ isError: query.isError,
6501
+ error: query.error,
6429
6502
  isFetchingNextPage,
6430
6503
  hasNextPage: !!hasNextPage,
6431
6504
  sentinelRef,
@@ -6542,54 +6615,6 @@ function Pagination({
6542
6615
  showEdges && /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Last page", disabled: page === pageCount, onClick: () => go(pageCount), className: `${BTN} text-gray-600 hover:bg-gray-100`, children: "\xBB" })
6543
6616
  ] });
6544
6617
  }
6545
- 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";
6546
- var VARIANTS2 = {
6547
- primary: "bg-blue-600 text-white shadow-sm hover:bg-blue-700 focus:ring-blue-400/40",
6548
- secondary: "border border-gray-300 bg-white text-gray-700 shadow-sm hover:bg-gray-50 focus:ring-blue-400/30",
6549
- ghost: "text-gray-700 hover:bg-gray-100 focus:ring-blue-400/30",
6550
- danger: "bg-red-600 text-white shadow-sm hover:bg-red-700 focus:ring-red-400/40"
6551
- };
6552
- var SIZES = {
6553
- sm: "gap-1 px-2.5 py-1 text-xs",
6554
- md: "gap-1.5 px-3 py-1.5 text-sm"
6555
- };
6556
- function Spinner() {
6557
- return /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5 animate-spin", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
6558
- /* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
6559
- /* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" })
6560
- ] });
6561
- }
6562
- var Button = forwardRef(function Button2({
6563
- variant = "primary",
6564
- size = "md",
6565
- loading = false,
6566
- leftIcon,
6567
- rightIcon,
6568
- block = false,
6569
- disabled,
6570
- type = "button",
6571
- children,
6572
- className = "",
6573
- ...rest
6574
- }, ref) {
6575
- return /* @__PURE__ */ jsxs(
6576
- "button",
6577
- {
6578
- ref,
6579
- type,
6580
- disabled: disabled || loading,
6581
- "aria-busy": loading || void 0,
6582
- className: [BASE, VARIANTS2[variant], SIZES[size], block ? "w-full" : "", className].filter(Boolean).join(" "),
6583
- ...rest,
6584
- children: [
6585
- loading ? /* @__PURE__ */ jsx(Spinner, {}) : leftIcon,
6586
- children,
6587
- !loading && rightIcon
6588
- ]
6589
- }
6590
- );
6591
- });
6592
- var Button_default = Button;
6593
6618
  var Input = forwardRef(function Input2({ invalid, leftIcon, rightAdornment, className = "", ...rest }, ref) {
6594
6619
  const pad = `${leftIcon ? "pl-9" : ""} ${rightAdornment ? "pr-9" : ""}`.trim();
6595
6620
  const field = /* @__PURE__ */ jsx(
@@ -6629,7 +6654,44 @@ var Textarea = forwardRef(function Textarea2({ invalid, autoGrow, className = ""
6629
6654
  );
6630
6655
  });
6631
6656
  var Textarea_default = Textarea;
6632
- var Select = forwardRef(function Select2({ value, onChange, options, placeholder, invalid, className = "", ...rest }, ref) {
6657
+
6658
+ // src/forms/selectNav.ts
6659
+ function firstEnabledIndex(options) {
6660
+ for (let i = 0; i < options.length; i++) {
6661
+ if (!options[i].disabled) return i;
6662
+ }
6663
+ return -1;
6664
+ }
6665
+ function lastEnabledIndex(options) {
6666
+ for (let i = options.length - 1; i >= 0; i--) {
6667
+ if (!options[i].disabled) return i;
6668
+ }
6669
+ return -1;
6670
+ }
6671
+ function nextEnabledIndex(options, from, dir) {
6672
+ let i = from + dir;
6673
+ while (i >= 0 && i < options.length) {
6674
+ if (!options[i].disabled) return i;
6675
+ i += dir;
6676
+ }
6677
+ if (from >= 0 && from < options.length && !options[from].disabled) return from;
6678
+ return dir > 0 ? firstEnabledIndex(options) : lastEnabledIndex(options);
6679
+ }
6680
+ function matchTypeahead(options, buffer, fromIndex) {
6681
+ const q = buffer.toLowerCase();
6682
+ const n = options.length;
6683
+ if (!q || n === 0) return -1;
6684
+ for (let step = 1; step <= n; step++) {
6685
+ const i = ((fromIndex + step) % n + n) % n;
6686
+ const o = options[i];
6687
+ if (!o.disabled && o.label.toLowerCase().startsWith(q)) return i;
6688
+ }
6689
+ return -1;
6690
+ }
6691
+ var MENU_GAP2 = 4;
6692
+ var VIEWPORT_MARGIN2 = 8;
6693
+ var MENU_MAX_HEIGHT2 = 240;
6694
+ var NativeSelect = forwardRef(function NativeSelect2({ value, onChange, options, placeholder, invalid, className = "", ...rest }, ref) {
6633
6695
  return /* @__PURE__ */ jsxs(
6634
6696
  "select",
6635
6697
  {
@@ -6645,6 +6707,260 @@ var Select = forwardRef(function Select2({ value, onChange, options, placeholder
6645
6707
  }
6646
6708
  );
6647
6709
  });
6710
+ function useAnchoredPosition(triggerRef, open) {
6711
+ const [pos, setPos] = useState(null);
6712
+ useLayoutEffect(() => {
6713
+ if (!open) {
6714
+ setPos(null);
6715
+ return;
6716
+ }
6717
+ let lastLeft = NaN, lastTop = NaN, lastBottom = NaN;
6718
+ const compute = () => {
6719
+ const rect = triggerRef.current?.getBoundingClientRect();
6720
+ if (!rect) return;
6721
+ lastLeft = rect.left;
6722
+ lastTop = rect.top;
6723
+ lastBottom = rect.bottom;
6724
+ const spaceBelow = window.innerHeight - rect.bottom - MENU_GAP2 - VIEWPORT_MARGIN2;
6725
+ const spaceAbove = rect.top - MENU_GAP2 - VIEWPORT_MARGIN2;
6726
+ const placeAbove = spaceBelow < Math.min(MENU_MAX_HEIGHT2, 160) && spaceAbove > spaceBelow;
6727
+ const maxHeight = Math.max(96, Math.min(MENU_MAX_HEIGHT2, placeAbove ? spaceAbove : spaceBelow));
6728
+ const left = Math.max(VIEWPORT_MARGIN2, Math.min(rect.left, window.innerWidth - rect.width - VIEWPORT_MARGIN2));
6729
+ const next = { left, minWidth: rect.width, maxHeight };
6730
+ if (placeAbove) next.bottom = window.innerHeight - rect.top + MENU_GAP2;
6731
+ else next.top = rect.bottom + MENU_GAP2;
6732
+ setPos(next);
6733
+ };
6734
+ compute();
6735
+ let raf = requestAnimationFrame(function tick() {
6736
+ const rect = triggerRef.current?.getBoundingClientRect();
6737
+ if (rect && (rect.left !== lastLeft || rect.top !== lastTop || rect.bottom !== lastBottom)) compute();
6738
+ raf = requestAnimationFrame(tick);
6739
+ });
6740
+ window.addEventListener("scroll", compute, true);
6741
+ window.addEventListener("resize", compute);
6742
+ return () => {
6743
+ cancelAnimationFrame(raf);
6744
+ window.removeEventListener("scroll", compute, true);
6745
+ window.removeEventListener("resize", compute);
6746
+ };
6747
+ }, [open, triggerRef]);
6748
+ return pos;
6749
+ }
6750
+ var ListboxSelect = forwardRef(function ListboxSelect2({ value, onChange, options, placeholder, invalid, className = "", id, disabled, ...rest }, ref) {
6751
+ const [open, setOpen] = useState(false);
6752
+ const [active, setActive] = useState(-1);
6753
+ const wrapRef = useRef(null);
6754
+ const triggerRef = useRef(null);
6755
+ const listRef = useRef(null);
6756
+ const openRef = useRef(open);
6757
+ openRef.current = open;
6758
+ const typeahead = useRef({ buffer: "", timer: null });
6759
+ const baseId = useId();
6760
+ const listboxId = `${baseId}-listbox`;
6761
+ const optionId = (i) => `${baseId}-opt-${i}`;
6762
+ const menuPos = useAnchoredPosition(triggerRef, open);
6763
+ const selectedIndex = options.findIndex((o) => o.value === value);
6764
+ const selectedLabel = selectedIndex >= 0 ? options[selectedIndex].label : void 0;
6765
+ const close = useCallback((refocus = true) => {
6766
+ setOpen(false);
6767
+ setActive(-1);
6768
+ if (refocus) triggerRef.current?.focus();
6769
+ }, []);
6770
+ const openList = useCallback(() => {
6771
+ if (disabled) return;
6772
+ setActive(selectedIndex >= 0 ? selectedIndex : firstEnabledIndex(options));
6773
+ setOpen(true);
6774
+ }, [disabled, options, selectedIndex]);
6775
+ const commit = useCallback((i) => {
6776
+ const o = options[i];
6777
+ if (!o || o.disabled) return;
6778
+ onChange(o.value);
6779
+ close();
6780
+ }, [options, onChange, close]);
6781
+ useEffect(() => {
6782
+ if (!open) return;
6783
+ const unregister = registerModalEscapeInterceptor((e) => {
6784
+ if (e.key !== "Escape" || !openRef.current) return false;
6785
+ close();
6786
+ return true;
6787
+ });
6788
+ return unregister;
6789
+ }, [open, close]);
6790
+ useEffect(() => {
6791
+ if (!open) return;
6792
+ const handler = (e) => {
6793
+ const t = e.target;
6794
+ if (wrapRef.current?.contains(t) || listRef.current?.contains(t)) return;
6795
+ close(false);
6796
+ };
6797
+ document.addEventListener("pointerdown", handler);
6798
+ return () => document.removeEventListener("pointerdown", handler);
6799
+ }, [open, close]);
6800
+ useEffect(() => {
6801
+ if (!open || active < 0) return;
6802
+ listRef.current?.querySelector(`#${CSS.escape(optionId(active))}`)?.scrollIntoView({ block: "nearest" });
6803
+ });
6804
+ const runTypeahead = useCallback((char) => {
6805
+ const ta = typeahead.current;
6806
+ if (ta.timer) clearTimeout(ta.timer);
6807
+ ta.buffer += char;
6808
+ ta.timer = setTimeout(() => {
6809
+ ta.buffer = "";
6810
+ ta.timer = null;
6811
+ }, 500);
6812
+ const from = open ? active : selectedIndex;
6813
+ const hit = matchTypeahead(options, ta.buffer, from);
6814
+ if (hit < 0) return;
6815
+ if (open) setActive(hit);
6816
+ else commit(hit);
6817
+ }, [open, active, selectedIndex, options, commit]);
6818
+ const onKeyDown = (e) => {
6819
+ if (disabled) return;
6820
+ switch (e.key) {
6821
+ case "ArrowDown":
6822
+ e.preventDefault();
6823
+ if (!open) openList();
6824
+ else setActive((a) => nextEnabledIndex(options, a, 1));
6825
+ return;
6826
+ case "ArrowUp":
6827
+ e.preventDefault();
6828
+ if (!open) openList();
6829
+ else setActive((a) => nextEnabledIndex(options, a, -1));
6830
+ return;
6831
+ case "Home":
6832
+ if (open) {
6833
+ e.preventDefault();
6834
+ setActive(firstEnabledIndex(options));
6835
+ }
6836
+ return;
6837
+ case "End":
6838
+ if (open) {
6839
+ e.preventDefault();
6840
+ setActive(lastEnabledIndex(options));
6841
+ }
6842
+ return;
6843
+ case "Enter":
6844
+ if (open) {
6845
+ e.preventDefault();
6846
+ commit(active);
6847
+ }
6848
+ return;
6849
+ case " ":
6850
+ e.preventDefault();
6851
+ if (!open) openList();
6852
+ else commit(active);
6853
+ return;
6854
+ case "Escape":
6855
+ if (open) {
6856
+ e.preventDefault();
6857
+ e.stopPropagation();
6858
+ close();
6859
+ }
6860
+ return;
6861
+ case "Tab":
6862
+ if (open) close(false);
6863
+ return;
6864
+ default:
6865
+ if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
6866
+ e.preventDefault();
6867
+ runTypeahead(e.key);
6868
+ }
6869
+ }
6870
+ };
6871
+ return /* @__PURE__ */ jsxs("div", { ref: wrapRef, className: "relative", children: [
6872
+ /* @__PURE__ */ jsx(
6873
+ "button",
6874
+ {
6875
+ ref: triggerRef,
6876
+ id,
6877
+ type: "button",
6878
+ role: "combobox",
6879
+ "aria-haspopup": "listbox",
6880
+ "aria-expanded": open,
6881
+ "aria-controls": open ? listboxId : void 0,
6882
+ "aria-activedescendant": open && active >= 0 ? optionId(active) : void 0,
6883
+ "aria-invalid": invalid || void 0,
6884
+ disabled,
6885
+ onClick: () => open ? close() : openList(),
6886
+ onKeyDown,
6887
+ className: inputClasses({ invalid, className: `pr-8 text-left ${className}`.trim() }),
6888
+ children: /* @__PURE__ */ jsx("span", { className: `block truncate ${selectedLabel === void 0 ? "text-gray-400" : ""}`, children: selectedLabel ?? placeholder ?? "\xA0" })
6889
+ }
6890
+ ),
6891
+ /* @__PURE__ */ jsx(
6892
+ "svg",
6893
+ {
6894
+ "aria-hidden": "true",
6895
+ className: "pointer-events-none absolute right-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400",
6896
+ fill: "none",
6897
+ viewBox: "0 0 24 24",
6898
+ stroke: "currentColor",
6899
+ strokeWidth: 2,
6900
+ children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 9l3.75-3.75L15.75 9M8.25 15l3.75 3.75L15.75 15" })
6901
+ }
6902
+ ),
6903
+ /* @__PURE__ */ jsxs(
6904
+ "select",
6905
+ {
6906
+ ref,
6907
+ value,
6908
+ onChange: (e) => onChange(e.target.value),
6909
+ disabled,
6910
+ "aria-hidden": "true",
6911
+ tabIndex: -1,
6912
+ className: "sr-only",
6913
+ ...rest,
6914
+ children: [
6915
+ placeholder !== void 0 && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
6916
+ options.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, disabled: o.disabled, children: o.label }, o.value))
6917
+ ]
6918
+ }
6919
+ ),
6920
+ open && createPortal(
6921
+ /* @__PURE__ */ jsx(
6922
+ "div",
6923
+ {
6924
+ ref: listRef,
6925
+ id: listboxId,
6926
+ role: "listbox",
6927
+ className: "fixed z-[400] overflow-y-auto rounded-2xl py-1",
6928
+ style: {
6929
+ left: menuPos?.left,
6930
+ top: menuPos?.top,
6931
+ bottom: menuPos?.bottom,
6932
+ minWidth: menuPos?.minWidth,
6933
+ maxHeight: menuPos?.maxHeight ?? MENU_MAX_HEIGHT2,
6934
+ visibility: menuPos ? void 0 : "hidden",
6935
+ ...glassStyle()
6936
+ },
6937
+ children: options.map((o, i) => /* @__PURE__ */ jsx(
6938
+ "div",
6939
+ {
6940
+ id: optionId(i),
6941
+ role: "option",
6942
+ "aria-selected": value === o.value,
6943
+ "aria-disabled": o.disabled || void 0,
6944
+ onPointerEnter: () => {
6945
+ if (!o.disabled) setActive(i);
6946
+ },
6947
+ onClick: () => commit(i),
6948
+ className: `mx-1 cursor-pointer truncate rounded-lg px-3 py-1.5 text-sm
6949
+ ${o.disabled ? "cursor-not-allowed text-gray-400" : i === active ? "bg-blue-50 text-blue-700" : value === o.value ? "font-medium text-blue-600" : "text-gray-700"}`,
6950
+ children: o.label
6951
+ },
6952
+ o.value
6953
+ ))
6954
+ }
6955
+ ),
6956
+ document.body
6957
+ )
6958
+ ] });
6959
+ });
6960
+ var Select = forwardRef(function Select2(props, ref) {
6961
+ const isMobile = useIsMobile();
6962
+ return isMobile ? /* @__PURE__ */ jsx(NativeSelect, { ref, ...props }) : /* @__PURE__ */ jsx(ListboxSelect, { ref, ...props });
6963
+ });
6648
6964
  var Select_default = Select;
6649
6965
  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";
6650
6966
  var Checkbox = forwardRef(function Checkbox2({ checked, onChange, label, description, disabled, className = "", ...rest }, ref) {
@@ -7489,16 +7805,12 @@ function FilterBar({ filters, values, onChange, onClear, children }) {
7489
7805
  onChange: (v) => onChange(f.field, v)
7490
7806
  },
7491
7807
  f.field
7492
- ) : /* @__PURE__ */ jsxs(
7493
- "select",
7808
+ ) : /* @__PURE__ */ jsx(
7809
+ PlainFilter,
7494
7810
  {
7811
+ filter: f,
7495
7812
  value: values[f.field] || "",
7496
- onChange: (e) => onChange(f.field, e.target.value),
7497
- className: `border rounded-lg px-2.5 py-1.5 text-sm ${values[f.field] ? "border-blue-400 bg-blue-50 text-blue-700" : "border-gray-300 text-gray-600"}`,
7498
- children: [
7499
- /* @__PURE__ */ jsx("option", { value: "", children: f.label }),
7500
- f.options.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
7501
- ]
7813
+ onChange: (v) => onChange(f.field, v)
7502
7814
  },
7503
7815
  f.field
7504
7816
  );
@@ -7510,6 +7822,155 @@ function FilterBar({ filters, values, onChange, onClear, children }) {
7510
7822
  ] })
7511
7823
  ] });
7512
7824
  }
7825
+ function PlainFilter({ filter, value, onChange }) {
7826
+ const [open, setOpen] = useState(false);
7827
+ const [active, setActive] = useState(-1);
7828
+ const ref = useRef(null);
7829
+ const triggerRef = useRef(null);
7830
+ const listRef = useRef(null);
7831
+ const openRef = useRef(open);
7832
+ openRef.current = open;
7833
+ const typeahead = useRef({ buffer: "", timer: null });
7834
+ const baseId = useId();
7835
+ const listboxId = `${baseId}-listbox`;
7836
+ const optionId = (i) => `${baseId}-opt-${i}`;
7837
+ const items = [{ value: "", label: filter.label }, ...filter.options];
7838
+ const selectedIndex = value ? items.findIndex((o) => o.value === value) : 0;
7839
+ const selectedLabel = value ? filter.options.find((o) => o.value === value)?.label : null;
7840
+ const close = useCallback((refocus = true) => {
7841
+ setOpen(false);
7842
+ setActive(-1);
7843
+ if (refocus) triggerRef.current?.focus();
7844
+ }, []);
7845
+ const openList = useCallback(() => {
7846
+ setActive(selectedIndex >= 0 ? selectedIndex : firstEnabledIndex(items));
7847
+ setOpen(true);
7848
+ }, [selectedIndex, items]);
7849
+ const commit = useCallback((i) => {
7850
+ const o = items[i];
7851
+ if (!o) return;
7852
+ onChange(o.value);
7853
+ close();
7854
+ }, [items, onChange, close]);
7855
+ useClickOutside(ref, useCallback(() => {
7856
+ if (open) close(false);
7857
+ }, [open, close]));
7858
+ useEffect(() => {
7859
+ if (!open) return;
7860
+ return registerModalEscapeInterceptor((e) => {
7861
+ if (e.key !== "Escape" || !openRef.current) return false;
7862
+ close();
7863
+ return true;
7864
+ });
7865
+ }, [open, close]);
7866
+ useEffect(() => {
7867
+ if (!open || active < 0) return;
7868
+ listRef.current?.querySelector(`#${CSS.escape(optionId(active))}`)?.scrollIntoView({ block: "nearest" });
7869
+ });
7870
+ const runTypeahead = (char) => {
7871
+ const ta = typeahead.current;
7872
+ if (ta.timer) clearTimeout(ta.timer);
7873
+ ta.buffer += char;
7874
+ ta.timer = setTimeout(() => {
7875
+ ta.buffer = "";
7876
+ ta.timer = null;
7877
+ }, 500);
7878
+ const hit = matchTypeahead(items, ta.buffer, open ? active : selectedIndex);
7879
+ if (hit < 0) return;
7880
+ if (open) setActive(hit);
7881
+ else commit(hit);
7882
+ };
7883
+ const onKeyDown = (e) => {
7884
+ switch (e.key) {
7885
+ case "ArrowDown":
7886
+ e.preventDefault();
7887
+ if (!open) openList();
7888
+ else setActive((a) => nextEnabledIndex(items, a, 1));
7889
+ return;
7890
+ case "ArrowUp":
7891
+ e.preventDefault();
7892
+ if (!open) openList();
7893
+ else setActive((a) => nextEnabledIndex(items, a, -1));
7894
+ return;
7895
+ case "Home":
7896
+ if (open) {
7897
+ e.preventDefault();
7898
+ setActive(firstEnabledIndex(items));
7899
+ }
7900
+ return;
7901
+ case "End":
7902
+ if (open) {
7903
+ e.preventDefault();
7904
+ setActive(lastEnabledIndex(items));
7905
+ }
7906
+ return;
7907
+ case "Enter":
7908
+ if (open) {
7909
+ e.preventDefault();
7910
+ commit(active);
7911
+ }
7912
+ return;
7913
+ case " ":
7914
+ e.preventDefault();
7915
+ if (!open) openList();
7916
+ else commit(active);
7917
+ return;
7918
+ case "Escape":
7919
+ if (open) {
7920
+ e.preventDefault();
7921
+ e.stopPropagation();
7922
+ close();
7923
+ }
7924
+ return;
7925
+ case "Tab":
7926
+ if (open) close(false);
7927
+ return;
7928
+ default:
7929
+ if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
7930
+ e.preventDefault();
7931
+ runTypeahead(e.key);
7932
+ }
7933
+ }
7934
+ };
7935
+ return /* @__PURE__ */ jsxs("div", { ref, className: "relative", children: [
7936
+ /* @__PURE__ */ jsxs(
7937
+ "button",
7938
+ {
7939
+ ref: triggerRef,
7940
+ type: "button",
7941
+ role: "combobox",
7942
+ "aria-haspopup": "listbox",
7943
+ "aria-expanded": open,
7944
+ "aria-controls": open ? listboxId : void 0,
7945
+ "aria-activedescendant": open && active >= 0 ? optionId(active) : void 0,
7946
+ onClick: () => open ? close() : openList(),
7947
+ onKeyDown,
7948
+ className: `flex items-center gap-1.5 border rounded-lg px-2.5 py-1.5 text-sm transition-colors ${value ? "border-blue-400 bg-blue-50 text-blue-700" : "border-gray-300 text-gray-600 hover:border-gray-400"}`,
7949
+ children: [
7950
+ /* @__PURE__ */ jsx("span", { className: "truncate max-w-[140px]", children: selectedLabel || filter.label }),
7951
+ value ? /* @__PURE__ */ jsx("button", { type: "button", onClick: (e) => {
7952
+ e.stopPropagation();
7953
+ onChange("");
7954
+ }, className: "text-blue-400 hover:text-blue-600", children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-3.5 w-3.5" }) }) : /* @__PURE__ */ jsx("svg", { className: `h-3.5 w-3.5 text-gray-400 transition-transform ${open ? "rotate-180" : ""}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19.5 8.25l-7.5 7.5-7.5-7.5" }) })
7955
+ ]
7956
+ }
7957
+ ),
7958
+ open && /* @__PURE__ */ jsx("div", { ref: listRef, id: listboxId, role: "listbox", className: "absolute top-full left-0 mt-1 w-56 rounded-2xl z-[400] max-h-52 overflow-y-auto py-1", style: glassStyle(), children: items.map((o, i) => /* @__PURE__ */ jsx(
7959
+ "div",
7960
+ {
7961
+ id: optionId(i),
7962
+ role: "option",
7963
+ "aria-selected": i === selectedIndex,
7964
+ onPointerEnter: () => setActive(i),
7965
+ onClick: () => commit(i),
7966
+ className: `mx-1 cursor-pointer truncate rounded-lg px-3 py-1.5 text-sm
7967
+ ${i === active ? "bg-blue-50 text-blue-700" : i === selectedIndex ? "font-medium text-blue-600" : "text-gray-700"}`,
7968
+ children: o.label
7969
+ },
7970
+ o.value || "__all__"
7971
+ )) })
7972
+ ] });
7973
+ }
7513
7974
  function SearchableFilter({ filter, value, onChange }) {
7514
7975
  const [open, setOpen] = useState(false);
7515
7976
  const [search, setSearch] = useState("");
@@ -9879,6 +10340,6 @@ function useEditHotkey(callback) {
9879
10340
  }, [callback, isActive]);
9880
10341
  }
9881
10342
 
9882
- export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, BehaviorPanel, BulkImportGrid, Button_default as Button, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, Card, ChangePasswordForm, ChatTemplate, Checkbox_default as Checkbox, CheckoutTemplate, ColoredBadge, ContainerFillChart, Customization, DEV_BANNER_TEXT, DashboardTemplate, DataTablePage, Desktop, DesktopHostProvider, DevIndicator, DonutChart, ENTER, EmailTemplate, EmptyState, EntityList, ErrorPage, FilterBar, FormField, FormLayoutPage, GalleryTemplate, GlobalSearch, HelpCenter, INPUT_BASE, Input_default as Input, Kanban, Label, Layout, ListFooter, LoadingSpinner2 as LoadingSpinner, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NotificationBell, PageHeader, Pagination, PdfActionButton, Radio_default as Radio, ResizableTable, SHIFT, SearchableSelect, Select_default as Select, ServerStatusIndicator, ShellEntityFetcherProvider, ShortcutHelp, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, SoundsPanel, Sparkline, StartMenu, StatCard, StatusBadge, StatusBadgeProvider, SystemPreferences, Tabs, Textarea_default as Textarea, Tooltip, TopNav, WidgetManager, applyDevTitle, createWindowRegistry, findDuplicateKeys, formatDate, inputClasses, isDevEnv, isMac, isSeverityTone, mediaFileName, mergeBulkItems, severityOf, useClickOutside, useColumnConfig, useDesktopHost, useEditHotkey, useFilters, useInfiniteScroll, useNewHotkey, useShellEntityFetcher, useSort, useTableNav };
10343
+ export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, BehaviorPanel, BulkImportGrid, Button_default as Button, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, Card, ChangePasswordForm, ChatTemplate, Checkbox_default as Checkbox, CheckoutTemplate, ColoredBadge, ContainerFillChart, Customization, DEV_BANNER_TEXT, DashboardTemplate, DataTablePage, Desktop, DesktopHostProvider, DevIndicator, DonutChart, ENTER, EmailTemplate, EmptyState, EntityList, ErrorPage, FilterBar, FormField, FormLayoutPage, GalleryTemplate, GlobalSearch, HelpCenter, INPUT_BASE, Input_default as Input, Kanban, Label, Layout, ListFooter, ListLoadError, LoadingSpinner2 as LoadingSpinner, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NotificationBell, PageHeader, Pagination, PdfActionButton, Radio_default as Radio, ResizableTable, SHIFT, SearchableSelect, Select_default as Select, ServerStatusIndicator, ShellEntityFetcherProvider, ShortcutHelp, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, SoundsPanel, Sparkline, StartMenu, StatCard, StatusBadge, StatusBadgeProvider, SystemPreferences, Tabs, Textarea_default as Textarea, Tooltip, TopNav, WidgetManager, applyDevTitle, createWindowRegistry, findDuplicateKeys, formatDate, inputClasses, isDevEnv, isMac, isSeverityTone, mediaFileName, mergeBulkItems, severityOf, useClickOutside, useColumnConfig, useDesktopHost, useEditHotkey, useFilters, useInfiniteScroll, useNewHotkey, useShellEntityFetcher, useSort, useTableNav };
9883
10344
  //# sourceMappingURL=index.js.map
9884
10345
  //# sourceMappingURL=index.js.map