this repo has no description

appview: pages/blob: toggle render for markdown files

Also sets up a pages/markup package with extensible rendering for future markup languages that we may support.
We may also want an interface or something for markup.RenderMarkdown (maybe just markup.Render), but don't have to bother with that now.

authored by icyphox.sh and committed by Tangled b328b08d 9cb8591e

Changed files
+67 -9
appview
pages
state
+2 -1
appview/pages/funcmap.go
··· 13 13 "time" 14 14 15 15 "github.com/dustin/go-humanize" 16 + "tangled.sh/tangled.sh/core/appview/pages/markup" 16 17 ) 17 18 18 19 func funcMap() template.FuncMap { ··· 137 138 return v.Slice(start, end).Interface() 138 139 }, 139 140 "markdown": func(text string) template.HTML { 140 - return template.HTML(renderMarkdown(text)) 141 + return template.HTML(markup.RenderMarkdown(text)) 141 142 }, 142 143 "isNil": func(t any) bool { 143 144 // returns false for other "zero" values
+3 -2
appview/pages/markdown.go appview/pages/markup/markdown.go
··· 1 - package pages 1 + // Package markup is an umbrella package for all markups and their renderers. 2 + package markup 2 3 3 4 import ( 4 5 "bytes" ··· 8 9 "github.com/yuin/goldmark/parser" 9 10 ) 10 11 11 - func renderMarkdown(source string) string { 12 + func RenderMarkdown(source string) string { 12 13 md := goldmark.New( 13 14 goldmark.WithExtensions(extension.GFM), 14 15 goldmark.WithParserOptions(
+26
appview/pages/markup/readme.go
··· 1 + package markup 2 + 3 + import "strings" 4 + 5 + type Format string 6 + 7 + const ( 8 + FormatMarkdown Format = "markdown" 9 + FormatText Format = "text" 10 + ) 11 + 12 + var FileTypes map[Format][]string = map[Format][]string{ 13 + FormatMarkdown: []string{".md", ".markdown", ".mdown", ".mkdn", ".mkd"}, 14 + } 15 + 16 + func GetFormat(filename string) Format { 17 + for format, extensions := range FileTypes { 18 + for _, extension := range extensions { 19 + if strings.HasSuffix(filename, extension) { 20 + return format 21 + } 22 + } 23 + } 24 + // default format 25 + return FormatText 26 + }
+15 -5
appview/pages/pages.go
··· 24 24 "github.com/microcosm-cc/bluemonday" 25 25 "tangled.sh/tangled.sh/core/appview/auth" 26 26 "tangled.sh/tangled.sh/core/appview/db" 27 + "tangled.sh/tangled.sh/core/appview/pages/markup" 27 28 "tangled.sh/tangled.sh/core/appview/state/userutil" 28 29 "tangled.sh/tangled.sh/core/types" 29 30 ) ··· 350 351 ext := filepath.Ext(params.ReadmeFileName) 351 352 switch ext { 352 353 case ".md", ".markdown", ".mdown", ".mkdn", ".mkd": 353 - htmlString = renderMarkdown(params.Readme) 354 + htmlString = markup.RenderMarkdown(params.Readme) 354 355 params.Raw = false 355 356 params.HTMLReadme = template.HTML(bluemonday.UGCPolicy().Sanitize(htmlString)) 356 357 default: ··· 446 447 } 447 448 448 449 type RepoBlobParams struct { 449 - LoggedInUser *auth.User 450 - RepoInfo RepoInfo 451 - Active string 452 - BreadCrumbs [][]string 450 + LoggedInUser *auth.User 451 + RepoInfo RepoInfo 452 + Active string 453 + BreadCrumbs [][]string 454 + ShowRendered bool 455 + RenderedContents template.HTML 453 456 types.RepoBlobResponse 454 457 } 455 458 ··· 458 461 b := style.Builder() 459 462 b.Add(chroma.LiteralString, "noitalic") 460 463 style, _ = b.Build() 464 + 465 + if params.ShowRendered { 466 + switch markup.GetFormat(params.Path) { 467 + case markup.FormatMarkdown: 468 + params.RenderedContents = template.HTML(markup.RenderMarkdown(params.Contents)) 469 + } 470 + } 461 471 462 472 if params.Lines < 5000 { 463 473 c := params.Contents
+10 -1
appview/pages/templates/repo/blob.html
··· 43 43 <span>{{ byteFmt .SizeHint }}</span> 44 44 <span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span> 45 45 <a href="/{{ .RepoInfo.FullName }}/blob/{{ .Ref }}/raw/{{ .Path }}">view raw</a> 46 + <span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span> 47 + <a 48 + href="/{{ .RepoInfo.FullName }}/blob/{{ .Ref }}/{{ .Path }}?code={{ .ShowRendered }}" 49 + hx-boost="true" 50 + >view {{ if .ShowRendered }}code{{ else }}rendered{{ end }}</a> 46 51 </div> 47 52 </div> 48 53 </div> ··· 52 57 </p> 53 58 {{ else }} 54 59 <div class="overflow-auto relative"> 55 - <div class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ $.Contents | escapeHtml }}</div> 60 + {{ if .ShowRendered }} 61 + <div id="blob-contents" class="prose dark:prose-invert p-6">{{ .RenderedContents }}</div> 62 + {{ else }} 63 + <div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ $.Contents | escapeHtml }}</div> 64 + {{ end }} 56 65 </div> 57 66 {{ end }} 58 67 {{ end }}
+11
appview/state/repo.go
··· 25 25 "tangled.sh/tangled.sh/core/appview/auth" 26 26 "tangled.sh/tangled.sh/core/appview/db" 27 27 "tangled.sh/tangled.sh/core/appview/pages" 28 + "tangled.sh/tangled.sh/core/appview/pages/markup" 28 29 "tangled.sh/tangled.sh/core/types" 29 30 30 31 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 453 454 } 454 455 } 455 456 457 + var showRendered = false 458 + if markup.GetFormat(result.Path) == markup.FormatMarkdown { 459 + showRendered = true 460 + } 461 + 462 + if r.URL.Query().Get("code") == "true" { 463 + showRendered = false 464 + } 465 + 456 466 user := s.auth.GetUser(r) 457 467 s.pages.RepoBlob(w, pages.RepoBlobParams{ 458 468 LoggedInUser: user, 459 469 RepoInfo: f.RepoInfo(s, user), 460 470 RepoBlobResponse: result, 461 471 BreadCrumbs: breadcrumbs, 472 + ShowRendered: showRendered, 462 473 }) 463 474 return 464 475 }