diff options
author | Marius Peter <marius.peter@tutanota.com> | 2025-03-16 19:12:26 +0100 |
---|---|---|
committer | Marius Peter <marius.peter@tutanota.com> | 2025-03-16 19:12:26 +0100 |
commit | 4d38a55e52bbb88cacb8c5f5a996c7cdbfcc75bb (patch) | |
tree | ed7d34d53e4a820c3a2cc7ce8c525ea9023d4225 | |
parent | 1d1b8113e117be008573a82c5596ac1e25799307 (diff) |
Moderate amount of progress today 💯
-rw-r--r-- | lib/config.ml | 2 | ||||
-rw-r--r-- | lib/handlers.ml | 26 | ||||
-rw-r--r-- | lib/views.ml | 101 |
3 files changed, 88 insertions, 41 deletions
diff --git a/lib/config.ml b/lib/config.ml index d47101f..61810fa 100644 --- a/lib/config.ml +++ b/lib/config.ml @@ -1,2 +1,2 @@ -let git_directory = Filename.concat (Unix.getenv "HOME") "git" +let git_directory = Filename.concat (Unix.getenv "HOME") "git.test" let author = "Marius Peter" diff --git a/lib/handlers.ml b/lib/handlers.ml index 3211929..d50eca8 100644 --- a/lib/handlers.ml +++ b/lib/handlers.ml @@ -1,4 +1,3 @@ -(* Handlers for different routes *) let ogit_root _req = Views.Ogit_root.render () |> Dream_html.respond let repo_root req = @@ -7,19 +6,22 @@ let repo_root req = let repo_tree req = let repo_name = Dream.param req "repo_name" in - let dir_path = Dream.param req "**" in + let dir_path = Dream.query req "path" |> Option.value ~default:"" in Views.Repo_tree.render repo_name dir_path |> Dream_html.respond let repo_blob req = let repo_name = Dream.param req "repo_name" in - let blob_name = Dream.param req "blob_name" in - Views.Repo_blob.render repo_name blob_name |> Dream_html.respond + let blob_path = Dream.query req "path" |> Option.value ~default:"" in + Views.Repo_blob.render repo_name blob_path |> Dream_html.respond -(* Route definitions *) -let all_handlers = [ - Dream.get "/" ogit_root; - Dream.get "/:repo_name" repo_root; - Dream.get "/:repo_name/tree/**" repo_tree; - Dream.get "/:repo_name/blob/:blob_name" repo_blob; - Dream.get "/static/**" (Dream.static "./lib/static"); -] +let all_handlers = + [ + Dream.get "/" ogit_root; + Dream.scope "/:repo_name" [] + [ + Dream.get "/" repo_root; + Dream.get "/tree" repo_tree; + Dream.get "/blob" repo_blob; + ]; + Dream.get "/static/**" (Dream.static "./lib/static"); + ] diff --git a/lib/views.ml b/lib/views.ml index d09892b..6621831 100644 --- a/lib/views.ml +++ b/lib/views.ml @@ -29,7 +29,8 @@ module Layout = struct [ 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"]; + link + [ rel "icon"; type_ "image/x-icon"; href "/static/git_icon.svg" ]; ]; body [] [ @@ -50,7 +51,7 @@ module Topnav = struct let is_active = path = current_path in let attrs = if is_active then [ id "active" ] else [] in let url = - if String.length path = 0 then Printf.sprintf "/%s" repo_path + 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 ] ] @@ -61,7 +62,7 @@ module Topnav = struct ul [] @@ List.map li_of_a [ - ("", "summary"); + ("/", "summary"); ("refs", "refs"); ("log", "log"); ("tree", "tree"); @@ -129,15 +130,14 @@ module Repo_tree = struct open Dream_html open HTML - let full_path repo_path dir_path = - Filename.concat (Filename.concat Config.git_directory repo_path) dir_path + 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_path dir_path = - let dir_full_path = full_path repo_path dir_path in + 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 + 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 @@ -145,34 +145,70 @@ module Repo_tree = struct (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 *) + | 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_path (entry, entry_rel_path, is_dir) = + let link_for_entry repo_name (entry, entry_rel_path, is_dir) = let link = - if is_dir then Printf.sprintf "/%s/tree/%s" repo_path entry_rel_path - else Printf.sprintf "/%s/blob/%s" repo_path entry_rel_path + 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 - li [] [ a [ href "%s" link ] [ txt "%s" entry ] ] + let display_name = if is_dir then entry ^ "/" else entry in + li [] [ a [ href "%s" link ] [ txt "%s" display_name ] ] - (* Render function, now supporting nested directories *) - let render repo_path dir_path = - let title = Filename.concat repo_path dir_path in - let subtitle = "Repository Files" in + (* Generate breadcrumb navigation for the directory path *) + let breadcrumb_navigation repo_name dir_path = + let parts = String.split_on_char '/' dir_path in - let repo_entries = ls_dir repo_path dir_path in + let rec build_paths acc paths = + match paths with + | [] -> List.rev acc + | part :: rest -> + let new_path = + match acc with [] -> part | _ -> List.hd acc ^ "/" ^ part + in + build_paths (new_path :: acc) rest + in + + let breadcrumb_links = + build_paths [] parts + |> List.mapi (fun i path -> + let display_name = List.nth parts i in + a + [ href "%s" (Printf.sprintf "/%s/tree?path=%s" repo_name path) ] + [ txt "%s" display_name ]) + in + + (* Manually intersperse " / " separators *) + let rec intersperse sep = function + | [] -> [] + | [ x ] -> [ x ] + | x :: xs -> x :: txt sep :: intersperse sep xs + in + + match breadcrumb_links with + | [] -> txt "/" + | _ -> span [] (intersperse " / " breadcrumb_links) + + (* Render function *) + let render repo_name dir_path = + let title = repo_name in + (* Only display the repo name as the title *) + let subtitle = "Files" in + + let repo_entries = ls_dir repo_name dir_path in let content = [ - h3 [] [ txt "%s" (Printf.sprintf "Contents of /%s" dir_path) ]; - ul [] (List.map (link_for_entry repo_path) repo_entries); + h3 [] [ breadcrumb_navigation repo_name dir_path ]; + ul [] (List.map (link_for_entry repo_name) repo_entries); ] in let body_data = - { title; subtitle; topnav = Topnav.repo repo_path "tree"; content } + { title; subtitle; topnav = Topnav.repo repo_name "tree"; content } in Layout.application body_data end @@ -187,7 +223,8 @@ module Repo_blob = struct 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 *) + with _ -> + None (* Handle cases where the file doesn't exist or can't be read *) (* Render function *) let render repo_path blob_name = @@ -196,17 +233,25 @@ module Repo_blob = struct match read_blob repo_path blob_name with | Some content -> - let content_display = - pre [] [ code [] [ txt "%s" content ] ] (* Render as code block *) - in + let content_display = pre [] [ code [] [ txt "%s" content ] ] in let body_data = - { title; subtitle; topnav = Topnav.repo repo_path "blob"; content = [content_display] } + { + 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] } + { + title; + subtitle; + topnav = Topnav.repo repo_path "blob"; + content = [ error_message ]; + } in Layout.application body_data end |