type head_data = { page_title : string } type body_data = { title : string; subtitle : string; topnav : Dream_html.node; content : Dream_html.node list; } module Layout = struct open Dream_html open HTML let header title subtitle = null [ h1 [] [ txt "%s" title ]; h2 [] [ txt "%s" subtitle ] ] let footer = let today = Unix.localtime (Unix.time ()) in let year = string_of_int (today.Unix.tm_year + 1900) in let footer_text = Printf.sprintf "Copyright %s %s" year Config.author in footer [] [ txt "%s" footer_text ] let default_head_data = { page_title = "Ogit" } let application ?(head_data = default_head_data) body_data = html [] [ head [] [ title [] "%s" head_data.page_title; link [ rel "stylesheet"; href "/static/styles.css" ]; link [ rel "icon"; type_ "image/x-icon"; href "/static/git_icon.svg" ]; ]; body [] [ header body_data.title body_data.subtitle; body_data.topnav; div [ id "main" ] body_data.content; footer; ]; ] end module Topnav = struct open Dream_html open HTML let repo repo_path current_path = let li_of_a (path, text) = let is_active = path = current_path in let attrs = if is_active then [ id "active" ] else [] in let url = if String.equal path "/" then Printf.sprintf "/%s/" repo_path else Printf.sprintf "/%s/%s" repo_path path in li attrs [ a [ href "%s" url ] [ txt text ] ] in nav [ id "top" ] [ ul [] @@ List.map li_of_a [ ("/", "summary"); ("refs", "refs"); ("log", "log"); ("tree", "tree"); ("commit", "commit"); ("diff", "diff"); ]; ] end module Ogit_root = struct open Dream_html open HTML let repositories_in directory = try let repos = Sys.readdir directory |> Array.to_list |> List.sort String.compare in let li_of_repo repo = li [] [ a [ href "%s" repo ] [ txt "%s" repo ] ] in div [ id "repositories" ] [ ul [] @@ List.map li_of_repo repos ] with Sys_error _ -> div [] [ txt "Error: Unable to read repository list." ] let body_data = { title = "Ogit"; subtitle = "Repositories for " ^ Config.author; topnav = null []; content = [ repositories_in Config.git_directory ]; } let render () = Layout.application body_data end module Repo_root = struct open Dream_html open HTML let branches = let repo_path = Fpath.v "/home/blendux/git.test/ogit.git/" in let load_repo () = Git_unix.Store.v ~dotgit:repo_path repo_path in let store = Lwt_main.run @@ load_repo () in let refs = Lwt_main.run @@ Git_unix.Store.Ref.list @@ Result.get_ok store in refs |> List.map fst |> List.map Git.Reference.to_string let render repo_path = let title = repo_path in let subtitle = Filename.concat Config.git_directory repo_path in let li_of_branch hash = li [] [ txt "%s" hash ] in let recent_commits = Git_unhelpers.get_latest_commits repo_path 10 in let li_of_commit commit = li [] [ a [ href "%s" commit ] [ txt "%s" commit ] ] in let content = [ h3 [] [ txt "Branches" ]; ul [] (List.map li_of_branch branches); h3 [] [ txt "Recent commits" ]; ul [] (List.map li_of_commit recent_commits); ] in let body_data = { title; subtitle; topnav = Topnav.repo repo_path ""; content } in Layout.application body_data end module Repo_tree = struct open Dream_html open HTML let full_path repo_name dir_path = Filename.concat (Filename.concat Config.git_directory repo_name) dir_path (* Helper function to list contents of a given directory *) let ls_dir repo_name dir_path = let dir_full_path = full_path repo_name dir_path in try Sys.readdir dir_full_path |> Array.to_list |> List.filter (fun name -> name <> ".git") (* Exclude .git *) |> List.map (fun entry -> let entry_rel_path = Filename.concat dir_path entry in let full_entry_path = Filename.concat dir_full_path entry in (entry, entry_rel_path, Sys.is_directory full_entry_path)) |> List.sort (fun (a, _, is_dir_a) (b, _, is_dir_b) -> match (is_dir_a, is_dir_b) with | true, false -> -1 (* Directories first *) | false, true -> 1 | _ -> String.compare a b) with Sys_error _ -> [] (* Function to create a link based on file type *) let link_for_entry repo_name (entry, entry_rel_path, is_dir) = let link = if is_dir then Printf.sprintf "/%s/tree?path=%s" repo_name entry_rel_path else Printf.sprintf "/%s/blob?path=%s" repo_name entry_rel_path in let display_name = if is_dir then entry ^ "/" else entry in li [] [ a [ href "%s" link ] [ txt "%s" display_name ] ] (* Render function *) let render repo_name dir_path = let title = repo_name in let subtitle = "Files" in let repo_entries = ls_dir repo_name @@ dir_path in let content = [ txt "%s" dir_path; ul [] (List.map (link_for_entry repo_name) repo_entries); ] in let body_data = { title; subtitle; topnav = Topnav.repo repo_name "tree"; content } in Layout.application body_data end module Repo_blob = struct open Dream_html open HTML let full_path repo_path = Filename.concat Config.git_directory repo_path (* Read the contents of a file *) let read_blob repo_path blob_name = let file_path = Filename.concat (full_path repo_path) blob_name in try Some (In_channel.with_open_text file_path In_channel.input_all) with _ -> None (* Handle cases where the file doesn't exist or can't be read *) (* Render function *) let render repo_path blob_name = let title = blob_name in let subtitle = "File Contents" in match read_blob repo_path blob_name with | Some content -> let content_display = pre [] [ code [] [ txt "%s" content ] ] in let body_data = { title; subtitle; topnav = Topnav.repo repo_path "blob"; content = [ content_display ]; } in Layout.application body_data | None -> let error_message = p [] [ txt "Error: Unable to read file." ] in let body_data = { title; subtitle; topnav = Topnav.repo repo_path "blob"; content = [ error_message ]; } in Layout.application body_data end