dsh-vscode-mode 0.1.21 → 0.1.23
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 +26 -20
- package/lib/client.js +711 -77
- package/lib/client.js.map +1 -1
- package/lib/index.js +113 -0
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/client/events.ts +8 -4
- package/src/client/fileReveal.ts +28 -0
- package/src/client/index.ts +44 -9
- package/src/client/sidebar/contextMenu.ts +99 -0
- package/src/client/sidebar/menuItems.ts +28 -0
- package/src/client/sidebar/panels/FileExplorer.ts +34 -4
- package/src/client/sidebar/types.ts +5 -0
- package/src/client/sidebarBridge.ts +247 -0
- package/src/client/styles/editor.css +13 -1
- package/src/client/ui/ContextMenu.ts +82 -0
- package/src/client/ui/DiffBadge.ts +5 -3
- package/src/client/ui/EditorView.ts +113 -43
- package/src/client/ui/SideEditorTab.ts +48 -0
- package/src/compat.ts +2 -0
- package/src/reveal.ts +64 -0
- package/src/rpc.ts +21 -0
- package/src/shared/rpc.ts +2 -0
package/lib/index.js
CHANGED
|
@@ -378,6 +378,7 @@ function detectExternal(ctx, depsAvailable) {
|
|
|
378
378
|
const mcpCount = entriesOf(ctx).length;
|
|
379
379
|
const settings = ctx.get("settings");
|
|
380
380
|
const hasSettings = Boolean(settings?.describe || settings?.update);
|
|
381
|
+
const sub = ctx.get("subprocess");
|
|
381
382
|
return [
|
|
382
383
|
{
|
|
383
384
|
name: MCP_PACKAGE,
|
|
@@ -393,6 +394,11 @@ function detectExternal(ctx, depsAvailable) {
|
|
|
393
394
|
name: "settings 服务",
|
|
394
395
|
active: hasSettings,
|
|
395
396
|
note: hasSettings ? "可读写设置" : "不可用(设置读写走配置回退)"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: "文件浏览器打开(subprocess 服务)",
|
|
400
|
+
active: typeof sub?.spawn === "function",
|
|
401
|
+
note: typeof sub?.spawn === "function" ? "可定位/打开 OS 文件浏览器" : "不可用(右键「在文件浏览器中打开」将提示失败)"
|
|
396
402
|
}
|
|
397
403
|
];
|
|
398
404
|
}
|
|
@@ -2451,6 +2457,73 @@ function toTreeEntries(rel, children) {
|
|
|
2451
2457
|
return out.sort(entryOrder);
|
|
2452
2458
|
}
|
|
2453
2459
|
//#endregion
|
|
2460
|
+
//#region src/reveal.ts
|
|
2461
|
+
/**
|
|
2462
|
+
* dsh-vscode-mode host — 在 OS 文件浏览器中打开/定位路径(reveal 能力)。
|
|
2463
|
+
* 纯函数 revealCommand 平台分发 + revealInExplorer 经 ctx.subprocess.spawn 发射
|
|
2464
|
+
* (argv 数组、无 shell 插值,沿 workspace/revert 的 subprocess 契约)。
|
|
2465
|
+
* 文件 → 资源管理器选中定位;目录 → 打开目录;Linux 无通用定位协议 → 打开所在目录。
|
|
2466
|
+
* 作者 ddj 2026-08-27
|
|
2467
|
+
*/
|
|
2468
|
+
/**
|
|
2469
|
+
* 构造平台 opener 的 argv(纯函数,platform 可注入便于单测)。
|
|
2470
|
+
* @author ddj 2026年08月27号
|
|
2471
|
+
* @param absPath 绝对路径
|
|
2472
|
+
* @param isDir 是否为目录
|
|
2473
|
+
* @param platform 目标平台(缺省当前进程平台)
|
|
2474
|
+
* @returns opener argv(无 shell 插值)
|
|
2475
|
+
*/
|
|
2476
|
+
function revealCommand(absPath, isDir, platform = process.platform) {
|
|
2477
|
+
switch (platform) {
|
|
2478
|
+
case "darwin": return { argv: [
|
|
2479
|
+
"open",
|
|
2480
|
+
"-R",
|
|
2481
|
+
absPath
|
|
2482
|
+
] };
|
|
2483
|
+
case "win32": return isDir ? { argv: ["explorer.exe", absPath] } : { argv: [
|
|
2484
|
+
"explorer.exe",
|
|
2485
|
+
"/select,",
|
|
2486
|
+
absPath
|
|
2487
|
+
] };
|
|
2488
|
+
default: return { argv: ["xdg-open", isDir ? absPath : dirname(absPath)] };
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
/**
|
|
2492
|
+
* 经 host subprocess 服务发射 opener(fire-and-forget 语义)。
|
|
2493
|
+
* Explorer 为 GUI 分离进程,非零退出码不视为失败;仅 spawn 级错误回失败。
|
|
2494
|
+
* @author ddj 2026年08月27号
|
|
2495
|
+
* @param ctx DSH host 上下文
|
|
2496
|
+
* @param absPath 绝对路径
|
|
2497
|
+
* @param isDir 是否为目录
|
|
2498
|
+
* @returns 成功或失败原因
|
|
2499
|
+
*/
|
|
2500
|
+
async function revealInExplorer(ctx, absPath, isDir) {
|
|
2501
|
+
const sub = ctx.get("subprocess");
|
|
2502
|
+
if (!sub || typeof sub.spawn !== "function") return {
|
|
2503
|
+
ok: false,
|
|
2504
|
+
error: "打开文件浏览器不可用:缺少 subprocess 服务"
|
|
2505
|
+
};
|
|
2506
|
+
const { argv } = revealCommand(absPath, isDir);
|
|
2507
|
+
try {
|
|
2508
|
+
await sub.spawn({
|
|
2509
|
+
argv,
|
|
2510
|
+
cwd: dirname(absPath),
|
|
2511
|
+
stdio: {
|
|
2512
|
+
stdout: { maxBytes: 65536 },
|
|
2513
|
+
stderr: { maxBytes: 65536 },
|
|
2514
|
+
stdin: "ignore"
|
|
2515
|
+
},
|
|
2516
|
+
graceMs: 1e4
|
|
2517
|
+
}).done;
|
|
2518
|
+
return { ok: true };
|
|
2519
|
+
} catch (error) {
|
|
2520
|
+
return {
|
|
2521
|
+
ok: false,
|
|
2522
|
+
error: "打开失败:" + String(error)
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
//#endregion
|
|
2454
2527
|
//#region src/rpc.ts
|
|
2455
2528
|
/** cwd → Promise 链:串行化 debug 日志追加(fs read+write 非原子,避免并发丢行)。 */
|
|
2456
2529
|
const debugWriteQueues = /* @__PURE__ */ new Map();
|
|
@@ -2925,6 +2998,46 @@ function buildHandlers(ctx, registry, searcher = newSearcher(ctx)) {
|
|
|
2925
2998
|
};
|
|
2926
2999
|
}
|
|
2927
3000
|
},
|
|
3001
|
+
"edrv.revealInExplorer": async (args) => {
|
|
3002
|
+
const sc = await requireSession(ctx, args.sessionId);
|
|
3003
|
+
if ("err" in sc) return {
|
|
3004
|
+
ok: false,
|
|
3005
|
+
error: sc.err
|
|
3006
|
+
};
|
|
3007
|
+
const fs = ctx.get("fs");
|
|
3008
|
+
if (!fs) return {
|
|
3009
|
+
ok: false,
|
|
3010
|
+
error: "缺少 fs"
|
|
3011
|
+
};
|
|
3012
|
+
try {
|
|
3013
|
+
const rel = normalizeRel(args.path);
|
|
3014
|
+
if (rel === null) return {
|
|
3015
|
+
ok: false,
|
|
3016
|
+
error: "路径不合法"
|
|
3017
|
+
};
|
|
3018
|
+
const target = await fs.resolve(rel || ".", { cwd: sc.cwd });
|
|
3019
|
+
const info = await fs.stat(target);
|
|
3020
|
+
if (!info) return {
|
|
3021
|
+
ok: false,
|
|
3022
|
+
error: "路径不存在"
|
|
3023
|
+
};
|
|
3024
|
+
const abs = fs.processPath(target);
|
|
3025
|
+
const outcome = await revealInExplorer(ctx, abs, info.type === "directory");
|
|
3026
|
+
if (!outcome.ok) return {
|
|
3027
|
+
ok: false,
|
|
3028
|
+
error: outcome.error
|
|
3029
|
+
};
|
|
3030
|
+
return {
|
|
3031
|
+
ok: true,
|
|
3032
|
+
revealed: abs
|
|
3033
|
+
};
|
|
3034
|
+
} catch (error) {
|
|
3035
|
+
return {
|
|
3036
|
+
ok: false,
|
|
3037
|
+
error: "打开失败:" + String(error)
|
|
3038
|
+
};
|
|
3039
|
+
}
|
|
3040
|
+
},
|
|
2928
3041
|
"mcp.list": async () => ({
|
|
2929
3042
|
ok: true,
|
|
2930
3043
|
...listMcp(ctx)
|