size: 4 KiB

1const std = @import("std");
2const mem = std.mem;
3const Io = std.Io;
4const fmt = std.fmt;
5const html = @import("html.zig");
6const utils = @import("utils.zig");
7const constants = @import("constants.zig");
8
9pub const Crumb = struct {
10 href: ?[]const u8 = null,
11 text: []const u8,
12};
13
14// Remember to close with base_end()
15pub fn base_start(arena: mem.Allocator, writer: *Io.Writer, args: struct {
16 title: ?[]const u8 = null,
17 breadcrumbs: []const Crumb,
18 opengraph: struct {
19 title: []const u8,
20 description: []const u8,
21 },
22}) !void {
23 try writer.print(
24 \\<!doctype html>
25 \\<html lang="en">
26 \\ <head>
27 \\ <meta charset="utf-8" />
28 \\ <title>{0s}</title>
29 \\ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
30 \\
31 \\ <meta property="og:site_name" content="khoe" />
32 \\ <meta property="og:title" content="{1s}" />
33 \\ <meta property="og:description" content="{2s}" />
34 \\
35 \\ <link rel="stylesheet" href="/_/_khoe-hang/style.css">
36 \\ <script src="/_/_khoe-hang/script.js"></script>
37 \\ </head>
38 \\ <body>
39 \\ <header>
40 \\ <div id="breadcrumbs">
41 \\ /
42 , .{
43 if (args.title) |title|
44 try html.escapeAlloc(
45 arena,
46 try fmt.allocPrint(arena, "{s} | khoe", .{title}),
47 )
48 else
49 "khoe",
50 try html.escapeAlloc(arena, args.opengraph.title),
51 try html.escapeAlloc(arena, args.opengraph.description),
52 });
53
54 for (args.breadcrumbs) |crumb| {
55 const escaped_text = try html.escapeAlloc(arena, crumb.text);
56 if (crumb.href) |href| {
57 try writer.print(
58 \\<a href="{0s}">{1s}</a>/
59 , .{ href, escaped_text });
60 } else {
61 try writer.print(
62 \\<span>{0s}</span>
63 , .{escaped_text});
64 }
65 }
66
67 try writer.writeAll(
68 \\ </div>
69 \\ </header>
70 \\
71 );
72}
73
74pub fn base_end(writer: *Io.Writer) !void {
75 try writer.writeAll(
76 \\ </body>
77 \\</html>
78 \\
79 );
80}
81
82pub fn repo_metadata(arena: mem.Allocator, writer: *Io.Writer, args: struct {
83 description: []const u8,
84 repo_size: u64,
85 commit_count: usize,
86 site_url: [*:0]const u8,
87 repo_name: []const u8,
88 git_dir_path: []const u8,
89}) !void {
90 const clone_path =
91 if (mem.eql(u8, args.git_dir_path, "."))
92 args.repo_name
93 else
94 try std.fmt.allocPrint(
95 arena,
96 "{s}/{s}",
97 .{ args.repo_name, args.git_dir_path },
98 );
99
100 try writer.print(
101 \\ <p style="margin:0; font-style:italic;">{0s}</p>
102 \\ <p style="margin:0">{1s} | {2d} commits</p>
103 \\
104 \\ <pre class="git-clone-command">git clone {3s}/{4s}</pre>
105 \\
106 ,
107 .{
108 try html.escapeAlloc(arena, args.description),
109 try utils.humanReadableSize(arena, args.repo_size),
110 args.commit_count,
111 args.site_url,
112 clone_path,
113 },
114 );
115}
116
117pub fn repo_nav(
118 writer: *Io.Writer,
119 args: struct {
120 repo_name: []const u8,
121 active: enum { source, readme, commits },
122 readme_filename: ?[]const u8,
123 },
124) !void {
125 try writer.writeAll(
126 \\<nav class="repo-nav">
127 \\ <ul>
128 \\
129 );
130
131 const source_class = if (args.active == .source) "active" else "";
132 const readme_class = if (args.active == .readme) "active" else "";
133 const commits_class = if (args.active == .commits) "active" else "";
134
135 try writer.print(
136 \\ <li class="{0s}"><a href="/{1s}/{2s}/">source</a></li>
137 \\
138 , .{
139 source_class,
140 constants.web_path,
141 args.repo_name,
142 });
143
144 if (args.readme_filename) |fname| {
145 try writer.print(
146 \\ <li class="{0s}"><a href="/{1s}/{2s}/{3s}/">{4s}</a></li>
147 \\
148 , .{
149 readme_class,
150 constants.web_path,
151 args.repo_name,
152 constants.web_readme_path,
153 fname,
154 });
155 }
156
157 try writer.print(
158 \\ <li class="{0s}"><a href="/{1s}/{2s}/{3s}">commits</a></li>
159 \\
160 , .{
161 commits_class,
162 constants.web_path,
163 args.repo_name,
164 constants.web_commits_path,
165 });
166
167 try writer.writeAll(
168 \\ </ul>
169 \\</nav>
170 \\
171 );
172}
173
174pub fn repo_content_start(writer: *Io.Writer) !void {
175 try writer.writeAll(
176 \\<div class="repo-content">
177 \\
178 );
179}
180
181pub fn repo_content_end(writer: *Io.Writer) !void {
182 try writer.writeAll(
183 \\</div>
184 \\
185 );
186}