+11
-7
appview/pages/templates/repo/blob.html
+11
-7
appview/pages/templates/repo/blob.html
···
17
{{ $tot_chars := len (printf "%d" $tot_lines) }}
18
{{ $code_number_style := "text-gray-400 left-0 bg-white text-right mr-6 select-none inline-block w-12" }}
19
{{ $linkstyle := "no-underline hover:underline" }}
20
-
<div class="pb-2 text-base">
21
-
<div class="flex justify-between">
22
-
<div id="breadcrumbs">
23
{{ range $idx, $value := .BreadCrumbs }}
24
{{ if ne $idx (sub (len $.BreadCrumbs) 1) }}
25
<a
···
35
{{ end }}
36
{{ end }}
37
</div>
38
-
<div id="file-info" class="text-gray-500 text-xs">
39
-
{{ .Lines }} lines
40
-
<span class="select-none px-2 [&:before]:content-['·']"></span>
41
-
{{ byteFmt .SizeHint }}
42
</div>
43
</div>
44
</div>
···
17
{{ $tot_chars := len (printf "%d" $tot_lines) }}
18
{{ $code_number_style := "text-gray-400 left-0 bg-white text-right mr-6 select-none inline-block w-12" }}
19
{{ $linkstyle := "no-underline hover:underline" }}
20
+
<div class="pb-2 mb-3 text-base border-b border-gray-200">
21
+
<div class="flex flex-col md:flex-row md:justify-between gap-2">
22
+
<div id="breadcrumbs" class="overflow-x-auto whitespace-nowrap">
23
{{ range $idx, $value := .BreadCrumbs }}
24
{{ if ne $idx (sub (len $.BreadCrumbs) 1) }}
25
<a
···
35
{{ end }}
36
{{ end }}
37
</div>
38
+
<div id="file-info" class="text-gray-500 text-xs md:text-sm flex flex-wrap items-center gap-1 md:gap-0">
39
+
<span>at <a href="/{{ .RepoInfo.FullName }}/tree/{{ .Ref }}">{{ .Ref }}</a></span>
40
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
41
+
<span>{{ .Lines }} lines</span>
42
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
43
+
<span>{{ byteFmt .SizeHint }}</span>
44
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
45
+
<a href="/{{ .RepoInfo.FullName }}/blob/raw/{{ .Ref }}/{{ .Path }}">view raw</a>
46
</div>
47
</div>
48
</div>
+18
-17
appview/pages/templates/repo/tree.html
+18
-17
appview/pages/templates/repo/tree.html
···
17
{{ $containerstyle := "py-1" }}
18
{{ $linkstyle := "no-underline hover:underline" }}
19
20
-
<div class="pb-2 text-base">
21
<div class="flex justify-between">
22
<div id="breadcrumbs">
23
{{ range .BreadCrumbs }}
24
<a href="{{ index . 1}}" class="text-bold text-gray-500 {{ $linkstyle }}">{{ index . 0 }}</a> /
25
{{ end }}
26
</div>
27
-
<div id="dir-info">
28
-
<span class="text-gray-500 text-xs">
29
-
{{ $stats := .TreeStats }}
30
31
-
{{ if eq $stats.NumFolders 1 }}
32
-
{{ $stats.NumFolders }} folder
33
-
<span class="px-1 select-none">·</span>
34
-
{{ else if gt $stats.NumFolders 1 }}
35
-
{{ $stats.NumFolders }} folders
36
-
<span class="px-1 select-none">·</span>
37
-
{{ end }}
38
39
-
{{ if eq $stats.NumFiles 1 }}
40
-
{{ $stats.NumFiles }} file
41
-
{{ else if gt $stats.NumFiles 1 }}
42
-
{{ $stats.NumFiles }} files
43
-
{{ end }}
44
-
</span>
45
</div>
46
</div>
47
</div>
···
17
{{ $containerstyle := "py-1" }}
18
{{ $linkstyle := "no-underline hover:underline" }}
19
20
+
<div class="pb-2 mb-3 text-base border-b border-gray-200">
21
<div class="flex justify-between">
22
<div id="breadcrumbs">
23
{{ range .BreadCrumbs }}
24
<a href="{{ index . 1}}" class="text-bold text-gray-500 {{ $linkstyle }}">{{ index . 0 }}</a> /
25
{{ end }}
26
</div>
27
+
<div id="dir-info" class="text-gray-500 text-xs md:text-sm flex flex-wrap items-center gap-1 md:gap-0">
28
+
{{ $stats := .TreeStats }}
29
30
+
<span>at <a href="/{{ $.RepoInfo.FullName }}/tree/{{ $.Ref }}">{{ $.Ref }}</a></span>
31
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
32
+
{{ if eq $stats.NumFolders 1 }}
33
+
<span>{{ $stats.NumFolders }} folder</span>
34
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
35
+
{{ else if gt $stats.NumFolders 1 }}
36
+
<span>{{ $stats.NumFolders }} folders</span>
37
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
38
+
{{ end }}
39
+
40
+
{{ if eq $stats.NumFiles 1 }}
41
+
<span>{{ $stats.NumFiles }} file</span>
42
+
{{ else if gt $stats.NumFiles 1 }}
43
+
<span>{{ $stats.NumFiles }} files</span>
44
+
{{ end }}
45
46
</div>
47
</div>
48
</div>
+44
appview/state/repo.go
+44
appview/state/repo.go
···
460
return
461
}
462
463
+
func (s *State) RepoBlobRaw(w http.ResponseWriter, r *http.Request) {
464
+
f, err := fullyResolvedRepo(r)
465
+
if err != nil {
466
+
log.Println("failed to get repo and knot", err)
467
+
return
468
+
}
469
+
470
+
ref := chi.URLParam(r, "ref")
471
+
filePath := chi.URLParam(r, "*")
472
+
473
+
protocol := "http"
474
+
if !s.config.Dev {
475
+
protocol = "https"
476
+
}
477
+
resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))
478
+
if err != nil {
479
+
log.Println("failed to reach knotserver", err)
480
+
return
481
+
}
482
+
483
+
body, err := io.ReadAll(resp.Body)
484
+
if err != nil {
485
+
log.Printf("Error reading response body: %v", err)
486
+
return
487
+
}
488
+
489
+
var result types.RepoBlobResponse
490
+
err = json.Unmarshal(body, &result)
491
+
if err != nil {
492
+
log.Println("failed to parse response:", err)
493
+
return
494
+
}
495
+
496
+
if result.IsBinary {
497
+
w.Header().Set("Content-Type", "application/octet-stream")
498
+
w.Write(body)
499
+
return
500
+
}
501
+
502
+
w.Header().Set("Content-Type", "text/plain")
503
+
w.Write([]byte(result.Contents))
504
+
return
505
+
}
506
+
507
func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) {
508
f, err := fullyResolvedRepo(r)
509
if err != nil {
+1
appview/state/router.go
+1
appview/state/router.go