/** * The diff tab: one change opened from the changes tab, like VSCode's diff * editor. A worktree ref loads the file's unified diff (`git diff`, staged or * not; untracked files — which git diff never covers — render as a full-file * addition from their content), a commit ref loads the commit's full patch * (`git.show`-style). The header carries a refresh button because the tab * stays mounted while the changes tab's staging/discard operations change the * very content it shows. Loading IS the shared `useGitDiffTarget` hook (the * changes tab's inline preview runs the same one, so the staged-side and * untracked fallbacks and the per-file fold cache cannot drift apart); * rendering goes through the shared {@link DiffFiles} renderer. */ import { useMemo } from 'react' import { IconRefreshOutlineRegular } from '@deepseek-ai/dsh-client-ui-primitives' import type { SessionScope } from './api.ts' import type { SidebarDiffRef } from './state.ts' import { DiffFiles } from './diff/DiffFiles.tsx' import { useGitDiffTarget } from './diff/use-git-diff.ts' import { t } from './locales.ts' import css from './sidebar.module.css' export function DiffTab(props: { sessionId: string; cwd: string | undefined; diff: SidebarDiffRef }) { const { sessionId, cwd, diff } = props const scope = useMemo(() => ({ sessionId, cwd }), [sessionId, cwd]) const { loading, error, diffText, untracked, refresh, resolveFold } = useGitDiffTarget(diff, scope) return (
{diff.kind === 'worktree' ? diff.path : `${diff.hash} ${diff.subject}`}
{loading &&
{t('loading')}
} {!loading && error !== null &&
{t('diffLoadError')}: {error}
} {!loading && error === null && diffText !== null && ( <> {untracked !== undefined ? : } {diffText === '' && untracked === undefined && (
{t('diffEmpty')}
)} )}
) }