refactor rationalize navbars and toolbars across all views

Navbar: - #nav-logo on / links to git-scm.com; all other views link to / - Logo image is configurable via nav_logo in config.toml (default: /static/git_icon.svg); local paths are normalized to root-relative - #nav-home displays ogit_root_title (or 'Repositories for <user_name>' fallback); project pages display the project directory name - Repo sub-pages render a hierarchical #nav-home with separate links for each directory in the nesting path, ending at the repo summary - Introduced Layout.site record to consolidate branding arguments Toolbar: - Now a standalone sticky #toolbar element below nav#top for repo views - Summary: 'Clone repo' button - Commits: left-aligned scrollable filters + right-aligned pagination - Files: repo-relative breadcrumb pill - README: no toolbar - Toolbar is omitted entirely when empty (no phantom 48px bar) Config: - Added nav_logo (string, default '/static/git_icon.svg') - Renamed title to ogit_root_title (legacy 'title' key still parsed) - Backward-compat test extended to cover both new keys

Commit
9666176d427e1d7895220b1f4c5ce7339e640537
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
config.toml
index 472ec3eb..b6152382 100644..100644
@@ -20,10 +20,15 @@
20 20 # Type: integer (must be positive)
21 21 commits_max_displayed = 10
22 22
23 Removed: # Site title shown in the navigation bar. When empty, defaults to
24 Removed: # "Repositories" (or "Repositories for <user_name>" if user_name is set).
23 Added: # Root navigation title. When empty, defaults to "Repositories" (or
24 Added: # "Repositories for <user_name>" if user_name is set).
25 25 # Type: string
26 Removed: title = ""
26 Added: ogit_root_title = ""
27 Added:
28 Added: # Image URL used by the navigation logo. Local paths are normalized to start
29 Added: # at the site root; absolute HTTP(S) and data URLs are also accepted.
30 Added: # Type: string
31 Added: nav_logo = "/static/git_icon.svg"
27 32
28 33 # Network interface to bind the HTTP server to.
29 34 # Type: string (IP address)
lib/config.ml
index 85c45e86..ed5793f4 100644..100644
@@ -8,6 +8,7 @@
8 8 git_project_root : string;
9 9 commits_max_displayed : int;
10 10 title : string;
11 Added: nav_logo : string;
11 12 host : string;
12 13 port : int;
13 14 favorite_repositories : string list;
@@ -30,6 +31,7 @@
30 31 git_project_root = "/srv/git";
31 32 commits_max_displayed = 10;
32 33 title = "";
34 Added: nav_logo = "/static/git_icon.svg";
33 35 host = "127.0.0.1";
34 36 port = 8081;
35 37 favorite_repositories = [];
@@ -57,7 +59,8 @@
57 59 ("default_branch", TString t.default_branch);
58 60 ("git_project_root", TString t.git_project_root);
59 61 ("commits_max_displayed", TInt t.commits_max_displayed);
60 Removed: ("title", TString t.title);
62 Added: ("ogit_root_title", TString t.title);
63 Added: ("nav_logo", TString t.nav_logo);
61 64 ("host", TString t.host);
62 65 ("port", TInt t.port);
63 66 ("favorite_repositories", string_list_to_value t.favorite_repositories);
@@ -110,7 +113,12 @@
110 113 in
111 114 let* default_branch = find_string table "default_branch" in
112 115 let* commits_max_displayed = find_int table "commits_max_displayed" in
113 Removed: let* title = find_string_opt table "title" ~default:default.title in
116 Added: let* title =
117 Added: match Types.Table.find_opt (Min.key "ogit_root_title") table with
118 Added: | Some _ -> find_string_opt table "ogit_root_title" ~default:default.title
119 Added: | None -> find_string_opt table "title" ~default:default.title
120 Added: in
121 Added: let* nav_logo = find_string_opt table "nav_logo" ~default:default.nav_logo in
114 122 let* host = find_string_opt table "host" ~default:default.host in
115 123 let* port = find_int_opt table "port" ~default:default.port in
116 124 let* favorite_repositories =
@@ -131,6 +139,7 @@
131 139 default_branch;
132 140 commits_max_displayed;
133 141 title;
142 Added: nav_logo;
134 143 host;
135 144 port;
136 145 favorite_repositories;
lib/handlers.ml
index df1851d1..4fb1b3a3 100644..100644
@@ -22,6 +22,10 @@
22 22 else "Repositories for " ^ config.Config.user_name
23 23 else config.Config.title
24 24
25 Added: let site config =
26 Added: Layout.site ~user_name:config.Config.user_name ~root_title:(root_title config)
27 Added: ~nav_logo:config.Config.nav_logo
28 Added:
25 29 let collect_repo_paths nodes =
26 30 let rec walk prefix acc = function
27 31 | Resolvers.Repo { repo_name; _ } ->
@@ -87,9 +91,8 @@
87 91 config.Config.favorite_repositories
88 92 in
89 93 let readme = Resolvers.read_root_readme config in
90 Removed: Views.root ~user_name:config.Config.user_name
91 Removed: ~root_title:(root_title config) ~dates ~favorites:sorted_favorites
92 Removed: ~archived ?readme regular
94 Added: Views.root (site config) ~dates ~favorites:sorted_favorites ~archived
95 Added: ?readme regular
93 96 | Error error -> error_response error
94 97
95 98 module Repo = struct
@@ -99,8 +102,7 @@
99 102 | Error error -> error_response error
100 103
101 104 let view_context config repository =
102 Removed: Views.Repo.context ~user_name:config.Config.user_name
103 Removed: ~root_title:(root_title config)
105 Added: Views.Repo.context ~site:(site config)
104 106 ~repo:(Resolvers.repository_name repository)
105 107 ~description:(Resolvers.repository_description repository)
106 108
@@ -328,8 +330,7 @@
328 330 repo_paths
329 331 in
330 332 let readme = Resolvers.read_subdir_readme config subdir in
331 Removed: Views.root ~user_name:config.Config.user_name
332 Removed: ~root_title:(root_title config) ~dates ~prefix:subdir ?readme nodes
333 Added: Views.root (site config) ~dates ~prefix:subdir ?readme nodes
333 334 | Error error -> error_response error
334 335
335 336 let routes config =
lib/static/styles.css
index e8721b1f..390d8a2a 100644..100644
@@ -1,8 +1,8 @@
1 1 @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap");
2 2
3 3 :root {
4 Removed: --nav-height: 44px;
5 Removed: --toolbar-height: 44px;
4 Added: --nav-height: 48px;
5 Added: --toolbar-height: 52px;
6 6 --target-size: 44px;
7 7 }
8 8
@@ -111,6 +111,7 @@
111 111 align-items: center;
112 112 min-height: var(--nav-height);
113 113 box-sizing: border-box;
114 Added: flex-shrink: 0;
114 115 }
115 116
116 117 .nav-toggle {
@@ -126,28 +127,60 @@
126 127 display: none;
127 128 }
128 129
129 Removed: .nav-home {
130 Added: #nav-home {
130 131 display: flex;
131 132 align-items: center;
132 Removed: padding: 0.5em 1em;
133 Added: min-width: 0;
133 134 min-height: var(--target-size);
134 135 border-radius: 0.25rem;
135 136 font-family: monospace;
136 137 box-sizing: border-box;
138 Added: white-space: nowrap;
137 139 }
138 140
139 Removed: .toolbar ~ .nav-home,
140 Removed: .nav-home:has(~ .toolbar) {
141 Added: a#nav-home {
142 Added: padding: 0.5em 1em;
143 Added: }
144 Added:
145 Added: a#nav-home:hover {
146 Added: background-color: white;
147 Added: color: black;
148 Added: text-decoration: none;
149 Added: }
150 Added:
151 Added: #nav-home.repo-hierarchy {
152 Added: padding: 0 0.75em;
153 Added: overflow-x: auto;
154 Added: scrollbar-width: none;
155 Added: }
156 Added:
157 Added: #nav-home.repo-hierarchy::-webkit-scrollbar {
141 158 display: none;
142 159 }
143 160
144 Removed: .nav-home:hover {
161 Added: #nav-home.repo-hierarchy > a {
162 Added: display: inline-flex;
163 Added: align-items: center;
164 Added: flex-shrink: 0;
165 Added: min-height: var(--target-size);
166 Added: padding: 0 0.25em;
167 Added: border-radius: 0.25rem;
168 Added: box-sizing: border-box;
169 Added: }
170 Added:
171 Added: #nav-home.repo-hierarchy > a:hover {
145 172 background-color: white;
146 173 color: black;
147 174 text-decoration: none;
148 175 }
149 176
150 Removed: .nav-logo {
177 Added: .nav-home-sep {
178 Added: flex-shrink: 0;
179 Added: padding: 0 0.15em;
180 Added: color: #6ab0d4;
181 Added: }
182 Added:
183 Added: #nav-logo {
151 184 display: flex;
152 185 align-items: center;
153 186 padding: 0.5em;
@@ -156,21 +189,22 @@
156 189 box-sizing: border-box;
157 190 }
158 191
159 Removed: .nav-logo:hover {
192 Added: #nav-logo:hover {
160 193 background-color: #333;
161 194 text-decoration: none;
162 195 }
163 196
164 Removed: .nav-logo .site-logo {
197 Added: #nav-logo .site-logo {
165 198 width: 2em;
166 199 height: 2em;
200 Added: object-fit: contain;
167 201 }
168 202
169 203 nav#top ul {
170 204 display: flex;
171 205 flex-wrap: nowrap;
172 206 list-style-type: none;
173 Removed: padding: 0.25em;
207 Added: padding: 0 0.25em;
174 208 margin: 0;
175 209 margin-left: auto;
176 210 gap: 0.25em;
@@ -512,27 +546,45 @@
512 546 color: #fbb6d0;
513 547 }
514 548
515 Removed: /* Toolbar — persistent bar between navbar and content in commits/files views */
549 Added: /* Repository toolbar — a sticky sibling directly below nav#top */
516 550
517 Removed: .toolbar {
551 Added: #toolbar {
552 Added: position: sticky;
553 Added: top: var(--nav-height);
554 Added: z-index: 9;
518 555 display: flex;
519 556 align-items: center;
520 557 gap: 0.75em;
521 Removed: height: var(--nav-height);
558 Added: min-height: var(--toolbar-height);
522 559 box-sizing: border-box;
523 Removed: flex: 1;
524 560 min-width: 0;
525 Removed: background-color: black;
526 Removed: padding: 0 0.75em;
527 Removed: overflow-x: auto;
528 Removed: scrollbar-width: none;
529 Removed: flex-wrap: nowrap;
561 Added: padding: 0.25em 1em;
562 Added: overflow: hidden;
563 Added: background-color: #111;
564 Added: border-bottom: 1px solid #303030;
565 Added: flex-shrink: 0;
530 566 }
531 567
532 Removed: .toolbar::-webkit-scrollbar {
533 Removed: display: none;
568 Added: .toolbar-button {
569 Added: display: inline-flex;
570 Added: align-items: center;
571 Added: justify-content: center;
572 Added: flex-shrink: 0;
573 Added: min-height: calc(var(--target-size) - 0.5em);
574 Added: padding: 0.35em 0.9em;
575 Added: border: 1px solid #444;
576 Added: border-radius: 0.35rem;
577 Added: background-color: #2a2a2a;
578 Added: font-weight: 600;
579 Added: box-sizing: border-box;
534 580 }
535 581
582 Added: .toolbar-button:hover {
583 Added: background-color: white;
584 Added: color: black;
585 Added: text-decoration: none;
586 Added: }
587 Added:
536 588 .toolbar-filter {
537 589 display: inline-flex;
538 590 align-items: center;
@@ -606,14 +658,14 @@
606 658 margin: 0;
607 659 box-sizing: border-box;
608 660 }
609 Removed: }
610 661
611 662 .toolbar-pagination {
612 663 display: flex;
613 664 align-items: center;
614 665 justify-content: flex-end;
666 Added: flex-shrink: 0;
615 667 gap: 0.5em;
616 Removed: margin-top: 1em;
668 Added: margin-left: auto;
617 669 }
618 670
619 671 .pagination-btn {
@@ -729,15 +781,23 @@
729 781 .path-pill {
730 782 display: inline-flex;
731 783 align-items: center;
732 Removed: flex-shrink: 0;
784 Added: max-width: 100%;
785 Added: min-width: 0;
733 786 padding: 0.4em 0.9em;
787 Added: overflow-x: auto;
788 Added: scrollbar-width: none;
734 789 border-radius: 9999px;
735 790 font-size: 0.85em;
736 791 font-family: monospace;
792 Added: white-space: nowrap;
737 793 background-color: #1a3a4d;
738 794 color: #93dbfd;
739 795 }
740 796
797 Added: .path-pill::-webkit-scrollbar {
798 Added: display: none;
799 Added: }
800 Added:
741 801 .path-pill a:hover {
742 802 text-decoration: underline;
743 803 }
@@ -785,6 +845,10 @@
785 845 background-color: #181818;
786 846 }
787 847
848 Added: body.has-toolbar details > .tree-toggle {
849 Added: top: calc(var(--nav-height) + var(--toolbar-height));
850 Added: }
851 Added:
788 852 #repositories details > .tree-toggle,
789 853 .repo-section details > .tree-toggle {
790 854 position: static;
@@ -1046,8 +1110,19 @@
1046 1110 border-radius: 0;
1047 1111 }
1048 1112
1049 Removed: nav#top .nav-home {
1113 Added: nav#top #nav-home {
1050 1114 margin-right: auto;
1115 Added: }
1116 Added:
1117 Added: #toolbar {
1118 Added: padding-right: 0.5em;
1119 Added: padding-left: 0.5em;
1120 Added: }
1121 Added:
1122 Added: details > .tree-toggle,
1123 Added: body.has-toolbar details > .tree-toggle,
1124 Added: .section-toggle {
1125 Added: top: 0;
1051 1126 }
1052 1127
1053 1128 nav#top ul {
lib/views/layout.ml
index a294158f..09a2bc40 100644..100644
@@ -3,7 +3,10 @@
3 3 open Dream_html
4 4
5 5 type page = Summary | Commits | Files | Branches | Tags | Readme
6 Added: type site = { user_name : string; root_title : string; nav_logo : string }
6 7
8 Added: let site ~user_name ~root_title ~nav_logo = { user_name; root_title; nav_logo }
9 Added:
7 10 type body_data = {
8 11 title : string;
9 12 repo : string option;
@@ -22,21 +25,70 @@
22 25 | Tags -> (Routes.Tags repo, "Tags", Tags)
23 26 | Readme -> (Routes.Readme repo, "README", Readme)
24 27
25 Removed: let rootnav ~title:nav_title ?home_href () =
28 Added: let normalize_asset_url source =
29 Added: if
30 Added: String.starts_with ~prefix:"/" source
31 Added: || String.starts_with ~prefix:"http://" source
32 Added: || String.starts_with ~prefix:"https://" source
33 Added: || String.starts_with ~prefix:"data:" source
34 Added: then source
35 Added: else "/" ^ source
36 Added:
37 Added: let nav_logo ~href:logo_href ~alt:alt_text logo =
26 38 HTML.(
39 Added: a
40 Added: [ id "nav-logo"; href "%s" logo_href ]
41 Added: [
42 Added: img
43 Added: [
44 Added: src "%s" (normalize_asset_url logo);
45 Added: alt "%s" alt_text;
46 Added: class_ "site-logo";
47 Added: ];
48 Added: ])
49 Added:
50 Added: let rootnav ~title:nav_title ~nav_logo:logo ?home_href () =
51 Added: let logo_href, logo_alt =
52 Added: match home_href with
53 Added: | None -> ("https://git-scm.com", "Git website")
54 Added: | Some _ -> ("/", "Repository list")
55 Added: in
56 Added: let home_href = Option.value home_href ~default:"/" in
57 Added: HTML.(
27 58 nav
28 59 [ id "top"; Aria.label "Site navigation" ]
29 60 [
30 Removed: a
31 Removed: [ href "https://git-scm.com"; class_ "nav-logo" ]
32 Removed: [ img [ src "/static/git_icon.svg"; alt "Git"; class_ "site-logo" ] ];
33 Removed: (match home_href with
34 Removed: | None -> span [ class_ "nav-home" ] [ txt "%s" nav_title ]
35 Removed: | Some href_val ->
36 Removed: a [ href "%s" href_val; class_ "nav-home" ] [ txt "%s" nav_title ]);
61 Added: nav_logo ~href:logo_href ~alt:logo_alt logo;
62 Added: a [ id "nav-home"; href "%s" home_href ] [ txt "%s" nav_title ];
37 63 ])
38 64
39 Removed: let topnav ?(active = Summary) ?(toolbar = []) repo =
65 Added: let repo_home repo =
66 Added: let segments =
67 Added: String.split_on_char '/' repo |> List.filter (fun segment -> segment <> "")
68 Added: in
69 Added: let last_index = List.length segments - 1 in
70 Added: let nodes =
71 Added: List.mapi
72 Added: (fun index segment ->
73 Added: let path = String.concat "/" (List_ext.take (index + 1) segments) in
74 Added: let repo_link =
75 Added: if index = last_index then
76 Added: Routes.link_to (Repo repo) (txt "%s" segment)
77 Added: else Routes.link_to (Project_dir path) (txt "%s" segment)
78 Added: in
79 Added: if index = 0 then repo_link
80 Added: else
81 Added: HTML.(
82 Added: null
83 Added: [
84 Added: span [ class_ "nav-home-sep"; Aria.hidden true ] [ txt "/" ];
85 Added: repo_link;
86 Added: ]))
87 Added: segments
88 Added: in
89 Added: HTML.(span [ id "nav-home"; class_ "repo-hierarchy" ] nodes)
90 Added:
91 Added: let topnav ?(active = Summary) ~nav_logo:logo repo =
40 92 let nav_items =
41 93 List.map (page_to_nav_item repo)
42 94 [ Summary; Commits; Files; Branches; Tags; Readme ]
@@ -48,23 +100,25 @@
48 100 HTML.(
49 101 nav
50 102 [ id "top"; Aria.label "Repository navigation" ]
51 Removed: ([
52 Removed: Routes.link_to Root
53 Removed: ~other_attrs:[ class_ "nav-logo" ]
54 Removed: (img [ src "/static/git_icon.svg"; alt "Home"; class_ "site-logo" ]);
55 Removed: Routes.link_to (Repo repo)
56 Removed: ~other_attrs:[ class_ "nav-home" ]
57 Removed: (txt "%s" repo);
58 Removed: ]
59 Removed: @ toolbar
60 Removed: @ [
61 Removed: input [ type_ "checkbox"; id "nav-toggle"; class_ "nav-toggle" ];
62 Removed: label
63 Removed: [ for_ "nav-toggle"; class_ "nav-hamburger"; Aria.label "Menu" ]
64 Removed: [ txt "\xe2\x8b\xae" ];
65 Removed: ul [ id "nav-links" ] (List.map li_of_item nav_items);
66 Removed: ]))
103 Added: [
104 Added: nav_logo ~href:"/" ~alt:"Repository list" logo;
105 Added: repo_home repo;
106 Added: input [ type_ "checkbox"; id "nav-toggle"; class_ "nav-toggle" ];
107 Added: label
108 Added: [ for_ "nav-toggle"; class_ "nav-hamburger"; Aria.label "Menu" ]
109 Added: [ txt "\xe2\x8b\xae" ];
110 Added: ul [ id "nav-links" ] (List.map li_of_item nav_items);
111 Added: ])
67 112
113 Added: let repo_toolbar children =
114 Added: match children with
115 Added: | [] -> HTML.null []
116 Added: | _ ->
117 Added: HTML.(
118 Added: div
119 Added: [ id "toolbar"; role `toolbar; Aria.label "Repository toolbar" ]
120 Added: children)
121 Added:
68 122 let page_header ~has_repo page_title subtitle =
69 123 let subtitle =
70 124 if String.starts_with ~prefix:"Unnamed repository" subtitle then ""
@@ -147,15 +201,25 @@
147 201 [ id "bottom-nav"; Aria.label "Mobile navigation" ]
148 202 [ ul [ id "bottom-nav-links" ] (List.map li_of_item items) ])
149 203
150 Removed: let body ~user_name ~root_title page_data =
204 Added: let body site page_data =
151 205 let open HTML in
152 Removed: body []
206 Added: let body_attrs =
207 Added: match (page_data.repo, page_data.toolbar) with
208 Added: | Some _, _ :: _ -> [ class_ "has-toolbar" ]
209 Added: | _ -> []
210 Added: in
211 Added: body body_attrs
153 212 [
154 213 a [ href "#main"; class_ "skip-link" ] [ txt "Skip to content" ];
155 214 (match page_data.repo with
156 Removed: | None -> rootnav ~title:root_title ?home_href:page_data.home_href ()
215 Added: | None ->
216 Added: rootnav ~title:site.root_title ~nav_logo:site.nav_logo
217 Added: ?home_href:page_data.home_href ()
157 218 | Some repo ->
158 Removed: topnav ~active:page_data.active ~toolbar:page_data.toolbar repo);
219 Added: topnav ~active:page_data.active ~nav_logo:site.nav_logo repo);
220 Added: (match page_data.repo with
221 Added: | None -> HTML.null []
222 Added: | Some _ -> repo_toolbar page_data.toolbar);
159 223 HTML.main
160 224 [ id "main" ]
161 225 ((match page_data.repo with
@@ -166,12 +230,10 @@
166 230 (match page_data.repo with
167 231 | None -> HTML.null []
168 232 | Some repo -> bottomnav ~active:page_data.active repo);
169 Removed: page_footer user_name;
233 Added: page_footer site.user_name;
170 234 script []
171 235 {|document.addEventListener("DOMContentLoaded",function(){var b=document.getElementById("blob");if(!b||typeof hljs==="undefined")return;var cls=b.className.match(/language-([\w-]+)/);if(!cls)return;var lang=cls[1];b.querySelectorAll("span.line").forEach(function(el){var r=hljs.highlight(el.textContent,{language:lang,ignoreIllegals:true});el.innerHTML=r.value})});|};
172 236 ]
173 237
174 Removed: let render ?(page_title = "Ogit") ~user_name ~root_title body_data =
175 Removed: HTML.html
176 Removed: [ HTML.lang "en" ]
177 Removed: [ head page_title; body ~user_name ~root_title body_data ]
238 Added: let render ?(page_title = "Ogit") site body_data =
239 Added: HTML.html [ HTML.lang "en" ] [ head page_title; body site body_data ]
lib/views/repo.ml
index 8c07b3d7..0711a3ee 100644..100644
@@ -2,17 +2,10 @@
2 2
3 3 open Dream_html
4 4
5 Removed: type context = {
6 Removed: repo : string;
7 Removed: description : string;
8 Removed: user_name : string;
9 Removed: root_title : string;
10 Removed: }
11 Removed:
5 Added: type context = { repo : string; description : string; site : Layout.site }
12 6 type commit_message = { summary : string; body : string }
13 7
14 Removed: let context ~user_name ~root_title ~repo ~description =
15 Removed: { repo; description; user_name; root_title }
8 Added: let context ~site ~repo ~description = { repo; description; site }
16 9
17 10 let language_of_filename name =
18 11 match Filename.extension name |> String.lowercase_ascii with
@@ -251,8 +244,7 @@
251 244
252 245 let render_page ?heading ?toolbar context ~active content =
253 246 respond
254 Removed: @@ Layout.render ~user_name:context.user_name ~root_title:context.root_title
255 Removed: ~page_title:(page_title context)
247 Added: @@ Layout.render context.site ~page_title:(page_title context)
256 248 {
257 249 repo = Some context.repo;
258 250 title = Option.value heading ~default:context.repo;
@@ -528,7 +520,17 @@
528 520 [ class_ "readme-inline" ]
529 521 [ h3 [] [ txt "README" ]; div [ class_ "blob" ] formatted ])
530 522 in
531 Removed: render_page context ~active:Summary
523 Added: let clone_link =
524 Added: HTML.(
525 Added: a
526 Added: [
527 Added: href "/%s" context.repo;
528 Added: class_ "toolbar-button";
529 Added: Aria.label "Clone %s" context.repo;
530 Added: ]
531 Added: [ txt "Clone repo" ])
532 Added: in
533 Added: render_page context ~active:Summary ~toolbar:[ clone_link ]
532 534 HTML.
533 535 [
534 536 div
@@ -539,26 +541,6 @@
539 541 ];
540 542 ]
541 543
542 Removed: let toolbar ?(filters = []) content =
543 Removed: let filter_el (filter_name, display, dismiss_href, value_class) =
544 Removed: HTML.(
545 Removed: span
546 Removed: [ class_ "toolbar-filter" ]
547 Removed: [
548 Removed: span [ class_ "%s" value_class ] [ txt "%s" display ];
549 Removed: a
550 Removed: [
551 Removed: href "%s" dismiss_href;
552 Removed: class_ "toolbar-dismiss";
553 Removed: Aria.label "Remove %s filter" filter_name;
554 Removed: ]
555 Removed: [ txt "\xc3\x97" ];
556 Removed: ])
557 Removed: in
558 Removed: let children = content @ List.map filter_el filters in
559 Removed: HTML.(
560 Removed: div [ class_ "toolbar"; role `toolbar; Aria.label "View toolbar" ] children)
561 Removed:
562 544 let commits ?filter_type ?author ?committer ~page ~has_prev ~has_next context
563 545 commits =
564 546 let hide_pill = Option.is_some filter_type in
@@ -596,9 +578,9 @@
596 578 commits_url ?filter_type ?author ?committer ~page context.repo
597 579 in
598 580 let show_pagination = has_prev || has_next in
599 Removed: let filters_span =
581 Added: let filters_toolbar =
600 582 match filters with
601 Removed: | [] -> HTML.null []
583 Added: | [] -> None
602 584 | _ ->
603 585 let filter_el (filter_name, display, dismiss_href, value_class) =
604 586 HTML.(
@@ -615,100 +597,77 @@
615 597 [ txt "\xc3\x97" ];
616 598 ])
617 599 in
618 Removed: HTML.(span [ class_ "toolbar-filters" ] (List.map filter_el filters))
600 Added: Some
601 Added: HTML.(div [ class_ "toolbar-filters" ] (List.map filter_el filters))
619 602 in
620 Removed: let pagination_span =
621 Removed: if not show_pagination then HTML.null []
603 Added: let pagination_toolbar =
604 Added: if not show_pagination then None
622 605 else
623 Removed: HTML.(
624 Removed: nav
625 Removed: [ class_ "toolbar-pagination"; Aria.label "Pagination" ]
626 Removed: [
627 Removed: (if has_prev then
628 Removed: a
629 Removed: [
630 Removed: href "%s" (page_url (page - 1));
631 Removed: class_ "pagination-btn";
632 Removed: Aria.label "Previous page";
633 Removed: ]
634 Removed: [ txt "<" ]
635 Removed: else
636 Removed: span
637 Removed: [
638 Removed: class_ "pagination-btn pagination-disabled"; Aria.hidden true;
639 Removed: ]
640 Removed: [ txt "<" ]);
641 Removed: span
642 Removed: [ class_ "pagination-page"; Aria.current `page ]
643 Removed: [ txt "%d" page ];
644 Removed: (if has_next then
645 Removed: a
646 Removed: [
647 Removed: href "%s" (page_url (page + 1));
648 Removed: class_ "pagination-btn";
649 Removed: Aria.label "Next page";
650 Removed: ]
651 Removed: [ txt ">" ]
652 Removed: else
653 Removed: span
654 Removed: [
655 Removed: class_ "pagination-btn pagination-disabled"; Aria.hidden true;
656 Removed: ]
657 Removed: [ txt ">" ]);
658 Removed: ])
606 Added: Some
607 Added: HTML.(
608 Added: nav
609 Added: [ class_ "toolbar-pagination"; Aria.label "Pagination" ]
610 Added: [
611 Added: (if has_prev then
612 Added: a
613 Added: [
614 Added: href "%s" (page_url (page - 1));
615 Added: class_ "pagination-btn";
616 Added: Aria.label "Previous page";
617 Added: ]
618 Added: [ txt "<" ]
619 Added: else
620 Added: span
621 Added: [
622 Added: class_ "pagination-btn pagination-disabled";
623 Added: Aria.hidden true;
624 Added: ]
625 Added: [ txt "<" ]);
626 Added: span
627 Added: [ class_ "pagination-page"; Aria.current `page ]
628 Added: [ txt "%d" page ];
629 Added: (if has_next then
630 Added: a
631 Added: [
632 Added: href "%s" (page_url (page + 1));
633 Added: class_ "pagination-btn";
634 Added: Aria.label "Next page";
635 Added: ]
636 Added: [ txt ">" ]
637 Added: else
638 Added: span
639 Added: [
640 Added: class_ "pagination-btn pagination-disabled";
641 Added: Aria.hidden true;
642 Added: ]
643 Added: [ txt ">" ]);
644 Added: ])
659 645 in
660 Removed: let tb =
661 Removed: HTML.(
662 Removed: div
663 Removed: [ class_ "toolbar"; role `toolbar; Aria.label "Filters" ]
664 Removed: [ filters_span ])
646 Added: let toolbar =
647 Added: List.filter_map Fun.id [ filters_toolbar; pagination_toolbar ]
665 648 in
666 Removed: render_page context ~active:Commits ~toolbar:[ tb ]
649 Added: render_page context ~active:Commits ~toolbar
667 650 HTML.
668 651 [
669 Removed: pagination_span;
670 652 ul
671 653 [ id "commit-list" ]
672 654 (List.map
673 655 (li_of_commit ~hide_pill ?filter_type ?author ?committer
674 656 context.repo)
675 657 commits);
676 Removed: pagination_span;
677 658 ]
678 659
679 660 let breadcrumb_pill repo (trail : (string * string) list) =
680 Removed: let repo_segments = String.split_on_char '/' repo in
681 Removed: let repo_anchors =
682 Removed: match repo_segments with
683 Removed: | [] -> [ txt "%s" repo ]
684 Removed: | [ single ] ->
685 Removed: [
686 Removed: Routes.link_to (Files repo)
687 Removed: ~other_attrs:[ HTML.class_ "path-pill-link" ]
688 Removed: (txt "%s" single);
689 Removed: ]
690 Removed: | segments ->
691 Removed: let last_idx = List.length segments - 1 in
692 Removed: List.mapi
693 Removed: (fun i seg ->
694 Removed: let node =
695 Removed: if i = last_idx then
696 Removed: Routes.link_to (Files repo)
697 Removed: ~other_attrs:[ HTML.class_ "path-pill-link" ]
698 Removed: (txt "%s" seg)
699 Removed: else
700 Removed: let dir_path =
701 Removed: String.concat "/" (List_ext.take (i + 1) segments)
702 Removed: in
703 Removed: Routes.link_to (Project_dir dir_path)
704 Removed: ~other_attrs:[ HTML.class_ "path-pill-link" ]
705 Removed: (txt "%s" seg)
706 Removed: in
707 Removed: if i = 0 then node
708 Removed: else
709 Removed: HTML.(null [ span [ class_ "path-pill-sep" ] [ txt "/" ]; node ]))
710 Removed: segments
661 Added: let repo_name =
662 Added: match List.rev (String.split_on_char '/' repo) with
663 Added: | name :: _ -> name
664 Added: | [] -> repo
711 665 in
666 Added: let repo_anchor =
667 Added: Routes.link_to (Files repo)
668 Added: ~other_attrs:[ HTML.class_ "path-pill-link" ]
669 Added: (txt "%s" repo_name)
670 Added: in
712 671 let file_segments =
713 672 List.map
714 673 (fun (entry_name, hash) ->
@@ -723,11 +682,10 @@
723 682 ]))
724 683 trail
725 684 in
726 Removed: HTML.(span [ class_ "path-pill" ] (repo_anchors @ file_segments))
685 Added: HTML.(span [ class_ "path-pill" ] (repo_anchor :: file_segments))
727 686
728 687 let files context trail ?readme (entries : Resolvers.Tree.tree_node list) =
729 688 let pill = breadcrumb_pill context.repo trail in
730 Removed: let tb = toolbar [ pill ] in
731 689 let readme_section =
732 690 match readme with
733 691 | None -> HTML.null []
@@ -755,7 +713,7 @@
755 713 [ class_ "readme-inline" ]
756 714 [ h3 [] [ txt "README" ]; div [ class_ "blob" ] formatted ])
757 715 in
758 Removed: render_page context ~active:Files ~toolbar:[ tb ]
716 Added: render_page context ~active:Files ~toolbar:[ pill ]
759 717 HTML.
760 718 [
761 719 ul [ id "file-tree" ] (List.map (li_of_tree_node context.repo) entries);
@@ -805,8 +763,12 @@
805 763 [ Routes.link_to (Raw_file (context.repo, hash)) (txt "View raw") ])
806 764 | [] -> HTML.null []
807 765 in
808 Removed: render_page context ~active
809 Removed: ~toolbar:[ toolbar [ breadcrumb_pill context.repo trail ] ]
766 Added: let toolbar =
767 Added: match active with
768 Added: | Layout.Readme -> []
769 Added: | _ -> [ breadcrumb_pill context.repo trail ]
770 Added: in
771 Added: render_page context ~active ~toolbar
810 772 HTML.[ raw_link; div blob_attrs formatted_blob ]
811 773
812 774 let commit context (commit : Resolvers.Commit.t) diff =
lib/views/root.ml
index 94f9a273..668809e8 100644..100644
@@ -55,7 +55,7 @@
55 55 ];
56 56 ])
57 57
58 Removed: let render ~user_name ~root_title ~dates ?(prefix = "") ?(favorites = [])
58 Added: let render (site : Layout.site) ~dates ?(prefix = "") ?(favorites = [])
59 59 ?(archived = []) ?readme nodes =
60 60 let repo_list list_id nodes =
61 61 HTML.(
@@ -152,10 +152,17 @@
152 152 [ class_ "readme-inline" ]
153 153 [ h3 [] [ txt "README" ]; div [ class_ "blob" ] formatted ])
154 154 in
155 Added: let nav_title =
156 Added: if prefix = "" then site.root_title
157 Added: else
158 Added: match List.rev (String.split_on_char '/' prefix) with
159 Added: | name :: _ -> name
160 Added: | [] -> prefix
161 Added: in
155 162 respond
156 Removed: @@ Layout.render ~user_name ~root_title
163 Added: @@ Layout.render site
157 164 {
158 Removed: title = root_title;
165 Added: title = nav_title;
159 166 repo = None;
160 167 subtitle = "";
161 168 active = Summary;
test/test_config.ml
index 3af2cc7c..bbf6010d 100644..100644
@@ -12,6 +12,7 @@
12 12 git_project_root = "/srv/git";
13 13 commits_max_displayed = 25;
14 14 title = "";
15 Added: nav_logo = "/branding/logo.svg";
15 16 host = "127.0.0.1";
16 17 port = 9000;
17 18 favorite_repositories = [];
@@ -29,6 +30,7 @@
29 30 Alcotest.(check int)
30 31 "commits" config.commits_max_displayed config'.commits_max_displayed;
31 32 Alcotest.(check string) "host" config.host config'.host;
33 Added: Alcotest.(check string) "nav_logo" config.nav_logo config'.nav_logo;
32 34 Alcotest.(check int) "port" config.port config'.port
33 35 | Error error -> fail_config_error error)
34 36
@@ -38,10 +40,15 @@
38 40 Printf.fprintf channel
39 41 "default_branch = \"main\"\n\
40 42 git_project_root = \"/srv/git\"\n\
41 Removed: commits_max_displayed = 10\n");
43 Added: commits_max_displayed = 10\n\
44 Added: title = \"Legacy repositories\"\n");
42 45 match Ogit.Config.read_file ~file () with
43 46 | Ok config ->
44 47 Alcotest.(check string) "default user_name" "" config.user_name;
48 Added: Alcotest.(check string)
49 Added: "legacy title" "Legacy repositories" config.title;
50 Added: Alcotest.(check string)
51 Added: "default nav logo" "/static/git_icon.svg" config.nav_logo;
45 52 Alcotest.(check string) "default host" "127.0.0.1" config.host;
46 53 Alcotest.(check int) "default port" 8081 config.port
47 54 | Error error -> fail_config_error error)