🐛 fix(vscode): 改动 tab 默认统一视图并保留阅读位置

This commit is contained in:
cruldra committed 2026-07-25 11:08:48 +08:00
1 parent 84a5cda8d2
commit d68603ee06
9 files changed
+213 -20

No files matched your search

@@ -6,13 +6,14 @@
* - 主题跟随 VS Code(body 的 vscode-light/dark class),MutationObserver 监听切换。
* - 语法高亮用库自带(diffViewHighlight),色彩取库的 light/dark 主题——不会和 VS Code
* 主题 100% 一致,是本方案已知且可接受的小瑕疵。
* - 分栏/统一模式由父级持有(workspace 级);本组件只按 path 记 scrollTop,换文件可回位。
*/
import type { FileDiff } from '../../hooks/usePrFiles'
import { generateDiffFile } from '@git-diff-view/file'
import { DiffModeEnum, DiffView } from '@git-diff-view/react'
import { Check, Loader2, Sparkles } from 'lucide-react'
import { useEffect, useMemo, useState } from 'react'
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { statusBadge, statusColorVar } from './fileTree'
import '@git-diff-view/react/styles/diff-view.css'
@@ -33,6 +34,10 @@ interface FileDiffPaneProps {
generating: boolean
/** 触发 deepseek 生成当前文件说明。 */
onGenerate: () => void
/** 统一 / 分栏;由父级从 workspaceState 拉并持久化。 */
mode: DiffModeEnum
/** 用户切换模式。 */
onModeChange: (mode: DiffModeEnum) => void
}
/** 读当前 VS Code 主题明暗:body 带 vscode-light / vscode-high-contrast-light 即浅色,否则深色。 */
@@ -53,9 +58,22 @@ function Centered({ children }: { children: React.ReactNode }) {
)
}
export function FileDiffPane({ path, status, diff, confirmed, onToggleConfirm, summary, generating, onGenerate }: FileDiffPaneProps) {
const [mode, setMode] = useState<DiffModeEnum>(DiffModeEnum.Split)
export function FileDiffPane({
path,
status,
diff,
confirmed,
onToggleConfirm,
summary,
generating,
onGenerate,
mode,
onModeChange,
}: FileDiffPaneProps) {
const [theme, setTheme] = useState<'light' | 'dark'>(() => readThemeKind())
const scrollRef = useRef<HTMLDivElement | null>(null)
// 按文件路径记 scrollTop;随组件生命周期(key=issueNumber)重置。
const scrollByPath = useRef<Record<string, number>>({})
// 主题随 VS Code 实时切换。
useEffect(() => {
@@ -88,6 +106,20 @@ export function FileDiffPane({ path, status, diff, confirmed, onToggleConfirm, s
return file
}, [path, diff, mode, theme])
// path / 内容就绪后恢复该文件上次的滚动位置;DiffView 异步出高时再 rAF 补一次。
useLayoutEffect(() => {
const el = scrollRef.current
if (!el || !path)
return
const top = scrollByPath.current[path] ?? 0
el.scrollTop = top
const id = requestAnimationFrame(() => {
if (scrollRef.current)
scrollRef.current.scrollTop = top
})
return () => cancelAnimationFrame(id)
}, [path, diffFile])
if (!path)
return <Centered>选择左侧文件查看改动</Centered>
@@ -141,7 +173,7 @@ export function FileDiffPane({ path, status, diff, confirmed, onToggleConfirm, s
<button
key={label}
type="button"
onClick={() => setMode(m)}
onClick={() => onModeChange(m)}
className={`px-1.5 py-0.5 transition-colors ${
mode === m
? 'bg-[var(--vscode-button-background)] text-[var(--vscode-button-foreground)]'
@@ -160,8 +192,15 @@ export function FileDiffPane({ path, status, diff, confirmed, onToggleConfirm, s
)}
</div>
{/* 内容区 */}
<div className="min-h-0 flex-1 overflow-auto bg-[var(--vscode-editor-background)] text-xs">
{/* 内容区:按 path 记 scrollTop,换文件再回来可恢复 */}
<div
ref={scrollRef}
className="min-h-0 flex-1 overflow-auto bg-[var(--vscode-editor-background)] text-xs"
onScroll={(e) => {
if (path)
scrollByPath.current[path] = e.currentTarget.scrollTop
}}
>
{diff === undefined
? <Centered>加载中…</Centered>
: diff.error