Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
crates/web/src/repo.rs +61 −37
| @@ -806,43 +806,8 @@ async fn repo_home( | |||
| 806 | let body = match resolved { | 806 | let body = match resolved { |
| 807 | Err(_) => empty_repo_body(state, &ctx), | 807 | Err(_) => empty_repo_body(state, &ctx), |
| 808 | Ok(rev) => { | 808 | Ok(rev) => { |
| 809 | let entries = git_read(state, &ctx.repo.id, { | ||
| 810 | let rev = rev.clone(); | ||
| 811 | move |repo| repo.tree_entries(&rev, FsPath::new("")) | ||
| 812 | }) | ||
| 813 | .await?; | ||
| 814 | let readme = load_readme(state, &ctx.repo.id, &rev, &entries).await; | ||
| 815 | let annotations = git_read(state, &ctx.repo.id, { | ||
| 816 | let rev = rev.clone(); | ||
| 817 | move |repo| repo.tree_annotations(&rev, FsPath::new("")) | ||
| 818 | }) | ||
| 819 | .await | ||
| 820 | .unwrap_or_default(); | ||
| 821 | let branches = branch_names(state, &ctx.repo.id).await; | 809 | let branches = branch_names(state, &ctx.repo.id).await; |
| 822 | // The language breakdown of the default branch (best-effort). | 810 | landing_body(state, &ctx, &rev_name, &rev, &branches).await? |
| 823 | let langs = git_read(state, &ctx.repo.id, { | ||
| 824 | let rev = rev.clone(); | ||
| 825 | move |repo| repo.blob_sizes(&rev) | ||
| 826 | }) | ||
| 827 | .await | ||
| 828 | .map(|files| crate::languages::breakdown(&files)) | ||
| 829 | .unwrap_or_default(); | ||
| 830 | let base = format!("/{}/{}", ctx.owner.username, ctx.repo.path); | ||
| 831 | html! { | ||
| 832 | (repo_header(state, &ctx, Tab::Code, &rev_name, &branches)) | ||
| 833 | form class="search-row repo-codesearch" method="get" action=(format!("{base}/-/search")) { | ||
| 834 | input type="search" name="q" placeholder="Search this repository…" | ||
| 835 | aria-label="Search this repository"; | ||
| 836 | button class="btn" type="submit" { (icon(Icon::Search)) span { "Search" } } | ||
| 837 | } | ||
| 838 | @if !langs.is_empty() { | ||
| 839 | (language_bar(&langs)) | ||
| 840 | } | ||
| 841 | (tree_table(&ctx.owner.username, &ctx.repo.path, &rev_name, "", &entries, &annotations)) | ||
| 842 | @if let Some(rendered) = readme { | ||
| 843 | div class="card markdown readme" { (rendered) } | ||
| 844 | } | ||
| 845 | } | ||
| 846 | } | 811 | } |
| 847 | }; | 812 | }; |
| 848 | 813 | ||
| @@ -851,6 +816,55 @@ async fn repo_home( | |||
| 851 | Ok((jar, page(&chrome, &title, body)).into_response()) | 816 | Ok((jar, page(&chrome, &title, body)).into_response()) |
| 852 | } | 817 | } |
| 853 | 818 | ||
| 819 | /// The rich repository landing for a resolved ref: search box, language bar, the | ||
| 820 | /// root tree, and the README. Shared by the repo home (default branch) and the | ||
| 821 | /// root tree view, so selecting a branch or tag in the ref switcher lands on the | ||
| 822 | /// same page for that ref rather than a bare file list. | ||
| 823 | async fn landing_body( | ||
| 824 | state: &AppState, | ||
| 825 | ctx: &RepoCtx, | ||
| 826 | rev_name: &str, | ||
| 827 | rev: &git::Resolved, | ||
| 828 | branches: &[String], | ||
| 829 | ) -> AppResult<Markup> { | ||
| 830 | let entries = git_read(state, &ctx.repo.id, { | ||
| 831 | let rev = rev.clone(); | ||
| 832 | move |repo| repo.tree_entries(&rev, FsPath::new("")) | ||
| 833 | }) | ||
| 834 | .await?; | ||
| 835 | let readme = load_readme(state, &ctx.repo.id, rev, &entries).await; | ||
| 836 | let annotations = git_read(state, &ctx.repo.id, { | ||
| 837 | let rev = rev.clone(); | ||
| 838 | move |repo| repo.tree_annotations(&rev, FsPath::new("")) | ||
| 839 | }) | ||
| 840 | .await | ||
| 841 | .unwrap_or_default(); | ||
| 842 | // The language breakdown of this ref (best-effort). | ||
| 843 | let langs = git_read(state, &ctx.repo.id, { | ||
| 844 | let rev = rev.clone(); | ||
| 845 | move |repo| repo.blob_sizes(&rev) | ||
| 846 | }) | ||
| 847 | .await | ||
| 848 | .map(|files| crate::languages::breakdown(&files)) | ||
| 849 | .unwrap_or_default(); | ||
| 850 | let base = format!("/{}/{}", ctx.owner.username, ctx.repo.path); | ||
| 851 | Ok(html! { | ||
| 852 | (repo_header(state, ctx, Tab::Code, rev_name, branches)) | ||
| 853 | form class="search-row repo-codesearch" method="get" action=(format!("{base}/-/search")) { | ||
| 854 | input type="search" name="q" placeholder="Search this repository…" | ||
| 855 | aria-label="Search this repository"; | ||
| 856 | button class="btn" type="submit" { (icon(Icon::Search)) span { "Search" } } | ||
| 857 | } | ||
| 858 | @if !langs.is_empty() { | ||
| 859 | (language_bar(&langs)) | ||
| 860 | } | ||
| 861 | (tree_table(&ctx.owner.username, &ctx.repo.path, rev_name, "", &entries, &annotations)) | ||
| 862 | @if let Some(rendered) = readme { | ||
| 863 | div class="card markdown readme" { (rendered) } | ||
| 864 | } | ||
| 865 | }) | ||
| 866 | } | ||
| 867 | |||
| 854 | /// Find and render the first matching README in the root tree. | 868 | /// Find and render the first matching README in the root tree. |
| 855 | async fn load_readme( | 869 | async fn load_readme( |
| 856 | state: &AppState, | 870 | state: &AppState, |
| @@ -909,6 +923,17 @@ async fn tree_view( | |||
| 909 | move |repo| repo.resolve_ref(&rev_name) | 923 | move |repo| repo.resolve_ref(&rev_name) |
| 910 | }) | 924 | }) |
| 911 | .await?; | 925 | .await?; |
| 926 | let branches = branch_names(state, &ctx.repo.id).await; | ||
| 927 | |||
| 928 | // At the repo root, render the full landing (search, language bar, README) | ||
| 929 | // for this ref, so switching to a tag/branch mirrors the home page. | ||
| 930 | if path.is_empty() { | ||
| 931 | let body = landing_body(state, &ctx, &rev_name, &rev, &branches).await?; | ||
| 932 | let (jar, chrome) = build_chrome(state, jar, viewer, uri.path().to_string()); | ||
| 933 | let title = format!("{}/{}", ctx.owner.username, ctx.repo.path); | ||
| 934 | return Ok((jar, page(&chrome, &title, body)).into_response()); | ||
| 935 | } | ||
| 936 | |||
| 912 | let entries = git_read(state, &ctx.repo.id, { | 937 | let entries = git_read(state, &ctx.repo.id, { |
| 913 | let rev = rev.clone(); | 938 | let rev = rev.clone(); |
| 914 | let path = path.clone(); | 939 | let path = path.clone(); |
| @@ -923,7 +948,6 @@ async fn tree_view( | |||
| 923 | .await | 948 | .await |
| 924 | .unwrap_or_default(); | 949 | .unwrap_or_default(); |
| 925 | 950 | ||
| 926 | let branches = branch_names(state, &ctx.repo.id).await; | ||
| 927 | let body = html! { | 951 | let body = html! { |
| 928 | (repo_header(state, &ctx, Tab::Code, &rev_name, &branches)) | 952 | (repo_header(state, &ctx, Tab::Code, &rev_name, &branches)) |
| 929 | (breadcrumb(&ctx.owner.username, &ctx.repo.path, &rev_name, &path)) | 953 | (breadcrumb(&ctx.owner.username, &ctx.repo.path, &rev_name, &path)) |