git / brickware / marrow.git - c369640

(21 hours ago)commit c369640: back -> breadcrumb

Summary | History | Files

commit c369640

authorTanner Stenson <tanner@brickware.sh>
dateWed Jan 7 23:47:55 2026 -0500

message

back -> breadcrumb

diff

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",