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,617 @@
1
+ import { onOverlayOpen, Z_LAYERS, registerModalEscapeInterceptor } from './chunk-HYZ5QMVO.js';
2
+ import { glassStyle, GLASS_DIVIDER } from './chunk-3CUSBZWG.js';
3
+ import { createContext, forwardRef, useContext, useMemo, useRef, useId, useState, useEffect, useCallback, useLayoutEffect } from 'react';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+ import { createPortal } from 'react-dom';
6
+
7
+ var DEFAULT_SHELL_STRINGS = {
8
+ window: {
9
+ minimize: "Minimize",
10
+ restore: "Restore",
11
+ maximize: "Maximize",
12
+ windowed: "Windowed",
13
+ pinOnTop: "Pin on top",
14
+ unpin: "Unpin from top",
15
+ close: "Close",
16
+ windowSuffix: "window",
17
+ prev: "Prev",
18
+ next: "Next",
19
+ snapLayouts: "Snap layouts",
20
+ snapLeft: "Snap left half",
21
+ snapRight: "Snap right half",
22
+ snapTopLeft: "Snap top-left quarter",
23
+ snapTopRight: "Snap top-right quarter",
24
+ snapBottomLeft: "Snap bottom-left quarter",
25
+ snapBottomRight: "Snap bottom-right quarter"
26
+ },
27
+ taskbar: {
28
+ closeWindow: "Close window",
29
+ offscreenHint: "This window is off screen \u2014 click to bring it back",
30
+ offscreenDot: "Off screen \u2014 click to bring this window back",
31
+ expose: "Expos\xE9 \u2014 show all open windows as thumbnails",
32
+ addToDesktop: "Add to desktop",
33
+ removeFromDesktop: "Remove from desktop",
34
+ minimizeAllWindows: "Minimize all",
35
+ restoreAllWindows: "Restore all",
36
+ closeAllWindows: "Close all"
37
+ },
38
+ logout: {
39
+ goodbye: "Goodbye",
40
+ seeYou: "See you next time"
41
+ },
42
+ about: {
43
+ aboutPrefix: "About",
44
+ thisApp: "this app",
45
+ openSourceLicenses: "Open Source Licenses",
46
+ whatsNew: "What's New",
47
+ noChangelog: "No changelog available."
48
+ },
49
+ table: {
50
+ empty: "Nothing to show",
51
+ loading: "Loading",
52
+ selectAll: "Select all rows",
53
+ selectRow: "Select row"
54
+ },
55
+ select: {
56
+ none: "\u2014 None \u2014",
57
+ noMatches: "No matches",
58
+ allSelected: "All options selected",
59
+ clear: "Clear selection",
60
+ remove: "Remove"
61
+ },
62
+ notifications: {
63
+ title: "Notifications",
64
+ markAllRead: "Mark all read",
65
+ dnd: "Do not disturb",
66
+ dndOn: "Do not disturb is on",
67
+ caughtUp: "All caught up",
68
+ empty: "No notifications yet",
69
+ viewAll: "View all notifications",
70
+ cardLabel: "Notification",
71
+ dismiss: "Dismiss notification"
72
+ },
73
+ form: {
74
+ errorSummaryTitle: "There is a problem"
75
+ },
76
+ help: {
77
+ title: "Help",
78
+ searchPlaceholder: "Search help\u2026",
79
+ empty: "No help articles yet.",
80
+ noResults: "No articles match your search.",
81
+ loading: "Loading\u2026",
82
+ pickArticle: "Pick a help article from the left.",
83
+ noBody: "This article has no body yet.",
84
+ draft: "Draft",
85
+ newArticle: "+ New",
86
+ edit: "Edit"
87
+ },
88
+ update: {
89
+ available: "A new version is available.",
90
+ refreshNow: "Refresh now"
91
+ },
92
+ pageTabs: {
93
+ label: "Open pages",
94
+ close: "Close",
95
+ closeOthers: "Close other tabs",
96
+ closeRight: "Close tabs to the right",
97
+ closeAll: "Close all tabs",
98
+ unsaved: "Unsaved changes",
99
+ discardTitle: "Discard unsaved changes?",
100
+ discardOne: "This page has changes that have not been saved. Close it anyway?",
101
+ discardMany: "pages have changes that have not been saved. Close them anyway?",
102
+ discardConfirm: "Discard and close",
103
+ keepEditing: "Keep editing"
104
+ }
105
+ };
106
+ var ShellStringsContext = createContext(DEFAULT_SHELL_STRINGS);
107
+ function ShellStringsProvider({ value, children }) {
108
+ const merged = useMemo(() => {
109
+ const out = {};
110
+ for (const key of Object.keys(DEFAULT_SHELL_STRINGS)) {
111
+ out[key] = { ...DEFAULT_SHELL_STRINGS[key], ...value[key] ?? {} };
112
+ }
113
+ return out;
114
+ }, [value]);
115
+ return /* @__PURE__ */ jsx(ShellStringsContext.Provider, { value: merged, children });
116
+ }
117
+ function useShellStrings() {
118
+ return useContext(ShellStringsContext);
119
+ }
120
+
121
+ // src/shell/contextMenuTarget.ts
122
+ var TEXT_INPUT_TYPES = /* @__PURE__ */ new Set([
123
+ "text",
124
+ "search",
125
+ "url",
126
+ "tel",
127
+ "email",
128
+ "password",
129
+ "number",
130
+ "date",
131
+ "datetime-local",
132
+ "month",
133
+ "week",
134
+ "time"
135
+ ]);
136
+ var NATIVE_MEDIA = "img, picture, canvas, video, audio";
137
+ var NATIVE_MENU_ATTR = "data-native-context-menu";
138
+ function closest(el, selector) {
139
+ if (!el || typeof el.closest !== "function") return null;
140
+ return el.closest(selector);
141
+ }
142
+ function keepsNativeMenu(target) {
143
+ const el = target;
144
+ if (!el || typeof el.closest !== "function") return false;
145
+ if (closest(el, `[${NATIVE_MENU_ATTR}]`)) return true;
146
+ if (closest(el, "textarea")) return true;
147
+ if (closest(el, NATIVE_MEDIA)) return true;
148
+ const input = closest(el, "input");
149
+ if (input) {
150
+ const type = (input.type || "text").toLowerCase();
151
+ if (TEXT_INPUT_TYPES.has(type)) return true;
152
+ }
153
+ const editable = closest(el, "[contenteditable]");
154
+ if (editable) {
155
+ const value = (editable.getAttribute("contenteditable") || "").toLowerCase();
156
+ if (value === "" || value === "true" || value === "plaintext-only") return true;
157
+ }
158
+ return false;
159
+ }
160
+ var LINK_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:", "blob:"]);
161
+ function linkAddress(href, base) {
162
+ if (!href || href === "#") return void 0;
163
+ try {
164
+ const url = new URL(href, base ?? (typeof document !== "undefined" ? document.baseURI : void 0));
165
+ return LINK_SCHEMES.has(url.protocol) ? url.href : void 0;
166
+ } catch {
167
+ return void 0;
168
+ }
169
+ }
170
+ function describeContextTarget(target, selectionText, baseUrl) {
171
+ const el = target;
172
+ const link = closest(el, "a[href]");
173
+ const text = (selectionText || "").trim();
174
+ const linkUrl = linkAddress(link?.getAttribute("href"), baseUrl);
175
+ const kind = text ? "selection" : linkUrl ? "link" : "default";
176
+ return {
177
+ kind,
178
+ ...text ? { selectionText: text } : {},
179
+ ...linkUrl ? { linkUrl } : {}
180
+ };
181
+ }
182
+
183
+ // src/shell/menuPath.ts
184
+ function openMenuLevel(path, depth, anchor) {
185
+ if (path.length === depth + 1 && path[depth].key === anchor.key) return path;
186
+ return [...path.slice(0, depth), anchor];
187
+ }
188
+ function closeMenuBelow(path, depth) {
189
+ const keep = Math.max(0, depth);
190
+ return path.length > keep ? path.slice(0, keep) : path;
191
+ }
192
+ function resolveMenuLevels(rootItems, path, childrenOf) {
193
+ if (path.length === 0 || rootItems.length === 0) return [];
194
+ const levels = [rootItems];
195
+ for (let i = 1; i < path.length; i++) {
196
+ const parent = levels[i - 1].find((item) => item.to === path[i].key);
197
+ const kids = parent ? childrenOf(parent) : [];
198
+ if (kids.length === 0) break;
199
+ levels.push(kids);
200
+ }
201
+ return levels;
202
+ }
203
+ function clampMenuTop(anchorY, height, minTop, maxBottom) {
204
+ const span = Math.max(0, maxBottom - minTop);
205
+ const h = Math.min(height, span);
206
+ const top = anchorY - h / 2;
207
+ if (top < minTop) return minTop;
208
+ if (top + h > maxBottom) return Math.max(minTop, maxBottom - h);
209
+ return top;
210
+ }
211
+ function menuPanelLeft(ownerLeft, ownerRight, width, viewportW, gap, preferLeft = false, margin = 8) {
212
+ const onRight = ownerRight + gap;
213
+ const onLeft = ownerLeft - gap - width;
214
+ const rightFits = onRight + width <= viewportW - margin;
215
+ const leftFits = onLeft >= margin;
216
+ if (preferLeft && leftFits) return { left: onLeft, flipped: true };
217
+ if (rightFits) return { left: onRight, flipped: false };
218
+ if (leftFits) return { left: onLeft, flipped: true };
219
+ return preferLeft ? { left: margin, flipped: true } : { left: Math.max(margin, viewportW - margin - width), flipped: false };
220
+ }
221
+ function getDensity() {
222
+ return getComputedStyle(document.documentElement).getPropertyValue("--menu-density")?.trim() || "normal";
223
+ }
224
+ var SUBMENU_CLOSE_DELAY = 200;
225
+ var SUBMENU_OPEN_DELAY = 120;
226
+ var SUBMENU_GAP = 4;
227
+ var MenuTreeContext = createContext(null);
228
+ function PopupMenu({ children, style, className = "", onClose, minWidth = 180, portal = false }) {
229
+ const ref = useRef(null);
230
+ const treeId = useId();
231
+ const [path, setPath] = useState([]);
232
+ const closeTimer = useRef();
233
+ useEffect(() => () => clearTimeout(closeTimer.current), []);
234
+ const tree = useMemo(() => {
235
+ const cancelClose = () => clearTimeout(closeTimer.current);
236
+ return {
237
+ id: treeId,
238
+ depth: 0,
239
+ path,
240
+ open: (depth, anchor) => {
241
+ cancelClose();
242
+ setPath((prev) => openMenuLevel(prev, depth, anchor));
243
+ },
244
+ scheduleClose: (depth) => {
245
+ cancelClose();
246
+ closeTimer.current = setTimeout(() => setPath((prev) => closeMenuBelow(prev, depth)), SUBMENU_CLOSE_DELAY);
247
+ },
248
+ closeBelow: (depth) => {
249
+ cancelClose();
250
+ setPath((prev) => closeMenuBelow(prev, depth));
251
+ },
252
+ cancelClose,
253
+ closeTree: () => {
254
+ cancelClose();
255
+ setPath([]);
256
+ onClose?.();
257
+ },
258
+ root: ref
259
+ };
260
+ }, [treeId, path, onClose]);
261
+ useEffect(() => {
262
+ if (!onClose) return;
263
+ const handleClick = (e) => {
264
+ if (e.target.closest("[data-menu-toggle]")) return;
265
+ if (e.target.closest?.(`[data-popup-menu-tree="${treeId}"]`)) return;
266
+ if (ref.current && !ref.current.contains(e.target)) onClose();
267
+ };
268
+ const handleKey = (e) => {
269
+ if (e.key === "Escape") onClose();
270
+ };
271
+ window.addEventListener("pointerdown", handleClick);
272
+ window.addEventListener("keydown", handleKey);
273
+ const offOverlay = onOverlayOpen(onClose);
274
+ return () => {
275
+ window.removeEventListener("pointerdown", handleClick);
276
+ window.removeEventListener("keydown", handleKey);
277
+ offOverlay();
278
+ };
279
+ }, [onClose, treeId]);
280
+ useEffect(() => {
281
+ const el = ref.current;
282
+ if (!el) return;
283
+ requestAnimationFrame(() => {
284
+ const rect = el.getBoundingClientRect();
285
+ const margin = 8;
286
+ if (rect.right > window.innerWidth - margin) {
287
+ const overflow = rect.right - window.innerWidth + margin;
288
+ if (el.style.left) {
289
+ el.style.left = `${parseFloat(el.style.left) - overflow}px`;
290
+ } else if (!el.style.right) {
291
+ el.style.left = `${rect.left - overflow}px`;
292
+ }
293
+ }
294
+ if (rect.left < margin) {
295
+ if (el.style.left) {
296
+ el.style.left = `${margin}px`;
297
+ } else if (el.style.right) {
298
+ el.style.right = `${window.innerWidth - rect.width - margin}px`;
299
+ }
300
+ }
301
+ if (rect.bottom > window.innerHeight - margin) {
302
+ const overflow = rect.bottom - window.innerHeight + margin;
303
+ if (el.style.top) {
304
+ el.style.top = `${parseFloat(el.style.top) - overflow}px`;
305
+ } else if (el.style.bottom) {
306
+ el.style.bottom = `${margin}px`;
307
+ } else {
308
+ el.style.top = `${rect.top - overflow}px`;
309
+ }
310
+ }
311
+ if (rect.top < margin) {
312
+ if (el.style.top) {
313
+ el.style.top = `${margin}px`;
314
+ } else if (el.style.bottom) {
315
+ el.style.bottom = `${window.innerHeight - rect.height - margin}px`;
316
+ }
317
+ }
318
+ });
319
+ });
320
+ const density = getDensity();
321
+ const ownsZ = /(^|[\s:])!?z-/.test(className);
322
+ const menu = /* @__PURE__ */ jsxs(
323
+ "div",
324
+ {
325
+ ref,
326
+ "data-popup-menu-tree": treeId,
327
+ className: `fixed rounded-2xl ${density === "tight" ? "py-1" : density === "large" ? "py-2" : "py-1.5"} ${className}`,
328
+ onContextMenu: (e) => {
329
+ if (!keepsNativeMenu(e.target)) e.preventDefault();
330
+ },
331
+ style: { ...ownsZ ? {} : { zIndex: Z_LAYERS.menu }, minWidth, animation: "popup-in 0.12s ease-out", ...glassStyle(), ...style },
332
+ children: [
333
+ /* @__PURE__ */ jsx(MenuTreeContext.Provider, { value: tree, children }),
334
+ /* @__PURE__ */ jsx("style", { children: `@keyframes popup-in { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }` })
335
+ ]
336
+ }
337
+ );
338
+ return portal ? createPortal(menu, document.body) : menu;
339
+ }
340
+ function itemLook(danger, disabled, className, highlighted = false) {
341
+ const density = getDensity();
342
+ const itemPadY = density === "tight" ? "0.25rem" : density === "large" ? "0.6rem" : "max(0.25rem, calc(var(--menu-padding-y, 0.5rem) - 0.1rem))";
343
+ const tone = danger ? "text-red-600 hover:bg-red-50" : highlighted ? "bg-blue-50 text-blue-700" : "text-gray-700 hover:bg-blue-50 hover:text-blue-700";
344
+ return {
345
+ className: `w-full flex items-center gap-2 text-left transition-colors rounded-lg mx-auto
346
+ ${tone}
347
+ ${disabled ? "opacity-40 cursor-not-allowed" : ""}
348
+ ${className}`,
349
+ style: {
350
+ width: "calc(100% - 8px)",
351
+ marginLeft: 4,
352
+ marginRight: 4,
353
+ fontSize: "var(--menu-font-size, 14px)",
354
+ paddingLeft: "var(--menu-padding-x, 1rem)",
355
+ paddingRight: "var(--menu-padding-x, 1rem)",
356
+ paddingTop: itemPadY,
357
+ paddingBottom: itemPadY
358
+ }
359
+ };
360
+ }
361
+ var PopupMenuItem = forwardRef(function PopupMenuItem2({
362
+ onClick,
363
+ children,
364
+ className = "",
365
+ danger,
366
+ disabled,
367
+ role,
368
+ tabIndex,
369
+ id,
370
+ onMouseEnter
371
+ }, ref) {
372
+ const tree = useContext(MenuTreeContext);
373
+ const look = itemLook(danger, disabled, className);
374
+ return /* @__PURE__ */ jsx(
375
+ "button",
376
+ {
377
+ ref,
378
+ id,
379
+ role,
380
+ tabIndex,
381
+ onClick: () => {
382
+ onClick?.();
383
+ if (tree && tree.depth > 0) tree.closeTree();
384
+ },
385
+ onMouseEnter: () => {
386
+ tree?.scheduleClose(tree.depth);
387
+ onMouseEnter?.();
388
+ },
389
+ disabled,
390
+ className: look.className,
391
+ style: look.style,
392
+ children
393
+ }
394
+ );
395
+ });
396
+ var chevron = /* @__PURE__ */ jsx("svg", { "aria-hidden": true, className: "h-3.5 w-3.5 shrink-0 ml-auto text-gray-500", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) });
397
+ function layerOf(el) {
398
+ let z = Z_LAYERS.menu;
399
+ for (let node = el; node && node !== document.body; node = node.parentElement) {
400
+ const value = parseInt(getComputedStyle(node).zIndex, 10);
401
+ if (!Number.isNaN(value)) z = value;
402
+ }
403
+ return z;
404
+ }
405
+ function focusables(panel) {
406
+ if (!panel) return [];
407
+ return [...panel.querySelectorAll("button:not([disabled])")].filter((b) => b.closest("[data-popup-submenu-panel]") === panel);
408
+ }
409
+ function PopupSubmenu({ label, icon, children, disabled, className = "", minWidth = 180, maxHeight }) {
410
+ const tree = useContext(MenuTreeContext);
411
+ const key = useId();
412
+ const rowRef = useRef(null);
413
+ const panelRef = useRef(null);
414
+ const openTimer = useRef();
415
+ const focusOnOpen = useRef(false);
416
+ const panelId = `${key}-menu`;
417
+ useEffect(() => () => clearTimeout(openTimer.current), []);
418
+ const depth = tree?.depth ?? 0;
419
+ const anchor = tree?.path[depth];
420
+ const isOpen = !!anchor && anchor.key === key && !disabled;
421
+ const openNow = useCallback((withFocus) => {
422
+ if (!tree || disabled || !rowRef.current) return;
423
+ clearTimeout(openTimer.current);
424
+ focusOnOpen.current = withFocus;
425
+ const row = rowRef.current.getBoundingClientRect();
426
+ const owner = rowRef.current.closest("[data-popup-submenu-panel]") ?? rowRef.current.closest("[data-popup-menu-tree]");
427
+ const ownerRect = owner?.getBoundingClientRect() ?? row;
428
+ const preferLeft = depth > 0 && (tree.path[depth - 1]?.flipped ?? false);
429
+ const place = menuPanelLeft(ownerRect.left, ownerRect.right, minWidth, window.innerWidth, SUBMENU_GAP, preferLeft);
430
+ tree.open(depth, { key, y: row.top, ...place });
431
+ }, [tree, disabled, depth, key, minWidth]);
432
+ const closeSelf = (restoreFocus) => {
433
+ tree?.closeBelow(depth);
434
+ rowRef.current?.focus();
435
+ };
436
+ useEffect(() => {
437
+ if (!isOpen) return;
438
+ return registerModalEscapeInterceptor((e) => {
439
+ if (e.key !== "Escape") return false;
440
+ if (tree && tree.path.length > depth + 1) return false;
441
+ closeSelf();
442
+ return true;
443
+ });
444
+ });
445
+ useLayoutEffect(() => {
446
+ if (!isOpen || !anchor) return;
447
+ const place = () => {
448
+ const panel = panelRef.current;
449
+ if (!panel || !rowRef.current) return;
450
+ const margin = 8;
451
+ const owner = rowRef.current.closest("[data-popup-submenu-panel]") ?? rowRef.current.closest("[data-popup-menu-tree]");
452
+ const ownerRect = owner?.getBoundingClientRect() ?? rowRef.current.getBoundingClientRect();
453
+ const preferLeft = depth > 0 && (tree?.path[depth - 1]?.flipped ?? false);
454
+ const width = panel.offsetWidth || minWidth;
455
+ const spot = menuPanelLeft(ownerRect.left, ownerRect.right, width, window.innerWidth, SUBMENU_GAP, preferLeft, margin);
456
+ const height = panel.offsetHeight;
457
+ const rowTop = rowRef.current.getBoundingClientRect().top;
458
+ const top = clampMenuTop(rowTop + height / 2, height, margin, window.innerHeight - margin);
459
+ panel.style.left = `${spot.left}px`;
460
+ panel.style.top = `${top}px`;
461
+ panel.dataset.flipped = String(spot.flipped);
462
+ };
463
+ place();
464
+ let second = 0;
465
+ const first = requestAnimationFrame(() => {
466
+ second = requestAnimationFrame(place);
467
+ });
468
+ return () => {
469
+ cancelAnimationFrame(first);
470
+ cancelAnimationFrame(second);
471
+ };
472
+ });
473
+ useEffect(() => {
474
+ if (!isOpen || !focusOnOpen.current) return;
475
+ focusOnOpen.current = false;
476
+ focusables(panelRef.current)[0]?.focus();
477
+ }, [isOpen]);
478
+ const childTree = useMemo(() => tree && { ...tree, depth: depth + 1 }, [tree, depth]);
479
+ if (!tree) throw new Error("PopupSubmenu must be rendered inside a PopupMenu");
480
+ const onRowKeyDown = (e) => {
481
+ if (e.key === "ArrowRight") {
482
+ e.preventDefault();
483
+ e.stopPropagation();
484
+ openNow(true);
485
+ }
486
+ };
487
+ const onPanelKeyDown = (e) => {
488
+ if (e.target.closest("[data-popup-submenu-panel]") !== e.currentTarget) return;
489
+ const items = focusables(panelRef.current);
490
+ const at = items.indexOf(document.activeElement);
491
+ const move = (to) => {
492
+ e.preventDefault();
493
+ e.stopPropagation();
494
+ items[(to + items.length) % items.length]?.focus();
495
+ };
496
+ if (e.key === "ArrowDown") move(at + 1);
497
+ else if (e.key === "ArrowUp") move(at < 0 ? items.length - 1 : at - 1);
498
+ else if (e.key === "Home") move(0);
499
+ else if (e.key === "End") move(items.length - 1);
500
+ else if (e.key === "ArrowLeft") {
501
+ e.preventDefault();
502
+ e.stopPropagation();
503
+ closeSelf();
504
+ }
505
+ };
506
+ const look = itemLook(false, disabled, className, isOpen);
507
+ const density = getDensity();
508
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
509
+ /* @__PURE__ */ jsxs(
510
+ "button",
511
+ {
512
+ ref: rowRef,
513
+ type: "button",
514
+ role: "menuitem",
515
+ "aria-haspopup": "menu",
516
+ "aria-expanded": isOpen,
517
+ "aria-controls": isOpen ? panelId : void 0,
518
+ disabled,
519
+ "data-popup-submenu-row": true,
520
+ className: look.className,
521
+ style: look.style,
522
+ onClick: (e) => {
523
+ const byKey = e.detail === 0;
524
+ if (!isOpen) openNow(byKey);
525
+ else if (byKey) focusables(panelRef.current)[0]?.focus();
526
+ },
527
+ onKeyDown: onRowKeyDown,
528
+ onMouseEnter: () => {
529
+ if (disabled) return;
530
+ tree.cancelClose();
531
+ if (isOpen) {
532
+ tree.closeBelow(depth + 1);
533
+ return;
534
+ }
535
+ clearTimeout(openTimer.current);
536
+ openTimer.current = setTimeout(() => openNow(false), SUBMENU_OPEN_DELAY);
537
+ },
538
+ onMouseLeave: () => {
539
+ clearTimeout(openTimer.current);
540
+ tree.scheduleClose(depth);
541
+ },
542
+ children: [
543
+ icon,
544
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: label }),
545
+ chevron
546
+ ]
547
+ }
548
+ ),
549
+ isOpen && createPortal(
550
+ /* @__PURE__ */ jsxs(
551
+ "div",
552
+ {
553
+ ref: panelRef,
554
+ id: panelId,
555
+ role: "menu",
556
+ "data-popup-menu-tree": tree.id,
557
+ "data-popup-submenu-panel": true,
558
+ className: `fixed rounded-2xl overflow-x-hidden overflow-y-auto overscroll-contain ${density === "tight" ? "py-1" : density === "large" ? "py-2" : "py-1.5"}`,
559
+ style: {
560
+ left: anchor.left,
561
+ top: anchor.y,
562
+ minWidth,
563
+ // The viewport cap always applies, so a long list scrolls rather
564
+ // than running off the screen even when no maxHeight is passed.
565
+ maxHeight: maxHeight === void 0 ? "calc(100vh - 16px)" : `min(${typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight}, calc(100vh - 16px))`,
566
+ zIndex: layerOf(tree.root.current) + depth + 1,
567
+ animation: `${anchor.flipped ? "popup-submenu-in-left" : "popup-submenu-in"} 0.1s ease-out`,
568
+ ...glassStyle()
569
+ },
570
+ onKeyDown: onPanelKeyDown,
571
+ onMouseEnter: () => {
572
+ tree.cancelClose();
573
+ clearTimeout(openTimer.current);
574
+ },
575
+ onMouseLeave: () => tree.scheduleClose(depth + 1),
576
+ onContextMenu: (e) => {
577
+ if (!keepsNativeMenu(e.target)) e.preventDefault();
578
+ },
579
+ children: [
580
+ /* @__PURE__ */ jsx(MenuTreeContext.Provider, { value: childTree, children }),
581
+ /* @__PURE__ */ jsx("style", { children: `@keyframes popup-submenu-in { from { opacity: 0; transform: translateX(-4px); } to { opacity: 1; transform: translateX(0); } }
582
+ @keyframes popup-submenu-in-left { from { opacity: 0; transform: translateX(4px); } to { opacity: 1; transform: translateX(0); } }` })
583
+ ]
584
+ }
585
+ ),
586
+ document.body
587
+ )
588
+ ] });
589
+ }
590
+ function PopupMenuDivider() {
591
+ const density = getDensity();
592
+ return /* @__PURE__ */ jsx("div", { className: `border-t ${GLASS_DIVIDER} ${density === "tight" ? "my-0.5" : density === "large" ? "my-1.5" : "my-1"} mx-3` });
593
+ }
594
+ function PopupMenuLabel({ children }) {
595
+ const density = getDensity();
596
+ const labelPadY = density === "tight" ? "0.125rem" : density === "large" ? "0.375rem" : "0.25rem";
597
+ return /* @__PURE__ */ jsx(
598
+ "div",
599
+ {
600
+ className: "text-[10px] font-medium text-gray-400 uppercase tracking-wider",
601
+ style: {
602
+ paddingLeft: "var(--menu-padding-x, 1rem)",
603
+ paddingRight: "var(--menu-padding-x, 1rem)",
604
+ paddingTop: labelPadY,
605
+ paddingBottom: labelPadY
606
+ },
607
+ children
608
+ }
609
+ );
610
+ }
611
+ function MenuIcon({ d, className = "h-4 w-4 text-gray-400" }) {
612
+ return /* @__PURE__ */ jsx("svg", { className, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d }) });
613
+ }
614
+
615
+ export { DEFAULT_SHELL_STRINGS, MenuIcon, NATIVE_MENU_ATTR, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, PopupSubmenu, ShellStringsProvider, clampMenuTop, closeMenuBelow, describeContextTarget, keepsNativeMenu, menuPanelLeft, openMenuLevel, resolveMenuLevels, useShellStrings };
616
+ //# sourceMappingURL=chunk-7KIOEKHS.js.map
617
+ //# sourceMappingURL=chunk-7KIOEKHS.js.map