dsh-vscode-mode 0.1.26 → 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.26",
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
 
@@ -37,7 +37,10 @@ import { createOutlinePanel } from './outline/index.js'
37
37
  import { createOutlineSourceRegistry, registerBuiltinOutlineSources } from './outline/sources.js'
38
38
  import type { CompatAdapter } from '../shared/compat.js'
39
39
 
40
- export const inject = ['slots', 'timer', 'locale', 'connection', 'remote', 'workspaces', 'sessions', 'conversation', 'settingsScope', 'webUiSettings']
40
+ // ⚠️ inject 只列必需服务:webUiSettings 是 @linxin666/dsh-client-ui-web-ui-settings 提供的
41
+ // 可选兼容桥(非官方服务),列入 inject 会让未装该桥的部署启动时 entry 永久 pending,
42
+ // web boot 直接失败('1 entry did not activate')。兼容层用 ctx.get 运行时探测 + 降级,不靠 inject。
43
+ export const inject = ['slots', 'timer', 'locale', 'connection', 'remote', 'workspaces', 'sessions', 'conversation', 'settingsScope']
41
44
 
42
45
  /**
43
46
  * 装配客户端:注册中央编辑区视图与 header 差异角标。
@@ -76,7 +79,7 @@ export function apply(ctx: any): void {
76
79
  const settings = binder.scope
77
80
  let selected = autoValue('auto')
78
81
  /** 可选探测 betterSidebar 服务(不进 inject:缺失会让插件停靠等待,杀死回退路径)。 */
79
- const sideService = detectSidebarService(ctx)
82
+ let sideService = detectSidebarService(ctx)
80
83
  /** 「添加到对话」动作集:编辑区/Tab 右键菜单注入文件引用与代码块(conversation 服务缺失时各动作安全降级)。 */
81
84
  const addToConversation = createAddToConversation(ctx)
82
85
 
@@ -142,8 +145,8 @@ export function apply(ctx: any): void {
142
145
 
143
146
  // 中央「文件编辑」页签(旧形态回退):类 VSCode 编辑器;侧边栏形态可用时编辑器住 betterSidebar Tab,
144
147
  // 本页签不注册(避免双实例:Monaco×2 + diff dock 每会话单源抢占)。
145
- const registerLegacyTab = (): void => {
146
- registerSlotSafely(ctx, {
148
+ const registerLegacyTab = (): (() => void) | null => {
149
+ return registerSlotSafely(ctx, {
147
150
  name: 'conversation.view',
148
151
  id: 'edrv-editor',
149
152
  order: 5,
@@ -152,23 +155,52 @@ export function apply(ctx: any): void {
152
155
  }, (props: unknown) => React.createElement(EditorView, Object.assign({}, props, { layout: 'tab', sideHint: SIDEBAR_INSTALL_CMD, schedule, addToConversation, sidebarPanels, outlineSources, fileMenuItems })))
153
156
  }
154
157
 
155
- if (sideService) {
156
- // 侧边栏形态:注册「文件编辑」Tab(单实例、可按会话持久化、内容打开自动展开面板)
157
- ctx.effect(() => installSideEditor({
158
- service: sideService,
159
- renderTab: (props: Record<string, unknown>) => React.createElement(SideEditorTab, Object.assign({}, props, { schedule, addToConversation, sidebarPanels, outlineSources, fileMenuItems })),
160
- activeSession: () => {
161
- const snapshot = sessions?.list?.getSnapshot?.() as { current?: string; byId?: Record<string, { cwd?: string }> } | undefined
162
- const sessionId = snapshot?.current
163
- if (!sessionId) return undefined
164
- return { sessionId, cwd: snapshot?.byId?.[sessionId]?.cwd }
165
- },
166
- registerLegacyFallback: registerLegacyTab,
167
- }), 'vscode-mode: sidebar editor tab')
168
- } else {
169
- setEnsureSideEditor(null)
170
- 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)
171
202
  }
203
+ retrySideService()
172
204
 
173
205
  // 对话输入框上方差异 dock:普通对话显示单文案按钮,文件编辑页由 EditorView 隐藏
174
206
  registerSlotSafely(ctx, {