From 2fe3f7b94f9e121e410732d1145fbdebcc9fac68 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Tue, 12 May 2026 01:34:33 +0800 Subject: [PATCH] feat: subtext under pr title --- .../issues.pull_request.PR_kwDOAgent47.json | 6 + .../issues.pull_request.PR_kwDODesign31.json | 6 + .../issues.pull_request.PR_kwDOInfra19.json | 6 + .../issues.pull_request.PR_kwDONovem84.json | 6 + .../issues.pull_request.PR_kwDONovem85.json | 6 + .../issues.pull_request.PR_kwDOSprint62.json | 6 + src/api/graphql/fetch_pull_request.graphql | 11 ++ src/api/issues.rs | 165 +++++++++--------- src/api/mock.rs | 65 +++++++ src/api/user.rs | 6 + src/screen/dashboard/pull_request_view.rs | 92 ++++++++-- src/screen/dashboard/screen.rs | 20 +-- 12 files changed, 290 insertions(+), 105 deletions(-) diff --git a/fixtures/github/issues.pull_request.PR_kwDOAgent47.json b/fixtures/github/issues.pull_request.PR_kwDOAgent47.json index e982fb2..22f10f7 100644 --- a/fixtures/github/issues.pull_request.PR_kwDOAgent47.json +++ b/fixtures/github/issues.pull_request.PR_kwDOAgent47.json @@ -2,5 +2,11 @@ "title": "feat(prompts): split context loading from execution workers", "state": "OPEN", "is_draft": true, + "author": { + "login": "leaferiksen", + "avatar_url": "https://avatars.githubusercontent.com/u/5151?v=4" + }, + "base_branch_name": "main", + "head_branch_name": "feat/worker-context-envelope", "body": "## Goal\n\nSplit context loading from execution workers so delegation stays predictable while this pull request is still in draft.\n\n### Why\n- workers should receive a compact payload\n- prompt packing should be testable without spawning a worker\n- retry policy should stay in one place\n\n### Proposed flow\n1. Load repository context once.\n2. Normalize file excerpts and metadata.\n3. Hand workers a stable execution envelope.\n\n```text\nContextLoader -> PromptAssembler -> WorkerRunner\n```\n\n> Draft status stays until we decide whether token counts belong in the worker response.\n\n### Questions\n- Should `ContextLoader` expose cache hit metrics?\n- Should worker retries carry the same prompt hash?\n- [ ] Add a regression test for interrupted workers" } diff --git a/fixtures/github/issues.pull_request.PR_kwDODesign31.json b/fixtures/github/issues.pull_request.PR_kwDODesign31.json index 64d8db8..c05dc75 100644 --- a/fixtures/github/issues.pull_request.PR_kwDODesign31.json +++ b/fixtures/github/issues.pull_request.PR_kwDODesign31.json @@ -2,5 +2,11 @@ "title": "chore(tokens): tighten dashboard spacing scale", "state": "OPEN", "is_draft": false, + "author": { + "login": "mariahops", + "avatar_url": "https://avatars.githubusercontent.com/u/6161?v=4" + }, + "base_branch_name": "main", + "head_branch_name": "chore/dashboard-spacing-scale", "body": "## Summary\n\nTightens the dashboard spacing scale before the next visual refresh.\n\n### Updated tokens\n- `space.3` for compact sidebar gaps\n- `space.5` for section rhythm\n- `space.8` for page-level separation\n\n| Surface | Before | After |\n| --- | --- | --- |\n| Sidebar section gap | `space.6` | `space.5` |\n| Filter row padding | `space.4` | `space.3` |\n| Dashboard gutter | `space.7` | `space.6` |\n\n### Review notes\n- verify heading baselines still align with list content\n- compare 1280px and 1440px screenshots side by side\n- [ ] revisit compact mode once the nav collapse lands\n\n**Design intent:** make dense screens feel more deliberate without looking cramped." } diff --git a/fixtures/github/issues.pull_request.PR_kwDOInfra19.json b/fixtures/github/issues.pull_request.PR_kwDOInfra19.json index 07a8698..99e0773 100644 --- a/fixtures/github/issues.pull_request.PR_kwDOInfra19.json +++ b/fixtures/github/issues.pull_request.PR_kwDOInfra19.json @@ -2,5 +2,11 @@ "title": "docs(deploy): document manual failover steps", "state": "CLOSED", "is_draft": false, + "author": { + "login": "kennethnym", + "avatar_url": "https://avatars.githubusercontent.com/u/4242?v=4" + }, + "base_branch_name": "main", + "head_branch_name": "docs/manual-failover-steps", "body": "## Context\n\nDocuments the manual failover sequence for the staging stack while the automated recovery path is still unstable.\n\n### Draft runbook\n1. Put the primary deployment in maintenance mode.\n2. Promote the standby database.\n3. Repoint the app workers.\n4. Warm the cache before reopening traffic.\n\n```bash\n./scripts/failover promote-standby --env staging\n./scripts/failover repoint-workers --env staging\n./scripts/failover verify --env staging\n```\n\n> This pull request was closed because the final DNS validation steps were still changing underneath the runbook.\n\n### Remaining gaps\n- secrets rotation is still manual\n- rollback screenshots are missing\n- [ ] add the final post-cutover checklist" } diff --git a/fixtures/github/issues.pull_request.PR_kwDONovem84.json b/fixtures/github/issues.pull_request.PR_kwDONovem84.json index ccd972c..fc213ba 100644 --- a/fixtures/github/issues.pull_request.PR_kwDONovem84.json +++ b/fixtures/github/issues.pull_request.PR_kwDONovem84.json @@ -2,5 +2,11 @@ "title": "feat(dashboard): hydrate issue pane from cached query state", "state": "OPEN", "is_draft": false, + "author": { + "login": "kennethnym", + "avatar_url": "https://avatars.githubusercontent.com/u/4242?v=4" + }, + "base_branch_name": "main", + "head_branch_name": "feat/cached-issue-pane", "body": "## Summary\n\nHydrates the dashboard issue pane from cached query state so selection and scroll position stay stable during refetches.\n\n### Rendering coverage\n- [x] headings\n- [x] bullet lists\n- [x] task list items\n- [x] inline code like `use_query`\n- [x] tables\n\n### Implementation sketch\n```rust\nlet cached = query_store.read(key);\nlet selection = cached.and_then(|data| data.selected_issue_id.clone());\n```\n\n| Case | Expected behavior |\n| --- | --- |\n| Cache hit | Keep the current selection pinned |\n| Cache miss | Fall back to the first visible item |\n| Refetch in flight | Preserve scroll position |\n\n### Follow-up\n- [ ] mirror the same cache behavior in the pull request detail pane\n- [ ] add a smoke test around keyboard navigation during refetch\n\nSee also the [query store](src/query.rs) integration notes." } diff --git a/fixtures/github/issues.pull_request.PR_kwDONovem85.json b/fixtures/github/issues.pull_request.PR_kwDONovem85.json index 02fa998..f492d15 100644 --- a/fixtures/github/issues.pull_request.PR_kwDONovem85.json +++ b/fixtures/github/issues.pull_request.PR_kwDONovem85.json @@ -2,5 +2,11 @@ "title": "feat(repo): add cached repository query for titlebar picker", "state": "OPEN", "is_draft": false, + "author": { + "login": "kennethnym", + "avatar_url": "https://avatars.githubusercontent.com/u/4242?v=4" + }, + "base_branch_name": "main", + "head_branch_name": "feat/cached-repo-picker", "body": "## Summary\n\nIntroduces a cached repository query so the titlebar picker can switch context without hitting GitHub on every open.\n\n### Why\n- reduces flicker while the picker opens\n- keeps recent repositories visible during short reconnects\n- avoids duplicate requests when the titlebar rerenders\n\n### Cache rules\n- explicit refresh invalidates the cached list\n- fresh network data still wins when available\n- empty responses should not overwrite a warm cache\n\n```text\nopen picker -> read cache -> render immediately -> refresh in background\n```\n\n### Follow-up\n1. Measure cache hit rate in debug builds.\n2. Add eviction telemetry.\n3. [ ] Consider persisting the last successful repository list across launches." } diff --git a/fixtures/github/issues.pull_request.PR_kwDOSprint62.json b/fixtures/github/issues.pull_request.PR_kwDOSprint62.json index a3939e6..80d690d 100644 --- a/fixtures/github/issues.pull_request.PR_kwDOSprint62.json +++ b/fixtures/github/issues.pull_request.PR_kwDOSprint62.json @@ -2,5 +2,11 @@ "title": "feat(calendar): ship release handoff checklist in weekly planner", "state": "MERGED", "is_draft": false, + "author": { + "login": "rorycraft", + "avatar_url": "https://avatars.githubusercontent.com/u/7171?v=4" + }, + "base_branch_name": "main", + "head_branch_name": "feat/release-handoff-checklist", "body": "## Release handoff checklist\n\nAdds the release checklist views and closes the loop for the May rollout.\n\n### Included\n- launch readiness checklist for QA, docs, and release engineering\n- handoff status badges in the weekly planner\n- empty-state copy for weeks without a scheduled release\n\n| Stage | Owner | Status |\n| --- | --- | --- |\n| QA sign-off | `@mariahops` | Done |\n| Docs publish | `@rorycraft` | Done |\n| Release window confirm | `@kennethnym` | Done |\n\n### Verification\n1. Open a release week and confirm checklist sections render in order.\n2. Mark each handoff item complete and confirm the summary badge updates.\n3. Review the planner on a narrow viewport.\n\n> The merged version intentionally keeps the checklist readable even when one section has no pending items.\n\n- [x] QA sign-off state is visible\n- [x] Docs handoff state is visible\n- [ ] Add screenshot coverage for the compact layout" } diff --git a/src/api/graphql/fetch_pull_request.graphql b/src/api/graphql/fetch_pull_request.graphql index 8e814a9..a41f758 100644 --- a/src/api/graphql/fetch_pull_request.graphql +++ b/src/api/graphql/fetch_pull_request.graphql @@ -6,6 +6,17 @@ query PullRequestQuery($id: ID!) { body state isDraft + baseRef { + name + } + headRef { + name + } + author { + __typename + login + avatarUrl(size: 32) + } } } } diff --git a/src/api/issues.rs b/src/api/issues.rs index 090182a..1e5d48c 100644 --- a/src/api/issues.rs +++ b/src/api/issues.rs @@ -70,6 +70,9 @@ pub(crate) struct DetailedPullRequest { pub(crate) state: PullRequestState, pub(crate) is_draft: bool, pub(crate) body: String, + pub(crate) author: Option, + pub(crate) base_branch_name: Option, + pub(crate) head_branch_name: Option, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -285,8 +288,8 @@ impl query::QueryFn for ListPullRequests { } let query_string = match self.filter { - Some(filter) => format!("is:pr archived:false sort:updated-desc {}", filter), - None => "is:pr archived:false sort:updated-desc".into(), + | Some(filter) => format!("is:pr archived:false sort:updated-desc {}", filter), + | None => "is:pr archived:false sort:updated-desc".into(), }; let gql = @@ -308,19 +311,19 @@ impl query::QueryFn for ListPullRequests { .flatten() .filter_map(|edge| { edge.node.and_then(|n| match n { - PullRequestPaginationQuerySearchEdgesNode::PullRequest(p) => { - Some(PullRequest { - id: p.id.into(), - title: p.title, - state: p.state, - is_draft: p.is_draft, - repo_slug: format!( - "{}/{}", - p.repository.owner.login, p.repository.name - ), - }) - } - _ => None, + | PullRequestPaginationQuerySearchEdgesNode::PullRequest(p) => { + Some(PullRequest { + id: p.id.into(), + title: p.title, + state: p.state, + is_draft: p.is_draft, + repo_slug: format!( + "{}/{}", + p.repository.owner.login, p.repository.name + ), + }) + } + | _ => None, }) }) .collect::>() @@ -365,15 +368,21 @@ impl query::QueryFn for FetchPullRequest { "missing 'node' field on PullRequestQuery response".into(), )) .and_then(|n| match n { - PullRequestQueryNode::PullRequest(p) => Ok(DetailedPullRequest { - title: p.title, - state: p.state, - is_draft: p.is_draft, - body: p.body, + | PullRequestQueryNode::PullRequest(p) => Ok(DetailedPullRequest { + title: p.title, + state: p.state, + is_draft: p.is_draft, + body: p.body, + author: p.author.map(|it| api::user::Actor { + login: it.login, + avatar_url: it.avatar_url, }), - _ => Err(api::Error::MalformedResponse( - "unexpected node type on PullRequestQuery".into(), - )), + base_branch_name: p.base_ref.map(|r| r.name), + head_branch_name: p.head_ref.map(|r| r.name), + }), + | _ => Err(api::Error::MalformedResponse( + "unexpected node type on PullRequestQuery".into(), + )), }) } } @@ -416,11 +425,11 @@ impl query::QueryFn for FetchPullRequestTimeline { TimelineActor { kind: match on { - actorFieldsOn::Bot => "Bot", - actorFieldsOn::EnterpriseUserAccount => "EnterpriseUserAccount", - actorFieldsOn::Mannequin => "Mannequin", - actorFieldsOn::Organization => "Organization", - actorFieldsOn::User => "User", + | actorFieldsOn::Bot => "Bot", + | actorFieldsOn::EnterpriseUserAccount => "EnterpriseUserAccount", + | actorFieldsOn::Mannequin => "Mannequin", + | actorFieldsOn::Organization => "Organization", + | actorFieldsOn::User => "User", } .into(), name: login, @@ -430,62 +439,62 @@ impl query::QueryFn for FetchPullRequestTimeline { fn normalize_assignee(actor: assigneeFields) -> TimelineActor { match actor { - assigneeFields::Bot(actor) => TimelineActor { - kind: "Bot".into(), - name: actor.login, - avatar_url: Some(actor.avatar_url), - }, - assigneeFields::Mannequin(actor) => TimelineActor { - kind: "Mannequin".into(), - name: actor.login, - avatar_url: Some(actor.avatar_url), - }, - assigneeFields::Organization(actor) => TimelineActor { - kind: "Organization".into(), - name: actor.login, - avatar_url: Some(actor.avatar_url), - }, - assigneeFields::User(actor) => TimelineActor { - kind: "User".into(), - name: actor.login, - avatar_url: Some(actor.avatar_url), - }, + | assigneeFields::Bot(actor) => TimelineActor { + kind: "Bot".into(), + name: actor.login, + avatar_url: Some(actor.avatar_url), + }, + | assigneeFields::Mannequin(actor) => TimelineActor { + kind: "Mannequin".into(), + name: actor.login, + avatar_url: Some(actor.avatar_url), + }, + | assigneeFields::Organization(actor) => TimelineActor { + kind: "Organization".into(), + name: actor.login, + avatar_url: Some(actor.avatar_url), + }, + | assigneeFields::User(actor) => TimelineActor { + kind: "User".into(), + name: actor.login, + avatar_url: Some(actor.avatar_url), + }, } } fn normalize_requested_reviewer(actor: requestedReviewerFields) -> TimelineActor { match actor { - requestedReviewerFields::Bot(actor) => TimelineActor { - kind: "Bot".into(), - name: actor.login, - avatar_url: Some(actor.avatar_url), - }, - requestedReviewerFields::Mannequin(actor) => TimelineActor { - kind: "Mannequin".into(), - name: actor.login, - avatar_url: Some(actor.avatar_url), - }, - requestedReviewerFields::Team(actor) => TimelineActor { - kind: "Team".into(), - name: actor.name, - avatar_url: None, - }, - requestedReviewerFields::User(actor) => TimelineActor { - kind: "User".into(), - name: actor.login, - avatar_url: Some(actor.avatar_url), - }, + | requestedReviewerFields::Bot(actor) => TimelineActor { + kind: "Bot".into(), + name: actor.login, + avatar_url: Some(actor.avatar_url), + }, + | requestedReviewerFields::Mannequin(actor) => TimelineActor { + kind: "Mannequin".into(), + name: actor.login, + avatar_url: Some(actor.avatar_url), + }, + | requestedReviewerFields::Team(actor) => TimelineActor { + kind: "Team".into(), + name: actor.name, + avatar_url: None, + }, + | requestedReviewerFields::User(actor) => TimelineActor { + kind: "User".into(), + name: actor.login, + avatar_url: Some(actor.avatar_url), + }, } } fn normalize_review_state(state: PullRequestReviewState) -> String { match state { - PullRequestReviewState::PENDING => "PENDING", - PullRequestReviewState::COMMENTED => "COMMENTED", - PullRequestReviewState::APPROVED => "APPROVED", - PullRequestReviewState::CHANGES_REQUESTED => "CHANGES_REQUESTED", - PullRequestReviewState::DISMISSED => "DISMISSED", - _ => "OTHER", + | PullRequestReviewState::PENDING => "PENDING", + | PullRequestReviewState::COMMENTED => "COMMENTED", + | PullRequestReviewState::APPROVED => "APPROVED", + | PullRequestReviewState::CHANGES_REQUESTED => "CHANGES_REQUESTED", + | PullRequestReviewState::DISMISSED => "DISMISSED", + | _ => "OTHER", } .into() } @@ -705,10 +714,10 @@ impl query::QueryFn for FetchPullRequestTimeline { "missing 'node' field on PullRequestTimelineQuery response".into(), )) .and_then(|node| match node { - PullRequestTimelineQueryNode::PullRequest(pull_request) => Ok(pull_request), - _ => Err(api::Error::MalformedResponse( - "unexpected node type on PullRequestTimelineQuery".into(), - )), + | PullRequestTimelineQueryNode::PullRequest(pull_request) => Ok(pull_request), + | _ => Err(api::Error::MalformedResponse( + "unexpected node type on PullRequestTimelineQuery".into(), + )), })?; let timeline = pull_request.timeline_items; diff --git a/src/api/mock.rs b/src/api/mock.rs index 23ea297..5c83d25 100644 --- a/src/api/mock.rs +++ b/src/api/mock.rs @@ -133,15 +133,80 @@ mod tests { .expect("closed pull request fixture should parse"); let dashboard_markdown = fetch_pull_request(&issues::Id::from("PR_kwDONovem84")) .expect("dashboard pull request fixture should parse"); + let cached_repo_picker = fetch_pull_request(&issues::Id::from("PR_kwDONovem85")) + .expect("repo picker pull request fixture should parse"); + let worker_split = fetch_pull_request(&issues::Id::from("PR_kwDOAgent47")) + .expect("worker split pull request fixture should parse"); + let spacing_tokens = fetch_pull_request(&issues::Id::from("PR_kwDODesign31")) + .expect("spacing token pull request fixture should parse"); assert_eq!(merged.state, issues::PullRequestState::Merged); assert!(merged.body.contains("| Stage | Owner | Status |")); + assert_eq!( + merged.author.as_ref().map(|author| author.login.as_str()), + Some("rorycraft") + ); + assert_eq!(merged.base_branch_name.as_deref(), Some("main")); + assert_eq!( + merged.head_branch_name.as_deref(), + Some("feat/release-handoff-checklist") + ); assert!( documented_failover .body .contains("./scripts/failover promote-standby") ); + assert_eq!( + documented_failover + .author + .as_ref() + .map(|author| author.login.as_str()), + Some("kennethnym") + ); + assert_eq!(documented_failover.base_branch_name.as_deref(), Some("main")); + assert_eq!( + documented_failover.head_branch_name.as_deref(), + Some("docs/manual-failover-steps") + ); assert!(dashboard_markdown.body.contains("```rust")); + assert_eq!(dashboard_markdown.base_branch_name.as_deref(), Some("main")); + assert_eq!( + dashboard_markdown.head_branch_name.as_deref(), + Some("feat/cached-issue-pane") + ); + assert_eq!( + cached_repo_picker + .author + .as_ref() + .map(|author| author.login.as_str()), + Some("kennethnym") + ); + assert_eq!(cached_repo_picker.base_branch_name.as_deref(), Some("main")); + assert_eq!( + cached_repo_picker.head_branch_name.as_deref(), + Some("feat/cached-repo-picker") + ); + assert_eq!( + worker_split.author.as_ref().map(|author| author.login.as_str()), + Some("leaferiksen") + ); + assert_eq!(worker_split.base_branch_name.as_deref(), Some("main")); + assert_eq!( + worker_split.head_branch_name.as_deref(), + Some("feat/worker-context-envelope") + ); + assert_eq!( + spacing_tokens + .author + .as_ref() + .map(|author| author.login.as_str()), + Some("mariahops") + ); + assert_eq!(spacing_tokens.base_branch_name.as_deref(), Some("main")); + assert_eq!( + spacing_tokens.head_branch_name.as_deref(), + Some("chore/dashboard-spacing-scale") + ); } #[test] diff --git a/src/api/user.rs b/src/api/user.rs index 04c8459..a80496d 100644 --- a/src/api/user.rs +++ b/src/api/user.rs @@ -20,6 +20,12 @@ pub struct User { pub email: Option, } +#[derive(Debug, Deserialize)] +pub(crate) struct Actor { + pub(crate) login: String, + pub(crate) avatar_url: String, +} + impl Deref for Id { type Target = u64; diff --git a/src/screen/dashboard/pull_request_view.rs b/src/screen/dashboard/pull_request_view.rs index 10a9325..ea035e8 100644 --- a/src/screen/dashboard/pull_request_view.rs +++ b/src/screen/dashboard/pull_request_view.rs @@ -1,6 +1,6 @@ use gpui::{ AppContext, InteractiveElement, IntoElement, ParentElement, StatefulInteractiveElement, Styled, - div, prelude::FluentBuilder, + div, img, prelude::FluentBuilder, }; use crate::{ @@ -118,20 +118,80 @@ impl PullRequestView { } } - let author_pill = div() - .px_2() - .border_1() - .border_color(theme.colors.border) - .rounded_full() - .bg(theme.colors.surface_elevated) - .child(text("kennethnym").text_xs()); + let merge_text = match ( + pr.author.as_ref(), + pr.base_branch_name.as_ref(), + pr.head_branch_name.as_ref(), + ) { + | (Some(author), Some(base_branch), Some(head_branch)) => { + let str = format!( + "{} requested to merge {} into {}", + author.login, head_branch, base_branch + ); - let row = div() - .flex() - .flex_row() - .gap_2() - .child(status_pill) - .child(author_pill); + let head_branch_text_offset = author.login.len() + 20; + let base_branch_text_offset = head_branch_text_offset + head_branch.len() + 6; + + let highlights = [ + ( + 0..author.login.len(), + gpui::HighlightStyle { + font_weight: Some(gpui::FontWeight::BOLD), + ..Default::default() + }, + ), + ( + head_branch_text_offset..head_branch_text_offset + head_branch.len(), + gpui::HighlightStyle { + font_weight: Some(gpui::FontWeight::BOLD), + color: Some(theme.colors.accent.into()), + ..Default::default() + }, + ), + ( + base_branch_text_offset..base_branch_text_offset + base_branch.len(), + gpui::HighlightStyle { + font_weight: Some(gpui::FontWeight::BOLD), + color: Some(theme.colors.accent.into()), + ..Default::default() + }, + ), + ]; + + Some(( + author, + gpui::StyledText::new(str).with_highlights(highlights), + )) + } + + | _ => None, + }; + + let metadata_line = + div() + .flex() + .flex_row() + .gap_2() + .when_some(merge_text, |it, (author, t)| { + it.child( + div() + .flex() + .flex_row() + .items_center() + .gap_1p5() + .child(img(author.avatar_url.clone()).size_4().rounded_full()) + .child( + div() + .min_w_0() + .w_full() + .text_color(theme.colors.text) + .text_xs() + .font_weight(gpui::FontWeight::LIGHT) + .opacity(0.8) + .child(t), + ), + ) + }); div() .size_full() @@ -145,8 +205,8 @@ impl PullRequestView { .py_3() .border_b_1() .border_color(theme.colors.border) - .child(text(pr.title.clone()).w_full().text_xl().mb_2()) - .child(row), + .child(text(pr.title.clone()).w_full().text_xl().mb_1()) + .child(metadata_line), ) .child( div().flex_1().min_h_0().w_full().child( diff --git a/src/screen/dashboard/screen.rs b/src/screen/dashboard/screen.rs index 9d6ffc4..5903522 100644 --- a/src/screen/dashboard/screen.rs +++ b/src/screen/dashboard/screen.rs @@ -43,9 +43,9 @@ impl Screen { _ = cx .subscribe(&self.issue_list, |this, _, event, cx| match event { - issue_list::Event::ItemSelected(pr_id) => { - this.handle_issue_list_item_selected(pr_id, cx); - } + | issue_list::Event::ItemSelected(pr_id) => { + this.handle_issue_list_item_selected(pr_id, cx); + } }) .detach(); } @@ -56,17 +56,18 @@ impl Screen { cx: &mut gpui::Context, ) { match value { - SidebarItemValue::PullRequest { filter } => { - self.issue_filter = Some(*filter); - cx.notify(); - } + | SidebarItemValue::PullRequest { filter } => { + self.issue_filter = Some(*filter); + cx.notify(); + } } } fn handle_issue_list_item_selected( &mut self, id: &api::issues::Id, - cx: &mut gpui::Context, ) { + cx: &mut gpui::Context, + ) { println!("handle issue list item selected: {:?}", id); self.pull_request_view.update(cx, |view, cx| { view.change_displayed_pull_request(id.clone(), cx); @@ -111,7 +112,6 @@ impl gpui::Render for Screen { .bg(theme.colors.surface) .border_x_1() .border_color(theme.colors.border) - .mr_2() .overflow_hidden() .child(self.issue_list.clone()), ) @@ -123,8 +123,6 @@ impl gpui::Render for Screen { .h_full() .overflow_hidden() .bg(theme.colors.surface) - .border_l_1() - .border_color(theme.colors.border) .child(self.pull_request_view.clone()), ), )