dsh-vscode-mode 0.1.19 → 0.1.21
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 +17 -7
- package/lib/client.js +1649 -272
- package/lib/client.js.map +1 -1
- package/lib/index.js +87 -0
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/diffDock.ts +52 -0
- package/src/client/diffDockStore.ts +87 -0
- package/src/client/editorLayout.ts +19 -0
- package/src/client/events.ts +40 -11
- package/src/client/index.ts +49 -13
- package/src/client/outline/OutlinePanel.tsx +213 -0
- package/src/client/outline/index.ts +24 -0
- package/src/client/outline/parse.ts +271 -0
- package/src/client/outline/sources.ts +134 -0
- package/src/client/outline/types.ts +51 -0
- package/src/client/sidebar/SidebarView.ts +73 -0
- package/src/client/sidebar/panels/FileExplorer.ts +161 -0
- package/src/client/sidebar/panels/index.ts +24 -0
- package/src/client/sidebar/registry.ts +52 -0
- package/src/client/sidebar/types.ts +36 -0
- package/src/client/styles/editor.css +130 -5
- package/src/client/ui/ConversationDiffDock.ts +84 -0
- package/src/client/ui/DiffBox.ts +78 -29
- package/src/client/ui/EditorView.ts +209 -52
- package/src/rpc.ts +21 -0
- package/src/shared/rpc.ts +10 -0
- package/src/tree.ts +62 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* dsh-vscode-mode client — 「文件管理」面板定义(注册表一项)。
|
|
4
|
+
* 作者 ddj 2026-08-26
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react'
|
|
7
|
+
import { FileExplorer } from './FileExplorer.js'
|
|
8
|
+
import type { SidebarPanelDef, SidebarCtx } from '../types.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 构造文件管理面板定义。
|
|
12
|
+
* @author ddj 2026年08月26号
|
|
13
|
+
* @returns 面板定义(含活动栏徽标 = 差异文件总数)
|
|
14
|
+
*/
|
|
15
|
+
export function createFilePanel(): SidebarPanelDef {
|
|
16
|
+
return {
|
|
17
|
+
id: 'explorer',
|
|
18
|
+
title: '文件管理',
|
|
19
|
+
icon: '📁',
|
|
20
|
+
order: 10,
|
|
21
|
+
badge: (ctx: SidebarCtx) => (ctx.sum && ctx.sum.totalFiles > 0 ? ctx.sum.totalFiles : null),
|
|
22
|
+
render: (ctx: SidebarCtx) => React.createElement(FileExplorer, { ctx }),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-vscode-mode client — 侧边栏面板注册表。
|
|
3
|
+
* 镜像 fileOpeners.ts 的注册表模式:create + register(返回注销) + list + subscribe,
|
|
4
|
+
* 由 client/index.ts `ctx.provide('edrvSidebarPanels', registry)` 对外暴露(第三方可注册)。
|
|
5
|
+
* 作者 ddj 2026-08-26
|
|
6
|
+
*/
|
|
7
|
+
import type { SidebarPanelDef } from './types.js'
|
|
8
|
+
|
|
9
|
+
export interface SidebarPanelRegistry {
|
|
10
|
+
register(panel: SidebarPanelDef): () => void
|
|
11
|
+
list(): readonly SidebarPanelDef[]
|
|
12
|
+
subscribe(listener: () => void): () => void
|
|
13
|
+
get(id: string): SidebarPanelDef | undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** 校验面板定义并写入注册表(缺 id/render 抛 TypeError)。 */
|
|
17
|
+
function panelRegister(entries: Map<string, SidebarPanelDef>, notify: () => void, panel: SidebarPanelDef): void {
|
|
18
|
+
if (!panel.id || typeof panel.render !== 'function') throw new TypeError('侧边栏面板必须提供 id 和 render')
|
|
19
|
+
entries.set(panel.id, panel)
|
|
20
|
+
notify()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 创建生命周期独立的面板注册表。
|
|
25
|
+
* @author ddj 2026年08月26号
|
|
26
|
+
* @returns 面板注册表
|
|
27
|
+
*/
|
|
28
|
+
export function createSidebarPanelRegistry(): SidebarPanelRegistry {
|
|
29
|
+
const entries = new Map<string, SidebarPanelDef>()
|
|
30
|
+
const listeners = new Set<() => void>()
|
|
31
|
+
const notify = (): void => {
|
|
32
|
+
for (const listener of listeners) listener()
|
|
33
|
+
}
|
|
34
|
+
const list = (): readonly SidebarPanelDef[] =>
|
|
35
|
+
[...entries.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100))
|
|
36
|
+
return {
|
|
37
|
+
register(panel: SidebarPanelDef): () => void {
|
|
38
|
+
panelRegister(entries, notify, panel)
|
|
39
|
+
return () => {
|
|
40
|
+
if (entries.get(panel.id) !== panel) return
|
|
41
|
+
entries.delete(panel.id)
|
|
42
|
+
notify()
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
list,
|
|
46
|
+
subscribe(listener: () => void): () => void {
|
|
47
|
+
listeners.add(listener)
|
|
48
|
+
return () => listeners.delete(listener)
|
|
49
|
+
},
|
|
50
|
+
get: (id: string) => entries.get(id),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-vscode-mode client — 侧边栏面板契约(类型 + 上下文)。
|
|
3
|
+
* 面板 = 活动栏一项 + 面板区一处渲染;新增面板只需注册一条 SidebarPanelDef。
|
|
4
|
+
* 作者 ddj 2026-08-26
|
|
5
|
+
*/
|
|
6
|
+
import type { OutlineSourceRegistry } from '../outline/types.js'
|
|
7
|
+
|
|
8
|
+
/** 面板可用的共享上下文(由 SidebarView 从 EditorView 注入,面板不直碰其内部)。 */
|
|
9
|
+
export interface SidebarCtx {
|
|
10
|
+
sessionId?: string
|
|
11
|
+
openFile: (path: string) => void
|
|
12
|
+
activePath: string | null
|
|
13
|
+
/** 文件路径 → 待处理差异数(>0 才收录)。 */
|
|
14
|
+
pendingByPath: Record<string, number>
|
|
15
|
+
/** 按文件分组的差异摘要(records 状态派生)。 */
|
|
16
|
+
sum: { totalFiles: number; files: Array<{ path: string; pending: number }> }
|
|
17
|
+
/** 触发差异记录刷新(保存/决策后由 EditorView 统一 emitRefresh)。 */
|
|
18
|
+
refreshRecords: () => void
|
|
19
|
+
/** 取当前 Monaco 编辑器实例(可能为 null;大纲面板读 model/跳转用)。 */
|
|
20
|
+
editor?: () => unknown | null
|
|
21
|
+
/** 大纲源注册表(公开 provide 为 edrvOutlineSources;大纲面板解析符号用)。 */
|
|
22
|
+
outlineSources?: OutlineSourceRegistry
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 单个侧边栏面板定义。 */
|
|
26
|
+
export interface SidebarPanelDef {
|
|
27
|
+
id: string
|
|
28
|
+
title: string
|
|
29
|
+
/** 活动栏图标(文本/SVG 均可,横向居中显示)。 */
|
|
30
|
+
icon: string
|
|
31
|
+
order?: number
|
|
32
|
+
/** 活动栏徽标:返回数字/null;缺省不显示。 */
|
|
33
|
+
badge?: (ctx: SidebarCtx) => number | null
|
|
34
|
+
/** 面板区渲染(ctx 每次渲染注入最新快照)。 */
|
|
35
|
+
render: (ctx: SidebarCtx) => unknown
|
|
36
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* dsh-vscode-mode 编辑区样式(原 ensureEdrvStyles 字符串,落盘为真 .css,由 tsdown CSS-inline 注入) */
|
|
2
2
|
/* 浅色主题(对齐参考图:#f8f8f8 近白底 + 薄荷绿药丸 + 青绿文字 + 三文鱼 undo + 浅灰边框):
|
|
3
3
|
在 [data-edrv-view] 作用域内重定义 --dsw-alias-*,级联命中组件内联样式与 CSS。 */
|
|
4
|
-
[data-edrv-view] { --edrv-tab-h: 34px; --dsw-alias-bg-base: #f8f8f8; --dsw-alias-bg-layer-1: #ffffff; --dsw-alias-bg-layer-2: #f0f4f4; --dsw-alias-bg-overlay: #ffffff; --dsw-alias-label-primary: #1f2933; --dsw-alias-label-secondary: #52606d; --dsw-alias-label-tertiary: #9aa5b1; --dsw-alias-border-l1: #e0e6e8; --dsw-alias-border-l2: #cbd2d9; --dsw-alias-brand-primary: #0f9d58; --dsw-alias-state-success-primary: #0f9d58; --dsw-alias-state-error-primary: #d9534f; --dsw-alias-state-warn-primary: #d97706; --dsw-alias-interactive-bg-hover: rgba(15,157,88,.08); }
|
|
4
|
+
[data-edrv-view] { box-sizing: border-box; --edrv-tab-h: 34px; --dsw-alias-bg-base: #f8f8f8; --dsw-alias-bg-layer-1: #ffffff; --dsw-alias-bg-layer-2: #f0f4f4; --dsw-alias-bg-overlay: #ffffff; --dsw-alias-label-primary: #1f2933; --dsw-alias-label-secondary: #52606d; --dsw-alias-label-tertiary: #9aa5b1; --dsw-alias-border-l1: #e0e6e8; --dsw-alias-border-l2: #cbd2d9; --dsw-alias-brand-primary: #0f9d58; --dsw-alias-state-success-primary: #0f9d58; --dsw-alias-state-error-primary: #d9534f; --dsw-alias-state-warn-primary: #d97706; --dsw-alias-interactive-bg-hover: rgba(15,157,88,.08); }
|
|
5
5
|
|
|
6
6
|
[data-edrv-view] .edrv-tabs { display: flex; align-items: center; gap: 2px; padding: 2px 8px 0; background: var(--dsw-alias-bg-layer-1, transparent); overflow-x: auto; scrollbar-width: thin; flex-shrink: 0; }
|
|
7
7
|
[data-edrv-view] .edrv-tab { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 8px 0 12px; border: none; border-radius: 6px 6px 0 0; background: transparent; color: var(--dsw-alias-label-secondary, #aaa); font-size: 12px; cursor: pointer; white-space: nowrap; flex-shrink: 0; }
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
[data-edrv-view] .edrv-pathbar { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 10px; background: var(--dsw-alias-bg-layer-1, #ffffff); border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); font-size: 12px; flex-shrink: 0; overflow: hidden; }
|
|
16
16
|
[data-edrv-view] .edrv-pathbar .edrv-pb-name { font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }
|
|
17
17
|
[data-edrv-view] .edrv-pathbar .edrv-pb-full { flex: 1; min-width: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: rtl; text-align: left; }
|
|
18
|
+
[data-edrv-view] .edrv-pathbar .edrv-pb-meta { flex-shrink: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-variant-numeric: tabular-nums; }
|
|
19
|
+
[data-edrv-view] .edrv-pathbar .edrv-pb-status { color: var(--dsw-alias-label-secondary, #52606d); }
|
|
18
20
|
[data-edrv-view] .edrv-path-input { flex: 1; min-width: 140px; 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: 12px; outline: none; }
|
|
19
21
|
[data-edrv-view] .edrv-search-wrap { position: relative; flex-shrink: 0; display: flex; align-items: center; padding: 2px 8px 2px 4px; }
|
|
20
22
|
[data-edrv-view] .edrv-search { width: 190px; 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: 12px; outline: none; }
|
|
@@ -43,11 +45,12 @@
|
|
|
43
45
|
[data-edrv-view] .edrv-minus-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; max-width: 100%; overflow: visible; pointer-events: none; z-index: 6; }
|
|
44
46
|
[data-edrv-view] .edrv-minus-item { position: absolute; box-sizing: border-box; max-width: 100%; overflow: hidden; }
|
|
45
47
|
[data-edrv-view] .edrv-minus-item::before { content: ''; position: absolute; left: 4px; top: 50%; width: 8px; height: 2px; transform: translateY(-50%); border-radius: 2px; background: #d9534f; }
|
|
46
|
-
[data-edrv-view] .edrv-monaco-host { flex: 1; min-height: 0; position: relative; }
|
|
47
|
-
[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 0; }
|
|
48
|
+
[data-edrv-view] .edrv-monaco-host { flex: 1 1 auto; min-width: 0; min-height: 0; position: relative; overflow: hidden; }
|
|
49
|
+
[data-edrv-view] .edrv-monaco-host > div { position: absolute; inset: 0; min-width: 0; min-height: 0; }
|
|
48
50
|
[data-edrv-view] .edrv-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; height: 100%; color: var(--dsw-alias-label-tertiary, #777); font-size: 13px; }
|
|
49
|
-
[data-edrv-view] .edrv-diffbar { position: absolute; left: 0; right: 0; bottom: 10px; margin: 0 auto; z-index: 25; width: fit-content; max-width: calc(100% - 20px); border-radius:
|
|
50
|
-
[data-edrv-view] .edrv-
|
|
51
|
+
[data-edrv-view] .edrv-diffbar { position: absolute; left: 0; right: 0; bottom: 10px; margin: 0 auto; z-index: 25; width: fit-content; max-width: calc(100% - 20px); border-radius: 12px; background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, #f0f4f4)); border: 1px solid var(--dsw-alias-border-l1, #e0e6e8); box-shadow: none; overflow: visible; display: flex; flex-direction: column; }
|
|
52
|
+
[data-edrv-view] .edrv-editor-area > .edrv-diffbar { max-height: calc(100% - 20px); }
|
|
53
|
+
[data-edrv-view] .edrv-diffbar-row { display: flex; align-items: center; gap: 6px; height: 36px; padding: 4px 5px 4px 12px; flex-wrap: nowrap; }
|
|
51
54
|
[data-edrv-view] .edrv-diffbar-count { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); min-width: 42px; text-align: center; white-space: nowrap; cursor: pointer; }
|
|
52
55
|
[data-edrv-view] .edrv-diffbar-count.edrv-count-file { min-width: 64px; cursor: default; }
|
|
53
56
|
[data-edrv-view] .edrv-diffbar-file { flex: 1; min-width: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
@@ -105,3 +108,125 @@
|
|
|
105
108
|
[data-edrv-view] .edrv-progress-value { transition: none; }
|
|
106
109
|
}
|
|
107
110
|
|
|
111
|
+
/* ===== 唯一会话级差异 dock(遵循 DSH todo/queue 原生布局) ===== */
|
|
112
|
+
[data-edrv-diff-dock-shell] {
|
|
113
|
+
box-sizing: border-box;
|
|
114
|
+
width: calc(100% - var(--dsh-composer-side-clearance) - var(--dsh-composer-side-clearance) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));
|
|
115
|
+
max-width: calc(var(--dsh-composer-card-max-width) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset) - var(--dsh-composer-dock-inset));
|
|
116
|
+
margin: 0 auto;
|
|
117
|
+
padding: 0;
|
|
118
|
+
flex: none;
|
|
119
|
+
border: 1px solid var(--dsw-alias-border-l1, #e0e6e8);
|
|
120
|
+
border-radius: 12px;
|
|
121
|
+
background: var(--dsw-specific-tip, var(--dsw-alias-bg-layer-2, transparent));
|
|
122
|
+
box-shadow: none;
|
|
123
|
+
overflow: hidden;
|
|
124
|
+
}
|
|
125
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-dock {
|
|
126
|
+
box-sizing: border-box;
|
|
127
|
+
position: relative;
|
|
128
|
+
left: auto;
|
|
129
|
+
right: auto;
|
|
130
|
+
bottom: auto;
|
|
131
|
+
z-index: auto;
|
|
132
|
+
width: 100%;
|
|
133
|
+
max-width: none;
|
|
134
|
+
margin: 0;
|
|
135
|
+
display: flex;
|
|
136
|
+
flex-direction: column;
|
|
137
|
+
border: none;
|
|
138
|
+
border-radius: 0;
|
|
139
|
+
background: transparent;
|
|
140
|
+
box-shadow: none;
|
|
141
|
+
overflow: visible;
|
|
142
|
+
}
|
|
143
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-row { min-height: 36px; padding: 6px 12px; }
|
|
144
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-dock-row { min-height: 36px; padding: 6px 12px; }
|
|
145
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-body { max-height: 180px; overflow: auto; padding: 6px 12px 10px; border-top: 1px solid var(--dsw-alias-border-l1); }
|
|
146
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button { box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: 100%; height: 24px; border: none; border-radius: 8px; padding: 0 12px; background: transparent; color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 500; line-height: 24px; text-align: center; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
147
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-dock-button:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
148
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-count { display: inline-flex; align-items: center; color: var(--dsw-alias-label-primary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 500; line-height: 24px; }
|
|
149
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-file { display: inline-flex; align-items: center; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }
|
|
150
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-editor-row { display: flex; align-items: center; height: 36px; min-height: 36px; padding: 4px 12px; overflow-x: auto; scrollbar-width: thin; }
|
|
151
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { display: flex; align-items: center; gap: 10px; width: 100%; min-width: max-content; min-height: 28px; }
|
|
152
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-nav,
|
|
153
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-files,
|
|
154
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-actions { display: flex; align-items: center; gap: 6px; flex: none; height: 28px; }
|
|
155
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-actions { margin-left: auto; }
|
|
156
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle { display: inline-flex; align-items: center; justify-content: center; width: 28px; min-width: 28px; padding: 0; font-size: 13px; line-height: 24px; }
|
|
157
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-details-toggle svg { width: 14px; height: 14px; }
|
|
158
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-files { min-width: 0; }
|
|
159
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { min-width: 72px; max-width: 240px; }
|
|
160
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-progress { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; font-variant-numeric: tabular-nums; }
|
|
161
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-summary { min-width: 0; color: var(--dsw-alias-label-tertiary); font-family: Inter, var(--dsw-font-family); font-size: 13px; font-weight: 400; line-height: 24px; }
|
|
162
|
+
[data-edrv-diff-dock-shell] .edrv-pill { box-sizing: border-box; height: 28px; padding: 0 9px; border: none; border-radius: 999px; cursor: pointer; white-space: nowrap; font-size: 11px; line-height: 26px; font-weight: 600; }
|
|
163
|
+
[data-edrv-diff-dock-shell] .edrv-pill-keep { background: var(--dsw-alias-state-success-tertiary); color: var(--dsw-alias-state-success-primary); border: 1px solid var(--dsw-alias-state-success-secondary); }
|
|
164
|
+
[data-edrv-diff-dock-shell] .edrv-pill-undo { background: var(--dsw-alias-state-error-tertiary); color: var(--dsw-alias-state-error-primary); border: 1px solid var(--dsw-alias-state-error-secondary); }
|
|
165
|
+
[data-edrv-diff-dock-shell] .edrv-pill-ghost { background: transparent; color: var(--dsw-alias-label-secondary); border: 1px solid var(--dsw-alias-border-l1); }
|
|
166
|
+
[data-edrv-diff-dock-shell] .edrv-pill:hover:not(:disabled) { background-color: var(--dsw-alias-interactive-bg-hover); }
|
|
167
|
+
[data-edrv-diff-dock-shell] .edrv-pill:focus-visible { outline: 2px solid var(--dsw-alias-label-tertiary); outline-offset: -2px; }
|
|
168
|
+
[data-edrv-diff-dock-shell] .edrv-pill:disabled { opacity: .45; cursor: default; }
|
|
169
|
+
[data-edrv-diff-dock-shell] .edrv-diffrow { display: flex; align-items: center; gap: 8px; padding: 3px 6px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; background: transparent; cursor: pointer; flex-wrap: wrap; }
|
|
170
|
+
[data-edrv-diff-dock-shell] .edrv-diffrow:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
171
|
+
[data-edrv-diff-dock-shell] .edrv-diffrow-l { color: var(--dsw-alias-label-primary); }
|
|
172
|
+
[data-edrv-diff-dock-shell] .edrv-diffrow-o { color: var(--dsw-alias-state-error-primary); }
|
|
173
|
+
[data-edrv-diff-dock-shell] .edrv-diffrow-n { color: var(--dsw-alias-state-success-primary); }
|
|
174
|
+
[data-edrv-diff-dock-shell] .edrv-diffrow-tag { color: var(--dsw-alias-label-tertiary); }
|
|
175
|
+
[data-edrv-diff-dock-shell] .edrv-diffrow-stale { border-color: var(--dsw-alias-state-warn-secondary); cursor: default; }
|
|
176
|
+
[data-edrv-diff-dock-shell] .edrv-diffbox-others { border-top: 1px solid var(--dsw-alias-border-l1); }
|
|
177
|
+
[data-edrv-diff-dock-shell] .edrv-diffrow-file { border-color: var(--dsw-alias-border-l1); background: transparent; color: var(--dsw-alias-label-secondary); }
|
|
178
|
+
[data-edrv-diff-dock-shell] .edrv-diffrow-file:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
|
|
179
|
+
[data-edrv-diff-dock-shell] .edrv-diff-details { display: flex; flex-direction: column; gap: 2px; padding: 6px 12px 8px; border-top: 1px solid var(--dsw-alias-border-l1); }
|
|
180
|
+
[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item { display: flex; align-items: center; width: 100%; min-height: 28px; gap: 8px; padding: 4px 8px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-primary); cursor: pointer; text-align: left; font-size: 12px; line-height: 20px; }
|
|
181
|
+
[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
182
|
+
[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item.danger { color: var(--dsw-alias-state-error-primary); }
|
|
183
|
+
[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-item:disabled { opacity: .45; cursor: default; }
|
|
184
|
+
[data-edrv-diff-dock-shell] .edrv-diff-details .edrv-diffmenu-sep { height: 1px; margin: 2px 4px; background: var(--dsw-alias-border-l1); }
|
|
185
|
+
@media (max-width: 620px) {
|
|
186
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-editor-content { width: max-content; }
|
|
187
|
+
[data-edrv-diff-dock-shell] .edrv-diffbar-files .edrv-diffbar-file { max-width: 160px; }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* ===== 侧边栏(活动栏 + 面板区 + 文件树) ===== */
|
|
191
|
+
[data-edrv-view] .edrv-editor-row { display: flex; flex: 1 1 auto; min-width: 0; min-height: 0; overflow: hidden; }
|
|
192
|
+
[data-edrv-view] .edrv-main-col { box-sizing: border-box; min-width: 0; min-height: 0; overflow: hidden; }
|
|
193
|
+
[data-edrv-view] .edrv-main-col > .edrv-pathbar,
|
|
194
|
+
[data-edrv-view] .edrv-main-col > .edrv-tabs { flex: 0 0 auto; }
|
|
195
|
+
[data-edrv-view] .edrv-editor-area { position: relative; display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; }
|
|
196
|
+
[data-edrv-view] .edrv-sidebar { display: flex; flex-direction: row; flex-shrink: 0; min-width: 0; height: 100%; background: var(--dsw-alias-bg-layer-1, #ffffff); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); overflow: hidden; }
|
|
197
|
+
[data-edrv-view] .edrv-rail { display: flex; flex-direction: column; align-items: center; width: 44px; flex-shrink: 0; padding: 6px 0; background: var(--dsw-alias-bg-layer-2, #f0f4f4); border-right: 1px solid var(--dsw-alias-border-l1, #e0e6e8); gap: 4px; }
|
|
198
|
+
[data-edrv-view] .edrv-rail-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; border-radius: 8px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); cursor: pointer; flex-shrink: 0; }
|
|
199
|
+
[data-edrv-view] .edrv-rail-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }
|
|
200
|
+
[data-edrv-view] .edrv-rail-btn.edrv-rail-on { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.12)); color: var(--dsw-alias-label-primary, #1f2933); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary, #0f9d58); }
|
|
201
|
+
[data-edrv-view] .edrv-rail-icon { font-size: 15px; line-height: 1; }
|
|
202
|
+
[data-edrv-view] .edrv-rail-badge { position: absolute; top: 2px; right: 2px; min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px; background: var(--dsw-alias-brand-primary, #0f9d58); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; }
|
|
203
|
+
[data-edrv-view] .edrv-side-body { flex: 1; min-width: 0; min-height: 0; display: flex; overflow: hidden; }
|
|
204
|
+
[data-edrv-view] .edrv-side-panel { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
|
|
205
|
+
[data-edrv-view] .edrv-side-head { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px 0 12px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e0e6e8); flex-shrink: 0; }
|
|
206
|
+
[data-edrv-view] .edrv-side-title { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary, #1f2933); white-space: nowrap; }
|
|
207
|
+
[data-edrv-view] .edrv-side-root { font-size: 11px; color: var(--dsw-alias-label-tertiary, #9aa5b1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
208
|
+
[data-edrv-view] .edrv-side-btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: none; border-radius: 6px; background: transparent; color: var(--dsw-alias-label-secondary, #52606d); font-size: 12px; cursor: pointer; flex-shrink: 0; }
|
|
209
|
+
[data-edrv-view] .edrv-side-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }
|
|
210
|
+
[data-edrv-view] .edrv-tree { flex: 1; min-height: 0; overflow: auto; padding: 4px 6px 12px; font-size: 12px; }
|
|
211
|
+
[data-edrv-view] .edrv-tree-row { display: flex; align-items: center; gap: 4px; height: 22px; padding-right: 6px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2933); cursor: pointer; white-space: nowrap; }
|
|
212
|
+
[data-edrv-view] .edrv-tree-row:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.08)); }
|
|
213
|
+
[data-edrv-view] .edrv-tree-row.edrv-tree-active { background: var(--dsw-alias-interactive-bg-hover, rgba(15,157,88,.14)); }
|
|
214
|
+
[data-edrv-view] .edrv-tree-chev { width: 12px; flex-shrink: 0; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; text-align: center; }
|
|
215
|
+
[data-edrv-view] .edrv-tree-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
216
|
+
[data-edrv-view] .edrv-tree-name.edrv-tree-dim { color: var(--dsw-alias-label-tertiary, #9aa5b1); }
|
|
217
|
+
[data-edrv-view] .edrv-tree-badge { min-width: 14px; height: 14px; padding: 0 4px; border-radius: 999px; background: var(--dsw-alias-state-warn-primary, #d97706); color: #fff; font-size: 9px; font-weight: 600; line-height: 14px; text-align: center; box-sizing: border-box; flex-shrink: 0; }
|
|
218
|
+
[data-edrv-view] .edrv-tree-loading { height: 22px; line-height: 22px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }
|
|
219
|
+
[data-edrv-view] .edrv-tree-error { display: flex; align-items: center; gap: 8px; margin: 4px 2px 6px; 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; }
|
|
220
|
+
/* 大纲面板:kind 字形 / detail / 行号 */
|
|
221
|
+
[data-edrv-view] .edrv-outline-kind { display: inline-flex; align-items: center; justify-content: center; width: 14px; flex-shrink: 0; font-size: 12px; font-weight: 600; text-align: center; }
|
|
222
|
+
[data-edrv-view] .edrv-outline-kind.edrv-ol-func { color: var(--dsw-alias-brand-primary, #0f9d58); }
|
|
223
|
+
[data-edrv-view] .edrv-outline-kind.edrv-ol-type { color: var(--dsw-alias-label-primary, #1f2933); }
|
|
224
|
+
[data-edrv-view] .edrv-outline-kind.edrv-ol-data { color: var(--dsw-alias-state-warn-primary, #d97706); }
|
|
225
|
+
[data-edrv-view] .edrv-outline-kind.edrv-ol-ns { color: var(--dsw-alias-label-secondary, #52606d); }
|
|
226
|
+
[data-edrv-view] .edrv-outline-kind.edrv-ol-key { color: var(--dsw-alias-label-tertiary, #9aa5b1); }
|
|
227
|
+
[data-edrv-view] .edrv-outline-detail { flex-shrink: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 11px; }
|
|
228
|
+
[data-edrv-view] .edrv-outline-ln { flex-shrink: 0; padding-right: 2px; color: var(--dsw-alias-label-tertiary, #9aa5b1); font-size: 10px; font-variant-numeric: tabular-nums; }
|
|
229
|
+
[data-edrv-view] .edrv-side-resize { width: 5px; flex-shrink: 0; cursor: col-resize; background: transparent; }
|
|
230
|
+
[data-edrv-view] .edrv-side-resize:hover { background: var(--dsw-alias-brand-primary, #0f9d58); opacity: .35; }
|
|
231
|
+
[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); }
|
|
232
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* dsh-vscode-mode client — 会话级差异 dock。
|
|
4
|
+
* conversation.input.dock 是唯一实例:普通对话显示摘要,文件编辑页显示完整操作条。
|
|
5
|
+
* 作者 ddj 2026-08-26
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react'
|
|
8
|
+
import { rpc } from '../rpc.js'
|
|
9
|
+
import { openDiffView } from '../events.js'
|
|
10
|
+
import { summarize } from '../state/records.js'
|
|
11
|
+
import { nextDiffPath } from '../diffDock.js'
|
|
12
|
+
import { readDiffDock, subscribeDiffDock } from '../diffDockStore.js'
|
|
13
|
+
import { DiffBox } from './DiffBox.js'
|
|
14
|
+
|
|
15
|
+
const nextIndexBySession = new Map()
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 对话输入框上方的唯一差异 dock。
|
|
19
|
+
* @author ddj 2026年08月26号
|
|
20
|
+
* @param props DSH 注入的会话属性
|
|
21
|
+
* @returns 差异 dock React 元素或 null
|
|
22
|
+
*/
|
|
23
|
+
export function ConversationDiffDock(props) {
|
|
24
|
+
const sessionId = props?.sessionId
|
|
25
|
+
const [summary, setSummary] = React.useState(null)
|
|
26
|
+
const [, setStoreRevision] = React.useState(0)
|
|
27
|
+
const seq = React.useRef(0)
|
|
28
|
+
|
|
29
|
+
const load = React.useCallback(() => {
|
|
30
|
+
if (!sessionId) return
|
|
31
|
+
const current = ++seq.current
|
|
32
|
+
rpc('edrv.list', { sessionId }).then((res) => {
|
|
33
|
+
if (current !== seq.current || !res || !res.ok || !Array.isArray(res.records)) return
|
|
34
|
+
setSummary(summarize(res.records))
|
|
35
|
+
}).catch(() => {})
|
|
36
|
+
}, [sessionId])
|
|
37
|
+
|
|
38
|
+
React.useEffect(() => {
|
|
39
|
+
load()
|
|
40
|
+
const timer = setInterval(load, 5000)
|
|
41
|
+
const onRefresh = () => load()
|
|
42
|
+
window.addEventListener('edrv:refresh', onRefresh)
|
|
43
|
+
return () => {
|
|
44
|
+
clearInterval(timer)
|
|
45
|
+
window.removeEventListener('edrv:refresh', onRefresh)
|
|
46
|
+
}
|
|
47
|
+
}, [load])
|
|
48
|
+
|
|
49
|
+
React.useEffect(() => {
|
|
50
|
+
if (!sessionId) return undefined
|
|
51
|
+
const sync = () => setStoreRevision((value) => value + 1)
|
|
52
|
+
sync()
|
|
53
|
+
return subscribeDiffDock(sessionId, sync)
|
|
54
|
+
}, [sessionId])
|
|
55
|
+
|
|
56
|
+
const renderDock = (content, key) => React.createElement('div', {
|
|
57
|
+
key,
|
|
58
|
+
className: 'edrv-diff-dock-shell',
|
|
59
|
+
'data-edrv-diff-dock-shell': '1',
|
|
60
|
+
}, content)
|
|
61
|
+
|
|
62
|
+
const editorSnapshot = readDiffDock(sessionId)
|
|
63
|
+
if (editorSnapshot) {
|
|
64
|
+
if (editorSnapshot.mode === 'editor-empty' && !editorSnapshot.fileTotal) return null
|
|
65
|
+
return renderDock(React.createElement(DiffBox, Object.assign({}, editorSnapshot, { dock: true })), sessionId)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!sessionId || !summary?.pendingFiles?.length) return null
|
|
69
|
+
|
|
70
|
+
const paths = summary.pendingFiles.map((file) => file.path)
|
|
71
|
+
const click = () => {
|
|
72
|
+
const current = nextIndexBySession.get(sessionId) ?? 0
|
|
73
|
+
const next = nextDiffPath(paths, current)
|
|
74
|
+
nextIndexBySession.set(sessionId, next.index)
|
|
75
|
+
if (next.path) openDiffView(next.path)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return renderDock(React.createElement(DiffBox, {
|
|
79
|
+
mode: 'chat',
|
|
80
|
+
dock: true,
|
|
81
|
+
fileTotal: paths.length,
|
|
82
|
+
onOpenNextFile: click,
|
|
83
|
+
}), sessionId || 'chat')
|
|
84
|
+
}
|
package/src/client/ui/DiffBox.ts
CHANGED
|
@@ -6,24 +6,63 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import React from 'react'
|
|
8
8
|
import { callIdAttr } from '../state/records.js'
|
|
9
|
+
import { diffDockText } from '../diffDock.js'
|
|
9
10
|
import { badgeOf } from './shared.js'
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* 折叠控制使用任务栏同尺寸的 chevron 轮廓,避免依赖未声明的运行时图标包。
|
|
14
|
+
* @author ddj 2026年08月25号
|
|
15
|
+
* @param open 是否已展开
|
|
16
|
+
* @returns 14×14 的装饰性 chevron SVG
|
|
17
|
+
*/
|
|
18
|
+
function chevronOf(open) {
|
|
19
|
+
const path = open ? 'M3.25 5.25 7 9l3.75-3.75' : 'M3.25 8.75 7 5l3.75 3.75'
|
|
20
|
+
return React.createElement('svg', {
|
|
21
|
+
width: 14,
|
|
22
|
+
height: 14,
|
|
23
|
+
viewBox: '0 0 14 14',
|
|
24
|
+
fill: 'none',
|
|
25
|
+
'aria-hidden': true,
|
|
26
|
+
focusable: false,
|
|
27
|
+
}, React.createElement('path', {
|
|
28
|
+
d: path,
|
|
29
|
+
stroke: 'currentColor',
|
|
30
|
+
strokeWidth: 1.2,
|
|
31
|
+
strokeLinecap: 'round',
|
|
32
|
+
strokeLinejoin: 'round',
|
|
33
|
+
}))
|
|
34
|
+
}
|
|
35
|
+
|
|
11
36
|
/**
|
|
12
37
|
* 差异条:当前文件内 diff 导航(↑↓←→)、Keep/Undo、展开 hunk 列表、⋮ 二级菜单
|
|
13
38
|
* (Keep All / Undo All / 差异总览 / 归档 / 回滚 / 刷新)+ 其他差异文件小节。
|
|
14
39
|
*/
|
|
15
40
|
export function DiffBox(props) {
|
|
16
41
|
const {
|
|
17
|
-
pendingRegions, staleRegions, onAct, onAcceptFile, onUndoFile, onAcceptAllFiles, onUndoAllFiles,
|
|
18
|
-
allPendingCount, onRollback, onJump, otherFiles, onOpenOther, onOpenLauncher, onRefresh, activePath,
|
|
19
|
-
diffIdx, diffTotal, fileIdx, fileTotal, onPrevDiff, onNextDiff, onPrevFile, onNextFile,
|
|
42
|
+
pendingRegions = [], staleRegions = [], onAct, onAcceptFile, onUndoFile, onAcceptAllFiles, onUndoAllFiles,
|
|
43
|
+
allPendingCount = 0, onRollback, onJump, otherFiles = [], onOpenOther, onOpenLauncher, onRefresh, activePath,
|
|
44
|
+
diffIdx = 0, diffTotal = 0, fileIdx = 0, fileTotal = 0, onPrevDiff, onNextDiff, onPrevFile, onNextFile,
|
|
45
|
+
mode = 'editor', onOpenNextFile, dock = false,
|
|
20
46
|
} = props
|
|
21
47
|
const [expanded, setExpanded] = React.useState(false)
|
|
22
|
-
const [
|
|
48
|
+
const [detailsOpen, setDetailsOpen] = React.useState(false)
|
|
49
|
+
const detailsId = React.useId()
|
|
23
50
|
const canAct = pendingRegions.length > 0
|
|
24
51
|
|
|
25
52
|
const base = String(activePath || '').split(/[\\/]/).pop() || ''
|
|
26
53
|
|
|
54
|
+
if (mode === 'chat' || mode === 'editor-empty') {
|
|
55
|
+
return React.createElement('div', { className: 'edrv-diffbar edrv-diffbar-dock', 'data-edrv-diffbox': '1', 'data-edrv-diff-mode': mode },
|
|
56
|
+
React.createElement('div', { className: 'edrv-diffbar-row edrv-diffbar-dock-row' },
|
|
57
|
+
React.createElement('button', {
|
|
58
|
+
type: 'button',
|
|
59
|
+
className: 'edrv-diffbar-dock-button',
|
|
60
|
+
title: '切换到下一个差异文件',
|
|
61
|
+
'aria-label': diffDockText(fileTotal),
|
|
62
|
+
onClick: onOpenNextFile,
|
|
63
|
+
}, diffDockText(fileTotal))))
|
|
64
|
+
}
|
|
65
|
+
|
|
27
66
|
let bodyEl = null
|
|
28
67
|
if (expanded) {
|
|
29
68
|
const rows = pendingRegions.map((r) => {
|
|
@@ -49,32 +88,42 @@ export function DiffBox(props) {
|
|
|
49
88
|
bodyEl = React.createElement('div', { className: 'edrv-diffbar-body' }, ...rows, ...stale, othersEl)
|
|
50
89
|
}
|
|
51
90
|
|
|
52
|
-
const
|
|
53
|
-
? React.createElement(
|
|
54
|
-
React.createElement('
|
|
55
|
-
React.createElement('
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
React.createElement('button', { className: 'edrv-diffmenu-item', disabled: !canAct, onClick: () => { onRollback(); setMenuOpen(false) } }, '⟲ 回滚文件'),
|
|
63
|
-
React.createElement('button', { className: 'edrv-diffmenu-item', onClick: () => { onRefresh(); setMenuOpen(false) } }, '⟳ 刷新')))
|
|
91
|
+
const details = detailsOpen
|
|
92
|
+
? React.createElement('div', { className: 'edrv-diff-details', id: detailsId },
|
|
93
|
+
React.createElement('button', { className: 'edrv-diffmenu-item', disabled: !allPendingCount, onClick: onAcceptAllFiles }, '✓ Keep All(全部文件采纳)'),
|
|
94
|
+
React.createElement('button', { className: 'edrv-diffmenu-item danger', disabled: !allPendingCount, onClick: onUndoAllFiles }, '↩ Undo All(全部文件不采纳)'),
|
|
95
|
+
React.createElement('div', { className: 'edrv-diffmenu-sep' }),
|
|
96
|
+
React.createElement('button', { className: 'edrv-diffmenu-item', onClick: () => onOpenLauncher('pending') }, '🗂 差异总览'),
|
|
97
|
+
React.createElement('button', { className: 'edrv-diffmenu-item', onClick: () => onOpenLauncher('archive') }, '📁 归档'),
|
|
98
|
+
React.createElement('div', { className: 'edrv-diffmenu-sep' }),
|
|
99
|
+
React.createElement('button', { className: 'edrv-diffmenu-item', disabled: !canAct, onClick: onRollback }, '⟲ 回滚文件'),
|
|
100
|
+
React.createElement('button', { className: 'edrv-diffmenu-item', onClick: onRefresh }, '⟳ 刷新'))
|
|
64
101
|
: null
|
|
65
102
|
|
|
66
|
-
return React.createElement('div', { className: 'edrv-diffbar', 'data-edrv-diffbox': '1' },
|
|
67
|
-
React.createElement('div', { className: 'edrv-diffbar-row' },
|
|
68
|
-
React.createElement('
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
103
|
+
return React.createElement('div', { className: 'edrv-diffbar edrv-diffbar-dock', 'data-edrv-diffbox': '1', 'data-edrv-diff-mode': 'editor' },
|
|
104
|
+
React.createElement('div', { className: 'edrv-diffbar-row edrv-diffbar-editor-row' },
|
|
105
|
+
React.createElement('div', { className: 'edrv-diffbar-editor-content' },
|
|
106
|
+
React.createElement('div', { className: 'edrv-diffbar-nav' },
|
|
107
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-ghost', title: '上一个差异', disabled: !canAct, onClick: onPrevDiff }, '↑'),
|
|
108
|
+
React.createElement('span', { className: 'edrv-diffbar-count edrv-diffbar-progress', title: '当前文件内差异位置(点击展开/收起差异列表)', onClick: () => setExpanded((v) => !v) }, diffTotal ? (diffIdx + 1) + '/' + diffTotal : '0/0'),
|
|
109
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-ghost', title: '下一个差异', disabled: !canAct, onClick: onNextDiff }, '↓')),
|
|
110
|
+
React.createElement('div', { className: 'edrv-diffbar-files' },
|
|
111
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-ghost', title: '上一个差异文件', disabled: !fileTotal, onClick: onPrevFile }, '←'),
|
|
112
|
+
React.createElement('span', { className: 'edrv-diffbar-count edrv-count-file edrv-diffbar-progress', title: '差异文件位置' }, fileTotal ? (fileIdx + 1) + '/' + fileTotal + ' 文件' : '0/0 文件'),
|
|
113
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-ghost', title: '下一个差异文件', disabled: !fileTotal, onClick: onNextFile }, '→'),
|
|
114
|
+
React.createElement('span', { className: 'edrv-diffbar-file edrv-diffbar-summary', title: activePath || '' }, base)),
|
|
115
|
+
|
|
116
|
+
React.createElement('div', { className: 'edrv-diffbar-actions' },
|
|
117
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-keep', title: '采纳当前文件的全部差异', disabled: !canAct, onClick: onAcceptFile }, '✓ Keep'),
|
|
118
|
+
React.createElement('button', { className: 'edrv-pill edrv-pill-undo', title: '不采纳当前文件的全部差异(回滚)', disabled: !canAct, onClick: onUndoFile }, '↩ Undo'),
|
|
119
|
+
React.createElement('button', {
|
|
120
|
+
className: 'edrv-pill edrv-pill-ghost edrv-diff-details-toggle',
|
|
121
|
+
title: detailsOpen ? '收起差异操作' : '展开差异操作',
|
|
122
|
+
'aria-label': detailsOpen ? '收起差异操作' : '展开差异操作',
|
|
123
|
+
'aria-expanded': detailsOpen,
|
|
124
|
+
'aria-controls': detailsId,
|
|
125
|
+
onClick: () => setDetailsOpen((value) => !value),
|
|
126
|
+
}, chevronOf(detailsOpen))))),
|
|
78
127
|
bodyEl,
|
|
79
|
-
|
|
128
|
+
details)
|
|
80
129
|
}
|