mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
* feat(app): add fileView preference for changes panel * feat(app): add buildDiffTree util for changed-files tree * feat(app): add changed-files tree directory row * feat(app): changed-files tree view mode in changes panel (#117) * feat(protocol): add checkout.commits.list RPC + commitsList feature flag * feat(server): list branch commits ahead of base with on-remote flags * feat(server): handle checkout.commits.list RPC and advertise capability * feat(client): checkout.commits.list method + useCommitsQuery hook * feat(app): per-commit inline view with local-vs-remote markers (#117) * feat(protocol,server): per-commit file diff RPC (checkout.commits.file_diff) * feat(app): open per-commit file diff on click (#117) * refactor: surface baseRef + commits loading/error, drop dead depth field * fix(app): flip commit local/remote dot — local hollow, remote filled * feat(app): list/tree view for expanded commit file list (#117) * fix(app): syntax-highlight per-commit file diff to match Changes view * feat(app): draggable resize between commits and diff sections * refactor(app): dedupe wrap-text helpers into diff-highlighted-text * fix(app): clean up commits resize drag on unmount + a11y label * fix(app): collapse commits section by default * fix(app): render per-commit file diff with the shared Changes line renderer * perf(app): memoize shared diff line row; drop redundant DiffLineView wrapper * refactor(app): extract shared DiffFileBody; render commit file diff through it * fix(app): hide inline-comment affordance in per-commit diffs (no reviewActions) * feat(app): move commits to a resizable bottom drawer in the Changes panel * feat(review): commitSha scoping for per-commit review drafts + attachment * feat(app): per-commit inline comments wired through to the composer (#117) * refactor(app): own commit file-diff open state in CommitFileList (drop reset effect) * refactor(app): colocate diff-render cluster under git/diff-file-body/ * feat(app): add diff tab target kind (working/commit diff tabs) * feat(app): useDiffFiles hook unifying working + commit diff targets * feat(app): diff tab panel rendering working/commit diffs * feat(app): open a commit diff tab on commit click; drop per-commit drawer/file list * feat(app): open/scroll working diff tab on changed-file click * fix(app): keep commit diff tabs ephemeral; align working diff whitespace * feat(app): collapsible file sections in the diff tab, collapsed by default * feat(app): make the on-remote commit dot more subtle Dims the filled green remote dot (row + legend) to ~0.55 opacity so the local-only ring stays the state that draws the eye. * Reshape commit diffs around the existing Changes view * Fix diff tab migration complexity after rebase * Address diff tab review findings * Clean up diff tab review interfaces * Collapse commit diffs into the existing view * Load commits when expanded --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
118 lines
4.0 KiB
TypeScript
118 lines
4.0 KiB
TypeScript
import { useMemo } from "react";
|
|
import type { CheckoutCommitFile, ParsedDiffFile } from "@getpaseo/protocol/messages";
|
|
import { useFetchQueries } from "@/data/query";
|
|
import { checkoutCommitFileDiffQueryKey, COMMIT_FILE_DIFF_STALE_TIME } from "@/git/query-keys";
|
|
import { useCheckoutCommitsQuery } from "@/git/use-commits-query";
|
|
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
|
|
|
/**
|
|
* Context needed to resolve a commit diff against a host: which daemon
|
|
* (`serverId`), which checkout (`cwd`), and which commit (`sha`). `enabled`
|
|
* lets the consumer pause all fetching (e.g. an inactive tab).
|
|
*/
|
|
export interface CommitDiffFilesContext {
|
|
serverId: string;
|
|
cwd: string;
|
|
sha: string;
|
|
enabled?: boolean;
|
|
}
|
|
|
|
export interface CommitDiffFilesResult {
|
|
files: ParsedDiffFile[];
|
|
isLoading: boolean;
|
|
error: Error | null;
|
|
capabilityMissing: boolean;
|
|
}
|
|
|
|
export function resolveCommitDiffFile(
|
|
file: CheckoutCommitFile,
|
|
resolved: ParsedDiffFile | null | undefined,
|
|
): ParsedDiffFile | null {
|
|
if (resolved !== undefined && resolved !== null) {
|
|
return resolved;
|
|
}
|
|
if (resolved === undefined) {
|
|
return null;
|
|
}
|
|
return {
|
|
path: file.path,
|
|
isNew: file.status === "added",
|
|
isDeleted: file.status === "deleted",
|
|
additions: file.additions,
|
|
deletions: file.deletions,
|
|
hunks: [],
|
|
status: "binary",
|
|
};
|
|
}
|
|
|
|
export function resolveCommitDiffFiles(
|
|
files: CheckoutCommitFile[],
|
|
resolvedByPath: ReadonlyMap<string, ParsedDiffFile | null | undefined>,
|
|
): ParsedDiffFile[] {
|
|
return files.flatMap((file) => {
|
|
const resolved = resolveCommitDiffFile(file, resolvedByPath.get(file.path));
|
|
return resolved ? [resolved] : [];
|
|
});
|
|
}
|
|
|
|
export function useCommitDiffFiles(ctx: CommitDiffFilesContext): CommitDiffFilesResult {
|
|
const { serverId, cwd, sha, enabled = true } = ctx;
|
|
const client = useHostRuntimeClient(serverId);
|
|
const isConnected = useHostRuntimeIsConnected(serverId);
|
|
const commitsQuery = useCheckoutCommitsQuery({ serverId, cwd, enabled });
|
|
const commitsData = commitsQuery.status === "loaded" ? commitsQuery.data : null;
|
|
const commitFiles = useMemo(() => {
|
|
if (!sha || !commitsData) {
|
|
return [];
|
|
}
|
|
return commitsData.commits.find((commit) => commit.sha === sha)?.files ?? [];
|
|
}, [commitsData, sha]);
|
|
|
|
const fileDiffsEnabled =
|
|
enabled &&
|
|
commitsQuery.status === "loaded" &&
|
|
Boolean(cwd) &&
|
|
Boolean(sha) &&
|
|
Boolean(client) &&
|
|
isConnected;
|
|
const fileDiffResults = useFetchQueries(
|
|
commitFiles.map((file) => ({
|
|
queryKey: checkoutCommitFileDiffQueryKey(serverId, cwd, sha, file.path),
|
|
queryFn: async (): Promise<{ file: ParsedDiffFile | null }> => {
|
|
if (!client) {
|
|
throw new Error("Host disconnected");
|
|
}
|
|
return client.getCommitFileDiff(cwd, sha, file.path);
|
|
},
|
|
enabled: fileDiffsEnabled,
|
|
staleTimeMs: COMMIT_FILE_DIFF_STALE_TIME,
|
|
dataShape: "value" as const,
|
|
})),
|
|
);
|
|
const commitsLoading = commitsQuery.status === "connecting" || commitsQuery.status === "loading";
|
|
const commitsError = commitsQuery.status === "error" ? commitsQuery.error : null;
|
|
const capabilityMissing = commitsQuery.status === "unsupported";
|
|
|
|
return useMemo<CommitDiffFilesResult>(() => {
|
|
const resolvedByPath = new Map<string, ParsedDiffFile | null | undefined>();
|
|
commitFiles.forEach((file, index) => {
|
|
const fileResult = fileDiffResults[index];
|
|
resolvedByPath.set(file.path, fileResult?.data ? fileResult.data.file : undefined);
|
|
});
|
|
const files = resolveCommitDiffFiles(commitFiles, resolvedByPath);
|
|
let firstFileError: Error | null = null;
|
|
for (const fileResult of fileDiffResults) {
|
|
if (fileResult.error) {
|
|
firstFileError = fileResult.error;
|
|
break;
|
|
}
|
|
}
|
|
return {
|
|
files,
|
|
isLoading: commitsLoading || fileDiffResults.some((r) => r.isLoading),
|
|
error: commitsError ?? firstFileError,
|
|
capabilityMissing,
|
|
};
|
|
}, [capabilityMissing, commitFiles, commitsError, commitsLoading, fileDiffResults]);
|
|
}
|