efficient-shell 4.137.0 → 4.137.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 (166) hide show
  1. package/LICENSE +6 -17
  2. package/README.md +3 -1
  3. package/dist/Browser-ZX5OTOKK.js +43 -0
  4. package/dist/Calculator-4SQGOQFQ.js +29 -0
  5. package/dist/ConfirmDialog-NPKW2XHM.js +1 -0
  6. package/dist/CurrencyConverter-2KOM7L6V.js +21 -0
  7. package/dist/Documents-IZTIQYPG.js +52 -0
  8. package/dist/Files-B63KKXGZ.js +89 -0
  9. package/dist/Notepad-WA77V45J.js +25 -0
  10. package/dist/PdfViewer-RBRGEKWF.js +1 -0
  11. package/dist/PomodoroTimer-SDUBKXUS.js +99 -0
  12. package/dist/Preview-D4KOA4OI.js +187 -0
  13. package/dist/Sidebar-Y57LYXXI.js +23 -0
  14. package/dist/Spreadsheet-JX5E6WH6.js +37 -0
  15. package/dist/Stock-XAQ5WMUZ.js +16 -0
  16. package/dist/Weather-HLAGJ5LV.js +60 -0
  17. package/dist/WorldClock-IRI4VCTA.js +32 -0
  18. package/dist/apps/index.js +1 -151
  19. package/dist/chunk-3A46NCXK.js +29 -0
  20. package/dist/chunk-44UO7FYU.js +11 -0
  21. package/dist/chunk-466CM2TJ.js +1 -0
  22. package/dist/chunk-57MIENLJ.js +1 -0
  23. package/dist/chunk-5CRAF2AD.js +2 -0
  24. package/dist/chunk-A2OQLLYK.js +6 -0
  25. package/dist/chunk-B43AGQET.js +4 -0
  26. package/dist/chunk-BR22X7E5.js +1 -0
  27. package/dist/chunk-BUTSU7FO.js +1 -0
  28. package/dist/chunk-D65T4H6J.js +1 -0
  29. package/dist/chunk-ESL5TDNA.js +1 -0
  30. package/dist/chunk-FBDZGO3V.js +1 -0
  31. package/dist/chunk-FHTLC7HH.js +1 -0
  32. package/dist/chunk-FQLMX3IE.js +948 -0
  33. package/dist/chunk-H562R4GX.js +34 -0
  34. package/dist/chunk-JN4WXTWJ.js +106 -0
  35. package/dist/chunk-LVDRBRYU.js +1 -0
  36. package/dist/chunk-LWLCNQY7.js +1 -0
  37. package/dist/chunk-MAIHBBWT.js +1 -0
  38. package/dist/chunk-NPJH5MD7.js +13 -0
  39. package/dist/chunk-OCSOMMY2.js +1 -0
  40. package/dist/chunk-OQDWLHZI.js +1 -0
  41. package/dist/chunk-OWSAMA3I.js +23 -0
  42. package/dist/chunk-PH3PRIFP.js +5 -0
  43. package/dist/chunk-TAWKWSPN.js +9 -0
  44. package/dist/chunk-U4GTZPXP.js +1 -0
  45. package/dist/chunk-UB4JIOLD.js +2 -0
  46. package/dist/chunk-UZAJ5BF6.js +9 -0
  47. package/dist/chunk-VEGMDXT4.js +1 -0
  48. package/dist/chunk-VTVSMRFX.js +1 -0
  49. package/dist/chunk-VVAGSKW7.js +1 -0
  50. package/dist/chunk-XO43FJRM.js +19 -0
  51. package/dist/chunk-Y7BOLDRM.js +69 -0
  52. package/dist/file-intake/index.js +1 -4
  53. package/dist/index.js +639 -7473
  54. package/dist/map/index.js +20 -540
  55. package/dist/markdown/index.js +6 -184
  56. package/dist/markup/index.js +1 -135
  57. package/dist/page-tabs/index.js +21 -709
  58. package/dist/sounds-OMHXVVQ4.js +1 -0
  59. package/dist/ui/index.js +1 -19
  60. package/package.json +4 -3
  61. package/dist/Browser-ZNX4WFUH.js +0 -482
  62. package/dist/Browser-ZNX4WFUH.js.map +0 -1
  63. package/dist/Calculator-6FGNKOXU.js +0 -192
  64. package/dist/Calculator-6FGNKOXU.js.map +0 -1
  65. package/dist/ConfirmDialog-NPQMIGUR.js +0 -4
  66. package/dist/ConfirmDialog-NPQMIGUR.js.map +0 -1
  67. package/dist/CurrencyConverter-FF7EXRYL.js +0 -236
  68. package/dist/CurrencyConverter-FF7EXRYL.js.map +0 -1
  69. package/dist/Documents-H3R7XSBH.js +0 -441
  70. package/dist/Documents-H3R7XSBH.js.map +0 -1
  71. package/dist/Files-ZHJIAUC2.js +0 -805
  72. package/dist/Files-ZHJIAUC2.js.map +0 -1
  73. package/dist/Notepad-VE7ISWRQ.js +0 -410
  74. package/dist/Notepad-VE7ISWRQ.js.map +0 -1
  75. package/dist/PdfViewer-SQBFYAGW.js +0 -5
  76. package/dist/PdfViewer-SQBFYAGW.js.map +0 -1
  77. package/dist/PomodoroTimer-IR3WREED.js +0 -633
  78. package/dist/PomodoroTimer-IR3WREED.js.map +0 -1
  79. package/dist/Preview-DAQGRNRM.js +0 -2959
  80. package/dist/Preview-DAQGRNRM.js.map +0 -1
  81. package/dist/Sidebar-YW6H4NDJ.js +0 -291
  82. package/dist/Sidebar-YW6H4NDJ.js.map +0 -1
  83. package/dist/Spreadsheet-HOCPHJOC.js +0 -519
  84. package/dist/Spreadsheet-HOCPHJOC.js.map +0 -1
  85. package/dist/Stock-LVRQPURG.js +0 -158
  86. package/dist/Stock-LVRQPURG.js.map +0 -1
  87. package/dist/Weather-LZQGVDJL.js +0 -431
  88. package/dist/Weather-LZQGVDJL.js.map +0 -1
  89. package/dist/WorldClock-XPD7WOZT.js +0 -202
  90. package/dist/WorldClock-XPD7WOZT.js.map +0 -1
  91. package/dist/apps/index.js.map +0 -1
  92. package/dist/chunk-5O42JXQE.js +0 -26
  93. package/dist/chunk-5O42JXQE.js.map +0 -1
  94. package/dist/chunk-5RPTS3BT.js +0 -70
  95. package/dist/chunk-5RPTS3BT.js.map +0 -1
  96. package/dist/chunk-6XJEJWNJ.js +0 -61
  97. package/dist/chunk-6XJEJWNJ.js.map +0 -1
  98. package/dist/chunk-7CCHEEYC.js +0 -94
  99. package/dist/chunk-7CCHEEYC.js.map +0 -1
  100. package/dist/chunk-BE7FZOFU.js +0 -17
  101. package/dist/chunk-BE7FZOFU.js.map +0 -1
  102. package/dist/chunk-COWYSHG2.js +0 -79
  103. package/dist/chunk-COWYSHG2.js.map +0 -1
  104. package/dist/chunk-D7PYW2QS.js +0 -265
  105. package/dist/chunk-D7PYW2QS.js.map +0 -1
  106. package/dist/chunk-DVZWYWSD.js +0 -26
  107. package/dist/chunk-DVZWYWSD.js.map +0 -1
  108. package/dist/chunk-E7TB6WD2.js +0 -153
  109. package/dist/chunk-E7TB6WD2.js.map +0 -1
  110. package/dist/chunk-FIHOGVUP.js +0 -416
  111. package/dist/chunk-FIHOGVUP.js.map +0 -1
  112. package/dist/chunk-GA35NEND.js +0 -56
  113. package/dist/chunk-GA35NEND.js.map +0 -1
  114. package/dist/chunk-HUBJAUGG.js +0 -316
  115. package/dist/chunk-HUBJAUGG.js.map +0 -1
  116. package/dist/chunk-I2TAAJHS.js +0 -465
  117. package/dist/chunk-I2TAAJHS.js.map +0 -1
  118. package/dist/chunk-ICTRCKUE.js +0 -3710
  119. package/dist/chunk-ICTRCKUE.js.map +0 -1
  120. package/dist/chunk-IPXN646P.js +0 -14
  121. package/dist/chunk-IPXN646P.js.map +0 -1
  122. package/dist/chunk-IRWSRJ4K.js +0 -48
  123. package/dist/chunk-IRWSRJ4K.js.map +0 -1
  124. package/dist/chunk-JOPU6WPY.js +0 -504
  125. package/dist/chunk-JOPU6WPY.js.map +0 -1
  126. package/dist/chunk-MGGY74RY.js +0 -14
  127. package/dist/chunk-MGGY74RY.js.map +0 -1
  128. package/dist/chunk-PVPPAWVM.js +0 -46
  129. package/dist/chunk-PVPPAWVM.js.map +0 -1
  130. package/dist/chunk-R7QGMK6N.js +0 -102
  131. package/dist/chunk-R7QGMK6N.js.map +0 -1
  132. package/dist/chunk-RT6DSB5Y.js +0 -143
  133. package/dist/chunk-RT6DSB5Y.js.map +0 -1
  134. package/dist/chunk-TRCWINHF.js +0 -105
  135. package/dist/chunk-TRCWINHF.js.map +0 -1
  136. package/dist/chunk-TW7LPMVR.js +0 -16
  137. package/dist/chunk-TW7LPMVR.js.map +0 -1
  138. package/dist/chunk-UTYKLGGT.js +0 -195
  139. package/dist/chunk-UTYKLGGT.js.map +0 -1
  140. package/dist/chunk-VNJJCH5A.js +0 -822
  141. package/dist/chunk-VNJJCH5A.js.map +0 -1
  142. package/dist/chunk-XJHZWSKV.js +0 -1103
  143. package/dist/chunk-XJHZWSKV.js.map +0 -1
  144. package/dist/chunk-Y3GYJ7YR.js +0 -549
  145. package/dist/chunk-Y3GYJ7YR.js.map +0 -1
  146. package/dist/chunk-YA4EE7ST.js +0 -14107
  147. package/dist/chunk-YA4EE7ST.js.map +0 -1
  148. package/dist/chunk-YDHXWCCJ.js +0 -145
  149. package/dist/chunk-YDHXWCCJ.js.map +0 -1
  150. package/dist/chunk-YE6GUDGP.js +0 -548
  151. package/dist/chunk-YE6GUDGP.js.map +0 -1
  152. package/dist/chunk-YNKK7NE3.js +0 -171
  153. package/dist/chunk-YNKK7NE3.js.map +0 -1
  154. package/dist/chunk-YPO6RDMX.js +0 -20
  155. package/dist/chunk-YPO6RDMX.js.map +0 -1
  156. package/dist/chunk-ZS7PMGUW.js +0 -7
  157. package/dist/chunk-ZS7PMGUW.js.map +0 -1
  158. package/dist/file-intake/index.js.map +0 -1
  159. package/dist/index.js.map +0 -1
  160. package/dist/map/index.js.map +0 -1
  161. package/dist/markdown/index.js.map +0 -1
  162. package/dist/markup/index.js.map +0 -1
  163. package/dist/page-tabs/index.js.map +0 -1
  164. package/dist/sounds-NT4DEZGD.js +0 -3
  165. package/dist/sounds-NT4DEZGD.js.map +0 -1
  166. package/dist/ui/index.js.map +0 -1
@@ -1,710 +1,22 @@
1
1
  "use client";
2
- import { PopupMenu, PopupMenuItem, PopupMenuDivider } from '../chunk-JOPU6WPY.js';
3
- import { confirm } from '../chunk-I2TAAJHS.js';
4
- import '../chunk-6XJEJWNJ.js';
5
- import { useShellStrings } from '../chunk-YE6GUDGP.js';
6
- import '../chunk-IPXN646P.js';
7
- import { useBrowserLayoutEffect } from '../chunk-IRWSRJ4K.js';
8
- import * as ReactModule from 'react';
9
- import { createContext, memo, useRef, useEffect, useMemo, useId, useCallback, useReducer, useContext, useState, Fragment as Fragment$1 } from 'react';
10
- import { useLocation, useNavigate } from 'react-router-dom';
11
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
12
-
13
- var PageTabsContext = createContext(null);
14
- var PageTabFrameContext = createContext(null);
15
-
16
- // src/page-tabs/tabsModel.ts
17
- var DEFAULT_MAX_TABS = 12;
18
- function emptyState() {
19
- return { tabs: [], activeKey: null, mounted: [], dirty: [], closing: null, clock: 0 };
20
- }
21
- var without = (list, key) => list.filter((k) => k !== key);
22
- var withKey = (list, key) => list.includes(key) ? list : [...list, key];
23
- function orderPinned(tabs, pinnedKeys) {
24
- const pinned = pinnedKeys.map((key) => tabs.find((t) => t.key === key)).filter((t) => Boolean(t));
25
- return [...pinned, ...tabs.filter((t) => !pinnedKeys.includes(t.key))];
26
- }
27
- function visit(state, entry, maxTabs = DEFAULT_MAX_TABS) {
28
- const clock = state.clock + 1;
29
- const existing = state.tabs.find((t) => t.key === entry.key);
30
- let tabs;
31
- if (existing) {
32
- tabs = state.tabs.map(
33
- (t) => t.key === entry.key ? {
34
- ...t,
35
- path: entry.path,
36
- title: t.titleFrom === "page" ? t.title : entry.title || t.title,
37
- lastActive: clock
38
- } : t
39
- );
40
- } else {
41
- tabs = [
42
- ...state.tabs,
43
- { key: entry.key, path: entry.path, title: entry.title, titleFrom: "route", pinned: false, lastActive: clock }
44
- ];
45
- }
46
- let next = {
47
- ...state,
48
- tabs,
49
- activeKey: entry.key,
50
- mounted: withKey(state.mounted, entry.key),
51
- clock
52
- };
53
- if (next.closing && next.closing !== entry.key) next = removeTab(next, next.closing);
54
- else if (next.closing === entry.key) next = { ...next, closing: null };
55
- return evictOverflow(next, maxTabs);
56
- }
57
- function visitTransient(state) {
58
- let next = { ...state, activeKey: null };
59
- if (next.closing) next = removeTab(next, next.closing);
60
- return next;
61
- }
62
- function evictOverflow(state, maxTabs) {
63
- let next = state;
64
- while (next.tabs.length > maxTabs) {
65
- const candidates = next.tabs.filter((t) => !t.pinned && t.key !== next.activeKey && !next.dirty.includes(t.key) && t.key !== next.closing).sort((a, b) => a.lastActive - b.lastActive);
66
- if (candidates.length === 0) break;
67
- next = removeTab(next, candidates[0].key);
68
- }
69
- return next;
70
- }
71
- function removeTab(state, key) {
72
- return {
73
- ...state,
74
- tabs: state.tabs.filter((t) => t.key !== key),
75
- mounted: without(state.mounted, key),
76
- dirty: without(state.dirty, key),
77
- activeKey: state.activeKey === key ? null : state.activeKey,
78
- closing: state.closing === key ? null : state.closing
79
- };
80
- }
81
- function neighbourOf(state, key) {
82
- const visible = state.tabs.filter((t) => t.key !== state.closing || t.key === key);
83
- const i = visible.findIndex((t) => t.key === key);
84
- if (i < 0) return null;
85
- return visible[i + 1] ?? visible[i - 1] ?? null;
86
- }
87
- function beginClose(state, key) {
88
- return { ...state, closing: key };
89
- }
90
- function keysToClose(state, scope, key) {
91
- const closable = state.tabs.filter((t) => !t.pinned);
92
- if (scope === "all") return closable.map((t) => t.key);
93
- if (scope === "others") return closable.filter((t) => t.key !== key).map((t) => t.key);
94
- const i = state.tabs.findIndex((t) => t.key === key);
95
- if (i < 0) return [];
96
- return state.tabs.slice(i + 1).filter((t) => !t.pinned).map((t) => t.key);
97
- }
98
- function setTitle(state, key, title) {
99
- const tab = state.tabs.find((t) => t.key === key);
100
- if (!tab || !title || tab.title === title && tab.titleFrom === "page") return state;
101
- return {
102
- ...state,
103
- tabs: state.tabs.map((t) => t.key === key ? { ...t, title, titleFrom: "page" } : t)
104
- };
105
- }
106
- function setDirty(state, key, dirty) {
107
- const has = state.dirty.includes(key);
108
- if (dirty === has) return state;
109
- return { ...state, dirty: dirty ? [...state.dirty, key] : without(state.dirty, key) };
110
- }
111
- function ensurePinned(state, pinned) {
112
- const pinnedKeys = pinned.map((p) => p.key);
113
- let tabs = state.tabs.map((t) => ({ ...t, pinned: pinnedKeys.includes(t.key) }));
114
- for (const p of pinned) {
115
- if (!tabs.some((t) => t.key === p.key)) {
116
- tabs = [...tabs, { key: p.key, path: p.path, title: p.title, titleFrom: "route", pinned: true, lastActive: 0 }];
117
- }
118
- }
119
- return { ...state, tabs: orderPinned(tabs, pinnedKeys) };
120
- }
121
- function serialize(state) {
122
- const stored = {
123
- v: 1,
124
- tabs: state.tabs.filter((t) => t.key !== state.closing).map(({ key, path, title, titleFrom }) => ({ key, path, title, titleFrom }))
125
- };
126
- return JSON.stringify(stored);
127
- }
128
- function restore(raw, resolve) {
129
- if (!raw) return [];
130
- let parsed;
131
- try {
132
- parsed = JSON.parse(raw);
133
- } catch {
134
- return [];
135
- }
136
- const stored = parsed;
137
- if (!stored || stored.v !== 1 || !Array.isArray(stored.tabs)) return [];
138
- const out = [];
139
- for (const item of stored.tabs) {
140
- if (!item || typeof item.path !== "string" || !item.path.startsWith("/") || item.path.startsWith("//")) continue;
141
- const hashAt = item.path.indexOf("#");
142
- const path = hashAt >= 0 ? item.path.slice(0, hashAt) : item.path;
143
- const entry = resolve(path);
144
- if (!entry || out.some((t) => t.key === entry.key)) continue;
145
- const pageTitle = item.titleFrom === "page" && typeof item.title === "string" && item.title;
146
- out.push({
147
- key: entry.key,
148
- path,
149
- title: pageTitle || entry.title,
150
- titleFrom: pageTitle ? "page" : "route",
151
- pinned: false,
152
- lastActive: 0
153
- });
154
- }
155
- return out;
156
- }
157
- function reducer(state, action) {
158
- switch (action.type) {
159
- case "visit":
160
- return visit(state, action.entry, action.maxTabs);
161
- case "transient":
162
- return visitTransient(state);
163
- case "close": {
164
- let next = state;
165
- for (const key of action.keys) next = removeTab(next, key);
166
- return action.closing ? beginClose(next, action.closing) : next;
167
- }
168
- case "title":
169
- return setTitle(state, action.key, action.title);
170
- case "dirty":
171
- return setDirty(state, action.key, action.dirty);
172
- }
173
- }
174
- var pathOf = (location) => `${location.pathname}${location.search}`;
175
- function parseTabPath(path) {
176
- const hashAt = path.indexOf("#");
177
- const hash = hashAt >= 0 ? path.slice(hashAt) : "";
178
- const rest = hashAt >= 0 ? path.slice(0, hashAt) : path;
179
- const searchAt = rest.indexOf("?");
180
- return {
181
- pathname: searchAt >= 0 ? rest.slice(0, searchAt) : rest,
182
- search: searchAt >= 0 ? rest.slice(searchAt) : "",
183
- hash
184
- };
185
- }
186
- function readStorage(key) {
187
- if (!key) return null;
188
- try {
189
- return localStorage.getItem(key);
190
- } catch {
191
- return null;
192
- }
193
- }
194
- function PageTabsProvider({
195
- children,
196
- resolve,
197
- pinned = [],
198
- storageKey,
199
- maxTabs = DEFAULT_MAX_TABS,
200
- enabled = true,
201
- confirmDiscard
202
- }) {
203
- const location = useLocation();
204
- const navigate = useNavigate();
205
- const strings = useShellStrings().pageTabs;
206
- const idPrefix = useId();
207
- const resolveRef = useRef(resolve);
208
- resolveRef.current = resolve;
209
- const entryFor = useCallback((loc) => {
210
- const route = resolveRef.current(loc);
211
- if (!route) return null;
212
- return { key: route.key ?? loc.pathname, path: pathOf(loc), title: route.title };
213
- }, []);
214
- const [state, dispatch] = useReducer(reducer, void 0, () => {
215
- const restored = restore(readStorage(storageKey), (path) => entryFor(parseTabPath(path)));
216
- const pinnedEntries = pinned.map((path) => entryFor(parseTabPath(path))).filter((e) => Boolean(e));
217
- let initial = ensurePinned({ ...emptyState(), tabs: restored }, pinnedEntries);
218
- const entry = enabled ? entryFor(location) : null;
219
- initial = entry ? visit(initial, entry, maxTabs) : visitTransient(initial);
220
- return initial;
221
- });
222
- const stateRef = useRef(state);
223
- stateRef.current = state;
224
- const locations = useRef(/* @__PURE__ */ new Map());
225
- const current = enabled ? entryFor(location) : null;
226
- const currentKey = current?.key ?? null;
227
- useBrowserLayoutEffect(() => {
228
- if (!enabled) return;
229
- const entry = entryFor(location);
230
- if (entry) {
231
- locations.current.set(entry.key, location);
232
- dispatch({ type: "visit", entry, maxTabs });
233
- } else {
234
- dispatch({ type: "transient" });
235
- }
236
- }, [location, enabled, maxTabs, entryFor]);
237
- useEffect(() => {
238
- const open = new Set(state.tabs.map((t) => t.key));
239
- for (const key of locations.current.keys()) if (!open.has(key)) locations.current.delete(key);
240
- }, [state.tabs]);
241
- const serialized = useMemo(() => serialize(state), [state]);
242
- useEffect(() => {
243
- if (!storageKey) return;
244
- try {
245
- localStorage.setItem(storageKey, serialized);
246
- } catch {
247
- }
248
- }, [serialized, storageKey]);
249
- const anyDirty = state.dirty.length > 0;
250
- useEffect(() => {
251
- if (!anyDirty) return;
252
- const hold = (e) => {
253
- e.preventDefault();
254
- e.returnValue = "";
255
- };
256
- window.addEventListener("beforeunload", hold);
257
- return () => window.removeEventListener("beforeunload", hold);
258
- }, [anyDirty]);
259
- const ids = useRef(/* @__PURE__ */ new Map());
260
- const idFor = useCallback(
261
- (key, part) => {
262
- let n = ids.current.get(key);
263
- if (n === void 0) {
264
- n = ids.current.size;
265
- ids.current.set(key, n);
266
- }
267
- return `${idPrefix}-page-${part}-${n}`;
268
- },
269
- [idPrefix]
270
- );
271
- const askDiscard = useCallback(
272
- (tabs) => {
273
- if (confirmDiscard) return confirmDiscard(tabs);
274
- return confirm({
275
- title: strings.discardTitle,
276
- message: tabs.length === 1 ? strings.discardOne : strings.discardCount?.(tabs.length) ?? `${tabs.length} ${strings.discardMany}`,
277
- confirmLabel: strings.discardConfirm,
278
- cancelLabel: strings.keepEditing,
279
- variant: "warning"
280
- });
281
- },
282
- [confirmDiscard, strings]
283
- );
284
- const fallbackPath = useCallback(() => {
285
- const s = stateRef.current;
286
- return s.tabs.find((t) => t.pinned)?.path ?? "/";
287
- }, []);
288
- const closeNow = useCallback(
289
- (keys, to, prefer) => {
290
- const s = stateRef.current;
291
- const active = s.activeKey;
292
- if (!active || !keys.includes(active)) {
293
- dispatch({ type: "close", keys, closing: null });
294
- return;
295
- }
296
- let target = to;
297
- if (!target) {
298
- const survivors = { ...s, tabs: s.tabs.filter((t) => t.key === active || !keys.includes(t.key)) };
299
- const preferred = prefer ? s.tabs.find((t) => t.key === prefer && !keys.includes(t.key)) : void 0;
300
- target = (preferred ?? neighbourOf(survivors, active))?.path ?? fallbackPath();
301
- }
302
- dispatch({ type: "close", keys: keys.filter((k) => k !== active), closing: active });
303
- navigate(target);
304
- },
305
- [navigate, fallbackPath]
306
- );
307
- const close = useCallback(
308
- async (key, options = {}) => {
309
- const tab = stateRef.current.tabs.find((t) => t.key === key);
310
- if (!tab || tab.pinned) {
311
- if (options.to) navigate(options.to);
312
- return false;
313
- }
314
- if (!options.force && stateRef.current.dirty.includes(key) && !await askDiscard([tab])) return false;
315
- if (!stateRef.current.tabs.some((t) => t.key === key)) return false;
316
- closeNow([key], options.to);
317
- return true;
318
- },
319
- [askDiscard, closeNow, navigate]
320
- );
321
- const closeMany = useCallback(
322
- async (scope, key) => {
323
- let keys = keysToClose(stateRef.current, scope, key);
324
- const dirty = stateRef.current.tabs.filter(
325
- (t) => keys.includes(t.key) && stateRef.current.dirty.includes(t.key)
326
- );
327
- if (dirty.length && !await askDiscard(dirty)) {
328
- keys = keys.filter((k) => !dirty.some((t) => t.key === k));
329
- }
330
- const open = new Set(stateRef.current.tabs.map((t) => t.key));
331
- keys = keys.filter((k) => open.has(k));
332
- if (keys.length) closeNow(keys, void 0, scope === "all" ? null : key);
333
- },
334
- [askDiscard, closeNow]
335
- );
336
- const activate = useCallback(
337
- (key) => {
338
- const tab = stateRef.current.tabs.find((t) => t.key === key);
339
- if (!tab || key === stateRef.current.activeKey) return;
340
- navigate(tab.path);
341
- },
342
- [navigate]
343
- );
344
- const setTitle2 = useCallback((key, title) => dispatch({ type: "title", key, title }), []);
345
- const setDirty2 = useCallback((key, dirty) => dispatch({ type: "dirty", key, dirty }), []);
346
- const keyFor = useCallback((loc) => entryFor(loc)?.key ?? null, [entryFor]);
347
- const locationFor = useCallback((key) => locations.current.get(key), []);
348
- const visibleTabs = useMemo(
349
- () => enabled ? state.tabs.filter((t) => t.key !== state.closing) : [],
350
- [enabled, state.tabs, state.closing]
351
- );
352
- const value = useMemo(
353
- () => ({
354
- enabled,
355
- state,
356
- currentKey,
357
- resolve,
358
- keyFor,
359
- locationFor,
360
- idFor,
361
- activate,
362
- close,
363
- closeMany,
364
- setTitle: setTitle2,
365
- setDirty: setDirty2,
366
- visibleTabs
367
- }),
368
- [enabled, state, currentKey, resolve, keyFor, locationFor, idFor, activate, close, closeMany, setTitle2, setDirty2, visibleTabs]
369
- );
370
- return /* @__PURE__ */ jsx(PageTabsContext.Provider, { value, children });
371
- }
372
- var Activity2 = ReactModule.Activity;
373
- var PAGE_TABS_PAUSE_HIDDEN = Boolean(Activity2);
374
- function KeepAlive({ active, children }) {
375
- if (Activity2) return /* @__PURE__ */ jsx(Activity2, { mode: active ? "visible" : "hidden", children });
376
- return /* @__PURE__ */ jsx(Fragment, { children });
377
- }
378
- var sameFrame = (a, b) => a.active === b.active && a.location === b.location && a.className === b.className && a.panelId === b.panelId && a.tabId === b.tabId && (!b.active || a.render === b.render);
379
- var TabFrame = memo(function TabFrame2({ tabKey, active, location, render, className, panelId, tabId }) {
380
- const ref = useRef(null);
381
- const activeRef = useRef(active);
382
- const scrolls = useRef(/* @__PURE__ */ new Map());
383
- useBrowserLayoutEffect(() => {
384
- const wasActive = activeRef.current;
385
- activeRef.current = active;
386
- if (!active || wasActive) return;
387
- for (const [el, [top, left]] of scrolls.current) {
388
- if (!el.isConnected) {
389
- scrolls.current.delete(el);
390
- continue;
391
- }
392
- el.scrollTop = top;
393
- el.scrollLeft = left;
394
- }
395
- }, [active]);
396
- useEffect(() => {
397
- const el = ref.current;
398
- if (!el) return;
399
- const onScroll = (e) => {
400
- if (!activeRef.current) return;
401
- const target = e.target;
402
- if (!target || typeof target.scrollTop !== "number") return;
403
- scrolls.current.set(target, [target.scrollTop, target.scrollLeft]);
404
- };
405
- el.addEventListener("scroll", onScroll, true);
406
- return () => el.removeEventListener("scroll", onScroll, true);
407
- }, []);
408
- const frame = useMemo(() => ({ key: tabKey, activeRef }), [tabKey]);
409
- return /* @__PURE__ */ jsx(
410
- "div",
411
- {
412
- ref,
413
- role: panelId ? "tabpanel" : void 0,
414
- id: panelId,
415
- "aria-labelledby": tabId,
416
- hidden: !active,
417
- style: active ? void 0 : { display: "none" },
418
- className,
419
- "data-page-tab": tabKey,
420
- children: /* @__PURE__ */ jsx(PageTabFrameContext.Provider, { value: frame, children: /* @__PURE__ */ jsx(KeepAlive, { active, children: render(location) }) })
421
- }
422
- );
423
- }, sameFrame);
424
- var TRANSIENT = "\0transient";
425
- function PageTabsOutlet({ children, frameClassName }) {
426
- const ctx = useContext(PageTabsContext);
427
- const location = useLocation();
428
- if (!ctx || !ctx.enabled) {
429
- return /* @__PURE__ */ jsx("div", { className: frameClassName, children: children(location) }, TRANSIENT);
430
- }
431
- const { state, currentKey, locationFor, idFor } = ctx;
432
- const keys = state.tabs.filter((t) => state.mounted.includes(t.key)).map((t) => t.key);
433
- if (currentKey && !keys.includes(currentKey)) keys.push(currentKey);
434
- return /* @__PURE__ */ jsxs(Fragment, { children: [
435
- keys.map((key) => {
436
- const active = key === currentKey;
437
- const loc = active ? location : locationFor(key);
438
- if (!loc) return null;
439
- return /* @__PURE__ */ jsx(
440
- TabFrame,
441
- {
442
- tabKey: key,
443
- active,
444
- location: loc,
445
- render: children,
446
- className: frameClassName,
447
- panelId: idFor(key, "panel"),
448
- tabId: idFor(key, "tab")
449
- },
450
- key
451
- );
452
- }),
453
- !currentKey && /* @__PURE__ */ jsx(TabFrame, { tabKey: TRANSIENT, active: true, location, render: children, className: frameClassName }, TRANSIENT)
454
- ] });
455
- }
456
- var STRIP = "relative flex h-10 shrink-0 items-end gap-3 bg-gray-100 px-3";
457
- var TABLIST = "flex min-w-0 flex-1 items-end overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden";
458
- var TAB_BASE = "group @container relative flex h-9 flex-[0_1_15rem] items-center rounded-t-md border border-b-0 text-[13px] transition-colors";
459
- var TAB_FLOOR_IDLE = "min-w-[2.75rem]";
460
- var TAB_FLOOR_ACTIVE = "min-w-[4.25rem]";
461
- var TAB_PINNED = "group relative flex h-9 flex-none items-center justify-center rounded-t-md border border-b-0 transition-colors";
462
- var TAB_ACTIVE = "z-[1] border-gray-200 bg-gray-50 text-gray-900";
463
- var TAB_IDLE = "border-transparent text-gray-600 hover:bg-gray-200/70 hover:text-gray-900";
464
- var TITLE = "min-w-0 flex-1 overflow-hidden whitespace-nowrap [mask-image:linear-gradient(to_right,#000_calc(100%-1rem),transparent)]";
465
- var ICON_WRAP = "relative flex size-4 shrink-0 items-center justify-center text-gray-500 [&>svg]:size-4";
466
- var FOCUS_RING = "outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500";
467
- function CloseIcon() {
468
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", className: "size-3.5", children: /* @__PURE__ */ jsx("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) });
469
- }
470
- function PageTabBar({ className, activeClassName, end }) {
471
- const ctx = useContext(PageTabsContext);
472
- const strings = useShellStrings().pageTabs;
473
- const stripRef = useRef(null);
474
- const [menu, setMenu] = useState(null);
475
- const activeKey = ctx?.currentKey ?? null;
476
- useEffect(() => {
477
- if (!ctx || !activeKey) return;
478
- const el = document.getElementById(ctx.idFor(activeKey, "tab"));
479
- el?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
480
- }, [ctx, activeKey]);
481
- if (!ctx || !ctx.enabled) return null;
482
- const { visibleTabs: tabs, state, idFor, activate, close, closeMany, resolve } = ctx;
483
- const focusKey = tabs.some((t) => t.key === activeKey) ? activeKey : tabs[0]?.key;
484
- const focusTab = (index) => {
485
- const tab = tabs[(index + tabs.length) % tabs.length];
486
- if (tab) document.getElementById(idFor(tab.key, "tab"))?.focus();
487
- };
488
- const onKeyDown = (e) => {
489
- const i = tabs.findIndex((t) => document.getElementById(idFor(t.key, "tab")) === e.target);
490
- if (i < 0) return;
491
- if (e.key === "ArrowRight") focusTab(i + 1);
492
- else if (e.key === "ArrowLeft") focusTab(i - 1);
493
- else if (e.key === "Home") focusTab(0);
494
- else if (e.key === "End") focusTab(tabs.length - 1);
495
- else if (e.key === "Delete" && !tabs[i].pinned) void close(tabs[i].key);
496
- else return;
497
- e.preventDefault();
498
- };
499
- const onContextMenu = (e, tab) => {
500
- e.preventDefault();
501
- setMenu({ x: e.clientX, y: e.clientY, key: tab.key });
502
- };
503
- const onAuxClick = (e, tab) => {
504
- if (e.button !== 1 || tab.pinned) return;
505
- e.preventDefault();
506
- void close(tab.key);
507
- };
508
- const menuTab = menu ? tabs.find((t) => t.key === menu.key) : void 0;
509
- const menuIndex = menuTab ? tabs.indexOf(menuTab) : -1;
510
- const closable = (list) => list.some((t) => !t.pinned);
511
- const run = (fn) => () => {
512
- setMenu(null);
513
- void fn();
514
- };
515
- return /* @__PURE__ */ jsxs("div", { className: className ?? STRIP, children: [
516
- /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-0 bottom-0 h-px bg-gray-200" }),
517
- /* @__PURE__ */ jsx(
518
- "div",
519
- {
520
- ref: stripRef,
521
- role: "tablist",
522
- "aria-label": strings.label,
523
- onKeyDown,
524
- className: TABLIST,
525
- children: tabs.map((tab, i) => {
526
- const active = tab.key === activeKey;
527
- const dirty = state.dirty.includes(tab.key);
528
- const icon = resolve(parseTabPath(tab.path))?.icon;
529
- const colours = active ? activeClassName ?? TAB_ACTIVE : TAB_IDLE;
530
- const divider = i > 0 && !active && tabs[i - 1].key !== activeKey;
531
- const tabButton = {
532
- type: "button",
533
- role: "tab",
534
- id: idFor(tab.key, "tab"),
535
- "aria-selected": active,
536
- "aria-controls": state.mounted.includes(tab.key) ? idFor(tab.key, "panel") : void 0,
537
- tabIndex: tab.key === focusKey ? 0 : -1,
538
- title: tab.title,
539
- onClick: () => activate(tab.key)
540
- };
541
- const handlers = {
542
- onContextMenu: (e) => onContextMenu(e, tab),
543
- onAuxClick: (e) => onAuxClick(e, tab),
544
- // A middle-press would otherwise start the browser's autoscroll.
545
- onMouseDown: (e) => {
546
- if (e.button === 1) e.preventDefault();
547
- }
548
- };
549
- const iconEl = icon && // The consumer's nav icons, whatever size they were drawn at for
550
- // the sidebar, sit at the strip's size here.
551
- /* @__PURE__ */ jsxs("span", { className: ICON_WRAP, "aria-hidden": "true", children: [
552
- icon,
553
- dirty && !active && !tab.pinned && // Unsaved work on a tab too narrow for its × slot: the dot
554
- // moves onto the icon.
555
- /* @__PURE__ */ jsx("span", { className: "absolute -right-1 -top-1 hidden size-2 rounded-full bg-gray-500 ring-2 ring-gray-100 @max-[6.5rem]:block @max-[6.5rem]:group-hover:hidden" })
556
- ] });
557
- if (tab.pinned) {
558
- return /* @__PURE__ */ jsx("div", { role: "presentation", className: `${TAB_PINNED} ${icon ? "w-10" : ""} ${colours}`, ...handlers, children: /* @__PURE__ */ jsxs("button", { ...tabButton, className: `flex h-full items-center justify-center rounded-t-md ${icon ? "w-full" : "px-3"} ${FOCUS_RING}`, children: [
559
- iconEl,
560
- /* @__PURE__ */ jsx("span", { className: icon ? "sr-only" : "whitespace-nowrap text-[13px]", children: tab.title })
561
- ] }) }, tab.key);
562
- }
563
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
564
- divider && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "mb-2.5 h-4 w-px shrink-0 self-end border-l border-gray-300" }),
565
- /* @__PURE__ */ jsxs(
566
- "div",
567
- {
568
- role: "presentation",
569
- className: `${TAB_BASE} ${active ? TAB_FLOOR_ACTIVE : TAB_FLOOR_IDLE} ${colours}`,
570
- ...handlers,
571
- children: [
572
- /* @__PURE__ */ jsxs(
573
- "button",
574
- {
575
- ...tabButton,
576
- className: `flex h-full min-w-0 flex-1 items-center gap-2 rounded-t-md pl-3 pr-1 text-left ${FOCUS_RING} ${active ? "@max-[5rem]:pl-2.5" : "@max-[6.5rem]:pr-3 @max-[6.5rem]:group-hover:pr-1 @max-[4.5rem]:justify-center @max-[4.5rem]:px-0 @max-[4.5rem]:group-hover:pl-1.5"}`,
577
- children: [
578
- iconEl,
579
- /* @__PURE__ */ jsx("span", { className: `${TITLE} ${icon ? active ? "@max-[5rem]:sr-only" : "@max-[4.5rem]:sr-only" : ""}`, children: tab.title })
580
- ]
581
- }
582
- ),
583
- /* @__PURE__ */ jsxs(
584
- "span",
585
- {
586
- className: `mr-1.5 flex size-5 shrink-0 items-center justify-center ${active ? "" : "@max-[6.5rem]:hidden @max-[6.5rem]:group-hover:flex"}`,
587
- children: [
588
- dirty && /* @__PURE__ */ jsx(
589
- "span",
590
- {
591
- role: "img",
592
- "aria-label": strings.unsaved,
593
- className: "size-2 rounded-full bg-gray-500 group-hover:hidden"
594
- }
595
- ),
596
- /* @__PURE__ */ jsx(
597
- "button",
598
- {
599
- type: "button",
600
- tabIndex: -1,
601
- "aria-label": strings.closeTab?.(tab.title) ?? `${strings.close} ${tab.title}`,
602
- onClick: () => void close(tab.key),
603
- className: `size-5 items-center justify-center rounded text-gray-400 hover:bg-gray-200 hover:text-gray-700 ${dirty ? "hidden group-hover:flex" : active ? "flex" : "flex opacity-0 group-hover:opacity-100 focus-visible:opacity-100"}`,
604
- children: /* @__PURE__ */ jsx(CloseIcon, {})
605
- }
606
- )
607
- ]
608
- }
609
- )
610
- ]
611
- }
612
- )
613
- ] }, tab.key);
614
- })
615
- }
616
- ),
617
- end && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2 self-center", children: end }),
618
- menu && menuTab && /* @__PURE__ */ jsxs(PopupMenu, { portal: true, style: { left: menu.x, top: menu.y }, onClose: () => setMenu(null), children: [
619
- /* @__PURE__ */ jsx(PopupMenuItem, { disabled: menuTab.pinned, onClick: run(() => close(menuTab.key)), children: strings.close }),
620
- /* @__PURE__ */ jsx(PopupMenuDivider, {}),
621
- /* @__PURE__ */ jsx(
622
- PopupMenuItem,
623
- {
624
- disabled: !closable(tabs.filter((t) => t.key !== menuTab.key)),
625
- onClick: run(() => closeMany("others", menuTab.key)),
626
- children: strings.closeOthers
627
- }
628
- ),
629
- /* @__PURE__ */ jsx(
630
- PopupMenuItem,
631
- {
632
- disabled: !closable(tabs.slice(menuIndex + 1)),
633
- onClick: run(() => closeMany("right", menuTab.key)),
634
- children: strings.closeRight
635
- }
636
- ),
637
- /* @__PURE__ */ jsx(PopupMenuItem, { disabled: !closable(tabs), onClick: run(() => closeMany("all", null)), children: strings.closeAll })
638
- ] })
639
- ] });
640
- }
641
- var NO_TABS = {
642
- enabled: false,
643
- tabs: [],
644
- activeKey: null,
645
- activate: () => {
646
- },
647
- close: async () => false,
648
- closeOthers: async () => {
649
- },
650
- closeRight: async () => {
651
- },
652
- closeAll: async () => {
653
- }
654
- };
655
- function usePageTabs() {
656
- const ctx = useContext(PageTabsContext);
657
- if (!ctx || !ctx.enabled) return NO_TABS;
658
- return {
659
- enabled: true,
660
- tabs: ctx.visibleTabs,
661
- activeKey: ctx.currentKey,
662
- activate: ctx.activate,
663
- close: ctx.close,
664
- closeOthers: (key) => ctx.closeMany("others", key),
665
- closeRight: (key) => ctx.closeMany("right", key),
666
- closeAll: () => ctx.closeMany("all", null)
667
- };
668
- }
669
- function usePageTab() {
670
- const ctx = useContext(PageTabsContext);
671
- const frame = useContext(PageTabFrameContext);
672
- const navigate = useNavigate();
673
- const key = ctx?.enabled && frame ? frame.key : null;
674
- const close = useCallback(
675
- async (options = {}) => {
676
- if (ctx?.enabled && frame && ctx.state.tabs.some((t) => t.key === frame.key)) {
677
- return ctx.close(frame.key, options);
678
- }
679
- if (options.to) navigate(options.to);
680
- return false;
681
- },
682
- [ctx, frame, navigate]
683
- );
684
- return { key, close };
685
- }
686
- function usePageTabTitle(title) {
687
- const ctx = useContext(PageTabsContext);
688
- const frame = useContext(PageTabFrameContext);
689
- const setTitle2 = ctx?.enabled ? ctx.setTitle : void 0;
690
- const key = frame?.key;
691
- useEffect(() => {
692
- if (setTitle2 && key && title) setTitle2(key, title);
693
- }, [setTitle2, key, title]);
694
- }
695
- function usePageTabDirty(dirty) {
696
- const ctx = useContext(PageTabsContext);
697
- const frame = useContext(PageTabFrameContext);
698
- const setDirty2 = ctx?.enabled ? ctx.setDirty : void 0;
699
- useEffect(() => {
700
- if (!setDirty2 || !frame) return;
701
- setDirty2(frame.key, dirty);
702
- return () => {
703
- if (frame.activeRef.current) setDirty2(frame.key, false);
704
- };
705
- }, [setDirty2, frame, dirty]);
706
- }
707
-
708
- export { DEFAULT_MAX_TABS, PAGE_TABS_PAUSE_HIDDEN, PageTabBar, PageTabsOutlet, PageTabsProvider, parseTabPath, usePageTab, usePageTabDirty, usePageTabTitle, usePageTabs };
709
- //# sourceMappingURL=index.js.map
710
- //# sourceMappingURL=index.js.map
2
+ import{PopupMenu as e,PopupMenuItem as t,PopupMenuDivider as n}from"../chunk-UZAJ5BF6.js";import{confirm as r}from"../chunk-3A46NCXK.js";import"../chunk-57MIENLJ.js";import{useShellStrings as i}from"../chunk-OQDWLHZI.js";import"../chunk-MAIHBBWT.js";import{useBrowserLayoutEffect as l}from"../chunk-D65T4H6J.js";import*as a from"react";import{createContext as s,memo as o,useRef as c,useEffect as d,useMemo as u,useId as y,useCallback as f,useReducer as m,useContext as p,useState as h,Fragment as b}from"react";import{useLocation as k,useNavigate as v}from"react-router-dom";import{jsx as g,Fragment as x,jsxs as w}from"react/jsx-runtime";var N=s(null),I=s(null),K=12;var A=(e,t)=>e.filter(e=>e!==t);function F(e,t){return[...t.map(t=>e.find(e=>e.key===t)).filter(e=>Boolean(e)),...e.filter(e=>!t.includes(e.key))]}function T(e,t,n=12){const r=e.clock+1;let i;i=e.tabs.find(e=>e.key===t.key)?e.tabs.map(e=>e.key===t.key?{...e,path:t.path,title:"page"===e.titleFrom?e.title:t.title||e.title,lastActive:r}:e):[...e.tabs,{key:t.key,path:t.path,title:t.title,titleFrom:"route",pinned:!1,lastActive:r}];let l={...e,tabs:i,activeKey:t.key,mounted:(a=e.mounted,s=t.key,a.includes(s)?a:[...a,s]),clock:r};var a,s;return l.closing&&l.closing!==t.key?l=C(l,l.closing):l.closing===t.key&&(l={...l,closing:null}),function(e,t){let n=e;for(;n.tabs.length>t;){const e=n.tabs.filter(e=>!e.pinned&&e.key!==n.activeKey&&!n.dirty.includes(e.key)&&e.key!==n.closing).sort((e,t)=>e.lastActive-t.lastActive);if(0===e.length)break;n=C(n,e[0].key)}return n}(l,n)}function $(e){let t={...e,activeKey:null};return t.closing&&(t=C(t,t.closing)),t}function C(e,t){return{...e,tabs:e.tabs.filter(e=>e.key!==t),mounted:A(e.mounted,t),dirty:A(e.dirty,t),activeKey:e.activeKey===t?null:e.activeKey,closing:e.closing===t?null:e.closing}}function L(e,t){switch(t.type){case"visit":return T(e,t.entry,t.maxTabs);case"transient":return $(e);case"close":{let n=e;for(const e of t.keys)n=C(n,e);return t.closing?function(e,t){return{...e,closing:t}}(n,t.closing):n}case"title":return function(e,t,n){const r=e.tabs.find(e=>e.key===t);return!r||!n||r.title===n&&"page"===r.titleFrom?e:{...e,tabs:e.tabs.map(e=>e.key===t?{...e,title:n,titleFrom:"page"}:e)}}(e,t.key,t.title);case"dirty":return function(e,t,n){return n===e.dirty.includes(t)?e:{...e,dirty:n?[...e.dirty,t]:A(e.dirty,t)}}(e,t.key,t.dirty)}}var M=e=>`${e.pathname}${e.search}`;function j(e){const t=e.indexOf("#"),n=t>=0?e.slice(t):"",r=t>=0?e.slice(0,t):e,i=r.indexOf("?");return{pathname:i>=0?r.slice(0,i):r,search:i>=0?r.slice(i):"",hash:n}}function D({children:e,resolve:t,pinned:n=[],storageKey:a,maxTabs:s=12,enabled:o=!0,confirmDiscard:p}){const h=k(),b=v(),x=i().pageTabs,w=y(),I=c(t);I.current=t;const K=f(e=>{const t=I.current(e);return t?{key:t.key??e.pathname,path:M(e),title:t.title}:null},[]),[A,C]=m(L,void 0,()=>{const e=function(e,t){if(!e)return[];let n;try{n=JSON.parse(e)}catch{return[]}const r=n;if(!r||1!==r.v||!Array.isArray(r.tabs))return[];const i=[];for(const e of r.tabs){if(!e||"string"!=typeof e.path||!e.path.startsWith("/")||e.path.startsWith("//"))continue;const n=e.path.indexOf("#"),r=n>=0?e.path.slice(0,n):e.path,l=t(r);if(!l||i.some(e=>e.key===l.key))continue;const a="page"===e.titleFrom&&"string"==typeof e.title&&e.title;i.push({key:l.key,path:r,title:a||l.title,titleFrom:a?"page":"route",pinned:!1,lastActive:0})}return i}(function(e){if(!e)return null;try{return localStorage.getItem(e)}catch{return null}}(a),e=>K(j(e)));let t=function(e,t){const n=t.map(e=>e.key);let r=e.tabs.map(e=>({...e,pinned:n.includes(e.key)}));for(const e of t)r.some(t=>t.key===e.key)||(r=[...r,{key:e.key,path:e.path,title:e.title,titleFrom:"route",pinned:!0,lastActive:0}]);return{...e,tabs:F(r,n)}}({tabs:[],activeKey:null,mounted:[],dirty:[],closing:null,clock:0,tabs:e},n.map(e=>K(j(e))).filter(e=>Boolean(e)));const r=o?K(h):null;return t=r?T(t,r,s):$(t),t}),D=c(A);D.current=A;const O=c(/* @__PURE__ */new Map),z=o?K(h):null,B=z?.key??null;l(()=>{if(!o)return;const e=K(h);e?(O.current.set(e.key,h),C({type:"visit",entry:e,maxTabs:s})):C({type:"transient"})},[h,o,s,K]),d(()=>{const e=new Set(A.tabs.map(e=>e.key));for(const t of O.current.keys())e.has(t)||O.current.delete(t)},[A.tabs]);const E=u(()=>function(e){const t={v:1,tabs:e.tabs.filter(t=>t.key!==e.closing).map(({key:e,path:t,title:n,titleFrom:r})=>({key:e,path:t,title:n,titleFrom:r}))};return JSON.stringify(t)}(A),[A]);d(()=>{if(a)try{localStorage.setItem(a,E)}catch{}},[E,a]);const R=A.dirty.length>0;d(()=>{if(!R)return;const e=e=>{e.preventDefault(),e.returnValue=""};return window.addEventListener("beforeunload",e),()=>window.removeEventListener("beforeunload",e)},[R]);const S=c(/* @__PURE__ */new Map),J=f((e,t)=>{let n=S.current.get(e);return void 0===n&&(n=S.current.size,S.current.set(e,n)),`${w}-page-${t}-${n}`},[w]),H=f(e=>p?p(e):r({title:x.discardTitle,message:1===e.length?x.discardOne:x.discardCount?.(e.length)??`${e.length} ${x.discardMany}`,confirmLabel:x.discardConfirm,cancelLabel:x.keepEditing,variant:"warning"}),[p,x]),W=f(()=>{const e=D.current;return e.tabs.find(e=>e.pinned)?.path??"/"},[]),_=f((e,t,n)=>{const r=D.current,i=r.activeKey;if(!i||!e.includes(i))return void C({type:"close",keys:e,closing:null});let l=t;if(!l){const t={...r,tabs:r.tabs.filter(t=>t.key===i||!e.includes(t.key))},a=n?r.tabs.find(t=>t.key===n&&!e.includes(t.key)):void 0;l=(a??function(e,t){const n=e.tabs.filter(n=>n.key!==e.closing||n.key===t),r=n.findIndex(e=>e.key===t);return r<0?null:n[r+1]??n[r-1]??null}(t,i))?.path??W()}C({type:"close",keys:e.filter(e=>e!==i),closing:i}),b(l)},[b,W]),P=f(async(e,t={})=>{const n=D.current.tabs.find(t=>t.key===e);return!n||n.pinned?(t.to&&b(t.to),!1):!(!t.force&&D.current.dirty.includes(e)&&!await H([n]))&&(!!D.current.tabs.some(t=>t.key===e)&&(_([e],t.to),!0))},[H,_,b]),V=f(async(e,t)=>{let n=function(e,t,n){const r=e.tabs.filter(e=>!e.pinned);if("all"===t)return r.map(e=>e.key);if("others"===t)return r.filter(e=>e.key!==n).map(e=>e.key);const i=e.tabs.findIndex(e=>e.key===n);return i<0?[]:e.tabs.slice(i+1).filter(e=>!e.pinned).map(e=>e.key)}(D.current,e,t);const r=D.current.tabs.filter(e=>n.includes(e.key)&&D.current.dirty.includes(e.key));r.length&&!await H(r)&&(n=n.filter(e=>!r.some(t=>t.key===e)));const i=new Set(D.current.tabs.map(e=>e.key));n=n.filter(e=>i.has(e)),n.length&&_(n,void 0,"all"===e?null:t)},[H,_]),X=f(e=>{const t=D.current.tabs.find(t=>t.key===e);t&&e!==D.current.activeKey&&b(t.path)},[b]),Z=f((e,t)=>C({type:"title",key:e,title:t}),[]),Q=f((e,t)=>C({type:"dirty",key:e,dirty:t}),[]),U=f(e=>K(e)?.key??null,[K]),Y=f(e=>O.current.get(e),[]),q=u(()=>o?A.tabs.filter(e=>e.key!==A.closing):[],[o,A.tabs,A.closing]),G=u(()=>({enabled:o,state:A,currentKey:B,resolve:t,keyFor:U,locationFor:Y,idFor:J,activate:X,close:P,closeMany:V,setTitle:Z,setDirty:Q,visibleTabs:q}),[o,A,B,t,U,Y,J,X,P,V,Z,Q,q]);/* @__PURE__ */
3
+ return g(N.Provider,{value:G,children:e})}var O=a.Activity,z=Boolean(O);function B({active:e,children:t}){return O?/* @__PURE__ */g(O,{mode:e?"visible":"hidden",children:t}):/* @__PURE__ */g(x,{children:t})}var E=o(function({tabKey:e,active:t,location:n,render:r,className:i,panelId:a,tabId:s}){const o=c(null),y=c(t),f=c(/* @__PURE__ */new Map);l(()=>{const e=y.current;if(y.current=t,t&&!e)for(const[e,[t,n]]of f.current)e.isConnected?(e.scrollTop=t,e.scrollLeft=n):f.current.delete(e)},[t]),d(()=>{const e=o.current;if(!e)return;const t=e=>{if(!y.current)return;const t=e.target;t&&"number"==typeof t.scrollTop&&f.current.set(t,[t.scrollTop,t.scrollLeft])};return e.addEventListener("scroll",t,!0),()=>e.removeEventListener("scroll",t,!0)},[]);const m=u(()=>({key:e,activeRef:y}),[e]);/* @__PURE__ */
4
+ return g("div",{ref:o,role:a?"tabpanel":void 0,id:a,"aria-labelledby":s,hidden:!t,style:t?void 0:{display:"none"},className:i,"data-page-tab":e,children:/* @__PURE__ */g(I.Provider,{value:m,children:/* @__PURE__ */g(B,{active:t,children:r(n)})})})},(e,t)=>e.active===t.active&&e.location===t.location&&e.className===t.className&&e.panelId===t.panelId&&e.tabId===t.tabId&&(!t.active||e.render===t.render)),R="\0transient";function S({children:e,frameClassName:t}){const n=p(N),r=k();if(!n||!n.enabled)/* @__PURE__ */
5
+ return g("div",{className:t,children:e(r)},R);const{state:i,currentKey:l,locationFor:a,idFor:s}=n,o=i.tabs.filter(e=>i.mounted.includes(e.key)).map(e=>e.key);return l&&!o.includes(l)&&o.push(l),/* @__PURE__ */w(x,{children:[o.map(n=>{const i=n===l,o=i?r:a(n);return o?/* @__PURE__ */g(E,{tabKey:n,active:i,location:o,render:e,className:t,panelId:s(n,"panel"),tabId:s(n,"tab")},n):null}),!l&&/* @__PURE__ */g(E,{tabKey:R,active:!0,location:r,render:e,className:t},R)]})}var J="outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500";function H(){/* @__PURE__ */
6
+ return g("svg",{viewBox:"0 0 20 20",fill:"currentColor","aria-hidden":"true",className:"size-3.5",children:/* @__PURE__ */g("path",{d:"M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"})})}function W({className:r,activeClassName:l,end:a}){const s=p(N),o=i().pageTabs,u=c(null),[y,f]=h(null),m=s?.currentKey??null;if(d(()=>{if(!s||!m)return;const e=document.getElementById(s.idFor(m,"tab"));e?.scrollIntoView?.({block:"nearest",inline:"nearest"})},[s,m]),!s||!s.enabled)return null;const{visibleTabs:k,state:v,idFor:x,activate:I,close:K,closeMany:A,resolve:F}=s,T=k.some(e=>e.key===m)?m:k[0]?.key,$=e=>{const t=k[(e+k.length)%k.length];t&&document.getElementById(x(t.key,"tab"))?.focus()},C=y?k.find(e=>e.key===y.key):void 0,L=C?k.indexOf(C):-1,M=e=>e.some(e=>!e.pinned),D=e=>()=>{f(null),e()};/* @__PURE__ */
7
+ return w("div",{className:r??"relative flex h-10 shrink-0 items-end gap-3 bg-gray-100 px-3",children:[
8
+ /* @__PURE__ */g("div",{"aria-hidden":"true",className:"pointer-events-none absolute inset-x-0 bottom-0 h-px bg-gray-200"}),
9
+ /* @__PURE__ */g("div",{ref:u,role:"tablist","aria-label":o.label,onKeyDown:e=>{const t=k.findIndex(t=>document.getElementById(x(t.key,"tab"))===e.target);if(!(t<0)){if("ArrowRight"===e.key)$(t+1);else if("ArrowLeft"===e.key)$(t-1);else if("Home"===e.key)$(0);else if("End"===e.key)$(k.length-1);else{if("Delete"!==e.key||k[t].pinned)return;K(k[t].key)}e.preventDefault()}},className:"flex min-w-0 flex-1 items-end overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",children:k.map((e,t)=>{const n=e.key===m,r=v.dirty.includes(e.key),i=F(j(e.path))?.icon,a=n?l??"z-[1] border-gray-200 bg-gray-50 text-gray-900":"border-transparent text-gray-600 hover:bg-gray-200/70 hover:text-gray-900",s=t>0&&!n&&k[t-1].key!==m,c={type:"button",role:"tab",id:x(e.key,"tab"),"aria-selected":n,"aria-controls":v.mounted.includes(e.key)?x(e.key,"panel"):void 0,tabIndex:e.key===T?0:-1,title:e.title,onClick:()=>I(e.key)},d={onContextMenu:t=>((e,t)=>{e.preventDefault(),f({x:e.clientX,y:e.clientY,key:t.key})})(t,e),onAuxClick:t=>((e,t)=>{1!==e.button||t.pinned||(e.preventDefault(),K(t.key))})(t,e),onMouseDown:e=>{1===e.button&&e.preventDefault()}},u=i&&
10
+ /* @__PURE__ */w("span",{className:"relative flex size-4 shrink-0 items-center justify-center text-gray-500 [&>svg]:size-4","aria-hidden":"true",children:[i,r&&!n&&!e.pinned&&
11
+ /* @__PURE__ */g("span",{className:"absolute -right-1 -top-1 hidden size-2 rounded-full bg-gray-500 ring-2 ring-gray-100 @max-[6.5rem]:block @max-[6.5rem]:group-hover:hidden"})]});return e.pinned?/* @__PURE__ */g("div",{role:"presentation",className:`group relative flex h-9 flex-none items-center justify-center rounded-t-md border border-b-0 transition-colors ${i?"w-10":""} ${a}`,...d,children:/* @__PURE__ */w("button",{...c,className:`flex h-full items-center justify-center rounded-t-md ${i?"w-full":"px-3"} ${J}`,children:[u,
12
+ /* @__PURE__ */g("span",{className:i?"sr-only":"whitespace-nowrap text-[13px]",children:e.title})]})},e.key):/* @__PURE__ */w(b,{children:[s&&/* @__PURE__ */g("span",{"aria-hidden":"true",className:"mb-2.5 h-4 w-px shrink-0 self-end border-l border-gray-300"}),
13
+ /* @__PURE__ */w("div",{role:"presentation",className:`group @container relative flex h-9 flex-[0_1_15rem] items-center rounded-t-md border border-b-0 text-[13px] transition-colors ${n?"min-w-[4.25rem]":"min-w-[2.75rem]"} ${a}`,...d,children:[
14
+ /* @__PURE__ */w("button",{...c,className:`flex h-full min-w-0 flex-1 items-center gap-2 rounded-t-md pl-3 pr-1 text-left ${J} ${n?"@max-[5rem]:pl-2.5":"@max-[6.5rem]:pr-3 @max-[6.5rem]:group-hover:pr-1 @max-[4.5rem]:justify-center @max-[4.5rem]:px-0 @max-[4.5rem]:group-hover:pl-1.5"}`,children:[u,
15
+ /* @__PURE__ */g("span",{className:"min-w-0 flex-1 overflow-hidden whitespace-nowrap [mask-image:linear-gradient(to_right,#000_calc(100%-1rem),transparent)] "+(i?n?"@max-[5rem]:sr-only":"@max-[4.5rem]:sr-only":""),children:e.title})]}),
16
+ /* @__PURE__ */w("span",{className:"mr-1.5 flex size-5 shrink-0 items-center justify-center "+(n?"":"@max-[6.5rem]:hidden @max-[6.5rem]:group-hover:flex"),children:[r&&/* @__PURE__ */g("span",{role:"img","aria-label":o.unsaved,className:"size-2 rounded-full bg-gray-500 group-hover:hidden"}),
17
+ /* @__PURE__ */g("button",{type:"button",tabIndex:-1,"aria-label":o.closeTab?.(e.title)??`${o.close} ${e.title}`,onClick:()=>{K(e.key)},className:"size-5 items-center justify-center rounded text-gray-400 hover:bg-gray-200 hover:text-gray-700 "+(r?"hidden group-hover:flex":n?"flex":"flex opacity-0 group-hover:opacity-100 focus-visible:opacity-100"),children:/* @__PURE__ */g(H,{})})]})]})]},e.key)})}),a&&/* @__PURE__ */g("div",{className:"flex shrink-0 items-center gap-2 self-center",children:a}),y&&C&&/* @__PURE__ */w(e,{portal:!0,style:{left:y.x,top:y.y},onClose:()=>f(null),children:[
18
+ /* @__PURE__ */g(t,{disabled:C.pinned,onClick:D(()=>K(C.key)),children:o.close}),
19
+ /* @__PURE__ */g(n,{}),
20
+ /* @__PURE__ */g(t,{disabled:!M(k.filter(e=>e.key!==C.key)),onClick:D(()=>A("others",C.key)),children:o.closeOthers}),
21
+ /* @__PURE__ */g(t,{disabled:!M(k.slice(L+1)),onClick:D(()=>A("right",C.key)),children:o.closeRight}),
22
+ /* @__PURE__ */g(t,{disabled:!M(k),onClick:D(()=>A("all",null)),children:o.closeAll})]})]})}var _={enabled:!1,tabs:[],activeKey:null,activate:()=>{},close:async()=>!1,closeOthers:async()=>{},closeRight:async()=>{},closeAll:async()=>{}};function P(){const e=p(N);return e&&e.enabled?{enabled:!0,tabs:e.visibleTabs,activeKey:e.currentKey,activate:e.activate,close:e.close,closeOthers:t=>e.closeMany("others",t),closeRight:t=>e.closeMany("right",t),closeAll:()=>e.closeMany("all",null)}:_}function V(){const e=p(N),t=p(I),n=v();return{key:e?.enabled&&t?t.key:null,close:f(async(r={})=>e?.enabled&&t&&e.state.tabs.some(e=>e.key===t.key)?e.close(t.key,r):(r.to&&n(r.to),!1),[e,t,n])}}function X(e){const t=p(N),n=p(I),r=t?.enabled?t.setTitle:void 0,i=n?.key;d(()=>{r&&i&&e&&r(i,e)},[r,i,e])}function Z(e){const t=p(N),n=p(I),r=t?.enabled?t.setDirty:void 0;d(()=>{if(r&&n)return r(n.key,e),()=>{n.activeRef.current&&r(n.key,!1)}},[r,n,e])}export{K as DEFAULT_MAX_TABS,z as PAGE_TABS_PAUSE_HIDDEN,W as PageTabBar,S as PageTabsOutlet,D as PageTabsProvider,j as parseTabPath,V as usePageTab,Z as usePageTabDirty,X as usePageTabTitle,P as usePageTabs};