efficient-shell 4.134.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 (171) hide show
  1. package/LICENSE +6 -17
  2. package/README.md +84 -8
  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/brand.css +1 -1
  20. package/dist/chunk-3A46NCXK.js +29 -0
  21. package/dist/chunk-44UO7FYU.js +11 -0
  22. package/dist/chunk-466CM2TJ.js +1 -0
  23. package/dist/chunk-57MIENLJ.js +1 -0
  24. package/dist/chunk-5CRAF2AD.js +2 -0
  25. package/dist/chunk-A2OQLLYK.js +6 -0
  26. package/dist/chunk-B43AGQET.js +4 -0
  27. package/dist/chunk-BR22X7E5.js +1 -0
  28. package/dist/chunk-BUTSU7FO.js +1 -0
  29. package/dist/chunk-D65T4H6J.js +1 -0
  30. package/dist/chunk-ESL5TDNA.js +1 -0
  31. package/dist/chunk-FBDZGO3V.js +1 -0
  32. package/dist/chunk-FHTLC7HH.js +1 -0
  33. package/dist/chunk-FQLMX3IE.js +948 -0
  34. package/dist/chunk-H562R4GX.js +34 -0
  35. package/dist/chunk-JN4WXTWJ.js +106 -0
  36. package/dist/chunk-LVDRBRYU.js +1 -0
  37. package/dist/chunk-LWLCNQY7.js +1 -0
  38. package/dist/chunk-MAIHBBWT.js +1 -0
  39. package/dist/chunk-NPJH5MD7.js +13 -0
  40. package/dist/chunk-OCSOMMY2.js +1 -0
  41. package/dist/chunk-OQDWLHZI.js +1 -0
  42. package/dist/chunk-OWSAMA3I.js +23 -0
  43. package/dist/chunk-PH3PRIFP.js +5 -0
  44. package/dist/chunk-TAWKWSPN.js +9 -0
  45. package/dist/chunk-U4GTZPXP.js +1 -0
  46. package/dist/chunk-UB4JIOLD.js +2 -0
  47. package/dist/chunk-UZAJ5BF6.js +9 -0
  48. package/dist/chunk-VEGMDXT4.js +1 -0
  49. package/dist/chunk-VTVSMRFX.js +1 -0
  50. package/dist/chunk-VVAGSKW7.js +1 -0
  51. package/dist/chunk-XO43FJRM.js +19 -0
  52. package/dist/chunk-Y7BOLDRM.js +69 -0
  53. package/dist/file-intake/index.js +1 -4
  54. package/dist/{index-DZ7E7bCl.d.ts → index-DVI9DMSu.d.ts} +189 -18
  55. package/dist/index.d.ts +2 -2
  56. package/dist/index.js +639 -7473
  57. package/dist/map/index.js +20 -540
  58. package/dist/markdown/index.js +6 -184
  59. package/dist/markup/index.js +1 -135
  60. package/dist/page-tabs/index.js +21 -709
  61. package/dist/sounds-OMHXVVQ4.js +1 -0
  62. package/dist/ui/index.d.ts +1 -1
  63. package/dist/ui/index.js +1 -19
  64. package/dist/ui.css +78 -10
  65. package/package.json +4 -3
  66. package/dist/Browser-O3QZGUF2.js +0 -482
  67. package/dist/Browser-O3QZGUF2.js.map +0 -1
  68. package/dist/Calculator-L6N5X3KU.js +0 -192
  69. package/dist/Calculator-L6N5X3KU.js.map +0 -1
  70. package/dist/ConfirmDialog-LYSSZSUD.js +0 -4
  71. package/dist/ConfirmDialog-LYSSZSUD.js.map +0 -1
  72. package/dist/CurrencyConverter-64MKXB3D.js +0 -236
  73. package/dist/CurrencyConverter-64MKXB3D.js.map +0 -1
  74. package/dist/Documents-5WXXYCLI.js +0 -441
  75. package/dist/Documents-5WXXYCLI.js.map +0 -1
  76. package/dist/Files-AUURUUDN.js +0 -805
  77. package/dist/Files-AUURUUDN.js.map +0 -1
  78. package/dist/Notepad-GPXJVPB7.js +0 -410
  79. package/dist/Notepad-GPXJVPB7.js.map +0 -1
  80. package/dist/PdfViewer-HKO6P4SR.js +0 -5
  81. package/dist/PdfViewer-HKO6P4SR.js.map +0 -1
  82. package/dist/PomodoroTimer-4TQBWLP2.js +0 -633
  83. package/dist/PomodoroTimer-4TQBWLP2.js.map +0 -1
  84. package/dist/Preview-FOZYPKM2.js +0 -2959
  85. package/dist/Preview-FOZYPKM2.js.map +0 -1
  86. package/dist/Sidebar-BPU7RCZP.js +0 -291
  87. package/dist/Sidebar-BPU7RCZP.js.map +0 -1
  88. package/dist/Spreadsheet-3C66BBBF.js +0 -519
  89. package/dist/Spreadsheet-3C66BBBF.js.map +0 -1
  90. package/dist/Stock-IF4IHYLU.js +0 -158
  91. package/dist/Stock-IF4IHYLU.js.map +0 -1
  92. package/dist/Weather-725PFR7T.js +0 -431
  93. package/dist/Weather-725PFR7T.js.map +0 -1
  94. package/dist/WorldClock-UU4NZIQ7.js +0 -202
  95. package/dist/WorldClock-UU4NZIQ7.js.map +0 -1
  96. package/dist/apps/index.js.map +0 -1
  97. package/dist/chunk-2TPTHAKI.js +0 -316
  98. package/dist/chunk-2TPTHAKI.js.map +0 -1
  99. package/dist/chunk-4P44JXDS.js +0 -195
  100. package/dist/chunk-4P44JXDS.js.map +0 -1
  101. package/dist/chunk-5O42JXQE.js +0 -26
  102. package/dist/chunk-5O42JXQE.js.map +0 -1
  103. package/dist/chunk-72CQWN7O.js +0 -547
  104. package/dist/chunk-72CQWN7O.js.map +0 -1
  105. package/dist/chunk-7CCHEEYC.js +0 -94
  106. package/dist/chunk-7CCHEEYC.js.map +0 -1
  107. package/dist/chunk-BE7FZOFU.js +0 -17
  108. package/dist/chunk-BE7FZOFU.js.map +0 -1
  109. package/dist/chunk-D276EMXS.js +0 -57
  110. package/dist/chunk-D276EMXS.js.map +0 -1
  111. package/dist/chunk-D7PYW2QS.js +0 -265
  112. package/dist/chunk-D7PYW2QS.js.map +0 -1
  113. package/dist/chunk-DVZWYWSD.js +0 -26
  114. package/dist/chunk-DVZWYWSD.js.map +0 -1
  115. package/dist/chunk-ET6ID4LP.js +0 -102
  116. package/dist/chunk-ET6ID4LP.js.map +0 -1
  117. package/dist/chunk-GA35NEND.js +0 -56
  118. package/dist/chunk-GA35NEND.js.map +0 -1
  119. package/dist/chunk-GIGV2ZRW.js +0 -70
  120. package/dist/chunk-GIGV2ZRW.js.map +0 -1
  121. package/dist/chunk-IPXN646P.js +0 -14
  122. package/dist/chunk-IPXN646P.js.map +0 -1
  123. package/dist/chunk-IRWSRJ4K.js +0 -48
  124. package/dist/chunk-IRWSRJ4K.js.map +0 -1
  125. package/dist/chunk-IULP5BCD.js +0 -79
  126. package/dist/chunk-IULP5BCD.js.map +0 -1
  127. package/dist/chunk-MGGY74RY.js +0 -14
  128. package/dist/chunk-MGGY74RY.js.map +0 -1
  129. package/dist/chunk-OLJDJYSU.js +0 -13678
  130. package/dist/chunk-OLJDJYSU.js.map +0 -1
  131. package/dist/chunk-PIGDNN5F.js +0 -46
  132. package/dist/chunk-PIGDNN5F.js.map +0 -1
  133. package/dist/chunk-Q2MJVSJT.js +0 -7
  134. package/dist/chunk-Q2MJVSJT.js.map +0 -1
  135. package/dist/chunk-Q3LT745N.js +0 -3710
  136. package/dist/chunk-Q3LT745N.js.map +0 -1
  137. package/dist/chunk-QAGQRNKI.js +0 -145
  138. package/dist/chunk-QAGQRNKI.js.map +0 -1
  139. package/dist/chunk-QRL64J5F.js +0 -503
  140. package/dist/chunk-QRL64J5F.js.map +0 -1
  141. package/dist/chunk-QSKCQUZE.js +0 -1103
  142. package/dist/chunk-QSKCQUZE.js.map +0 -1
  143. package/dist/chunk-R4OQIQYX.js +0 -153
  144. package/dist/chunk-R4OQIQYX.js.map +0 -1
  145. package/dist/chunk-RT6DSB5Y.js +0 -143
  146. package/dist/chunk-RT6DSB5Y.js.map +0 -1
  147. package/dist/chunk-SHQM5CK3.js +0 -549
  148. package/dist/chunk-SHQM5CK3.js.map +0 -1
  149. package/dist/chunk-T4VQHMG4.js +0 -822
  150. package/dist/chunk-T4VQHMG4.js.map +0 -1
  151. package/dist/chunk-TRCWINHF.js +0 -105
  152. package/dist/chunk-TRCWINHF.js.map +0 -1
  153. package/dist/chunk-TW7LPMVR.js +0 -16
  154. package/dist/chunk-TW7LPMVR.js.map +0 -1
  155. package/dist/chunk-W622KQTD.js +0 -465
  156. package/dist/chunk-W622KQTD.js.map +0 -1
  157. package/dist/chunk-X4Q6G253.js +0 -416
  158. package/dist/chunk-X4Q6G253.js.map +0 -1
  159. package/dist/chunk-YNKK7NE3.js +0 -171
  160. package/dist/chunk-YNKK7NE3.js.map +0 -1
  161. package/dist/chunk-YPO6RDMX.js +0 -20
  162. package/dist/chunk-YPO6RDMX.js.map +0 -1
  163. package/dist/file-intake/index.js.map +0 -1
  164. package/dist/index.js.map +0 -1
  165. package/dist/map/index.js.map +0 -1
  166. package/dist/markdown/index.js.map +0 -1
  167. package/dist/markup/index.js.map +0 -1
  168. package/dist/page-tabs/index.js.map +0 -1
  169. package/dist/sounds-NT4DEZGD.js +0 -3
  170. package/dist/sounds-NT4DEZGD.js.map +0 -1
  171. package/dist/ui/index.js.map +0 -1
@@ -1,710 +1,22 @@
1
1
  "use client";
2
- import { PopupMenu, PopupMenuItem, PopupMenuDivider } from '../chunk-QRL64J5F.js';
3
- import { confirm } from '../chunk-W622KQTD.js';
4
- import '../chunk-D276EMXS.js';
5
- import { useShellStrings } from '../chunk-72CQWN7O.js';
6
- import { useBrowserLayoutEffect } from '../chunk-IRWSRJ4K.js';
7
- import '../chunk-IPXN646P.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};