dsh-vscode-mode 0.1.20 → 0.1.22
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/lib/client.js +1009 -8
- package/lib/client.js.map +1 -1
- package/lib/index.js +113 -0
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/fileReveal.ts +28 -0
- package/src/client/index.ts +16 -1
- package/src/client/outline/OutlinePanel.tsx +213 -0
- package/src/client/outline/index.ts +24 -0
- package/src/client/outline/parse.ts +271 -0
- package/src/client/outline/sources.ts +134 -0
- package/src/client/outline/types.ts +51 -0
- 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 +10 -0
- package/src/client/styles/editor.css +12 -1
- package/src/client/ui/ContextMenu.ts +82 -0
- package/src/client/ui/EditorView.ts +27 -1
- 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/src/reveal.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-vscode-mode host — 在 OS 文件浏览器中打开/定位路径(reveal 能力)。
|
|
3
|
+
* 纯函数 revealCommand 平台分发 + revealInExplorer 经 ctx.subprocess.spawn 发射
|
|
4
|
+
* (argv 数组、无 shell 插值,沿 workspace/revert 的 subprocess 契约)。
|
|
5
|
+
* 文件 → 资源管理器选中定位;目录 → 打开目录;Linux 无通用定位协议 → 打开所在目录。
|
|
6
|
+
* 作者 ddj 2026-08-27
|
|
7
|
+
*/
|
|
8
|
+
import { dirname } from 'node:path'
|
|
9
|
+
import type { Ctx } from './store.js'
|
|
10
|
+
|
|
11
|
+
/** reveal 结果(沿 revert.ts 的 Result 风格)。 */
|
|
12
|
+
export type RevealResult = { ok: true } | { ok: false; error: string }
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 构造平台 opener 的 argv(纯函数,platform 可注入便于单测)。
|
|
16
|
+
* @author ddj 2026年08月27号
|
|
17
|
+
* @param absPath 绝对路径
|
|
18
|
+
* @param isDir 是否为目录
|
|
19
|
+
* @param platform 目标平台(缺省当前进程平台)
|
|
20
|
+
* @returns opener argv(无 shell 插值)
|
|
21
|
+
*/
|
|
22
|
+
export function revealCommand(absPath: string, isDir: boolean, platform: NodeJS.Platform = process.platform): { argv: string[] } {
|
|
23
|
+
switch (platform) {
|
|
24
|
+
case 'darwin':
|
|
25
|
+
return { argv: ['open', '-R', absPath] }
|
|
26
|
+
case 'win32':
|
|
27
|
+
// 文件 → 定位并选中;目录 → 直接打开该目录
|
|
28
|
+
return isDir
|
|
29
|
+
? { argv: ['explorer.exe', absPath] }
|
|
30
|
+
: { argv: ['explorer.exe', '/select,', absPath] }
|
|
31
|
+
default:
|
|
32
|
+
// Linux 无通用 select 协议:文件打开所在目录、目录打开自身(KISS,对齐 better-sidebar)
|
|
33
|
+
return { argv: ['xdg-open', isDir ? absPath : dirname(absPath)] }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 经 host subprocess 服务发射 opener(fire-and-forget 语义)。
|
|
39
|
+
* Explorer 为 GUI 分离进程,非零退出码不视为失败;仅 spawn 级错误回失败。
|
|
40
|
+
* @author ddj 2026年08月27号
|
|
41
|
+
* @param ctx DSH host 上下文
|
|
42
|
+
* @param absPath 绝对路径
|
|
43
|
+
* @param isDir 是否为目录
|
|
44
|
+
* @returns 成功或失败原因
|
|
45
|
+
*/
|
|
46
|
+
export async function revealInExplorer(ctx: Ctx, absPath: string, isDir: boolean): Promise<RevealResult> {
|
|
47
|
+
const sub = ctx.get('subprocess')
|
|
48
|
+
if (!sub || typeof sub.spawn !== 'function') {
|
|
49
|
+
return { ok: false, error: '打开文件浏览器不可用:缺少 subprocess 服务' }
|
|
50
|
+
}
|
|
51
|
+
const { argv } = revealCommand(absPath, isDir)
|
|
52
|
+
try {
|
|
53
|
+
const handle = sub.spawn({
|
|
54
|
+
argv,
|
|
55
|
+
cwd: dirname(absPath),
|
|
56
|
+
stdio: { stdout: { maxBytes: 1 << 16 }, stderr: { maxBytes: 1 << 16 }, stdin: 'ignore' },
|
|
57
|
+
graceMs: 10000,
|
|
58
|
+
})
|
|
59
|
+
await handle.done
|
|
60
|
+
return { ok: true }
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return { ok: false, error: '打开失败:' + String(error) }
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/rpc.ts
CHANGED
|
@@ -30,6 +30,7 @@ import { normalizeFileOpenTool, FILE_OPEN_DEFAULT, FILE_OPEN_SETTINGS_NS } from
|
|
|
30
30
|
import { buildReport } from './compat.js'
|
|
31
31
|
import { readDevForm, setDevForm } from './devForm.js'
|
|
32
32
|
import { normalizeRel, toTreeEntries } from './tree.js'
|
|
33
|
+
import { revealInExplorer } from './reveal.js'
|
|
33
34
|
|
|
34
35
|
/** cwd → Promise 链:串行化 debug 日志追加(fs read+write 非原子,避免并发丢行)。 */
|
|
35
36
|
const debugWriteQueues = new Map<string, Promise<void>>()
|
|
@@ -336,6 +337,26 @@ export function buildHandlers(ctx: Ctx, registry: Registry, searcher = newSearch
|
|
|
336
337
|
return { ok: false, error: '读取目录失败:' + String(error) }
|
|
337
338
|
}
|
|
338
339
|
},
|
|
340
|
+
'edrv.revealInExplorer': async (args) => {
|
|
341
|
+
// 在 OS 文件浏览器中打开/定位路径(树行/编辑器右键菜单用),相对工作区解析。
|
|
342
|
+
const sc = await requireSession(ctx, args.sessionId)
|
|
343
|
+
if ('err' in sc) return { ok: false, error: sc.err }
|
|
344
|
+
const fs = ctx.get('fs')
|
|
345
|
+
if (!fs) return { ok: false, error: '缺少 fs' }
|
|
346
|
+
try {
|
|
347
|
+
const rel = normalizeRel(args.path)
|
|
348
|
+
if (rel === null) return { ok: false, error: '路径不合法' }
|
|
349
|
+
const target = await fs.resolve(rel || '.', { cwd: sc.cwd })
|
|
350
|
+
const info = await fs.stat(target)
|
|
351
|
+
if (!info) return { ok: false, error: '路径不存在' }
|
|
352
|
+
const abs = fs.processPath(target)
|
|
353
|
+
const outcome = await revealInExplorer(ctx, abs, info.type === 'directory')
|
|
354
|
+
if (!outcome.ok) return { ok: false, error: outcome.error }
|
|
355
|
+
return { ok: true, revealed: abs }
|
|
356
|
+
} catch (error) {
|
|
357
|
+
return { ok: false, error: '打开失败:' + String(error) }
|
|
358
|
+
}
|
|
359
|
+
},
|
|
339
360
|
'mcp.list': async () => ({ ok: true, ...listMcp(ctx) }),
|
|
340
361
|
'mcp.save': async (args) => {
|
|
341
362
|
try { return { ok: true, server: await saveMcp(ctx, args.config) } }
|
package/src/shared/rpc.ts
CHANGED
|
@@ -60,6 +60,7 @@ export interface RpcRequestMap {
|
|
|
60
60
|
'edrv.debug': { sessionId?: string; text: string }
|
|
61
61
|
'edrv.searchFiles': { sessionId?: string; query: string }
|
|
62
62
|
'edrv.listDir': { sessionId?: string; path: string }
|
|
63
|
+
'edrv.revealInExplorer': { sessionId?: string; path: string }
|
|
63
64
|
'mcp.list': {}
|
|
64
65
|
'mcp.save': { config: MpcConfig }
|
|
65
66
|
'mcp.remove': { id: string }
|
|
@@ -94,6 +95,7 @@ export interface RpcOkMap {
|
|
|
94
95
|
'edrv.debug': object
|
|
95
96
|
'edrv.searchFiles': { files: string[]; truncated: boolean }
|
|
96
97
|
'edrv.listDir': { root: string; path: string; entries: TreeEntry[] }
|
|
98
|
+
'edrv.revealInExplorer': { revealed: string }
|
|
97
99
|
'mcp.list': { servers: MpcServer[] }
|
|
98
100
|
'mcp.save': { server: MpcServer }
|
|
99
101
|
'mcp.remove': object
|