From 5440bf6ce29382bf5e0e5a96422677ce1155a552 Mon Sep 17 00:00:00 2001 From: BrookJeynes Date: Sat, 12 Jul 2025 06:37:11 +1000 Subject: [PATCH] appview: markdown: introduce syntax highlighting to code blocks Signed-off-by: BrookJeynes --- appview/pages/markup/markdown.go | 21 ++++++++++++++++++++- flake.nix | 2 +- go.mod | 3 ++- go.sum | 6 +++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/appview/pages/markup/markdown.go b/appview/pages/markup/markdown.go index df4afa5..19a0959 100644 --- a/appview/pages/markup/markdown.go +++ b/appview/pages/markup/markdown.go @@ -5,12 +5,17 @@ import ( "bytes" "fmt" "io" + "log" "net/url" "path" "strings" + "github.com/alecthomas/chroma/v2" + chromahtml "github.com/alecthomas/chroma/v2/formatters/html" + "github.com/alecthomas/chroma/v2/styles" "github.com/microcosm-cc/bluemonday" "github.com/yuin/goldmark" + highlighting "github.com/yuin/goldmark-highlighting/v2" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/parser" @@ -43,12 +48,22 @@ type RenderContext struct { } func (rctx *RenderContext) RenderMarkdown(source string) string { + var style *chroma.Style = styles.Get("catppuccin-latte") md := goldmark.New( - goldmark.WithExtensions(extension.GFM), goldmark.WithParserOptions( parser.WithAutoHeadingID(), ), goldmark.WithRendererOptions(html.WithUnsafe()), + goldmark.WithExtensions( + extension.GFM, + highlighting.NewHighlighting( + highlighting.WithFormatOptions( + chromahtml.Standalone(false), + chromahtml.WithClasses(true), + ), + highlighting.WithCustomStyle(style), + ), + ), ) if rctx != nil { @@ -63,6 +78,7 @@ func (rctx *RenderContext) RenderMarkdown(source string) string { var buf bytes.Buffer if err := md.Convert([]byte(source), &buf); err != nil { + log.Println("failed to convert markdown:", err) return source } @@ -173,6 +189,9 @@ func (rctx *RenderContext) Sanitize(html string) string { "margin-top", "margin-bottom", ) + + policy.AllowAttrs("class").OnElements("code", "pre", "span") + return policy.Sanitize(html) } diff --git a/flake.nix b/flake.nix index c5d06ac..fd18683 100644 --- a/flake.nix +++ b/flake.nix @@ -61,7 +61,7 @@ inherit (gitignore.lib) gitignoreSource; in { overlays.default = final: prev: let - goModHash = "sha256-2RUwj16RNaZ/gCOcd7b3LRCHiROCRj9HuzbBdLdgWGo="; + goModHash = "sha256-7l5x9qeesQI+HCh9YS2EBba86lS2CQGugqMVbqvfEqw="; appviewDeps = { inherit htmx-src htmx-ws-src lucide-src inter-fonts-src ibm-plex-mono-src goModHash gitignoreSource; }; diff --git a/go.mod b/go.mod index c5466cb..d3be92e 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,8 @@ require ( github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v3 v3.3.3 github.com/whyrusleeping/cbor-gen v0.3.1 - github.com/yuin/goldmark v1.4.13 + github.com/yuin/goldmark v1.4.15 + github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc golang.org/x/crypto v0.38.0 golang.org/x/net v0.40.0 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da diff --git a/go.sum b/go.sum index 6ecfbfb..a1fe98f 100644 --- a/go.sum +++ b/go.sum @@ -75,6 +75,7 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= @@ -403,8 +404,11 @@ github.com/whyrusleeping/cbor-gen v0.3.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.4.15 h1:CFa84T0goNn/UIXYS+dmjjVxMyTAvpOmzld40N/nfK0= +github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= +github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= -- 2.43.0