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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fish-dock-plugin-filemgr",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "FISH Dock 文件管理插件:目录浏览 + 文件预览(Markdown / 图片 / Office / 代码)+ 文件暂存中转站,兼容 FISH Dock v1.2.3 及更早官方发行版。",
5
5
  "type": "module",
6
6
  "files": [
package/plugin.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "schema": 1,
3
3
  "id": "jassdingk.filemgr",
4
4
  "name": "文件管理",
5
- "version": "1.0.7",
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": "330dcaaf54a8291462d17ef6a28d7b49465d7a603add478f03fb35feb49a4bee",
31
- "plugins/filemgr/backend.mjs": "7cd37f01f6d4ae8b9693d2b1642f5078855fd405dae1e1cf9a6c10d7a8c15b15",
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": "c6d6e4041901e37d023205d8d7025b597294a8503bb74be74ed774eeb86fe1e3",
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
- async function searchFiles(query, list) {
172
- const raw = String(query || '').trim()
173
- const needle = raw.toLowerCase()
174
- if (!needle) return { query: raw, results: [], scanned: 0, dirs: 0, truncated: false, timedOut: false }
175
- const MAX_RESULTS = 300
176
- const MAX_SCAN = 80000
177
- const MAX_DEPTH = 14
178
- const deadline = Date.now() + 8000
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
- let timedOut = false
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 (results.length >= MAX_RESULTS || scanned >= MAX_SCAN || Date.now() > deadline) { truncated = true; timedOut = Date.now() > deadline; break }
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 (results.length >= MAX_RESULTS || scanned >= MAX_SCAN || Date.now() > deadline) { truncated = true; timedOut = Date.now() > deadline; break }
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: cur.root, size, mtime })
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, root: cur.root })
205
+ stack.push({ dir: full, depth: cur.depth + 1 })
208
206
  }
209
207
  }
210
208
  }
211
- results.sort((a, b) => {
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
- return { query: raw, results, scanned, dirs, truncated, timedOut }
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:auto;min-width:0;max-width:260px;flex:0 1 auto}
16
- .search-box input[type=search]{width:180px;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}
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}