diff --git a/crates/workspace/src/views/repo_detail/send_patch.rs b/crates/workspace/src/views/repo_detail/send_patch.rs index 9159553..f9279f4 100644 --- a/crates/workspace/src/views/repo_detail/send_patch.rs +++ b/crates/workspace/src/views/repo_detail/send_patch.rs @@ -1,13 +1,14 @@ -use assets::CustomIconName; use dock::{BasePanel, DockArea, DockPlacement, Panel, PanelEvent, panel_handle}; use gpui::prelude::*; use gpui::{ AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, Render, SharedString, Subscription, WeakEntity, Window, div, px, }; -use gpui_component::button::{Button, ButtonVariants}; +use gpui_base::{Button as BaseButton, StyledExt}; use gpui_component::input::{Input, InputEvent, InputState, Textarea, TextareaState}; -use gpui_component::{ActiveTheme, Disableable, Icon, IconName, Sizable, h_flex, v_flex}; +use gpui_component::scroll::ScrollableElement; +use gpui_component::spinner::Spinner; +use gpui_component::{ActiveTheme, Icon, IconName, Sizable, h_flex, v_flex}; use signed_state::RepoStore; pub struct SendPatchView { @@ -43,7 +44,7 @@ impl SendPatchView { let description = cx .new(|cx| TextareaState::new(window, cx).placeholder("Describe the change (optional)")); let patch = cx.new(|cx| { - TextareaState::new(window, cx).placeholder("Paste `git format-patch` output here...") + TextareaState::new(window, cx).placeholder("diff --git a/file.txt b/file.txt\nindex 1234567..abcdefg 100644\n--- a/file.txt\n+++ b/file.txt") }); // Re-evaluate the Send button's enabled state as the inputs change. @@ -130,44 +131,40 @@ impl SendPatchView { cx.notify(); } - /// Top bar: a short caption and the Send button. - fn render_header(&self, cx: &mut Context) -> AnyElement { + fn render_footer(&self, cx: &mut Context) -> AnyElement { let can_submit = !self.submitting && !self.subject.read(cx).value().is_empty() && !self.patch.read(cx).value().is_empty(); h_flex() .px_4() - .h_12() + .h_16() .w_full() .gap_2() .items_center() - .border_b_1() + .border_t_1() .border_color(cx.theme().border) + .child(div().flex_1()) .child( - h_flex() - .flex_1() - .min_w_0() - .gap_2() - .items_center() + BaseButton::new("send-patch") + .h_flex() + .h_8() + .px_2() + .gap_1() .text_sm() - .text_color(cx.theme().muted_foreground) - .child(Icon::new(IconName::FileText).small().flex_shrink_0()) - .child( - div() - .min_w_0() - .overflow_hidden() - .text_ellipsis() - .whitespace_nowrap() - .child("Send a patch from `git format-patch` output"), - ), - ) - .child( - Button::new("send-patch") - .icon(CustomIconName::CirclePlus) - .label("Send patch") - .primary() - .loading(self.submitting) + .items_center() + .justify_center() + .bg(cx.theme().primary) + .text_color(cx.theme().primary_foreground) + .hover(|this| this.bg(cx.theme().primary_hover)) + .active(|this| this.bg(cx.theme().primary_active)) + .map(|this| { + if self.submitting { + this.child(Spinner::new().small()) + } else { + this.child(Icon::new(IconName::ArrowUp)).child("Send patch") + } + }) .disabled(!can_submit) .on_click(cx.listener(|this, _event, window, cx| { this.submit(window, cx); @@ -176,22 +173,20 @@ impl SendPatchView { .into_any_element() } - /// Title and description inputs. - fn render_inputs(&self, cx: &mut Context) -> AnyElement { + fn render_inputs(&self, _cx: &mut Context) -> AnyElement { v_flex() .px_4() .py_2() .w_full() .gap_2() - .border_b_1() - .border_color(cx.theme().border) .child(Input::new(&self.subject)) .child(Textarea::new(&self.description).h(px(64.))) .into_any_element() } - /// The patch textarea, the main content of the panel. fn render_patch(&self, cx: &mut Context) -> AnyElement { + const MSG: &str = "You can paste a git diff or a git format-patch patch series here."; + v_flex() .px_4() .py_2() @@ -201,14 +196,13 @@ impl SendPatchView { div() .text_xs() .text_color(cx.theme().muted_foreground) - .child("Patch — `git format-patch` output"), + .child(MSG), ) - .child(Textarea::new(&self.patch).h(px(240.))) + .child(Textarea::new(&self.patch).h_56()) .into_any_element() } } -/// Open the "send patch" panel for `store` in the center dock. pub(super) fn open_send_patch_panel( dock_area: WeakEntity, store: Entity, @@ -247,19 +241,25 @@ impl Render for SendPatchView { v_flex() .id("send-patch") .size_full() - .child(self.render_header(cx)) - .child(self.render_inputs(cx)) - .when_some(self.error.clone(), |this, error| { - this.child( - h_flex() - .px_4() - .py_1() - .w_full() - .text_xs() - .text_color(cx.theme().danger) - .child(error), - ) - }) - .child(self.render_patch(cx)) + .child( + v_flex() + .overflow_y_scrollbar() + .flex_1() + .w_full() + .child(self.render_inputs(cx)) + .when_some(self.error.clone(), |this, error| { + this.child( + h_flex() + .px_4() + .py_1() + .w_full() + .text_xs() + .text_color(cx.theme().danger) + .child(error), + ) + }) + .child(self.render_patch(cx)), + ) + .child(self.render_footer(cx)) } } diff --git a/docs/TODO.md b/docs/TODO.md index 55f7568..05e68b9 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -34,6 +34,7 @@ - [ ] GRASP-06 `/prs//.git` contributor endpoints + kind-10317 user grasp-list fallback. - [ ] Merge button in the PR detail view (`merge_pull_request` is store-only today), then fetch-and-merge (`merge-commit`) when the push backend is guaranteed. - [ ] Local-checkout generation for the update-PR dialog (currently paste-only). +- [ ] Fork-aware compare in the New PR panel: today both branch selectors come from the user-picked local checkout, so a cross-fork PR (GitHub's "compare across forks") requires the fork's branch to exist locally. Add picking the fork repository from announced repos (its 30617 may point at this repo via the `u` tag, or share the EUC) + a branch, fetch it into the `GitCache` mirror, and run the `merge-base`/diff/`format-patch` flow against the base repo's mirror — like `choose_checkout` today but repo-driven. ## Performance: render path