feat: out-of-box experience (#2)

Reviewed-on: https://git.reya.su/reya/signed/pulls/2
This commit was merged in pull request #2.
This commit is contained in:
2026-08-25 13:23:07 +00:00
parent 7249a323f8
commit dacfd49cdf
180 changed files with 16763 additions and 1042 deletions
@@ -0,0 +1,164 @@
//! Commits tab of the repository detail view: a virtual list of all
//! commits reachable from HEAD, newest first, with the total count shown
//! as a badge on the tab.
use gpui::prelude::*;
use gpui::{AnyElement, App, Context, WeakEntity, div, px};
use gpui_component::scroll::Scrollbar;
use gpui_component::spinner::Spinner;
use gpui_component::{ActiveTheme, Sizable, h_flex, v_flex, v_virtual_list};
use signed_git::FileCommit;
use utils::relative_time_secs;
use super::RepoDetailView;
use super::helpers::placeholder;
/// Height of one commit row in the virtual list.
pub(super) const COMMIT_ROW_HEIGHT: f32 = 56.;
/// One row of the commit list: id, summary, author and relative time.
/// Clicking a row opens the diff of that commit in a new panel.
fn commit_row(
ix: usize,
commit: &FileCommit,
view: &WeakEntity<RepoDetailView>,
cx: &App,
) -> AnyElement {
let view = view.clone();
// Only the id is needed by the click handler: the diff panel fetches
// the full commit itself.
let id = commit.id.clone();
h_flex()
.id(ix)
.px_4()
.h(px(COMMIT_ROW_HEIGHT))
.w_full()
.gap_3()
.items_center()
.border_b(px(1.))
.border_color(cx.theme().border)
.hover(|this| this.bg(cx.theme().list_hover))
.child(
v_flex()
.flex_1()
.min_w_0()
.gap_0p5()
.justify_center()
.child(
h_flex()
.gap_2()
.items_center()
.overflow_hidden()
.child(
div()
.font_family(cx.theme().mono_font_family.clone())
.text_xs()
.text_color(cx.theme().muted_foreground)
.child(commit.id.clone()),
)
.child(
div()
.flex_1()
.min_w_0()
.text_sm()
.text_ellipsis()
.whitespace_nowrap()
.child(commit.summary.clone()),
),
)
.child(
h_flex()
.gap_2()
.text_xs()
.text_color(cx.theme().muted_foreground)
.child(commit.author.clone())
.child(relative_time_secs(commit.time)),
),
)
.on_click(move |_event, window, cx| {
if let Some(view) = view.upgrade() {
view.update(cx, |this, cx| this.open_commit_diff(&id, window, cx));
}
})
.into_any_element()
}
impl RepoDetailView {
/// Full-height body of the Commits tab: all commits in a virtual
/// list, or a status message while loading / when there are none.
pub(super) fn render_commits_tab(&self, cx: &mut Context<Self>) -> AnyElement {
let Some(list) = self.all_commits.as_ref() else {
return if self.loading_all_commits {
v_flex()
.size_full()
.items_center()
.justify_center()
.child(Spinner::new().small())
.into_any_element()
} else {
placeholder("Failed to load commits", cx)
};
};
if list.commits.is_empty() {
return placeholder("No commits found", cx);
}
// Copy only the values the element tree needs; the list itself is
// borrowed inside the renderer below instead of being cloned per
// frame (a full history can be tens of thousands of commits).
let view = cx.entity().clone();
let sizes = self.item_sizes.clone();
let scroll_handle = self.scroll_handle.clone();
let shown = list.commits.len();
let total = list.total;
v_flex()
.relative()
.flex_1()
.w_full()
.min_h_0()
.child(
v_virtual_list(
view,
"repo-commits",
sizes,
move |this, range, _window, cx| {
let commits = this
.all_commits
.as_ref()
.map(|list| list.commits.as_slice())
.unwrap_or(&[]);
let view = cx.entity().downgrade();
range
.map(|ix| commit_row(ix, &commits[ix], &view, cx))
.collect()
},
)
.track_scroll(&scroll_handle)
.size_full(),
)
.when(shown < total, |this| {
// The history is capped; tell the user the list is truncated.
this.child(
div()
.py_2()
.w_full()
.text_xs()
.text_color(cx.theme().muted_foreground)
.child(format!("Showing {shown} of {total} commits")),
)
})
.child(
div()
.absolute()
.top_0()
.left_0()
.right_0()
.bottom_0()
.child(Scrollbar::vertical(&self.scroll_handle)),
)
.into_any_element()
}
}