| author | |
| committer | |
| log | 78fabcfde9576fbcca2133d0ec173485ba879bc0 |
| tree | 6b7be11788a67a183a154b8c858d458f76d7bde6 |
| parent | 7690837f38c56b79d26f61b16fc60ad671dcc1ee |
- replace generic success text with action-specific toast titles/descriptions
- include short SHA/branch details for push and commit outcomes
- add tests for PR/commit/push summaries and description truncation3 files changed, 131 insertions(+), 16 deletions(-)
apps/web/src/components/GitActionsControl.logic.test.ts+91| ... | @@ -4,6 +4,7 @@ import { | ... | @@ -4,6 +4,7 @@ import { |
| 4 | buildMenuItems, | 4 | buildMenuItems, |
| 5 | requiresDefaultBranchConfirmation, | 5 | requiresDefaultBranchConfirmation, |
| 6 | resolveQuickAction, | 6 | resolveQuickAction, |
| 7 | summarizeGitResult, | ||
| 7 | } from "./GitActionsControl.logic"; | 8 | } from "./GitActionsControl.logic"; |
| 8 | 9 | ||
| 9 | function status(overrides: Partial<GitStatusResult> = {}): GitStatusResult { | 10 | function status(overrides: Partial<GitStatusResult> = {}): GitStatusResult { |
| ... | @@ -577,3 +578,93 @@ describe("requiresDefaultBranchConfirmation", () => { | ... | @@ -577,3 +578,93 @@ describe("requiresDefaultBranchConfirmation", () => { |
| 577 | assert.isFalse(requiresDefaultBranchConfirmation("commit_push", false)); | 578 | assert.isFalse(requiresDefaultBranchConfirmation("commit_push", false)); |
| 578 | }); | 579 | }); |
| 579 | }); | 580 | }); |
| 581 | |||
| 582 | describe("summarizeGitResult", () => { | ||
| 583 | it("returns commit-focused toast for commit action", () => { | ||
| 584 | const result = summarizeGitResult({ | ||
| 585 | action: "commit", | ||
| 586 | commit: { | ||
| 587 | status: "created", | ||
| 588 | commitSha: "0123456789abcdef", | ||
| 589 | subject: "feat: add optimistic UI for git action button", | ||
| 590 | }, | ||
| 591 | push: { status: "skipped_not_requested" }, | ||
| 592 | pr: { status: "skipped_not_requested" }, | ||
| 593 | }); | ||
| 594 | |||
| 595 | assert.deepEqual(result, { | ||
| 596 | title: "Committed 0123456", | ||
| 597 | description: "feat: add optimistic UI for git action button", | ||
| 598 | }); | ||
| 599 | }); | ||
| 600 | |||
| 601 | it("returns push-focused toast for push action", () => { | ||
| 602 | const result = summarizeGitResult({ | ||
| 603 | action: "commit_push", | ||
| 604 | commit: { | ||
| 605 | status: "created", | ||
| 606 | commitSha: "abcdef0123456789", | ||
| 607 | subject: "fix: tighten quick action tooltip hover handling", | ||
| 608 | }, | ||
| 609 | push: { | ||
| 610 | status: "pushed", | ||
| 611 | branch: "foo", | ||
| 612 | upstreamBranch: "origin/foo", | ||
| 613 | }, | ||
| 614 | pr: { status: "skipped_not_requested" }, | ||
| 615 | }); | ||
| 616 | |||
| 617 | assert.deepEqual(result, { | ||
| 618 | title: "Pushed abcdef0 to foo", | ||
| 619 | description: "fix: tighten quick action tooltip hover handling", | ||
| 620 | }); | ||
| 621 | }); | ||
| 622 | |||
| 623 | it("returns PR-focused toast for created PR action", () => { | ||
| 624 | const result = summarizeGitResult({ | ||
| 625 | action: "commit_push_pr", | ||
| 626 | commit: { | ||
| 627 | status: "created", | ||
| 628 | commitSha: "89abcdef01234567", | ||
| 629 | subject: "feat: ship github shortcuts", | ||
| 630 | }, | ||
| 631 | push: { | ||
| 632 | status: "pushed", | ||
| 633 | branch: "foo", | ||
| 634 | }, | ||
| 635 | pr: { | ||
| 636 | status: "created", | ||
| 637 | number: 42, | ||
| 638 | title: "feat: ship github shortcuts and improve PR CTA in success toast", | ||
| 639 | }, | ||
| 640 | }); | ||
| 641 | |||
| 642 | assert.deepEqual(result, { | ||
| 643 | title: "Created PR #42", | ||
| 644 | description: "feat: ship github shortcuts and improve PR CTA in success toast", | ||
| 645 | }); | ||
| 646 | }); | ||
| 647 | |||
| 648 | it("truncates long description text", () => { | ||
| 649 | const result = summarizeGitResult({ | ||
| 650 | action: "commit_push_pr", | ||
| 651 | commit: { | ||
| 652 | status: "created", | ||
| 653 | commitSha: "89abcdef01234567", | ||
| 654 | subject: "short subject", | ||
| 655 | }, | ||
| 656 | push: { status: "pushed", branch: "foo" }, | ||
| 657 | pr: { | ||
| 658 | status: "created", | ||
| 659 | number: 99, | ||
| 660 | title: | ||
| 661 | "feat: this title is intentionally extremely long so we can validate that toast descriptions are truncated with an ellipsis suffix", | ||
| 662 | }, | ||
| 663 | }); | ||
| 664 | |||
| 665 | assert.deepEqual(result, { | ||
| 666 | title: "Created PR #99", | ||
| 667 | description: "feat: this title is intentionally extremely long so we can validate t...", | ||
| 668 | }); | ||
| 669 | }); | ||
| 670 | }); |
apps/web/src/components/GitActionsControl.logic.ts+36-13| ... | @@ -21,28 +21,51 @@ export interface GitQuickAction { | ... | @@ -21,28 +21,51 @@ export interface GitQuickAction { |
| 21 | hint?: string; | 21 | hint?: string; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | export function describeGitResult(result: GitRunStackedActionResult): string { | 24 | const SHORT_SHA_LENGTH = 7; |
| 25 | const parts: string[] = []; | 25 | const TOAST_DESCRIPTION_MAX = 72; |
| 26 | 26 | ||
| 27 | if (result.commit.status === "created") { | 27 | function shortenSha(sha: string | undefined): string | null { |
| 28 | parts.push(result.commit.subject ?? "Committed changes"); | 28 | if (!sha) return null; |
| 29 | return sha.slice(0, SHORT_SHA_LENGTH); | ||
| 30 | } | ||
| 31 | |||
| 32 | function truncateText(value: string | undefined, maxLength = TOAST_DESCRIPTION_MAX): string | undefined { | ||
| 33 | if (!value) return undefined; | ||
| 34 | if (value.length <= maxLength) return value; | ||
| 35 | if (maxLength <= 3) return "...".slice(0, maxLength); | ||
| 36 | return `${value.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`; | ||
| 37 | } | ||
| 38 | |||
| 39 | export function summarizeGitResult( | ||
| 40 | result: GitRunStackedActionResult, | ||
| 41 | ): { title: string; description?: string } { | ||
| 42 | const withDescription = (title: string, description: string | undefined) => | ||
| 43 | description ? { title, description } : { title }; | ||
| 44 | |||
| 45 | if (result.pr.status === "created" || result.pr.status === "opened_existing") { | ||
| 46 | const prNumber = result.pr.number ? ` #${result.pr.number}` : ""; | ||
| 47 | const title = `${result.pr.status === "created" ? "Created PR" : "Opened PR"}${prNumber}`; | ||
| 48 | return withDescription(title, truncateText(result.pr.title)); | ||
| 29 | } | 49 | } |
| 30 | 50 | ||
| 31 | if (result.push.status === "pushed") { | 51 | if (result.push.status === "pushed") { |
| 32 | parts.push( | 52 | const shortSha = shortenSha(result.commit.commitSha); |
| 33 | result.push.upstreamBranch | 53 | const branch = result.push.branch ?? result.push.upstreamBranch; |
| 34 | ? `Pushed to ${result.push.upstreamBranch}` | 54 | const pushedCommitPart = shortSha ? ` ${shortSha}` : ""; |
| 35 | : "Pushed", | 55 | const branchPart = branch ? ` to ${branch}` : ""; |
| 56 | return withDescription( | ||
| 57 | `Pushed${pushedCommitPart}${branchPart}`, | ||
| 58 | truncateText(result.commit.subject), | ||
| 36 | ); | 59 | ); |
| 37 | } | 60 | } |
| 38 | 61 | ||
| 39 | if (result.pr.status === "created") { | 62 | if (result.commit.status === "created") { |
| 40 | parts.push(result.pr.number ? `Created PR #${result.pr.number}` : "Created PR"); | 63 | const shortSha = shortenSha(result.commit.commitSha); |
| 41 | } else if (result.pr.status === "opened_existing") { | 64 | const title = shortSha ? `Committed ${shortSha}` : "Committed changes"; |
| 42 | parts.push(result.pr.number ? `PR #${result.pr.number}` : "Opened existing PR"); | 65 | return withDescription(title, truncateText(result.commit.subject)); |
| 43 | } | 66 | } |
| 44 | 67 | ||
| 45 | return parts.join(" · ") || "No changes needed."; | 68 | return { title: "Done" }; |
| 46 | } | 69 | } |
| 47 | 70 | ||
| 48 | export function buildMenuItems( | 71 | export function buildMenuItems( |
apps/web/src/components/GitActionsControl.tsx+4-3| ... | @@ -13,9 +13,9 @@ import { | ... | @@ -13,9 +13,9 @@ import { |
| 13 | type GitActionMenuItem, | 13 | type GitActionMenuItem, |
| 14 | type GitDialogAction, | 14 | type GitDialogAction, |
| 15 | type GitQuickAction, | 15 | type GitQuickAction, |
| 16 | describeGitResult, | ||
| 17 | requiresDefaultBranchConfirmation, | 16 | requiresDefaultBranchConfirmation, |
| 18 | resolveQuickAction, | 17 | resolveQuickAction, |
| 18 | summarizeGitResult, | ||
| 19 | } from "./GitActionsControl.logic"; | 19 | } from "./GitActionsControl.logic"; |
| 20 | import { Badge } from "~/components/ui/badge"; | 20 | import { Badge } from "~/components/ui/badge"; |
| 21 | import { Button } from "~/components/ui/button"; | 21 | import { Button } from "~/components/ui/button"; |
| ... | @@ -282,6 +282,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp | ... | @@ -282,6 +282,7 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp |
| 282 | try { | 282 | try { |
| 283 | const result = await promise; | 283 | const result = await promise; |
| 284 | stopProgressUpdates(); | 284 | stopProgressUpdates(); |
| 285 | const resultToast = summarizeGitResult(result); | ||
| 285 | 286 | ||
| 286 | const prUrl = result.pr.url ?? gitStatus?.openPr?.url; | 287 | const prUrl = result.pr.url ?? gitStatus?.openPr?.url; |
| 287 | const shouldOfferPushCta = action === "commit" && result.commit.status === "created"; | 288 | const shouldOfferPushCta = action === "commit" && result.commit.status === "created"; |
| ... | @@ -292,8 +293,8 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp | ... | @@ -292,8 +293,8 @@ export default function GitActionsControl({ api, gitCwd }: GitActionsControlProp |
| 292 | 293 | ||
| 293 | toastManager.update(progressToastId, { | 294 | toastManager.update(progressToastId, { |
| 294 | type: "success", | 295 | type: "success", |
| 295 | title: "Done", | 296 | title: resultToast.title, |
| 296 | description: describeGitResult(result), | 297 | description: resultToast.description, |
| 297 | timeout: 10_000, | 298 | timeout: 10_000, |
| 298 | ...(shouldOfferPushCta | 299 | ...(shouldOfferPushCta |
| 299 | ? { | 300 | ? { |