dsh-vscode-mode 0.21.4 → 0.23.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 (43) hide show
  1. package/README.md +52 -4
  2. package/lib/client.js +3442 -183
  3. package/lib/client.js.map +2 -2
  4. package/lib/index.js +3510 -222
  5. package/lib/index.js.map +1 -1
  6. package/package.json +1 -1
  7. package/src/client/index.ts +166 -0
  8. package/src/client/quickMenuHost.ts +46 -0
  9. package/src/client/quickMenuStore.ts +260 -0
  10. package/src/client/state/diffSummary.ts +52 -8
  11. package/src/client/styles/quickmenu.css +1049 -0
  12. package/src/client/ui/DiffLauncher.ts +1 -1
  13. package/src/client/ui/EditorView.ts +10 -2
  14. package/src/client/ui/OfficialDiffBar.tsx +1 -12
  15. package/src/client/ui/OfficialReview.ts +13 -3
  16. package/src/client/ui/QuickMenuDialog.tsx +835 -0
  17. package/src/client/ui/QuickMenuLauncher.tsx +792 -0
  18. package/src/client/ui/QuickMenuPickers.ts +577 -0
  19. package/src/client/ui/SidecarDiffBar.tsx +0 -22
  20. package/src/index.ts +9 -0
  21. package/src/missingReview.ts +36 -14
  22. package/src/paths.ts +10 -0
  23. package/src/pending/capture.ts +165 -0
  24. package/src/pending/decide.ts +213 -0
  25. package/src/pending/hunks.ts +241 -0
  26. package/src/pending/kernel.ts +233 -0
  27. package/src/pending/persist.ts +329 -0
  28. package/src/pending/store.ts +472 -0
  29. package/src/pending/vcs.ts +444 -0
  30. package/src/pendingCompat.ts +184 -0
  31. package/src/pendingRpc.ts +210 -0
  32. package/src/quickMenu.ts +269 -0
  33. package/src/reviewDb.ts +3 -0
  34. package/src/reviewRpc.ts +52 -1
  35. package/src/rpc.ts +66 -2
  36. package/src/shared/pathKey.ts +54 -0
  37. package/src/shared/pendingReview.ts +105 -0
  38. package/src/shared/quickMenu.ts +1009 -0
  39. package/src/shared/quickPick.ts +67 -0
  40. package/src/shared/rpc.ts +28 -0
  41. package/src/shared/skillCatalog.ts +60 -0
  42. package/src/skillCatalog.ts +138 -0
  43. package/src/store.ts +19 -0
@@ -106,7 +106,7 @@ export function DiffLauncher(props) {
106
106
  }, [tab, sessionId, source])
107
107
 
108
108
  let body
109
- const legacyFiles = sum.pendingFiles.filter((file) => file.sourceKind !== 'official')
109
+ const legacyFiles = sum.pendingFiles.filter((file) => file.sourceKind === 'legacy')
110
110
  if (source === 'official') {
111
111
  body = React.createElement(React.Fragment, null,
112
112
  React.createElement('div', { className: 'edrv-search-empty' }, '官方轮次净差异 · 原始历史只读;编辑按钮打开当前文件,保存不代表已审。'),
@@ -26,6 +26,7 @@ import { subscribeReview, refreshReview } from '../state/reviewFeed.js'
26
26
  import { runReviewBatch } from '../reviewBatch.js'
27
27
  import { ReviewAllConfirm } from './ReviewAllConfirm.js'
28
28
  import { reviewRegions } from '../../shared/reviewRegions.js'
29
+ import { PENDING_PREFIX } from '../../shared/pendingReview.js'
29
30
  import { diffSummary, officialPath } from '../state/diffSummary.js'
30
31
  import { emitFileChanged, emitRefresh } from '../events.js'
31
32
  import { langOf, loadMonaco, snippetLanguageOf } from '../monaco/loader.js'
@@ -579,7 +580,15 @@ export function EditorView(props) {
579
580
  }, [sessionId, active, selectedReview])
580
581
  React.useEffect(() => {
581
582
  const controller = new AbortController()
582
- if (!active || !review.page?.overview?.coveredPaths.some((path) => sameFile(path, active))) {
583
+ // 待审条目也走同一条行内差异通道:它由累计基线投影而来,形状与官方条目一致,
584
+ // 因此编辑器里天然就能画行内差异与 Keep/Undo 药丸,不必另开一个窄面板。
585
+ const pendingPath = (review.page?.items ?? [])
586
+ .filter((item) => String(item.id).startsWith(PENDING_PREFIX))
587
+ .map((item) => item.currentPath || item.path)
588
+ .find((path) => sameFile(path, active))
589
+ const coveredPath = review.page?.overview?.coveredPaths.find((path) => sameFile(path, active))
590
+ const reviewPath = coveredPath ?? pendingPath
591
+ if (!active || !reviewPath) {
583
592
  setOfficialDetail(null)
584
593
  setOfficialRecords([])
585
594
  return () => controller.abort()
@@ -588,7 +597,6 @@ export function EditorView(props) {
588
597
  async function loadReview() {
589
598
  try {
590
599
  // Tabs use workspace-relative paths; the metadata index owns the backend identity.
591
- const reviewPath = review.page?.overview?.coveredPaths.find((path) => sameFile(path, active)) ?? active
592
600
  const page = await rpc('edrv.review.list', { sessionId, path: reviewPath }, controller.signal)
593
601
  if (controller.signal.aborted) return
594
602
  if (!page.ok) throw new Error(page.error)
@@ -1,7 +1,6 @@
1
1
  import React from 'react'
2
2
  import { Button, Menu } from '@deepseek-ai/dsh-client-ui-primitives'
3
3
  import type { ReviewDetail, ReviewItem, ReviewPage } from '../../shared/review.js'
4
- import { ReviewLedger } from './OfficialReview.js'
5
4
  import { reviewBarView } from '../reviewBar.js'
6
5
  import { ReviewAllRow } from './reviewAllRow.js'
7
6
 
@@ -55,11 +54,9 @@ export function barIcon(mark: Mark): React.ReactElement {
55
54
  /** @public @author ddj 2026年10月27号 @param props Existing review callbacks and immutable record metadata. @returns Responsive review toolbar; no disk or decision side effects until an explicit action. */
56
55
  export function OfficialDiffBar(props: OfficialBarProps): React.ReactElement {
57
56
  const { official, pendingRegions = [], staleRegions = [], diffIdx = 0 } = props
58
- const [expanded, setExpanded] = React.useState(false)
59
- const [menu, setMenu] = React.useState<'records' | 'more' | null>(null)
57
+ const [menu, setMenu] = React.useState<'more' | null>(null)
60
58
  const name = String(props.activePath || '').split(/[\\/]/).pop() || '当前文件'
61
59
  const detail = official.detail
62
- const records = official.records ?? []
63
60
  const count = pendingRegions.length
64
61
  const view = reviewBarView({ jumps: count, stale: staleRegions.length, index: diffIdx, ready: props.contentReady === true,
65
62
  pendingRecords: official.pending, canKeep: detail?.capability.keep === true,
@@ -67,8 +64,6 @@ export function OfficialDiffBar(props: OfficialBarProps): React.ReactElement {
67
64
 
68
65
  /** @private @author ddj 2026年10月27号 @returns Close current popup without changing selection. */
69
66
  function closeMenu(): void { setMenu(null) }
70
- /** @private @author ddj 2026年10月27号 @param id Immutable record ID. @returns Select record and dismiss popup. */
71
- function selectRecord(id: string): void { official.onSelect(id); closeMenu() }
72
67
  /** @private @author ddj 2026年10月27号 @param tab Existing launcher tab. @returns Open existing overview, never create another review entry. */
73
68
  function openOverview(tab: string): void { props.onOpenLauncher(tab); closeMenu() }
74
69
  /** @private @author ddj 2026年10月27号 @param mark Navigation icon. @param label Accessible action. @param action Existing callback. @param disabled Whether navigation is unavailable. @param title Hover reason, defaults to the label. @returns Compact keyboard-focusable navigation control. */
@@ -76,9 +71,6 @@ export function OfficialDiffBar(props: OfficialBarProps): React.ReactElement {
76
71
  return <Button size="sm" variant="ghost" className="edrv-review-icon" aria-label={label} title={title || label} disabled={disabled} onClick={action}>{barIcon(mark)}</Button>
77
72
  }
78
73
 
79
- const recordTrigger = <Button size="sm" variant="ghost" className="edrv-review-record" aria-label="选择官方差异记录" aria-haspopup="menu" aria-expanded={menu === 'records'} disabled={!records.length} onClick={() => setMenu(menu === 'records' ? null : 'records')}>
80
- {detail ? `轮次 ${detail.item.turn}` : '选择记录'}{barIcon('down')}
81
- </Button>
82
74
  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>
83
75
 
84
76
  return <div className="edrv-diffbar edrv-diffbar-dock edrv-reviewbar" data-edrv-diffbox="1" data-edrv-diff-source="official" role="region" aria-label="文件差异审查">
@@ -99,7 +91,6 @@ export function OfficialDiffBar(props: OfficialBarProps): React.ReactElement {
99
91
  <span className="edrv-review-filemark">{barIcon('file')}</span>
100
92
  <span className="edrv-review-filename" title={props.activePath}>{name}</span>
101
93
  <span className="edrv-review-pending" title={view.pendingTitle}>待审 <strong>{official.pending}</strong></span>
102
- <Menu open={menu === 'records'} anchor={recordTrigger} items={records.map((item) => ({ id: item.id, label: `轮次 ${item.turn} / 事件 ${item.seq}` }))} selectedId={detail?.item.id} onSelect={selectRecord} onClose={closeMenu} side="top" align="end" portal compact autoFocus />
103
94
  </div>
104
95
  <div className="edrv-review-tools">
105
96
  <div className="edrv-review-nav" role="group" aria-label="文件导航">
@@ -114,12 +105,10 @@ export function OfficialDiffBar(props: OfficialBarProps): React.ReactElement {
114
105
  <div className="edrv-review-actions">
115
106
  <Button size="sm" variant="ghost" className="edrv-review-undo" icon={barIcon('undo')} onClick={props.onUndoFile} disabled={props.reviewBatchBusy || !detail?.capability.undo} title={detail?.capability.reason}>Undo</Button>
116
107
  <Button size="sm" variant="primary" className="edrv-review-keep" icon={barIcon('check')} onClick={props.onAcceptFile} disabled={props.reviewBatchBusy || !detail?.capability.keep} title={detail?.capability.keep ? view.keepHint : detail?.capability.reason}>Keep</Button>
117
- <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>
118
108
  <Menu open={menu === 'more'} anchor={moreTrigger} items={[{ id: 'pending', label: '差异总览' }, { id: 'archive', label: '归档 / 历史' }]} onSelect={openOverview} onClose={closeMenu} side="top" align="end" portal compact autoFocus />
119
109
  </div>
120
110
  </div>
121
111
  {detail && view.notice ? <div className={view.noticeTone === 'warn' ? 'edrv-review-notice edrv-review-notice-warn' : 'edrv-review-notice'} role="status">{view.notice}</div> : null}
122
112
  {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}
123
- {expanded ? <div className="edrv-review-body"><ReviewLedger key={`${official.sessionId}:${official.path}`} sessionId={official.sessionId} first={official.page} path={official.path} /></div> : null}
124
113
  </div>
125
114
  }
@@ -3,6 +3,7 @@ import React from 'react'
3
3
  import { rpc } from '../rpc.js'
4
4
  import { openEditorView } from '../events.js'
5
5
  import { refreshReview } from '../state/reviewFeed.js'
6
+ import { PENDING_PREFIX } from '../../shared/pendingReview.js'
6
7
  import type { ReviewDetail, ReviewItem, ReviewPage } from '../../shared/review.js'
7
8
  const h = React.createElement
8
9
  /** @private @author ddj 2026年10月26号 @param props Latest first-page metadata. @returns Paginated rows independent of expanded historical patch. */
@@ -37,18 +38,27 @@ export function ReviewLedger(props: { sessionId: string; first: ReviewPage | nul
37
38
  else setError(result.error)
38
39
  } catch (cause) { if (!controller.signal.aborted) setError(String(cause)) }
39
40
  }
41
+ const gaps = page?.gaps ?? []
40
42
  return h('div', { style: { marginTop: 8 } },
41
43
  h('label', null, h('input', { type: 'checkbox', checked: history, onChange: () => { setHistory(!history); setCursor(0) } }), '显示已处理及被替换的历史'),
42
- page?.gaps.map((gap) => h('div', { key: `${gap.sessionId}:${gap.seq}`, role: 'status' }, `轮次 ${gap.turn} 采集不完整:${gap.error}`)),
43
44
  error ? h('div', { role: 'alert' }, error) : null,
44
45
  !page ? h('p', { role: 'status' }, '正在读取官方审查记录…') : null,
45
46
  page && !page.items.length ? h('p', null, history ? '暂无已落盘历史。' : '没有待处理的官方记录;可切换查看历史。') : null,
46
47
  h('div', { style: { maxHeight: 210, overflow: 'auto' } }, page?.items.map((item) => h('button', {
47
48
  key: item.id, type: 'button', onClick: () => { void select(item) },
48
- style: { display: 'block', width: '100%', textAlign: 'left', padding: '6px 4px' },
49
- }, `轮次 ${item.turn} · ${item.display} +${item.added} −${item.deleted} · ${item.decision}${item.current ? '' : '(已被同轮新版本替换)'}`))),
49
+ title: item.currentPath || item.path,
50
+ style: { display: 'block', width: '100%', textAlign: 'left', padding: '6px 4px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' },
51
+ }, item.id.startsWith(PENDING_PREFIX)
52
+ ? `【待审】${item.display} +${item.added} −${item.deleted} · ${item.decision}${item.kind === 'oversized' ? ' · 过大' : ''}`
53
+ : `轮次 ${item.turn} · ${item.display} +${item.added} −${item.deleted} · ${item.decision}${item.current ? '' : '(已被同轮新版本替换)'}`))),
50
54
  cursor > 0 ? h('button', { type: 'button', onClick: () => setCursor(Math.max(0, cursor - 50)) }, '上一页') : null,
51
55
  page?.nextCursor !== null && page?.nextCursor !== undefined ? h('button', { type: 'button', onClick: () => setCursor(page.nextCursor!) }, '下一页') : null,
56
+ // 采集缺口是诊断信息,排在条目之后折叠起来:放最前面会把真正要点的待审条目挤出可视区。
57
+ gaps.length
58
+ ? h('details', { key: 'gaps', style: { marginTop: 6 } },
59
+ h('summary', { style: { cursor: 'pointer', fontSize: 11 } }, `采集不完整 ${gaps.length} 条(诊断信息)`),
60
+ gaps.map((gap) => h('div', { key: `${gap.sessionId}:${gap.seq}`, role: 'status', style: { fontSize: 11 } }, `轮次 ${gap.turn} 采集不完整:${gap.error}`)))
61
+ : null,
52
62
  detail ? h(PatchDetail, { key: detail.item.id, sessionId: props.sessionId, detail, onChanged: () => { setDetail(null); refreshReview(props.sessionId) } }) : null)
53
63
  }
54
64