| ... | @@ -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"; |
| 16 | import { Badge } from "~/components/ui/badge"; | | |
| 17 | import { Button } from "~/components/ui/button"; | 16 | import { Button } from "~/components/ui/button"; |
| 18 | import { | 17 | import { |
| 19 | Dialog, | 18 | Dialog, |
| ... | @@ -27,6 +26,7 @@ import { | ... | @@ -27,6 +26,7 @@ import { |
| 27 | import { Group, GroupSeparator } from "~/components/ui/group"; | 26 | import { Group, GroupSeparator } from "~/components/ui/group"; |
| 28 | import { Menu, MenuItem, MenuPopup, MenuTrigger } from "~/components/ui/menu"; | 27 | import { Menu, MenuItem, MenuPopup, MenuTrigger } from "~/components/ui/menu"; |
| 29 | import { Popover, PopoverPopup, PopoverTrigger } from "~/components/ui/popover"; | 28 | import { Popover, PopoverPopup, PopoverTrigger } from "~/components/ui/popover"; |
| | 29 | import { ScrollArea } from "~/components/ui/scroll-area"; |
| 30 | import { Textarea } from "~/components/ui/textarea"; | 30 | import { Textarea } from "~/components/ui/textarea"; |
| 31 | import { toastManager } from "~/components/ui/toast"; | 31 | import { toastManager } from "~/components/ui/toast"; |
| 32 | import { | 32 | import { |
| ... | @@ -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 | } |
| 105 | | 105 | |
| 106 | function 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 | | | |
| 120 | function GitActionItemIcon({ icon }: { icon: GitActionIconName }) { | 106 | function 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 | : shouldOfferOpenPrCta | 282 | : 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 | ); |
| 380 | | 370 | |
| 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; | | |
| 432 | | 420 | |
| 433 | if (!gitCwd) return null; | 421 | if (!gitCwd) return null; |
| 434 | | 422 | |
| ... | @@ -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 | {gitStatus | 573 | {!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 | <button | 613 | <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} more | 629 | </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> |