[OCaml] Mobile-friendly clone of cgit.
Great work today, again!
Changed files
bin/main.ml
@@ -1,13 +1,1 @@
1
Removed:
let () =
2
Removed:
Dream.run @@ Dream.logger
3
Removed:
@@ Dream.router
4
Removed:
[
5
Removed:
Dream.get "/" (fun _req -> Ogit.Handlers.ogit_root _req);
6
Removed:
Dream.get "/:repo_name" (fun req ->
7
Removed:
let repo_name = Dream.param req "repo_name" in
8
Removed:
Ogit.Handlers.repo_root repo_name);
9
Removed:
Dream.get "/:repo_name/tree" (fun req ->
10
Removed:
let repo_name = Dream.param req "repo_name" in
11
Removed:
Ogit.Handlers.repo_tree repo_name);
12
Removed:
Dream.get "/static/**" (Dream.static "./lib/static");
13
Removed:
]
1
Added:
let () = Dream.run @@ Dream.logger @@ Dream.router Ogit.Handlers.all_handlers
lib/dune
@@ -1,5 +1,5 @@
1
1
(library
2
2
(name ogit)
3
Removed:
(libraries unix dream dream-html git git-unix)
3
Added:
(libraries dream dream-html git git-unix)
4
4
(preprocess
5
5
(pps lwt_ppx)))
lib/handlers.ml
@@ -1,3 +1,25 @@
1
Removed:
let ogit_root _ = Views.Ogit_root.render () |> Dream_html.respond
2
Removed:
let repo_root repo_name = Views.Repo_root.render repo_name |> Dream_html.respond
3
Removed:
let repo_tree repo_name = Views.Repo_tree.render repo_name |> Dream_html.respond
1
Added:
(* Handlers for different routes *)
2
Added:
let ogit_root _req = Views.Ogit_root.render () |> Dream_html.respond
3
Added:
4
Added:
let repo_root req =
5
Added:
let repo_name = Dream.param req "repo_name" in
6
Added:
Views.Repo_root.render repo_name |> Dream_html.respond
7
Added:
8
Added:
let repo_tree req =
9
Added:
let repo_name = Dream.param req "repo_name" in
10
Added:
let dir_path = Dream.param req "**" in
11
Added:
Views.Repo_tree.render repo_name dir_path |> Dream_html.respond
12
Added:
13
Added:
let repo_blob req =
14
Added:
let repo_name = Dream.param req "repo_name" in
15
Added:
let blob_name = Dream.param req "blob_name" in
16
Added:
Views.Repo_blob.render repo_name blob_name |> Dream_html.respond
17
Added:
18
Added:
(* Route definitions *)
19
Added:
let all_handlers = [
20
Added:
Dream.get "/" ogit_root;
21
Added:
Dream.get "/:repo_name" repo_root;
22
Added:
Dream.get "/:repo_name/tree/**" repo_tree;
23
Added:
Dream.get "/:repo_name/blob/:blob_name" repo_blob;
24
Added:
Dream.get "/static/**" (Dream.static "./lib/static");
25
Added:
]
lib/static/styles.css
@@ -1,29 +1,18 @@
1
Removed:
@import url("fonts/Inter-4.1/web/inter.css");
2
Removed:
3
1
body {
4
Removed:
font-family: Inter, sans-serif;
5
Removed:
max-width: 50em;
6
Removed:
margin: auto;
7
Removed:
padding: 0 1em;
8
Removed:
background-color: #181818;
9
Removed:
color: white;
2
Added:
font-family: sans-serif;
10
3
}
11
4
12
5
nav#top {
13
Removed:
background-color: black;
14
Removed:
border-radius: 0.25rem;
6
Added:
background-color: dimgray;
15
7
}
16
8
17
9
nav#top ul {
18
Removed:
display: flex;
19
Removed:
flex-wrap: wrap;
10
Added:
display: flex;
20
11
list-style-type: none;
21
Removed:
padding: 0;
22
12
}
23
13
24
14
nav#top ul li {
25
Removed:
padding: 0.5em 0;
26
Removed:
border-radius: 0.25rem;
15
Added:
padding: 1em 0;
27
16
}
28
17
29
18
nav#top ul li a {
@@ -33,39 +22,6 @@
33
22
}
34
23
35
24
nav#top ul li a:hover {
36
Removed:
background-color: white;
37
Removed:
color: black;
38
Removed:
}
39
Removed:
40
Removed:
nav#top ul li#active {
41
Removed:
border-radius: 0.25rem;
42
Removed:
background-color: rgb(194, 79, 30);
43
Removed:
}
44
Removed:
45
Removed:
nav#top ul li#active a:hover {
46
Removed:
border-radius: 0.25rem;
47
Removed:
background-color: rgb(132, 40, 0);
48
Removed:
color: white;
49
Removed:
}
50
Removed:
51
Removed:
div#main ul {
52
Removed:
padding-left: 0;
53
Removed:
list-style: none;
54
Removed:
}
55
Removed:
56
Removed:
div#main ul li {
57
Removed:
border: 1px solid #303030;
58
Removed:
}
59
Removed:
60
Removed:
div#main ul li a {
61
Removed:
display: block;
62
Removed:
color: white;
63
Removed:
text-decoration: none;
64
Removed:
padding: 0.5em;
65
Removed:
}
66
Removed:
67
Removed:
div#main a:hover {
68
Removed:
background-color: white;
69
Removed:
color: black;
25
Added:
background-color: #555;
70
26
text-decoration: revert;
71
27
}
lib/views.ml
@@ -29,6 +29,7 @@
29
29
[
30
30
title [] "%s" head_data.page_title;
31
31
link [ rel "stylesheet"; href "/static/styles.css" ];
32
Added:
link [ rel "icon"; type_ "image/x-icon"; href "/static/git_icon.svg"];
32
33
];
33
34
body []
34
35
[
@@ -68,30 +69,6 @@
68
69
("diff", "diff");
69
70
];
70
71
]
71
Removed:
72
Removed:
(* let li_of_a (path, text) = *)
73
Removed:
(* let is_active = path = current_path in *)
74
Removed:
(* li (if is_active then [ id "active" ] else []) in *)
75
Removed:
(* let link_of_path = *)
76
Removed:
(* if String.length path = 0 then Printf.sprintf "/%s" repo_path *)
77
Removed:
(* else Printf.sprintf "/%s/%s" repo_path path *)
78
Removed:
(* in *)
79
Removed:
(* li [ id "active" ] [ a [ href "%s" link_of_path ] [ txt text ] ] *)
80
Removed:
(* in *)
81
Removed:
(* nav *)
82
Removed:
(* [ id "top" ] *)
83
Removed:
(* [ *)
84
Removed:
(* ul [] *)
85
Removed:
(* @@ List.map li_of_a *)
86
Removed:
(* [ *)
87
Removed:
(* ("", "summary"); *)
88
Removed:
(* ("refs", "refs"); *)
89
Removed:
(* ("log", "log"); *)
90
Removed:
(* ("tree", "tree"); *)
91
Removed:
(* ("commit", "commit"); *)
92
Removed:
(* ("diff", "diff"); *)
93
Removed:
(* ]; *)
94
Removed:
(* ] *)
95
72
end
96
73
97
74
module Ogit_root = struct
@@ -123,13 +100,6 @@
123
100
open Dream_html
124
101
open HTML
125
102
126
Removed:
(* let href_for_git_object kind identifier = *)
127
Removed:
(* let open Printf in *)
128
Removed:
(* href "%s" @@ *)
129
Removed:
(* match kind with *)
130
Removed:
(* | `Branch -> sprintf "/tree/%s" identifier *)
131
Removed:
(* | `Commit -> sprintf "/commit/%s" identifier *)
132
Removed:
133
103
let render repo_path =
134
104
let title = repo_path in
135
105
let subtitle = Filename.concat Config.git_directory repo_path in
@@ -159,41 +129,84 @@
159
129
open Dream_html
160
130
open HTML
161
131
162
Removed:
let full_path repo_path = Filename.concat Config.git_directory repo_path
132
Added:
let full_path repo_path dir_path =
133
Added:
Filename.concat (Filename.concat Config.git_directory repo_path) dir_path
163
134
164
Removed:
(* Helper function to list top-level files and directories *)
165
Removed:
let ls_repo repo_path =
135
Added:
(* Helper function to list contents of a given directory *)
136
Added:
let ls_dir repo_path dir_path =
137
Added:
let dir_full_path = full_path repo_path dir_path in
166
138
try
167
Removed:
Sys.readdir (full_path repo_path)
139
Added:
Sys.readdir dir_full_path
168
140
|> Array.to_list
169
141
|> List.filter (fun name -> name <> ".git") (* Exclude .git *)
170
Removed:
|> List.sort String.compare
142
Added:
|> List.map (fun entry ->
143
Added:
let entry_rel_path = Filename.concat dir_path entry in
144
Added:
let full_entry_path = Filename.concat dir_full_path entry in
145
Added:
(entry, entry_rel_path, Sys.is_directory full_entry_path))
146
Added:
|> List.sort (fun (a, _, is_dir_a) (b, _, is_dir_b) ->
147
Added:
match (is_dir_a, is_dir_b) with
148
Added:
| true, false -> -1 (* Directories first *)
149
Added:
| false, true -> 1
150
Added:
| _ -> String.compare a b)
171
151
with Sys_error _ -> []
172
152
173
153
(* Function to create a link based on file type *)
174
Removed:
let link_for_entry repo_path entry =
175
Removed:
let entry_path = Filename.concat (full_path repo_path) entry in
176
Removed:
if Sys.is_directory entry_path then
177
Removed:
a
178
Removed:
[ href "%s" @@ Printf.sprintf "/%s/tree/%s" repo_path entry ]
179
Removed:
[ txt "%s" (entry ^ "/") ]
180
Removed:
else
181
Removed:
a
182
Removed:
[ href "%s" @@ Printf.sprintf "/%s/blob/%s" repo_path entry ]
183
Removed:
[ txt "%s" entry ]
154
Added:
let link_for_entry repo_path (entry, entry_rel_path, is_dir) =
155
Added:
let link =
156
Added:
if is_dir then Printf.sprintf "/%s/tree/%s" repo_path entry_rel_path
157
Added:
else Printf.sprintf "/%s/blob/%s" repo_path entry_rel_path
158
Added:
in
159
Added:
li [] [ a [ href "%s" link ] [ txt "%s" entry ] ]
184
160
185
Removed:
let li_of_entry repo_path entry = li [] [ link_for_entry repo_path entry ]
161
Added:
(* Render function, now supporting nested directories *)
162
Added:
let render repo_path dir_path =
163
Added:
let title = Filename.concat repo_path dir_path in
164
Added:
let subtitle = "Repository Files" in
186
165
187
Removed:
(* Render function *)
188
Removed:
let render repo_path =
189
Removed:
let title = repo_path in
190
Removed:
let subtitle = "Files" in
166
Added:
let repo_entries = ls_dir repo_path dir_path in
167
Added:
let content =
168
Added:
[
169
Added:
h3 [] [ txt "%s" (Printf.sprintf "Contents of /%s" dir_path) ];
170
Added:
ul [] (List.map (link_for_entry repo_path) repo_entries);
171
Added:
]
172
Added:
in
191
173
192
Removed:
let repo_entries = ls_repo repo_path in
193
Removed:
let content = [ ul [] (List.map (li_of_entry repo_path) repo_entries) ] in
194
Removed:
195
174
let body_data =
196
175
{ title; subtitle; topnav = Topnav.repo repo_path "tree"; content }
197
176
in
198
177
Layout.application body_data
178
Added:
end
179
Added:
180
Added:
module Repo_blob = struct
181
Added:
open Dream_html
182
Added:
open HTML
183
Added:
184
Added:
let full_path repo_path = Filename.concat Config.git_directory repo_path
185
Added:
186
Added:
(* Read the contents of a file *)
187
Added:
let read_blob repo_path blob_name =
188
Added:
let file_path = Filename.concat (full_path repo_path) blob_name in
189
Added:
try Some (In_channel.with_open_text file_path In_channel.input_all)
190
Added:
with _ -> None (* Handle cases where the file doesn't exist or can't be read *)
191
Added:
192
Added:
(* Render function *)
193
Added:
let render repo_path blob_name =
194
Added:
let title = blob_name in
195
Added:
let subtitle = "File Contents" in
196
Added:
197
Added:
match read_blob repo_path blob_name with
198
Added:
| Some content ->
199
Added:
let content_display =
200
Added:
pre [] [ code [] [ txt "%s" content ] ] (* Render as code block *)
201
Added:
in
202
Added:
let body_data =
203
Added:
{ title; subtitle; topnav = Topnav.repo repo_path "blob"; content = [content_display] }
204
Added:
in
205
Added:
Layout.application body_data
206
Added:
| None ->
207
Added:
let error_message = p [] [ txt "Error: Unable to read file." ] in
208
Added:
let body_data =
209
Added:
{ title; subtitle; topnav = Topnav.repo repo_path "blob"; content = [error_message] }
210
Added:
in
211
Added:
Layout.application body_data
199
212
end