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,805 +0,0 @@
1
- import { openPreviewFile } from './chunk-PIGDNN5F.js';
2
- import { getFilesDemoTree } from './chunk-BE7FZOFU.js';
3
- import { AboutApp } from './chunk-IULP5BCD.js';
4
- import { peekFilesViewRequest, consumeFilesViewRequest, FILES_SHOW_TRASH_EVENT, FILES_OPEN_DESKTOP_FOLDER_EVENT, subscribeDesktopFolders, getDesktopFoldersSnapshot, FolderGlyph, DesktopItemMiniIcon, desktopItemTypeLabel } from './chunk-YNKK7NE3.js';
5
- import './chunk-DVZWYWSD.js';
6
- import './chunk-5O42JXQE.js';
7
- import { useWindowManager, WindowTitle } from './chunk-Q3LT745N.js';
8
- import './chunk-GA35NEND.js';
9
- import { Breadcrumbs } from './chunk-GIGV2ZRW.js';
10
- import { SidebarLayout } from './chunk-ET6ID4LP.js';
11
- import './chunk-Q2MJVSJT.js';
12
- import { toast_default } from './chunk-4P44JXDS.js';
13
- import './chunk-2TPTHAKI.js';
14
- import './chunk-RT6DSB5Y.js';
15
- import './chunk-QRL64J5F.js';
16
- import { confirm, prompt } from './chunk-W622KQTD.js';
17
- import './chunk-D276EMXS.js';
18
- import './chunk-72CQWN7O.js';
19
- import './chunk-IRWSRJ4K.js';
20
- import './chunk-IPXN646P.js';
21
- import { useState, useEffect, useSyncExternalStore, useRef, useCallback } from 'react';
22
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
23
-
24
- var DEFAULT_SERVER = typeof window !== "undefined" && window.__REACT_OS_SHELL_FILE_SERVER__ || "http://localhost:4000";
25
- var PREVIEW_EXTS = {
26
- pdf: "pdf",
27
- dxf: "dxf",
28
- csv: "csv",
29
- jpg: "image",
30
- jpeg: "image",
31
- png: "image",
32
- gif: "image",
33
- webp: "image",
34
- svg: "image",
35
- avif: "image",
36
- bmp: "image",
37
- stp: "3d",
38
- step: "3d",
39
- stl: "3d",
40
- obj: "3d",
41
- gltf: "3d",
42
- glb: "3d",
43
- "3mf": "3d",
44
- iges: "3d",
45
- igs: "3d",
46
- ply: "3d",
47
- fbx: "3d"
48
- };
49
- function joinPath(parent, name) {
50
- if (parent === "/" || parent === "") return "/" + name;
51
- return parent.replace(/\/$/, "") + "/" + name;
52
- }
53
- function parentOf(p) {
54
- if (p === "/" || p === "") return "/";
55
- const trimmed = p.replace(/\/$/, "");
56
- const idx = trimmed.lastIndexOf("/");
57
- return idx <= 0 ? "/" : trimmed.slice(0, idx);
58
- }
59
- function formatSize(bytes) {
60
- if (!bytes) return "\u2014";
61
- const units = ["B", "KB", "MB", "GB"];
62
- let v = bytes, i = 0;
63
- while (v >= 1024 && i < units.length - 1) {
64
- v /= 1024;
65
- i++;
66
- }
67
- return `${v < 10 ? v.toFixed(1) : Math.round(v)} ${units[i]}`;
68
- }
69
- function formatTime(iso) {
70
- if (!iso) return "\u2014";
71
- try {
72
- const d = new Date(iso);
73
- if (isNaN(d.getTime())) return "\u2014";
74
- return d.toLocaleString(void 0, { dateStyle: "short", timeStyle: "short" });
75
- } catch {
76
- return iso;
77
- }
78
- }
79
- function demoEntriesAt(dir) {
80
- const filesDemoTree = getFilesDemoTree();
81
- if (!filesDemoTree) return [];
82
- let level = filesDemoTree;
83
- for (const part of dir.split("/").filter(Boolean)) {
84
- const node = level.find((n) => n.kind === "folder" && n.name === part);
85
- if (!node || !node.children) return [];
86
- level = node.children;
87
- }
88
- return level.map((n) => ({
89
- name: n.name,
90
- kind: n.kind,
91
- size: n.size ?? 0,
92
- modifiedAt: n.modifiedAt ?? ""
93
- }));
94
- }
95
- function Files() {
96
- const server = DEFAULT_SERVER.replace(/\/$/, "");
97
- const demoMode = getFilesDemoTree() !== null;
98
- const [path, setPath] = useState("/");
99
- const [entries, setEntries] = useState([]);
100
- const [rootFolders, setRootFolders] = useState([]);
101
- const [selected, setSelected] = useState(null);
102
- const [loading, setLoading] = useState(true);
103
- const [unreachable, setUnreachable] = useState(false);
104
- const [quota, setQuota] = useState(null);
105
- const [isDragging, setIsDragging] = useState(false);
106
- const [view, setView] = useState(() => {
107
- const req = peekFilesViewRequest();
108
- if (req === "trash") return "trash";
109
- if (req) return { desktopFolderId: req.folderId };
110
- return "files";
111
- });
112
- useEffect(() => {
113
- consumeFilesViewRequest();
114
- }, []);
115
- const [trash, setTrash] = useState([]);
116
- useEffect(() => {
117
- const showTrash = () => {
118
- consumeFilesViewRequest();
119
- setView("trash");
120
- };
121
- const showDesktopFolder = (e) => {
122
- consumeFilesViewRequest();
123
- const folderId = e.detail?.folderId;
124
- if (folderId) setView({ desktopFolderId: folderId });
125
- };
126
- window.addEventListener(FILES_SHOW_TRASH_EVENT, showTrash);
127
- window.addEventListener(FILES_OPEN_DESKTOP_FOLDER_EVENT, showDesktopFolder);
128
- return () => {
129
- window.removeEventListener(FILES_SHOW_TRASH_EVENT, showTrash);
130
- window.removeEventListener(FILES_OPEN_DESKTOP_FOLDER_EVENT, showDesktopFolder);
131
- };
132
- }, []);
133
- const desktopFolders = useSyncExternalStore(subscribeDesktopFolders, getDesktopFoldersSnapshot, getDesktopFoldersSnapshot);
134
- const desktopFolderId = typeof view === "object" ? view.desktopFolderId : null;
135
- const desktopFolder = desktopFolderId ? desktopFolders?.folders.find((f) => f.id === desktopFolderId) ?? null : null;
136
- const desktopItems = desktopFolderId && desktopFolders?.itemsByFolder[desktopFolderId] || [];
137
- useEffect(() => {
138
- if (desktopFolderId && desktopFolders && !desktopFolders.folders.some((f) => f.id === desktopFolderId)) {
139
- setView("files");
140
- }
141
- }, [desktopFolderId, desktopFolders]);
142
- const dragDepthRef = useRef(0);
143
- const fileRef = useRef(null);
144
- const { openPage, openEntity } = useWindowManager();
145
- const openDesktopItem = (item) => {
146
- if (item.entityType === "preview-file" && item.filePath && item.fileKind) {
147
- openPreviewFile({
148
- filePath: item.filePath,
149
- filename: item.label,
150
- kind: item.fileKind,
151
- onStaged: (route) => openPage(route)
152
- });
153
- } else if (item.entityType === "page") {
154
- openPage(item.entityId);
155
- } else {
156
- openEntity(item.entityType, item.entityId, null, item.label);
157
- }
158
- };
159
- const authedFetch = useCallback(
160
- (url, init = {}) => (
161
- // `credentials: 'include'` makes the browser send the identity
162
- // cookie with every cross-origin request, and accept any
163
- // Set-Cookie response (e.g. the first-visit assignment).
164
- fetch(url, { ...init, credentials: "include" })
165
- ),
166
- []
167
- );
168
- const refreshQuota = useCallback(async () => {
169
- try {
170
- const res = await authedFetch(`${server}/api/quota`);
171
- if (res.ok) {
172
- const q = await res.json();
173
- setQuota({ used: q.used, limit: q.limit });
174
- }
175
- } catch {
176
- }
177
- }, [authedFetch, server]);
178
- const loadDir = useCallback(async (dir) => {
179
- setSelected(null);
180
- if (demoMode) {
181
- const list = demoEntriesAt(dir);
182
- setEntries(list);
183
- setPath(dir);
184
- setUnreachable(false);
185
- setLoading(false);
186
- if (dir === "/") setRootFolders(list.filter((e) => e.kind === "folder").map((e) => e.name));
187
- return;
188
- }
189
- setLoading(true);
190
- try {
191
- const res = await authedFetch(
192
- `${server}/api/files?path=${encodeURIComponent(dir)}`
193
- );
194
- if (!res.ok) {
195
- const msg = await res.json().catch(() => ({}));
196
- toast_default.error(msg.error || `Failed to list (${res.status})`);
197
- return;
198
- }
199
- const data = await res.json();
200
- const list = data.entries || [];
201
- setEntries(list);
202
- setPath(data.path || dir);
203
- setUnreachable(false);
204
- if ((data.path || dir) === "/") setRootFolders(list.filter((e) => e.kind === "folder").map((e) => e.name));
205
- } catch (e) {
206
- setUnreachable(true);
207
- } finally {
208
- setLoading(false);
209
- }
210
- refreshQuota();
211
- }, [authedFetch, server, refreshQuota, demoMode]);
212
- const loadTrash = useCallback(async () => {
213
- if (demoMode) {
214
- setTrash([]);
215
- setLoading(false);
216
- return;
217
- }
218
- setLoading(true);
219
- setSelected(null);
220
- try {
221
- const res = await authedFetch(`${server}/api/trash`);
222
- if (!res.ok) {
223
- const msg = await res.json().catch(() => ({}));
224
- toast_default.error(msg.error || `Failed to list trash (${res.status})`);
225
- return;
226
- }
227
- const data = await res.json();
228
- setTrash(data.entries || []);
229
- setUnreachable(false);
230
- } catch {
231
- setUnreachable(true);
232
- } finally {
233
- setLoading(false);
234
- }
235
- refreshQuota();
236
- }, [authedFetch, server, refreshQuota, demoMode]);
237
- useEffect(() => {
238
- if (view === "files") loadDir(path);
239
- else if (view === "trash") loadTrash();
240
- }, [path, view]);
241
- const handleRestore = async (entry) => {
242
- const res = await authedFetch(`${server}/api/trash/restore`, {
243
- method: "POST",
244
- headers: { "Content-Type": "application/json" },
245
- body: JSON.stringify({ id: entry.id })
246
- });
247
- if (!res.ok) {
248
- const msg = await res.json().catch(() => ({}));
249
- toast_default.error(msg.error || `Restore failed (${res.status})`);
250
- return;
251
- }
252
- toast_default.success(`Restored "${entry.name}"`);
253
- loadTrash();
254
- };
255
- const handleTrashPermanentDelete = async (entry) => {
256
- const ok = await confirm({
257
- title: "Permanently delete",
258
- message: `"${entry.name}" will be permanently removed. This can't be undone.`,
259
- confirmLabel: "Delete forever",
260
- variant: "danger"
261
- });
262
- if (!ok) return;
263
- const res = await authedFetch(`${server}/api/trash/${encodeURIComponent(entry.id)}`, {
264
- method: "DELETE"
265
- });
266
- if (!res.ok) {
267
- const msg = await res.json().catch(() => ({}));
268
- toast_default.error(msg.error || `Delete failed (${res.status})`);
269
- return;
270
- }
271
- loadTrash();
272
- };
273
- const handleEmptyTrash = async () => {
274
- if (trash.length === 0) return;
275
- const ok = await confirm({
276
- title: "Empty trash",
277
- message: `${trash.length} item${trash.length === 1 ? "" : "s"} will be permanently removed. This can't be undone.`,
278
- confirmLabel: "Empty trash",
279
- variant: "danger"
280
- });
281
- if (!ok) return;
282
- const res = await authedFetch(`${server}/api/trash`, { method: "DELETE" });
283
- if (!res.ok) {
284
- const msg = await res.json().catch(() => ({}));
285
- toast_default.error(msg.error || `Empty trash failed (${res.status})`);
286
- return;
287
- }
288
- loadTrash();
289
- };
290
- const openFile = async (entry) => {
291
- if (demoMode) return;
292
- const fullPath = joinPath(path, entry.name);
293
- const ext = (entry.name.split(".").pop() || "").toLowerCase();
294
- const kind = PREVIEW_EXTS[ext];
295
- if (!kind) {
296
- downloadFile(entry);
297
- return;
298
- }
299
- await openPreviewFile({
300
- filePath: fullPath,
301
- filename: entry.name,
302
- kind,
303
- onStaged: (route) => openPage(route)
304
- });
305
- };
306
- const downloadFile = async (entry) => {
307
- const fullPath = joinPath(path, entry.name);
308
- try {
309
- const res = await authedFetch(
310
- `${server}/api/file?path=${encodeURIComponent(fullPath)}`
311
- );
312
- if (!res.ok) {
313
- toast_default.error(`Download failed (${res.status})`);
314
- return;
315
- }
316
- const blob = await res.blob();
317
- const url = URL.createObjectURL(blob);
318
- const a = document.createElement("a");
319
- a.href = url;
320
- a.download = entry.name;
321
- a.click();
322
- setTimeout(() => URL.revokeObjectURL(url), 1e3);
323
- } catch (e) {
324
- toast_default.error(e?.message || "Download failed");
325
- }
326
- };
327
- const handlePick = () => fileRef.current?.click();
328
- const uploadFiles = async (files) => {
329
- const arr = Array.from(files);
330
- for (const file of arr) {
331
- const form = new FormData();
332
- form.append("file", file);
333
- try {
334
- const res = await authedFetch(
335
- `${server}/api/upload?path=${encodeURIComponent(path)}`,
336
- { method: "POST", body: form }
337
- );
338
- if (!res.ok) {
339
- const msg = await res.json().catch(() => ({}));
340
- if (res.status === 413) {
341
- const remaining = Math.max(0, (msg.limit || 0) - (msg.used || 0));
342
- toast_default.error(
343
- `Quota exceeded \u2014 ${formatSize(remaining)} free, ${file.name} is ${formatSize(msg.attempted || file.size)}`
344
- );
345
- } else {
346
- toast_default.error(`Upload ${file.name}: ${msg.error || res.status}`);
347
- }
348
- }
349
- } catch (e) {
350
- toast_default.error(`Upload ${file.name}: ${e?.message || "failed"}`);
351
- }
352
- }
353
- loadDir(path);
354
- };
355
- const handleNewFolder = async () => {
356
- const name = await prompt({
357
- title: "New folder",
358
- placeholder: "Folder name",
359
- confirmLabel: "Create"
360
- });
361
- if (!name) return;
362
- if (/[\\/]/.test(name)) {
363
- toast_default.error("Folder names cannot contain slashes");
364
- return;
365
- }
366
- const target = joinPath(path, name);
367
- const res = await authedFetch(`${server}/api/folder`, {
368
- method: "POST",
369
- headers: { "Content-Type": "application/json" },
370
- body: JSON.stringify({ path: target })
371
- });
372
- if (!res.ok) {
373
- const msg = await res.json().catch(() => ({}));
374
- toast_default.error(msg.error || `Create folder failed (${res.status})`);
375
- return;
376
- }
377
- loadDir(path);
378
- };
379
- const handleRename = async (entry) => {
380
- const next = await prompt({
381
- title: `Rename ${entry.kind}`,
382
- defaultValue: entry.name,
383
- confirmLabel: "Rename"
384
- });
385
- if (!next || next === entry.name) return;
386
- if (/[\\/]/.test(next)) {
387
- toast_default.error("Names cannot contain slashes");
388
- return;
389
- }
390
- const from = joinPath(path, entry.name);
391
- const to = joinPath(path, next);
392
- const res = await authedFetch(`${server}/api/rename`, {
393
- method: "POST",
394
- headers: { "Content-Type": "application/json" },
395
- body: JSON.stringify({ from, to })
396
- });
397
- if (!res.ok) {
398
- const msg = await res.json().catch(() => ({}));
399
- toast_default.error(msg.error || `Rename failed (${res.status})`);
400
- return;
401
- }
402
- loadDir(path);
403
- };
404
- const handleDelete = async (entry) => {
405
- const ok = await confirm({
406
- title: `Delete ${entry.kind}`,
407
- message: `"${entry.name}"${entry.kind === "folder" ? " and everything inside" : ""} will be permanently removed.`,
408
- confirmLabel: "Delete",
409
- variant: "danger"
410
- });
411
- if (!ok) return;
412
- const target = joinPath(path, entry.name);
413
- const res = await authedFetch(
414
- `${server}/api/files?path=${encodeURIComponent(target)}`,
415
- { method: "DELETE" }
416
- );
417
- if (!res.ok) {
418
- const msg = await res.json().catch(() => ({}));
419
- toast_default.error(msg.error || `Delete failed (${res.status})`);
420
- return;
421
- }
422
- loadDir(path);
423
- };
424
- const resetDrag = () => {
425
- dragDepthRef.current = 0;
426
- setIsDragging(false);
427
- };
428
- useEffect(() => {
429
- const reset = () => resetDrag();
430
- window.addEventListener("dragend", reset);
431
- window.addEventListener("drop", reset);
432
- return () => {
433
- window.removeEventListener("dragend", reset);
434
- window.removeEventListener("drop", reset);
435
- };
436
- }, []);
437
- if (unreachable) {
438
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full bg-white", children: [
439
- /* @__PURE__ */ jsx(WindowTitle, { title: "Files - offline" }),
440
- /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center p-8 text-center", children: /* @__PURE__ */ jsxs("div", { className: "max-w-md", children: [
441
- /* @__PURE__ */ jsxs("svg", { className: "h-12 w-12 mx-auto text-gray-300 mb-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.4, children: [
442
- /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M18.364 5.636l-12.728 12.728M5.636 5.636l12.728 12.728" }),
443
- /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 21a9 9 0 100-18 9 9 0 000 18z" })
444
- ] }),
445
- /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-gray-800 mb-1", children: "Can't reach the file server" }),
446
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500 mb-3", children: [
447
- "No response from ",
448
- /* @__PURE__ */ jsx("span", { className: "font-mono", children: server }),
449
- ". Make sure the server is running \u2014 see ",
450
- /* @__PURE__ */ jsx("span", { className: "font-mono", children: "examples/file-server/README.md" }),
451
- "."
452
- ] }),
453
- /* @__PURE__ */ jsx("button", { onClick: () => loadDir(path), className: "px-3 py-1.5 text-sm bg-blue-500 text-white rounded hover:bg-blue-600", children: "Retry" })
454
- ] }) })
455
- ] });
456
- }
457
- const segments = path === "/" ? [] : path.split("/").filter(Boolean);
458
- const usagePct = quota && quota.limit > 0 ? Math.min(100, Math.round(quota.used / quota.limit * 100)) : 0;
459
- const usageColor = usagePct > 90 ? "bg-red-500" : usagePct > 75 ? "bg-amber-500" : "bg-blue-500";
460
- const crumbs = [
461
- { label: "My files", onClick: () => setPath("/") },
462
- ...segments.map((seg, i) => ({
463
- label: seg,
464
- onClick: () => setPath("/" + segments.slice(0, i + 1).join("/"))
465
- }))
466
- ];
467
- const navBtn = (active) => `flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm transition-colors ${active ? "bg-blue-50 font-medium text-blue-700" : "text-gray-700 hover:bg-gray-100"}`;
468
- const navSvg = (d) => /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 shrink-0 text-gray-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.6, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d }) });
469
- const folderNav = /* @__PURE__ */ jsxs("nav", { className: "p-2", children: [
470
- /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-400", children: "Locations" }),
471
- /* @__PURE__ */ jsxs("button", { onClick: () => {
472
- setView("files");
473
- setPath("/");
474
- }, className: navBtn(view === "files" && path === "/"), children: [
475
- navSvg("M2.25 12l8.954-8.955a1.5 1.5 0 012.122 0L21.75 12M4.5 9.75v9.75a.75.75 0 00.75.75H9V15a.75.75 0 01.75-.75h4.5A.75.75 0 0115 15v5.25h3.75a.75.75 0 00.75-.75V9.75"),
476
- "My files"
477
- ] }),
478
- rootFolders.map((name) => /* @__PURE__ */ jsxs("button", { onClick: () => {
479
- setView("files");
480
- setPath("/" + name);
481
- }, className: navBtn(view === "files" && path === "/" + name), children: [
482
- navSvg("M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z"),
483
- /* @__PURE__ */ jsx("span", { className: "truncate", children: name })
484
- ] }, name)),
485
- !demoMode && /* @__PURE__ */ jsxs("button", { onClick: () => setView("trash"), className: navBtn(view === "trash"), children: [
486
- navSvg("M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79"),
487
- "Trash"
488
- ] }),
489
- desktopFolders && desktopFolders.folders.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
490
- /* @__PURE__ */ jsx("div", { className: "px-2 pt-3 pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-400", children: "Desktop" }),
491
- desktopFolders.folders.map((f) => /* @__PURE__ */ jsxs("button", { onClick: () => setView({ desktopFolderId: f.id }), className: navBtn(desktopFolderId === f.id), children: [
492
- /* @__PURE__ */ jsx(FolderGlyph, { className: "h-4 w-4 shrink-0 text-amber-500" }),
493
- /* @__PURE__ */ jsx("span", { className: "truncate", children: f.name }),
494
- f.itemCount > 0 && /* @__PURE__ */ jsx("span", { className: "ml-auto text-[10px] text-gray-400 tabular-nums", children: f.itemCount })
495
- ] }, f.id))
496
- ] })
497
- ] });
498
- return /* @__PURE__ */ jsxs(
499
- "div",
500
- {
501
- className: "relative flex flex-col h-full bg-white",
502
- onDragEnter: (e) => {
503
- if (demoMode || typeof view === "object") return;
504
- if (!e.dataTransfer?.types?.includes?.("Files")) return;
505
- e.preventDefault();
506
- dragDepthRef.current++;
507
- if (!isDragging) setIsDragging(true);
508
- },
509
- onDragOver: (e) => {
510
- if (!e.dataTransfer?.types?.includes?.("Files")) return;
511
- e.preventDefault();
512
- },
513
- onDragLeave: () => {
514
- if (dragDepthRef.current > 0) dragDepthRef.current--;
515
- if (dragDepthRef.current === 0) setIsDragging(false);
516
- },
517
- onDrop: (e) => {
518
- e.preventDefault();
519
- resetDrag();
520
- if (demoMode || typeof view === "object") return;
521
- if (e.dataTransfer.files?.length) uploadFiles(e.dataTransfer.files);
522
- },
523
- children: [
524
- /* @__PURE__ */ jsx(WindowTitle, { title: desktopFolder ? `Files - ${desktopFolder.name}` : `Files${path === "/" ? "" : " - " + path}` }),
525
- /* @__PURE__ */ jsx(AboutApp, { app: "files" }),
526
- /* @__PURE__ */ jsx(
527
- "input",
528
- {
529
- ref: fileRef,
530
- type: "file",
531
- multiple: true,
532
- className: "hidden",
533
- onChange: (e) => {
534
- if (e.target.files?.length) uploadFiles(e.target.files);
535
- if (fileRef.current) fileRef.current.value = "";
536
- }
537
- }
538
- ),
539
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsx(
540
- SidebarLayout,
541
- {
542
- sidebar: folderNav,
543
- storageKey: "files.sidebarWidth",
544
- defaultWidth: 200,
545
- minWidth: 150,
546
- maxWidth: 320,
547
- children: /* @__PURE__ */ jsxs("div", { className: "flex h-full min-w-0 flex-col", children: [
548
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-2 py-1.5 border-b border-gray-200 bg-gray-50 shrink-0 text-xs", children: [
549
- view === "files" ? /* @__PURE__ */ jsxs(Fragment, { children: [
550
- /* @__PURE__ */ jsx(
551
- "button",
552
- {
553
- onClick: () => setPath(parentOf(path)),
554
- disabled: path === "/",
555
- className: "px-2 py-1 rounded hover:bg-gray-200 disabled:opacity-30 text-gray-600",
556
- title: "Parent folder",
557
- children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" }) })
558
- }
559
- ),
560
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(Breadcrumbs, { items: crumbs, maxItems: 5 }) }),
561
- /* @__PURE__ */ jsx("button", { onClick: () => loadDir(path), className: "px-2 py-1 rounded hover:bg-gray-200 text-gray-600", title: "Refresh", children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.8, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.023 9.348h4.992V4.356M2.985 19.644v-4.992h4.992M3.05 9.348a9 9 0 0114.85-3.36L21.015 9.348m0 5.304a9 9 0 01-14.85 3.36l-3.115-3.36" }) }) }),
562
- !demoMode && /* @__PURE__ */ jsxs(Fragment, { children: [
563
- /* @__PURE__ */ jsxs("button", { onClick: () => setView("trash"), className: "px-2 py-1 rounded hover:bg-gray-200 text-gray-600 flex items-center gap-1", title: "Open trash", children: [
564
- /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" }) }),
565
- "Trash"
566
- ] }),
567
- /* @__PURE__ */ jsxs("button", { onClick: handleNewFolder, className: "px-2 py-1 rounded hover:bg-gray-200 text-gray-600 flex items-center gap-1", children: [
568
- /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 10.5v6m3-3H9m4.06-7.19l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" }) }),
569
- "New Folder"
570
- ] }),
571
- /* @__PURE__ */ jsxs("button", { onClick: handlePick, className: "px-2 py-1 rounded bg-blue-500 text-white hover:bg-blue-600 flex items-center gap-1", children: [
572
- /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.8, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M7.5 7.5L12 3m0 0l4.5 4.5M12 3v13.5" }) }),
573
- "Upload"
574
- ] })
575
- ] })
576
- ] }) : view === "trash" ? /* @__PURE__ */ jsxs(Fragment, { children: [
577
- /* @__PURE__ */ jsx(
578
- "button",
579
- {
580
- onClick: () => setView("files"),
581
- className: "px-2 py-1 rounded hover:bg-gray-200 text-gray-600",
582
- title: "Back to files",
583
- children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" }) })
584
- }
585
- ),
586
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-gray-700 font-medium px-2", children: "Trash" }),
587
- /* @__PURE__ */ jsx("button", { onClick: loadTrash, className: "px-2 py-1 rounded hover:bg-gray-200 text-gray-600", title: "Refresh", children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.8, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.023 9.348h4.992V4.356M2.985 19.644v-4.992h4.992M3.05 9.348a9 9 0 0114.85-3.36L21.015 9.348m0 5.304a9 9 0 01-14.85 3.36l-3.115-3.36" }) }) }),
588
- /* @__PURE__ */ jsx(
589
- "button",
590
- {
591
- onClick: handleEmptyTrash,
592
- disabled: trash.length === 0,
593
- className: "px-2 py-1 rounded text-red-600 hover:bg-red-50 disabled:opacity-30 flex items-center gap-1",
594
- children: "Empty trash"
595
- }
596
- )
597
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
598
- /* @__PURE__ */ jsx(
599
- "button",
600
- {
601
- onClick: () => setView("files"),
602
- className: "px-2 py-1 rounded hover:bg-gray-200 text-gray-600",
603
- title: "Back to files",
604
- children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18" }) })
605
- }
606
- ),
607
- /* @__PURE__ */ jsx(FolderGlyph, { className: "h-4 w-4 text-amber-500 ml-1" }),
608
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-gray-700 font-medium px-1 truncate", children: desktopFolder?.name ?? "Desktop folder" }),
609
- /* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400 px-2", children: "Desktop shortcuts" })
610
- ] }),
611
- quota && /* @__PURE__ */ jsxs(Fragment, { children: [
612
- /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-gray-300 mx-1" }),
613
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", title: `${formatSize(quota.used)} of ${formatSize(quota.limit)} used`, children: [
614
- /* @__PURE__ */ jsx("div", { className: "w-20 h-1.5 rounded-full bg-gray-200 overflow-hidden", children: /* @__PURE__ */ jsx(
615
- "div",
616
- {
617
- className: `h-full ${usageColor} transition-all`,
618
- style: { width: `${usagePct}%` }
619
- }
620
- ) }),
621
- /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-gray-500 tabular-nums whitespace-nowrap", children: [
622
- formatSize(quota.used),
623
- " / ",
624
- formatSize(quota.limit)
625
- ] })
626
- ] })
627
- ] })
628
- ] }),
629
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", children: typeof view === "object" ? desktopItems.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-10 text-center text-sm text-gray-400", children: "This desktop folder is empty. Drag shortcuts onto it on the desktop." }) : /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
630
- /* @__PURE__ */ jsx("thead", { className: "bg-gray-50 text-[11px] uppercase tracking-wide text-gray-500", children: /* @__PURE__ */ jsxs("tr", { children: [
631
- /* @__PURE__ */ jsx("th", { className: "text-left font-medium px-3 py-1.5", children: "Name" }),
632
- /* @__PURE__ */ jsx("th", { className: "text-left font-medium px-3 py-1.5 w-28", children: "Type" }),
633
- /* @__PURE__ */ jsx("th", { className: "w-56" })
634
- ] }) }),
635
- /* @__PURE__ */ jsx("tbody", { children: desktopItems.map((item) => {
636
- const key = `${item.entityType}|${item.entityId}`;
637
- return /* @__PURE__ */ jsxs(
638
- "tr",
639
- {
640
- onClick: () => setSelected(key),
641
- onDoubleClick: () => openDesktopItem(item),
642
- className: `cursor-default border-b border-gray-100 ${selected === key ? "bg-blue-50" : "hover:bg-gray-50"}`,
643
- children: [
644
- /* @__PURE__ */ jsxs("td", { className: "px-3 py-1.5 flex items-center gap-2", children: [
645
- /* @__PURE__ */ jsx(DesktopItemMiniIcon, { item }),
646
- /* @__PURE__ */ jsx("span", { className: "truncate", title: item.label, children: item.label })
647
- ] }),
648
- /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 text-xs text-gray-500", children: desktopItemTypeLabel(item) }),
649
- /* @__PURE__ */ jsxs("td", { className: "px-3 py-1.5 text-right whitespace-nowrap", children: [
650
- /* @__PURE__ */ jsx(
651
- "button",
652
- {
653
- onClick: (ev) => {
654
- ev.stopPropagation();
655
- openDesktopItem(item);
656
- },
657
- className: "px-1.5 py-0.5 rounded hover:bg-gray-200 text-blue-600 text-[11px]",
658
- children: "Open"
659
- }
660
- ),
661
- /* @__PURE__ */ jsx(
662
- "button",
663
- {
664
- onClick: (ev) => {
665
- ev.stopPropagation();
666
- desktopFolders?.moveToDesktop([item]);
667
- },
668
- className: "px-1.5 py-0.5 rounded hover:bg-gray-200 text-gray-500 text-[11px] ml-1",
669
- children: "Move to desktop"
670
- }
671
- ),
672
- /* @__PURE__ */ jsx(
673
- "button",
674
- {
675
- onClick: (ev) => {
676
- ev.stopPropagation();
677
- desktopFolders?.removeShortcuts([item]);
678
- },
679
- className: "px-1.5 py-0.5 rounded hover:bg-red-100 text-red-600 text-[11px] ml-1",
680
- children: "Remove"
681
- }
682
- )
683
- ] })
684
- ]
685
- },
686
- key
687
- );
688
- }) })
689
- ] }) : view === "trash" ? loading && trash.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-6 text-center text-sm text-gray-400", children: "Loading\u2026" }) : trash.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-10 text-center text-sm text-gray-400", children: "Trash is empty." }) : /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
690
- /* @__PURE__ */ jsx("thead", { className: "bg-gray-50 text-[11px] uppercase tracking-wide text-gray-500", children: /* @__PURE__ */ jsxs("tr", { children: [
691
- /* @__PURE__ */ jsx("th", { className: "text-left font-medium px-3 py-1.5", children: "Name" }),
692
- /* @__PURE__ */ jsx("th", { className: "text-left font-medium px-3 py-1.5", children: "Original location" }),
693
- /* @__PURE__ */ jsx("th", { className: "text-right font-medium px-3 py-1.5 w-24", children: "Size" }),
694
- /* @__PURE__ */ jsx("th", { className: "text-right font-medium px-3 py-1.5 w-40", children: "Deleted" }),
695
- /* @__PURE__ */ jsx("th", { className: "w-44" })
696
- ] }) }),
697
- /* @__PURE__ */ jsx("tbody", { children: trash.map((e) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-gray-100 hover:bg-gray-50", children: [
698
- /* @__PURE__ */ jsxs("td", { className: "px-3 py-1.5 flex items-center gap-2", children: [
699
- e.kind === "folder" ? /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 text-amber-500 shrink-0", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { d: "M2.25 7.125A2.25 2.25 0 014.5 4.875h4.504c.61 0 1.193.243 1.624.673l1.494 1.494a.75.75 0 00.53.22h7.098A2.25 2.25 0 0122 9.51v8.366A2.25 2.25 0 0119.75 20.125H4.25A2.25 2.25 0 012 17.875V7.125z" }) }) : /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 text-gray-400 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" }) }),
700
- /* @__PURE__ */ jsx("span", { className: "truncate", title: e.name, children: e.name })
701
- ] }),
702
- /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 text-gray-500 text-xs font-mono truncate", title: e.originalPath, children: e.originalPath || "\u2014" }),
703
- /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 text-right tabular-nums text-gray-500", children: formatSize(e.size) }),
704
- /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 text-right text-gray-500 tabular-nums", children: e.deletedAt ? formatTime(e.deletedAt) : "\u2014" }),
705
- /* @__PURE__ */ jsxs("td", { className: "px-3 py-1.5 text-right whitespace-nowrap", children: [
706
- /* @__PURE__ */ jsx(
707
- "button",
708
- {
709
- onClick: () => handleRestore(e),
710
- className: "px-1.5 py-0.5 rounded hover:bg-gray-200 text-blue-600 text-[11px]",
711
- children: "Restore"
712
- }
713
- ),
714
- /* @__PURE__ */ jsx(
715
- "button",
716
- {
717
- onClick: () => handleTrashPermanentDelete(e),
718
- className: "px-1.5 py-0.5 rounded hover:bg-red-100 text-red-600 text-[11px] ml-1",
719
- children: "Delete forever"
720
- }
721
- )
722
- ] })
723
- ] }, e.id)) })
724
- ] }) : loading && entries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-6 text-center text-sm text-gray-400", children: "Loading\u2026" }) : entries.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "p-10 text-center text-sm text-gray-400", children: [
725
- "Empty folder.",
726
- !demoMode && /* @__PURE__ */ jsxs(Fragment, { children: [
727
- " Drop files here or click ",
728
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Upload" }),
729
- "."
730
- ] })
731
- ] }) : /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
732
- /* @__PURE__ */ jsx("thead", { className: "bg-gray-50 text-[11px] uppercase tracking-wide text-gray-500", children: /* @__PURE__ */ jsxs("tr", { children: [
733
- /* @__PURE__ */ jsx("th", { className: "text-left font-medium px-3 py-1.5", children: "Name" }),
734
- /* @__PURE__ */ jsx("th", { className: "text-right font-medium px-3 py-1.5 w-24", children: "Size" }),
735
- /* @__PURE__ */ jsx("th", { className: "text-right font-medium px-3 py-1.5 w-40", children: "Modified" }),
736
- /* @__PURE__ */ jsx("th", { className: "w-32" })
737
- ] }) }),
738
- /* @__PURE__ */ jsx("tbody", { children: entries.map((e) => /* @__PURE__ */ jsxs(
739
- "tr",
740
- {
741
- onClick: () => setSelected(e.name),
742
- onDoubleClick: () => {
743
- if (e.kind === "folder") setPath(joinPath(path, e.name));
744
- else openFile(e);
745
- },
746
- className: `cursor-default border-b border-gray-100 ${selected === e.name ? "bg-blue-50" : "hover:bg-gray-50"}`,
747
- children: [
748
- /* @__PURE__ */ jsxs("td", { className: "px-3 py-1.5 flex items-center gap-2", children: [
749
- e.kind === "folder" ? /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 text-amber-500 shrink-0", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { d: "M2.25 7.125A2.25 2.25 0 014.5 4.875h4.504c.61 0 1.193.243 1.624.673l1.494 1.494a.75.75 0 00.53.22h7.098A2.25 2.25 0 0122 9.51v8.366A2.25 2.25 0 0119.75 20.125H4.25A2.25 2.25 0 012 17.875V7.125z" }) }) : /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 text-gray-400 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" }) }),
750
- /* @__PURE__ */ jsx("span", { className: "truncate", title: e.name, children: e.name })
751
- ] }),
752
- /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 text-right tabular-nums text-gray-500", children: e.kind === "folder" ? "\u2014" : formatSize(e.size) }),
753
- /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 text-right text-gray-500 tabular-nums", children: formatTime(e.modifiedAt) }),
754
- /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 text-right whitespace-nowrap", children: !demoMode && /* @__PURE__ */ jsxs(Fragment, { children: [
755
- e.kind === "file" && /* @__PURE__ */ jsx(
756
- "button",
757
- {
758
- onClick: (ev) => {
759
- ev.stopPropagation();
760
- downloadFile(e);
761
- },
762
- className: "px-1.5 py-0.5 rounded hover:bg-gray-200 text-gray-500 text-[11px]",
763
- children: "Download"
764
- }
765
- ),
766
- /* @__PURE__ */ jsx(
767
- "button",
768
- {
769
- onClick: (ev) => {
770
- ev.stopPropagation();
771
- handleRename(e);
772
- },
773
- className: "px-1.5 py-0.5 rounded hover:bg-gray-200 text-gray-500 text-[11px] ml-1",
774
- children: "Rename"
775
- }
776
- ),
777
- /* @__PURE__ */ jsx(
778
- "button",
779
- {
780
- onClick: (ev) => {
781
- ev.stopPropagation();
782
- handleDelete(e);
783
- },
784
- className: "px-1.5 py-0.5 rounded hover:bg-red-100 text-red-600 text-[11px] ml-1",
785
- children: "Delete"
786
- }
787
- )
788
- ] }) })
789
- ]
790
- },
791
- e.name
792
- )) })
793
- ] }) })
794
- ] })
795
- }
796
- ) }),
797
- isDragging && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-blue-500/15 border-4 border-dashed border-blue-500 pointer-events-none flex items-center justify-center z-20", children: /* @__PURE__ */ jsx("div", { className: "px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium shadow-lg", children: "Drop to upload" }) })
798
- ]
799
- }
800
- );
801
- }
802
-
803
- export { Files as default };
804
- //# sourceMappingURL=Files-AUURUUDN.js.map
805
- //# sourceMappingURL=Files-AUURUUDN.js.map