download patch
commit a458f8738428da71b5b00518bdd5d224698e477f
Author: tri <tri@thac.loan>
Date: Sun Sep 28 20:44:45 2025 +0700
write repo index page
diff --git a/Makefile b/Makefile
index e3c5cc4..2f8c180 100644
--- a/Makefile
+++ b/Makefile
watch:
- zig build run --watch -- demo
+ zig build run --watch -- demo http://localhost:8000
# TODO: write a simple zig server instead
serve:
diff --git a/src/assets/style.css b/src/assets/style.css
index 7c3d652..a7a18dc 100644
--- a/src/assets/style.css
+++ b/src/assets/style.css
--table-odd-row-bg: #eee;
--table-border-color: #ddd;
--table-row-hover-bg: papayawhip;
+ --table-border-color: black;
+ --code-bg: gainsboro;
+}
+
+.monospace,
+code {
+ font-family: monospace;
+ font-size: 1rem;
+}
+
+#breadcrumbs {
+ font-family: monospace;
+ font-size: 1.2rem;
+}
+
+h1 {
+ font-size: inherit;
+ font-weight: normal;
+ display: inline-block;
+ margin: 0;
+}
+
+code {
+ background-color: var(--code-bg);
+ padding: 0 5px;
}
table {
text-align: left;
border-collapse: collapse;
- width: 100%;
+ max-width: 100%;
overflow: scroll;
+
+ white-space: nowrap;
+ border-top: 2px solid var(--table-border-color);
}
/*
tbody tr:nth-child(odd) {
tbody tr:hover {
}
td,
th {
- padding: 0.1rem 5px;
+ padding: 0.1rem 10px;
}
table {
- margin-left: -5px;
+ margin-left: -10px;
font-variant-numeric: tabular-nums;
}
img {
--table-odd-row-bg: #333;
--table-border-color: #444;
--table-row-hover-bg: #444;
+ --table-border-color: #ddd;
+ --code-bg: #444;
}
a {
diff --git a/src/main.zig b/src/main.zig
index 6f1d53a..fe3d279 100644
--- a/src/main.zig
+++ b/src/main.zig
const git = @import("git.zig");
const web_prefix = "-";
pub fn main() !u8 {
- if (std.os.argv.len != 2) {
- println("Usage: khoe <dir>", .{});
+ if (std.os.argv.len != 3) {
+ println("Usage: khoe <dir> <site-url>", .{});
+ println(
+ \\For example:
+ \\ khoe /srv/git/repos https://khoe.thac.loan
+ \\
+ , .{});
return 1;
}
+ const site_url = std.os.argv[2];
+
var dba_impl: std.heap.DebugAllocator(.{}) = .init;
defer _ = dba_impl.deinit();
const dba = dba_impl.allocator();
pub fn main() !u8 {
.last_commit_time = if (commits.len == 0) "" else try arena.dupe(u8, commits[0].time),
});
- // TODO: write repo's index
+ try writeRepoPage(site_url, entry.name, target_dir, repo_dir, commits);
+
// TODO: write repo's commits
}
pub fn writeHomePage(dir: fs.Dir, repos: []RepoSummary) !void {
var file = try dir.createFile("index.html", .{});
defer file.close();
- var buf: [4096]u8 = undefined;
+ var buf: [1024 * 16]u8 = undefined;
var writer = file.writer(&buf);
try writer.interface.print(
\\<!doctype html>
pub fn writeHomePage(dir: fs.Dir, repos: []RepoSummary) !void {
\\ <script src="/script.js"></script>
\\ </head>
\\ <body>
- \\ <h1>Khoe</h1>
- \\ <p>Listing <b>{d}</b> repos:</p>
- \\ <hr>
+ \\ <header>
+ \\ <div id="breadcrumbs">
+ \\ /<h1>repos</h1>/
+ \\ </div>
+ \\ </header>
+ \\ <p>listing <b>{d}</b> repos:</p>
\\ <div style="overflow-x:auto; padding-bottom:1rem">
- \\ <table style="white-space:nowrap">
+ \\ <table>
\\ <thead>
\\ <tr>
- \\ <th>Name</th>
- \\ <th>Commits</th>
- \\ <th>Last commit</th>
- \\ <th>Last modified</th>
+ \\ <th>name</th>
+ \\ <th>commits</th>
+ \\ <th>last commit</th>
+ \\ <th>last modified</th>
\\ </tr>
\\ </thead>
\\ <tbody>
pub fn writeHomePage(dir: fs.Dir, repos: []RepoSummary) !void {
try writer.interface.flush();
}
+// TODO: write repo's index
+pub fn writeRepoPage(
+ site_url: [*:0]const u8,
+ repo_name: []const u8,
+ target_dir: fs.Dir,
+ in_repo_dir: fs.Dir,
+ commits: []git.Commit,
+) !void {
+ var buf: [1024]u8 = undefined;
+ var out_repo_dir = try target_dir.makeOpenPath(
+ try std.fmt.bufPrint(&buf, "{s}/{s}", .{ web_prefix, repo_name }),
+ .{},
+ );
+ defer out_repo_dir.close();
+
+ _ = in_repo_dir; // TODO: do I need this?
+
+ var file = try out_repo_dir.createFile("index.html", .{});
+ defer file.close();
+
+ var buf2: [1024 * 16]u8 = undefined;
+ var writer = file.writer(&buf2);
+ try writer.interface.print(
+ \\<!doctype html>
+ \\<html lang="en">
+ \\ <head>
+ \\ <meta charset="utf-8" />
+ \\ <title>{0s} | Khoe</title>
+ \\ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ \\ <link rel="stylesheet" href="/style.css">
+ \\ <script src="/script.js"></script>
+ \\ </head>
+ \\ <body>
+ \\ <header>
+ \\ <div id="breadcrumbs">
+ \\ /<a href="/">repos</a>/<h1>{0s}</h1>/
+ \\ </div>
+ \\ </header>
+ \\
+ \\ <p>
+ \\ clone command: <code>git clone {2s}/{3s}/</code><br>
+ \\ commits: <b>{1d}</b><br>
+ \\ </p>
+ \\
+ \\ <div style="overflow-x:auto; padding-bottom:1rem">
+ \\ <table>
+ \\ <thead>
+ \\ <tr>
+ \\ <th>hash</th>
+ \\ <th>subject</th>
+ \\ <th>time</th>
+ \\ </tr>
+ \\ </thead>
+ \\ <tbody>
+ \\
+ , .{
+ repo_name,
+ commits.len,
+ site_url,
+ if (std.mem.endsWith(u8, repo_name, ".git"))
+ repo_name
+ else
+ try std.fmt.bufPrint(&buf, "{s}/.git", .{repo_name}),
+ });
+
+ for (commits) |cmt| {
+ try writer.interface.print(
+ \\<tr>
+ \\ <td class="monospace" title="commit diff coming Soon™">{0s}</td>
+ \\ <td>{1s}</td>
+ \\ <td><time class="relative" datetime="{2s}" title="{2s}">{2s}</time></td>
+ \\</tr>
+ \\
+ ,
+ .{
+ //cmt.hash,
+ cmt.hash[0..10],
+ cmt.subject[0..@min(cmt.subject.len, 80)],
+ cmt.time,
+ },
+ );
+ }
+
+ try writer.interface.writeAll(
+ \\ </tbody>
+ \\ </table>
+ \\ </div>
+ \\ </body>
+ \\</html>
+ );
+
+ try writer.interface.flush();
+}
+
test "all" {
_ = @import("html.zig");
std.testing.refAllDeclsRecursive(@This());