From f198d0069cae6ea731176d7626534657bd40fd69 Mon Sep 17 00:00:00 2001 From: Janus Troelsen Date: Wed, 14 Oct 2026 19:37:10 -0600 Subject: [PATCH] Share syntax highlighting assets Highlight code blocks on the standalone readme and changelog pages (package and candidate) as well. --- datafiles/static/prism-setup.js | 6 ++++ .../templates/Html/candidate-page.html.st | 5 +-- datafiles/templates/Html/package-page.html.st | 5 +-- .../Server/Features/PackageCandidates.hs | 8 +++-- .../Server/Features/PackageContents.hs | 11 ++++--- .../Server/Pages/PackageFromTemplate.hs | 9 ++++++ src/Distribution/Server/Pages/Template.hs | 31 +++++++++++++++++++ 7 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 datafiles/static/prism-setup.js diff --git a/datafiles/static/prism-setup.js b/datafiles/static/prism-setup.js new file mode 100644 index 000000000..3139ce361 --- /dev/null +++ b/datafiles/static/prism-setup.js @@ -0,0 +1,6 @@ +window.Prism = { manual: true }; +document.addEventListener('DOMContentLoaded', function () { + if (!Prism.plugins || !Prism.plugins.autoloader) return; + Prism.plugins.autoloader.languages_path = 'https://cdn.jsdelivr.net/npm/prismjs@1.30.0/components/'; + Prism.highlightAll(); +}); diff --git a/datafiles/templates/Html/candidate-page.html.st b/datafiles/templates/Html/candidate-page.html.st index 5952b9650..45b3d2d19 100644 --- a/datafiles/templates/Html/candidate-page.html.st +++ b/datafiles/templates/Html/candidate-page.html.st @@ -5,8 +5,7 @@ $endif$ $hackageCssTheme()$ - - + $syntaxHighlighting$ $package.name$$if(package.optional.hasSynopsis)$: $package.optional.synopsis$$endif$ @@ -161,8 +160,6 @@

Readme for $package.name$-$package.version$

[back to package description] $package.optional.readme$ - - $endif$ $if(doc.hasQuickNavV1)$ diff --git a/datafiles/templates/Html/package-page.html.st b/datafiles/templates/Html/package-page.html.st index 4a42b83c8..d7b8b28e6 100644 --- a/datafiles/templates/Html/package-page.html.st +++ b/datafiles/templates/Html/package-page.html.st @@ -5,8 +5,7 @@ $endif$ $hackageCssTheme()$ - - + $syntaxHighlighting$ $package.name$$if(package.optional.hasSynopsis)$: $package.optional.synopsis$$endif$ @@ -302,8 +301,6 @@ [back to package description] $package.optional.readme$ - - $endif$ diff --git a/src/Distribution/Server/Features/PackageCandidates.hs b/src/Distribution/Server/Features/PackageCandidates.hs index ee82812ce..28ec3ec2a 100644 --- a/src/Distribution/Server/Features/PackageCandidates.hs +++ b/src/Distribution/Server/Features/PackageCandidates.hs @@ -35,7 +35,7 @@ import Distribution.Server.Features.Security.Migration import Distribution.Server.Util.Parse (unpackUTF8) import Distribution.Server.Util.ServeTarball import Distribution.Server.Util.Markdown (renderMarkdown, supposedToBeMarkdown) -import Distribution.Server.Pages.Template (hackagePage) +import Distribution.Server.Pages.Template (hackagePageWithHead, syntaxHighlightingHead) import Distribution.Text import Distribution.Package @@ -586,8 +586,10 @@ candidatesFeature ServerEnv{serverBlobStore = store} =<< liftIO (loadTarEntry tarfile offset) cacheControl [Public, maxAgeDays 30] etag return $ toResponse $ Resource.XHtml $ - let title = "Changelog for " ++ display (packageId pkg) in - hackagePage title + let title = "Changelog for " ++ display (packageId pkg) + headExtra = if supposedToBeMarkdown filename then syntaxHighlightingHead else [] + in + hackagePageWithHead headExtra title [ XHtml.h2 << title , XHtml.thediv ! [XHtml.theclass "embedded-author-content"] << if supposedToBeMarkdown filename diff --git a/src/Distribution/Server/Features/PackageContents.hs b/src/Distribution/Server/Features/PackageContents.hs index 43b2df134..03dddbd17 100644 --- a/src/Distribution/Server/Features/PackageContents.hs +++ b/src/Distribution/Server/Features/PackageContents.hs @@ -19,7 +19,7 @@ import Distribution.Server.Features.Users import Distribution.Server.Util.ServeTarball import Distribution.Server.Util.Markdown (renderMarkdown, supposedToBeMarkdown) import Distribution.Server.Util.Parse (unpackUTF8) -import Distribution.Server.Pages.Template (hackagePage) +import Distribution.Server.Pages.Template (hackagePageWithHead, syntaxHighlightingHead) import Distribution.Text import Distribution.Package @@ -151,7 +151,8 @@ packageContentsFeature CoreFeature{ coreResource = CoreResource{ return $ toResponse $ Resource.XHtml $ let title = "Changelog for " ++ pkgName title2 = "Changelog for " XHtml.+++ (XHtml.anchor ! [XHtml.href url] << pkgName) - in hackagePage title + headExtra = if supposedToBeMarkdown filename then syntaxHighlightingHead else [] + in hackagePageWithHead headExtra title [ XHtml.h2 << title2 , XHtml.thediv ! [XHtml.theclass "embedded-author-content"] << if supposedToBeMarkdown filename @@ -184,8 +185,10 @@ packageContentsFeature CoreFeature{ coreResource = CoreResource{ =<< liftIO (loadTarEntry tarfile offset) cacheControl [Public, maxAgeDays 30] etag return $ toResponse $ Resource.XHtml $ - let title = "Readme for " ++ display (packageId pkg) in - hackagePage title + let title = "Readme for " ++ display (packageId pkg) + headExtra = if supposedToBeMarkdown filename then syntaxHighlightingHead else [] + in + hackagePageWithHead headExtra title [ XHtml.h2 << title , XHtml.thediv ! [XHtml.theclass "embedded-author-content"] << if supposedToBeMarkdown filename diff --git a/src/Distribution/Server/Pages/PackageFromTemplate.hs b/src/Distribution/Server/Pages/PackageFromTemplate.hs index d1b919857..71d5e429e 100644 --- a/src/Distribution/Server/Pages/PackageFromTemplate.hs +++ b/src/Distribution/Server/Pages/PackageFromTemplate.hs @@ -41,6 +41,7 @@ import Data.Time.Format (defaultTimeLocale, formatTime) import qualified Data.ByteString.Lazy as BS (ByteString) import qualified Distribution.Server.Pages.Package as Old +import Distribution.Server.Pages.Template (syntaxHighlightingHead) import Data.Time.Clock (UTCTime) import Distribution.Server.Features.Html.HtmlUtilities @@ -97,6 +98,7 @@ packagePageTemplate render -- Miscellaneous things that could still stand to be refactored a bit. [ "moduleList" $= Old.moduleSection render mdocIndex docURL mPkgId hasQuickNav , "downloadSection" $= Old.downloadSection render + , "syntaxHighlighting" $= readmeSyntaxHighlighting ] else -- The main two namespaces @@ -111,8 +113,15 @@ packagePageTemplate render , "stability" $= renderStability desc , "isDeprecated" $= isJust deprs , "deprecatedMsg" $= deprHtml deprs + , "syntaxHighlighting" $= readmeSyntaxHighlighting ] where + readmeSyntaxHighlighting = + case (rendReadme render, mreadme) of + (Just (_, _, _, filename), Just _) + | supposedToBeMarkdown filename -> toHtml syntaxHighlightingHead + _ -> noHtml + -- Access via "$hackage.varName$" hackageFieldsTemplate = if isCandidate diff --git a/src/Distribution/Server/Pages/Template.hs b/src/Distribution/Server/Pages/Template.hs index 18096d90e..57818f1d2 100644 --- a/src/Distribution/Server/Pages/Template.hs +++ b/src/Distribution/Server/Pages/Template.hs @@ -3,6 +3,7 @@ module Distribution.Server.Pages.Template ( hackagePage , hackagePageWith , hackagePageWithHead + , syntaxHighlightingHead ) where import Text.XHtml.Strict @@ -63,6 +64,36 @@ navigationBar = ] +-- | Stylesheets and scripts that highlight fenced code blocks in rendered +-- Markdown. +syntaxHighlightingHead :: [Html] +syntaxHighlightingHead = + [ cdnStylesheet "themes/prism-solarizedlight.min.css" + "sha384-k3ZUhhfN2mU6+BTQVzpopVVQOC5ZJ4WNroBrXNNQqawO4e3kOWFCYCT77lDQupo2" + "(prefers-color-scheme: light)" + , cdnStylesheet "themes/prism-tomorrow.min.css" + "sha384-wFjoQjtV1y5jVHbt0p35Ui8aV8GVpEZkyF99OXWqP/eNJDU93D3Ugxkoyh6Y2I4A" + "(prefers-color-scheme: dark)" + , (script noHtml) ! [strAttr "defer" "defer", src "/static/prism-setup.js"] + , cdnScript "components/prism-core.min.js" + "sha384-zLRFO4dwowZvh8kzutOb5AWhH7f39HeJp+N7PtHF1SQtTBnifRx0AtmvTYs3F4YV" + , cdnScript "plugins/autoloader/prism-autoloader.min.js" + "sha384-Uq05+JLko69eOiPr39ta9bh7kld5PKZoU+fF7g0EXTAriEollhZ+DrN8Q/Oi8J2Q" + ] + where + prismURL path = "https://cdn.jsdelivr.net/npm/prismjs@1.30.0/" ++ path + cdnStylesheet path hash media = + thelink ! [ rel "stylesheet" + , href (prismURL path) + , strAttr "integrity" hash + , strAttr "crossorigin" "anonymous" + , strAttr "media" media] << noHtml + cdnScript path hash = + (script noHtml) ! [ strAttr "defer" "defer" + , src (prismURL path) + , strAttr "integrity" hash + , strAttr "crossorigin" "anonymous"] + googleFontURL :: URL googleFontURL = "https://fonts.googleapis.com/css?family=PT+Sans:400,400i,700"