(21 hours ago)commit c369640: back -> breadcrumb
| author | Tanner Stenson <tanner@brickware.sh> |
| date | Wed Jan 7 23:47:55 2026 -0500 |
back -> breadcrumb
commit c369640c9741f4fb04c890741d97a4356d2281f4
Author: Tanner Stenson <tanner@brickware.sh>
Date: Wed Jan 7 23:47:55 2026 -0500
back -> breadcrumb
diff --git a/marrow-static.c b/marrow-static.c
index 877ed63..ab15d32 100644
--- a/marrow-static.c
+++ b/marrow-static.c
@@ -865,10 +865,132 @@ generate_branch_tree_page(FILE *out, const char *repo_path, const char *repo_nam
return 1;
}
+static void
+generate_tree_path_header(FILE *out, const char *repo_name, const char *ref,
+ const char *tree_path) {
+ fprintf(out, "<!DOCTYPE html>\n");
+ fprintf(out, "<html>\n<head>\n");
+ fprintf(out, "<meta charset=\"utf-8\">\n");
+ fprintf(out, "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n");
+ fprintf(out, "<title>%s - %s - %s</title>\n", repo_name, ref, tree_path);
+ fprintf(out, "<style>\n");
+ fprintf(out, "body{font-family:monospace;max-width:900px;margin:40px auto;padding:0 20px;line-height:1.6;}\n");
+ fprintf(out, "a{color:#00f;text-decoration:none;}\n");
+ fprintf(out, "a:hover{text-decoration:underline;}\n");
+ fprintf(out, "table{border-collapse:collapse;width:100%%;}\n");
+ fprintf(out, "th,td{text-align:left;padding:8px 12px;border-bottom:1px solid #ddd;}\n");
+ fprintf(out, "th{font-weight:bold;}\n");
+ fprintf(out, "pre{background:#f5f5f5;padding:10px;overflow-x:auto;}\n");
+ fprintf(out, "code{background:#f5f5f5;padding:2px 4px;}\n");
+ fprintf(out, ".header{border-bottom:2px solid #000;margin-bottom:20px;padding-bottom:10px;}\n");
+ fprintf(out, ".footer{margin-top:40px;padding-top:20px;border-top:1px solid #ddd;font-size:0.9em;color:#666;}\n");
+ fprintf(out, "</style>\n");
+ fprintf(out, "</head>\n<body>\n");
+
+ /* Generate breadcrumb with full path including tree path */
+ fprintf(out, "<div class=\"header\"><h1>");
+
+ /* Count segments in repo_name and tree_path */
+ int32_t repo_segments = 1;
+ for (const char *p = repo_name; *p; p++) {
+ if ('/' == *p) repo_segments++;
+ }
+
+ int32_t tree_segments = 0;
+ if (tree_path && tree_path[0]) {
+ tree_segments = 1;
+ for (const char *p = tree_path; *p; p++) {
+ if ('/' == *p) tree_segments++;
+ }
+ }
+
+ /* Root "git" link */
+ fprintf(out, "<a href=\"");
+ /* From tree/path/to/file: up tree_segments, then up 3 (tree/, ref/, branch/), then repo_segments */
+ for (int32_t i = 0; i < tree_segments + 3 + repo_segments; i++) {
+ fprintf(out, "../");
+ }
+ fprintf(out, "\">git</a>");
+
+ /* Repository path segments */
+ if (repo_name && repo_name[0]) {
+ char path_copy[PATH_MAX];
+ snprintf(path_copy, sizeof(path_copy), "%s", repo_name);
+ char *token = strtok(path_copy, "/");
+ int32_t current_segment = 0;
+
+ while (token) {
+ fprintf(out, " / ");
+ fprintf(out, "<a href=\"");
+ int32_t levels_up = tree_segments + 3 + (repo_segments - current_segment - 1);
+ for (int32_t i = 0; i < levels_up; i++) {
+ fprintf(out, "../");
+ }
+ fprintf(out, "\">");
+ html_escape(out, token);
+ fprintf(out, "</a>");
+
+ current_segment++;
+ token = strtok(NULL, "/");
+ }
+ }
+
+ /* Branch/commit reference */
+ fprintf(out, " / <a href=\"");
+ for (int32_t i = 0; i < tree_segments + 2; i++) { /* Up to branch/ref level */
+ fprintf(out, "../");
+ }
+ fprintf(out, "\">");
+ html_escape(out, ref);
+ fprintf(out, "</a>");
+
+ /* Tree root link */
+ fprintf(out, " / <a href=\"");
+ for (int32_t i = 0; i < tree_segments + 1; i++) { /* Up to tree/ level */
+ fprintf(out, "../");
+ }
+ fprintf(out, "\">tree</a>");
+
+ /* Tree path segments */
+ if (tree_path && tree_path[0]) {
+ char tree_copy[PATH_MAX];
+ snprintf(tree_copy, sizeof(tree_copy), "%s", tree_path);
+ char *token = strtok(tree_copy, "/");
+
+ while (token) {
+ fprintf(out, " / ");
+
+ char *next = strtok(NULL, "/");
+ if (next) {
+ /* Not last segment - make it a link */
+ fprintf(out, "<a href=\"");
+ int32_t remaining = 1;
+ for (char *p = next; *p; p++) {
+ if ('/' == *p) remaining++;
+ }
+ for (int32_t i = 0; i < remaining + 1; i++) {
+ fprintf(out, "../");
+ }
+ fprintf(out, "\">");
+ html_escape(out, token);
+ fprintf(out, "</a>");
+ token = next;
+ } else {
+ /* Last segment - no link */
+ html_escape(out, token);
+ token = NULL;
+ }
+ }
+ }
+
+ fprintf(out, "</h1></div>\n");
+}
+
static int32_t
generate_tree_path_page(FILE *out, const char *repo_path, const char *repo_name,
const char *ref, const char *tree_path, const char *page_title) {
- generate_html_header(out, repo_name, page_title);
+ (void)page_title; /* Unused - kept for function signature compatibility */
+ generate_tree_path_header(out, repo_name, ref, tree_path);
/* Get latest commit for this path */
char cmd[MAX_CMD_LEN];
@@ -908,9 +1030,6 @@ generate_tree_path_page(FILE *out, const char *repo_path, const char *repo_name,
fprintf(out, "</small></p>\n");
}
- /* Navigation */
- fprintf(out, "<p><a href=\"../../\">Back</a></p>\n");
-
/* Check if path is tree or blob */
snprintf(cmd, sizeof(cmd),
"cd '%s' && git cat-file -t %s:'%s' 2>/dev/null",