authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-14 23:00:53-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-14 23:35:48-08:00
logc0bdd46ddd761a176de23c4ea926737e537fcfa9
tree801d96987b3cdbe5467517e883dd3fd5405c285e
parentc743239b5e83db7142cccf99dcacba7fd66b1d25

Streamline git action flow and improve changed-files dialog

- Trigger `commit_push` directly from primary Push actions - Simplify push confirmation copy and dialog descriptions - Replace 6-file cap with a scrollable full changed-files list plus totals

1 files changed, 44 insertions(+), 50 deletions(-)

apps/web/src/components/GitActionsControl.tsx+44-50
......@@ -13,7 +13,6 @@ import {
1313 resolveQuickAction,
1414 summarizeGitResult,
1515} from "./GitActionsControl.logic";
16import { Badge } from "~/components/ui/badge";
1716import { Button } from "~/components/ui/button";
1817import {
1918 Dialog,
......@@ -27,6 +26,7 @@ import {
2726import { Group, GroupSeparator } from "~/components/ui/group";
2827import { Menu, MenuItem, MenuPopup, MenuTrigger } from "~/components/ui/menu";
2928import { Popover, PopoverPopup, PopoverTrigger } from "~/components/ui/popover";
29import { ScrollArea } from "~/components/ui/scroll-area";
3030import { Textarea } from "~/components/ui/textarea";
3131import { toastManager } from "~/components/ui/toast";
3232import {
......@@ -103,20 +103,6 @@ function getMenuActionDisabledReason(
103103 return "Create PR is currently unavailable.";
104104}
105105
106function formatUpstreamStatus(input: {
107 hasUpstream: boolean;
108 aheadCount: number;
109 behindCount: number;
110}): string {
111 if (!input.hasUpstream) return "No upstream configured";
112 if (input.aheadCount === 0 && input.behindCount === 0) return "Up to date";
113 if (input.aheadCount > 0 && input.behindCount > 0) {
114 return `Diverged (+${input.aheadCount} / -${input.behindCount})`;
115 }
116 if (input.aheadCount > 0) return `Ahead by ${input.aheadCount}`;
117 return `Behind by ${input.behindCount}`;
118}
119
120106function GitActionItemIcon({ icon }: { icon: GitActionIconName }) {
121107 if (icon === "commit") return <GitCommitIcon />;
122108 if (icon === "push") return <CloudUploadIcon />;
......@@ -207,7 +193,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
207193 return true;
208194 }
209195 return api.dialogs.confirm(
210 `Push to default branch "${gitStatus.branch}"? This will update the shared base branch.`,
196 `You're about to push to the default branch "${gitStatus.branch}". Continue?`,
211197 );
212198 },
213199 [api, gitStatus?.branch, isDefaultBranch],
......@@ -290,7 +276,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
290276 ? {
291277 actionProps: {
292278 children: "Push",
293 onClick: () => setActiveDialogAction("push"),
279 onClick: () => void runGitActionWithToast({ action: "commit_push" }),
294280 },
295281 }
296282 : shouldOfferOpenPrCta
......@@ -371,11 +357,15 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
371357 void openExistingPr();
372358 return;
373359 }
360 if (item.dialogAction === "push") {
361 void runGitActionWithToast({ action: "commit_push" });
362 return;
363 }
374364 if (item.dialogAction) {
375365 setActiveDialogAction(item.dialogAction);
376366 }
377367 },
378 [openExistingPr],
368 [openExistingPr, runGitActionWithToast],
379369 );
380370
381371 const runDialogAction = useCallback(() => {
......@@ -425,10 +415,8 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
425415 activeDialogAction === "commit"
426416 ? "Review and confirm your commit. Leave the message blank to auto-generate one."
427417 : activeDialogAction === "push"
428 ? "Push this branch now. If this is the default branch, you'll be asked to confirm."
418 ? "Push this branch now."
429419 : "Create a pull request using generated title/body content.";
430 const showCompactStatusCard =
431 !!gitStatus && gitStatus.hasWorkingTreeChanges && !gitStatus.hasUpstream;
432420
433421 if (!gitCwd) return null;
434422
......@@ -578,17 +566,19 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
578566 <div className="grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-1">
579567 <span className="text-muted-foreground">Branch</span>
580568 <span className="font-medium">{gitStatus?.branch ?? "(detached HEAD)"}</span>
581 {!showCompactStatusCard && (
569 {activeDialogAction !== "commit" && (
582570 <>
583571 <span className="text-muted-foreground">Upstream</span>
584572 <span className="font-medium">
585 {gitStatus
586 ? formatUpstreamStatus({
587 hasUpstream: gitStatus.hasUpstream,
588 aheadCount: gitStatus.aheadCount,
589 behindCount: gitStatus.behindCount,
590 })
591 : "unknown"}
573 {!gitStatus || !gitStatus.hasUpstream
574 ? "No upstream configured"
575 : gitStatus.aheadCount === 0 && gitStatus.behindCount === 0
576 ? "Up to date"
577 : gitStatus.aheadCount > 0 && gitStatus.behindCount > 0
578 ? `Diverged (+${gitStatus.aheadCount} / -${gitStatus.behindCount})`
579 : gitStatus.aheadCount > 0
580 ? `Ahead by ${gitStatus.aheadCount}`
581 : `Behind by ${gitStatus.behindCount}`}
592582 </span>
593583 <span className="text-muted-foreground">Working tree</span>
594584 <span className="font-medium">
......@@ -618,27 +608,31 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
618608 {!gitStatus || gitStatus.workingTree.files.length === 0 ? (
619609 <p className="font-medium">none</p>
620610 ) : (
621 <div className="space-y-1">
622 {gitStatus.workingTree.files.slice(0, 6).map((file) => (
623 <button
624 type="button"
625 key={file.path}
626 className="flex w-full items-center justify-between gap-3 rounded-md border border-input bg-background px-2 py-1 font-mono text-left transition-colors hover:bg-accent/50"
627 onClick={() => openChangedFileInEditor(file.path)}
628 >
629 <span className="truncate">{file.path}</span>
630 <span className="shrink-0">
631 <span className="text-success">+{file.insertions}</span>
632 <span className="text-muted-foreground"> / </span>
633 <span className="text-destructive">-{file.deletions}</span>
634 </span>
635 </button>
636 ))}
637 {gitStatus.workingTree.files.length > 6 && (
638 <Badge variant="outline" size="sm">
639 +{gitStatus.workingTree.files.length - 6} more
640 </Badge>
641 )}
611 <div className="space-y-2">
612 <ScrollArea className="h-44 rounded-md border border-input bg-background">
613 <div className="space-y-1 p-1">
614 {gitStatus.workingTree.files.map((file) => (
615 <button
616 type="button"
617 key={file.path}
618 className="flex w-full items-center justify-between gap-3 rounded-md px-2 py-1 font-mono text-left transition-colors hover:bg-accent/50"
619 onClick={() => openChangedFileInEditor(file.path)}
620 >
621 <span className="truncate">{file.path}</span>
622 <span className="shrink-0">
623 <span className="text-success">+{file.insertions}</span>
624 <span className="text-muted-foreground"> / </span>
625 <span className="text-destructive">-{file.deletions}</span>
626 </span>
627 </button>
628 ))}
629 </div>
630 </ScrollArea>
631 <div className="flex justify-end font-mono">
632 <span className="text-success">+{gitStatus.workingTree.insertions}</span>
633 <span className="text-muted-foreground"> / </span>
634 <span className="text-destructive">-{gitStatus.workingTree.deletions}</span>
635 </div>
642636 </div>
643637 )}
644638 </div>