dsh-vscode-mode 0.1.6 → 0.1.10
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 +243 -47
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/monaco/diffRender.ts +17 -2
- package/src/client/monaco/loader.ts +56 -9
- package/src/client/styles/editor.css +16 -2
- package/src/client/ui/EditorView.ts +114 -19
package/package.json
CHANGED
|
@@ -86,8 +86,15 @@ export function createDiffRenderer(log) {
|
|
|
86
86
|
const lineH = editor.getOption(monaco.editor.EditorOption.lineHeight) || 20
|
|
87
87
|
let li = null
|
|
88
88
|
try { li = editor.getLayoutInfo() } catch (e) { /* ignore */ }
|
|
89
|
-
const
|
|
89
|
+
const rawLeft = li ? li.decorationsLeft : 0
|
|
90
90
|
const width = li && li.decorationsWidth > 0 ? li.decorationsWidth : 16
|
|
91
|
+
// decorationsLeft 是 Monaco 内部坐标,需限制在宿主根节点内,避免窄窗口/边框
|
|
92
|
+
// 或布局尚未完成时把减号绘制到编辑器左、右边界之外。
|
|
93
|
+
const maxLeft = Math.max(0, root.clientWidth - width)
|
|
94
|
+
// 字符本身有少量字体侧向留白;给标记列留出 4px 内缩,避免减号半个
|
|
95
|
+
// 字符落到 Monaco 根节点左边框外,同时仍限制右侧不越界。
|
|
96
|
+
const markerInset = 12
|
|
97
|
+
const left = Math.max(0, Math.min(maxLeft, rawLeft + markerInset))
|
|
91
98
|
const lineCount = editor.getModel().getLineCount()
|
|
92
99
|
let itemIdx = 0
|
|
93
100
|
let zi = 0
|
|
@@ -100,7 +107,8 @@ export function createDiffRenderer(log) {
|
|
|
100
107
|
for (let i = 0; i < z.n; i++) {
|
|
101
108
|
const item = document.createElement('div')
|
|
102
109
|
item.className = 'edrv-minus-item'
|
|
103
|
-
|
|
110
|
+
// 使用 CSS 线段代替文本字符,避免不同字体的侧向留白把 “-” 绘制到边界外。
|
|
111
|
+
item.textContent = ''
|
|
104
112
|
item.style.width = width + 'px'
|
|
105
113
|
item.style.height = lineH + 'px'
|
|
106
114
|
item.dataset.zone = String(zi)
|
|
@@ -130,7 +138,14 @@ export function createDiffRenderer(log) {
|
|
|
130
138
|
const edRect = root.getBoundingClientRect()
|
|
131
139
|
const st = editor.getScrollTop() || 0
|
|
132
140
|
const items = overlay.querySelectorAll('.edrv-minus-item')
|
|
141
|
+
let correctedLeft = left
|
|
142
|
+
try {
|
|
143
|
+
const layout = editor.getLayoutInfo()
|
|
144
|
+
const markerWidth = layout.decorationsWidth > 0 ? layout.decorationsWidth : width
|
|
145
|
+
correctedLeft = Math.max(0, Math.min(Math.max(0, root.clientWidth - markerWidth), (layout.decorationsLeft || 0) + 12))
|
|
146
|
+
} catch (e) { /* 保留首次布局计算结果 */ }
|
|
133
147
|
for (const item of items) {
|
|
148
|
+
item.style.left = correctedLeft + 'px'
|
|
134
149
|
const zIndex = Number(item.dataset.zone)
|
|
135
150
|
const rIndex = Number(item.dataset.row)
|
|
136
151
|
const z = createdZones[zIndex]
|
|
@@ -7,6 +7,46 @@
|
|
|
7
7
|
|
|
8
8
|
export const MONACO_BASE = '/edrv/vendor/monaco/vs'
|
|
9
9
|
let monacoPromise = null
|
|
10
|
+
let monacoStage = { phase: 'idle', progress: 0, message: '准备加载 Monaco…' }
|
|
11
|
+
const stageListeners = new Set()
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Monaco 加载阶段:这是阶段进度而非网络字节进度,避免误导用户。
|
|
15
|
+
* @author ddj 2026年08月22号
|
|
16
|
+
*/
|
|
17
|
+
export const MONACO_STAGES = {
|
|
18
|
+
loader: { progress: 18, message: '加载 Monaco 引导模块…' },
|
|
19
|
+
core: { progress: 72, message: '加载编辑器核心模块…' },
|
|
20
|
+
ready: { progress: 100, message: 'Monaco 编辑器已就绪' },
|
|
21
|
+
error: { progress: 0, message: 'Monaco 编辑器加载失败' },
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 发布 Monaco 加载阶段,监听器异常不得影响编辑器加载。
|
|
26
|
+
* @author ddj 2026年08月22号
|
|
27
|
+
* @param phase 阶段名
|
|
28
|
+
* @param progress 阶段百分比
|
|
29
|
+
* @param message 用户可读状态
|
|
30
|
+
*/
|
|
31
|
+
function publishStage(phase, progress, message) {
|
|
32
|
+
monacoStage = { phase, progress, message }
|
|
33
|
+
for (const listener of stageListeners) {
|
|
34
|
+
try { listener(monacoStage) } catch (error) { /* UI 回调异常忽略 */ }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 订阅 Monaco 加载阶段。
|
|
40
|
+
* @author ddj 2026年08月22号
|
|
41
|
+
* @param listener 阶段回调
|
|
42
|
+
* @returns 取消订阅函数
|
|
43
|
+
*/
|
|
44
|
+
function subscribeStage(listener) {
|
|
45
|
+
if (typeof listener !== 'function') return () => {}
|
|
46
|
+
stageListeners.add(listener)
|
|
47
|
+
listener(monacoStage)
|
|
48
|
+
return () => stageListeners.delete(listener)
|
|
49
|
+
}
|
|
10
50
|
|
|
11
51
|
/** 扩展名 → Monaco language id(常见语言;未知回退 plaintext)。 */
|
|
12
52
|
export const LANG_BY_EXT = {
|
|
@@ -40,19 +80,26 @@ export function langOf(path) {
|
|
|
40
80
|
* @author ddj 2026年08月20号
|
|
41
81
|
* @returns Promise<object> window.monaco
|
|
42
82
|
*/
|
|
43
|
-
export function loadMonaco() {
|
|
83
|
+
export function loadMonaco(onProgress) {
|
|
84
|
+
const unsubscribe = subscribeStage(onProgress)
|
|
44
85
|
if (!monacoPromise) {
|
|
86
|
+
publishStage('loader', MONACO_STAGES.loader.progress, MONACO_STAGES.loader.message)
|
|
45
87
|
monacoPromise = new Promise((resolve, reject) => {
|
|
88
|
+
const fail = (error) => {
|
|
89
|
+
monacoPromise = null
|
|
90
|
+
publishStage('error', MONACO_STAGES.error.progress, MONACO_STAGES.error.message)
|
|
91
|
+
reject(error)
|
|
92
|
+
}
|
|
46
93
|
const boot = () => {
|
|
47
94
|
try {
|
|
48
95
|
window.require.config({ paths: { vs: MONACO_BASE } })
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
96
|
+
publishStage('core', MONACO_STAGES.core.progress, MONACO_STAGES.core.message)
|
|
97
|
+
window.require(['vs/editor/editor.main'], () => {
|
|
98
|
+
publishStage('ready', MONACO_STAGES.ready.progress, MONACO_STAGES.ready.message)
|
|
99
|
+
resolve(window.monaco)
|
|
100
|
+
}, (err) => fail(new Error('Monaco 模块加载失败:' + String(err))))
|
|
53
101
|
} catch (error) {
|
|
54
|
-
|
|
55
|
-
reject(error)
|
|
102
|
+
fail(error)
|
|
56
103
|
}
|
|
57
104
|
}
|
|
58
105
|
const existing = document.querySelector('script[data-edrv-monaco-loader]')
|
|
@@ -62,10 +109,10 @@ export function loadMonaco() {
|
|
|
62
109
|
s.src = MONACO_BASE + '/loader.js'
|
|
63
110
|
s.dataset.edrvMonacoLoader = '1'
|
|
64
111
|
s.onload = boot
|
|
65
|
-
s.onerror = () =>
|
|
112
|
+
s.onerror = () => fail(new Error('Monaco loader 加载失败'))
|
|
66
113
|
document.head.appendChild(s)
|
|
67
114
|
}
|
|
68
115
|
})
|
|
69
116
|
}
|
|
70
|
-
return monacoPromise
|
|
117
|
+
return monacoPromise.finally(unsubscribe)
|
|
71
118
|
}
|
|
@@ -40,8 +40,9 @@
|
|
|
40
40
|
[data-edrv-view] .edrv-del-zone { background: #fdeaea; border-top: 1px solid rgba(217,83,79,.35); border-bottom: 1px solid rgba(217,83,79,.35); box-shadow: inset 0 0 0 1px rgba(217,83,79,.12); }
|
|
41
41
|
[data-edrv-view] .edrv-del-row { white-space: pre; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 12.5px; line-height: 20px; color: #c0392b; }
|
|
42
42
|
[data-edrv-view] .edrv-del-text { display: inline-block; white-space: pre; }
|
|
43
|
-
[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; pointer-events: none; z-index: 6; }
|
|
44
|
-
[data-edrv-view] .edrv-minus-item { position: absolute;
|
|
43
|
+
[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; max-width: 100%; overflow: visible; pointer-events: none; z-index: 6; }
|
|
44
|
+
[data-edrv-view] .edrv-minus-item { position: absolute; box-sizing: border-box; max-width: 100%; overflow: hidden; }
|
|
45
|
+
[data-edrv-view] .edrv-minus-item::before { content: ''; position: absolute; left: 4px; top: 50%; width: 8px; height: 2px; transform: translateY(-50%); border-radius: 2px; background: #d9534f; }
|
|
45
46
|
[data-edrv-view] .edrv-monaco-host { flex: 1; min-height: 0; position: relative; }
|
|
46
47
|
[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 0; }
|
|
47
48
|
[data-edrv-view] .edrv-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; height: 100%; color: var(--dsw-alias-label-tertiary, #777); font-size: 13px; }
|
|
@@ -83,5 +84,18 @@
|
|
|
83
84
|
[data-edrv-view] .edrv-launch-path { flex: 1; min-width: 0; font-size: 12px; color: var(--dsw-alias-label-primary, #eee); word-break: break-all; }
|
|
84
85
|
[data-edrv-view] .edrv-launch-cnt { font-size: 11px; color: var(--dsw-alias-label-tertiary, #888); white-space: nowrap; }
|
|
85
86
|
[data-edrv-view] .edrv-launch-arch { display: flex; flex-direction: column; gap: 6px; }
|
|
87
|
+
[data-edrv-view] .monaco-editor { overflow: hidden !important; }
|
|
88
|
+
[data-edrv-view] .monaco-editor .sticky-widget { z-index: 30 !important; background: var(--dsw-alias-bg-base, #f8f8f8) !important; }
|
|
89
|
+
[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-content,
|
|
90
|
+
[data-edrv-view] .monaco-editor .sticky-widget .sticky-line-number { background: var(--dsw-alias-bg-base, #f8f8f8) !important; }
|
|
86
91
|
[data-edrv-view] .monaco-editor, [data-edrv-view] .monaco-editor .margin, [data-edrv-view] .monaco-editor-background { background: transparent !important; }
|
|
87
92
|
[data-edrv-view] .monaco-editor .scrollbar .slider { background: rgba(128,128,128,.3) !important; }
|
|
93
|
+
[data-edrv-view] .edrv-loading { gap: 8px; padding: 20px; }
|
|
94
|
+
[data-edrv-view] .edrv-loading-title { color: var(--dsw-alias-label-primary, #1f2933); font-size: 13px; }
|
|
95
|
+
[data-edrv-view] .edrv-progress { width: min(360px, 72%); height: 6px; overflow: hidden; border-radius: 999px; background: var(--dsw-alias-border-l1, #e0e6e8); }
|
|
96
|
+
[data-edrv-view] .edrv-progress-value { height: 100%; border-radius: inherit; background: var(--dsw-alias-brand-primary, #0f9d58); transition: width .24s ease; }
|
|
97
|
+
[data-edrv-view] .edrv-loading-percent { color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; font-variant-numeric: tabular-nums; }
|
|
98
|
+
@media (prefers-reduced-motion: reduce) {
|
|
99
|
+
[data-edrv-view] .edrv-progress-value { transition: none; }
|
|
100
|
+
}
|
|
101
|
+
|
|
@@ -35,6 +35,8 @@ export function EditorView(props) {
|
|
|
35
35
|
const [content, setContent] = React.useState(null)
|
|
36
36
|
const [status, setStatus] = React.useState('')
|
|
37
37
|
const [error, setError] = React.useState(null)
|
|
38
|
+
const [loadError, setLoadError] = React.useState(null)
|
|
39
|
+
const [loadStage, setLoadStage] = React.useState({ progress: 0, message: '准备加载编辑器…' })
|
|
38
40
|
const [openInput, setOpenInput] = React.useState(false)
|
|
39
41
|
const [pathDraft, setPathDraft] = React.useState('')
|
|
40
42
|
const [cursor, setCursor] = React.useState('')
|
|
@@ -55,7 +57,10 @@ export function EditorView(props) {
|
|
|
55
57
|
const lineRegionMapRef = React.useRef(new Map()) // 行 → 区域 映射(hover 命中)
|
|
56
58
|
const hoverKeyRef = React.useRef(null) // 当前 hover 区域 key(区域不变不重渲染)
|
|
57
59
|
const hoverTopRef = React.useRef(null) // 当前 hover 浮窗 top(滚动后位置变化才重定位)
|
|
60
|
+
const hoverRightRef = React.useRef(null) // 浮窗右侧避让 Monaco 缩略栏
|
|
58
61
|
const hideTimerRef = React.useRef(null) // 延迟隐藏计时器(防闪烁)
|
|
62
|
+
const hoverEditorRef = React.useRef(false) // 鼠标是否仍在编辑器命中区
|
|
63
|
+
const hoverPanelRef = React.useRef(false) // 鼠标是否已进入 Keep/Undo 浮层
|
|
59
64
|
const batchBusyRef = React.useRef(false) // 批量 Keep All/Undo All 防重入
|
|
60
65
|
const diffRendererRef = React.useRef(null)
|
|
61
66
|
if (!diffRendererRef.current) diffRendererRef.current = createDiffRenderer((sid, t) => dbg(sid, t))
|
|
@@ -115,11 +120,27 @@ export function EditorView(props) {
|
|
|
115
120
|
|
|
116
121
|
const loadContent = (path, sid) => {
|
|
117
122
|
const seq = ++loadSeqRef.current
|
|
123
|
+
setLoadError(null)
|
|
124
|
+
setLoadStage({ progress: monaco ? 72 : 12, message: '读取文件内容…' })
|
|
118
125
|
rpc('edrv.read', { sessionId: sid, path }).then((res) => {
|
|
119
126
|
if (seq !== loadSeqRef.current || path !== active) return
|
|
120
|
-
if (res && res.ok) {
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
if (res && res.ok) {
|
|
128
|
+
setContent(res.content)
|
|
129
|
+
setLoadStage((prev) => ({ progress: Math.max(84, prev.progress), message: '文件已读取,准备创建编辑器…' }))
|
|
130
|
+
setStatus('已加载')
|
|
131
|
+
} else {
|
|
132
|
+
const message = res?.error ? String(res.error) : '读取失败'
|
|
133
|
+
setLoadError(message)
|
|
134
|
+
setError(message)
|
|
135
|
+
setStatus('读取失败')
|
|
136
|
+
}
|
|
137
|
+
}).catch((e) => {
|
|
138
|
+
if (seq !== loadSeqRef.current) return
|
|
139
|
+
const message = 'read异常:' + String(e)
|
|
140
|
+
setLoadError(message)
|
|
141
|
+
setError(message)
|
|
142
|
+
setStatus('读取失败')
|
|
143
|
+
})
|
|
123
144
|
}
|
|
124
145
|
|
|
125
146
|
// 挂载轮询 + 事件订阅
|
|
@@ -170,14 +191,27 @@ export function EditorView(props) {
|
|
|
170
191
|
|
|
171
192
|
React.useEffect(() => {
|
|
172
193
|
if (!active) return
|
|
173
|
-
setContent(null)
|
|
194
|
+
setContent(null)
|
|
195
|
+
setLoadError(null)
|
|
196
|
+
setLoadStage({ progress: 10, message: '准备读取文件…' })
|
|
197
|
+
setStatus('加载中…')
|
|
174
198
|
loadContent(active, sessionId)
|
|
175
199
|
}, [active, sessionId])
|
|
176
200
|
|
|
177
201
|
React.useEffect(() => {
|
|
178
202
|
if (monaco || monacoErr) return
|
|
179
203
|
let alive = true
|
|
180
|
-
|
|
204
|
+
const onProgress = (stage) => {
|
|
205
|
+
if (!alive) return
|
|
206
|
+
const progress = stage.phase === 'ready' ? 70 : Math.round(10 + stage.progress * 0.6)
|
|
207
|
+
setLoadStage((prev) => ({ progress: Math.max(prev.progress, progress), message: stage.message }))
|
|
208
|
+
}
|
|
209
|
+
loadMonaco(onProgress).then((m) => { if (alive) setMonaco(m) }).catch((e) => {
|
|
210
|
+
if (alive) {
|
|
211
|
+
setMonacoErr(String(e?.message ?? e))
|
|
212
|
+
setStatus('Monaco 不可用')
|
|
213
|
+
}
|
|
214
|
+
})
|
|
181
215
|
return () => { alive = false }
|
|
182
216
|
}, [monaco, monacoErr])
|
|
183
217
|
|
|
@@ -232,12 +266,14 @@ export function EditorView(props) {
|
|
|
232
266
|
const ed = editorRef.current
|
|
233
267
|
const model = getModel(active, content)
|
|
234
268
|
if (ed.getModel() !== model) ed.setModel(model)
|
|
269
|
+
setLoadStage((prev) => ({ progress: Math.max(96, prev.progress), message: '创建编辑器视图…' }))
|
|
235
270
|
}, [monaco, active, content])
|
|
236
271
|
|
|
237
272
|
// 行内差异自绘(decorations / view zones / minus overlay)→ diffRenderer
|
|
238
273
|
React.useEffect(() => {
|
|
239
274
|
if (!monaco || !editorRef.current || !active || content === null) return
|
|
240
275
|
diffRendererRef.current.render(monaco, editorRef.current, pendingRegions, sessionId)
|
|
276
|
+
setLoadStage({ progress: 100, message: '编辑器已就绪' })
|
|
241
277
|
}, [monaco, active, content, pendingRegions])
|
|
242
278
|
|
|
243
279
|
React.useEffect(() => () => {
|
|
@@ -292,29 +328,34 @@ export function EditorView(props) {
|
|
|
292
328
|
// 防闪烁:① 区域不变不 setState(浮窗锚定差异块起始行,不跟随鼠标);② 延迟隐藏;
|
|
293
329
|
// ③ 浮窗自身 onMouseEnter 取消隐藏计时(鼠标在浮窗与编辑器间移动不闪)。
|
|
294
330
|
const hideSoon = () => {
|
|
295
|
-
if (hideTimerRef.current) return
|
|
331
|
+
if (hoverEditorRef.current || hoverPanelRef.current || hideTimerRef.current) return
|
|
296
332
|
hideTimerRef.current = setTimeout(() => {
|
|
297
333
|
hideTimerRef.current = null
|
|
334
|
+
if (hoverEditorRef.current || hoverPanelRef.current) return
|
|
298
335
|
hoverKeyRef.current = null
|
|
299
336
|
hoverTopRef.current = null
|
|
300
337
|
setHoverAct(null)
|
|
301
338
|
}, 180)
|
|
302
339
|
}
|
|
303
340
|
ed.onMouseMove((e) => {
|
|
341
|
+
hoverEditorRef.current = true
|
|
304
342
|
const line = e?.target?.position?.lineNumber
|
|
305
343
|
const map = lineRegionMapRef.current
|
|
306
|
-
if (!line || !map.size) { hideSoon(); return }
|
|
344
|
+
if (!line || !map.size) { hoverEditorRef.current = false; hideSoon(); return }
|
|
307
345
|
const hit = map.get(line)
|
|
308
|
-
if (!hit) { hideSoon(); return }
|
|
346
|
+
if (!hit) { hoverEditorRef.current = false; hideSoon(); return }
|
|
309
347
|
const key = callIdAttr(hit.callId, hit.idx)
|
|
310
348
|
const top = Math.max(0, ed.getTopForLineNumber(Math.max(1, hit.start)) - ed.getScrollTop())
|
|
311
|
-
|
|
349
|
+
const layout = ed.getLayoutInfo()
|
|
350
|
+
const minimapLeft = layout.minimap?.minimapLeft || 0
|
|
351
|
+
const right = minimapLeft > 0 ? Math.max(12, layout.width - minimapLeft + 8) : Math.max(12, layout.verticalScrollbarWidth + 8)
|
|
352
|
+
if (hoverKeyRef.current === key && hoverTopRef.current === top && hoverRightRef.current === right) return // 同区域同位置不重复更新
|
|
312
353
|
hoverKeyRef.current = key
|
|
313
354
|
hoverTopRef.current = top
|
|
355
|
+
hoverRightRef.current = right
|
|
314
356
|
if (hideTimerRef.current) { clearTimeout(hideTimerRef.current); hideTimerRef.current = null }
|
|
315
|
-
setHoverAct({ region: hit, top })
|
|
357
|
+
setHoverAct({ region: hit, top, right })
|
|
316
358
|
})
|
|
317
|
-
ed.onMouseLeave(hideSoon)
|
|
318
359
|
editorRef.current = ed
|
|
319
360
|
}, [])
|
|
320
361
|
|
|
@@ -372,6 +413,20 @@ export function EditorView(props) {
|
|
|
372
413
|
refreshRecords()
|
|
373
414
|
}
|
|
374
415
|
|
|
416
|
+
/**
|
|
417
|
+
* 关闭当前差异浮窗并清理 hover 锚点。
|
|
418
|
+
* @author ddj 2026年08月21号
|
|
419
|
+
*/
|
|
420
|
+
const dismissHover = () => {
|
|
421
|
+
hoverKeyRef.current = null
|
|
422
|
+
hoverTopRef.current = null
|
|
423
|
+
hoverRightRef.current = null
|
|
424
|
+
hoverEditorRef.current = false
|
|
425
|
+
hoverPanelRef.current = false
|
|
426
|
+
if (hideTimerRef.current) { clearTimeout(hideTimerRef.current); hideTimerRef.current = null }
|
|
427
|
+
setHoverAct(null)
|
|
428
|
+
}
|
|
429
|
+
|
|
375
430
|
/**
|
|
376
431
|
* 对单个差异区域执行采纳/不采纳。
|
|
377
432
|
* @author ddj 2026年08月20号
|
|
@@ -503,19 +558,48 @@ export function EditorView(props) {
|
|
|
503
558
|
|
|
504
559
|
const otherFiles = sum.pendingFiles.filter((f) => f.path !== active)
|
|
505
560
|
|
|
561
|
+
/**
|
|
562
|
+
* 渲染编辑器/文件加载进度面板。
|
|
563
|
+
* @author ddj 2026年08月22号
|
|
564
|
+
* @param message 当前加载阶段
|
|
565
|
+
* @param progress 阶段进度
|
|
566
|
+
* @param retry 可选重试回调
|
|
567
|
+
* @returns 加载面板 React 元素
|
|
568
|
+
*/
|
|
569
|
+
const loadingBody = (message, progress, retry) => React.createElement('div', { className: 'edrv-empty edrv-loading', 'aria-live': 'polite' },
|
|
570
|
+
React.createElement('div', { className: 'edrv-loading-title' }, message),
|
|
571
|
+
React.createElement('div', {
|
|
572
|
+
className: 'edrv-progress',
|
|
573
|
+
role: 'progressbar',
|
|
574
|
+
'aria-label': '编辑器加载进度',
|
|
575
|
+
'aria-valuemin': 0,
|
|
576
|
+
'aria-valuemax': 100,
|
|
577
|
+
'aria-valuenow': progress,
|
|
578
|
+
}, React.createElement('div', { className: 'edrv-progress-value', style: { width: Math.max(0, Math.min(100, progress)) + '%' } })),
|
|
579
|
+
React.createElement('div', { className: 'edrv-loading-percent' }, Math.round(progress) + '%'),
|
|
580
|
+
retry ? React.createElement('button', { className: 'edrv-pill edrv-pill-ghost', onClick: retry }, '重试') : null)
|
|
581
|
+
|
|
506
582
|
let body
|
|
507
583
|
if (!monaco && !monacoErr) {
|
|
508
|
-
body =
|
|
584
|
+
body = loadingBody(loadStage.message, loadStage.progress)
|
|
509
585
|
} else if (monacoErr) {
|
|
510
586
|
body = React.createElement('div', { className: 'edrv-empty' },
|
|
511
587
|
React.createElement('div', null, 'Monaco 编辑器加载失败:' + String(monacoErr)),
|
|
512
|
-
React.createElement('div', { style: { fontSize: 11 } }, '请确认插件包 assets/vendor/monaco 完整(/edrv/vendor 路由可达)')
|
|
588
|
+
React.createElement('div', { style: { fontSize: 11 } }, '请确认插件包 assets/vendor/monaco 完整(/edrv/vendor 路由可达)'),
|
|
589
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-ghost', onClick: () => { setMonacoErr(null); setLoadStage({ progress: 0, message: '准备重试 Monaco…' }) } }, '重试'))
|
|
513
590
|
} else if (!active) {
|
|
514
591
|
body = React.createElement('div', { className: 'edrv-empty' },
|
|
515
592
|
React.createElement('div', null, '暂无打开的文件'),
|
|
516
593
|
React.createElement('div', { style: { fontSize: 12 } }, '使用右上搜索框 (Ctrl+P) 打开工作区文件;agent 修改文件后顶部会出现差异角标'))
|
|
594
|
+
} else if (content === null && loadError) {
|
|
595
|
+
body = loadingBody('文件加载失败:' + loadError, 0, () => {
|
|
596
|
+
setLoadError(null)
|
|
597
|
+
setContent(null)
|
|
598
|
+
setLoadStage({ progress: 10, message: '重新读取文件…' })
|
|
599
|
+
loadContent(active, sessionId)
|
|
600
|
+
})
|
|
517
601
|
} else if (content === null) {
|
|
518
|
-
body =
|
|
602
|
+
body = loadingBody(loadStage.message || '读取文件内容…', loadStage.progress)
|
|
519
603
|
} else {
|
|
520
604
|
body = React.createElement('div', { className: 'edrv-monaco-host' },
|
|
521
605
|
React.createElement('div', { ref: ensureEditor }))
|
|
@@ -524,12 +608,23 @@ export function EditorView(props) {
|
|
|
524
608
|
const hoverEl = (hoverAct && !launcherOpen)
|
|
525
609
|
? React.createElement('div', {
|
|
526
610
|
className: 'edrv-hoveract',
|
|
527
|
-
style: { top: hoverAct.top, right: 12 },
|
|
528
|
-
onMouseEnter: () => { if (hideTimerRef.current) { clearTimeout(hideTimerRef.current); hideTimerRef.current = null } },
|
|
529
|
-
|
|
611
|
+
style: { top: hoverAct.top, right: hoverAct.right ?? 12 },
|
|
612
|
+
onMouseEnter: () => { hoverPanelRef.current = true; if (hideTimerRef.current) { clearTimeout(hideTimerRef.current); hideTimerRef.current = null } },
|
|
613
|
+
onMouseMove: () => { hoverPanelRef.current = true; if (hideTimerRef.current) { clearTimeout(hideTimerRef.current); hideTimerRef.current = null } },
|
|
614
|
+
onMouseLeave: () => {
|
|
615
|
+
hoverPanelRef.current = false
|
|
616
|
+
if (hoverEditorRef.current || hideTimerRef.current) return
|
|
617
|
+
hideTimerRef.current = setTimeout(() => {
|
|
618
|
+
hideTimerRef.current = null
|
|
619
|
+
if (hoverEditorRef.current || hoverPanelRef.current) return
|
|
620
|
+
hoverKeyRef.current = null
|
|
621
|
+
hoverTopRef.current = null
|
|
622
|
+
setHoverAct(null)
|
|
623
|
+
}, 420)
|
|
624
|
+
},
|
|
530
625
|
},
|
|
531
|
-
React.createElement('button', { className: 'edrv-pill edrv-pill-keep', onClick: () => actHunk(hoverAct.region, false) }, '✓ Keep'),
|
|
532
|
-
React.createElement('button', { className: 'edrv-pill edrv-pill-undo', onClick: () => actHunk(hoverAct.region, true) }, '↩ Undo'))
|
|
626
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-keep', onClick: () => { dismissHover(); actHunk(hoverAct.region, false) } }, '✓ Keep'),
|
|
627
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-undo', onClick: () => { dismissHover(); actHunk(hoverAct.region, true) } }, '↩ Undo'))
|
|
533
628
|
: null
|
|
534
629
|
|
|
535
630
|
const overlay = launcherOpen
|