Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
# APP-3922: Create PR Dialog
|
||||
|
||||
## Summary
|
||||
|
||||
Add a "Create PR" dialog and a "Commit and create PR" flow to the code review panel, allowing users to create GitHub pull requests directly from the diff view. The dialog shows the branch name and a summary of all changes that will be included in the PR. The commit dialog gains a third intent option that chains commit → push → PR creation in one action.
|
||||
|
||||
## Problem
|
||||
|
||||
After APP-3920 (push/publish dialogs), the git operations button can reach the "Create PR" state (everything committed and pushed, no existing PR), but clicking it was a no-op. Similarly, the "Commit and create PR" dropdown option was stubbed out. Users had to leave the editor to create a PR on GitHub.
|
||||
|
||||
## Goals
|
||||
|
||||
- Provide a confirmation dialog before creating a PR that shows the target branch and aggregate change stats (files, additions, deletions).
|
||||
- Allow expanding the changes section to see per-file stats with additions/deletions.
|
||||
- Show loading state during PR creation with disabled controls.
|
||||
- On success, show a toast with an "Open PR" link and refresh the git operations button to show "PR #N".
|
||||
- On failure, close the dialog and surface a friendly error toast so the user can retry via the git operations button.
|
||||
- Add "Commit and create PR" as a third intent in the commit dialog, chaining commit → push → `gh pr create --fill` in one operation.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Editing PR title, body, reviewers, or labels (uses `gh pr create --fill` which derives title/body from commits).
|
||||
- Draft PR support.
|
||||
- Checking whether `gh` CLI is installed/authenticated before opening the dialog (TODO for follow-up).
|
||||
|
||||
## Figma
|
||||
|
||||
https://www.figma.com/design/T2CtyXgIdjtrLfC03K1n1H/Code-review-2.0?node-id=6138-21140&m=dev
|
||||
|
||||
## User Experience
|
||||
|
||||
### Opening the dialog
|
||||
|
||||
The PR dialog opens when the user clicks:
|
||||
- The "Create PR" primary action button (when in CreatePr mode — everything pushed, no existing PR, not on main).
|
||||
- "Create PR" from the git operations dropdown menu.
|
||||
|
||||
The "Commit and create PR" flow opens the commit dialog with the `CommitAndCreatePr` intent selected instead.
|
||||
|
||||
Only one dialog can be open at a time.
|
||||
|
||||
### Dialog layout
|
||||
|
||||
The dialog is a centered modal overlay (460px wide) with a blurred background. It contains:
|
||||
|
||||
1. **Header**: Title "Create pull request" and a close button (X, with "ESC" tooltip).
|
||||
2. **Branch section**: "Branch" label with a git branch icon and the current branch name.
|
||||
3. **Changes section**: A bordered card showing aggregate stats (file count, +additions in green, -deletions in red) with a chevron to expand/collapse the per-file list.
|
||||
4. **File list (expanded)**: Scrollable list (max 130px) of per-file rows showing filename, directory, and +/- stats. Files are loaded asynchronously when the dialog opens.
|
||||
5. **Footer**: Cancel button and "Create PR" primary button.
|
||||
|
||||
### Loading state
|
||||
|
||||
When the user clicks "Create PR":
|
||||
- The button label changes to "Creating…" and becomes disabled.
|
||||
- The cancel button becomes disabled.
|
||||
- The PR creation runs asynchronously via `gh pr create --fill`.
|
||||
|
||||
### Success
|
||||
|
||||
On success:
|
||||
- The dialog closes.
|
||||
- A toast appears: "PR successfully created." with an "Open PR" link that opens the PR URL in the browser.
|
||||
- PR info is refreshed, which updates the git operations button to show "PR #N".
|
||||
|
||||
### Error
|
||||
|
||||
On failure:
|
||||
- The dialog closes.
|
||||
- An ephemeral toast appears with a friendly error message mapped from the raw git / `gh` error (e.g. "GitHub CLI (gh) not installed. See https://cli.github.com/.", "Authentication failed. Check your Git credentials.", or the generic "Git operation failed." fallback).
|
||||
- The git operations button stays in `CreatePr` mode (since nothing changed), so the user can retry by clicking it again.
|
||||
|
||||
### Cancellation
|
||||
|
||||
The user can cancel via the Cancel button, the X button, or pressing ESC. Cancellation closes the dialog with no side effects.
|
||||
|
||||
### Commit and create PR flow
|
||||
|
||||
The "Commit and create PR" intent is shown in the commit dialog only when creating a PR would be meaningful — i.e. the branch has no existing PR and the user is not on the repo's main branch. In either of those cases the intent is hidden entirely (not just disabled); only "Commit" and "Commit and push" remain.
|
||||
|
||||
When the user selects "Commit and create PR" from the commit dialog:
|
||||
1. The commit executes first.
|
||||
2. On successful commit, the branch is pushed.
|
||||
3. On successful push, `gh pr create --fill` runs.
|
||||
4. On success, the commit dialog closes and the same "PR successfully created." toast with "Open PR" link appears.
|
||||
5. On failure at any stage, an error toast is shown.
|
||||
|
||||
The commit dialog shows "Committing and pushing…" during the operation. No separate PR dialog is shown for this flow.
|
||||
|
||||
### Changes section data
|
||||
|
||||
The changes section shows the diff between the base (main) branch and `origin/{current_branch}`. If the remote ref doesn't exist yet (branch not pushed), it falls back to diffing against HEAD. This represents what would actually be included in the PR.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. Clicking "Create PR" in the header opens a dialog showing the branch name and aggregate change stats.
|
||||
2. Expanding the changes section shows per-file +/- stats.
|
||||
3. Confirming PR creation shows a loading state, then closes the dialog and shows a success toast with "Open PR" link.
|
||||
4. If PR creation fails, the dialog closes and a friendly error toast is shown; the header button stays in `CreatePr` mode so the user can retry.
|
||||
5. "Commit and create PR" from the commit dialog chains commit → push → PR creation without opening the PR dialog.
|
||||
6. After a successful PR creation, the git operations button updates to show "PR #N".
|
||||
7. The dialog can be dismissed via Cancel, X, or ESC at any time (when not loading).
|
||||
|
||||
## Validation
|
||||
|
||||
- On a branch with everything pushed and no PR, click "Create PR", verify the dialog shows the correct branch and change stats.
|
||||
- Expand changes and verify per-file list loads with correct stats.
|
||||
- Confirm PR creation, verify loading state, success toast with link, and dialog dismissal.
|
||||
- Simulate a failure (e.g. `gh` not authenticated) and verify the dialog closes, an error toast appears with appropriate copy, and the header button still allows retrying.
|
||||
- Use "Commit and create PR" from the commit dialog and verify all three operations succeed with a single toast.
|
||||
- Cancel the dialog via each method (Cancel, X, ESC) and verify no operation is performed.
|
||||
- After PR creation, verify the header button shows "PR #N" and clicking it opens the PR URL.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Should we check for `gh` CLI availability/auth before opening the dialog?
|
||||
- Should we support editing the PR title/body instead of using `--fill`?
|
||||
- Should we support draft PRs?
|
||||
@@ -0,0 +1,110 @@
|
||||
# APP-3922: Create PR Dialog — Tech Spec
|
||||
## Problem
|
||||
APP-3920 unified commit, push, and publish into a single `GitDialog` view and collapsed the event contract to `Completed | Cancelled` (each mode owns its own toasts and error messaging). The git operations button can reach `PrimaryGitActionMode::CreatePr` (everything pushed, no PR, not on main), but `OpenCreatePrDialog` and `CommitAndCreatePr` actions were stubbed as TODOs. This branch adds the PR dialog as a third mode alongside commit and push, wires the "Commit and create PR" chain in commit mode, and includes a few related fixes.
|
||||
## Relevant Code
|
||||
- `app/src/code_review/git_dialog/pr.rs` — new submodule for the `CreatePr` mode
|
||||
- `app/src/code_review/git_dialog/mod.rs` — extended with `CreatePr(PrState)`, `Pr(PrSubAction)`, `new_for_pr()`, and mode dispatch
|
||||
- `app/src/code_review/git_dialog/commit.rs` — extended with `CommitAndCreatePr` intent, `allow_create_pr: bool` parameter, third intent button (hidden when the intent isn't meaningful), a private `CommitOutcome` enum for confirm results, and a chained `run_commit → run_push → create_pr` async
|
||||
- `app/src/code_review/code_review_view.rs` — `open_pr_dialog()`, `allow_create_pr` computed and passed through `open_commit_dialog()`, `OpenCreatePrDialog` / `CommitAndCreatePr` action handlers, `update_git_operations_ui` refresh on `NewDiffsComputed`
|
||||
- `app/src/util/git.rs` — `get_branch_diff_entries()` (branch-level numstat diff). `create_pr()` and `PrInfo` already existed.
|
||||
## Current State
|
||||
`GitDialog` owns commit and push modes with a shared shape: per-mode state struct, body renderer, confirm async, and dispatch in `mod.rs`. Each mode calls `show_toast` / `user_facing_git_error` (declared in `git_dialog/mod.rs`) on success and failure, then emits `GitDialogEvent::Completed`. The parent closes the dialog and refreshes metadata; it no longer knows anything about outcomes. `gh` CLI helpers (`run_gh_command`, `get_pr_for_branch`, `PrInfo`, `create_pr`) already exist.
|
||||
## Proposed Changes
|
||||
### 1. PR mode for `GitDialog` (`git_dialog/pr.rs`)
|
||||
A new submodule, following the same shape as `commit.rs` and `push.rs`.
|
||||
**State:**
|
||||
```rust
|
||||
pub struct PrState {
|
||||
file_changes: Vec<FileChangeEntry>,
|
||||
changes_expanded: bool,
|
||||
summary_mouse_state: MouseStateHandle,
|
||||
changes_scroll_state: ClippedScrollStateHandle,
|
||||
}
|
||||
```
|
||||
**Sub-action:**
|
||||
```rust
|
||||
pub enum PrSubAction {
|
||||
ToggleChangesExpanded,
|
||||
}
|
||||
```
|
||||
**Body:** branch header + "Changes" section with aggregate stats (file count, +additions, -deletions) and expandable per-file list (scrollable, max 130px). Uses the shared `render_branch_section` / `render_chevron_icon` / `render_file_list` helpers in `git_dialog/mod.rs`.
|
||||
**Constructor:** `pr::new_state(repo_path, ctx)` spawns `get_branch_diff_entries` to populate `file_changes`.
|
||||
**Confirm:** `pr::start_confirm` spawns `create_pr(&repo_path)`.
|
||||
- On success: calls `show_pr_created_toast(&pr_info, ctx)` (see below).
|
||||
- On failure: logs the raw error and calls `show_toast(user_facing_git_error(&err), ctx)`.
|
||||
- Either way emits `GitDialogEvent::Completed`.
|
||||
**Toast helper:** `pr::show_pr_created_toast(pr_info, ctx)` — ephemeral `DismissibleToast` with message `"PR successfully created."` and a clickable "Open PR" `ToastLink` pointing at `pr_info.url`. Declared `pub(super)` so `commit.rs` can reuse it for the `CommitAndCreatePr` chain.
|
||||
**Labels/icon:** title = "Create pull request"; confirm button = "Create PR" / `Icon::Github`; loading = "Creating…".
|
||||
### 2. `GitDialog` mode dispatch (`git_dialog/mod.rs`)
|
||||
- New variant `GitDialogMode::CreatePr(PrState)`
|
||||
- New variant `GitDialogAction::Pr(PrSubAction)`
|
||||
- New constructor `GitDialog::new_for_pr(repo_path, branch_name, ctx)`
|
||||
- Title / body / focus / confirm / sub-action dispatch extended for `CreatePr`
|
||||
- `new_for_commit` signature grows an `allow_create_pr: bool` parameter so commit mode can hide its "Commit and create PR" button when the intent isn't meaningful (existing PR or main branch). The caller encodes both conditions into the single boolean so the dialog doesn't need to know the underlying reasons.
|
||||
### 3. `CommitAndCreatePr` intent (`git_dialog/commit.rs`)
|
||||
- Adds `CommitIntent::CommitAndCreatePr` variant
|
||||
- `confirm_label_for` / `confirm_icon_for` / `loading_label_for` extended
|
||||
- `CommitState` gains `commit_and_create_pr_button: Option<ViewHandle<ActionButton>>` — `None` when `allow_create_pr` is false (existing PR or main branch)
|
||||
- `new_state` takes `allow_create_pr: bool`. When false:
|
||||
- The "Commit and create PR" button is omitted entirely (not just disabled)
|
||||
- A `debug_assert!` catches callers that dispatched `CommitAndCreatePr` anyway; in release the subsequent `create_pr` call surfaces the real `gh` error via the normal failure path rather than silently rewriting the intent
|
||||
- `apply_intent_selector` and `render_intent_buttons` skip the third button when it's `None`
|
||||
- A private `CommitOutcome { Committed | Pushed | PrCreated(PrInfo) }` enum represents what actually ran, keeping "which stages fired" decoupled from the user's selected intent so the callback can't drift out of sync with the async body.
|
||||
- `start_confirm` chains `run_commit` → `run_push` (for `CommitAndPush` or `CommitAndCreatePr`) → `create_pr` (for `CommitAndCreatePr`) in a single `ctx.spawn`, returning a `CommitOutcome`
|
||||
- Success toasts by outcome:
|
||||
- `Committed` → `"Changes successfully committed."`
|
||||
- `Pushed` → `"Changes committed and pushed."`
|
||||
- `PrCreated(pr)` → `show_pr_created_toast(&pr, ctx)` (same link-bearing toast as standalone PR creation)
|
||||
- Failure → `show_toast(user_facing_git_error(...), ctx)`
|
||||
- Either way emits `GitDialogEvent::Completed`
|
||||
### 4. Code review view integration (`code_review_view.rs`)
|
||||
- **`open_pr_dialog(ctx)`** — uses the existing `prepare_git_dialog` / `attach_git_dialog` helpers; constructs `GitDialog::new_for_pr(...)`.
|
||||
- **`open_commit_dialog(intent, ctx)`** — computes `allow_create_pr = pr_info.is_none() && !is_on_main_branch()` from `diff_state_model` and passes it through to `GitDialog::new_for_commit`, so the commit dialog can hide the "Create PR" intent when it isn't meaningful.
|
||||
- **Action wiring:**
|
||||
- `OpenCreatePrDialog` → `self.open_pr_dialog(ctx)` (was TODO)
|
||||
- `CommitAndCreatePr` → `self.open_commit_dialog(CommitIntent::CommitAndCreatePr, ctx)` (was TODO)
|
||||
- **Git operations button refresh fix:** `DiffStateModelEvent::NewDiffsComputed` handler now calls `update_git_operations_ui(ctx)` so after a commit the button transitions from "Commit" → "Push" (or "Create PR") without waiting for another event.
|
||||
Parent still knows nothing about outcomes — its `attach_git_dialog` subscriber remains just `Completed → close + refresh` and `Cancelled → close`.
|
||||
### 5. Git utility (`util/git.rs`)
|
||||
**`get_branch_diff_entries(repo_path)`** — returns per-file change stats for the branch diff:
|
||||
- Detects base branch via `detect_main_branch`, current branch via `detect_current_branch`.
|
||||
- Diffs `{base}..origin/{current}`, falling back to `{base}..HEAD` if the remote ref doesn't exist (e.g. branch not yet pushed).
|
||||
- Parses `git diff --numstat` into `Vec<FileChangeEntry>`.
|
||||
## End-to-End Flows
|
||||
### Standalone "Create PR" flow
|
||||
1. User clicks "Create PR" button → `OpenCreatePrDialog` action
|
||||
2. `open_pr_dialog(ctx)` → `GitDialog::new_for_pr(...)`; `pr::new_state` spawns `get_branch_diff_entries`
|
||||
3. Dialog renders with branch info and change summary
|
||||
4. User clicks "Create PR" → `GitDialogAction::Confirm` → `pr::start_confirm` spawns `create_pr`
|
||||
5. Confirm/cancel/close disabled, confirm label reads "Creating…"
|
||||
6. On success → `show_pr_created_toast` fires ("PR successfully created." with "Open PR" link) → emits `Completed` → parent closes dialog + refreshes metadata (header button becomes "PR #N")
|
||||
7. On error → toast with friendly message → emits `Completed` → parent closes dialog + refreshes
|
||||
### "Commit and create PR" flow
|
||||
1. User selects "Commit and create PR" in the commit dialog → `CommitIntent::CommitAndCreatePr`
|
||||
2. Confirm handler chains `run_commit` → `run_push` → `create_pr` in a single `ctx.spawn`
|
||||
3. On success → `show_pr_created_toast(&pr, ctx)` → `Completed` → parent closes dialog + refreshes
|
||||
4. On failure at any stage → friendly error toast → `Completed` → parent closes dialog + refreshes
|
||||
### "Commit and create PR" when the intent isn't meaningful (existing PR or main branch)
|
||||
1. `open_commit_dialog` computes `allow_create_pr` from the diff state model (PR info + `is_on_main_branch`) and passes it to `GitDialog::new_for_commit`
|
||||
2. The third intent button is omitted entirely (no disabled dead button)
|
||||
3. The caller is expected to not dispatch `CommitAndCreatePr` in this state; a `debug_assert!` in `commit::new_state` catches violations in dev builds, and in release the subsequent `create_pr` failure surfaces via the normal error toast path
|
||||
## Risks and Mitigations
|
||||
### `gh` CLI not installed or not authenticated
|
||||
Currently no pre-check. `create_pr` fails with a descriptive error from `gh`. The error surfaces as a friendly toast via the unified `Failed` path. Follow-up: add a `gh` availability check before enabling the action.
|
||||
### Git operations button not updating after commit
|
||||
Fixed by calling `update_git_operations_ui` in the `NewDiffsComputed` handler, so after a commit triggers a diff reload, the button state is re-evaluated.
|
||||
## Testing and Validation
|
||||
- Verify PR dialog opens with correct branch name and file change stats.
|
||||
- Verify expanding changes shows per-file list with correct +/- stats.
|
||||
- Verify loading state (all three chrome buttons disabled) during PR creation.
|
||||
- Verify success shows the "PR successfully created." toast with "Open PR" link and header updates to "PR #N".
|
||||
- Verify error shows friendly error toast and dialog closes.
|
||||
- Verify "Commit and create PR" chains all three operations and shows the same PR toast on success.
|
||||
- Verify commit dialog omits the third button entirely when the branch already has a PR OR when on the repo's main branch.
|
||||
- Verify cancel/close/ESC dismisses the dialog without side effects.
|
||||
- Verify the git operations button transitions correctly across Commit → Push → Create PR → PR #N.
|
||||
## Follow-ups
|
||||
- Add `gh` CLI availability/auth check before enabling "Create PR" and "Commit and create PR" actions.
|
||||
- Support editing PR title and body (currently uses `--fill`).
|
||||
- Support draft PRs.
|
||||
- Mid-flight mode transitions during `CommitAndCreatePr` so the dialog reflects "Committing…" → "Pushing…" → "Creating PR…" stages.
|
||||
Reference in New Issue
Block a user