[OCaml] Mobile-friendly clone of cgit.
Add breadcrumbs to file browser pages
- Add Tree.find_path to resolve a tree hash to its path trail from root - Render clickable breadcrumb navigation at the top of every /files page - Each path segment links to its corresponding tree - Add .breadcrumbs CSS styling
Changed files
lib/handlers.ml
@@ -40,12 +40,14 @@
40
40
41
41
let files_at_head repo =
42
42
let* tree = Resolvers.Tree.head repo in
43
Removed:
Views.Repo.files repo tree
43
Added:
Views.Repo.files repo [] tree
44
44
45
45
let file_id repo id =
46
46
let* res = Resolvers.blob_or_tree repo id in
47
47
match res with
48
Removed:
| `Tree tree -> Views.Repo.files repo tree
48
Added:
| `Tree tree ->
49
Added:
let* trail = Resolvers.Tree.find_path repo id in
50
Added:
Views.Repo.files repo trail tree
49
51
| `Blob blob -> Views.Repo.file repo blob
50
52
51
53
let branches repo =
lib/resolvers.ml
@@ -285,6 +285,45 @@
285
285
let tree_id = Store.Value.Commit.tree commit |> Store.Hash.to_hex in
286
286
of_id repo tree_id
287
287
| _ -> Lwt_result.fail @@ `Msg "HEAD reference does not point to a commit"
288
Added:
289
Added:
let find_path repo target_hash =
290
Added:
let* store = store repo in
291
Added:
let* head_hash = resolve_head_hash store in
292
Added:
let head_tree_hash =
293
Added:
Lwt_result.bind (Store.read store head_hash) @@ function
294
Added:
| Git.Value.Commit commit ->
295
Added:
Lwt_result.return (Store.Value.Commit.tree commit |> Store.Hash.to_hex)
296
Added:
| _ -> Lwt_result.fail @@ `Msg "HEAD is not a commit"
297
Added:
in
298
Added:
let* root_hash = head_tree_hash in
299
Added:
if root_hash = target_hash then Lwt_result.return []
300
Added:
else
301
Added:
let rec search trail tree_hash =
302
Added:
let* hash = hash_of_hex tree_hash in
303
Added:
Lwt_result.bind (Store.read store hash) @@ function
304
Added:
| Git.Value.Tree tree ->
305
Added:
let entries = Store.Value.Tree.to_list tree in
306
Added:
let rec try_entries = function
307
Added:
| [] -> Lwt_result.return None
308
Added:
| (entry : Store.Value.Tree.entry) :: rest ->
309
Added:
let entry_hash = Store.Hash.to_hex entry.node in
310
Added:
let step = (entry.name, entry_hash) in
311
Added:
if entry_hash = target_hash then
312
Added:
Lwt_result.return (Some (List.rev (step :: trail)))
313
Added:
else
314
Added:
match entry.perm with
315
Added:
| `Dir -> (
316
Added:
let* found = search (step :: trail) entry_hash in
317
Added:
match found with
318
Added:
| Some _ as result -> Lwt_result.return result
319
Added:
| None -> try_entries rest)
320
Added:
| _ -> try_entries rest
321
Added:
in
322
Added:
try_entries entries
323
Added:
| _ -> Lwt_result.return None
324
Added:
in
325
Added:
let* result = search [] root_hash in
326
Added:
Lwt_result.return (match result with Some trail -> trail | None -> [])
288
327
end
289
328
290
329
module Blob = struct
lib/static/styles.css
@@ -134,6 +134,20 @@
134
134
border: 1px solid #303030;
135
135
}
136
136
137
Added:
.breadcrumbs {
138
Added:
font-family: monospace;
139
Added:
padding: 0.5em 0;
140
Added:
}
141
Added:
142
Added:
.breadcrumbs a {
143
Added:
color: skyblue;
144
Added:
text-decoration: none;
145
Added:
}
146
Added:
147
Added:
.breadcrumbs a:hover {
148
Added:
text-decoration: underline;
149
Added:
}
150
Added:
137
151
.diff-file {
138
152
margin: 1.5em 0;
139
153
border: 1px solid #3a3a3a;
lib/views.ml
@@ -214,7 +214,23 @@
214
214
content = HTML.[ ul [] @@ List.map (li_of_commit repo) commits ];
215
215
}
216
216
217
Removed:
let files repo (tree : Resolvers.Tree.t) =
217
Added:
let files repo (trail : (string * string) list) (tree : Resolvers.Tree.t) =
218
Added:
let breadcrumbs =
219
Added:
let root_link = Routes.link_to (Files repo) (txt "/") in
220
Added:
let crumbs =
221
Added:
List.map
222
Added:
(fun (name, hash) ->
223
Added:
Routes.link_to (File (repo, hash)) (txt "%s" name))
224
Added:
trail
225
Added:
in
226
Added:
let separator = txt " / " in
227
Added:
let rec interleave = function
228
Added:
| [] -> []
229
Added:
| [ x ] -> [ x ]
230
Added:
| x :: rest -> x :: separator :: interleave rest
231
Added:
in
232
Added:
HTML.(nav [ class_ "breadcrumbs" ] (interleave (root_link :: crumbs)))
233
Added:
in
218
234
respond
219
235
@@ Page.render ~page_title:(page_title repo)
220
236
{
@@ -222,7 +238,7 @@
222
238
title = repo;
223
239
subtitle = Resolvers.repo_description repo;
224
240
active = Files;
225
Removed:
content = HTML.[ ul [] @@ List.map (li_of_entry repo) tree.entries ];
241
Added:
content = HTML.[ breadcrumbs; ul [] @@ List.map (li_of_entry repo) tree.entries ];
226
242
}
227
243
228
244
let file repo (blob : Resolvers.Blob.t) =