@civilization/dsh-workspace-files 0.3.0 → 0.3.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.
package/README.md CHANGED
@@ -17,7 +17,12 @@ DeepSeek Harness (DSH) 原生增强型文件浏览器插件。
17
17
  - 🎯 **官方原生交互式 Tag 胶囊引用 (ReferenceChipNode)**:
18
18
  - 深度贯通会话输入框的 Lexical AST 引擎,点击 `@引用` 直接生成与官方敲 `@` 回车 100% 一模一样带图标的深色圆角 **Tag 胶囊卡片**(`[📁 folder/]` 与 `[📄 file]`);
19
19
  - 智能消除用户光标前已键入的 `@` 字符,杜绝 `@@path` 冗余;文件与文件夹均支持一键引用。
20
- - 📂 **默认折叠与持久化展开记忆 (localStorage)**:
20
+ - 📂 **在文件资源管理器中显示 (Reveal in Explorer)**:
21
+ - 悬浮在文件上点击,即刻在 Windows 资源管理器中打开并高亮选中该文件(`explorer.exe /select`);
22
+ - 悬浮在目录上点击,直接在资源管理器中打开该文件夹,告别手动层层翻找目录的繁琐操作。
23
+ - 🚀 **用系统默认应用打开 (Open with Default App)**:
24
+ - 悬浮在任意文件/目录上点击,直接调起系统默认程序打开;对于图片、音频、PDF、Office 文档等外部文件极为方便。
25
+ - 📁 **默认折叠与持久化展开记忆 (localStorage)**:
21
26
  - 首次打开默认全部折叠关闭,清爽简洁;
22
27
  - 用户展开的每一个文件夹均按工作区独立持久化记忆,无论刷新页面还是切换会话再切回,均完美保持展开状态。
23
28
  - 📋 **快速复制相对路径**:支持一键复制文件/目录的相对工作区路径,方便在终端或配置中直接使用。
package/lib/client.js CHANGED
@@ -265,6 +265,47 @@ window.__ModuleLoader__.load({
265
265
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "4 6 8 10 12 6" })
266
266
  });
267
267
  }
268
+ function IconExplorer({ size = 12 }) {
269
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
270
+ width: size,
271
+ height: size,
272
+ viewBox: "0 0 16 16",
273
+ fill: "currentColor",
274
+ style: {
275
+ display: "inline-block",
276
+ verticalAlign: "middle",
277
+ flexShrink: 0
278
+ },
279
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M1.75 2A1.75 1.75 0 000 3.75v8.5C0 13.216.784 14 1.75 14h12.5A1.75 1.75 0 0016 12.25v-6.5A1.75 1.75 0 0014.25 4H7.5a.25.25 0 01-.2-.1l-.9-1.2C6.07 2.26 5.55 2 5 2H1.75zM1.5 5.5h13v6.75a.25.25 0 01-.25.25H1.75a.25.25 0 01-.25-.25V5.5z" })
280
+ });
281
+ }
282
+ function IconExternalApp({ size = 12 }) {
283
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
284
+ width: size,
285
+ height: size,
286
+ viewBox: "0 0 16 16",
287
+ fill: "none",
288
+ stroke: "currentColor",
289
+ strokeWidth: "1.35",
290
+ strokeLinecap: "round",
291
+ strokeLinejoin: "round",
292
+ style: {
293
+ display: "inline-block",
294
+ verticalAlign: "middle",
295
+ flexShrink: 0
296
+ },
297
+ children: [
298
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12.5 9.5v3.25a1.25 1.25 0 01-1.25 1.25H3.25A1.25 1.25 0 012 12.75V4.75A1.25 1.25 0 013.25 3.5H6.5" }),
299
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: "10 2 14 2 14 6" }),
300
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
301
+ x1: "6.5",
302
+ y1: "9.5",
303
+ x2: "13.5",
304
+ y2: "2.5"
305
+ })
306
+ ]
307
+ });
308
+ }
268
309
  //#endregion
269
310
  //#region src/client/index.tsx
270
311
  const inject = ["slots", "sidebarRightTabs"];
@@ -769,7 +810,7 @@ window.__ModuleLoader__.load({
769
810
  window.addEventListener("dsh-workspace-files:config-change", handleCfgChange);
770
811
  return () => window.removeEventListener("dsh-workspace-files:config-change", handleCfgChange);
771
812
  }, []);
772
- const loadTree = (0, react.useCallback)(async (quiet = false) => {
813
+ const loadTree = (0, react.useCallback)(async (quiet = false, forceRefresh = false) => {
773
814
  if (!quiet) setLoading(true);
774
815
  setError(null);
775
816
  try {
@@ -777,7 +818,8 @@ window.__ModuleLoader__.load({
777
818
  sessionId,
778
819
  cwd,
779
820
  showHidden,
780
- maxDepth: activeConfig.maxDepth
821
+ maxDepth: activeConfig.maxDepth,
822
+ forceRefresh
781
823
  });
782
824
  setData(res);
783
825
  setExpandedPaths((prev) => {
@@ -885,6 +927,44 @@ window.__ModuleLoader__.load({
885
927
  else showToastMsg(`已复制引用: ${mentionText.trim()}`);
886
928
  });
887
929
  };
930
+ const handleRevealInExplorer = (e, item) => {
931
+ e.stopPropagation();
932
+ fetch("/dsh-workspace-files/api/files.open", {
933
+ method: "POST",
934
+ headers: { "content-type": "application/json" },
935
+ body: JSON.stringify({
936
+ action: "reveal",
937
+ path: item.path,
938
+ isDirectory: Boolean(item.isDirectory),
939
+ sessionId,
940
+ cwd
941
+ })
942
+ }).then((res) => res.json()).then((data) => {
943
+ if (data.ok) showToastMsg(`已在资源管理器中显示: ${item.name}`);
944
+ else showToastMsg(`打开失败: ${data.error?.message || "未知错误"}`);
945
+ }).catch((err) => {
946
+ showToastMsg(`请求失败: ${err?.message || String(err)}`);
947
+ });
948
+ };
949
+ const handleOpenDefaultApp = (e, item) => {
950
+ e.stopPropagation();
951
+ fetch("/dsh-workspace-files/api/files.open", {
952
+ method: "POST",
953
+ headers: { "content-type": "application/json" },
954
+ body: JSON.stringify({
955
+ action: "openDefault",
956
+ path: item.path,
957
+ isDirectory: Boolean(item.isDirectory),
958
+ sessionId,
959
+ cwd
960
+ })
961
+ }).then((res) => res.json()).then((data) => {
962
+ if (data.ok) showToastMsg(`已启动系统应用打开: ${item.name}`);
963
+ else showToastMsg(`打开失败: ${data.error?.message || "未知错误"}`);
964
+ }).catch((err) => {
965
+ showToastMsg(`请求失败: ${err?.message || String(err)}`);
966
+ });
967
+ };
888
968
  const [searchViewMode, setSearchViewMode] = (0, react.useState)("flat");
889
969
  const [searchExpandedPaths, setSearchExpandedPaths] = (0, react.useState)(() => /* @__PURE__ */ new Set());
890
970
  const { filteredTreeItems, flatSearchResults } = (0, react.useMemo)(() => {
@@ -1044,7 +1124,7 @@ window.__ModuleLoader__.load({
1044
1124
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1045
1125
  type: "button",
1046
1126
  className: "dsh-files-tool-btn",
1047
- onClick: () => void loadTree(true),
1127
+ onClick: () => void loadTree(false, true),
1048
1128
  disabled: loading,
1049
1129
  title: "刷新文件树",
1050
1130
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconRefresh, { size: 13 })
@@ -1107,7 +1187,8 @@ window.__ModuleLoader__.load({
1107
1187
  children: [
1108
1188
  "找到 ",
1109
1189
  flatSearchResults.length,
1110
- " 个匹配项"
1190
+ " 个匹配项",
1191
+ flatSearchResults.length > 150 ? "(展示前 150 项)" : ""
1111
1192
  ]
1112
1193
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1113
1194
  type: "button",
@@ -1116,9 +1197,9 @@ window.__ModuleLoader__.load({
1116
1197
  title: "切换为可折叠的树形视图",
1117
1198
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTree, { size: 12 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "切为树形" })]
1118
1199
  })]
1119
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1200
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1120
1201
  className: "dsh-files-flat-list",
1121
- children: flatSearchResults.map(({ item, parentDir }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1202
+ children: [flatSearchResults.slice(0, 150).map(({ item, parentDir }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1122
1203
  className: `dsh-files-flat-row ${item.isDirectory ? "is-directory" : ""}`,
1123
1204
  onClick: () => {
1124
1205
  if (item.isDirectory) handleJumpToFolderInTree(item.path);
@@ -1162,25 +1243,51 @@ window.__ModuleLoader__.load({
1162
1243
  }),
1163
1244
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1164
1245
  className: "dsh-files-row-actions",
1165
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1166
- type: "button",
1167
- className: "dsh-files-action-btn dsh-files-action-at",
1168
- onClick: (e) => handleMention(e, item),
1169
- title: `一键 @ 引用此${item.isDirectory ? "目录" : "文件"}到当前会话`,
1170
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAt, { size: 13 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1171
- className: "dsh-files-action-at-text",
1172
- children: "@引用"
1173
- })]
1174
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1175
- type: "button",
1176
- className: "dsh-files-action-btn",
1177
- onClick: (e) => handleCopyPath(e, item.path),
1178
- title: "复制相对路径",
1179
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCopy, { size: 12 })
1180
- })]
1246
+ children: [
1247
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1248
+ type: "button",
1249
+ className: "dsh-files-action-btn dsh-files-action-at",
1250
+ onClick: (e) => handleMention(e, item),
1251
+ title: `一键 @ 引用此${item.isDirectory ? "目录" : "文件"}到当前会话`,
1252
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAt, { size: 13 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1253
+ className: "dsh-files-action-at-text",
1254
+ children: "@引用"
1255
+ })]
1256
+ }),
1257
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1258
+ type: "button",
1259
+ className: "dsh-files-action-btn",
1260
+ onClick: (e) => handleRevealInExplorer(e, item),
1261
+ title: `在文件资源管理器中显示此${item.isDirectory ? "目录" : "文件"}`,
1262
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconExplorer, { size: 12 })
1263
+ }),
1264
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1265
+ type: "button",
1266
+ className: "dsh-files-action-btn",
1267
+ onClick: (e) => handleOpenDefaultApp(e, item),
1268
+ title: `用系统默认应用打开此${item.isDirectory ? "目录" : "文件"}`,
1269
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconExternalApp, { size: 12 })
1270
+ }),
1271
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1272
+ type: "button",
1273
+ className: "dsh-files-action-btn",
1274
+ onClick: (e) => handleCopyPath(e, item.path),
1275
+ title: "复制相对路径",
1276
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCopy, { size: 12 })
1277
+ })
1278
+ ]
1181
1279
  })
1182
1280
  ]
1183
- }, item.path))
1281
+ }, item.path)), flatSearchResults.length > 150 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1282
+ style: {
1283
+ padding: "10px 14px",
1284
+ fontSize: 11,
1285
+ textAlign: "center",
1286
+ color: "var(--dsw-alias-label-tertiary, #8b93a1)",
1287
+ borderTop: "1px dashed var(--dsw-alias-border-l2, #e5e7eb)"
1288
+ },
1289
+ children: "匹配项较多,已限制展示前 150 项以保证流畅度,请输入更详细关键字"
1290
+ })]
1184
1291
  })]
1185
1292
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1186
1293
  className: "dsh-files-tree",
@@ -1213,7 +1320,9 @@ window.__ModuleLoader__.load({
1213
1320
  },
1214
1321
  onOpenFile,
1215
1322
  onMention: handleMention,
1216
- onCopyPath: handleCopyPath
1323
+ onCopyPath: handleCopyPath,
1324
+ onRevealInExplorer: handleRevealInExplorer,
1325
+ onOpenDefaultApp: handleOpenDefaultApp
1217
1326
  }, item.path))]
1218
1327
  })
1219
1328
  }),
@@ -1225,7 +1334,7 @@ window.__ModuleLoader__.load({
1225
1334
  });
1226
1335
  }
1227
1336
  function FileTreeNode(props) {
1228
- const { item, depth, query, isSearchMode, expandedPaths, onToggleExpand, onOpenFile, onMention, onCopyPath } = props;
1337
+ const { item, depth, query, isSearchMode, expandedPaths, onToggleExpand, onOpenFile, onMention, onCopyPath, onRevealInExplorer, onOpenDefaultApp } = props;
1229
1338
  const isExpanded = expandedPaths.has(item.path);
1230
1339
  if (item.isDirectory) {
1231
1340
  const childCount = item.children?.length ?? 0;
@@ -1263,22 +1372,39 @@ window.__ModuleLoader__.load({
1263
1372
  }),
1264
1373
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1265
1374
  className: "dsh-files-row-actions",
1266
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1267
- type: "button",
1268
- className: "dsh-files-action-btn dsh-files-action-at",
1269
- onClick: (e) => onMention(e, item),
1270
- title: "一键 @ 引用此目录到当前会话",
1271
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAt, { size: 13 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1272
- className: "dsh-files-action-at-text",
1273
- children: "@引用"
1274
- })]
1275
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1276
- type: "button",
1277
- className: "dsh-files-action-btn",
1278
- onClick: (e) => onCopyPath(e, item.path),
1279
- title: "复制目录路径",
1280
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCopy, { size: 12 })
1281
- })]
1375
+ children: [
1376
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1377
+ type: "button",
1378
+ className: "dsh-files-action-btn dsh-files-action-at",
1379
+ onClick: (e) => onMention(e, item),
1380
+ title: "一键 @ 引用此目录到当前会话",
1381
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAt, { size: 13 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1382
+ className: "dsh-files-action-at-text",
1383
+ children: "@引用"
1384
+ })]
1385
+ }),
1386
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1387
+ type: "button",
1388
+ className: "dsh-files-action-btn",
1389
+ onClick: (e) => onRevealInExplorer(e, item),
1390
+ title: "在文件资源管理器中打开此目录",
1391
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconExplorer, { size: 12 })
1392
+ }),
1393
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1394
+ type: "button",
1395
+ className: "dsh-files-action-btn",
1396
+ onClick: (e) => onOpenDefaultApp(e, item),
1397
+ title: "用系统默认应用打开此目录",
1398
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconExternalApp, { size: 12 })
1399
+ }),
1400
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1401
+ type: "button",
1402
+ className: "dsh-files-action-btn",
1403
+ onClick: (e) => onCopyPath(e, item.path),
1404
+ title: "复制目录路径",
1405
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCopy, { size: 12 })
1406
+ })
1407
+ ]
1282
1408
  })
1283
1409
  ]
1284
1410
  }), isExpanded && item.children && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -1292,7 +1418,9 @@ window.__ModuleLoader__.load({
1292
1418
  onToggleExpand,
1293
1419
  onOpenFile,
1294
1420
  onMention,
1295
- onCopyPath
1421
+ onCopyPath,
1422
+ onRevealInExplorer,
1423
+ onOpenDefaultApp
1296
1424
  }, child.path))
1297
1425
  })]
1298
1426
  });
@@ -1322,22 +1450,39 @@ window.__ModuleLoader__.load({
1322
1450
  }),
1323
1451
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1324
1452
  className: "dsh-files-row-actions",
1325
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1326
- type: "button",
1327
- className: "dsh-files-action-btn dsh-files-action-at",
1328
- onClick: (e) => onMention(e, item),
1329
- title: "一键 @ 引用此文件到当前会话",
1330
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAt, { size: 13 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1331
- className: "dsh-files-action-at-text",
1332
- children: "@引用"
1333
- })]
1334
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1335
- type: "button",
1336
- className: "dsh-files-action-btn",
1337
- onClick: (e) => onCopyPath(e, item.path),
1338
- title: "复制相对路径",
1339
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCopy, { size: 12 })
1340
- })]
1453
+ children: [
1454
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1455
+ type: "button",
1456
+ className: "dsh-files-action-btn dsh-files-action-at",
1457
+ onClick: (e) => onMention(e, item),
1458
+ title: "一键 @ 引用此文件到当前会话",
1459
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAt, { size: 13 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1460
+ className: "dsh-files-action-at-text",
1461
+ children: "@引用"
1462
+ })]
1463
+ }),
1464
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1465
+ type: "button",
1466
+ className: "dsh-files-action-btn",
1467
+ onClick: (e) => onRevealInExplorer(e, item),
1468
+ title: "在文件资源管理器中显示此文件",
1469
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconExplorer, { size: 12 })
1470
+ }),
1471
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1472
+ type: "button",
1473
+ className: "dsh-files-action-btn",
1474
+ onClick: (e) => onOpenDefaultApp(e, item),
1475
+ title: "用系统默认应用打开此文件",
1476
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconExternalApp, { size: 12 })
1477
+ }),
1478
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1479
+ type: "button",
1480
+ className: "dsh-files-action-btn",
1481
+ onClick: (e) => onCopyPath(e, item.path),
1482
+ title: "复制相对路径",
1483
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCopy, { size: 12 })
1484
+ })
1485
+ ]
1341
1486
  })
1342
1487
  ]
1343
1488
  });