@huyooo/file-explorer-core 0.2.0
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.
- package/dist/index.cjs +1259 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +492 -0
- package/dist/index.d.ts +492 -0
- package/dist/index.js +1182 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
FileType: () => FileType,
|
|
34
|
+
SqliteThumbnailDatabase: () => SqliteThumbnailDatabase,
|
|
35
|
+
ThumbnailService: () => ThumbnailService,
|
|
36
|
+
copyFiles: () => copyFiles,
|
|
37
|
+
copyFilesToClipboard: () => copyFilesToClipboard,
|
|
38
|
+
createFfmpegVideoProcessor: () => createFfmpegVideoProcessor,
|
|
39
|
+
createFile: () => createFile,
|
|
40
|
+
createFolder: () => createFolder,
|
|
41
|
+
createSharpImageProcessor: () => createSharpImageProcessor,
|
|
42
|
+
createSqliteThumbnailDatabase: () => createSqliteThumbnailDatabase,
|
|
43
|
+
deleteFiles: () => deleteFiles,
|
|
44
|
+
exists: () => exists,
|
|
45
|
+
formatDate: () => formatDate,
|
|
46
|
+
formatDateTime: () => formatDateTime,
|
|
47
|
+
formatFileSize: () => formatFileSize,
|
|
48
|
+
getAllSystemPaths: () => getAllSystemPaths,
|
|
49
|
+
getApplicationIcon: () => getApplicationIcon,
|
|
50
|
+
getClipboardFiles: () => getClipboardFiles,
|
|
51
|
+
getFileHash: () => getFileHash,
|
|
52
|
+
getFileHashes: () => getFileHashes,
|
|
53
|
+
getFileInfo: () => getFileInfo,
|
|
54
|
+
getFileType: () => getFileType,
|
|
55
|
+
getHomeDirectory: () => getHomeDirectory,
|
|
56
|
+
getPlatform: () => getPlatform,
|
|
57
|
+
getSqliteThumbnailDatabase: () => getSqliteThumbnailDatabase,
|
|
58
|
+
getSystemPath: () => getSystemPath,
|
|
59
|
+
getThumbnailService: () => getThumbnailService,
|
|
60
|
+
initThumbnailService: () => initThumbnailService,
|
|
61
|
+
isDirectory: () => isDirectory,
|
|
62
|
+
isMediaFile: () => isMediaFile,
|
|
63
|
+
isPreviewable: () => isPreviewable,
|
|
64
|
+
moveFiles: () => moveFiles,
|
|
65
|
+
pasteFiles: () => pasteFiles,
|
|
66
|
+
readDirectory: () => readDirectory,
|
|
67
|
+
readFileContent: () => readFileContent,
|
|
68
|
+
readImageAsBase64: () => readImageAsBase64,
|
|
69
|
+
renameFile: () => renameFile,
|
|
70
|
+
searchFiles: () => searchFiles,
|
|
71
|
+
searchFilesStream: () => searchFilesStream,
|
|
72
|
+
searchFilesSync: () => searchFilesSync,
|
|
73
|
+
writeFileContent: () => writeFileContent
|
|
74
|
+
});
|
|
75
|
+
module.exports = __toCommonJS(index_exports);
|
|
76
|
+
|
|
77
|
+
// src/types.ts
|
|
78
|
+
var FileType = /* @__PURE__ */ ((FileType2) => {
|
|
79
|
+
FileType2["FOLDER"] = "folder";
|
|
80
|
+
FileType2["FILE"] = "file";
|
|
81
|
+
FileType2["IMAGE"] = "image";
|
|
82
|
+
FileType2["VIDEO"] = "video";
|
|
83
|
+
FileType2["MUSIC"] = "music";
|
|
84
|
+
FileType2["DOCUMENT"] = "document";
|
|
85
|
+
FileType2["CODE"] = "code";
|
|
86
|
+
FileType2["TEXT"] = "text";
|
|
87
|
+
FileType2["ARCHIVE"] = "archive";
|
|
88
|
+
FileType2["APPLICATION"] = "application";
|
|
89
|
+
FileType2["UNKNOWN"] = "unknown";
|
|
90
|
+
return FileType2;
|
|
91
|
+
})(FileType || {});
|
|
92
|
+
|
|
93
|
+
// src/utils/file-type.ts
|
|
94
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
95
|
+
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
96
|
+
".jpg",
|
|
97
|
+
".jpeg",
|
|
98
|
+
".png",
|
|
99
|
+
".gif",
|
|
100
|
+
".bmp",
|
|
101
|
+
".webp",
|
|
102
|
+
".svg",
|
|
103
|
+
".ico",
|
|
104
|
+
".tiff",
|
|
105
|
+
".tif",
|
|
106
|
+
".heic",
|
|
107
|
+
".heif"
|
|
108
|
+
]);
|
|
109
|
+
var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
110
|
+
".mp4",
|
|
111
|
+
".mov",
|
|
112
|
+
".avi",
|
|
113
|
+
".mkv",
|
|
114
|
+
".wmv",
|
|
115
|
+
".flv",
|
|
116
|
+
".webm",
|
|
117
|
+
".m4v",
|
|
118
|
+
".3gp",
|
|
119
|
+
".mpeg",
|
|
120
|
+
".mpg"
|
|
121
|
+
]);
|
|
122
|
+
var MUSIC_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
123
|
+
".mp3",
|
|
124
|
+
".wav",
|
|
125
|
+
".flac",
|
|
126
|
+
".aac",
|
|
127
|
+
".ogg",
|
|
128
|
+
".wma",
|
|
129
|
+
".m4a",
|
|
130
|
+
".aiff",
|
|
131
|
+
".alac"
|
|
132
|
+
]);
|
|
133
|
+
var DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
134
|
+
".pdf",
|
|
135
|
+
".doc",
|
|
136
|
+
".docx",
|
|
137
|
+
".xls",
|
|
138
|
+
".xlsx",
|
|
139
|
+
".ppt",
|
|
140
|
+
".pptx",
|
|
141
|
+
".odt",
|
|
142
|
+
".ods",
|
|
143
|
+
".odp",
|
|
144
|
+
".rtf",
|
|
145
|
+
".pages",
|
|
146
|
+
".numbers",
|
|
147
|
+
".key"
|
|
148
|
+
]);
|
|
149
|
+
var CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
150
|
+
".js",
|
|
151
|
+
".ts",
|
|
152
|
+
".jsx",
|
|
153
|
+
".tsx",
|
|
154
|
+
".vue",
|
|
155
|
+
".svelte",
|
|
156
|
+
".py",
|
|
157
|
+
".rb",
|
|
158
|
+
".go",
|
|
159
|
+
".rs",
|
|
160
|
+
".java",
|
|
161
|
+
".kt",
|
|
162
|
+
".swift",
|
|
163
|
+
".c",
|
|
164
|
+
".cpp",
|
|
165
|
+
".h",
|
|
166
|
+
".hpp",
|
|
167
|
+
".html",
|
|
168
|
+
".css",
|
|
169
|
+
".scss",
|
|
170
|
+
".sass",
|
|
171
|
+
".less",
|
|
172
|
+
".json",
|
|
173
|
+
".yaml",
|
|
174
|
+
".yml",
|
|
175
|
+
".toml",
|
|
176
|
+
".xml",
|
|
177
|
+
".sh",
|
|
178
|
+
".bash",
|
|
179
|
+
".zsh",
|
|
180
|
+
".fish",
|
|
181
|
+
".ps1",
|
|
182
|
+
".sql",
|
|
183
|
+
".graphql",
|
|
184
|
+
".prisma"
|
|
185
|
+
]);
|
|
186
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
187
|
+
".txt",
|
|
188
|
+
".md",
|
|
189
|
+
".markdown",
|
|
190
|
+
".log",
|
|
191
|
+
".ini",
|
|
192
|
+
".conf",
|
|
193
|
+
".cfg",
|
|
194
|
+
".env"
|
|
195
|
+
]);
|
|
196
|
+
var ARCHIVE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
197
|
+
".zip",
|
|
198
|
+
".rar",
|
|
199
|
+
".7z",
|
|
200
|
+
".tar",
|
|
201
|
+
".gz",
|
|
202
|
+
".bz2",
|
|
203
|
+
".xz",
|
|
204
|
+
".dmg",
|
|
205
|
+
".iso"
|
|
206
|
+
]);
|
|
207
|
+
var APPLICATION_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
208
|
+
".app",
|
|
209
|
+
".exe",
|
|
210
|
+
".msi",
|
|
211
|
+
".deb",
|
|
212
|
+
".rpm",
|
|
213
|
+
".pkg",
|
|
214
|
+
".apk",
|
|
215
|
+
".ipa"
|
|
216
|
+
]);
|
|
217
|
+
function getFileType(filePath, stats) {
|
|
218
|
+
if (stats.isDirectory()) {
|
|
219
|
+
if (filePath.endsWith(".app")) {
|
|
220
|
+
return "application" /* APPLICATION */;
|
|
221
|
+
}
|
|
222
|
+
return "folder" /* FOLDER */;
|
|
223
|
+
}
|
|
224
|
+
const ext = import_node_path.default.extname(filePath).toLowerCase();
|
|
225
|
+
if (IMAGE_EXTENSIONS.has(ext)) return "image" /* IMAGE */;
|
|
226
|
+
if (VIDEO_EXTENSIONS.has(ext)) return "video" /* VIDEO */;
|
|
227
|
+
if (MUSIC_EXTENSIONS.has(ext)) return "music" /* MUSIC */;
|
|
228
|
+
if (DOCUMENT_EXTENSIONS.has(ext)) return "document" /* DOCUMENT */;
|
|
229
|
+
if (CODE_EXTENSIONS.has(ext)) return "code" /* CODE */;
|
|
230
|
+
if (TEXT_EXTENSIONS.has(ext)) return "text" /* TEXT */;
|
|
231
|
+
if (ARCHIVE_EXTENSIONS.has(ext)) return "archive" /* ARCHIVE */;
|
|
232
|
+
if (APPLICATION_EXTENSIONS.has(ext)) return "application" /* APPLICATION */;
|
|
233
|
+
return "file" /* FILE */;
|
|
234
|
+
}
|
|
235
|
+
function isMediaFile(type) {
|
|
236
|
+
return type === "image" /* IMAGE */ || type === "video" /* VIDEO */ || type === "music" /* MUSIC */;
|
|
237
|
+
}
|
|
238
|
+
function isPreviewable(type) {
|
|
239
|
+
return type === "image" /* IMAGE */ || type === "video" /* VIDEO */ || type === "music" /* MUSIC */ || type === "text" /* TEXT */ || type === "code" /* CODE */;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/utils/formatters.ts
|
|
243
|
+
function formatFileSize(bytes) {
|
|
244
|
+
if (bytes === 0) return "0 B";
|
|
245
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
246
|
+
const k = 1024;
|
|
247
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
248
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${units[i]}`;
|
|
249
|
+
}
|
|
250
|
+
function formatDate(date) {
|
|
251
|
+
const now = /* @__PURE__ */ new Date();
|
|
252
|
+
const diff = now.getTime() - date.getTime();
|
|
253
|
+
const days = Math.floor(diff / (1e3 * 60 * 60 * 24));
|
|
254
|
+
if (days === 0) {
|
|
255
|
+
return date.toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
|
|
256
|
+
} else if (days === 1) {
|
|
257
|
+
return "\u6628\u5929";
|
|
258
|
+
} else if (days < 7) {
|
|
259
|
+
return `${days} \u5929\u524D`;
|
|
260
|
+
} else {
|
|
261
|
+
return date.toLocaleDateString("zh-CN", {
|
|
262
|
+
year: "numeric",
|
|
263
|
+
month: "2-digit",
|
|
264
|
+
day: "2-digit"
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function formatDateTime(date) {
|
|
269
|
+
return date.toLocaleString("zh-CN", {
|
|
270
|
+
year: "numeric",
|
|
271
|
+
month: "2-digit",
|
|
272
|
+
day: "2-digit",
|
|
273
|
+
hour: "2-digit",
|
|
274
|
+
minute: "2-digit"
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/operations/read.ts
|
|
279
|
+
var import_node_fs = require("fs");
|
|
280
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
281
|
+
var defaultUrlEncoder = (filePath) => `file://${encodeURIComponent(filePath)}`;
|
|
282
|
+
async function readDirectory(dirPath, options = {}) {
|
|
283
|
+
const {
|
|
284
|
+
urlEncoder = defaultUrlEncoder,
|
|
285
|
+
includeHidden = false,
|
|
286
|
+
getThumbnailUrl
|
|
287
|
+
} = options;
|
|
288
|
+
try {
|
|
289
|
+
const entries = await import_node_fs.promises.readdir(dirPath, { withFileTypes: true });
|
|
290
|
+
const items = [];
|
|
291
|
+
for (const entry of entries) {
|
|
292
|
+
if (!includeHidden && entry.name.startsWith(".")) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const fullPath = import_node_path2.default.join(dirPath, entry.name);
|
|
296
|
+
try {
|
|
297
|
+
let stats;
|
|
298
|
+
try {
|
|
299
|
+
stats = await import_node_fs.promises.lstat(fullPath);
|
|
300
|
+
} catch {
|
|
301
|
+
try {
|
|
302
|
+
stats = await import_node_fs.promises.stat(fullPath);
|
|
303
|
+
} catch (statError) {
|
|
304
|
+
const err = statError;
|
|
305
|
+
if (err.code !== "ENOENT") {
|
|
306
|
+
console.warn(`Cannot access file ${fullPath}:`, err.message);
|
|
307
|
+
}
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const fileType = getFileType(fullPath, stats);
|
|
312
|
+
const fileUrl = urlEncoder(fullPath);
|
|
313
|
+
const item = {
|
|
314
|
+
id: fullPath,
|
|
315
|
+
name: entry.name,
|
|
316
|
+
type: fileType,
|
|
317
|
+
dateModified: formatDate(stats.mtime),
|
|
318
|
+
url: fileUrl
|
|
319
|
+
};
|
|
320
|
+
if (stats.isDirectory()) {
|
|
321
|
+
item.children = [];
|
|
322
|
+
} else {
|
|
323
|
+
item.size = formatFileSize(stats.size);
|
|
324
|
+
if (getThumbnailUrl && (fileType === "image" /* IMAGE */ || fileType === "video" /* VIDEO */)) {
|
|
325
|
+
const thumbUrl = await getThumbnailUrl(fullPath);
|
|
326
|
+
if (thumbUrl) {
|
|
327
|
+
item.thumbnailUrl = thumbUrl;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
items.push(item);
|
|
332
|
+
} catch (itemError) {
|
|
333
|
+
const err = itemError;
|
|
334
|
+
if (err.code !== "ENOENT") {
|
|
335
|
+
console.warn(`Error processing file ${fullPath}:`, err.message);
|
|
336
|
+
}
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
items.sort((a, b) => {
|
|
341
|
+
if (a.type === "folder" /* FOLDER */ && b.type !== "folder" /* FOLDER */) return -1;
|
|
342
|
+
if (a.type !== "folder" /* FOLDER */ && b.type === "folder" /* FOLDER */) return 1;
|
|
343
|
+
return a.name.localeCompare(b.name, "zh-CN");
|
|
344
|
+
});
|
|
345
|
+
return items;
|
|
346
|
+
} catch (error) {
|
|
347
|
+
console.error(`Error reading directory ${dirPath}:`, error);
|
|
348
|
+
return [];
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async function readFileContent(filePath) {
|
|
352
|
+
return await import_node_fs.promises.readFile(filePath, "utf-8");
|
|
353
|
+
}
|
|
354
|
+
async function readImageAsBase64(imagePath) {
|
|
355
|
+
try {
|
|
356
|
+
let actualPath = imagePath;
|
|
357
|
+
if (imagePath.startsWith("app://file")) {
|
|
358
|
+
actualPath = decodeURIComponent(imagePath.replace("app://file", ""));
|
|
359
|
+
} else if (imagePath.startsWith("file://")) {
|
|
360
|
+
actualPath = decodeURIComponent(imagePath.replace("file://", ""));
|
|
361
|
+
}
|
|
362
|
+
const stats = await import_node_fs.promises.stat(actualPath);
|
|
363
|
+
if (!stats.isFile()) {
|
|
364
|
+
return { success: false, error: `\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${actualPath}` };
|
|
365
|
+
}
|
|
366
|
+
const buffer = await import_node_fs.promises.readFile(actualPath);
|
|
367
|
+
const base64 = buffer.toString("base64");
|
|
368
|
+
const ext = import_node_path2.default.extname(actualPath).toLowerCase().slice(1);
|
|
369
|
+
const mimeTypes = {
|
|
370
|
+
jpg: "image/jpeg",
|
|
371
|
+
jpeg: "image/jpeg",
|
|
372
|
+
png: "image/png",
|
|
373
|
+
gif: "image/gif",
|
|
374
|
+
webp: "image/webp",
|
|
375
|
+
bmp: "image/bmp",
|
|
376
|
+
svg: "image/svg+xml"
|
|
377
|
+
};
|
|
378
|
+
const mimeType = mimeTypes[ext] || "image/jpeg";
|
|
379
|
+
return { success: true, base64, mimeType };
|
|
380
|
+
} catch (error) {
|
|
381
|
+
return { success: false, error: String(error) };
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// src/operations/write.ts
|
|
386
|
+
var import_node_fs2 = require("fs");
|
|
387
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
388
|
+
async function writeFileContent(filePath, content) {
|
|
389
|
+
try {
|
|
390
|
+
await import_node_fs2.promises.writeFile(filePath, content, "utf-8");
|
|
391
|
+
return { success: true };
|
|
392
|
+
} catch (error) {
|
|
393
|
+
return { success: false, error: String(error) };
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function createFolder(folderPath) {
|
|
397
|
+
try {
|
|
398
|
+
try {
|
|
399
|
+
await import_node_fs2.promises.access(folderPath);
|
|
400
|
+
} catch {
|
|
401
|
+
await import_node_fs2.promises.mkdir(folderPath, { recursive: true });
|
|
402
|
+
return { success: true, data: { finalPath: folderPath } };
|
|
403
|
+
}
|
|
404
|
+
const dir = import_node_path3.default.dirname(folderPath);
|
|
405
|
+
const baseName = import_node_path3.default.basename(folderPath);
|
|
406
|
+
let counter = 2;
|
|
407
|
+
let finalPath = import_node_path3.default.join(dir, `${baseName} ${counter}`);
|
|
408
|
+
while (true) {
|
|
409
|
+
try {
|
|
410
|
+
await import_node_fs2.promises.access(finalPath);
|
|
411
|
+
counter++;
|
|
412
|
+
finalPath = import_node_path3.default.join(dir, `${baseName} ${counter}`);
|
|
413
|
+
} catch {
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
await import_node_fs2.promises.mkdir(finalPath, { recursive: true });
|
|
418
|
+
return { success: true, data: { finalPath } };
|
|
419
|
+
} catch (error) {
|
|
420
|
+
return { success: false, error: String(error) };
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
async function createFile(filePath, content = "") {
|
|
424
|
+
try {
|
|
425
|
+
const dir = import_node_path3.default.dirname(filePath);
|
|
426
|
+
await import_node_fs2.promises.mkdir(dir, { recursive: true });
|
|
427
|
+
try {
|
|
428
|
+
await import_node_fs2.promises.access(filePath);
|
|
429
|
+
} catch {
|
|
430
|
+
await import_node_fs2.promises.writeFile(filePath, content, "utf-8");
|
|
431
|
+
return { success: true, data: { finalPath: filePath } };
|
|
432
|
+
}
|
|
433
|
+
const dirname = import_node_path3.default.dirname(filePath);
|
|
434
|
+
const ext = import_node_path3.default.extname(filePath);
|
|
435
|
+
const basename = import_node_path3.default.basename(filePath, ext);
|
|
436
|
+
let counter = 2;
|
|
437
|
+
let finalPath = import_node_path3.default.join(dirname, `${basename} ${counter}${ext}`);
|
|
438
|
+
while (true) {
|
|
439
|
+
try {
|
|
440
|
+
await import_node_fs2.promises.access(finalPath);
|
|
441
|
+
counter++;
|
|
442
|
+
finalPath = import_node_path3.default.join(dirname, `${basename} ${counter}${ext}`);
|
|
443
|
+
} catch {
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
await import_node_fs2.promises.writeFile(finalPath, content, "utf-8");
|
|
448
|
+
return { success: true, data: { finalPath } };
|
|
449
|
+
} catch (error) {
|
|
450
|
+
return { success: false, error: String(error) };
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// src/operations/delete.ts
|
|
455
|
+
var import_node_fs3 = require("fs");
|
|
456
|
+
async function deleteFiles(paths, options = {}) {
|
|
457
|
+
const { adapter, useTrash = true, onDeleted } = options;
|
|
458
|
+
try {
|
|
459
|
+
for (const filePath of paths) {
|
|
460
|
+
if (useTrash && adapter?.trashItem) {
|
|
461
|
+
await adapter.trashItem(filePath);
|
|
462
|
+
} else {
|
|
463
|
+
const stats = await import_node_fs3.promises.stat(filePath);
|
|
464
|
+
if (stats.isDirectory()) {
|
|
465
|
+
await import_node_fs3.promises.rm(filePath, { recursive: true, force: true });
|
|
466
|
+
} else {
|
|
467
|
+
await import_node_fs3.promises.unlink(filePath);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
onDeleted?.(filePath);
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
success: true,
|
|
474
|
+
message: useTrash ? "\u6587\u4EF6\u5DF2\u79FB\u52A8\u5230\u56DE\u6536\u7AD9" : "\u6587\u4EF6\u5DF2\u5220\u9664"
|
|
475
|
+
};
|
|
476
|
+
} catch (error) {
|
|
477
|
+
return { success: false, error: String(error) };
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/operations/rename.ts
|
|
482
|
+
var import_node_fs4 = require("fs");
|
|
483
|
+
async function renameFile(oldPath, newPath, options = {}) {
|
|
484
|
+
const { onRenamed } = options;
|
|
485
|
+
try {
|
|
486
|
+
await import_node_fs4.promises.rename(oldPath, newPath);
|
|
487
|
+
onRenamed?.(oldPath, newPath);
|
|
488
|
+
return { success: true };
|
|
489
|
+
} catch (error) {
|
|
490
|
+
return { success: false, error: String(error) };
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/operations/copy.ts
|
|
495
|
+
var import_node_fs5 = require("fs");
|
|
496
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
497
|
+
async function copyFiles(sourcePaths, targetDir) {
|
|
498
|
+
try {
|
|
499
|
+
const copiedPaths = [];
|
|
500
|
+
for (const sourcePath of sourcePaths) {
|
|
501
|
+
const fileName = import_node_path4.default.basename(sourcePath);
|
|
502
|
+
let destPath = import_node_path4.default.join(targetDir, fileName);
|
|
503
|
+
let counter = 1;
|
|
504
|
+
while (true) {
|
|
505
|
+
try {
|
|
506
|
+
await import_node_fs5.promises.access(destPath);
|
|
507
|
+
const ext = import_node_path4.default.extname(fileName);
|
|
508
|
+
const baseName = import_node_path4.default.basename(fileName, ext);
|
|
509
|
+
destPath = import_node_path4.default.join(targetDir, `${baseName} ${++counter}${ext}`);
|
|
510
|
+
} catch {
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
const stats = await import_node_fs5.promises.stat(sourcePath);
|
|
515
|
+
if (stats.isDirectory()) {
|
|
516
|
+
await copyDirectory(sourcePath, destPath);
|
|
517
|
+
} else {
|
|
518
|
+
await import_node_fs5.promises.copyFile(sourcePath, destPath);
|
|
519
|
+
}
|
|
520
|
+
copiedPaths.push(destPath);
|
|
521
|
+
}
|
|
522
|
+
return { success: true, data: { copiedPaths } };
|
|
523
|
+
} catch (error) {
|
|
524
|
+
return { success: false, error: String(error) };
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async function moveFiles(sourcePaths, targetDir) {
|
|
528
|
+
try {
|
|
529
|
+
const movedPaths = [];
|
|
530
|
+
for (const sourcePath of sourcePaths) {
|
|
531
|
+
const fileName = import_node_path4.default.basename(sourcePath);
|
|
532
|
+
let destPath = import_node_path4.default.join(targetDir, fileName);
|
|
533
|
+
let counter = 1;
|
|
534
|
+
while (true) {
|
|
535
|
+
try {
|
|
536
|
+
await import_node_fs5.promises.access(destPath);
|
|
537
|
+
const ext = import_node_path4.default.extname(fileName);
|
|
538
|
+
const baseName = import_node_path4.default.basename(fileName, ext);
|
|
539
|
+
destPath = import_node_path4.default.join(targetDir, `${baseName} ${++counter}${ext}`);
|
|
540
|
+
} catch {
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
try {
|
|
545
|
+
await import_node_fs5.promises.rename(sourcePath, destPath);
|
|
546
|
+
} catch {
|
|
547
|
+
const stats = await import_node_fs5.promises.stat(sourcePath);
|
|
548
|
+
if (stats.isDirectory()) {
|
|
549
|
+
await copyDirectory(sourcePath, destPath);
|
|
550
|
+
} else {
|
|
551
|
+
await import_node_fs5.promises.copyFile(sourcePath, destPath);
|
|
552
|
+
}
|
|
553
|
+
await import_node_fs5.promises.rm(sourcePath, { recursive: true, force: true });
|
|
554
|
+
}
|
|
555
|
+
movedPaths.push(destPath);
|
|
556
|
+
}
|
|
557
|
+
return { success: true, data: { movedPaths } };
|
|
558
|
+
} catch (error) {
|
|
559
|
+
return { success: false, error: String(error) };
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
async function copyDirectory(source, dest) {
|
|
563
|
+
await import_node_fs5.promises.mkdir(dest, { recursive: true });
|
|
564
|
+
const entries = await import_node_fs5.promises.readdir(source, { withFileTypes: true });
|
|
565
|
+
for (const entry of entries) {
|
|
566
|
+
const sourcePath = import_node_path4.default.join(source, entry.name);
|
|
567
|
+
const destPath = import_node_path4.default.join(dest, entry.name);
|
|
568
|
+
if (entry.isDirectory()) {
|
|
569
|
+
await copyDirectory(sourcePath, destPath);
|
|
570
|
+
} else {
|
|
571
|
+
await import_node_fs5.promises.copyFile(sourcePath, destPath);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/operations/info.ts
|
|
577
|
+
var import_node_fs6 = require("fs");
|
|
578
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
579
|
+
async function getFileInfo(filePath) {
|
|
580
|
+
try {
|
|
581
|
+
const stats = await import_node_fs6.promises.stat(filePath);
|
|
582
|
+
const name = import_node_path5.default.basename(filePath);
|
|
583
|
+
const ext = import_node_path5.default.extname(name);
|
|
584
|
+
return {
|
|
585
|
+
success: true,
|
|
586
|
+
data: {
|
|
587
|
+
path: filePath,
|
|
588
|
+
name,
|
|
589
|
+
size: stats.size,
|
|
590
|
+
isFile: stats.isFile(),
|
|
591
|
+
isDirectory: stats.isDirectory(),
|
|
592
|
+
createdAt: stats.birthtime,
|
|
593
|
+
updatedAt: stats.mtime,
|
|
594
|
+
extension: ext || void 0
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
} catch (error) {
|
|
598
|
+
return { success: false, error: String(error) };
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
async function exists(filePath) {
|
|
602
|
+
try {
|
|
603
|
+
await import_node_fs6.promises.access(filePath);
|
|
604
|
+
return true;
|
|
605
|
+
} catch {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
async function isDirectory(filePath) {
|
|
610
|
+
try {
|
|
611
|
+
const stats = await import_node_fs6.promises.stat(filePath);
|
|
612
|
+
return stats.isDirectory();
|
|
613
|
+
} catch {
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/system-paths.ts
|
|
619
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
620
|
+
var import_node_os = __toESM(require("os"), 1);
|
|
621
|
+
var platform = process.platform;
|
|
622
|
+
function getSystemPath(pathId) {
|
|
623
|
+
const homeDir = import_node_os.default.homedir();
|
|
624
|
+
switch (pathId) {
|
|
625
|
+
case "desktop":
|
|
626
|
+
return import_node_path6.default.join(homeDir, "Desktop");
|
|
627
|
+
case "documents":
|
|
628
|
+
return import_node_path6.default.join(homeDir, "Documents");
|
|
629
|
+
case "downloads":
|
|
630
|
+
return import_node_path6.default.join(homeDir, "Downloads");
|
|
631
|
+
case "pictures":
|
|
632
|
+
return import_node_path6.default.join(homeDir, "Pictures");
|
|
633
|
+
case "music":
|
|
634
|
+
return import_node_path6.default.join(homeDir, "Music");
|
|
635
|
+
case "videos":
|
|
636
|
+
return platform === "darwin" ? import_node_path6.default.join(homeDir, "Movies") : import_node_path6.default.join(homeDir, "Videos");
|
|
637
|
+
case "applications":
|
|
638
|
+
return platform === "darwin" ? "/Applications" : platform === "win32" ? process.env.ProgramFiles || "C:\\Program Files" : "/usr/share/applications";
|
|
639
|
+
case "home":
|
|
640
|
+
return homeDir;
|
|
641
|
+
case "root":
|
|
642
|
+
return platform === "darwin" ? "/" : platform === "win32" ? "C:\\" : "/";
|
|
643
|
+
default:
|
|
644
|
+
return null;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
function getAllSystemPaths() {
|
|
648
|
+
const pathIds = [
|
|
649
|
+
"desktop",
|
|
650
|
+
"documents",
|
|
651
|
+
"downloads",
|
|
652
|
+
"pictures",
|
|
653
|
+
"music",
|
|
654
|
+
"videos",
|
|
655
|
+
"applications",
|
|
656
|
+
"home",
|
|
657
|
+
"root"
|
|
658
|
+
];
|
|
659
|
+
const result = {};
|
|
660
|
+
for (const id of pathIds) {
|
|
661
|
+
result[id] = getSystemPath(id);
|
|
662
|
+
}
|
|
663
|
+
return result;
|
|
664
|
+
}
|
|
665
|
+
function getHomeDirectory() {
|
|
666
|
+
return import_node_os.default.homedir();
|
|
667
|
+
}
|
|
668
|
+
function getPlatform() {
|
|
669
|
+
return process.platform;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// src/search.ts
|
|
673
|
+
var import_fdir = require("fdir");
|
|
674
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
675
|
+
var import_node_fs7 = require("fs");
|
|
676
|
+
function patternToRegex(pattern) {
|
|
677
|
+
return new RegExp(pattern.replace(/\*/g, ".*"), "i");
|
|
678
|
+
}
|
|
679
|
+
async function searchFiles(searchPath, pattern, maxDepth) {
|
|
680
|
+
const builder = new import_fdir.fdir().withFullPaths().withDirs();
|
|
681
|
+
if (maxDepth && maxDepth > 0) {
|
|
682
|
+
builder.withMaxDepth(maxDepth);
|
|
683
|
+
}
|
|
684
|
+
const api = builder.crawl(searchPath);
|
|
685
|
+
const files = await api.withPromise();
|
|
686
|
+
if (pattern) {
|
|
687
|
+
const regex = patternToRegex(pattern);
|
|
688
|
+
return files.filter((file) => regex.test(import_node_path7.default.basename(file)));
|
|
689
|
+
}
|
|
690
|
+
return files;
|
|
691
|
+
}
|
|
692
|
+
async function searchFilesStream(searchPath, pattern, onResults, maxResults = 100) {
|
|
693
|
+
const regex = patternToRegex(pattern);
|
|
694
|
+
const results = [];
|
|
695
|
+
let stopped = false;
|
|
696
|
+
async function searchDir(dirPath) {
|
|
697
|
+
if (stopped) return;
|
|
698
|
+
try {
|
|
699
|
+
const entries = await import_node_fs7.promises.readdir(dirPath, { withFileTypes: true });
|
|
700
|
+
const matched = [];
|
|
701
|
+
const subdirs = [];
|
|
702
|
+
for (const entry of entries) {
|
|
703
|
+
if (stopped) return;
|
|
704
|
+
const fullPath = import_node_path7.default.join(dirPath, entry.name);
|
|
705
|
+
if (regex.test(entry.name)) {
|
|
706
|
+
matched.push(fullPath);
|
|
707
|
+
results.push(fullPath);
|
|
708
|
+
if (results.length >= maxResults) {
|
|
709
|
+
stopped = true;
|
|
710
|
+
onResults(matched, true);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
if (entry.isDirectory()) {
|
|
715
|
+
subdirs.push(fullPath);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (matched.length > 0) {
|
|
719
|
+
onResults(matched, false);
|
|
720
|
+
}
|
|
721
|
+
for (const subdir of subdirs) {
|
|
722
|
+
await searchDir(subdir);
|
|
723
|
+
}
|
|
724
|
+
} catch {
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
await searchDir(searchPath);
|
|
728
|
+
if (!stopped) {
|
|
729
|
+
onResults([], true);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
function searchFilesSync(searchPath, pattern, maxDepth) {
|
|
733
|
+
const builder = new import_fdir.fdir().withFullPaths().withDirs();
|
|
734
|
+
if (maxDepth && maxDepth > 0) {
|
|
735
|
+
builder.withMaxDepth(maxDepth);
|
|
736
|
+
}
|
|
737
|
+
const api = builder.crawl(searchPath);
|
|
738
|
+
const files = api.sync();
|
|
739
|
+
if (pattern) {
|
|
740
|
+
const regex = new RegExp(pattern.replace(/\*/g, ".*"), "i");
|
|
741
|
+
return files.filter((file) => regex.test(import_node_path7.default.basename(file)));
|
|
742
|
+
}
|
|
743
|
+
return files;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/hash.ts
|
|
747
|
+
var import_node_crypto = require("crypto");
|
|
748
|
+
var import_promises = require("fs/promises");
|
|
749
|
+
async function getFileHash(filePath) {
|
|
750
|
+
try {
|
|
751
|
+
const stats = await (0, import_promises.stat)(filePath);
|
|
752
|
+
const hashInput = `${filePath}:${stats.size}:${stats.mtime.getTime()}`;
|
|
753
|
+
return (0, import_node_crypto.createHash)("md5").update(hashInput).digest("hex");
|
|
754
|
+
} catch (error) {
|
|
755
|
+
console.error(`Error computing hash for ${filePath}:`, error);
|
|
756
|
+
return (0, import_node_crypto.createHash)("md5").update(filePath).digest("hex");
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
async function getFileHashes(filePaths) {
|
|
760
|
+
const hashMap = /* @__PURE__ */ new Map();
|
|
761
|
+
await Promise.allSettled(
|
|
762
|
+
filePaths.map(async (filePath) => {
|
|
763
|
+
const hash = await getFileHash(filePath);
|
|
764
|
+
hashMap.set(filePath, hash);
|
|
765
|
+
})
|
|
766
|
+
);
|
|
767
|
+
return hashMap;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// src/clipboard.ts
|
|
771
|
+
var import_node_fs8 = require("fs");
|
|
772
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
773
|
+
async function copyFilesToClipboard(filePaths, clipboard) {
|
|
774
|
+
try {
|
|
775
|
+
const cleanPaths = [];
|
|
776
|
+
for (const p of filePaths) {
|
|
777
|
+
try {
|
|
778
|
+
await import_node_fs8.promises.access(p);
|
|
779
|
+
cleanPaths.push(p);
|
|
780
|
+
} catch {
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (cleanPaths.length === 0) {
|
|
784
|
+
return { success: false, error: "\u6CA1\u6709\u627E\u5230\u6709\u6548\u7684\u6587\u4EF6" };
|
|
785
|
+
}
|
|
786
|
+
clipboard.writeFiles(cleanPaths);
|
|
787
|
+
return { success: true };
|
|
788
|
+
} catch (error) {
|
|
789
|
+
return { success: false, error: String(error) };
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function getClipboardFiles(clipboard) {
|
|
793
|
+
try {
|
|
794
|
+
const files = clipboard.readFiles();
|
|
795
|
+
if (files && Array.isArray(files) && files.length > 0) {
|
|
796
|
+
return { success: true, data: { files } };
|
|
797
|
+
}
|
|
798
|
+
return { success: false, error: "\u526A\u8D34\u677F\u4E2D\u6CA1\u6709\u6587\u4EF6" };
|
|
799
|
+
} catch (error) {
|
|
800
|
+
return { success: false, error: String(error) };
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
async function pasteFiles(targetDir, sourcePaths) {
|
|
804
|
+
try {
|
|
805
|
+
if (!targetDir || typeof targetDir !== "string") {
|
|
806
|
+
return { success: false, error: "\u76EE\u6807\u76EE\u5F55\u8DEF\u5F84\u65E0\u6548" };
|
|
807
|
+
}
|
|
808
|
+
if (!sourcePaths || !Array.isArray(sourcePaths) || sourcePaths.length === 0) {
|
|
809
|
+
return { success: false, error: "\u6E90\u6587\u4EF6\u8DEF\u5F84\u5217\u8868\u65E0\u6548" };
|
|
810
|
+
}
|
|
811
|
+
const pastedPaths = [];
|
|
812
|
+
for (const sourcePath of sourcePaths) {
|
|
813
|
+
const fileName = import_node_path8.default.basename(sourcePath);
|
|
814
|
+
let destPath = import_node_path8.default.join(targetDir, fileName);
|
|
815
|
+
let counter = 1;
|
|
816
|
+
while (true) {
|
|
817
|
+
try {
|
|
818
|
+
await import_node_fs8.promises.access(destPath);
|
|
819
|
+
const ext = import_node_path8.default.extname(fileName);
|
|
820
|
+
const baseName = import_node_path8.default.basename(fileName, ext);
|
|
821
|
+
destPath = import_node_path8.default.join(targetDir, `${baseName} ${++counter}${ext}`);
|
|
822
|
+
} catch {
|
|
823
|
+
break;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
const stats = await import_node_fs8.promises.stat(sourcePath);
|
|
827
|
+
if (stats.isDirectory()) {
|
|
828
|
+
await copyDirectory2(sourcePath, destPath);
|
|
829
|
+
} else {
|
|
830
|
+
await import_node_fs8.promises.copyFile(sourcePath, destPath);
|
|
831
|
+
}
|
|
832
|
+
pastedPaths.push(destPath);
|
|
833
|
+
}
|
|
834
|
+
return { success: true, data: { pastedPaths } };
|
|
835
|
+
} catch (error) {
|
|
836
|
+
return { success: false, error: String(error) };
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
async function copyDirectory2(source, dest) {
|
|
840
|
+
await import_node_fs8.promises.mkdir(dest, { recursive: true });
|
|
841
|
+
const entries = await import_node_fs8.promises.readdir(source, { withFileTypes: true });
|
|
842
|
+
for (const entry of entries) {
|
|
843
|
+
const sourcePath = import_node_path8.default.join(source, entry.name);
|
|
844
|
+
const destPath = import_node_path8.default.join(dest, entry.name);
|
|
845
|
+
if (entry.isDirectory()) {
|
|
846
|
+
await copyDirectory2(sourcePath, destPath);
|
|
847
|
+
} else {
|
|
848
|
+
await import_node_fs8.promises.copyFile(sourcePath, destPath);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/application-icon.ts
|
|
854
|
+
var import_node_fs9 = require("fs");
|
|
855
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
856
|
+
var import_node_os2 = __toESM(require("os"), 1);
|
|
857
|
+
var import_node_child_process = require("child_process");
|
|
858
|
+
var import_node_util = require("util");
|
|
859
|
+
var execAsync = (0, import_node_util.promisify)(import_node_child_process.exec);
|
|
860
|
+
async function findAppIconPath(appPath) {
|
|
861
|
+
const resourcesPath = import_node_path9.default.join(appPath, "Contents", "Resources");
|
|
862
|
+
try {
|
|
863
|
+
const commonIconNames = [
|
|
864
|
+
"AppIcon.icns",
|
|
865
|
+
"app.icns",
|
|
866
|
+
"application.icns",
|
|
867
|
+
"icon.icns"
|
|
868
|
+
];
|
|
869
|
+
const infoPlistPath = import_node_path9.default.join(appPath, "Contents", "Info.plist");
|
|
870
|
+
try {
|
|
871
|
+
const infoPlistContent = await import_node_fs9.promises.readFile(infoPlistPath, "utf-8");
|
|
872
|
+
const iconFileMatch = infoPlistContent.match(/<key>CFBundleIconFile<\/key>\s*<string>([^<]+)<\/string>/);
|
|
873
|
+
if (iconFileMatch && iconFileMatch[1]) {
|
|
874
|
+
let iconFileName = iconFileMatch[1].trim();
|
|
875
|
+
if (!iconFileName.endsWith(".icns")) {
|
|
876
|
+
iconFileName += ".icns";
|
|
877
|
+
}
|
|
878
|
+
const iconPath = import_node_path9.default.join(resourcesPath, iconFileName);
|
|
879
|
+
try {
|
|
880
|
+
await import_node_fs9.promises.access(iconPath);
|
|
881
|
+
return iconPath;
|
|
882
|
+
} catch {
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
} catch {
|
|
886
|
+
}
|
|
887
|
+
for (const iconName of commonIconNames) {
|
|
888
|
+
const iconPath = import_node_path9.default.join(resourcesPath, iconName);
|
|
889
|
+
try {
|
|
890
|
+
await import_node_fs9.promises.access(iconPath);
|
|
891
|
+
return iconPath;
|
|
892
|
+
} catch {
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
try {
|
|
897
|
+
const entries = await import_node_fs9.promises.readdir(resourcesPath);
|
|
898
|
+
const icnsFile = entries.find((entry) => entry.toLowerCase().endsWith(".icns"));
|
|
899
|
+
if (icnsFile) {
|
|
900
|
+
return import_node_path9.default.join(resourcesPath, icnsFile);
|
|
901
|
+
}
|
|
902
|
+
} catch {
|
|
903
|
+
}
|
|
904
|
+
return null;
|
|
905
|
+
} catch {
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
async function getApplicationIcon(appPath) {
|
|
910
|
+
if (process.platform !== "darwin") {
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
try {
|
|
914
|
+
const stats = await import_node_fs9.promises.stat(appPath);
|
|
915
|
+
if (!stats.isDirectory() || !appPath.endsWith(".app")) {
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
918
|
+
} catch {
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
const iconPath = await findAppIconPath(appPath);
|
|
922
|
+
if (!iconPath) {
|
|
923
|
+
return null;
|
|
924
|
+
}
|
|
925
|
+
try {
|
|
926
|
+
const tempPngPath = import_node_path9.default.join(import_node_os2.default.tmpdir(), `app-icon-${Date.now()}.png`);
|
|
927
|
+
await execAsync(
|
|
928
|
+
`sips -s format png "${iconPath}" --out "${tempPngPath}" --resampleHeightWidthMax 128`
|
|
929
|
+
);
|
|
930
|
+
const pngBuffer = await import_node_fs9.promises.readFile(tempPngPath);
|
|
931
|
+
try {
|
|
932
|
+
await import_node_fs9.promises.unlink(tempPngPath);
|
|
933
|
+
} catch {
|
|
934
|
+
}
|
|
935
|
+
const base64 = pngBuffer.toString("base64");
|
|
936
|
+
return `data:image/png;base64,${base64}`;
|
|
937
|
+
} catch {
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// src/thumbnail/service.ts
|
|
943
|
+
var import_node_fs10 = require("fs");
|
|
944
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
945
|
+
var IMAGE_EXTENSIONS2 = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"];
|
|
946
|
+
var VIDEO_EXTENSIONS2 = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".flv", ".wmv"];
|
|
947
|
+
var ThumbnailService = class {
|
|
948
|
+
database;
|
|
949
|
+
imageProcessor;
|
|
950
|
+
videoProcessor;
|
|
951
|
+
urlEncoder;
|
|
952
|
+
getApplicationIcon;
|
|
953
|
+
constructor(options) {
|
|
954
|
+
this.database = options.database;
|
|
955
|
+
this.imageProcessor = options.imageProcessor || null;
|
|
956
|
+
this.videoProcessor = options.videoProcessor || null;
|
|
957
|
+
this.urlEncoder = options.urlEncoder || ((p) => `file://${encodeURIComponent(p)}`);
|
|
958
|
+
this.getApplicationIcon = options.getApplicationIcon || null;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* 获取缓存的缩略图 URL(不生成新的)
|
|
962
|
+
*/
|
|
963
|
+
async getCachedThumbnailUrl(filePath) {
|
|
964
|
+
try {
|
|
965
|
+
const stats = await import_node_fs10.promises.stat(filePath);
|
|
966
|
+
const fileType = getFileType(filePath, stats);
|
|
967
|
+
if (fileType === "application" /* APPLICATION */ && this.getApplicationIcon) {
|
|
968
|
+
return await this.getApplicationIcon(filePath);
|
|
969
|
+
}
|
|
970
|
+
if (fileType !== "image" /* IMAGE */ && fileType !== "video" /* VIDEO */) {
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
const mtime = stats.mtime.getTime();
|
|
974
|
+
const cachedPath = this.database.getThumbnailPathFast(filePath, mtime);
|
|
975
|
+
if (cachedPath) {
|
|
976
|
+
return this.urlEncoder(cachedPath);
|
|
977
|
+
}
|
|
978
|
+
getFileHash(filePath).then((fileHash) => {
|
|
979
|
+
this.generateThumbnail(filePath, fileHash, mtime).catch(() => {
|
|
980
|
+
});
|
|
981
|
+
}).catch(() => {
|
|
982
|
+
});
|
|
983
|
+
return null;
|
|
984
|
+
} catch (error) {
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* 获取缩略图 URL(如果没有缓存则生成)
|
|
990
|
+
*/
|
|
991
|
+
async getThumbnailUrl(filePath) {
|
|
992
|
+
try {
|
|
993
|
+
const stats = await import_node_fs10.promises.stat(filePath);
|
|
994
|
+
const fileType = getFileType(filePath, stats);
|
|
995
|
+
if (fileType === "application" /* APPLICATION */ && this.getApplicationIcon) {
|
|
996
|
+
return await this.getApplicationIcon(filePath);
|
|
997
|
+
}
|
|
998
|
+
if (fileType !== "image" /* IMAGE */ && fileType !== "video" /* VIDEO */) {
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
const mtime = stats.mtime.getTime();
|
|
1002
|
+
const cachedPath = this.database.getThumbnailPathFast(filePath, mtime);
|
|
1003
|
+
if (cachedPath) {
|
|
1004
|
+
return this.urlEncoder(cachedPath);
|
|
1005
|
+
}
|
|
1006
|
+
const fileHash = await getFileHash(filePath);
|
|
1007
|
+
const thumbnailPath = await this.generateThumbnail(filePath, fileHash, mtime);
|
|
1008
|
+
if (thumbnailPath) {
|
|
1009
|
+
return this.urlEncoder(thumbnailPath);
|
|
1010
|
+
}
|
|
1011
|
+
return null;
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
console.error(`Error getting thumbnail for ${filePath}:`, error);
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* 生成缩略图
|
|
1019
|
+
*/
|
|
1020
|
+
async generateThumbnail(filePath, fileHash, mtime) {
|
|
1021
|
+
const cachedPath = this.database.getThumbnailPath(filePath, fileHash, mtime);
|
|
1022
|
+
if (cachedPath) {
|
|
1023
|
+
return cachedPath;
|
|
1024
|
+
}
|
|
1025
|
+
try {
|
|
1026
|
+
const ext = import_node_path10.default.extname(filePath).toLowerCase();
|
|
1027
|
+
const hashPrefix = fileHash.substring(0, 16);
|
|
1028
|
+
const thumbnailFileName = `${hashPrefix}.jpg`;
|
|
1029
|
+
const thumbnailPath = import_node_path10.default.join(this.database.getCacheDir(), thumbnailFileName);
|
|
1030
|
+
if (IMAGE_EXTENSIONS2.includes(ext) && this.imageProcessor) {
|
|
1031
|
+
await this.imageProcessor.resize(filePath, thumbnailPath, 256);
|
|
1032
|
+
} else if (VIDEO_EXTENSIONS2.includes(ext) && this.videoProcessor) {
|
|
1033
|
+
await this.videoProcessor.screenshot(filePath, thumbnailPath, "00:00:01", "256x256");
|
|
1034
|
+
} else {
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
this.database.saveThumbnail(filePath, fileHash, mtime, thumbnailPath);
|
|
1038
|
+
return thumbnailPath;
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
console.debug(`Error generating thumbnail for ${filePath}:`, error);
|
|
1041
|
+
return null;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* 批量生成缩略图
|
|
1046
|
+
*/
|
|
1047
|
+
async generateThumbnailsBatch(files) {
|
|
1048
|
+
await Promise.allSettled(
|
|
1049
|
+
files.map((file) => this.generateThumbnail(file.path, file.hash, file.mtime))
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* 删除缩略图
|
|
1054
|
+
*/
|
|
1055
|
+
deleteThumbnail(filePath) {
|
|
1056
|
+
this.database.deleteThumbnail(filePath);
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* 清理旧缩略图
|
|
1060
|
+
*/
|
|
1061
|
+
cleanupOldThumbnails() {
|
|
1062
|
+
this.database.cleanupOldThumbnails();
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
var thumbnailService = null;
|
|
1066
|
+
function initThumbnailService(options) {
|
|
1067
|
+
thumbnailService = new ThumbnailService(options);
|
|
1068
|
+
return thumbnailService;
|
|
1069
|
+
}
|
|
1070
|
+
function getThumbnailService() {
|
|
1071
|
+
return thumbnailService;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/thumbnail/database.ts
|
|
1075
|
+
var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
|
|
1076
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
1077
|
+
var import_node_fs11 = require("fs");
|
|
1078
|
+
var SqliteThumbnailDatabase = class {
|
|
1079
|
+
db = null;
|
|
1080
|
+
cacheDir;
|
|
1081
|
+
constructor(userDataPath) {
|
|
1082
|
+
this.cacheDir = import_node_path11.default.join(userDataPath, "cache");
|
|
1083
|
+
if (!(0, import_node_fs11.existsSync)(this.cacheDir)) {
|
|
1084
|
+
(0, import_node_fs11.mkdirSync)(this.cacheDir, { recursive: true });
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* 初始化数据库
|
|
1089
|
+
*/
|
|
1090
|
+
init() {
|
|
1091
|
+
if (this.db) return;
|
|
1092
|
+
const dbPath = import_node_path11.default.join(this.cacheDir, "thumbnails.db");
|
|
1093
|
+
this.db = new import_better_sqlite3.default(dbPath, {
|
|
1094
|
+
fileMustExist: false
|
|
1095
|
+
});
|
|
1096
|
+
this.db.pragma("journal_mode = WAL");
|
|
1097
|
+
this.db.exec(`
|
|
1098
|
+
CREATE TABLE IF NOT EXISTS thumbnails (
|
|
1099
|
+
file_path TEXT PRIMARY KEY,
|
|
1100
|
+
file_hash TEXT NOT NULL,
|
|
1101
|
+
mtime INTEGER NOT NULL,
|
|
1102
|
+
thumbnail_path TEXT NOT NULL,
|
|
1103
|
+
created_at INTEGER NOT NULL
|
|
1104
|
+
);
|
|
1105
|
+
|
|
1106
|
+
CREATE INDEX IF NOT EXISTS idx_file_hash ON thumbnails(file_hash);
|
|
1107
|
+
CREATE INDEX IF NOT EXISTS idx_mtime ON thumbnails(mtime);
|
|
1108
|
+
`);
|
|
1109
|
+
}
|
|
1110
|
+
getCacheDir() {
|
|
1111
|
+
return this.cacheDir;
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* 快速查询缩略图(用路径和修改时间,不需要哈希)
|
|
1115
|
+
* 比计算文件哈希快很多
|
|
1116
|
+
*/
|
|
1117
|
+
getThumbnailPathFast(filePath, mtime) {
|
|
1118
|
+
if (!this.db) this.init();
|
|
1119
|
+
const stmt = this.db.prepare(`
|
|
1120
|
+
SELECT thumbnail_path
|
|
1121
|
+
FROM thumbnails
|
|
1122
|
+
WHERE file_path = ? AND mtime = ?
|
|
1123
|
+
`);
|
|
1124
|
+
const row = stmt.get(filePath, mtime);
|
|
1125
|
+
if (row && (0, import_node_fs11.existsSync)(row.thumbnail_path)) {
|
|
1126
|
+
return row.thumbnail_path;
|
|
1127
|
+
}
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
getThumbnailPath(filePath, fileHash, mtime) {
|
|
1131
|
+
if (!this.db) this.init();
|
|
1132
|
+
const stmt = this.db.prepare(`
|
|
1133
|
+
SELECT thumbnail_path, mtime
|
|
1134
|
+
FROM thumbnails
|
|
1135
|
+
WHERE file_path = ? AND file_hash = ?
|
|
1136
|
+
`);
|
|
1137
|
+
const row = stmt.get(filePath, fileHash);
|
|
1138
|
+
if (row && row.mtime === mtime && (0, import_node_fs11.existsSync)(row.thumbnail_path)) {
|
|
1139
|
+
return row.thumbnail_path;
|
|
1140
|
+
}
|
|
1141
|
+
return null;
|
|
1142
|
+
}
|
|
1143
|
+
saveThumbnail(filePath, fileHash, mtime, thumbnailPath) {
|
|
1144
|
+
if (!this.db) this.init();
|
|
1145
|
+
const stmt = this.db.prepare(`
|
|
1146
|
+
INSERT OR REPLACE INTO thumbnails
|
|
1147
|
+
(file_path, file_hash, mtime, thumbnail_path, created_at)
|
|
1148
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1149
|
+
`);
|
|
1150
|
+
stmt.run(filePath, fileHash, mtime, thumbnailPath, Date.now());
|
|
1151
|
+
}
|
|
1152
|
+
deleteThumbnail(filePath) {
|
|
1153
|
+
if (!this.db) this.init();
|
|
1154
|
+
const stmt = this.db.prepare("DELETE FROM thumbnails WHERE file_path = ?");
|
|
1155
|
+
stmt.run(filePath);
|
|
1156
|
+
}
|
|
1157
|
+
cleanupOldThumbnails() {
|
|
1158
|
+
if (!this.db) this.init();
|
|
1159
|
+
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1e3;
|
|
1160
|
+
const stmt = this.db.prepare("DELETE FROM thumbnails WHERE created_at < ?");
|
|
1161
|
+
stmt.run(thirtyDaysAgo);
|
|
1162
|
+
}
|
|
1163
|
+
close() {
|
|
1164
|
+
if (this.db) {
|
|
1165
|
+
this.db.close();
|
|
1166
|
+
this.db = null;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
var thumbnailDb = null;
|
|
1171
|
+
function createSqliteThumbnailDatabase(userDataPath) {
|
|
1172
|
+
if (!thumbnailDb) {
|
|
1173
|
+
thumbnailDb = new SqliteThumbnailDatabase(userDataPath);
|
|
1174
|
+
thumbnailDb.init();
|
|
1175
|
+
}
|
|
1176
|
+
return thumbnailDb;
|
|
1177
|
+
}
|
|
1178
|
+
function getSqliteThumbnailDatabase() {
|
|
1179
|
+
return thumbnailDb;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/thumbnail/processors.ts
|
|
1183
|
+
var import_node_child_process2 = require("child_process");
|
|
1184
|
+
var import_node_util2 = require("util");
|
|
1185
|
+
var execFileAsync = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
1186
|
+
function createSharpImageProcessor(sharp) {
|
|
1187
|
+
return {
|
|
1188
|
+
async resize(filePath, outputPath, size) {
|
|
1189
|
+
await sharp(filePath).resize(size, size, {
|
|
1190
|
+
fit: "inside",
|
|
1191
|
+
withoutEnlargement: true
|
|
1192
|
+
}).jpeg({ quality: 85 }).toFile(outputPath);
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
function createFfmpegVideoProcessor(ffmpegPath) {
|
|
1197
|
+
return {
|
|
1198
|
+
async screenshot(filePath, outputPath, timestamp, size) {
|
|
1199
|
+
const scaleSize = size.replace("x", ":");
|
|
1200
|
+
await execFileAsync(ffmpegPath, [
|
|
1201
|
+
"-i",
|
|
1202
|
+
filePath,
|
|
1203
|
+
"-ss",
|
|
1204
|
+
timestamp,
|
|
1205
|
+
"-vframes",
|
|
1206
|
+
"1",
|
|
1207
|
+
"-vf",
|
|
1208
|
+
`scale=${scaleSize}:force_original_aspect_ratio=decrease`,
|
|
1209
|
+
"-y",
|
|
1210
|
+
outputPath
|
|
1211
|
+
]);
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1216
|
+
0 && (module.exports = {
|
|
1217
|
+
FileType,
|
|
1218
|
+
SqliteThumbnailDatabase,
|
|
1219
|
+
ThumbnailService,
|
|
1220
|
+
copyFiles,
|
|
1221
|
+
copyFilesToClipboard,
|
|
1222
|
+
createFfmpegVideoProcessor,
|
|
1223
|
+
createFile,
|
|
1224
|
+
createFolder,
|
|
1225
|
+
createSharpImageProcessor,
|
|
1226
|
+
createSqliteThumbnailDatabase,
|
|
1227
|
+
deleteFiles,
|
|
1228
|
+
exists,
|
|
1229
|
+
formatDate,
|
|
1230
|
+
formatDateTime,
|
|
1231
|
+
formatFileSize,
|
|
1232
|
+
getAllSystemPaths,
|
|
1233
|
+
getApplicationIcon,
|
|
1234
|
+
getClipboardFiles,
|
|
1235
|
+
getFileHash,
|
|
1236
|
+
getFileHashes,
|
|
1237
|
+
getFileInfo,
|
|
1238
|
+
getFileType,
|
|
1239
|
+
getHomeDirectory,
|
|
1240
|
+
getPlatform,
|
|
1241
|
+
getSqliteThumbnailDatabase,
|
|
1242
|
+
getSystemPath,
|
|
1243
|
+
getThumbnailService,
|
|
1244
|
+
initThumbnailService,
|
|
1245
|
+
isDirectory,
|
|
1246
|
+
isMediaFile,
|
|
1247
|
+
isPreviewable,
|
|
1248
|
+
moveFiles,
|
|
1249
|
+
pasteFiles,
|
|
1250
|
+
readDirectory,
|
|
1251
|
+
readFileContent,
|
|
1252
|
+
readImageAsBase64,
|
|
1253
|
+
renameFile,
|
|
1254
|
+
searchFiles,
|
|
1255
|
+
searchFilesStream,
|
|
1256
|
+
searchFilesSync,
|
|
1257
|
+
writeFileContent
|
|
1258
|
+
});
|
|
1259
|
+
//# sourceMappingURL=index.cjs.map
|