fish-dock-plugin-filemgr 1.0.7 → 1.0.8
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/package.json +1 -1
- package/plugin.json +4 -4
- package/plugins/filemgr/backend.mjs +43 -18
- package/plugins/filemgr/filemgr.css +4 -2
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"id": "jassdingk.filemgr",
|
|
4
4
|
"name": "文件管理",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.8",
|
|
6
6
|
"hostApi": 1,
|
|
7
7
|
"platforms": [
|
|
8
8
|
"windows-x86_64"
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
],
|
|
28
28
|
"files": {
|
|
29
29
|
"bridge.js": "f38f93994e3cb2b71f2ff1810b3f68fb42f4261550f8ab01b0bfa3eecd5f94ee",
|
|
30
|
-
"package.json": "
|
|
31
|
-
"plugins/filemgr/backend.mjs": "
|
|
30
|
+
"package.json": "3d4301227f9025eac6274be6cc660f4036943fca12a231c1486c1c130d0fd521",
|
|
31
|
+
"plugins/filemgr/backend.mjs": "f16d378743eaa51759e53fe8974354f5cb95f7d6694dafba48e041cce7cadbdd",
|
|
32
32
|
"plugins/filemgr/clip.ps1": "b0a32fca35cf2d25133338f46e20a0db20d78c5bb6b5c2aab899998bfbd6c6ab",
|
|
33
33
|
"plugins/filemgr/dock.css": "e795214dd39de3b473ac0b1a7b79f6922a88c761fca4d3ea4da8fd9b60188bcf",
|
|
34
|
-
"plugins/filemgr/filemgr.css": "
|
|
34
|
+
"plugins/filemgr/filemgr.css": "f6baa1c309d9655d32a16d284cad00b862a0504a254190f10b52e505fd77de63",
|
|
35
35
|
"plugins/filemgr/FolderPicker.ps1": "4b7f86c543e06fd8f93fa5d8002997d944c8189cb0ae031bedf864ea89196427",
|
|
36
36
|
"plugins/filemgr/page.css": "0801a05b5148cd091a68957beac21cc982a7818ef4707cd06fd11b20adbc7765",
|
|
37
37
|
"plugins/filemgr/page.html": "415e9ae0d7ed85040974498d4d5581d707a668d55c917181025d4fee4dda67de",
|
|
@@ -168,28 +168,26 @@ const SEARCH_HIDDEN = new Set([
|
|
|
168
168
|
'$RECYCLE.BIN', 'System Volume Information', 'AppData', 'Windows', 'Program Files', 'Program Files (x86)',
|
|
169
169
|
'ProgramData', 'Recovery', 'PerfLogs', '$WinREAgent', 'OneDriveTemp',
|
|
170
170
|
])
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
171
|
+
const MAX_DEPTH = 16
|
|
172
|
+
// 每个根目录单独扫描、并发推进:避免某一根(如 C:\Users 用户目录)先把总配额吃光,
|
|
173
|
+
// 导致其它盘(D:/E:/...)根本没扫到 —— 这正是「经常检索不到」的主因。
|
|
174
|
+
const PER_ROOT_SCAN = 60000
|
|
175
|
+
const MAX_RESULTS = 800
|
|
176
|
+
const MAX_SCAN_TOTAL = 400000
|
|
177
|
+
const SEARCH_DEADLINE_MS = 12000 // 宿主单次调用硬超时 15s,留 3s 余量
|
|
178
|
+
|
|
179
|
+
async function scanRoot(root, needle, deadline, budget) {
|
|
179
180
|
const results = []
|
|
180
181
|
let scanned = 0
|
|
181
|
-
let dirs = 0
|
|
182
182
|
let truncated = false
|
|
183
|
-
|
|
184
|
-
const stack = list.map(r => ({ dir: r.path, depth: 0, root: r.label }))
|
|
183
|
+
const stack = [{ dir: root.path, depth: 0 }]
|
|
185
184
|
while (stack.length) {
|
|
186
|
-
if (
|
|
185
|
+
if (scanned >= budget || Date.now() > deadline) { truncated = true; break }
|
|
187
186
|
const cur = stack.pop()
|
|
188
187
|
let ents
|
|
189
188
|
try { ents = await fs.promises.readdir(cur.dir, { withFileTypes: true }) } catch { continue }
|
|
190
|
-
dirs++
|
|
191
189
|
for (const e of ents) {
|
|
192
|
-
if (
|
|
190
|
+
if (scanned >= budget || Date.now() > deadline) { truncated = true; break }
|
|
193
191
|
const name = e.name
|
|
194
192
|
if (!name || name.startsWith('.')) continue
|
|
195
193
|
if (e.isSymbolicLink && e.isSymbolicLink()) continue
|
|
@@ -201,20 +199,47 @@ async function searchFiles(query, list) {
|
|
|
201
199
|
if (name.toLowerCase().includes(needle)) {
|
|
202
200
|
let size = 0, mtime = 0
|
|
203
201
|
try { const s = await fs.promises.stat(full); size = s.size; mtime = s.mtimeMs } catch { /* 用 0 兜底 */ }
|
|
204
|
-
results.push({ name, path: full, dir: cur.dir, root:
|
|
202
|
+
results.push({ name, path: full, dir: cur.dir, root: root.label, size, mtime })
|
|
205
203
|
}
|
|
206
204
|
} else if (cur.depth < MAX_DEPTH) {
|
|
207
|
-
stack.push({ dir: full, depth: cur.depth + 1
|
|
205
|
+
stack.push({ dir: full, depth: cur.depth + 1 })
|
|
208
206
|
}
|
|
209
207
|
}
|
|
210
208
|
}
|
|
211
|
-
results
|
|
209
|
+
return { results, scanned, truncated }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function searchFiles(query, list) {
|
|
213
|
+
const raw = String(query || '').trim()
|
|
214
|
+
const needle = raw.toLowerCase()
|
|
215
|
+
if (!needle) return { query: raw, results: [], scanned: 0, dirs: 0, truncated: false, timedOut: false }
|
|
216
|
+
const deadline = Date.now() + SEARCH_DEADLINE_MS
|
|
217
|
+
// 每个根并发,单根预算 PER_ROOT_SCAN;总体上限 MAX_SCAN_TOTAL 兜底。
|
|
218
|
+
const parts = await Promise.all(list.map(r => scanRoot(r, needle, deadline, PER_ROOT_SCAN)))
|
|
219
|
+
const seen = new Set()
|
|
220
|
+
const all = []
|
|
221
|
+
let scanned = 0
|
|
222
|
+
let truncated = false
|
|
223
|
+
for (const p of parts) {
|
|
224
|
+
scanned += p.scanned
|
|
225
|
+
if (p.truncated) truncated = true
|
|
226
|
+
for (const it of p.results) {
|
|
227
|
+
const key = it.path.toLowerCase()
|
|
228
|
+
if (seen.has(key)) continue
|
|
229
|
+
seen.add(key)
|
|
230
|
+
all.push(it)
|
|
231
|
+
if (all.length >= MAX_SCAN_TOTAL) { truncated = true; break }
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
all.sort((a, b) => {
|
|
212
235
|
const ea = a.name.toLowerCase() === needle ? 1 : 0
|
|
213
236
|
const eb = b.name.toLowerCase() === needle ? 1 : 0
|
|
214
237
|
if (ea !== eb) return eb - ea
|
|
215
238
|
return (b.mtime || 0) - (a.mtime || 0)
|
|
216
239
|
})
|
|
217
|
-
|
|
240
|
+
if (all.length > MAX_RESULTS) { truncated = true }
|
|
241
|
+
const results = all.slice(0, MAX_RESULTS)
|
|
242
|
+
return { query: raw, results, scanned, dirs: 0, truncated, timedOut: Date.now() > deadline, total: all.length }
|
|
218
243
|
}
|
|
219
244
|
|
|
220
245
|
// ---- Everything 索引搜索(优先):调用本机 Everything 的 HTTP JSON 接口 ----
|
|
@@ -12,8 +12,10 @@ body.fm-layout, main.fm{height:820px;display:flex;flex-direction:column;padding:
|
|
|
12
12
|
.fm-head .fm-meta:empty{display:none}
|
|
13
13
|
|
|
14
14
|
/* 预览工具栏右侧的全局搜索框(在「📌 固定当前文件夹」右边) */
|
|
15
|
-
.search-box{display:flex;align-items:center;gap:4px;margin-left:
|
|
16
|
-
.search-box input[type=search]{width:
|
|
15
|
+
.search-box{display:flex;align-items:center;gap:4px;margin-left:8px;min-width:120px;flex:1 1 auto}
|
|
16
|
+
.search-box input[type=search]{width:100%;min-width:80px;height:26px;padding:0 9px;font:inherit;font-size:12px;color:var(--dock-fg);background:var(--dock-control);border:1px solid var(--dock-line);border-radius:999px;outline:none}
|
|
17
|
+
/* 工具栏里的按钮固定宽度,搜索框加宽时不被压缩,「固定当前文件夹」等功能不受影响 */
|
|
18
|
+
#preview-col .preview-toolbar > button{flex:none}
|
|
17
19
|
.search-box input[type=search]:focus{border-color:var(--dock-accent)}
|
|
18
20
|
.search-box #search-clear{margin:0;padding:2px 7px;border-radius:999px;flex:none}
|
|
19
21
|
.search-head{padding:6px 2px 8px;color:var(--dock-muted);font-size:12px}
|