1 module Utils.Pandoc where
3 import Data.List qualified as List
5 import Text.Pandoc.Definition
6 import Text.Pandoc.Walk (query)
8 trimWhiteInlines :: [Inline] -> [Inline]
10 List.dropWhile isWhite .
11 List.dropWhileEnd isWhite
14 isWhite SoftBreak = True
18 renderPandocBlock :: B.Block -> H.Html
19 renderPandocBlock = \case
21 H.blockquote $ forM_ bs renderPandocBlock
23 H.ul ! A.class_ (listStyle <> " list-disc") $
25 H.li ! A.class_ listItemStyle $ forM_ bs renderPandocBlock
26 B.CodeBlock (id', classes, attrs) s ->
27 -- Prism friendly classes
28 let classes' = flip List.concatMap classes $ \classes -> [classes, "language-" <> classes]
30 ! A.class_ "py-0.5 text-sm"
32 ! renderPandocAttr (id', classes', attrs)
34 ! renderPandocAttr ("", classes', [])
36 B.DefinitionList defs ->
38 forM_ defs $ \(term, descList) -> do
39 forM_ term renderPandocInline
40 forM_ descList $ \desc ->
41 H.dd $ forM_ desc renderPandocBlock
43 H.div ! renderPandocAttr attr $ forM_ bs renderPandocBlock
44 B.Header level attr is ->
45 renderHeader level ! renderPandocAttr attr $ do
46 fromString $ show attr
47 forM_ is renderPandocInline
48 B.HorizontalRule -> H.hr
51 forM_ is renderPandocInline >> "\n"
53 B.OrderedList _ bss ->
54 H.ol ! A.class_ (listStyle <> " list-decimal") $
56 H.li ! A.class_ listItemStyle $
57 forM_ bs renderPandocBlock
59 H.p ! A.class_ "my-2" $ forM_ is renderPandocInline
61 forM_ is renderPandocInline
62 B.RawBlock (B.Format fmt) html
63 | fmt == "html" -> H.unsafeByteString $ encodeUtf8 html
64 | otherwise -> throw Unsupported
68 listStyle = "list-inside ml-2 space-y-1 "
71 renderHeader :: Int -> H.Html -> H.Html
73 1 -> H.h1 ! classes ("text-xl":cs)
74 2 -> H.h2 ! classes ("text-xl":cs)
75 3 -> H.h3 ! classes ("text-lg":cs)
76 4 -> H.h4 ! classes ("text-lg":cs)
77 5 -> H.h5 ! classes ("text-lg":cs)
78 6 -> H.h6 ! classes ("text-lg":cs)
79 _ -> error "Invalid pandoc header level"
81 cs = ["mt-4", "mb-2", "font-bold"]
83 renderPandocInline :: B.Inline -> H.Html
84 renderPandocInline = \case
86 H.code ! renderPandocAttr attr $ H.toHtml s
88 H.em $ forM_ is renderPandocInline
89 B.Image attr is (url, title) ->
90 H.img ! A.src (H.textValue url) ! A.title (H.textValue title) ! A.alt (H.textValue $ Markdown.plainify is) ! renderPandocAttr attr
91 B.Link attr is (url, title) -> do
92 let (classes, target) =
93 if "://" `T.isInfixOf` url
94 then ("text-blue-600 hover:underline", targetBlank)
95 else ("text-blue-600 hover:bg-blue-50", mempty)
98 ! A.href (H.textValue url)
99 ! A.title (H.textValue title)
101 ! renderPandocAttr attr
102 $ forM_ is renderPandocInline
109 flip inQuotes qt $ forM_ is renderPandocInline
110 B.RawInline _fmt s ->
115 H.span ! renderPandocAttr attr $ forM_ is renderPandocInline
116 B.Str s -> H.toHtml s
118 H.del $ forM_ is renderPandocInline
120 H.strong $ forM_ is renderPandocInline
122 H.sub $ forM_ is renderPandocInline
124 H.sup $ forM_ is renderPandocInline
126 H.u $ forM_ is renderPandocInline
128 H.pre $ H.toHtml $ show @Text x
130 inQuotes :: H.Html -> B.QuoteType -> H.Html
132 B.SingleQuote -> "‘" >> w <* "’"
133 B.DoubleQuote -> "“" >> w <* "”"
135 targetBlank :: H.Attribute
136 targetBlank = A.target "_blank" <> A.rel "noopener"
138 renderPandocAttr :: B.Attr -> H.Attribute
139 renderPandocAttr (id_, classes, attrs) =
140 unlessNull id_ (A.id (H.textValue id_))
141 <> unlessNull class_ (A.class_ (H.textValue class_))
142 <> foldMap (\(k, v) -> H.dataAttribute (H.textTag k) (H.textValue v)) attrs
144 class_ = T.intercalate " " classes
149 data Unsupported = Unsupported
150 deriving (Show, Exception)