Signed-off-by: oppiliappan me@oppi.li
+74
appview/pages/pages.go
+74
appview/pages/pages.go
···
31
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
32
"github.com/alecthomas/chroma/v2/lexers"
33
"github.com/alecthomas/chroma/v2/styles"
34
"github.com/bluesky-social/indigo/atproto/syntax"
35
"github.com/go-git/go-git/v5/plumbing"
36
"github.com/go-git/go-git/v5/plumbing/object"
···
1144
return p.executeRepo("repo/pipelines/workflow", w, params)
1145
}
1146
1147
func (p *Pages) Static() http.Handler {
1148
if p.dev {
1149
return http.StripPrefix("/static/", http.FileServer(http.Dir("appview/pages/static")))
···
31
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
32
"github.com/alecthomas/chroma/v2/lexers"
33
"github.com/alecthomas/chroma/v2/styles"
34
+
"github.com/bluesky-social/indigo/atproto/identity"
35
"github.com/bluesky-social/indigo/atproto/syntax"
36
"github.com/go-git/go-git/v5/plumbing"
37
"github.com/go-git/go-git/v5/plumbing/object"
···
1145
return p.executeRepo("repo/pipelines/workflow", w, params)
1146
}
1147
1148
+
type PutStringParams struct {
1149
+
LoggedInUser *oauth.User
1150
+
Action string
1151
+
1152
+
// this is supplied in the case of editing an existing string
1153
+
String db.String
1154
+
}
1155
+
1156
+
func (p *Pages) PutString(w io.Writer, params PutStringParams) error {
1157
+
return p.execute("strings/put", w, params)
1158
+
}
1159
+
1160
+
type StringsDashboardParams struct {
1161
+
LoggedInUser *oauth.User
1162
+
Card ProfileCard
1163
+
Strings []db.String
1164
+
}
1165
+
1166
+
func (p *Pages) StringsDashboard(w io.Writer, params StringsDashboardParams) error {
1167
+
return p.execute("strings/dashboard", w, params)
1168
+
}
1169
+
1170
+
type SingleStringParams struct {
1171
+
LoggedInUser *oauth.User
1172
+
ShowRendered bool
1173
+
RenderToggle bool
1174
+
RenderedContents template.HTML
1175
+
String db.String
1176
+
Stats db.StringStats
1177
+
Owner identity.Identity
1178
+
}
1179
+
1180
+
func (p *Pages) SingleString(w io.Writer, params SingleStringParams) error {
1181
+
var style *chroma.Style = styles.Get("catpuccin-latte")
1182
+
1183
+
if params.ShowRendered {
1184
+
switch markup.GetFormat(params.String.Filename) {
1185
+
case markup.FormatMarkdown:
1186
+
p.rctx.RendererType = markup.RendererTypeDefault
1187
+
htmlString := p.rctx.RenderMarkdown(params.String.Contents)
1188
+
params.RenderedContents = template.HTML(p.rctx.Sanitize(htmlString))
1189
+
}
1190
+
}
1191
+
1192
+
c := params.String.Contents
1193
+
formatter := chromahtml.New(
1194
+
chromahtml.InlineCode(false),
1195
+
chromahtml.WithLineNumbers(true),
1196
+
chromahtml.WithLinkableLineNumbers(true, "L"),
1197
+
chromahtml.Standalone(false),
1198
+
chromahtml.WithClasses(true),
1199
+
)
1200
+
1201
+
lexer := lexers.Get(filepath.Base(params.String.Filename))
1202
+
if lexer == nil {
1203
+
lexer = lexers.Fallback
1204
+
}
1205
+
1206
+
iterator, err := lexer.Tokenise(nil, c)
1207
+
if err != nil {
1208
+
return fmt.Errorf("chroma tokenize: %w", err)
1209
+
}
1210
+
1211
+
var code bytes.Buffer
1212
+
err = formatter.Format(&code, style, iterator)
1213
+
if err != nil {
1214
+
return fmt.Errorf("chroma format: %w", err)
1215
+
}
1216
+
1217
+
params.String.Contents = code.String()
1218
+
return p.execute("strings/string", w, params)
1219
+
}
1220
+
1221
func (p *Pages) Static() http.Handler {
1222
if p.dev {
1223
return http.StripPrefix("/static/", http.FileServer(http.Dir("appview/pages/static")))
+85
appview/pages/templates/strings/string.html
+85
appview/pages/templates/strings/string.html
···
···
1
+
{{ define "title" }}{{ .String.Filename }} · by {{ didOrHandle .Owner.DID.String .Owner.Handle.String }}{{ end }}
2
+
3
+
{{ define "extrameta" }}
4
+
{{ $ownerId := didOrHandle .Owner.DID.String .Owner.Handle.String }}
5
+
<meta property="og:title" content="{{ .String.Filename }} · by {{ $ownerId }}" />
6
+
<meta property="og:type" content="object" />
7
+
<meta property="og:url" content="https://tangled.sh/strings/{{ $ownerId }}/{{ .String.Rkey }}" />
8
+
<meta property="og:description" content="{{ .String.Description }}" />
9
+
{{ end }}
10
+
11
+
{{ define "topbar" }}
12
+
{{ template "layouts/topbar" $ }}
13
+
{{ end }}
14
+
15
+
{{ define "content" }}
16
+
{{ $ownerId := didOrHandle .Owner.DID.String .Owner.Handle.String }}
17
+
<section id="string-header" class="mb-4 py-2 px-6 dark:text-white">
18
+
<div class="text-lg flex items-center justify-between">
19
+
<div>
20
+
<a href="/strings/{{ $ownerId }}">{{ $ownerId }}</a>
21
+
<span class="select-none">/</span>
22
+
<a href="/{{ $ownerId }}/{{ .String.Rkey }}" class="font-bold">{{ .String.Filename }}</a>
23
+
</div>
24
+
{{ if and .LoggedInUser (eq .LoggedInUser.Did .String.Did) }}
25
+
<div class="flex gap-2 text-base">
26
+
<a class="btn flex items-center gap-2 no-underline hover:no-underline p-2 group"
27
+
hx-boost="true"
28
+
href="/strings/{{ .String.Did }}/{{ .String.Rkey }}/edit">
29
+
{{ i "pencil" "size-4" }}
30
+
<span class="hidden md:inline">edit</span>
31
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
32
+
</a>
33
+
<button
34
+
class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group p-2"
35
+
title="Delete string"
36
+
hx-delete="/strings/{{ .String.Did }}/{{ .String.Rkey }}/"
37
+
hx-swap="none"
38
+
hx-confirm="Are you sure you want to delete the gist `{{ .String.Filename }}`?"
39
+
>
40
+
{{ i "trash-2" "size-4" }}
41
+
<span class="hidden md:inline">delete</span>
42
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
43
+
</button>
44
+
</div>
45
+
{{ end }}
46
+
</div>
47
+
<span class="flex items-center">
48
+
{{ with .String.Description }}
49
+
{{ . }}
50
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
51
+
{{ end }}
52
+
53
+
{{ with .String.Edited }}
54
+
<span>edited {{ template "repo/fragments/shortTimeAgo" . }}</span>
55
+
{{ else }}
56
+
{{ template "repo/fragments/shortTimeAgo" .String.Created }}
57
+
{{ end }}
58
+
</span>
59
+
</section>
60
+
<section class="bg-white dark:bg-gray-800 px-6 py-4 rounded relative w-full dark:text-white">
61
+
<div class="flex justify-between items-center text-gray-500 dark:text-gray-400 text-sm md:text-base pb-2 mb-3 text-base border-b border-gray-200 dark:border-gray-700">
62
+
<span>{{ .String.Filename }}</span>
63
+
<div>
64
+
<span>{{ .Stats.LineCount }} lines</span>
65
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
66
+
<span>{{ byteFmt .Stats.ByteCount }}</span>
67
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
68
+
<a href="/strings/{{ $ownerId }}/{{ .String.Rkey }}/raw">view raw</a>
69
+
{{ if .RenderToggle }}
70
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
71
+
<a href="?code={{ .ShowRendered }}" hx-boost="true">
72
+
view {{ if .ShowRendered }}code{{ else }}rendered{{ end }}
73
+
</a>
74
+
{{ end }}
75
+
</div>
76
+
</div>
77
+
<div class="overflow-auto relative">
78
+
{{ if .ShowRendered }}
79
+
<div id="blob-contents" class="prose dark:prose-invert">{{ .RenderedContents }}</div>
80
+
{{ else }}
81
+
<div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ .String.Contents | escapeHtml }}</div>
82
+
{{ end }}
83
+
</div>
84
+
</section>
85
+
{{ end }}