mikepenz/multiplatform-markdown-renderer

[question/enhancement] override default markdownComponents recursively

Closed this issue ยท 6 comments

About this issue

This is not a real issue but more of a question or enhancement proposal, because either I am not getting it (despite having checked the "Advanced usage" section in the README multiple times), or what I would like to do can not be done actually with the current version and from this discussion new evolution proposals can come. I candidate myself to help out if needed.

First of all, thanks for this awesome library: I am following the project since year and I think this is one of the "missing pieces" that are crucial for Kotlin Multiplatform development. Also, I really appreciated the possibility to provide custom MarkdownComponents that was introduced with 0.9.0, because it was really difficult to support Markdown dialectal extensions (e.g. spoilers with the ::: spoiler <title>\n<content>\n::: syntax).

But, from what I see, the user-provided components are used only at the top level and there is no easy way to use the newly provided component as a subcomponent of another one. Let me explain with an example: suppose I am providing my custom MarkdownComponent to render images, this works fine to handle an IMAGE element but if a TEXT element contains an inline image the default Image is used and not the custom one because MarkdownText directly uses the built-in Image Composable. Similarly, if I provide a custom MarkdownComponent to render TEXT blocks, this is not called inside a PARAGRAPH because MarkdownParagraph internally calls the default MarkdownText and custom components are only queried in the handleElement function which is running of every node of the AST.

Shouldn't it be possible to retrieve MarkdownComponents from composition locals?
Feel free to close if not relevant and thank you in the meantime for this great project.

I'm currently using library version 0.12.0, Compose multiplatform 1.5.12, Gradle 8.6 with AGP 8.2.2

Details

  •  Used library version
  •  Used platform
  •  Used support library version
  •  Used gradle build tools version
  •  Used tooling / Android Studio version
  •  Other used libraries, potential conflicting libraries

Checklist

Thank you very much @diegoberaldin for the question.

The note on top-level is not fully the case. The custom components apply to children and the childrens children as identified by the markdown parser. You can see the specific logic here:
https://github.com/mikepenz/multiplatform-markdown-renderer/blob/develop/multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/compose/Markdown.kt#L56-L63

With that said, there is different handling at play depending on the item. For example an IMAGE top level is a very different component than an inline image.

https://github.com/mikepenz/multiplatform-markdown-renderer/blob/develop/multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/compose/Markdown.kt#L94
vs
https://github.com/mikepenz/multiplatform-markdown-renderer/blob/develop/multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/utils/AnnotatedStringKtx.kt#L71-L73

Which makes it unfortunately not as simple.
Also at this time items require a ColumnScope to be retrieved, however the specific composeables don't have the columnscope anymore.

The general idea was, in case you require a very different handling of paragraphs, that it is very likely it to be custom handled all-together - That said, you can use whichever custom MarkdownText you'd have in that case in your custom MarkdownParagraph to make it work the way needed.
In that specific example, top level MarkdownText get's a String whereas the one in the Paragraph gets an AnnotatedString already.

I had a look to check how simple / complex it would be to bring this feature in, unfortunately I think at this time it's not as feasible, due to the way things need to be different as a core component, vs when used as component within a component.

Ok thanks for answering.

With all of that said though, I am open to see improvements made on the flexibility of the library. Aiming to provide a great baseline, with the opportunity to expand for special cases! So if you, or someone else has a nice way to contribute some more flexibility I am happy to review pr's.

Also thanks again for the kind words!

Ok, I can fork and experiment, I'll let you know if I acheive some interesting result. With the current situation not only I managed to support spoilers (which are common on Lemmy) but also custom links (the so called "handles" that are used on Lemmy).

Ok, I can fork and experiment, I'll let you know if I acheive some interesting result. With the current situation not only I managed to support spoilers (which are common on Lemmy) but also custom links (the so called "handles" that are used on Lemmy).

May I ask how you have done so? I am also in the case where I need support of spoilers and other components not already in the base library.

I didn't have time to fork and pass custom handlers through composition locals, unfortunately. And in the meantime I managed to add support to custom spoilers using a workaround you can find here and here.

This, of course, doesn't mean the issue for me is closed, my project heavily relies on MD rendering and the platform I'm working on has several "dialectal" extensions to deal with, I have open issues for some of them e.g. this one. If you want to "join forces" and work on a solution together I'm in.