254 lines · 6.7 KB · TypeScript
RawDownload
import type {  GitRunStackedActionResult,  GitStackedAction,  GitStatusResult,} from "@t3tools/contracts"; export type GitActionIconName = "commit" | "push" | "pr"; export type GitDialogAction = "commit" | "push" | "create_pr"; export interface GitActionMenuItem {  id: "commit" | "push" | "pr";  label: string;  disabled: boolean;  icon: GitActionIconName;  kind: "open_dialog" | "open_pr";  dialogAction?: GitDialogAction;} export interface GitQuickAction {  label: string;  disabled: boolean;  kind: "run_action" | "run_pull" | "open_pr" | "show_hint";  action?: GitStackedAction;  hint?: string;} const SHORT_SHA_LENGTH = 7;const TOAST_DESCRIPTION_MAX = 72; function shortenSha(sha: string | undefined): string | null {  if (!sha) return null;  return sha.slice(0, SHORT_SHA_LENGTH);} function truncateText(  value: string | undefined,  maxLength = TOAST_DESCRIPTION_MAX,): string | undefined {  if (!value) return undefined;  if (value.length <= maxLength) return value;  if (maxLength <= 3) return "...".slice(0, maxLength);  return `${value.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;} export function buildGitActionProgressStages(input: {  action: GitStackedAction;  hasCustomCommitMessage: boolean;  hasWorkingTreeChanges: boolean;  forcePushOnly?: boolean;  pushTarget?: string;}): string[] {  const shouldIncludeCommitStages =    !input.forcePushOnly && (input.action === "commit" || input.hasWorkingTreeChanges);  const commitStages = !shouldIncludeCommitStages    ? []    : input.hasCustomCommitMessage      ? ["Committing..."]      : ["Generating commit message...", "Committing..."];  const pushStage = input.pushTarget ? `Pushing to ${input.pushTarget}...` : "Pushing...";  if (input.action === "commit") {    return commitStages;  }  if (input.action === "commit_push") {    return [...commitStages, pushStage];  }  return [...commitStages, pushStage, "Creating PR..."];} const withDescription = (title: string, description: string | undefined) =>  description ? { title, description } : { title }; export function summarizeGitResult(result: GitRunStackedActionResult): {  title: string;  description?: string;} {  if (result.pr.status === "created" || result.pr.status === "opened_existing") {    const prNumber = result.pr.number ? ` #${result.pr.number}` : "";    const title = `${result.pr.status === "created" ? "Created PR" : "Opened PR"}${prNumber}`;    return withDescription(title, truncateText(result.pr.title));  }   if (result.push.status === "pushed") {    const shortSha = shortenSha(result.commit.commitSha);    const branch = result.push.upstreamBranch ?? result.push.branch;    const pushedCommitPart = shortSha ? ` ${shortSha}` : "";    const branchPart = branch ? ` to ${branch}` : "";    return withDescription(      `Pushed${pushedCommitPart}${branchPart}`,      truncateText(result.commit.subject),    );  }   if (result.commit.status === "created") {    const shortSha = shortenSha(result.commit.commitSha);    const title = shortSha ? `Committed ${shortSha}` : "Committed changes";    return withDescription(title, truncateText(result.commit.subject));  }   return { title: "Done" };} export function buildMenuItems(  gitStatus: GitStatusResult | null,  isBusy: boolean,): GitActionMenuItem[] {  if (!gitStatus) return [];   const hasBranch = gitStatus.branch !== null;  const hasChanges = gitStatus.hasWorkingTreeChanges;  const hasOpenPr = gitStatus.pr?.state === "open";  const isBehind = gitStatus.behindCount > 0;  const canCommit = !isBusy && hasChanges;  const canPush = !isBusy && hasBranch && !hasChanges && !isBehind && gitStatus.aheadCount > 0;  const canCreatePr =    !isBusy &&    hasBranch &&    !hasChanges &&    !hasOpenPr &&    gitStatus.aheadCount > 0 &&    gitStatus.hasUpstream &&    !isBehind;  const canOpenPr = !isBusy && hasOpenPr;   return [    {      id: "commit",      label: "Commit",      disabled: !canCommit,      icon: "commit",      kind: "open_dialog",      dialogAction: "commit",    },    {      id: "push",      label: "Push",      disabled: !canPush,      icon: "push",      kind: "open_dialog",      dialogAction: "push",    },    hasOpenPr      ? {          id: "pr",          label: "Open PR",          disabled: !canOpenPr,          icon: "pr",          kind: "open_pr",        }      : {          id: "pr",          label: "Create PR",          disabled: !canCreatePr,          icon: "pr",          kind: "open_dialog",          dialogAction: "create_pr",        },  ];} export function resolveQuickAction(  gitStatus: GitStatusResult | null,  isBusy: boolean,  isDefaultBranch = false,): GitQuickAction {  if (isBusy) {    return { label: "Commit", disabled: true, kind: "show_hint", hint: "Git action in progress." };  }   if (!gitStatus) {    return {      label: "Commit",      disabled: true,      kind: "show_hint",      hint: "Git status is unavailable.",    };  }   const hasBranch = gitStatus.branch !== null;  const hasChanges = gitStatus.hasWorkingTreeChanges;  const hasOpenPr = gitStatus.pr?.state === "open";  const isAhead = gitStatus.aheadCount > 0;  const isBehind = gitStatus.behindCount > 0;  const isDiverged = isAhead && isBehind;   if (!hasBranch) {    return {      label: "Commit",      disabled: true,      kind: "show_hint",      hint: "Create and checkout a branch before pushing or opening a PR.",    };  }   if (hasChanges) {    if (hasOpenPr || isDefaultBranch) {      return { label: "Commit & push", disabled: false, kind: "run_action", action: "commit_push" };    }    return {      label: "Commit, push & create PR",      disabled: false,      kind: "run_action",      action: "commit_push_pr",    };  }   if (isDiverged) {    return {      label: "Sync branch",      disabled: true,      kind: "show_hint",      hint: "Branch has diverged from upstream. Rebase/merge first.",    };  }   if (isBehind) {    return {      label: "Pull",      disabled: false,      kind: "run_pull",    };  }   if (isAhead) {    if (hasOpenPr || isDefaultBranch) {      return { label: "Push", disabled: false, kind: "run_action", action: "commit_push" };    }    return {      label: "Push & create PR",      disabled: false,      kind: "run_action",      action: "commit_push_pr",    };  }   if (hasOpenPr && gitStatus.hasUpstream) {    return { label: "Open PR", disabled: false, kind: "open_pr" };  }   return {    label: "Commit",    disabled: true,    kind: "show_hint",    hint: "Branch is up to date. No action needed.",  };} export function requiresDefaultBranchConfirmation(  action: GitStackedAction,  isDefaultBranch: boolean,): boolean {  if (!isDefaultBranch) return false;  return action === "commit_push" || action === "commit_push_pr";}