download patch
commit dccf967fc1d2ffd604e8a3e5e32337635ad27fba
Author: tri <tri@thac.loan>
Date: Sun Sep 28 12:10:18 2025 +0700
style: horizontally scroll table, relative time
diff --git a/Makefile b/Makefile
index 9762b32..e3c5cc4 100644
--- a/Makefile
+++ b/Makefile
watch:
# TODO: write a simple zig server instead
serve:
python -m http.server -b localhost -d demo 8000
+
+test:
+ zig build test --watch
diff --git a/src/assets/script.js b/src/assets/script.js
new file mode 100644
index 0000000..2406b38
--- /dev/null
+++ b/src/assets/script.js
+const millisecondsPerSecond = 1000;
+const secondsPerMinute = 60;
+const minutesPerHour = 60;
+const hoursPerDay = 24;
+const daysPerWeek = 7;
+const daysPerYear = 365; // getting imprecise here, but no big deal
+const intervals = {
+ year:
+ millisecondsPerSecond *
+ secondsPerMinute *
+ minutesPerHour *
+ hoursPerDay *
+ daysPerYear,
+ week:
+ millisecondsPerSecond *
+ secondsPerMinute *
+ minutesPerHour *
+ hoursPerDay *
+ daysPerWeek,
+ day: millisecondsPerSecond * secondsPerMinute * minutesPerHour * hoursPerDay,
+ hour: millisecondsPerSecond * secondsPerMinute * minutesPerHour,
+ minute: millisecondsPerSecond * secondsPerMinute,
+ second: millisecondsPerSecond,
+};
+const relativeDateFormat = new Intl.RelativeTimeFormat("en", { style: "long" });
+
+// https://stackoverflow.com/a/78704662
+function formatRelativeTime(isoStr) {
+ const diff = new Date(isoStr) - new Date();
+ for (const interval in intervals) {
+ if (intervals[interval] <= Math.abs(diff)) {
+ return relativeDateFormat.format(
+ Math.trunc(diff / intervals[interval]),
+ interval,
+ );
+ }
+ }
+ return relativeDateFormat.format(diff / 1000, "second");
+}
+
+document.addEventListener("DOMContentLoaded", function () {
+ document.querySelectorAll("time.relative").forEach((element) => {
+ const timeStr = element.getAttribute("datetime");
+ if (!timeStr) return;
+ const relativeTimeStr = formatRelativeTime(timeStr);
+ element.innerHTML = relativeTimeStr;
+ });
+});
diff --git a/src/assets/style.css b/src/assets/style.css
index aafc5a7..7c3d652 100644
--- a/src/assets/style.css
+++ b/src/assets/style.css
table {
text-align: left;
- width: 100%;
border-collapse: collapse;
+ width: 100%;
+ overflow: scroll;
}
/*
tbody tr:nth-child(odd) {
th {
}
table {
margin-left: -5px;
+ font-variant-numeric: tabular-nums;
}
img {
diff --git a/src/main.zig b/src/main.zig
index 3396052..00c953b 100644
--- a/src/main.zig
+++ b/src/main.zig
pub fn main() !u8 {
}
try writeHomePage(target_dir, repo_summaries.items);
- try target_dir.writeFile(.{
- .sub_path = "style.css",
- .data = @embedFile("assets/style.css"),
- });
+
+ inline for (.{ "style.css", "script.js" }) |asset_name| {
+ try target_dir.writeFile(.{
+ .sub_path = asset_name,
+ .data = @embedFile("assets/" ++ asset_name),
+ });
+ }
return 0;
}
pub fn writeHomePage(dir: fs.Dir, repos: []RepoSummary) !void {
\\ <meta charset="utf-8" />
\\ <title>Khoe</title>
\\ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- \\ <link rel="stylesheet" href="/style.css">
+ \\ <link rel="stylesheet" href="/style.css">
+ \\ <script src="/script.js"></script>
\\ </head>
\\ <body>
\\ <h1>Khoe</h1>
- \\ <p>We've got <b>{d}</b> repos in total.</p>
+ \\ <p>Listing <b>{d}</b> repos:</p>
\\ <hr>
- \\ <table>
- \\ <thead>
- \\ <tr>
- \\ <th>Name</th>
- \\ <th>Commits</th>
- \\ <th>Last commit</th>
- \\ <th>Last commit time</th>
- \\ </tr>
- \\ </thead>
- \\ <tbody>
+ \\ <div style="overflow-x:auto; padding-bottom:1rem">
+ \\ <table style="white-space:nowrap">
+ \\ <thead>
+ \\ <tr>
+ \\ <th>Name</th>
+ \\ <th>Commits</th>
+ \\ <th>Last commit</th>
+ \\ <th>Last modified</th>
+ \\ </tr>
+ \\ </thead>
+ \\ <tbody>
\\
, .{repos.len});
for (repos) |repo| {
try writer.interface.print(
\\<tr>
- \\ <td style="white-space: nowrap;"><a href="/{0s}/{1s}/">{1s}</a></td>
+ \\ <td><a href="/{0s}/{1s}/">{1s}</a></td>
\\ <td>{2d}</td>
\\ <td>{3s}</td>
- \\ <td style="white-space: nowrap;">{4s}</td>
+ \\ <td><time class="relative" datetime="{4s}" title="{4s}">{4s}</time></td>
\\</tr>
\\
,
.{
- web_prefix,
- repo.name,
- repo.commit_count,
- repo.last_commit_msg,
- repo.last_commit_time,
+ web_prefix, // 0
+ repo.name, // 1
+ repo.commit_count, // 2
+ repo.last_commit_msg, // 3
+ repo.last_commit_time, // 4
},
);
}
try writer.interface.writeAll(
- \\ </tbody>
- \\ </table>
+ \\ </tbody>
+ \\ </table>
+ \\ </div>
\\ </body>
\\</html>
);
try writer.interface.flush();
}
+
+test "all" {
+ _ = @import("html.zig");
+ std.testing.refAllDeclsRecursive(@This());
+}