fish-dock-plugin-filemgr 1.0.5 → 1.0.6

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.5",
3
+ "version": "1.0.6",
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.5",
5
+ "version": "1.0.6",
6
6
  "hostApi": 1,
7
7
  "platforms": [
8
8
  "windows-x86_64"
@@ -27,19 +27,19 @@
27
27
  ],
28
28
  "files": {
29
29
  "bridge.js": "f38f93994e3cb2b71f2ff1810b3f68fb42f4261550f8ab01b0bfa3eecd5f94ee",
30
- "package.json": "7e9124508cc113f5039ce514573a5f8f280faf9392e4677d30bb144c55351e5c",
30
+ "package.json": "d2b066289c54ebfb40da8591f8dbaf261c8cbd0dcb80ea03f5206c7f0aa50e35",
31
31
  "plugins/filemgr/backend.mjs": "b448aa688ff11f2abc4c5281b4714646ed6c384473d873cfe431a8e64b066d26",
32
32
  "plugins/filemgr/clip.ps1": "b0a32fca35cf2d25133338f46e20a0db20d78c5bb6b5c2aab899998bfbd6c6ab",
33
33
  "plugins/filemgr/dock.css": "e795214dd39de3b473ac0b1a7b79f6922a88c761fca4d3ea4da8fd9b60188bcf",
34
- "plugins/filemgr/filemgr.css": "16c621471133d29c6942781bedf86e0a4de806865ff004b666bcad8b1929434e",
34
+ "plugins/filemgr/filemgr.css": "79b878b29499284935d71dfa877716232f85ed40d864437382a059fa8ced0479",
35
35
  "plugins/filemgr/FolderPicker.ps1": "4b7f86c543e06fd8f93fa5d8002997d944c8189cb0ae031bedf864ea89196427",
36
36
  "plugins/filemgr/page.css": "0801a05b5148cd091a68957beac21cc982a7818ef4707cd06fd11b20adbc7765",
37
37
  "plugins/filemgr/page.html": "3cfda31be29cd23b1e0f7f6004b6c03522c3526b78f9df4711e320962a660831",
38
- "plugins/filemgr/page.js": "282f11d803ddc8bb87a0a99d8af68315f4ff7304672072da838a07a47c5cdc62",
38
+ "plugins/filemgr/page.js": "74a7ccae18f7857be9656f14db07c2bddbf4591fff57a7eb16722ee4128e739d",
39
39
  "plugins/filemgr/pick-files.ps1": "88bff92d9a2e5b69e9411bb372441ff0b86899b7f5b31f5ed8d154c4f6fc2f37",
40
40
  "plugins/filemgr/pick-folder.ps1": "ff031c14df32149c1c80c7ab6a21710c1a68e823e96e3068c458a60902ae3b6f",
41
41
  "plugins/filemgr/preview.mjs": "a5596e77180fe7928443244a0148b6d3ea8381a1f886ee01f9a08be038c7ee80",
42
- "plugins/filemgr/sdk.mjs": "76b052b8d99ff6b6b0b149aee7479d1129bb248a959625642282184978065f38",
42
+ "plugins/filemgr/sdk.mjs": "91061dae337157c88e2303d1899f9149ccecd656df4675c76dd5721bc405bf07",
43
43
  "plugins/filemgr/stash.css": "fce2aaa13a86dbd7675b685c533f0bb916c7e610fc612a3ade660c67069f5662",
44
44
  "plugins/filemgr/stash.mjs": "c33cea6b845a3847950d26a10b9aba1ef68d39b1f9fc423b23055dead7638866",
45
45
  "README.md": "e9fb4ca9831b778a1d530b67518fcaf2b42cf02494b9d68deaad16bf2b947687"
@@ -71,6 +71,14 @@ body[data-resizing=true]{user-select:none;cursor:col-resize}
71
71
  .stash-group>.stash-tag.active>.stash-tag-count{opacity:.8}
72
72
  .stash-group>.stash-tag.active>.stash-tag-del{color:#1a1035;opacity:.75}
73
73
  .stash-group>.stash-tag.active>.stash-tag-del:hover{color:#7a1010}
74
+ /* 拖动文件悬停到某个分组标签上:高亮该分组名,提示松手即落入此分组 */
75
+ .stash-group>.stash-tag.drop-hover{
76
+ color:#1a1035;background:var(--dock-accent);border-color:var(--dock-accent);
77
+ box-shadow:0 0 0 2px color-mix(in srgb,var(--dock-accent) 40%,transparent);
78
+ }
79
+ .stash-group>.stash-tag.drop-hover>.stash-tag-dot{background:#1a1035}
80
+ .stash-group>.stash-tag.drop-hover>.stash-tag-count{opacity:.85}
81
+ .stash-group>.stash-tag.drop-hover>.stash-tag-del{color:#1a1035;opacity:.75}
74
82
  /* 右侧文件列表:已在暂存/分组里的文件 */
75
83
  .filelist .row.stashed .nm{color:var(--dock-accent)}
76
84
  .stash-badge{flex:none;font-size:10px;line-height:1;padding:2px 6px;border-radius:999px;
@@ -1472,6 +1472,7 @@
1472
1472
 
1473
1473
  const summary = el('summary', {
1474
1474
  class: 'stash-tag' + (active ? ' active' : ''),
1475
+ dataset: { group: bucket.key }, // 供拖拽命中:拖到此标签上即高亮该组名并落入该分组
1475
1476
  title: `${bucket.label} · ${bucket.items.length} 项(点箭头/整条折叠;点右侧漏斗图标只在看本组与全部间切换)`,
1476
1477
  })
1477
1478
  // 单击整条标签 = 折叠/展开(summary 默认行为,不拦截)。
@@ -1765,11 +1766,12 @@
1765
1766
  // 批量加入(框选后拖进暂存区走这一条)。
1766
1767
  // 后端 stash.add 本来就吃 paths 数组,所以只是把多行一次性递过去;
1767
1768
  // 逐个报告"加了几项/几项重复",比只弹一个含糊的 toast 更好懂。
1768
- async function addManyToStash(paths) {
1769
+ async function addManyToStash(paths, group) {
1769
1770
  const list = (paths || []).filter(Boolean)
1770
1771
  if (!list.length) return
1771
- const group = state.view === 'all' ? '' : state.view
1772
- const row = await callSoft('stash.add', { paths: list, group })
1772
+ // 显式传入的分组(拖到某个分组标签上)优先;未指定时回退到当前视图分组。
1773
+ const targetGroup = group === undefined ? (state.view === 'all' ? '' : state.view) : group
1774
+ const row = await callSoft('stash.add', { paths: list, group: targetGroup })
1773
1775
  if (row && row.ok) {
1774
1776
  const added = Array.isArray(row.items) ? row.items.length : 0
1775
1777
  const dup = list.length - added
@@ -1800,6 +1802,7 @@
1800
1802
  let moved = false
1801
1803
  let suppressClick = false
1802
1804
  let ghost = null
1805
+ let dropGroup = undefined // 拖拽时命中的分组标签 key(undefined=未命中,回退当前视图)
1803
1806
 
1804
1807
  const rows = () => Array.from(list.querySelectorAll('.row[data-path]'))
1805
1808
 
@@ -1836,6 +1839,8 @@
1836
1839
  if (ghost) { ghost.remove(); ghost = null }
1837
1840
  for (const node of list.querySelectorAll('.row.drag-src')) node.classList.remove('drag-src')
1838
1841
  if (stashCol) stashCol.classList.remove('drop-target')
1842
+ dropGroup = undefined
1843
+ for (const node of document.querySelectorAll('.stash-tag.drop-hover')) node.classList.remove('drop-hover')
1839
1844
  }
1840
1845
 
1841
1846
  list.addEventListener('pointerdown', event => {
@@ -1920,8 +1925,14 @@
1920
1925
  } else if (mode === 'drag' && ghost) {
1921
1926
  ghost.style.left = `${event.clientX + 14}px`
1922
1927
  ghost.style.top = `${event.clientY + 14}px`
1923
- const over = stashCol && document.elementFromPoint(event.clientX, event.clientY)?.closest('#stash-col')
1928
+ const under = document.elementFromPoint(event.clientX, event.clientY)
1929
+ const over = stashCol && under?.closest('#stash-col')
1924
1930
  if (stashCol) stashCol.classList.toggle('drop-target', !!over)
1931
+ // 命中哪个分组标签,就高亮哪个分组名并把它作为落点;未命中分组标签则回退当前视图分组。
1932
+ const hitTag = under && under.closest('.stash-tag[data-group]')
1933
+ dropGroup = hitTag ? hitTag.dataset.group : undefined
1934
+ for (const node of document.querySelectorAll('.stash-tag.drop-hover')) node.classList.remove('drop-hover')
1935
+ if (hitTag) hitTag.classList.add('drop-hover')
1925
1936
  }
1926
1937
  event.preventDefault()
1927
1938
  })
@@ -1930,6 +1941,7 @@
1930
1941
  if (mode === 'idle') return
1931
1942
  const was = mode
1932
1943
  const paths = dragPaths.slice()
1944
+ const group = dropGroup // 松手前记录命中的分组(reset 会清空)
1933
1945
  const dropped = was === 'drag' && !!(
1934
1946
  stashCol && document.elementFromPoint(event.clientX, event.clientY)?.closest('#stash-col')
1935
1947
  )
@@ -1940,7 +1952,7 @@
1940
1952
  setTimeout(() => { suppressClick = false }, 0)
1941
1953
  }
1942
1954
  reset()
1943
- if (dropped && paths.length) await addManyToStash(paths)
1955
+ if (dropped && paths.length) await addManyToStash(paths, group)
1944
1956
  }
1945
1957
 
1946
1958
  list.addEventListener('pointerup', event => { finish(event) })
@@ -3,7 +3,7 @@ const CATEGORIES = new Set(['credentials','roster','keys','config','exports','ba
3
3
  const DEFAULT_LIMIT = 1024 * 1024;
4
4
  export function createPlugin({ id, input = process.stdin, output = process.stdout,
5
5
  activate = async () => {}, deactivate = async () => {}, onEvent = async () => {}, commands = {},
6
- onError = () => {}, maxLine = DEFAULT_LIMIT, requestTimeout = 5000 } = {}) {
6
+ onError = () => {}, maxLine = DEFAULT_LIMIT, requestTimeout = 12000 } = {}) {
7
7
  if (!/^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z0-9]+(?:-[a-z0-9]+)*$/.test(id ?? '')) throw new Error('插件 ID 格式无效');
8
8
  if (!Number.isSafeInteger(maxLine) || maxLine < 128 || maxLine > DEFAULT_LIMIT) throw new Error('协议帧大小无效');
9
9
  if (!Number.isSafeInteger(requestTimeout) || requestTimeout < 1 || requestTimeout > 30000) throw new Error('宿主调用时限无效');
@@ -94,7 +94,10 @@ export function createPlugin({ id, input = process.stdin, output = process.stdou
94
94
  if (!row || typeof row !== 'object' || Array.isArray(row)) throw new Error('插件协议应为 JSON 对象');
95
95
  if (!Object.hasOwn(row,'method')) {
96
96
  const waiter = pending.get(row.id);
97
- if (!waiter) throw new Error('宿主响应编号不匹配');
97
+ // 迟到/重复的宿主响应(原请求已因超时被清理):静默丢弃,绝不让整个插件会话崩溃。
98
+ // 原实现此处 throw 会经 receive() → close()+onError() 杀死插件;任何一次超过
99
+ // requestTimeout 的存储调用,其迟到响应都会触发。调用方早已通过超时 reject 感知失败。
100
+ if (!waiter) return;
98
101
  pending.delete(row.id); clearTimeout(waiter.timer);
99
102
  if (Object.hasOwn(row,'result') === Object.hasOwn(row,'error')) return waiter.reject(new Error('宿主响应结构无效'));
100
103
  if (row.error) waiter.reject(new Error('宿主存储操作失败')); else waiter.resolve(row.result);