dsh-vscode-mode 0.1.48 → 0.1.49
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 +10 -1
- package/lib/client.js +557 -32
- package/lib/client.js.map +1 -1
- package/lib/index.js +562 -3
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/compat.ts +1 -1
- package/src/client/index.ts +9 -2
- package/src/client/paths.ts +2 -0
- package/src/client/sidebar/SidebarView.ts +82 -16
- package/src/client/sidebar/panels/RulesPanel.ts +284 -0
- package/src/client/sidebar/panels/index.ts +18 -2
- package/src/client/sidebar/types.ts +2 -0
- package/src/client/sidebarMin.ts +48 -0
- package/src/client/styles/editor.css +43 -0
- package/src/client/ui/EditorView.ts +22 -4
- package/src/client/ui/McpSettings.ts +37 -2
- package/src/client/ui/SideEditorTab.ts +1 -0
- package/src/fileOpenSettings.ts +2 -0
- package/src/index.ts +5 -1
- package/src/rpc.ts +37 -0
- package/src/rules.ts +528 -0
- package/src/shared/rpc.ts +11 -0
- package/src/shared/rules.ts +60 -0
|
@@ -295,5 +295,48 @@
|
|
|
295
295
|
[data-edrv-view] .edrv-search-toggle.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); border-color: var(--dsw-alias-brand-primary, #0f9d58); }
|
|
296
296
|
[data-edrv-view] .edrv-search-toggle:disabled { opacity: .4; cursor: default; }
|
|
297
297
|
|
|
298
|
+
/* ===== rules panel (sidebar, Codebuddy rules view) ===== */
|
|
299
|
+
[data-edrv-view] .edrv-rules-panel { display: flex; flex-direction: column; min-height: 0; flex: 1; padding: 6px; gap: 4px; }
|
|
300
|
+
[data-edrv-view] .edrv-rules-tabs { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
|
|
301
|
+
[data-edrv-view] .edrv-rules-tab { height: 24px; padding: 0 10px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; }
|
|
302
|
+
[data-edrv-view] .edrv-rules-tab:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }
|
|
303
|
+
[data-edrv-view] .edrv-rules-tab.on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); color: var(--dsw-alias-label-primary, #1f2933); font-weight: 600; }
|
|
304
|
+
[data-edrv-view] .edrv-rules-new { margin-left: auto; height: 24px; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #cbd2d9); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; cursor: pointer; flex-shrink: 0; }
|
|
305
|
+
[data-edrv-view] .edrv-rules-new:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }
|
|
306
|
+
[data-edrv-view] .edrv-rules-ws-line { font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); padding: 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 0 auto; user-select: none; }
|
|
307
|
+
[data-edrv-view] .edrv-rules-hint { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); padding: 0 2px; flex: 0 0 auto; }
|
|
308
|
+
[data-edrv-view] .edrv-rules-list { flex: 1; min-height: 0; overflow: auto; }
|
|
309
|
+
[data-edrv-view] .edrv-rules-row { position: relative; padding: 7px 100px 6px 4px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }
|
|
310
|
+
[data-edrv-view] .edrv-rules-row.off .edrv-rules-file, [data-edrv-view] .edrv-rules-row.off .edrv-rules-desc { opacity: .5; }
|
|
311
|
+
[data-edrv-view] .edrv-rules-main { display: flex; align-items: center; gap: 6px; min-width: 0; cursor: pointer; }
|
|
312
|
+
[data-edrv-view] .edrv-rules-file { font-weight: 600; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
313
|
+
[data-edrv-view] .edrv-rules-path { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
314
|
+
[data-edrv-view] .edrv-rules-type { flex-shrink: 0; height: 15px; padding: 0 5px; border-radius: 999px; font-size: 10px; line-height: 15px; }
|
|
315
|
+
[data-edrv-view] .edrv-rules-type-always { background: rgba(15,157,88,.15); color: #0b7c46; }
|
|
316
|
+
[data-edrv-view] .edrv-rules-type-auto { background: rgba(79,140,255,.16); color: #3367c9; }
|
|
317
|
+
[data-edrv-view] .edrv-rules-type-manual { background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }
|
|
318
|
+
[data-edrv-view] .edrv-rules-offtag { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }
|
|
319
|
+
[data-edrv-view] .edrv-rules-desc { margin-top: 3px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
320
|
+
[data-edrv-view] .edrv-rules-actions { position: absolute; top: 5px; right: 4px; display: flex; align-items: center; gap: 2px; }
|
|
321
|
+
[data-edrv-view] .edrv-rules-act { min-width: 22px; height: 22px; padding: 0 4px; border: none; border-radius: 5px; background: transparent; font-size: 12px; line-height: 1; cursor: pointer; color: var(--dsw-alias-label-secondary, #52606d); }
|
|
322
|
+
[data-edrv-view] .edrv-rules-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }
|
|
323
|
+
[data-edrv-view] .edrv-rules-switch { position: relative; width: 30px; height: 16px; border-radius: 999px; border: none; background: #b9c4cc; cursor: pointer; transition: background .15s; flex-shrink: 0; padding: 0; }
|
|
324
|
+
[data-edrv-view] .edrv-rules-switch.on { background: #7c5cff; }
|
|
325
|
+
[data-edrv-view] .edrv-rules-knob { position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%; background: #fff; transition: left .15s; }
|
|
326
|
+
[data-edrv-view] .edrv-rules-switch.on .edrv-rules-knob { left: 16px; }
|
|
327
|
+
[data-edrv-view] .edrv-rules-empty { padding: 10px 4px; font-size: 12px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.6; }
|
|
328
|
+
[data-edrv-view] .edrv-rules-err { padding: 6px 8px; border: 1px solid rgba(217,83,79,.4); border-radius: 6px; background: #fdeaea; color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; word-break: break-all; }
|
|
329
|
+
[data-edrv-view] .edrv-rules-editor { display: flex; flex-direction: column; min-height: 0; flex: 1; gap: 6px; }
|
|
330
|
+
[data-edrv-view] .edrv-rules-editor-head { display: flex; align-items: center; justify-content: space-between; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); flex: 0 0 auto; }
|
|
331
|
+
[data-edrv-view] .edrv-rules-editor-file { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); flex: 0 0 auto; }
|
|
332
|
+
[data-edrv-view] .edrv-rules-input { flex: 1; min-width: 0; height: 24px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 11px; outline: none; font-family: var(--ds-font-family-code, ui-monospace, monospace); }
|
|
333
|
+
[data-edrv-view] .edrv-rules-input:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }
|
|
334
|
+
[data-edrv-view] .edrv-rules-body { flex: 1; min-height: 0; resize: none; padding: 8px; border: 1px solid var(--dsw-alias-border-l2, #555); border-radius: 6px; background: var(--dsw-alias-bg-base, #111); color: var(--dsw-alias-label-primary, #eee); font-size: 12px; line-height: 1.5; outline: none; font-family: var(--ds-font-family-code, ui-monospace, monospace); }
|
|
335
|
+
[data-edrv-view] .edrv-rules-body:focus { border-color: var(--dsw-alias-brand-primary, #4f8cff); }
|
|
336
|
+
[data-edrv-view] .edrv-rules-editor-hint { font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); line-height: 1.5; flex: 0 0 auto; }
|
|
337
|
+
[data-edrv-view] .edrv-rules-editor-foot { display: flex; justify-content: flex-end; flex: 0 0 auto; }
|
|
338
|
+
[data-edrv-view] .edrv-rules-save { height: 24px; padding: 0 14px; border: none; border-radius: 6px; background: var(--dsw-alias-brand-primary, #0f9d58); color: #fff; font-size: 12px; cursor: pointer; }
|
|
339
|
+
[data-edrv-view] .edrv-rules-save:disabled { opacity: .5; cursor: default; }
|
|
340
|
+
|
|
298
341
|
|
|
299
342
|
|
|
@@ -23,6 +23,7 @@ import { revealInExplorer as revealPathInExplorer } from '../fileReveal.js'
|
|
|
23
23
|
import { setSidePending, SIDEBAR_INSTALL_CMD } from '../sidebarBridge.js'
|
|
24
24
|
import { upsertViewState, viewStatesLoad, viewStatesSave } from '../state/viewStateCache.js'
|
|
25
25
|
import { bindingsOf, chordOf, matchEvent, useKeybindingsVersion } from '../keybindings.js'
|
|
26
|
+
import { getSidebarMinWidth } from '../sidebarMin.js'
|
|
26
27
|
import { createNavHistory } from '../navHistory.js'
|
|
27
28
|
import { statusOfAdd } from '../addToConversation.js'
|
|
28
29
|
import { CACHE_KEY } from '../paths.js'
|
|
@@ -67,7 +68,7 @@ export function EditorView(props) {
|
|
|
67
68
|
const [fileIdx, setFileIdx] = React.useState(0) // 全局差异文件位置(x/x 文件 显示)
|
|
68
69
|
const [tabMenu, setTabMenu] = React.useState(null) // Tab 右键菜单 { x,y,path }(编辑区右键走 Monaco 原生菜单,无此浮层)
|
|
69
70
|
const [sidebarOn, setSidebarOn] = React.useState(layout !== 'side') // 侧边栏显隐(侧栏形态默认收起)
|
|
70
|
-
const [sidebarW, setSidebarW] = React.useState(
|
|
71
|
+
const [sidebarW, setSidebarW] = React.useState(() => getSidebarMinWidth()) // 侧边栏宽度(初值 = 最小宽度,默认 300)
|
|
71
72
|
const [activePanel, setActivePanel] = React.useState('explorer') // 激活面板 id
|
|
72
73
|
const [focusRequest, setFocusRequest] = React.useState(0) // 外部差异聚焦请求版本
|
|
73
74
|
const kbVersion = useKeybindingsVersion() // 快捷键配置版本(tooltip 文案随键位刷新)
|
|
@@ -508,6 +509,7 @@ export function EditorView(props) {
|
|
|
508
509
|
}, [tabs, active, sessionId])
|
|
509
510
|
|
|
510
511
|
// 侧边栏状态:恢复(显隐/宽度/激活面板);侧栏形态独立键(默认收起,不共享页签形态偏好)
|
|
512
|
+
// 宽度下限来自通用设置 sidebarMinWidth(默认 300),恢复值按其重夹
|
|
511
513
|
const sidebarKey = CACHE_KEY.sidebar + (layout === 'side' ? 'side.' : '') + String(sessionId)
|
|
512
514
|
React.useEffect(() => {
|
|
513
515
|
if (!sessionId) return
|
|
@@ -516,12 +518,23 @@ export function EditorView(props) {
|
|
|
516
518
|
if (raw) {
|
|
517
519
|
const saved = JSON.parse(raw)
|
|
518
520
|
if (typeof saved.on === 'boolean') setSidebarOn(saved.on)
|
|
519
|
-
if (typeof saved.width === 'number') setSidebarW(Math.max(
|
|
521
|
+
if (typeof saved.width === 'number') setSidebarW(Math.max(getSidebarMinWidth(), Math.min(560, saved.width)))
|
|
520
522
|
if (typeof saved.panel === 'string') setActivePanel(saved.panel)
|
|
521
523
|
}
|
|
522
524
|
} catch (e) { /* 损坏忽略 */ }
|
|
523
525
|
}, [sessionId, sidebarKey])
|
|
524
526
|
|
|
527
|
+
// 侧边栏最小宽度设置变更(edrv:sidebar-min-width)→ 更新 minW 并重夹当前宽度
|
|
528
|
+
const [sidebarMinW, setSidebarMinW] = React.useState(() => getSidebarMinWidth())
|
|
529
|
+
React.useEffect(() => {
|
|
530
|
+
const onMinChange = () => {
|
|
531
|
+
setSidebarMinW(getSidebarMinWidth())
|
|
532
|
+
setSidebarW((w) => Math.max(getSidebarMinWidth(), w))
|
|
533
|
+
}
|
|
534
|
+
window.addEventListener('edrv:sidebar-min-width', onMinChange)
|
|
535
|
+
return () => window.removeEventListener('edrv:sidebar-min-width', onMinChange)
|
|
536
|
+
}, [])
|
|
537
|
+
|
|
525
538
|
// 侧边栏状态持久化
|
|
526
539
|
React.useEffect(() => {
|
|
527
540
|
if (!sessionId) return
|
|
@@ -1244,7 +1257,7 @@ export function EditorView(props) {
|
|
|
1244
1257
|
'aria-label': '前进',
|
|
1245
1258
|
onClick: navForward,
|
|
1246
1259
|
}, '→'),
|
|
1247
|
-
React.createElement('button', { className: 'edrv-chip-btn' + (sidebarOn ? ' edrv-chip-on' : ''), title: '
|
|
1260
|
+
React.createElement('button', { className: 'edrv-chip-btn' + (sidebarOn ? ' edrv-chip-on' : ''), title: '切换面板区 (' + (chordOf('edrv.toggleSidebar') ?? 'Ctrl+B') + '),图标列常显', onClick: () => setSidebarOn((v) => !v) }, '☰'),
|
|
1248
1261
|
React.createElement('button', { className: 'edrv-chip-btn', title: '刷新', onClick: reloadFile }, '⟳'),
|
|
1249
1262
|
React.createElement(QuickOpen, { sessionId, onOpen: (p) => openFile(p, false) }))
|
|
1250
1263
|
|
|
@@ -1390,6 +1403,8 @@ export function EditorView(props) {
|
|
|
1390
1403
|
const sidebarPanels = props.sidebarPanels
|
|
1391
1404
|
const sidebarCtx = {
|
|
1392
1405
|
sessionId,
|
|
1406
|
+
// 当前会话工作区(sessions 快照 byId[sessionId].cwd;规则面板项目 Tab 自动匹配)
|
|
1407
|
+
cwd: props.sessions?.list?.getSnapshot?.()?.byId?.[sessionId]?.cwd ?? null,
|
|
1393
1408
|
openFile: (p) => openFile(p, false),
|
|
1394
1409
|
openFileAt,
|
|
1395
1410
|
activePath: active,
|
|
@@ -1432,15 +1447,18 @@ export function EditorView(props) {
|
|
|
1432
1447
|
// 编辑器根节点按 composer 顶部边界动态限高,底部对话区域继续由 DSH 原生渲染。
|
|
1433
1448
|
// 侧栏形态由面板容器给高(100%),不做 composer 几何同步。
|
|
1434
1449
|
const editorRow = React.createElement('div', { className: 'edrv-editor-row' },
|
|
1435
|
-
(
|
|
1450
|
+
(sidebarPanels
|
|
1436
1451
|
? React.createElement(SidebarView, {
|
|
1437
1452
|
registry: sidebarPanels,
|
|
1438
1453
|
ctx: sidebarCtx,
|
|
1439
1454
|
activePanel,
|
|
1440
1455
|
onActive: setActivePanel,
|
|
1456
|
+
visible: sidebarOn,
|
|
1457
|
+
onShow: () => setSidebarOn(true),
|
|
1441
1458
|
width: sidebarW,
|
|
1442
1459
|
onWidth: setSidebarW,
|
|
1443
1460
|
onHide: () => setSidebarOn(false),
|
|
1461
|
+
minWidth: sidebarMinW,
|
|
1444
1462
|
})
|
|
1445
1463
|
: null),
|
|
1446
1464
|
mainCol)
|
|
@@ -10,6 +10,7 @@ import type { MpcProject, MpcServer } from '../../shared/mcp.js'
|
|
|
10
10
|
import '../styles/mcp.css'
|
|
11
11
|
import { availableOpeners, AUTO_OPEN_TOOL } from '../fileOpeners.js'
|
|
12
12
|
import { SettingsContext } from '../settingsContext.js'
|
|
13
|
+
import { normalizeSidebarMinWidth, SIDEBAR_MIN_DEFAULT } from '../sidebarMin.js'
|
|
13
14
|
import { KeybindingsPanel } from './KeybindingsPanel.js'
|
|
14
15
|
import { LspSettings } from './LspSettings.js'
|
|
15
16
|
import { PerfSettings } from './PerfSettings.js'
|
|
@@ -122,7 +123,7 @@ function ProjectGroup({ project, busy, onAdd, onRefresh, onToggle, onRemove }) {
|
|
|
122
123
|
return React.createElement('section', { className: 'vsm-project' }, head, body)
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
/** 通用设置:选择当前对话文件链接的打开器 +
|
|
126
|
+
/** 通用设置:选择当前对话文件链接的打开器 + 开发形态开关 + 编辑器布局(侧边栏最小宽度)。 */
|
|
126
127
|
function GeneralSettings({ registry }) {
|
|
127
128
|
const [tool, setTool] = React.useState(AUTO_OPEN_TOOL)
|
|
128
129
|
const [busy, setBusy] = React.useState(false)
|
|
@@ -130,6 +131,8 @@ function GeneralSettings({ registry }) {
|
|
|
130
131
|
const [devForm, setDevForm] = React.useState(null)
|
|
131
132
|
const [devBusy, setDevBusy] = React.useState(false)
|
|
132
133
|
const [devMessage, setDevMessage] = React.useState('')
|
|
134
|
+
const [minW, setMinW] = React.useState(SIDEBAR_MIN_DEFAULT)
|
|
135
|
+
const [minDraft, setMinDraft] = React.useState(null) // 输入草稿(null=跟随已保存值;blur/Enter 提交)
|
|
133
136
|
const settings = React.useContext(SettingsContext)
|
|
134
137
|
const snapshot = settings?.getSnapshot?.()
|
|
135
138
|
const loading = !snapshot || snapshot.status === 'loading'
|
|
@@ -137,8 +140,10 @@ function GeneralSettings({ registry }) {
|
|
|
137
140
|
const notReady = snapshot?.status !== 'ready'
|
|
138
141
|
React.useEffect(() => {
|
|
139
142
|
const onChange = () => {
|
|
140
|
-
const
|
|
143
|
+
const snap = settings?.getSnapshot?.()
|
|
144
|
+
const next = snap?.value?.fileOpenTool
|
|
141
145
|
if (typeof next === 'string') setTool(next)
|
|
146
|
+
setMinW(normalizeSidebarMinWidth(snap?.value?.sidebarMinWidth))
|
|
142
147
|
}
|
|
143
148
|
onChange()
|
|
144
149
|
return settings?.subscribe?.(onChange)
|
|
@@ -153,6 +158,19 @@ function GeneralSettings({ registry }) {
|
|
|
153
158
|
if (!settings?.set) { setError('设置服务不可用'); setBusy(false); return }
|
|
154
159
|
settings.set('fileOpenTool', value).catch((e) => setError(String(e))).finally(() => setBusy(false))
|
|
155
160
|
}
|
|
161
|
+
/** 提交侧边栏最小宽度(归一化夹取 180–560 后持久化)。 */
|
|
162
|
+
const saveMinW = (raw) => {
|
|
163
|
+
const next = normalizeSidebarMinWidth(raw)
|
|
164
|
+
setMinW(next); setBusy(true); setError('')
|
|
165
|
+
if (!settings?.set) { setError('设置服务不可用'); setBusy(false); return }
|
|
166
|
+
settings.set('sidebarMinWidth', next).catch((e) => setError(String(e))).finally(() => setBusy(false))
|
|
167
|
+
}
|
|
168
|
+
/** 结束输入(blur/Enter)时提交草稿。 */
|
|
169
|
+
const commitMinW = () => {
|
|
170
|
+
if (minDraft === null) return
|
|
171
|
+
setMinDraft(null)
|
|
172
|
+
saveMinW(minDraft)
|
|
173
|
+
}
|
|
156
174
|
const closeDevForm = () => {
|
|
157
175
|
if (!window.confirm('关闭开发形态:插件将切换为正式版安装(版本依赖 + 删除工作区链接),pnpm 装配后需重启 DSH 生效。确认关闭?')) return
|
|
158
176
|
setDevBusy(true)
|
|
@@ -184,6 +202,23 @@ function GeneralSettings({ registry }) {
|
|
|
184
202
|
devFormRow,
|
|
185
203
|
),
|
|
186
204
|
),
|
|
205
|
+
React.createElement('section', { className: 'vsm-panel' },
|
|
206
|
+
React.createElement('h3', { className: 'vsm-panel-title' }, '编辑器'),
|
|
207
|
+
React.createElement('div', { className: 'vsm-panel-body' },
|
|
208
|
+
React.createElement('label', { className: 'vsm-general-row' },
|
|
209
|
+
React.createElement('span', null, '侧边栏最小宽度'),
|
|
210
|
+
React.createElement('input', {
|
|
211
|
+
type: 'number', min: 180, max: 560, step: 10,
|
|
212
|
+
value: minDraft ?? minW,
|
|
213
|
+
disabled: loading || unavailable || notReady || busy || snapshot?.writable === false,
|
|
214
|
+
title: '180–560 px;拖拽侧边栏低于该宽度时自动隐藏',
|
|
215
|
+
onChange: (event) => setMinDraft(event.target.value),
|
|
216
|
+
onBlur: commitMinW,
|
|
217
|
+
onKeyDown: (event) => { if (event.key === 'Enter') commitMinW() },
|
|
218
|
+
}),
|
|
219
|
+
React.createElement('small', null, '180–560 px;拖拽低于该宽度自动隐藏')),
|
|
220
|
+
),
|
|
221
|
+
),
|
|
187
222
|
)
|
|
188
223
|
}
|
|
189
224
|
|
package/src/fileOpenSettings.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface SettingsDeps {
|
|
|
28
28
|
object: (shape: Record<string, unknown>) => { default: (value: Record<string, unknown>) => unknown }
|
|
29
29
|
string: () => { default: (value: string) => unknown }
|
|
30
30
|
boolean: () => { default: (value: boolean) => unknown }
|
|
31
|
+
number: () => { default: (value: number) => unknown }
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -208,6 +209,7 @@ export async function installOpenSettingsSection(
|
|
|
208
209
|
const schema = deps.z.object({
|
|
209
210
|
fileOpenTool: deps.z.string().default(FILE_OPEN_DEFAULT),
|
|
210
211
|
keybindings: deps.z.object(keybindingsShape(deps.z)).default({ ...KEYBINDING_DEFAULTS }),
|
|
212
|
+
sidebarMinWidth: deps.z.number().default(300),
|
|
211
213
|
})
|
|
212
214
|
const strategy = await runSettingsInstall(ctx, ns, schema, entry, {
|
|
213
215
|
setSource: (source) => hooks.setSource(source as () => FileOpenSettings),
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { createLspManager } from './lsp/manager.js'
|
|
|
22
22
|
import { createLspRpc } from './lsp/rpc.js'
|
|
23
23
|
import { installLspSettingsSection } from './lsp/settings.js'
|
|
24
24
|
import { disposeAllServers } from './lsp/transport.js'
|
|
25
|
+
import { installRulesSection } from './rules.js'
|
|
25
26
|
import type { RpcHandlerMap } from './shared/rpc.js'
|
|
26
27
|
import type { Registry } from './registry.js'
|
|
27
28
|
import type { Ctx } from './store.js'
|
|
@@ -45,6 +46,9 @@ export function apply(ctx: Ctx, config?: unknown): void {
|
|
|
45
46
|
const warnings: string[] = []
|
|
46
47
|
setupOpenSettings(ctx, config, () => {})
|
|
47
48
|
void installLspSettingsSection(ctx, {})
|
|
49
|
+
/** 规则注入 section(~/.dsh/rules 与 <工作区>/.dsh/rules;旧版 DSH 无 systemPrompt 时静默降级)。 */
|
|
50
|
+
const rulesInstalled = installRulesSection(ctx)
|
|
51
|
+
if (!rulesInstalled) ctx.logger?.warn?.('[' + name + '] 未检测到 systemPrompt 服务,规则仅可管理不注入')
|
|
48
52
|
/** LSP RPC 与会话清理(一次性创建,tracker 状态跨请求保留)。 */
|
|
49
53
|
const lspRpc = createLspRpc({ ctx, pluginConfig: config, manager: lspManager })
|
|
50
54
|
const lspHandlers: Partial<RpcHandlerMap> = lspRpc.handlers
|
|
@@ -77,7 +81,7 @@ export function apply(ctx: Ctx, config?: unknown): void {
|
|
|
77
81
|
disposeAllServers()
|
|
78
82
|
})
|
|
79
83
|
|
|
80
|
-
ctx.logger?.info?.('[' + name + '] 编辑差异审查已装配(/edrv/rpc 路由就绪,项目 MCP 隔离已启用,语言服务器 LSP
|
|
84
|
+
ctx.logger?.info?.('[' + name + '] 编辑差异审查已装配(/edrv/rpc 路由就绪,项目 MCP 隔离已启用,语言服务器 LSP 已接入,规则注入' + (rulesInstalled ? '已接入' : '未接入') + ')')
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/**
|
package/src/rpc.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { newSearcher } from './search/orchestrator.js'
|
|
|
28
28
|
import type { ContentSearcher } from './search/content.js'
|
|
29
29
|
import { newContentSearcher } from './search/content.js'
|
|
30
30
|
import { restoreFile, revertCall, revertHunk } from './revert.js'
|
|
31
|
+
import { rulesList, rulesRead, rulesRemove, rulesSave, rulesToggle } from './rules.js'
|
|
31
32
|
import { listMcp, refreshMcp, removeMcp, saveMcp, toggleMcp } from './mcp.js'
|
|
32
33
|
import { listProjects, projectRefresh, projectRemove, projectSave, projectToggle } from './mcpProject.js'
|
|
33
34
|
import { normalizeFileOpenTool, FILE_OPEN_DEFAULT, FILE_OPEN_SETTINGS_NS } from './fileOpenSettings.js'
|
|
@@ -661,6 +662,42 @@ export function buildHandlers(
|
|
|
661
662
|
return { ok: false, error: '撤销压缩配置失败:' + String(error) }
|
|
662
663
|
}
|
|
663
664
|
},
|
|
665
|
+
'rules.list': async () => {
|
|
666
|
+
try {
|
|
667
|
+
return { ok: true, ...(await rulesList(ctx)) }
|
|
668
|
+
} catch (error) {
|
|
669
|
+
return { ok: false, error: '读取规则失败:' + String(error) }
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
'rules.read': async (args) => {
|
|
673
|
+
try {
|
|
674
|
+
return { ok: true, content: await rulesRead(ctx, args) }
|
|
675
|
+
} catch (error) {
|
|
676
|
+
return { ok: false, error: '读取规则失败:' + String(error) }
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
'rules.save': async (args) => {
|
|
680
|
+
try {
|
|
681
|
+
return { ok: true, rule: await rulesSave(ctx, args) }
|
|
682
|
+
} catch (error) {
|
|
683
|
+
return { ok: false, error: '保存规则失败:' + String(error) }
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
'rules.remove': async (args) => {
|
|
687
|
+
try {
|
|
688
|
+
await rulesRemove(ctx, args)
|
|
689
|
+
return { ok: true }
|
|
690
|
+
} catch (error) {
|
|
691
|
+
return { ok: false, error: '删除规则失败:' + String(error) }
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
'rules.toggle': async (args) => {
|
|
695
|
+
try {
|
|
696
|
+
return { ok: true, rule: await rulesToggle(ctx, args, args.enabled === true) }
|
|
697
|
+
} catch (error) {
|
|
698
|
+
return { ok: false, error: '切换规则失败:' + String(error) }
|
|
699
|
+
}
|
|
700
|
+
},
|
|
664
701
|
}
|
|
665
702
|
}
|
|
666
703
|
|