dsh-vscode-mode 0.12.0 → 0.13.0
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 +129 -103
- package/assets/shell/dsh-open.cs +65 -149
- package/assets/shell/dsh-open.mjs +241 -0
- package/assets/shell/dsh-open.ps1 +28 -47
- package/assets/shell/dsh-open.sh +25 -66
- package/lib/client.js +12465 -11526
- package/lib/client.js.map +1 -1
- package/lib/index.js +3889 -2336
- package/lib/index.js.map +1 -1
- package/package.json +9 -8
- package/skills/dsh-vscodemode-mcp/SKILL.md +94 -132
- package/src/childEnv.ts +19 -0
- package/src/client/addToConversation.ts +4 -2
- package/src/client/ai/inlineProvider.ts +1 -8
- package/src/client/commandBridge.ts +6 -2
- package/src/client/externalOpen.ts +40 -19
- package/src/client/index.ts +10 -4
- package/src/client/keybindings.ts +18 -1
- package/src/client/monaco/loader.ts +3 -1
- package/src/client/openFlow.ts +36 -28
- package/src/client/openReceipt.ts +64 -0
- package/src/client/panelCommands.ts +101 -0
- package/src/client/pdf/pdfLoader.ts +4 -2
- package/src/client/receiptState.ts +120 -0
- package/src/client/rpc.ts +30 -4
- package/src/client/shortcutRecorder.ts +161 -0
- package/src/client/shortcutsOfficial.ts +13 -4
- package/src/client/sidebarBridge.ts +7 -0
- package/src/client/ui/DiffBadge.ts +2 -1
- package/src/client/ui/EditorView.ts +237 -55
- package/src/client/ui/McpSettings.ts +6 -3
- package/src/client/ui/OfficialSideTab.ts +5 -2
- package/src/client/ui/QuickOpen.ts +10 -9
- package/src/client/ui/ShortcutSettings.ts +34 -54
- package/src/client/ui/commandCatalog.ts +5 -4
- package/src/client/useOpenReceipt.ts +128 -0
- package/src/compat.ts +5 -3
- package/src/dap/manager.ts +46 -13
- package/src/devForm.ts +252 -92
- package/src/externalHandoff.ts +93 -52
- package/src/fileOpenSettings.ts +1 -24
- package/src/hostImport.ts +19 -0
- package/src/httpGuard.ts +101 -0
- package/src/index.ts +106 -97
- package/src/integrate.ts +56 -50
- package/src/lsp/transport.ts +84 -127
- package/src/mcp.ts +58 -9
- package/src/mcpIsolation.ts +211 -102
- package/src/mcpProject.ts +220 -133
- package/src/mcpRuntime.ts +343 -0
- package/src/openBridge.ts +69 -0
- package/src/openInbox.ts +207 -0
- package/src/paths.ts +8 -5
- package/src/processTree.ts +63 -0
- package/src/reveal.ts +5 -3
- package/src/routes.ts +156 -154
- package/src/rpc.ts +14 -13
- package/src/shared/appUrl.ts +19 -0
- package/src/shared/externalOpen.ts +71 -0
- package/src/shared/integration.ts +2 -0
- package/src/shared/keybindings.ts +8 -5
- package/src/shared/mcp.ts +4 -1
- package/src/shared/rpc.ts +7 -4
- package/src/shared/svn.ts +6 -8
- package/src/svnPlanTool.ts +51 -0
- package/src/unityBridge.ts +26 -3
- package/unity/com.dsh.editor/Editor/DshCodeEditor.cs +99 -203
- package/unity/com.dsh.editor/Editor/DshOpenWorker.cs +166 -0
- package/unity/com.dsh.editor/Editor/DshOpenWorker.cs.meta +2 -0
- package/unity/com.dsh.editor/README.md +16 -25
- package/unity/com.dsh.editor/package.json +2 -2
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { OfficialBinding, OfficialModifier } from '../shared/keybindings.js'
|
|
2
|
+
import type { ShortcutsServiceLike } from './shortcutsOfficial.js'
|
|
3
|
+
|
|
4
|
+
export interface RecordHooks {
|
|
5
|
+
ready(): void
|
|
6
|
+
save(binding: OfficialBinding | null): void
|
|
7
|
+
cancel(): void
|
|
8
|
+
error(error: unknown): void
|
|
9
|
+
}
|
|
10
|
+
const recorderHost = globalThis as typeof globalThis & { __edrvRecording__?: WeakMap<ShortcutsServiceLike, Promise<void>> }
|
|
11
|
+
const leases = recorderHost.__edrvRecording__ ??= new WeakMap<ShortcutsServiceLike, Promise<void>>()
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Serialize native recorder state, including cleanup after a late ready response.
|
|
15
|
+
* @author ddj 2026年09月28号
|
|
16
|
+
* @param service Native/Web shortcut service.
|
|
17
|
+
* @param active Desired native interception state.
|
|
18
|
+
* @returns Completion of the state change; errors remain visible to the caller.
|
|
19
|
+
*/
|
|
20
|
+
function recordState(service: ShortcutsServiceLike, active: boolean): Promise<void> {
|
|
21
|
+
const prior = leases.get(service) ?? Promise.resolve()
|
|
22
|
+
const next = prior.catch(() => {}).then(() => service.recording(active))
|
|
23
|
+
leases.set(service, next)
|
|
24
|
+
return next
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read physical modifiers without converting Control or Meta into logical primary.
|
|
29
|
+
* @author ddj 2026年09月28号
|
|
30
|
+
* @param event Keyboard event.
|
|
31
|
+
* @returns Modifiers in official canonical order.
|
|
32
|
+
*/
|
|
33
|
+
export function eventModifiers(event: KeyboardEvent): OfficialModifier[] {
|
|
34
|
+
const states = { control: event.ctrlKey, alt: event.altKey, shift: event.shiftKey, meta: event.metaKey }
|
|
35
|
+
return (Object.keys(states) as Array<keyof typeof states>).filter((key) => states[key])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Physical key collection; native readiness and listeners are owned by startRecording. */
|
|
39
|
+
export class KeyCapture {
|
|
40
|
+
private held = new Set<string>()
|
|
41
|
+
private pending: OfficialBinding | null = null
|
|
42
|
+
private blocked = false
|
|
43
|
+
private dead = false
|
|
44
|
+
private composing = false
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create a physical collector with official platform capabilities.
|
|
48
|
+
* @author ddj 2026年09月28号
|
|
49
|
+
* @param service Official binding validator and profile.
|
|
50
|
+
* @param hooks Completion/error handlers.
|
|
51
|
+
*/
|
|
52
|
+
constructor(private service: ShortcutsServiceLike, private hooks: RecordHooks) {}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Reset a gesture on blur or composition transitions.
|
|
56
|
+
* @author ddj 2026年09月28号
|
|
57
|
+
* @param composing Whether IME composition is currently active.
|
|
58
|
+
*/
|
|
59
|
+
reset(composing = false): void {
|
|
60
|
+
this.held.clear(); this.pending = null; this.blocked = false; this.dead = false
|
|
61
|
+
this.composing = composing
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Collect one physical press; commit only when a constituent key is released.
|
|
66
|
+
* @author ddj 2026年09月28号
|
|
67
|
+
* @param event Keyboard press; guarded IME/AltGraph input is left untouched.
|
|
68
|
+
*/
|
|
69
|
+
down(event: KeyboardEvent): void {
|
|
70
|
+
if (this.composing || event.isComposing || event.keyCode === 229 || event.getModifierState?.('AltGraph')) {
|
|
71
|
+
this.pending = null; this.held.clear(); return
|
|
72
|
+
}
|
|
73
|
+
const commandDead = this.service.runtime === 'web' && this.service.platform === 'macos'
|
|
74
|
+
&& event.code === 'KeyN' && event.metaKey && event.altKey && !event.ctrlKey && !event.shiftKey
|
|
75
|
+
if (event.key === 'Dead' && !commandDead) { this.reset(); this.dead = true; return }
|
|
76
|
+
if (this.dead) { this.dead = false; return }
|
|
77
|
+
event.preventDefault(); event.stopPropagation()
|
|
78
|
+
if (event.repeat) return
|
|
79
|
+
const modifiers = eventModifiers(event)
|
|
80
|
+
if (!modifiers.length && event.key === 'Escape') { this.hooks.cancel(); return }
|
|
81
|
+
if (!modifiers.length && (event.key === 'Backspace' || event.key === 'Delete')) { this.hooks.save(null); return }
|
|
82
|
+
if (/^(Control|Alt|Shift|Meta|AltGraph)(Left|Right)?$/u.test(event.code || event.key)) {
|
|
83
|
+
if (this.pending) this.reset()
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
const chords = this.service.runtime === 'desktop' && ['macos', 'windows'].includes(this.service.platform)
|
|
87
|
+
if (chords) this.held.add(event.code)
|
|
88
|
+
if (this.blocked) return
|
|
89
|
+
if (this.held.size > 2) {
|
|
90
|
+
this.pending = null; this.blocked = true; this.hooks.error('最多同时录制两个主键'); return
|
|
91
|
+
}
|
|
92
|
+
const codes = chords ? [...this.held] : [event.code]
|
|
93
|
+
try {
|
|
94
|
+
const probe = this.service.describeBinding({ code: codes[0], modifiers, ...(codes[1] ? { secondCode: codes[1] } : {}) })
|
|
95
|
+
if (!probe.binding || probe.issue) throw new Error(probe.issue ?? 'unsupported-key')
|
|
96
|
+
this.pending = probe.binding
|
|
97
|
+
} catch (error) { this.pending = null; this.hooks.error(error) }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Commit an accepted physical gesture once at release.
|
|
102
|
+
* @author ddj 2026年09月28号
|
|
103
|
+
* @param event Released key or modifier.
|
|
104
|
+
*/
|
|
105
|
+
up(event: KeyboardEvent): void {
|
|
106
|
+
this.held.delete(event.code)
|
|
107
|
+
const binding = this.pending
|
|
108
|
+
const modifier = event.code.replace(/(Left|Right)$/u, '').toLowerCase() as OfficialModifier
|
|
109
|
+
if (!binding || !(binding.code === event.code || binding.secondCode === event.code || binding.modifiers.includes(modifier))) return
|
|
110
|
+
this.pending = null
|
|
111
|
+
event.preventDefault(); event.stopPropagation()
|
|
112
|
+
this.hooks.save(binding)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Protect recording before listening, and restore interception on every exit path.
|
|
118
|
+
* @author ddj 2026年09月28号
|
|
119
|
+
* @param service Official service whose asynchronous native bridge must be ready.
|
|
120
|
+
* @param target Owning window, injectable for lifecycle tests.
|
|
121
|
+
* @param hooks Ready, save, cancel and failure callbacks.
|
|
122
|
+
* @returns Idempotent disposal, safe before readiness and after failure.
|
|
123
|
+
*/
|
|
124
|
+
export function startRecording(service: ShortcutsServiceLike, target: EventTarget, hooks: RecordHooks): () => void {
|
|
125
|
+
let disposed = false
|
|
126
|
+
let ready = false
|
|
127
|
+
/** @author ddj 2026年09月28号 @param binding Accepted binding; release interception before saving. */
|
|
128
|
+
const save = (binding: OfficialBinding | null): void => { dispose(); hooks.save(binding) }
|
|
129
|
+
/** @author ddj 2026年09月28号 Cancel capture and restore native interception. */
|
|
130
|
+
const cancel = (): void => { dispose(); hooks.cancel() }
|
|
131
|
+
const capture = new KeyCapture(service, { ...hooks, save, cancel })
|
|
132
|
+
/** @author ddj 2026年09月28号 @param event Physical key press. */
|
|
133
|
+
const down = (event: Event): void => { if (ready) capture.down(event as KeyboardEvent) }
|
|
134
|
+
/** @author ddj 2026年09月28号 @param event Physical key release. */
|
|
135
|
+
const up = (event: Event): void => { if (ready) capture.up(event as KeyboardEvent) }
|
|
136
|
+
/** @author ddj 2026年09月28号 Reset incomplete gesture on focus loss. */
|
|
137
|
+
const blur = (): void => capture.reset()
|
|
138
|
+
/** @author ddj 2026年09月28号 Guard the complete IME composition lifecycle. */
|
|
139
|
+
const compose = (): void => capture.reset(true)
|
|
140
|
+
const listeners: Array<[string, EventListener]> = [
|
|
141
|
+
['keydown', down], ['keyup', up], ['blur', blur], ['compositionstart', compose], ['compositionend', blur],
|
|
142
|
+
]
|
|
143
|
+
/** @author ddj 2026年09月28号 Release DOM listeners and serialize native restoration. */
|
|
144
|
+
const dispose = (): void => {
|
|
145
|
+
if (disposed) return
|
|
146
|
+
disposed = true; ready = false
|
|
147
|
+
for (const [name, handler] of listeners) target.removeEventListener(name, handler, { capture: true })
|
|
148
|
+
void recordState(service, false).catch(hooks.error)
|
|
149
|
+
}
|
|
150
|
+
/** @author ddj 2026年09月28号 Activate capture only after native acknowledgment. */
|
|
151
|
+
const activate = (): void => {
|
|
152
|
+
if (disposed) return
|
|
153
|
+
ready = true
|
|
154
|
+
for (const [name, handler] of listeners) target.addEventListener(name, handler, { capture: true })
|
|
155
|
+
hooks.ready()
|
|
156
|
+
}
|
|
157
|
+
/** @author ddj 2026年09月28号 @param error Native activation failure. */
|
|
158
|
+
const fail = (error: unknown): void => { if (!disposed) { dispose(); hooks.error(error) } }
|
|
159
|
+
void recordState(service, true).then(activate, fail)
|
|
160
|
+
return dispose
|
|
161
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 作者 ddj 2026年10月
|
|
9
9
|
*/
|
|
10
10
|
import { bindingToChord, chordToBinding, defaultKeybindings, normalizeKeybindings } from '../shared/keybindings.js'
|
|
11
|
-
import { matchEvent, parseChords } from './keybindings.js'
|
|
11
|
+
import { applyKeyAria, matchEvent, parseChords } from './keybindings.js'
|
|
12
12
|
import type { OfficialBinding, ShortcutProfileKey } from '../shared/keybindings.js'
|
|
13
13
|
import { log } from './log.js'
|
|
14
14
|
|
|
@@ -193,7 +193,9 @@ export function installLegacyKeys(
|
|
|
193
193
|
): () => void {
|
|
194
194
|
if (!target) return () => {}
|
|
195
195
|
const cache = new Map<string, ReturnType<typeof parseChords>>()
|
|
196
|
+
let disposed = false
|
|
196
197
|
const onKey = (event: Event): void => {
|
|
198
|
+
if (disposed) return
|
|
197
199
|
const key = event as KeyboardEvent
|
|
198
200
|
const current = chords()
|
|
199
201
|
for (const def of defs) {
|
|
@@ -211,8 +213,12 @@ export function installLegacyKeys(
|
|
|
211
213
|
return
|
|
212
214
|
}
|
|
213
215
|
}
|
|
214
|
-
target.addEventListener('keydown', onKey, true)
|
|
215
|
-
return () =>
|
|
216
|
+
target.addEventListener('keydown', onKey, { capture: true })
|
|
217
|
+
return () => {
|
|
218
|
+
if (disposed) return
|
|
219
|
+
disposed = true
|
|
220
|
+
target.removeEventListener('keydown', onKey, { capture: true })
|
|
221
|
+
}
|
|
216
222
|
}
|
|
217
223
|
|
|
218
224
|
/**
|
|
@@ -283,16 +289,19 @@ export function registerOfficialShortcuts(
|
|
|
283
289
|
export function bindCatalogChords(service: ShortcutsServiceLike, onChange: (chords: Readonly<Record<string, string>>) => void): () => void {
|
|
284
290
|
const sync = (): void => {
|
|
285
291
|
const chords: Record<string, string> = {}
|
|
292
|
+
const aria: Record<string, string | undefined> = {}
|
|
286
293
|
try {
|
|
287
294
|
for (const row of service.catalog.getSnapshot()) {
|
|
288
295
|
if (!row.id.startsWith('edrv.') || row.binding === null) continue
|
|
289
296
|
if (row.issue !== null) continue
|
|
290
|
-
const chord = bindingToChord(row.binding)
|
|
297
|
+
const chord = row.keys.length ? row.keys.join(' ') : bindingToChord(row.binding)
|
|
291
298
|
if (chord !== null) chords[row.id] = chord
|
|
299
|
+
aria[row.id] = row.aria
|
|
292
300
|
}
|
|
293
301
|
} catch (error) {
|
|
294
302
|
bridgeLog.warn('官方目录弦表重建失败:' + String(error))
|
|
295
303
|
}
|
|
304
|
+
applyKeyAria(aria)
|
|
296
305
|
onChange(chords)
|
|
297
306
|
}
|
|
298
307
|
sync()
|
|
@@ -133,6 +133,13 @@ export function takePendingSideOpen(sessionId?: string): { path: string | null;
|
|
|
133
133
|
* @param focusDiff 是否聚焦首个差异
|
|
134
134
|
* @returns 是否路由成功
|
|
135
135
|
*/
|
|
136
|
+
/**
|
|
137
|
+
* Check the installed editor route without opening any UI.
|
|
138
|
+
* @author ddj 2026年09月28号
|
|
139
|
+
* @returns Whether a mounted sidebar adapter owns editor navigation.
|
|
140
|
+
*/
|
|
141
|
+
export function hasSideRoute(): boolean { return ensureSideEditor !== null }
|
|
142
|
+
|
|
136
143
|
export function routeSideEditor(path: string | null, focusDiff: boolean): boolean {
|
|
137
144
|
if (typeof ensureSideEditor !== 'function') return false
|
|
138
145
|
try {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* 作者 ddj 2026-08-20
|
|
6
6
|
*/
|
|
7
7
|
import React from 'react'
|
|
8
|
+
import { appUrl } from '../../shared/appUrl.js'
|
|
8
9
|
import { rpc } from '../rpc.js'
|
|
9
10
|
import { openEditorView, emitShowLauncher } from '../events.js'
|
|
10
11
|
import { summarize } from '../state/records.js'
|
|
@@ -48,6 +49,6 @@ export function DiffBadge(props) {
|
|
|
48
49
|
title: n + ' 个文件有差异,点击在编辑区查看',
|
|
49
50
|
style: { position: 'relative', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 30, height: 30, padding: 0, background: 'transparent', border: 'none', borderRadius: 6, cursor: 'pointer' },
|
|
50
51
|
},
|
|
51
|
-
React.createElement('img', { src: '/edrv/assets/compare-select.png', alt: '差异', style: { width: 22, height: 22, display: 'block' } }),
|
|
52
|
+
React.createElement('img', { src: appUrl('/edrv/assets/compare-select.png'), alt: '差异', style: { width: 22, height: 22, display: 'block' } }),
|
|
52
53
|
React.createElement('span', { style: { position: 'absolute', top: 1, right: 0, minWidth: 15, height: 15, padding: '0 3px', borderRadius: 8, background: 'var(--dsw-alias-state-warn-primary,#d97706)', color: '#fff', fontSize: 10, lineHeight: '15px', textAlign: 'center', fontWeight: 700, boxSizing: 'border-box' } }, String(n)))
|
|
53
54
|
}
|