hi! i'm trying to render markdown footnotes to HTML. i see that md/parse preserves them (out-of-line, not in :content), but when i call md/->hiccup, they get discarded:
user=> note
"abc [^1]\n\n[^1]: def"
user=> (md/parse note)
{:toc {:type :toc}, :footnotes [{:type :footnote, :label "1", :content [{:type :paragraph, :content [{:type :text, :text "def"}]}], :ref 0}], :content [{:type :paragraph, :content [{:type :text, :text "abc "} {:type :footnote-ref, :ref 0, :label "1"}]}], :type :doc}
user=> (md/->hiccup note)
[:div [:p "abc " [:sup.sidenote-ref {:data-label "1"} "1"]]]
i also see that this happens on the live demo on https://nextjournal.github.io/markdown/notebooks/try/. what is the intended way to use footnotes with nextjournal?Good question!
Let's ping @andrea712
this seemed to work ok 🙂
(defn render-md [src]
(let [parsed (md/parse src)
footnotes (map md/->hiccup (:footnotes parsed))
rendered (md/->hiccup parsed)
combined (concat rendered [[:hr]] footnotes)]
(h/html combined)))nice!
yeah, I don't think there's a built-in handling of footnotes, we have some transformation to generate sidenotes as this is the usecase we had in Clerk https://github.com/nextjournal/markdown/blob/51d2b58168def75b16a89693b74ee74d6cf3e0a7/test/nextjournal/markdown_test.cljc#L780-L823
there's also a NOTE: https://github.com/nextjournal/markdown/blob/bdd31ddfb0eda047d009c8ad8da65081093d7714/src/nextjournal/markdown/transform.cljc#L139 🫢
lol
what is the difference between a :sidenote and a :footnote? how does a :sidenote get generated?
the second link I pasted above e.g
(-> "Text[^firstnote] and^[inline _note_ here].
Par.
- again[^note2]
- here
[^firstnote]: Explain 1
[^note2]: Explain 2
"
md/parse
u/insert-sidenote-containers)i'm confused - those look like footnotes to me
yeah, the function u/insert-sidenote-containers builds sidenotes, I think it reflows data to be compatible with the sidenote type renderers
:sidenote-container
:sidenote-column
:sidenote-ref
:sidenote oh, so you're saying they're parsed as :footnotes, and then insert-sidenote transforms the AST into :sidenotes, before they're transformed to hiccup?
yes, but that's a very specific markup, I think what you came up with above is also nice
you can also move that into the :doc renderer
(nextjournal.markdown/->hiccup
(assoc nextjournal.markdown/default-hiccup-renderers
:doc (fn [_ node]
(concat [(nextjournal.markdown/->hiccup node)]
[[:hr]]
(map nextjournal.markdown/->hiccup (:footnotes node)))))
"Text[^firstnote] and^[inline _note_ here].
Par.
- again[^note2]
- here
[^firstnote]: Explain 1
[^note2]: Explain 2
")
and along those lines also customize how the :footnote and :footnote-ref types are rendered
i was just looking at that, yeah
i think the :doc renderer is a little overkill but i do really like the :footnote hook, it's made things a lot easier
what return types does :footnote expect? i gave it a hiccup-style array and it said "Unknown type: ''. in bold.
if there's a way to get md/->hiccup to actually throw an exception instead of returning a markdown string that says "error", that would be very helpful
It seems type is nil?yes but my question is what format it expects. does it want a single map? how does that account for sibling HTML nodes?
for context, here is :content on a footnote:
[{:type :paragraph, :content [{:type :text, :text "def"}]}]i can guess what it wants but i worry that it will break the second the markdown is slightly different than what i tested with
there's into-markup, that's what the default renderers are using internally, but i'm not sure if it's intended to be public api <https://github.com/nextjournal/markdown/blob/bdd31ddfb0eda047d009c8ad8da65081093d7714/src/nextjournal/markdown/transform.cljc#L34>
markdown.transform is a public namespace. you can view the API in API.md
no sorry
it's not listed: https://github.com/nextjournal/markdown/blob/16be4ef6b8b5741c5d8f8432dd9ce8b52b1f3f68/API.md
right
that said into-hiccup is an alias for it, just with worse docs
it used to be public but we decided that those function could go into nextjournal.markdown
https://github.com/nextjournal/markdown/blob/16be4ef6b8b5741c5d8f8432dd9ce8b52b1f3f68/src/nextjournal/markdown/transform.cljc#L4
skip-wiki basically means: not public
ok. do you have docs anywhere on how to write a custom renderer?
I agree that the docs can be improved. PR welcome
for that function I mean
happy to send a PR once i understand how to do the thing i want to do lol
do you know what hiccup is?
(no offense intended)
yes, i'm familiar
what i don't know is how to recurse on the :content in a footnote. i expected it to be md/->hiccup and that broke
as long as you return valid hiccup, you're good. the functions available are just intended to make it easier, e.g. to render child nodes you want to render according to the defaults
recurse can be done using into-hiccup indeed
e.g. this is how you can render a different "wrapper":
:doc (partial md/into-hiccup [:div.viewer-markdown])(this is from the README)
this worked
(defn trans-footnote [cx note]
; NOTE: we ignore :label for now
(md/into-hiccup [:li {:id (str "fn-" (:ref note))}] cx note))👍
ok, i will add some docs to into-hiccup
❤️
feedback: no need to insert literal \n in docstrings, just the visible returns are sufficient.
merged, thanks!
am i reading correctly that into-hiccup only lets me prepend content, not append? oh wait it's returning hiccup data, i can append to that
right