add commit panel

This commit is contained in:
2026-08-14 08:49:08 +07:00
parent 9b1dd526a5
commit 080a026d3f
10 changed files with 1135 additions and 16 deletions
+53 -5
View File
@@ -1,17 +1,18 @@
use std::collections::{HashMap, HashSet, VecDeque};
use std::path::{Component, Path, PathBuf};
use std::rc::Rc;
use std::sync::Arc;
use anyhow::Error;
use assets::CustomIconName;
use gpui::prelude::*;
use gpui::{
AnyElement, App, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, Focusable, Pixels,
Render, SharedString, Size, Subscription, Task, Window, div, px, size,
Render, SharedString, Size, Subscription, Task, WeakEntity, Window, div, px, size,
};
use gpui_component::button::{Button, ButtonVariants, DropdownButton};
use gpui_component::combobox::{Caret, Combobox, ComboboxEvent, ComboboxState, ComboboxTriggerCtx};
use gpui_component::dock::{Panel, PanelEvent};
use gpui_component::dock::{DockArea, DockPlacement, Panel, PanelEvent};
use gpui_component::menu::PopupMenuItem;
use gpui_component::searchable_list::SearchableVec;
use gpui_component::tab::{Tab, TabBar};
@@ -26,6 +27,7 @@ use signed_state::{GitStore, RepoStore};
mod browser;
mod commits;
mod diff;
mod helpers;
use browser::{
@@ -33,6 +35,7 @@ use browser::{
MarkdownView,
};
use commits::COMMIT_ROW_HEIGHT;
use diff::CommitDiffView;
use helpers::{build_tree_items, is_markdown_path};
/// What kind of ref the header selectors switch to.
@@ -47,6 +50,10 @@ enum RefKind {
/// Detail view of a repository: header, stats, a file explorer with README
/// preview (cloned from the announcement's `clone` URLs), and metadata.
pub struct RepoDetailView {
focus_handle: FocusHandle,
/// Dock area the detail view lives in; new panels (commit diffs) are
/// added there.
dock_area: WeakEntity<DockArea>,
/// Snapshot taken at open time, shown until the store's first refresh
/// completes (and as a fallback while the store has no announcement).
initial: Announcement,
@@ -107,7 +114,6 @@ pub struct RepoDetailView {
/// Bumped on every branch/tag switch; in-flight loads tagged with an
/// older generation are discarded when they complete.
ref_generation: u64,
focus_handle: FocusHandle,
/// In-flight tasks; finished tasks are pruned on every push, so the vec
/// stays bounded by the number of concurrent loads.
tasks: Vec<Task<Result<(), Error>>>,
@@ -117,7 +123,12 @@ pub struct RepoDetailView {
}
impl RepoDetailView {
pub fn new(initial: Announcement, window: &mut Window, cx: &mut Context<Self>) -> Self {
pub fn new(
dock_area: WeakEntity<DockArea>,
initial: Announcement,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let store = cx.new(|cx| RepoStore::new(initial.addr(), cx));
let tree_state = cx.new(|cx| TreeState::new(cx));
@@ -215,6 +226,7 @@ impl RepoDetailView {
Self {
initial,
dock_area,
announcement: None,
relays,
web,
@@ -568,6 +580,36 @@ impl RepoDetailView {
self.track(task);
}
/// Open a new panel showing the diff of `commit` (all files it changed,
/// with the line diff of each). Called from the Commits tab rows and the
/// latest-commit button in the header.
fn open_commit_diff(
&mut self,
commit: &FileCommit,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(worktree) = self.worktree.clone() else {
return;
};
// Same display name as the repo detail panel's title.
let announcement = self.announcement.as_ref().unwrap_or(&self.initial);
let repo_name = announcement
.name
.clone()
.unwrap_or_else(|| SharedString::from(announcement.id.clone()));
let panel =
cx.new(|cx| CommitDiffView::new(worktree, repo_name, commit.clone(), window, cx));
if let Some(dock_area) = self.dock_area.upgrade() {
dock_area.update(cx, |dock_area, cx| {
// The diff viewer lives in the bottom dock, leaving the
// central explorer open while browsing a commit.
dock_area.add_panel(Arc::new(panel), DockPlacement::Bottom, None, window, cx);
});
}
}
/// Check out `name` (a branch or tag picked in the header) and refresh
/// the explorer once the switch completes.
fn switch_ref(
@@ -1057,7 +1099,13 @@ impl Render for RepoDetailView {
.map_or_else(SharedString::default, |commit| {
commit.summary.clone().into()
}),
),
)
.on_click(cx.listener(|this, _event, window, cx| {
if let Some(commit) = &this.head_commit {
let commit = commit.clone();
this.open_commit_diff(&commit, window, cx);
}
})),
),
),
),