tenseleyflow/shithub / e97d889

Browse files

S17: tree/blob/finder templates + tree icons + code-tab CSS

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
e97d889045e38e79639b4fe75a6b82d7c6129add
Parents
912d82a
Tree
12f492f

6 changed files

StatusFile+-
M internal/web/render/octicons.go 9 0
M internal/web/static/css/shithub.css 100 0
M internal/web/templates/_layout.html 1 0
A internal/web/templates/repo/blob.html 44 0
A internal/web/templates/repo/finder.html 22 0
A internal/web/templates/repo/tree.html 65 0
internal/web/render/octicons.gomodified
@@ -31,6 +31,15 @@ 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
+		// Tree-listing icons used by the code tab (S17).
35
+		"directory": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
36
+			`><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3h-6.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.358 1.26 5.769 1 5.142 1z"/></svg>`),
37
+		"file": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
38
+			`><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25zm10.5 1.5L9.75 1.75v3.5h3.5L12.5 3.25z"/></svg>`),
39
+		"submodule": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
40
+			`><path d="M9.75 6.5h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5zm0 4h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5zm-7.5-9C1.784 1.5 1 2.284 1 3.25v9.5C1 13.716 1.784 14.5 2.75 14.5h10.5A1.75 1.75 0 0 0 15 12.75v-7.5A1.75 1.75 0 0 0 13.25 3.5h-5.69L6.276 2.22A2.25 2.25 0 0 0 4.69 1.5z"/></svg>`),
41
+		"symlink": trustedSVG(`<svg xmlns="http://www.w3.org/2000/svg" ` + cls +
42
+			`><path d="M9 11h2.5a2.5 2.5 0 0 0 0-5H9V4.25a.75.75 0 0 0-1.28-.53L3.97 7.47a.75.75 0 0 0 0 1.06l3.75 3.75A.75.75 0 0 0 9 11.75z"/></svg>`),
3443
 	}
3544
 	return func(name string) (template.HTML, bool) {
3645
 		v, ok := icons[name]
internal/web/static/css/shithub.cssmodified
@@ -891,3 +891,103 @@ code {
891891
   background: var(--canvas-default);
892892
 }
893893
 .shithub-repo-populated-note { margin-top: 1rem; font-size: 0.8rem; color: var(--fg-muted); }
894
+
895
+/* ========== Code tab (S17) ========== */
896
+.shithub-code, .shithub-blob, .shithub-finder {
897
+  max-width: 64rem;
898
+  margin: 1.5rem auto;
899
+  padding: 0 1rem;
900
+}
901
+.shithub-code-head {
902
+  display: flex;
903
+  align-items: center;
904
+  justify-content: space-between;
905
+  margin-bottom: 1rem;
906
+  padding-bottom: 0.5rem;
907
+  border-bottom: 1px solid var(--border-default);
908
+}
909
+.shithub-code-crumbs { font-size: 1rem; }
910
+.shithub-code-crumbs a { color: var(--fg-default); }
911
+.shithub-code-sep { color: var(--fg-muted); margin: 0 0.25rem; }
912
+.shithub-code-actions { display: flex; gap: 0.5rem; align-items: center; }
913
+
914
+.shithub-ref-switcher { position: relative; }
915
+.shithub-ref-switcher > summary {
916
+  list-style: none;
917
+  display: inline-flex;
918
+  align-items: center;
919
+  padding: 0.3rem 0.7rem;
920
+  border: 1px solid var(--border-default);
921
+  border-radius: 6px;
922
+  cursor: pointer;
923
+  font-size: 0.875rem;
924
+}
925
+.shithub-ref-switcher > summary::-webkit-details-marker { display: none; }
926
+.shithub-ref-panel {
927
+  position: absolute;
928
+  z-index: 30;
929
+  top: calc(100% + 0.4rem);
930
+  left: 0;
931
+  min-width: 220px;
932
+  max-height: 360px;
933
+  overflow-y: auto;
934
+  background: var(--canvas-default);
935
+  border: 1px solid var(--border-default);
936
+  border-radius: 6px;
937
+  padding: 0.5rem;
938
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
939
+}
940
+.shithub-ref-panel a {
941
+  display: block;
942
+  padding: 0.3rem 0.4rem;
943
+  color: var(--fg-default);
944
+  font-size: 0.875rem;
945
+}
946
+.shithub-ref-panel strong { display: block; margin: 0.4rem 0 0.2rem; font-size: 0.75rem; color: var(--fg-muted); text-transform: uppercase; }
947
+
948
+.shithub-tree {
949
+  width: 100%;
950
+  border-collapse: collapse;
951
+  font-size: 0.9rem;
952
+}
953
+.shithub-tree td {
954
+  padding: 0.4rem 0.5rem;
955
+  border-bottom: 1px solid var(--border-default);
956
+}
957
+.shithub-tree-icon { width: 24px; color: var(--fg-muted); }
958
+.shithub-tree-icon svg { display: block; }
959
+.shithub-tree-name a { color: var(--fg-default); }
960
+.shithub-tree-size { width: 100px; text-align: right; color: var(--fg-muted); font-variant-numeric: tabular-nums; }
961
+.shithub-tree-symlink, .shithub-tree-submodule { color: var(--fg-muted); font-style: italic; font-size: 0.8rem; }
962
+
963
+.shithub-readme {
964
+  margin-top: 2rem;
965
+  padding: 1.25rem;
966
+  border: 1px solid var(--border-default);
967
+  border-radius: 6px;
968
+  background: var(--canvas-subtle);
969
+}
970
+.shithub-readme h1, .shithub-readme h2 { border-bottom: 1px solid var(--border-default); padding-bottom: 0.3rem; }
971
+.shithub-readme code { font-family: monospace; padding: 0.1em 0.3em; background: var(--canvas-default); border-radius: 3px; }
972
+.shithub-readme pre code { padding: 0; background: none; }
973
+.shithub-readme-plain { white-space: pre-wrap; }
974
+
975
+.shithub-blob-meta { color: var(--fg-muted); font-size: 0.85rem; margin-right: 0.5rem; }
976
+.shithub-blob-too-large, .shithub-blob-binary {
977
+  padding: 1rem;
978
+  border: 1px dashed var(--border-default);
979
+  border-radius: 6px;
980
+  text-align: center;
981
+  color: var(--fg-muted);
982
+}
983
+.shithub-blob-image { text-align: center; padding: 1rem; background: var(--canvas-subtle); border-radius: 6px; }
984
+.shithub-blob-image img { max-width: 100%; max-height: 80vh; }
985
+.shithub-blob-source { overflow-x: auto; }
986
+.shithub-blob-source .chroma { font-size: 0.85rem; }
987
+.shithub-blob-markdown { padding: 1rem; }
988
+.shithub-button-disabled { opacity: 0.5; pointer-events: none; }
989
+
990
+.shithub-finder-form { display: flex; gap: 0.5rem; align-items: center; margin: 1rem 0; }
991
+.shithub-finder-form input { font: inherit; padding: 0.4rem 0.6rem; border: 1px solid var(--border-default); border-radius: 6px; flex: 1; }
992
+.shithub-finder-results { list-style: none; padding: 0; }
993
+.shithub-finder-results li { padding: 0.3rem 0.5rem; border-bottom: 1px solid var(--border-default); font-family: monospace; font-size: 0.875rem; }
internal/web/templates/_layout.htmlmodified
@@ -26,6 +26,7 @@
2626
   <link rel="icon" type="image/svg+xml" href="/static/logo/favicon.svg">
2727
   <link rel="stylesheet" href="/static/primer/primer.css" onerror="this.remove()">
2828
   <link rel="stylesheet" href="/static/css/shithub.css">
29
+  <link rel="stylesheet" href="/static/css/chroma.css">
2930
 </head>
3031
 <body class="shithub-body">
3132
 {{ template "nav" . }}
internal/web/templates/repo/blob.htmladded
@@ -0,0 +1,44 @@
1
+{{ define "page" -}}
2
+<section class="shithub-blob">
3
+  <header class="shithub-code-head">
4
+    <nav class="shithub-code-crumbs" aria-label="Breadcrumb">
5
+      {{ range $i, $c := .Crumbs }}
6
+        {{ if $i }}<span class="shithub-code-sep">/</span>{{ end }}
7
+        <a href="{{ $c.URL }}">{{ $c.Name }}</a>
8
+      {{ end }}
9
+    </nav>
10
+    <div class="shithub-code-actions">
11
+      <span class="shithub-blob-meta">{{ .Language }} · {{ .Size }} bytes</span>
12
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/raw/{{ .Ref }}/{{ .Path }}" class="shithub-button">Raw</a>
13
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/blob/{{ .Ref }}/{{ .Path }}#blame" class="shithub-button shithub-button-disabled" title="Coming in S18">Blame</a>
14
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/commits/{{ .Ref }}/{{ .Path }}" class="shithub-button shithub-button-disabled" title="Coming in S18">History</a>
15
+    </div>
16
+  </header>
17
+
18
+  {{ if .IsLarge }}
19
+  <div class="shithub-blob-too-large">
20
+    This file is too large to display ({{ .Size }} bytes).
21
+    <a href="/{{ .Owner }}/{{ .Repo.Name }}/raw/{{ .Ref }}/{{ .Path }}">Download raw</a>.
22
+  </div>
23
+  {{ else if .IsBinary }}
24
+    {{ if .IsImage }}
25
+    <div class="shithub-blob-image">
26
+      <img src="/{{ .Owner }}/{{ .Repo.Name }}/raw/{{ .Ref }}/{{ .Path }}" alt="{{ .Path }}">
27
+    </div>
28
+    {{ else }}
29
+    <div class="shithub-blob-binary">
30
+      Binary file — {{ .Size }} bytes.
31
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/raw/{{ .Ref }}/{{ .Path }}">Download raw</a>.
32
+    </div>
33
+    {{ end }}
34
+  {{ else if .IsMarkdown }}
35
+    <div class="shithub-blob-markdown">{{ .MarkdownHTML }}</div>
36
+    <details class="shithub-blob-source">
37
+      <summary>View source</summary>
38
+      {{ .HighlightedHTML }}
39
+    </details>
40
+  {{ else }}
41
+    <div class="shithub-blob-source">{{ .HighlightedHTML }}</div>
42
+  {{ end }}
43
+</section>
44
+{{- end }}
internal/web/templates/repo/finder.htmladded
@@ -0,0 +1,22 @@
1
+{{ define "page" -}}
2
+<section class="shithub-finder">
3
+  <header>
4
+    <h1>Find file in <a href="/{{ .Owner }}/{{ .Repo.Name }}/tree/{{ .Ref }}">{{ .Owner }}/{{ .Repo.Name }}@{{ .Ref }}</a></h1>
5
+    <form method="GET" action="/{{ .Owner }}/{{ .Repo.Name }}/find/{{ .Ref }}" class="shithub-finder-form">
6
+      <label>Filter <input type="text" name="q" value="{{ .Query }}" autofocus placeholder="type to filter"></label>
7
+      <button type="submit" class="shithub-button">Search</button>
8
+    </form>
9
+  </header>
10
+  {{ if .Matches }}
11
+  <ul class="shithub-finder-results">
12
+    {{ range .Matches }}
13
+    <li><a href="/{{ $.Owner }}/{{ $.Repo.Name }}/blob/{{ $.Ref }}/{{ .Path }}">{{ .Path }}</a></li>
14
+    {{ end }}
15
+  </ul>
16
+  {{ else if .Query }}
17
+  <p>No files match <code>{{ .Query }}</code>.</p>
18
+  {{ else }}
19
+  <p>Type a query to filter the tree.</p>
20
+  {{ end }}
21
+</section>
22
+{{- end }}
internal/web/templates/repo/tree.htmladded
@@ -0,0 +1,65 @@
1
+{{ define "page" -}}
2
+<section class="shithub-code">
3
+  <header class="shithub-code-head">
4
+    <nav class="shithub-code-crumbs" aria-label="Breadcrumb">
5
+      {{ range $i, $c := .Crumbs }}
6
+        {{ if $i }}<span class="shithub-code-sep">/</span>{{ end }}
7
+        <a href="{{ $c.URL }}">{{ $c.Name }}</a>
8
+      {{ end }}
9
+    </nav>
10
+    <div class="shithub-code-actions">
11
+      <details class="shithub-ref-switcher">
12
+        <summary>{{ .Ref }}</summary>
13
+        <div class="shithub-ref-panel">
14
+          {{ if .Branches }}
15
+          <strong>Branches</strong>
16
+          {{ range .Branches }}
17
+          <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ .Name }}">{{ .Name }}</a>
18
+          {{ end }}
19
+          {{ end }}
20
+          {{ if .Tags }}
21
+          <strong>Tags</strong>
22
+          {{ range .Tags }}
23
+          <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ .Name }}">{{ .Name }}</a>
24
+          {{ end }}
25
+          {{ end }}
26
+        </div>
27
+      </details>
28
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/find/{{ .Ref }}" class="shithub-button">Go to file</a>
29
+    </div>
30
+  </header>
31
+
32
+  <table class="shithub-tree">
33
+    <tbody>
34
+      {{ range .Entries }}
35
+      <tr>
36
+        <td class="shithub-tree-icon" aria-hidden="true">
37
+          {{ if eq (printf "%s" .Kind) "tree" }}{{ octicon "directory" }}
38
+          {{ else if eq (printf "%s" .Kind) "commit" }}{{ octicon "submodule" }}
39
+          {{ else if eq (printf "%s" .Kind) "symlink" }}{{ octicon "symlink" }}
40
+          {{ else }}{{ octicon "file" }}{{ end }}
41
+        </td>
42
+        <td class="shithub-tree-name">
43
+          {{ if eq (printf "%s" .Kind) "tree" }}
44
+          <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/tree/{{ $.Ref }}/{{ if $.Path }}{{ $.Path }}/{{ end }}{{ .Name }}">{{ .Name }}</a>
45
+          {{ else if eq (printf "%s" .Kind) "blob" }}
46
+          <a href="/{{ $.Owner }}/{{ $.Repo.Name }}/blob/{{ $.Ref }}/{{ if $.Path }}{{ $.Path }}/{{ end }}{{ .Name }}">{{ .Name }}</a>
47
+          {{ else if eq (printf "%s" .Kind) "symlink" }}
48
+          {{ .Name }} <em class="shithub-tree-symlink">(symlink)</em>
49
+          {{ else }}
50
+          {{ .Name }} <em class="shithub-tree-submodule">@ {{ slice .OID 0 7 }}</em>
51
+          {{ end }}
52
+        </td>
53
+        <td class="shithub-tree-size">{{ if gt .Size 0 }}{{ .Size }}{{ end }}</td>
54
+      </tr>
55
+      {{ end }}
56
+    </tbody>
57
+  </table>
58
+
59
+  {{ if .README }}
60
+  <section class="shithub-readme" aria-label="README">
61
+    {{ .README }}
62
+  </section>
63
+  {{ end }}
64
+</section>
65
+{{- end }}