Slide 1

Slide 1 text

NOTJUST PLAINTEXT RENDERING MARKDOWN IN COMPOSE Sebastiano Poggi @sebastiano.dev @[email protected]

Slide 2

Slide 2 text

HAS THIS EVER HAPPENED TO YOU?

Slide 3

Slide 3 text

“I want to display rich text in this label” — You, a fancy cursive-speaking person

Slide 4

Slide 4 text

Reticulate splines Enable spline reticulation algorithms Learn more “I want to display rich text in this label” — You, a fancy cursive-speaking person

Slide 5

Slide 5 text

Web HTML & CSS

Slide 6

Slide 6 text

Web HTML & CSS Swing HTML & CSS

Slide 7

Slide 7 text

Web HTML & CSS Swing HTML & CSS **

Slide 8

Slide 8 text

Web HTML & CSS Swing HTML & CSS ** Android views HTML, to some extent

Slide 9

Slide 9 text

Web HTML & CSS Swing HTML & CSS ** Android views HTML, to some extent Jetpack Compose HTML, to some extent

Slide 10

Slide 10 text

Web HTML & CSS Swing HTML & CSS ** Android views HTML, to some extent Jetpack Compose HTML, to some extent Compose for Desktop Well, err… DIY

Slide 11

Slide 11 text

WE CAN DO BETTER

Slide 12

Slide 12 text

Small detour

Slide 13

Slide 13 text

** HTML & CSS

Slide 14

Slide 14 text

** a lot of heavy lifting

Slide 15

Slide 15 text

https://www.codewiththeitalians.it/

Slide 16

Slide 16 text

https://www.codewiththeitalians.it/

Slide 17

Slide 17 text

https://www.codewiththeitalians.it/ Fix with CSS! overflow-wrap: break-word;

Slide 18

Slide 18 text

https://www.codewitht heitalians.it/ overflow-wrap: break-word; Fix with CSS!

Slide 19

Slide 19 text

overflow-wrap: break-word; …does not work https://www.codewiththeitalians.it/ Fix with CSS!

Slide 20

Slide 20 text

What do we do, then?

Slide 21

Slide 21 text

IF YOU GUESSED HERE’S YOUR TROPHY ! HACKS What do we do, then?

Slide 22

Slide 22 text

Inject strategically https://www.codewiththeitalians.it/

Slide 23

Slide 23 text

https://www.codewiththeitalians.it/ Inject strategically

Slide 24

Slide 24 text

https://www. codewiththeitalians.it/ Inject strategically

Slide 25

Slide 25 text

https://www.codewitht heitalians.it/ EVERYTHING IS A LIE Inject strategically

Slide 26

Slide 26 text

DO NOT WANT /end of detour

Slide 27

Slide 27 text

WHAT DO WE DO? MARKDOWN maybe slightly spoiled by the talk title

Slide 28

Slide 28 text

Why Markdown Easy to read and write Does not allow users to de!ne styling Good for my use case ! Great for UI text and i18n

Slide 29

Slide 29 text

Why Markdown Easy to read and write Does not allow users to de!ne styling Good for my use case ! Great for UI text and i18n

Slide 30

Slide 30 text

Hello world!"font>

Slide 31

Slide 31 text

Hello world!"font> Hello world

Slide 32

Slide 32 text

Hello world!"font> Hello world

Slide 33

Slide 33 text

world Hello world Hello world!"font>

Slide 34

Slide 34 text

world Hello world Hello world!"font>

Slide 35

Slide 35 text

Hello world Hello world!"font>

Slide 36

Slide 36 text

Can’t happen in Markdown!

Slide 37

Slide 37 text

Can’t happen in Markdown! bold strong emphasis

Slide 38

Slide 38 text

Hello **world**

Slide 39

Slide 39 text

Hello **world** Hello world!"strong>

Slide 40

Slide 40 text

Hello **world** Hello world Hello world!"strong>

Slide 41

Slide 41 text

Hello **world** Hello world Hello world!"strong>

Slide 42

Slide 42 text

Hello **world** Hello world Hello world!"strong>

Slide 43

Slide 43 text

Hello **world** Hello world Hello world!"strong>

Slide 44

Slide 44 text

WE WANT NORMAL

Slide 45

Slide 45 text

WHAT IS NORMAL, ANYWAY?

Slide 46

Slide 46 text

what’s expected normal

Slide 47

Slide 47 text

what’s expected normal

Slide 48

Slide 48 text

GitHub GitLab Issue trackers Google Docs Blog post composers StackOver"ow …and more

Slide 49

Slide 49 text

Intuitive expectations GitHub GitLab Issue trackers Google Docs Blog post composers StackOver"ow …and more

Slide 50

Slide 50 text

Intuitive expectations MARKDOWN IS A LIE

Slide 51

Slide 51 text

Time for another rabbit hole? "

Slide 52

Slide 52 text

John Gruber’s Daring Fireball — 2004

Slide 53

Slide 53 text

John Gruber’s Daring Fireball — 2004 Loves Apple Tech opinionist

Slide 54

Slide 54 text

John Gruber’s Daring Fireball — 2004 Reference parser The spec lives here (intentionally) Vague

Slide 55

Slide 55 text

Reference parser …written in Perl

Slide 56

Slide 56 text

Reference parser …written in Perl “write-only language”

Slide 57

Slide 57 text

Markdown specs are limited

Slide 58

Slide 58 text

Missing “expected” features: Tables Syntax highlighting Emojis Footnotes Strikethrough …

Slide 59

Slide 59 text

VAGUE SPECS MISSING FEATURES BAD REFERENCE # "

Slide 60

Slide 60 text

Many incompatible implementations

Slide 61

Slide 61 text

Many incompatible implementations Babelmark

Slide 62

Slide 62 text

Many incompatible implementations GitHub Flavored Markdown

Slide 63

Slide 63 text

Many incompatible implementations GFM

Slide 64

Slide 64 text

What is the !x?

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

COMMONMARK Born in 2014 as Standard Markdown

Slide 67

Slide 67 text

CommonMark GFM GLFM StackOver"ow …

Slide 68

Slide 68 text

“FUN” FACT GFM is also poorly specced Features de!ned in docs but not specs

Slide 69

Slide 69 text

HOWEVER For many Markdown is ~GFM CommonMark has a nice parser

Slide 70

Slide 70 text

Let’s use CommonMark! commonmark-java, not jetbrains/markdown

Slide 71

Slide 71 text

Let’s use CommonMark! commonmark-java, not jetbrains/markdown Kotlin-based, extensible Missing GFM features Not fully compliant

Slide 72

Slide 72 text

Let’s use CommonMark! commonmark-java, not jetbrains/markdown Java-based, extensible GFM extensions Fully compliant

Slide 73

Slide 73 text

HOW TO RENDER MARKDOWN IN TWO EASY* STEPS

Slide 74

Slide 74 text

Small detour

Slide 75

Slide 75 text

Jewel Compose for Desktop theme & components code lives here

Slide 76

Slide 76 text

End of detour

Slide 77

Slide 77 text

MARKDOWN

Slide 78

Slide 78 text

MARKDOWN ...how?

Slide 79

Slide 79 text

Raw MarkDown Parse as AST Emit UI components Raw Markdown

Slide 80

Slide 80 text

Raw MarkDown Parse as AST Emit UI components step 1 step 2 Raw Markdown

Slide 81

Slide 81 text

Step 1: Parse as AST commonmark-java AST

Slide 82

Slide 82 text

Step 1: Parse as AST commonmark-java AST outside the composition

Slide 83

Slide 83 text

Block Block Block Block …

Slide 84

Slide 84 text

Block Block Block Block … Block Block or Inline node Inline node … …

Slide 85

Slide 85 text

Can’t use CommonMark models as-is No equals() and hashCode() No random access to children

Slide 86

Slide 86 text

Parse as AST List ✅

Slide 87

Slide 87 text

Step 2a: Render block nodes Step 2b: Render inline nodes Step 2: Emit UI components

Slide 88

Slide 88 text

Step 2a: Render block nodes Step 2b: Render inline nodes Step 2: Emit UI components inside the composition

Slide 89

Slide 89 text

Step 2a: Render block nodes Step 2b: Render inline nodes inside the composition

Slide 90

Slide 90 text

Step 2a: Render block nodes Step 2b: Render inline nodes inside the composition MarkdownBlocks composables

Slide 91

Slide 91 text

Block Block … Block … Block How do we handle nested blocks? Block

Slide 92

Slide 92 text

RECURSION $

Slide 93

Slide 93 text

Blockquote Bullet list Paragraph Header 1 Image Divider Paragraph Code block Paragraph … … Simple item Complex item …

Slide 94

Slide 94 text

Paragraph … Blockquote Complex item Simple item … Bullet list Paragraph Header 1 Image Divider Paragraph Code block … render( )

Slide 95

Slide 95 text

Blockquote Paragraph … Complex item Simple item … Bullet list Paragraph Header 1 Image Divider Paragraph Code block … render( )

Slide 96

Slide 96 text

Blockquote Paragraph … Blockquote Complex item Simple item … Bullet list Paragraph Header 1 Image Divider Paragraph Code block … render( )

Slide 97

Slide 97 text

Blockquote Paragraph Header 1 Image Divider Paragraph Code block … Paragraph … Complex item Simple item … Bullet list render( render( )

Slide 98

Slide 98 text

Blockquote Paragraph Header 1 Image Divider Paragraph Code block … Paragraph … Complex item Simple item … Bullet list render( render( )

Slide 99

Slide 99 text

YEAH BUT ACTUALLY WHAT ABOUT HTML?

Slide 100

Slide 100 text

No HTML. Don’t want to implement a browser

Slide 101

Slide 101 text

No HTML. Don’t want to implement a browser May have special casing

Slide 102

Slide 102 text

Step 2a: Step 2b: Render inline nodes ✅ Render block nodes

Slide 103

Slide 103 text

Paragraph Inline node Inline node … No recursion needed %

Slide 104

Slide 104 text

Step 2a: Render block nodes InlineMarkdown inside the composition AnnotatedString Step 2b: Render inline nodes

Slide 105

Slide 105 text

WE HAVE A PROBLEM AnnotatedString

Slide 106

Slide 106 text

WE HAVE A PROBLEM AnnotatedString limited by TextStyle capabilities

Slide 107

Slide 107 text

Case study: inline code RAW MARKDOWN RENDERED MARKDOWN This PR adds animations to the `Onboarding` flow.

Slide 108

Slide 108 text

Case study: inline code RAW MARKDOWN RENDERED MARKDOWN This PR adds animations to the Onboarding "ow. This PR adds animations to the `Onboarding` flow.

Slide 109

Slide 109 text

Case study: inline code This PR adds animations to the `Onboarding` flow. RAW MARKDOWN This PR adds animations to the Onboarding "ow. RENDERED MARKDOWN Onboa &

Slide 110

Slide 110 text

Case study: inline code This PR adds animations to the `Onboarding` flow. RAW MARKDOWN RENDERED MARKDOWN This PR adds animations to the Onboarding "ow. Onboardin '

Slide 111

Slide 111 text

Case study: inline code I reckon you could improve it by using `animateFloatAsState(target, tween(1000, 1500))` ! RAW MARKDOWN RENDERED MARKDOWN I reckon you could improve it by using animateFloatAsState (target, tween(1000, 1500)) ! (

Slide 112

Slide 112 text

inline images and links

Slide 113

Slide 113 text

inline images and links doable, but wip InlineTextContent Coil for loading Layout jumps unavoidable

Slide 114

Slide 114 text

inline images and links Use LinkAnnotation Stateful styling Can’t draw focus ring

Slide 115

Slide 115 text

inline images and links Use LinkAnnotation Stateful styling Can’t draw focus ring Reticulate splines Enable spline reticulation algorithms Learn more

Slide 116

Slide 116 text

IT WILL GET BETTER (I hope)

Slide 117

Slide 117 text

PUTTING IT ALL TOGETHER

Slide 118

Slide 118 text

Two-pass processing: Parse as AST Emit UI components

Slide 119

Slide 119 text

Two-pass processing: Parse as AST Parse as AST Emit UI components Emit UI components Outside the composition Run only when raw Markdown changes

Slide 120

Slide 120 text

Two-pass processing: Parse as AST Emit UI components Outside the composition Run only when raw Markdown changes Inside the composition Run when raw Markdown changes Run when theme changes Run when editor scheme changes Builds AnnotatedStrings: expensive?

Slide 121

Slide 121 text

Display rich text Render large !le vs

Slide 122

Slide 122 text

Display rich text Render large !le vs many small instances minimise overhead

Slide 123

Slide 123 text

Display rich text Render large !le vs many small instances lazy container editor mode minimise overhead

Slide 124

Slide 124 text

Display rich text Render large !le vs many small instances lazy container editor mode scroll sync (wip) minimise overhead

Slide 125

Slide 125 text

ONE LAST THING ) ☝️

Slide 126

Slide 126 text

WYSIWYG is not possible

Slide 127

Slide 127 text

LET’S SEE THIS IN ACTION

Slide 128

Slide 128 text

No content

Slide 129

Slide 129 text

Good enough* to render its readme Has missing/WIP features Extensible: GFM features

Slide 130

Slide 130 text

I SWEAR THIS IS ALMOST OVER

Slide 131

Slide 131 text

Forked by OpenAI for ChatGPT Android A HAT TIP TO https://halilibo.com/compose-richtext/ the one from the keynote

Slide 132

Slide 132 text

The OG Markdown renderer inspo ANOTHER HAT TIP TO https://github.com/ErikHellman/MarkdownComposer

Slide 133

Slide 133 text

SO WHAT I MEANT TO SAY ALL ALONG IS…

Slide 134

Slide 134 text

Markdown is great Especially for i18n and server-driven UI Try out Markdown in Jewel …or make your own renderer

Slide 135

Slide 135 text

THANK YOU ♥️ @sebastiano.dev @[email protected]