efficient-shell 0.0.0-stage → 4.126.2

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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +753 -2
  3. package/dist/Browser-DBRSIQ4J.js +478 -0
  4. package/dist/Browser-DBRSIQ4J.js.map +1 -0
  5. package/dist/Calculator-HCKFTSJR.js +188 -0
  6. package/dist/Calculator-HCKFTSJR.js.map +1 -0
  7. package/dist/ConfirmDialog-LJXMB45A.js +3 -0
  8. package/dist/ConfirmDialog-LJXMB45A.js.map +1 -0
  9. package/dist/CurrencyConverter-BKKORN6K.js +232 -0
  10. package/dist/CurrencyConverter-BKKORN6K.js.map +1 -0
  11. package/dist/Documents-5MD5SKPB.js +437 -0
  12. package/dist/Documents-5MD5SKPB.js.map +1 -0
  13. package/dist/Files-KY35DPWB.js +801 -0
  14. package/dist/Files-KY35DPWB.js.map +1 -0
  15. package/dist/Notepad-2BO5VEUG.js +406 -0
  16. package/dist/Notepad-2BO5VEUG.js.map +1 -0
  17. package/dist/PdfViewer-LSP3YNFC.js +4 -0
  18. package/dist/PdfViewer-LSP3YNFC.js.map +1 -0
  19. package/dist/PomodoroTimer-SVJ62RXM.js +629 -0
  20. package/dist/PomodoroTimer-SVJ62RXM.js.map +1 -0
  21. package/dist/Preview-N4UYYXG6.js +2955 -0
  22. package/dist/Preview-N4UYYXG6.js.map +1 -0
  23. package/dist/Sidebar-J76PV4QF.js +290 -0
  24. package/dist/Sidebar-J76PV4QF.js.map +1 -0
  25. package/dist/Spreadsheet-5EWZGMXN.js +515 -0
  26. package/dist/Spreadsheet-5EWZGMXN.js.map +1 -0
  27. package/dist/Stock-CDUS4VCZ.js +154 -0
  28. package/dist/Stock-CDUS4VCZ.js.map +1 -0
  29. package/dist/Weather-EEDBWBPQ.js +427 -0
  30. package/dist/Weather-EEDBWBPQ.js.map +1 -0
  31. package/dist/WorldClock-FFFULVTJ.js +198 -0
  32. package/dist/WorldClock-FFFULVTJ.js.map +1 -0
  33. package/dist/apps/index.d.ts +267 -0
  34. package/dist/apps/index.js +147 -0
  35. package/dist/apps/index.js.map +1 -0
  36. package/dist/brand.css +2 -0
  37. package/dist/chunk-2VM2FRZJ.js +215 -0
  38. package/dist/chunk-2VM2FRZJ.js.map +1 -0
  39. package/dist/chunk-32W6VY2J.js +549 -0
  40. package/dist/chunk-32W6VY2J.js.map +1 -0
  41. package/dist/chunk-3CUSBZWG.js +59 -0
  42. package/dist/chunk-3CUSBZWG.js.map +1 -0
  43. package/dist/chunk-3L463T5H.js +26 -0
  44. package/dist/chunk-3L463T5H.js.map +1 -0
  45. package/dist/chunk-4TVV2BIU.js +13334 -0
  46. package/dist/chunk-4TVV2BIU.js.map +1 -0
  47. package/dist/chunk-6XQHH7D2.js +105 -0
  48. package/dist/chunk-6XQHH7D2.js.map +1 -0
  49. package/dist/chunk-7CCHEEYC.js +94 -0
  50. package/dist/chunk-7CCHEEYC.js.map +1 -0
  51. package/dist/chunk-7KIOEKHS.js +617 -0
  52. package/dist/chunk-7KIOEKHS.js.map +1 -0
  53. package/dist/chunk-AXWC3332.js +1103 -0
  54. package/dist/chunk-AXWC3332.js.map +1 -0
  55. package/dist/chunk-BFBWLCO6.js +16 -0
  56. package/dist/chunk-BFBWLCO6.js.map +1 -0
  57. package/dist/chunk-C3NELUTX.js +79 -0
  58. package/dist/chunk-C3NELUTX.js.map +1 -0
  59. package/dist/chunk-CT3HVZRZ.js +26 -0
  60. package/dist/chunk-CT3HVZRZ.js.map +1 -0
  61. package/dist/chunk-D7PYW2QS.js +265 -0
  62. package/dist/chunk-D7PYW2QS.js.map +1 -0
  63. package/dist/chunk-DMCFOWOJ.js +153 -0
  64. package/dist/chunk-DMCFOWOJ.js.map +1 -0
  65. package/dist/chunk-FQB24B42.js +416 -0
  66. package/dist/chunk-FQB24B42.js.map +1 -0
  67. package/dist/chunk-GA35NEND.js +56 -0
  68. package/dist/chunk-GA35NEND.js.map +1 -0
  69. package/dist/chunk-HNH7FOYB.js +69 -0
  70. package/dist/chunk-HNH7FOYB.js.map +1 -0
  71. package/dist/chunk-HYZ5QMVO.js +462 -0
  72. package/dist/chunk-HYZ5QMVO.js.map +1 -0
  73. package/dist/chunk-I464GYJJ.js +17 -0
  74. package/dist/chunk-I464GYJJ.js.map +1 -0
  75. package/dist/chunk-JFQGPY7W.js +819 -0
  76. package/dist/chunk-JFQGPY7W.js.map +1 -0
  77. package/dist/chunk-MGGY74RY.js +14 -0
  78. package/dist/chunk-MGGY74RY.js.map +1 -0
  79. package/dist/chunk-N3CG5HG5.js +144 -0
  80. package/dist/chunk-N3CG5HG5.js.map +1 -0
  81. package/dist/chunk-W6XTG23K.js +3708 -0
  82. package/dist/chunk-W6XTG23K.js.map +1 -0
  83. package/dist/chunk-YG45DAUT.js +96 -0
  84. package/dist/chunk-YG45DAUT.js.map +1 -0
  85. package/dist/chunk-YMGXZABY.js +7 -0
  86. package/dist/chunk-YMGXZABY.js.map +1 -0
  87. package/dist/chunk-YNGPTOL6.js +46 -0
  88. package/dist/chunk-YNGPTOL6.js.map +1 -0
  89. package/dist/chunk-YNHLQ24V.js +400 -0
  90. package/dist/chunk-YNHLQ24V.js.map +1 -0
  91. package/dist/chunk-YNKK7NE3.js +171 -0
  92. package/dist/chunk-YNKK7NE3.js.map +1 -0
  93. package/dist/chunk-YPO6RDMX.js +20 -0
  94. package/dist/chunk-YPO6RDMX.js.map +1 -0
  95. package/dist/file-intake/index.d.ts +87 -0
  96. package/dist/file-intake/index.js +3 -0
  97. package/dist/file-intake/index.js.map +1 -0
  98. package/dist/index-dQia6NpB.d.ts +5904 -0
  99. package/dist/index.d.ts +2495 -0
  100. package/dist/index.js +7460 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/markdown/index.d.ts +118 -0
  103. package/dist/markdown/index.js +184 -0
  104. package/dist/markdown/index.js.map +1 -0
  105. package/dist/markup/index.d.ts +235 -0
  106. package/dist/markup/index.js +135 -0
  107. package/dist/markup/index.js.map +1 -0
  108. package/dist/page-tabs/index.d.ts +154 -0
  109. package/dist/page-tabs/index.js +706 -0
  110. package/dist/page-tabs/index.js.map +1 -0
  111. package/dist/shell.css +105 -0
  112. package/dist/sounds-NT4DEZGD.js +3 -0
  113. package/dist/sounds-NT4DEZGD.js.map +1 -0
  114. package/dist/styles.css +22 -0
  115. package/dist/themes.css +249 -0
  116. package/dist/types-BOinJXUf.d.ts +118 -0
  117. package/dist/ui/index.d.ts +4 -0
  118. package/dist/ui/index.js +15 -0
  119. package/dist/ui/index.js.map +1 -0
  120. package/dist/ui.css +1518 -0
  121. package/package.json +178 -4
@@ -0,0 +1,69 @@
1
+ import { Fragment } from 'react';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+
4
+ // src/shell/Breadcrumbs.tsx
5
+ var DEFAULT_SEPARATOR = /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5 shrink-0 text-gray-300", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx(
6
+ "path",
7
+ {
8
+ fillRule: "evenodd",
9
+ d: "M7.21 14.77a.75.75 0 0 1 .02-1.06L11.168 10 7.23 6.29a.75.75 0 1 1 1.04-1.08l4.5 4.25a.75.75 0 0 1 0 1.08l-4.5 4.25a.75.75 0 0 1-1.06-.02Z",
10
+ clipRule: "evenodd"
11
+ }
12
+ ) });
13
+ var CRUMB_LINK = "inline-flex min-w-0 items-center gap-1 truncate rounded px-1 -mx-1 text-gray-500 transition-colors hover:text-gray-900 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400";
14
+ function Breadcrumbs({ items, separator, maxItems = 0, className }) {
15
+ if (items.length === 0) return null;
16
+ const sep = separator ?? DEFAULT_SEPARATOR;
17
+ const crumbs = [];
18
+ const lastIndex = items.length - 1;
19
+ if (maxItems > 0 && items.length > maxItems && maxItems >= 2) {
20
+ const tailCount = Math.max(1, maxItems - 1);
21
+ crumbs.push({ kind: "item", item: items[0], isLast: false });
22
+ crumbs.push({ kind: "ellipsis" });
23
+ for (let i = items.length - tailCount; i <= lastIndex; i++) {
24
+ crumbs.push({ kind: "item", item: items[i], isLast: i === lastIndex });
25
+ }
26
+ } else {
27
+ items.forEach((item, i) => crumbs.push({ kind: "item", item, isLast: i === lastIndex }));
28
+ }
29
+ return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: `min-w-0 ${className ?? ""}`, children: /* @__PURE__ */ jsx("ol", { className: "flex items-center gap-1.5 text-sm", children: crumbs.map((crumb, i) => /* @__PURE__ */ jsxs(Fragment, { children: [
30
+ i > 0 && /* @__PURE__ */ jsx("li", { "aria-hidden": true, className: "flex items-center", children: sep }),
31
+ /* @__PURE__ */ jsx("li", { className: "flex min-w-0 items-center", children: crumb.kind === "ellipsis" ? /* @__PURE__ */ jsx("span", { className: "px-0.5 text-gray-400 select-none", "aria-label": "Hidden crumbs", children: "\u2026" }) : crumb.isLast || !(crumb.item.href || crumb.item.onClick) ? /* @__PURE__ */ jsxs(
32
+ "span",
33
+ {
34
+ "aria-current": crumb.isLast ? "page" : void 0,
35
+ className: `inline-flex min-w-0 items-center gap-1 truncate ${crumb.isLast ? "font-medium text-gray-900" : "text-gray-500"}`,
36
+ children: [
37
+ crumb.item.icon,
38
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: crumb.item.label })
39
+ ]
40
+ }
41
+ ) : crumb.item.href ? /* @__PURE__ */ jsxs(
42
+ "a",
43
+ {
44
+ href: crumb.item.href,
45
+ onClick: crumb.item.onClick,
46
+ className: CRUMB_LINK,
47
+ children: [
48
+ crumb.item.icon,
49
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: crumb.item.label })
50
+ ]
51
+ }
52
+ ) : /* @__PURE__ */ jsxs(
53
+ "button",
54
+ {
55
+ type: "button",
56
+ onClick: crumb.item.onClick,
57
+ className: CRUMB_LINK,
58
+ children: [
59
+ crumb.item.icon,
60
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: crumb.item.label })
61
+ ]
62
+ }
63
+ ) })
64
+ ] }, i)) }) });
65
+ }
66
+
67
+ export { Breadcrumbs };
68
+ //# sourceMappingURL=chunk-HNH7FOYB.js.map
69
+ //# sourceMappingURL=chunk-HNH7FOYB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shell/Breadcrumbs.tsx"],"names":[],"mappings":"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","file":"chunk-HNH7FOYB.js","sourcesContent":["import { Fragment } from 'react';\nimport type { MouseEvent, ReactNode } from 'react';\n\n/**\n * Generic breadcrumb trail. Self-contained and styled with the same Tailwind\n * utilities the shell already ships, so consumers get it for free.\n *\n * Pass an ordered list of `items` from root → current. Every crumb except the\n * last renders as a button when it has an `onClick`; the last crumb is treated\n * as the current location — rendered inert with `aria-current=\"page\"`. When the\n * trail is long, set `maxItems` to collapse the middle into an ellipsis\n * (`first … last-n` ), keeping the first and the tail visible.\n */\nexport interface BreadcrumbItem {\n /** Visible label. */\n label: ReactNode;\n /** Optional leading icon (typically a 3.5×3.5 svg). */\n icon?: ReactNode;\n /**\n * Navigate to this crumb. Omitted on the current (last) crumb.\n *\n * Receives the event, which is what makes `href` usable from a routed app:\n * the handler calls `preventDefault()` on a plain click and hands the\n * navigation to its router, while ctrl/middle/right clicks fall through to\n * the browser untouched. Without the argument an anchor could only ever do a\n * full page load, which is the thing a single-page app is avoiding.\n */\n onClick?: (event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) => void;\n /**\n * Where this crumb goes, when it is a real destination.\n *\n * A routed app needs this rather than `onClick` alone: an `<a href>` can be\n * middle-clicked into a new tab, copied, and read off the status bar before\n * committing to it — none of which a button offers, however well it behaves\n * once pressed. Given both, the anchor still calls `onClick`, so a client-side\n * router can intercept the plain-click case and leave the rest to the browser.\n */\n href?: string;\n}\n\nexport interface BreadcrumbsProps {\n items: BreadcrumbItem[];\n /** Node rendered between crumbs. Defaults to a chevron. */\n separator?: ReactNode;\n /** Collapse the middle to an ellipsis when there are more than this many\n * crumbs. `0` (default) never collapses. */\n maxItems?: number;\n className?: string;\n}\n\nconst DEFAULT_SEPARATOR = (\n <svg className=\"h-3.5 w-3.5 shrink-0 text-gray-300\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden>\n <path\n fillRule=\"evenodd\"\n d=\"M7.21 14.77a.75.75 0 0 1 .02-1.06L11.168 10 7.23 6.29a.75.75 0 1 1 1.04-1.08l4.5 4.25a.75.75 0 0 1 0 1.08l-4.5 4.25a.75.75 0 0 1-1.06-.02Z\"\n clipRule=\"evenodd\"\n />\n </svg>\n);\n\n/** Shared by the anchor and the button forms so the two cannot drift apart. */\nconst CRUMB_LINK =\n 'inline-flex min-w-0 items-center gap-1 truncate rounded px-1 -mx-1 text-gray-500 ' +\n 'transition-colors hover:text-gray-900 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400';\n\ntype Crumb = { kind: 'item'; item: BreadcrumbItem; isLast: boolean } | { kind: 'ellipsis' };\n\nexport default function Breadcrumbs({ items, separator, maxItems = 0, className }: BreadcrumbsProps) {\n if (items.length === 0) return null;\n const sep = separator ?? DEFAULT_SEPARATOR;\n\n // Build the display list, collapsing the middle if `maxItems` is exceeded.\n const crumbs: Crumb[] = [];\n const lastIndex = items.length - 1;\n if (maxItems > 0 && items.length > maxItems && maxItems >= 2) {\n const tailCount = Math.max(1, maxItems - 1); // keep the first + this many from the end\n crumbs.push({ kind: 'item', item: items[0], isLast: false });\n crumbs.push({ kind: 'ellipsis' });\n for (let i = items.length - tailCount; i <= lastIndex; i++) {\n crumbs.push({ kind: 'item', item: items[i], isLast: i === lastIndex });\n }\n } else {\n items.forEach((item, i) => crumbs.push({ kind: 'item', item, isLast: i === lastIndex }));\n }\n\n return (\n <nav aria-label=\"Breadcrumb\" className={`min-w-0 ${className ?? ''}`}>\n <ol className=\"flex items-center gap-1.5 text-sm\">\n {crumbs.map((crumb, i) => (\n <Fragment key={i}>\n {i > 0 && <li aria-hidden className=\"flex items-center\">{sep}</li>}\n <li className=\"flex min-w-0 items-center\">\n {crumb.kind === 'ellipsis' ? (\n <span className=\"px-0.5 text-gray-400 select-none\" aria-label=\"Hidden crumbs\">…</span>\n ) : crumb.isLast || !(crumb.item.href || crumb.item.onClick) ? (\n <span\n aria-current={crumb.isLast ? 'page' : undefined}\n className={`inline-flex min-w-0 items-center gap-1 truncate ${\n crumb.isLast ? 'font-medium text-gray-900' : 'text-gray-500'\n }`}\n >\n {crumb.item.icon}\n <span className=\"truncate\">{crumb.item.label}</span>\n </span>\n ) : crumb.item.href ? (\n <a\n href={crumb.item.href}\n onClick={crumb.item.onClick}\n className={CRUMB_LINK}\n >\n {crumb.item.icon}\n <span className=\"truncate\">{crumb.item.label}</span>\n </a>\n ) : (\n <button\n type=\"button\"\n onClick={crumb.item.onClick}\n className={CRUMB_LINK}\n >\n {crumb.item.icon}\n <span className=\"truncate\">{crumb.item.label}</span>\n </button>\n )}\n </li>\n </Fragment>\n ))}\n </ol>\n </nav>\n );\n}\n"]}
@@ -0,0 +1,462 @@
1
+ import { createContext, useEffect, useRef, useId, useContext, useState, useCallback, useLayoutEffect } from 'react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+
4
+ // src/shell/ConfirmDialog.tsx
5
+ var FOCUSABLE = [
6
+ "a[href]",
7
+ "button:not([disabled])",
8
+ "input:not([disabled])",
9
+ "select:not([disabled])",
10
+ "textarea:not([disabled])",
11
+ '[tabindex]:not([tabindex="-1"])'
12
+ ].join(",");
13
+ function focusable(root) {
14
+ return Array.from(root.querySelectorAll(FOCUSABLE)).filter((el) => el.offsetParent !== null || el === document.activeElement);
15
+ }
16
+ function useFocusTrap(ref, active, initialFocus) {
17
+ useEffect(() => {
18
+ if (!active) return;
19
+ const root = ref.current;
20
+ if (!root) return;
21
+ const previous = document.activeElement;
22
+ const first = initialFocus?.current ?? focusable(root)[0] ?? root;
23
+ if (first === root && !root.hasAttribute("tabindex")) root.setAttribute("tabindex", "-1");
24
+ first.focus();
25
+ const onKeyDown = (e) => {
26
+ if (e.key !== "Tab") return;
27
+ const stops = focusable(root);
28
+ if (stops.length === 0) {
29
+ e.preventDefault();
30
+ return;
31
+ }
32
+ const firstStop = stops[0];
33
+ const lastStop = stops[stops.length - 1];
34
+ const current = document.activeElement;
35
+ if (e.shiftKey && (current === firstStop || !root.contains(current))) {
36
+ e.preventDefault();
37
+ lastStop.focus();
38
+ } else if (!e.shiftKey && (current === lastStop || !root.contains(current))) {
39
+ e.preventDefault();
40
+ firstStop.focus();
41
+ }
42
+ };
43
+ document.addEventListener("keydown", onKeyDown, true);
44
+ return () => {
45
+ document.removeEventListener("keydown", onKeyDown, true);
46
+ if (!root.contains(document.activeElement) && document.activeElement !== document.body) return;
47
+ previous?.focus?.();
48
+ };
49
+ }, [ref, active, initialFocus]);
50
+ }
51
+ var lockCount = 0;
52
+ var restore = null;
53
+ function useScrollLock(active) {
54
+ useEffect(() => {
55
+ if (!active) return;
56
+ const body = document.body;
57
+ if (lockCount === 0) {
58
+ restore = { overflow: body.style.overflow, paddingRight: body.style.paddingRight };
59
+ const gap = window.innerWidth - document.documentElement.clientWidth;
60
+ body.style.overflow = "hidden";
61
+ if (gap > 0) body.style.paddingRight = `${gap}px`;
62
+ }
63
+ lockCount += 1;
64
+ return () => {
65
+ lockCount -= 1;
66
+ if (lockCount === 0 && restore) {
67
+ body.style.overflow = restore.overflow;
68
+ body.style.paddingRight = restore.paddingRight;
69
+ restore = null;
70
+ }
71
+ };
72
+ }, [active]);
73
+ }
74
+
75
+ // src/shell/escapeInterceptors.ts
76
+ var escapeInterceptors = /* @__PURE__ */ new Set();
77
+ var detachDrain = null;
78
+ function attachDrain() {
79
+ if (detachDrain || typeof document === "undefined") return;
80
+ const onKeyDown = (e) => {
81
+ if (e.key !== "Escape") return;
82
+ if (runEscapeInterceptors(e)) {
83
+ e.preventDefault();
84
+ e.stopPropagation();
85
+ }
86
+ };
87
+ document.addEventListener("keydown", onKeyDown, true);
88
+ detachDrain = () => document.removeEventListener("keydown", onKeyDown, true);
89
+ }
90
+ function registerModalEscapeInterceptor(fn) {
91
+ escapeInterceptors.add(fn);
92
+ attachDrain();
93
+ return () => {
94
+ escapeInterceptors.delete(fn);
95
+ if (escapeInterceptors.size === 0 && detachDrain) {
96
+ detachDrain();
97
+ detachDrain = null;
98
+ }
99
+ };
100
+ }
101
+ function runEscapeInterceptors(e) {
102
+ const ordered = Array.from(escapeInterceptors);
103
+ for (let i = ordered.length - 1; i >= 0; i--) {
104
+ try {
105
+ if (ordered[i](e)) return true;
106
+ } catch {
107
+ }
108
+ }
109
+ return false;
110
+ }
111
+
112
+ // src/shell/overlayEvents.ts
113
+ var OVERLAY_OPEN_EVENT = "react-os-shell:overlay-open";
114
+ function dismissPopups() {
115
+ if (typeof window === "undefined") return;
116
+ window.dispatchEvent(new Event(OVERLAY_OPEN_EVENT));
117
+ }
118
+ function onOverlayOpen(handler) {
119
+ if (typeof window === "undefined") return () => {
120
+ };
121
+ window.addEventListener(OVERLAY_OPEN_EVENT, handler);
122
+ return () => window.removeEventListener(OVERLAY_OPEN_EVENT, handler);
123
+ }
124
+
125
+ // src/shell/useDismissPopupsOnOpen.ts
126
+ var useBrowserLayoutEffect = typeof document === "undefined" ? useEffect : useLayoutEffect;
127
+ function useDismissPopupsOnOpen(open) {
128
+ useBrowserLayoutEffect(() => {
129
+ if (open) dismissPopups();
130
+ }, [open]);
131
+ }
132
+
133
+ // src/shell/zLayers.ts
134
+ var Z_LAYERS = {
135
+ window: 50,
136
+ windowStep: 10,
137
+ menu: 400,
138
+ pinnedWindow: 999,
139
+ overlay: 9999,
140
+ popup: 1e4
141
+ };
142
+ var SIZES = {
143
+ sm: "max-w-sm",
144
+ md: "max-w-md",
145
+ lg: "max-w-lg"
146
+ };
147
+ function Dialog({
148
+ open,
149
+ onClose,
150
+ title,
151
+ children,
152
+ footer,
153
+ blocking = false,
154
+ size = "md",
155
+ initialFocus,
156
+ "aria-label": ariaLabel,
157
+ className = ""
158
+ }) {
159
+ const panelRef = useRef(null);
160
+ const bodyId = useId();
161
+ const showClose = !blocking && !footer;
162
+ const titleId = useId();
163
+ useDismissPopupsOnOpen(open);
164
+ useFocusTrap(panelRef, open, initialFocus);
165
+ useScrollLock(open);
166
+ useEffect(() => {
167
+ if (!open) return;
168
+ return registerModalEscapeInterceptor(() => {
169
+ if (blocking) return true;
170
+ onClose();
171
+ return true;
172
+ });
173
+ }, [open, blocking, onClose]);
174
+ if (!open) return null;
175
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0", style: { zIndex: Z_LAYERS.overlay }, role: "presentation", children: [
176
+ /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black/30", "aria-hidden": "true" }),
177
+ /* @__PURE__ */ jsx(
178
+ "div",
179
+ {
180
+ className: "fixed inset-0 flex items-center justify-center p-4",
181
+ onClick: blocking ? void 0 : ((e) => {
182
+ if (e.target === e.currentTarget) onClose();
183
+ }),
184
+ children: /* @__PURE__ */ jsxs(
185
+ "div",
186
+ {
187
+ ref: panelRef,
188
+ role: "dialog",
189
+ "aria-modal": "true",
190
+ "aria-labelledby": title ? titleId : void 0,
191
+ "aria-label": title ? void 0 : ariaLabel,
192
+ "aria-describedby": children ? bodyId : void 0,
193
+ className: `relative w-full ${SIZES[size]} rounded-lg bg-white p-6 shadow-xl ${className}`.trim(),
194
+ children: [
195
+ title && /* @__PURE__ */ jsx("h2", { id: titleId, className: `${showClose ? "pr-8 " : ""}text-base font-semibold text-gray-900`, children: title }),
196
+ children && /* @__PURE__ */ jsx("div", { id: bodyId, className: "mt-2 text-sm text-gray-600", children }),
197
+ footer && /* @__PURE__ */ jsx("div", { className: "mt-6 flex justify-end gap-3", children: footer }),
198
+ showClose && /* @__PURE__ */ jsx(
199
+ "button",
200
+ {
201
+ type: "button",
202
+ onClick: onClose,
203
+ "aria-label": "Close",
204
+ className: "absolute right-3 top-3 rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-400",
205
+ children: /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M6 6l8 8M14 6l-8 8" }) })
206
+ }
207
+ )
208
+ ]
209
+ }
210
+ )
211
+ }
212
+ )
213
+ ] });
214
+ }
215
+ var ConfirmContext = createContext(() => Promise.resolve(false));
216
+ var useConfirm = () => useContext(ConfirmContext);
217
+ var globalConfirmFn = () => Promise.resolve(false);
218
+ var confirm = (opts) => globalConfirmFn(opts);
219
+ var globalDestructiveConfirmFn = () => Promise.resolve(false);
220
+ var confirmDestructive = (opts) => globalDestructiveConfirmFn(opts);
221
+ var globalPromptFn = () => Promise.resolve(null);
222
+ var prompt = (opts) => globalPromptFn(opts);
223
+ var TONE = {
224
+ danger: { icon: "text-red-600 bg-red-100", button: "bg-red-600 hover:bg-red-700 text-white" },
225
+ warning: { icon: "text-yellow-600 bg-yellow-100", button: "bg-yellow-600 hover:bg-yellow-700 text-white" },
226
+ info: { icon: "text-blue-600 bg-blue-100", button: "bg-blue-600 hover:bg-blue-700 text-white" }
227
+ };
228
+ var CANCEL_BTN = "bg-white text-gray-700 border border-gray-300 px-4 py-2 text-sm font-medium rounded-lg hover:bg-gray-50";
229
+ function WarningIcon() {
230
+ return /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" }) });
231
+ }
232
+ function CloseIcon() {
233
+ return /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) });
234
+ }
235
+ function ConfirmProvider({ children }) {
236
+ const [open, setOpen] = useState(false);
237
+ const [options, setOptions] = useState({ message: "" });
238
+ const [confirmId, setConfirmId] = useState(0);
239
+ const resolveRef = useRef();
240
+ const activeConfirmIdRef = useRef(null);
241
+ const nextConfirmIdRef = useRef(0);
242
+ const confirmQueueRef = useRef([]);
243
+ const cancelRef = useRef(null);
244
+ const dCancelRef = useRef(null);
245
+ const showConfirm = useCallback((request) => {
246
+ activeConfirmIdRef.current = request.id;
247
+ resolveRef.current = request.resolve;
248
+ setConfirmId(request.id);
249
+ setOptions(request.options);
250
+ setOpen(true);
251
+ }, []);
252
+ const confirmFn = useCallback((opts) => {
253
+ const normalized = typeof opts === "string" ? { message: opts } : opts;
254
+ return new Promise((resolve) => {
255
+ const request = { id: ++nextConfirmIdRef.current, options: normalized, resolve };
256
+ if (activeConfirmIdRef.current === null) showConfirm(request);
257
+ else confirmQueueRef.current.push(request);
258
+ });
259
+ }, [showConfirm]);
260
+ const [dOpen, setDOpen] = useState(false);
261
+ const [dOptions, setDOptions] = useState({ message: "" });
262
+ const [dInput, setDInput] = useState("");
263
+ const dResolveRef = useRef();
264
+ const destructiveConfirmFn = useCallback((opts) => {
265
+ setDOptions(opts);
266
+ setDInput("");
267
+ setDOpen(true);
268
+ return new Promise((resolve) => {
269
+ dResolveRef.current = resolve;
270
+ });
271
+ }, []);
272
+ const [pOpen, setPOpen] = useState(false);
273
+ const [pOptions, setPOptions] = useState({});
274
+ const [pInput, setPInput] = useState("");
275
+ const pResolveRef = useRef();
276
+ const promptFn = useCallback((opts) => {
277
+ const normalized = typeof opts === "string" ? { message: opts } : opts;
278
+ setPOptions(normalized);
279
+ setPInput(normalized.defaultValue ?? "");
280
+ setPOpen(true);
281
+ return new Promise((resolve) => {
282
+ pResolveRef.current = resolve;
283
+ });
284
+ }, []);
285
+ useEffect(() => {
286
+ globalConfirmFn = confirmFn;
287
+ globalDestructiveConfirmFn = destructiveConfirmFn;
288
+ globalPromptFn = promptFn;
289
+ }, [confirmFn, destructiveConfirmFn, promptFn]);
290
+ const handleClose = (id, result) => {
291
+ if (activeConfirmIdRef.current !== id) return;
292
+ const resolve = resolveRef.current;
293
+ const next = confirmQueueRef.current.shift();
294
+ if (next) showConfirm(next);
295
+ else {
296
+ activeConfirmIdRef.current = null;
297
+ resolveRef.current = void 0;
298
+ setOpen(false);
299
+ }
300
+ resolve?.(result);
301
+ };
302
+ const handleDClose = (result) => {
303
+ setDOpen(false);
304
+ setDInput("");
305
+ dResolveRef.current?.(result);
306
+ };
307
+ const handlePClose = (commit) => {
308
+ if (commit) {
309
+ const trimmed = pInput.trim();
310
+ if (!trimmed && !pOptions.allowEmpty) {
311
+ setPOpen(false);
312
+ pResolveRef.current?.(null);
313
+ return;
314
+ }
315
+ setPOpen(false);
316
+ pResolveRef.current?.(trimmed);
317
+ } else {
318
+ setPOpen(false);
319
+ pResolveRef.current?.(null);
320
+ }
321
+ };
322
+ const variant = options.variant || (options.confirmLabel?.toLowerCase().includes("delete") || options.message.toLowerCase().includes("delete") ? "danger" : "info");
323
+ const tone = TONE[variant];
324
+ const dTone = TONE[dOptions.variant === "warning" ? "warning" : "danger"];
325
+ const dWord = dOptions.confirmWord;
326
+ const dSatisfied = dWord == null || dInput === dWord;
327
+ return /* @__PURE__ */ jsxs(ConfirmContext.Provider, { value: confirmFn, children: [
328
+ children,
329
+ /* @__PURE__ */ jsx(
330
+ Dialog,
331
+ {
332
+ open,
333
+ onClose: () => handleClose(confirmId, false),
334
+ initialFocus: cancelRef,
335
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
336
+ /* @__PURE__ */ jsx("button", { ref: cancelRef, type: "button", onClick: () => handleClose(confirmId, false), className: CANCEL_BTN, children: options.cancelLabel || "Cancel" }),
337
+ /* @__PURE__ */ jsx(
338
+ "button",
339
+ {
340
+ type: "button",
341
+ onClick: () => handleClose(confirmId, true),
342
+ className: `px-4 py-2 text-sm font-medium rounded-lg ${tone.button}`,
343
+ children: options.confirmLabel || "OK"
344
+ }
345
+ )
346
+ ] }),
347
+ children: /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
348
+ /* @__PURE__ */ jsx("div", { className: `shrink-0 w-10 h-10 rounded-full flex items-center justify-center ${tone.icon}`, children: /* @__PURE__ */ jsx(WarningIcon, {}) }),
349
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
350
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-gray-900", children: options.title || "Confirm" }),
351
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-600", children: options.message })
352
+ ] })
353
+ ] })
354
+ }
355
+ ),
356
+ /* @__PURE__ */ jsxs(
357
+ Dialog,
358
+ {
359
+ open: dOpen,
360
+ onClose: () => handleDClose(false),
361
+ initialFocus: dWord != null ? void 0 : dCancelRef,
362
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
363
+ /* @__PURE__ */ jsx("button", { ref: dCancelRef, type: "button", onClick: () => handleDClose(false), className: CANCEL_BTN, children: dOptions.cancelLabel || "Dismiss" }),
364
+ /* @__PURE__ */ jsx(
365
+ "button",
366
+ {
367
+ type: "button",
368
+ onClick: () => handleDClose(true),
369
+ disabled: !dSatisfied,
370
+ className: `px-4 py-2 text-sm font-medium rounded-lg disabled:opacity-40 ${dTone.button}`,
371
+ children: dWord ?? dOptions.confirmLabel ?? "Delete"
372
+ }
373
+ )
374
+ ] }),
375
+ children: [
376
+ /* @__PURE__ */ jsx(
377
+ "button",
378
+ {
379
+ type: "button",
380
+ onClick: () => handleDClose(false),
381
+ "aria-label": "Close",
382
+ className: "absolute right-4 top-4 rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600",
383
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
384
+ }
385
+ ),
386
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
387
+ /* @__PURE__ */ jsx("div", { className: `shrink-0 w-10 h-10 rounded-full flex items-center justify-center ${dTone.icon}`, children: /* @__PURE__ */ jsx(WarningIcon, {}) }),
388
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
389
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-gray-900 pr-6", children: dOptions.title || "Confirm Action" }),
390
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-600", children: dOptions.message }),
391
+ dWord != null && /* @__PURE__ */ jsxs(Fragment, { children: [
392
+ /* @__PURE__ */ jsxs("p", { className: "mt-3 text-sm text-gray-700", children: [
393
+ "Type ",
394
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 bg-gray-50 px-1.5 py-0.5 text-xs font-bold text-red-600 font-mono", children: dWord }),
395
+ " to confirm:"
396
+ ] }),
397
+ /* @__PURE__ */ jsx(
398
+ "input",
399
+ {
400
+ autoFocus: true,
401
+ type: "text",
402
+ value: dInput,
403
+ onChange: (e) => setDInput(e.target.value),
404
+ onKeyDown: (e) => {
405
+ if (e.key === "Enter" && dInput === dWord) handleDClose(true);
406
+ },
407
+ placeholder: dWord,
408
+ className: "mt-2 w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-red-500 focus:ring-red-500"
409
+ }
410
+ )
411
+ ] })
412
+ ] })
413
+ ] })
414
+ ]
415
+ }
416
+ ),
417
+ /* @__PURE__ */ jsxs(
418
+ Dialog,
419
+ {
420
+ open: pOpen,
421
+ onClose: () => handlePClose(false),
422
+ title: pOptions.title || "Enter a value",
423
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
424
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => handlePClose(false), className: CANCEL_BTN, children: pOptions.cancelLabel || "Cancel" }),
425
+ /* @__PURE__ */ jsx(
426
+ "button",
427
+ {
428
+ type: "button",
429
+ onClick: () => handlePClose(true),
430
+ disabled: !pOptions.allowEmpty && !pInput.trim(),
431
+ className: "bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 text-sm font-medium rounded-lg disabled:opacity-40",
432
+ children: pOptions.confirmLabel || "OK"
433
+ }
434
+ )
435
+ ] }),
436
+ children: [
437
+ pOptions.message && /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: pOptions.message }),
438
+ /* @__PURE__ */ jsx(
439
+ "input",
440
+ {
441
+ autoFocus: true,
442
+ type: "text",
443
+ value: pInput,
444
+ onChange: (e) => setPInput(e.target.value),
445
+ onKeyDown: (e) => {
446
+ if (e.key === "Enter") handlePClose(true);
447
+ else if (e.key === "Escape") handlePClose(false);
448
+ },
449
+ onFocus: (e) => e.target.select(),
450
+ placeholder: pOptions.placeholder,
451
+ className: "mt-3 w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
452
+ }
453
+ )
454
+ ]
455
+ }
456
+ )
457
+ ] });
458
+ }
459
+
460
+ export { ConfirmProvider, Dialog, OVERLAY_OPEN_EVENT, Z_LAYERS, confirm, confirmDestructive, dismissPopups, onOverlayOpen, prompt, registerModalEscapeInterceptor, runEscapeInterceptors, useConfirm, useDismissPopupsOnOpen, useFocusTrap, useScrollLock };
461
+ //# sourceMappingURL=chunk-HYZ5QMVO.js.map
462
+ //# sourceMappingURL=chunk-HYZ5QMVO.js.map