dsh-vscode-mode 0.4.5 → 0.5.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.
Files changed (44) hide show
  1. package/README.md +591 -415
  2. package/lib/client.js +8854 -4025
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +1842 -65
  5. package/lib/index.js.map +1 -1
  6. package/package.json +2 -2
  7. package/src/client/index.ts +3 -0
  8. package/src/client/monaco/lsp/providers.ts +28 -6
  9. package/src/client/navHighlight.ts +96 -0
  10. package/src/client/paths.ts +2 -0
  11. package/src/client/rpc.ts +32 -12
  12. package/src/client/sidebar/SidebarView.ts +6 -2
  13. package/src/client/sidebar/menuItems.ts +93 -2
  14. package/src/client/sidebar/panels/FileExplorer.ts +18 -3
  15. package/src/client/sidebar/panels/SvnPanel.ts +303 -0
  16. package/src/client/sidebar/panels/index.ts +44 -3
  17. package/src/client/sidebar/types.ts +20 -0
  18. package/src/client/styles/editor.css +177 -0
  19. package/src/client/svnActions.ts +153 -0
  20. package/src/client/svnLog.ts +356 -0
  21. package/src/client/svnStatus.ts +363 -0
  22. package/src/client/svnStore.ts +150 -0
  23. package/src/client/tabMenu.ts +51 -0
  24. package/src/client/ui/EditorView.ts +583 -24
  25. package/src/client/ui/LogDialog.ts +333 -0
  26. package/src/client/ui/McpSettings.ts +98 -0
  27. package/src/client/ui/ModalShell.ts +79 -0
  28. package/src/client/ui/SideBySideDiff.tsx +139 -0
  29. package/src/client/ui/SnippetsPicker.ts +70 -58
  30. package/src/client/ui/SvnDiffPanel.ts +50 -0
  31. package/src/client/ui/SvnLogDialog.ts +1141 -0
  32. package/src/client/ui/SvnLogStats.ts +243 -0
  33. package/src/client/ui/commandCatalog.ts +72 -0
  34. package/src/debugLog.ts +81 -12
  35. package/src/fileOpenSettings.ts +40 -3
  36. package/src/index.ts +5 -1
  37. package/src/rpc.ts +41 -4
  38. package/src/shared/rpc.ts +53 -1
  39. package/src/shared/svn.ts +377 -0
  40. package/src/shared/svnActions.ts +296 -0
  41. package/src/svn.ts +870 -0
  42. package/src/svnLog.ts +315 -0
  43. package/src/svnText.ts +175 -0
  44. package/src/svnXml.ts +110 -0
@@ -0,0 +1,303 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * dsh-vscode-mode client — 侧边栏「SVN 变更」面板(工作副本状态视图)。
4
+ *
5
+ * 形态对齐 TortoiseSVN「Check for Modifications」的**列表 + 分组 + 行内动作**骨架:
6
+ * 头部(标题 / 工作副本根名 / ⟳)+ 显示开关(未版本控制 / 忽略项)+ 批量条 +
7
+ * 按 changelist 分组的行列表 + 空态/加载态/未受管理态。行 = 状态字母(按 SVN_STATUS_TONE
8
+ * 着色)+ 相对路径 + 动作按钮(比较 / 加入 / 还原);双击行走「与基线比较」。
9
+ *
10
+ * 数据来源:ctx.svnChanges(EditorView 经 svnStatus.ensureSvnChanges 拉取后注入),
11
+ * 本面板不自行发 RPC,避免与文件树重复拉取;⟳ 触发 refreshSvnChanges 后由事件回流重渲染。
12
+ * 显示开关持久化到 localStorage(CACHE_KEY.svn + scope),与既有面板一致。
13
+ * 作者 ddj 2026年09月16号
14
+ */
15
+ import React from 'react'
16
+ import { IconRefreshOutline16 } from '@deepseek-ai/dsh-client-ui-primitives'
17
+ import {
18
+ SVN_ADD_LABEL,
19
+ SVN_DIFF_BASE_LABEL,
20
+ SVN_REVERT_LABEL,
21
+ SVN_STATUS_LABEL,
22
+ SVN_STATUS_LETTER,
23
+ SVN_STATUS_TONE,
24
+ isSvnDiffable,
25
+ svnVisibleChanges,
26
+ } from '../../../shared/svn.js'
27
+ import { CACHE_KEY } from '../../paths.js'
28
+ import { refreshSvnChanges, svnAdd, svnRevert } from '../../svnStatus.js'
29
+ import type { SidebarCtx } from '../types.js'
30
+
31
+ /** 未分组条目的分组键(changelist 为空)。 */
32
+ const NO_CHANGELIST = '(未分组)'
33
+
34
+ /** 还原确认文案(新增文件不会被删除是 svn revert 的实际语义,须如实告知)。 */
35
+ const REVERT_CONFIRM_HEAD = '确认还原以下 '
36
+ const REVERT_CONFIRM_TAIL = ' 个文件的本地改动?新增(A)文件在磁盘上会保留,仅取消登记。'
37
+
38
+ // --region 显示开关持久化
39
+
40
+ /**
41
+ * 读取面板显示开关(损坏/不可用安全)。
42
+ * @author ddj 2026年09月16号
43
+ * @param scope 作用域键
44
+ * @returns 开关(默认:显示未版本控制、隐藏忽略项)
45
+ */
46
+ function loadFilter(scope) {
47
+ const fallback = { unversioned: true, ignored: false }
48
+ if (!scope) return fallback
49
+ try {
50
+ const raw = window.localStorage.getItem(CACHE_KEY.svn + scope)
51
+ if (!raw) return fallback
52
+ const parsed = JSON.parse(raw)
53
+ return {
54
+ unversioned: parsed?.unversioned !== false,
55
+ ignored: parsed?.ignored === true,
56
+ }
57
+ } catch (error) {
58
+ return fallback
59
+ }
60
+ }
61
+
62
+ /**
63
+ * 写面板显示开关(配额/隐私模式失败静默)。
64
+ * @author ddj 2026年09月16号
65
+ * @param scope 作用域键
66
+ * @param filter 开关
67
+ */
68
+ function saveFilter(scope, filter) {
69
+ if (!scope) return
70
+ try {
71
+ window.localStorage.setItem(CACHE_KEY.svn + scope, JSON.stringify(filter))
72
+ } catch (error) { /* 配额满/隐私模式忽略 */ }
73
+ }
74
+ // --endregion
75
+
76
+ // --region 展示元素
77
+
78
+ /**
79
+ * 刷新按钮图标(官方 IconRefreshOutline16;原语缺失回落文本 ⟳)。
80
+ * @author ddj 2026年09月16号
81
+ * @returns 图标元素或回落文本
82
+ */
83
+ function refreshIconEl() {
84
+ if (typeof IconRefreshOutline16 !== 'function') return '⟳'
85
+ return React.createElement(IconRefreshOutline16, { size: 14 })
86
+ }
87
+
88
+ /**
89
+ * 条目状态字母元素(按 tone 着色)。
90
+ * @author ddj 2026年09月16号
91
+ * @param entry 变更条目
92
+ * @returns 字母元素
93
+ */
94
+ function statusLetterEl(entry) {
95
+ const tone = SVN_STATUS_TONE[entry.status] ?? 'plain'
96
+ return React.createElement('span', {
97
+ className: 'edrv-svn-ch edrv-svn-ch-' + tone,
98
+ title: SVN_STATUS_LABEL[entry.status] ?? entry.status,
99
+ }, SVN_STATUS_LETTER[entry.status] ?? '?')
100
+ }
101
+
102
+ /**
103
+ * 按 changelist 把条目分组(未分组恒排末位;组内保持工作副本原顺序)。
104
+ * @author ddj 2026年09月16号
105
+ * @param entries 已过滤的变更清单
106
+ * @returns 分组数组(name + entries)
107
+ */
108
+ export function groupByChangelist(entries) {
109
+ const groups = new Map()
110
+ for (const entry of entries) {
111
+ const name = entry.changelist || NO_CHANGELIST
112
+ const bucket = groups.get(name)
113
+ if (bucket) bucket.push(entry)
114
+ else groups.set(name, [entry])
115
+ }
116
+ const named = [...groups.entries()].filter(([name]) => name !== NO_CHANGELIST)
117
+ const rest = groups.get(NO_CHANGELIST)
118
+ if (rest) named.push([NO_CHANGELIST, rest])
119
+ return named.map(([name, items]) => ({ name, entries: items }))
120
+ }
121
+
122
+ /**
123
+ * 单条变更行(状态字母 + 路径 + 行内动作;双击 = 与基线比较)。
124
+ * @author ddj 2026年09月16号
125
+ * @param props.entry 变更条目
126
+ * @param props.busy 批量动作进行中(按钮置灰)
127
+ * @param props.onDiff 打开基线差异
128
+ * @param props.onAdd 加入版本控制
129
+ * @param props.onRevert 还原
130
+ * @returns 行元素
131
+ */
132
+ function SvnRow(props) {
133
+ const { entry, busy, onDiff, onAdd, onRevert } = props
134
+ const diffable = isSvnDiffable(entry.path, entry.status)
135
+ const act = (label, title, danger, run) => React.createElement('button', {
136
+ className: 'edrv-svn-act' + (danger ? ' edrv-svn-act-danger' : ''),
137
+ title,
138
+ disabled: busy,
139
+ onClick: (event) => { event.stopPropagation(); run() },
140
+ }, label)
141
+ return React.createElement('div', {
142
+ className: 'edrv-svn-row',
143
+ title: entry.path,
144
+ onDoubleClick: () => { if (diffable) onDiff(entry.path) },
145
+ },
146
+ statusLetterEl(entry),
147
+ React.createElement('span', { className: 'edrv-svn-path' }, entry.path),
148
+ React.createElement('span', { className: 'edrv-svn-acts' },
149
+ (diffable ? act('比较', SVN_DIFF_BASE_LABEL + '(BASE 与工作区并排)', false, () => onDiff(entry.path)) : null),
150
+ (!entry.versioned ? act('加入', SVN_ADD_LABEL, false, () => onAdd([entry.path])) : null),
151
+ (entry.versioned ? act('还原', SVN_REVERT_LABEL + '(放弃本地改动)', true, () => onRevert([entry.path])) : null)))
152
+ }
153
+
154
+ /**
155
+ * 列表主体:分组行 + 条目行;未受管理/加载中/无变更各自给出稳定文案。
156
+ * @author ddj 2026年09月16号
157
+ * @param state 列表状态(managed/entries/visible/groups)
158
+ * @param handlers 行动作(onDiff/onAdd/onRevert/busy)
159
+ * @returns 主体元素或元素数组
160
+ */
161
+ function svnListBody(state, handlers) {
162
+ if (!state.managed) return React.createElement('div', { className: 'edrv-tree-loading' }, '当前工作区不受 SVN 管理')
163
+ if (state.entries === null) return React.createElement('div', { className: 'edrv-tree-loading' }, '读取变更中…')
164
+ if (!state.visible.length) return React.createElement('div', { className: 'edrv-tree-loading' }, '无变更(工作副本干净)')
165
+ const rows = []
166
+ for (const group of state.groups) {
167
+ if (state.groups.length > 1 || group.name !== NO_CHANGELIST) {
168
+ rows.push(React.createElement('div', { key: 'g:' + group.name, className: 'edrv-svn-group' },
169
+ React.createElement('span', null, 'changelist: ' + group.name),
170
+ React.createElement('span', { className: 'edrv-svn-group-n' }, String(group.entries.length))))
171
+ }
172
+ for (const entry of group.entries) {
173
+ rows.push(React.createElement(SvnRow, Object.assign({ key: entry.path, entry }, handlers)))
174
+ }
175
+ }
176
+ return rows
177
+ }
178
+
179
+ /**
180
+ * 工具条(显示开关 + 批量动作)。
181
+ * @author ddj 2026年09月16号
182
+ * @param props.filter 当前开关
183
+ * @param props.count 可见条目数
184
+ * @param props.paths 批量动作目标(add/revert 各自清单)
185
+ * @param props.handlers 动作回调(toggle/add/revert/busy)
186
+ * @returns 工具条元素数组
187
+ */
188
+ function svnToolbarEl(props) {
189
+ const { filter, count, paths, handlers } = props
190
+ const check = (label, title, checked, onChange) => React.createElement('label', {
191
+ className: 'edrv-svn-check', title,
192
+ },
193
+ React.createElement('input', { type: 'checkbox', checked, onChange }),
194
+ React.createElement('span', null, label))
195
+ return [
196
+ React.createElement('div', { key: 'tools', className: 'edrv-svn-toolbar' },
197
+ check('未版本控制', '显示未纳入版本控制的文件', filter.unversioned, handlers.toggleUnversioned),
198
+ check('忽略项', '显示被忽略的文件', filter.ignored, handlers.toggleIgnored)),
199
+ (count
200
+ ? React.createElement('div', { key: 'bulk', className: 'edrv-svn-bulk' },
201
+ React.createElement('span', { className: 'edrv-svn-count' }, String(count) + ' 项'),
202
+ React.createElement('span', { style: { flex: 1 } }),
203
+ (paths.unversioned.length
204
+ ? React.createElement('button', {
205
+ className: 'edrv-svn-act', disabled: handlers.busy,
206
+ title: '把全部未版本控制文件加入版本控制',
207
+ onClick: () => handlers.add(paths.unversioned),
208
+ }, '全部加入')
209
+ : null),
210
+ (paths.revert.length
211
+ ? React.createElement('button', {
212
+ className: 'edrv-svn-act edrv-svn-act-danger', disabled: handlers.busy,
213
+ title: '还原全部已受版本控制文件的本地改动',
214
+ onClick: () => handlers.revert(paths.revert),
215
+ }, '全部还原')
216
+ : null))
217
+ : null),
218
+ ]
219
+ }
220
+ // --endregion
221
+
222
+ /**
223
+ * 「SVN 变更」面板主体。
224
+ * @author ddj 2026年09月16号
225
+ * @param props.ctx 面板共享上下文(sessionId/scope/svnChanges/svn/notify/openSvnDiff)
226
+ * @returns 面板元素
227
+ */
228
+ export function SvnPanel(props) {
229
+ const ctx = props?.ctx
230
+ const sessionId = ctx?.sessionId
231
+ const scope = ctx?.scope
232
+ const entries = ctx?.svnChanges ?? null
233
+ const managed = Boolean(ctx?.svn?.managed && ctx?.svn?.svnCli)
234
+ const [filter, setFilter] = React.useState(() => loadFilter(scope))
235
+ const [busy, setBusy] = React.useState(false)
236
+
237
+ // 作用域切换:重读该作用域自己的开关(同一工作区共享)
238
+ React.useEffect(() => {
239
+ setFilter(loadFilter(scope))
240
+ }, [scope])
241
+
242
+ const applyFilter = (next) => {
243
+ setFilter(next)
244
+ saveFilter(scope, next)
245
+ }
246
+
247
+ /**
248
+ * 批量动作统一流程:先 confirm(仅还原需要)→ 执行 → 反馈 → 强制重查。
249
+ * @author ddj 2026年09月16号
250
+ * @param kind 'add' | 'revert'
251
+ * @param paths 目标相对路径列表
252
+ */
253
+ const runBatch = (kind, paths) => {
254
+ if (!paths.length) return
255
+ if (kind === 'revert') {
256
+ const head = REVERT_CONFIRM_HEAD + paths.length + REVERT_CONFIRM_TAIL
257
+ if (!window.confirm(head + '\n\n' + paths.join('\n'))) return
258
+ }
259
+ setBusy(true)
260
+ const task = kind === 'revert' ? svnRevert(sessionId, paths) : svnAdd(sessionId, paths)
261
+ void task.then((outcome) => {
262
+ ctx?.notify?.(outcome.message)
263
+ refreshSvnChanges(sessionId, scope)
264
+ }).finally(() => setBusy(false))
265
+ }
266
+
267
+ const visible = entries === null ? [] : svnVisibleChanges(entries, filter)
268
+ const openSvnDiff = (p) => ctx?.openSvnDiff?.(p)
269
+ const handlers = {
270
+ busy,
271
+ onDiff: openSvnDiff,
272
+ onAdd: (paths) => runBatch('add', paths),
273
+ onRevert: (paths) => runBatch('revert', paths),
274
+ }
275
+ const listState = { managed, entries, visible, groups: groupByChangelist(visible) }
276
+ const toolbar = svnToolbarEl({
277
+ filter,
278
+ count: visible.length,
279
+ paths: {
280
+ unversioned: visible.filter((entry) => !entry.versioned).map((entry) => entry.path),
281
+ revert: visible.filter((entry) => entry.versioned).map((entry) => entry.path),
282
+ },
283
+ handlers: Object.assign({}, handlers, {
284
+ toggleUnversioned: () => applyFilter({ ...filter, unversioned: !filter.unversioned }),
285
+ toggleIgnored: () => applyFilter({ ...filter, ignored: !filter.ignored }),
286
+ add: (paths) => runBatch('add', paths),
287
+ revert: (paths) => runBatch('revert', paths),
288
+ }),
289
+ })
290
+ const rootName = ctx?.svn?.wcRoot ? String(ctx.svn.wcRoot).split(/[\\/]/).pop() || ctx.svn.wcRoot : ''
291
+
292
+ return React.createElement('div', { className: 'edrv-side-panel' },
293
+ React.createElement('div', { className: 'edrv-side-head' },
294
+ React.createElement('span', { className: 'edrv-side-title' }, 'SVN 变更'),
295
+ React.createElement('span', { className: 'edrv-side-root', title: ctx?.svn?.wcRoot || '' }, rootName),
296
+ React.createElement('span', { style: { flex: 1 } }),
297
+ React.createElement('button', {
298
+ className: 'edrv-side-btn', title: '刷新变更',
299
+ onClick: () => { refreshSvnChanges(sessionId, scope); ctx?.refreshRecords?.() },
300
+ }, refreshIconEl())),
301
+ ...toolbar,
302
+ React.createElement('div', { className: 'edrv-svn-list' }, svnListBody(listState, handlers)))
303
+ }
@@ -1,14 +1,16 @@
1
1
  // @ts-nocheck
2
2
  /**
3
- * dsh-vscode-mode client — 侧边栏面板定义(文件管理 + 搜索 + 规则)。
3
+ * dsh-vscode-mode client — 侧边栏面板定义(文件管理 + 搜索 + 规则 + SVN 变更)。
4
4
  * 活动栏图标用官方 UI 原语(跟随 DSH 主题与皮肤);原语缺失时由 SidebarView 回落文本渲染。
5
- * 作者 ddj 2026-08-26 / 2026-09-03 / 2026-09-10
5
+ * 「SVN 变更」带 visible 守卫:仅受 SVN 管理的工作区出现(非 SVN 工作区零变化)。
6
+ * 作者 ddj 2026-08-26 / 2026-09-03 / 2026-09-10 / 2026-09-16
6
7
  */
7
8
  import React from 'react'
8
- import { IconFolderOpenOutline16, IconListPenOutline16, IconSearchOutline16 } from '@deepseek-ai/dsh-client-ui-primitives'
9
+ import { IconBranchOutline16, IconFolderOpenOutline16, IconListPenOutline16, IconSearchOutline16 } from '@deepseek-ai/dsh-client-ui-primitives'
9
10
  import { FileExplorer } from './FileExplorer.js'
10
11
  import { SearchPanel } from './SearchPanel.js'
11
12
  import { RulesPanel } from './RulesPanel.js'
13
+ import { SvnPanel } from './SvnPanel.js'
12
14
  import type { SidebarPanelDef, SidebarCtx } from '../types.js'
13
15
 
14
16
  /**
@@ -56,3 +58,42 @@ export function createRulesPanel(): SidebarPanelDef {
56
58
  render: (ctx: SidebarCtx) => React.createElement(RulesPanel, { ctx }),
57
59
  }
58
60
  }
61
+
62
+ /**
63
+ * 构造「SVN 变更」面板定义。
64
+ * 徽标 = 过滤后仍显示的变更数(0/未加载不显示);visible 守卫保证非 SVN 工作区
65
+ * 既无活动栏图标也无面板(避免出现永远空的入口)。
66
+ * @author ddj 2026年09月16号
67
+ * @returns 面板定义(图标 = 官方 IconBranchOutline16;无原语时回落文本)
68
+ */
69
+ export function createSvnPanel(): SidebarPanelDef {
70
+ return {
71
+ id: 'svn',
72
+ title: 'SVN 变更',
73
+ icon: typeof IconBranchOutline16 === 'function' ? IconBranchOutline16 : '⎇',
74
+ order: 25,
75
+ visible: (ctx: SidebarCtx) => Boolean(ctx?.svn?.managed && ctx?.svn?.svnCli),
76
+ badge: (ctx: SidebarCtx) => {
77
+ const entries = ctx?.svnChanges
78
+ if (!Array.isArray(entries)) return null
79
+ const count = svnVisibleCount(entries)
80
+ return count > 0 ? count : null
81
+ },
82
+ render: (ctx: SidebarCtx) => React.createElement(SvnPanel, { ctx }),
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 徽标计数:与面板默认过滤口径一致(显示未版本控制、隐藏忽略项)。
88
+ * @author ddj 2026年09月16号
89
+ * @param entries 变更清单
90
+ * @returns 计数
91
+ */
92
+ function svnVisibleCount(entries) {
93
+ let count = 0
94
+ for (const entry of entries) {
95
+ if (entry?.status === 'ignored' || entry?.status === 'normal' || entry?.status === 'external') continue
96
+ count += 1
97
+ }
98
+ return count
99
+ }
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import type { OutlineSourceRegistry } from '../outline/types.js'
7
7
  import type { AddToConversation } from '../addToConversation.js'
8
+ import type { SvnChangeEntry, SvnStatusPayload } from '../../shared/svn.js'
8
9
  import type { TreeMenuRegistry } from './contextMenu.js'
9
10
 
10
11
  /** 面板可用的共享上下文(由 SidebarView 从 EditorView 注入,面板不直碰其内部)。 */
@@ -32,6 +33,20 @@ export interface SidebarCtx {
32
33
  fileMenuItems?: TreeMenuRegistry
33
34
  /** 「添加到对话」动作集(文件/文件夹引用注入对话输入框;缺省时菜单项降级提示)。 */
34
35
  addToConversation?: AddToConversation
36
+ /** SVN 能力状态(未加载/未检出为 null;SVN 菜单项显隐判定)。 */
37
+ svn?: SvnStatusPayload | null
38
+ /** SVN 变更清单(未加载为 null;文件树徽标与变更类菜单项判定)。 */
39
+ svnChanges?: SvnChangeEntry[] | null
40
+ /** 路径 → 变更条目(文件树徽标查表;由 svnStatus.svnChangeMapOf 产出)。 */
41
+ svnChangeMap?: Record<string, SvnChangeEntry>
42
+ /** 打开 SVN 基线差异视图(变更面板行内「与基线比较」;缺省时面板降级提示)。 */
43
+ openSvnDiff?: (path: string) => void
44
+ /** 重查 SVN 变更清单(CLI 更新/还原/加入版本控制后由菜单动作调用)。 */
45
+ refreshSvnChanges?: () => void
46
+ /** 打开 SVN 日志弹窗(P3;菜单/命令栏「查看日志」)。 */
47
+ openSvnLog?: (path?: string) => void
48
+ /** 确认对话框(破坏性动作守卫;缺省走 window.confirm,测试可注入)。 */
49
+ confirm?: (message: string) => boolean
35
50
  /** 面板动作反馈(如右键菜单操作结果 → 编辑区路径栏状态)。 */
36
51
  notify?: (message: string) => void
37
52
  }
@@ -45,6 +60,11 @@ export interface SidebarPanelDef {
45
60
  order?: number
46
61
  /** 活动栏徽标:返回数字/null;缺省不显示。 */
47
62
  badge?: (ctx: SidebarCtx) => number | null
63
+ /**
64
+ * 面板可用性守卫:返回 false 时活动栏图标与面板区都不出现(每次渲染求值)。
65
+ * 用于能力相关面板(如「SVN 变更」仅在受 SVN 管理时出现);缺省恒可见。
66
+ */
67
+ visible?: (ctx: SidebarCtx) => boolean
48
68
  /** 面板区渲染(ctx 每次渲染注入最新快照)。 */
49
69
  render: (ctx: SidebarCtx) => unknown
50
70
  }
@@ -49,6 +49,9 @@
49
49
  @keyframes edrv-lsp-pulse { 50% { opacity: .45; } }
50
50
  /* Ctrl+hover 可导航标识符下划线提示(绑定 Monaco inlineClassName 装饰) */
51
51
  [data-edrv-view] .edrv-nav-underline { text-decoration: underline; text-decoration-color: var(--dsw-alias-interactive-primary, #4fc1ff); text-underline-offset: 1px; cursor: pointer; }
52
+ /* 跳转目标区域高亮(LSP 定义/引用、搜索命中落点):落点一眼可见,约 1.2s 后由 EditorView 自动清除。
53
+ 用官方令牌并带回落值,不在插件作用域重定义 --dsw-* (否则永久脱离官方主题)。 */
54
+ [data-edrv-view] .edrv-nav-target { background: var(--dsw-alias-interactive-bg-hover, rgba(79,193,255,.28)); border-radius: 2px; box-shadow: 0 0 0 1px var(--dsw-alias-interactive-primary, rgba(79,193,255,.55)); }
52
55
  [data-edrv-view] .edrv-diffchip { font-size: 11px; padding: 2px 10px; border: 1px solid var(--dsw-alias-border-l1, #333); border-radius: 999px; background: var(--dsw-alias-bg-layer-2, transparent); color: var(--dsw-alias-state-warn-primary, #b7791f); cursor: pointer; white-space: nowrap; flex-shrink: 0; }
53
56
  [data-edrv-view] .edrv-chip-btn { font-size: 12px; padding: 2px 6px; border: none; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); cursor: pointer; border-radius: 4px; flex-shrink: 0; }
54
57
  [data-edrv-view] .edrv-chip-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,.06)); }
@@ -281,6 +284,180 @@
281
284
  [data-edrv-view] .edrv-side-resize:hover { background: var(--dsw-alias-brand-primary, #0f9d58); opacity: .35; }
282
285
  [data-edrv-view] .edrv-chip-btn.edrv-chip-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); }
283
286
 
287
+ /* ===== SVN 变更面板(工作副本状态列表;颜色分级对齐 TortoiseSVN 口径) @author ddj 2026-09-16 ===== */
288
+ [data-edrv-view] .edrv-svn-toolbar { display: flex; align-items: center; gap: 12px; padding: 4px 10px 6px 12px; flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); }
289
+ [data-edrv-view] .edrv-svn-check { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; white-space: nowrap; }
290
+ [data-edrv-view] .edrv-svn-check input { margin: 0; cursor: pointer; }
291
+ [data-edrv-view] .edrv-svn-bulk { display: flex; align-items: center; gap: 6px; padding: 0 10px 6px 12px; flex-shrink: 0; }
292
+ [data-edrv-view] .edrv-svn-count { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }
293
+ [data-edrv-view] .edrv-svn-list { flex: 1; min-height: 0; overflow: auto; padding: 2px 6px 12px; font-size: 12px; }
294
+ [data-edrv-view] .edrv-svn-group { display: flex; align-items: center; gap: 6px; height: 22px; padding: 0 6px; margin: 2px 0; border-radius: 5px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; font-weight: 600; }
295
+ [data-edrv-view] .edrv-svn-group-n { margin-left: auto; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-weight: 400; }
296
+ [data-edrv-view] .edrv-svn-row { display: flex; align-items: center; gap: 6px; height: 22px; padding: 0 6px; border-radius: 5px; cursor: default; white-space: nowrap; }
297
+ [data-edrv-view] .edrv-svn-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }
298
+ [data-edrv-view] .edrv-svn-path { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-primary, #1f2933); }
299
+ [data-edrv-view] .edrv-svn-acts { display: none; align-items: center; gap: 4px; flex-shrink: 0; }
300
+ [data-edrv-view] .edrv-svn-row:hover .edrv-svn-acts { display: inline-flex; }
301
+ [data-edrv-view] .edrv-svn-act { border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 5px; padding: 1px 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-secondary, #52606d); font-size: 10px; line-height: 14px; cursor: pointer; white-space: nowrap; }
302
+ [data-edrv-view] .edrv-svn-act:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); color: var(--dsw-alias-label-primary, #1f2933); }
303
+ [data-edrv-view] .edrv-svn-act:disabled { opacity: .45; cursor: default; }
304
+ [data-edrv-view] .edrv-svn-act-danger { color: var(--dsw-alias-state-error-primary, #d9534f); border-color: var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); }
305
+ [data-edrv-view] .edrv-svn-ch { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; font-weight: 700; }
306
+ [data-edrv-view] .edrv-svn-ch-modified { color: var(--dsw-alias-brand-primary, #2563eb); }
307
+ [data-edrv-view] .edrv-svn-ch-added { color: var(--dsw-alias-state-warn-primary, #7c3aed); }
308
+ [data-edrv-view] .edrv-svn-ch-deleted { color: var(--dsw-alias-label-secondary, #b91c1c); }
309
+ [data-edrv-view] .edrv-svn-ch-conflict { color: var(--dsw-alias-state-error-primary, #d9534f); }
310
+ [data-edrv-view] .edrv-svn-ch-plain { color: var(--dsw-alias-label-tertiary, #9aa5b1); }
311
+ /* 文件树行尾 SVN 状态徽标(与差异角标并列;方形字母,区别圆形差异计数) */
312
+ [data-edrv-view] .edrv-tree-svn { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 4px; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 9px; font-weight: 700; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-secondary, #52606d); }
313
+ [data-edrv-view] .edrv-tree-svn.edrv-svn-ch-modified { color: var(--dsw-alias-brand-primary, #2563eb); }
314
+ [data-edrv-view] .edrv-tree-svn.edrv-svn-ch-added { color: var(--dsw-alias-state-warn-primary, #7c3aed); }
315
+ [data-edrv-view] .edrv-tree-svn.edrv-svn-ch-deleted { color: var(--dsw-alias-label-secondary, #b91c1c); }
316
+ [data-edrv-view] .edrv-tree-svn.edrv-svn-ch-conflict { color: var(--dsw-alias-state-error-primary, #d9534f); }
317
+ /* ===== SVN 基线差异视图(BASE 左 / 工作区右,只读并排) @author ddj 2026-09-16 ===== */
318
+ [data-edrv-view] .edrv-svndiff { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; overflow: hidden; }
319
+ [data-edrv-view] .edrv-svndiff-bar { display: flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px; flex-shrink: 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); }
320
+ [data-edrv-view] .edrv-svndiff-title { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
321
+ [data-edrv-view] .edrv-svndiff-tag { padding: 1px 6px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); }
322
+ [data-edrv-view] .edrv-svndiff-host { flex: 1 1 auto; min-height: 0; }
323
+ [data-edrv-view] .edrv-svndiff-empty { display: flex; flex: 1 1 auto; align-items: center; justify-content: center; padding: 24px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 12px; text-align: center; }
324
+
325
+ /* ===== 通用弹窗外壳(ModalShell)+ SVN 日志弹窗 @author ddj 2026-09-16 ===== */
326
+ /* z-index 190:落在既有阶梯内(命令栏 200 > 片段二级 197 > 片段一级 195 > 190),
327
+ 不遮挡命令栏;既有浮层数值不受影响(见 tests/overlayZOrder.test.ts)。 */
328
+ [data-edrv-view] .edrv-modal-mask { position: fixed; inset: 0; z-index: 190; display: flex; align-items: center; justify-content: center; padding: 24px; box-sizing: border-box; background: var(--dsw-alias-bg-mask-drop, #000000aa); }
329
+ [data-edrv-view] .edrv-svnlog-dialog { position: relative; display: flex; flex-direction: column; gap: 8px; }
330
+ [data-edrv-view] .edrv-svnlog-head { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
331
+ [data-edrv-view] .edrv-svnlog-title { margin: 0; font-size: 15px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }
332
+ [data-edrv-view] .edrv-svnlog-target { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
333
+ [data-edrv-view] .edrv-svnlog-filter { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
334
+ [data-edrv-view] .edrv-svnlog-field { flex: 0 0 auto; padding: 4px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }
335
+ [data-edrv-view] .edrv-svnlog-search { min-width: 160px; flex: 1 1 220px; padding: 4px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }
336
+ [data-edrv-view] .edrv-svnlog-date { padding: 2px 4px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }
337
+ [data-edrv-view] .edrv-svnlog-check { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); white-space: nowrap; cursor: pointer; }
338
+ [data-edrv-view] .edrv-svnlog-error { padding: 4px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }
339
+ /* P0-14 上中下三段全宽:body 纵向铺满弹窗(外框高度由拖拽受控) */
340
+ [data-edrv-view] .edrv-svnlog-body { display: flex; flex-direction: column; gap: 6px; flex: 1 1 auto; min-height: 0; }
341
+ /* 上段修订列表:列头行 + 单行五列(版本/动作/作者/日期/信息),列宽由 cell 类与 nth-child 对齐(P0-12/D30) */
342
+ [data-edrv-view] .edrv-svnlog-list { flex: 0 0 auto; display: flex; flex-direction: column; overflow: auto; border: 2px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; padding: 4px; }
343
+ [data-edrv-view] .edrv-svnlog-cols { display: flex; align-items: center; gap: 6px; padding: 2px 6px; border-bottom: 2px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary, #52606d); flex-shrink: 0; }
344
+ [data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(1) { width: 52px; flex: none; }
345
+ [data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(2) { width: 52px; flex: none; }
346
+ [data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(3) { width: 88px; flex: none; overflow: hidden; text-overflow: ellipsis; }
347
+ [data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(4) { width: 82px; flex: none; }
348
+ [data-edrv-view] .edrv-svnlog-cols .edrv-svnlog-col:nth-child(5) { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; }
349
+ [data-edrv-view] .edrv-svnlog-col-sortable { cursor: pointer; }
350
+ [data-edrv-view] .edrv-svnlog-col-sortable:hover { color: var(--dsw-alias-label-primary, #1f2933); }
351
+ [data-edrv-view] .edrv-svnlog-col-on { color: var(--dsw-alias-brand-primary, #2563eb); }
352
+ [data-edrv-view] .edrv-svnlog-sort-reset { flex: none; }
353
+ [data-edrv-view] .edrv-svnlog-item { display: flex; align-items: center; gap: 6px; padding: 2px 6px; border-radius: 6px; cursor: pointer; white-space: nowrap; }
354
+ [data-edrv-view] .edrv-svnlog-item:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }
355
+ [data-edrv-view] .edrv-svnlog-item.edrv-svnlog-item-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); }
356
+ [data-edrv-view] .edrv-svnlog-item-copied .edrv-svnlog-cell-msg,
357
+ [data-edrv-view] .edrv-svnlog-item-copied .edrv-svnlog-rev { color: var(--dsw-alias-brand-primary, #2563eb); }
358
+ [data-edrv-view] .edrv-svnlog-cell { overflow: hidden; text-overflow: ellipsis; font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); }
359
+ [data-edrv-view] .edrv-svnlog-cell-rev { width: 52px; flex: none; }
360
+ [data-edrv-view] .edrv-svnlog-cell-act { width: 52px; flex: none; display: inline-flex; gap: 3px; }
361
+ [data-edrv-view] .edrv-svnlog-cell-author { width: 88px; flex: none; }
362
+ [data-edrv-view] .edrv-svnlog-cell-date { width: 82px; flex: none; color: var(--dsw-alias-label-tertiary, #9aa5b1); }
363
+ [data-edrv-view] .edrv-svnlog-cell-msg { flex: 1 1 auto; }
364
+ [data-edrv-view] .edrv-svnlog-rev { font-family: var(--ds-font-family-code, ui-monospace, monospace); font-weight: 700; color: var(--dsw-alias-brand-primary, #2563eb); }
365
+ [data-edrv-view] .edrv-svnlog-badge { display: inline-flex; align-items: center; justify-content: center; width: 12px; height: 12px; border-radius: 3px; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 9px; font-weight: 700; }
366
+ [data-edrv-view] .edrv-svnlog-badge-modified { color: var(--dsw-alias-brand-primary, #2563eb); background: rgba(37,99,235,.12); }
367
+ [data-edrv-view] .edrv-svnlog-badge-added { color: var(--dsw-alias-state-warn-primary, #7c3aed); background: rgba(124,58,237,.12); }
368
+ [data-edrv-view] .edrv-svnlog-badge-deleted { color: #b91c1c; background: rgba(185,28,28,.12); }
369
+ [data-edrv-view] .edrv-svnlog-badge-plain { color: var(--dsw-alias-label-tertiary, #9aa5b1); background: var(--dsw-alias-bg-layer-2, #f0f4f4); }
370
+ [data-edrv-view] .edrv-svnlog-more { padding: 6px 4px 2px; text-align: center; }
371
+ /* P0-14 三段 pane 公共外观(上段列表自带 .edrv-svnlog-list 边框) */
372
+ [data-edrv-view] .edrv-svnlog-pane { border: 2px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; padding: 8px 10px; overflow: hidden; }
373
+ [data-edrv-view] .edrv-svnlog-msgpane { flex: 0 0 auto; display: flex; flex-direction: column; }
374
+ [data-edrv-view] .edrv-svnlog-pathspane { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
375
+ /* P0-13/P0-14:分段横向拖拽分隔条 + 变更路径独立滚动容器 */
376
+ [data-edrv-view] .edrv-svnlog-hsplit { flex: 0 0 6px; cursor: row-resize; margin: 2px 0; border-radius: 3px; touch-action: none; }
377
+ [data-edrv-view] .edrv-svnlog-hsplit:hover, [data-edrv-view] .edrv-svnlog-hsplit:active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.28)); }
378
+ [data-edrv-view] .edrv-svnlog-paths-wrap { flex: 1 1 auto; min-height: 0; overflow: auto; display: flex; flex-direction: column; }
379
+ [data-edrv-view] .edrv-svnlog-meta { display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); flex-wrap: wrap; }
380
+ [data-edrv-view] .edrv-svnlog-date { color: var(--dsw-alias-label-tertiary, #9aa5b1); }
381
+ [data-edrv-view] .edrv-svnlog-msgpane pre.edrv-svnlog-body { margin: 6px 0 0; padding: 6px 8px; border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; overflow: auto; flex: 1 1 auto; min-height: 0; }
382
+ /* P0-14 外框尺寸手柄(右缘调宽 / 底缘调高 / 右下角双向) */
383
+ [data-edrv-view] .edrv-svnlog-grip { position: absolute; z-index: 2; touch-action: none; }
384
+ [data-edrv-view] .edrv-svnlog-grip-r { top: 12px; bottom: 12px; right: 0; width: 8px; cursor: ew-resize; }
385
+ [data-edrv-view] .edrv-svnlog-grip-b { left: 12px; right: 12px; bottom: 0; height: 8px; cursor: ns-resize; }
386
+ [data-edrv-view] .edrv-svnlog-grip-c { right: 0; bottom: 0; width: 14px; height: 14px; cursor: nwse-resize; border-radius: 3px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); }
387
+ [data-edrv-view] .edrv-svnlog-grip:hover, [data-edrv-view] .edrv-svnlog-grip:active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.35)); }
388
+ [data-edrv-view] .edrv-svnlog-paths-head { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary, #52606d); margin-bottom: 4px; }
389
+ [data-edrv-view] .edrv-svnlog-hint { font-weight: 400; color: var(--dsw-alias-label-tertiary, #9aa5b1); }
390
+ [data-edrv-view] .edrv-svnlog-paths { display: flex; flex-direction: column; gap: 1px; }
391
+ [data-edrv-view] .edrv-svnlog-path { display: flex; align-items: center; gap: 6px; height: 22px; padding: 0 6px; border-radius: 5px; cursor: default; white-space: nowrap; }
392
+ [data-edrv-view] .edrv-svnlog-path:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }
393
+ [data-edrv-view] .edrv-svnlog-path-dim .edrv-svnlog-pathtext,
394
+ [data-edrv-view] .edrv-svnlog-path-dim .edrv-svnlog-copyfrom { opacity: .45; }
395
+ [data-edrv-view] .edrv-svnlog-pathtext { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: 11px; color: var(--dsw-alias-label-primary, #1f2933); }
396
+ [data-edrv-view] .edrv-svnlog-copyfrom { flex: 0 1 160px; overflow: hidden; text-overflow: ellipsis; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-family: var(--ds-font-family-code, ui-monospace, monospace); }
397
+ [data-edrv-view] .edrv-svnlog-kind { font-size: 10px; padding: 0 5px; border-radius: 999px; background: var(--dsw-alias-bg-layer-2, #f0f4f4); color: var(--dsw-alias-label-tertiary, #9aa5b1); }
398
+ [data-edrv-view] .edrv-svnlog-acts { display: none; align-items: center; gap: 4px; flex-shrink: 0; }
399
+ [data-edrv-view] .edrv-svnlog-path:hover .edrv-svnlog-acts { display: inline-flex; }
400
+ [data-edrv-view] .edrv-svnlog-footwrap { display: flex; flex-direction: column; gap: 4px; flex-shrink: 0; }
401
+ [data-edrv-view] .edrv-svnlog-foot { display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }
402
+ [data-edrv-view] .edrv-svnlog-btns { display: flex; align-items: center; gap: 6px; }
403
+ /* ===== SVN 日志统计窗(P1-10)@author ddj 2026-09-17 ===== */
404
+ [data-edrv-view] .edrv-svnlog-stats { display: flex; flex-direction: column; max-height: 82vh; }
405
+ [data-edrv-view] .edrv-svnlog-stats-head { display: flex; align-items: center; gap: 8px; padding: 12px 16px 0; }
406
+ [data-edrv-view] .edrv-svnlog-stats-body { display: flex; flex-direction: column; gap: 14px; padding: 10px 16px 16px; overflow: auto; }
407
+ [data-edrv-view] .edrv-svnlog-stat-line { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12px; color: var(--dsw-alias-label-primary, #1f2933); }
408
+ [data-edrv-view] .edrv-svnlog-stat-block { display: flex; flex-direction: column; gap: 8px; }
409
+ [data-edrv-view] .edrv-svnlog-stat-label { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); }
410
+ [data-edrv-view] .edrv-svnlog-stat-duo { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }
411
+ [data-edrv-view] .edrv-svnlog-stat-hist { flex: 1 1 260px; display: flex; flex-direction: column; gap: 4px; min-width: 220px; }
412
+ [data-edrv-view] .edrv-svnlog-stat-row { display: flex; align-items: center; gap: 8px; font-size: 11px; }
413
+ [data-edrv-view] .edrv-svnlog-stat-name { width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #52606d); }
414
+ [data-edrv-view] .edrv-svnlog-stat-barwrap { flex: 1; height: 12px; border-radius: 6px; background: rgba(127,127,127,.15); overflow: hidden; }
415
+ [data-edrv-view] .edrv-svnlog-stat-bar { display: block; height: 100%; border-radius: 6px; }
416
+ [data-edrv-view] .edrv-svnlog-stat-count { width: 36px; text-align: right; color: var(--dsw-alias-label-secondary, #52606d); }
417
+ [data-edrv-view] .edrv-svnlog-stat-pie { width: 120px; height: 120px; flex-shrink: 0; border-radius: 50%; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); }
418
+ [data-edrv-view] .edrv-svnlog-stat-days { display: flex; align-items: flex-end; gap: 2px; height: 120px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow-x: auto; }
419
+ [data-edrv-view] .edrv-svnlog-stat-day { flex: 1 0 6px; min-width: 6px; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; }
420
+ [data-edrv-view] .edrv-svnlog-stat-seg { display: block; width: 100%; }
421
+ /* ===== SVN 日志区间弹窗(P1-6)@author ddj 2026-09-17 ===== */
422
+ [data-edrv-view] .edrv-svnlog-range-body { display: flex; flex-direction: column; gap: 12px; padding: 14px 16px 16px; }
423
+ [data-edrv-view] .edrv-svnlog-range-row { display: flex; gap: 14px; flex-wrap: wrap; }
424
+ [data-edrv-view] .edrv-svnlog-range-input { width: 110px; padding: 4px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }
425
+ [data-edrv-view] .edrv-svnlog-range-btns { display: flex; gap: 8px; justify-content: flex-end; }
426
+ /* SVN 日志 WC 版号行加粗(P1-9) */
427
+ [data-edrv-view] .edrv-svnlog-item-wc { font-weight: 700; }
428
+ /* SVN 日志 merged 行(P1-5):灰字 + 缩进(缩进量按层级内联),前置「↳」标记 */
429
+ [data-edrv-view] .edrv-svnlog-item-merged { color: var(--dsw-alias-label-tertiary, #7b8794); }
430
+ [data-edrv-view] .edrv-svnlog-item-merged .edrv-svnlog-merge-mark { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary, #7b8794); }
431
+ [data-edrv-view] .edrv-svnlog-item-merged .edrv-svnlog-cell-msg { font-style: italic; }
432
+ /* ===== 诊断日志弹窗(LogDialog)@author ddj 2026-09-17 ===== */
433
+ [data-edrv-view] .edrv-dlog-dialog { display: flex; flex-direction: column; gap: 8px; max-height: 88vh; }
434
+ [data-edrv-view] .edrv-dlog-head { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
435
+ [data-edrv-view] .edrv-dlog-title { margin: 0; font-size: 15px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }
436
+ [data-edrv-view] .edrv-dlog-file { max-width: 200px; padding: 4px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }
437
+ [data-edrv-view] .edrv-dlog-level { padding: 4px 6px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }
438
+ [data-edrv-view] .edrv-dlog-search { min-width: 120px; flex: 0 1 200px; padding: 4px 8px; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 6px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2933); font-size: 11px; }
439
+ [data-edrv-view] .edrv-dlog-auto { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); white-space: nowrap; cursor: pointer; }
440
+ [data-edrv-view] .edrv-dlog-error { padding: 4px 8px; border: 1px solid var(--dsw-alias-state-error-primary, rgba(217,83,79,.4)); border-radius: 6px; background: var(--dsw-alias-interactive-bg-hover-danger, rgba(217,83,79,.12)); color: var(--dsw-alias-state-error-primary, #d9534f); font-size: 11px; }
441
+ [data-edrv-view] .edrv-dlog-scroll { flex: 1 1 auto; min-height: 240px; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); border-radius: 8px; padding: 4px 0; background: var(--dsw-alias-bg-layer-1, #fff); }
442
+ [data-edrv-view] .edrv-dlog-row { display: flex; align-items: baseline; gap: 8px; padding: 1px 8px; font-family: var(--ds-font-family-code, ui-monospace, monospace); font-size: 11px; line-height: 1.55; }
443
+ [data-edrv-view] .edrv-dlog-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.06)); }
444
+ [data-edrv-view] .edrv-dlog-at { flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); }
445
+ [data-edrv-view] .edrv-dlog-lvl { flex-shrink: 0; width: 26px; text-align: center; font-size: 10px; color: var(--dsw-alias-label-tertiary, #9aa5b1); }
446
+ [data-edrv-view] .edrv-dlog-text { flex: 1; min-width: 0; white-space: pre-wrap; word-break: break-word; color: var(--dsw-alias-label-primary, #1f2933); }
447
+ [data-edrv-view] .edrv-dlog-row-error .edrv-dlog-text { color: var(--dsw-alias-state-error-primary, #d9534f); }
448
+ [data-edrv-view] .edrv-dlog-row-warn .edrv-dlog-text { color: var(--dsw-alias-state-warning-primary, #b45309); }
449
+ [data-edrv-view] .edrv-dlog-row-debug .edrv-dlog-text { color: var(--dsw-alias-label-secondary, #52606d); }
450
+ [data-edrv-view] .edrv-dlog-foot { display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); flex-shrink: 0; }
451
+ [data-edrv-view] .edrv-dlog-path { font-family: var(--ds-font-family-code, ui-monospace, monospace); max-width: 46%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
452
+ [data-edrv-view] .edrv-dlog-stat { white-space: nowrap; }
453
+ [data-edrv-view] .edrv-dlog-switch { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--dsw-alias-label-secondary, #52606d); white-space: nowrap; cursor: pointer; flex-shrink: 0; }
454
+ /* SVN 更新结果条(P3:update 条目级明细,可关闭) */
455
+ [data-edrv-view] .edrv-svnres { display: flex; align-items: flex-start; gap: 8px; flex-shrink: 0; padding: 5px 10px; font-size: 11px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); background: var(--dsw-alias-bg-layer-1, rgba(15,157,88,.06)); }
456
+ [data-edrv-view] .edrv-svnres-head { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }
457
+ [data-edrv-view] .edrv-svnres-list { display: flex; flex-wrap: wrap; gap: 4px 10px; min-width: 0; }
458
+ [data-edrv-view] .edrv-svnres-item { display: inline-flex; align-items: center; gap: 3px; font-family: var(--ds-font-family-code, ui-monospace, monospace); color: var(--dsw-alias-label-secondary, #52606d); }
459
+ [data-edrv-view] .edrv-svnres-item.edrv-svnres-conflict { color: var(--dsw-alias-state-error-primary, #d9534f); font-weight: 700; }
460
+
284
461
  /* ===== 侧栏形态(betterSidebar 面板内编辑区) ===== */
285
462
  [data-edrv-view][data-edrv-layout='side'] { box-sizing: border-box; height: 100%; }
286
463