appview/pages: add templates to render strings #413

merged
opened by oppi.li targeting master from push-potvrpwlpwsl
Changed files
+159
appview
pages
templates
strings
+74
appview/pages/pages.go
···
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
"github.com/bluesky-social/indigo/atproto/syntax"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
···
return p.executeRepo("repo/pipelines/workflow", w, params)
}
func (p *Pages) Static() http.Handler {
if p.dev {
return http.StripPrefix("/static/", http.FileServer(http.Dir("appview/pages/static")))
···
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
+
"github.com/bluesky-social/indigo/atproto/identity"
"github.com/bluesky-social/indigo/atproto/syntax"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
···
return p.executeRepo("repo/pipelines/workflow", w, params)
}
+
type PutStringParams struct {
+
LoggedInUser *oauth.User
+
Action string
+
+
// this is supplied in the case of editing an existing string
+
String db.String
+
}
+
+
func (p *Pages) PutString(w io.Writer, params PutStringParams) error {
+
return p.execute("strings/put", w, params)
+
}
+
+
type StringsDashboardParams struct {
+
LoggedInUser *oauth.User
+
Card ProfileCard
+
Strings []db.String
+
}
+
+
func (p *Pages) StringsDashboard(w io.Writer, params StringsDashboardParams) error {
+
return p.execute("strings/dashboard", w, params)
+
}
+
+
type SingleStringParams struct {
+
LoggedInUser *oauth.User
+
ShowRendered bool
+
RenderToggle bool
+
RenderedContents template.HTML
+
String db.String
+
Stats db.StringStats
+
Owner identity.Identity
+
}
+
+
func (p *Pages) SingleString(w io.Writer, params SingleStringParams) error {
+
var style *chroma.Style = styles.Get("catpuccin-latte")
+
+
if params.ShowRendered {
+
switch markup.GetFormat(params.String.Filename) {
+
case markup.FormatMarkdown:
+
p.rctx.RendererType = markup.RendererTypeDefault
+
htmlString := p.rctx.RenderMarkdown(params.String.Contents)
+
params.RenderedContents = template.HTML(p.rctx.Sanitize(htmlString))
+
}
+
}
+
+
c := params.String.Contents
+
formatter := chromahtml.New(
+
chromahtml.InlineCode(false),
+
chromahtml.WithLineNumbers(true),
+
chromahtml.WithLinkableLineNumbers(true, "L"),
+
chromahtml.Standalone(false),
+
chromahtml.WithClasses(true),
+
)
+
+
lexer := lexers.Get(filepath.Base(params.String.Filename))
+
if lexer == nil {
+
lexer = lexers.Fallback
+
}
+
+
iterator, err := lexer.Tokenise(nil, c)
+
if err != nil {
+
return fmt.Errorf("chroma tokenize: %w", err)
+
}
+
+
var code bytes.Buffer
+
err = formatter.Format(&code, style, iterator)
+
if err != nil {
+
return fmt.Errorf("chroma format: %w", err)
+
}
+
+
params.String.Contents = code.String()
+
return p.execute("strings/string", w, params)
+
}
+
func (p *Pages) Static() http.Handler {
if p.dev {
return http.StripPrefix("/static/", http.FileServer(http.Dir("appview/pages/static")))
+85
appview/pages/templates/strings/string.html
···
···
+
{{ define "title" }}{{ .String.Filename }} · by {{ didOrHandle .Owner.DID.String .Owner.Handle.String }}{{ end }}
+
+
{{ define "extrameta" }}
+
{{ $ownerId := didOrHandle .Owner.DID.String .Owner.Handle.String }}
+
<meta property="og:title" content="{{ .String.Filename }} · by {{ $ownerId }}" />
+
<meta property="og:type" content="object" />
+
<meta property="og:url" content="https://tangled.sh/strings/{{ $ownerId }}/{{ .String.Rkey }}" />
+
<meta property="og:description" content="{{ .String.Description }}" />
+
{{ end }}
+
+
{{ define "topbar" }}
+
{{ template "layouts/topbar" $ }}
+
{{ end }}
+
+
{{ define "content" }}
+
{{ $ownerId := didOrHandle .Owner.DID.String .Owner.Handle.String }}
+
<section id="string-header" class="mb-4 py-2 px-6 dark:text-white">
+
<div class="text-lg flex items-center justify-between">
+
<div>
+
<a href="/strings/{{ $ownerId }}">{{ $ownerId }}</a>
+
<span class="select-none">/</span>
+
<a href="/{{ $ownerId }}/{{ .String.Rkey }}" class="font-bold">{{ .String.Filename }}</a>
+
</div>
+
{{ if and .LoggedInUser (eq .LoggedInUser.Did .String.Did) }}
+
<div class="flex gap-2 text-base">
+
<a class="btn flex items-center gap-2 no-underline hover:no-underline p-2 group"
+
hx-boost="true"
+
href="/strings/{{ .String.Did }}/{{ .String.Rkey }}/edit">
+
{{ i "pencil" "size-4" }}
+
<span class="hidden md:inline">edit</span>
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
+
</a>
+
<button
+
class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group p-2"
+
title="Delete string"
+
hx-delete="/strings/{{ .String.Did }}/{{ .String.Rkey }}/"
+
hx-swap="none"
+
hx-confirm="Are you sure you want to delete the gist `{{ .String.Filename }}`?"
+
>
+
{{ i "trash-2" "size-4" }}
+
<span class="hidden md:inline">delete</span>
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
+
</button>
+
</div>
+
{{ end }}
+
</div>
+
<span class="flex items-center">
+
{{ with .String.Description }}
+
{{ . }}
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
+
{{ end }}
+
+
{{ with .String.Edited }}
+
<span>edited {{ template "repo/fragments/shortTimeAgo" . }}</span>
+
{{ else }}
+
{{ template "repo/fragments/shortTimeAgo" .String.Created }}
+
{{ end }}
+
</span>
+
</section>
+
<section class="bg-white dark:bg-gray-800 px-6 py-4 rounded relative w-full dark:text-white">
+
<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">
+
<span>{{ .String.Filename }}</span>
+
<div>
+
<span>{{ .Stats.LineCount }} lines</span>
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
+
<span>{{ byteFmt .Stats.ByteCount }}</span>
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
+
<a href="/strings/{{ $ownerId }}/{{ .String.Rkey }}/raw">view raw</a>
+
{{ if .RenderToggle }}
+
<span class="select-none px-1 md:px-2 [&:before]:content-['·']"></span>
+
<a href="?code={{ .ShowRendered }}" hx-boost="true">
+
view {{ if .ShowRendered }}code{{ else }}rendered{{ end }}
+
</a>
+
{{ end }}
+
</div>
+
</div>
+
<div class="overflow-auto relative">
+
{{ if .ShowRendered }}
+
<div id="blob-contents" class="prose dark:prose-invert">{{ .RenderedContents }}</div>
+
{{ else }}
+
<div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ .String.Contents | escapeHtml }}</div>
+
{{ end }}
+
</div>
+
</section>
+
{{ end }}