diff --git a/appview/pages/funcmap.go b/appview/pages/funcmap.go index 4997d0bb..58f2560c 100644 --- a/appview/pages/funcmap.go +++ b/appview/pages/funcmap.go @@ -29,6 +29,8 @@ import ( "github.com/go-enry/go-enry/v2" "github.com/yuin/goldmark" emoji "github.com/yuin/goldmark-emoji" + htmlparse "golang.org/x/net/html" + "golang.org/x/net/html/atom" "tangled.org/core/appview/cache" "tangled.org/core/appview/db" "tangled.org/core/appview/models" @@ -305,13 +307,6 @@ func (p *Pages) funcMap() template.FuncMap { } return v.Slice(0, min(n, v.Len())).Interface() }, - "markdown": func(text string) template.HTML { - rctx := p.rctx.Clone() - rctx.RendererType = markup.RendererTypeDefault - htmlString := rctx.RenderMarkdown(text) - sanitized := rctx.SanitizeDefault(htmlString) - return template.HTML(sanitized) - }, "markdownSegments": func(text string) []MarkdownSegment { rctx := p.rctx.Clone() rctx.RendererType = markup.RendererTypeDefault @@ -638,71 +633,59 @@ func (p *Pages) AvatarUrl(actor, size string) string { return baseUrl } -func splitCodeBlockSegments(html string) []MarkdownSegment { - var segments []MarkdownSegment - remaining := html - closeTag := `` - - for { - idx := strings.Index(remaining, ``) - if openEnd == -1 { - remaining = remaining[idx:] - continue - } - openEnd += idx - openTag := remaining[idx : openEnd+1] +func splitCodeBlockSegments(rawHTML string) []MarkdownSegment { + nodes, err := htmlparse.ParseFragment(strings.NewReader(rawHTML), &htmlparse.Node{ + Type: htmlparse.ElementNode, + Data: "body", + DataAtom: atom.Body, + }) + if err != nil { + return []MarkdownSegment{{HTML: template.HTML(rawHTML)}} + } - if strings.Contains(openTag, `mermaid`) { - closeIdx := strings.Index(remaining[openEnd+1:], closeTag) - if closeIdx == -1 { - remaining = remaining[idx:] - continue + var segments []MarkdownSegment + var buf strings.Builder + for _, n := range nodes { + if n.Type == htmlparse.ElementNode && n.Data == "pre" { + if buf.Len() > 0 { + segments = append(segments, MarkdownSegment{HTML: template.HTML(buf.String())}) + buf.Reset() + } + _ = htmlparse.Render(&buf, n) + isMermaid := false + for _, attr := range n.Attr { + if attr.Key == "class" && strings.Contains(attr.Val, "mermaid") { + isMermaid = true + break + } } - closeIdx += openEnd + 1 - - if idx > 0 { - segments = append(segments, MarkdownSegment{HTML: template.HTML(remaining[:idx])}) + seg := MarkdownSegment{HTML: template.HTML(buf.String())} + if !isMermaid { + seg.IsCode = true + seg.Lines = countTextNewlines(n) } - - preEnd := closeIdx + len(closeTag) - preHTML := remaining[idx:preEnd] - segments = append(segments, MarkdownSegment{ - HTML: template.HTML(preHTML), - }) - remaining = remaining[preEnd:] - continue - } - - closeIdx := strings.Index(remaining[openEnd+1:], closeTag) - if closeIdx == -1 { - remaining = remaining[idx:] - continue + segments = append(segments, seg) + buf.Reset() + } else { + _ = htmlparse.Render(&buf, n) } - closeIdx += openEnd + 1 + } + if buf.Len() > 0 { + segments = append(segments, MarkdownSegment{HTML: template.HTML(buf.String())}) + } + return segments +} - if idx > 0 { - segments = append(segments, MarkdownSegment{HTML: template.HTML(remaining[:idx])}) +func countTextNewlines(n *htmlparse.Node) int { + count := 0 + for c := n.FirstChild; c != nil; c = c.NextSibling { + if c.Type == htmlparse.TextNode { + count += strings.Count(c.Data, "\n") + } else { + count += countTextNewlines(c) } - - preEnd := closeIdx + len(closeTag) - preHTML := remaining[idx:preEnd] - content := remaining[openEnd+1 : closeIdx] - lines := strings.Count(content, "\n") + 1 - segments = append(segments, MarkdownSegment{ - HTML: template.HTML(preHTML), - IsCode: true, - Lines: lines, - }) - remaining = remaining[preEnd:] } - return segments + return count } func (p *Pages) icon(name string, classes []string) (template.HTML, error) {