dsh-vscode-mode 0.4.2 → 0.4.4
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 +22 -1
- package/lib/client.js +729 -44
- package/lib/client.js.map +1 -1
- package/lib/index.js +261 -33
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/diffDock.ts +12 -0
- package/src/client/events.ts +11 -0
- package/src/client/sidebar/panels/FileExplorer.ts +47 -0
- package/src/client/ui/DiffBox.ts +5 -3
- package/src/client/ui/EditorView.ts +317 -41
- package/src/client/ui/useFileWatch.ts +214 -0
- package/src/client/watchDecision.ts +274 -0
- package/src/compat.ts +1 -1
- package/src/dshVersion.ts +42 -10
- package/src/fileOpenSettings.ts +26 -2
- package/src/fileVersions.ts +167 -0
- package/src/index.ts +6 -1
- package/src/revert.ts +4 -2
- package/src/rpc.ts +62 -12
- package/src/shared/rpc.ts +25 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vscode-mode",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "DSH 上的类 VSCode 编码体验:Monaco 编辑器(文件页签/QuickOpen/状态栏,可驻 DSH 0.1.5+ 官方右侧 Sidebar 与对话同屏)+ 命令栏(Ctrl+Shift+P)与可扩展指令系统 + VS Code 兼容代码片段(.code-snippets 全局/项目,IntelliSense 展开)+ Agent 编辑差异审查(整文件差异/采纳/拒绝/归档/回滚)+ 语言服务器(LSP)智能(跳转定义/引用/hover/大纲 + VSIX 扩展市场安装),状态持久化到工作区旁车",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
package/src/client/diffDock.ts
CHANGED
|
@@ -38,6 +38,18 @@ export function editorDockMode(activePath: string | null | undefined): 'editor'
|
|
|
38
38
|
return activePath ? 'editor' : 'editor-empty'
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* 文件是否仍可执行 Keep/Undo:可定位差异与无法定位的冲突差异都算待处理。
|
|
43
|
+
* 冲突差异的 newText 已不在文件中(被后续修改覆盖),由 host 按"已不存在"记录决策。
|
|
44
|
+
* @author ddj 2026年09月15号
|
|
45
|
+
* @param pendingCount 可定位的待处理差异数
|
|
46
|
+
* @param staleCount 无法定位(冲突)的待处理差异数
|
|
47
|
+
* @returns 两者任一大于 0 时为 true
|
|
48
|
+
*/
|
|
49
|
+
export function canDecideFile(pendingCount: number, staleCount: number): boolean {
|
|
50
|
+
return Math.max(0, pendingCount) + Math.max(0, staleCount) > 0
|
|
51
|
+
}
|
|
52
|
+
|
|
41
53
|
/**
|
|
42
54
|
* 获取切换文件期间稳定展示的文件内差异数量。
|
|
43
55
|
* @author ddj 2026年08月26号
|
package/src/client/events.ts
CHANGED
|
@@ -54,6 +54,17 @@ export function emitRefresh(): void {
|
|
|
54
54
|
window.dispatchEvent(new CustomEvent('edrv:refresh'))
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* 磁盘文件变化(外部写入/删除):文件树按路径失效对应目录并强制重列。
|
|
59
|
+
* 与 edrv:refresh 分开:这里不触发差异记录重算与 stale 清理,只动目录缓存。
|
|
60
|
+
* @author ddj 2026年09月15号
|
|
61
|
+
* @param path 发生变化的文件路径
|
|
62
|
+
*/
|
|
63
|
+
export function emitFileChanged(path: string): void {
|
|
64
|
+
if (!path) return
|
|
65
|
+
window.dispatchEvent(new CustomEvent('edrv:file-changed', { detail: { path } }))
|
|
66
|
+
}
|
|
67
|
+
|
|
57
68
|
/** 打开指定路径到编辑区页签。 */
|
|
58
69
|
export function emitOpenEditor(path: string): void {
|
|
59
70
|
window.dispatchEvent(new CustomEvent('edrv:open-editor', { detail: { path } }))
|
|
@@ -28,6 +28,8 @@ const PREFETCH_EXCLUDED = new Set(['node_modules', '.git', '.hg', '.svn', '.pnpm
|
|
|
28
28
|
const REVEAL_HIGHLIGHT_MS = 2000
|
|
29
29
|
const REVEAL_RETRY_MAX = 6
|
|
30
30
|
const REVEAL_RETRY_MS = 120
|
|
31
|
+
/** 外部文件变化重列去抖:一轮外部批量写入(如 agent 连写多文件)合并为一次重列。 */
|
|
32
|
+
const FILE_CHANGE_DEBOUNCE_MS = 400
|
|
31
33
|
|
|
32
34
|
// --region 行图标(官方原语:目录文件夹图标 + 文件类型图标;缺失时回落纯文本)
|
|
33
35
|
|
|
@@ -125,6 +127,11 @@ export function FileExplorer(props) {
|
|
|
125
127
|
const revealTryRef = React.useRef(0) // 当前定位的重试计数(行渲染需等目录加载)
|
|
126
128
|
const revealInTreeRef = React.useRef(null) // 定位动作最新闭包(窗口监听读取)
|
|
127
129
|
const treeRef = React.useRef(null) // 目录树容器(定位时按 data-edrv-path 查行)
|
|
130
|
+
const reloadDirRef = React.useRef(null) // loadDir 最新闭包(文件变化监听读取,防陈旧闭包)
|
|
131
|
+
const changedTimerRef = React.useRef(null) // 文件变化合并去抖计时器
|
|
132
|
+
const changedRelRef = React.useRef(new Set()) // 待重列的相对目录集合(去抖窗口内合并)
|
|
133
|
+
const dirsMapRef = React.useRef(null) // loadDir 最新闭包(文件变化监听读取,防陈旧闭包)
|
|
134
|
+
dirsMapRef.current = loadDir
|
|
128
135
|
|
|
129
136
|
/** 渲染取数:内存态 → 本地条目缓存 → null(显示加载态)。 */
|
|
130
137
|
const entriesOf = (rel) => dirsRef.current[rel] ?? entriesCacheGet(scope, rel) ?? null
|
|
@@ -261,6 +268,46 @@ export function FileExplorer(props) {
|
|
|
261
268
|
void loadDir('', { force: true, prefetch: true })
|
|
262
269
|
}
|
|
263
270
|
refreshRef.current = refresh
|
|
271
|
+
reloadDirRef.current = loadDir
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 磁盘文件变化(外部写入/删除/改名):把变化路径的父目录并入待重列集合,
|
|
275
|
+
* 去抖合并后对「已展开且存在」的目录强制重列(force 跳过 host 索引命中)。
|
|
276
|
+
* host 侧 fileVersions 已顺手失效目录树缓存,这里补上「已渲染行」的即时刷新。
|
|
277
|
+
* @author ddj 2026年09月15号
|
|
278
|
+
* @param path 发生变化的文件路径(工作区相对;绝对路径按最长已展开祖先匹配)
|
|
279
|
+
*/
|
|
280
|
+
const onFileChanged = (path) => {
|
|
281
|
+
if (typeof path !== 'string' || !path) return
|
|
282
|
+
const rel = path.replace(/\\/g, '/').replace(/^\.\//, '')
|
|
283
|
+
const parts = rel.split('/').filter(Boolean)
|
|
284
|
+
if (parts.length > 1) changedRelRef.current.add(parts.slice(0, -1).join('/'))
|
|
285
|
+
// 绝对路径(或已在更深层目录):补上所有已展开的祖先目录
|
|
286
|
+
for (const dir of ancestorDirsOf(rel)) {
|
|
287
|
+
if (expandedRef.current[dir] === true) changedRelRef.current.add(dir)
|
|
288
|
+
}
|
|
289
|
+
if (changedTimerRef.current) return
|
|
290
|
+
changedTimerRef.current = window.setTimeout(() => {
|
|
291
|
+
changedTimerRef.current = null
|
|
292
|
+
const targets = [...changedRelRef.current]
|
|
293
|
+
changedRelRef.current.clear()
|
|
294
|
+
for (const dir of targets) {
|
|
295
|
+
if (dir !== '' && expandedRef.current[dir] !== true) continue
|
|
296
|
+
void reloadDirRef.current?.(dir, { force: true, prefetch: false })
|
|
297
|
+
}
|
|
298
|
+
}, FILE_CHANGE_DEBOUNCE_MS)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// edrv:file-changed(外部改动同步):按变化路径强制重列对应目录(去抖合并)
|
|
302
|
+
React.useEffect(() => {
|
|
303
|
+
const handler = (event) => onFileChanged(event?.detail?.path)
|
|
304
|
+
window.addEventListener('edrv:file-changed', handler)
|
|
305
|
+
return () => {
|
|
306
|
+
window.removeEventListener('edrv:file-changed', handler)
|
|
307
|
+
if (changedTimerRef.current) { clearTimeout(changedTimerRef.current); changedTimerRef.current = null }
|
|
308
|
+
}
|
|
309
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
310
|
+
}, [])
|
|
264
311
|
|
|
265
312
|
React.useEffect(() => {
|
|
266
313
|
tokensRef.current = {}
|
package/src/client/ui/DiffBox.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import React from 'react'
|
|
8
8
|
import { callIdAttr } from '../state/records.js'
|
|
9
|
-
import { diffDockText } from '../diffDock.js'
|
|
9
|
+
import { canDecideFile, diffDockText } from '../diffDock.js'
|
|
10
10
|
import { badgeOf } from './shared.js'
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -48,6 +48,8 @@ export function DiffBox(props) {
|
|
|
48
48
|
const [detailsOpen, setDetailsOpen] = React.useState(false)
|
|
49
49
|
const detailsId = React.useId()
|
|
50
50
|
const canAct = pendingRegions.length > 0
|
|
51
|
+
// Keep/Undo 覆盖无法定位的冲突差异:它们仍占待处理列表,但不在 pendingRegions 内
|
|
52
|
+
const canDecide = canDecideFile(pendingRegions.length, staleRegions.length)
|
|
51
53
|
|
|
52
54
|
const base = String(activePath || '').split(/[\\/]/).pop() || ''
|
|
53
55
|
|
|
@@ -114,8 +116,8 @@ export function DiffBox(props) {
|
|
|
114
116
|
React.createElement('span', { className: 'edrv-diffbar-file edrv-diffbar-summary', title: activePath || '' }, base)),
|
|
115
117
|
|
|
116
118
|
React.createElement('div', { className: 'edrv-diffbar-actions' },
|
|
117
|
-
React.createElement('button', { className: 'edrv-pill edrv-pill-keep', title: '采纳当前文件的全部差异', disabled: !
|
|
118
|
-
React.createElement('button', { className: 'edrv-pill edrv-pill-undo', title: '不采纳当前文件的全部差异(回滚)', disabled: !
|
|
119
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-keep', title: '采纳当前文件的全部差异', disabled: !canDecide, onClick: onAcceptFile }, '✓ Keep'),
|
|
120
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-undo', title: '不采纳当前文件的全部差异(回滚)', disabled: !canDecide, onClick: onUndoFile }, '↩ Undo'),
|
|
119
121
|
React.createElement('button', {
|
|
120
122
|
className: 'edrv-pill edrv-pill-ghost edrv-diff-details-toggle',
|
|
121
123
|
title: detailsOpen ? '收起差异操作' : '展开差异操作',
|