(** The repository list, and the project directory pages that share its shape. A project directory is the same page scoped to a subtree, so both are described here. *) (** One entry in the list: either a repository, or a directory that expands to reveal the repositories beneath it. *) let rec repo_row ~prefix ~dates node = match node with | Resolvers.Repo { repo_name; description } -> let path = if prefix = "" then repo_name else prefix ^ "/" ^ repo_name in let described = if description = Resolvers.default_repo_description then [] else [ Ui.inline_text ~class_:"repo-description" description ] in let updated = match List.assoc_opt path dates with | None | Some None -> Ui.nothing | Some (Some date) -> Ui.inline_text ~class_:"commit-ago" (Time_format.relative_time date) in Ui.item [ Ui.link ~href:(Components.url (Routes.Repo path)) (Ui.inline_text ~class_:"repo-name" repo_name :: described); updated; ] | Resolvers.Directory (name, children) -> let path = if prefix = "" then name else prefix ^ "/" ^ name in Components.directory ~route:(Routes.Project_dir path) ~name (List.map (repo_row ~prefix:path ~dates) children) (** Repositories may be grouped into Favorites and Archived sections. With no grouping configured the list renders flat and unheaded, which keeps the common case free of pointless chrome. *) let render (site : Layout.site) ~dates ?(prefix = "") ?(favorites = []) ?(archived = []) ?readme nodes = let repo_list id nodes = Ui.items_of ~id (repo_row ~prefix ~dates) nodes in let optional_group ~class_ ~title ~expanded ~id = function | [] -> Ui.nothing | repos -> Ui.region ~class_ [ Components.group ~expanded ~title [ repo_list id repos ] ] in let favorites_group = optional_group ~class_:"repo-section repo-favorites" ~title:"Favorites" ~expanded:true ~id:"repo-list-favorites" favorites in let archived_group = optional_group ~class_:"repo-section repo-archived" ~title:"Archived" ~expanded:false ~id:"repo-list-archived" archived in let main_group = Ui.block ~id:"repositories" (match (favorites, archived) with | [], [] -> [ repo_list "repo-list" nodes ] | _ when nodes = [] -> [] | _ -> [ Components.group ~expanded:true ~title:"Repositories" [ repo_list "repo-list" nodes ]; ]) in let readme_panel = match readme with | None -> Ui.nothing | Some (blob : Resolvers.Blob.t) -> Components.inline_readme blob.content in (* A project directory page is titled by its own segment; the root page by the configured site title. *) let nav_title = if prefix = "" then site.root_title else match List.rev (String.split_on_char '/' prefix) with | name :: _ -> name | [] -> prefix in Ui.respond @@ Layout.render site { title = nav_title; repo = None; subtitle = ""; active = Summary; toolbar = []; home_href = (if prefix = "" then None else Some (Components.url (Project_dir prefix))); content = [ Ui.block ~class_:"root-layout" [ Ui.block ~class_:"root-repos" [ favorites_group; main_group; archived_group ]; Ui.block ~class_:"root-readme" [ readme_panel ]; ]; ]; }