tenseleyflow/shithub / 51fc2ce

Browse files

Align repo and issue navigation

Authored by espadonne
SHA
51fc2ceeceeef5b211a044a6fa19193f008566e6
Parents
24a480c
Tree
4188611

15 changed files

StatusFile+-
M internal/web/handlers/repo/code.go 24 7
M internal/web/handlers/repo/issues.go 20 11
M internal/web/handlers/repo/labels_milestones.go 6 0
M internal/web/handlers/repo/repo.go 17 8
M internal/web/render/octicons.go 16 0
M internal/web/static/css/shithub.css 129 18
A internal/web/templates/_repo_about_sidebar.html 26 0
A internal/web/templates/_repo_header.html 19 0
M internal/web/templates/_repo_subnav.html 5 5
M internal/web/templates/repo/issue_new.html 4 3
M internal/web/templates/repo/issue_view.html 24 21
M internal/web/templates/repo/issues_list.html 2 10
M internal/web/templates/repo/labels.html 4 3
M internal/web/templates/repo/milestones.html 4 3
M internal/web/templates/repo/tree.html 105 87
internal/web/handlers/repo/code.gomodified
@@ -69,13 +69,7 @@ func (h *Handlers) loadCodeContext(w http.ResponseWriter, r *http.Request) (*cod
6969
 	if err != nil {
7070
 		h.d.Logger.WarnContext(r.Context(), "code: ListRefs", "error", err)
7171
 	}
72
-	allNames := make([]string, 0, len(refs.Branches)+len(refs.Tags))
73
-	for _, b := range refs.Branches {
74
-		allNames = append(allNames, b.Name)
75
-	}
76
-	for _, t := range refs.Tags {
77
-		allNames = append(allNames, t.Name)
78
-	}
72
+	allNames := refNames(refs)
7973
 
8074
 	rest := chi.URLParam(r, "*")
8175
 	rest = strings.Trim(rest, "/")
@@ -120,6 +114,10 @@ func (h *Handlers) codeTree(w http.ResponseWriter, r *http.Request) {
120114
 	if !ok {
121115
 		return
122116
 	}
117
+	h.renderRepoTree(w, r, cc)
118
+}
119
+
120
+func (h *Handlers) renderRepoTree(w http.ResponseWriter, r *http.Request, cc *codeContext) {
123121
 	kind, _, _, err := repogit.StatPath(r.Context(), cc.gitDir, cc.ref, cc.subpath)
124122
 	if err != nil {
125123
 		if errors.Is(err, repogit.ErrPathNotFound) {
@@ -146,6 +144,11 @@ func (h *Handlers) codeTree(w http.ResponseWriter, r *http.Request) {
146144
 	}
147145
 	// README detection on the requested directory only.
148146
 	readmeHTML := h.findAndRenderREADME(r, cc, entries)
147
+	head, headFound, headErr := repogit.HeadOf(r.Context(), cc.gitDir, cc.ref)
148
+	if headErr != nil {
149
+		h.d.Logger.WarnContext(r.Context(), "code: HeadOf", "error", headErr)
150
+	}
151
+	topics, _ := h.rq.ListRepoTopics(r.Context(), h.d.Pool, cc.row.ID)
149152
 
150153
 	h.d.Render.RenderPage(w, r, "repo/tree", map[string]any{
151154
 		"Title":         cc.row.Name + " · " + cc.owner,
@@ -158,16 +161,30 @@ func (h *Handlers) codeTree(w http.ResponseWriter, r *http.Request) {
158161
 		"Entries":       entries,
159162
 		"Branches":      cc.refs.Branches,
160163
 		"Tags":          cc.refs.Tags,
164
+		"Head":          head,
165
+		"HeadFound":     headFound,
161166
 		"README":        template.HTML(readmeHTML), //nolint:gosec // sanitized by mdrender
162167
 		"HTTPSCloneURL": h.cloneHTTPS(cc.owner, cc.row.Name),
163168
 		"SSHEnabled":    h.d.CloneURLs.SSHEnabled,
164169
 		"SSHCloneURL":   h.cloneSSH(cc.owner, cc.row.Name),
170
+		"RepoTopics":    topics,
165171
 		"RepoCounts":    h.subnavCounts(r.Context(), cc.row.ID, cc.row.ForkCount),
166172
 		"CanSettings":   h.canViewSettings(middleware.CurrentUserFromContext(r.Context())),
167173
 		"ActiveSubnav":  "code",
168174
 	})
169175
 }
170176
 
177
+func refNames(refs repogit.RefListing) []string {
178
+	allNames := make([]string, 0, len(refs.Branches)+len(refs.Tags))
179
+	for _, b := range refs.Branches {
180
+		allNames = append(allNames, b.Name)
181
+	}
182
+	for _, t := range refs.Tags {
183
+		allNames = append(allNames, t.Name)
184
+	}
185
+	return allNames
186
+}
187
+
171188
 // findAndRenderREADME looks for README* in the supplied entries (case-
172189
 // insensitive). Returns rendered HTML for markdown sources; returns a
173190
 // `<pre>`-wrapped escaped string for non-markdown text. Empty when
internal/web/handlers/repo/issues.gomodified
@@ -172,10 +172,13 @@ func (h *Handlers) issueNewForm(w http.ResponseWriter, r *http.Request) {
172172
 	}
173173
 	w.Header().Set("Content-Type", "text/html; charset=utf-8")
174174
 	_ = h.d.Render.RenderPage(w, r, "repo/issue_new", map[string]any{
175
-		"Title":     "New issue · " + row.Name,
176
-		"Owner":     owner.Username,
177
-		"Repo":      row,
178
-		"CSRFToken": middleware.CSRFTokenForRequest(r),
175
+		"Title":        "New issue · " + row.Name,
176
+		"Owner":        owner.Username,
177
+		"Repo":         row,
178
+		"CSRFToken":    middleware.CSRFTokenForRequest(r),
179
+		"RepoCounts":   h.subnavCounts(r.Context(), row.ID, row.ForkCount),
180
+		"CanSettings":  h.canViewSettings(middleware.CurrentUserFromContext(r.Context())),
181
+		"ActiveSubnav": "issues",
179182
 	})
180183
 }
181184
 
@@ -228,13 +231,16 @@ func (h *Handlers) renderIssueCreateError(w http.ResponseWriter, r *http.Request
228231
 	w.Header().Set("Content-Type", "text/html; charset=utf-8")
229232
 	w.WriteHeader(http.StatusBadRequest)
230233
 	_ = h.d.Render.RenderPage(w, r, "repo/issue_new", map[string]any{
231
-		"Title":     "New issue · " + row.Name,
232
-		"Owner":     owner,
233
-		"Repo":      row,
234
-		"FormTitle": title,
235
-		"FormBody":  body,
236
-		"Error":     msg,
237
-		"CSRFToken": middleware.CSRFTokenForRequest(r),
234
+		"Title":        "New issue · " + row.Name,
235
+		"Owner":        owner,
236
+		"Repo":         row,
237
+		"FormTitle":    title,
238
+		"FormBody":     body,
239
+		"Error":        msg,
240
+		"CSRFToken":    middleware.CSRFTokenForRequest(r),
241
+		"RepoCounts":   h.subnavCounts(r.Context(), row.ID, row.ForkCount),
242
+		"CanSettings":  h.canViewSettings(middleware.CurrentUserFromContext(r.Context())),
243
+		"ActiveSubnav": "issues",
238244
 	})
239245
 }
240246
 
@@ -343,6 +349,9 @@ func (h *Handlers) issueView(w http.ResponseWriter, r *http.Request) {
343349
 		"CanEditIssueMilestone": policy.Can(r.Context(), pdeps, actor, policy.ActionIssueLabel, repoRef).Allow,
344350
 		"CanLockIssue":          policy.Can(r.Context(), pdeps, actor, policy.ActionIssueClose, repoRef).Allow,
345351
 		"CSRFToken":             middleware.CSRFTokenForRequest(r),
352
+		"RepoCounts":            h.subnavCounts(r.Context(), row.ID, row.ForkCount),
353
+		"CanSettings":           h.canViewSettings(viewer),
354
+		"ActiveSubnav":          "issues",
346355
 	})
347356
 }
348357
 
internal/web/handlers/repo/labels_milestones.gomodified
@@ -35,6 +35,9 @@ func (h *Handlers) labelsList(w http.ResponseWriter, r *http.Request) {
3535
 		"Labels":         labels,
3636
 		"CanManageIssue": canManage,
3737
 		"CSRFToken":      middleware.CSRFTokenForRequest(r),
38
+		"RepoCounts":     h.subnavCounts(r.Context(), row.ID, row.ForkCount),
39
+		"CanSettings":    h.canViewSettings(viewer),
40
+		"ActiveSubnav":   "issues",
3841
 	})
3942
 }
4043
 
@@ -134,6 +137,9 @@ func (h *Handlers) milestonesList(w http.ResponseWriter, r *http.Request) {
134137
 		"Milestones":     ms,
135138
 		"CanManageIssue": canManage,
136139
 		"CSRFToken":      middleware.CSRFTokenForRequest(r),
140
+		"RepoCounts":     h.subnavCounts(r.Context(), row.ID, row.ForkCount),
141
+		"CanSettings":    h.canViewSettings(viewer),
142
+		"ActiveSubnav":   "issues",
137143
 	})
138144
 }
139145
 
internal/web/handlers/repo/repo.gomodified
@@ -311,10 +311,8 @@ func (h *Handlers) ownerOptions(r *http.Request) []ownerOption {
311311
 	return out
312312
 }
313313
 
314
-// repoHome serves GET /{owner}/{repo}. Forks on whether the bare repo
315
-// has any branches: empty → quick-setup placeholder; populated → a slim
316
-// "post-push" view with the head commit on the default branch. The full
317
-// tree/file listing is S17.
314
+// repoHome serves GET /{owner}/{repo}. Empty repos render the quick setup
315
+// placeholder; populated repos render the Code tab at the repository home URL.
318316
 func (h *Handlers) repoHome(w http.ResponseWriter, r *http.Request) {
319317
 	owner := chi.URLParam(r, "owner")
320318
 	name := chi.URLParam(r, "repo")
@@ -332,9 +330,8 @@ func (h *Handlers) repoHome(w http.ResponseWriter, r *http.Request) {
332330
 		return
333331
 	}
334332
 
335
-	// S17: when the repo has any branch, the canonical view is the
336
-	// tree at default_branch. The S11 quick-setup placeholder still
337
-	// covers the empty case.
333
+	// Keep the populated repository home at /owner/name, matching GitHub's
334
+	// Code tab instead of forcing users through the /tree/default-branch URL.
338335
 	diskPath, fsErr := h.d.RepoFS.RepoPath(owner, row.Name)
339336
 	hasBranch := false
340337
 	if fsErr == nil {
@@ -345,7 +342,19 @@ func (h *Handlers) repoHome(w http.ResponseWriter, r *http.Request) {
345342
 		}
346343
 	}
347344
 	if hasBranch {
348
-		http.Redirect(w, r, "/"+owner+"/"+row.Name+"/tree/"+row.DefaultBranch, http.StatusSeeOther)
345
+		refs, refsErr := repogit.ListRefs(r.Context(), diskPath)
346
+		if refsErr != nil {
347
+			h.d.Logger.WarnContext(r.Context(), "repo: ListRefs", "error", refsErr)
348
+		}
349
+		h.renderRepoTree(w, r, &codeContext{
350
+			owner:   owner,
351
+			row:     row,
352
+			gitDir:  diskPath,
353
+			refs:    refs,
354
+			allRefs: refNames(refs),
355
+			ref:     row.DefaultBranch,
356
+			subpath: "",
357
+		})
349358
 		return
350359
 	}
351360
 
internal/web/render/octicons.gomodified
@@ -31,6 +31,22 @@ func BuiltinOcticons() OcticonResolver {
3131
 			`><path d="M9.598 1.591a.75.75 0 0 1 .785-.175 7 7 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786z"/></svg>`),
3232
 		"alert": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
3333
 			`><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575zM8 5a.75.75 0 0 0-.75.75v2.5a.75.75 0 0 0 1.5 0v-2.5A.75.75 0 0 0 8 5zm1 6a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/></svg>`),
34
+		"repo": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
35
+			`><path d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75A.75.75 0 0 1 14 .75v12.5a.75.75 0 0 1-.75.75H4.5a1 1 0 0 0 0 2h8.75a.75.75 0 0 1 0 1.5H4.5A2.5 2.5 0 0 1 2 15V2.5Zm2.5-1A1 1 0 0 0 3.5 2.5v10.21c.31-.13.648-.21 1-.21h8V1.5Z"/></svg>`),
36
+		"code": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
37
+			`><path d="M5.22 4.22a.75.75 0 0 1 1.06 1.06L3.56 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L1.97 8.53a.75.75 0 0 1 0-1.06Zm5.56 0a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 1 1-1.06-1.06L13.5 8l-2.72-2.72a.75.75 0 0 1 0-1.06Z"/></svg>`),
38
+		"git-pull-request": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
39
+			`><path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm2.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.5-9.5a.75.75 0 0 0-.75.75v1.5h1.5a.75.75 0 0 1 0 1.5h-1.5v1.5a.75.75 0 0 1-1.5 0v-1.5H8.5a.75.75 0 0 1 0-1.5H10v-1.5a.75.75 0 0 0-.75-.75H7.75A2.75 2.75 0 0 0 5 5.25V6a.75.75 0 0 1-1.5 0v-.75A4.25 4.25 0 0 1 7.75 1h1.5a2.25 2.25 0 0 1 2.25 2.25Z"/></svg>`),
40
+		"git-branch": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
41
+			`><path d="M4.25 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0ZM3.5 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm6.25-9.5a.75.75 0 0 0-.75.75V5.5h1.75A2.75 2.75 0 0 1 13.5 8.25v2.378a2.251 2.251 0 1 1-1.5 0V8.25A1.25 1.25 0 0 0 10.75 7H8.25A.75.75 0 0 1 7.5 6.25v-3a2.25 2.25 0 1 1 2.25 2.25V4a.75.75 0 0 0 0-1.5ZM12.75 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg>`),
42
+		"repo-forked": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
43
+			`><path d="M5 3.25a2.25 2.25 0 1 1-3 2.122v1.878A2.75 2.75 0 0 0 4.75 10H7.25v-4.628a2.25 2.25 0 1 1 1.5 0V10h2.5A2.75 2.75 0 0 0 14 7.25V5.372a2.25 2.25 0 1 1 1.5 0V7.25a4.25 4.25 0 0 1-4.25 4.25h-2.5v2.128a2.251 2.251 0 1 1-1.5 0V11.5H4.75A4.25 4.25 0 0 1 .5 7.25V5.372A2.25 2.25 0 0 1 5 3.25Zm-2.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm5.25 0a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm5.25 0a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM8 13.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"/></svg>`),
44
+		"eye": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
45
+			`><path d="M8 2c3.767 0 6.313 3.148 7.33 4.655a2.33 2.33 0 0 1 0 2.69C14.313 10.852 11.767 14 8 14S1.687 10.852.67 9.345a2.33 2.33 0 0 1 0-2.69C1.687 5.148 4.233 2 8 2Zm0 1.5c-3.07 0-5.248 2.621-6.087 3.865a.83.83 0 0 0 0 .97C2.752 9.579 4.93 12.5 8 12.5s5.248-2.921 6.087-4.165a.83.83 0 0 0 0-.97C13.248 6.121 11.07 3.5 8 3.5ZM8 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 1.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"/></svg>`),
46
+		"book": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
47
+			`><path d="M0 1.75A.75.75 0 0 1 .75 1h4.5C6.768 1 8 2.232 8 3.75A2.75 2.75 0 0 1 10.75 1h4.5a.75.75 0 0 1 .75.75v11.5a.75.75 0 0 1-.75.75h-4.5A1.25 1.25 0 0 0 9.5 15.25a.75.75 0 0 1-1.5 0A1.25 1.25 0 0 0 6.75 14H.75A.75.75 0 0 1 0 13.25ZM1.5 2.5v10h5.25c.63 0 1.21.23 1.75.61V3.75A1.25 1.25 0 0 0 7.25 2.5Zm7 10.61c.54-.38 1.12-.61 1.75-.61h4.25v-10h-3.75A1.25 1.25 0 0 0 9.5 3.75v9.36Z"/></svg>`),
48
+		"law": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
49
+			`><path d="M8.75.75a.75.75 0 0 0-1.5 0V2h-4.5a.75.75 0 0 0 0 1.5h.48L.34 9.13a.75.75 0 0 0-.09.36C.25 11.43 1.82 13 3.75 13s3.5-1.57 3.5-3.51a.75.75 0 0 0-.09-.36L4.27 3.5h2.98v10H5.75a.75.75 0 0 0 0 1.5h4.5a.75.75 0 0 0 0-1.5h-1.5v-10h2.98L8.84 9.13a.75.75 0 0 0-.09.36c0 1.94 1.57 3.51 3.5 3.51s3.5-1.57 3.5-3.51a.75.75 0 0 0-.09-.36L12.77 3.5h.48a.75.75 0 0 0 0-1.5h-4.5ZM3.75 11.5a2 2 0 0 1-1.88-1.31h3.76a2 2 0 0 1-1.88 1.31Zm8.5 0a2 2 0 0 1-1.88-1.31h3.76a2 2 0 0 1-1.88 1.31ZM2.2 8.69l1.55-3.02 1.55 3.02Zm8.5 0 1.55-3.02 1.55 3.02Z"/></svg>`),
3450
 		"issue-opened": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
3551
 			`><path d="M8 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-3.25a.75.75 0 0 1 .75.75v2.75a.75.75 0 0 1-1.5 0V5.5A.75.75 0 0 1 8 4.75ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"/></svg>`),
3652
 		"issue-closed": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
internal/web/static/css/shithub.cssmodified
@@ -903,7 +903,7 @@ code {
903903
    column. */
904904
 .shithub-repo-page {
905905
   max-width: 1280px;
906
-  margin: 1.5rem auto;
906
+  margin: 1rem auto 2rem;
907907
   padding: 0 1.25rem;
908908
 }
909909
 .shithub-code, .shithub-blob, .shithub-finder {
@@ -913,32 +913,34 @@ code {
913913
 /* Code body (tree + blob source) sits in a bordered panel so it
914914
    reads as a distinct surface — matches the panelled "Code" view in
915915
    the GitHub reference screenshots. */
916
-.shithub-tree, .shithub-blob-source {
916
+.shithub-blob-source {
917917
   border: 1px solid var(--border-default);
918918
   border-radius: 6px;
919919
   background: var(--canvas-subtle);
920920
   overflow-x: auto;
921921
 }
922
-.shithub-tree { background: var(--canvas-default); }
923922
 .shithub-blob-source { background: var(--canvas-default); }
924923
 .shithub-code-head {
925924
   display: flex;
926925
   align-items: center;
927926
   justify-content: space-between;
928
-  margin-bottom: 1rem;
929
-  padding-bottom: 0.5rem;
930
-  border-bottom: 1px solid var(--border-default);
927
+  gap: 0.75rem;
928
+  margin-bottom: 0.75rem;
929
+  flex-wrap: wrap;
931930
 }
932931
 .shithub-code-crumbs { font-size: 1rem; }
933932
 .shithub-code-crumbs a { color: var(--fg-default); }
934933
 .shithub-code-sep { color: var(--fg-muted); margin: 0 0.25rem; }
935934
 .shithub-code-actions { display: flex; gap: 0.5rem; align-items: center; }
935
+.shithub-code-primary-actions { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
936
+.shithub-code-count { color: var(--fg-muted); display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.9rem; }
936937
 
937938
 .shithub-ref-switcher { position: relative; }
938939
 .shithub-ref-switcher > summary {
939940
   list-style: none;
940941
   display: inline-flex;
941942
   align-items: center;
943
+  gap: 0.35rem;
942944
   padding: 0.3rem 0.7rem;
943945
   border: 1px solid var(--border-default);
944946
   border-radius: 6px;
@@ -1041,16 +1043,41 @@ code {
10411043
 .shithub-repo-list-meta { color: var(--fg-muted); font-size: 0.8rem; display: flex; gap: 1rem; flex-wrap: wrap; margin: 0.4rem 0 0; }
10421044
 .shithub-pill-archived { background: #ffd35a; color: #3b2300; }
10431045
 
1044
-/* Repo subnav (S30 polish): GitHub-style Code / Issues / Pulls / Settings tabs. */
1045
-.shithub-repo-page-head {
1046
-  margin-bottom: 0.25rem;
1046
+.shithub-repo-header { margin-bottom: 1.25rem; }
1047
+.shithub-repo-header-inner {
1048
+  display: flex;
1049
+  align-items: center;
1050
+  justify-content: space-between;
1051
+  gap: 1rem;
1052
+  padding: 0.25rem 0 0.75rem;
10471053
 }
1048
-.shithub-repo-page-title { font-size: 1.4rem; margin: 0.5rem 0; display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
1054
+.shithub-repo-page-title { font-size: 1.4rem; margin: 0; display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
10491055
 .shithub-repo-page-title a { color: var(--accent-fg, #4493f8); }
1056
+.shithub-repo-page-title .shithub-repo-name { font-weight: 600; }
1057
+.shithub-repo-title-icon { color: var(--fg-muted); display: inline-flex; align-items: center; }
1058
+.shithub-repo-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
1059
+.shithub-repo-action {
1060
+  display: inline-flex;
1061
+  align-items: center;
1062
+  gap: 0.35rem;
1063
+  padding: 0.35rem 0.7rem;
1064
+  border: 1px solid var(--border-default);
1065
+  border-radius: 6px;
1066
+  color: var(--fg-default);
1067
+  background: var(--canvas-subtle);
1068
+  font-size: 0.875rem;
1069
+  font-weight: 600;
1070
+}
1071
+.shithub-repo-action span {
1072
+  color: var(--fg-muted);
1073
+  font-weight: 600;
1074
+  padding-left: 0.35rem;
1075
+  border-left: 1px solid var(--border-default);
1076
+}
10501077
 .shithub-repo-subnav {
10511078
   display: flex;
10521079
   gap: 0.25rem;
1053
-  margin: 0 0 1.25rem;
1080
+  margin: 0;
10541081
   border-bottom: 1px solid var(--border-default);
10551082
   flex-wrap: wrap;
10561083
 }
@@ -1074,15 +1101,49 @@ code {
10741101
 .shithub-repo-new-owner select { padding: 0.4rem 0.5rem; }
10751102
 .shithub-repo-new-sep { font-size: 1.5rem; color: var(--fg-muted); padding-bottom: 0.4rem; }
10761103
 
1104
+.shithub-repo-content-grid {
1105
+  display: grid;
1106
+  grid-template-columns: minmax(0, 1fr) 296px;
1107
+  gap: 1.5rem;
1108
+  align-items: start;
1109
+}
1110
+.shithub-repo-main { min-width: 0; }
1111
+.shithub-tree-panel {
1112
+  border: 1px solid var(--border-default);
1113
+  border-radius: 6px;
1114
+  background: var(--canvas-default);
1115
+  overflow: hidden;
1116
+}
1117
+.shithub-tree-commit {
1118
+  display: flex;
1119
+  justify-content: space-between;
1120
+  align-items: center;
1121
+  gap: 1rem;
1122
+  padding: 0.75rem 1rem;
1123
+  background: var(--canvas-subtle);
1124
+  border-bottom: 1px solid var(--border-default);
1125
+  font-size: 0.9rem;
1126
+}
1127
+.shithub-tree-commit-message,
1128
+.shithub-tree-commit-meta {
1129
+  display: flex;
1130
+  align-items: center;
1131
+  gap: 0.5rem;
1132
+  min-width: 0;
1133
+}
1134
+.shithub-tree-commit-message span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1135
+.shithub-tree-commit-meta { color: var(--fg-muted); flex: 0 0 auto; }
10771136
 .shithub-tree {
10781137
   width: 100%;
10791138
   border-collapse: collapse;
10801139
   font-size: 0.9rem;
1140
+  background: var(--canvas-default);
10811141
 }
10821142
 .shithub-tree td {
1083
-  padding: 0.4rem 0.5rem;
1143
+  padding: 0.5rem 0.75rem;
10841144
   border-bottom: 1px solid var(--border-default);
10851145
 }
1146
+.shithub-tree tr:last-child td { border-bottom: 0; }
10861147
 .shithub-tree-icon { width: 24px; color: var(--fg-muted); }
10871148
 .shithub-tree-icon svg { display: block; }
10881149
 .shithub-tree-name a { color: var(--fg-default); }
@@ -1090,17 +1151,68 @@ code {
10901151
 .shithub-tree-symlink, .shithub-tree-submodule { color: var(--fg-muted); font-style: italic; font-size: 0.8rem; }
10911152
 
10921153
 .shithub-readme {
1093
-  margin-top: 2rem;
1094
-  padding: 1.25rem;
1154
+  margin-top: 1rem;
1155
+  padding: 0;
10951156
   border: 1px solid var(--border-default);
10961157
   border-radius: 6px;
1097
-  background: var(--canvas-subtle);
1158
+  background: var(--canvas-default);
1159
+  overflow: hidden;
10981160
 }
1161
+.shithub-readme-head {
1162
+  display: flex;
1163
+  align-items: center;
1164
+  gap: 0.45rem;
1165
+  padding: 0.75rem 1rem;
1166
+  border-bottom: 1px solid var(--border-default);
1167
+  font-weight: 600;
1168
+}
1169
+.shithub-readme-body { padding: 1rem; }
10991170
 .shithub-readme h1, .shithub-readme h2 { border-bottom: 1px solid var(--border-default); padding-bottom: 0.3rem; }
11001171
 .shithub-readme code { font-family: monospace; padding: 0.1em 0.3em; background: var(--canvas-default); border-radius: 3px; }
11011172
 .shithub-readme pre code { padding: 0; background: none; }
11021173
 .shithub-readme-plain { white-space: pre-wrap; }
11031174
 
1175
+.shithub-repo-about { font-size: 0.9rem; }
1176
+.shithub-repo-about-heading {
1177
+  display: flex;
1178
+  justify-content: space-between;
1179
+  align-items: center;
1180
+  gap: 0.75rem;
1181
+  margin-bottom: 0.75rem;
1182
+}
1183
+.shithub-repo-about-heading h2 { font-size: 1rem; margin: 0; }
1184
+.shithub-repo-about-heading a { color: var(--fg-muted); display: inline-flex; }
1185
+.shithub-repo-about-desc { margin: 0 0 0.85rem; font-size: 1rem; line-height: 1.45; }
1186
+.shithub-repo-topics { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.9rem; }
1187
+.shithub-topic {
1188
+  display: inline-flex;
1189
+  padding: 0.18rem 0.55rem;
1190
+  border-radius: 999px;
1191
+  background: color-mix(in srgb, var(--accent-fg, #4493f8) 12%, transparent);
1192
+  color: var(--accent-fg, #4493f8);
1193
+  font-size: 0.8rem;
1194
+  font-weight: 600;
1195
+}
1196
+.shithub-repo-about-list {
1197
+  list-style: none;
1198
+  padding: 0.85rem 0 0;
1199
+  margin: 0;
1200
+  border-top: 1px solid var(--border-default);
1201
+  color: var(--fg-muted);
1202
+}
1203
+.shithub-repo-about-list li {
1204
+  display: flex;
1205
+  align-items: center;
1206
+  gap: 0.55rem;
1207
+  margin: 0.55rem 0;
1208
+}
1209
+.shithub-repo-about-list svg { flex: 0 0 auto; }
1210
+@media (max-width: 900px) {
1211
+  .shithub-repo-header-inner { align-items: flex-start; flex-direction: column; }
1212
+  .shithub-repo-content-grid { grid-template-columns: 1fr; }
1213
+  .shithub-tree-commit { align-items: flex-start; flex-direction: column; }
1214
+}
1215
+
11041216
 .shithub-blob-meta { color: var(--fg-muted); font-size: 0.85rem; margin-right: 0.5rem; }
11051217
 /* Blob source: a row-per-line <table>. We render the gutter + code
11061218
    ourselves and feed Chroma only token spans (no <pre>, no <table>)
@@ -1366,9 +1478,8 @@ code {
13661478
 
13671479
 /* ========== Issues / Labels / Milestones (S21) ========== */
13681480
 .shithub-issues, .shithub-issue-view, .shithub-issue-new, .shithub-labels, .shithub-milestones {
1369
-  max-width: 64rem;
1370
-  margin: 1.5rem auto;
1371
-  padding: 0 1rem;
1481
+  margin: 0;
1482
+  padding: 0;
13721483
 }
13731484
 .shithub-issues-head { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; }
13741485
 .shithub-issues-actions { display: flex; gap: 0.4rem; }
internal/web/templates/_repo_about_sidebar.htmladded
@@ -0,0 +1,26 @@
1
+{{ define "repo-about-sidebar" -}}
2
+<aside class="shithub-repo-about" aria-label="Repository information">
3
+  <div class="shithub-repo-about-heading">
4
+    <h2>About</h2>
5
+    {{ if .CanSettings }}<a href="/{{ .Owner }}/{{ .Repo.Name }}/settings/general" aria-label="Edit repository about">{{ octicon "gear" }}</a>{{ end }}
6
+  </div>
7
+  {{ if .Repo.Description }}
8
+    <p class="shithub-repo-about-desc">{{ .Repo.Description }}</p>
9
+  {{ else }}
10
+    <p class="shithub-muted">No description, website, or topics provided.</p>
11
+  {{ end }}
12
+  {{ if .RepoTopics }}
13
+  <div class="shithub-repo-topics">
14
+    {{ range .RepoTopics }}<a href="/search?q=topic%3A{{ . }}" class="shithub-topic">{{ . }}</a>{{ end }}
15
+  </div>
16
+  {{ end }}
17
+  <ul class="shithub-repo-about-list">
18
+    <li>{{ octicon "book" }} <a href="#readme">Readme</a></li>
19
+    {{ if .Repo.LicenseKey.Valid }}<li>{{ octicon "law" }} {{ .Repo.LicenseKey.String }} license</li>{{ end }}
20
+    {{ if .Repo.PrimaryLanguage.Valid }}<li>{{ octicon "code" }} {{ .Repo.PrimaryLanguage.String }}</li>{{ end }}
21
+    <li>{{ octicon "star" }} {{ .Repo.StarCount }} star{{ if ne .Repo.StarCount 1 }}s{{ end }}</li>
22
+    <li>{{ octicon "eye" }} {{ .Repo.WatcherCount }} watching</li>
23
+    <li>{{ octicon "repo-forked" }} {{ .Repo.ForkCount }} fork{{ if ne .Repo.ForkCount 1 }}s{{ end }}</li>
24
+  </ul>
25
+</aside>
26
+{{- end }}
internal/web/templates/_repo_header.htmladded
@@ -0,0 +1,19 @@
1
+{{ define "repo-header" -}}
2
+<header class="shithub-repo-header">
3
+  <div class="shithub-repo-header-inner">
4
+    <h1 class="shithub-repo-page-title">
5
+      <span class="shithub-repo-title-icon">{{ octicon "repo" }}</span>
6
+      <a href="/{{ .Owner }}">{{ .Owner }}</a>
7
+      <span class="shithub-code-sep">/</span>
8
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}" class="shithub-repo-name">{{ .Repo.Name }}</a>
9
+      {{ if eq (printf "%s" .Repo.Visibility) "private" }}<span class="shithub-pill shithub-pill-private">Private</span>{{ else }}<span class="shithub-pill">Public</span>{{ end }}
10
+    </h1>
11
+    <div class="shithub-repo-actions" aria-label="Repository actions">
12
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/watchers" class="shithub-repo-action">{{ octicon "eye" }} Watch <span>{{ .Repo.WatcherCount }}</span></a>
13
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/forks" class="shithub-repo-action">{{ octicon "repo-forked" }} Fork <span>{{ .Repo.ForkCount }}</span></a>
14
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/stargazers" class="shithub-repo-action">{{ octicon "star" }} Star <span>{{ .Repo.StarCount }}</span></a>
15
+    </div>
16
+  </div>
17
+  {{ template "repo-subnav" . }}
18
+</header>
19
+{{- end }}
internal/web/templates/_repo_subnav.htmlmodified
@@ -1,23 +1,23 @@
11
 {{ define "repo-subnav" -}}
22
 <nav class="shithub-repo-subnav" aria-label="Repository sections">
33
   <a href="/{{ .Owner }}/{{ .Repo.Name }}" class="shithub-repo-subnav-tab{{ if eq .ActiveSubnav "code" }} is-active{{ end }}">
4
-    {{ octicon "directory" }} Code
4
+    {{ octicon "code" }} Code
55
   </a>
66
   <a href="/{{ .Owner }}/{{ .Repo.Name }}/issues" class="shithub-repo-subnav-tab{{ if eq .ActiveSubnav "issues" }} is-active{{ end }}">
7
-    {{ octicon "alert" }} Issues
7
+    {{ octicon "issue-opened" }} Issues
88
     {{ with .RepoCounts.Issues }}<span class="shithub-tab-count">{{ . }}</span>{{ end }}
99
   </a>
1010
   <a href="/{{ .Owner }}/{{ .Repo.Name }}/pulls" class="shithub-repo-subnav-tab{{ if eq .ActiveSubnav "pulls" }} is-active{{ end }}">
11
-    {{ octicon "submodule" }} Pull requests
11
+    {{ octicon "git-pull-request" }} Pull requests
1212
     {{ with .RepoCounts.Pulls }}<span class="shithub-tab-count">{{ . }}</span>{{ end }}
1313
   </a>
1414
   <a href="/{{ .Owner }}/{{ .Repo.Name }}/forks" class="shithub-repo-subnav-tab{{ if eq .ActiveSubnav "forks" }} is-active{{ end }}">
15
-    {{ octicon "submodule" }} Forks
15
+    {{ octicon "repo-forked" }} Forks
1616
     {{ with .RepoCounts.Forks }}<span class="shithub-tab-count">{{ . }}</span>{{ end }}
1717
   </a>
1818
   {{ if .CanSettings }}
1919
   <a href="/{{ .Owner }}/{{ .Repo.Name }}/settings" class="shithub-repo-subnav-tab{{ if eq .ActiveSubnav "settings" }} is-active{{ end }}">
20
-    {{ octicon "directory" }} Settings
20
+    {{ octicon "gear" }} Settings
2121
   </a>
2222
   {{ end }}
2323
 </nav>
internal/web/templates/repo/issue_new.htmlmodified
@@ -1,9 +1,9 @@
11
 {{ define "page" -}}
2
-<section class="shithub-issue-new">
2
+<section class="shithub-repo-page">
3
+  {{ template "repo-header" . }}
4
+  <section class="shithub-issue-new">
35
   <header class="shithub-issues-head">
46
     <h1>
5
-      <a href="/{{ .Owner }}/{{ .Repo.Name }}">{{ .Owner }}/{{ .Repo.Name }}</a>
6
-      <span class="shithub-code-sep">/</span>
77
       <a href="/{{ .Owner }}/{{ .Repo.Name }}/issues">Issues</a>
88
       <span class="shithub-code-sep">/</span>
99
       New
@@ -27,5 +27,6 @@
2727
       <button type="submit" class="shithub-button shithub-button-primary">Submit new issue</button>
2828
     </div>
2929
   </form>
30
+  </section>
3031
 </section>
3132
 {{- end }}
internal/web/templates/repo/issue_view.htmlmodified
@@ -1,26 +1,28 @@
11
 {{ define "page" -}}
2
-<section class="shithub-issue-view">
3
-  <header class="shithub-issue-view-head">
4
-    <div class="shithub-issue-title-row">
5
-      <h1 class="shithub-issue-title">
6
-        <span>{{ .Issue.Title }}</span>
7
-        <span class="shithub-issue-num">#{{ .Issue.Number }}</span>
8
-      </h1>
9
-      <div class="shithub-issue-head-actions">
10
-        <a href="/{{ .Owner }}/{{ .Repo.Name }}/issues/new" class="shithub-button shithub-button-primary">New issue</a>
2
+<section class="shithub-repo-page">
3
+  {{ template "repo-header" . }}
4
+  <section class="shithub-issue-view">
5
+    <header class="shithub-issue-view-head">
6
+      <div class="shithub-issue-title-row">
7
+        <h1 class="shithub-issue-title">
8
+          <span>{{ .Issue.Title }}</span>
9
+          <span class="shithub-issue-num">#{{ .Issue.Number }}</span>
10
+        </h1>
11
+        <div class="shithub-issue-head-actions">
12
+          <a href="/{{ .Owner }}/{{ .Repo.Name }}/issues/new" class="shithub-button shithub-button-primary">New issue</a>
13
+        </div>
14
+      </div>
15
+      <div class="shithub-issue-meta">
16
+        <span class="shithub-pill shithub-issues-state-{{ printf "%s" .Issue.State }}">
17
+          {{ if eq (printf "%s" .Issue.State) "open" }}{{ octicon "issue-opened" }} Open{{ else }}{{ octicon "issue-closed" }} Closed{{ end }}
18
+        </span>
19
+        {{ if .AuthorName }}<a href="/{{ .AuthorName }}">{{ .AuthorName }}</a>{{ end }}
20
+        opened this issue
21
+        <time datetime="{{ .Issue.CreatedAt.Time.Format "2006-01-02T15:04:05Z" }}">{{ relativeTime .Issue.CreatedAt.Time }}</time>
22
+        · {{ .CommentCount }} comment{{ if ne .CommentCount 1 }}s{{ end }}
23
+        {{ if .Issue.Locked }}<span class="shithub-pill">{{ octicon "lock" }} locked</span>{{ end }}
1124
       </div>
12
-    </div>
13
-    <div class="shithub-issue-meta">
14
-      <span class="shithub-pill shithub-issues-state-{{ printf "%s" .Issue.State }}">
15
-        {{ if eq (printf "%s" .Issue.State) "open" }}{{ octicon "issue-opened" }} Open{{ else }}{{ octicon "issue-closed" }} Closed{{ end }}
16
-      </span>
17
-      {{ if .AuthorName }}<a href="/{{ .AuthorName }}">{{ .AuthorName }}</a>{{ end }}
18
-      opened this issue
19
-      <time datetime="{{ .Issue.CreatedAt.Time.Format "2006-01-02T15:04:05Z" }}">{{ relativeTime .Issue.CreatedAt.Time }}</time>
20
-      · {{ .CommentCount }} comment{{ if ne .CommentCount 1 }}s{{ end }}
21
-      {{ if .Issue.Locked }}<span class="shithub-pill">{{ octicon "lock" }} locked</span>{{ end }}
22
-    </div>
23
-  </header>
25
+    </header>
2426
 
2527
   <div class="shithub-issue-grid">
2628
     <article class="shithub-issue-thread">
@@ -243,5 +245,6 @@
243245
       {{ end }}
244246
     </aside>
245247
   </div>
248
+  </section>
246249
 </section>
247250
 {{- end }}
internal/web/templates/repo/issues_list.htmlmodified
@@ -1,15 +1,7 @@
11
 {{ define "page" -}}
22
 <section class="shithub-repo-page">
3
-  <header class="shithub-repo-page-head">
4
-    <h1 class="shithub-repo-page-title">
5
-      <a href="/{{ .Owner }}">{{ .Owner }}</a>
6
-      <span class="shithub-code-sep">/</span>
7
-      <a href="/{{ .Owner }}/{{ .Repo.Name }}">{{ .Repo.Name }}</a>
8
-      {{ if eq (printf "%s" .Repo.Visibility) "private" }}<span class="shithub-pill shithub-pill-private">private</span>{{ else }}<span class="shithub-pill">public</span>{{ end }}
9
-    </h1>
10
-  </header>
11
-  {{ template "repo-subnav" . }}
12
-<section class="shithub-issues">
3
+  {{ template "repo-header" . }}
4
+  <section class="shithub-issues">
135
   <header class="shithub-issues-head">
146
     <h1>Issues</h1>
157
     <div class="shithub-issues-actions">
internal/web/templates/repo/labels.htmlmodified
@@ -1,9 +1,9 @@
11
 {{ define "page" -}}
2
-<section class="shithub-labels">
2
+<section class="shithub-repo-page">
3
+  {{ template "repo-header" . }}
4
+  <section class="shithub-labels">
35
   <header class="shithub-issues-head">
46
     <h1>
5
-      <a href="/{{ .Owner }}/{{ .Repo.Name }}">{{ .Owner }}/{{ .Repo.Name }}</a>
6
-      <span class="shithub-code-sep">/</span>
77
       Labels
88
     </h1>
99
   </header>
@@ -45,5 +45,6 @@
4545
     </li>
4646
     {{ end }}
4747
   </ul>
48
+  </section>
4849
 </section>
4950
 {{- end }}
internal/web/templates/repo/milestones.htmlmodified
@@ -1,9 +1,9 @@
11
 {{ define "page" -}}
2
-<section class="shithub-milestones">
2
+<section class="shithub-repo-page">
3
+  {{ template "repo-header" . }}
4
+  <section class="shithub-milestones">
35
   <header class="shithub-issues-head">
46
     <h1>
5
-      <a href="/{{ .Owner }}/{{ .Repo.Name }}">{{ .Owner }}/{{ .Repo.Name }}</a>
6
-      <span class="shithub-code-sep">/</span>
77
       Milestones
88
     </h1>
99
   </header>
@@ -48,5 +48,6 @@
4848
     </li>
4949
     {{ end }}
5050
   </ul>
51
+  </section>
5152
 </section>
5253
 {{- end }}
internal/web/templates/repo/tree.htmlmodified
@@ -1,99 +1,117 @@
11
 {{ define "page" -}}
22
 <section class="shithub-repo-page">
3
-  <header class="shithub-repo-page-head">
4
-    <h1 class="shithub-repo-page-title">
5
-      <a href="/{{ .Owner }}">{{ .Owner }}</a>
6
-      <span class="shithub-code-sep">/</span>
7
-      <a href="/{{ .Owner }}/{{ .Repo.Name }}">{{ .Repo.Name }}</a>
8
-      {{ if eq (printf "%s" .Repo.Visibility) "private" }}<span class="shithub-pill shithub-pill-private">private</span>{{ else }}<span class="shithub-pill">public</span>{{ end }}
9
-    </h1>
10
-  </header>
11
-  {{ template "repo-subnav" . }}
3
+  {{ template "repo-header" . }}
124
 
13
-<section class="shithub-code">
14
-  <header class="shithub-code-head">
15
-    <nav class="shithub-code-crumbs" aria-label="Breadcrumb">
16
-      {{ range $i, $c := .Crumbs }}
17
-        {{ if $i }}<span class="shithub-code-sep">/</span>{{ end }}
18
-        <a href="{{ $c.URL }}">{{ $c.Name }}</a>
19
-      {{ end }}
20
-    </nav>
21
-    <div class="shithub-code-actions">
22
-      <details class="shithub-ref-switcher">
23
-        <summary>{{ .Ref }}</summary>
24
-        <div class="shithub-ref-panel">
25
-          {{ if .Branches }}
26
-          <strong>Branches</strong>
27
-          {{ range .Branches }}
28
-          <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ .Name }}">{{ .Name }}</a>
29
-          {{ end }}
30
-          {{ end }}
31
-          {{ if .Tags }}
32
-          <strong>Tags</strong>
33
-          {{ range .Tags }}
34
-          <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ .Name }}">{{ .Name }}</a>
35
-          {{ end }}
5
+  <div class="shithub-repo-content-grid">
6
+    <main class="shithub-repo-main">
7
+      <section class="shithub-code">
8
+        <header class="shithub-code-head">
9
+          <div class="shithub-code-primary-actions">
10
+            <details class="shithub-ref-switcher">
11
+              <summary>{{ octicon "git-branch" }} {{ .Ref }}</summary>
12
+              <div class="shithub-ref-panel">
13
+                {{ if .Branches }}
14
+                <strong>Branches</strong>
15
+                {{ range .Branches }}
16
+                <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ .Name }}">{{ .Name }}</a>
17
+                {{ end }}
18
+                {{ end }}
19
+                {{ if .Tags }}
20
+                <strong>Tags</strong>
21
+                {{ range .Tags }}
22
+                <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ .Name }}">{{ .Name }}</a>
23
+                {{ end }}
24
+                {{ end }}
25
+              </div>
26
+            </details>
27
+            <a href="/{{ .Owner }}/{{ .Repo.Name }}/branches" class="shithub-code-count">{{ octicon "git-branch" }} {{ len .Branches }} Branches</a>
28
+            <a href="/{{ .Owner }}/{{ .Repo.Name }}/tags" class="shithub-code-count">{{ octicon "tag" }} {{ len .Tags }} Tags</a>
29
+          </div>
30
+          <div class="shithub-code-actions">
31
+            <a href="/{{ .Owner }}/{{ .Repo.Name }}/find/{{ .Ref }}" class="shithub-button">Go to file</a>
32
+            <details class="shithub-clone-dropdown">
33
+              <summary class="shithub-button shithub-button-primary">{{ octicon "code" }} Code</summary>
34
+              <div class="shithub-clone-panel" role="dialog" aria-label="Clone this repository">
35
+                <div class="shithub-clone-row">
36
+                  <label for="clone-https">Clone with HTTPS</label>
37
+                  <div class="shithub-clone-input">
38
+                    <input id="clone-https" type="text" readonly value="{{ .HTTPSCloneURL }}" onclick="this.select()">
39
+                    <button type="button" class="shithub-button" data-clone-copy="#clone-https" title="Copy">{{ octicon "copy" }}</button>
40
+                  </div>
41
+                </div>
42
+                {{ if .SSHEnabled }}
43
+                <div class="shithub-clone-row">
44
+                  <label for="clone-ssh">Clone with SSH</label>
45
+                  <div class="shithub-clone-input">
46
+                    <input id="clone-ssh" type="text" readonly value="{{ .SSHCloneURL }}" onclick="this.select()">
47
+                    <button type="button" class="shithub-button" data-clone-copy="#clone-ssh" title="Copy">{{ octicon "copy" }}</button>
48
+                  </div>
49
+                </div>
50
+                {{ end }}
51
+                <p class="shithub-clone-hint">Use Git or checkout with SVN using the web URL.</p>
52
+              </div>
53
+            </details>
54
+          </div>
55
+        </header>
56
+
57
+        {{ if .Path }}
58
+        <nav class="shithub-code-crumbs" aria-label="Breadcrumb">
59
+          {{ range $i, $c := .Crumbs }}
60
+            {{ if $i }}<span class="shithub-code-sep">/</span>{{ end }}
61
+            <a href="{{ $c.URL }}">{{ $c.Name }}</a>
3662
           {{ end }}
37
-        </div>
38
-      </details>
39
-      <a href="/{{ .Owner }}/{{ .Repo.Name }}/find/{{ .Ref }}" class="shithub-button">Go to file</a>
40
-      <details class="shithub-clone-dropdown">
41
-        <summary class="shithub-button shithub-button-primary">{{ octicon "download" }} Code</summary>
42
-        <div class="shithub-clone-panel" role="dialog" aria-label="Clone this repository">
43
-          <div class="shithub-clone-row">
44
-            <label for="clone-https">Clone with HTTPS</label>
45
-            <div class="shithub-clone-input">
46
-              <input id="clone-https" type="text" readonly value="{{ .HTTPSCloneURL }}" onclick="this.select()">
47
-              <button type="button" class="shithub-button" data-clone-copy="#clone-https" title="Copy">{{ octicon "copy" }}</button>
63
+        </nav>
64
+        {{ end }}
65
+
66
+        <div class="shithub-tree-panel">
67
+          {{ if .HeadFound }}
68
+          <div class="shithub-tree-commit">
69
+            <div class="shithub-tree-commit-message">
70
+              <strong>{{ .Head.AuthorName }}</strong>
71
+              <span>{{ .Head.Subject }}</span>
4872
             </div>
49
-          </div>
50
-          {{ if .SSHEnabled }}
51
-          <div class="shithub-clone-row">
52
-            <label for="clone-ssh">Clone with SSH</label>
53
-            <div class="shithub-clone-input">
54
-              <input id="clone-ssh" type="text" readonly value="{{ .SSHCloneURL }}" onclick="this.select()">
55
-              <button type="button" class="shithub-button" data-clone-copy="#clone-ssh" title="Copy">{{ octicon "copy" }}</button>
73
+            <div class="shithub-tree-commit-meta">
74
+              <code title="{{ .Head.OID }}">{{ slice .Head.OID 0 7 }}</code>
75
+              <time datetime="{{ .Head.AuthorWhen.Format "2006-01-02T15:04:05Z" }}">{{ relativeTime .Head.AuthorWhen }}</time>
5676
             </div>
5777
           </div>
5878
           {{ end }}
59
-          <p class="shithub-clone-hint">Use Git or checkout with SVN using the web URL.</p>
79
+          <table class="shithub-tree">
80
+            <tbody>
81
+              {{ range .Entries }}
82
+              <tr>
83
+                <td class="shithub-tree-icon" aria-hidden="true">
84
+                  {{ if eq (printf "%s" .Kind) "tree" }}{{ octicon "directory" }}
85
+                  {{ else if eq (printf "%s" .Kind) "commit" }}{{ octicon "submodule" }}
86
+                  {{ else if eq (printf "%s" .Kind) "symlink" }}{{ octicon "symlink" }}
87
+                  {{ else }}{{ octicon "file" }}{{ end }}
88
+                </td>
89
+                <td class="shithub-tree-name">
90
+                  {{ if eq (printf "%s" .Kind) "tree" }}
91
+                  <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ $.Ref }}/{{ if $.Path }}{{ $.Path }}/{{ end }}{{ .Name }}">{{ .Name }}</a>
92
+                  {{ else if eq (printf "%s" .Kind) "blob" }}
93
+                  <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/blob/{{ $.Ref }}/{{ if $.Path }}{{ $.Path }}/{{ end }}{{ .Name }}">{{ .Name }}</a>
94
+                  {{ else if eq (printf "%s" .Kind) "symlink" }}
95
+                  {{ .Name }} <em class="shithub-tree-symlink">(symlink)</em>
96
+                  {{ else }}
97
+                  {{ .Name }} <em class="shithub-tree-submodule">@ {{ slice .OID 0 7 }}</em>
98
+                  {{ end }}
99
+                </td>
100
+                <td class="shithub-tree-size">{{ if gt .Size 0 }}{{ .Size }}{{ end }}</td>
101
+              </tr>
102
+              {{ end }}
103
+            </tbody>
104
+          </table>
60105
         </div>
61
-      </details>
62
-    </div>
63
-  </header>
64
-
65
-  <table class="shithub-tree">
66
-    <tbody>
67
-      {{ range .Entries }}
68
-      <tr>
69
-        <td class="shithub-tree-icon" aria-hidden="true">
70
-          {{ if eq (printf "%s" .Kind) "tree" }}{{ octicon "directory" }}
71
-          {{ else if eq (printf "%s" .Kind) "commit" }}{{ octicon "submodule" }}
72
-          {{ else if eq (printf "%s" .Kind) "symlink" }}{{ octicon "symlink" }}
73
-          {{ else }}{{ octicon "file" }}{{ end }}
74
-        </td>
75
-        <td class="shithub-tree-name">
76
-          {{ if eq (printf "%s" .Kind) "tree" }}
77
-          <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ $.Ref }}/{{ if $.Path }}{{ $.Path }}/{{ end }}{{ .Name }}">{{ .Name }}</a>
78
-          {{ else if eq (printf "%s" .Kind) "blob" }}
79
-          <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/blob/{{ $.Ref }}/{{ if $.Path }}{{ $.Path }}/{{ end }}{{ .Name }}">{{ .Name }}</a>
80
-          {{ else if eq (printf "%s" .Kind) "symlink" }}
81
-          {{ .Name }} <em class="shithub-tree-symlink">(symlink)</em>
82
-          {{ else }}
83
-          {{ .Name }} <em class="shithub-tree-submodule">@ {{ slice .OID 0 7 }}</em>
84
-          {{ end }}
85
-        </td>
86
-        <td class="shithub-tree-size">{{ if gt .Size 0 }}{{ .Size }}{{ end }}</td>
87
-      </tr>
106
+      </section>
107
+      {{ if .README }}
108
+      <section class="shithub-readme markdown-body" id="readme" aria-label="README">
109
+        <div class="shithub-readme-head">{{ octicon "book" }} README</div>
110
+        <div class="shithub-readme-body">{{ .README }}</div>
111
+      </section>
88112
       {{ end }}
89
-    </tbody>
90
-  </table>
91
-
92
-  </section>
93
-  {{ if .README }}
94
-  <section class="shithub-readme" aria-label="README">
95
-    {{ .README }}
96
-  </section>
97
-  {{ end }}
113
+    </main>
114
+    {{ template "repo-about-sidebar" . }}
115
+  </div>
98116
 </section>
99117
 {{- end }}