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 {...@@ -13,7 +13,6 @@ import {
13 resolveQuickAction,13 resolveQuickAction,
14 summarizeGitResult,14 summarizeGitResult,
15} from "./GitActionsControl.logic";15} from "./GitActionsControl.logic";
16import { Badge } from "~/components/ui/badge";
17import { Button } from "~/components/ui/button";16import { Button } from "~/components/ui/button";
18import {17import {
19 Dialog,18 Dialog,
...@@ -27,6 +26,7 @@ import {...@@ -27,6 +26,7 @@ import {
27import { Group, GroupSeparator } from "~/components/ui/group";26import { Group, GroupSeparator } from "~/components/ui/group";
28import { Menu, MenuItem, MenuPopup, MenuTrigger } from "~/components/ui/menu";27import { Menu, MenuItem, MenuPopup, MenuTrigger } from "~/components/ui/menu";
29import { Popover, PopoverPopup, PopoverTrigger } from "~/components/ui/popover";28import { Popover, PopoverPopup, PopoverTrigger } from "~/components/ui/popover";
29import { ScrollArea } from "~/components/ui/scroll-area";
30import { Textarea } from "~/components/ui/textarea";30import { Textarea } from "~/components/ui/textarea";
31import { toastManager } from "~/components/ui/toast";31import { toastManager } from "~/components/ui/toast";
32import {32import {
...@@ -103,20 +103,6 @@ function getMenuActionDisabledReason(...@@ -103,20 +103,6 @@ function getMenuActionDisabledReason(
103 return "Create PR is currently unavailable.";103 return "Create PR is currently unavailable.";
104}104}
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
120function GitActionItemIcon({ icon }: { icon: GitActionIconName }) {106function GitActionItemIcon({ icon }: { icon: GitActionIconName }) {
121 if (icon === "commit") return <GitCommitIcon />;107 if (icon === "commit") return <GitCommitIcon />;
122 if (icon === "push") return <CloudUploadIcon />;108 if (icon === "push") return <CloudUploadIcon />;
...@@ -207,7 +193,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -207,7 +193,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
207 return true;193 return true;
208 }194 }
209 return api.dialogs.confirm(195 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?`,
211 );197 );
212 },198 },
213 [api, gitStatus?.branch, isDefaultBranch],199 [api, gitStatus?.branch, isDefaultBranch],
...@@ -290,7 +276,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -290,7 +276,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
290 ? {276 ? {
291 actionProps: {277 actionProps: {
292 children: "Push",278 children: "Push",
293 onClick: () => setActiveDialogAction("push"),279 onClick: () => void runGitActionWithToast({ action: "commit_push" }),
294 },280 },
295 }281 }
296 : shouldOfferOpenPrCta282 : shouldOfferOpenPrCta
...@@ -371,11 +357,15 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -371,11 +357,15 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
371 void openExistingPr();357 void openExistingPr();
372 return;358 return;
373 }359 }
360 if (item.dialogAction === "push") {
361 void runGitActionWithToast({ action: "commit_push" });
362 return;
363 }
374 if (item.dialogAction) {364 if (item.dialogAction) {
375 setActiveDialogAction(item.dialogAction);365 setActiveDialogAction(item.dialogAction);
376 }366 }
377 },367 },
378 [openExistingPr],368 [openExistingPr, runGitActionWithToast],
379 );369 );
380370
381 const runDialogAction = useCallback(() => {371 const runDialogAction = useCallback(() => {
...@@ -425,10 +415,8 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -425,10 +415,8 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
425 activeDialogAction === "commit"415 activeDialogAction === "commit"
426 ? "Review and confirm your commit. Leave the message blank to auto-generate one."416 ? "Review and confirm your commit. Leave the message blank to auto-generate one."
427 : activeDialogAction === "push"417 : activeDialogAction === "push"
428 ? "Push this branch now. If this is the default branch, you'll be asked to confirm."418 ? "Push this branch now."
429 : "Create a pull request using generated title/body content.";419 : "Create a pull request using generated title/body content.";
430 const showCompactStatusCard =
431 !!gitStatus && gitStatus.hasWorkingTreeChanges && !gitStatus.hasUpstream;
432420
433 if (!gitCwd) return null;421 if (!gitCwd) return null;
434422
...@@ -578,17 +566,19 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -578,17 +566,19 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
578 <div className="grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-1">566 <div className="grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-1">
579 <span className="text-muted-foreground">Branch</span>567 <span className="text-muted-foreground">Branch</span>
580 <span className="font-medium">{gitStatus?.branch ?? "(detached HEAD)"}</span>568 <span className="font-medium">{gitStatus?.branch ?? "(detached HEAD)"}</span>
581 {!showCompactStatusCard && (569 {activeDialogAction !== "commit" && (
582 <>570 <>
583 <span className="text-muted-foreground">Upstream</span>571 <span className="text-muted-foreground">Upstream</span>
584 <span className="font-medium">572 <span className="font-medium">
585 {gitStatus573 {!gitStatus || !gitStatus.hasUpstream
586 ? formatUpstreamStatus({574 ? "No upstream configured"
587 hasUpstream: gitStatus.hasUpstream,575 : gitStatus.aheadCount === 0 && gitStatus.behindCount === 0
588 aheadCount: gitStatus.aheadCount,576 ? "Up to date"
589 behindCount: gitStatus.behindCount,577 : gitStatus.aheadCount > 0 && gitStatus.behindCount > 0
590 })578 ? `Diverged (+${gitStatus.aheadCount} / -${gitStatus.behindCount})`
591 : "unknown"}579 : gitStatus.aheadCount > 0
580 ? `Ahead by ${gitStatus.aheadCount}`
581 : `Behind by ${gitStatus.behindCount}`}
592 </span>582 </span>
593 <span className="text-muted-foreground">Working tree</span>583 <span className="text-muted-foreground">Working tree</span>
594 <span className="font-medium">584 <span className="font-medium">
...@@ -618,27 +608,31 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp...@@ -618,27 +608,31 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp
618 {!gitStatus || gitStatus.workingTree.files.length === 0 ? (608 {!gitStatus || gitStatus.workingTree.files.length === 0 ? (
619 <p className="font-medium">none</p>609 <p className="font-medium">none</p>
620 ) : (610 ) : (
621 <div className="space-y-1">611 <div className="space-y-2">
622 {gitStatus.workingTree.files.slice(0, 6).map((file) => (612 <ScrollArea className="h-44 rounded-md border border-input bg-background">
623 <button613 <div className="space-y-1 p-1">
624 type="button"614 {gitStatus.workingTree.files.map((file) => (
625 key={file.path}615 <button
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"616 type="button"
627 onClick={() => openChangedFileInEditor(file.path)}617 key={file.path}
628 >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"
629 <span className="truncate">{file.path}</span>619 onClick={() => openChangedFileInEditor(file.path)}
630 <span className="shrink-0">620 >
631 <span className="text-success">+{file.insertions}</span>621 <span className="truncate">{file.path}</span>
632 <span className="text-muted-foreground"> / </span>622 <span className="shrink-0">
633 <span className="text-destructive">-{file.deletions}</span>623 <span className="text-success">+{file.insertions}</span>
634 </span>624 <span className="text-muted-foreground"> / </span>
635 </button>625 <span className="text-destructive">-{file.deletions}</span>
636 ))}626 </span>
637 {gitStatus.workingTree.files.length > 6 && (627 </button>
638 <Badge variant="outline" size="sm">628 ))}
639 +{gitStatus.workingTree.files.length - 6} more629 </div>
640 </Badge>630 </ScrollArea>
641 )}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>
642 </div>636 </div>
643 )}637 )}
644 </div>638 </div>