feat consistent navbar on all pages, configurable root title

- Add box-sizing: border-box to .nav-logo and .nav-home so they match the height/hover behavior of nav ul li a items. - Show a navbar on the root '/' page with the logo and a nav-home span displaying 'Repositories for <user>' (or a custom title). - Add an optional 'title' field to config that overrides the default root page heading when set. - Remove the old page-header from the root page in favor of the unified navigation bar.

Commit
8398af71a9ec6951897ab709880014b8a150992b
Author
GPT-5.6 Sol <kiro@amazon.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
lib/config.ml
index 21c3583b..39c28f4e 100644..100644
@@ -7,6 +7,7 @@
7 7 default_branch : string;
8 8 git_project_root : string;
9 9 commits_max_displayed : int;
10 Added: title : string;
10 11 host : string;
11 12 port : int;
12 13 }
@@ -30,6 +31,7 @@
30 31 default_branch = "main";
31 32 git_project_root = Filename.concat home "git";
32 33 commits_max_displayed = 10;
34 Added: title = "";
33 35 host = "127.0.0.1";
34 36 port = 8081;
35 37 }
@@ -52,6 +54,7 @@
52 54 ("default_branch", TString t.default_branch);
53 55 ("git_project_root", TString t.git_project_root);
54 56 ("commits_max_displayed", TInt t.commits_max_displayed);
57 Added: ("title", TString t.title);
55 58 ("host", TString t.host);
56 59 ("port", TInt t.port);
57 60 ]
@@ -92,6 +95,7 @@
92 95 let* user = find_string table "user" in
93 96 let* default_branch = find_string table "default_branch" in
94 97 let* commits_max_displayed = find_int table "commits_max_displayed" in
98 Added: let* title = find_string_opt table "title" ~default:default.title in
95 99 let* host = find_string_opt table "host" ~default:default.host in
96 100 let* port = find_int_opt table "port" ~default:default.port in
97 101 if commits_max_displayed <= 0 then
@@ -105,6 +109,7 @@
105 109 user;
106 110 default_branch;
107 111 commits_max_displayed;
112 Added: title;
108 113 host;
109 114 port;
110 115 }
lib/handlers.ml
index 0e9c365e..39e6cf52 100644..100644
@@ -11,9 +11,15 @@
11 11 let message = Format.asprintf "%a" Resolvers.pp_error error in
12 12 Views.error_page ~status ~title message
13 13
14 Added: let root_title config =
15 Added: if config.Config.title = "" then "Repositories for " ^ config.Config.user
16 Added: else config.Config.title
17 Added:
14 18 let root config _request =
15 19 match Resolvers.repositories config with
16 Removed: | Ok repositories -> Views.root ~user:config.Config.user repositories
20 Added: | Ok repositories ->
21 Added: Views.root ~user:config.Config.user ~root_title:(root_title config)
22 Added: repositories
17 23 | Error error -> error_response error
18 24
19 25 module Repo = struct
@@ -23,7 +29,7 @@
23 29 | Error error -> error_response error
24 30
25 31 let view_context config repository =
26 Removed: Views.Repo.context ~user:config.Config.user
32 Added: Views.Repo.context ~user:config.Config.user ~root_title:(root_title config)
27 33 ~repo:(Resolvers.repository_name repository)
28 34 ~description:(Resolvers.repository_description repository)
29 35
lib/static/styles.css
index fa6b8947..fbaea2b9 100644..100644
@@ -101,6 +101,7 @@
101 101 min-height: 44px;
102 102 border-radius: 0.25rem;
103 103 font-family: monospace;
104 Added: box-sizing: border-box;
104 105 }
105 106
106 107 .nav-home:hover {
@@ -114,6 +115,7 @@
114 115 padding: 0.5em;
115 116 min-height: 44px;
116 117 border-radius: 0.25rem;
118 Added: box-sizing: border-box;
117 119 }
118 120
119 121 .nav-logo:hover {
lib/views/layout.ml
index e501d5e4..4759b315 100644..100644
@@ -20,6 +20,17 @@
20 20 | Tags -> (Routes.Tags repo, "Tags", Tags)
21 21 | Readme -> (Routes.Readme repo, "README", Readme)
22 22
23 Added: let rootnav ~title:nav_title =
24 Added: HTML.(
25 Added: nav
26 Added: [ id "top"; Aria.label "Site navigation" ]
27 Added: [
28 Added: Routes.link_to Root
29 Added: ~other_attrs:[ class_ "nav-logo" ]
30 Added: (img [ src "/static/git_icon.svg"; alt "Home"; class_ "site-logo" ]);
31 Added: span [ class_ "nav-home" ] [ txt "%s" nav_title ];
32 Added: ])
33 Added:
23 34 let topnav ?(active = Summary) repo =
24 35 let nav_items =
25 36 List.map (page_to_nav_item repo)
@@ -87,23 +98,25 @@
87 98 link [ rel "icon"; type_ "image/x-icon"; href "/static/git_icon.svg" ];
88 99 ]
89 100
90 Removed: let body ~user page_data =
101 Added: let body ~user ~root_title page_data =
91 102 let open HTML in
92 103 body []
93 104 [
94 105 a [ href "#main"; class_ "skip-link" ] [ txt "Skip to content" ];
106 Added: (match page_data.repo with
107 Added: | None -> rootnav ~title:root_title
108 Added: | Some repo -> topnav ~active:page_data.active repo);
95 109 page_header
96 110 ~has_repo:(Option.is_some page_data.repo)
97 111 page_data.title page_data.subtitle;
98 Removed: (match page_data.repo with
99 Removed: | None -> HTML.null []
100 Removed: | Some repo -> topnav ~active:page_data.active repo);
101 112 div [ id "main" ] page_data.content;
102 113 page_footer user;
103 114 ]
104 115
105 Removed: let render ?(page_title = "Ogit") ~user body_data =
106 Removed: HTML.html [ HTML.lang "en" ] [ head page_title; body ~user body_data ]
116 Added: let render ?(page_title = "Ogit") ~user ~root_title body_data =
117 Added: HTML.html
118 Added: [ HTML.lang "en" ]
119 Added: [ head page_title; body ~user ~root_title body_data ]
107 120
108 121 let error_page ?(title = "Request failed") ?(status = `Internal_Server_Error)
109 122 message =
lib/views/repo.ml
index 892c38e2..ad887e0a 100644..100644
@@ -2,15 +2,24 @@
2 2
3 3 open Dream_html
4 4
5 Removed: type context = { repo : string; description : string; user : string }
5 Added: type context = {
6 Added: repo : string;
7 Added: description : string;
8 Added: user : string;
9 Added: root_title : string;
10 Added: }
11 Added:
6 12 type commit_message = { summary : string; body : string }
7 13
8 Removed: let context ~user ~repo ~description = { repo; description; user }
14 Added: let context ~user ~root_title ~repo ~description =
15 Added: { repo; description; user; root_title }
16 Added:
9 17 let page_title context = context.repo ^ " — " ^ context.description
10 18
11 19 let render_page ?heading context ~active content =
12 20 respond
13 Removed: @@ Layout.render ~user:context.user ~page_title:(page_title context)
21 Added: @@ Layout.render ~user:context.user ~root_title:context.root_title
22 Added: ~page_title:(page_title context)
14 23 {
15 24 repo = Some context.repo;
16 25 title = Option.value heading ~default:context.repo;
lib/views/root.ml
index 36e8c297..fa939698 100644..100644
@@ -2,7 +2,7 @@
2 2
3 3 open Dream_html
4 4
5 Removed: let render ~user repositories =
5 Added: let render ~user ~root_title repositories =
6 6 let li_of_repo repo =
7 7 HTML.li [] [ Routes.link_to (Routes.Repo repo) (txt "%s" repo) ]
8 8 in
@@ -11,11 +11,11 @@
11 11 div [ id "repositories" ] [ ul [] (List.map li_of_repo repositories) ])
12 12 in
13 13 respond
14 Removed: @@ Layout.render ~user
14 Added: @@ Layout.render ~user ~root_title
15 15 {
16 Removed: title = "Ogit";
16 Added: title = root_title;
17 17 repo = None;
18 Removed: subtitle = "Repositories for " ^ user;
18 Added: subtitle = "";
19 19 active = Summary;
20 20 content = [ all_repositories ];
21 21 }
test/test_config.ml
index 1b0b4d7e..497c15e6 100644..100644
@@ -11,6 +11,7 @@
11 11 default_branch = "main";
12 12 git_project_root = "/srv/git";
13 13 commits_max_displayed = 25;
14 Added: title = "";
14 15 host = "127.0.0.1";
15 16 port = 9000;
16 17 }