dsh-vscode-mode 0.20.2 → 0.20.4
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 +6 -4
- package/lib/client.js +441 -233
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/reviewBar.ts +126 -0
- package/src/client/sessionError.ts +12 -0
- package/src/client/styles/editor.css +4 -0
- package/src/client/ui/DiffBox.ts +11 -108
- package/src/client/ui/EditorView.ts +20 -3
- package/src/client/ui/OfficialDiffBar.tsx +37 -28
- package/src/client/ui/PerfSettings.ts +1 -1
- package/src/client/ui/SidecarDiffBar.tsx +154 -0
- package/src/client/ui/reviewAllRow.tsx +48 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Button, Menu } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
3
|
+
import { callIdAttr } from '../state/records.js'
|
|
4
|
+
import { SIDECAR_KEEP_TITLE, SIDECAR_LABEL, SIDECAR_LABEL_TITLE, SIDECAR_UNDO_TITLE, reviewBarView } from '../reviewBar.js'
|
|
5
|
+
import type { OfficialDownView } from '../reviewBar.js'
|
|
6
|
+
import { barIcon } from './OfficialDiffBar.js'
|
|
7
|
+
import { ReviewAllRow } from './reviewAllRow.js'
|
|
8
|
+
import { badgeOf } from './shared.js'
|
|
9
|
+
|
|
10
|
+
/** 侧车差异区域:由 records 派生的行范围,不含官方块元数据。 */
|
|
11
|
+
export interface SidecarRegion {
|
|
12
|
+
callId: string
|
|
13
|
+
idx: number
|
|
14
|
+
start?: number
|
|
15
|
+
end?: number
|
|
16
|
+
oldLines?: string[]
|
|
17
|
+
newLines?: string[]
|
|
18
|
+
create?: boolean
|
|
19
|
+
status?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 侧车栏输入:字段与官方栏对齐,缺的是官方记录元数据,多的是侧车区域的 keep/undo 能力。 */
|
|
23
|
+
export interface SidecarBarProps {
|
|
24
|
+
activePath?: string
|
|
25
|
+
pendingRegions?: SidecarRegion[]
|
|
26
|
+
staleRegions?: SidecarRegion[]
|
|
27
|
+
diffIdx?: number
|
|
28
|
+
/** 编辑器内容是否就绪;未就绪时不得断言差异失效 */
|
|
29
|
+
contentReady?: boolean
|
|
30
|
+
fileIdx?: number
|
|
31
|
+
fileTotal?: number
|
|
32
|
+
allPendingCount?: number
|
|
33
|
+
reviewBatchBusy?: boolean
|
|
34
|
+
/** 官方元数据通道失败时的可操作提示 */
|
|
35
|
+
officialDown?: OfficialDownView
|
|
36
|
+
onRetryOfficial?: () => void
|
|
37
|
+
onAcceptFile: () => void
|
|
38
|
+
onUndoFile: () => void
|
|
39
|
+
onAcceptAllFiles: () => void
|
|
40
|
+
onUndoAllFiles: () => void
|
|
41
|
+
onPrevFile: () => void
|
|
42
|
+
onNextFile: () => void
|
|
43
|
+
onPrevDiff: () => void
|
|
44
|
+
onNextDiff: () => void
|
|
45
|
+
onOpenLauncher: (tab: string) => void
|
|
46
|
+
onJump?: (region: SidecarRegion) => void
|
|
47
|
+
onRollback?: () => void
|
|
48
|
+
onRefresh?: () => void
|
|
49
|
+
reviewError?: string
|
|
50
|
+
reviewErrorTone?: 'info' | 'error'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 侧车(历史捕获)差异栏:与官方栏同一套版式、同一套按钮位置,只把数据源与标识换掉。
|
|
55
|
+
* 两套栏并存是数据现实,但外观不能分家,否则用户会以为插件版本不一致。
|
|
56
|
+
* @public
|
|
57
|
+
* @author ddj 2026年10月28号
|
|
58
|
+
* @param props 侧车区域计数、批量可用性与回调
|
|
59
|
+
* @returns 与官方栏同构的差异工具栏;展开后列出侧车区域
|
|
60
|
+
*/
|
|
61
|
+
export function SidecarDiffBar(props: SidecarBarProps): React.ReactElement {
|
|
62
|
+
const pendingRegions = props.pendingRegions ?? []
|
|
63
|
+
const staleRegions = props.staleRegions ?? []
|
|
64
|
+
const [expanded, setExpanded] = React.useState(false)
|
|
65
|
+
const [menu, setMenu] = React.useState<'more' | null>(null)
|
|
66
|
+
const name = String(props.activePath || '').split(/[\\/]/).pop() || '当前文件'
|
|
67
|
+
const count = pendingRegions.length
|
|
68
|
+
const total = count + staleRegions.length
|
|
69
|
+
const busy = props.reviewBatchBusy === true
|
|
70
|
+
const canDecide = total > 0 && !busy
|
|
71
|
+
const view = reviewBarView({
|
|
72
|
+
jumps: count, stale: staleRegions.length, index: props.diffIdx ?? 0,
|
|
73
|
+
ready: props.contentReady === true, pendingRecords: total, canKeep: true,
|
|
74
|
+
})
|
|
75
|
+
const down = props.officialDown
|
|
76
|
+
|
|
77
|
+
/** @private @author ddj 2026年10月28号 @param tab 既有总览页签 @returns 打开既有总览并收起菜单 */
|
|
78
|
+
function openOverview(tab: string): void { props.onOpenLauncher(tab); setMenu(null) }
|
|
79
|
+
|
|
80
|
+
/** @private @author ddj 2026年10月28号 @param mark 工具栏图标 @param label 无障碍名称 @param action 既有回调 @param disabled 是否禁用 @param title 禁用原因 @returns 紧凑导航按钮 */
|
|
81
|
+
function navButton(mark: Parameters<typeof barIcon>[0], label: string, action: () => void, disabled: boolean, title?: string): React.ReactElement {
|
|
82
|
+
return <Button size="sm" variant="ghost" className="edrv-review-icon" aria-label={label} title={title || label} disabled={disabled} onClick={action}>{barIcon(mark)}</Button>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const moreTrigger = <Button size="sm" variant="ghost" className="edrv-review-icon" aria-label="更多审查操作" title="更多审查操作" aria-haspopup="menu" aria-expanded={menu === 'more'} onClick={() => setMenu(menu === 'more' ? null : 'more')}>{barIcon('more')}</Button>
|
|
86
|
+
|
|
87
|
+
const body = expanded
|
|
88
|
+
? <div className="edrv-review-body">
|
|
89
|
+
{pendingRegions.map((region) => {
|
|
90
|
+
const key = callIdAttr(region.callId, region.idx)
|
|
91
|
+
return <div key={key} className="edrv-diffrow" data-edrv-hunk={key} title={'跳转到 L' + (region.start ?? '?')} onClick={() => props.onJump?.(region)}>
|
|
92
|
+
<span className="edrv-diffrow-l">{'L' + (region.start ?? '?') + '-' + (region.end ?? '?')}</span>
|
|
93
|
+
{(region.oldLines?.length ?? 0) > 0 ? <span className="edrv-diffrow-o">{'-' + (region.oldLines?.length ?? 0)}</span> : null}
|
|
94
|
+
{(region.newLines?.length ?? 0) > 0 ? <span className="edrv-diffrow-n">{'+' + (region.newLines?.length ?? 0)}</span> : null}
|
|
95
|
+
{region.create ? <span className="edrv-diffrow-tag">新建</span> : null}
|
|
96
|
+
</div>
|
|
97
|
+
})}
|
|
98
|
+
{staleRegions.map((region) => <div key={'stale' + callIdAttr(region.callId, region.idx)} className="edrv-diffrow edrv-diffrow-stale" title="差异无法定位(文件可能已被手动修改)">
|
|
99
|
+
<span className="edrv-diffrow-l">{'L' + (region.start ?? '?') + ' 无法定位'}</span>
|
|
100
|
+
<span className="edrv-diffrow-tag">冲突/需刷新</span>
|
|
101
|
+
<span className="edrv-diffrow-tag">{badgeOf(region.status ?? '')}</span>
|
|
102
|
+
</div>)}
|
|
103
|
+
</div>
|
|
104
|
+
: null
|
|
105
|
+
|
|
106
|
+
return <div className="edrv-diffbar edrv-diffbar-dock edrv-reviewbar" data-edrv-diffbox="1" data-edrv-diff-source="sidecar" role="region" aria-label="文件差异审查">
|
|
107
|
+
<ReviewAllRow
|
|
108
|
+
total={props.fileTotal ?? 0}
|
|
109
|
+
current={(props.fileTotal ?? 0) && (props.fileIdx ?? -1) >= 0 ? (props.fileIdx ?? 0) + 1 : 0}
|
|
110
|
+
busy={busy}
|
|
111
|
+
pending={props.allPendingCount}
|
|
112
|
+
onOpen={() => props.onOpenLauncher('pending')}
|
|
113
|
+
onKeepAll={props.onAcceptAllFiles}
|
|
114
|
+
onUndoAll={props.onUndoAllFiles}
|
|
115
|
+
keepTitle="把这批历史改动全部标记为已看过(只改记录,不动磁盘文件)"
|
|
116
|
+
undoTitle="逐项撤销这些历史改动;缺少证据或遇到冲突即停止"
|
|
117
|
+
/>
|
|
118
|
+
{busy ? <div className="edrv-review-notice" role="status">正在处理历史改动…</div> : null}
|
|
119
|
+
{down ? <div className="edrv-review-notice edrv-review-notice-warn edrv-sidecar-down" role="status">
|
|
120
|
+
<span className="edrv-sidecar-down-text">{down.text}</span>
|
|
121
|
+
{props.onRetryOfficial ? <Button size="sm" variant="ghost" onClick={props.onRetryOfficial} title="重新读取官方待审记录">{down.retryLabel || '重试'}</Button> : null}
|
|
122
|
+
</div> : null}
|
|
123
|
+
<div className="edrv-review-heading">
|
|
124
|
+
<span className="edrv-review-filemark">{barIcon('file')}</span>
|
|
125
|
+
<span className="edrv-review-filename" title={props.activePath}>{name}</span>
|
|
126
|
+
<span className="edrv-review-source" title={SIDECAR_LABEL_TITLE}>{SIDECAR_LABEL}</span>
|
|
127
|
+
<span className="edrv-review-pending" title={view.pendingTitle}>待审 <strong>{total}</strong></span>
|
|
128
|
+
</div>
|
|
129
|
+
<div className="edrv-review-tools">
|
|
130
|
+
<div className="edrv-review-nav" role="group" aria-label="文件导航">
|
|
131
|
+
{navButton('left', '上一个差异文件', props.onPrevFile, !props.fileTotal)}
|
|
132
|
+
{navButton('right', '下一个差异文件', props.onNextFile, !props.fileTotal)}
|
|
133
|
+
</div>
|
|
134
|
+
<div className="edrv-review-nav edrv-review-blocknav" role="group" aria-label="差异导航">
|
|
135
|
+
{navButton('up', '上一个差异', props.onPrevDiff, !count, view.navPrevTitle)}
|
|
136
|
+
<span className="edrv-review-position" aria-live="polite">{view.position}</span>
|
|
137
|
+
{navButton('down', '下一个差异', props.onNextDiff, !count, view.navNextTitle)}
|
|
138
|
+
</div>
|
|
139
|
+
<div className="edrv-review-actions">
|
|
140
|
+
<Button size="sm" variant="ghost" className="edrv-review-undo" icon={barIcon('undo')} onClick={props.onUndoFile} disabled={!canDecide} title={SIDECAR_UNDO_TITLE}>Undo</Button>
|
|
141
|
+
<Button size="sm" variant="primary" className="edrv-review-keep" icon={barIcon('check')} onClick={props.onAcceptFile} disabled={!canDecide} title={SIDECAR_KEEP_TITLE}>Keep</Button>
|
|
142
|
+
<Button size="sm" variant="ghost" className="edrv-review-details" onClick={() => setExpanded(!expanded)} aria-expanded={expanded} aria-label={expanded ? '收起差异' : '展开差异'} title={expanded ? '收起差异' : '展开差异'}>详情{barIcon(expanded ? 'up' : 'down')}</Button>
|
|
143
|
+
<Menu open={menu === 'more'} anchor={moreTrigger} items={[{ id: 'pending', label: '差异总览' }, { id: 'archive', label: '归档 / 历史' }, { id: 'rollback', label: '回滚当前文件' }, { id: 'refresh', label: '刷新' }]} onSelect={(id: string) => {
|
|
144
|
+
if (id === 'rollback') { setMenu(null); props.onRollback?.(); return }
|
|
145
|
+
if (id === 'refresh') { setMenu(null); props.onRefresh?.(); return }
|
|
146
|
+
openOverview(id)
|
|
147
|
+
}} onClose={() => setMenu(null)} side="top" align="end" portal compact autoFocus />
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
{view.notice ? <div className={view.noticeTone === 'warn' ? 'edrv-review-notice edrv-review-notice-warn' : 'edrv-review-notice'} role="status">{view.notice}</div> : null}
|
|
151
|
+
{props.reviewError ? <div className={props.reviewErrorTone === 'info' ? 'edrv-review-notice' : 'edrv-review-notice edrv-review-error'} role={props.reviewErrorTone === 'info' ? 'status' : 'alert'}>{props.reviewError}</div> : null}
|
|
152
|
+
{body}
|
|
153
|
+
</div>
|
|
154
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Button } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
3
|
+
|
|
4
|
+
/** 全工作区批量行输入:官方栏与侧车栏共用同一套外观与禁用语义。 */
|
|
5
|
+
export interface ReviewAllRowProps {
|
|
6
|
+
/** 当前文件序号(1 基,0 表示未定位) */
|
|
7
|
+
current: number
|
|
8
|
+
/** 差异文件总数 */
|
|
9
|
+
total: number
|
|
10
|
+
/** 批量操作进行中 */
|
|
11
|
+
busy?: boolean
|
|
12
|
+
/** 批量不可用原因(悬浮提示) */
|
|
13
|
+
unavailable?: string
|
|
14
|
+
/** 工作区待处理项总数;为 0 时两个按钮禁用 */
|
|
15
|
+
pending?: number
|
|
16
|
+
/** 打开差异总览 */
|
|
17
|
+
onOpen: () => void
|
|
18
|
+
/** 全部 Keep */
|
|
19
|
+
onKeepAll?: () => void
|
|
20
|
+
/** 全部 Undo */
|
|
21
|
+
onUndoAll?: () => void
|
|
22
|
+
/** Keep All 悬浮说明 */
|
|
23
|
+
keepTitle?: string
|
|
24
|
+
/** Undo All 悬浮说明 */
|
|
25
|
+
undoTitle?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 全局批量行:差异文件计数 + Keep All / Undo All。
|
|
30
|
+
* 两套差异栏(官方 / 侧车)必须长得一样,否则用户会以为看到的是不同产品。
|
|
31
|
+
* @public
|
|
32
|
+
* @author ddj 2026年10月28号
|
|
33
|
+
* @param props 计数、批量可用性与回调
|
|
34
|
+
* @returns 持久化的全工作区控制条
|
|
35
|
+
*/
|
|
36
|
+
export function ReviewAllRow(props: ReviewAllRowProps): React.ReactElement {
|
|
37
|
+
const total = props.total ?? 0
|
|
38
|
+
const current = total && props.current > 0 ? props.current : 0
|
|
39
|
+
const disabled = props.busy || !!props.unavailable || !props.pending
|
|
40
|
+
|
|
41
|
+
return <div className="edrv-review-global" role="group" aria-label="全部待审文件">
|
|
42
|
+
<Button size="sm" variant="ghost" className="edrv-review-total" onClick={props.onOpen} title="打开当前工作区全部待审差异文件">差异文件 <strong>{current} / {total}</strong></Button>
|
|
43
|
+
<span className="edrv-review-all-actions">
|
|
44
|
+
<Button size="sm" variant="ghost" onClick={props.onKeepAll} disabled={disabled || !props.onKeepAll} title={props.unavailable || props.keepTitle || 'Keep 当前工作区全部待审文件'}>Keep All</Button>
|
|
45
|
+
<Button size="sm" variant="ghost" className="edrv-review-undo" onClick={props.onUndoAll} disabled={disabled || !props.onUndoAll} title={props.unavailable || props.undoTitle || 'Undo 当前工作区全部待审文件'}>Undo All</Button>
|
|
46
|
+
</span>
|
|
47
|
+
</div>
|
|
48
|
+
}
|