dsh-vscode-mode 0.1.27 → 0.1.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-vscode-mode",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "DSH 上的类 VSCode 编码体验:Monaco 编辑器(文件页签/QuickOpen/状态栏,可驻 dsh-better-sidebar 侧边栏与对话同屏)+ Agent 编辑差异审查(整文件差异/采纳/拒绝/归档/回滚),状态持久化到工作区旁车",
5
5
  "keywords": [
6
6
  "dsh",
@@ -54,17 +54,20 @@ export interface SlotSpec {
54
54
  * @param ctx 客户端上下文(slots 服务)
55
55
  * @param spec slot 描述
56
56
  * @param render 渲染函数
57
+ * @returns 注销函数(未注册返回 null;形态切换时用于卸载旧 slot)
57
58
  */
58
- export function registerSlotSafely(ctx: { slots?: { inject?: (name: string, register: () => unknown) => unknown; register?: (...args: unknown[]) => unknown } }, spec: SlotSpec, render: (props: unknown) => unknown): void {
59
+ export function registerSlotSafely(ctx: { slots?: { inject?: (name: string, register: () => unknown) => unknown; register?: (...args: unknown[]) => unknown } }, spec: SlotSpec, render: (props: unknown) => unknown): (() => void) | null {
59
60
  const slots = ctx?.slots as { inject: (name: string, register: () => unknown) => unknown; register: (...args: unknown[]) => unknown } | undefined
60
61
  if (!slots || typeof slots.inject !== 'function' || typeof slots.register !== 'function') {
61
62
  console.warn('[' + PLUGIN_NAME + '] slots 服务不可用,跳过 slot ' + spec.name)
62
- return
63
+ return null
63
64
  }
64
65
  try {
65
- slots.inject(spec.name, () => slots.register(spec, render))
66
+ const disposer = slots.inject(spec.name, () => slots.register(spec, render))
67
+ return typeof disposer === 'function' ? disposer as () => void : null
66
68
  } catch (error) {
67
69
  console.warn('[' + PLUGIN_NAME + '] slot ' + spec.name + ' 注册失败(' + String(error) + '),已跳过')
70
+ return null
68
71
  }
69
72
  }
70
73
 
@@ -79,7 +79,7 @@ export function apply(ctx: any): void {
79
79
  const settings = binder.scope
80
80
  let selected = autoValue('auto')
81
81
  /** 可选探测 betterSidebar 服务(不进 inject:缺失会让插件停靠等待,杀死回退路径)。 */
82
- const sideService = detectSidebarService(ctx)
82
+ let sideService = detectSidebarService(ctx)
83
83
  /** 「添加到对话」动作集:编辑区/Tab 右键菜单注入文件引用与代码块(conversation 服务缺失时各动作安全降级)。 */
84
84
  const addToConversation = createAddToConversation(ctx)
85
85
 
@@ -145,8 +145,8 @@ export function apply(ctx: any): void {
145
145
 
146
146
  // 中央「文件编辑」页签(旧形态回退):类 VSCode 编辑器;侧边栏形态可用时编辑器住 betterSidebar Tab,
147
147
  // 本页签不注册(避免双实例:Monaco×2 + diff dock 每会话单源抢占)。
148
- const registerLegacyTab = (): void => {
149
- registerSlotSafely(ctx, {
148
+ const registerLegacyTab = (): (() => void) | null => {
149
+ return registerSlotSafely(ctx, {
150
150
  name: 'conversation.view',
151
151
  id: 'edrv-editor',
152
152
  order: 5,
@@ -155,23 +155,52 @@ export function apply(ctx: any): void {
155
155
  }, (props: unknown) => React.createElement(EditorView, Object.assign({}, props, { layout: 'tab', sideHint: SIDEBAR_INSTALL_CMD, schedule, addToConversation, sidebarPanels, outlineSources, fileMenuItems })))
156
156
  }
157
157
 
158
- if (sideService) {
159
- // 侧边栏形态:注册「文件编辑」Tab(单实例、可按会话持久化、内容打开自动展开面板)
160
- ctx.effect(() => installSideEditor({
161
- service: sideService,
162
- renderTab: (props: Record<string, unknown>) => React.createElement(SideEditorTab, Object.assign({}, props, { schedule, addToConversation, sidebarPanels, outlineSources, fileMenuItems })),
163
- activeSession: () => {
164
- const snapshot = sessions?.list?.getSnapshot?.() as { current?: string; byId?: Record<string, { cwd?: string }> } | undefined
165
- const sessionId = snapshot?.current
166
- if (!sessionId) return undefined
167
- return { sessionId, cwd: snapshot?.byId?.[sessionId]?.cwd }
168
- },
169
- registerLegacyFallback: registerLegacyTab,
170
- }), 'vscode-mode: sidebar editor tab')
171
- } else {
172
- setEnsureSideEditor(null)
173
- registerLegacyTab()
158
+ // 侧边栏/中央页签两形态互斥切换:sideDisposer(侧边栏 Tab)与 legacyDisposer(中央页签)只保留其一。
159
+ // ⚠️ betterSidebar 服务由 dsh-better-sidebar 的 client bundle(1MB+)在页面加载时提供,本插件
160
+ // bundle 较小往往先执行完启动检测——一次性探测会误报「未检测到」并永久停在中央页签形态(实测
161
+ // 竞态:better-sidebar 已装,但「文件编辑」Tab 未注册)。故启动后按 2s 间隔重试(窗口 ~30s),
162
+ // 命中即自动切换侧边栏形态;始终未命中则保持中央页签回退。
163
+ let sideDisposer: (() => void) | null = null
164
+ let legacyDisposer: (() => void) | null = null
165
+ const applySideForm = (): void => {
166
+ if (sideService) {
167
+ if (sideDisposer !== null) return
168
+ if (legacyDisposer !== null) { legacyDisposer(); legacyDisposer = null }
169
+ sideDisposer = ctx.effect(() => installSideEditor({
170
+ service: sideService as NonNullable<typeof sideService>,
171
+ renderTab: (props: Record<string, unknown>) => React.createElement(SideEditorTab, Object.assign({}, props, { schedule, addToConversation, sidebarPanels, outlineSources, fileMenuItems })),
172
+ activeSession: () => {
173
+ const snapshot = sessions?.list?.getSnapshot?.() as { current?: string; byId?: Record<string, { cwd?: string }> } | undefined
174
+ const sessionId = snapshot?.current
175
+ if (!sessionId) return undefined
176
+ return { sessionId, cwd: snapshot?.byId?.[sessionId]?.cwd }
177
+ },
178
+ registerLegacyFallback: registerLegacyTab,
179
+ }), 'vscode-mode: sidebar editor tab')
180
+ } else {
181
+ if (legacyDisposer !== null) return
182
+ if (sideDisposer !== null) { sideDisposer(); sideDisposer = null }
183
+ setEnsureSideEditor(null)
184
+ legacyDisposer = registerLegacyTab()
185
+ }
186
+ }
187
+ applySideForm()
188
+ let sideRetries = 0
189
+ const retrySideService = (): void => {
190
+ if (sideService !== undefined || sideRetries >= 15) return
191
+ sideRetries += 1
192
+ schedule(() => {
193
+ if (sideService !== undefined) return
194
+ sideService = detectSidebarService(ctx)
195
+ if (sideService !== undefined) {
196
+ console.info('[dsh-vscode-mode] 检测到 ' + SIDEBAR_PLUGIN + ',切换侧边栏编辑形态')
197
+ applySideForm()
198
+ } else {
199
+ retrySideService()
200
+ }
201
+ }, 2000)
174
202
  }
203
+ retrySideService()
175
204
 
176
205
  // 对话输入框上方差异 dock:普通对话显示单文案按钮,文件编辑页由 EditorView 隐藏
177
206
  registerSlotSafely(ctx, {