| ... | ... | @@ -350,6 +350,20 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp |
| 350 | 350 | }); |
| 351 | 351 | }, [api, gitStatus?.openPr?.url]); |
| 352 | 352 | |
| 353 | const openPrFromResult = useCallback(() => { |
| 354 | if (!api) { |
| 355 | setGitModalError("Link opening is unavailable."); |
| 356 | return; |
| 357 | } |
| 358 | |
| 359 | const prUrl = gitModalResult?.pr.url ?? null; |
| 360 | if (!prUrl) return; |
| 361 | |
| 362 | void api.shell.openExternal(prUrl).catch((error) => { |
| 363 | setGitModalError(error instanceof Error ? error.message : "Unable to open PR link."); |
| 364 | }); |
| 365 | }, [api, gitModalResult?.pr.url]); |
| 366 | |
| 353 | 367 | const runGitAction = useCallback(async () => { |
| 354 | 368 | if (!api || !gitCwd || !isGitModalOpen) return; |
| 355 | 369 | if (!selectedGitModalActionOption || selectedGitModalActionOption.disabled) return; |
| ... | ... | @@ -398,12 +412,12 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp |
| 398 | 412 | |
| 399 | 413 | if (includeGeneratedCommitMessage) { |
| 400 | 414 | if (commitRun.commit.status === "created") { |
| 415 | if (commitRun.commit.subject) { |
| 416 | setGitModalCommitMessage(commitRun.commit.subject); |
| 417 | } |
| 401 | 418 | updateStep( |
| 402 | 419 | "generate", |
| 403 | 420 | "completed", |
| 404 | | commitRun.commit.subject |
| 405 | | ? `Generated: ${commitRun.commit.subject}` |
| 406 | | : "Generated commit message.", |
| 407 | 421 | ); |
| 408 | 422 | } else { |
| 409 | 423 | updateStep("generate", "skipped", "No local changes to commit."); |
| ... | ... | @@ -694,15 +708,20 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp |
| 694 | 708 | const borderClass = |
| 695 | 709 | index < gitModalActionOptions.length - 1 ? "border-b border-border/70" : ""; |
| 696 | 710 | const selected = option.action === gitModalSelectedAction && !option.disabled; |
| 711 | const disabled = option.disabled || isGitActionRunning; |
| 697 | 712 | |
| 698 | 713 | return ( |
| 699 | 714 | <button |
| 700 | 715 | key={option.action} |
| 701 | 716 | type="button" |
| 702 | 717 | className={`flex w-full items-start gap-3 px-4 py-3 text-left transition-colors duration-150 ${borderClass} ${ |
| 703 | | selected ? "bg-accent/55" : "bg-card/45 hover:bg-accent/35" |
| 704 | | } disabled:cursor-not-allowed disabled:bg-card/45 disabled:text-muted-foreground/65`} |
| 705 | | disabled={option.disabled || isGitActionRunning} |
| 718 | selected |
| 719 | ? "bg-accent/55" |
| 720 | : disabled |
| 721 | ? "bg-card/25" |
| 722 | : "bg-card/45 hover:bg-accent/35" |
| 723 | } disabled:cursor-not-allowed`} |
| 724 | disabled={disabled} |
| 706 | 725 | onClick={() => { |
| 707 | 726 | setGitModalSelectedAction(option.action); |
| 708 | 727 | }} |
| ... | ... | @@ -711,14 +730,20 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp |
| 711 | 730 | <GitActionIcon icon={option.icon} disabled={option.disabled} /> |
| 712 | 731 | </span> |
| 713 | 732 | <div className="min-w-0 flex-1"> |
| 714 | | <p className="text-sm text-foreground">{option.label}</p> |
| 733 | <p |
| 734 | className={`text-sm ${disabled ? "text-muted-foreground/60" : "text-foreground"}`} |
| 735 | > |
| 736 | {option.label} |
| 737 | </p> |
| 715 | 738 | {option.detail && ( |
| 716 | | <p className="mt-0.5 text-xs text-muted-foreground/70"> |
| 739 | <p |
| 740 | className={`mt-0.5 text-xs ${disabled ? "text-muted-foreground/55" : "text-muted-foreground/70"}`} |
| 741 | > |
| 717 | 742 | {option.detail} |
| 718 | 743 | </p> |
| 719 | 744 | )} |
| 720 | 745 | </div> |
| 721 | | {selected ? ( |
| 746 | {selected && !disabled ? ( |
| 722 | 747 | <CheckIcon className="mt-0.5 h-4 w-4 shrink-0 text-foreground" /> |
| 723 | 748 | ) : null} |
| 724 | 749 | </button> |
| ... | ... | @@ -731,9 +756,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp |
| 731 | 756 | const statusTextClass = |
| 732 | 757 | step.status === "failed" |
| 733 | 758 | ? "text-rose-500 dark:text-rose-300" |
| 734 | | : step.status === "completed" |
| 735 | | ? "text-emerald-600 dark:text-emerald-300" |
| 736 | | : "text-muted-foreground/70"; |
| 759 | : "text-muted-foreground/70"; |
| 737 | 760 | |
| 738 | 761 | return ( |
| 739 | 762 | <div |
| ... | ... | @@ -783,6 +806,16 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp |
| 783 | 806 | </div> |
| 784 | 807 | )} |
| 785 | 808 | <div className="mt-6 flex justify-end gap-2"> |
| 809 | {gitModalResult?.pr.url && ( |
| 810 | <button |
| 811 | type="button" |
| 812 | className="rounded-xl border border-border px-4 py-2 text-sm text-foreground transition-colors duration-150 hover:bg-accent disabled:cursor-not-allowed disabled:opacity-60" |
| 813 | onClick={openPrFromResult} |
| 814 | disabled={isGitActionRunning} |
| 815 | > |
| 816 | Open PR |
| 817 | </button> |
| 818 | )} |
| 786 | 819 | <button |
| 787 | 820 | type="button" |
| 788 | 821 | className="rounded-xl border border-border px-4 py-2 text-sm text-foreground transition-colors duration-150 hover:bg-accent disabled:cursor-not-allowed disabled:opacity-60" |