Files
paseo/packages/app/src/git/query-keys.ts
T
0d3b717cf3 Git commit history (#1534)
* 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>
2026-07-16 17:36:41 +02:00

118 lines
3.3 KiB
TypeScript

import type { Query, QueryClient } from "@tanstack/react-query";
import { prPaneTimelineQueryKind } from "./pull-request-panel/query-keys";
interface CheckoutQueryIdentity {
serverId: string;
cwd: string;
}
interface CheckoutQueryScope {
serverId: string;
cwd?: string;
}
type CheckoutQueryKey = readonly unknown[];
// A commit's file diff is immutable for a given sha+path, so every consumer
// can share the same long-lived cache policy.
export const COMMIT_FILE_DIFF_STALE_TIME = 5 * 60_000;
export function checkoutStatusQueryKey(serverId: string, cwd: string) {
return ["checkoutStatus", serverId, cwd] as const;
}
export function checkoutDiffQueryKey(
serverId: string,
cwd: string,
mode: "uncommitted" | "base",
baseRef?: string,
ignoreWhitespace?: boolean,
) {
return ["checkoutDiff", serverId, cwd, mode, baseRef ?? "", ignoreWhitespace === true] as const;
}
export function checkoutPrStatusQueryKey(serverId: string, cwd: string) {
return ["checkoutPrStatus", serverId, cwd] as const;
}
export function checkoutCommitsQueryKey(serverId: string, cwd: string) {
return ["checkoutCommits", serverId, cwd] as const;
}
export function checkoutCommitFileDiffQueryKey(
serverId: string,
cwd: string,
sha: string,
path: string,
) {
return ["checkoutCommitFileDiff", serverId, cwd, sha, path] as const;
}
export async function invalidateCheckoutGitQueriesForClient(
queryClient: QueryClient,
identity: CheckoutQueryIdentity,
) {
await Promise.all([
queryClient.invalidateQueries({
queryKey: checkoutStatusQueryKey(identity.serverId, identity.cwd),
}),
queryClient.invalidateQueries({
predicate: checkoutQueryPredicate("checkoutDiff", identity),
}),
queryClient.invalidateQueries({
predicate: checkoutQueryPredicate("checkoutPrStatus", identity),
}),
queryClient.invalidateQueries({
predicate: checkoutQueryPredicate(prPaneTimelineQueryKind, identity),
}),
]);
}
// checkoutDiff is excluded: diff queries are subscription-fed (queryFn: skipToken) and
// receive a fresh snapshot on every resubscribe, so invalidation cannot and need not
// refetch them.
export async function invalidateCheckoutGitQueriesForServer(
queryClient: QueryClient,
serverId: string,
) {
const kinds = ["checkoutStatus", "checkoutPrStatus", prPaneTimelineQueryKind];
await Promise.all(
kinds.map((kind) =>
queryClient.invalidateQueries({ predicate: checkoutQueryPredicate(kind, { serverId }) }),
),
);
}
export async function invalidatePrPaneTimelineForCheckout(
queryClient: QueryClient,
identity: CheckoutQueryIdentity,
) {
await queryClient.invalidateQueries({
predicate: checkoutQueryPredicate(prPaneTimelineQueryKind, identity),
});
}
function checkoutQueryPredicate(
queryKind: CheckoutQueryKey[0],
scope: CheckoutQueryScope,
): (query: Query) => boolean {
return (query) => {
const key = query.queryKey;
return (
isCheckoutQueryKey(key) &&
key[0] === queryKind &&
key[1] === scope.serverId &&
(scope.cwd === undefined || key[2] === scope.cwd)
);
};
}
function isCheckoutQueryKey(key: readonly unknown[]): key is CheckoutQueryKey {
return (
key.length >= 3 &&
typeof key[0] === "string" &&
typeof key[1] === "string" &&
typeof key[2] === "string"
);
}